1987da915Sopenharmony_ci/** 2987da915Sopenharmony_ci * attrib.c - Attribute handling code. Originated from the Linux-NTFS project. 3987da915Sopenharmony_ci * 4987da915Sopenharmony_ci * Copyright (c) 2000-2010 Anton Altaparmakov 5987da915Sopenharmony_ci * Copyright (c) 2002-2005 Richard Russon 6987da915Sopenharmony_ci * Copyright (c) 2002-2008 Szabolcs Szakacsits 7987da915Sopenharmony_ci * Copyright (c) 2004-2007 Yura Pakhuchiy 8987da915Sopenharmony_ci * Copyright (c) 2007-2021 Jean-Pierre Andre 9987da915Sopenharmony_ci * Copyright (c) 2010 Erik Larsson 10987da915Sopenharmony_ci * 11987da915Sopenharmony_ci * This program/include file is free software; you can redistribute it and/or 12987da915Sopenharmony_ci * modify it under the terms of the GNU General Public License as published 13987da915Sopenharmony_ci * by the Free Software Foundation; either version 2 of the License, or 14987da915Sopenharmony_ci * (at your option) any later version. 15987da915Sopenharmony_ci * 16987da915Sopenharmony_ci * This program/include file is distributed in the hope that it will be 17987da915Sopenharmony_ci * useful, but WITHOUT ANY WARRANTY; without even the implied warranty 18987da915Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19987da915Sopenharmony_ci * GNU General Public License for more details. 20987da915Sopenharmony_ci * 21987da915Sopenharmony_ci * You should have received a copy of the GNU General Public License 22987da915Sopenharmony_ci * along with this program (in the main directory of the NTFS-3G 23987da915Sopenharmony_ci * distribution in the file COPYING); if not, write to the Free Software 24987da915Sopenharmony_ci * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 25987da915Sopenharmony_ci */ 26987da915Sopenharmony_ci 27987da915Sopenharmony_ci#ifdef HAVE_CONFIG_H 28987da915Sopenharmony_ci#include "config.h" 29987da915Sopenharmony_ci#endif 30987da915Sopenharmony_ci 31987da915Sopenharmony_ci#ifdef HAVE_STDIO_H 32987da915Sopenharmony_ci#include <stdio.h> 33987da915Sopenharmony_ci#endif 34987da915Sopenharmony_ci#ifdef HAVE_STRING_H 35987da915Sopenharmony_ci#include <string.h> 36987da915Sopenharmony_ci#endif 37987da915Sopenharmony_ci#ifdef HAVE_STDLIB_H 38987da915Sopenharmony_ci#include <stdlib.h> 39987da915Sopenharmony_ci#endif 40987da915Sopenharmony_ci#ifdef HAVE_ERRNO_H 41987da915Sopenharmony_ci#include <errno.h> 42987da915Sopenharmony_ci#endif 43987da915Sopenharmony_ci#ifdef HAVE_LIMITS_H 44987da915Sopenharmony_ci#include <limits.h> 45987da915Sopenharmony_ci#endif 46987da915Sopenharmony_ci 47987da915Sopenharmony_ci#include "param.h" 48987da915Sopenharmony_ci#include "compat.h" 49987da915Sopenharmony_ci#include "attrib.h" 50987da915Sopenharmony_ci#include "attrlist.h" 51987da915Sopenharmony_ci#include "device.h" 52987da915Sopenharmony_ci#include "mft.h" 53987da915Sopenharmony_ci#include "debug.h" 54987da915Sopenharmony_ci#include "mst.h" 55987da915Sopenharmony_ci#include "volume.h" 56987da915Sopenharmony_ci#include "types.h" 57987da915Sopenharmony_ci#include "layout.h" 58987da915Sopenharmony_ci#include "inode.h" 59987da915Sopenharmony_ci#include "runlist.h" 60987da915Sopenharmony_ci#include "lcnalloc.h" 61987da915Sopenharmony_ci#include "dir.h" 62987da915Sopenharmony_ci#include "compress.h" 63987da915Sopenharmony_ci#include "bitmap.h" 64987da915Sopenharmony_ci#include "logging.h" 65987da915Sopenharmony_ci#include "misc.h" 66987da915Sopenharmony_ci#include "efs.h" 67987da915Sopenharmony_ci 68987da915Sopenharmony_cintfschar AT_UNNAMED[] = { const_cpu_to_le16('\0') }; 69987da915Sopenharmony_cintfschar STREAM_SDS[] = { const_cpu_to_le16('$'), 70987da915Sopenharmony_ci const_cpu_to_le16('S'), 71987da915Sopenharmony_ci const_cpu_to_le16('D'), 72987da915Sopenharmony_ci const_cpu_to_le16('S'), 73987da915Sopenharmony_ci const_cpu_to_le16('\0') }; 74987da915Sopenharmony_ci 75987da915Sopenharmony_cintfschar TXF_DATA[] = { const_cpu_to_le16('$'), 76987da915Sopenharmony_ci const_cpu_to_le16('T'), 77987da915Sopenharmony_ci const_cpu_to_le16('X'), 78987da915Sopenharmony_ci const_cpu_to_le16('F'), 79987da915Sopenharmony_ci const_cpu_to_le16('_'), 80987da915Sopenharmony_ci const_cpu_to_le16('D'), 81987da915Sopenharmony_ci const_cpu_to_le16('A'), 82987da915Sopenharmony_ci const_cpu_to_le16('T'), 83987da915Sopenharmony_ci const_cpu_to_le16('A'), 84987da915Sopenharmony_ci const_cpu_to_le16('\0') }; 85987da915Sopenharmony_ci 86987da915Sopenharmony_cistatic int NAttrFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) 87987da915Sopenharmony_ci{ 88987da915Sopenharmony_ci if (na->type == AT_DATA && na->name == AT_UNNAMED) 89987da915Sopenharmony_ci return (na->ni->flags & flag); 90987da915Sopenharmony_ci return 0; 91987da915Sopenharmony_ci} 92987da915Sopenharmony_ci 93987da915Sopenharmony_cistatic void NAttrSetFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) 94987da915Sopenharmony_ci{ 95987da915Sopenharmony_ci if (na->type == AT_DATA && na->name == AT_UNNAMED) 96987da915Sopenharmony_ci na->ni->flags |= flag; 97987da915Sopenharmony_ci else 98987da915Sopenharmony_ci ntfs_log_trace("Denied setting flag %d for not unnamed data " 99987da915Sopenharmony_ci "attribute\n", le32_to_cpu(flag)); 100987da915Sopenharmony_ci} 101987da915Sopenharmony_ci 102987da915Sopenharmony_cistatic void NAttrClearFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) 103987da915Sopenharmony_ci{ 104987da915Sopenharmony_ci if (na->type == AT_DATA && na->name == AT_UNNAMED) 105987da915Sopenharmony_ci na->ni->flags &= ~flag; 106987da915Sopenharmony_ci} 107987da915Sopenharmony_ci 108987da915Sopenharmony_ci#define GenNAttrIno(func_name, flag) \ 109987da915Sopenharmony_ciint NAttr##func_name(ntfs_attr *na) { return NAttrFlag (na, flag); } \ 110987da915Sopenharmony_civoid NAttrSet##func_name(ntfs_attr *na) { NAttrSetFlag (na, flag); } \ 111987da915Sopenharmony_civoid NAttrClear##func_name(ntfs_attr *na){ NAttrClearFlag(na, flag); } 112987da915Sopenharmony_ci 113987da915Sopenharmony_ciGenNAttrIno(Compressed, FILE_ATTR_COMPRESSED) 114987da915Sopenharmony_ciGenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED) 115987da915Sopenharmony_ciGenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE) 116987da915Sopenharmony_ci 117987da915Sopenharmony_ci/** 118987da915Sopenharmony_ci * ntfs_get_attribute_value_length - Find the length of an attribute 119987da915Sopenharmony_ci * @a: 120987da915Sopenharmony_ci * 121987da915Sopenharmony_ci * Description... 122987da915Sopenharmony_ci * 123987da915Sopenharmony_ci * Returns: 124987da915Sopenharmony_ci */ 125987da915Sopenharmony_cis64 ntfs_get_attribute_value_length(const ATTR_RECORD *a) 126987da915Sopenharmony_ci{ 127987da915Sopenharmony_ci if (!a) { 128987da915Sopenharmony_ci errno = EINVAL; 129987da915Sopenharmony_ci return 0; 130987da915Sopenharmony_ci } 131987da915Sopenharmony_ci errno = 0; 132987da915Sopenharmony_ci if (a->non_resident) 133987da915Sopenharmony_ci return sle64_to_cpu(a->data_size); 134987da915Sopenharmony_ci 135987da915Sopenharmony_ci return (s64)le32_to_cpu(a->value_length); 136987da915Sopenharmony_ci} 137987da915Sopenharmony_ci 138987da915Sopenharmony_ci/** 139987da915Sopenharmony_ci * ntfs_get_attribute_value - Get a copy of an attribute 140987da915Sopenharmony_ci * @vol: 141987da915Sopenharmony_ci * @a: 142987da915Sopenharmony_ci * @b: 143987da915Sopenharmony_ci * 144987da915Sopenharmony_ci * Description... 145987da915Sopenharmony_ci * 146987da915Sopenharmony_ci * Returns: 147987da915Sopenharmony_ci */ 148987da915Sopenharmony_cis64 ntfs_get_attribute_value(const ntfs_volume *vol, 149987da915Sopenharmony_ci const ATTR_RECORD *a, u8 *b) 150987da915Sopenharmony_ci{ 151987da915Sopenharmony_ci runlist *rl; 152987da915Sopenharmony_ci s64 total, r; 153987da915Sopenharmony_ci int i; 154987da915Sopenharmony_ci 155987da915Sopenharmony_ci /* Sanity checks. */ 156987da915Sopenharmony_ci if (!vol || !a || !b) { 157987da915Sopenharmony_ci errno = EINVAL; 158987da915Sopenharmony_ci return 0; 159987da915Sopenharmony_ci } 160987da915Sopenharmony_ci /* Complex attribute? */ 161987da915Sopenharmony_ci /* 162987da915Sopenharmony_ci * Ignore the flags in case they are not zero for an attribute list 163987da915Sopenharmony_ci * attribute. Windows does not complain about invalid flags and chkdsk 164987da915Sopenharmony_ci * does not detect or fix them so we need to cope with it, too. 165987da915Sopenharmony_ci */ 166987da915Sopenharmony_ci if (a->type != AT_ATTRIBUTE_LIST && a->flags) { 167987da915Sopenharmony_ci ntfs_log_error("Non-zero (%04x) attribute flags. Cannot handle " 168987da915Sopenharmony_ci "this yet.\n", le16_to_cpu(a->flags)); 169987da915Sopenharmony_ci errno = EOPNOTSUPP; 170987da915Sopenharmony_ci return 0; 171987da915Sopenharmony_ci } 172987da915Sopenharmony_ci if (!a->non_resident) { 173987da915Sopenharmony_ci /* Attribute is resident. */ 174987da915Sopenharmony_ci 175987da915Sopenharmony_ci /* Sanity check. */ 176987da915Sopenharmony_ci if (le32_to_cpu(a->value_length) + le16_to_cpu(a->value_offset) 177987da915Sopenharmony_ci > le32_to_cpu(a->length)) { 178987da915Sopenharmony_ci return 0; 179987da915Sopenharmony_ci } 180987da915Sopenharmony_ci 181987da915Sopenharmony_ci memcpy(b, (const char*)a + le16_to_cpu(a->value_offset), 182987da915Sopenharmony_ci le32_to_cpu(a->value_length)); 183987da915Sopenharmony_ci errno = 0; 184987da915Sopenharmony_ci return (s64)le32_to_cpu(a->value_length); 185987da915Sopenharmony_ci } 186987da915Sopenharmony_ci 187987da915Sopenharmony_ci /* Attribute is not resident. */ 188987da915Sopenharmony_ci 189987da915Sopenharmony_ci /* If no data, return 0. */ 190987da915Sopenharmony_ci if (!(a->data_size)) { 191987da915Sopenharmony_ci errno = 0; 192987da915Sopenharmony_ci return 0; 193987da915Sopenharmony_ci } 194987da915Sopenharmony_ci /* 195987da915Sopenharmony_ci * FIXME: What about attribute lists?!? (AIA) 196987da915Sopenharmony_ci */ 197987da915Sopenharmony_ci /* Decompress the mapping pairs array into a runlist. */ 198987da915Sopenharmony_ci rl = ntfs_mapping_pairs_decompress(vol, a, NULL); 199987da915Sopenharmony_ci if (!rl) { 200987da915Sopenharmony_ci errno = EINVAL; 201987da915Sopenharmony_ci return 0; 202987da915Sopenharmony_ci } 203987da915Sopenharmony_ci /* 204987da915Sopenharmony_ci * FIXED: We were overflowing here in a nasty fashion when we 205987da915Sopenharmony_ci * reach the last cluster in the runlist as the buffer will 206987da915Sopenharmony_ci * only be big enough to hold data_size bytes while we are 207987da915Sopenharmony_ci * reading in allocated_size bytes which is usually larger 208987da915Sopenharmony_ci * than data_size, since the actual data is unlikely to have a 209987da915Sopenharmony_ci * size equal to a multiple of the cluster size! 210987da915Sopenharmony_ci * FIXED2: We were also overflowing here in the same fashion 211987da915Sopenharmony_ci * when the data_size was more than one run smaller than the 212987da915Sopenharmony_ci * allocated size which happens with Windows XP sometimes. 213987da915Sopenharmony_ci */ 214987da915Sopenharmony_ci /* Now load all clusters in the runlist into b. */ 215987da915Sopenharmony_ci for (i = 0, total = 0; rl[i].length; i++) { 216987da915Sopenharmony_ci if (total + (rl[i].length << vol->cluster_size_bits) >= 217987da915Sopenharmony_ci sle64_to_cpu(a->data_size)) { 218987da915Sopenharmony_ci unsigned char *intbuf = NULL; 219987da915Sopenharmony_ci s64 intlth; 220987da915Sopenharmony_ci /* 221987da915Sopenharmony_ci * We have reached the last run so we were going to 222987da915Sopenharmony_ci * overflow when executing the ntfs_pread() which is 223987da915Sopenharmony_ci * BAAAAAAAD! 224987da915Sopenharmony_ci * Temporary fix: 225987da915Sopenharmony_ci * Allocate a new buffer with size: 226987da915Sopenharmony_ci * rl[i].length << vol->cluster_size_bits, do the 227987da915Sopenharmony_ci * read into our buffer, then memcpy the correct 228987da915Sopenharmony_ci * amount of data into the caller supplied buffer, 229987da915Sopenharmony_ci * free our buffer, and continue. 230987da915Sopenharmony_ci * We have reached the end of data size so we were 231987da915Sopenharmony_ci * going to overflow in the same fashion. 232987da915Sopenharmony_ci * Temporary fix: same as above. 233987da915Sopenharmony_ci * 234987da915Sopenharmony_ci * For safety, limit the amount to read to the 235987da915Sopenharmony_ci * needed size, knowing that the whole attribute 236987da915Sopenharmony_ci * size has been checked to be <= 0x40000. 237987da915Sopenharmony_ci */ 238987da915Sopenharmony_ci intlth = (sle64_to_cpu(a->data_size) - total 239987da915Sopenharmony_ci + vol->cluster_size - 1) 240987da915Sopenharmony_ci >> vol->cluster_size_bits; 241987da915Sopenharmony_ci if (rl[i].length < intlth) 242987da915Sopenharmony_ci intlth = rl[i].length; 243987da915Sopenharmony_ci intbuf = (u8*)ntfs_malloc(intlth 244987da915Sopenharmony_ci << vol->cluster_size_bits); 245987da915Sopenharmony_ci if (!intbuf) { 246987da915Sopenharmony_ci free(rl); 247987da915Sopenharmony_ci return 0; 248987da915Sopenharmony_ci } 249987da915Sopenharmony_ci /* 250987da915Sopenharmony_ci * FIXME: If compressed file: Only read if lcn != -1. 251987da915Sopenharmony_ci * Otherwise, we are dealing with a sparse run and we 252987da915Sopenharmony_ci * just memset the user buffer to 0 for the length of 253987da915Sopenharmony_ci * the run, which should be 16 (= compression unit 254987da915Sopenharmony_ci * size). 255987da915Sopenharmony_ci * FIXME: Really only when file is compressed, or can 256987da915Sopenharmony_ci * we have sparse runs in uncompressed files as well? 257987da915Sopenharmony_ci * - Yes we can, in sparse files! But not necessarily 258987da915Sopenharmony_ci * size of 16, just run length. 259987da915Sopenharmony_ci */ 260987da915Sopenharmony_ci r = ntfs_pread(vol->dev, 261987da915Sopenharmony_ci rl[i].lcn << vol->cluster_size_bits, 262987da915Sopenharmony_ci intlth << vol->cluster_size_bits, 263987da915Sopenharmony_ci intbuf); 264987da915Sopenharmony_ci if (r != intlth << vol->cluster_size_bits) { 265987da915Sopenharmony_ci#define ESTR "Error reading attribute value" 266987da915Sopenharmony_ci if (r == -1) 267987da915Sopenharmony_ci ntfs_log_perror(ESTR); 268987da915Sopenharmony_ci else if (r < intlth << 269987da915Sopenharmony_ci vol->cluster_size_bits) { 270987da915Sopenharmony_ci ntfs_log_debug(ESTR ": Ran out of input data.\n"); 271987da915Sopenharmony_ci errno = EIO; 272987da915Sopenharmony_ci } else { 273987da915Sopenharmony_ci ntfs_log_debug(ESTR ": unknown error\n"); 274987da915Sopenharmony_ci errno = EIO; 275987da915Sopenharmony_ci } 276987da915Sopenharmony_ci#undef ESTR 277987da915Sopenharmony_ci free(rl); 278987da915Sopenharmony_ci free(intbuf); 279987da915Sopenharmony_ci return 0; 280987da915Sopenharmony_ci } 281987da915Sopenharmony_ci memcpy(b + total, intbuf, sle64_to_cpu(a->data_size) - 282987da915Sopenharmony_ci total); 283987da915Sopenharmony_ci free(intbuf); 284987da915Sopenharmony_ci total = sle64_to_cpu(a->data_size); 285987da915Sopenharmony_ci break; 286987da915Sopenharmony_ci } 287987da915Sopenharmony_ci /* 288987da915Sopenharmony_ci * FIXME: If compressed file: Only read if lcn != -1. 289987da915Sopenharmony_ci * Otherwise, we are dealing with a sparse run and we just 290987da915Sopenharmony_ci * memset the user buffer to 0 for the length of the run, which 291987da915Sopenharmony_ci * should be 16 (= compression unit size). 292987da915Sopenharmony_ci * FIXME: Really only when file is compressed, or can 293987da915Sopenharmony_ci * we have sparse runs in uncompressed files as well? 294987da915Sopenharmony_ci * - Yes we can, in sparse files! But not necessarily size of 295987da915Sopenharmony_ci * 16, just run length. 296987da915Sopenharmony_ci */ 297987da915Sopenharmony_ci r = ntfs_pread(vol->dev, rl[i].lcn << vol->cluster_size_bits, 298987da915Sopenharmony_ci rl[i].length << vol->cluster_size_bits, 299987da915Sopenharmony_ci b + total); 300987da915Sopenharmony_ci if (r != rl[i].length << vol->cluster_size_bits) { 301987da915Sopenharmony_ci#define ESTR "Error reading attribute value" 302987da915Sopenharmony_ci if (r == -1) 303987da915Sopenharmony_ci ntfs_log_perror(ESTR); 304987da915Sopenharmony_ci else if (r < rl[i].length << vol->cluster_size_bits) { 305987da915Sopenharmony_ci ntfs_log_debug(ESTR ": Ran out of input data.\n"); 306987da915Sopenharmony_ci errno = EIO; 307987da915Sopenharmony_ci } else { 308987da915Sopenharmony_ci ntfs_log_debug(ESTR ": unknown error\n"); 309987da915Sopenharmony_ci errno = EIO; 310987da915Sopenharmony_ci } 311987da915Sopenharmony_ci#undef ESTR 312987da915Sopenharmony_ci free(rl); 313987da915Sopenharmony_ci return 0; 314987da915Sopenharmony_ci } 315987da915Sopenharmony_ci total += r; 316987da915Sopenharmony_ci } 317987da915Sopenharmony_ci free(rl); 318987da915Sopenharmony_ci return total; 319987da915Sopenharmony_ci} 320987da915Sopenharmony_ci 321987da915Sopenharmony_ci/* Already cleaned up code below, but still look for FIXME:... */ 322987da915Sopenharmony_ci 323987da915Sopenharmony_ci/** 324987da915Sopenharmony_ci * __ntfs_attr_init - primary initialization of an ntfs attribute structure 325987da915Sopenharmony_ci * @na: ntfs attribute to initialize 326987da915Sopenharmony_ci * @ni: ntfs inode with which to initialize the ntfs attribute 327987da915Sopenharmony_ci * @type: attribute type 328987da915Sopenharmony_ci * @name: attribute name in little endian Unicode or NULL 329987da915Sopenharmony_ci * @name_len: length of attribute @name in Unicode characters (if @name given) 330987da915Sopenharmony_ci * 331987da915Sopenharmony_ci * Initialize the ntfs attribute @na with @ni, @type, @name, and @name_len. 332987da915Sopenharmony_ci */ 333987da915Sopenharmony_cistatic void __ntfs_attr_init(ntfs_attr *na, ntfs_inode *ni, 334987da915Sopenharmony_ci const ATTR_TYPES type, ntfschar *name, const u32 name_len) 335987da915Sopenharmony_ci{ 336987da915Sopenharmony_ci na->rl = NULL; 337987da915Sopenharmony_ci na->ni = ni; 338987da915Sopenharmony_ci na->type = type; 339987da915Sopenharmony_ci na->name = name; 340987da915Sopenharmony_ci if (name) 341987da915Sopenharmony_ci na->name_len = name_len; 342987da915Sopenharmony_ci else 343987da915Sopenharmony_ci na->name_len = 0; 344987da915Sopenharmony_ci} 345987da915Sopenharmony_ci 346987da915Sopenharmony_ci/** 347987da915Sopenharmony_ci * ntfs_attr_init - initialize an ntfs_attr with data sizes and status 348987da915Sopenharmony_ci * @na: 349987da915Sopenharmony_ci * @non_resident: 350987da915Sopenharmony_ci * @compressed: 351987da915Sopenharmony_ci * @encrypted: 352987da915Sopenharmony_ci * @sparse: 353987da915Sopenharmony_ci * @allocated_size: 354987da915Sopenharmony_ci * @data_size: 355987da915Sopenharmony_ci * @initialized_size: 356987da915Sopenharmony_ci * @compressed_size: 357987da915Sopenharmony_ci * @compression_unit: 358987da915Sopenharmony_ci * 359987da915Sopenharmony_ci * Final initialization for an ntfs attribute. 360987da915Sopenharmony_ci */ 361987da915Sopenharmony_civoid ntfs_attr_init(ntfs_attr *na, const BOOL non_resident, 362987da915Sopenharmony_ci const ATTR_FLAGS data_flags, 363987da915Sopenharmony_ci const BOOL encrypted, const BOOL sparse, 364987da915Sopenharmony_ci const s64 allocated_size, const s64 data_size, 365987da915Sopenharmony_ci const s64 initialized_size, const s64 compressed_size, 366987da915Sopenharmony_ci const u8 compression_unit) 367987da915Sopenharmony_ci{ 368987da915Sopenharmony_ci if (!NAttrInitialized(na)) { 369987da915Sopenharmony_ci na->data_flags = data_flags; 370987da915Sopenharmony_ci if (non_resident) 371987da915Sopenharmony_ci NAttrSetNonResident(na); 372987da915Sopenharmony_ci if (data_flags & ATTR_COMPRESSION_MASK) 373987da915Sopenharmony_ci NAttrSetCompressed(na); 374987da915Sopenharmony_ci if (encrypted) 375987da915Sopenharmony_ci NAttrSetEncrypted(na); 376987da915Sopenharmony_ci if (sparse) 377987da915Sopenharmony_ci NAttrSetSparse(na); 378987da915Sopenharmony_ci na->allocated_size = allocated_size; 379987da915Sopenharmony_ci na->data_size = data_size; 380987da915Sopenharmony_ci na->initialized_size = initialized_size; 381987da915Sopenharmony_ci if ((data_flags & ATTR_COMPRESSION_MASK) || sparse) { 382987da915Sopenharmony_ci ntfs_volume *vol = na->ni->vol; 383987da915Sopenharmony_ci 384987da915Sopenharmony_ci na->compressed_size = compressed_size; 385987da915Sopenharmony_ci na->compression_block_clusters = 1 << compression_unit; 386987da915Sopenharmony_ci na->compression_block_size = 1 << (compression_unit + 387987da915Sopenharmony_ci vol->cluster_size_bits); 388987da915Sopenharmony_ci na->compression_block_size_bits = ffs( 389987da915Sopenharmony_ci na->compression_block_size) - 1; 390987da915Sopenharmony_ci } 391987da915Sopenharmony_ci NAttrSetInitialized(na); 392987da915Sopenharmony_ci } 393987da915Sopenharmony_ci} 394987da915Sopenharmony_ci 395987da915Sopenharmony_ci/** 396987da915Sopenharmony_ci * ntfs_attr_open - open an ntfs attribute for access 397987da915Sopenharmony_ci * @ni: open ntfs inode in which the ntfs attribute resides 398987da915Sopenharmony_ci * @type: attribute type 399987da915Sopenharmony_ci * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL 400987da915Sopenharmony_ci * @name_len: length of attribute @name in Unicode characters (if @name given) 401987da915Sopenharmony_ci * 402987da915Sopenharmony_ci * Allocate a new ntfs attribute structure, initialize it with @ni, @type, 403987da915Sopenharmony_ci * @name, and @name_len, then return it. Return NULL on error with 404987da915Sopenharmony_ci * errno set to the error code. 405987da915Sopenharmony_ci * 406987da915Sopenharmony_ci * If @name is AT_UNNAMED look specifically for an unnamed attribute. If you 407987da915Sopenharmony_ci * do not care whether the attribute is named or not set @name to NULL. In 408987da915Sopenharmony_ci * both those cases @name_len is not used at all. 409987da915Sopenharmony_ci */ 410987da915Sopenharmony_cintfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, 411987da915Sopenharmony_ci ntfschar *name, u32 name_len) 412987da915Sopenharmony_ci{ 413987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 414987da915Sopenharmony_ci ntfs_attr *na = NULL; 415987da915Sopenharmony_ci ntfschar *newname = NULL; 416987da915Sopenharmony_ci ATTR_RECORD *a; 417987da915Sopenharmony_ci le16 cs; 418987da915Sopenharmony_ci 419987da915Sopenharmony_ci ntfs_log_enter("Entering for inode %lld, attr 0x%x.\n", 420987da915Sopenharmony_ci (unsigned long long)ni->mft_no, le32_to_cpu(type)); 421987da915Sopenharmony_ci 422987da915Sopenharmony_ci if (!ni || !ni->vol || !ni->mrec) { 423987da915Sopenharmony_ci errno = EINVAL; 424987da915Sopenharmony_ci goto out; 425987da915Sopenharmony_ci } 426987da915Sopenharmony_ci na = ntfs_calloc(sizeof(ntfs_attr)); 427987da915Sopenharmony_ci if (!na) 428987da915Sopenharmony_ci goto out; 429987da915Sopenharmony_ci if (!name_len) 430987da915Sopenharmony_ci name = (ntfschar*)NULL; 431987da915Sopenharmony_ci if (name && name != AT_UNNAMED && name != NTFS_INDEX_I30) { 432987da915Sopenharmony_ci /* A null char leads to a short name and unallocated bytes */ 433987da915Sopenharmony_ci if (ntfs_ucsnlen(name, name_len) != name_len) { 434987da915Sopenharmony_ci ntfs_log_error("Null character in attribute name" 435987da915Sopenharmony_ci " of inode %lld\n",(long long)ni->mft_no); 436987da915Sopenharmony_ci goto err_out; 437987da915Sopenharmony_ci } 438987da915Sopenharmony_ci name = ntfs_ucsndup(name, name_len); 439987da915Sopenharmony_ci if (!name) 440987da915Sopenharmony_ci goto err_out; 441987da915Sopenharmony_ci newname = name; 442987da915Sopenharmony_ci } 443987da915Sopenharmony_ci 444987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(ni, NULL); 445987da915Sopenharmony_ci if (!ctx) 446987da915Sopenharmony_ci goto err_out; 447987da915Sopenharmony_ci 448987da915Sopenharmony_ci if (ntfs_attr_lookup(type, name, name_len, 0, 0, NULL, 0, ctx)) 449987da915Sopenharmony_ci goto put_err_out; 450987da915Sopenharmony_ci 451987da915Sopenharmony_ci a = ctx->attr; 452987da915Sopenharmony_ci 453987da915Sopenharmony_ci if (!name) { 454987da915Sopenharmony_ci if (a->name_length) { 455987da915Sopenharmony_ci ntfschar *attr_name; 456987da915Sopenharmony_ci 457987da915Sopenharmony_ci attr_name = (ntfschar*)((u8*)a 458987da915Sopenharmony_ci + le16_to_cpu(a->name_offset)); 459987da915Sopenharmony_ci /* A null character leads to illegal memory access */ 460987da915Sopenharmony_ci if (ntfs_ucsnlen(attr_name, a->name_length) 461987da915Sopenharmony_ci != a->name_length) { 462987da915Sopenharmony_ci ntfs_log_error("Null character in attribute" 463987da915Sopenharmony_ci " name in inode %lld\n", 464987da915Sopenharmony_ci (long long)ni->mft_no); 465987da915Sopenharmony_ci goto put_err_out; 466987da915Sopenharmony_ci } 467987da915Sopenharmony_ci name = ntfs_ucsndup(attr_name, a->name_length); 468987da915Sopenharmony_ci if (!name) 469987da915Sopenharmony_ci goto put_err_out; 470987da915Sopenharmony_ci newname = name; 471987da915Sopenharmony_ci name_len = a->name_length; 472987da915Sopenharmony_ci } else { 473987da915Sopenharmony_ci name = AT_UNNAMED; 474987da915Sopenharmony_ci name_len = 0; 475987da915Sopenharmony_ci } 476987da915Sopenharmony_ci } 477987da915Sopenharmony_ci 478987da915Sopenharmony_ci __ntfs_attr_init(na, ni, type, name, name_len); 479987da915Sopenharmony_ci 480987da915Sopenharmony_ci /* 481987da915Sopenharmony_ci * Wipe the flags in case they are not zero for an attribute list 482987da915Sopenharmony_ci * attribute. Windows does not complain about invalid flags and chkdsk 483987da915Sopenharmony_ci * does not detect or fix them so we need to cope with it, too. 484987da915Sopenharmony_ci */ 485987da915Sopenharmony_ci if (type == AT_ATTRIBUTE_LIST) 486987da915Sopenharmony_ci a->flags = const_cpu_to_le16(0); 487987da915Sopenharmony_ci 488987da915Sopenharmony_ci if ((type == AT_DATA) 489987da915Sopenharmony_ci && (a->non_resident ? !a->initialized_size : !a->value_length)) { 490987da915Sopenharmony_ci /* 491987da915Sopenharmony_ci * Define/redefine the compression state if stream is 492987da915Sopenharmony_ci * empty, based on the compression mark on parent 493987da915Sopenharmony_ci * directory (for unnamed data streams) or on current 494987da915Sopenharmony_ci * inode (for named data streams). The compression mark 495987da915Sopenharmony_ci * may change any time, the compression state can only 496987da915Sopenharmony_ci * change when stream is wiped out. 497987da915Sopenharmony_ci * 498987da915Sopenharmony_ci * Also prevent compression on NTFS version < 3.0 499987da915Sopenharmony_ci * or cluster size > 4K or compression is disabled 500987da915Sopenharmony_ci */ 501987da915Sopenharmony_ci a->flags &= ~ATTR_COMPRESSION_MASK; 502987da915Sopenharmony_ci if ((ni->flags & FILE_ATTR_COMPRESSED) 503987da915Sopenharmony_ci && (ni->vol->major_ver >= 3) 504987da915Sopenharmony_ci && NVolCompression(ni->vol) 505987da915Sopenharmony_ci && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE)) 506987da915Sopenharmony_ci a->flags |= ATTR_IS_COMPRESSED; 507987da915Sopenharmony_ci } 508987da915Sopenharmony_ci 509987da915Sopenharmony_ci cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); 510987da915Sopenharmony_ci 511987da915Sopenharmony_ci /* a file may be sparse though its unnamed data is not (cf $UsnJrnl) */ 512987da915Sopenharmony_ci if (na->type == AT_DATA && na->name == AT_UNNAMED && 513987da915Sopenharmony_ci (((a->flags & ATTR_IS_SPARSE) && !NAttrSparse(na)) || 514987da915Sopenharmony_ci (!(a->flags & ATTR_IS_ENCRYPTED) != !NAttrEncrypted(na)))) { 515987da915Sopenharmony_ci errno = EIO; 516987da915Sopenharmony_ci ntfs_log_perror("Inode %lld has corrupt attribute flags " 517987da915Sopenharmony_ci "(0x%x <> 0x%x)",(unsigned long long)ni->mft_no, 518987da915Sopenharmony_ci le16_to_cpu(a->flags), le32_to_cpu(na->ni->flags)); 519987da915Sopenharmony_ci goto put_err_out; 520987da915Sopenharmony_ci } 521987da915Sopenharmony_ci 522987da915Sopenharmony_ci if (a->non_resident) { 523987da915Sopenharmony_ci if (((a->flags & ATTR_COMPRESSION_MASK) 524987da915Sopenharmony_ci || a->compression_unit) 525987da915Sopenharmony_ci && (ni->vol->major_ver < 3)) { 526987da915Sopenharmony_ci errno = EIO; 527987da915Sopenharmony_ci ntfs_log_perror("Compressed inode %lld not allowed" 528987da915Sopenharmony_ci " on NTFS %d.%d", 529987da915Sopenharmony_ci (unsigned long long)ni->mft_no, 530987da915Sopenharmony_ci ni->vol->major_ver, 531987da915Sopenharmony_ci ni->vol->major_ver); 532987da915Sopenharmony_ci goto put_err_out; 533987da915Sopenharmony_ci } 534987da915Sopenharmony_ci if ((a->flags & ATTR_COMPRESSION_MASK) 535987da915Sopenharmony_ci && !a->compression_unit) { 536987da915Sopenharmony_ci errno = EIO; 537987da915Sopenharmony_ci ntfs_log_perror("Compressed inode %lld attr 0x%x has " 538987da915Sopenharmony_ci "no compression unit", 539987da915Sopenharmony_ci (unsigned long long)ni->mft_no, le32_to_cpu(type)); 540987da915Sopenharmony_ci goto put_err_out; 541987da915Sopenharmony_ci } 542987da915Sopenharmony_ci if ((a->flags & ATTR_COMPRESSION_MASK) 543987da915Sopenharmony_ci && (a->compression_unit 544987da915Sopenharmony_ci != STANDARD_COMPRESSION_UNIT)) { 545987da915Sopenharmony_ci errno = EIO; 546987da915Sopenharmony_ci ntfs_log_perror("Compressed inode %lld attr 0x%lx has " 547987da915Sopenharmony_ci "an unsupported compression unit %d", 548987da915Sopenharmony_ci (unsigned long long)ni->mft_no, 549987da915Sopenharmony_ci (long)le32_to_cpu(type), 550987da915Sopenharmony_ci (int)a->compression_unit); 551987da915Sopenharmony_ci goto put_err_out; 552987da915Sopenharmony_ci } 553987da915Sopenharmony_ci ntfs_attr_init(na, TRUE, a->flags, 554987da915Sopenharmony_ci a->flags & ATTR_IS_ENCRYPTED, 555987da915Sopenharmony_ci a->flags & ATTR_IS_SPARSE, 556987da915Sopenharmony_ci sle64_to_cpu(a->allocated_size), 557987da915Sopenharmony_ci sle64_to_cpu(a->data_size), 558987da915Sopenharmony_ci sle64_to_cpu(a->initialized_size), 559987da915Sopenharmony_ci cs ? sle64_to_cpu(a->compressed_size) : 0, 560987da915Sopenharmony_ci cs ? a->compression_unit : 0); 561987da915Sopenharmony_ci } else { 562987da915Sopenharmony_ci s64 l = le32_to_cpu(a->value_length); 563987da915Sopenharmony_ci ntfs_attr_init(na, FALSE, a->flags, 564987da915Sopenharmony_ci a->flags & ATTR_IS_ENCRYPTED, 565987da915Sopenharmony_ci a->flags & ATTR_IS_SPARSE, (l + 7) & ~7, l, l, 566987da915Sopenharmony_ci cs ? (l + 7) & ~7 : 0, 0); 567987da915Sopenharmony_ci } 568987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 569987da915Sopenharmony_ciout: 570987da915Sopenharmony_ci ntfs_log_leave("\n"); 571987da915Sopenharmony_ci return na; 572987da915Sopenharmony_ci 573987da915Sopenharmony_ciput_err_out: 574987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 575987da915Sopenharmony_cierr_out: 576987da915Sopenharmony_ci free(newname); 577987da915Sopenharmony_ci free(na); 578987da915Sopenharmony_ci na = NULL; 579987da915Sopenharmony_ci goto out; 580987da915Sopenharmony_ci} 581987da915Sopenharmony_ci 582987da915Sopenharmony_ci/** 583987da915Sopenharmony_ci * ntfs_attr_close - free an ntfs attribute structure 584987da915Sopenharmony_ci * @na: ntfs attribute structure to free 585987da915Sopenharmony_ci * 586987da915Sopenharmony_ci * Release all memory associated with the ntfs attribute @na and then release 587987da915Sopenharmony_ci * @na itself. 588987da915Sopenharmony_ci */ 589987da915Sopenharmony_civoid ntfs_attr_close(ntfs_attr *na) 590987da915Sopenharmony_ci{ 591987da915Sopenharmony_ci if (!na) 592987da915Sopenharmony_ci return; 593987da915Sopenharmony_ci if (NAttrNonResident(na) && na->rl) 594987da915Sopenharmony_ci free(na->rl); 595987da915Sopenharmony_ci /* Don't release if using an internal constant. */ 596987da915Sopenharmony_ci if (na->name != AT_UNNAMED && na->name != NTFS_INDEX_I30 597987da915Sopenharmony_ci && na->name != STREAM_SDS) 598987da915Sopenharmony_ci free(na->name); 599987da915Sopenharmony_ci free(na); 600987da915Sopenharmony_ci} 601987da915Sopenharmony_ci 602987da915Sopenharmony_ci/** 603987da915Sopenharmony_ci * ntfs_attr_map_runlist - map (a part of) a runlist of an ntfs attribute 604987da915Sopenharmony_ci * @na: ntfs attribute for which to map (part of) a runlist 605987da915Sopenharmony_ci * @vcn: map runlist part containing this vcn 606987da915Sopenharmony_ci * 607987da915Sopenharmony_ci * Map the part of a runlist containing the @vcn of the ntfs attribute @na. 608987da915Sopenharmony_ci * 609987da915Sopenharmony_ci * Return 0 on success and -1 on error with errno set to the error code. 610987da915Sopenharmony_ci */ 611987da915Sopenharmony_ciint ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn) 612987da915Sopenharmony_ci{ 613987da915Sopenharmony_ci LCN lcn; 614987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 615987da915Sopenharmony_ci 616987da915Sopenharmony_ci ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn 0x%llx.\n", 617987da915Sopenharmony_ci (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type), (long long)vcn); 618987da915Sopenharmony_ci 619987da915Sopenharmony_ci lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); 620987da915Sopenharmony_ci if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT) 621987da915Sopenharmony_ci return 0; 622987da915Sopenharmony_ci 623987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(na->ni, NULL); 624987da915Sopenharmony_ci if (!ctx) 625987da915Sopenharmony_ci return -1; 626987da915Sopenharmony_ci 627987da915Sopenharmony_ci /* Find the attribute in the mft record. */ 628987da915Sopenharmony_ci if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, 629987da915Sopenharmony_ci vcn, NULL, 0, ctx)) { 630987da915Sopenharmony_ci runlist_element *rl; 631987da915Sopenharmony_ci 632987da915Sopenharmony_ci /* Decode the runlist. */ 633987da915Sopenharmony_ci rl = ntfs_mapping_pairs_decompress(na->ni->vol, ctx->attr, 634987da915Sopenharmony_ci na->rl); 635987da915Sopenharmony_ci if (rl) { 636987da915Sopenharmony_ci na->rl = rl; 637987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 638987da915Sopenharmony_ci return 0; 639987da915Sopenharmony_ci } 640987da915Sopenharmony_ci } 641987da915Sopenharmony_ci 642987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 643987da915Sopenharmony_ci return -1; 644987da915Sopenharmony_ci} 645987da915Sopenharmony_ci 646987da915Sopenharmony_ci#if PARTIAL_RUNLIST_UPDATING 647987da915Sopenharmony_ci 648987da915Sopenharmony_ci/* 649987da915Sopenharmony_ci * Map the runlist of an attribute from some point to the end 650987da915Sopenharmony_ci * 651987da915Sopenharmony_ci * Returns 0 if success, 652987da915Sopenharmony_ci * -1 if it failed (errno telling why) 653987da915Sopenharmony_ci */ 654987da915Sopenharmony_ci 655987da915Sopenharmony_cistatic int ntfs_attr_map_partial_runlist(ntfs_attr *na, VCN vcn) 656987da915Sopenharmony_ci{ 657987da915Sopenharmony_ci VCN last_vcn; 658987da915Sopenharmony_ci VCN highest_vcn; 659987da915Sopenharmony_ci VCN needed; 660987da915Sopenharmony_ci runlist_element *rl; 661987da915Sopenharmony_ci ATTR_RECORD *a; 662987da915Sopenharmony_ci BOOL startseen; 663987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 664987da915Sopenharmony_ci BOOL done; 665987da915Sopenharmony_ci BOOL newrunlist; 666987da915Sopenharmony_ci 667987da915Sopenharmony_ci if (NAttrFullyMapped(na)) 668987da915Sopenharmony_ci return 0; 669987da915Sopenharmony_ci 670987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(na->ni, NULL); 671987da915Sopenharmony_ci if (!ctx) 672987da915Sopenharmony_ci return -1; 673987da915Sopenharmony_ci 674987da915Sopenharmony_ci /* Get the last vcn in the attribute. */ 675987da915Sopenharmony_ci last_vcn = na->allocated_size >> na->ni->vol->cluster_size_bits; 676987da915Sopenharmony_ci 677987da915Sopenharmony_ci needed = vcn; 678987da915Sopenharmony_ci highest_vcn = 0; 679987da915Sopenharmony_ci startseen = FALSE; 680987da915Sopenharmony_ci done = FALSE; 681987da915Sopenharmony_ci rl = (runlist_element*)NULL; 682987da915Sopenharmony_ci do { 683987da915Sopenharmony_ci newrunlist = FALSE; 684987da915Sopenharmony_ci /* Find the attribute in the mft record. */ 685987da915Sopenharmony_ci if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, 686987da915Sopenharmony_ci needed, NULL, 0, ctx)) { 687987da915Sopenharmony_ci 688987da915Sopenharmony_ci a = ctx->attr; 689987da915Sopenharmony_ci /* Decode and merge the runlist. */ 690987da915Sopenharmony_ci if (ntfs_rl_vcn_to_lcn(na->rl, needed) 691987da915Sopenharmony_ci == LCN_RL_NOT_MAPPED) { 692987da915Sopenharmony_ci rl = ntfs_mapping_pairs_decompress(na->ni->vol, 693987da915Sopenharmony_ci a, na->rl); 694987da915Sopenharmony_ci newrunlist = TRUE; 695987da915Sopenharmony_ci } else 696987da915Sopenharmony_ci rl = na->rl; 697987da915Sopenharmony_ci if (rl) { 698987da915Sopenharmony_ci na->rl = rl; 699987da915Sopenharmony_ci highest_vcn = sle64_to_cpu(a->highest_vcn); 700987da915Sopenharmony_ci if (highest_vcn < needed) { 701987da915Sopenharmony_ci /* corruption detection on unchanged runlists */ 702987da915Sopenharmony_ci if (newrunlist 703987da915Sopenharmony_ci && ((highest_vcn + 1) < last_vcn)) { 704987da915Sopenharmony_ci ntfs_log_error("Corrupt attribute list\n"); 705987da915Sopenharmony_ci rl = (runlist_element*)NULL; 706987da915Sopenharmony_ci errno = EIO; 707987da915Sopenharmony_ci } 708987da915Sopenharmony_ci done = TRUE; 709987da915Sopenharmony_ci } 710987da915Sopenharmony_ci needed = highest_vcn + 1; 711987da915Sopenharmony_ci if (!a->lowest_vcn) 712987da915Sopenharmony_ci startseen = TRUE; 713987da915Sopenharmony_ci } 714987da915Sopenharmony_ci } else { 715987da915Sopenharmony_ci done = TRUE; 716987da915Sopenharmony_ci } 717987da915Sopenharmony_ci } while (rl && !done && (needed < last_vcn)); 718987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 719987da915Sopenharmony_ci /* 720987da915Sopenharmony_ci * Make sure we reached the end, unless the last 721987da915Sopenharmony_ci * runlist was modified earlier (using HOLES_DELAY 722987da915Sopenharmony_ci * leads to have a visibility over attributes which 723987da915Sopenharmony_ci * have not yet been fully updated) 724987da915Sopenharmony_ci */ 725987da915Sopenharmony_ci if (done && newrunlist && (needed < last_vcn)) { 726987da915Sopenharmony_ci ntfs_log_error("End of runlist not reached\n"); 727987da915Sopenharmony_ci rl = (runlist_element*)NULL; 728987da915Sopenharmony_ci errno = EIO; 729987da915Sopenharmony_ci } 730987da915Sopenharmony_ci /* mark fully mapped if we did so */ 731987da915Sopenharmony_ci if (rl && startseen) 732987da915Sopenharmony_ci NAttrSetFullyMapped(na); 733987da915Sopenharmony_ci return (rl ? 0 : -1); 734987da915Sopenharmony_ci} 735987da915Sopenharmony_ci 736987da915Sopenharmony_ci#endif 737987da915Sopenharmony_ci 738987da915Sopenharmony_ci/** 739987da915Sopenharmony_ci * ntfs_attr_map_whole_runlist - map the whole runlist of an ntfs attribute 740987da915Sopenharmony_ci * @na: ntfs attribute for which to map the runlist 741987da915Sopenharmony_ci * 742987da915Sopenharmony_ci * Map the whole runlist of the ntfs attribute @na. For an attribute made up 743987da915Sopenharmony_ci * of only one attribute extent this is the same as calling 744987da915Sopenharmony_ci * ntfs_attr_map_runlist(na, 0) but for an attribute with multiple extents this 745987da915Sopenharmony_ci * will map the runlist fragments from each of the extents thus giving access 746987da915Sopenharmony_ci * to the entirety of the disk allocation of an attribute. 747987da915Sopenharmony_ci * 748987da915Sopenharmony_ci * Return 0 on success and -1 on error with errno set to the error code. 749987da915Sopenharmony_ci */ 750987da915Sopenharmony_ciint ntfs_attr_map_whole_runlist(ntfs_attr *na) 751987da915Sopenharmony_ci{ 752987da915Sopenharmony_ci VCN next_vcn, last_vcn, highest_vcn; 753987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 754987da915Sopenharmony_ci ntfs_volume *vol = na->ni->vol; 755987da915Sopenharmony_ci ATTR_RECORD *a; 756987da915Sopenharmony_ci int ret = -1; 757987da915Sopenharmony_ci int not_mapped; 758987da915Sopenharmony_ci 759987da915Sopenharmony_ci ntfs_log_enter("Entering for inode %llu, attr 0x%x.\n", 760987da915Sopenharmony_ci (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type)); 761987da915Sopenharmony_ci 762987da915Sopenharmony_ci /* avoid multiple full runlist mappings */ 763987da915Sopenharmony_ci if (NAttrFullyMapped(na)) { 764987da915Sopenharmony_ci ret = 0; 765987da915Sopenharmony_ci goto out; 766987da915Sopenharmony_ci } 767987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(na->ni, NULL); 768987da915Sopenharmony_ci if (!ctx) 769987da915Sopenharmony_ci goto out; 770987da915Sopenharmony_ci 771987da915Sopenharmony_ci /* Map all attribute extents one by one. */ 772987da915Sopenharmony_ci next_vcn = last_vcn = highest_vcn = 0; 773987da915Sopenharmony_ci a = NULL; 774987da915Sopenharmony_ci while (1) { 775987da915Sopenharmony_ci runlist_element *rl; 776987da915Sopenharmony_ci 777987da915Sopenharmony_ci not_mapped = 0; 778987da915Sopenharmony_ci if (ntfs_rl_vcn_to_lcn(na->rl, next_vcn) == LCN_RL_NOT_MAPPED) 779987da915Sopenharmony_ci not_mapped = 1; 780987da915Sopenharmony_ci 781987da915Sopenharmony_ci if (ntfs_attr_lookup(na->type, na->name, na->name_len, 782987da915Sopenharmony_ci CASE_SENSITIVE, next_vcn, NULL, 0, ctx)) 783987da915Sopenharmony_ci break; 784987da915Sopenharmony_ci 785987da915Sopenharmony_ci a = ctx->attr; 786987da915Sopenharmony_ci 787987da915Sopenharmony_ci if (not_mapped) { 788987da915Sopenharmony_ci /* Decode the runlist. */ 789987da915Sopenharmony_ci rl = ntfs_mapping_pairs_decompress(na->ni->vol, 790987da915Sopenharmony_ci a, na->rl); 791987da915Sopenharmony_ci if (!rl) 792987da915Sopenharmony_ci goto err_out; 793987da915Sopenharmony_ci na->rl = rl; 794987da915Sopenharmony_ci } 795987da915Sopenharmony_ci 796987da915Sopenharmony_ci /* Are we in the first extent? */ 797987da915Sopenharmony_ci if (!next_vcn) { 798987da915Sopenharmony_ci if (a->lowest_vcn) { 799987da915Sopenharmony_ci errno = EIO; 800987da915Sopenharmony_ci ntfs_log_perror("First extent of inode %llu " 801987da915Sopenharmony_ci "attribute has non-zero lowest_vcn", 802987da915Sopenharmony_ci (unsigned long long)na->ni->mft_no); 803987da915Sopenharmony_ci goto err_out; 804987da915Sopenharmony_ci } 805987da915Sopenharmony_ci /* Get the last vcn in the attribute. */ 806987da915Sopenharmony_ci last_vcn = sle64_to_cpu(a->allocated_size) >> 807987da915Sopenharmony_ci vol->cluster_size_bits; 808987da915Sopenharmony_ci } 809987da915Sopenharmony_ci 810987da915Sopenharmony_ci /* Get the lowest vcn for the next extent. */ 811987da915Sopenharmony_ci highest_vcn = sle64_to_cpu(a->highest_vcn); 812987da915Sopenharmony_ci next_vcn = highest_vcn + 1; 813987da915Sopenharmony_ci 814987da915Sopenharmony_ci /* Only one extent or error, which we catch below. */ 815987da915Sopenharmony_ci if (next_vcn <= 0) { 816987da915Sopenharmony_ci errno = ENOENT; 817987da915Sopenharmony_ci break; 818987da915Sopenharmony_ci } 819987da915Sopenharmony_ci 820987da915Sopenharmony_ci /* Avoid endless loops due to corruption. */ 821987da915Sopenharmony_ci if (next_vcn < sle64_to_cpu(a->lowest_vcn)) { 822987da915Sopenharmony_ci errno = EIO; 823987da915Sopenharmony_ci ntfs_log_perror("Inode %llu has corrupt attribute list", 824987da915Sopenharmony_ci (unsigned long long)na->ni->mft_no); 825987da915Sopenharmony_ci goto err_out; 826987da915Sopenharmony_ci } 827987da915Sopenharmony_ci } 828987da915Sopenharmony_ci if (!a) { 829987da915Sopenharmony_ci ntfs_log_perror("Couldn't find attribute for runlist mapping"); 830987da915Sopenharmony_ci goto err_out; 831987da915Sopenharmony_ci } 832987da915Sopenharmony_ci /* 833987da915Sopenharmony_ci * Cannot check highest_vcn when the last runlist has 834987da915Sopenharmony_ci * been modified earlier, as runlists and sizes may be 835987da915Sopenharmony_ci * updated without highest_vcn being in sync, when 836987da915Sopenharmony_ci * HOLES_DELAY is used 837987da915Sopenharmony_ci */ 838987da915Sopenharmony_ci if (not_mapped && highest_vcn && highest_vcn != last_vcn - 1) { 839987da915Sopenharmony_ci errno = EIO; 840987da915Sopenharmony_ci ntfs_log_perror("Failed to load full runlist: inode: %llu " 841987da915Sopenharmony_ci "highest_vcn: 0x%llx last_vcn: 0x%llx", 842987da915Sopenharmony_ci (unsigned long long)na->ni->mft_no, 843987da915Sopenharmony_ci (long long)highest_vcn, (long long)last_vcn); 844987da915Sopenharmony_ci goto err_out; 845987da915Sopenharmony_ci } 846987da915Sopenharmony_ci if (errno == ENOENT) { 847987da915Sopenharmony_ci NAttrSetFullyMapped(na); 848987da915Sopenharmony_ci ret = 0; 849987da915Sopenharmony_ci } 850987da915Sopenharmony_cierr_out: 851987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 852987da915Sopenharmony_ciout: 853987da915Sopenharmony_ci ntfs_log_leave("\n"); 854987da915Sopenharmony_ci return ret; 855987da915Sopenharmony_ci} 856987da915Sopenharmony_ci 857987da915Sopenharmony_ci/** 858987da915Sopenharmony_ci * ntfs_attr_vcn_to_lcn - convert a vcn into a lcn given an ntfs attribute 859987da915Sopenharmony_ci * @na: ntfs attribute whose runlist to use for conversion 860987da915Sopenharmony_ci * @vcn: vcn to convert 861987da915Sopenharmony_ci * 862987da915Sopenharmony_ci * Convert the virtual cluster number @vcn of an attribute into a logical 863987da915Sopenharmony_ci * cluster number (lcn) of a device using the runlist @na->rl to map vcns to 864987da915Sopenharmony_ci * their corresponding lcns. 865987da915Sopenharmony_ci * 866987da915Sopenharmony_ci * If the @vcn is not mapped yet, attempt to map the attribute extent 867987da915Sopenharmony_ci * containing the @vcn and retry the vcn to lcn conversion. 868987da915Sopenharmony_ci * 869987da915Sopenharmony_ci * Since lcns must be >= 0, we use negative return values with special meaning: 870987da915Sopenharmony_ci * 871987da915Sopenharmony_ci * Return value Meaning / Description 872987da915Sopenharmony_ci * ========================================== 873987da915Sopenharmony_ci * -1 = LCN_HOLE Hole / not allocated on disk. 874987da915Sopenharmony_ci * -3 = LCN_ENOENT There is no such vcn in the attribute. 875987da915Sopenharmony_ci * -4 = LCN_EINVAL Input parameter error. 876987da915Sopenharmony_ci * -5 = LCN_EIO Corrupt fs, disk i/o error, or not enough memory. 877987da915Sopenharmony_ci */ 878987da915Sopenharmony_ciLCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn) 879987da915Sopenharmony_ci{ 880987da915Sopenharmony_ci LCN lcn; 881987da915Sopenharmony_ci BOOL is_retry = FALSE; 882987da915Sopenharmony_ci 883987da915Sopenharmony_ci if (!na || !NAttrNonResident(na) || vcn < 0) 884987da915Sopenharmony_ci return (LCN)LCN_EINVAL; 885987da915Sopenharmony_ci 886987da915Sopenharmony_ci ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long 887987da915Sopenharmony_ci long)na->ni->mft_no, le32_to_cpu(na->type)); 888987da915Sopenharmony_ciretry: 889987da915Sopenharmony_ci /* Convert vcn to lcn. If that fails map the runlist and retry once. */ 890987da915Sopenharmony_ci lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); 891987da915Sopenharmony_ci if (lcn >= 0) 892987da915Sopenharmony_ci return lcn; 893987da915Sopenharmony_ci if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) { 894987da915Sopenharmony_ci is_retry = TRUE; 895987da915Sopenharmony_ci goto retry; 896987da915Sopenharmony_ci } 897987da915Sopenharmony_ci /* 898987da915Sopenharmony_ci * If the attempt to map the runlist failed, or we are getting 899987da915Sopenharmony_ci * LCN_RL_NOT_MAPPED despite having mapped the attribute extent 900987da915Sopenharmony_ci * successfully, something is really badly wrong... 901987da915Sopenharmony_ci */ 902987da915Sopenharmony_ci if (!is_retry || lcn == (LCN)LCN_RL_NOT_MAPPED) 903987da915Sopenharmony_ci return (LCN)LCN_EIO; 904987da915Sopenharmony_ci /* lcn contains the appropriate error code. */ 905987da915Sopenharmony_ci return lcn; 906987da915Sopenharmony_ci} 907987da915Sopenharmony_ci 908987da915Sopenharmony_ci/** 909987da915Sopenharmony_ci * ntfs_attr_find_vcn - find a vcn in the runlist of an ntfs attribute 910987da915Sopenharmony_ci * @na: ntfs attribute whose runlist to search 911987da915Sopenharmony_ci * @vcn: vcn to find 912987da915Sopenharmony_ci * 913987da915Sopenharmony_ci * Find the virtual cluster number @vcn in the runlist of the ntfs attribute 914987da915Sopenharmony_ci * @na and return the the address of the runlist element containing the @vcn. 915987da915Sopenharmony_ci * 916987da915Sopenharmony_ci * Note you need to distinguish between the lcn of the returned runlist 917987da915Sopenharmony_ci * element being >= 0 and LCN_HOLE. In the later case you have to return zeroes 918987da915Sopenharmony_ci * on read and allocate clusters on write. You need to update the runlist, the 919987da915Sopenharmony_ci * attribute itself as well as write the modified mft record to disk. 920987da915Sopenharmony_ci * 921987da915Sopenharmony_ci * If there is an error return NULL with errno set to the error code. The 922987da915Sopenharmony_ci * following error codes are defined: 923987da915Sopenharmony_ci * EINVAL Input parameter error. 924987da915Sopenharmony_ci * ENOENT There is no such vcn in the runlist. 925987da915Sopenharmony_ci * ENOMEM Not enough memory. 926987da915Sopenharmony_ci * EIO I/O error or corrupt metadata. 927987da915Sopenharmony_ci */ 928987da915Sopenharmony_cirunlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn) 929987da915Sopenharmony_ci{ 930987da915Sopenharmony_ci runlist_element *rl; 931987da915Sopenharmony_ci BOOL is_retry = FALSE; 932987da915Sopenharmony_ci 933987da915Sopenharmony_ci if (!na || !NAttrNonResident(na) || vcn < 0) { 934987da915Sopenharmony_ci errno = EINVAL; 935987da915Sopenharmony_ci return NULL; 936987da915Sopenharmony_ci } 937987da915Sopenharmony_ci 938987da915Sopenharmony_ci ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn %llx\n", 939987da915Sopenharmony_ci (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type), 940987da915Sopenharmony_ci (long long)vcn); 941987da915Sopenharmony_ciretry: 942987da915Sopenharmony_ci rl = na->rl; 943987da915Sopenharmony_ci if (!rl) 944987da915Sopenharmony_ci goto map_rl; 945987da915Sopenharmony_ci if (vcn < rl[0].vcn) 946987da915Sopenharmony_ci goto map_rl; 947987da915Sopenharmony_ci while (rl->length) { 948987da915Sopenharmony_ci if (vcn < rl[1].vcn) { 949987da915Sopenharmony_ci if (rl->lcn >= (LCN)LCN_HOLE) 950987da915Sopenharmony_ci return rl; 951987da915Sopenharmony_ci break; 952987da915Sopenharmony_ci } 953987da915Sopenharmony_ci rl++; 954987da915Sopenharmony_ci } 955987da915Sopenharmony_ci switch (rl->lcn) { 956987da915Sopenharmony_ci case (LCN)LCN_RL_NOT_MAPPED: 957987da915Sopenharmony_ci goto map_rl; 958987da915Sopenharmony_ci case (LCN)LCN_ENOENT: 959987da915Sopenharmony_ci errno = ENOENT; 960987da915Sopenharmony_ci break; 961987da915Sopenharmony_ci case (LCN)LCN_EINVAL: 962987da915Sopenharmony_ci errno = EINVAL; 963987da915Sopenharmony_ci break; 964987da915Sopenharmony_ci default: 965987da915Sopenharmony_ci errno = EIO; 966987da915Sopenharmony_ci break; 967987da915Sopenharmony_ci } 968987da915Sopenharmony_ci return NULL; 969987da915Sopenharmony_cimap_rl: 970987da915Sopenharmony_ci /* The @vcn is in an unmapped region, map the runlist and retry. */ 971987da915Sopenharmony_ci if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) { 972987da915Sopenharmony_ci is_retry = TRUE; 973987da915Sopenharmony_ci goto retry; 974987da915Sopenharmony_ci } 975987da915Sopenharmony_ci /* 976987da915Sopenharmony_ci * If we already retried or the mapping attempt failed something has 977987da915Sopenharmony_ci * gone badly wrong. EINVAL and ENOENT coming from a failed mapping 978987da915Sopenharmony_ci * attempt are equivalent to errors for us as they should not happen 979987da915Sopenharmony_ci * in our code paths. 980987da915Sopenharmony_ci */ 981987da915Sopenharmony_ci if (is_retry || errno == EINVAL || errno == ENOENT) 982987da915Sopenharmony_ci errno = EIO; 983987da915Sopenharmony_ci return NULL; 984987da915Sopenharmony_ci} 985987da915Sopenharmony_ci 986987da915Sopenharmony_ci/** 987987da915Sopenharmony_ci * ntfs_attr_pread_i - see description at ntfs_attr_pread() 988987da915Sopenharmony_ci */ 989987da915Sopenharmony_cistatic s64 ntfs_attr_pread_i(ntfs_attr *na, const s64 pos, s64 count, void *b) 990987da915Sopenharmony_ci{ 991987da915Sopenharmony_ci s64 br, to_read, ofs, total, total2, max_read, max_init; 992987da915Sopenharmony_ci ntfs_volume *vol; 993987da915Sopenharmony_ci runlist_element *rl; 994987da915Sopenharmony_ci u16 efs_padding_length; 995987da915Sopenharmony_ci 996987da915Sopenharmony_ci /* Sanity checking arguments is done in ntfs_attr_pread(). */ 997987da915Sopenharmony_ci 998987da915Sopenharmony_ci if ((na->data_flags & ATTR_COMPRESSION_MASK) && NAttrNonResident(na)) { 999987da915Sopenharmony_ci if ((na->data_flags & ATTR_COMPRESSION_MASK) 1000987da915Sopenharmony_ci == ATTR_IS_COMPRESSED) 1001987da915Sopenharmony_ci return ntfs_compressed_attr_pread(na, pos, count, b); 1002987da915Sopenharmony_ci else { 1003987da915Sopenharmony_ci /* compression mode not supported */ 1004987da915Sopenharmony_ci errno = EOPNOTSUPP; 1005987da915Sopenharmony_ci return -1; 1006987da915Sopenharmony_ci } 1007987da915Sopenharmony_ci } 1008987da915Sopenharmony_ci /* 1009987da915Sopenharmony_ci * Encrypted non-resident attributes are not supported. We return 1010987da915Sopenharmony_ci * access denied, which is what Windows NT4 does, too. 1011987da915Sopenharmony_ci * However, allow if mounted with efs_raw option 1012987da915Sopenharmony_ci */ 1013987da915Sopenharmony_ci vol = na->ni->vol; 1014987da915Sopenharmony_ci if (!vol->efs_raw && NAttrEncrypted(na) && NAttrNonResident(na)) { 1015987da915Sopenharmony_ci errno = EACCES; 1016987da915Sopenharmony_ci return -1; 1017987da915Sopenharmony_ci } 1018987da915Sopenharmony_ci 1019987da915Sopenharmony_ci if (!count) 1020987da915Sopenharmony_ci return 0; 1021987da915Sopenharmony_ci /* 1022987da915Sopenharmony_ci * Truncate reads beyond end of attribute, 1023987da915Sopenharmony_ci * but round to next 512 byte boundary for encrypted 1024987da915Sopenharmony_ci * attributes with efs_raw mount option 1025987da915Sopenharmony_ci */ 1026987da915Sopenharmony_ci max_read = na->data_size; 1027987da915Sopenharmony_ci max_init = na->initialized_size; 1028987da915Sopenharmony_ci if (na->ni->vol->efs_raw 1029987da915Sopenharmony_ci && (na->data_flags & ATTR_IS_ENCRYPTED) 1030987da915Sopenharmony_ci && NAttrNonResident(na)) { 1031987da915Sopenharmony_ci if (na->data_size != na->initialized_size) { 1032987da915Sopenharmony_ci ntfs_log_error("uninitialized encrypted file not supported\n"); 1033987da915Sopenharmony_ci errno = EINVAL; 1034987da915Sopenharmony_ci return -1; 1035987da915Sopenharmony_ci } 1036987da915Sopenharmony_ci max_init = max_read = ((na->data_size + 511) & ~511) + 2; 1037987da915Sopenharmony_ci } 1038987da915Sopenharmony_ci if (pos + count > max_read) { 1039987da915Sopenharmony_ci if (pos >= max_read) 1040987da915Sopenharmony_ci return 0; 1041987da915Sopenharmony_ci count = max_read - pos; 1042987da915Sopenharmony_ci } 1043987da915Sopenharmony_ci /* If it is a resident attribute, get the value from the mft record. */ 1044987da915Sopenharmony_ci if (!NAttrNonResident(na)) { 1045987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 1046987da915Sopenharmony_ci char *val; 1047987da915Sopenharmony_ci 1048987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(na->ni, NULL); 1049987da915Sopenharmony_ci if (!ctx) 1050987da915Sopenharmony_ci return -1; 1051987da915Sopenharmony_ci if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 1052987da915Sopenharmony_ci 0, NULL, 0, ctx)) { 1053987da915Sopenharmony_cires_err_out: 1054987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 1055987da915Sopenharmony_ci return -1; 1056987da915Sopenharmony_ci } 1057987da915Sopenharmony_ci val = (char*)ctx->attr + le16_to_cpu(ctx->attr->value_offset); 1058987da915Sopenharmony_ci if (val < (char*)ctx->attr || val + 1059987da915Sopenharmony_ci le32_to_cpu(ctx->attr->value_length) > 1060987da915Sopenharmony_ci (char*)ctx->mrec + vol->mft_record_size) { 1061987da915Sopenharmony_ci errno = EIO; 1062987da915Sopenharmony_ci ntfs_log_perror("%s: Sanity check failed", __FUNCTION__); 1063987da915Sopenharmony_ci goto res_err_out; 1064987da915Sopenharmony_ci } 1065987da915Sopenharmony_ci memcpy(b, val + pos, count); 1066987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 1067987da915Sopenharmony_ci return count; 1068987da915Sopenharmony_ci } 1069987da915Sopenharmony_ci total = total2 = 0; 1070987da915Sopenharmony_ci /* Zero out reads beyond initialized size. */ 1071987da915Sopenharmony_ci if (pos + count > max_init) { 1072987da915Sopenharmony_ci if (pos >= max_init) { 1073987da915Sopenharmony_ci memset(b, 0, count); 1074987da915Sopenharmony_ci return count; 1075987da915Sopenharmony_ci } 1076987da915Sopenharmony_ci total2 = pos + count - max_init; 1077987da915Sopenharmony_ci count -= total2; 1078987da915Sopenharmony_ci memset((u8*)b + count, 0, total2); 1079987da915Sopenharmony_ci } 1080987da915Sopenharmony_ci /* 1081987da915Sopenharmony_ci * for encrypted non-resident attributes with efs_raw set 1082987da915Sopenharmony_ci * the last two bytes aren't read from disk but contain 1083987da915Sopenharmony_ci * the number of padding bytes so original size can be 1084987da915Sopenharmony_ci * restored 1085987da915Sopenharmony_ci */ 1086987da915Sopenharmony_ci if (na->ni->vol->efs_raw && 1087987da915Sopenharmony_ci (na->data_flags & ATTR_IS_ENCRYPTED) && 1088987da915Sopenharmony_ci ((pos + count) > max_init-2)) { 1089987da915Sopenharmony_ci efs_padding_length = 511 - ((na->data_size - 1) & 511); 1090987da915Sopenharmony_ci if (pos+count == max_init) { 1091987da915Sopenharmony_ci if (count == 1) { 1092987da915Sopenharmony_ci *((u8*)b+count-1) = (u8)(efs_padding_length >> 8); 1093987da915Sopenharmony_ci count--; 1094987da915Sopenharmony_ci total2++; 1095987da915Sopenharmony_ci } else { 1096987da915Sopenharmony_ci *(le16*)((u8*)b+count-2) = cpu_to_le16(efs_padding_length); 1097987da915Sopenharmony_ci count -= 2; 1098987da915Sopenharmony_ci total2 +=2; 1099987da915Sopenharmony_ci } 1100987da915Sopenharmony_ci } else { 1101987da915Sopenharmony_ci *((u8*)b+count-1) = (u8)(efs_padding_length & 0xff); 1102987da915Sopenharmony_ci count--; 1103987da915Sopenharmony_ci total2++; 1104987da915Sopenharmony_ci } 1105987da915Sopenharmony_ci } 1106987da915Sopenharmony_ci 1107987da915Sopenharmony_ci /* Find the runlist element containing the vcn. */ 1108987da915Sopenharmony_ci rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); 1109987da915Sopenharmony_ci if (!rl) { 1110987da915Sopenharmony_ci /* 1111987da915Sopenharmony_ci * If the vcn is not present it is an out of bounds read. 1112987da915Sopenharmony_ci * However, we already truncated the read to the data_size, 1113987da915Sopenharmony_ci * so getting this here is an error. 1114987da915Sopenharmony_ci */ 1115987da915Sopenharmony_ci if (errno == ENOENT) { 1116987da915Sopenharmony_ci errno = EIO; 1117987da915Sopenharmony_ci ntfs_log_perror("%s: Failed to find VCN #1", __FUNCTION__); 1118987da915Sopenharmony_ci } 1119987da915Sopenharmony_ci return -1; 1120987da915Sopenharmony_ci } 1121987da915Sopenharmony_ci /* 1122987da915Sopenharmony_ci * Gather the requested data into the linear destination buffer. Note, 1123987da915Sopenharmony_ci * a partial final vcn is taken care of by the @count capping of read 1124987da915Sopenharmony_ci * length. 1125987da915Sopenharmony_ci */ 1126987da915Sopenharmony_ci ofs = pos - (rl->vcn << vol->cluster_size_bits); 1127987da915Sopenharmony_ci for (; count; rl++, ofs = 0) { 1128987da915Sopenharmony_ci if (rl->lcn == LCN_RL_NOT_MAPPED) { 1129987da915Sopenharmony_ci rl = ntfs_attr_find_vcn(na, rl->vcn); 1130987da915Sopenharmony_ci if (!rl) { 1131987da915Sopenharmony_ci if (errno == ENOENT) { 1132987da915Sopenharmony_ci errno = EIO; 1133987da915Sopenharmony_ci ntfs_log_perror("%s: Failed to find VCN #2", 1134987da915Sopenharmony_ci __FUNCTION__); 1135987da915Sopenharmony_ci } 1136987da915Sopenharmony_ci goto rl_err_out; 1137987da915Sopenharmony_ci } 1138987da915Sopenharmony_ci /* Needed for case when runs merged. */ 1139987da915Sopenharmony_ci ofs = pos + total - (rl->vcn << vol->cluster_size_bits); 1140987da915Sopenharmony_ci } 1141987da915Sopenharmony_ci if (!rl->length) { 1142987da915Sopenharmony_ci errno = EIO; 1143987da915Sopenharmony_ci ntfs_log_perror("%s: Zero run length", __FUNCTION__); 1144987da915Sopenharmony_ci goto rl_err_out; 1145987da915Sopenharmony_ci } 1146987da915Sopenharmony_ci if (rl->lcn < (LCN)0) { 1147987da915Sopenharmony_ci if (rl->lcn != (LCN)LCN_HOLE) { 1148987da915Sopenharmony_ci ntfs_log_perror("%s: Bad run (%lld)", 1149987da915Sopenharmony_ci __FUNCTION__, 1150987da915Sopenharmony_ci (long long)rl->lcn); 1151987da915Sopenharmony_ci goto rl_err_out; 1152987da915Sopenharmony_ci } 1153987da915Sopenharmony_ci /* It is a hole, just zero the matching @b range. */ 1154987da915Sopenharmony_ci to_read = min(count, (rl->length << 1155987da915Sopenharmony_ci vol->cluster_size_bits) - ofs); 1156987da915Sopenharmony_ci memset(b, 0, to_read); 1157987da915Sopenharmony_ci /* Update progress counters. */ 1158987da915Sopenharmony_ci total += to_read; 1159987da915Sopenharmony_ci count -= to_read; 1160987da915Sopenharmony_ci b = (u8*)b + to_read; 1161987da915Sopenharmony_ci continue; 1162987da915Sopenharmony_ci } 1163987da915Sopenharmony_ci /* It is a real lcn, read it into @dst. */ 1164987da915Sopenharmony_ci to_read = min(count, (rl->length << vol->cluster_size_bits) - 1165987da915Sopenharmony_ci ofs); 1166987da915Sopenharmony_ciretry: 1167987da915Sopenharmony_ci ntfs_log_trace("Reading %lld bytes from vcn %lld, lcn %lld, ofs" 1168987da915Sopenharmony_ci " %lld.\n", (long long)to_read, (long long)rl->vcn, 1169987da915Sopenharmony_ci (long long )rl->lcn, (long long)ofs); 1170987da915Sopenharmony_ci br = ntfs_pread(vol->dev, (rl->lcn << vol->cluster_size_bits) + 1171987da915Sopenharmony_ci ofs, to_read, b); 1172987da915Sopenharmony_ci /* If everything ok, update progress counters and continue. */ 1173987da915Sopenharmony_ci if (br > 0) { 1174987da915Sopenharmony_ci total += br; 1175987da915Sopenharmony_ci count -= br; 1176987da915Sopenharmony_ci b = (u8*)b + br; 1177987da915Sopenharmony_ci } 1178987da915Sopenharmony_ci if (br == to_read) 1179987da915Sopenharmony_ci continue; 1180987da915Sopenharmony_ci /* If the syscall was interrupted, try again. */ 1181987da915Sopenharmony_ci if (br == (s64)-1 && errno == EINTR) 1182987da915Sopenharmony_ci goto retry; 1183987da915Sopenharmony_ci if (total) 1184987da915Sopenharmony_ci return total; 1185987da915Sopenharmony_ci if (!br) 1186987da915Sopenharmony_ci errno = EIO; 1187987da915Sopenharmony_ci ntfs_log_perror("%s: ntfs_pread failed", __FUNCTION__); 1188987da915Sopenharmony_ci return -1; 1189987da915Sopenharmony_ci } 1190987da915Sopenharmony_ci /* Finally, return the number of bytes read. */ 1191987da915Sopenharmony_ci return total + total2; 1192987da915Sopenharmony_cirl_err_out: 1193987da915Sopenharmony_ci if (total) 1194987da915Sopenharmony_ci return total; 1195987da915Sopenharmony_ci errno = EIO; 1196987da915Sopenharmony_ci return -1; 1197987da915Sopenharmony_ci} 1198987da915Sopenharmony_ci 1199987da915Sopenharmony_ci/** 1200987da915Sopenharmony_ci * ntfs_attr_pread - read from an attribute specified by an ntfs_attr structure 1201987da915Sopenharmony_ci * @na: ntfs attribute to read from 1202987da915Sopenharmony_ci * @pos: byte position in the attribute to begin reading from 1203987da915Sopenharmony_ci * @count: number of bytes to read 1204987da915Sopenharmony_ci * @b: output data buffer 1205987da915Sopenharmony_ci * 1206987da915Sopenharmony_ci * This function will read @count bytes starting at offset @pos from the ntfs 1207987da915Sopenharmony_ci * attribute @na into the data buffer @b. 1208987da915Sopenharmony_ci * 1209987da915Sopenharmony_ci * On success, return the number of successfully read bytes. If this number is 1210987da915Sopenharmony_ci * lower than @count this means that the read reached end of file or that an 1211987da915Sopenharmony_ci * error was encountered during the read so that the read is partial. 0 means 1212987da915Sopenharmony_ci * end of file or nothing was read (also return 0 when @count is 0). 1213987da915Sopenharmony_ci * 1214987da915Sopenharmony_ci * On error and nothing has been read, return -1 with errno set appropriately 1215987da915Sopenharmony_ci * to the return code of ntfs_pread(), or to EINVAL in case of invalid 1216987da915Sopenharmony_ci * arguments. 1217987da915Sopenharmony_ci */ 1218987da915Sopenharmony_cis64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) 1219987da915Sopenharmony_ci{ 1220987da915Sopenharmony_ci s64 ret; 1221987da915Sopenharmony_ci 1222987da915Sopenharmony_ci if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { 1223987da915Sopenharmony_ci errno = EINVAL; 1224987da915Sopenharmony_ci ntfs_log_perror("%s: na=%p b=%p pos=%lld count=%lld", 1225987da915Sopenharmony_ci __FUNCTION__, na, b, (long long)pos, 1226987da915Sopenharmony_ci (long long)count); 1227987da915Sopenharmony_ci return -1; 1228987da915Sopenharmony_ci } 1229987da915Sopenharmony_ci 1230987da915Sopenharmony_ci ntfs_log_enter("Entering for inode %lld attr 0x%x pos %lld count " 1231987da915Sopenharmony_ci "%lld\n", (unsigned long long)na->ni->mft_no, 1232987da915Sopenharmony_ci le32_to_cpu(na->type), (long long)pos, (long long)count); 1233987da915Sopenharmony_ci 1234987da915Sopenharmony_ci ret = ntfs_attr_pread_i(na, pos, count, b); 1235987da915Sopenharmony_ci 1236987da915Sopenharmony_ci ntfs_log_leave("\n"); 1237987da915Sopenharmony_ci return ret; 1238987da915Sopenharmony_ci} 1239987da915Sopenharmony_ci 1240987da915Sopenharmony_cistatic int ntfs_attr_fill_zero(ntfs_attr *na, s64 pos, s64 count) 1241987da915Sopenharmony_ci{ 1242987da915Sopenharmony_ci char *buf; 1243987da915Sopenharmony_ci s64 written, size, end = pos + count; 1244987da915Sopenharmony_ci s64 ofsi; 1245987da915Sopenharmony_ci const runlist_element *rli; 1246987da915Sopenharmony_ci ntfs_volume *vol; 1247987da915Sopenharmony_ci int ret = -1; 1248987da915Sopenharmony_ci 1249987da915Sopenharmony_ci ntfs_log_trace("pos %lld, count %lld\n", (long long)pos, 1250987da915Sopenharmony_ci (long long)count); 1251987da915Sopenharmony_ci 1252987da915Sopenharmony_ci if (!na || pos < 0 || count < 0) { 1253987da915Sopenharmony_ci errno = EINVAL; 1254987da915Sopenharmony_ci goto err_out; 1255987da915Sopenharmony_ci } 1256987da915Sopenharmony_ci 1257987da915Sopenharmony_ci buf = ntfs_calloc(NTFS_BUF_SIZE); 1258987da915Sopenharmony_ci if (!buf) 1259987da915Sopenharmony_ci goto err_out; 1260987da915Sopenharmony_ci 1261987da915Sopenharmony_ci rli = na->rl; 1262987da915Sopenharmony_ci ofsi = 0; 1263987da915Sopenharmony_ci vol = na->ni->vol; 1264987da915Sopenharmony_ci while (pos < end) { 1265987da915Sopenharmony_ci while (rli->length && (ofsi + (rli->length << 1266987da915Sopenharmony_ci vol->cluster_size_bits) <= pos)) { 1267987da915Sopenharmony_ci ofsi += (rli->length << vol->cluster_size_bits); 1268987da915Sopenharmony_ci rli++; 1269987da915Sopenharmony_ci } 1270987da915Sopenharmony_ci size = min(end - pos, NTFS_BUF_SIZE); 1271987da915Sopenharmony_ci /* 1272987da915Sopenharmony_ci * If the zeroed block is fully within a hole, 1273987da915Sopenharmony_ci * we need not write anything, so advance as far 1274987da915Sopenharmony_ci * as possible within the hole. 1275987da915Sopenharmony_ci */ 1276987da915Sopenharmony_ci if ((rli->lcn == (LCN)LCN_HOLE) 1277987da915Sopenharmony_ci && (ofsi <= pos) 1278987da915Sopenharmony_ci && (ofsi + (rli->length << vol->cluster_size_bits) 1279987da915Sopenharmony_ci >= (pos + size))) { 1280987da915Sopenharmony_ci size = min(end - pos, ofsi - pos 1281987da915Sopenharmony_ci + (rli->length << vol->cluster_size_bits)); 1282987da915Sopenharmony_ci pos += size; 1283987da915Sopenharmony_ci } else { 1284987da915Sopenharmony_ci written = ntfs_rl_pwrite(vol, rli, ofsi, pos, 1285987da915Sopenharmony_ci size, buf); 1286987da915Sopenharmony_ci if (written <= 0) { 1287987da915Sopenharmony_ci ntfs_log_perror("Failed to zero space"); 1288987da915Sopenharmony_ci goto err_free; 1289987da915Sopenharmony_ci } 1290987da915Sopenharmony_ci pos += written; 1291987da915Sopenharmony_ci } 1292987da915Sopenharmony_ci } 1293987da915Sopenharmony_ci 1294987da915Sopenharmony_ci ret = 0; 1295987da915Sopenharmony_cierr_free: 1296987da915Sopenharmony_ci free(buf); 1297987da915Sopenharmony_cierr_out: 1298987da915Sopenharmony_ci return ret; 1299987da915Sopenharmony_ci} 1300987da915Sopenharmony_ci 1301987da915Sopenharmony_cistatic int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, 1302987da915Sopenharmony_ci runlist_element **rl, VCN *update_from) 1303987da915Sopenharmony_ci{ 1304987da915Sopenharmony_ci s64 to_write; 1305987da915Sopenharmony_ci s64 need; 1306987da915Sopenharmony_ci ntfs_volume *vol = na->ni->vol; 1307987da915Sopenharmony_ci int eo, ret = -1; 1308987da915Sopenharmony_ci runlist *rlc; 1309987da915Sopenharmony_ci LCN lcn_seek_from = -1; 1310987da915Sopenharmony_ci VCN cur_vcn, from_vcn; 1311987da915Sopenharmony_ci 1312987da915Sopenharmony_ci if (na->ni->mft_no == FILE_Bitmap) { 1313987da915Sopenharmony_ci /* 1314987da915Sopenharmony_ci * Filling a hole in the main bitmap implies allocating 1315987da915Sopenharmony_ci * clusters, which is likely to imply updating the 1316987da915Sopenharmony_ci * bitmap in a cluster being allocated. 1317987da915Sopenharmony_ci * Not supported now, could lead to endless recursions. 1318987da915Sopenharmony_ci */ 1319987da915Sopenharmony_ci ntfs_log_error("Corrupt $BitMap not fully allocated\n"); 1320987da915Sopenharmony_ci errno = EIO; 1321987da915Sopenharmony_ci goto err_out; 1322987da915Sopenharmony_ci } 1323987da915Sopenharmony_ci to_write = min(count, ((*rl)->length << vol->cluster_size_bits) - *ofs); 1324987da915Sopenharmony_ci 1325987da915Sopenharmony_ci cur_vcn = (*rl)->vcn; 1326987da915Sopenharmony_ci from_vcn = (*rl)->vcn + (*ofs >> vol->cluster_size_bits); 1327987da915Sopenharmony_ci 1328987da915Sopenharmony_ci ntfs_log_trace("count: %lld, cur_vcn: %lld, from: %lld, to: %lld, ofs: " 1329987da915Sopenharmony_ci "%lld\n", (long long)count, (long long)cur_vcn, 1330987da915Sopenharmony_ci (long long)from_vcn, (long long)to_write, (long long)*ofs); 1331987da915Sopenharmony_ci 1332987da915Sopenharmony_ci /* Map the runlist to be able to update mapping pairs later. */ 1333987da915Sopenharmony_ci#if PARTIAL_RUNLIST_UPDATING 1334987da915Sopenharmony_ci if (!na->rl) { 1335987da915Sopenharmony_ci if (ntfs_attr_map_whole_runlist(na)) 1336987da915Sopenharmony_ci goto err_out; 1337987da915Sopenharmony_ci } else { 1338987da915Sopenharmony_ci /* make sure the run ahead of hole is mapped */ 1339987da915Sopenharmony_ci if ((*rl)->lcn == LCN_HOLE) { 1340987da915Sopenharmony_ci if (ntfs_attr_map_partial_runlist(na, 1341987da915Sopenharmony_ci (cur_vcn ? cur_vcn - 1 : cur_vcn))) 1342987da915Sopenharmony_ci goto err_out; 1343987da915Sopenharmony_ci } 1344987da915Sopenharmony_ci } 1345987da915Sopenharmony_ci#else 1346987da915Sopenharmony_ci if (ntfs_attr_map_whole_runlist(na)) 1347987da915Sopenharmony_ci goto err_out; 1348987da915Sopenharmony_ci#endif 1349987da915Sopenharmony_ci 1350987da915Sopenharmony_ci /* Restore @*rl, it probably get lost during runlist mapping. */ 1351987da915Sopenharmony_ci *rl = ntfs_attr_find_vcn(na, cur_vcn); 1352987da915Sopenharmony_ci if (!*rl) { 1353987da915Sopenharmony_ci ntfs_log_error("Failed to find run after mapping runlist. " 1354987da915Sopenharmony_ci "Please report to %s.\n", NTFS_DEV_LIST); 1355987da915Sopenharmony_ci errno = EIO; 1356987da915Sopenharmony_ci goto err_out; 1357987da915Sopenharmony_ci } 1358987da915Sopenharmony_ci 1359987da915Sopenharmony_ci /* Search backwards to find the best lcn to start seek from. */ 1360987da915Sopenharmony_ci rlc = *rl; 1361987da915Sopenharmony_ci while (rlc->vcn) { 1362987da915Sopenharmony_ci rlc--; 1363987da915Sopenharmony_ci if (rlc->lcn >= 0) { 1364987da915Sopenharmony_ci /* 1365987da915Sopenharmony_ci * avoid fragmenting a compressed file 1366987da915Sopenharmony_ci * Windows does not do that, and that may 1367987da915Sopenharmony_ci * not be desirable for files which can 1368987da915Sopenharmony_ci * be updated 1369987da915Sopenharmony_ci */ 1370987da915Sopenharmony_ci if (na->data_flags & ATTR_COMPRESSION_MASK) 1371987da915Sopenharmony_ci lcn_seek_from = rlc->lcn + rlc->length; 1372987da915Sopenharmony_ci else 1373987da915Sopenharmony_ci lcn_seek_from = rlc->lcn + (from_vcn - rlc->vcn); 1374987da915Sopenharmony_ci break; 1375987da915Sopenharmony_ci } 1376987da915Sopenharmony_ci } 1377987da915Sopenharmony_ci if (lcn_seek_from == -1) { 1378987da915Sopenharmony_ci /* Backwards search failed, search forwards. */ 1379987da915Sopenharmony_ci rlc = *rl; 1380987da915Sopenharmony_ci while (rlc->length) { 1381987da915Sopenharmony_ci rlc++; 1382987da915Sopenharmony_ci if (rlc->lcn >= 0) { 1383987da915Sopenharmony_ci lcn_seek_from = rlc->lcn - (rlc->vcn - from_vcn); 1384987da915Sopenharmony_ci if (lcn_seek_from < -1) 1385987da915Sopenharmony_ci lcn_seek_from = -1; 1386987da915Sopenharmony_ci break; 1387987da915Sopenharmony_ci } 1388987da915Sopenharmony_ci } 1389987da915Sopenharmony_ci } 1390987da915Sopenharmony_ci 1391987da915Sopenharmony_ci need = ((*ofs + to_write - 1) >> vol->cluster_size_bits) 1392987da915Sopenharmony_ci + 1 + (*rl)->vcn - from_vcn; 1393987da915Sopenharmony_ci if ((na->data_flags & ATTR_COMPRESSION_MASK) 1394987da915Sopenharmony_ci && (need < na->compression_block_clusters)) { 1395987da915Sopenharmony_ci /* 1396987da915Sopenharmony_ci * for a compressed file, be sure to allocate the full 1397987da915Sopenharmony_ci * compression block, as we may need space to decompress 1398987da915Sopenharmony_ci * existing compressed data. 1399987da915Sopenharmony_ci * So allocate the space common to compression block 1400987da915Sopenharmony_ci * and existing hole. 1401987da915Sopenharmony_ci */ 1402987da915Sopenharmony_ci VCN alloc_vcn; 1403987da915Sopenharmony_ci 1404987da915Sopenharmony_ci if ((from_vcn & -na->compression_block_clusters) <= (*rl)->vcn) 1405987da915Sopenharmony_ci alloc_vcn = (*rl)->vcn; 1406987da915Sopenharmony_ci else 1407987da915Sopenharmony_ci alloc_vcn = from_vcn & -na->compression_block_clusters; 1408987da915Sopenharmony_ci need = (alloc_vcn | (na->compression_block_clusters - 1)) 1409987da915Sopenharmony_ci + 1 - alloc_vcn; 1410987da915Sopenharmony_ci if (need > (*rl)->length) { 1411987da915Sopenharmony_ci ntfs_log_error("Cannot allocate %lld clusters" 1412987da915Sopenharmony_ci " within a hole of %lld\n", 1413987da915Sopenharmony_ci (long long)need, 1414987da915Sopenharmony_ci (long long)(*rl)->length); 1415987da915Sopenharmony_ci errno = EIO; 1416987da915Sopenharmony_ci goto err_out; 1417987da915Sopenharmony_ci } 1418987da915Sopenharmony_ci rlc = ntfs_cluster_alloc(vol, alloc_vcn, need, 1419987da915Sopenharmony_ci lcn_seek_from, DATA_ZONE); 1420987da915Sopenharmony_ci } else 1421987da915Sopenharmony_ci rlc = ntfs_cluster_alloc(vol, from_vcn, need, 1422987da915Sopenharmony_ci lcn_seek_from, DATA_ZONE); 1423987da915Sopenharmony_ci if (!rlc) 1424987da915Sopenharmony_ci goto err_out; 1425987da915Sopenharmony_ci if (na->data_flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) 1426987da915Sopenharmony_ci na->compressed_size += need << vol->cluster_size_bits; 1427987da915Sopenharmony_ci 1428987da915Sopenharmony_ci *rl = ntfs_runlists_merge(na->rl, rlc); 1429987da915Sopenharmony_ci NAttrSetRunlistDirty(na); 1430987da915Sopenharmony_ci /* 1431987da915Sopenharmony_ci * For a compressed attribute, we must be sure there are two 1432987da915Sopenharmony_ci * available entries, so reserve them before it gets too late. 1433987da915Sopenharmony_ci */ 1434987da915Sopenharmony_ci if (*rl && (na->data_flags & ATTR_COMPRESSION_MASK)) { 1435987da915Sopenharmony_ci runlist_element *oldrl = na->rl; 1436987da915Sopenharmony_ci na->rl = *rl; 1437987da915Sopenharmony_ci *rl = ntfs_rl_extend(na,*rl,2); 1438987da915Sopenharmony_ci if (!*rl) na->rl = oldrl; /* restore to original if failed */ 1439987da915Sopenharmony_ci } 1440987da915Sopenharmony_ci if (!*rl) { 1441987da915Sopenharmony_ci eo = errno; 1442987da915Sopenharmony_ci ntfs_log_perror("Failed to merge runlists"); 1443987da915Sopenharmony_ci if (ntfs_cluster_free_from_rl(vol, rlc)) { 1444987da915Sopenharmony_ci ntfs_log_perror("Failed to free hot clusters. " 1445987da915Sopenharmony_ci "Please run chkdsk /f"); 1446987da915Sopenharmony_ci } 1447987da915Sopenharmony_ci errno = eo; 1448987da915Sopenharmony_ci goto err_out; 1449987da915Sopenharmony_ci } 1450987da915Sopenharmony_ci na->unused_runs = 2; 1451987da915Sopenharmony_ci na->rl = *rl; 1452987da915Sopenharmony_ci if ((*update_from == -1) || (from_vcn < *update_from)) 1453987da915Sopenharmony_ci *update_from = from_vcn; 1454987da915Sopenharmony_ci *rl = ntfs_attr_find_vcn(na, cur_vcn); 1455987da915Sopenharmony_ci if (!*rl) { 1456987da915Sopenharmony_ci /* 1457987da915Sopenharmony_ci * It's definitely a BUG, if we failed to find @cur_vcn, because 1458987da915Sopenharmony_ci * we missed it during instantiating of the hole. 1459987da915Sopenharmony_ci */ 1460987da915Sopenharmony_ci ntfs_log_error("Failed to find run after hole instantiation. " 1461987da915Sopenharmony_ci "Please report to %s.\n", NTFS_DEV_LIST); 1462987da915Sopenharmony_ci errno = EIO; 1463987da915Sopenharmony_ci goto err_out; 1464987da915Sopenharmony_ci } 1465987da915Sopenharmony_ci /* If leaved part of the hole go to the next run. */ 1466987da915Sopenharmony_ci if ((*rl)->lcn < 0) 1467987da915Sopenharmony_ci (*rl)++; 1468987da915Sopenharmony_ci /* Now LCN shoudn't be less than 0. */ 1469987da915Sopenharmony_ci if ((*rl)->lcn < 0) { 1470987da915Sopenharmony_ci ntfs_log_error("BUG! LCN is lesser than 0. " 1471987da915Sopenharmony_ci "Please report to the %s.\n", NTFS_DEV_LIST); 1472987da915Sopenharmony_ci errno = EIO; 1473987da915Sopenharmony_ci goto err_out; 1474987da915Sopenharmony_ci } 1475987da915Sopenharmony_ci if (*ofs) { 1476987da915Sopenharmony_ci /* Clear non-sparse region from @cur_vcn to @*ofs. */ 1477987da915Sopenharmony_ci if (ntfs_attr_fill_zero(na, cur_vcn << vol->cluster_size_bits, 1478987da915Sopenharmony_ci *ofs)) 1479987da915Sopenharmony_ci goto err_out; 1480987da915Sopenharmony_ci } 1481987da915Sopenharmony_ci if ((*rl)->vcn < cur_vcn) { 1482987da915Sopenharmony_ci /* 1483987da915Sopenharmony_ci * Clusters that replaced hole are merged with 1484987da915Sopenharmony_ci * previous run, so we need to update offset. 1485987da915Sopenharmony_ci */ 1486987da915Sopenharmony_ci *ofs += (cur_vcn - (*rl)->vcn) << vol->cluster_size_bits; 1487987da915Sopenharmony_ci } 1488987da915Sopenharmony_ci if ((*rl)->vcn > cur_vcn) { 1489987da915Sopenharmony_ci /* 1490987da915Sopenharmony_ci * We left part of the hole, so we need to update offset 1491987da915Sopenharmony_ci */ 1492987da915Sopenharmony_ci *ofs -= ((*rl)->vcn - cur_vcn) << vol->cluster_size_bits; 1493987da915Sopenharmony_ci } 1494987da915Sopenharmony_ci 1495987da915Sopenharmony_ci ret = 0; 1496987da915Sopenharmony_cierr_out: 1497987da915Sopenharmony_ci return ret; 1498987da915Sopenharmony_ci} 1499987da915Sopenharmony_ci 1500987da915Sopenharmony_cistatic int stuff_hole(ntfs_attr *na, const s64 pos); 1501987da915Sopenharmony_ci 1502987da915Sopenharmony_ci/* 1503987da915Sopenharmony_ci * Split an existing hole for overwriting with data 1504987da915Sopenharmony_ci * The hole may have to be split into two or three parts, so 1505987da915Sopenharmony_ci * that the overwritten part fits within a single compression block 1506987da915Sopenharmony_ci * 1507987da915Sopenharmony_ci * No cluster allocation is needed, this will be done later in 1508987da915Sopenharmony_ci * standard hole filling, hence no need to reserve runs for 1509987da915Sopenharmony_ci * future needs. 1510987da915Sopenharmony_ci * 1511987da915Sopenharmony_ci * Returns the number of clusters with existing compressed data 1512987da915Sopenharmony_ci * in the compression block to be written to 1513987da915Sopenharmony_ci * (or the full block, if it was a full hole) 1514987da915Sopenharmony_ci * -1 if there were an error 1515987da915Sopenharmony_ci */ 1516987da915Sopenharmony_ci 1517987da915Sopenharmony_cistatic int split_compressed_hole(ntfs_attr *na, runlist_element **prl, 1518987da915Sopenharmony_ci s64 pos, s64 count, VCN *update_from) 1519987da915Sopenharmony_ci{ 1520987da915Sopenharmony_ci int compressed_part; 1521987da915Sopenharmony_ci int cluster_size_bits = na->ni->vol->cluster_size_bits; 1522987da915Sopenharmony_ci runlist_element *rl = *prl; 1523987da915Sopenharmony_ci 1524987da915Sopenharmony_ci compressed_part 1525987da915Sopenharmony_ci = na->compression_block_clusters; 1526987da915Sopenharmony_ci /* reserve entries in runlist if we have to split */ 1527987da915Sopenharmony_ci if (rl->length > na->compression_block_clusters) { 1528987da915Sopenharmony_ci *prl = ntfs_rl_extend(na,*prl,2); 1529987da915Sopenharmony_ci if (!*prl) { 1530987da915Sopenharmony_ci compressed_part = -1; 1531987da915Sopenharmony_ci } else { 1532987da915Sopenharmony_ci rl = *prl; 1533987da915Sopenharmony_ci na->unused_runs = 2; 1534987da915Sopenharmony_ci } 1535987da915Sopenharmony_ci } 1536987da915Sopenharmony_ci if (*prl && (rl->length > na->compression_block_clusters)) { 1537987da915Sopenharmony_ci /* 1538987da915Sopenharmony_ci * Locate the update part relative to beginning of 1539987da915Sopenharmony_ci * current run 1540987da915Sopenharmony_ci */ 1541987da915Sopenharmony_ci int beginwrite = (pos >> cluster_size_bits) - rl->vcn; 1542987da915Sopenharmony_ci s32 endblock = (((pos + count - 1) >> cluster_size_bits) 1543987da915Sopenharmony_ci | (na->compression_block_clusters - 1)) + 1 - rl->vcn; 1544987da915Sopenharmony_ci 1545987da915Sopenharmony_ci compressed_part = na->compression_block_clusters 1546987da915Sopenharmony_ci - (rl->length & (na->compression_block_clusters - 1)); 1547987da915Sopenharmony_ci if ((beginwrite + compressed_part) >= na->compression_block_clusters) 1548987da915Sopenharmony_ci compressed_part = na->compression_block_clusters; 1549987da915Sopenharmony_ci /* 1550987da915Sopenharmony_ci * if the run ends beyond end of needed block 1551987da915Sopenharmony_ci * we have to split the run 1552987da915Sopenharmony_ci */ 1553987da915Sopenharmony_ci if (endblock < rl[0].length) { 1554987da915Sopenharmony_ci runlist_element *xrl; 1555987da915Sopenharmony_ci int n; 1556987da915Sopenharmony_ci 1557987da915Sopenharmony_ci /* 1558987da915Sopenharmony_ci * we have to split into three parts if the run 1559987da915Sopenharmony_ci * does not end within the first compression block. 1560987da915Sopenharmony_ci * This means the hole begins before the 1561987da915Sopenharmony_ci * compression block. 1562987da915Sopenharmony_ci */ 1563987da915Sopenharmony_ci if (endblock > na->compression_block_clusters) { 1564987da915Sopenharmony_ci if (na->unused_runs < 2) { 1565987da915Sopenharmony_cintfs_log_error("No free run, case 1\n"); 1566987da915Sopenharmony_ci } 1567987da915Sopenharmony_ci na->unused_runs -= 2; 1568987da915Sopenharmony_ci xrl = rl; 1569987da915Sopenharmony_ci n = 0; 1570987da915Sopenharmony_ci while (xrl->length) { 1571987da915Sopenharmony_ci xrl++; 1572987da915Sopenharmony_ci n++; 1573987da915Sopenharmony_ci } 1574987da915Sopenharmony_ci do { 1575987da915Sopenharmony_ci xrl[2] = *xrl; 1576987da915Sopenharmony_ci xrl--; 1577987da915Sopenharmony_ci } while (xrl != rl); 1578987da915Sopenharmony_ci rl[1].length = na->compression_block_clusters; 1579987da915Sopenharmony_ci rl[2].length = rl[0].length - endblock; 1580987da915Sopenharmony_ci rl[0].length = endblock 1581987da915Sopenharmony_ci - na->compression_block_clusters; 1582987da915Sopenharmony_ci rl[1].lcn = LCN_HOLE; 1583987da915Sopenharmony_ci rl[2].lcn = LCN_HOLE; 1584987da915Sopenharmony_ci rl[1].vcn = rl[0].vcn + rl[0].length; 1585987da915Sopenharmony_ci rl[2].vcn = rl[1].vcn 1586987da915Sopenharmony_ci + na->compression_block_clusters; 1587987da915Sopenharmony_ci rl = ++(*prl); 1588987da915Sopenharmony_ci } else { 1589987da915Sopenharmony_ci /* 1590987da915Sopenharmony_ci * split into two parts and use the 1591987da915Sopenharmony_ci * first one 1592987da915Sopenharmony_ci */ 1593987da915Sopenharmony_ci if (!na->unused_runs) { 1594987da915Sopenharmony_cintfs_log_error("No free run, case 2\n"); 1595987da915Sopenharmony_ci } 1596987da915Sopenharmony_ci na->unused_runs--; 1597987da915Sopenharmony_ci xrl = rl; 1598987da915Sopenharmony_ci n = 0; 1599987da915Sopenharmony_ci while (xrl->length) { 1600987da915Sopenharmony_ci xrl++; 1601987da915Sopenharmony_ci n++; 1602987da915Sopenharmony_ci } 1603987da915Sopenharmony_ci do { 1604987da915Sopenharmony_ci xrl[1] = *xrl; 1605987da915Sopenharmony_ci xrl--; 1606987da915Sopenharmony_ci } while (xrl != rl); 1607987da915Sopenharmony_ci if (beginwrite < endblock) { 1608987da915Sopenharmony_ci /* we will write into the first part of hole */ 1609987da915Sopenharmony_ci rl[1].length = rl[0].length - endblock; 1610987da915Sopenharmony_ci rl[0].length = endblock; 1611987da915Sopenharmony_ci rl[1].vcn = rl[0].vcn + rl[0].length; 1612987da915Sopenharmony_ci rl[1].lcn = LCN_HOLE; 1613987da915Sopenharmony_ci } else { 1614987da915Sopenharmony_ci /* we will write into the second part of hole */ 1615987da915Sopenharmony_ci// impossible ? 1616987da915Sopenharmony_ci rl[1].length = rl[0].length - endblock; 1617987da915Sopenharmony_ci rl[0].length = endblock; 1618987da915Sopenharmony_ci rl[1].vcn = rl[0].vcn + rl[0].length; 1619987da915Sopenharmony_ci rl[1].lcn = LCN_HOLE; 1620987da915Sopenharmony_ci rl = ++(*prl); 1621987da915Sopenharmony_ci } 1622987da915Sopenharmony_ci } 1623987da915Sopenharmony_ci } else { 1624987da915Sopenharmony_ci if (rl[1].length) { 1625987da915Sopenharmony_ci runlist_element *xrl; 1626987da915Sopenharmony_ci int n; 1627987da915Sopenharmony_ci 1628987da915Sopenharmony_ci /* 1629987da915Sopenharmony_ci * split into two parts and use the 1630987da915Sopenharmony_ci * last one 1631987da915Sopenharmony_ci */ 1632987da915Sopenharmony_ci if (!na->unused_runs) { 1633987da915Sopenharmony_cintfs_log_error("No free run, case 4\n"); 1634987da915Sopenharmony_ci } 1635987da915Sopenharmony_ci na->unused_runs--; 1636987da915Sopenharmony_ci xrl = rl; 1637987da915Sopenharmony_ci n = 0; 1638987da915Sopenharmony_ci while (xrl->length) { 1639987da915Sopenharmony_ci xrl++; 1640987da915Sopenharmony_ci n++; 1641987da915Sopenharmony_ci } 1642987da915Sopenharmony_ci do { 1643987da915Sopenharmony_ci xrl[1] = *xrl; 1644987da915Sopenharmony_ci xrl--; 1645987da915Sopenharmony_ci } while (xrl != rl); 1646987da915Sopenharmony_ci } else { 1647987da915Sopenharmony_ci rl[2].lcn = rl[1].lcn; 1648987da915Sopenharmony_ci rl[2].vcn = rl[1].vcn; 1649987da915Sopenharmony_ci rl[2].length = rl[1].length; 1650987da915Sopenharmony_ci } 1651987da915Sopenharmony_ci rl[1].vcn -= na->compression_block_clusters; 1652987da915Sopenharmony_ci rl[1].lcn = LCN_HOLE; 1653987da915Sopenharmony_ci rl[1].length = na->compression_block_clusters; 1654987da915Sopenharmony_ci rl[0].length -= na->compression_block_clusters; 1655987da915Sopenharmony_ci if (pos >= (rl[1].vcn << cluster_size_bits)) { 1656987da915Sopenharmony_ci rl = ++(*prl); 1657987da915Sopenharmony_ci } 1658987da915Sopenharmony_ci } 1659987da915Sopenharmony_ci NAttrSetRunlistDirty(na); 1660987da915Sopenharmony_ci if ((*update_from == -1) || ((*prl)->vcn < *update_from)) 1661987da915Sopenharmony_ci *update_from = (*prl)->vcn; 1662987da915Sopenharmony_ci } 1663987da915Sopenharmony_ci return (compressed_part); 1664987da915Sopenharmony_ci} 1665987da915Sopenharmony_ci 1666987da915Sopenharmony_ci/* 1667987da915Sopenharmony_ci * Borrow space from adjacent hole for appending data 1668987da915Sopenharmony_ci * The hole may have to be split so that the end of hole is not 1669987da915Sopenharmony_ci * affected by cluster allocation and overwriting 1670987da915Sopenharmony_ci * Cluster allocation is needed for the overwritten compression block 1671987da915Sopenharmony_ci * 1672987da915Sopenharmony_ci * Must always leave two unused entries in the runlist 1673987da915Sopenharmony_ci * 1674987da915Sopenharmony_ci * Returns the number of clusters with existing compressed data 1675987da915Sopenharmony_ci * in the compression block to be written to 1676987da915Sopenharmony_ci * -1 if there were an error 1677987da915Sopenharmony_ci */ 1678987da915Sopenharmony_ci 1679987da915Sopenharmony_cistatic int borrow_from_hole(ntfs_attr *na, runlist_element **prl, 1680987da915Sopenharmony_ci s64 pos, s64 count, VCN *update_from, BOOL wasnonresident) 1681987da915Sopenharmony_ci{ 1682987da915Sopenharmony_ci int compressed_part = 0; 1683987da915Sopenharmony_ci int cluster_size_bits = na->ni->vol->cluster_size_bits; 1684987da915Sopenharmony_ci runlist_element *rl = *prl; 1685987da915Sopenharmony_ci s32 endblock; 1686987da915Sopenharmony_ci long long allocated; 1687987da915Sopenharmony_ci runlist_element *zrl; 1688987da915Sopenharmony_ci int irl; 1689987da915Sopenharmony_ci BOOL undecided; 1690987da915Sopenharmony_ci BOOL nothole; 1691987da915Sopenharmony_ci 1692987da915Sopenharmony_ci /* check whether the compression block is fully allocated */ 1693987da915Sopenharmony_ci endblock = (((pos + count - 1) >> cluster_size_bits) | (na->compression_block_clusters - 1)) + 1 - rl->vcn; 1694987da915Sopenharmony_ci allocated = 0; 1695987da915Sopenharmony_ci zrl = rl; 1696987da915Sopenharmony_ci irl = 0; 1697987da915Sopenharmony_ci while (zrl->length && (zrl->lcn >= 0) && (allocated < endblock)) { 1698987da915Sopenharmony_ci allocated += zrl->length; 1699987da915Sopenharmony_ci zrl++; 1700987da915Sopenharmony_ci irl++; 1701987da915Sopenharmony_ci } 1702987da915Sopenharmony_ci 1703987da915Sopenharmony_ci undecided = (allocated < endblock) && (zrl->lcn == LCN_RL_NOT_MAPPED); 1704987da915Sopenharmony_ci nothole = (allocated >= endblock) || (zrl->lcn != LCN_HOLE); 1705987da915Sopenharmony_ci 1706987da915Sopenharmony_ci if (undecided || nothole) { 1707987da915Sopenharmony_ci runlist_element *orl = na->rl; 1708987da915Sopenharmony_ci s64 olcn = (*prl)->lcn; 1709987da915Sopenharmony_ci#if PARTIAL_RUNLIST_UPDATING 1710987da915Sopenharmony_ci VCN prevblock; 1711987da915Sopenharmony_ci#endif 1712987da915Sopenharmony_ci /* 1713987da915Sopenharmony_ci * Map the runlist, unless it has not been created. 1714987da915Sopenharmony_ci * If appending data, a partial mapping from the 1715987da915Sopenharmony_ci * end of previous block will do. 1716987da915Sopenharmony_ci */ 1717987da915Sopenharmony_ci irl = *prl - na->rl; 1718987da915Sopenharmony_ci#if PARTIAL_RUNLIST_UPDATING 1719987da915Sopenharmony_ci prevblock = pos >> cluster_size_bits; 1720987da915Sopenharmony_ci if (prevblock) 1721987da915Sopenharmony_ci prevblock--; 1722987da915Sopenharmony_ci if (!NAttrBeingNonResident(na) 1723987da915Sopenharmony_ci && (NAttrDataAppending(na) 1724987da915Sopenharmony_ci ? ntfs_attr_map_partial_runlist(na,prevblock) 1725987da915Sopenharmony_ci : ntfs_attr_map_whole_runlist(na))) { 1726987da915Sopenharmony_ci#else 1727987da915Sopenharmony_ci if (!NAttrBeingNonResident(na) 1728987da915Sopenharmony_ci && ntfs_attr_map_whole_runlist(na)) { 1729987da915Sopenharmony_ci#endif 1730987da915Sopenharmony_ci rl = (runlist_element*)NULL; 1731987da915Sopenharmony_ci } else { 1732987da915Sopenharmony_ci /* 1733987da915Sopenharmony_ci * Mapping the runlist may cause its relocation, 1734987da915Sopenharmony_ci * and relocation may be at the same place with 1735987da915Sopenharmony_ci * relocated contents. 1736987da915Sopenharmony_ci * Have to find the current run again when this 1737987da915Sopenharmony_ci * happens. 1738987da915Sopenharmony_ci */ 1739987da915Sopenharmony_ci if ((na->rl != orl) || ((*prl)->lcn != olcn)) { 1740987da915Sopenharmony_ci zrl = &na->rl[irl]; 1741987da915Sopenharmony_ci while (zrl->length && (zrl->lcn != olcn)) 1742987da915Sopenharmony_ci zrl++; 1743987da915Sopenharmony_ci *prl = zrl; 1744987da915Sopenharmony_ci } 1745987da915Sopenharmony_ci if (!(*prl)->length) { 1746987da915Sopenharmony_ci ntfs_log_error("Mapped run not found," 1747987da915Sopenharmony_ci " inode %lld lcn 0x%llx\n", 1748987da915Sopenharmony_ci (long long)na->ni->mft_no, 1749987da915Sopenharmony_ci (long long)olcn); 1750987da915Sopenharmony_ci rl = (runlist_element*)NULL; 1751987da915Sopenharmony_ci } else { 1752987da915Sopenharmony_ci rl = ntfs_rl_extend(na,*prl,2); 1753987da915Sopenharmony_ci na->unused_runs = 2; 1754987da915Sopenharmony_ci } 1755987da915Sopenharmony_ci } 1756987da915Sopenharmony_ci *prl = rl; 1757987da915Sopenharmony_ci if (rl && undecided) { 1758987da915Sopenharmony_ci allocated = 0; 1759987da915Sopenharmony_ci zrl = rl; 1760987da915Sopenharmony_ci irl = 0; 1761987da915Sopenharmony_ci while (zrl->length && (zrl->lcn >= 0) 1762987da915Sopenharmony_ci && (allocated < endblock)) { 1763987da915Sopenharmony_ci allocated += zrl->length; 1764987da915Sopenharmony_ci zrl++; 1765987da915Sopenharmony_ci irl++; 1766987da915Sopenharmony_ci } 1767987da915Sopenharmony_ci } 1768987da915Sopenharmony_ci } 1769987da915Sopenharmony_ci /* 1770987da915Sopenharmony_ci * compression block not fully allocated and followed 1771987da915Sopenharmony_ci * by a hole : we must allocate in the hole. 1772987da915Sopenharmony_ci */ 1773987da915Sopenharmony_ci if (rl && (allocated < endblock) && (zrl->lcn == LCN_HOLE)) { 1774987da915Sopenharmony_ci s64 xofs; 1775987da915Sopenharmony_ci 1776987da915Sopenharmony_ci /* 1777987da915Sopenharmony_ci * split the hole if not fully needed 1778987da915Sopenharmony_ci */ 1779987da915Sopenharmony_ci if ((allocated + zrl->length) > endblock) { 1780987da915Sopenharmony_ci runlist_element *xrl; 1781987da915Sopenharmony_ci 1782987da915Sopenharmony_ci *prl = ntfs_rl_extend(na,*prl,1); 1783987da915Sopenharmony_ci if (*prl) { 1784987da915Sopenharmony_ci /* beware : rl was reallocated */ 1785987da915Sopenharmony_ci rl = *prl; 1786987da915Sopenharmony_ci zrl = &rl[irl]; 1787987da915Sopenharmony_ci na->unused_runs = 0; 1788987da915Sopenharmony_ci xrl = zrl; 1789987da915Sopenharmony_ci while (xrl->length) xrl++; 1790987da915Sopenharmony_ci do { 1791987da915Sopenharmony_ci xrl[1] = *xrl; 1792987da915Sopenharmony_ci } while (xrl-- != zrl); 1793987da915Sopenharmony_ci zrl->length = endblock - allocated; 1794987da915Sopenharmony_ci zrl[1].length -= zrl->length; 1795987da915Sopenharmony_ci zrl[1].vcn = zrl->vcn + zrl->length; 1796987da915Sopenharmony_ci NAttrSetRunlistDirty(na); 1797987da915Sopenharmony_ci } 1798987da915Sopenharmony_ci } 1799987da915Sopenharmony_ci if (*prl) { 1800987da915Sopenharmony_ci if (wasnonresident) 1801987da915Sopenharmony_ci compressed_part = na->compression_block_clusters 1802987da915Sopenharmony_ci - zrl->length; 1803987da915Sopenharmony_ci xofs = 0; 1804987da915Sopenharmony_ci if (ntfs_attr_fill_hole(na, 1805987da915Sopenharmony_ci zrl->length << cluster_size_bits, 1806987da915Sopenharmony_ci &xofs, &zrl, update_from)) 1807987da915Sopenharmony_ci compressed_part = -1; 1808987da915Sopenharmony_ci else { 1809987da915Sopenharmony_ci /* go back to initial cluster, now reallocated */ 1810987da915Sopenharmony_ci while (zrl->vcn > (pos >> cluster_size_bits)) 1811987da915Sopenharmony_ci zrl--; 1812987da915Sopenharmony_ci *prl = zrl; 1813987da915Sopenharmony_ci } 1814987da915Sopenharmony_ci } 1815987da915Sopenharmony_ci } 1816987da915Sopenharmony_ci if (!*prl) { 1817987da915Sopenharmony_ci ntfs_log_error("No elements to borrow from a hole\n"); 1818987da915Sopenharmony_ci compressed_part = -1; 1819987da915Sopenharmony_ci } else 1820987da915Sopenharmony_ci if ((*update_from == -1) || ((*prl)->vcn < *update_from)) 1821987da915Sopenharmony_ci *update_from = (*prl)->vcn; 1822987da915Sopenharmony_ci return (compressed_part); 1823987da915Sopenharmony_ci} 1824987da915Sopenharmony_ci 1825987da915Sopenharmony_cistatic int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize, 1826987da915Sopenharmony_ci hole_type holes); 1827987da915Sopenharmony_ci 1828987da915Sopenharmony_ci/** 1829987da915Sopenharmony_ci * ntfs_attr_pwrite - positioned write to an ntfs attribute 1830987da915Sopenharmony_ci * @na: ntfs attribute to write to 1831987da915Sopenharmony_ci * @pos: position in the attribute to write to 1832987da915Sopenharmony_ci * @count: number of bytes to write 1833987da915Sopenharmony_ci * @b: data buffer to write to disk 1834987da915Sopenharmony_ci * 1835987da915Sopenharmony_ci * This function will write @count bytes from data buffer @b to ntfs attribute 1836987da915Sopenharmony_ci * @na at position @pos. 1837987da915Sopenharmony_ci * 1838987da915Sopenharmony_ci * On success, return the number of successfully written bytes. If this number 1839987da915Sopenharmony_ci * is lower than @count this means that an error was encountered during the 1840987da915Sopenharmony_ci * write so that the write is partial. 0 means nothing was written (also return 1841987da915Sopenharmony_ci * 0 when @count is 0). 1842987da915Sopenharmony_ci * 1843987da915Sopenharmony_ci * On error and nothing has been written, return -1 with errno set 1844987da915Sopenharmony_ci * appropriately to the return code of ntfs_pwrite(), or to EINVAL in case of 1845987da915Sopenharmony_ci * invalid arguments. 1846987da915Sopenharmony_ci */ 1847987da915Sopenharmony_cistatic s64 ntfs_attr_pwrite_i(ntfs_attr *na, const s64 pos, s64 count, 1848987da915Sopenharmony_ci const void *b) 1849987da915Sopenharmony_ci{ 1850987da915Sopenharmony_ci s64 written, to_write, ofs, old_initialized_size, old_data_size; 1851987da915Sopenharmony_ci s64 total = 0; 1852987da915Sopenharmony_ci VCN update_from = -1; 1853987da915Sopenharmony_ci ntfs_volume *vol; 1854987da915Sopenharmony_ci s64 fullcount; 1855987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx = NULL; 1856987da915Sopenharmony_ci runlist_element *rl; 1857987da915Sopenharmony_ci s64 hole_end; 1858987da915Sopenharmony_ci int eo; 1859987da915Sopenharmony_ci int compressed_part; 1860987da915Sopenharmony_ci struct { 1861987da915Sopenharmony_ci unsigned int undo_initialized_size : 1; 1862987da915Sopenharmony_ci unsigned int undo_data_size : 1; 1863987da915Sopenharmony_ci } need_to = { 0, 0 }; 1864987da915Sopenharmony_ci BOOL wasnonresident = FALSE; 1865987da915Sopenharmony_ci BOOL compressed; 1866987da915Sopenharmony_ci 1867987da915Sopenharmony_ci vol = na->ni->vol; 1868987da915Sopenharmony_ci compressed = (na->data_flags & ATTR_COMPRESSION_MASK) 1869987da915Sopenharmony_ci != const_cpu_to_le16(0); 1870987da915Sopenharmony_ci na->unused_runs = 0; /* prepare overflow checks */ 1871987da915Sopenharmony_ci /* 1872987da915Sopenharmony_ci * Encrypted attributes are only supported in raw mode. We return 1873987da915Sopenharmony_ci * access denied, which is what Windows NT4 does, too. 1874987da915Sopenharmony_ci * Moreover a file cannot be both encrypted and compressed. 1875987da915Sopenharmony_ci */ 1876987da915Sopenharmony_ci if ((na->data_flags & ATTR_IS_ENCRYPTED) 1877987da915Sopenharmony_ci && (compressed || !vol->efs_raw)) { 1878987da915Sopenharmony_ci errno = EACCES; 1879987da915Sopenharmony_ci goto errno_set; 1880987da915Sopenharmony_ci } 1881987da915Sopenharmony_ci /* 1882987da915Sopenharmony_ci * Fill the gap, when writing beyond the end of a compressed 1883987da915Sopenharmony_ci * file. This will make recursive calls 1884987da915Sopenharmony_ci */ 1885987da915Sopenharmony_ci if (compressed 1886987da915Sopenharmony_ci && (na->type == AT_DATA) 1887987da915Sopenharmony_ci && (pos > na->initialized_size) 1888987da915Sopenharmony_ci && stuff_hole(na,pos)) 1889987da915Sopenharmony_ci goto errno_set; 1890987da915Sopenharmony_ci /* If this is a compressed attribute it needs special treatment. */ 1891987da915Sopenharmony_ci wasnonresident = NAttrNonResident(na) != 0; 1892987da915Sopenharmony_ci /* 1893987da915Sopenharmony_ci * Compression is restricted to data streams and 1894987da915Sopenharmony_ci * only ATTR_IS_COMPRESSED compression mode is supported. 1895987da915Sopenharmony_ci */ 1896987da915Sopenharmony_ci if (compressed 1897987da915Sopenharmony_ci && ((na->type != AT_DATA) 1898987da915Sopenharmony_ci || ((na->data_flags & ATTR_COMPRESSION_MASK) 1899987da915Sopenharmony_ci != ATTR_IS_COMPRESSED))) { 1900987da915Sopenharmony_ci errno = EOPNOTSUPP; 1901987da915Sopenharmony_ci goto errno_set; 1902987da915Sopenharmony_ci } 1903987da915Sopenharmony_ci 1904987da915Sopenharmony_ci if (!count) 1905987da915Sopenharmony_ci goto out; 1906987da915Sopenharmony_ci /* for a compressed file, get prepared to reserve a full block */ 1907987da915Sopenharmony_ci fullcount = count; 1908987da915Sopenharmony_ci /* If the write reaches beyond the end, extend the attribute. */ 1909987da915Sopenharmony_ci old_data_size = na->data_size; 1910987da915Sopenharmony_ci /* identify whether this is appending to a non resident data attribute */ 1911987da915Sopenharmony_ci if ((na->type == AT_DATA) && (pos >= old_data_size) 1912987da915Sopenharmony_ci && NAttrNonResident(na)) 1913987da915Sopenharmony_ci NAttrSetDataAppending(na); 1914987da915Sopenharmony_ci if (pos + count > na->data_size) { 1915987da915Sopenharmony_ci#if PARTIAL_RUNLIST_UPDATING 1916987da915Sopenharmony_ci /* 1917987da915Sopenharmony_ci * When appending data, the attribute is first extended 1918987da915Sopenharmony_ci * before being filled with data. This may cause the 1919987da915Sopenharmony_ci * attribute to be made temporarily sparse, which 1920987da915Sopenharmony_ci * implies reformating the inode and reorganizing the 1921987da915Sopenharmony_ci * full runlist. To avoid unnecessary reorganization, 1922987da915Sopenharmony_ci * we avoid sparse testing until the data is filled in. 1923987da915Sopenharmony_ci */ 1924987da915Sopenharmony_ci if (ntfs_attr_truncate_i(na, pos + count, 1925987da915Sopenharmony_ci (NAttrDataAppending(na) ? 1926987da915Sopenharmony_ci HOLES_DELAY : HOLES_OK))) { 1927987da915Sopenharmony_ci ntfs_log_perror("Failed to enlarge attribute"); 1928987da915Sopenharmony_ci goto errno_set; 1929987da915Sopenharmony_ci } 1930987da915Sopenharmony_ci /* 1931987da915Sopenharmony_ci * If we avoided updating the runlist, we must be sure 1932987da915Sopenharmony_ci * to cancel the enlargement and put back the runlist to 1933987da915Sopenharmony_ci * a clean state if we get into some error. 1934987da915Sopenharmony_ci */ 1935987da915Sopenharmony_ci if (NAttrDataAppending(na)) 1936987da915Sopenharmony_ci need_to.undo_data_size = 1; 1937987da915Sopenharmony_ci#else 1938987da915Sopenharmony_ci if (ntfs_attr_truncate_i(na, pos + count, HOLES_OK)) { 1939987da915Sopenharmony_ci ntfs_log_perror("Failed to enlarge attribute"); 1940987da915Sopenharmony_ci goto errno_set; 1941987da915Sopenharmony_ci } 1942987da915Sopenharmony_ci#endif 1943987da915Sopenharmony_ci /* resizing may change the compression mode */ 1944987da915Sopenharmony_ci compressed = (na->data_flags & ATTR_COMPRESSION_MASK) 1945987da915Sopenharmony_ci != const_cpu_to_le16(0); 1946987da915Sopenharmony_ci need_to.undo_data_size = 1; 1947987da915Sopenharmony_ci } 1948987da915Sopenharmony_ci /* 1949987da915Sopenharmony_ci * For compressed data, a single full block was allocated 1950987da915Sopenharmony_ci * to deal with compression, possibly in a previous call. 1951987da915Sopenharmony_ci * We are not able to process several blocks because 1952987da915Sopenharmony_ci * some clusters are freed after compression and 1953987da915Sopenharmony_ci * new allocations have to be done before proceeding, 1954987da915Sopenharmony_ci * so truncate the requested count if needed (big buffers). 1955987da915Sopenharmony_ci */ 1956987da915Sopenharmony_ci if (compressed) { 1957987da915Sopenharmony_ci fullcount = (pos | (na->compression_block_size - 1)) + 1 - pos; 1958987da915Sopenharmony_ci if (count > fullcount) 1959987da915Sopenharmony_ci count = fullcount; 1960987da915Sopenharmony_ci } 1961987da915Sopenharmony_ci old_initialized_size = na->initialized_size; 1962987da915Sopenharmony_ci /* If it is a resident attribute, write the data to the mft record. */ 1963987da915Sopenharmony_ci if (!NAttrNonResident(na)) { 1964987da915Sopenharmony_ci char *val; 1965987da915Sopenharmony_ci 1966987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(na->ni, NULL); 1967987da915Sopenharmony_ci if (!ctx) 1968987da915Sopenharmony_ci goto err_out; 1969987da915Sopenharmony_ci if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 1970987da915Sopenharmony_ci 0, NULL, 0, ctx)) { 1971987da915Sopenharmony_ci ntfs_log_perror("%s: lookup failed", __FUNCTION__); 1972987da915Sopenharmony_ci goto err_out; 1973987da915Sopenharmony_ci } 1974987da915Sopenharmony_ci val = (char*)ctx->attr + le16_to_cpu(ctx->attr->value_offset); 1975987da915Sopenharmony_ci if (val < (char*)ctx->attr || val + 1976987da915Sopenharmony_ci le32_to_cpu(ctx->attr->value_length) > 1977987da915Sopenharmony_ci (char*)ctx->mrec + vol->mft_record_size) { 1978987da915Sopenharmony_ci errno = EIO; 1979987da915Sopenharmony_ci ntfs_log_perror("%s: Sanity check failed", __FUNCTION__); 1980987da915Sopenharmony_ci goto err_out; 1981987da915Sopenharmony_ci } 1982987da915Sopenharmony_ci memcpy(val + pos, b, count); 1983987da915Sopenharmony_ci if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, 1984987da915Sopenharmony_ci ctx->mrec)) { 1985987da915Sopenharmony_ci /* 1986987da915Sopenharmony_ci * NOTE: We are in a bad state at this moment. We have 1987987da915Sopenharmony_ci * dirtied the mft record but we failed to commit it to 1988987da915Sopenharmony_ci * disk. Since we have read the mft record ok before, 1989987da915Sopenharmony_ci * it is unlikely to fail writing it, so is ok to just 1990987da915Sopenharmony_ci * return error here... (AIA) 1991987da915Sopenharmony_ci */ 1992987da915Sopenharmony_ci ntfs_log_perror("%s: failed to write mft record", __FUNCTION__); 1993987da915Sopenharmony_ci goto err_out; 1994987da915Sopenharmony_ci } 1995987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 1996987da915Sopenharmony_ci total = count; 1997987da915Sopenharmony_ci goto out; 1998987da915Sopenharmony_ci } 1999987da915Sopenharmony_ci 2000987da915Sopenharmony_ci /* Handle writes beyond initialized_size. */ 2001987da915Sopenharmony_ci 2002987da915Sopenharmony_ci if (pos + count > na->initialized_size) { 2003987da915Sopenharmony_ci#if PARTIAL_RUNLIST_UPDATING 2004987da915Sopenharmony_ci /* 2005987da915Sopenharmony_ci * When appending, we only need to map the end of the runlist, 2006987da915Sopenharmony_ci * starting at the last previously allocated run, so that 2007987da915Sopenharmony_ci * we are able a new one to it. 2008987da915Sopenharmony_ci * However, for compressed file, we need the full compression 2009987da915Sopenharmony_ci * block, which may be split in several extents. 2010987da915Sopenharmony_ci */ 2011987da915Sopenharmony_ci if (compressed && !NAttrDataAppending(na)) { 2012987da915Sopenharmony_ci if (ntfs_attr_map_whole_runlist(na)) 2013987da915Sopenharmony_ci goto err_out; 2014987da915Sopenharmony_ci } else { 2015987da915Sopenharmony_ci VCN block_begin; 2016987da915Sopenharmony_ci 2017987da915Sopenharmony_ci if (NAttrDataAppending(na) 2018987da915Sopenharmony_ci || (pos < na->initialized_size)) 2019987da915Sopenharmony_ci block_begin = pos >> vol->cluster_size_bits; 2020987da915Sopenharmony_ci else 2021987da915Sopenharmony_ci block_begin = na->initialized_size >> vol->cluster_size_bits; 2022987da915Sopenharmony_ci 2023987da915Sopenharmony_ci if (compressed) 2024987da915Sopenharmony_ci block_begin &= -na->compression_block_clusters; 2025987da915Sopenharmony_ci if (block_begin) 2026987da915Sopenharmony_ci block_begin--; 2027987da915Sopenharmony_ci if (ntfs_attr_map_partial_runlist(na, block_begin)) 2028987da915Sopenharmony_ci goto err_out; 2029987da915Sopenharmony_ci if ((update_from == -1) || (block_begin < update_from)) 2030987da915Sopenharmony_ci update_from = block_begin; 2031987da915Sopenharmony_ci } 2032987da915Sopenharmony_ci#else 2033987da915Sopenharmony_ci if (ntfs_attr_map_whole_runlist(na)) 2034987da915Sopenharmony_ci goto err_out; 2035987da915Sopenharmony_ci#endif 2036987da915Sopenharmony_ci /* 2037987da915Sopenharmony_ci * For a compressed attribute, we must be sure there is an 2038987da915Sopenharmony_ci * available entry, and, when reopening a compressed file, 2039987da915Sopenharmony_ci * we may need to split a hole. So reserve the entries 2040987da915Sopenharmony_ci * before it gets too late. 2041987da915Sopenharmony_ci */ 2042987da915Sopenharmony_ci if (compressed) { 2043987da915Sopenharmony_ci na->rl = ntfs_rl_extend(na,na->rl,2); 2044987da915Sopenharmony_ci if (!na->rl) 2045987da915Sopenharmony_ci goto err_out; 2046987da915Sopenharmony_ci na->unused_runs = 2; 2047987da915Sopenharmony_ci } 2048987da915Sopenharmony_ci /* Set initialized_size to @pos + @count. */ 2049987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(na->ni, NULL); 2050987da915Sopenharmony_ci if (!ctx) 2051987da915Sopenharmony_ci goto err_out; 2052987da915Sopenharmony_ci if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 2053987da915Sopenharmony_ci 0, NULL, 0, ctx)) 2054987da915Sopenharmony_ci goto err_out; 2055987da915Sopenharmony_ci 2056987da915Sopenharmony_ci /* If write starts beyond initialized_size, zero the gap. */ 2057987da915Sopenharmony_ci if (pos > na->initialized_size) 2058987da915Sopenharmony_ci if (ntfs_attr_fill_zero(na, na->initialized_size, 2059987da915Sopenharmony_ci pos - na->initialized_size)) 2060987da915Sopenharmony_ci goto err_out; 2061987da915Sopenharmony_ci 2062987da915Sopenharmony_ci ctx->attr->initialized_size = cpu_to_sle64(pos + count); 2063987da915Sopenharmony_ci /* fix data_size for compressed files */ 2064987da915Sopenharmony_ci if (compressed) { 2065987da915Sopenharmony_ci na->data_size = pos + count; 2066987da915Sopenharmony_ci ctx->attr->data_size = ctx->attr->initialized_size; 2067987da915Sopenharmony_ci } 2068987da915Sopenharmony_ci if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, 2069987da915Sopenharmony_ci ctx->mrec)) { 2070987da915Sopenharmony_ci /* 2071987da915Sopenharmony_ci * Undo the change in the in-memory copy and send it 2072987da915Sopenharmony_ci * back for writing. 2073987da915Sopenharmony_ci */ 2074987da915Sopenharmony_ci ctx->attr->initialized_size = 2075987da915Sopenharmony_ci cpu_to_sle64(old_initialized_size); 2076987da915Sopenharmony_ci ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, 2077987da915Sopenharmony_ci ctx->mrec); 2078987da915Sopenharmony_ci goto err_out; 2079987da915Sopenharmony_ci } 2080987da915Sopenharmony_ci na->initialized_size = pos + count; 2081987da915Sopenharmony_ci#if CACHE_NIDATA_SIZE 2082987da915Sopenharmony_ci if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY 2083987da915Sopenharmony_ci ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 2084987da915Sopenharmony_ci : na->type == AT_DATA && na->name == AT_UNNAMED) { 2085987da915Sopenharmony_ci na->ni->data_size = na->data_size; 2086987da915Sopenharmony_ci if ((compressed || NAttrSparse(na)) 2087987da915Sopenharmony_ci && NAttrNonResident(na)) 2088987da915Sopenharmony_ci na->ni->allocated_size = na->compressed_size; 2089987da915Sopenharmony_ci else 2090987da915Sopenharmony_ci na->ni->allocated_size = na->allocated_size; 2091987da915Sopenharmony_ci set_nino_flag(na->ni,KnownSize); 2092987da915Sopenharmony_ci } 2093987da915Sopenharmony_ci#endif 2094987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 2095987da915Sopenharmony_ci ctx = NULL; 2096987da915Sopenharmony_ci /* 2097987da915Sopenharmony_ci * NOTE: At this point the initialized_size in the mft record 2098987da915Sopenharmony_ci * has been updated BUT there is random data on disk thus if 2099987da915Sopenharmony_ci * we decide to abort, we MUST change the initialized_size 2100987da915Sopenharmony_ci * again. 2101987da915Sopenharmony_ci */ 2102987da915Sopenharmony_ci need_to.undo_initialized_size = 1; 2103987da915Sopenharmony_ci } 2104987da915Sopenharmony_ci /* Find the runlist element containing the vcn. */ 2105987da915Sopenharmony_ci rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); 2106987da915Sopenharmony_ci if (!rl) { 2107987da915Sopenharmony_ci /* 2108987da915Sopenharmony_ci * If the vcn is not present it is an out of bounds write. 2109987da915Sopenharmony_ci * However, we already extended the size of the attribute, 2110987da915Sopenharmony_ci * so getting this here must be an error of some kind. 2111987da915Sopenharmony_ci */ 2112987da915Sopenharmony_ci if (errno == ENOENT) { 2113987da915Sopenharmony_ci errno = EIO; 2114987da915Sopenharmony_ci ntfs_log_perror("%s: Failed to find VCN #3", __FUNCTION__); 2115987da915Sopenharmony_ci } 2116987da915Sopenharmony_ci goto err_out; 2117987da915Sopenharmony_ci } 2118987da915Sopenharmony_ci /* 2119987da915Sopenharmony_ci * Determine if there is compressed data in the current 2120987da915Sopenharmony_ci * compression block (when appending to an existing file). 2121987da915Sopenharmony_ci * If so, decompression will be needed, and the full block 2122987da915Sopenharmony_ci * must be allocated to be identified as uncompressed. 2123987da915Sopenharmony_ci * This comes in two variants, depending on whether 2124987da915Sopenharmony_ci * compression has saved at least one cluster. 2125987da915Sopenharmony_ci * The compressed size can never be over full size by 2126987da915Sopenharmony_ci * more than 485 (maximum for 15 compression blocks 2127987da915Sopenharmony_ci * compressed to 4098 and the last 3640 bytes compressed 2128987da915Sopenharmony_ci * to 3640 + 3640/8 = 4095, with 15*2 + 4095 - 3640 = 485) 2129987da915Sopenharmony_ci * This is less than the smallest cluster, so the hole is 2130987da915Sopenharmony_ci * is never beyond the cluster next to the position of 2131987da915Sopenharmony_ci * the first uncompressed byte to write. 2132987da915Sopenharmony_ci */ 2133987da915Sopenharmony_ci compressed_part = 0; 2134987da915Sopenharmony_ci if (compressed) { 2135987da915Sopenharmony_ci if ((rl->lcn == (LCN)LCN_HOLE) 2136987da915Sopenharmony_ci && wasnonresident) { 2137987da915Sopenharmony_ci if (rl->length < na->compression_block_clusters) 2138987da915Sopenharmony_ci /* 2139987da915Sopenharmony_ci * the needed block is in a hole smaller 2140987da915Sopenharmony_ci * than the compression block : we can use 2141987da915Sopenharmony_ci * it fully 2142987da915Sopenharmony_ci */ 2143987da915Sopenharmony_ci compressed_part 2144987da915Sopenharmony_ci = na->compression_block_clusters 2145987da915Sopenharmony_ci - rl->length; 2146987da915Sopenharmony_ci else { 2147987da915Sopenharmony_ci /* 2148987da915Sopenharmony_ci * the needed block is in a hole bigger 2149987da915Sopenharmony_ci * than the compression block : we must 2150987da915Sopenharmony_ci * split the hole and use it partially 2151987da915Sopenharmony_ci */ 2152987da915Sopenharmony_ci compressed_part = split_compressed_hole(na, 2153987da915Sopenharmony_ci &rl, pos, count, &update_from); 2154987da915Sopenharmony_ci } 2155987da915Sopenharmony_ci } else { 2156987da915Sopenharmony_ci if (rl->lcn >= 0) { 2157987da915Sopenharmony_ci /* 2158987da915Sopenharmony_ci * the needed block contains data, make 2159987da915Sopenharmony_ci * sure the full compression block is 2160987da915Sopenharmony_ci * allocated. Borrow from hole if needed 2161987da915Sopenharmony_ci */ 2162987da915Sopenharmony_ci compressed_part = borrow_from_hole(na, 2163987da915Sopenharmony_ci &rl, pos, count, &update_from, 2164987da915Sopenharmony_ci wasnonresident); 2165987da915Sopenharmony_ci } 2166987da915Sopenharmony_ci } 2167987da915Sopenharmony_ci 2168987da915Sopenharmony_ci if (compressed_part < 0) 2169987da915Sopenharmony_ci goto err_out; 2170987da915Sopenharmony_ci 2171987da915Sopenharmony_ci /* just making non-resident, so not yet compressed */ 2172987da915Sopenharmony_ci if (NAttrBeingNonResident(na) 2173987da915Sopenharmony_ci && (compressed_part < na->compression_block_clusters)) 2174987da915Sopenharmony_ci compressed_part = 0; 2175987da915Sopenharmony_ci } 2176987da915Sopenharmony_ci ofs = pos - (rl->vcn << vol->cluster_size_bits); 2177987da915Sopenharmony_ci /* 2178987da915Sopenharmony_ci * Scatter the data from the linear data buffer to the volume. Note, a 2179987da915Sopenharmony_ci * partial final vcn is taken care of by the @count capping of write 2180987da915Sopenharmony_ci * length. 2181987da915Sopenharmony_ci */ 2182987da915Sopenharmony_ci for (hole_end = 0; count; rl++, ofs = 0) { 2183987da915Sopenharmony_ci if (rl->lcn == LCN_RL_NOT_MAPPED) { 2184987da915Sopenharmony_ci rl = ntfs_attr_find_vcn(na, rl->vcn); 2185987da915Sopenharmony_ci if (!rl) { 2186987da915Sopenharmony_ci if (errno == ENOENT) { 2187987da915Sopenharmony_ci errno = EIO; 2188987da915Sopenharmony_ci ntfs_log_perror("%s: Failed to find VCN" 2189987da915Sopenharmony_ci " #4", __FUNCTION__); 2190987da915Sopenharmony_ci } 2191987da915Sopenharmony_ci goto rl_err_out; 2192987da915Sopenharmony_ci } 2193987da915Sopenharmony_ci /* Needed for case when runs merged. */ 2194987da915Sopenharmony_ci ofs = pos + total - (rl->vcn << vol->cluster_size_bits); 2195987da915Sopenharmony_ci } 2196987da915Sopenharmony_ci if (!rl->length) { 2197987da915Sopenharmony_ci errno = EIO; 2198987da915Sopenharmony_ci ntfs_log_perror("%s: Zero run length", __FUNCTION__); 2199987da915Sopenharmony_ci goto rl_err_out; 2200987da915Sopenharmony_ci } 2201987da915Sopenharmony_ci if (rl->lcn < (LCN)0) { 2202987da915Sopenharmony_ci hole_end = rl->vcn + rl->length; 2203987da915Sopenharmony_ci 2204987da915Sopenharmony_ci if (rl->lcn != (LCN)LCN_HOLE) { 2205987da915Sopenharmony_ci errno = EIO; 2206987da915Sopenharmony_ci ntfs_log_perror("%s: Unexpected LCN (%lld)", 2207987da915Sopenharmony_ci __FUNCTION__, 2208987da915Sopenharmony_ci (long long)rl->lcn); 2209987da915Sopenharmony_ci goto rl_err_out; 2210987da915Sopenharmony_ci } 2211987da915Sopenharmony_ci if (ntfs_attr_fill_hole(na, fullcount, &ofs, &rl, 2212987da915Sopenharmony_ci &update_from)) 2213987da915Sopenharmony_ci goto err_out; 2214987da915Sopenharmony_ci } 2215987da915Sopenharmony_ci if (compressed) { 2216987da915Sopenharmony_ci while (rl->length 2217987da915Sopenharmony_ci && (ofs >= (rl->length << vol->cluster_size_bits))) { 2218987da915Sopenharmony_ci ofs -= rl->length << vol->cluster_size_bits; 2219987da915Sopenharmony_ci rl++; 2220987da915Sopenharmony_ci } 2221987da915Sopenharmony_ci } 2222987da915Sopenharmony_ci 2223987da915Sopenharmony_ci /* It is a real lcn, write it to the volume. */ 2224987da915Sopenharmony_ci to_write = min(count, (rl->length << vol->cluster_size_bits) - ofs); 2225987da915Sopenharmony_ciretry: 2226987da915Sopenharmony_ci ntfs_log_trace("Writing %lld bytes to vcn %lld, lcn %lld, ofs " 2227987da915Sopenharmony_ci "%lld.\n", (long long)to_write, (long long)rl->vcn, 2228987da915Sopenharmony_ci (long long)rl->lcn, (long long)ofs); 2229987da915Sopenharmony_ci if (!NVolReadOnly(vol)) { 2230987da915Sopenharmony_ci 2231987da915Sopenharmony_ci s64 wpos = (rl->lcn << vol->cluster_size_bits) + ofs; 2232987da915Sopenharmony_ci s64 wend = (rl->vcn << vol->cluster_size_bits) + ofs + to_write; 2233987da915Sopenharmony_ci u32 bsize = vol->cluster_size; 2234987da915Sopenharmony_ci /* Byte size needed to zero fill a cluster */ 2235987da915Sopenharmony_ci s64 rounding = ((wend + bsize - 1) & ~(s64)(bsize - 1)) - wend; 2236987da915Sopenharmony_ci /** 2237987da915Sopenharmony_ci * Zero fill to cluster boundary if we're writing at the 2238987da915Sopenharmony_ci * end of the attribute or into an ex-sparse cluster. 2239987da915Sopenharmony_ci * This will cause the kernel not to seek and read disk 2240987da915Sopenharmony_ci * blocks during write(2) to fill the end of the buffer 2241987da915Sopenharmony_ci * which increases write speed by 2-10 fold typically. 2242987da915Sopenharmony_ci * 2243987da915Sopenharmony_ci * This is done even for compressed files, because 2244987da915Sopenharmony_ci * data is generally first written uncompressed. 2245987da915Sopenharmony_ci */ 2246987da915Sopenharmony_ci if (rounding && ((wend == na->initialized_size) || 2247987da915Sopenharmony_ci (wend < (hole_end << vol->cluster_size_bits)))){ 2248987da915Sopenharmony_ci 2249987da915Sopenharmony_ci char *cb; 2250987da915Sopenharmony_ci 2251987da915Sopenharmony_ci rounding += to_write; 2252987da915Sopenharmony_ci 2253987da915Sopenharmony_ci cb = ntfs_malloc(rounding); 2254987da915Sopenharmony_ci if (!cb) 2255987da915Sopenharmony_ci goto err_out; 2256987da915Sopenharmony_ci 2257987da915Sopenharmony_ci memcpy(cb, b, to_write); 2258987da915Sopenharmony_ci memset(cb + to_write, 0, rounding - to_write); 2259987da915Sopenharmony_ci 2260987da915Sopenharmony_ci if (compressed) { 2261987da915Sopenharmony_ci written = ntfs_compressed_pwrite(na, 2262987da915Sopenharmony_ci rl, wpos, ofs, to_write, 2263987da915Sopenharmony_ci rounding, cb, compressed_part, 2264987da915Sopenharmony_ci &update_from); 2265987da915Sopenharmony_ci } else { 2266987da915Sopenharmony_ci written = ntfs_pwrite(vol->dev, wpos, 2267987da915Sopenharmony_ci rounding, cb); 2268987da915Sopenharmony_ci if (written == rounding) 2269987da915Sopenharmony_ci written = to_write; 2270987da915Sopenharmony_ci } 2271987da915Sopenharmony_ci 2272987da915Sopenharmony_ci free(cb); 2273987da915Sopenharmony_ci } else { 2274987da915Sopenharmony_ci if (compressed) { 2275987da915Sopenharmony_ci written = ntfs_compressed_pwrite(na, 2276987da915Sopenharmony_ci rl, wpos, ofs, to_write, 2277987da915Sopenharmony_ci to_write, b, compressed_part, 2278987da915Sopenharmony_ci &update_from); 2279987da915Sopenharmony_ci } else 2280987da915Sopenharmony_ci written = ntfs_pwrite(vol->dev, wpos, 2281987da915Sopenharmony_ci to_write, b); 2282987da915Sopenharmony_ci } 2283987da915Sopenharmony_ci } else 2284987da915Sopenharmony_ci written = to_write; 2285987da915Sopenharmony_ci /* If everything ok, update progress counters and continue. */ 2286987da915Sopenharmony_ci if (written > 0) { 2287987da915Sopenharmony_ci total += written; 2288987da915Sopenharmony_ci count -= written; 2289987da915Sopenharmony_ci fullcount -= written; 2290987da915Sopenharmony_ci b = (const u8*)b + written; 2291987da915Sopenharmony_ci } 2292987da915Sopenharmony_ci if (written != to_write) { 2293987da915Sopenharmony_ci /* Partial write cannot be dealt with, stop there */ 2294987da915Sopenharmony_ci /* If the syscall was interrupted, try again. */ 2295987da915Sopenharmony_ci if (written == (s64)-1 && errno == EINTR) 2296987da915Sopenharmony_ci goto retry; 2297987da915Sopenharmony_ci if (!written) 2298987da915Sopenharmony_ci errno = EIO; 2299987da915Sopenharmony_ci goto rl_err_out; 2300987da915Sopenharmony_ci } 2301987da915Sopenharmony_ci compressed_part = 0; 2302987da915Sopenharmony_ci } 2303987da915Sopenharmony_cidone: 2304987da915Sopenharmony_ci if (ctx) 2305987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 2306987da915Sopenharmony_ci /* 2307987da915Sopenharmony_ci * Update mapping pairs if needed. 2308987da915Sopenharmony_ci * For a compressed file, we try to make a partial update 2309987da915Sopenharmony_ci * of the mapping list. This makes a difference only if 2310987da915Sopenharmony_ci * inode extents were needed. 2311987da915Sopenharmony_ci */ 2312987da915Sopenharmony_ci if (NAttrRunlistDirty(na)) { 2313987da915Sopenharmony_ci if (ntfs_attr_update_mapping_pairs(na, 2314987da915Sopenharmony_ci (update_from < 0 ? 0 : update_from))) { 2315987da915Sopenharmony_ci /* 2316987da915Sopenharmony_ci * FIXME: trying to recover by goto rl_err_out; 2317987da915Sopenharmony_ci * could cause driver hang by infinite looping. 2318987da915Sopenharmony_ci */ 2319987da915Sopenharmony_ci total = -1; 2320987da915Sopenharmony_ci goto out; 2321987da915Sopenharmony_ci } 2322987da915Sopenharmony_ci if (!wasnonresident) 2323987da915Sopenharmony_ci NAttrClearBeingNonResident(na); 2324987da915Sopenharmony_ci NAttrClearDataAppending(na); 2325987da915Sopenharmony_ci } 2326987da915Sopenharmony_ciout: 2327987da915Sopenharmony_ci return total; 2328987da915Sopenharmony_cirl_err_out: 2329987da915Sopenharmony_ci eo = errno; 2330987da915Sopenharmony_ci if (total) { 2331987da915Sopenharmony_ci if (need_to.undo_initialized_size) { 2332987da915Sopenharmony_ci if (pos + total > na->initialized_size) 2333987da915Sopenharmony_ci goto done; 2334987da915Sopenharmony_ci /* 2335987da915Sopenharmony_ci * TODO: Need to try to change initialized_size. If it 2336987da915Sopenharmony_ci * succeeds goto done, otherwise goto err_out. (AIA) 2337987da915Sopenharmony_ci */ 2338987da915Sopenharmony_ci goto err_out; 2339987da915Sopenharmony_ci } 2340987da915Sopenharmony_ci goto done; 2341987da915Sopenharmony_ci } 2342987da915Sopenharmony_ci errno = eo; 2343987da915Sopenharmony_cierr_out: 2344987da915Sopenharmony_ci eo = errno; 2345987da915Sopenharmony_ci if (need_to.undo_initialized_size) { 2346987da915Sopenharmony_ci int err; 2347987da915Sopenharmony_ci 2348987da915Sopenharmony_ci err = 0; 2349987da915Sopenharmony_ci if (!ctx) { 2350987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(na->ni, NULL); 2351987da915Sopenharmony_ci if (!ctx) 2352987da915Sopenharmony_ci err = 1; 2353987da915Sopenharmony_ci } else 2354987da915Sopenharmony_ci ntfs_attr_reinit_search_ctx(ctx); 2355987da915Sopenharmony_ci if (!err) { 2356987da915Sopenharmony_ci err = ntfs_attr_lookup(na->type, na->name, 2357987da915Sopenharmony_ci na->name_len, 0, 0, NULL, 0, ctx); 2358987da915Sopenharmony_ci if (!err) { 2359987da915Sopenharmony_ci na->initialized_size = old_initialized_size; 2360987da915Sopenharmony_ci ctx->attr->initialized_size = cpu_to_sle64( 2361987da915Sopenharmony_ci old_initialized_size); 2362987da915Sopenharmony_ci err = ntfs_mft_record_write(vol, 2363987da915Sopenharmony_ci ctx->ntfs_ino->mft_no, 2364987da915Sopenharmony_ci ctx->mrec); 2365987da915Sopenharmony_ci } 2366987da915Sopenharmony_ci } 2367987da915Sopenharmony_ci if (err) { 2368987da915Sopenharmony_ci /* 2369987da915Sopenharmony_ci * FIXME: At this stage could try to recover by filling 2370987da915Sopenharmony_ci * old_initialized_size -> new_initialized_size with 2371987da915Sopenharmony_ci * data or at least zeroes. (AIA) 2372987da915Sopenharmony_ci */ 2373987da915Sopenharmony_ci ntfs_log_error("Eeek! Failed to recover from error. " 2374987da915Sopenharmony_ci "Leaving metadata in inconsistent " 2375987da915Sopenharmony_ci "state! Run chkdsk!\n"); 2376987da915Sopenharmony_ci } 2377987da915Sopenharmony_ci } 2378987da915Sopenharmony_ci if (ctx) 2379987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 2380987da915Sopenharmony_ci /* Update mapping pairs if needed. */ 2381987da915Sopenharmony_ci if (NAttrRunlistDirty(na)) 2382987da915Sopenharmony_ci ntfs_attr_update_mapping_pairs(na, 0); 2383987da915Sopenharmony_ci /* Restore original data_size if needed. */ 2384987da915Sopenharmony_ci if (need_to.undo_data_size 2385987da915Sopenharmony_ci && ntfs_attr_truncate_i(na, old_data_size, HOLES_OK)) 2386987da915Sopenharmony_ci ntfs_log_perror("Failed to restore data_size"); 2387987da915Sopenharmony_ci errno = eo; 2388987da915Sopenharmony_cierrno_set: 2389987da915Sopenharmony_ci total = -1; 2390987da915Sopenharmony_ci goto out; 2391987da915Sopenharmony_ci} 2392987da915Sopenharmony_ci 2393987da915Sopenharmony_cis64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) 2394987da915Sopenharmony_ci{ 2395987da915Sopenharmony_ci s64 total; 2396987da915Sopenharmony_ci s64 written; 2397987da915Sopenharmony_ci 2398987da915Sopenharmony_ci ntfs_log_enter("Entering for inode %lld, attr 0x%x, pos 0x%llx, count " 2399987da915Sopenharmony_ci "0x%llx.\n", (long long)na->ni->mft_no, le32_to_cpu(na->type), 2400987da915Sopenharmony_ci (long long)pos, (long long)count); 2401987da915Sopenharmony_ci 2402987da915Sopenharmony_ci total = 0; 2403987da915Sopenharmony_ci if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { 2404987da915Sopenharmony_ci errno = EINVAL; 2405987da915Sopenharmony_ci written = -1; 2406987da915Sopenharmony_ci ntfs_log_perror("%s", __FUNCTION__); 2407987da915Sopenharmony_ci goto out; 2408987da915Sopenharmony_ci } 2409987da915Sopenharmony_ci 2410987da915Sopenharmony_ci /* 2411987da915Sopenharmony_ci * Compressed attributes may be written partially, so 2412987da915Sopenharmony_ci * we may have to iterate. 2413987da915Sopenharmony_ci */ 2414987da915Sopenharmony_ci do { 2415987da915Sopenharmony_ci written = ntfs_attr_pwrite_i(na, pos + total, 2416987da915Sopenharmony_ci count - total, (const u8*)b + total); 2417987da915Sopenharmony_ci if (written > 0) 2418987da915Sopenharmony_ci total += written; 2419987da915Sopenharmony_ci } while ((written > 0) && (total < count)); 2420987da915Sopenharmony_ciout : 2421987da915Sopenharmony_ci ntfs_log_leave("\n"); 2422987da915Sopenharmony_ci return (total > 0 ? total : written); 2423987da915Sopenharmony_ci} 2424987da915Sopenharmony_ci 2425987da915Sopenharmony_ci 2426987da915Sopenharmony_ciint ntfs_attr_pclose(ntfs_attr *na) 2427987da915Sopenharmony_ci{ 2428987da915Sopenharmony_ci s64 ofs; 2429987da915Sopenharmony_ci int failed; 2430987da915Sopenharmony_ci BOOL ok = TRUE; 2431987da915Sopenharmony_ci VCN update_from = -1; 2432987da915Sopenharmony_ci ntfs_volume *vol; 2433987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx = NULL; 2434987da915Sopenharmony_ci runlist_element *rl; 2435987da915Sopenharmony_ci int eo; 2436987da915Sopenharmony_ci int compressed_part; 2437987da915Sopenharmony_ci BOOL compressed; 2438987da915Sopenharmony_ci 2439987da915Sopenharmony_ci ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x.\n", 2440987da915Sopenharmony_ci (unsigned long long)na->ni->mft_no, 2441987da915Sopenharmony_ci le32_to_cpu(na->type)); 2442987da915Sopenharmony_ci 2443987da915Sopenharmony_ci if (!na || !na->ni || !na->ni->vol) { 2444987da915Sopenharmony_ci errno = EINVAL; 2445987da915Sopenharmony_ci ntfs_log_perror("%s", __FUNCTION__); 2446987da915Sopenharmony_ci goto errno_set; 2447987da915Sopenharmony_ci } 2448987da915Sopenharmony_ci vol = na->ni->vol; 2449987da915Sopenharmony_ci na->unused_runs = 0; 2450987da915Sopenharmony_ci compressed = (na->data_flags & ATTR_COMPRESSION_MASK) 2451987da915Sopenharmony_ci != const_cpu_to_le16(0); 2452987da915Sopenharmony_ci /* 2453987da915Sopenharmony_ci * Encrypted non-resident attributes are not supported. We return 2454987da915Sopenharmony_ci * access denied, which is what Windows NT4 does, too. 2455987da915Sopenharmony_ci */ 2456987da915Sopenharmony_ci if (NAttrEncrypted(na) && NAttrNonResident(na)) { 2457987da915Sopenharmony_ci errno = EACCES; 2458987da915Sopenharmony_ci goto errno_set; 2459987da915Sopenharmony_ci } 2460987da915Sopenharmony_ci /* If this is not a compressed attribute get out */ 2461987da915Sopenharmony_ci /* same if it is resident */ 2462987da915Sopenharmony_ci if (!compressed || !NAttrNonResident(na)) 2463987da915Sopenharmony_ci goto out; 2464987da915Sopenharmony_ci 2465987da915Sopenharmony_ci /* safety check : no recursion on close */ 2466987da915Sopenharmony_ci if (NAttrComprClosing(na)) { 2467987da915Sopenharmony_ci errno = EIO; 2468987da915Sopenharmony_ci ntfs_log_error("Bad ntfs_attr_pclose" 2469987da915Sopenharmony_ci " recursion on inode %lld\n", 2470987da915Sopenharmony_ci (long long)na->ni->mft_no); 2471987da915Sopenharmony_ci goto out; 2472987da915Sopenharmony_ci } 2473987da915Sopenharmony_ci NAttrSetComprClosing(na); 2474987da915Sopenharmony_ci /* 2475987da915Sopenharmony_ci * For a compressed attribute, we must be sure there are two 2476987da915Sopenharmony_ci * available entries, so reserve them before it gets too late. 2477987da915Sopenharmony_ci */ 2478987da915Sopenharmony_ci if (ntfs_attr_map_whole_runlist(na)) 2479987da915Sopenharmony_ci goto err_out; 2480987da915Sopenharmony_ci na->rl = ntfs_rl_extend(na,na->rl,2); 2481987da915Sopenharmony_ci if (!na->rl) 2482987da915Sopenharmony_ci goto err_out; 2483987da915Sopenharmony_ci na->unused_runs = 2; 2484987da915Sopenharmony_ci /* Find the runlist element containing the terminal vcn. */ 2485987da915Sopenharmony_ci rl = ntfs_attr_find_vcn(na, (na->initialized_size - 1) >> vol->cluster_size_bits); 2486987da915Sopenharmony_ci if (!rl) { 2487987da915Sopenharmony_ci /* 2488987da915Sopenharmony_ci * If the vcn is not present it is an out of bounds write. 2489987da915Sopenharmony_ci * However, we have already written the last byte uncompressed, 2490987da915Sopenharmony_ci * so getting this here must be an error of some kind. 2491987da915Sopenharmony_ci */ 2492987da915Sopenharmony_ci if (errno == ENOENT) { 2493987da915Sopenharmony_ci errno = EIO; 2494987da915Sopenharmony_ci ntfs_log_perror("%s: Failed to find VCN #5", __FUNCTION__); 2495987da915Sopenharmony_ci } 2496987da915Sopenharmony_ci goto err_out; 2497987da915Sopenharmony_ci } 2498987da915Sopenharmony_ci /* 2499987da915Sopenharmony_ci * Scatter the data from the linear data buffer to the volume. Note, a 2500987da915Sopenharmony_ci * partial final vcn is taken care of by the @count capping of write 2501987da915Sopenharmony_ci * length. 2502987da915Sopenharmony_ci */ 2503987da915Sopenharmony_ci compressed_part = 0; 2504987da915Sopenharmony_ci if (rl->lcn >= 0) { 2505987da915Sopenharmony_ci runlist_element *xrl; 2506987da915Sopenharmony_ci 2507987da915Sopenharmony_ci xrl = rl; 2508987da915Sopenharmony_ci do { 2509987da915Sopenharmony_ci xrl++; 2510987da915Sopenharmony_ci } while (xrl->lcn >= 0); 2511987da915Sopenharmony_ci compressed_part = (-xrl->length) 2512987da915Sopenharmony_ci & (na->compression_block_clusters - 1); 2513987da915Sopenharmony_ci } else 2514987da915Sopenharmony_ci if (rl->lcn == (LCN)LCN_HOLE) { 2515987da915Sopenharmony_ci if (rl->length < na->compression_block_clusters) 2516987da915Sopenharmony_ci compressed_part 2517987da915Sopenharmony_ci = na->compression_block_clusters 2518987da915Sopenharmony_ci - rl->length; 2519987da915Sopenharmony_ci else 2520987da915Sopenharmony_ci compressed_part 2521987da915Sopenharmony_ci = na->compression_block_clusters; 2522987da915Sopenharmony_ci } 2523987da915Sopenharmony_ci /* done, if the last block set was compressed */ 2524987da915Sopenharmony_ci if (compressed_part) 2525987da915Sopenharmony_ci goto out; 2526987da915Sopenharmony_ci 2527987da915Sopenharmony_ci ofs = na->initialized_size - (rl->vcn << vol->cluster_size_bits); 2528987da915Sopenharmony_ci 2529987da915Sopenharmony_ci if (rl->lcn == LCN_RL_NOT_MAPPED) { 2530987da915Sopenharmony_ci rl = ntfs_attr_find_vcn(na, rl->vcn); 2531987da915Sopenharmony_ci if (!rl) { 2532987da915Sopenharmony_ci if (errno == ENOENT) { 2533987da915Sopenharmony_ci errno = EIO; 2534987da915Sopenharmony_ci ntfs_log_perror("%s: Failed to find VCN" 2535987da915Sopenharmony_ci " #6", __FUNCTION__); 2536987da915Sopenharmony_ci } 2537987da915Sopenharmony_ci goto rl_err_out; 2538987da915Sopenharmony_ci } 2539987da915Sopenharmony_ci /* Needed for case when runs merged. */ 2540987da915Sopenharmony_ci ofs = na->initialized_size - (rl->vcn << vol->cluster_size_bits); 2541987da915Sopenharmony_ci } 2542987da915Sopenharmony_ci if (!rl->length) { 2543987da915Sopenharmony_ci errno = EIO; 2544987da915Sopenharmony_ci ntfs_log_perror("%s: Zero run length", __FUNCTION__); 2545987da915Sopenharmony_ci goto rl_err_out; 2546987da915Sopenharmony_ci } 2547987da915Sopenharmony_ci if (rl->lcn < (LCN)0) { 2548987da915Sopenharmony_ci if (rl->lcn != (LCN)LCN_HOLE) { 2549987da915Sopenharmony_ci errno = EIO; 2550987da915Sopenharmony_ci ntfs_log_perror("%s: Unexpected LCN (%lld)", 2551987da915Sopenharmony_ci __FUNCTION__, 2552987da915Sopenharmony_ci (long long)rl->lcn); 2553987da915Sopenharmony_ci goto rl_err_out; 2554987da915Sopenharmony_ci } 2555987da915Sopenharmony_ci 2556987da915Sopenharmony_ci if (ntfs_attr_fill_hole(na, (s64)0, &ofs, &rl, &update_from)) 2557987da915Sopenharmony_ci goto err_out; 2558987da915Sopenharmony_ci } 2559987da915Sopenharmony_ci while (rl->length 2560987da915Sopenharmony_ci && (ofs >= (rl->length << vol->cluster_size_bits))) { 2561987da915Sopenharmony_ci ofs -= rl->length << vol->cluster_size_bits; 2562987da915Sopenharmony_ci rl++; 2563987da915Sopenharmony_ci } 2564987da915Sopenharmony_ci 2565987da915Sopenharmony_ciretry: 2566987da915Sopenharmony_ci failed = 0; 2567987da915Sopenharmony_ci if (update_from < 0) update_from = 0; 2568987da915Sopenharmony_ci if (!NVolReadOnly(vol)) { 2569987da915Sopenharmony_ci failed = ntfs_compressed_close(na, rl, ofs, &update_from); 2570987da915Sopenharmony_ci#if CACHE_NIDATA_SIZE 2571987da915Sopenharmony_ci if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY 2572987da915Sopenharmony_ci ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 2573987da915Sopenharmony_ci : na->type == AT_DATA && na->name == AT_UNNAMED) { 2574987da915Sopenharmony_ci na->ni->data_size = na->data_size; 2575987da915Sopenharmony_ci na->ni->allocated_size = na->compressed_size; 2576987da915Sopenharmony_ci set_nino_flag(na->ni,KnownSize); 2577987da915Sopenharmony_ci } 2578987da915Sopenharmony_ci#endif 2579987da915Sopenharmony_ci } 2580987da915Sopenharmony_ci if (failed) { 2581987da915Sopenharmony_ci /* If the syscall was interrupted, try again. */ 2582987da915Sopenharmony_ci if (errno == EINTR) 2583987da915Sopenharmony_ci goto retry; 2584987da915Sopenharmony_ci else 2585987da915Sopenharmony_ci goto rl_err_out; 2586987da915Sopenharmony_ci } 2587987da915Sopenharmony_ci if (ctx) 2588987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 2589987da915Sopenharmony_ci /* Update mapping pairs if needed. */ 2590987da915Sopenharmony_ci if (NAttrFullyMapped(na)) 2591987da915Sopenharmony_ci if (ntfs_attr_update_mapping_pairs(na, update_from)) { 2592987da915Sopenharmony_ci /* 2593987da915Sopenharmony_ci * FIXME: trying to recover by goto rl_err_out; 2594987da915Sopenharmony_ci * could cause driver hang by infinite looping. 2595987da915Sopenharmony_ci */ 2596987da915Sopenharmony_ci ok = FALSE; 2597987da915Sopenharmony_ci goto out; 2598987da915Sopenharmony_ci } 2599987da915Sopenharmony_ciout: 2600987da915Sopenharmony_ci NAttrClearComprClosing(na); 2601987da915Sopenharmony_ci ntfs_log_leave("\n"); 2602987da915Sopenharmony_ci return (!ok); 2603987da915Sopenharmony_cirl_err_out: 2604987da915Sopenharmony_ci /* 2605987da915Sopenharmony_ci * need not restore old sizes, only compressed_size 2606987da915Sopenharmony_ci * can have changed. It has been set according to 2607987da915Sopenharmony_ci * the current runlist while updating the mapping pairs, 2608987da915Sopenharmony_ci * and must be kept consistent with the runlists. 2609987da915Sopenharmony_ci */ 2610987da915Sopenharmony_cierr_out: 2611987da915Sopenharmony_ci eo = errno; 2612987da915Sopenharmony_ci if (ctx) 2613987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 2614987da915Sopenharmony_ci /* Update mapping pairs if needed. */ 2615987da915Sopenharmony_ci if (NAttrFullyMapped(na)) 2616987da915Sopenharmony_ci ntfs_attr_update_mapping_pairs(na, 0); 2617987da915Sopenharmony_ci errno = eo; 2618987da915Sopenharmony_cierrno_set: 2619987da915Sopenharmony_ci ok = FALSE; 2620987da915Sopenharmony_ci goto out; 2621987da915Sopenharmony_ci} 2622987da915Sopenharmony_ci 2623987da915Sopenharmony_ci/** 2624987da915Sopenharmony_ci * ntfs_attr_mst_pread - multi sector transfer protected ntfs attribute read 2625987da915Sopenharmony_ci * @na: multi sector transfer protected ntfs attribute to read from 2626987da915Sopenharmony_ci * @pos: byte position in the attribute to begin reading from 2627987da915Sopenharmony_ci * @bk_cnt: number of mst protected blocks to read 2628987da915Sopenharmony_ci * @bk_size: size of each mst protected block in bytes 2629987da915Sopenharmony_ci * @dst: output data buffer 2630987da915Sopenharmony_ci * 2631987da915Sopenharmony_ci * This function will read @bk_cnt blocks of size @bk_size bytes each starting 2632987da915Sopenharmony_ci * at offset @pos from the ntfs attribute @na into the data buffer @b. 2633987da915Sopenharmony_ci * 2634987da915Sopenharmony_ci * On success, the multi sector transfer fixups are applied and the number of 2635987da915Sopenharmony_ci * read blocks is returned. If this number is lower than @bk_cnt this means 2636987da915Sopenharmony_ci * that the read has either reached end of attribute or that an error was 2637987da915Sopenharmony_ci * encountered during the read so that the read is partial. 0 means end of 2638987da915Sopenharmony_ci * attribute or nothing to read (also return 0 when @bk_cnt or @bk_size are 0). 2639987da915Sopenharmony_ci * 2640987da915Sopenharmony_ci * On error and nothing has been read, return -1 with errno set appropriately 2641987da915Sopenharmony_ci * to the return code of ntfs_attr_pread() or to EINVAL in case of invalid 2642987da915Sopenharmony_ci * arguments. 2643987da915Sopenharmony_ci * 2644987da915Sopenharmony_ci * NOTE: If an incomplete multi sector transfer is detected the magic is 2645987da915Sopenharmony_ci * changed to BAAD but no error is returned, i.e. it is possible that any of 2646987da915Sopenharmony_ci * the returned blocks have multi sector transfer errors. This should be 2647987da915Sopenharmony_ci * detected by the caller by checking each block with is_baad_recordp(&block). 2648987da915Sopenharmony_ci * The reasoning is that we want to fixup as many blocks as possible and we 2649987da915Sopenharmony_ci * want to return even bad ones to the caller so, e.g. in case of ntfsck, the 2650987da915Sopenharmony_ci * errors can be repaired. 2651987da915Sopenharmony_ci */ 2652987da915Sopenharmony_cis64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, 2653987da915Sopenharmony_ci const u32 bk_size, void *dst) 2654987da915Sopenharmony_ci{ 2655987da915Sopenharmony_ci s64 br; 2656987da915Sopenharmony_ci u8 *end; 2657987da915Sopenharmony_ci BOOL warn; 2658987da915Sopenharmony_ci 2659987da915Sopenharmony_ci ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n", 2660987da915Sopenharmony_ci (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type), 2661987da915Sopenharmony_ci (long long)pos); 2662987da915Sopenharmony_ci if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { 2663987da915Sopenharmony_ci errno = EINVAL; 2664987da915Sopenharmony_ci ntfs_log_perror("%s", __FUNCTION__); 2665987da915Sopenharmony_ci return -1; 2666987da915Sopenharmony_ci } 2667987da915Sopenharmony_ci br = ntfs_attr_pread(na, pos, bk_cnt * bk_size, dst); 2668987da915Sopenharmony_ci if (br <= 0) 2669987da915Sopenharmony_ci return br; 2670987da915Sopenharmony_ci br /= bk_size; 2671987da915Sopenharmony_ci /* log errors unless silenced */ 2672987da915Sopenharmony_ci warn = !na->ni || !na->ni->vol || !NVolNoFixupWarn(na->ni->vol); 2673987da915Sopenharmony_ci for (end = (u8*)dst + br * bk_size; (u8*)dst < end; dst = (u8*)dst + 2674987da915Sopenharmony_ci bk_size) 2675987da915Sopenharmony_ci ntfs_mst_post_read_fixup_warn((NTFS_RECORD*)dst, bk_size, warn); 2676987da915Sopenharmony_ci /* Finally, return the number of blocks read. */ 2677987da915Sopenharmony_ci return br; 2678987da915Sopenharmony_ci} 2679987da915Sopenharmony_ci 2680987da915Sopenharmony_ci/** 2681987da915Sopenharmony_ci * ntfs_attr_mst_pwrite - multi sector transfer protected ntfs attribute write 2682987da915Sopenharmony_ci * @na: multi sector transfer protected ntfs attribute to write to 2683987da915Sopenharmony_ci * @pos: position in the attribute to write to 2684987da915Sopenharmony_ci * @bk_cnt: number of mst protected blocks to write 2685987da915Sopenharmony_ci * @bk_size: size of each mst protected block in bytes 2686987da915Sopenharmony_ci * @src: data buffer to write to disk 2687987da915Sopenharmony_ci * 2688987da915Sopenharmony_ci * This function will write @bk_cnt blocks of size @bk_size bytes each from 2689987da915Sopenharmony_ci * data buffer @b to multi sector transfer (mst) protected ntfs attribute @na 2690987da915Sopenharmony_ci * at position @pos. 2691987da915Sopenharmony_ci * 2692987da915Sopenharmony_ci * On success, return the number of successfully written blocks. If this number 2693987da915Sopenharmony_ci * is lower than @bk_cnt this means that an error was encountered during the 2694987da915Sopenharmony_ci * write so that the write is partial. 0 means nothing was written (also 2695987da915Sopenharmony_ci * return 0 when @bk_cnt or @bk_size are 0). 2696987da915Sopenharmony_ci * 2697987da915Sopenharmony_ci * On error and nothing has been written, return -1 with errno set 2698987da915Sopenharmony_ci * appropriately to the return code of ntfs_attr_pwrite(), or to EINVAL in case 2699987da915Sopenharmony_ci * of invalid arguments. 2700987da915Sopenharmony_ci * 2701987da915Sopenharmony_ci * NOTE: We mst protect the data, write it, then mst deprotect it using a quick 2702987da915Sopenharmony_ci * deprotect algorithm (no checking). This saves us from making a copy before 2703987da915Sopenharmony_ci * the write and at the same time causes the usn to be incremented in the 2704987da915Sopenharmony_ci * buffer. This conceptually fits in better with the idea that cached data is 2705987da915Sopenharmony_ci * always deprotected and protection is performed when the data is actually 2706987da915Sopenharmony_ci * going to hit the disk and the cache is immediately deprotected again 2707987da915Sopenharmony_ci * simulating an mst read on the written data. This way cache coherency is 2708987da915Sopenharmony_ci * achieved. 2709987da915Sopenharmony_ci */ 2710987da915Sopenharmony_cis64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, s64 bk_cnt, 2711987da915Sopenharmony_ci const u32 bk_size, void *src) 2712987da915Sopenharmony_ci{ 2713987da915Sopenharmony_ci s64 written, i; 2714987da915Sopenharmony_ci 2715987da915Sopenharmony_ci ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n", 2716987da915Sopenharmony_ci (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type), 2717987da915Sopenharmony_ci (long long)pos); 2718987da915Sopenharmony_ci if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { 2719987da915Sopenharmony_ci errno = EINVAL; 2720987da915Sopenharmony_ci return -1; 2721987da915Sopenharmony_ci } 2722987da915Sopenharmony_ci if (!bk_cnt) 2723987da915Sopenharmony_ci return 0; 2724987da915Sopenharmony_ci /* Prepare data for writing. */ 2725987da915Sopenharmony_ci for (i = 0; i < bk_cnt; ++i) { 2726987da915Sopenharmony_ci int err; 2727987da915Sopenharmony_ci 2728987da915Sopenharmony_ci err = ntfs_mst_pre_write_fixup((NTFS_RECORD*) 2729987da915Sopenharmony_ci ((u8*)src + i * bk_size), bk_size); 2730987da915Sopenharmony_ci if (err < 0) { 2731987da915Sopenharmony_ci /* Abort write at this position. */ 2732987da915Sopenharmony_ci ntfs_log_perror("%s #1", __FUNCTION__); 2733987da915Sopenharmony_ci if (!i) 2734987da915Sopenharmony_ci return err; 2735987da915Sopenharmony_ci bk_cnt = i; 2736987da915Sopenharmony_ci break; 2737987da915Sopenharmony_ci } 2738987da915Sopenharmony_ci } 2739987da915Sopenharmony_ci /* Write the prepared data. */ 2740987da915Sopenharmony_ci written = ntfs_attr_pwrite(na, pos, bk_cnt * bk_size, src); 2741987da915Sopenharmony_ci if (written <= 0) { 2742987da915Sopenharmony_ci ntfs_log_perror("%s: written=%lld", __FUNCTION__, 2743987da915Sopenharmony_ci (long long)written); 2744987da915Sopenharmony_ci } 2745987da915Sopenharmony_ci /* Quickly deprotect the data again. */ 2746987da915Sopenharmony_ci for (i = 0; i < bk_cnt; ++i) 2747987da915Sopenharmony_ci ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)src + i * 2748987da915Sopenharmony_ci bk_size)); 2749987da915Sopenharmony_ci if (written <= 0) 2750987da915Sopenharmony_ci return written; 2751987da915Sopenharmony_ci /* Finally, return the number of complete blocks written. */ 2752987da915Sopenharmony_ci return written / bk_size; 2753987da915Sopenharmony_ci} 2754987da915Sopenharmony_ci 2755987da915Sopenharmony_ci/** 2756987da915Sopenharmony_ci * ntfs_attr_find - find (next) attribute in mft record 2757987da915Sopenharmony_ci * @type: attribute type to find 2758987da915Sopenharmony_ci * @name: attribute name to find (optional, i.e. NULL means don't care) 2759987da915Sopenharmony_ci * @name_len: attribute name length (only needed if @name present) 2760987da915Sopenharmony_ci * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) 2761987da915Sopenharmony_ci * @val: attribute value to find (optional, resident attributes only) 2762987da915Sopenharmony_ci * @val_len: attribute value length 2763987da915Sopenharmony_ci * @ctx: search context with mft record and attribute to search from 2764987da915Sopenharmony_ci * 2765987da915Sopenharmony_ci * You shouldn't need to call this function directly. Use lookup_attr() instead. 2766987da915Sopenharmony_ci * 2767987da915Sopenharmony_ci * ntfs_attr_find() takes a search context @ctx as parameter and searches the 2768987da915Sopenharmony_ci * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an 2769987da915Sopenharmony_ci * attribute of @type, optionally @name and @val. If found, ntfs_attr_find() 2770987da915Sopenharmony_ci * returns 0 and @ctx->attr will point to the found attribute. 2771987da915Sopenharmony_ci * 2772987da915Sopenharmony_ci * If not found, ntfs_attr_find() returns -1, with errno set to ENOENT and 2773987da915Sopenharmony_ci * @ctx->attr will point to the attribute before which the attribute being 2774987da915Sopenharmony_ci * searched for would need to be inserted if such an action were to be desired. 2775987da915Sopenharmony_ci * 2776987da915Sopenharmony_ci * On actual error, ntfs_attr_find() returns -1 with errno set to the error 2777987da915Sopenharmony_ci * code but not to ENOENT. In this case @ctx->attr is undefined and in 2778987da915Sopenharmony_ci * particular do not rely on it not changing. 2779987da915Sopenharmony_ci * 2780987da915Sopenharmony_ci * If @ctx->is_first is TRUE, the search begins with @ctx->attr itself. If it 2781987da915Sopenharmony_ci * is FALSE, the search begins after @ctx->attr. 2782987da915Sopenharmony_ci * 2783987da915Sopenharmony_ci * If @type is AT_UNUSED, return the first found attribute, i.e. one can 2784987da915Sopenharmony_ci * enumerate all attributes by setting @type to AT_UNUSED and then calling 2785987da915Sopenharmony_ci * ntfs_attr_find() repeatedly until it returns -1 with errno set to ENOENT to 2786987da915Sopenharmony_ci * indicate that there are no more entries. During the enumeration, each 2787987da915Sopenharmony_ci * successful call of ntfs_attr_find() will return the next attribute in the 2788987da915Sopenharmony_ci * mft record @ctx->mrec. 2789987da915Sopenharmony_ci * 2790987da915Sopenharmony_ci * If @type is AT_END, seek to the end and return -1 with errno set to ENOENT. 2791987da915Sopenharmony_ci * AT_END is not a valid attribute, its length is zero for example, thus it is 2792987da915Sopenharmony_ci * safer to return error instead of success in this case. This also allows us 2793987da915Sopenharmony_ci * to interoperate cleanly with ntfs_external_attr_find(). 2794987da915Sopenharmony_ci * 2795987da915Sopenharmony_ci * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present 2796987da915Sopenharmony_ci * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, 2797987da915Sopenharmony_ci * match both named and unnamed attributes. 2798987da915Sopenharmony_ci * 2799987da915Sopenharmony_ci * If @ic is IGNORE_CASE, the @name comparison is not case sensitive and 2800987da915Sopenharmony_ci * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record 2801987da915Sopenharmony_ci * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at 2802987da915Sopenharmony_ci * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case 2803987da915Sopenharmony_ci * sensitive. When @name is present, @name_len is the @name length in Unicode 2804987da915Sopenharmony_ci * characters. 2805987da915Sopenharmony_ci * 2806987da915Sopenharmony_ci * If @name is not present (NULL), we assume that the unnamed attribute is 2807987da915Sopenharmony_ci * being searched for. 2808987da915Sopenharmony_ci * 2809987da915Sopenharmony_ci * Finally, the resident attribute value @val is looked for, if present. 2810987da915Sopenharmony_ci * If @val is not present (NULL), @val_len is ignored. 2811987da915Sopenharmony_ci * 2812987da915Sopenharmony_ci * ntfs_attr_find() only searches the specified mft record and it ignores the 2813987da915Sopenharmony_ci * presence of an attribute list attribute (unless it is the one being searched 2814987da915Sopenharmony_ci * for, obviously). If you need to take attribute lists into consideration, use 2815987da915Sopenharmony_ci * ntfs_attr_lookup() instead (see below). This also means that you cannot use 2816987da915Sopenharmony_ci * ntfs_attr_find() to search for extent records of non-resident attributes, as 2817987da915Sopenharmony_ci * extents with lowest_vcn != 0 are usually described by the attribute list 2818987da915Sopenharmony_ci * attribute only. - Note that it is possible that the first extent is only in 2819987da915Sopenharmony_ci * the attribute list while the last extent is in the base mft record, so don't 2820987da915Sopenharmony_ci * rely on being able to find the first extent in the base mft record. 2821987da915Sopenharmony_ci * 2822987da915Sopenharmony_ci * Warning: Never use @val when looking for attribute types which can be 2823987da915Sopenharmony_ci * non-resident as this most likely will result in a crash! 2824987da915Sopenharmony_ci */ 2825987da915Sopenharmony_cistatic int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, 2826987da915Sopenharmony_ci const u32 name_len, const IGNORE_CASE_BOOL ic, 2827987da915Sopenharmony_ci const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx) 2828987da915Sopenharmony_ci{ 2829987da915Sopenharmony_ci ATTR_RECORD *a; 2830987da915Sopenharmony_ci ntfs_volume *vol; 2831987da915Sopenharmony_ci ntfschar *upcase; 2832987da915Sopenharmony_ci ptrdiff_t offs; 2833987da915Sopenharmony_ci ptrdiff_t space; 2834987da915Sopenharmony_ci u32 upcase_len; 2835987da915Sopenharmony_ci 2836987da915Sopenharmony_ci ntfs_log_trace("attribute type 0x%x.\n", le32_to_cpu(type)); 2837987da915Sopenharmony_ci 2838987da915Sopenharmony_ci if (ctx->ntfs_ino) { 2839987da915Sopenharmony_ci vol = ctx->ntfs_ino->vol; 2840987da915Sopenharmony_ci upcase = vol->upcase; 2841987da915Sopenharmony_ci upcase_len = vol->upcase_len; 2842987da915Sopenharmony_ci } else { 2843987da915Sopenharmony_ci if (name && name != AT_UNNAMED) { 2844987da915Sopenharmony_ci errno = EINVAL; 2845987da915Sopenharmony_ci ntfs_log_perror("%s", __FUNCTION__); 2846987da915Sopenharmony_ci return -1; 2847987da915Sopenharmony_ci } 2848987da915Sopenharmony_ci vol = NULL; 2849987da915Sopenharmony_ci upcase = NULL; 2850987da915Sopenharmony_ci upcase_len = 0; 2851987da915Sopenharmony_ci } 2852987da915Sopenharmony_ci /* 2853987da915Sopenharmony_ci * Iterate over attributes in mft record starting at @ctx->attr, or the 2854987da915Sopenharmony_ci * attribute following that, if @ctx->is_first is TRUE. 2855987da915Sopenharmony_ci */ 2856987da915Sopenharmony_ci if (ctx->is_first) { 2857987da915Sopenharmony_ci a = ctx->attr; 2858987da915Sopenharmony_ci ctx->is_first = FALSE; 2859987da915Sopenharmony_ci } else 2860987da915Sopenharmony_ci a = (ATTR_RECORD*)((char*)ctx->attr + 2861987da915Sopenharmony_ci le32_to_cpu(ctx->attr->length)); 2862987da915Sopenharmony_ci for (;; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) { 2863987da915Sopenharmony_ci /* 2864987da915Sopenharmony_ci * Make sure the attribute fully lies within the MFT record 2865987da915Sopenharmony_ci * and we can safely access its minimal fields. 2866987da915Sopenharmony_ci */ 2867987da915Sopenharmony_ci offs = p2n(a) - p2n(ctx->mrec); 2868987da915Sopenharmony_ci space = le32_to_cpu(ctx->mrec->bytes_in_use) - offs; 2869987da915Sopenharmony_ci if ((offs < 0) 2870987da915Sopenharmony_ci || (((space < (ptrdiff_t)offsetof(ATTR_RECORD, 2871987da915Sopenharmony_ci resident_end)) 2872987da915Sopenharmony_ci || (space < (ptrdiff_t)le32_to_cpu(a->length))) 2873987da915Sopenharmony_ci && ((space < 4) || (a->type != AT_END)))) 2874987da915Sopenharmony_ci break; 2875987da915Sopenharmony_ci ctx->attr = a; 2876987da915Sopenharmony_ci if (((type != AT_UNUSED) && (le32_to_cpu(a->type) > 2877987da915Sopenharmony_ci le32_to_cpu(type))) || 2878987da915Sopenharmony_ci (a->type == AT_END)) { 2879987da915Sopenharmony_ci errno = ENOENT; 2880987da915Sopenharmony_ci return -1; 2881987da915Sopenharmony_ci } 2882987da915Sopenharmony_ci if (!a->length) 2883987da915Sopenharmony_ci break; 2884987da915Sopenharmony_ci /* If this is an enumeration return this attribute. */ 2885987da915Sopenharmony_ci if (type == AT_UNUSED) 2886987da915Sopenharmony_ci return 0; 2887987da915Sopenharmony_ci if (a->type != type) 2888987da915Sopenharmony_ci continue; 2889987da915Sopenharmony_ci /* 2890987da915Sopenharmony_ci * If @name is AT_UNNAMED we want an unnamed attribute. 2891987da915Sopenharmony_ci * If @name is present, compare the two names. 2892987da915Sopenharmony_ci * Otherwise, match any attribute. 2893987da915Sopenharmony_ci */ 2894987da915Sopenharmony_ci if (name == AT_UNNAMED) { 2895987da915Sopenharmony_ci /* The search failed if the found attribute is named. */ 2896987da915Sopenharmony_ci if (a->name_length) { 2897987da915Sopenharmony_ci errno = ENOENT; 2898987da915Sopenharmony_ci return -1; 2899987da915Sopenharmony_ci } 2900987da915Sopenharmony_ci } else { 2901987da915Sopenharmony_ci register int rc; 2902987da915Sopenharmony_ci 2903987da915Sopenharmony_ci if (a->name_length 2904987da915Sopenharmony_ci && ((le16_to_cpu(a->name_offset) 2905987da915Sopenharmony_ci + a->name_length * sizeof(ntfschar)) 2906987da915Sopenharmony_ci > le32_to_cpu(a->length))) { 2907987da915Sopenharmony_ci ntfs_log_error("Corrupt attribute name" 2908987da915Sopenharmony_ci " in MFT record %lld\n", 2909987da915Sopenharmony_ci (long long)ctx->ntfs_ino->mft_no); 2910987da915Sopenharmony_ci break; 2911987da915Sopenharmony_ci } 2912987da915Sopenharmony_ci if (name && ((rc = ntfs_names_full_collate(name, 2913987da915Sopenharmony_ci name_len, (ntfschar*)((char*)a + 2914987da915Sopenharmony_ci le16_to_cpu(a->name_offset)), 2915987da915Sopenharmony_ci a->name_length, ic, 2916987da915Sopenharmony_ci upcase, upcase_len)))) { 2917987da915Sopenharmony_ci /* 2918987da915Sopenharmony_ci * If @name collates before a->name, 2919987da915Sopenharmony_ci * there is no matching attribute. 2920987da915Sopenharmony_ci */ 2921987da915Sopenharmony_ci if (rc < 0) { 2922987da915Sopenharmony_ci errno = ENOENT; 2923987da915Sopenharmony_ci return -1; 2924987da915Sopenharmony_ci } 2925987da915Sopenharmony_ci /* If the strings are not equal, continue search. */ 2926987da915Sopenharmony_ci continue; 2927987da915Sopenharmony_ci } 2928987da915Sopenharmony_ci } 2929987da915Sopenharmony_ci /* 2930987da915Sopenharmony_ci * The names match or @name not present and attribute is 2931987da915Sopenharmony_ci * unnamed. If no @val specified, we have found the attribute 2932987da915Sopenharmony_ci * and are done. 2933987da915Sopenharmony_ci */ 2934987da915Sopenharmony_ci if (!val) 2935987da915Sopenharmony_ci return 0; 2936987da915Sopenharmony_ci /* @val is present; compare values. */ 2937987da915Sopenharmony_ci else { 2938987da915Sopenharmony_ci register int rc; 2939987da915Sopenharmony_ci 2940987da915Sopenharmony_ci rc = memcmp(val, (char*)a +le16_to_cpu(a->value_offset), 2941987da915Sopenharmony_ci min(val_len, 2942987da915Sopenharmony_ci le32_to_cpu(a->value_length))); 2943987da915Sopenharmony_ci /* 2944987da915Sopenharmony_ci * If @val collates before the current attribute's 2945987da915Sopenharmony_ci * value, there is no matching attribute. 2946987da915Sopenharmony_ci */ 2947987da915Sopenharmony_ci if (!rc) { 2948987da915Sopenharmony_ci register u32 avl; 2949987da915Sopenharmony_ci avl = le32_to_cpu(a->value_length); 2950987da915Sopenharmony_ci if (val_len == avl) 2951987da915Sopenharmony_ci return 0; 2952987da915Sopenharmony_ci if (val_len < avl) { 2953987da915Sopenharmony_ci errno = ENOENT; 2954987da915Sopenharmony_ci return -1; 2955987da915Sopenharmony_ci } 2956987da915Sopenharmony_ci } else if (rc < 0) { 2957987da915Sopenharmony_ci errno = ENOENT; 2958987da915Sopenharmony_ci return -1; 2959987da915Sopenharmony_ci } 2960987da915Sopenharmony_ci } 2961987da915Sopenharmony_ci } 2962987da915Sopenharmony_ci errno = EIO; 2963987da915Sopenharmony_ci ntfs_log_perror("%s: Corrupt inode (%lld)", __FUNCTION__, 2964987da915Sopenharmony_ci ctx->ntfs_ino ? (long long)ctx->ntfs_ino->mft_no : -1); 2965987da915Sopenharmony_ci return -1; 2966987da915Sopenharmony_ci} 2967987da915Sopenharmony_ci 2968987da915Sopenharmony_civoid ntfs_attr_name_free(char **name) 2969987da915Sopenharmony_ci{ 2970987da915Sopenharmony_ci if (*name) { 2971987da915Sopenharmony_ci free(*name); 2972987da915Sopenharmony_ci *name = NULL; 2973987da915Sopenharmony_ci } 2974987da915Sopenharmony_ci} 2975987da915Sopenharmony_ci 2976987da915Sopenharmony_cichar *ntfs_attr_name_get(const ntfschar *uname, const int uname_len) 2977987da915Sopenharmony_ci{ 2978987da915Sopenharmony_ci char *name = NULL; 2979987da915Sopenharmony_ci int name_len; 2980987da915Sopenharmony_ci 2981987da915Sopenharmony_ci name_len = ntfs_ucstombs(uname, uname_len, &name, 0); 2982987da915Sopenharmony_ci if (name_len < 0) { 2983987da915Sopenharmony_ci ntfs_log_perror("ntfs_ucstombs"); 2984987da915Sopenharmony_ci return NULL; 2985987da915Sopenharmony_ci 2986987da915Sopenharmony_ci } else if (name_len > 0) 2987987da915Sopenharmony_ci return name; 2988987da915Sopenharmony_ci 2989987da915Sopenharmony_ci ntfs_attr_name_free(&name); 2990987da915Sopenharmony_ci return NULL; 2991987da915Sopenharmony_ci} 2992987da915Sopenharmony_ci 2993987da915Sopenharmony_ci/** 2994987da915Sopenharmony_ci * ntfs_external_attr_find - find an attribute in the attribute list of an inode 2995987da915Sopenharmony_ci * @type: attribute type to find 2996987da915Sopenharmony_ci * @name: attribute name to find (optional, i.e. NULL means don't care) 2997987da915Sopenharmony_ci * @name_len: attribute name length (only needed if @name present) 2998987da915Sopenharmony_ci * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) 2999987da915Sopenharmony_ci * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) 3000987da915Sopenharmony_ci * @val: attribute value to find (optional, resident attributes only) 3001987da915Sopenharmony_ci * @val_len: attribute value length 3002987da915Sopenharmony_ci * @ctx: search context with mft record and attribute to search from 3003987da915Sopenharmony_ci * 3004987da915Sopenharmony_ci * You shouldn't need to call this function directly. Use ntfs_attr_lookup() 3005987da915Sopenharmony_ci * instead. 3006987da915Sopenharmony_ci * 3007987da915Sopenharmony_ci * Find an attribute by searching the attribute list for the corresponding 3008987da915Sopenharmony_ci * attribute list entry. Having found the entry, map the mft record for read 3009987da915Sopenharmony_ci * if the attribute is in a different mft record/inode, find the attribute in 3010987da915Sopenharmony_ci * there and return it. 3011987da915Sopenharmony_ci * 3012987da915Sopenharmony_ci * If @type is AT_UNUSED, return the first found attribute, i.e. one can 3013987da915Sopenharmony_ci * enumerate all attributes by setting @type to AT_UNUSED and then calling 3014987da915Sopenharmony_ci * ntfs_external_attr_find() repeatedly until it returns -1 with errno set to 3015987da915Sopenharmony_ci * ENOENT to indicate that there are no more entries. During the enumeration, 3016987da915Sopenharmony_ci * each successful call of ntfs_external_attr_find() will return the next 3017987da915Sopenharmony_ci * attribute described by the attribute list of the base mft record described 3018987da915Sopenharmony_ci * by the search context @ctx. 3019987da915Sopenharmony_ci * 3020987da915Sopenharmony_ci * If @type is AT_END, seek to the end of the base mft record ignoring the 3021987da915Sopenharmony_ci * attribute list completely and return -1 with errno set to ENOENT. AT_END is 3022987da915Sopenharmony_ci * not a valid attribute, its length is zero for example, thus it is safer to 3023987da915Sopenharmony_ci * return error instead of success in this case. 3024987da915Sopenharmony_ci * 3025987da915Sopenharmony_ci * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present 3026987da915Sopenharmony_ci * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, 3027987da915Sopenharmony_ci * match both named and unnamed attributes. 3028987da915Sopenharmony_ci * 3029987da915Sopenharmony_ci * On first search @ctx->ntfs_ino must be the inode of the base mft record and 3030987da915Sopenharmony_ci * @ctx must have been obtained from a call to ntfs_attr_get_search_ctx(). 3031987da915Sopenharmony_ci * On subsequent calls, @ctx->ntfs_ino can be any extent inode, too 3032987da915Sopenharmony_ci * (@ctx->base_ntfs_ino is then the base inode). 3033987da915Sopenharmony_ci * 3034987da915Sopenharmony_ci * After finishing with the attribute/mft record you need to call 3035987da915Sopenharmony_ci * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any 3036987da915Sopenharmony_ci * mapped extent inodes, etc). 3037987da915Sopenharmony_ci * 3038987da915Sopenharmony_ci * Return 0 if the search was successful and -1 if not, with errno set to the 3039987da915Sopenharmony_ci * error code. 3040987da915Sopenharmony_ci * 3041987da915Sopenharmony_ci * On success, @ctx->attr is the found attribute, it is in mft record 3042987da915Sopenharmony_ci * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this 3043987da915Sopenharmony_ci * attribute with @ctx->base_* being the base mft record to which @ctx->attr 3044987da915Sopenharmony_ci * belongs. 3045987da915Sopenharmony_ci * 3046987da915Sopenharmony_ci * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the 3047987da915Sopenharmony_ci * attribute which collates just after the attribute being searched for in the 3048987da915Sopenharmony_ci * base ntfs inode, i.e. if one wants to add the attribute to the mft record 3049987da915Sopenharmony_ci * this is the correct place to insert it into, and if there is not enough 3050987da915Sopenharmony_ci * space, the attribute should be placed in an extent mft record. 3051987da915Sopenharmony_ci * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list 3052987da915Sopenharmony_ci * at which the new attribute's attribute list entry should be inserted. The 3053987da915Sopenharmony_ci * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. 3054987da915Sopenharmony_ci * The only exception to this is when @type is AT_END, in which case 3055987da915Sopenharmony_ci * @ctx->al_entry is set to NULL also (see above). 3056987da915Sopenharmony_ci * 3057987da915Sopenharmony_ci * The following error codes are defined: 3058987da915Sopenharmony_ci * ENOENT Attribute not found, not an error as such. 3059987da915Sopenharmony_ci * EINVAL Invalid arguments. 3060987da915Sopenharmony_ci * EIO I/O error or corrupt data structures found. 3061987da915Sopenharmony_ci * ENOMEM Not enough memory to allocate necessary buffers. 3062987da915Sopenharmony_ci */ 3063987da915Sopenharmony_cistatic int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name, 3064987da915Sopenharmony_ci const u32 name_len, const IGNORE_CASE_BOOL ic, 3065987da915Sopenharmony_ci const VCN lowest_vcn, const u8 *val, const u32 val_len, 3066987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx) 3067987da915Sopenharmony_ci{ 3068987da915Sopenharmony_ci ntfs_inode *base_ni, *ni; 3069987da915Sopenharmony_ci ntfs_volume *vol; 3070987da915Sopenharmony_ci ATTR_LIST_ENTRY *al_entry, *next_al_entry; 3071987da915Sopenharmony_ci u8 *al_start, *al_end; 3072987da915Sopenharmony_ci ATTR_RECORD *a; 3073987da915Sopenharmony_ci ntfschar *al_name; 3074987da915Sopenharmony_ci ptrdiff_t offs; 3075987da915Sopenharmony_ci ptrdiff_t space; 3076987da915Sopenharmony_ci u32 al_name_len; 3077987da915Sopenharmony_ci BOOL is_first_search = FALSE; 3078987da915Sopenharmony_ci 3079987da915Sopenharmony_ci ni = ctx->ntfs_ino; 3080987da915Sopenharmony_ci base_ni = ctx->base_ntfs_ino; 3081987da915Sopenharmony_ci ntfs_log_trace("Entering for inode %lld, attribute type 0x%x.\n", 3082987da915Sopenharmony_ci (unsigned long long)ni->mft_no, le32_to_cpu(type)); 3083987da915Sopenharmony_ci if (!base_ni) { 3084987da915Sopenharmony_ci /* First call happens with the base mft record. */ 3085987da915Sopenharmony_ci base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino; 3086987da915Sopenharmony_ci ctx->base_mrec = ctx->mrec; 3087987da915Sopenharmony_ci } 3088987da915Sopenharmony_ci if (ni == base_ni) 3089987da915Sopenharmony_ci ctx->base_attr = ctx->attr; 3090987da915Sopenharmony_ci if (type == AT_END) 3091987da915Sopenharmony_ci goto not_found; 3092987da915Sopenharmony_ci vol = base_ni->vol; 3093987da915Sopenharmony_ci al_start = base_ni->attr_list; 3094987da915Sopenharmony_ci al_end = al_start + base_ni->attr_list_size; 3095987da915Sopenharmony_ci if (!ctx->al_entry) { 3096987da915Sopenharmony_ci ctx->al_entry = (ATTR_LIST_ENTRY*)al_start; 3097987da915Sopenharmony_ci is_first_search = TRUE; 3098987da915Sopenharmony_ci } 3099987da915Sopenharmony_ci /* 3100987da915Sopenharmony_ci * Iterate over entries in attribute list starting at @ctx->al_entry, 3101987da915Sopenharmony_ci * or the entry following that, if @ctx->is_first is TRUE. 3102987da915Sopenharmony_ci */ 3103987da915Sopenharmony_ci if (ctx->is_first) { 3104987da915Sopenharmony_ci al_entry = ctx->al_entry; 3105987da915Sopenharmony_ci ctx->is_first = FALSE; 3106987da915Sopenharmony_ci /* 3107987da915Sopenharmony_ci * If an enumeration and the first attribute is higher than 3108987da915Sopenharmony_ci * the attribute list itself, need to return the attribute list 3109987da915Sopenharmony_ci * attribute. 3110987da915Sopenharmony_ci */ 3111987da915Sopenharmony_ci if ((type == AT_UNUSED) && is_first_search && 3112987da915Sopenharmony_ci le32_to_cpu(al_entry->type) > 3113987da915Sopenharmony_ci le32_to_cpu(AT_ATTRIBUTE_LIST)) 3114987da915Sopenharmony_ci goto find_attr_list_attr; 3115987da915Sopenharmony_ci } else { 3116987da915Sopenharmony_ci /* Check for small entry */ 3117987da915Sopenharmony_ci if (((p2n(al_end) - p2n(ctx->al_entry)) 3118987da915Sopenharmony_ci < (long)offsetof(ATTR_LIST_ENTRY, name)) 3119987da915Sopenharmony_ci || (le16_to_cpu(ctx->al_entry->length) & 7) 3120987da915Sopenharmony_ci || (le16_to_cpu(ctx->al_entry->length) 3121987da915Sopenharmony_ci < offsetof(ATTR_LIST_ENTRY, name))) 3122987da915Sopenharmony_ci goto corrupt; 3123987da915Sopenharmony_ci 3124987da915Sopenharmony_ci al_entry = (ATTR_LIST_ENTRY*)((char*)ctx->al_entry + 3125987da915Sopenharmony_ci le16_to_cpu(ctx->al_entry->length)); 3126987da915Sopenharmony_ci if ((u8*)al_entry == al_end) 3127987da915Sopenharmony_ci goto not_found; 3128987da915Sopenharmony_ci /* Preliminary check for small entry */ 3129987da915Sopenharmony_ci if ((p2n(al_end) - p2n(al_entry)) 3130987da915Sopenharmony_ci < (long)offsetof(ATTR_LIST_ENTRY, name)) 3131987da915Sopenharmony_ci goto corrupt; 3132987da915Sopenharmony_ci /* 3133987da915Sopenharmony_ci * If this is an enumeration and the attribute list attribute 3134987da915Sopenharmony_ci * is the next one in the enumeration sequence, just return the 3135987da915Sopenharmony_ci * attribute list attribute from the base mft record as it is 3136987da915Sopenharmony_ci * not listed in the attribute list itself. 3137987da915Sopenharmony_ci */ 3138987da915Sopenharmony_ci if ((type == AT_UNUSED) && le32_to_cpu(ctx->al_entry->type) < 3139987da915Sopenharmony_ci le32_to_cpu(AT_ATTRIBUTE_LIST) && 3140987da915Sopenharmony_ci le32_to_cpu(al_entry->type) > 3141987da915Sopenharmony_ci le32_to_cpu(AT_ATTRIBUTE_LIST)) { 3142987da915Sopenharmony_ci int rc; 3143987da915Sopenharmony_cifind_attr_list_attr: 3144987da915Sopenharmony_ci 3145987da915Sopenharmony_ci /* Check for bogus calls. */ 3146987da915Sopenharmony_ci if (name || name_len || val || val_len || lowest_vcn) { 3147987da915Sopenharmony_ci errno = EINVAL; 3148987da915Sopenharmony_ci ntfs_log_perror("%s", __FUNCTION__); 3149987da915Sopenharmony_ci return -1; 3150987da915Sopenharmony_ci } 3151987da915Sopenharmony_ci 3152987da915Sopenharmony_ci /* We want the base record. */ 3153987da915Sopenharmony_ci ctx->ntfs_ino = base_ni; 3154987da915Sopenharmony_ci ctx->mrec = ctx->base_mrec; 3155987da915Sopenharmony_ci ctx->is_first = TRUE; 3156987da915Sopenharmony_ci /* Sanity checks are performed elsewhere. */ 3157987da915Sopenharmony_ci ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + 3158987da915Sopenharmony_ci le16_to_cpu(ctx->mrec->attrs_offset)); 3159987da915Sopenharmony_ci 3160987da915Sopenharmony_ci /* Find the attribute list attribute. */ 3161987da915Sopenharmony_ci rc = ntfs_attr_find(AT_ATTRIBUTE_LIST, NULL, 0, 3162987da915Sopenharmony_ci IGNORE_CASE, NULL, 0, ctx); 3163987da915Sopenharmony_ci 3164987da915Sopenharmony_ci /* 3165987da915Sopenharmony_ci * Setup the search context so the correct 3166987da915Sopenharmony_ci * attribute is returned next time round. 3167987da915Sopenharmony_ci */ 3168987da915Sopenharmony_ci ctx->al_entry = al_entry; 3169987da915Sopenharmony_ci ctx->is_first = TRUE; 3170987da915Sopenharmony_ci 3171987da915Sopenharmony_ci /* Got it. Done. */ 3172987da915Sopenharmony_ci if (!rc) 3173987da915Sopenharmony_ci return 0; 3174987da915Sopenharmony_ci 3175987da915Sopenharmony_ci /* Error! If other than not found return it. */ 3176987da915Sopenharmony_ci if (errno != ENOENT) 3177987da915Sopenharmony_ci return rc; 3178987da915Sopenharmony_ci 3179987da915Sopenharmony_ci /* Not found?!? Absurd! */ 3180987da915Sopenharmony_ci errno = EIO; 3181987da915Sopenharmony_ci ntfs_log_error("Attribute list wasn't found"); 3182987da915Sopenharmony_ci return -1; 3183987da915Sopenharmony_ci } 3184987da915Sopenharmony_ci } 3185987da915Sopenharmony_ci for (;; al_entry = next_al_entry) { 3186987da915Sopenharmony_ci /* Out of bounds check. */ 3187987da915Sopenharmony_ci if ((u8*)al_entry < base_ni->attr_list || 3188987da915Sopenharmony_ci (u8*)al_entry > al_end) 3189987da915Sopenharmony_ci break; /* Inode is corrupt. */ 3190987da915Sopenharmony_ci ctx->al_entry = al_entry; 3191987da915Sopenharmony_ci /* Catch the end of the attribute list. */ 3192987da915Sopenharmony_ci if ((u8*)al_entry == al_end) 3193987da915Sopenharmony_ci goto not_found; 3194987da915Sopenharmony_ci 3195987da915Sopenharmony_ci if ((((u8*)al_entry + offsetof(ATTR_LIST_ENTRY, name)) > al_end) 3196987da915Sopenharmony_ci || ((u8*)al_entry + le16_to_cpu(al_entry->length) > al_end) 3197987da915Sopenharmony_ci || (le16_to_cpu(al_entry->length) & 7) 3198987da915Sopenharmony_ci || (le16_to_cpu(al_entry->length) 3199987da915Sopenharmony_ci < offsetof(ATTR_LIST_ENTRY, name_length)) 3200987da915Sopenharmony_ci || (al_entry->name_length 3201987da915Sopenharmony_ci && ((u8*)al_entry + al_entry->name_offset 3202987da915Sopenharmony_ci + al_entry->name_length * sizeof(ntfschar)) 3203987da915Sopenharmony_ci > al_end)) 3204987da915Sopenharmony_ci break; /* corrupt */ 3205987da915Sopenharmony_ci 3206987da915Sopenharmony_ci next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry + 3207987da915Sopenharmony_ci le16_to_cpu(al_entry->length)); 3208987da915Sopenharmony_ci if (type != AT_UNUSED) { 3209987da915Sopenharmony_ci if (le32_to_cpu(al_entry->type) > le32_to_cpu(type)) 3210987da915Sopenharmony_ci goto not_found; 3211987da915Sopenharmony_ci if (type != al_entry->type) 3212987da915Sopenharmony_ci continue; 3213987da915Sopenharmony_ci } 3214987da915Sopenharmony_ci al_name_len = al_entry->name_length; 3215987da915Sopenharmony_ci al_name = (ntfschar*)((u8*)al_entry + al_entry->name_offset); 3216987da915Sopenharmony_ci /* 3217987da915Sopenharmony_ci * If !@type we want the attribute represented by this 3218987da915Sopenharmony_ci * attribute list entry. 3219987da915Sopenharmony_ci */ 3220987da915Sopenharmony_ci if (type == AT_UNUSED) 3221987da915Sopenharmony_ci goto is_enumeration; 3222987da915Sopenharmony_ci /* 3223987da915Sopenharmony_ci * If @name is AT_UNNAMED we want an unnamed attribute. 3224987da915Sopenharmony_ci * If @name is present, compare the two names. 3225987da915Sopenharmony_ci * Otherwise, match any attribute. 3226987da915Sopenharmony_ci */ 3227987da915Sopenharmony_ci if (name == AT_UNNAMED) { 3228987da915Sopenharmony_ci if (al_name_len) 3229987da915Sopenharmony_ci goto not_found; 3230987da915Sopenharmony_ci } else { 3231987da915Sopenharmony_ci int rc; 3232987da915Sopenharmony_ci 3233987da915Sopenharmony_ci if (name && ((rc = ntfs_names_full_collate(name, 3234987da915Sopenharmony_ci name_len, al_name, al_name_len, ic, 3235987da915Sopenharmony_ci vol->upcase, vol->upcase_len)))) { 3236987da915Sopenharmony_ci 3237987da915Sopenharmony_ci /* 3238987da915Sopenharmony_ci * If @name collates before al_name, 3239987da915Sopenharmony_ci * there is no matching attribute. 3240987da915Sopenharmony_ci */ 3241987da915Sopenharmony_ci if (rc < 0) 3242987da915Sopenharmony_ci goto not_found; 3243987da915Sopenharmony_ci /* If the strings are not equal, continue search. */ 3244987da915Sopenharmony_ci continue; 3245987da915Sopenharmony_ci } 3246987da915Sopenharmony_ci } 3247987da915Sopenharmony_ci /* 3248987da915Sopenharmony_ci * The names match or @name not present and attribute is 3249987da915Sopenharmony_ci * unnamed. Now check @lowest_vcn. Continue search if the 3250987da915Sopenharmony_ci * next attribute list entry still fits @lowest_vcn. Otherwise 3251987da915Sopenharmony_ci * we have reached the right one or the search has failed. 3252987da915Sopenharmony_ci */ 3253987da915Sopenharmony_ci if (lowest_vcn && (u8*)next_al_entry >= al_start && 3254987da915Sopenharmony_ci (u8*)next_al_entry + 6 < al_end && 3255987da915Sopenharmony_ci (u8*)next_al_entry + le16_to_cpu( 3256987da915Sopenharmony_ci next_al_entry->length) <= al_end && 3257987da915Sopenharmony_ci sle64_to_cpu(next_al_entry->lowest_vcn) <= 3258987da915Sopenharmony_ci lowest_vcn && 3259987da915Sopenharmony_ci next_al_entry->type == al_entry->type && 3260987da915Sopenharmony_ci next_al_entry->name_length == al_name_len && 3261987da915Sopenharmony_ci ntfs_names_are_equal((ntfschar*)((char*) 3262987da915Sopenharmony_ci next_al_entry + 3263987da915Sopenharmony_ci next_al_entry->name_offset), 3264987da915Sopenharmony_ci next_al_entry->name_length, 3265987da915Sopenharmony_ci al_name, al_name_len, CASE_SENSITIVE, 3266987da915Sopenharmony_ci vol->upcase, vol->upcase_len)) 3267987da915Sopenharmony_ci continue; 3268987da915Sopenharmony_ciis_enumeration: 3269987da915Sopenharmony_ci if (MREF_LE(al_entry->mft_reference) == ni->mft_no) { 3270987da915Sopenharmony_ci if (MSEQNO_LE(al_entry->mft_reference) != 3271987da915Sopenharmony_ci le16_to_cpu( 3272987da915Sopenharmony_ci ni->mrec->sequence_number)) { 3273987da915Sopenharmony_ci ntfs_log_error("Found stale mft reference in " 3274987da915Sopenharmony_ci "attribute list!\n"); 3275987da915Sopenharmony_ci break; 3276987da915Sopenharmony_ci } 3277987da915Sopenharmony_ci } else { /* Mft references do not match. */ 3278987da915Sopenharmony_ci /* Do we want the base record back? */ 3279987da915Sopenharmony_ci if (MREF_LE(al_entry->mft_reference) == 3280987da915Sopenharmony_ci base_ni->mft_no) { 3281987da915Sopenharmony_ci ni = ctx->ntfs_ino = base_ni; 3282987da915Sopenharmony_ci ctx->mrec = ctx->base_mrec; 3283987da915Sopenharmony_ci } else { 3284987da915Sopenharmony_ci /* We want an extent record. */ 3285987da915Sopenharmony_ci if (!vol->mft_na) { 3286987da915Sopenharmony_ci ntfs_log_perror("$MFT not ready for " 3287987da915Sopenharmony_ci "opening an extent to inode %lld\n", 3288987da915Sopenharmony_ci (long long)base_ni->mft_no); 3289987da915Sopenharmony_ci break; 3290987da915Sopenharmony_ci } 3291987da915Sopenharmony_ci ni = ntfs_extent_inode_open(base_ni, 3292987da915Sopenharmony_ci al_entry->mft_reference); 3293987da915Sopenharmony_ci if (!ni) 3294987da915Sopenharmony_ci break; 3295987da915Sopenharmony_ci ctx->ntfs_ino = ni; 3296987da915Sopenharmony_ci ctx->mrec = ni->mrec; 3297987da915Sopenharmony_ci } 3298987da915Sopenharmony_ci } 3299987da915Sopenharmony_ci a = ctx->attr = (ATTR_RECORD*)((char*)ctx->mrec + 3300987da915Sopenharmony_ci le16_to_cpu(ctx->mrec->attrs_offset)); 3301987da915Sopenharmony_ci /* 3302987da915Sopenharmony_ci * ctx->ntfs_ino, ctx->mrec, and ctx->attr now point to the 3303987da915Sopenharmony_ci * mft record containing the attribute represented by the 3304987da915Sopenharmony_ci * current al_entry. 3305987da915Sopenharmony_ci * 3306987da915Sopenharmony_ci * We could call into ntfs_attr_find() to find the right 3307987da915Sopenharmony_ci * attribute in this mft record but this would be less 3308987da915Sopenharmony_ci * efficient and not quite accurate as ntfs_attr_find() ignores 3309987da915Sopenharmony_ci * the attribute instance numbers for example which become 3310987da915Sopenharmony_ci * important when one plays with attribute lists. Also, because 3311987da915Sopenharmony_ci * a proper match has been found in the attribute list entry 3312987da915Sopenharmony_ci * above, the comparison can now be optimized. So it is worth 3313987da915Sopenharmony_ci * re-implementing a simplified ntfs_attr_find() here. 3314987da915Sopenharmony_ci * 3315987da915Sopenharmony_ci * Use a manual loop so we can still use break and continue 3316987da915Sopenharmony_ci * with the same meanings as above. 3317987da915Sopenharmony_ci */ 3318987da915Sopenharmony_cido_next_attr_loop: 3319987da915Sopenharmony_ci /* 3320987da915Sopenharmony_ci * Make sure the attribute fully lies within the MFT record 3321987da915Sopenharmony_ci * and we can safely access its minimal fields. 3322987da915Sopenharmony_ci */ 3323987da915Sopenharmony_ci offs = p2n(a) - p2n(ctx->mrec); 3324987da915Sopenharmony_ci space = le32_to_cpu(ctx->mrec->bytes_in_use) - offs; 3325987da915Sopenharmony_ci if (offs < 0) 3326987da915Sopenharmony_ci break; 3327987da915Sopenharmony_ci if ((space >= 4) && (a->type == AT_END)) 3328987da915Sopenharmony_ci continue; 3329987da915Sopenharmony_ci if ((space < (ptrdiff_t)offsetof(ATTR_RECORD, resident_end)) 3330987da915Sopenharmony_ci || (space < (ptrdiff_t)le32_to_cpu(a->length))) 3331987da915Sopenharmony_ci break; 3332987da915Sopenharmony_ci if (al_entry->instance != a->instance) 3333987da915Sopenharmony_ci goto do_next_attr; 3334987da915Sopenharmony_ci /* 3335987da915Sopenharmony_ci * If the type and/or the name are/is mismatched between the 3336987da915Sopenharmony_ci * attribute list entry and the attribute record, there is 3337987da915Sopenharmony_ci * corruption so we break and return error EIO. 3338987da915Sopenharmony_ci */ 3339987da915Sopenharmony_ci if (al_entry->type != a->type) 3340987da915Sopenharmony_ci break; 3341987da915Sopenharmony_ci if (!ntfs_names_are_equal((ntfschar*)((char*)a + 3342987da915Sopenharmony_ci le16_to_cpu(a->name_offset)), 3343987da915Sopenharmony_ci a->name_length, al_name, 3344987da915Sopenharmony_ci al_name_len, CASE_SENSITIVE, 3345987da915Sopenharmony_ci vol->upcase, vol->upcase_len)) 3346987da915Sopenharmony_ci break; 3347987da915Sopenharmony_ci ctx->attr = a; 3348987da915Sopenharmony_ci /* 3349987da915Sopenharmony_ci * If no @val specified or @val specified and it matches, we 3350987da915Sopenharmony_ci * have found it! Also, if !@type, it is an enumeration, so we 3351987da915Sopenharmony_ci * want the current attribute. 3352987da915Sopenharmony_ci */ 3353987da915Sopenharmony_ci if ((type == AT_UNUSED) || !val || (!a->non_resident && 3354987da915Sopenharmony_ci le32_to_cpu(a->value_length) == val_len && 3355987da915Sopenharmony_ci !memcmp((char*)a + le16_to_cpu(a->value_offset), 3356987da915Sopenharmony_ci val, val_len))) { 3357987da915Sopenharmony_ci return 0; 3358987da915Sopenharmony_ci } 3359987da915Sopenharmony_cido_next_attr: 3360987da915Sopenharmony_ci /* Proceed to the next attribute in the current mft record. */ 3361987da915Sopenharmony_ci a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); 3362987da915Sopenharmony_ci goto do_next_attr_loop; 3363987da915Sopenharmony_ci } 3364987da915Sopenharmony_cicorrupt : 3365987da915Sopenharmony_ci if (ni != base_ni) { 3366987da915Sopenharmony_ci ctx->ntfs_ino = base_ni; 3367987da915Sopenharmony_ci ctx->mrec = ctx->base_mrec; 3368987da915Sopenharmony_ci ctx->attr = ctx->base_attr; 3369987da915Sopenharmony_ci } 3370987da915Sopenharmony_ci errno = EIO; 3371987da915Sopenharmony_ci ntfs_log_error("Corrupt attribute list entry in MFT record %lld\n", 3372987da915Sopenharmony_ci (long long)base_ni->mft_no); 3373987da915Sopenharmony_ci return -1; 3374987da915Sopenharmony_cinot_found: 3375987da915Sopenharmony_ci /* 3376987da915Sopenharmony_ci * If we were looking for AT_END or we were enumerating and reached the 3377987da915Sopenharmony_ci * end, we reset the search context @ctx and use ntfs_attr_find() to 3378987da915Sopenharmony_ci * seek to the end of the base mft record. 3379987da915Sopenharmony_ci */ 3380987da915Sopenharmony_ci if (type == AT_UNUSED || type == AT_END) { 3381987da915Sopenharmony_ci ntfs_attr_reinit_search_ctx(ctx); 3382987da915Sopenharmony_ci return ntfs_attr_find(AT_END, name, name_len, ic, val, val_len, 3383987da915Sopenharmony_ci ctx); 3384987da915Sopenharmony_ci } 3385987da915Sopenharmony_ci /* 3386987da915Sopenharmony_ci * The attribute wasn't found. Before we return, we want to ensure 3387987da915Sopenharmony_ci * @ctx->mrec and @ctx->attr indicate the position at which the 3388987da915Sopenharmony_ci * attribute should be inserted in the base mft record. Since we also 3389987da915Sopenharmony_ci * want to preserve @ctx->al_entry we cannot reinitialize the search 3390987da915Sopenharmony_ci * context using ntfs_attr_reinit_search_ctx() as this would set 3391987da915Sopenharmony_ci * @ctx->al_entry to NULL. Thus we do the necessary bits manually (see 3392987da915Sopenharmony_ci * ntfs_attr_init_search_ctx() below). Note, we _only_ preserve 3393987da915Sopenharmony_ci * @ctx->al_entry as the remaining fields (base_*) are identical to 3394987da915Sopenharmony_ci * their non base_ counterparts and we cannot set @ctx->base_attr 3395987da915Sopenharmony_ci * correctly yet as we do not know what @ctx->attr will be set to by 3396987da915Sopenharmony_ci * the call to ntfs_attr_find() below. 3397987da915Sopenharmony_ci */ 3398987da915Sopenharmony_ci ctx->mrec = ctx->base_mrec; 3399987da915Sopenharmony_ci ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + 3400987da915Sopenharmony_ci le16_to_cpu(ctx->mrec->attrs_offset)); 3401987da915Sopenharmony_ci ctx->is_first = TRUE; 3402987da915Sopenharmony_ci ctx->ntfs_ino = ctx->base_ntfs_ino; 3403987da915Sopenharmony_ci ctx->base_ntfs_ino = NULL; 3404987da915Sopenharmony_ci ctx->base_mrec = NULL; 3405987da915Sopenharmony_ci ctx->base_attr = NULL; 3406987da915Sopenharmony_ci /* 3407987da915Sopenharmony_ci * In case there are multiple matches in the base mft record, need to 3408987da915Sopenharmony_ci * keep enumerating until we get an attribute not found response (or 3409987da915Sopenharmony_ci * another error), otherwise we would keep returning the same attribute 3410987da915Sopenharmony_ci * over and over again and all programs using us for enumeration would 3411987da915Sopenharmony_ci * lock up in a tight loop. 3412987da915Sopenharmony_ci */ 3413987da915Sopenharmony_ci { 3414987da915Sopenharmony_ci int ret; 3415987da915Sopenharmony_ci 3416987da915Sopenharmony_ci do { 3417987da915Sopenharmony_ci ret = ntfs_attr_find(type, name, name_len, ic, val, 3418987da915Sopenharmony_ci val_len, ctx); 3419987da915Sopenharmony_ci } while (!ret); 3420987da915Sopenharmony_ci return ret; 3421987da915Sopenharmony_ci } 3422987da915Sopenharmony_ci} 3423987da915Sopenharmony_ci 3424987da915Sopenharmony_ci/* 3425987da915Sopenharmony_ci * Check the consistency of an attribute 3426987da915Sopenharmony_ci * 3427987da915Sopenharmony_ci * Do the general consistency checks of the selected attribute : 3428987da915Sopenharmony_ci * - the required fields can be accessed 3429987da915Sopenharmony_ci * - the variable fields do not overflow 3430987da915Sopenharmony_ci * - the attribute is [non-]resident if it must be 3431987da915Sopenharmony_ci * - miscelleaneous checks 3432987da915Sopenharmony_ci * 3433987da915Sopenharmony_ci * Returns 0 if the checks pass 3434987da915Sopenharmony_ci * -1 with errno = EIO otherwise 3435987da915Sopenharmony_ci */ 3436987da915Sopenharmony_ci 3437987da915Sopenharmony_ciint ntfs_attr_inconsistent(const ATTR_RECORD *a, const MFT_REF mref) 3438987da915Sopenharmony_ci{ 3439987da915Sopenharmony_ci const FILE_NAME_ATTR *fn; 3440987da915Sopenharmony_ci const INDEX_ROOT *ir; 3441987da915Sopenharmony_ci u64 inum; 3442987da915Sopenharmony_ci int ret; 3443987da915Sopenharmony_ci 3444987da915Sopenharmony_ci /* 3445987da915Sopenharmony_ci * The attribute was found to fully lie within the MFT 3446987da915Sopenharmony_ci * record, now make sure its relevant parts (name, runlist, 3447987da915Sopenharmony_ci * value) also lie within. The first step is to make sure 3448987da915Sopenharmony_ci * the attribute has the minimum length so that accesses to 3449987da915Sopenharmony_ci * the lengths and offsets of these parts are safe. 3450987da915Sopenharmony_ci */ 3451987da915Sopenharmony_ci ret = 0; 3452987da915Sopenharmony_ci inum = MREF(mref); 3453987da915Sopenharmony_ci if (a->non_resident) { 3454987da915Sopenharmony_ci if ((a->non_resident != 1) 3455987da915Sopenharmony_ci || (le32_to_cpu(a->length) 3456987da915Sopenharmony_ci < offsetof(ATTR_RECORD, non_resident_end)) 3457987da915Sopenharmony_ci || (le16_to_cpu(a->mapping_pairs_offset) 3458987da915Sopenharmony_ci >= le32_to_cpu(a->length)) 3459987da915Sopenharmony_ci || (a->name_length 3460987da915Sopenharmony_ci && (((u32)le16_to_cpu(a->name_offset) 3461987da915Sopenharmony_ci + a->name_length * sizeof(ntfschar)) 3462987da915Sopenharmony_ci > le32_to_cpu(a->length))) 3463987da915Sopenharmony_ci || (le64_to_cpu(a->highest_vcn) 3464987da915Sopenharmony_ci < le64_to_cpu(a->lowest_vcn))) { 3465987da915Sopenharmony_ci ntfs_log_error("Corrupt non resident attribute" 3466987da915Sopenharmony_ci " 0x%x in MFT record %lld\n", 3467987da915Sopenharmony_ci (int)le32_to_cpu(a->type), 3468987da915Sopenharmony_ci (long long)inum); 3469987da915Sopenharmony_ci errno = EIO; 3470987da915Sopenharmony_ci ret = -1; 3471987da915Sopenharmony_ci } 3472987da915Sopenharmony_ci } else { 3473987da915Sopenharmony_ci if ((le32_to_cpu(a->length) 3474987da915Sopenharmony_ci < offsetof(ATTR_RECORD, resident_end)) 3475987da915Sopenharmony_ci || (le32_to_cpu(a->value_length) & 0xff000000) 3476987da915Sopenharmony_ci || (a->value_length 3477987da915Sopenharmony_ci && ((le16_to_cpu(a->value_offset) 3478987da915Sopenharmony_ci + le32_to_cpu(a->value_length)) 3479987da915Sopenharmony_ci > le32_to_cpu(a->length))) 3480987da915Sopenharmony_ci || (a->name_length 3481987da915Sopenharmony_ci && (((u32)le16_to_cpu(a->name_offset) 3482987da915Sopenharmony_ci + a->name_length * sizeof(ntfschar)) 3483987da915Sopenharmony_ci > le32_to_cpu(a->length)))) { 3484987da915Sopenharmony_ci ntfs_log_error("Corrupt resident attribute" 3485987da915Sopenharmony_ci " 0x%x in MFT record %lld\n", 3486987da915Sopenharmony_ci (int)le32_to_cpu(a->type), 3487987da915Sopenharmony_ci (long long)inum); 3488987da915Sopenharmony_ci errno = EIO; 3489987da915Sopenharmony_ci ret = -1; 3490987da915Sopenharmony_ci } 3491987da915Sopenharmony_ci } 3492987da915Sopenharmony_ci if (!ret) { 3493987da915Sopenharmony_ci /* 3494987da915Sopenharmony_ci * Checking whether an attribute must be [non-]resident 3495987da915Sopenharmony_ci * is hard-coded for well-known ones. This should be 3496987da915Sopenharmony_ci * done through ntfs_attr_can_be_non_resident(), based on 3497987da915Sopenharmony_ci * $AttrDef, but this would give an easy way to bypass 3498987da915Sopenharmony_ci * the checks. 3499987da915Sopenharmony_ci * Attributes which are not well-known are not checked. 3500987da915Sopenharmony_ci * 3501987da915Sopenharmony_ci * Note : at this stage we know that a->length and 3502987da915Sopenharmony_ci * a->value_length cannot look like being negative. 3503987da915Sopenharmony_ci */ 3504987da915Sopenharmony_ci switch(a->type) { 3505987da915Sopenharmony_ci case AT_FILE_NAME : 3506987da915Sopenharmony_ci /* Check file names are resident and do not overflow */ 3507987da915Sopenharmony_ci fn = (const FILE_NAME_ATTR*)((const u8*)a 3508987da915Sopenharmony_ci + le16_to_cpu(a->value_offset)); 3509987da915Sopenharmony_ci if (a->non_resident 3510987da915Sopenharmony_ci || (le32_to_cpu(a->value_length) 3511987da915Sopenharmony_ci < offsetof(FILE_NAME_ATTR, file_name)) 3512987da915Sopenharmony_ci || !fn->file_name_length 3513987da915Sopenharmony_ci || ((fn->file_name_length * sizeof(ntfschar) 3514987da915Sopenharmony_ci + offsetof(FILE_NAME_ATTR, file_name)) 3515987da915Sopenharmony_ci > le32_to_cpu(a->value_length))) { 3516987da915Sopenharmony_ci ntfs_log_error("Corrupt file name" 3517987da915Sopenharmony_ci " attribute in MFT record %lld.\n", 3518987da915Sopenharmony_ci (long long)inum); 3519987da915Sopenharmony_ci errno = EIO; 3520987da915Sopenharmony_ci ret = -1; 3521987da915Sopenharmony_ci } 3522987da915Sopenharmony_ci break; 3523987da915Sopenharmony_ci case AT_INDEX_ROOT : 3524987da915Sopenharmony_ci /* Check root index is resident and does not overflow */ 3525987da915Sopenharmony_ci ir = (const INDEX_ROOT*)((const u8*)a + 3526987da915Sopenharmony_ci le16_to_cpu(a->value_offset)); 3527987da915Sopenharmony_ci /* index.allocated_size may overflow while resizing */ 3528987da915Sopenharmony_ci if (a->non_resident 3529987da915Sopenharmony_ci || (le32_to_cpu(a->value_length) 3530987da915Sopenharmony_ci < offsetof(INDEX_ROOT, index.reserved)) 3531987da915Sopenharmony_ci || (le32_to_cpu(ir->index.entries_offset) 3532987da915Sopenharmony_ci < sizeof(INDEX_HEADER)) 3533987da915Sopenharmony_ci || (le32_to_cpu(ir->index.index_length) 3534987da915Sopenharmony_ci < le32_to_cpu(ir->index.entries_offset)) 3535987da915Sopenharmony_ci || (le32_to_cpu(ir->index.allocated_size) 3536987da915Sopenharmony_ci < le32_to_cpu(ir->index.index_length)) 3537987da915Sopenharmony_ci || (le32_to_cpu(a->value_length) 3538987da915Sopenharmony_ci < (le32_to_cpu(ir->index.allocated_size) 3539987da915Sopenharmony_ci + offsetof(INDEX_ROOT, reserved)))) { 3540987da915Sopenharmony_ci ntfs_log_error("Corrupt index root" 3541987da915Sopenharmony_ci " in MFT record %lld.\n", 3542987da915Sopenharmony_ci (long long)inum); 3543987da915Sopenharmony_ci errno = EIO; 3544987da915Sopenharmony_ci ret = -1; 3545987da915Sopenharmony_ci } 3546987da915Sopenharmony_ci break; 3547987da915Sopenharmony_ci case AT_STANDARD_INFORMATION : 3548987da915Sopenharmony_ci if (a->non_resident 3549987da915Sopenharmony_ci || (le32_to_cpu(a->value_length) 3550987da915Sopenharmony_ci < offsetof(STANDARD_INFORMATION, 3551987da915Sopenharmony_ci v1_end))) { 3552987da915Sopenharmony_ci ntfs_log_error("Corrupt standard information" 3553987da915Sopenharmony_ci " in MFT record %lld\n", 3554987da915Sopenharmony_ci (long long)inum); 3555987da915Sopenharmony_ci errno = EIO; 3556987da915Sopenharmony_ci ret = -1; 3557987da915Sopenharmony_ci } 3558987da915Sopenharmony_ci break; 3559987da915Sopenharmony_ci case AT_OBJECT_ID : 3560987da915Sopenharmony_ci if (a->non_resident 3561987da915Sopenharmony_ci || (le32_to_cpu(a->value_length) 3562987da915Sopenharmony_ci < sizeof(GUID))) { 3563987da915Sopenharmony_ci ntfs_log_error("Corrupt object id" 3564987da915Sopenharmony_ci " in MFT record %lld\n", 3565987da915Sopenharmony_ci (long long)inum); 3566987da915Sopenharmony_ci errno = EIO; 3567987da915Sopenharmony_ci ret = -1; 3568987da915Sopenharmony_ci } 3569987da915Sopenharmony_ci break; 3570987da915Sopenharmony_ci case AT_VOLUME_NAME : 3571987da915Sopenharmony_ci case AT_EA_INFORMATION : 3572987da915Sopenharmony_ci if (a->non_resident) { 3573987da915Sopenharmony_ci ntfs_log_error("Attribute 0x%x in MFT record" 3574987da915Sopenharmony_ci " %lld should be resident.\n", 3575987da915Sopenharmony_ci (int)le32_to_cpu(a->type), 3576987da915Sopenharmony_ci (long long)inum); 3577987da915Sopenharmony_ci errno = EIO; 3578987da915Sopenharmony_ci ret = -1; 3579987da915Sopenharmony_ci } 3580987da915Sopenharmony_ci break; 3581987da915Sopenharmony_ci case AT_VOLUME_INFORMATION : 3582987da915Sopenharmony_ci if (a->non_resident 3583987da915Sopenharmony_ci || (le32_to_cpu(a->value_length) 3584987da915Sopenharmony_ci < sizeof(VOLUME_INFORMATION))) { 3585987da915Sopenharmony_ci ntfs_log_error("Corrupt volume information" 3586987da915Sopenharmony_ci " in MFT record %lld\n", 3587987da915Sopenharmony_ci (long long)inum); 3588987da915Sopenharmony_ci errno = EIO; 3589987da915Sopenharmony_ci ret = -1; 3590987da915Sopenharmony_ci } 3591987da915Sopenharmony_ci break; 3592987da915Sopenharmony_ci case AT_INDEX_ALLOCATION : 3593987da915Sopenharmony_ci if (!a->non_resident) { 3594987da915Sopenharmony_ci ntfs_log_error("Corrupt index allocation" 3595987da915Sopenharmony_ci " in MFT record %lld", 3596987da915Sopenharmony_ci (long long)inum); 3597987da915Sopenharmony_ci errno = EIO; 3598987da915Sopenharmony_ci ret = -1; 3599987da915Sopenharmony_ci } 3600987da915Sopenharmony_ci break; 3601987da915Sopenharmony_ci default : 3602987da915Sopenharmony_ci break; 3603987da915Sopenharmony_ci } 3604987da915Sopenharmony_ci } 3605987da915Sopenharmony_ci return (ret); 3606987da915Sopenharmony_ci} 3607987da915Sopenharmony_ci 3608987da915Sopenharmony_ci/** 3609987da915Sopenharmony_ci * ntfs_attr_lookup - find an attribute in an ntfs inode 3610987da915Sopenharmony_ci * @type: attribute type to find 3611987da915Sopenharmony_ci * @name: attribute name to find (optional, i.e. NULL means don't care) 3612987da915Sopenharmony_ci * @name_len: attribute name length (only needed if @name present) 3613987da915Sopenharmony_ci * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) 3614987da915Sopenharmony_ci * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) 3615987da915Sopenharmony_ci * @val: attribute value to find (optional, resident attributes only) 3616987da915Sopenharmony_ci * @val_len: attribute value length 3617987da915Sopenharmony_ci * @ctx: search context with mft record and attribute to search from 3618987da915Sopenharmony_ci * 3619987da915Sopenharmony_ci * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must 3620987da915Sopenharmony_ci * be the base mft record and @ctx must have been obtained from a call to 3621987da915Sopenharmony_ci * ntfs_attr_get_search_ctx(). 3622987da915Sopenharmony_ci * 3623987da915Sopenharmony_ci * This function transparently handles attribute lists and @ctx is used to 3624987da915Sopenharmony_ci * continue searches where they were left off at. 3625987da915Sopenharmony_ci * 3626987da915Sopenharmony_ci * If @type is AT_UNUSED, return the first found attribute, i.e. one can 3627987da915Sopenharmony_ci * enumerate all attributes by setting @type to AT_UNUSED and then calling 3628987da915Sopenharmony_ci * ntfs_attr_lookup() repeatedly until it returns -1 with errno set to ENOENT 3629987da915Sopenharmony_ci * to indicate that there are no more entries. During the enumeration, each 3630987da915Sopenharmony_ci * successful call of ntfs_attr_lookup() will return the next attribute, with 3631987da915Sopenharmony_ci * the current attribute being described by the search context @ctx. 3632987da915Sopenharmony_ci * 3633987da915Sopenharmony_ci * If @type is AT_END, seek to the end of the base mft record ignoring the 3634987da915Sopenharmony_ci * attribute list completely and return -1 with errno set to ENOENT. AT_END is 3635987da915Sopenharmony_ci * not a valid attribute, its length is zero for example, thus it is safer to 3636987da915Sopenharmony_ci * return error instead of success in this case. It should never be needed to 3637987da915Sopenharmony_ci * do this, but we implement the functionality because it allows for simpler 3638987da915Sopenharmony_ci * code inside ntfs_external_attr_find(). 3639987da915Sopenharmony_ci * 3640987da915Sopenharmony_ci * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present 3641987da915Sopenharmony_ci * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, 3642987da915Sopenharmony_ci * match both named and unnamed attributes. 3643987da915Sopenharmony_ci * 3644987da915Sopenharmony_ci * After finishing with the attribute/mft record you need to call 3645987da915Sopenharmony_ci * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any 3646987da915Sopenharmony_ci * mapped extent inodes, etc). 3647987da915Sopenharmony_ci * 3648987da915Sopenharmony_ci * Return 0 if the search was successful and -1 if not, with errno set to the 3649987da915Sopenharmony_ci * error code. 3650987da915Sopenharmony_ci * 3651987da915Sopenharmony_ci * On success, @ctx->attr is the found attribute, it is in mft record 3652987da915Sopenharmony_ci * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this 3653987da915Sopenharmony_ci * attribute with @ctx->base_* being the base mft record to which @ctx->attr 3654987da915Sopenharmony_ci * belongs. If no attribute list attribute is present @ctx->al_entry and 3655987da915Sopenharmony_ci * @ctx->base_* are NULL. 3656987da915Sopenharmony_ci * 3657987da915Sopenharmony_ci * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the 3658987da915Sopenharmony_ci * attribute which collates just after the attribute being searched for in the 3659987da915Sopenharmony_ci * base ntfs inode, i.e. if one wants to add the attribute to the mft record 3660987da915Sopenharmony_ci * this is the correct place to insert it into, and if there is not enough 3661987da915Sopenharmony_ci * space, the attribute should be placed in an extent mft record. 3662987da915Sopenharmony_ci * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list 3663987da915Sopenharmony_ci * at which the new attribute's attribute list entry should be inserted. The 3664987da915Sopenharmony_ci * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. 3665987da915Sopenharmony_ci * The only exception to this is when @type is AT_END, in which case 3666987da915Sopenharmony_ci * @ctx->al_entry is set to NULL also (see above). 3667987da915Sopenharmony_ci * 3668987da915Sopenharmony_ci * 3669987da915Sopenharmony_ci * The following error codes are defined: 3670987da915Sopenharmony_ci * ENOENT Attribute not found, not an error as such. 3671987da915Sopenharmony_ci * EINVAL Invalid arguments. 3672987da915Sopenharmony_ci * EIO I/O error or corrupt data structures found. 3673987da915Sopenharmony_ci * ENOMEM Not enough memory to allocate necessary buffers. 3674987da915Sopenharmony_ci */ 3675987da915Sopenharmony_ciint ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, 3676987da915Sopenharmony_ci const u32 name_len, const IGNORE_CASE_BOOL ic, 3677987da915Sopenharmony_ci const VCN lowest_vcn, const u8 *val, const u32 val_len, 3678987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx) 3679987da915Sopenharmony_ci{ 3680987da915Sopenharmony_ci ntfs_volume *vol; 3681987da915Sopenharmony_ci ntfs_inode *base_ni; 3682987da915Sopenharmony_ci int ret = -1; 3683987da915Sopenharmony_ci 3684987da915Sopenharmony_ci ntfs_log_enter("Entering for attribute type 0x%x\n", le32_to_cpu(type)); 3685987da915Sopenharmony_ci 3686987da915Sopenharmony_ci if (!ctx || !ctx->mrec || !ctx->attr || (name && name != AT_UNNAMED && 3687987da915Sopenharmony_ci (!ctx->ntfs_ino || !(vol = ctx->ntfs_ino->vol) || 3688987da915Sopenharmony_ci !vol->upcase || !vol->upcase_len))) { 3689987da915Sopenharmony_ci errno = EINVAL; 3690987da915Sopenharmony_ci ntfs_log_perror("%s", __FUNCTION__); 3691987da915Sopenharmony_ci goto out; 3692987da915Sopenharmony_ci } 3693987da915Sopenharmony_ci 3694987da915Sopenharmony_ci if (ctx->base_ntfs_ino) 3695987da915Sopenharmony_ci base_ni = ctx->base_ntfs_ino; 3696987da915Sopenharmony_ci else 3697987da915Sopenharmony_ci base_ni = ctx->ntfs_ino; 3698987da915Sopenharmony_ci if (!base_ni || !NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST) 3699987da915Sopenharmony_ci ret = ntfs_attr_find(type, name, name_len, ic, val, val_len, ctx); 3700987da915Sopenharmony_ci else 3701987da915Sopenharmony_ci ret = ntfs_external_attr_find(type, name, name_len, ic, 3702987da915Sopenharmony_ci lowest_vcn, val, val_len, ctx); 3703987da915Sopenharmony_ciout: 3704987da915Sopenharmony_ci ntfs_log_leave("\n"); 3705987da915Sopenharmony_ci return ret; 3706987da915Sopenharmony_ci} 3707987da915Sopenharmony_ci 3708987da915Sopenharmony_ci/** 3709987da915Sopenharmony_ci * ntfs_attr_position - find given or next attribute type in an ntfs inode 3710987da915Sopenharmony_ci * @type: attribute type to start lookup 3711987da915Sopenharmony_ci * @ctx: search context with mft record and attribute to search from 3712987da915Sopenharmony_ci * 3713987da915Sopenharmony_ci * Find an attribute type in an ntfs inode or the next attribute which is not 3714987da915Sopenharmony_ci * the AT_END attribute. Please see more details at ntfs_attr_lookup. 3715987da915Sopenharmony_ci * 3716987da915Sopenharmony_ci * Return 0 if the search was successful and -1 if not, with errno set to the 3717987da915Sopenharmony_ci * error code. 3718987da915Sopenharmony_ci * 3719987da915Sopenharmony_ci * The following error codes are defined: 3720987da915Sopenharmony_ci * EINVAL Invalid arguments. 3721987da915Sopenharmony_ci * EIO I/O error or corrupt data structures found. 3722987da915Sopenharmony_ci * ENOMEM Not enough memory to allocate necessary buffers. 3723987da915Sopenharmony_ci * ENOSPC No attribute was found after 'type', only AT_END. 3724987da915Sopenharmony_ci */ 3725987da915Sopenharmony_ciint ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx) 3726987da915Sopenharmony_ci{ 3727987da915Sopenharmony_ci if (ntfs_attr_lookup(type, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { 3728987da915Sopenharmony_ci if (errno != ENOENT) 3729987da915Sopenharmony_ci return -1; 3730987da915Sopenharmony_ci if (ctx->attr->type == AT_END) { 3731987da915Sopenharmony_ci errno = ENOSPC; 3732987da915Sopenharmony_ci return -1; 3733987da915Sopenharmony_ci } 3734987da915Sopenharmony_ci } 3735987da915Sopenharmony_ci return 0; 3736987da915Sopenharmony_ci} 3737987da915Sopenharmony_ci 3738987da915Sopenharmony_ci/** 3739987da915Sopenharmony_ci * ntfs_attr_init_search_ctx - initialize an attribute search context 3740987da915Sopenharmony_ci * @ctx: attribute search context to initialize 3741987da915Sopenharmony_ci * @ni: ntfs inode with which to initialize the search context 3742987da915Sopenharmony_ci * @mrec: mft record with which to initialize the search context 3743987da915Sopenharmony_ci * 3744987da915Sopenharmony_ci * Initialize the attribute search context @ctx with @ni and @mrec. 3745987da915Sopenharmony_ci */ 3746987da915Sopenharmony_cistatic void ntfs_attr_init_search_ctx(ntfs_attr_search_ctx *ctx, 3747987da915Sopenharmony_ci ntfs_inode *ni, MFT_RECORD *mrec) 3748987da915Sopenharmony_ci{ 3749987da915Sopenharmony_ci if (!mrec) 3750987da915Sopenharmony_ci mrec = ni->mrec; 3751987da915Sopenharmony_ci ctx->mrec = mrec; 3752987da915Sopenharmony_ci /* Sanity checks are performed elsewhere. */ 3753987da915Sopenharmony_ci ctx->attr = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset)); 3754987da915Sopenharmony_ci ctx->is_first = TRUE; 3755987da915Sopenharmony_ci ctx->ntfs_ino = ni; 3756987da915Sopenharmony_ci ctx->al_entry = NULL; 3757987da915Sopenharmony_ci ctx->base_ntfs_ino = NULL; 3758987da915Sopenharmony_ci ctx->base_mrec = NULL; 3759987da915Sopenharmony_ci ctx->base_attr = NULL; 3760987da915Sopenharmony_ci} 3761987da915Sopenharmony_ci 3762987da915Sopenharmony_ci/** 3763987da915Sopenharmony_ci * ntfs_attr_reinit_search_ctx - reinitialize an attribute search context 3764987da915Sopenharmony_ci * @ctx: attribute search context to reinitialize 3765987da915Sopenharmony_ci * 3766987da915Sopenharmony_ci * Reinitialize the attribute search context @ctx. 3767987da915Sopenharmony_ci * 3768987da915Sopenharmony_ci * This is used when a search for a new attribute is being started to reset 3769987da915Sopenharmony_ci * the search context to the beginning. 3770987da915Sopenharmony_ci */ 3771987da915Sopenharmony_civoid ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx) 3772987da915Sopenharmony_ci{ 3773987da915Sopenharmony_ci if (!ctx->base_ntfs_ino) { 3774987da915Sopenharmony_ci /* No attribute list. */ 3775987da915Sopenharmony_ci ctx->is_first = TRUE; 3776987da915Sopenharmony_ci /* Sanity checks are performed elsewhere. */ 3777987da915Sopenharmony_ci ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + 3778987da915Sopenharmony_ci le16_to_cpu(ctx->mrec->attrs_offset)); 3779987da915Sopenharmony_ci /* 3780987da915Sopenharmony_ci * This needs resetting due to ntfs_external_attr_find() which 3781987da915Sopenharmony_ci * can leave it set despite having zeroed ctx->base_ntfs_ino. 3782987da915Sopenharmony_ci */ 3783987da915Sopenharmony_ci ctx->al_entry = NULL; 3784987da915Sopenharmony_ci return; 3785987da915Sopenharmony_ci } /* Attribute list. */ 3786987da915Sopenharmony_ci ntfs_attr_init_search_ctx(ctx, ctx->base_ntfs_ino, ctx->base_mrec); 3787987da915Sopenharmony_ci return; 3788987da915Sopenharmony_ci} 3789987da915Sopenharmony_ci 3790987da915Sopenharmony_ci/** 3791987da915Sopenharmony_ci * ntfs_attr_get_search_ctx - allocate/initialize a new attribute search context 3792987da915Sopenharmony_ci * @ni: ntfs inode with which to initialize the search context 3793987da915Sopenharmony_ci * @mrec: mft record with which to initialize the search context 3794987da915Sopenharmony_ci * 3795987da915Sopenharmony_ci * Allocate a new attribute search context, initialize it with @ni and @mrec, 3796987da915Sopenharmony_ci * and return it. Return NULL on error with errno set. 3797987da915Sopenharmony_ci * 3798987da915Sopenharmony_ci * @mrec can be NULL, in which case the mft record is taken from @ni. 3799987da915Sopenharmony_ci * 3800987da915Sopenharmony_ci * Note: For low level utilities which know what they are doing we allow @ni to 3801987da915Sopenharmony_ci * be NULL and @mrec to be set. Do NOT do this unless you understand the 3802987da915Sopenharmony_ci * implications!!! For example it is no longer safe to call ntfs_attr_lookup(). 3803987da915Sopenharmony_ci */ 3804987da915Sopenharmony_cintfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec) 3805987da915Sopenharmony_ci{ 3806987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 3807987da915Sopenharmony_ci 3808987da915Sopenharmony_ci if (!ni && !mrec) { 3809987da915Sopenharmony_ci errno = EINVAL; 3810987da915Sopenharmony_ci ntfs_log_perror("NULL arguments"); 3811987da915Sopenharmony_ci return NULL; 3812987da915Sopenharmony_ci } 3813987da915Sopenharmony_ci ctx = ntfs_malloc(sizeof(ntfs_attr_search_ctx)); 3814987da915Sopenharmony_ci if (ctx) 3815987da915Sopenharmony_ci ntfs_attr_init_search_ctx(ctx, ni, mrec); 3816987da915Sopenharmony_ci return ctx; 3817987da915Sopenharmony_ci} 3818987da915Sopenharmony_ci 3819987da915Sopenharmony_ci/** 3820987da915Sopenharmony_ci * ntfs_attr_put_search_ctx - release an attribute search context 3821987da915Sopenharmony_ci * @ctx: attribute search context to free 3822987da915Sopenharmony_ci * 3823987da915Sopenharmony_ci * Release the attribute search context @ctx. 3824987da915Sopenharmony_ci */ 3825987da915Sopenharmony_civoid ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx) 3826987da915Sopenharmony_ci{ 3827987da915Sopenharmony_ci // NOTE: save errno if it could change and function stays void! 3828987da915Sopenharmony_ci free(ctx); 3829987da915Sopenharmony_ci} 3830987da915Sopenharmony_ci 3831987da915Sopenharmony_ci/** 3832987da915Sopenharmony_ci * ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file 3833987da915Sopenharmony_ci * @vol: ntfs volume to which the attribute belongs 3834987da915Sopenharmony_ci * @type: attribute type which to find 3835987da915Sopenharmony_ci * 3836987da915Sopenharmony_ci * Search for the attribute definition record corresponding to the attribute 3837987da915Sopenharmony_ci * @type in the $AttrDef system file. 3838987da915Sopenharmony_ci * 3839987da915Sopenharmony_ci * Return the attribute type definition record if found and NULL if not found 3840987da915Sopenharmony_ci * or an error occurred. On error the error code is stored in errno. The 3841987da915Sopenharmony_ci * following error codes are defined: 3842987da915Sopenharmony_ci * ENOENT - The attribute @type is not specified in $AttrDef. 3843987da915Sopenharmony_ci * EINVAL - Invalid parameters (e.g. @vol is not valid). 3844987da915Sopenharmony_ci */ 3845987da915Sopenharmony_ciATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, 3846987da915Sopenharmony_ci const ATTR_TYPES type) 3847987da915Sopenharmony_ci{ 3848987da915Sopenharmony_ci ATTR_DEF *ad; 3849987da915Sopenharmony_ci 3850987da915Sopenharmony_ci if (!vol || !vol->attrdef || !type) { 3851987da915Sopenharmony_ci errno = EINVAL; 3852987da915Sopenharmony_ci ntfs_log_perror("%s: type=%d", __FUNCTION__, le32_to_cpu(type)); 3853987da915Sopenharmony_ci return NULL; 3854987da915Sopenharmony_ci } 3855987da915Sopenharmony_ci for (ad = vol->attrdef; ((ptrdiff_t)((u8*)ad - (u8*)vol->attrdef 3856987da915Sopenharmony_ci + sizeof(ATTR_DEF)) <= vol->attrdef_len) 3857987da915Sopenharmony_ci && ad->type; ++ad) { 3858987da915Sopenharmony_ci /* We haven't found it yet, carry on searching. */ 3859987da915Sopenharmony_ci if (le32_to_cpu(ad->type) < le32_to_cpu(type)) 3860987da915Sopenharmony_ci continue; 3861987da915Sopenharmony_ci /* We found the attribute; return it. */ 3862987da915Sopenharmony_ci if (ad->type == type) 3863987da915Sopenharmony_ci return ad; 3864987da915Sopenharmony_ci /* We have gone too far already. No point in continuing. */ 3865987da915Sopenharmony_ci break; 3866987da915Sopenharmony_ci } 3867987da915Sopenharmony_ci errno = ENOENT; 3868987da915Sopenharmony_ci ntfs_log_perror("%s: type=%d", __FUNCTION__, le32_to_cpu(type)); 3869987da915Sopenharmony_ci return NULL; 3870987da915Sopenharmony_ci} 3871987da915Sopenharmony_ci 3872987da915Sopenharmony_ci/** 3873987da915Sopenharmony_ci * ntfs_attr_size_bounds_check - check a size of an attribute type for validity 3874987da915Sopenharmony_ci * @vol: ntfs volume to which the attribute belongs 3875987da915Sopenharmony_ci * @type: attribute type which to check 3876987da915Sopenharmony_ci * @size: size which to check 3877987da915Sopenharmony_ci * 3878987da915Sopenharmony_ci * Check whether the @size in bytes is valid for an attribute of @type on the 3879987da915Sopenharmony_ci * ntfs volume @vol. This information is obtained from $AttrDef system file. 3880987da915Sopenharmony_ci * 3881987da915Sopenharmony_ci * Return 0 if valid and -1 if not valid or an error occurred. On error the 3882987da915Sopenharmony_ci * error code is stored in errno. The following error codes are defined: 3883987da915Sopenharmony_ci * ERANGE - @size is not valid for the attribute @type. 3884987da915Sopenharmony_ci * ENOENT - The attribute @type is not specified in $AttrDef. 3885987da915Sopenharmony_ci * EINVAL - Invalid parameters (e.g. @size is < 0 or @vol is not valid). 3886987da915Sopenharmony_ci */ 3887987da915Sopenharmony_ciint ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type, 3888987da915Sopenharmony_ci const s64 size) 3889987da915Sopenharmony_ci{ 3890987da915Sopenharmony_ci ATTR_DEF *ad; 3891987da915Sopenharmony_ci s64 min_size, max_size; 3892987da915Sopenharmony_ci 3893987da915Sopenharmony_ci if (size < 0) { 3894987da915Sopenharmony_ci errno = EINVAL; 3895987da915Sopenharmony_ci ntfs_log_perror("%s: size=%lld", __FUNCTION__, 3896987da915Sopenharmony_ci (long long)size); 3897987da915Sopenharmony_ci return -1; 3898987da915Sopenharmony_ci } 3899987da915Sopenharmony_ci 3900987da915Sopenharmony_ci /* 3901987da915Sopenharmony_ci * $ATTRIBUTE_LIST shouldn't be greater than 0x40000, otherwise 3902987da915Sopenharmony_ci * Windows would crash. This is not listed in the AttrDef. 3903987da915Sopenharmony_ci */ 3904987da915Sopenharmony_ci if (type == AT_ATTRIBUTE_LIST && size > 0x40000) { 3905987da915Sopenharmony_ci errno = ERANGE; 3906987da915Sopenharmony_ci ntfs_log_perror("Too large attrlist (%lld)", (long long)size); 3907987da915Sopenharmony_ci return -1; 3908987da915Sopenharmony_ci } 3909987da915Sopenharmony_ci 3910987da915Sopenharmony_ci ad = ntfs_attr_find_in_attrdef(vol, type); 3911987da915Sopenharmony_ci if (!ad) 3912987da915Sopenharmony_ci return -1; 3913987da915Sopenharmony_ci 3914987da915Sopenharmony_ci min_size = sle64_to_cpu(ad->min_size); 3915987da915Sopenharmony_ci max_size = sle64_to_cpu(ad->max_size); 3916987da915Sopenharmony_ci 3917987da915Sopenharmony_ci /* The $AttrDef generated by Windows specifies 2 as min_size for the 3918987da915Sopenharmony_ci * volume name attribute, but in reality Windows sets it to 0 when 3919987da915Sopenharmony_ci * clearing the volume name. If we want to be able to clear the volume 3920987da915Sopenharmony_ci * name we must also accept 0 as min_size, despite the $AttrDef 3921987da915Sopenharmony_ci * definition. */ 3922987da915Sopenharmony_ci if(type == AT_VOLUME_NAME) 3923987da915Sopenharmony_ci min_size = 0; 3924987da915Sopenharmony_ci 3925987da915Sopenharmony_ci if ((min_size && (size < min_size)) || 3926987da915Sopenharmony_ci ((max_size > 0) && (size > max_size))) { 3927987da915Sopenharmony_ci errno = ERANGE; 3928987da915Sopenharmony_ci ntfs_log_perror("Attr type %d size check failed (min,size,max=" 3929987da915Sopenharmony_ci "%lld,%lld,%lld)", le32_to_cpu(type), (long long)min_size, 3930987da915Sopenharmony_ci (long long)size, (long long)max_size); 3931987da915Sopenharmony_ci return -1; 3932987da915Sopenharmony_ci } 3933987da915Sopenharmony_ci return 0; 3934987da915Sopenharmony_ci} 3935987da915Sopenharmony_ci 3936987da915Sopenharmony_ci/** 3937987da915Sopenharmony_ci * ntfs_attr_can_be_non_resident - check if an attribute can be non-resident 3938987da915Sopenharmony_ci * @vol: ntfs volume to which the attribute belongs 3939987da915Sopenharmony_ci * @type: attribute type to check 3940987da915Sopenharmony_ci * @name: attribute name to check 3941987da915Sopenharmony_ci * @name_len: attribute name length 3942987da915Sopenharmony_ci * 3943987da915Sopenharmony_ci * Check whether the attribute of @type and @name with name length @name_len on 3944987da915Sopenharmony_ci * the ntfs volume @vol is allowed to be non-resident. This information is 3945987da915Sopenharmony_ci * obtained from $AttrDef system file and is augmented by rules imposed by 3946987da915Sopenharmony_ci * Microsoft (e.g. see http://support.microsoft.com/kb/974729/). 3947987da915Sopenharmony_ci * 3948987da915Sopenharmony_ci * Return 0 if the attribute is allowed to be non-resident and -1 if not or an 3949987da915Sopenharmony_ci * error occurred. On error the error code is stored in errno. The following 3950987da915Sopenharmony_ci * error codes are defined: 3951987da915Sopenharmony_ci * EPERM - The attribute is not allowed to be non-resident. 3952987da915Sopenharmony_ci * ENOENT - The attribute @type is not specified in $AttrDef. 3953987da915Sopenharmony_ci * EINVAL - Invalid parameters (e.g. @vol is not valid). 3954987da915Sopenharmony_ci */ 3955987da915Sopenharmony_cistatic int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPES type, 3956987da915Sopenharmony_ci const ntfschar *name, int name_len) 3957987da915Sopenharmony_ci{ 3958987da915Sopenharmony_ci ATTR_DEF *ad; 3959987da915Sopenharmony_ci BOOL allowed; 3960987da915Sopenharmony_ci 3961987da915Sopenharmony_ci /* 3962987da915Sopenharmony_ci * Microsoft has decreed that $LOGGED_UTILITY_STREAM attributes with a 3963987da915Sopenharmony_ci * name of $TXF_DATA must be resident despite the entry for 3964987da915Sopenharmony_ci * $LOGGED_UTILITY_STREAM in $AttrDef allowing them to be non-resident. 3965987da915Sopenharmony_ci * Failure to obey this on the root directory mft record of a volume 3966987da915Sopenharmony_ci * causes Windows Vista and later to see the volume as a RAW volume and 3967987da915Sopenharmony_ci * thus cannot mount it at all. 3968987da915Sopenharmony_ci */ 3969987da915Sopenharmony_ci if ((type == AT_LOGGED_UTILITY_STREAM) 3970987da915Sopenharmony_ci && name 3971987da915Sopenharmony_ci && ntfs_names_are_equal(TXF_DATA, 9, name, name_len, 3972987da915Sopenharmony_ci CASE_SENSITIVE, vol->upcase, vol->upcase_len)) 3973987da915Sopenharmony_ci allowed = FALSE; 3974987da915Sopenharmony_ci else { 3975987da915Sopenharmony_ci /* Find the attribute definition record in $AttrDef. */ 3976987da915Sopenharmony_ci ad = ntfs_attr_find_in_attrdef(vol, type); 3977987da915Sopenharmony_ci if (!ad) 3978987da915Sopenharmony_ci return -1; 3979987da915Sopenharmony_ci /* Check the flags and return the result. */ 3980987da915Sopenharmony_ci allowed = !(ad->flags & ATTR_DEF_RESIDENT); 3981987da915Sopenharmony_ci } 3982987da915Sopenharmony_ci if (!allowed) { 3983987da915Sopenharmony_ci errno = EPERM; 3984987da915Sopenharmony_ci ntfs_log_trace("Attribute can't be non-resident\n"); 3985987da915Sopenharmony_ci return -1; 3986987da915Sopenharmony_ci } 3987987da915Sopenharmony_ci return 0; 3988987da915Sopenharmony_ci} 3989987da915Sopenharmony_ci 3990987da915Sopenharmony_ci/** 3991987da915Sopenharmony_ci * ntfs_attr_can_be_resident - check if an attribute can be resident 3992987da915Sopenharmony_ci * @vol: ntfs volume to which the attribute belongs 3993987da915Sopenharmony_ci * @type: attribute type which to check 3994987da915Sopenharmony_ci * 3995987da915Sopenharmony_ci * Check whether the attribute of @type on the ntfs volume @vol is allowed to 3996987da915Sopenharmony_ci * be resident. This information is derived from our ntfs knowledge and may 3997987da915Sopenharmony_ci * not be completely accurate, especially when user defined attributes are 3998987da915Sopenharmony_ci * present. Basically we allow everything to be resident except for index 3999987da915Sopenharmony_ci * allocation and extended attribute attributes. 4000987da915Sopenharmony_ci * 4001987da915Sopenharmony_ci * Return 0 if the attribute is allowed to be resident and -1 if not or an 4002987da915Sopenharmony_ci * error occurred. On error the error code is stored in errno. The following 4003987da915Sopenharmony_ci * error codes are defined: 4004987da915Sopenharmony_ci * EPERM - The attribute is not allowed to be resident. 4005987da915Sopenharmony_ci * EINVAL - Invalid parameters (e.g. @vol is not valid). 4006987da915Sopenharmony_ci * 4007987da915Sopenharmony_ci * Warning: In the system file $MFT the attribute $Bitmap must be non-resident 4008987da915Sopenharmony_ci * otherwise windows will not boot (blue screen of death)! We cannot 4009987da915Sopenharmony_ci * check for this here as we don't know which inode's $Bitmap is being 4010987da915Sopenharmony_ci * asked about so the caller needs to special case this. 4011987da915Sopenharmony_ci */ 4012987da915Sopenharmony_ciint ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPES type) 4013987da915Sopenharmony_ci{ 4014987da915Sopenharmony_ci if (!vol || !vol->attrdef || !type) { 4015987da915Sopenharmony_ci errno = EINVAL; 4016987da915Sopenharmony_ci return -1; 4017987da915Sopenharmony_ci } 4018987da915Sopenharmony_ci if (type != AT_INDEX_ALLOCATION) 4019987da915Sopenharmony_ci return 0; 4020987da915Sopenharmony_ci 4021987da915Sopenharmony_ci ntfs_log_trace("Attribute can't be resident\n"); 4022987da915Sopenharmony_ci errno = EPERM; 4023987da915Sopenharmony_ci return -1; 4024987da915Sopenharmony_ci} 4025987da915Sopenharmony_ci 4026987da915Sopenharmony_ci/** 4027987da915Sopenharmony_ci * ntfs_make_room_for_attr - make room for an attribute inside an mft record 4028987da915Sopenharmony_ci * @m: mft record 4029987da915Sopenharmony_ci * @pos: position at which to make space 4030987da915Sopenharmony_ci * @size: byte size to make available at this position 4031987da915Sopenharmony_ci * 4032987da915Sopenharmony_ci * @pos points to the attribute in front of which we want to make space. 4033987da915Sopenharmony_ci * 4034987da915Sopenharmony_ci * Return 0 on success or -1 on error. On error the error code is stored in 4035987da915Sopenharmony_ci * errno. Possible error codes are: 4036987da915Sopenharmony_ci * ENOSPC - There is not enough space available to complete operation. The 4037987da915Sopenharmony_ci * caller has to make space before calling this. 4038987da915Sopenharmony_ci * EINVAL - Input parameters were faulty. 4039987da915Sopenharmony_ci */ 4040987da915Sopenharmony_ciint ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size) 4041987da915Sopenharmony_ci{ 4042987da915Sopenharmony_ci u32 biu; 4043987da915Sopenharmony_ci 4044987da915Sopenharmony_ci ntfs_log_trace("Entering for pos 0x%d, size %u.\n", 4045987da915Sopenharmony_ci (int)(pos - (u8*)m), (unsigned) size); 4046987da915Sopenharmony_ci 4047987da915Sopenharmony_ci /* Make size 8-byte alignment. */ 4048987da915Sopenharmony_ci size = (size + 7) & ~7; 4049987da915Sopenharmony_ci 4050987da915Sopenharmony_ci /* Rigorous consistency checks. */ 4051987da915Sopenharmony_ci if (!m || !pos || pos < (u8*)m) { 4052987da915Sopenharmony_ci errno = EINVAL; 4053987da915Sopenharmony_ci ntfs_log_perror("%s: pos=%p m=%p", __FUNCTION__, pos, m); 4054987da915Sopenharmony_ci return -1; 4055987da915Sopenharmony_ci } 4056987da915Sopenharmony_ci /* The -8 is for the attribute terminator. */ 4057987da915Sopenharmony_ci if (pos - (u8*)m > (int)le32_to_cpu(m->bytes_in_use) - 8) { 4058987da915Sopenharmony_ci errno = EINVAL; 4059987da915Sopenharmony_ci return -1; 4060987da915Sopenharmony_ci } 4061987da915Sopenharmony_ci /* Nothing to do. */ 4062987da915Sopenharmony_ci if (!size) 4063987da915Sopenharmony_ci return 0; 4064987da915Sopenharmony_ci 4065987da915Sopenharmony_ci biu = le32_to_cpu(m->bytes_in_use); 4066987da915Sopenharmony_ci /* Do we have enough space? */ 4067987da915Sopenharmony_ci if (biu + size > le32_to_cpu(m->bytes_allocated) || 4068987da915Sopenharmony_ci pos + size > (u8*)m + le32_to_cpu(m->bytes_allocated)) { 4069987da915Sopenharmony_ci errno = ENOSPC; 4070987da915Sopenharmony_ci ntfs_log_trace("No enough space in the MFT record\n"); 4071987da915Sopenharmony_ci return -1; 4072987da915Sopenharmony_ci } 4073987da915Sopenharmony_ci /* Move everything after pos to pos + size. */ 4074987da915Sopenharmony_ci memmove(pos + size, pos, biu - (pos - (u8*)m)); 4075987da915Sopenharmony_ci /* Update mft record. */ 4076987da915Sopenharmony_ci m->bytes_in_use = cpu_to_le32(biu + size); 4077987da915Sopenharmony_ci return 0; 4078987da915Sopenharmony_ci} 4079987da915Sopenharmony_ci 4080987da915Sopenharmony_ci/** 4081987da915Sopenharmony_ci * ntfs_resident_attr_record_add - add resident attribute to inode 4082987da915Sopenharmony_ci * @ni: opened ntfs inode to which MFT record add attribute 4083987da915Sopenharmony_ci * @type: type of the new attribute 4084987da915Sopenharmony_ci * @name: name of the new attribute 4085987da915Sopenharmony_ci * @name_len: name length of the new attribute 4086987da915Sopenharmony_ci * @val: value of the new attribute 4087987da915Sopenharmony_ci * @size: size of new attribute (length of @val, if @val != NULL) 4088987da915Sopenharmony_ci * @flags: flags of the new attribute 4089987da915Sopenharmony_ci * 4090987da915Sopenharmony_ci * Return offset to attribute from the beginning of the mft record on success 4091987da915Sopenharmony_ci * and -1 on error. On error the error code is stored in errno. 4092987da915Sopenharmony_ci * Possible error codes are: 4093987da915Sopenharmony_ci * EINVAL - Invalid arguments passed to function. 4094987da915Sopenharmony_ci * EEXIST - Attribute of such type and with same name already exists. 4095987da915Sopenharmony_ci * EIO - I/O error occurred or damaged filesystem. 4096987da915Sopenharmony_ci */ 4097987da915Sopenharmony_ciint ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, 4098987da915Sopenharmony_ci const ntfschar *name, u8 name_len, const u8 *val, 4099987da915Sopenharmony_ci u32 size, ATTR_FLAGS data_flags) 4100987da915Sopenharmony_ci{ 4101987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 4102987da915Sopenharmony_ci u32 length; 4103987da915Sopenharmony_ci ATTR_RECORD *a; 4104987da915Sopenharmony_ci MFT_RECORD *m; 4105987da915Sopenharmony_ci int err, offset; 4106987da915Sopenharmony_ci ntfs_inode *base_ni; 4107987da915Sopenharmony_ci 4108987da915Sopenharmony_ci ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, flags 0x%x.\n", 4109987da915Sopenharmony_ci (long long) ni->mft_no, (unsigned) le32_to_cpu(type), (unsigned) le16_to_cpu(data_flags)); 4110987da915Sopenharmony_ci 4111987da915Sopenharmony_ci if (!ni || (!name && name_len)) { 4112987da915Sopenharmony_ci errno = EINVAL; 4113987da915Sopenharmony_ci return -1; 4114987da915Sopenharmony_ci } 4115987da915Sopenharmony_ci 4116987da915Sopenharmony_ci if (ntfs_attr_can_be_resident(ni->vol, type)) { 4117987da915Sopenharmony_ci if (errno == EPERM) 4118987da915Sopenharmony_ci ntfs_log_trace("Attribute can't be resident.\n"); 4119987da915Sopenharmony_ci else 4120987da915Sopenharmony_ci ntfs_log_trace("ntfs_attr_can_be_resident failed.\n"); 4121987da915Sopenharmony_ci return -1; 4122987da915Sopenharmony_ci } 4123987da915Sopenharmony_ci 4124987da915Sopenharmony_ci /* Locate place where record should be. */ 4125987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(ni, NULL); 4126987da915Sopenharmony_ci if (!ctx) 4127987da915Sopenharmony_ci return -1; 4128987da915Sopenharmony_ci /* 4129987da915Sopenharmony_ci * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for 4130987da915Sopenharmony_ci * attribute in @ni->mrec, not any extent inode in case if @ni is base 4131987da915Sopenharmony_ci * file record. 4132987da915Sopenharmony_ci */ 4133987da915Sopenharmony_ci if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, val, size, 4134987da915Sopenharmony_ci ctx)) { 4135987da915Sopenharmony_ci err = EEXIST; 4136987da915Sopenharmony_ci ntfs_log_trace("Attribute already present.\n"); 4137987da915Sopenharmony_ci goto put_err_out; 4138987da915Sopenharmony_ci } 4139987da915Sopenharmony_ci if (errno != ENOENT) { 4140987da915Sopenharmony_ci err = EIO; 4141987da915Sopenharmony_ci goto put_err_out; 4142987da915Sopenharmony_ci } 4143987da915Sopenharmony_ci a = ctx->attr; 4144987da915Sopenharmony_ci m = ctx->mrec; 4145987da915Sopenharmony_ci 4146987da915Sopenharmony_ci /* Make room for attribute. */ 4147987da915Sopenharmony_ci length = offsetof(ATTR_RECORD, resident_end) + 4148987da915Sopenharmony_ci ((name_len * sizeof(ntfschar) + 7) & ~7) + 4149987da915Sopenharmony_ci ((size + 7) & ~7); 4150987da915Sopenharmony_ci if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) { 4151987da915Sopenharmony_ci err = errno; 4152987da915Sopenharmony_ci ntfs_log_trace("Failed to make room for attribute.\n"); 4153987da915Sopenharmony_ci goto put_err_out; 4154987da915Sopenharmony_ci } 4155987da915Sopenharmony_ci 4156987da915Sopenharmony_ci /* Setup record fields. */ 4157987da915Sopenharmony_ci offset = ((u8*)a - (u8*)m); 4158987da915Sopenharmony_ci a->type = type; 4159987da915Sopenharmony_ci a->length = cpu_to_le32(length); 4160987da915Sopenharmony_ci a->non_resident = 0; 4161987da915Sopenharmony_ci a->name_length = name_len; 4162987da915Sopenharmony_ci a->name_offset = (name_len 4163987da915Sopenharmony_ci ? const_cpu_to_le16(offsetof(ATTR_RECORD, resident_end)) 4164987da915Sopenharmony_ci : const_cpu_to_le16(0)); 4165987da915Sopenharmony_ci a->flags = data_flags; 4166987da915Sopenharmony_ci a->instance = m->next_attr_instance; 4167987da915Sopenharmony_ci a->value_length = cpu_to_le32(size); 4168987da915Sopenharmony_ci a->value_offset = cpu_to_le16(length - ((size + 7) & ~7)); 4169987da915Sopenharmony_ci if (val) 4170987da915Sopenharmony_ci memcpy((u8*)a + le16_to_cpu(a->value_offset), val, size); 4171987da915Sopenharmony_ci else 4172987da915Sopenharmony_ci memset((u8*)a + le16_to_cpu(a->value_offset), 0, size); 4173987da915Sopenharmony_ci if (type == AT_FILE_NAME) 4174987da915Sopenharmony_ci a->resident_flags = RESIDENT_ATTR_IS_INDEXED; 4175987da915Sopenharmony_ci else 4176987da915Sopenharmony_ci a->resident_flags = 0; 4177987da915Sopenharmony_ci if (name_len) 4178987da915Sopenharmony_ci memcpy((u8*)a + le16_to_cpu(a->name_offset), 4179987da915Sopenharmony_ci name, sizeof(ntfschar) * name_len); 4180987da915Sopenharmony_ci m->next_attr_instance = 4181987da915Sopenharmony_ci cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff); 4182987da915Sopenharmony_ci if (ni->nr_extents == -1) 4183987da915Sopenharmony_ci base_ni = ni->base_ni; 4184987da915Sopenharmony_ci else 4185987da915Sopenharmony_ci base_ni = ni; 4186987da915Sopenharmony_ci if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { 4187987da915Sopenharmony_ci if (ntfs_attrlist_entry_add(ni, a)) { 4188987da915Sopenharmony_ci err = errno; 4189987da915Sopenharmony_ci ntfs_attr_record_resize(m, a, 0); 4190987da915Sopenharmony_ci ntfs_log_trace("Failed add attribute entry to " 4191987da915Sopenharmony_ci "ATTRIBUTE_LIST.\n"); 4192987da915Sopenharmony_ci goto put_err_out; 4193987da915Sopenharmony_ci } 4194987da915Sopenharmony_ci } 4195987da915Sopenharmony_ci if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY 4196987da915Sopenharmony_ci ? type == AT_INDEX_ROOT && name == NTFS_INDEX_I30 4197987da915Sopenharmony_ci : type == AT_DATA && name == AT_UNNAMED) { 4198987da915Sopenharmony_ci ni->data_size = size; 4199987da915Sopenharmony_ci ni->allocated_size = (size + 7) & ~7; 4200987da915Sopenharmony_ci set_nino_flag(ni,KnownSize); 4201987da915Sopenharmony_ci } 4202987da915Sopenharmony_ci ntfs_inode_mark_dirty(ni); 4203987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 4204987da915Sopenharmony_ci return offset; 4205987da915Sopenharmony_ciput_err_out: 4206987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 4207987da915Sopenharmony_ci errno = err; 4208987da915Sopenharmony_ci return -1; 4209987da915Sopenharmony_ci} 4210987da915Sopenharmony_ci 4211987da915Sopenharmony_ci/** 4212987da915Sopenharmony_ci * ntfs_non_resident_attr_record_add - add extent of non-resident attribute 4213987da915Sopenharmony_ci * @ni: opened ntfs inode to which MFT record add attribute 4214987da915Sopenharmony_ci * @type: type of the new attribute extent 4215987da915Sopenharmony_ci * @name: name of the new attribute extent 4216987da915Sopenharmony_ci * @name_len: name length of the new attribute extent 4217987da915Sopenharmony_ci * @lowest_vcn: lowest vcn of the new attribute extent 4218987da915Sopenharmony_ci * @dataruns_size: dataruns size of the new attribute extent 4219987da915Sopenharmony_ci * @flags: flags of the new attribute extent 4220987da915Sopenharmony_ci * 4221987da915Sopenharmony_ci * Return offset to attribute from the beginning of the mft record on success 4222987da915Sopenharmony_ci * and -1 on error. On error the error code is stored in errno. 4223987da915Sopenharmony_ci * Possible error codes are: 4224987da915Sopenharmony_ci * EINVAL - Invalid arguments passed to function. 4225987da915Sopenharmony_ci * EEXIST - Attribute of such type, with same lowest vcn and with same 4226987da915Sopenharmony_ci * name already exists. 4227987da915Sopenharmony_ci * EIO - I/O error occurred or damaged filesystem. 4228987da915Sopenharmony_ci */ 4229987da915Sopenharmony_ciint ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, 4230987da915Sopenharmony_ci const ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size, 4231987da915Sopenharmony_ci ATTR_FLAGS flags) 4232987da915Sopenharmony_ci{ 4233987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 4234987da915Sopenharmony_ci u32 length; 4235987da915Sopenharmony_ci ATTR_RECORD *a; 4236987da915Sopenharmony_ci MFT_RECORD *m; 4237987da915Sopenharmony_ci ntfs_inode *base_ni; 4238987da915Sopenharmony_ci int err, offset; 4239987da915Sopenharmony_ci 4240987da915Sopenharmony_ci ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld, " 4241987da915Sopenharmony_ci "dataruns_size %d, flags 0x%x.\n", 4242987da915Sopenharmony_ci (long long) ni->mft_no, (unsigned) le32_to_cpu(type), 4243987da915Sopenharmony_ci (long long) lowest_vcn, dataruns_size, (unsigned) le16_to_cpu(flags)); 4244987da915Sopenharmony_ci 4245987da915Sopenharmony_ci if (!ni || dataruns_size <= 0 || (!name && name_len)) { 4246987da915Sopenharmony_ci errno = EINVAL; 4247987da915Sopenharmony_ci return -1; 4248987da915Sopenharmony_ci } 4249987da915Sopenharmony_ci 4250987da915Sopenharmony_ci if (ntfs_attr_can_be_non_resident(ni->vol, type, name, name_len)) { 4251987da915Sopenharmony_ci if (errno == EPERM) 4252987da915Sopenharmony_ci ntfs_log_perror("Attribute can't be non resident"); 4253987da915Sopenharmony_ci else 4254987da915Sopenharmony_ci ntfs_log_perror("ntfs_attr_can_be_non_resident failed"); 4255987da915Sopenharmony_ci return -1; 4256987da915Sopenharmony_ci } 4257987da915Sopenharmony_ci 4258987da915Sopenharmony_ci /* Locate place where record should be. */ 4259987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(ni, NULL); 4260987da915Sopenharmony_ci if (!ctx) 4261987da915Sopenharmony_ci return -1; 4262987da915Sopenharmony_ci /* 4263987da915Sopenharmony_ci * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for 4264987da915Sopenharmony_ci * attribute in @ni->mrec, not any extent inode in case if @ni is base 4265987da915Sopenharmony_ci * file record. 4266987da915Sopenharmony_ci */ 4267987da915Sopenharmony_ci if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, NULL, 0, 4268987da915Sopenharmony_ci ctx)) { 4269987da915Sopenharmony_ci err = EEXIST; 4270987da915Sopenharmony_ci ntfs_log_perror("Attribute 0x%x already present", le32_to_cpu(type)); 4271987da915Sopenharmony_ci goto put_err_out; 4272987da915Sopenharmony_ci } 4273987da915Sopenharmony_ci if (errno != ENOENT) { 4274987da915Sopenharmony_ci ntfs_log_perror("ntfs_attr_find failed"); 4275987da915Sopenharmony_ci err = EIO; 4276987da915Sopenharmony_ci goto put_err_out; 4277987da915Sopenharmony_ci } 4278987da915Sopenharmony_ci a = ctx->attr; 4279987da915Sopenharmony_ci m = ctx->mrec; 4280987da915Sopenharmony_ci 4281987da915Sopenharmony_ci /* Make room for attribute. */ 4282987da915Sopenharmony_ci dataruns_size = (dataruns_size + 7) & ~7; 4283987da915Sopenharmony_ci length = offsetof(ATTR_RECORD, compressed_size) + ((sizeof(ntfschar) * 4284987da915Sopenharmony_ci name_len + 7) & ~7) + dataruns_size + 4285987da915Sopenharmony_ci ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? 4286987da915Sopenharmony_ci sizeof(a->compressed_size) : 0); 4287987da915Sopenharmony_ci if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) { 4288987da915Sopenharmony_ci err = errno; 4289987da915Sopenharmony_ci ntfs_log_perror("Failed to make room for attribute"); 4290987da915Sopenharmony_ci goto put_err_out; 4291987da915Sopenharmony_ci } 4292987da915Sopenharmony_ci 4293987da915Sopenharmony_ci /* Setup record fields. */ 4294987da915Sopenharmony_ci a->type = type; 4295987da915Sopenharmony_ci a->length = cpu_to_le32(length); 4296987da915Sopenharmony_ci a->non_resident = 1; 4297987da915Sopenharmony_ci a->name_length = name_len; 4298987da915Sopenharmony_ci a->name_offset = cpu_to_le16(offsetof(ATTR_RECORD, compressed_size) + 4299987da915Sopenharmony_ci ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? 4300987da915Sopenharmony_ci sizeof(a->compressed_size) : 0)); 4301987da915Sopenharmony_ci a->flags = flags; 4302987da915Sopenharmony_ci a->instance = m->next_attr_instance; 4303987da915Sopenharmony_ci a->lowest_vcn = cpu_to_sle64(lowest_vcn); 4304987da915Sopenharmony_ci a->mapping_pairs_offset = cpu_to_le16(length - dataruns_size); 4305987da915Sopenharmony_ci a->compression_unit = (flags & ATTR_IS_COMPRESSED) 4306987da915Sopenharmony_ci ? STANDARD_COMPRESSION_UNIT : 0; 4307987da915Sopenharmony_ci /* If @lowest_vcn == 0, than setup empty attribute. */ 4308987da915Sopenharmony_ci if (!lowest_vcn) { 4309987da915Sopenharmony_ci a->highest_vcn = const_cpu_to_sle64(-1); 4310987da915Sopenharmony_ci a->allocated_size = const_cpu_to_sle64(0); 4311987da915Sopenharmony_ci a->data_size = const_cpu_to_sle64(0); 4312987da915Sopenharmony_ci a->initialized_size = const_cpu_to_sle64(0); 4313987da915Sopenharmony_ci /* Set empty mapping pairs. */ 4314987da915Sopenharmony_ci *((u8*)a + le16_to_cpu(a->mapping_pairs_offset)) = 0; 4315987da915Sopenharmony_ci } 4316987da915Sopenharmony_ci if (name_len) 4317987da915Sopenharmony_ci memcpy((u8*)a + le16_to_cpu(a->name_offset), 4318987da915Sopenharmony_ci name, sizeof(ntfschar) * name_len); 4319987da915Sopenharmony_ci m->next_attr_instance = 4320987da915Sopenharmony_ci cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff); 4321987da915Sopenharmony_ci if (ni->nr_extents == -1) 4322987da915Sopenharmony_ci base_ni = ni->base_ni; 4323987da915Sopenharmony_ci else 4324987da915Sopenharmony_ci base_ni = ni; 4325987da915Sopenharmony_ci if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { 4326987da915Sopenharmony_ci if (ntfs_attrlist_entry_add(ni, a)) { 4327987da915Sopenharmony_ci err = errno; 4328987da915Sopenharmony_ci ntfs_log_perror("Failed add attr entry to attrlist"); 4329987da915Sopenharmony_ci ntfs_attr_record_resize(m, a, 0); 4330987da915Sopenharmony_ci goto put_err_out; 4331987da915Sopenharmony_ci } 4332987da915Sopenharmony_ci } 4333987da915Sopenharmony_ci ntfs_inode_mark_dirty(ni); 4334987da915Sopenharmony_ci /* 4335987da915Sopenharmony_ci * Locate offset from start of the MFT record where new attribute is 4336987da915Sopenharmony_ci * placed. We need relookup it, because record maybe moved during 4337987da915Sopenharmony_ci * update of attribute list. 4338987da915Sopenharmony_ci */ 4339987da915Sopenharmony_ci ntfs_attr_reinit_search_ctx(ctx); 4340987da915Sopenharmony_ci if (ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, 4341987da915Sopenharmony_ci lowest_vcn, NULL, 0, ctx)) { 4342987da915Sopenharmony_ci ntfs_log_perror("%s: attribute lookup failed", __FUNCTION__); 4343987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 4344987da915Sopenharmony_ci return -1; 4345987da915Sopenharmony_ci 4346987da915Sopenharmony_ci } 4347987da915Sopenharmony_ci offset = (u8*)ctx->attr - (u8*)ctx->mrec; 4348987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 4349987da915Sopenharmony_ci return offset; 4350987da915Sopenharmony_ciput_err_out: 4351987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 4352987da915Sopenharmony_ci errno = err; 4353987da915Sopenharmony_ci return -1; 4354987da915Sopenharmony_ci} 4355987da915Sopenharmony_ci 4356987da915Sopenharmony_ci/** 4357987da915Sopenharmony_ci * ntfs_attr_record_rm - remove attribute extent 4358987da915Sopenharmony_ci * @ctx: search context describing the attribute which should be removed 4359987da915Sopenharmony_ci * 4360987da915Sopenharmony_ci * If this function succeed, user should reinit search context if he/she wants 4361987da915Sopenharmony_ci * use it anymore. 4362987da915Sopenharmony_ci * 4363987da915Sopenharmony_ci * Return 0 on success and -1 on error. On error the error code is stored in 4364987da915Sopenharmony_ci * errno. Possible error codes are: 4365987da915Sopenharmony_ci * EINVAL - Invalid arguments passed to function. 4366987da915Sopenharmony_ci * EIO - I/O error occurred or damaged filesystem. 4367987da915Sopenharmony_ci */ 4368987da915Sopenharmony_ciint ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx) 4369987da915Sopenharmony_ci{ 4370987da915Sopenharmony_ci ntfs_inode *base_ni, *ni; 4371987da915Sopenharmony_ci ATTR_TYPES type; 4372987da915Sopenharmony_ci 4373987da915Sopenharmony_ci if (!ctx || !ctx->ntfs_ino || !ctx->mrec || !ctx->attr) { 4374987da915Sopenharmony_ci errno = EINVAL; 4375987da915Sopenharmony_ci return -1; 4376987da915Sopenharmony_ci } 4377987da915Sopenharmony_ci 4378987da915Sopenharmony_ci ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", 4379987da915Sopenharmony_ci (long long) ctx->ntfs_ino->mft_no, 4380987da915Sopenharmony_ci (unsigned) le32_to_cpu(ctx->attr->type)); 4381987da915Sopenharmony_ci type = ctx->attr->type; 4382987da915Sopenharmony_ci ni = ctx->ntfs_ino; 4383987da915Sopenharmony_ci if (ctx->base_ntfs_ino) 4384987da915Sopenharmony_ci base_ni = ctx->base_ntfs_ino; 4385987da915Sopenharmony_ci else 4386987da915Sopenharmony_ci base_ni = ctx->ntfs_ino; 4387987da915Sopenharmony_ci 4388987da915Sopenharmony_ci /* Remove attribute itself. */ 4389987da915Sopenharmony_ci if (ntfs_attr_record_resize(ctx->mrec, ctx->attr, 0)) { 4390987da915Sopenharmony_ci ntfs_log_trace("Couldn't remove attribute record. Bug or damaged MFT " 4391987da915Sopenharmony_ci "record.\n"); 4392987da915Sopenharmony_ci if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) 4393987da915Sopenharmony_ci if (ntfs_attrlist_entry_add(ni, ctx->attr)) 4394987da915Sopenharmony_ci ntfs_log_trace("Rollback failed. Leaving inconstant " 4395987da915Sopenharmony_ci "metadata.\n"); 4396987da915Sopenharmony_ci errno = EIO; 4397987da915Sopenharmony_ci return -1; 4398987da915Sopenharmony_ci } 4399987da915Sopenharmony_ci ntfs_inode_mark_dirty(ni); 4400987da915Sopenharmony_ci 4401987da915Sopenharmony_ci /* 4402987da915Sopenharmony_ci * Remove record from $ATTRIBUTE_LIST if present and we don't want 4403987da915Sopenharmony_ci * delete $ATTRIBUTE_LIST itself. 4404987da915Sopenharmony_ci */ 4405987da915Sopenharmony_ci if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) { 4406987da915Sopenharmony_ci if (ntfs_attrlist_entry_rm(ctx)) { 4407987da915Sopenharmony_ci ntfs_log_trace("Couldn't delete record from " 4408987da915Sopenharmony_ci "$ATTRIBUTE_LIST.\n"); 4409987da915Sopenharmony_ci return -1; 4410987da915Sopenharmony_ci } 4411987da915Sopenharmony_ci } 4412987da915Sopenharmony_ci 4413987da915Sopenharmony_ci /* Post $ATTRIBUTE_LIST delete setup. */ 4414987da915Sopenharmony_ci if (type == AT_ATTRIBUTE_LIST) { 4415987da915Sopenharmony_ci if (NInoAttrList(base_ni) && base_ni->attr_list) 4416987da915Sopenharmony_ci free(base_ni->attr_list); 4417987da915Sopenharmony_ci base_ni->attr_list = NULL; 4418987da915Sopenharmony_ci NInoClearAttrList(base_ni); 4419987da915Sopenharmony_ci NInoAttrListClearDirty(base_ni); 4420987da915Sopenharmony_ci } 4421987da915Sopenharmony_ci 4422987da915Sopenharmony_ci /* Free MFT record, if it doesn't contain attributes. */ 4423987da915Sopenharmony_ci if (le32_to_cpu(ctx->mrec->bytes_in_use) - 4424987da915Sopenharmony_ci le16_to_cpu(ctx->mrec->attrs_offset) == 8) { 4425987da915Sopenharmony_ci if (ntfs_mft_record_free(ni->vol, ni)) { 4426987da915Sopenharmony_ci // FIXME: We need rollback here. 4427987da915Sopenharmony_ci ntfs_log_trace("Couldn't free MFT record.\n"); 4428987da915Sopenharmony_ci errno = EIO; 4429987da915Sopenharmony_ci return -1; 4430987da915Sopenharmony_ci } 4431987da915Sopenharmony_ci /* Remove done if we freed base inode. */ 4432987da915Sopenharmony_ci if (ni == base_ni) 4433987da915Sopenharmony_ci return 0; 4434987da915Sopenharmony_ci } 4435987da915Sopenharmony_ci 4436987da915Sopenharmony_ci if (type == AT_ATTRIBUTE_LIST || !NInoAttrList(base_ni)) 4437987da915Sopenharmony_ci return 0; 4438987da915Sopenharmony_ci 4439987da915Sopenharmony_ci /* Remove attribute list if we don't need it any more. */ 4440987da915Sopenharmony_ci if (!ntfs_attrlist_need(base_ni)) { 4441987da915Sopenharmony_ci ntfs_attr_reinit_search_ctx(ctx); 4442987da915Sopenharmony_ci if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, CASE_SENSITIVE, 4443987da915Sopenharmony_ci 0, NULL, 0, ctx)) { 4444987da915Sopenharmony_ci /* 4445987da915Sopenharmony_ci * FIXME: Should we succeed here? Definitely something 4446987da915Sopenharmony_ci * goes wrong because NInoAttrList(base_ni) returned 4447987da915Sopenharmony_ci * that we have got attribute list. 4448987da915Sopenharmony_ci */ 4449987da915Sopenharmony_ci ntfs_log_trace("Couldn't find attribute list. Succeed " 4450987da915Sopenharmony_ci "anyway.\n"); 4451987da915Sopenharmony_ci return 0; 4452987da915Sopenharmony_ci } 4453987da915Sopenharmony_ci /* Deallocate clusters. */ 4454987da915Sopenharmony_ci if (ctx->attr->non_resident) { 4455987da915Sopenharmony_ci runlist *al_rl; 4456987da915Sopenharmony_ci 4457987da915Sopenharmony_ci al_rl = ntfs_mapping_pairs_decompress(base_ni->vol, 4458987da915Sopenharmony_ci ctx->attr, NULL); 4459987da915Sopenharmony_ci if (!al_rl) { 4460987da915Sopenharmony_ci ntfs_log_trace("Couldn't decompress attribute list " 4461987da915Sopenharmony_ci "runlist. Succeed anyway.\n"); 4462987da915Sopenharmony_ci return 0; 4463987da915Sopenharmony_ci } 4464987da915Sopenharmony_ci if (ntfs_cluster_free_from_rl(base_ni->vol, al_rl)) { 4465987da915Sopenharmony_ci ntfs_log_trace("Leaking clusters! Run chkdsk. " 4466987da915Sopenharmony_ci "Couldn't free clusters from " 4467987da915Sopenharmony_ci "attribute list runlist.\n"); 4468987da915Sopenharmony_ci } 4469987da915Sopenharmony_ci free(al_rl); 4470987da915Sopenharmony_ci } 4471987da915Sopenharmony_ci /* Remove attribute record itself. */ 4472987da915Sopenharmony_ci if (ntfs_attr_record_rm(ctx)) { 4473987da915Sopenharmony_ci /* 4474987da915Sopenharmony_ci * FIXME: Should we succeed here? BTW, chkdsk doesn't 4475987da915Sopenharmony_ci * complain if it find MFT record with attribute list, 4476987da915Sopenharmony_ci * but without extents. 4477987da915Sopenharmony_ci */ 4478987da915Sopenharmony_ci ntfs_log_trace("Couldn't remove attribute list. Succeed " 4479987da915Sopenharmony_ci "anyway.\n"); 4480987da915Sopenharmony_ci return 0; 4481987da915Sopenharmony_ci } 4482987da915Sopenharmony_ci } 4483987da915Sopenharmony_ci return 0; 4484987da915Sopenharmony_ci} 4485987da915Sopenharmony_ci 4486987da915Sopenharmony_ci/** 4487987da915Sopenharmony_ci * ntfs_attr_add - add attribute to inode 4488987da915Sopenharmony_ci * @ni: opened ntfs inode to which add attribute 4489987da915Sopenharmony_ci * @type: type of the new attribute 4490987da915Sopenharmony_ci * @name: name in unicode of the new attribute 4491987da915Sopenharmony_ci * @name_len: name length in unicode characters of the new attribute 4492987da915Sopenharmony_ci * @val: value of new attribute 4493987da915Sopenharmony_ci * @size: size of the new attribute / length of @val (if specified) 4494987da915Sopenharmony_ci * 4495987da915Sopenharmony_ci * @val should always be specified for always resident attributes (eg. FILE_NAME 4496987da915Sopenharmony_ci * attribute), for attributes that can become non-resident @val can be NULL 4497987da915Sopenharmony_ci * (eg. DATA attribute). @size can be specified even if @val is NULL, in this 4498987da915Sopenharmony_ci * case data size will be equal to @size and initialized size will be equal 4499987da915Sopenharmony_ci * to 0. 4500987da915Sopenharmony_ci * 4501987da915Sopenharmony_ci * If inode haven't got enough space to add attribute, add attribute to one of 4502987da915Sopenharmony_ci * it extents, if no extents present or no one of them have enough space, than 4503987da915Sopenharmony_ci * allocate new extent and add attribute to it. 4504987da915Sopenharmony_ci * 4505987da915Sopenharmony_ci * If on one of this steps attribute list is needed but not present, than it is 4506987da915Sopenharmony_ci * added transparently to caller. So, this function should not be called with 4507987da915Sopenharmony_ci * @type == AT_ATTRIBUTE_LIST, if you really need to add attribute list call 4508987da915Sopenharmony_ci * ntfs_inode_add_attrlist instead. 4509987da915Sopenharmony_ci * 4510987da915Sopenharmony_ci * On success return 0. On error return -1 with errno set to the error code. 4511987da915Sopenharmony_ci */ 4512987da915Sopenharmony_ciint ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, 4513987da915Sopenharmony_ci ntfschar *name, u8 name_len, const u8 *val, s64 size) 4514987da915Sopenharmony_ci{ 4515987da915Sopenharmony_ci u32 attr_rec_size; 4516987da915Sopenharmony_ci int err, i, offset; 4517987da915Sopenharmony_ci BOOL is_resident; 4518987da915Sopenharmony_ci BOOL can_be_non_resident = FALSE; 4519987da915Sopenharmony_ci ntfs_inode *attr_ni; 4520987da915Sopenharmony_ci ntfs_attr *na; 4521987da915Sopenharmony_ci ATTR_FLAGS data_flags; 4522987da915Sopenharmony_ci 4523987da915Sopenharmony_ci if (!ni || size < 0 || type == AT_ATTRIBUTE_LIST) { 4524987da915Sopenharmony_ci errno = EINVAL; 4525987da915Sopenharmony_ci ntfs_log_perror("%s: ni=%p size=%lld", __FUNCTION__, ni, 4526987da915Sopenharmony_ci (long long)size); 4527987da915Sopenharmony_ci return -1; 4528987da915Sopenharmony_ci } 4529987da915Sopenharmony_ci 4530987da915Sopenharmony_ci ntfs_log_trace("Entering for inode %lld, attr %x, size %lld.\n", 4531987da915Sopenharmony_ci (long long)ni->mft_no, le32_to_cpu(type), (long long)size); 4532987da915Sopenharmony_ci 4533987da915Sopenharmony_ci if (ni->nr_extents == -1) 4534987da915Sopenharmony_ci ni = ni->base_ni; 4535987da915Sopenharmony_ci 4536987da915Sopenharmony_ci /* Check the attribute type and the size. */ 4537987da915Sopenharmony_ci if (ntfs_attr_size_bounds_check(ni->vol, type, size)) { 4538987da915Sopenharmony_ci if (errno == ENOENT) 4539987da915Sopenharmony_ci errno = EIO; 4540987da915Sopenharmony_ci return -1; 4541987da915Sopenharmony_ci } 4542987da915Sopenharmony_ci 4543987da915Sopenharmony_ci /* Sanity checks for always resident attributes. */ 4544987da915Sopenharmony_ci if (ntfs_attr_can_be_non_resident(ni->vol, type, name, name_len)) { 4545987da915Sopenharmony_ci if (errno != EPERM) { 4546987da915Sopenharmony_ci err = errno; 4547987da915Sopenharmony_ci ntfs_log_perror("ntfs_attr_can_be_non_resident failed"); 4548987da915Sopenharmony_ci goto err_out; 4549987da915Sopenharmony_ci } 4550987da915Sopenharmony_ci /* @val is mandatory. */ 4551987da915Sopenharmony_ci if (!val) { 4552987da915Sopenharmony_ci errno = EINVAL; 4553987da915Sopenharmony_ci ntfs_log_perror("val is mandatory for always resident " 4554987da915Sopenharmony_ci "attributes"); 4555987da915Sopenharmony_ci return -1; 4556987da915Sopenharmony_ci } 4557987da915Sopenharmony_ci if (size > ni->vol->mft_record_size) { 4558987da915Sopenharmony_ci errno = ERANGE; 4559987da915Sopenharmony_ci ntfs_log_perror("Attribute is too big"); 4560987da915Sopenharmony_ci return -1; 4561987da915Sopenharmony_ci } 4562987da915Sopenharmony_ci } else 4563987da915Sopenharmony_ci can_be_non_resident = TRUE; 4564987da915Sopenharmony_ci 4565987da915Sopenharmony_ci /* 4566987da915Sopenharmony_ci * Determine resident or not will be new attribute. We add 8 to size in 4567987da915Sopenharmony_ci * non resident case for mapping pairs. 4568987da915Sopenharmony_ci */ 4569987da915Sopenharmony_ci if (!ntfs_attr_can_be_resident(ni->vol, type)) { 4570987da915Sopenharmony_ci is_resident = TRUE; 4571987da915Sopenharmony_ci } else { 4572987da915Sopenharmony_ci if (errno != EPERM) { 4573987da915Sopenharmony_ci err = errno; 4574987da915Sopenharmony_ci ntfs_log_perror("ntfs_attr_can_be_resident failed"); 4575987da915Sopenharmony_ci goto err_out; 4576987da915Sopenharmony_ci } 4577987da915Sopenharmony_ci is_resident = FALSE; 4578987da915Sopenharmony_ci } 4579987da915Sopenharmony_ci /* Calculate attribute record size. */ 4580987da915Sopenharmony_ci if (is_resident) 4581987da915Sopenharmony_ci attr_rec_size = offsetof(ATTR_RECORD, resident_end) + 4582987da915Sopenharmony_ci ((name_len * sizeof(ntfschar) + 7) & ~7) + 4583987da915Sopenharmony_ci ((size + 7) & ~7); 4584987da915Sopenharmony_ci else 4585987da915Sopenharmony_ci attr_rec_size = offsetof(ATTR_RECORD, non_resident_end) + 4586987da915Sopenharmony_ci ((name_len * sizeof(ntfschar) + 7) & ~7) + 8; 4587987da915Sopenharmony_ci 4588987da915Sopenharmony_ci /* 4589987da915Sopenharmony_ci * If we have enough free space for the new attribute in the base MFT 4590987da915Sopenharmony_ci * record, then add attribute to it. 4591987da915Sopenharmony_ci */ 4592987da915Sopenharmony_ci if (le32_to_cpu(ni->mrec->bytes_allocated) - 4593987da915Sopenharmony_ci le32_to_cpu(ni->mrec->bytes_in_use) >= attr_rec_size) { 4594987da915Sopenharmony_ci attr_ni = ni; 4595987da915Sopenharmony_ci goto add_attr_record; 4596987da915Sopenharmony_ci } 4597987da915Sopenharmony_ci 4598987da915Sopenharmony_ci /* Try to add to extent inodes. */ 4599987da915Sopenharmony_ci if (ntfs_inode_attach_all_extents(ni)) { 4600987da915Sopenharmony_ci err = errno; 4601987da915Sopenharmony_ci ntfs_log_perror("Failed to attach all extents to inode"); 4602987da915Sopenharmony_ci goto err_out; 4603987da915Sopenharmony_ci } 4604987da915Sopenharmony_ci for (i = 0; i < ni->nr_extents; i++) { 4605987da915Sopenharmony_ci attr_ni = ni->extent_nis[i]; 4606987da915Sopenharmony_ci if (le32_to_cpu(attr_ni->mrec->bytes_allocated) - 4607987da915Sopenharmony_ci le32_to_cpu(attr_ni->mrec->bytes_in_use) >= 4608987da915Sopenharmony_ci attr_rec_size) 4609987da915Sopenharmony_ci goto add_attr_record; 4610987da915Sopenharmony_ci } 4611987da915Sopenharmony_ci 4612987da915Sopenharmony_ci /* There is no extent that contain enough space for new attribute. */ 4613987da915Sopenharmony_ci if (!NInoAttrList(ni)) { 4614987da915Sopenharmony_ci /* Add attribute list not present, add it and retry. */ 4615987da915Sopenharmony_ci if (ntfs_inode_add_attrlist(ni)) { 4616987da915Sopenharmony_ci err = errno; 4617987da915Sopenharmony_ci ntfs_log_perror("Failed to add attribute list"); 4618987da915Sopenharmony_ci goto err_out; 4619987da915Sopenharmony_ci } 4620987da915Sopenharmony_ci return ntfs_attr_add(ni, type, name, name_len, val, size); 4621987da915Sopenharmony_ci } 4622987da915Sopenharmony_ci /* Allocate new extent. */ 4623987da915Sopenharmony_ci attr_ni = ntfs_mft_record_alloc(ni->vol, ni); 4624987da915Sopenharmony_ci if (!attr_ni) { 4625987da915Sopenharmony_ci err = errno; 4626987da915Sopenharmony_ci ntfs_log_perror("Failed to allocate extent record"); 4627987da915Sopenharmony_ci goto err_out; 4628987da915Sopenharmony_ci } 4629987da915Sopenharmony_ci 4630987da915Sopenharmony_ciadd_attr_record: 4631987da915Sopenharmony_ci if ((ni->flags & FILE_ATTR_COMPRESSED) 4632987da915Sopenharmony_ci && (ni->vol->major_ver >= 3) 4633987da915Sopenharmony_ci && NVolCompression(ni->vol) 4634987da915Sopenharmony_ci && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) 4635987da915Sopenharmony_ci && ((type == AT_DATA) 4636987da915Sopenharmony_ci || ((type == AT_INDEX_ROOT) && (name == NTFS_INDEX_I30)))) 4637987da915Sopenharmony_ci data_flags = ATTR_IS_COMPRESSED; 4638987da915Sopenharmony_ci else 4639987da915Sopenharmony_ci data_flags = const_cpu_to_le16(0); 4640987da915Sopenharmony_ci if (is_resident) { 4641987da915Sopenharmony_ci /* Add resident attribute. */ 4642987da915Sopenharmony_ci offset = ntfs_resident_attr_record_add(attr_ni, type, name, 4643987da915Sopenharmony_ci name_len, val, size, data_flags); 4644987da915Sopenharmony_ci if (offset < 0) { 4645987da915Sopenharmony_ci if (errno == ENOSPC && can_be_non_resident) 4646987da915Sopenharmony_ci goto add_non_resident; 4647987da915Sopenharmony_ci err = errno; 4648987da915Sopenharmony_ci ntfs_log_perror("Failed to add resident attribute"); 4649987da915Sopenharmony_ci goto free_err_out; 4650987da915Sopenharmony_ci } 4651987da915Sopenharmony_ci return 0; 4652987da915Sopenharmony_ci } 4653987da915Sopenharmony_ci 4654987da915Sopenharmony_ciadd_non_resident: 4655987da915Sopenharmony_ci /* Add non resident attribute. */ 4656987da915Sopenharmony_ci offset = ntfs_non_resident_attr_record_add(attr_ni, type, name, 4657987da915Sopenharmony_ci name_len, 0, 8, data_flags); 4658987da915Sopenharmony_ci if (offset < 0) { 4659987da915Sopenharmony_ci err = errno; 4660987da915Sopenharmony_ci ntfs_log_perror("Failed to add non resident attribute"); 4661987da915Sopenharmony_ci goto free_err_out; 4662987da915Sopenharmony_ci } 4663987da915Sopenharmony_ci 4664987da915Sopenharmony_ci /* If @size == 0, we are done. */ 4665987da915Sopenharmony_ci if (!size) 4666987da915Sopenharmony_ci return 0; 4667987da915Sopenharmony_ci 4668987da915Sopenharmony_ci /* Open new attribute and resize it. */ 4669987da915Sopenharmony_ci na = ntfs_attr_open(ni, type, name, name_len); 4670987da915Sopenharmony_ci if (!na) { 4671987da915Sopenharmony_ci err = errno; 4672987da915Sopenharmony_ci ntfs_log_perror("Failed to open just added attribute"); 4673987da915Sopenharmony_ci goto rm_attr_err_out; 4674987da915Sopenharmony_ci } 4675987da915Sopenharmony_ci /* Resize and set attribute value. */ 4676987da915Sopenharmony_ci if (ntfs_attr_truncate_i(na, size, HOLES_OK) || 4677987da915Sopenharmony_ci (val && (ntfs_attr_pwrite(na, 0, size, val) != size))) { 4678987da915Sopenharmony_ci err = errno; 4679987da915Sopenharmony_ci ntfs_log_perror("Failed to initialize just added attribute"); 4680987da915Sopenharmony_ci if (ntfs_attr_rm(na)) 4681987da915Sopenharmony_ci ntfs_log_perror("Failed to remove just added attribute"); 4682987da915Sopenharmony_ci ntfs_attr_close(na); 4683987da915Sopenharmony_ci goto err_out; 4684987da915Sopenharmony_ci } 4685987da915Sopenharmony_ci ntfs_attr_close(na); 4686987da915Sopenharmony_ci return 0; 4687987da915Sopenharmony_ci 4688987da915Sopenharmony_cirm_attr_err_out: 4689987da915Sopenharmony_ci /* Remove just added attribute. */ 4690987da915Sopenharmony_ci if (ntfs_attr_record_resize(attr_ni->mrec, 4691987da915Sopenharmony_ci (ATTR_RECORD*)((u8*)attr_ni->mrec + offset), 0)) 4692987da915Sopenharmony_ci ntfs_log_perror("Failed to remove just added attribute #2"); 4693987da915Sopenharmony_cifree_err_out: 4694987da915Sopenharmony_ci /* Free MFT record, if it doesn't contain attributes. */ 4695987da915Sopenharmony_ci if (le32_to_cpu(attr_ni->mrec->bytes_in_use) - 4696987da915Sopenharmony_ci le16_to_cpu(attr_ni->mrec->attrs_offset) == 8) 4697987da915Sopenharmony_ci if (ntfs_mft_record_free(attr_ni->vol, attr_ni)) 4698987da915Sopenharmony_ci ntfs_log_perror("Failed to free MFT record"); 4699987da915Sopenharmony_cierr_out: 4700987da915Sopenharmony_ci errno = err; 4701987da915Sopenharmony_ci return -1; 4702987da915Sopenharmony_ci} 4703987da915Sopenharmony_ci 4704987da915Sopenharmony_ci/* 4705987da915Sopenharmony_ci * Change an attribute flag 4706987da915Sopenharmony_ci */ 4707987da915Sopenharmony_ci 4708987da915Sopenharmony_ciint ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type, const ntfschar *name, 4709987da915Sopenharmony_ci u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask) 4710987da915Sopenharmony_ci{ 4711987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 4712987da915Sopenharmony_ci int res; 4713987da915Sopenharmony_ci 4714987da915Sopenharmony_ci res = -1; 4715987da915Sopenharmony_ci /* Search for designated attribute */ 4716987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(ni, NULL); 4717987da915Sopenharmony_ci if (ctx) { 4718987da915Sopenharmony_ci if (!ntfs_attr_lookup(type, name, name_len, 4719987da915Sopenharmony_ci CASE_SENSITIVE, 0, NULL, 0, ctx)) { 4720987da915Sopenharmony_ci /* do the requested change (all small endian le16) */ 4721987da915Sopenharmony_ci ctx->attr->flags = (ctx->attr->flags & ~mask) 4722987da915Sopenharmony_ci | (flags & mask); 4723987da915Sopenharmony_ci NInoSetDirty(ni); 4724987da915Sopenharmony_ci res = 0; 4725987da915Sopenharmony_ci } 4726987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 4727987da915Sopenharmony_ci } 4728987da915Sopenharmony_ci return (res); 4729987da915Sopenharmony_ci} 4730987da915Sopenharmony_ci 4731987da915Sopenharmony_ci 4732987da915Sopenharmony_ci/** 4733987da915Sopenharmony_ci * ntfs_attr_rm - remove attribute from ntfs inode 4734987da915Sopenharmony_ci * @na: opened ntfs attribute to delete 4735987da915Sopenharmony_ci * 4736987da915Sopenharmony_ci * Remove attribute and all it's extents from ntfs inode. If attribute was non 4737987da915Sopenharmony_ci * resident also free all clusters allocated by attribute. 4738987da915Sopenharmony_ci * 4739987da915Sopenharmony_ci * Return 0 on success or -1 on error with errno set to the error code. 4740987da915Sopenharmony_ci */ 4741987da915Sopenharmony_ciint ntfs_attr_rm(ntfs_attr *na) 4742987da915Sopenharmony_ci{ 4743987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 4744987da915Sopenharmony_ci int ret = 0; 4745987da915Sopenharmony_ci 4746987da915Sopenharmony_ci if (!na) { 4747987da915Sopenharmony_ci ntfs_log_trace("Invalid arguments passed.\n"); 4748987da915Sopenharmony_ci errno = EINVAL; 4749987da915Sopenharmony_ci return -1; 4750987da915Sopenharmony_ci } 4751987da915Sopenharmony_ci 4752987da915Sopenharmony_ci ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", 4753987da915Sopenharmony_ci (long long) na->ni->mft_no, le32_to_cpu(na->type)); 4754987da915Sopenharmony_ci 4755987da915Sopenharmony_ci /* Free cluster allocation. */ 4756987da915Sopenharmony_ci if (NAttrNonResident(na)) { 4757987da915Sopenharmony_ci if (ntfs_attr_map_whole_runlist(na)) 4758987da915Sopenharmony_ci return -1; 4759987da915Sopenharmony_ci if (ntfs_cluster_free(na->ni->vol, na, 0, -1) < 0) { 4760987da915Sopenharmony_ci ntfs_log_trace("Failed to free cluster allocation. Leaving " 4761987da915Sopenharmony_ci "inconstant metadata.\n"); 4762987da915Sopenharmony_ci ret = -1; 4763987da915Sopenharmony_ci } 4764987da915Sopenharmony_ci } 4765987da915Sopenharmony_ci 4766987da915Sopenharmony_ci /* Search for attribute extents and remove them all. */ 4767987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(na->ni, NULL); 4768987da915Sopenharmony_ci if (!ctx) 4769987da915Sopenharmony_ci return -1; 4770987da915Sopenharmony_ci while (!ntfs_attr_lookup(na->type, na->name, na->name_len, 4771987da915Sopenharmony_ci CASE_SENSITIVE, 0, NULL, 0, ctx)) { 4772987da915Sopenharmony_ci if (ntfs_attr_record_rm(ctx)) { 4773987da915Sopenharmony_ci ntfs_log_trace("Failed to remove attribute extent. Leaving " 4774987da915Sopenharmony_ci "inconstant metadata.\n"); 4775987da915Sopenharmony_ci ret = -1; 4776987da915Sopenharmony_ci } 4777987da915Sopenharmony_ci ntfs_attr_reinit_search_ctx(ctx); 4778987da915Sopenharmony_ci } 4779987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 4780987da915Sopenharmony_ci if (errno != ENOENT) { 4781987da915Sopenharmony_ci ntfs_log_trace("Attribute lookup failed. Probably leaving inconstant " 4782987da915Sopenharmony_ci "metadata.\n"); 4783987da915Sopenharmony_ci ret = -1; 4784987da915Sopenharmony_ci } 4785987da915Sopenharmony_ci 4786987da915Sopenharmony_ci return ret; 4787987da915Sopenharmony_ci} 4788987da915Sopenharmony_ci 4789987da915Sopenharmony_ci/** 4790987da915Sopenharmony_ci * ntfs_attr_record_resize - resize an attribute record 4791987da915Sopenharmony_ci * @m: mft record containing attribute record 4792987da915Sopenharmony_ci * @a: attribute record to resize 4793987da915Sopenharmony_ci * @new_size: new size in bytes to which to resize the attribute record @a 4794987da915Sopenharmony_ci * 4795987da915Sopenharmony_ci * Resize the attribute record @a, i.e. the resident part of the attribute, in 4796987da915Sopenharmony_ci * the mft record @m to @new_size bytes. 4797987da915Sopenharmony_ci * 4798987da915Sopenharmony_ci * Return 0 on success and -1 on error with errno set to the error code. 4799987da915Sopenharmony_ci * The following error codes are defined: 4800987da915Sopenharmony_ci * ENOSPC - Not enough space in the mft record @m to perform the resize. 4801987da915Sopenharmony_ci * Note that on error no modifications have been performed whatsoever. 4802987da915Sopenharmony_ci * 4803987da915Sopenharmony_ci * Warning: If you make a record smaller without having copied all the data you 4804987da915Sopenharmony_ci * are interested in the data may be overwritten! 4805987da915Sopenharmony_ci */ 4806987da915Sopenharmony_ciint ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) 4807987da915Sopenharmony_ci{ 4808987da915Sopenharmony_ci u32 old_size, alloc_size, attr_size; 4809987da915Sopenharmony_ci 4810987da915Sopenharmony_ci old_size = le32_to_cpu(m->bytes_in_use); 4811987da915Sopenharmony_ci alloc_size = le32_to_cpu(m->bytes_allocated); 4812987da915Sopenharmony_ci attr_size = le32_to_cpu(a->length); 4813987da915Sopenharmony_ci 4814987da915Sopenharmony_ci ntfs_log_trace("Sizes: old=%u alloc=%u attr=%u new=%u\n", 4815987da915Sopenharmony_ci (unsigned)old_size, (unsigned)alloc_size, 4816987da915Sopenharmony_ci (unsigned)attr_size, (unsigned)new_size); 4817987da915Sopenharmony_ci 4818987da915Sopenharmony_ci /* Align to 8 bytes, just in case the caller hasn't. */ 4819987da915Sopenharmony_ci new_size = (new_size + 7) & ~7; 4820987da915Sopenharmony_ci 4821987da915Sopenharmony_ci /* If the actual attribute length has changed, move things around. */ 4822987da915Sopenharmony_ci if (new_size != attr_size) { 4823987da915Sopenharmony_ci 4824987da915Sopenharmony_ci u32 new_muse = old_size - attr_size + new_size; 4825987da915Sopenharmony_ci 4826987da915Sopenharmony_ci /* Not enough space in this mft record. */ 4827987da915Sopenharmony_ci if (new_muse > alloc_size) { 4828987da915Sopenharmony_ci errno = ENOSPC; 4829987da915Sopenharmony_ci ntfs_log_trace("Not enough space in the MFT record " 4830987da915Sopenharmony_ci "(%u > %u)\n", new_muse, alloc_size); 4831987da915Sopenharmony_ci return -1; 4832987da915Sopenharmony_ci } 4833987da915Sopenharmony_ci 4834987da915Sopenharmony_ci if (a->type == AT_INDEX_ROOT && new_size > attr_size && 4835987da915Sopenharmony_ci new_muse + 120 > alloc_size && old_size + 120 <= alloc_size) { 4836987da915Sopenharmony_ci errno = ENOSPC; 4837987da915Sopenharmony_ci ntfs_log_trace("Too big INDEX_ROOT (%u > %u)\n", 4838987da915Sopenharmony_ci new_muse, alloc_size); 4839987da915Sopenharmony_ci return STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT; 4840987da915Sopenharmony_ci } 4841987da915Sopenharmony_ci 4842987da915Sopenharmony_ci /* Move attributes following @a to their new location. */ 4843987da915Sopenharmony_ci if (((u8 *)m + old_size) < ((u8 *)a + attr_size)) { 4844987da915Sopenharmony_ci ntfs_log_error("Attribute 0x%x overflows" 4845987da915Sopenharmony_ci " from MFT record\n", 4846987da915Sopenharmony_ci (int)le32_to_cpu(a->type)); 4847987da915Sopenharmony_ci errno = EIO; 4848987da915Sopenharmony_ci return (-1); 4849987da915Sopenharmony_ci } 4850987da915Sopenharmony_ci memmove((u8 *)a + new_size, (u8 *)a + attr_size, 4851987da915Sopenharmony_ci old_size - ((u8 *)a - (u8 *)m) - attr_size); 4852987da915Sopenharmony_ci 4853987da915Sopenharmony_ci /* Adjust @m to reflect the change in used space. */ 4854987da915Sopenharmony_ci m->bytes_in_use = cpu_to_le32(new_muse); 4855987da915Sopenharmony_ci 4856987da915Sopenharmony_ci /* Adjust @a to reflect the new size. */ 4857987da915Sopenharmony_ci if (new_size >= offsetof(ATTR_REC, length) + sizeof(a->length)) 4858987da915Sopenharmony_ci a->length = cpu_to_le32(new_size); 4859987da915Sopenharmony_ci } 4860987da915Sopenharmony_ci return 0; 4861987da915Sopenharmony_ci} 4862987da915Sopenharmony_ci 4863987da915Sopenharmony_ci/** 4864987da915Sopenharmony_ci * ntfs_resident_attr_value_resize - resize the value of a resident attribute 4865987da915Sopenharmony_ci * @m: mft record containing attribute record 4866987da915Sopenharmony_ci * @a: attribute record whose value to resize 4867987da915Sopenharmony_ci * @new_size: new size in bytes to which to resize the attribute value of @a 4868987da915Sopenharmony_ci * 4869987da915Sopenharmony_ci * Resize the value of the attribute @a in the mft record @m to @new_size bytes. 4870987da915Sopenharmony_ci * If the value is made bigger, the newly "allocated" space is cleared. 4871987da915Sopenharmony_ci * 4872987da915Sopenharmony_ci * Return 0 on success and -1 on error with errno set to the error code. 4873987da915Sopenharmony_ci * The following error codes are defined: 4874987da915Sopenharmony_ci * ENOSPC - Not enough space in the mft record @m to perform the resize. 4875987da915Sopenharmony_ci * Note that on error no modifications have been performed whatsoever. 4876987da915Sopenharmony_ci */ 4877987da915Sopenharmony_ciint ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, 4878987da915Sopenharmony_ci const u32 new_size) 4879987da915Sopenharmony_ci{ 4880987da915Sopenharmony_ci int ret; 4881987da915Sopenharmony_ci 4882987da915Sopenharmony_ci ntfs_log_trace("Entering for new size %u.\n", (unsigned)new_size); 4883987da915Sopenharmony_ci 4884987da915Sopenharmony_ci if (!a->value_length) { 4885987da915Sopenharmony_ci /* Offset is unsafe when no value */ 4886987da915Sopenharmony_ci int offset = ((offsetof(ATTR_RECORD, resident_end) 4887987da915Sopenharmony_ci + a->name_length*sizeof(ntfschar) - 1) | 7) + 1; 4888987da915Sopenharmony_ci a->value_offset = cpu_to_le16(offset); 4889987da915Sopenharmony_ci } 4890987da915Sopenharmony_ci 4891987da915Sopenharmony_ci /* Resize the resident part of the attribute record. */ 4892987da915Sopenharmony_ci if ((ret = ntfs_attr_record_resize(m, a, (le16_to_cpu(a->value_offset) + 4893987da915Sopenharmony_ci new_size + 7) & ~7)) < 0) 4894987da915Sopenharmony_ci return ret; 4895987da915Sopenharmony_ci /* 4896987da915Sopenharmony_ci * If we made the attribute value bigger, clear the area between the 4897987da915Sopenharmony_ci * old size and @new_size. 4898987da915Sopenharmony_ci */ 4899987da915Sopenharmony_ci if (new_size > le32_to_cpu(a->value_length)) 4900987da915Sopenharmony_ci memset((u8*)a + le16_to_cpu(a->value_offset) + 4901987da915Sopenharmony_ci le32_to_cpu(a->value_length), 0, new_size - 4902987da915Sopenharmony_ci le32_to_cpu(a->value_length)); 4903987da915Sopenharmony_ci /* Finally update the length of the attribute value. */ 4904987da915Sopenharmony_ci a->value_length = cpu_to_le32(new_size); 4905987da915Sopenharmony_ci return 0; 4906987da915Sopenharmony_ci} 4907987da915Sopenharmony_ci 4908987da915Sopenharmony_ci/** 4909987da915Sopenharmony_ci * ntfs_attr_record_move_to - move attribute record to target inode 4910987da915Sopenharmony_ci * @ctx: attribute search context describing the attribute record 4911987da915Sopenharmony_ci * @ni: opened ntfs inode to which move attribute record 4912987da915Sopenharmony_ci * 4913987da915Sopenharmony_ci * If this function succeed, user should reinit search context if he/she wants 4914987da915Sopenharmony_ci * use it anymore. 4915987da915Sopenharmony_ci * 4916987da915Sopenharmony_ci * Return 0 on success and -1 on error with errno set to the error code. 4917987da915Sopenharmony_ci */ 4918987da915Sopenharmony_ciint ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni) 4919987da915Sopenharmony_ci{ 4920987da915Sopenharmony_ci ntfs_attr_search_ctx *nctx; 4921987da915Sopenharmony_ci ATTR_RECORD *a; 4922987da915Sopenharmony_ci int err; 4923987da915Sopenharmony_ci 4924987da915Sopenharmony_ci if (!ctx || !ctx->attr || !ctx->ntfs_ino || !ni) { 4925987da915Sopenharmony_ci ntfs_log_trace("Invalid arguments passed.\n"); 4926987da915Sopenharmony_ci errno = EINVAL; 4927987da915Sopenharmony_ci return -1; 4928987da915Sopenharmony_ci } 4929987da915Sopenharmony_ci 4930987da915Sopenharmony_ci ntfs_log_trace("Entering for ctx->attr->type 0x%x, ctx->ntfs_ino->mft_no " 4931987da915Sopenharmony_ci "0x%llx, ni->mft_no 0x%llx.\n", 4932987da915Sopenharmony_ci (unsigned) le32_to_cpu(ctx->attr->type), 4933987da915Sopenharmony_ci (long long) ctx->ntfs_ino->mft_no, 4934987da915Sopenharmony_ci (long long) ni->mft_no); 4935987da915Sopenharmony_ci 4936987da915Sopenharmony_ci if (ctx->ntfs_ino == ni) 4937987da915Sopenharmony_ci return 0; 4938987da915Sopenharmony_ci 4939987da915Sopenharmony_ci if (!ctx->al_entry) { 4940987da915Sopenharmony_ci ntfs_log_trace("Inode should contain attribute list to use this " 4941987da915Sopenharmony_ci "function.\n"); 4942987da915Sopenharmony_ci errno = EINVAL; 4943987da915Sopenharmony_ci return -1; 4944987da915Sopenharmony_ci } 4945987da915Sopenharmony_ci 4946987da915Sopenharmony_ci /* Find place in MFT record where attribute will be moved. */ 4947987da915Sopenharmony_ci a = ctx->attr; 4948987da915Sopenharmony_ci nctx = ntfs_attr_get_search_ctx(ni, NULL); 4949987da915Sopenharmony_ci if (!nctx) 4950987da915Sopenharmony_ci return -1; 4951987da915Sopenharmony_ci 4952987da915Sopenharmony_ci /* 4953987da915Sopenharmony_ci * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for 4954987da915Sopenharmony_ci * attribute in @ni->mrec, not any extent inode in case if @ni is base 4955987da915Sopenharmony_ci * file record. 4956987da915Sopenharmony_ci */ 4957987da915Sopenharmony_ci if (!ntfs_attr_find(a->type, (ntfschar*)((u8*)a + le16_to_cpu( 4958987da915Sopenharmony_ci a->name_offset)), a->name_length, CASE_SENSITIVE, NULL, 4959987da915Sopenharmony_ci 0, nctx)) { 4960987da915Sopenharmony_ci ntfs_log_trace("Attribute of such type, with same name already " 4961987da915Sopenharmony_ci "present in this MFT record.\n"); 4962987da915Sopenharmony_ci err = EEXIST; 4963987da915Sopenharmony_ci goto put_err_out; 4964987da915Sopenharmony_ci } 4965987da915Sopenharmony_ci if (errno != ENOENT) { 4966987da915Sopenharmony_ci err = errno; 4967987da915Sopenharmony_ci ntfs_log_debug("Attribute lookup failed.\n"); 4968987da915Sopenharmony_ci goto put_err_out; 4969987da915Sopenharmony_ci } 4970987da915Sopenharmony_ci 4971987da915Sopenharmony_ci /* Make space and move attribute. */ 4972987da915Sopenharmony_ci if (ntfs_make_room_for_attr(ni->mrec, (u8*) nctx->attr, 4973987da915Sopenharmony_ci le32_to_cpu(a->length))) { 4974987da915Sopenharmony_ci err = errno; 4975987da915Sopenharmony_ci ntfs_log_trace("Couldn't make space for attribute.\n"); 4976987da915Sopenharmony_ci goto put_err_out; 4977987da915Sopenharmony_ci } 4978987da915Sopenharmony_ci memcpy(nctx->attr, a, le32_to_cpu(a->length)); 4979987da915Sopenharmony_ci nctx->attr->instance = nctx->mrec->next_attr_instance; 4980987da915Sopenharmony_ci nctx->mrec->next_attr_instance = cpu_to_le16( 4981987da915Sopenharmony_ci (le16_to_cpu(nctx->mrec->next_attr_instance) + 1) & 0xffff); 4982987da915Sopenharmony_ci ntfs_attr_record_resize(ctx->mrec, a, 0); 4983987da915Sopenharmony_ci ntfs_inode_mark_dirty(ctx->ntfs_ino); 4984987da915Sopenharmony_ci ntfs_inode_mark_dirty(ni); 4985987da915Sopenharmony_ci 4986987da915Sopenharmony_ci /* Update attribute list. */ 4987987da915Sopenharmony_ci ctx->al_entry->mft_reference = 4988987da915Sopenharmony_ci MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); 4989987da915Sopenharmony_ci ctx->al_entry->instance = nctx->attr->instance; 4990987da915Sopenharmony_ci ntfs_attrlist_mark_dirty(ni); 4991987da915Sopenharmony_ci 4992987da915Sopenharmony_ci ntfs_attr_put_search_ctx(nctx); 4993987da915Sopenharmony_ci return 0; 4994987da915Sopenharmony_ciput_err_out: 4995987da915Sopenharmony_ci ntfs_attr_put_search_ctx(nctx); 4996987da915Sopenharmony_ci errno = err; 4997987da915Sopenharmony_ci return -1; 4998987da915Sopenharmony_ci} 4999987da915Sopenharmony_ci 5000987da915Sopenharmony_ci/** 5001987da915Sopenharmony_ci * ntfs_attr_record_move_away - move away attribute record from it's mft record 5002987da915Sopenharmony_ci * @ctx: attribute search context describing the attribute record 5003987da915Sopenharmony_ci * @extra: minimum amount of free space in the new holder of record 5004987da915Sopenharmony_ci * 5005987da915Sopenharmony_ci * New attribute record holder must have free @extra bytes after moving 5006987da915Sopenharmony_ci * attribute record to it. 5007987da915Sopenharmony_ci * 5008987da915Sopenharmony_ci * If this function succeed, user should reinit search context if he/she wants 5009987da915Sopenharmony_ci * use it anymore. 5010987da915Sopenharmony_ci * 5011987da915Sopenharmony_ci * Return 0 on success and -1 on error with errno set to the error code. 5012987da915Sopenharmony_ci */ 5013987da915Sopenharmony_ciint ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra) 5014987da915Sopenharmony_ci{ 5015987da915Sopenharmony_ci ntfs_inode *base_ni, *ni; 5016987da915Sopenharmony_ci MFT_RECORD *m; 5017987da915Sopenharmony_ci int i; 5018987da915Sopenharmony_ci 5019987da915Sopenharmony_ci if (!ctx || !ctx->attr || !ctx->ntfs_ino || extra < 0) { 5020987da915Sopenharmony_ci errno = EINVAL; 5021987da915Sopenharmony_ci ntfs_log_perror("%s: ctx=%p ctx->attr=%p extra=%d", __FUNCTION__, 5022987da915Sopenharmony_ci ctx, ctx ? ctx->attr : NULL, extra); 5023987da915Sopenharmony_ci return -1; 5024987da915Sopenharmony_ci } 5025987da915Sopenharmony_ci 5026987da915Sopenharmony_ci ntfs_log_trace("Entering for attr 0x%x, inode %llu\n", 5027987da915Sopenharmony_ci (unsigned) le32_to_cpu(ctx->attr->type), 5028987da915Sopenharmony_ci (unsigned long long)ctx->ntfs_ino->mft_no); 5029987da915Sopenharmony_ci 5030987da915Sopenharmony_ci if (ctx->ntfs_ino->nr_extents == -1) 5031987da915Sopenharmony_ci base_ni = ctx->base_ntfs_ino; 5032987da915Sopenharmony_ci else 5033987da915Sopenharmony_ci base_ni = ctx->ntfs_ino; 5034987da915Sopenharmony_ci 5035987da915Sopenharmony_ci if (!NInoAttrList(base_ni)) { 5036987da915Sopenharmony_ci errno = EINVAL; 5037987da915Sopenharmony_ci ntfs_log_perror("Inode %llu has no attrlist", 5038987da915Sopenharmony_ci (unsigned long long)base_ni->mft_no); 5039987da915Sopenharmony_ci return -1; 5040987da915Sopenharmony_ci } 5041987da915Sopenharmony_ci 5042987da915Sopenharmony_ci if (ntfs_inode_attach_all_extents(ctx->ntfs_ino)) { 5043987da915Sopenharmony_ci ntfs_log_perror("Couldn't attach extents, inode=%llu", 5044987da915Sopenharmony_ci (unsigned long long)base_ni->mft_no); 5045987da915Sopenharmony_ci return -1; 5046987da915Sopenharmony_ci } 5047987da915Sopenharmony_ci 5048987da915Sopenharmony_ci /* Walk through all extents and try to move attribute to them. */ 5049987da915Sopenharmony_ci for (i = 0; i < base_ni->nr_extents; i++) { 5050987da915Sopenharmony_ci ni = base_ni->extent_nis[i]; 5051987da915Sopenharmony_ci m = ni->mrec; 5052987da915Sopenharmony_ci 5053987da915Sopenharmony_ci if (ctx->ntfs_ino->mft_no == ni->mft_no) 5054987da915Sopenharmony_ci continue; 5055987da915Sopenharmony_ci 5056987da915Sopenharmony_ci if (le32_to_cpu(m->bytes_allocated) - 5057987da915Sopenharmony_ci le32_to_cpu(m->bytes_in_use) < 5058987da915Sopenharmony_ci le32_to_cpu(ctx->attr->length) + extra) 5059987da915Sopenharmony_ci continue; 5060987da915Sopenharmony_ci 5061987da915Sopenharmony_ci /* 5062987da915Sopenharmony_ci * ntfs_attr_record_move_to can fail if extent with other lowest 5063987da915Sopenharmony_ci * VCN already present in inode we trying move record to. So, 5064987da915Sopenharmony_ci * do not return error. 5065987da915Sopenharmony_ci */ 5066987da915Sopenharmony_ci if (!ntfs_attr_record_move_to(ctx, ni)) 5067987da915Sopenharmony_ci return 0; 5068987da915Sopenharmony_ci } 5069987da915Sopenharmony_ci 5070987da915Sopenharmony_ci /* 5071987da915Sopenharmony_ci * Failed to move attribute to one of the current extents, so allocate 5072987da915Sopenharmony_ci * new extent and move attribute to it. 5073987da915Sopenharmony_ci */ 5074987da915Sopenharmony_ci ni = ntfs_mft_record_alloc(base_ni->vol, base_ni); 5075987da915Sopenharmony_ci if (!ni) { 5076987da915Sopenharmony_ci ntfs_log_perror("Couldn't allocate MFT record"); 5077987da915Sopenharmony_ci return -1; 5078987da915Sopenharmony_ci } 5079987da915Sopenharmony_ci if (ntfs_attr_record_move_to(ctx, ni)) { 5080987da915Sopenharmony_ci ntfs_log_perror("Couldn't move attribute to MFT record"); 5081987da915Sopenharmony_ci return -1; 5082987da915Sopenharmony_ci } 5083987da915Sopenharmony_ci return 0; 5084987da915Sopenharmony_ci} 5085987da915Sopenharmony_ci 5086987da915Sopenharmony_ci/** 5087987da915Sopenharmony_ci * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute 5088987da915Sopenharmony_ci * @na: open ntfs attribute to make non-resident 5089987da915Sopenharmony_ci * @ctx: ntfs search context describing the attribute 5090987da915Sopenharmony_ci * 5091987da915Sopenharmony_ci * Convert a resident ntfs attribute to a non-resident one. 5092987da915Sopenharmony_ci * 5093987da915Sopenharmony_ci * Return 0 on success and -1 on error with errno set to the error code. The 5094987da915Sopenharmony_ci * following error codes are defined: 5095987da915Sopenharmony_ci * EPERM - The attribute is not allowed to be non-resident. 5096987da915Sopenharmony_ci * TODO: others... 5097987da915Sopenharmony_ci * 5098987da915Sopenharmony_ci * NOTE to self: No changes in the attribute list are required to move from 5099987da915Sopenharmony_ci * a resident to a non-resident attribute. 5100987da915Sopenharmony_ci * 5101987da915Sopenharmony_ci * Warning: We do not set the inode dirty and we do not write out anything! 5102987da915Sopenharmony_ci * We expect the caller to do this as this is a fairly low level 5103987da915Sopenharmony_ci * function and it is likely there will be further changes made. 5104987da915Sopenharmony_ci */ 5105987da915Sopenharmony_ciint ntfs_attr_make_non_resident(ntfs_attr *na, 5106987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx) 5107987da915Sopenharmony_ci{ 5108987da915Sopenharmony_ci s64 new_allocated_size, bw; 5109987da915Sopenharmony_ci ntfs_volume *vol = na->ni->vol; 5110987da915Sopenharmony_ci ATTR_REC *a = ctx->attr; 5111987da915Sopenharmony_ci runlist *rl; 5112987da915Sopenharmony_ci int mp_size, mp_ofs, name_ofs, arec_size, err; 5113987da915Sopenharmony_ci 5114987da915Sopenharmony_ci ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long 5115987da915Sopenharmony_ci long)na->ni->mft_no, le32_to_cpu(na->type)); 5116987da915Sopenharmony_ci 5117987da915Sopenharmony_ci /* Some preliminary sanity checking. */ 5118987da915Sopenharmony_ci if (NAttrNonResident(na)) { 5119987da915Sopenharmony_ci ntfs_log_trace("Eeek! Trying to make non-resident attribute " 5120987da915Sopenharmony_ci "non-resident. Aborting...\n"); 5121987da915Sopenharmony_ci errno = EINVAL; 5122987da915Sopenharmony_ci return -1; 5123987da915Sopenharmony_ci } 5124987da915Sopenharmony_ci 5125987da915Sopenharmony_ci /* Check that the attribute is allowed to be non-resident. */ 5126987da915Sopenharmony_ci if (ntfs_attr_can_be_non_resident(vol, na->type, na->name, na->name_len)) 5127987da915Sopenharmony_ci return -1; 5128987da915Sopenharmony_ci 5129987da915Sopenharmony_ci new_allocated_size = (le32_to_cpu(a->value_length) + vol->cluster_size 5130987da915Sopenharmony_ci - 1) & ~(vol->cluster_size - 1); 5131987da915Sopenharmony_ci 5132987da915Sopenharmony_ci if (new_allocated_size > 0) { 5133987da915Sopenharmony_ci if ((a->flags & ATTR_COMPRESSION_MASK) 5134987da915Sopenharmony_ci == ATTR_IS_COMPRESSED) { 5135987da915Sopenharmony_ci /* must allocate full compression blocks */ 5136987da915Sopenharmony_ci new_allocated_size = ((new_allocated_size - 1) 5137987da915Sopenharmony_ci | ((1L << (STANDARD_COMPRESSION_UNIT 5138987da915Sopenharmony_ci + vol->cluster_size_bits)) - 1)) + 1; 5139987da915Sopenharmony_ci } 5140987da915Sopenharmony_ci /* Start by allocating clusters to hold the attribute value. */ 5141987da915Sopenharmony_ci rl = ntfs_cluster_alloc(vol, 0, new_allocated_size >> 5142987da915Sopenharmony_ci vol->cluster_size_bits, -1, DATA_ZONE); 5143987da915Sopenharmony_ci if (!rl) 5144987da915Sopenharmony_ci return -1; 5145987da915Sopenharmony_ci } else 5146987da915Sopenharmony_ci rl = NULL; 5147987da915Sopenharmony_ci /* 5148987da915Sopenharmony_ci * Setup the in-memory attribute structure to be non-resident so that 5149987da915Sopenharmony_ci * we can use ntfs_attr_pwrite(). 5150987da915Sopenharmony_ci */ 5151987da915Sopenharmony_ci NAttrSetNonResident(na); 5152987da915Sopenharmony_ci NAttrSetBeingNonResident(na); 5153987da915Sopenharmony_ci na->rl = rl; 5154987da915Sopenharmony_ci na->allocated_size = new_allocated_size; 5155987da915Sopenharmony_ci na->data_size = na->initialized_size = le32_to_cpu(a->value_length); 5156987da915Sopenharmony_ci /* 5157987da915Sopenharmony_ci * FIXME: For now just clear all of these as we don't support them when 5158987da915Sopenharmony_ci * writing. 5159987da915Sopenharmony_ci */ 5160987da915Sopenharmony_ci NAttrClearSparse(na); 5161987da915Sopenharmony_ci NAttrClearEncrypted(na); 5162987da915Sopenharmony_ci if ((a->flags & ATTR_COMPRESSION_MASK) == ATTR_IS_COMPRESSED) { 5163987da915Sopenharmony_ci /* set compression writing parameters */ 5164987da915Sopenharmony_ci na->compression_block_size 5165987da915Sopenharmony_ci = 1 << (STANDARD_COMPRESSION_UNIT + vol->cluster_size_bits); 5166987da915Sopenharmony_ci na->compression_block_clusters = 1 << STANDARD_COMPRESSION_UNIT; 5167987da915Sopenharmony_ci } 5168987da915Sopenharmony_ci 5169987da915Sopenharmony_ci if (rl) { 5170987da915Sopenharmony_ci /* Now copy the attribute value to the allocated cluster(s). */ 5171987da915Sopenharmony_ci bw = ntfs_attr_pwrite(na, 0, le32_to_cpu(a->value_length), 5172987da915Sopenharmony_ci (u8*)a + le16_to_cpu(a->value_offset)); 5173987da915Sopenharmony_ci if (bw != le32_to_cpu(a->value_length)) { 5174987da915Sopenharmony_ci err = errno; 5175987da915Sopenharmony_ci ntfs_log_debug("Eeek! Failed to write out attribute value " 5176987da915Sopenharmony_ci "(bw = %lli, errno = %i). " 5177987da915Sopenharmony_ci "Aborting...\n", (long long)bw, err); 5178987da915Sopenharmony_ci if (bw >= 0) 5179987da915Sopenharmony_ci err = EIO; 5180987da915Sopenharmony_ci goto cluster_free_err_out; 5181987da915Sopenharmony_ci } 5182987da915Sopenharmony_ci } 5183987da915Sopenharmony_ci /* Determine the size of the mapping pairs array. */ 5184987da915Sopenharmony_ci mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0, INT_MAX); 5185987da915Sopenharmony_ci if (mp_size < 0) { 5186987da915Sopenharmony_ci err = errno; 5187987da915Sopenharmony_ci ntfs_log_debug("Eeek! Failed to get size for mapping pairs array. " 5188987da915Sopenharmony_ci "Aborting...\n"); 5189987da915Sopenharmony_ci goto cluster_free_err_out; 5190987da915Sopenharmony_ci } 5191987da915Sopenharmony_ci /* Calculate new offsets for the name and the mapping pairs array. */ 5192987da915Sopenharmony_ci if (na->ni->flags & FILE_ATTR_COMPRESSED) 5193987da915Sopenharmony_ci name_ofs = (sizeof(ATTR_REC) + 7) & ~7; 5194987da915Sopenharmony_ci else 5195987da915Sopenharmony_ci name_ofs = (sizeof(ATTR_REC) - sizeof(a->compressed_size) + 7) & ~7; 5196987da915Sopenharmony_ci mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7; 5197987da915Sopenharmony_ci /* 5198987da915Sopenharmony_ci * Determine the size of the resident part of the non-resident 5199987da915Sopenharmony_ci * attribute record. (Not compressed thus no compressed_size element 5200987da915Sopenharmony_ci * present.) 5201987da915Sopenharmony_ci */ 5202987da915Sopenharmony_ci arec_size = (mp_ofs + mp_size + 7) & ~7; 5203987da915Sopenharmony_ci 5204987da915Sopenharmony_ci /* Resize the resident part of the attribute record. */ 5205987da915Sopenharmony_ci if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) { 5206987da915Sopenharmony_ci err = errno; 5207987da915Sopenharmony_ci goto cluster_free_err_out; 5208987da915Sopenharmony_ci } 5209987da915Sopenharmony_ci 5210987da915Sopenharmony_ci /* 5211987da915Sopenharmony_ci * Convert the resident part of the attribute record to describe a 5212987da915Sopenharmony_ci * non-resident attribute. 5213987da915Sopenharmony_ci */ 5214987da915Sopenharmony_ci a->non_resident = 1; 5215987da915Sopenharmony_ci 5216987da915Sopenharmony_ci /* Move the attribute name if it exists and update the offset. */ 5217987da915Sopenharmony_ci if (a->name_length) 5218987da915Sopenharmony_ci memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), 5219987da915Sopenharmony_ci a->name_length * sizeof(ntfschar)); 5220987da915Sopenharmony_ci a->name_offset = cpu_to_le16(name_ofs); 5221987da915Sopenharmony_ci 5222987da915Sopenharmony_ci /* Setup the fields specific to non-resident attributes. */ 5223987da915Sopenharmony_ci a->lowest_vcn = const_cpu_to_sle64(0); 5224987da915Sopenharmony_ci a->highest_vcn = cpu_to_sle64((new_allocated_size - 1) >> 5225987da915Sopenharmony_ci vol->cluster_size_bits); 5226987da915Sopenharmony_ci 5227987da915Sopenharmony_ci a->mapping_pairs_offset = cpu_to_le16(mp_ofs); 5228987da915Sopenharmony_ci 5229987da915Sopenharmony_ci /* 5230987da915Sopenharmony_ci * Update the flags to match the in-memory ones. 5231987da915Sopenharmony_ci * However cannot change the compression state if we had 5232987da915Sopenharmony_ci * a fuse_file_info open with a mark for release. 5233987da915Sopenharmony_ci * The decisions about compression can only be made when 5234987da915Sopenharmony_ci * creating/recreating the stream, not when making non resident. 5235987da915Sopenharmony_ci */ 5236987da915Sopenharmony_ci a->flags &= ~(ATTR_IS_SPARSE | ATTR_IS_ENCRYPTED); 5237987da915Sopenharmony_ci if ((a->flags & ATTR_COMPRESSION_MASK) == ATTR_IS_COMPRESSED) { 5238987da915Sopenharmony_ci /* support only ATTR_IS_COMPRESSED compression mode */ 5239987da915Sopenharmony_ci a->compression_unit = STANDARD_COMPRESSION_UNIT; 5240987da915Sopenharmony_ci a->compressed_size = const_cpu_to_sle64(0); 5241987da915Sopenharmony_ci } else { 5242987da915Sopenharmony_ci a->compression_unit = 0; 5243987da915Sopenharmony_ci a->flags &= ~ATTR_COMPRESSION_MASK; 5244987da915Sopenharmony_ci na->data_flags = a->flags; 5245987da915Sopenharmony_ci } 5246987da915Sopenharmony_ci 5247987da915Sopenharmony_ci memset(&a->reserved1, 0, sizeof(a->reserved1)); 5248987da915Sopenharmony_ci 5249987da915Sopenharmony_ci a->allocated_size = cpu_to_sle64(new_allocated_size); 5250987da915Sopenharmony_ci a->data_size = a->initialized_size = cpu_to_sle64(na->data_size); 5251987da915Sopenharmony_ci 5252987da915Sopenharmony_ci /* Generate the mapping pairs array in the attribute record. */ 5253987da915Sopenharmony_ci if (ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs, arec_size - mp_ofs, 5254987da915Sopenharmony_ci rl, 0, NULL) < 0) { 5255987da915Sopenharmony_ci // FIXME: Eeek! We need rollback! (AIA) 5256987da915Sopenharmony_ci ntfs_log_trace("Eeek! Failed to build mapping pairs. Leaving " 5257987da915Sopenharmony_ci "corrupt attribute record on disk. In memory " 5258987da915Sopenharmony_ci "runlist is still intact! Error code is %i. " 5259987da915Sopenharmony_ci "FIXME: Need to rollback instead!\n", errno); 5260987da915Sopenharmony_ci return -1; 5261987da915Sopenharmony_ci } 5262987da915Sopenharmony_ci 5263987da915Sopenharmony_ci /* Done! */ 5264987da915Sopenharmony_ci return 0; 5265987da915Sopenharmony_ci 5266987da915Sopenharmony_cicluster_free_err_out: 5267987da915Sopenharmony_ci if (rl && ntfs_cluster_free(vol, na, 0, -1) < 0) 5268987da915Sopenharmony_ci ntfs_log_trace("Eeek! Failed to release allocated clusters in error " 5269987da915Sopenharmony_ci "code path. Leaving inconsistent metadata...\n"); 5270987da915Sopenharmony_ci NAttrClearNonResident(na); 5271987da915Sopenharmony_ci NAttrClearFullyMapped(na); 5272987da915Sopenharmony_ci na->allocated_size = na->data_size; 5273987da915Sopenharmony_ci na->rl = NULL; 5274987da915Sopenharmony_ci free(rl); 5275987da915Sopenharmony_ci errno = err; 5276987da915Sopenharmony_ci return -1; 5277987da915Sopenharmony_ci} 5278987da915Sopenharmony_ci 5279987da915Sopenharmony_ci 5280987da915Sopenharmony_cistatic int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize); 5281987da915Sopenharmony_ci 5282987da915Sopenharmony_ci/** 5283987da915Sopenharmony_ci * ntfs_resident_attr_resize - resize a resident, open ntfs attribute 5284987da915Sopenharmony_ci * @na: resident ntfs attribute to resize 5285987da915Sopenharmony_ci * @newsize: new size (in bytes) to which to resize the attribute 5286987da915Sopenharmony_ci * 5287987da915Sopenharmony_ci * Change the size of a resident, open ntfs attribute @na to @newsize bytes. 5288987da915Sopenharmony_ci * Can also be used to force an attribute non-resident. In this case, the 5289987da915Sopenharmony_ci * size cannot be changed. 5290987da915Sopenharmony_ci * 5291987da915Sopenharmony_ci * On success return 0 5292987da915Sopenharmony_ci * On error return values are: 5293987da915Sopenharmony_ci * STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT 5294987da915Sopenharmony_ci * STATUS_ERROR - otherwise 5295987da915Sopenharmony_ci * The following error codes are defined: 5296987da915Sopenharmony_ci * ENOMEM - Not enough memory to complete operation. 5297987da915Sopenharmony_ci * ERANGE - @newsize is not valid for the attribute type of @na. 5298987da915Sopenharmony_ci * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. 5299987da915Sopenharmony_ci */ 5300987da915Sopenharmony_cistatic int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, 5301987da915Sopenharmony_ci hole_type holes) 5302987da915Sopenharmony_ci{ 5303987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 5304987da915Sopenharmony_ci ntfs_volume *vol; 5305987da915Sopenharmony_ci ntfs_inode *ni; 5306987da915Sopenharmony_ci int err, ret = STATUS_ERROR; 5307987da915Sopenharmony_ci 5308987da915Sopenharmony_ci ntfs_log_trace("Inode 0x%llx attr 0x%x new size %lld\n", 5309987da915Sopenharmony_ci (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type), 5310987da915Sopenharmony_ci (long long)newsize); 5311987da915Sopenharmony_ci 5312987da915Sopenharmony_ci /* Get the attribute record that needs modification. */ 5313987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(na->ni, NULL); 5314987da915Sopenharmony_ci if (!ctx) 5315987da915Sopenharmony_ci return -1; 5316987da915Sopenharmony_ci if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 0, NULL, 0, 5317987da915Sopenharmony_ci ctx)) { 5318987da915Sopenharmony_ci err = errno; 5319987da915Sopenharmony_ci ntfs_log_perror("ntfs_attr_lookup failed"); 5320987da915Sopenharmony_ci goto put_err_out; 5321987da915Sopenharmony_ci } 5322987da915Sopenharmony_ci vol = na->ni->vol; 5323987da915Sopenharmony_ci /* 5324987da915Sopenharmony_ci * Check the attribute type and the corresponding minimum and maximum 5325987da915Sopenharmony_ci * sizes against @newsize and fail if @newsize is out of bounds. 5326987da915Sopenharmony_ci */ 5327987da915Sopenharmony_ci if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { 5328987da915Sopenharmony_ci err = errno; 5329987da915Sopenharmony_ci if (err == ENOENT) 5330987da915Sopenharmony_ci err = EIO; 5331987da915Sopenharmony_ci ntfs_log_perror("%s: bounds check failed", __FUNCTION__); 5332987da915Sopenharmony_ci goto put_err_out; 5333987da915Sopenharmony_ci } 5334987da915Sopenharmony_ci /* 5335987da915Sopenharmony_ci * If @newsize is bigger than the mft record we need to make the 5336987da915Sopenharmony_ci * attribute non-resident if the attribute type supports it. If it is 5337987da915Sopenharmony_ci * smaller we can go ahead and attempt the resize. 5338987da915Sopenharmony_ci */ 5339987da915Sopenharmony_ci if ((newsize < vol->mft_record_size) && (holes != HOLES_NONRES)) { 5340987da915Sopenharmony_ci /* Perform the resize of the attribute record. */ 5341987da915Sopenharmony_ci if (!(ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, 5342987da915Sopenharmony_ci newsize))) { 5343987da915Sopenharmony_ci /* Update attribute size everywhere. */ 5344987da915Sopenharmony_ci na->data_size = na->initialized_size = newsize; 5345987da915Sopenharmony_ci na->allocated_size = (newsize + 7) & ~7; 5346987da915Sopenharmony_ci if ((na->data_flags & ATTR_COMPRESSION_MASK) 5347987da915Sopenharmony_ci || NAttrSparse(na)) 5348987da915Sopenharmony_ci na->compressed_size = na->allocated_size; 5349987da915Sopenharmony_ci if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY 5350987da915Sopenharmony_ci ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 5351987da915Sopenharmony_ci : na->type == AT_DATA && na->name == AT_UNNAMED) { 5352987da915Sopenharmony_ci na->ni->data_size = na->data_size; 5353987da915Sopenharmony_ci if (((na->data_flags & ATTR_COMPRESSION_MASK) 5354987da915Sopenharmony_ci || NAttrSparse(na)) 5355987da915Sopenharmony_ci && NAttrNonResident(na)) 5356987da915Sopenharmony_ci na->ni->allocated_size 5357987da915Sopenharmony_ci = na->compressed_size; 5358987da915Sopenharmony_ci else 5359987da915Sopenharmony_ci na->ni->allocated_size 5360987da915Sopenharmony_ci = na->allocated_size; 5361987da915Sopenharmony_ci set_nino_flag(na->ni,KnownSize); 5362987da915Sopenharmony_ci if (na->type == AT_DATA) 5363987da915Sopenharmony_ci NInoFileNameSetDirty(na->ni); 5364987da915Sopenharmony_ci } 5365987da915Sopenharmony_ci goto resize_done; 5366987da915Sopenharmony_ci } 5367987da915Sopenharmony_ci /* Prefer AT_INDEX_ALLOCATION instead of AT_ATTRIBUTE_LIST */ 5368987da915Sopenharmony_ci if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) { 5369987da915Sopenharmony_ci err = errno; 5370987da915Sopenharmony_ci goto put_err_out; 5371987da915Sopenharmony_ci } 5372987da915Sopenharmony_ci } 5373987da915Sopenharmony_ci /* There is not enough space in the mft record to perform the resize. */ 5374987da915Sopenharmony_ci 5375987da915Sopenharmony_ci /* Make the attribute non-resident if possible. */ 5376987da915Sopenharmony_ci if (!ntfs_attr_make_non_resident(na, ctx)) { 5377987da915Sopenharmony_ci ntfs_inode_mark_dirty(ctx->ntfs_ino); 5378987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 5379987da915Sopenharmony_ci /* 5380987da915Sopenharmony_ci * do not truncate when forcing non-resident, this 5381987da915Sopenharmony_ci * could cause the attribute to be made resident again, 5382987da915Sopenharmony_ci * so size changes are not allowed. 5383987da915Sopenharmony_ci */ 5384987da915Sopenharmony_ci if (holes == HOLES_NONRES) { 5385987da915Sopenharmony_ci ret = 0; 5386987da915Sopenharmony_ci if (newsize != na->data_size) { 5387987da915Sopenharmony_ci ntfs_log_error("Cannot change size when" 5388987da915Sopenharmony_ci " forcing non-resident\n"); 5389987da915Sopenharmony_ci errno = EIO; 5390987da915Sopenharmony_ci ret = STATUS_ERROR; 5391987da915Sopenharmony_ci } 5392987da915Sopenharmony_ci return (ret); 5393987da915Sopenharmony_ci } 5394987da915Sopenharmony_ci /* Resize non-resident attribute */ 5395987da915Sopenharmony_ci return ntfs_attr_truncate_i(na, newsize, holes); 5396987da915Sopenharmony_ci } else if (errno != ENOSPC && errno != EPERM) { 5397987da915Sopenharmony_ci err = errno; 5398987da915Sopenharmony_ci ntfs_log_perror("Failed to make attribute non-resident"); 5399987da915Sopenharmony_ci goto put_err_out; 5400987da915Sopenharmony_ci } 5401987da915Sopenharmony_ci 5402987da915Sopenharmony_ci /* Try to make other attributes non-resident and retry each time. */ 5403987da915Sopenharmony_ci ntfs_attr_init_search_ctx(ctx, NULL, na->ni->mrec); 5404987da915Sopenharmony_ci while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { 5405987da915Sopenharmony_ci ntfs_attr *tna; 5406987da915Sopenharmony_ci ATTR_RECORD *a; 5407987da915Sopenharmony_ci 5408987da915Sopenharmony_ci a = ctx->attr; 5409987da915Sopenharmony_ci if (a->non_resident) 5410987da915Sopenharmony_ci continue; 5411987da915Sopenharmony_ci 5412987da915Sopenharmony_ci /* 5413987da915Sopenharmony_ci * Check out whether convert is reasonable. Assume that mapping 5414987da915Sopenharmony_ci * pairs will take 8 bytes. 5415987da915Sopenharmony_ci */ 5416987da915Sopenharmony_ci if (le32_to_cpu(a->length) <= offsetof(ATTR_RECORD, 5417987da915Sopenharmony_ci compressed_size) + ((a->name_length * 5418987da915Sopenharmony_ci sizeof(ntfschar) + 7) & ~7) + 8) 5419987da915Sopenharmony_ci continue; 5420987da915Sopenharmony_ci 5421987da915Sopenharmony_ci tna = ntfs_attr_open(na->ni, a->type, (ntfschar*)((u8*)a + 5422987da915Sopenharmony_ci le16_to_cpu(a->name_offset)), a->name_length); 5423987da915Sopenharmony_ci if (!tna) { 5424987da915Sopenharmony_ci err = errno; 5425987da915Sopenharmony_ci ntfs_log_perror("Couldn't open attribute"); 5426987da915Sopenharmony_ci goto put_err_out; 5427987da915Sopenharmony_ci } 5428987da915Sopenharmony_ci if (ntfs_attr_make_non_resident(tna, ctx)) { 5429987da915Sopenharmony_ci ntfs_attr_close(tna); 5430987da915Sopenharmony_ci continue; 5431987da915Sopenharmony_ci } 5432987da915Sopenharmony_ci if ((tna->type == AT_DATA) && !tna->name_len) { 5433987da915Sopenharmony_ci /* 5434987da915Sopenharmony_ci * If we had to make the unnamed data attribute 5435987da915Sopenharmony_ci * non-resident, propagate its new allocated size 5436987da915Sopenharmony_ci * to all name attributes and directory indexes 5437987da915Sopenharmony_ci */ 5438987da915Sopenharmony_ci tna->ni->allocated_size = tna->allocated_size; 5439987da915Sopenharmony_ci NInoFileNameSetDirty(tna->ni); 5440987da915Sopenharmony_ci } 5441987da915Sopenharmony_ci if (((tna->data_flags & ATTR_COMPRESSION_MASK) 5442987da915Sopenharmony_ci == ATTR_IS_COMPRESSED) 5443987da915Sopenharmony_ci && ntfs_attr_pclose(tna)) { 5444987da915Sopenharmony_ci err = errno; 5445987da915Sopenharmony_ci ntfs_attr_close(tna); 5446987da915Sopenharmony_ci goto put_err_out; 5447987da915Sopenharmony_ci } 5448987da915Sopenharmony_ci ntfs_inode_mark_dirty(tna->ni); 5449987da915Sopenharmony_ci ntfs_attr_close(tna); 5450987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 5451987da915Sopenharmony_ci return ntfs_resident_attr_resize_i(na, newsize, holes); 5452987da915Sopenharmony_ci } 5453987da915Sopenharmony_ci /* Check whether error occurred. */ 5454987da915Sopenharmony_ci if (errno != ENOENT) { 5455987da915Sopenharmony_ci err = errno; 5456987da915Sopenharmony_ci ntfs_log_perror("%s: Attribute lookup failed 1", __FUNCTION__); 5457987da915Sopenharmony_ci goto put_err_out; 5458987da915Sopenharmony_ci } 5459987da915Sopenharmony_ci 5460987da915Sopenharmony_ci /* 5461987da915Sopenharmony_ci * The standard information and attribute list attributes can't be 5462987da915Sopenharmony_ci * moved out from the base MFT record, so try to move out others. 5463987da915Sopenharmony_ci */ 5464987da915Sopenharmony_ci if (na->type==AT_STANDARD_INFORMATION || na->type==AT_ATTRIBUTE_LIST) { 5465987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 5466987da915Sopenharmony_ci if (!NInoAttrList(na->ni) && ntfs_inode_add_attrlist(na->ni)) { 5467987da915Sopenharmony_ci ntfs_log_perror("Could not add attribute list"); 5468987da915Sopenharmony_ci return -1; 5469987da915Sopenharmony_ci } 5470987da915Sopenharmony_ci if (ntfs_inode_free_space(na->ni, offsetof(ATTR_RECORD, 5471987da915Sopenharmony_ci non_resident_end) + 8)) { 5472987da915Sopenharmony_ci ntfs_log_perror("Could not free space in MFT record"); 5473987da915Sopenharmony_ci return -1; 5474987da915Sopenharmony_ci } 5475987da915Sopenharmony_ci return ntfs_resident_attr_resize_i(na, newsize, holes); 5476987da915Sopenharmony_ci } 5477987da915Sopenharmony_ci 5478987da915Sopenharmony_ci /* 5479987da915Sopenharmony_ci * Move the attribute to a new mft record, creating an attribute list 5480987da915Sopenharmony_ci * attribute or modifying it if it is already present. 5481987da915Sopenharmony_ci */ 5482987da915Sopenharmony_ci 5483987da915Sopenharmony_ci /* Point search context back to attribute which we need resize. */ 5484987da915Sopenharmony_ci ntfs_attr_init_search_ctx(ctx, na->ni, NULL); 5485987da915Sopenharmony_ci if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, 5486987da915Sopenharmony_ci 0, NULL, 0, ctx)) { 5487987da915Sopenharmony_ci ntfs_log_perror("%s: Attribute lookup failed 2", __FUNCTION__); 5488987da915Sopenharmony_ci err = errno; 5489987da915Sopenharmony_ci goto put_err_out; 5490987da915Sopenharmony_ci } 5491987da915Sopenharmony_ci 5492987da915Sopenharmony_ci /* 5493987da915Sopenharmony_ci * Check whether attribute is already single in this MFT record. 5494987da915Sopenharmony_ci * 8 added for the attribute terminator. 5495987da915Sopenharmony_ci */ 5496987da915Sopenharmony_ci if (le32_to_cpu(ctx->mrec->bytes_in_use) == 5497987da915Sopenharmony_ci le16_to_cpu(ctx->mrec->attrs_offset) + 5498987da915Sopenharmony_ci le32_to_cpu(ctx->attr->length) + 8) { 5499987da915Sopenharmony_ci err = ENOSPC; 5500987da915Sopenharmony_ci ntfs_log_trace("MFT record is filled with one attribute\n"); 5501987da915Sopenharmony_ci ret = STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT; 5502987da915Sopenharmony_ci goto put_err_out; 5503987da915Sopenharmony_ci } 5504987da915Sopenharmony_ci 5505987da915Sopenharmony_ci /* Add attribute list if not present. */ 5506987da915Sopenharmony_ci if (na->ni->nr_extents == -1) 5507987da915Sopenharmony_ci ni = na->ni->base_ni; 5508987da915Sopenharmony_ci else 5509987da915Sopenharmony_ci ni = na->ni; 5510987da915Sopenharmony_ci if (!NInoAttrList(ni)) { 5511987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 5512987da915Sopenharmony_ci if (ntfs_inode_add_attrlist(ni)) 5513987da915Sopenharmony_ci return -1; 5514987da915Sopenharmony_ci return ntfs_resident_attr_resize_i(na, newsize, holes); 5515987da915Sopenharmony_ci } 5516987da915Sopenharmony_ci /* Allocate new mft record. */ 5517987da915Sopenharmony_ci ni = ntfs_mft_record_alloc(vol, ni); 5518987da915Sopenharmony_ci if (!ni) { 5519987da915Sopenharmony_ci err = errno; 5520987da915Sopenharmony_ci ntfs_log_perror("Couldn't allocate new MFT record"); 5521987da915Sopenharmony_ci goto put_err_out; 5522987da915Sopenharmony_ci } 5523987da915Sopenharmony_ci /* Move attribute to it. */ 5524987da915Sopenharmony_ci if (ntfs_attr_record_move_to(ctx, ni)) { 5525987da915Sopenharmony_ci err = errno; 5526987da915Sopenharmony_ci ntfs_log_perror("Couldn't move attribute to new MFT record"); 5527987da915Sopenharmony_ci goto put_err_out; 5528987da915Sopenharmony_ci } 5529987da915Sopenharmony_ci /* Update ntfs attribute. */ 5530987da915Sopenharmony_ci if (na->ni->nr_extents == -1) 5531987da915Sopenharmony_ci na->ni = ni; 5532987da915Sopenharmony_ci 5533987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 5534987da915Sopenharmony_ci /* Try to perform resize once again. */ 5535987da915Sopenharmony_ci return ntfs_resident_attr_resize_i(na, newsize, holes); 5536987da915Sopenharmony_ci 5537987da915Sopenharmony_ciresize_done: 5538987da915Sopenharmony_ci /* 5539987da915Sopenharmony_ci * Set the inode (and its base inode if it exists) dirty so it is 5540987da915Sopenharmony_ci * written out later. 5541987da915Sopenharmony_ci */ 5542987da915Sopenharmony_ci ntfs_inode_mark_dirty(ctx->ntfs_ino); 5543987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 5544987da915Sopenharmony_ci return 0; 5545987da915Sopenharmony_ciput_err_out: 5546987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 5547987da915Sopenharmony_ci errno = err; 5548987da915Sopenharmony_ci return ret; 5549987da915Sopenharmony_ci} 5550987da915Sopenharmony_ci 5551987da915Sopenharmony_cistatic int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) 5552987da915Sopenharmony_ci{ 5553987da915Sopenharmony_ci int ret; 5554987da915Sopenharmony_ci 5555987da915Sopenharmony_ci ntfs_log_enter("Entering\n"); 5556987da915Sopenharmony_ci ret = ntfs_resident_attr_resize_i(na, newsize, HOLES_OK); 5557987da915Sopenharmony_ci ntfs_log_leave("\n"); 5558987da915Sopenharmony_ci return ret; 5559987da915Sopenharmony_ci} 5560987da915Sopenharmony_ci 5561987da915Sopenharmony_ci/* 5562987da915Sopenharmony_ci * Force an attribute to be made non-resident without 5563987da915Sopenharmony_ci * changing its size. 5564987da915Sopenharmony_ci * 5565987da915Sopenharmony_ci * This is particularly needed when the attribute has no data, 5566987da915Sopenharmony_ci * as the non-resident variant requires more space in the MFT 5567987da915Sopenharmony_ci * record, and may imply expelling some other attribute. 5568987da915Sopenharmony_ci * 5569987da915Sopenharmony_ci * As a consequence the existing ntfs_attr_search_ctx's have to 5570987da915Sopenharmony_ci * be closed or reinitialized. 5571987da915Sopenharmony_ci * 5572987da915Sopenharmony_ci * returns 0 if successful, 5573987da915Sopenharmony_ci * < 0 if failed, with errno telling why 5574987da915Sopenharmony_ci */ 5575987da915Sopenharmony_ci 5576987da915Sopenharmony_ciint ntfs_attr_force_non_resident(ntfs_attr *na) 5577987da915Sopenharmony_ci{ 5578987da915Sopenharmony_ci int res; 5579987da915Sopenharmony_ci 5580987da915Sopenharmony_ci res = ntfs_resident_attr_resize_i(na, na->data_size, HOLES_NONRES); 5581987da915Sopenharmony_ci if (!res && !NAttrNonResident(na)) { 5582987da915Sopenharmony_ci res = -1; 5583987da915Sopenharmony_ci errno = EIO; 5584987da915Sopenharmony_ci ntfs_log_error("Failed to force non-resident\n"); 5585987da915Sopenharmony_ci } 5586987da915Sopenharmony_ci return (res); 5587987da915Sopenharmony_ci} 5588987da915Sopenharmony_ci 5589987da915Sopenharmony_ci/** 5590987da915Sopenharmony_ci * ntfs_attr_make_resident - convert a non-resident to a resident attribute 5591987da915Sopenharmony_ci * @na: open ntfs attribute to make resident 5592987da915Sopenharmony_ci * @ctx: ntfs search context describing the attribute 5593987da915Sopenharmony_ci * 5594987da915Sopenharmony_ci * Convert a non-resident ntfs attribute to a resident one. 5595987da915Sopenharmony_ci * 5596987da915Sopenharmony_ci * Return 0 on success and -1 on error with errno set to the error code. The 5597987da915Sopenharmony_ci * following error codes are defined: 5598987da915Sopenharmony_ci * EINVAL - Invalid arguments passed. 5599987da915Sopenharmony_ci * EPERM - The attribute is not allowed to be resident. 5600987da915Sopenharmony_ci * EIO - I/O error, damaged inode or bug. 5601987da915Sopenharmony_ci * ENOSPC - There is no enough space to perform conversion. 5602987da915Sopenharmony_ci * EOPNOTSUPP - Requested conversion is not supported yet. 5603987da915Sopenharmony_ci * 5604987da915Sopenharmony_ci * Warning: We do not set the inode dirty and we do not write out anything! 5605987da915Sopenharmony_ci * We expect the caller to do this as this is a fairly low level 5606987da915Sopenharmony_ci * function and it is likely there will be further changes made. 5607987da915Sopenharmony_ci */ 5608987da915Sopenharmony_cistatic int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) 5609987da915Sopenharmony_ci{ 5610987da915Sopenharmony_ci ntfs_volume *vol = na->ni->vol; 5611987da915Sopenharmony_ci ATTR_REC *a = ctx->attr; 5612987da915Sopenharmony_ci int name_ofs, val_ofs, err = EIO; 5613987da915Sopenharmony_ci s64 arec_size, bytes_read; 5614987da915Sopenharmony_ci 5615987da915Sopenharmony_ci ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long 5616987da915Sopenharmony_ci long)na->ni->mft_no, le32_to_cpu(na->type)); 5617987da915Sopenharmony_ci 5618987da915Sopenharmony_ci /* Should be called for the first extent of the attribute. */ 5619987da915Sopenharmony_ci if (sle64_to_cpu(a->lowest_vcn)) { 5620987da915Sopenharmony_ci ntfs_log_trace("Eeek! Should be called for the first extent of the " 5621987da915Sopenharmony_ci "attribute. Aborting...\n"); 5622987da915Sopenharmony_ci errno = EINVAL; 5623987da915Sopenharmony_ci return -1; 5624987da915Sopenharmony_ci } 5625987da915Sopenharmony_ci 5626987da915Sopenharmony_ci /* Some preliminary sanity checking. */ 5627987da915Sopenharmony_ci if (!NAttrNonResident(na)) { 5628987da915Sopenharmony_ci ntfs_log_trace("Eeek! Trying to make resident attribute resident. " 5629987da915Sopenharmony_ci "Aborting...\n"); 5630987da915Sopenharmony_ci errno = EINVAL; 5631987da915Sopenharmony_ci return -1; 5632987da915Sopenharmony_ci } 5633987da915Sopenharmony_ci 5634987da915Sopenharmony_ci /* Make sure this is not $MFT/$BITMAP or Windows will not boot! */ 5635987da915Sopenharmony_ci if (na->type == AT_BITMAP && na->ni->mft_no == FILE_MFT) { 5636987da915Sopenharmony_ci errno = EPERM; 5637987da915Sopenharmony_ci return -1; 5638987da915Sopenharmony_ci } 5639987da915Sopenharmony_ci 5640987da915Sopenharmony_ci /* Check that the attribute is allowed to be resident. */ 5641987da915Sopenharmony_ci if (ntfs_attr_can_be_resident(vol, na->type)) 5642987da915Sopenharmony_ci return -1; 5643987da915Sopenharmony_ci 5644987da915Sopenharmony_ci if (na->data_flags & ATTR_IS_ENCRYPTED) { 5645987da915Sopenharmony_ci ntfs_log_trace("Making encrypted streams resident is not " 5646987da915Sopenharmony_ci "implemented yet.\n"); 5647987da915Sopenharmony_ci errno = EOPNOTSUPP; 5648987da915Sopenharmony_ci return -1; 5649987da915Sopenharmony_ci } 5650987da915Sopenharmony_ci 5651987da915Sopenharmony_ci /* Work out offsets into and size of the resident attribute. */ 5652987da915Sopenharmony_ci name_ofs = 24; /* = sizeof(resident_ATTR_REC); */ 5653987da915Sopenharmony_ci val_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7; 5654987da915Sopenharmony_ci arec_size = (val_ofs + na->data_size + 7) & ~7; 5655987da915Sopenharmony_ci 5656987da915Sopenharmony_ci /* Sanity check the size before we start modifying the attribute. */ 5657987da915Sopenharmony_ci if (le32_to_cpu(ctx->mrec->bytes_in_use) - le32_to_cpu(a->length) + 5658987da915Sopenharmony_ci arec_size > le32_to_cpu(ctx->mrec->bytes_allocated)) { 5659987da915Sopenharmony_ci errno = ENOSPC; 5660987da915Sopenharmony_ci ntfs_log_trace("Not enough space to make attribute resident\n"); 5661987da915Sopenharmony_ci return -1; 5662987da915Sopenharmony_ci } 5663987da915Sopenharmony_ci 5664987da915Sopenharmony_ci /* Read and cache the whole runlist if not already done. */ 5665987da915Sopenharmony_ci if (ntfs_attr_map_whole_runlist(na)) 5666987da915Sopenharmony_ci return -1; 5667987da915Sopenharmony_ci 5668987da915Sopenharmony_ci /* Move the attribute name if it exists and update the offset. */ 5669987da915Sopenharmony_ci if (a->name_length) { 5670987da915Sopenharmony_ci memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), 5671987da915Sopenharmony_ci a->name_length * sizeof(ntfschar)); 5672987da915Sopenharmony_ci } 5673987da915Sopenharmony_ci a->name_offset = cpu_to_le16(name_ofs); 5674987da915Sopenharmony_ci 5675987da915Sopenharmony_ci /* Resize the resident part of the attribute record. */ 5676987da915Sopenharmony_ci if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) { 5677987da915Sopenharmony_ci /* 5678987da915Sopenharmony_ci * Bug, because ntfs_attr_record_resize should not fail (we 5679987da915Sopenharmony_ci * already checked that attribute fits MFT record). 5680987da915Sopenharmony_ci */ 5681987da915Sopenharmony_ci ntfs_log_error("BUG! Failed to resize attribute record. " 5682987da915Sopenharmony_ci "Please report to the %s. Aborting...\n", 5683987da915Sopenharmony_ci NTFS_DEV_LIST); 5684987da915Sopenharmony_ci errno = EIO; 5685987da915Sopenharmony_ci return -1; 5686987da915Sopenharmony_ci } 5687987da915Sopenharmony_ci 5688987da915Sopenharmony_ci /* Convert the attribute record to describe a resident attribute. */ 5689987da915Sopenharmony_ci a->non_resident = 0; 5690987da915Sopenharmony_ci a->flags = const_cpu_to_le16(0); 5691987da915Sopenharmony_ci a->value_length = cpu_to_le32(na->data_size); 5692987da915Sopenharmony_ci a->value_offset = cpu_to_le16(val_ofs); 5693987da915Sopenharmony_ci /* 5694987da915Sopenharmony_ci * If a data stream was wiped out, adjust the compression mode 5695987da915Sopenharmony_ci * to current state of compression flag 5696987da915Sopenharmony_ci */ 5697987da915Sopenharmony_ci if (!na->data_size 5698987da915Sopenharmony_ci && (na->type == AT_DATA) 5699987da915Sopenharmony_ci && (na->ni->vol->major_ver >= 3) 5700987da915Sopenharmony_ci && NVolCompression(na->ni->vol) 5701987da915Sopenharmony_ci && (na->ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) 5702987da915Sopenharmony_ci && (na->ni->flags & FILE_ATTR_COMPRESSED)) { 5703987da915Sopenharmony_ci a->flags |= ATTR_IS_COMPRESSED; 5704987da915Sopenharmony_ci na->data_flags = a->flags; 5705987da915Sopenharmony_ci } 5706987da915Sopenharmony_ci /* 5707987da915Sopenharmony_ci * File names cannot be non-resident so we would never see this here 5708987da915Sopenharmony_ci * but at least it serves as a reminder that there may be attributes 5709987da915Sopenharmony_ci * for which we do need to set this flag. (AIA) 5710987da915Sopenharmony_ci */ 5711987da915Sopenharmony_ci if (a->type == AT_FILE_NAME) 5712987da915Sopenharmony_ci a->resident_flags = RESIDENT_ATTR_IS_INDEXED; 5713987da915Sopenharmony_ci else 5714987da915Sopenharmony_ci a->resident_flags = 0; 5715987da915Sopenharmony_ci a->reservedR = 0; 5716987da915Sopenharmony_ci 5717987da915Sopenharmony_ci /* Sanity fixup... Shouldn't really happen. (AIA) */ 5718987da915Sopenharmony_ci if (na->initialized_size > na->data_size) 5719987da915Sopenharmony_ci na->initialized_size = na->data_size; 5720987da915Sopenharmony_ci 5721987da915Sopenharmony_ci /* Copy data from run list to resident attribute value. */ 5722987da915Sopenharmony_ci bytes_read = ntfs_rl_pread(vol, na->rl, 0, na->initialized_size, 5723987da915Sopenharmony_ci (u8*)a + val_ofs); 5724987da915Sopenharmony_ci if (bytes_read != na->initialized_size) { 5725987da915Sopenharmony_ci if (bytes_read < 0) 5726987da915Sopenharmony_ci err = errno; 5727987da915Sopenharmony_ci ntfs_log_trace("Eeek! Failed to read attribute data. Leaving " 5728987da915Sopenharmony_ci "inconstant metadata. Run chkdsk. " 5729987da915Sopenharmony_ci "Aborting...\n"); 5730987da915Sopenharmony_ci errno = err; 5731987da915Sopenharmony_ci return -1; 5732987da915Sopenharmony_ci } 5733987da915Sopenharmony_ci 5734987da915Sopenharmony_ci /* Clear memory in gap between initialized_size and data_size. */ 5735987da915Sopenharmony_ci if (na->initialized_size < na->data_size) 5736987da915Sopenharmony_ci memset((u8*)a + val_ofs + na->initialized_size, 0, 5737987da915Sopenharmony_ci na->data_size - na->initialized_size); 5738987da915Sopenharmony_ci 5739987da915Sopenharmony_ci /* 5740987da915Sopenharmony_ci * Deallocate clusters from the runlist. 5741987da915Sopenharmony_ci * 5742987da915Sopenharmony_ci * NOTE: We can use ntfs_cluster_free() because we have already mapped 5743987da915Sopenharmony_ci * the whole run list and thus it doesn't matter that the attribute 5744987da915Sopenharmony_ci * record is in a transiently corrupted state at this moment in time. 5745987da915Sopenharmony_ci */ 5746987da915Sopenharmony_ci if (ntfs_cluster_free(vol, na, 0, -1) < 0) { 5747987da915Sopenharmony_ci ntfs_log_perror("Eeek! Failed to release allocated clusters"); 5748987da915Sopenharmony_ci ntfs_log_trace("Ignoring error and leaving behind wasted " 5749987da915Sopenharmony_ci "clusters.\n"); 5750987da915Sopenharmony_ci } 5751987da915Sopenharmony_ci 5752987da915Sopenharmony_ci /* Throw away the now unused runlist. */ 5753987da915Sopenharmony_ci free(na->rl); 5754987da915Sopenharmony_ci na->rl = NULL; 5755987da915Sopenharmony_ci 5756987da915Sopenharmony_ci /* Update in-memory struct ntfs_attr. */ 5757987da915Sopenharmony_ci NAttrClearNonResident(na); 5758987da915Sopenharmony_ci NAttrClearFullyMapped(na); 5759987da915Sopenharmony_ci NAttrClearSparse(na); 5760987da915Sopenharmony_ci NAttrClearEncrypted(na); 5761987da915Sopenharmony_ci na->initialized_size = na->data_size; 5762987da915Sopenharmony_ci na->allocated_size = na->compressed_size = (na->data_size + 7) & ~7; 5763987da915Sopenharmony_ci na->compression_block_size = 0; 5764987da915Sopenharmony_ci na->compression_block_size_bits = na->compression_block_clusters = 0; 5765987da915Sopenharmony_ci return 0; 5766987da915Sopenharmony_ci} 5767987da915Sopenharmony_ci 5768987da915Sopenharmony_ci/* 5769987da915Sopenharmony_ci * If we are in the first extent, then set/clean sparse bit, 5770987da915Sopenharmony_ci * update allocated and compressed size. 5771987da915Sopenharmony_ci */ 5772987da915Sopenharmony_cistatic int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, 5773987da915Sopenharmony_ci hole_type holes, ntfs_attr_search_ctx *ctx) 5774987da915Sopenharmony_ci{ 5775987da915Sopenharmony_ci int sparse, ret = 0; 5776987da915Sopenharmony_ci 5777987da915Sopenharmony_ci ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x\n", 5778987da915Sopenharmony_ci (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type)); 5779987da915Sopenharmony_ci 5780987da915Sopenharmony_ci if (a->lowest_vcn) 5781987da915Sopenharmony_ci goto out; 5782987da915Sopenharmony_ci 5783987da915Sopenharmony_ci a->allocated_size = cpu_to_sle64(na->allocated_size); 5784987da915Sopenharmony_ci 5785987da915Sopenharmony_ci /* Update sparse bit, unless this is an intermediate state */ 5786987da915Sopenharmony_ci if (holes == HOLES_DELAY) 5787987da915Sopenharmony_ci sparse = (a->flags & ATTR_IS_SPARSE) != const_cpu_to_le16(0); 5788987da915Sopenharmony_ci else { 5789987da915Sopenharmony_ci sparse = ntfs_rl_sparse(na->rl); 5790987da915Sopenharmony_ci if (sparse == -1) { 5791987da915Sopenharmony_ci errno = EIO; 5792987da915Sopenharmony_ci goto error; 5793987da915Sopenharmony_ci } 5794987da915Sopenharmony_ci } 5795987da915Sopenharmony_ci 5796987da915Sopenharmony_ci /* Check whether attribute becomes sparse, unless check is delayed. */ 5797987da915Sopenharmony_ci if ((holes != HOLES_DELAY) 5798987da915Sopenharmony_ci && sparse 5799987da915Sopenharmony_ci && !(a->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED))) { 5800987da915Sopenharmony_ci /* 5801987da915Sopenharmony_ci * Move attribute to another mft record, if attribute is too 5802987da915Sopenharmony_ci * small to add compressed_size field to it and we have no 5803987da915Sopenharmony_ci * free space in the current mft record. 5804987da915Sopenharmony_ci */ 5805987da915Sopenharmony_ci if ((le32_to_cpu(a->length) - 5806987da915Sopenharmony_ci le16_to_cpu(a->mapping_pairs_offset) == 8) 5807987da915Sopenharmony_ci && !(le32_to_cpu(m->bytes_allocated) - 5808987da915Sopenharmony_ci le32_to_cpu(m->bytes_in_use))) { 5809987da915Sopenharmony_ci 5810987da915Sopenharmony_ci if (!NInoAttrList(na->ni)) { 5811987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 5812987da915Sopenharmony_ci if (ntfs_inode_add_attrlist(na->ni)) 5813987da915Sopenharmony_ci goto leave; 5814987da915Sopenharmony_ci goto retry; 5815987da915Sopenharmony_ci } 5816987da915Sopenharmony_ci if (ntfs_attr_record_move_away(ctx, 8)) { 5817987da915Sopenharmony_ci ntfs_log_perror("Failed to move attribute"); 5818987da915Sopenharmony_ci goto error; 5819987da915Sopenharmony_ci } 5820987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 5821987da915Sopenharmony_ci goto retry; 5822987da915Sopenharmony_ci } 5823987da915Sopenharmony_ci if (!(le32_to_cpu(a->length) - le16_to_cpu( 5824987da915Sopenharmony_ci a->mapping_pairs_offset))) { 5825987da915Sopenharmony_ci errno = EIO; 5826987da915Sopenharmony_ci ntfs_log_perror("Mapping pairs space is 0"); 5827987da915Sopenharmony_ci goto error; 5828987da915Sopenharmony_ci } 5829987da915Sopenharmony_ci 5830987da915Sopenharmony_ci NAttrSetSparse(na); 5831987da915Sopenharmony_ci a->flags |= ATTR_IS_SPARSE; 5832987da915Sopenharmony_ci na->data_flags = a->flags; 5833987da915Sopenharmony_ci a->compression_unit = STANDARD_COMPRESSION_UNIT; /* Windows 5834987da915Sopenharmony_ci set it so, even if attribute is not actually compressed. */ 5835987da915Sopenharmony_ci 5836987da915Sopenharmony_ci memmove((u8*)a + le16_to_cpu(a->name_offset) + 8, 5837987da915Sopenharmony_ci (u8*)a + le16_to_cpu(a->name_offset), 5838987da915Sopenharmony_ci a->name_length * sizeof(ntfschar)); 5839987da915Sopenharmony_ci 5840987da915Sopenharmony_ci a->name_offset = cpu_to_le16(le16_to_cpu(a->name_offset) + 8); 5841987da915Sopenharmony_ci 5842987da915Sopenharmony_ci a->mapping_pairs_offset = 5843987da915Sopenharmony_ci cpu_to_le16(le16_to_cpu(a->mapping_pairs_offset) + 8); 5844987da915Sopenharmony_ci } 5845987da915Sopenharmony_ci 5846987da915Sopenharmony_ci /* Attribute no longer sparse. */ 5847987da915Sopenharmony_ci if (!sparse && (a->flags & ATTR_IS_SPARSE) && 5848987da915Sopenharmony_ci !(a->flags & ATTR_IS_COMPRESSED)) { 5849987da915Sopenharmony_ci 5850987da915Sopenharmony_ci NAttrClearSparse(na); 5851987da915Sopenharmony_ci a->flags &= ~ATTR_IS_SPARSE; 5852987da915Sopenharmony_ci na->data_flags = a->flags; 5853987da915Sopenharmony_ci a->compression_unit = 0; 5854987da915Sopenharmony_ci 5855987da915Sopenharmony_ci memmove((u8*)a + le16_to_cpu(a->name_offset) - 8, 5856987da915Sopenharmony_ci (u8*)a + le16_to_cpu(a->name_offset), 5857987da915Sopenharmony_ci a->name_length * sizeof(ntfschar)); 5858987da915Sopenharmony_ci 5859987da915Sopenharmony_ci if (le16_to_cpu(a->name_offset) >= 8) 5860987da915Sopenharmony_ci a->name_offset = cpu_to_le16(le16_to_cpu(a->name_offset) - 8); 5861987da915Sopenharmony_ci 5862987da915Sopenharmony_ci a->mapping_pairs_offset = 5863987da915Sopenharmony_ci cpu_to_le16(le16_to_cpu(a->mapping_pairs_offset) - 8); 5864987da915Sopenharmony_ci } 5865987da915Sopenharmony_ci 5866987da915Sopenharmony_ci /* Update compressed size if required. */ 5867987da915Sopenharmony_ci if (NAttrFullyMapped(na) 5868987da915Sopenharmony_ci && (sparse || (na->data_flags & ATTR_COMPRESSION_MASK))) { 5869987da915Sopenharmony_ci s64 new_compr_size; 5870987da915Sopenharmony_ci 5871987da915Sopenharmony_ci new_compr_size = ntfs_rl_get_compressed_size(na->ni->vol, na->rl); 5872987da915Sopenharmony_ci if (new_compr_size == -1) 5873987da915Sopenharmony_ci goto error; 5874987da915Sopenharmony_ci 5875987da915Sopenharmony_ci na->compressed_size = new_compr_size; 5876987da915Sopenharmony_ci a->compressed_size = cpu_to_sle64(new_compr_size); 5877987da915Sopenharmony_ci } 5878987da915Sopenharmony_ci /* 5879987da915Sopenharmony_ci * Set FILE_NAME dirty flag, to update sparse bit and 5880987da915Sopenharmony_ci * allocated size in the index. 5881987da915Sopenharmony_ci */ 5882987da915Sopenharmony_ci if (na->type == AT_DATA && na->name == AT_UNNAMED) { 5883987da915Sopenharmony_ci if (sparse || (na->data_flags & ATTR_COMPRESSION_MASK)) 5884987da915Sopenharmony_ci na->ni->allocated_size = na->compressed_size; 5885987da915Sopenharmony_ci else 5886987da915Sopenharmony_ci na->ni->allocated_size = na->allocated_size; 5887987da915Sopenharmony_ci NInoFileNameSetDirty(na->ni); 5888987da915Sopenharmony_ci } 5889987da915Sopenharmony_ciout: 5890987da915Sopenharmony_ci return ret; 5891987da915Sopenharmony_cileave: ret = -1; goto out; /* return -1 */ 5892987da915Sopenharmony_ciretry: ret = -2; goto out; 5893987da915Sopenharmony_cierror: ret = -3; goto out; 5894987da915Sopenharmony_ci} 5895987da915Sopenharmony_ci 5896987da915Sopenharmony_ci#define NTFS_VCN_DELETE_MARK -2 5897987da915Sopenharmony_ci/** 5898987da915Sopenharmony_ci * ntfs_attr_update_mapping_pairs_i - see ntfs_attr_update_mapping_pairs 5899987da915Sopenharmony_ci */ 5900987da915Sopenharmony_cistatic int ntfs_attr_update_mapping_pairs_i(ntfs_attr *na, VCN from_vcn, 5901987da915Sopenharmony_ci hole_type holes) 5902987da915Sopenharmony_ci{ 5903987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 5904987da915Sopenharmony_ci ntfs_inode *ni, *base_ni; 5905987da915Sopenharmony_ci MFT_RECORD *m; 5906987da915Sopenharmony_ci ATTR_RECORD *a; 5907987da915Sopenharmony_ci VCN stop_vcn; 5908987da915Sopenharmony_ci const runlist_element *stop_rl; 5909987da915Sopenharmony_ci int err, mp_size, cur_max_mp_size, exp_max_mp_size, ret = -1; 5910987da915Sopenharmony_ci BOOL finished_build; 5911987da915Sopenharmony_ci BOOL first_updated = FALSE; 5912987da915Sopenharmony_ci 5913987da915Sopenharmony_ciretry: 5914987da915Sopenharmony_ci if (!na || !na->rl) { 5915987da915Sopenharmony_ci errno = EINVAL; 5916987da915Sopenharmony_ci ntfs_log_perror("%s: na=%p", __FUNCTION__, na); 5917987da915Sopenharmony_ci return -1; 5918987da915Sopenharmony_ci } 5919987da915Sopenharmony_ci 5920987da915Sopenharmony_ci ntfs_log_trace("Entering for inode %llu, attr 0x%x\n", 5921987da915Sopenharmony_ci (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type)); 5922987da915Sopenharmony_ci 5923987da915Sopenharmony_ci if (!NAttrNonResident(na)) { 5924987da915Sopenharmony_ci errno = EINVAL; 5925987da915Sopenharmony_ci ntfs_log_perror("%s: resident attribute", __FUNCTION__); 5926987da915Sopenharmony_ci return -1; 5927987da915Sopenharmony_ci } 5928987da915Sopenharmony_ci 5929987da915Sopenharmony_ci#if PARTIAL_RUNLIST_UPDATING 5930987da915Sopenharmony_ci /* 5931987da915Sopenharmony_ci * For a file just been made sparse, we will have 5932987da915Sopenharmony_ci * to reformat the first extent, so be sure the 5933987da915Sopenharmony_ci * runlist is fully mapped and fully processed. 5934987da915Sopenharmony_ci * Same if the file was sparse and is not any more. 5935987da915Sopenharmony_ci * Note : not needed if the full runlist is to be processed 5936987da915Sopenharmony_ci */ 5937987da915Sopenharmony_ci if ((holes != HOLES_DELAY) 5938987da915Sopenharmony_ci && (!NAttrFullyMapped(na) || from_vcn) 5939987da915Sopenharmony_ci && !(na->data_flags & ATTR_IS_COMPRESSED)) { 5940987da915Sopenharmony_ci BOOL changed; 5941987da915Sopenharmony_ci 5942987da915Sopenharmony_ci if (!(na->data_flags & ATTR_IS_SPARSE)) { 5943987da915Sopenharmony_ci int sparse = 0; 5944987da915Sopenharmony_ci runlist_element *xrl; 5945987da915Sopenharmony_ci 5946987da915Sopenharmony_ci /* 5947987da915Sopenharmony_ci * If attribute was not sparse, we only 5948987da915Sopenharmony_ci * have to check whether there is a hole 5949987da915Sopenharmony_ci * in the updated region. 5950987da915Sopenharmony_ci */ 5951987da915Sopenharmony_ci for (xrl = na->rl; xrl->length; xrl++) { 5952987da915Sopenharmony_ci if (xrl->lcn < 0) { 5953987da915Sopenharmony_ci if (xrl->lcn == LCN_HOLE) { 5954987da915Sopenharmony_ci sparse = 1; 5955987da915Sopenharmony_ci break; 5956987da915Sopenharmony_ci } 5957987da915Sopenharmony_ci if (xrl->lcn != LCN_RL_NOT_MAPPED) { 5958987da915Sopenharmony_ci sparse = -1; 5959987da915Sopenharmony_ci break; 5960987da915Sopenharmony_ci } 5961987da915Sopenharmony_ci } 5962987da915Sopenharmony_ci } 5963987da915Sopenharmony_ci if (sparse < 0) { 5964987da915Sopenharmony_ci ntfs_log_error("Could not check whether sparse\n"); 5965987da915Sopenharmony_ci errno = EIO; 5966987da915Sopenharmony_ci return (-1); 5967987da915Sopenharmony_ci } 5968987da915Sopenharmony_ci changed = sparse > 0; 5969987da915Sopenharmony_ci } else { 5970987da915Sopenharmony_ci /* 5971987da915Sopenharmony_ci * If attribute was sparse, the compressed 5972987da915Sopenharmony_ci * size has been maintained, and it gives 5973987da915Sopenharmony_ci * and easy way to check whether the 5974987da915Sopenharmony_ci * attribute is still sparse. 5975987da915Sopenharmony_ci */ 5976987da915Sopenharmony_ci changed = (((na->data_size - 1) 5977987da915Sopenharmony_ci | (na->ni->vol->cluster_size - 1)) + 1) 5978987da915Sopenharmony_ci == na->compressed_size; 5979987da915Sopenharmony_ci } 5980987da915Sopenharmony_ci if (changed) { 5981987da915Sopenharmony_ci if (ntfs_attr_map_whole_runlist(na)) { 5982987da915Sopenharmony_ci ntfs_log_error("Could not map whole for sparse change\n"); 5983987da915Sopenharmony_ci errno = EIO; 5984987da915Sopenharmony_ci return (-1); 5985987da915Sopenharmony_ci } 5986987da915Sopenharmony_ci from_vcn = 0; 5987987da915Sopenharmony_ci } 5988987da915Sopenharmony_ci } 5989987da915Sopenharmony_ci#endif 5990987da915Sopenharmony_ci if (na->ni->nr_extents == -1) 5991987da915Sopenharmony_ci base_ni = na->ni->base_ni; 5992987da915Sopenharmony_ci else 5993987da915Sopenharmony_ci base_ni = na->ni; 5994987da915Sopenharmony_ci 5995987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(base_ni, NULL); 5996987da915Sopenharmony_ci if (!ctx) 5997987da915Sopenharmony_ci return -1; 5998987da915Sopenharmony_ci 5999987da915Sopenharmony_ci /* Fill attribute records with new mapping pairs. */ 6000987da915Sopenharmony_ci stop_vcn = 0; 6001987da915Sopenharmony_ci stop_rl = na->rl; 6002987da915Sopenharmony_ci finished_build = FALSE; 6003987da915Sopenharmony_ci while (!ntfs_attr_lookup(na->type, na->name, na->name_len, 6004987da915Sopenharmony_ci CASE_SENSITIVE, from_vcn, NULL, 0, ctx)) { 6005987da915Sopenharmony_ci a = ctx->attr; 6006987da915Sopenharmony_ci m = ctx->mrec; 6007987da915Sopenharmony_ci if (!a->lowest_vcn) 6008987da915Sopenharmony_ci first_updated = TRUE; 6009987da915Sopenharmony_ci /* 6010987da915Sopenharmony_ci * If runlist is updating not from the beginning, then set 6011987da915Sopenharmony_ci * @stop_vcn properly, i.e. to the lowest vcn of record that 6012987da915Sopenharmony_ci * contain @from_vcn. Also we do not need @from_vcn anymore, 6013987da915Sopenharmony_ci * set it to 0 to make ntfs_attr_lookup enumerate attributes. 6014987da915Sopenharmony_ci */ 6015987da915Sopenharmony_ci if (from_vcn) { 6016987da915Sopenharmony_ci LCN first_lcn; 6017987da915Sopenharmony_ci 6018987da915Sopenharmony_ci stop_vcn = sle64_to_cpu(a->lowest_vcn); 6019987da915Sopenharmony_ci from_vcn = 0; 6020987da915Sopenharmony_ci /* 6021987da915Sopenharmony_ci * Check whether the first run we need to update is 6022987da915Sopenharmony_ci * the last run in runlist, if so, then deallocate 6023987da915Sopenharmony_ci * all attrubute extents starting this one. 6024987da915Sopenharmony_ci */ 6025987da915Sopenharmony_ci first_lcn = ntfs_rl_vcn_to_lcn(na->rl, stop_vcn); 6026987da915Sopenharmony_ci if (first_lcn == LCN_EINVAL) { 6027987da915Sopenharmony_ci errno = EIO; 6028987da915Sopenharmony_ci ntfs_log_perror("Bad runlist"); 6029987da915Sopenharmony_ci goto put_err_out; 6030987da915Sopenharmony_ci } 6031987da915Sopenharmony_ci if (first_lcn == LCN_ENOENT || 6032987da915Sopenharmony_ci first_lcn == LCN_RL_NOT_MAPPED) 6033987da915Sopenharmony_ci finished_build = TRUE; 6034987da915Sopenharmony_ci } 6035987da915Sopenharmony_ci 6036987da915Sopenharmony_ci /* 6037987da915Sopenharmony_ci * Check whether we finished mapping pairs build, if so mark 6038987da915Sopenharmony_ci * extent as need to delete (by setting highest vcn to 6039987da915Sopenharmony_ci * NTFS_VCN_DELETE_MARK (-2), we shall check it later and 6040987da915Sopenharmony_ci * delete extent) and continue search. 6041987da915Sopenharmony_ci */ 6042987da915Sopenharmony_ci if (finished_build) { 6043987da915Sopenharmony_ci ntfs_log_trace("Mark attr 0x%x for delete in inode " 6044987da915Sopenharmony_ci "%lld.\n", (unsigned)le32_to_cpu(a->type), 6045987da915Sopenharmony_ci (long long)ctx->ntfs_ino->mft_no); 6046987da915Sopenharmony_ci a->highest_vcn = cpu_to_sle64(NTFS_VCN_DELETE_MARK); 6047987da915Sopenharmony_ci ntfs_inode_mark_dirty(ctx->ntfs_ino); 6048987da915Sopenharmony_ci continue; 6049987da915Sopenharmony_ci } 6050987da915Sopenharmony_ci 6051987da915Sopenharmony_ci switch (ntfs_attr_update_meta(a, na, m, holes, ctx)) { 6052987da915Sopenharmony_ci case -1: return -1; 6053987da915Sopenharmony_ci case -2: goto retry; 6054987da915Sopenharmony_ci case -3: goto put_err_out; 6055987da915Sopenharmony_ci } 6056987da915Sopenharmony_ci 6057987da915Sopenharmony_ci /* 6058987da915Sopenharmony_ci * Determine maximum possible length of mapping pairs, 6059987da915Sopenharmony_ci * if we shall *not* expand space for mapping pairs. 6060987da915Sopenharmony_ci */ 6061987da915Sopenharmony_ci cur_max_mp_size = le32_to_cpu(a->length) - 6062987da915Sopenharmony_ci le16_to_cpu(a->mapping_pairs_offset); 6063987da915Sopenharmony_ci /* 6064987da915Sopenharmony_ci * Determine maximum possible length of mapping pairs in the 6065987da915Sopenharmony_ci * current mft record, if we shall expand space for mapping 6066987da915Sopenharmony_ci * pairs. 6067987da915Sopenharmony_ci */ 6068987da915Sopenharmony_ci exp_max_mp_size = le32_to_cpu(m->bytes_allocated) - 6069987da915Sopenharmony_ci le32_to_cpu(m->bytes_in_use) + cur_max_mp_size; 6070987da915Sopenharmony_ci /* Get the size for the rest of mapping pairs array. */ 6071987da915Sopenharmony_ci mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, stop_rl, 6072987da915Sopenharmony_ci stop_vcn, exp_max_mp_size); 6073987da915Sopenharmony_ci if (mp_size <= 0) { 6074987da915Sopenharmony_ci ntfs_log_perror("%s: get MP size failed", __FUNCTION__); 6075987da915Sopenharmony_ci goto put_err_out; 6076987da915Sopenharmony_ci } 6077987da915Sopenharmony_ci /* Test mapping pairs for fitting in the current mft record. */ 6078987da915Sopenharmony_ci if (mp_size > exp_max_mp_size) { 6079987da915Sopenharmony_ci /* 6080987da915Sopenharmony_ci * Mapping pairs of $ATTRIBUTE_LIST attribute must fit 6081987da915Sopenharmony_ci * in the base mft record. Try to move out other 6082987da915Sopenharmony_ci * attributes and try again. 6083987da915Sopenharmony_ci */ 6084987da915Sopenharmony_ci if (na->type == AT_ATTRIBUTE_LIST) { 6085987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 6086987da915Sopenharmony_ci if (ntfs_inode_free_space(na->ni, mp_size - 6087987da915Sopenharmony_ci cur_max_mp_size)) { 6088987da915Sopenharmony_ci ntfs_log_perror("Attribute list is too " 6089987da915Sopenharmony_ci "big. Defragment the " 6090987da915Sopenharmony_ci "volume\n"); 6091987da915Sopenharmony_ci return -1; 6092987da915Sopenharmony_ci } 6093987da915Sopenharmony_ci goto retry; 6094987da915Sopenharmony_ci } 6095987da915Sopenharmony_ci 6096987da915Sopenharmony_ci /* Add attribute list if it isn't present, and retry. */ 6097987da915Sopenharmony_ci if (!NInoAttrList(base_ni)) { 6098987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 6099987da915Sopenharmony_ci if (ntfs_inode_add_attrlist(base_ni)) { 6100987da915Sopenharmony_ci ntfs_log_perror("Can not add attrlist"); 6101987da915Sopenharmony_ci return -1; 6102987da915Sopenharmony_ci } 6103987da915Sopenharmony_ci goto retry; 6104987da915Sopenharmony_ci } 6105987da915Sopenharmony_ci 6106987da915Sopenharmony_ci /* 6107987da915Sopenharmony_ci * Set mapping pairs size to maximum possible for this 6108987da915Sopenharmony_ci * mft record. We shall write the rest of mapping pairs 6109987da915Sopenharmony_ci * to another MFT records. 6110987da915Sopenharmony_ci */ 6111987da915Sopenharmony_ci mp_size = exp_max_mp_size; 6112987da915Sopenharmony_ci } 6113987da915Sopenharmony_ci 6114987da915Sopenharmony_ci /* Change space for mapping pairs if we need it. */ 6115987da915Sopenharmony_ci if (((mp_size + 7) & ~7) != cur_max_mp_size) { 6116987da915Sopenharmony_ci if (ntfs_attr_record_resize(m, a, 6117987da915Sopenharmony_ci le16_to_cpu(a->mapping_pairs_offset) + 6118987da915Sopenharmony_ci mp_size)) { 6119987da915Sopenharmony_ci errno = EIO; 6120987da915Sopenharmony_ci ntfs_log_perror("Failed to resize attribute"); 6121987da915Sopenharmony_ci goto put_err_out; 6122987da915Sopenharmony_ci } 6123987da915Sopenharmony_ci } 6124987da915Sopenharmony_ci 6125987da915Sopenharmony_ci /* Update lowest vcn. */ 6126987da915Sopenharmony_ci a->lowest_vcn = cpu_to_sle64(stop_vcn); 6127987da915Sopenharmony_ci ntfs_inode_mark_dirty(ctx->ntfs_ino); 6128987da915Sopenharmony_ci if ((ctx->ntfs_ino->nr_extents == -1 || 6129987da915Sopenharmony_ci NInoAttrList(ctx->ntfs_ino)) && 6130987da915Sopenharmony_ci ctx->attr->type != AT_ATTRIBUTE_LIST) { 6131987da915Sopenharmony_ci ctx->al_entry->lowest_vcn = cpu_to_sle64(stop_vcn); 6132987da915Sopenharmony_ci ntfs_attrlist_mark_dirty(ctx->ntfs_ino); 6133987da915Sopenharmony_ci } 6134987da915Sopenharmony_ci 6135987da915Sopenharmony_ci /* 6136987da915Sopenharmony_ci * Generate the new mapping pairs array directly into the 6137987da915Sopenharmony_ci * correct destination, i.e. the attribute record itself. 6138987da915Sopenharmony_ci */ 6139987da915Sopenharmony_ci if (!ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + le16_to_cpu( 6140987da915Sopenharmony_ci a->mapping_pairs_offset), mp_size, na->rl, 6141987da915Sopenharmony_ci stop_vcn, &stop_rl)) 6142987da915Sopenharmony_ci finished_build = TRUE; 6143987da915Sopenharmony_ci if (stop_rl) 6144987da915Sopenharmony_ci stop_vcn = stop_rl->vcn; 6145987da915Sopenharmony_ci else 6146987da915Sopenharmony_ci stop_vcn = 0; 6147987da915Sopenharmony_ci if (!finished_build && errno != ENOSPC) { 6148987da915Sopenharmony_ci ntfs_log_perror("Failed to build mapping pairs"); 6149987da915Sopenharmony_ci goto put_err_out; 6150987da915Sopenharmony_ci } 6151987da915Sopenharmony_ci a->highest_vcn = cpu_to_sle64(stop_vcn - 1); 6152987da915Sopenharmony_ci } 6153987da915Sopenharmony_ci /* Check whether error occurred. */ 6154987da915Sopenharmony_ci if (errno != ENOENT) { 6155987da915Sopenharmony_ci ntfs_log_perror("%s: Attribute lookup failed", __FUNCTION__); 6156987da915Sopenharmony_ci goto put_err_out; 6157987da915Sopenharmony_ci } 6158987da915Sopenharmony_ci /* 6159987da915Sopenharmony_ci * If the base extent was skipped in the above process, 6160987da915Sopenharmony_ci * we still may have to update the sizes. 6161987da915Sopenharmony_ci */ 6162987da915Sopenharmony_ci if (!first_updated) { 6163987da915Sopenharmony_ci le16 spcomp; 6164987da915Sopenharmony_ci 6165987da915Sopenharmony_ci ntfs_attr_reinit_search_ctx(ctx); 6166987da915Sopenharmony_ci if (!ntfs_attr_lookup(na->type, na->name, na->name_len, 6167987da915Sopenharmony_ci CASE_SENSITIVE, 0, NULL, 0, ctx)) { 6168987da915Sopenharmony_ci a = ctx->attr; 6169987da915Sopenharmony_ci a->allocated_size = cpu_to_sle64(na->allocated_size); 6170987da915Sopenharmony_ci spcomp = na->data_flags 6171987da915Sopenharmony_ci & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); 6172987da915Sopenharmony_ci if (spcomp) 6173987da915Sopenharmony_ci a->compressed_size = cpu_to_sle64(na->compressed_size); 6174987da915Sopenharmony_ci /* Updating sizes taints the extent holding the attr */ 6175987da915Sopenharmony_ci if (ctx->ntfs_ino) 6176987da915Sopenharmony_ci NInoSetDirty(ctx->ntfs_ino); 6177987da915Sopenharmony_ci if ((na->type == AT_DATA) && (na->name == AT_UNNAMED)) { 6178987da915Sopenharmony_ci na->ni->allocated_size 6179987da915Sopenharmony_ci = (spcomp 6180987da915Sopenharmony_ci ? na->compressed_size 6181987da915Sopenharmony_ci : na->allocated_size); 6182987da915Sopenharmony_ci NInoFileNameSetDirty(na->ni); 6183987da915Sopenharmony_ci } 6184987da915Sopenharmony_ci } else { 6185987da915Sopenharmony_ci ntfs_log_error("Failed to update sizes in base extent\n"); 6186987da915Sopenharmony_ci goto put_err_out; 6187987da915Sopenharmony_ci } 6188987da915Sopenharmony_ci } 6189987da915Sopenharmony_ci 6190987da915Sopenharmony_ci /* Deallocate not used attribute extents and return with success. */ 6191987da915Sopenharmony_ci if (finished_build) { 6192987da915Sopenharmony_ci ntfs_attr_reinit_search_ctx(ctx); 6193987da915Sopenharmony_ci ntfs_log_trace("Deallocate marked extents.\n"); 6194987da915Sopenharmony_ci while (!ntfs_attr_lookup(na->type, na->name, na->name_len, 6195987da915Sopenharmony_ci CASE_SENSITIVE, 0, NULL, 0, ctx)) { 6196987da915Sopenharmony_ci if (sle64_to_cpu(ctx->attr->highest_vcn) != 6197987da915Sopenharmony_ci NTFS_VCN_DELETE_MARK) 6198987da915Sopenharmony_ci continue; 6199987da915Sopenharmony_ci /* Remove unused attribute record. */ 6200987da915Sopenharmony_ci if (ntfs_attr_record_rm(ctx)) { 6201987da915Sopenharmony_ci ntfs_log_perror("Could not remove unused attr"); 6202987da915Sopenharmony_ci goto put_err_out; 6203987da915Sopenharmony_ci } 6204987da915Sopenharmony_ci ntfs_attr_reinit_search_ctx(ctx); 6205987da915Sopenharmony_ci } 6206987da915Sopenharmony_ci if (errno != ENOENT) { 6207987da915Sopenharmony_ci ntfs_log_perror("%s: Attr lookup failed", __FUNCTION__); 6208987da915Sopenharmony_ci goto put_err_out; 6209987da915Sopenharmony_ci } 6210987da915Sopenharmony_ci ntfs_log_trace("Deallocate done.\n"); 6211987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 6212987da915Sopenharmony_ci goto ok; 6213987da915Sopenharmony_ci } 6214987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 6215987da915Sopenharmony_ci ctx = NULL; 6216987da915Sopenharmony_ci 6217987da915Sopenharmony_ci /* Allocate new MFT records for the rest of mapping pairs. */ 6218987da915Sopenharmony_ci while (1) { 6219987da915Sopenharmony_ci /* Calculate size of rest mapping pairs. */ 6220987da915Sopenharmony_ci mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, 6221987da915Sopenharmony_ci na->rl, stop_vcn, INT_MAX); 6222987da915Sopenharmony_ci if (mp_size <= 0) { 6223987da915Sopenharmony_ci ntfs_log_perror("%s: get mp size failed", __FUNCTION__); 6224987da915Sopenharmony_ci goto put_err_out; 6225987da915Sopenharmony_ci } 6226987da915Sopenharmony_ci /* Allocate new mft record, with special case for mft itself */ 6227987da915Sopenharmony_ci if (!na->ni->mft_no) 6228987da915Sopenharmony_ci ni = ntfs_mft_rec_alloc(na->ni->vol, 6229987da915Sopenharmony_ci na->type == AT_DATA); 6230987da915Sopenharmony_ci else 6231987da915Sopenharmony_ci ni = ntfs_mft_record_alloc(na->ni->vol, base_ni); 6232987da915Sopenharmony_ci if (!ni) { 6233987da915Sopenharmony_ci ntfs_log_perror("Could not allocate new MFT record"); 6234987da915Sopenharmony_ci goto put_err_out; 6235987da915Sopenharmony_ci } 6236987da915Sopenharmony_ci m = ni->mrec; 6237987da915Sopenharmony_ci /* 6238987da915Sopenharmony_ci * If mapping size exceed available space, set them to 6239987da915Sopenharmony_ci * possible maximum. 6240987da915Sopenharmony_ci */ 6241987da915Sopenharmony_ci cur_max_mp_size = le32_to_cpu(m->bytes_allocated) - 6242987da915Sopenharmony_ci le32_to_cpu(m->bytes_in_use) - 6243987da915Sopenharmony_ci (offsetof(ATTR_RECORD, compressed_size) + 6244987da915Sopenharmony_ci (((na->data_flags & ATTR_COMPRESSION_MASK) 6245987da915Sopenharmony_ci || NAttrSparse(na)) ? 6246987da915Sopenharmony_ci sizeof(a->compressed_size) : 0)) - 6247987da915Sopenharmony_ci ((sizeof(ntfschar) * na->name_len + 7) & ~7); 6248987da915Sopenharmony_ci if (mp_size > cur_max_mp_size) 6249987da915Sopenharmony_ci mp_size = cur_max_mp_size; 6250987da915Sopenharmony_ci /* Add attribute extent to new record. */ 6251987da915Sopenharmony_ci err = ntfs_non_resident_attr_record_add(ni, na->type, 6252987da915Sopenharmony_ci na->name, na->name_len, stop_vcn, mp_size, 6253987da915Sopenharmony_ci na->data_flags); 6254987da915Sopenharmony_ci if (err == -1) { 6255987da915Sopenharmony_ci err = errno; 6256987da915Sopenharmony_ci ntfs_log_perror("Could not add attribute extent"); 6257987da915Sopenharmony_ci if (ntfs_mft_record_free(na->ni->vol, ni)) 6258987da915Sopenharmony_ci ntfs_log_perror("Could not free MFT record"); 6259987da915Sopenharmony_ci errno = err; 6260987da915Sopenharmony_ci goto put_err_out; 6261987da915Sopenharmony_ci } 6262987da915Sopenharmony_ci a = (ATTR_RECORD*)((u8*)m + err); 6263987da915Sopenharmony_ci 6264987da915Sopenharmony_ci err = ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + 6265987da915Sopenharmony_ci le16_to_cpu(a->mapping_pairs_offset), mp_size, na->rl, 6266987da915Sopenharmony_ci stop_vcn, &stop_rl); 6267987da915Sopenharmony_ci if (stop_rl) 6268987da915Sopenharmony_ci stop_vcn = stop_rl->vcn; 6269987da915Sopenharmony_ci else 6270987da915Sopenharmony_ci stop_vcn = 0; 6271987da915Sopenharmony_ci if (err < 0 && errno != ENOSPC) { 6272987da915Sopenharmony_ci err = errno; 6273987da915Sopenharmony_ci ntfs_log_perror("Failed to build MP"); 6274987da915Sopenharmony_ci if (ntfs_mft_record_free(na->ni->vol, ni)) 6275987da915Sopenharmony_ci ntfs_log_perror("Couldn't free MFT record"); 6276987da915Sopenharmony_ci errno = err; 6277987da915Sopenharmony_ci goto put_err_out; 6278987da915Sopenharmony_ci } 6279987da915Sopenharmony_ci a->highest_vcn = cpu_to_sle64(stop_vcn - 1); 6280987da915Sopenharmony_ci ntfs_inode_mark_dirty(ni); 6281987da915Sopenharmony_ci /* All mapping pairs has been written. */ 6282987da915Sopenharmony_ci if (!err) 6283987da915Sopenharmony_ci break; 6284987da915Sopenharmony_ci } 6285987da915Sopenharmony_ciok: 6286987da915Sopenharmony_ci NAttrClearRunlistDirty(na); 6287987da915Sopenharmony_ci ret = 0; 6288987da915Sopenharmony_ciout: 6289987da915Sopenharmony_ci return ret; 6290987da915Sopenharmony_ciput_err_out: 6291987da915Sopenharmony_ci if (ctx) 6292987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 6293987da915Sopenharmony_ci goto out; 6294987da915Sopenharmony_ci} 6295987da915Sopenharmony_ci#undef NTFS_VCN_DELETE_MARK 6296987da915Sopenharmony_ci 6297987da915Sopenharmony_ci/** 6298987da915Sopenharmony_ci * ntfs_attr_update_mapping_pairs - update mapping pairs for ntfs attribute 6299987da915Sopenharmony_ci * @na: non-resident ntfs open attribute for which we need update 6300987da915Sopenharmony_ci * @from_vcn: update runlist starting this VCN 6301987da915Sopenharmony_ci * 6302987da915Sopenharmony_ci * Build mapping pairs from @na->rl and write them to the disk. Also, this 6303987da915Sopenharmony_ci * function updates sparse bit, allocated and compressed size (allocates/frees 6304987da915Sopenharmony_ci * space for this field if required). 6305987da915Sopenharmony_ci * 6306987da915Sopenharmony_ci * @na->allocated_size should be set to correct value for the new runlist before 6307987da915Sopenharmony_ci * call to this function. Vice-versa @na->compressed_size will be calculated and 6308987da915Sopenharmony_ci * set to correct value during this function. 6309987da915Sopenharmony_ci * 6310987da915Sopenharmony_ci * FIXME: This function does not update sparse bit and compressed size correctly 6311987da915Sopenharmony_ci * if called with @from_vcn != 0. 6312987da915Sopenharmony_ci * 6313987da915Sopenharmony_ci * FIXME: Rewrite without using NTFS_VCN_DELETE_MARK define. 6314987da915Sopenharmony_ci * 6315987da915Sopenharmony_ci * On success return 0 and on error return -1 with errno set to the error code. 6316987da915Sopenharmony_ci * The following error codes are defined: 6317987da915Sopenharmony_ci * EINVAL - Invalid arguments passed. 6318987da915Sopenharmony_ci * ENOMEM - Not enough memory to complete operation. 6319987da915Sopenharmony_ci * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST 6320987da915Sopenharmony_ci * or there is no free MFT records left to allocate. 6321987da915Sopenharmony_ci */ 6322987da915Sopenharmony_ciint ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn) 6323987da915Sopenharmony_ci{ 6324987da915Sopenharmony_ci int ret; 6325987da915Sopenharmony_ci 6326987da915Sopenharmony_ci ntfs_log_enter("Entering\n"); 6327987da915Sopenharmony_ci ret = ntfs_attr_update_mapping_pairs_i(na, from_vcn, HOLES_OK); 6328987da915Sopenharmony_ci ntfs_log_leave("\n"); 6329987da915Sopenharmony_ci return ret; 6330987da915Sopenharmony_ci} 6331987da915Sopenharmony_ci 6332987da915Sopenharmony_ci/** 6333987da915Sopenharmony_ci * ntfs_non_resident_attr_shrink - shrink a non-resident, open ntfs attribute 6334987da915Sopenharmony_ci * @na: non-resident ntfs attribute to shrink 6335987da915Sopenharmony_ci * @newsize: new size (in bytes) to which to shrink the attribute 6336987da915Sopenharmony_ci * 6337987da915Sopenharmony_ci * Reduce the size of a non-resident, open ntfs attribute @na to @newsize bytes. 6338987da915Sopenharmony_ci * 6339987da915Sopenharmony_ci * On success return 0 and on error return -1 with errno set to the error code. 6340987da915Sopenharmony_ci * The following error codes are defined: 6341987da915Sopenharmony_ci * ENOMEM - Not enough memory to complete operation. 6342987da915Sopenharmony_ci * ERANGE - @newsize is not valid for the attribute type of @na. 6343987da915Sopenharmony_ci */ 6344987da915Sopenharmony_cistatic int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) 6345987da915Sopenharmony_ci{ 6346987da915Sopenharmony_ci ntfs_volume *vol; 6347987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 6348987da915Sopenharmony_ci VCN first_free_vcn; 6349987da915Sopenharmony_ci s64 nr_freed_clusters; 6350987da915Sopenharmony_ci int err; 6351987da915Sopenharmony_ci 6352987da915Sopenharmony_ci ntfs_log_trace("Inode 0x%llx attr 0x%x new size %lld\n", (unsigned long long) 6353987da915Sopenharmony_ci na->ni->mft_no, le32_to_cpu(na->type), (long long)newsize); 6354987da915Sopenharmony_ci 6355987da915Sopenharmony_ci vol = na->ni->vol; 6356987da915Sopenharmony_ci 6357987da915Sopenharmony_ci /* 6358987da915Sopenharmony_ci * Check the attribute type and the corresponding minimum size 6359987da915Sopenharmony_ci * against @newsize and fail if @newsize is too small. 6360987da915Sopenharmony_ci */ 6361987da915Sopenharmony_ci if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { 6362987da915Sopenharmony_ci if (errno == ERANGE) { 6363987da915Sopenharmony_ci ntfs_log_trace("Eeek! Size bounds check failed. " 6364987da915Sopenharmony_ci "Aborting...\n"); 6365987da915Sopenharmony_ci } else if (errno == ENOENT) 6366987da915Sopenharmony_ci errno = EIO; 6367987da915Sopenharmony_ci return -1; 6368987da915Sopenharmony_ci } 6369987da915Sopenharmony_ci 6370987da915Sopenharmony_ci /* The first cluster outside the new allocation. */ 6371987da915Sopenharmony_ci if (na->data_flags & ATTR_COMPRESSION_MASK) 6372987da915Sopenharmony_ci /* 6373987da915Sopenharmony_ci * For compressed files we must keep full compressions blocks, 6374987da915Sopenharmony_ci * but currently we do not decompress/recompress the last 6375987da915Sopenharmony_ci * block to truncate the data, so we may leave more allocated 6376987da915Sopenharmony_ci * clusters than really needed. 6377987da915Sopenharmony_ci */ 6378987da915Sopenharmony_ci first_free_vcn = (((newsize - 1) 6379987da915Sopenharmony_ci | (na->compression_block_size - 1)) + 1) 6380987da915Sopenharmony_ci >> vol->cluster_size_bits; 6381987da915Sopenharmony_ci else 6382987da915Sopenharmony_ci first_free_vcn = (newsize + vol->cluster_size - 1) >> 6383987da915Sopenharmony_ci vol->cluster_size_bits; 6384987da915Sopenharmony_ci /* 6385987da915Sopenharmony_ci * Compare the new allocation with the old one and only deallocate 6386987da915Sopenharmony_ci * clusters if there is a change. 6387987da915Sopenharmony_ci */ 6388987da915Sopenharmony_ci if ((na->allocated_size >> vol->cluster_size_bits) != first_free_vcn) { 6389987da915Sopenharmony_ci if (ntfs_attr_map_whole_runlist(na)) { 6390987da915Sopenharmony_ci ntfs_log_trace("Eeek! ntfs_attr_map_whole_runlist " 6391987da915Sopenharmony_ci "failed.\n"); 6392987da915Sopenharmony_ci return -1; 6393987da915Sopenharmony_ci } 6394987da915Sopenharmony_ci /* Deallocate all clusters starting with the first free one. */ 6395987da915Sopenharmony_ci nr_freed_clusters = ntfs_cluster_free(vol, na, first_free_vcn, 6396987da915Sopenharmony_ci -1); 6397987da915Sopenharmony_ci if (nr_freed_clusters < 0) { 6398987da915Sopenharmony_ci ntfs_log_trace("Eeek! Freeing of clusters failed. " 6399987da915Sopenharmony_ci "Aborting...\n"); 6400987da915Sopenharmony_ci return -1; 6401987da915Sopenharmony_ci } 6402987da915Sopenharmony_ci 6403987da915Sopenharmony_ci /* Truncate the runlist itself. */ 6404987da915Sopenharmony_ci if (ntfs_rl_truncate(&na->rl, first_free_vcn)) { 6405987da915Sopenharmony_ci /* 6406987da915Sopenharmony_ci * Failed to truncate the runlist, so just throw it 6407987da915Sopenharmony_ci * away, it will be mapped afresh on next use. 6408987da915Sopenharmony_ci */ 6409987da915Sopenharmony_ci free(na->rl); 6410987da915Sopenharmony_ci na->rl = NULL; 6411987da915Sopenharmony_ci ntfs_log_trace("Eeek! Run list truncation failed.\n"); 6412987da915Sopenharmony_ci return -1; 6413987da915Sopenharmony_ci } 6414987da915Sopenharmony_ci NAttrSetRunlistDirty(na); 6415987da915Sopenharmony_ci 6416987da915Sopenharmony_ci /* Prepare to mapping pairs update. */ 6417987da915Sopenharmony_ci na->allocated_size = first_free_vcn << vol->cluster_size_bits; 6418987da915Sopenharmony_ci /* Write mapping pairs for new runlist. */ 6419987da915Sopenharmony_ci if (ntfs_attr_update_mapping_pairs(na, 0 /*first_free_vcn*/)) { 6420987da915Sopenharmony_ci ntfs_log_trace("Eeek! Mapping pairs update failed. " 6421987da915Sopenharmony_ci "Leaving inconstant metadata. " 6422987da915Sopenharmony_ci "Run chkdsk.\n"); 6423987da915Sopenharmony_ci return -1; 6424987da915Sopenharmony_ci } 6425987da915Sopenharmony_ci } 6426987da915Sopenharmony_ci 6427987da915Sopenharmony_ci /* Get the first attribute record. */ 6428987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(na->ni, NULL); 6429987da915Sopenharmony_ci if (!ctx) 6430987da915Sopenharmony_ci return -1; 6431987da915Sopenharmony_ci 6432987da915Sopenharmony_ci if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, 6433987da915Sopenharmony_ci 0, NULL, 0, ctx)) { 6434987da915Sopenharmony_ci err = errno; 6435987da915Sopenharmony_ci if (err == ENOENT) 6436987da915Sopenharmony_ci err = EIO; 6437987da915Sopenharmony_ci ntfs_log_trace("Eeek! Lookup of first attribute extent failed. " 6438987da915Sopenharmony_ci "Leaving inconstant metadata.\n"); 6439987da915Sopenharmony_ci goto put_err_out; 6440987da915Sopenharmony_ci } 6441987da915Sopenharmony_ci 6442987da915Sopenharmony_ci /* Update data and initialized size. */ 6443987da915Sopenharmony_ci na->data_size = newsize; 6444987da915Sopenharmony_ci ctx->attr->data_size = cpu_to_sle64(newsize); 6445987da915Sopenharmony_ci if (newsize < na->initialized_size) { 6446987da915Sopenharmony_ci na->initialized_size = newsize; 6447987da915Sopenharmony_ci ctx->attr->initialized_size = cpu_to_sle64(newsize); 6448987da915Sopenharmony_ci } 6449987da915Sopenharmony_ci /* Update data size in the index. */ 6450987da915Sopenharmony_ci if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { 6451987da915Sopenharmony_ci if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) { 6452987da915Sopenharmony_ci na->ni->data_size = na->data_size; 6453987da915Sopenharmony_ci na->ni->allocated_size = na->allocated_size; 6454987da915Sopenharmony_ci set_nino_flag(na->ni,KnownSize); 6455987da915Sopenharmony_ci } 6456987da915Sopenharmony_ci } else { 6457987da915Sopenharmony_ci if (na->type == AT_DATA && na->name == AT_UNNAMED) { 6458987da915Sopenharmony_ci na->ni->data_size = na->data_size; 6459987da915Sopenharmony_ci NInoFileNameSetDirty(na->ni); 6460987da915Sopenharmony_ci } 6461987da915Sopenharmony_ci } 6462987da915Sopenharmony_ci 6463987da915Sopenharmony_ci /* If the attribute now has zero size, make it resident. */ 6464987da915Sopenharmony_ci if (!newsize) { 6465987da915Sopenharmony_ci if (!(na->data_flags & ATTR_IS_ENCRYPTED) 6466987da915Sopenharmony_ci && ntfs_attr_make_resident(na, ctx)) { 6467987da915Sopenharmony_ci /* If couldn't make resident, just continue. */ 6468987da915Sopenharmony_ci if (errno != EPERM) 6469987da915Sopenharmony_ci ntfs_log_error("Failed to make attribute " 6470987da915Sopenharmony_ci "resident. Leaving as is...\n"); 6471987da915Sopenharmony_ci } 6472987da915Sopenharmony_ci } 6473987da915Sopenharmony_ci 6474987da915Sopenharmony_ci /* Set the inode dirty so it is written out later. */ 6475987da915Sopenharmony_ci ntfs_inode_mark_dirty(ctx->ntfs_ino); 6476987da915Sopenharmony_ci /* Done! */ 6477987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 6478987da915Sopenharmony_ci return 0; 6479987da915Sopenharmony_ciput_err_out: 6480987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 6481987da915Sopenharmony_ci errno = err; 6482987da915Sopenharmony_ci return -1; 6483987da915Sopenharmony_ci} 6484987da915Sopenharmony_ci 6485987da915Sopenharmony_ci/** 6486987da915Sopenharmony_ci * ntfs_non_resident_attr_expand - expand a non-resident, open ntfs attribute 6487987da915Sopenharmony_ci * @na: non-resident ntfs attribute to expand 6488987da915Sopenharmony_ci * @newsize: new size (in bytes) to which to expand the attribute 6489987da915Sopenharmony_ci * 6490987da915Sopenharmony_ci * Expand the size of a non-resident, open ntfs attribute @na to @newsize bytes, 6491987da915Sopenharmony_ci * by allocating new clusters. 6492987da915Sopenharmony_ci * 6493987da915Sopenharmony_ci * On success return 0 and on error return -1 with errno set to the error code. 6494987da915Sopenharmony_ci * The following error codes are defined: 6495987da915Sopenharmony_ci * ENOMEM - Not enough memory to complete operation. 6496987da915Sopenharmony_ci * ERANGE - @newsize is not valid for the attribute type of @na. 6497987da915Sopenharmony_ci * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. 6498987da915Sopenharmony_ci */ 6499987da915Sopenharmony_cistatic int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize, 6500987da915Sopenharmony_ci hole_type holes) 6501987da915Sopenharmony_ci{ 6502987da915Sopenharmony_ci LCN lcn_seek_from; 6503987da915Sopenharmony_ci VCN first_free_vcn; 6504987da915Sopenharmony_ci ntfs_volume *vol; 6505987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 6506987da915Sopenharmony_ci runlist *rl, *rln; 6507987da915Sopenharmony_ci s64 org_alloc_size; 6508987da915Sopenharmony_ci int err; 6509987da915Sopenharmony_ci 6510987da915Sopenharmony_ci ntfs_log_trace("Inode %lld, attr 0x%x, new size %lld old size %lld\n", 6511987da915Sopenharmony_ci (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type), 6512987da915Sopenharmony_ci (long long)newsize, (long long)na->data_size); 6513987da915Sopenharmony_ci 6514987da915Sopenharmony_ci vol = na->ni->vol; 6515987da915Sopenharmony_ci 6516987da915Sopenharmony_ci /* 6517987da915Sopenharmony_ci * Check the attribute type and the corresponding maximum size 6518987da915Sopenharmony_ci * against @newsize and fail if @newsize is too big. 6519987da915Sopenharmony_ci */ 6520987da915Sopenharmony_ci if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { 6521987da915Sopenharmony_ci if (errno == ENOENT) 6522987da915Sopenharmony_ci errno = EIO; 6523987da915Sopenharmony_ci ntfs_log_perror("%s: bounds check failed", __FUNCTION__); 6524987da915Sopenharmony_ci return -1; 6525987da915Sopenharmony_ci } 6526987da915Sopenharmony_ci 6527987da915Sopenharmony_ci if (na->type == AT_DATA) 6528987da915Sopenharmony_ci NAttrSetDataAppending(na); 6529987da915Sopenharmony_ci /* Save for future use. */ 6530987da915Sopenharmony_ci org_alloc_size = na->allocated_size; 6531987da915Sopenharmony_ci /* The first cluster outside the new allocation. */ 6532987da915Sopenharmony_ci first_free_vcn = (newsize + vol->cluster_size - 1) >> 6533987da915Sopenharmony_ci vol->cluster_size_bits; 6534987da915Sopenharmony_ci /* 6535987da915Sopenharmony_ci * Compare the new allocation with the old one and only allocate 6536987da915Sopenharmony_ci * clusters if there is a change. 6537987da915Sopenharmony_ci */ 6538987da915Sopenharmony_ci if ((na->allocated_size >> vol->cluster_size_bits) < first_free_vcn) { 6539987da915Sopenharmony_ci#if PARTIAL_RUNLIST_UPDATING 6540987da915Sopenharmony_ci s64 start_update; 6541987da915Sopenharmony_ci 6542987da915Sopenharmony_ci /* 6543987da915Sopenharmony_ci * Update from the last previously allocated run, 6544987da915Sopenharmony_ci * as we may have to expand an existing hole. 6545987da915Sopenharmony_ci */ 6546987da915Sopenharmony_ci start_update = na->allocated_size >> vol->cluster_size_bits; 6547987da915Sopenharmony_ci if (start_update) 6548987da915Sopenharmony_ci start_update--; 6549987da915Sopenharmony_ci if (ntfs_attr_map_partial_runlist(na, start_update)) { 6550987da915Sopenharmony_ci ntfs_log_perror("failed to map partial runlist"); 6551987da915Sopenharmony_ci return -1; 6552987da915Sopenharmony_ci } 6553987da915Sopenharmony_ci#else 6554987da915Sopenharmony_ci if (ntfs_attr_map_whole_runlist(na)) { 6555987da915Sopenharmony_ci ntfs_log_perror("ntfs_attr_map_whole_runlist failed"); 6556987da915Sopenharmony_ci return -1; 6557987da915Sopenharmony_ci } 6558987da915Sopenharmony_ci#endif 6559987da915Sopenharmony_ci 6560987da915Sopenharmony_ci /* 6561987da915Sopenharmony_ci * If we extend $DATA attribute on NTFS 3+ volume, we can add 6562987da915Sopenharmony_ci * sparse runs instead of real allocation of clusters. 6563987da915Sopenharmony_ci */ 6564987da915Sopenharmony_ci if ((na->type == AT_DATA) && (vol->major_ver >= 3) 6565987da915Sopenharmony_ci && (holes != HOLES_NO)) { 6566987da915Sopenharmony_ci rl = ntfs_malloc(0x1000); 6567987da915Sopenharmony_ci if (!rl) 6568987da915Sopenharmony_ci return -1; 6569987da915Sopenharmony_ci 6570987da915Sopenharmony_ci rl[0].vcn = (na->allocated_size >> 6571987da915Sopenharmony_ci vol->cluster_size_bits); 6572987da915Sopenharmony_ci rl[0].lcn = LCN_HOLE; 6573987da915Sopenharmony_ci rl[0].length = first_free_vcn - 6574987da915Sopenharmony_ci (na->allocated_size >> vol->cluster_size_bits); 6575987da915Sopenharmony_ci rl[1].vcn = first_free_vcn; 6576987da915Sopenharmony_ci rl[1].lcn = LCN_ENOENT; 6577987da915Sopenharmony_ci rl[1].length = 0; 6578987da915Sopenharmony_ci } else { 6579987da915Sopenharmony_ci /* 6580987da915Sopenharmony_ci * Determine first after last LCN of attribute. 6581987da915Sopenharmony_ci * We will start seek clusters from this LCN to avoid 6582987da915Sopenharmony_ci * fragmentation. If there are no valid LCNs in the 6583987da915Sopenharmony_ci * attribute let the cluster allocator choose the 6584987da915Sopenharmony_ci * starting LCN. 6585987da915Sopenharmony_ci */ 6586987da915Sopenharmony_ci lcn_seek_from = -1; 6587987da915Sopenharmony_ci if (na->rl->length) { 6588987da915Sopenharmony_ci /* Seek to the last run list element. */ 6589987da915Sopenharmony_ci for (rl = na->rl; (rl + 1)->length; rl++) 6590987da915Sopenharmony_ci ; 6591987da915Sopenharmony_ci /* 6592987da915Sopenharmony_ci * If the last LCN is a hole or similar seek 6593987da915Sopenharmony_ci * back to last valid LCN. 6594987da915Sopenharmony_ci */ 6595987da915Sopenharmony_ci while (rl->lcn < 0 && rl != na->rl) 6596987da915Sopenharmony_ci rl--; 6597987da915Sopenharmony_ci /* 6598987da915Sopenharmony_ci * Only set lcn_seek_from it the LCN is valid. 6599987da915Sopenharmony_ci */ 6600987da915Sopenharmony_ci if (rl->lcn >= 0) 6601987da915Sopenharmony_ci lcn_seek_from = rl->lcn + rl->length; 6602987da915Sopenharmony_ci } 6603987da915Sopenharmony_ci 6604987da915Sopenharmony_ci rl = ntfs_cluster_alloc(vol, na->allocated_size >> 6605987da915Sopenharmony_ci vol->cluster_size_bits, first_free_vcn - 6606987da915Sopenharmony_ci (na->allocated_size >> 6607987da915Sopenharmony_ci vol->cluster_size_bits), lcn_seek_from, 6608987da915Sopenharmony_ci DATA_ZONE); 6609987da915Sopenharmony_ci if (!rl) { 6610987da915Sopenharmony_ci ntfs_log_perror("Cluster allocation failed " 6611987da915Sopenharmony_ci "(%lld)", 6612987da915Sopenharmony_ci (long long)first_free_vcn - 6613987da915Sopenharmony_ci ((long long)na->allocated_size >> 6614987da915Sopenharmony_ci vol->cluster_size_bits)); 6615987da915Sopenharmony_ci return -1; 6616987da915Sopenharmony_ci } 6617987da915Sopenharmony_ci } 6618987da915Sopenharmony_ci 6619987da915Sopenharmony_ci /* Append new clusters to attribute runlist. */ 6620987da915Sopenharmony_ci rln = ntfs_runlists_merge(na->rl, rl); 6621987da915Sopenharmony_ci if (!rln) { 6622987da915Sopenharmony_ci /* Failed, free just allocated clusters. */ 6623987da915Sopenharmony_ci err = errno; 6624987da915Sopenharmony_ci ntfs_log_perror("Run list merge failed"); 6625987da915Sopenharmony_ci ntfs_cluster_free_from_rl(vol, rl); 6626987da915Sopenharmony_ci free(rl); 6627987da915Sopenharmony_ci errno = err; 6628987da915Sopenharmony_ci return -1; 6629987da915Sopenharmony_ci } 6630987da915Sopenharmony_ci na->rl = rln; 6631987da915Sopenharmony_ci NAttrSetRunlistDirty(na); 6632987da915Sopenharmony_ci 6633987da915Sopenharmony_ci /* Prepare to mapping pairs update. */ 6634987da915Sopenharmony_ci na->allocated_size = first_free_vcn << vol->cluster_size_bits; 6635987da915Sopenharmony_ci#if PARTIAL_RUNLIST_UPDATING 6636987da915Sopenharmony_ci /* 6637987da915Sopenharmony_ci * Write mapping pairs for new runlist, unless this is 6638987da915Sopenharmony_ci * a temporary state before appending data. 6639987da915Sopenharmony_ci * If the update is not done, we must be sure to do 6640987da915Sopenharmony_ci * it later, and to get to a clean state even on errors. 6641987da915Sopenharmony_ci */ 6642987da915Sopenharmony_ci if ((holes != HOLES_DELAY) 6643987da915Sopenharmony_ci && ntfs_attr_update_mapping_pairs_i(na, start_update, 6644987da915Sopenharmony_ci holes)) { 6645987da915Sopenharmony_ci#else 6646987da915Sopenharmony_ci /* Write mapping pairs for new runlist. */ 6647987da915Sopenharmony_ci if (ntfs_attr_update_mapping_pairs(na, 0)) { 6648987da915Sopenharmony_ci#endif 6649987da915Sopenharmony_ci err = errno; 6650987da915Sopenharmony_ci ntfs_log_perror("Mapping pairs update failed"); 6651987da915Sopenharmony_ci goto rollback; 6652987da915Sopenharmony_ci } 6653987da915Sopenharmony_ci } 6654987da915Sopenharmony_ci 6655987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(na->ni, NULL); 6656987da915Sopenharmony_ci if (!ctx) { 6657987da915Sopenharmony_ci err = errno; 6658987da915Sopenharmony_ci if (na->allocated_size == org_alloc_size) { 6659987da915Sopenharmony_ci errno = err; 6660987da915Sopenharmony_ci return -1; 6661987da915Sopenharmony_ci } else 6662987da915Sopenharmony_ci goto rollback; 6663987da915Sopenharmony_ci } 6664987da915Sopenharmony_ci 6665987da915Sopenharmony_ci if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, 6666987da915Sopenharmony_ci 0, NULL, 0, ctx)) { 6667987da915Sopenharmony_ci err = errno; 6668987da915Sopenharmony_ci ntfs_log_perror("Lookup of first attribute extent failed"); 6669987da915Sopenharmony_ci if (err == ENOENT) 6670987da915Sopenharmony_ci err = EIO; 6671987da915Sopenharmony_ci if (na->allocated_size != org_alloc_size) { 6672987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 6673987da915Sopenharmony_ci goto rollback; 6674987da915Sopenharmony_ci } else 6675987da915Sopenharmony_ci goto put_err_out; 6676987da915Sopenharmony_ci } 6677987da915Sopenharmony_ci 6678987da915Sopenharmony_ci /* Update data size. */ 6679987da915Sopenharmony_ci na->data_size = newsize; 6680987da915Sopenharmony_ci ctx->attr->data_size = cpu_to_sle64(newsize); 6681987da915Sopenharmony_ci /* Update data size in the index. */ 6682987da915Sopenharmony_ci if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { 6683987da915Sopenharmony_ci if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) { 6684987da915Sopenharmony_ci na->ni->data_size = na->data_size; 6685987da915Sopenharmony_ci na->ni->allocated_size = na->allocated_size; 6686987da915Sopenharmony_ci set_nino_flag(na->ni,KnownSize); 6687987da915Sopenharmony_ci } 6688987da915Sopenharmony_ci } else { 6689987da915Sopenharmony_ci if (na->type == AT_DATA && na->name == AT_UNNAMED) { 6690987da915Sopenharmony_ci na->ni->data_size = na->data_size; 6691987da915Sopenharmony_ci NInoFileNameSetDirty(na->ni); 6692987da915Sopenharmony_ci } 6693987da915Sopenharmony_ci } 6694987da915Sopenharmony_ci /* Set the inode dirty so it is written out later. */ 6695987da915Sopenharmony_ci ntfs_inode_mark_dirty(ctx->ntfs_ino); 6696987da915Sopenharmony_ci /* Done! */ 6697987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 6698987da915Sopenharmony_ci return 0; 6699987da915Sopenharmony_cirollback: 6700987da915Sopenharmony_ci /* Free allocated clusters. */ 6701987da915Sopenharmony_ci if (ntfs_cluster_free(vol, na, org_alloc_size >> 6702987da915Sopenharmony_ci vol->cluster_size_bits, -1) < 0) { 6703987da915Sopenharmony_ci err = EIO; 6704987da915Sopenharmony_ci ntfs_log_perror("Leaking clusters"); 6705987da915Sopenharmony_ci } 6706987da915Sopenharmony_ci /* Now, truncate the runlist itself. */ 6707987da915Sopenharmony_ci if (ntfs_rl_truncate(&na->rl, org_alloc_size >> 6708987da915Sopenharmony_ci vol->cluster_size_bits)) { 6709987da915Sopenharmony_ci /* 6710987da915Sopenharmony_ci * Failed to truncate the runlist, so just throw it away, it 6711987da915Sopenharmony_ci * will be mapped afresh on next use. 6712987da915Sopenharmony_ci */ 6713987da915Sopenharmony_ci free(na->rl); 6714987da915Sopenharmony_ci na->rl = NULL; 6715987da915Sopenharmony_ci ntfs_log_perror("Couldn't truncate runlist. Rollback failed"); 6716987da915Sopenharmony_ci } else { 6717987da915Sopenharmony_ci NAttrSetRunlistDirty(na); 6718987da915Sopenharmony_ci /* Prepare to mapping pairs update. */ 6719987da915Sopenharmony_ci na->allocated_size = org_alloc_size; 6720987da915Sopenharmony_ci /* Restore mapping pairs. */ 6721987da915Sopenharmony_ci if (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >> 6722987da915Sopenharmony_ci vol->cluster_size_bits*/)) { 6723987da915Sopenharmony_ci ntfs_log_perror("Failed to restore old mapping pairs"); 6724987da915Sopenharmony_ci } 6725987da915Sopenharmony_ci } 6726987da915Sopenharmony_ci errno = err; 6727987da915Sopenharmony_ci return -1; 6728987da915Sopenharmony_ciput_err_out: 6729987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 6730987da915Sopenharmony_ci errno = err; 6731987da915Sopenharmony_ci return -1; 6732987da915Sopenharmony_ci} 6733987da915Sopenharmony_ci 6734987da915Sopenharmony_ci 6735987da915Sopenharmony_cistatic int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize, 6736987da915Sopenharmony_ci hole_type holes) 6737987da915Sopenharmony_ci{ 6738987da915Sopenharmony_ci int ret; 6739987da915Sopenharmony_ci 6740987da915Sopenharmony_ci ntfs_log_enter("Entering\n"); 6741987da915Sopenharmony_ci ret = ntfs_non_resident_attr_expand_i(na, newsize, holes); 6742987da915Sopenharmony_ci ntfs_log_leave("\n"); 6743987da915Sopenharmony_ci return ret; 6744987da915Sopenharmony_ci} 6745987da915Sopenharmony_ci 6746987da915Sopenharmony_ci/** 6747987da915Sopenharmony_ci * ntfs_attr_truncate - resize an ntfs attribute 6748987da915Sopenharmony_ci * @na: open ntfs attribute to resize 6749987da915Sopenharmony_ci * @newsize: new size (in bytes) to which to resize the attribute 6750987da915Sopenharmony_ci * @holes: how to create a hole if expanding 6751987da915Sopenharmony_ci * 6752987da915Sopenharmony_ci * Change the size of an open ntfs attribute @na to @newsize bytes. If the 6753987da915Sopenharmony_ci * attribute is made bigger and the attribute is resident the newly 6754987da915Sopenharmony_ci * "allocated" space is cleared and if the attribute is non-resident the 6755987da915Sopenharmony_ci * newly allocated space is marked as not initialised and no real allocation 6756987da915Sopenharmony_ci * on disk is performed. 6757987da915Sopenharmony_ci * 6758987da915Sopenharmony_ci * On success return 0. 6759987da915Sopenharmony_ci * On error return values are: 6760987da915Sopenharmony_ci * STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT 6761987da915Sopenharmony_ci * STATUS_ERROR - otherwise 6762987da915Sopenharmony_ci * The following error codes are defined: 6763987da915Sopenharmony_ci * EINVAL - Invalid arguments were passed to the function. 6764987da915Sopenharmony_ci * EOPNOTSUPP - The desired resize is not implemented yet. 6765987da915Sopenharmony_ci * EACCES - Encrypted attribute. 6766987da915Sopenharmony_ci */ 6767987da915Sopenharmony_cistatic int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize, 6768987da915Sopenharmony_ci hole_type holes) 6769987da915Sopenharmony_ci{ 6770987da915Sopenharmony_ci int ret = STATUS_ERROR; 6771987da915Sopenharmony_ci s64 fullsize; 6772987da915Sopenharmony_ci BOOL compressed; 6773987da915Sopenharmony_ci 6774987da915Sopenharmony_ci if (!na || newsize < 0 || 6775987da915Sopenharmony_ci (na->ni->mft_no == FILE_MFT && na->type == AT_DATA)) { 6776987da915Sopenharmony_ci ntfs_log_trace("Invalid arguments passed.\n"); 6777987da915Sopenharmony_ci errno = EINVAL; 6778987da915Sopenharmony_ci return STATUS_ERROR; 6779987da915Sopenharmony_ci } 6780987da915Sopenharmony_ci 6781987da915Sopenharmony_ci ntfs_log_enter("Entering for inode %lld, attr 0x%x, size %lld\n", 6782987da915Sopenharmony_ci (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type), 6783987da915Sopenharmony_ci (long long)newsize); 6784987da915Sopenharmony_ci 6785987da915Sopenharmony_ci if (na->data_size == newsize) { 6786987da915Sopenharmony_ci ntfs_log_trace("Size is already ok\n"); 6787987da915Sopenharmony_ci ret = STATUS_OK; 6788987da915Sopenharmony_ci goto out; 6789987da915Sopenharmony_ci } 6790987da915Sopenharmony_ci /* 6791987da915Sopenharmony_ci * Encrypted attributes are not supported. We return access denied, 6792987da915Sopenharmony_ci * which is what Windows NT4 does, too. 6793987da915Sopenharmony_ci */ 6794987da915Sopenharmony_ci if ((na->data_flags & ATTR_IS_ENCRYPTED) && !na->ni->vol->efs_raw) { 6795987da915Sopenharmony_ci errno = EACCES; 6796987da915Sopenharmony_ci ntfs_log_trace("Cannot truncate encrypted attribute\n"); 6797987da915Sopenharmony_ci goto out; 6798987da915Sopenharmony_ci } 6799987da915Sopenharmony_ci /* 6800987da915Sopenharmony_ci * TODO: Implement making handling of compressed attributes. 6801987da915Sopenharmony_ci * Currently we can only expand the attribute or delete it, 6802987da915Sopenharmony_ci * and only for ATTR_IS_COMPRESSED. This is however possible 6803987da915Sopenharmony_ci * for resident attributes when there is no open fuse context 6804987da915Sopenharmony_ci * (important case : $INDEX_ROOT:$I30) 6805987da915Sopenharmony_ci */ 6806987da915Sopenharmony_ci compressed = (na->data_flags & ATTR_COMPRESSION_MASK) 6807987da915Sopenharmony_ci != const_cpu_to_le16(0); 6808987da915Sopenharmony_ci if (compressed 6809987da915Sopenharmony_ci && NAttrNonResident(na) 6810987da915Sopenharmony_ci && ((na->data_flags & ATTR_COMPRESSION_MASK) != ATTR_IS_COMPRESSED)) { 6811987da915Sopenharmony_ci errno = EOPNOTSUPP; 6812987da915Sopenharmony_ci ntfs_log_perror("Failed to truncate compressed attribute"); 6813987da915Sopenharmony_ci goto out; 6814987da915Sopenharmony_ci } 6815987da915Sopenharmony_ci if (NAttrNonResident(na)) { 6816987da915Sopenharmony_ci /* 6817987da915Sopenharmony_ci * For compressed data, the last block must be fully 6818987da915Sopenharmony_ci * allocated, and we do not know the size of compression 6819987da915Sopenharmony_ci * block until the attribute has been made non-resident. 6820987da915Sopenharmony_ci * Moreover we can only process a single compression 6821987da915Sopenharmony_ci * block at a time (from where we are about to write), 6822987da915Sopenharmony_ci * so we silently do not allocate more. 6823987da915Sopenharmony_ci * 6824987da915Sopenharmony_ci * Note : do not request upsizing of compressed files 6825987da915Sopenharmony_ci * unless being able to face the consequences ! 6826987da915Sopenharmony_ci */ 6827987da915Sopenharmony_ci if (compressed && newsize && (newsize > na->data_size)) 6828987da915Sopenharmony_ci fullsize = (na->initialized_size 6829987da915Sopenharmony_ci | (na->compression_block_size - 1)) + 1; 6830987da915Sopenharmony_ci else 6831987da915Sopenharmony_ci fullsize = newsize; 6832987da915Sopenharmony_ci if (fullsize > na->data_size) 6833987da915Sopenharmony_ci ret = ntfs_non_resident_attr_expand(na, fullsize, 6834987da915Sopenharmony_ci holes); 6835987da915Sopenharmony_ci else 6836987da915Sopenharmony_ci ret = ntfs_non_resident_attr_shrink(na, fullsize); 6837987da915Sopenharmony_ci } else 6838987da915Sopenharmony_ci ret = ntfs_resident_attr_resize_i(na, newsize, holes); 6839987da915Sopenharmony_ciout: 6840987da915Sopenharmony_ci ntfs_log_leave("Return status %d\n", ret); 6841987da915Sopenharmony_ci return ret; 6842987da915Sopenharmony_ci} 6843987da915Sopenharmony_ci 6844987da915Sopenharmony_ci/* 6845987da915Sopenharmony_ci * Resize an attribute, creating a hole if relevant 6846987da915Sopenharmony_ci */ 6847987da915Sopenharmony_ci 6848987da915Sopenharmony_ciint ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) 6849987da915Sopenharmony_ci{ 6850987da915Sopenharmony_ci int r; 6851987da915Sopenharmony_ci 6852987da915Sopenharmony_ci r = ntfs_attr_truncate_i(na, newsize, HOLES_OK); 6853987da915Sopenharmony_ci NAttrClearDataAppending(na); 6854987da915Sopenharmony_ci NAttrClearBeingNonResident(na); 6855987da915Sopenharmony_ci return (r); 6856987da915Sopenharmony_ci} 6857987da915Sopenharmony_ci 6858987da915Sopenharmony_ci/* 6859987da915Sopenharmony_ci * Resize an attribute, avoiding hole creation 6860987da915Sopenharmony_ci */ 6861987da915Sopenharmony_ci 6862987da915Sopenharmony_ciint ntfs_attr_truncate_solid(ntfs_attr *na, const s64 newsize) 6863987da915Sopenharmony_ci{ 6864987da915Sopenharmony_ci return (ntfs_attr_truncate_i(na, newsize, HOLES_NO)); 6865987da915Sopenharmony_ci} 6866987da915Sopenharmony_ci 6867987da915Sopenharmony_ci/* 6868987da915Sopenharmony_ci * Stuff a hole in a compressed file 6869987da915Sopenharmony_ci * 6870987da915Sopenharmony_ci * An unallocated hole must be aligned on compression block size. 6871987da915Sopenharmony_ci * If needed current block and target block are stuffed with zeroes. 6872987da915Sopenharmony_ci * 6873987da915Sopenharmony_ci * Returns 0 if succeeded, 6874987da915Sopenharmony_ci * -1 if it failed (as explained in errno) 6875987da915Sopenharmony_ci */ 6876987da915Sopenharmony_ci 6877987da915Sopenharmony_cistatic int stuff_hole(ntfs_attr *na, const s64 pos) 6878987da915Sopenharmony_ci{ 6879987da915Sopenharmony_ci s64 size; 6880987da915Sopenharmony_ci s64 begin_size; 6881987da915Sopenharmony_ci s64 end_size; 6882987da915Sopenharmony_ci char *buf; 6883987da915Sopenharmony_ci int ret; 6884987da915Sopenharmony_ci 6885987da915Sopenharmony_ci ret = 0; 6886987da915Sopenharmony_ci /* 6887987da915Sopenharmony_ci * If the attribute is resident, the compression block size 6888987da915Sopenharmony_ci * is not defined yet and we can make no decision. 6889987da915Sopenharmony_ci * So we first try resizing to the target and if the 6890987da915Sopenharmony_ci * attribute is still resident, we're done 6891987da915Sopenharmony_ci */ 6892987da915Sopenharmony_ci if (!NAttrNonResident(na)) { 6893987da915Sopenharmony_ci ret = ntfs_resident_attr_resize(na, pos); 6894987da915Sopenharmony_ci if (!ret && !NAttrNonResident(na)) 6895987da915Sopenharmony_ci na->initialized_size = na->data_size = pos; 6896987da915Sopenharmony_ci } 6897987da915Sopenharmony_ci if (!ret && NAttrNonResident(na)) { 6898987da915Sopenharmony_ci /* does the hole span over several compression block ? */ 6899987da915Sopenharmony_ci if ((pos ^ na->initialized_size) 6900987da915Sopenharmony_ci & ~(na->compression_block_size - 1)) { 6901987da915Sopenharmony_ci begin_size = ((na->initialized_size - 1) 6902987da915Sopenharmony_ci | (na->compression_block_size - 1)) 6903987da915Sopenharmony_ci + 1 - na->initialized_size; 6904987da915Sopenharmony_ci end_size = pos & (na->compression_block_size - 1); 6905987da915Sopenharmony_ci size = (begin_size > end_size ? begin_size : end_size); 6906987da915Sopenharmony_ci } else { 6907987da915Sopenharmony_ci /* short stuffing in a single compression block */ 6908987da915Sopenharmony_ci begin_size = size = pos - na->initialized_size; 6909987da915Sopenharmony_ci end_size = 0; 6910987da915Sopenharmony_ci } 6911987da915Sopenharmony_ci if (size) 6912987da915Sopenharmony_ci buf = (char*)ntfs_malloc(size); 6913987da915Sopenharmony_ci else 6914987da915Sopenharmony_ci buf = (char*)NULL; 6915987da915Sopenharmony_ci if (buf || !size) { 6916987da915Sopenharmony_ci memset(buf,0,size); 6917987da915Sopenharmony_ci /* stuff into current block */ 6918987da915Sopenharmony_ci if (begin_size 6919987da915Sopenharmony_ci && (ntfs_attr_pwrite(na, 6920987da915Sopenharmony_ci na->initialized_size, begin_size, buf) 6921987da915Sopenharmony_ci != begin_size)) 6922987da915Sopenharmony_ci ret = -1; 6923987da915Sopenharmony_ci /* create an unstuffed hole */ 6924987da915Sopenharmony_ci if (!ret 6925987da915Sopenharmony_ci && ((na->initialized_size + end_size) < pos) 6926987da915Sopenharmony_ci && ntfs_non_resident_attr_expand(na, 6927987da915Sopenharmony_ci pos - end_size, HOLES_OK)) 6928987da915Sopenharmony_ci ret = -1; 6929987da915Sopenharmony_ci else 6930987da915Sopenharmony_ci na->initialized_size 6931987da915Sopenharmony_ci = na->data_size = pos - end_size; 6932987da915Sopenharmony_ci /* stuff into the target block */ 6933987da915Sopenharmony_ci if (!ret && end_size 6934987da915Sopenharmony_ci && (ntfs_attr_pwrite(na, 6935987da915Sopenharmony_ci na->initialized_size, end_size, buf) 6936987da915Sopenharmony_ci != end_size)) 6937987da915Sopenharmony_ci ret = -1; 6938987da915Sopenharmony_ci if (buf) 6939987da915Sopenharmony_ci free(buf); 6940987da915Sopenharmony_ci } else 6941987da915Sopenharmony_ci ret = -1; 6942987da915Sopenharmony_ci } 6943987da915Sopenharmony_ci /* make absolutely sure we have reached the target */ 6944987da915Sopenharmony_ci if (!ret && (na->initialized_size != pos)) { 6945987da915Sopenharmony_ci ntfs_log_error("Failed to stuff a compressed file" 6946987da915Sopenharmony_ci "target %lld reached %lld\n", 6947987da915Sopenharmony_ci (long long)pos, (long long)na->initialized_size); 6948987da915Sopenharmony_ci errno = EIO; 6949987da915Sopenharmony_ci ret = -1; 6950987da915Sopenharmony_ci } 6951987da915Sopenharmony_ci return (ret); 6952987da915Sopenharmony_ci} 6953987da915Sopenharmony_ci 6954987da915Sopenharmony_ci/** 6955987da915Sopenharmony_ci * ntfs_attr_readall - read the entire data from an ntfs attribute 6956987da915Sopenharmony_ci * @ni: open ntfs inode in which the ntfs attribute resides 6957987da915Sopenharmony_ci * @type: attribute type 6958987da915Sopenharmony_ci * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL 6959987da915Sopenharmony_ci * @name_len: length of attribute @name in Unicode characters (if @name given) 6960987da915Sopenharmony_ci * @data_size: if non-NULL then store here the data size 6961987da915Sopenharmony_ci * 6962987da915Sopenharmony_ci * This function will read the entire content of an ntfs attribute. 6963987da915Sopenharmony_ci * If @name is AT_UNNAMED then look specifically for an unnamed attribute. 6964987da915Sopenharmony_ci * If @name is NULL then the attribute could be either named or not. 6965987da915Sopenharmony_ci * In both those cases @name_len is not used at all. 6966987da915Sopenharmony_ci * 6967987da915Sopenharmony_ci * On success a buffer is allocated with the content of the attribute 6968987da915Sopenharmony_ci * and which needs to be freed when it's not needed anymore. If the 6969987da915Sopenharmony_ci * @data_size parameter is non-NULL then the data size is set there. 6970987da915Sopenharmony_ci * 6971987da915Sopenharmony_ci * On error NULL is returned with errno set to the error code. 6972987da915Sopenharmony_ci */ 6973987da915Sopenharmony_civoid *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, 6974987da915Sopenharmony_ci ntfschar *name, u32 name_len, s64 *data_size) 6975987da915Sopenharmony_ci{ 6976987da915Sopenharmony_ci ntfs_attr *na; 6977987da915Sopenharmony_ci void *data, *ret = NULL; 6978987da915Sopenharmony_ci s64 size; 6979987da915Sopenharmony_ci 6980987da915Sopenharmony_ci ntfs_log_enter("Entering\n"); 6981987da915Sopenharmony_ci 6982987da915Sopenharmony_ci na = ntfs_attr_open(ni, type, name, name_len); 6983987da915Sopenharmony_ci if (!na) { 6984987da915Sopenharmony_ci ntfs_log_perror("ntfs_attr_open failed, inode %lld attr 0x%lx", 6985987da915Sopenharmony_ci (long long)ni->mft_no,(long)le32_to_cpu(type)); 6986987da915Sopenharmony_ci goto err_exit; 6987987da915Sopenharmony_ci } 6988987da915Sopenharmony_ci /* 6989987da915Sopenharmony_ci * Consistency check : restrict to 65536 bytes. 6990987da915Sopenharmony_ci * index bitmaps may need more, but still limited by 6991987da915Sopenharmony_ci * the number of clusters. 6992987da915Sopenharmony_ci */ 6993987da915Sopenharmony_ci if (((u64)na->data_size > 65536) 6994987da915Sopenharmony_ci && ((type != AT_BITMAP) 6995987da915Sopenharmony_ci || ((u64)na->data_size > 6996987da915Sopenharmony_ci (u64)((ni->vol->nr_clusters + 7) >> 3)))) { 6997987da915Sopenharmony_ci ntfs_log_error("Corrupt attribute 0x%lx in inode %lld\n", 6998987da915Sopenharmony_ci (long)le32_to_cpu(type),(long long)ni->mft_no); 6999987da915Sopenharmony_ci errno = EOVERFLOW; 7000987da915Sopenharmony_ci goto out; 7001987da915Sopenharmony_ci } 7002987da915Sopenharmony_ci data = ntfs_malloc(na->data_size); 7003987da915Sopenharmony_ci if (!data) 7004987da915Sopenharmony_ci goto out; 7005987da915Sopenharmony_ci 7006987da915Sopenharmony_ci size = ntfs_attr_pread(na, 0, na->data_size, data); 7007987da915Sopenharmony_ci if (size != na->data_size) { 7008987da915Sopenharmony_ci ntfs_log_perror("ntfs_attr_pread failed"); 7009987da915Sopenharmony_ci free(data); 7010987da915Sopenharmony_ci goto out; 7011987da915Sopenharmony_ci } 7012987da915Sopenharmony_ci ret = data; 7013987da915Sopenharmony_ci if (data_size) 7014987da915Sopenharmony_ci *data_size = size; 7015987da915Sopenharmony_ciout: 7016987da915Sopenharmony_ci ntfs_attr_close(na); 7017987da915Sopenharmony_cierr_exit: 7018987da915Sopenharmony_ci ntfs_log_leave("\n"); 7019987da915Sopenharmony_ci return ret; 7020987da915Sopenharmony_ci} 7021987da915Sopenharmony_ci 7022987da915Sopenharmony_ci/* 7023987da915Sopenharmony_ci * Read some data from a data attribute 7024987da915Sopenharmony_ci * 7025987da915Sopenharmony_ci * Returns the amount of data read, negative if there was an error 7026987da915Sopenharmony_ci */ 7027987da915Sopenharmony_ci 7028987da915Sopenharmony_ciint ntfs_attr_data_read(ntfs_inode *ni, 7029987da915Sopenharmony_ci ntfschar *stream_name, int stream_name_len, 7030987da915Sopenharmony_ci char *buf, size_t size, off_t offset) 7031987da915Sopenharmony_ci{ 7032987da915Sopenharmony_ci ntfs_attr *na = NULL; 7033987da915Sopenharmony_ci int res, total = 0; 7034987da915Sopenharmony_ci 7035987da915Sopenharmony_ci na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); 7036987da915Sopenharmony_ci if (!na) { 7037987da915Sopenharmony_ci res = -errno; 7038987da915Sopenharmony_ci goto exit; 7039987da915Sopenharmony_ci } 7040987da915Sopenharmony_ci if ((size_t)offset < (size_t)na->data_size) { 7041987da915Sopenharmony_ci if (offset + size > (size_t)na->data_size) 7042987da915Sopenharmony_ci size = na->data_size - offset; 7043987da915Sopenharmony_ci while (size) { 7044987da915Sopenharmony_ci res = ntfs_attr_pread(na, offset, size, buf + total); 7045987da915Sopenharmony_ci if ((off_t)res < (off_t)size) 7046987da915Sopenharmony_ci ntfs_log_perror("ntfs_attr_pread partial read " 7047987da915Sopenharmony_ci "(%lld : %lld <> %d)", 7048987da915Sopenharmony_ci (long long)offset, 7049987da915Sopenharmony_ci (long long)size, res); 7050987da915Sopenharmony_ci if (res <= 0) { 7051987da915Sopenharmony_ci res = -errno; 7052987da915Sopenharmony_ci goto exit; 7053987da915Sopenharmony_ci } 7054987da915Sopenharmony_ci size -= res; 7055987da915Sopenharmony_ci offset += res; 7056987da915Sopenharmony_ci total += res; 7057987da915Sopenharmony_ci } 7058987da915Sopenharmony_ci } 7059987da915Sopenharmony_ci res = total; 7060987da915Sopenharmony_ciexit: 7061987da915Sopenharmony_ci if (na) 7062987da915Sopenharmony_ci ntfs_attr_close(na); 7063987da915Sopenharmony_ci return res; 7064987da915Sopenharmony_ci} 7065987da915Sopenharmony_ci 7066987da915Sopenharmony_ci 7067987da915Sopenharmony_ci/* 7068987da915Sopenharmony_ci * Write some data into a data attribute 7069987da915Sopenharmony_ci * 7070987da915Sopenharmony_ci * Returns the amount of data written, negative if there was an error 7071987da915Sopenharmony_ci */ 7072987da915Sopenharmony_ci 7073987da915Sopenharmony_ciint ntfs_attr_data_write(ntfs_inode *ni, ntfschar *stream_name, 7074987da915Sopenharmony_ci int stream_name_len, const char *buf, size_t size, off_t offset) 7075987da915Sopenharmony_ci{ 7076987da915Sopenharmony_ci ntfs_attr *na = NULL; 7077987da915Sopenharmony_ci int res, total = 0; 7078987da915Sopenharmony_ci 7079987da915Sopenharmony_ci na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); 7080987da915Sopenharmony_ci if (!na) { 7081987da915Sopenharmony_ci res = -errno; 7082987da915Sopenharmony_ci goto exit; 7083987da915Sopenharmony_ci } 7084987da915Sopenharmony_ci while (size) { 7085987da915Sopenharmony_ci res = ntfs_attr_pwrite(na, offset, size, buf + total); 7086987da915Sopenharmony_ci if (res < (s64)size) 7087987da915Sopenharmony_ci ntfs_log_perror("ntfs_attr_pwrite partial write (%lld: " 7088987da915Sopenharmony_ci "%lld <> %d)", (long long)offset, 7089987da915Sopenharmony_ci (long long)size, res); 7090987da915Sopenharmony_ci if (res <= 0) { 7091987da915Sopenharmony_ci res = -errno; 7092987da915Sopenharmony_ci goto exit; 7093987da915Sopenharmony_ci } 7094987da915Sopenharmony_ci size -= res; 7095987da915Sopenharmony_ci offset += res; 7096987da915Sopenharmony_ci total += res; 7097987da915Sopenharmony_ci } 7098987da915Sopenharmony_ci res = total; 7099987da915Sopenharmony_ciexit: 7100987da915Sopenharmony_ci if (na) 7101987da915Sopenharmony_ci ntfs_attr_close(na); 7102987da915Sopenharmony_ci return res; 7103987da915Sopenharmony_ci} 7104987da915Sopenharmony_ci 7105987da915Sopenharmony_ci/* 7106987da915Sopenharmony_ci * Shrink the size of a data attribute if needed 7107987da915Sopenharmony_ci * 7108987da915Sopenharmony_ci * For non-resident attributes only. 7109987da915Sopenharmony_ci * The space remains allocated. 7110987da915Sopenharmony_ci * 7111987da915Sopenharmony_ci * Returns 0 if successful 7112987da915Sopenharmony_ci * -1 if failed, with errno telling why 7113987da915Sopenharmony_ci */ 7114987da915Sopenharmony_ci 7115987da915Sopenharmony_ci 7116987da915Sopenharmony_ciint ntfs_attr_shrink_size(ntfs_inode *ni, ntfschar *stream_name, 7117987da915Sopenharmony_ci int stream_name_len, off_t offset) 7118987da915Sopenharmony_ci{ 7119987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 7120987da915Sopenharmony_ci ATTR_RECORD *a; 7121987da915Sopenharmony_ci int res; 7122987da915Sopenharmony_ci 7123987da915Sopenharmony_ci res = -1; 7124987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(ni, NULL); 7125987da915Sopenharmony_ci if (ctx) { 7126987da915Sopenharmony_ci if (!ntfs_attr_lookup(AT_DATA, stream_name, stream_name_len, 7127987da915Sopenharmony_ci CASE_SENSITIVE, 0, NULL, 0, ctx)) { 7128987da915Sopenharmony_ci a = ctx->attr; 7129987da915Sopenharmony_ci 7130987da915Sopenharmony_ci if (a->non_resident 7131987da915Sopenharmony_ci && (sle64_to_cpu(a->initialized_size) > offset)) { 7132987da915Sopenharmony_ci a->initialized_size = cpu_to_sle64(offset); 7133987da915Sopenharmony_ci a->data_size = a->initialized_size; 7134987da915Sopenharmony_ci } 7135987da915Sopenharmony_ci res = 0; 7136987da915Sopenharmony_ci } 7137987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 7138987da915Sopenharmony_ci } 7139987da915Sopenharmony_ci return (res); 7140987da915Sopenharmony_ci} 7141987da915Sopenharmony_ci 7142987da915Sopenharmony_ciint ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, const ntfschar *name, 7143987da915Sopenharmony_ci u32 name_len) 7144987da915Sopenharmony_ci{ 7145987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 7146987da915Sopenharmony_ci int ret; 7147987da915Sopenharmony_ci 7148987da915Sopenharmony_ci ntfs_log_trace("Entering\n"); 7149987da915Sopenharmony_ci 7150987da915Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(ni, NULL); 7151987da915Sopenharmony_ci if (!ctx) 7152987da915Sopenharmony_ci return 0; 7153987da915Sopenharmony_ci 7154987da915Sopenharmony_ci ret = ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, 0, NULL, 0, 7155987da915Sopenharmony_ci ctx); 7156987da915Sopenharmony_ci 7157987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 7158987da915Sopenharmony_ci 7159987da915Sopenharmony_ci return !ret; 7160987da915Sopenharmony_ci} 7161987da915Sopenharmony_ci 7162987da915Sopenharmony_ciint ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, 7163987da915Sopenharmony_ci u32 name_len) 7164987da915Sopenharmony_ci{ 7165987da915Sopenharmony_ci ntfs_attr *na; 7166987da915Sopenharmony_ci int ret; 7167987da915Sopenharmony_ci 7168987da915Sopenharmony_ci ntfs_log_trace("Entering\n"); 7169987da915Sopenharmony_ci 7170987da915Sopenharmony_ci if (!ni) { 7171987da915Sopenharmony_ci ntfs_log_error("%s: NULL inode pointer", __FUNCTION__); 7172987da915Sopenharmony_ci errno = EINVAL; 7173987da915Sopenharmony_ci return -1; 7174987da915Sopenharmony_ci } 7175987da915Sopenharmony_ci 7176987da915Sopenharmony_ci na = ntfs_attr_open(ni, type, name, name_len); 7177987da915Sopenharmony_ci if (!na) { 7178987da915Sopenharmony_ci /* do not log removal of non-existent stream */ 7179987da915Sopenharmony_ci if (type != AT_DATA) { 7180987da915Sopenharmony_ci ntfs_log_perror("Failed to open attribute 0x%02x of inode " 7181987da915Sopenharmony_ci "0x%llx", le32_to_cpu(type), (unsigned long long)ni->mft_no); 7182987da915Sopenharmony_ci } 7183987da915Sopenharmony_ci return -1; 7184987da915Sopenharmony_ci } 7185987da915Sopenharmony_ci 7186987da915Sopenharmony_ci ret = ntfs_attr_rm(na); 7187987da915Sopenharmony_ci if (ret) 7188987da915Sopenharmony_ci ntfs_log_perror("Failed to remove attribute 0x%02x of inode " 7189987da915Sopenharmony_ci "0x%llx", le32_to_cpu(type), (unsigned long long)ni->mft_no); 7190987da915Sopenharmony_ci ntfs_attr_close(na); 7191987da915Sopenharmony_ci 7192987da915Sopenharmony_ci return ret; 7193987da915Sopenharmony_ci} 7194987da915Sopenharmony_ci 7195987da915Sopenharmony_ci/* Below macros are 32-bit ready. */ 7196987da915Sopenharmony_ci#define BCX(x) ((x) - (((x) >> 1) & 0x77777777) - \ 7197987da915Sopenharmony_ci (((x) >> 2) & 0x33333333) - \ 7198987da915Sopenharmony_ci (((x) >> 3) & 0x11111111)) 7199987da915Sopenharmony_ci#define BITCOUNT(x) (((BCX(x) + (BCX(x) >> 4)) & 0x0F0F0F0F) % 255) 7200987da915Sopenharmony_ci 7201987da915Sopenharmony_cistatic u8 *ntfs_init_lut256(void) 7202987da915Sopenharmony_ci{ 7203987da915Sopenharmony_ci int i; 7204987da915Sopenharmony_ci u8 *lut; 7205987da915Sopenharmony_ci 7206987da915Sopenharmony_ci lut = ntfs_malloc(256); 7207987da915Sopenharmony_ci if (lut) 7208987da915Sopenharmony_ci for(i = 0; i < 256; i++) 7209987da915Sopenharmony_ci *(lut + i) = 8 - BITCOUNT(i); 7210987da915Sopenharmony_ci return lut; 7211987da915Sopenharmony_ci} 7212987da915Sopenharmony_ci 7213987da915Sopenharmony_cis64 ntfs_attr_get_free_bits(ntfs_attr *na) 7214987da915Sopenharmony_ci{ 7215987da915Sopenharmony_ci u8 *buf, *lut; 7216987da915Sopenharmony_ci s64 br = 0; 7217987da915Sopenharmony_ci s64 total = 0; 7218987da915Sopenharmony_ci s64 nr_free = 0; 7219987da915Sopenharmony_ci 7220987da915Sopenharmony_ci lut = ntfs_init_lut256(); 7221987da915Sopenharmony_ci if (!lut) 7222987da915Sopenharmony_ci return -1; 7223987da915Sopenharmony_ci 7224987da915Sopenharmony_ci buf = ntfs_malloc(65536); 7225987da915Sopenharmony_ci if (!buf) 7226987da915Sopenharmony_ci goto out; 7227987da915Sopenharmony_ci 7228987da915Sopenharmony_ci while (1) { 7229987da915Sopenharmony_ci u32 *p; 7230987da915Sopenharmony_ci br = ntfs_attr_pread(na, total, 65536, buf); 7231987da915Sopenharmony_ci if (br <= 0) 7232987da915Sopenharmony_ci break; 7233987da915Sopenharmony_ci total += br; 7234987da915Sopenharmony_ci p = (u32 *)buf + br / 4 - 1; 7235987da915Sopenharmony_ci for (; (u8 *)p >= buf; p--) { 7236987da915Sopenharmony_ci nr_free += lut[ *p & 255] + 7237987da915Sopenharmony_ci lut[(*p >> 8) & 255] + 7238987da915Sopenharmony_ci lut[(*p >> 16) & 255] + 7239987da915Sopenharmony_ci lut[(*p >> 24) ]; 7240987da915Sopenharmony_ci } 7241987da915Sopenharmony_ci switch (br % 4) { 7242987da915Sopenharmony_ci case 3: nr_free += lut[*(buf + br - 3)]; 7243987da915Sopenharmony_ci /* FALLTHRU */ 7244987da915Sopenharmony_ci case 2: nr_free += lut[*(buf + br - 2)]; 7245987da915Sopenharmony_ci /* FALLTHRU */ 7246987da915Sopenharmony_ci case 1: nr_free += lut[*(buf + br - 1)]; 7247987da915Sopenharmony_ci } 7248987da915Sopenharmony_ci } 7249987da915Sopenharmony_ci free(buf); 7250987da915Sopenharmony_ciout: 7251987da915Sopenharmony_ci free(lut); 7252987da915Sopenharmony_ci if (!total || br < 0) 7253987da915Sopenharmony_ci return -1; 7254987da915Sopenharmony_ci return nr_free; 7255987da915Sopenharmony_ci} 7256