--- zzzz-none-000/linux-2.4.17/fs/ntfs/inode.c 2001-12-21 17:42:03.000000000 +0000 +++ sangam-fb-322/linux-2.4.17/fs/ntfs/inode.c 2004-11-24 13:22:16.000000000 +0000 @@ -1,2317 +1,2317 @@ -/* - * inode.c - * - * Copyright (C) 1995-1999 Martin von Löwis - * Copyright (C) 1996 Albert D. Cahalan - * Copyright (C) 1996-1997 Régis Duchesne - * Copyright (C) 1998 Joseph Malicki - * Copyright (C) 1999 Steve Dodd - * Copyright (C) 2000-2001 Anton Altaparmakov (AIA) - */ -#include "ntfstypes.h" -#include "ntfsendian.h" -#include "struct.h" -#include "inode.h" -#include -#include "macros.h" -#include "attr.h" -#include "super.h" -#include "dir.h" -#include "support.h" -#include "util.h" -#include -#include - -typedef struct { - int recno; - unsigned char *record; -} ntfs_mft_record; - -typedef struct { - int size; - int count; - ntfs_mft_record *records; -} ntfs_disk_inode; - -static void ntfs_fill_mft_header(ntfs_u8 *mft, int rec_size, int seq_no, - int links, int flags) -{ - int fixup_ofs = 0x2a; - int fixup_cnt = rec_size / NTFS_SECTOR_SIZE + 1; - int attr_ofs = (fixup_ofs + 2 * fixup_cnt + 7) & ~7; - - NTFS_PUTU32(mft + 0x00, 0x454c4946); /* FILE */ - NTFS_PUTU16(mft + 0x04, fixup_ofs); /* Offset to fixup. */ - NTFS_PUTU16(mft + 0x06, fixup_cnt); /* Number of fixups. */ - NTFS_PUTU64(mft + 0x08, 0); /* Logical sequence number. */ - NTFS_PUTU16(mft + 0x10, seq_no); /* Sequence number. */ - NTFS_PUTU16(mft + 0x12, links); /* Hard link count. */ - NTFS_PUTU16(mft + 0x14, attr_ofs); /* Offset to attributes. */ - NTFS_PUTU16(mft + 0x16, flags); /* Flags: 1 = In use, - 2 = Directory. */ - NTFS_PUTU32(mft + 0x18, attr_ofs + 8); /* Bytes in use. */ - NTFS_PUTU32(mft + 0x1c, rec_size); /* Total allocated size. */ - NTFS_PUTU64(mft + 0x20, 0); /* Base mft record. */ - NTFS_PUTU16(mft + 0x28, 0); /* Next attr instance. */ - NTFS_PUTU16(mft + fixup_ofs, 1); /* Fixup word. */ - NTFS_PUTU32(mft + attr_ofs, (__u32)-1); /* End of attributes marker. */ -} - -/* - * Search in an inode an attribute by type and name. - * FIXME: Check that when attributes are inserted all attribute list - * attributes are expanded otherwise need to modify this function to deal - * with attribute lists. (AIA) - */ -ntfs_attribute *ntfs_find_attr(ntfs_inode *ino, int type, char *name) -{ - int i; - - if (!ino) { - ntfs_error("ntfs_find_attr: NO INODE!\n"); - return 0; - } - for (i = 0; i < ino->attr_count; i++) { - if (type < ino->attrs[i].type) - return 0; - if (type == ino->attrs[i].type) { - if (!name) { - if (!ino->attrs[i].name) - return ino->attrs + i; - } else if (ino->attrs[i].name && - !ntfs_ua_strncmp(ino->attrs[i].name, name, - strlen(name))) - return ino->attrs + i; - } - } - return 0; -} - -/* - * Insert all attributes from the record mftno of the MFT in the inode ino. - * If mftno is a base mft record we abort as soon as we find the attribute - * list, but only on the first pass. We will get called later when the attribute - * list attribute is being parsed so we need to distinguish the two cases. - * FIXME: We should be performing structural consistency checks. (AIA) - * Return 0 on success or -errno on error. - */ -static int ntfs_insert_mft_attributes(ntfs_inode* ino, char *mft, int mftno) -{ - int i, error, type, len, present = 0; - char *it; - - /* Check for duplicate extension record. */ - for(i = 0; i < ino->record_count; i++) - if (ino->records[i] == mftno) { - if (i) - return 0; - present = 1; - break; - } - if (!present) { - /* (re-)allocate space if necessary. */ - if (ino->record_count % 8 == 0) { - int *new; - - new = ntfs_malloc((ino->record_count + 8) * - sizeof(int)); - if (!new) - return -ENOMEM; - if (ino->records) { - for (i = 0; i < ino->record_count; i++) - new[i] = ino->records[i]; - ntfs_free(ino->records); - } - ino->records = new; - } - ino->records[ino->record_count] = mftno; - ino->record_count++; - } - it = mft + NTFS_GETU16(mft + 0x14); /* mft->attrs_offset */ - do { - type = NTFS_GETU32(it); - len = NTFS_GETU32(it + 4); - if (type != -1) { - error = ntfs_insert_attribute(ino, it); - if (error) - return error; - } - /* If we have just processed the attribute list and this is - * the first time we are parsing this (base) mft record then we - * are done so that the attribute list gets parsed before the - * entries in the base mft record. Otherwise we run into - * problems with encountering attributes out of order and when - * this happens with different attribute extents we die. )-: - * This way we are ok as the attribute list is always sorted - * fully and correctly. (-: */ - if (type == 0x20 && !present) - return 0; - it += len; - } while (type != -1); /* Attribute listing ends with type -1. */ - return 0; -} - -/* - * Insert a single specific attribute from the record mftno of the MFT in the - * inode ino. We disregard the attribute list assuming we have already parsed - * it. - * FIXME: We should be performing structural consistency checks. (AIA) - * Return 0 on success or -errno on error. - */ -static int ntfs_insert_mft_attribute(ntfs_inode* ino, int mftno, - ntfs_u8 *attr) -{ - int i, error, present = 0; - - /* Check for duplicate extension record. */ - for(i = 0; i < ino->record_count; i++) - if (ino->records[i] == mftno) { - present = 1; - break; - } - if (!present) { - /* (re-)allocate space if necessary. */ - if (ino->record_count % 8 == 0) { - int *new; - - new = ntfs_malloc((ino->record_count + 8) * - sizeof(int)); - if (!new) - return -ENOMEM; - if (ino->records) { - for (i = 0; i < ino->record_count; i++) - new[i] = ino->records[i]; - ntfs_free(ino->records); - } - ino->records = new; - } - ino->records[ino->record_count] = mftno; - ino->record_count++; - } - if (NTFS_GETU32(attr) == -1) { - ntfs_debug(DEBUG_FILE3, "ntfs_insert_mft_attribute: attribute " - "type is -1.\n"); - return 0; - } - error = ntfs_insert_attribute(ino, attr); - if (error) - return error; - return 0; -} - -/* Read and insert all the attributes of an 'attribute list' attribute. - * Return the number of remaining bytes in *plen. */ -static int parse_attributes(ntfs_inode *ino, ntfs_u8 *alist, int *plen) -{ - ntfs_u8 *mft, *attr; - int mftno, l, error; - int last_mft = -1; - int len = *plen; - int tries = 0; - - if (!ino->attr) { - ntfs_error("parse_attributes: called on inode 0x%x without a " - "loaded base mft record.\n", ino->i_number); - return -EINVAL; - } - mft = ntfs_malloc(ino->vol->mft_record_size); - if (!mft) - return -ENOMEM; - while (len > 8) { - l = NTFS_GETU16(alist + 4); - if (l > len) - break; - /* Process an attribute description. */ - mftno = NTFS_GETU32(alist + 0x10); - /* FIXME: The mft reference (alist + 0x10) is __s64. - * - Not a problem unless we encounter a huge partition. - * - Should be consistency checking the sequence numbers - * though! This should maybe happen in - * ntfs_read_mft_record() itself and a hotfix could - * then occur there or the user notified to run - * ntfsck. (AIA) */ - if (mftno != ino->i_number && mftno != last_mft) { -continue_after_loading_mft_data: - last_mft = mftno; - error = ntfs_read_mft_record(ino->vol, mftno, mft); - if (error) { - if (error == -EINVAL && !tries) - goto force_load_mft_data; -failed_reading_mft_data: - ntfs_debug(DEBUG_FILE3, "parse_attributes: " - "ntfs_read_mft_record(mftno = 0x%x) " - "failed\n", mftno); - ntfs_free(mft); - return error; - } - } - attr = ntfs_find_attr_in_mft_rec( - ino->vol, /* ntfs volume */ - mftno == ino->i_number ?/* mft record is: */ - ino->attr: /* base record */ - mft, /* extension record */ - NTFS_GETU32(alist + 0), /* type */ - (wchar_t*)(alist + alist[7]), /* name */ - alist[6], /* name length */ - 1, /* ignore case */ - NTFS_GETU16(alist + 24) /* instance number */ - ); - if (!attr) { - ntfs_error("parse_attributes: mft records 0x%x and/or " - "0x%x corrupt!\n", ino->i_number, mftno); - ntfs_free(mft); - return -EINVAL; /* FIXME: Better error code? (AIA) */ - } - error = ntfs_insert_mft_attribute(ino, mftno, attr); - if (error) { - ntfs_debug(DEBUG_FILE3, "parse_attributes: " - "ntfs_insert_mft_attribute(mftno 0x%x, " - "attribute type 0x%x) failed\n", mftno, - NTFS_GETU32(alist + 0)); - ntfs_free(mft); - return error; - } - len -= l; - alist += l; - } - ntfs_free(mft); - *plen = len; - return 0; -force_load_mft_data: -{ - ntfs_u8 *mft2, *attr2; - int mftno2; - int last_mft2 = last_mft; - int len2 = len; - int error2; - int found2 = 0; - ntfs_u8 *alist2 = alist; - /* - * We only get here if $DATA wasn't found in $MFT which only happens - * on volume mount when $MFT has an attribute list and there are - * attributes before $DATA which are inside extent mft records. So - * we just skip forward to the $DATA attribute and read that. Then we - * restart which is safe as an attribute will not be inserted twice. - * - * This still will not fix the case where the attribute list is non- - * resident, larger than 1024 bytes, and the $DATA attribute list entry - * is not in the first 1024 bytes. FIXME: This should be implemented - * somehow! Perhaps by passing special error code up to - * ntfs_load_attributes() so it keeps going trying to get to $DATA - * regardless. Then it would have to restart just like we do here. - */ - mft2 = ntfs_malloc(ino->vol->mft_record_size); - if (!mft2) { - ntfs_free(mft); - return -ENOMEM; - } - ntfs_memcpy(mft2, mft, ino->vol->mft_record_size); - while (len2 > 8) { - l = NTFS_GETU16(alist2 + 4); - if (l > len2) - break; - if (NTFS_GETU32(alist2 + 0x0) < ino->vol->at_data) { - len2 -= l; - alist2 += l; - continue; - } - if (NTFS_GETU32(alist2 + 0x0) > ino->vol->at_data) { - if (found2) - break; - /* Uh-oh! It really isn't there! */ - ntfs_error("Either the $MFT is corrupt or, equally " - "likely, the $MFT is too complex for " - "the current driver to handle. Please " - "email the ntfs maintainer that you " - "saw this message. Thank you.\n"); - goto failed_reading_mft_data; - } - /* Process attribute description. */ - mftno2 = NTFS_GETU32(alist2 + 0x10); - if (mftno2 != ino->i_number && mftno2 != last_mft2) { - last_mft2 = mftno2; - error2 = ntfs_read_mft_record(ino->vol, mftno2, mft2); - if (error2) { - ntfs_debug(DEBUG_FILE3, "parse_attributes: " - "ntfs_read_mft_record(mftno2 = 0x%x) " - "failed\n", mftno2); - ntfs_free(mft2); - goto failed_reading_mft_data; - } - } - attr2 = ntfs_find_attr_in_mft_rec( - ino->vol, /* ntfs volume */ - mftno2 == ino->i_number ?/* mft record is: */ - ino->attr: /* base record */ - mft2, /* extension record */ - NTFS_GETU32(alist2 + 0), /* type */ - (wchar_t*)(alist2 + alist2[7]), /* name */ - alist2[6], /* name length */ - 1, /* ignore case */ - NTFS_GETU16(alist2 + 24) /* instance number */ - ); - if (!attr2) { - ntfs_error("parse_attributes: mft records 0x%x and/or " - "0x%x corrupt!\n", ino->i_number, - mftno2); - ntfs_free(mft2); - goto failed_reading_mft_data; - } - error2 = ntfs_insert_mft_attribute(ino, mftno2, attr2); - if (error2) { - ntfs_debug(DEBUG_FILE3, "parse_attributes: " - "ntfs_insert_mft_attribute(mftno2 0x%x, " - "attribute2 type 0x%x) failed\n", mftno2, - NTFS_GETU32(alist2 + 0)); - ntfs_free(mft2); - goto failed_reading_mft_data; - } - len2 -= l; - alist2 += l; - found2 = 1; - } - ntfs_free(mft2); - tries = 1; - goto continue_after_loading_mft_data; -} -} - -static void ntfs_load_attributes(ntfs_inode *ino) -{ - ntfs_attribute *alist; - int datasize; - int offset, len, delta; - char *buf; - ntfs_volume *vol = ino->vol; - - ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 1\n", ino->i_number); - if (ntfs_insert_mft_attributes(ino, ino->attr, ino->i_number)) - return; - ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 2\n", ino->i_number); - alist = ntfs_find_attr(ino, vol->at_attribute_list, 0); - ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 3\n", ino->i_number); - if (!alist) - return; - ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 4\n", ino->i_number); - datasize = alist->size; - ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: alist->size = 0x%x\n", - ino->i_number, alist->size); - if (alist->resident) { - parse_attributes(ino, alist->d.data, &datasize); - return; - } - ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 5\n", ino->i_number); - buf = ntfs_malloc(1024); - if (!buf) /* FIXME: Should be passing error code to caller. (AIA) */ - return; - delta = 0; - for (offset = 0; datasize; datasize -= len, offset += len) { - ntfs_io io; - - io.fn_put = ntfs_put; - io.fn_get = 0; - io.param = buf + delta; - len = 1024 - delta; - if (len > datasize) - len = datasize; - ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: len = %i\n", - ino->i_number, len); - ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: delta = %i\n", - ino->i_number, delta); - io.size = len; - if (ntfs_read_attr(ino, vol->at_attribute_list, 0, offset, - &io)) - ntfs_error("error in load_attributes\n"); - delta += len; - ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: after += len, " - "delta = %i\n", ino->i_number, delta); - parse_attributes(ino, buf, &delta); - ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: after " - "parse_attr, delta = %i\n", ino->i_number, - delta); - if (delta) - /* Move remaining bytes to buffer start. */ - ntfs_memmove(buf, buf + len - delta, delta); - } - ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 6\n", ino->i_number); - ntfs_free(buf); -} - -int ntfs_init_inode(ntfs_inode *ino, ntfs_volume *vol, int inum) -{ - char *buf; - int error; - - ntfs_debug(DEBUG_FILE1, "Initializing inode 0x%x\n", inum); - ino->i_number = inum; - ino->vol = vol; - ino->attr = buf = ntfs_malloc(vol->mft_record_size); - if (!buf) - return -ENOMEM; - error = ntfs_read_mft_record(vol, inum, ino->attr); - if (error) { - ntfs_debug(DEBUG_OTHER, "Init inode: 0x%x failed\n", inum); - return error; - } - ntfs_debug(DEBUG_FILE2, "Init inode: got mft 0x%x\n", inum); - ino->sequence_number = NTFS_GETU16(buf + 0x10); - ino->attr_count = 0; - ino->record_count = 0; - ino->records = 0; - ino->attrs = 0; - ntfs_load_attributes(ino); - ntfs_debug(DEBUG_FILE2, "Init inode: done 0x%x\n", inum); - return 0; -} - -void ntfs_clear_inode(ntfs_inode *ino) -{ - int i; - if (!ino->attr) { - ntfs_error("ntfs_clear_inode: double free\n"); - return; - } - ntfs_free(ino->attr); - ino->attr = 0; - ntfs_free(ino->records); - ino->records = 0; - for (i = 0; i < ino->attr_count; i++) { - if (ino->attrs[i].name) - ntfs_free(ino->attrs[i].name); - if (ino->attrs[i].resident) { - if (ino->attrs[i].d.data) - ntfs_free(ino->attrs[i].d.data); - } else { - if (ino->attrs[i].d.r.runlist) - ntfs_vfree(ino->attrs[i].d.r.runlist); - } - } - ntfs_free(ino->attrs); - ino->attrs = 0; -} - -/* Check and fixup a MFT record. */ -int ntfs_check_mft_record(ntfs_volume *vol, char *record) -{ - return ntfs_fixup_record(record, "FILE", vol->mft_record_size); -} - -/* Return (in result) the value indicating the next available attribute - * chunk number. Works for inodes w/o extension records only. */ -int ntfs_allocate_attr_number(ntfs_inode *ino, int *result) -{ - if (ino->record_count != 1) - return -EOPNOTSUPP; - *result = NTFS_GETU16(ino->attr + 0x28); - NTFS_PUTU16(ino->attr + 0x28, (*result) + 1); - return 0; -} - -/* Find the location of an attribute in the inode. A name of NULL indicates - * unnamed attributes. Return pointer to attribute or NULL if not found. */ -char *ntfs_get_attr(ntfs_inode *ino, int attr, char *name) -{ - /* Location of first attribute. */ - char *it = ino->attr + NTFS_GETU16(ino->attr + 0x14); - int type; - int len; - - /* Only check for magic DWORD here, fixup should have happened before.*/ - if (!IS_MFT_RECORD(ino->attr)) - return 0; - do { - type = NTFS_GETU32(it); - len = NTFS_GETU16(it + 4); - /* We found the attribute type. Is the name correct, too? */ - if (type == attr) { - int namelen = NTFS_GETU8(it + 9); - char *name_it, *n = name; - /* Match given name and attribute name if present. - Make sure attribute name is Unicode. */ - if (!name) { - goto check_namelen; - } else if (namelen) { - for (name_it = it + NTFS_GETU16(it + 10); - namelen; n++, name_it += 2, namelen--) - if (*name_it != *n || name_it[1]) - break; -check_namelen: - if (!namelen) - break; - } - } - it += len; - } while (type != -1); /* List of attributes ends with type -1. */ - if (type == -1) - return 0; - return it; -} - -__s64 ntfs_get_attr_size(ntfs_inode *ino, int type, char *name) -{ - ntfs_attribute *attr = ntfs_find_attr(ino, type, name); - if (!attr) - return 0; - return - attr->size; -} - -int ntfs_attr_is_resident(ntfs_inode *ino, int type, char *name) -{ - ntfs_attribute *attr = ntfs_find_attr(ino, type, name); - if (!attr) - return 0; - return attr->resident; -} - -/* - * A run is coded as a type indicator, an unsigned length, and a signed cluster - * offset. - * . To save space, length and offset are fields of variable length. The low - * nibble of the type indicates the width of the length :), the high nibble - * the width of the offset. - * . The first offset is relative to cluster 0, later offsets are relative to - * the previous cluster. - * - * This function decodes a run. Length is an output parameter, data and cluster - * are in/out parameters. - */ -int ntfs_decompress_run(unsigned char **data, int *length, - ntfs_cluster_t *cluster, int *ctype) -{ - unsigned char type = *(*data)++; - *ctype = 0; - switch (type & 0xF) { - case 1: - *length = NTFS_GETS8(*data); - break; - case 2: - *length = NTFS_GETS16(*data); - break; - case 3: - *length = NTFS_GETS24(*data); - break; - case 4: - *length = NTFS_GETS32(*data); - break; - /* Note: cases 5-8 are probably pointless to code, since how - * many runs > 4GB of length are there? At the most, cases 5 - * and 6 are probably necessary, and would also require making - * length 64-bit throughout. */ - default: - ntfs_error("Can't decode run type field 0x%x\n", type); - return -1; - } -// ntfs_debug(DEBUG_FILE3, "ntfs_decompress_run: length = 0x%x\n",*length); - if (*length < 0) - { - ntfs_error("Negative run length decoded\n"); - return -1; - } - *data += (type & 0xF); - switch (type & 0xF0) { - case 0: - *ctype = 2; - break; - case 0x10: - *cluster += NTFS_GETS8(*data); - break; - case 0x20: - *cluster += NTFS_GETS16(*data); - break; - case 0x30: - *cluster += NTFS_GETS24(*data); - break; - case 0x40: - *cluster += NTFS_GETS32(*data); - break; -#if 0 /* Keep for future, in case ntfs_cluster_t ever becomes 64bit. */ - case 0x50: - *cluster += NTFS_GETS40(*data); - break; - case 0x60: - *cluster += NTFS_GETS48(*data); - break; - case 0x70: - *cluster += NTFS_GETS56(*data); - break; - case 0x80: - *cluster += NTFS_GETS64(*data); - break; -#endif - default: - ntfs_error("Can't decode run type field 0x%x\n", type); - return -1; - } -// ntfs_debug(DEBUG_FILE3, "ntfs_decompress_run: cluster = 0x%x\n", -// *cluster); - *data += (type >> 4); - return 0; -} - -static void dump_runlist(const ntfs_runlist *rl, const int rlen); - -/* - * FIXME: ntfs_readwrite_attr() has the effect of writing @dest to @offset of - * the attribute value of the attribute @attr in the in memory inode @ino. - * If the attribute value of @attr is non-resident the value's contents at - * @offset are actually written to disk (from @dest). The on disk mft record - * describing the non-resident attribute value is not updated! - * If the attribute value is resident then the value is written only in - * memory. The on disk mft record containing the value is not written to disk. - * A possible fix would be to call ntfs_update_inode() before returning. (AIA) - */ -/* Reads l bytes of the attribute (attr, name) of ino starting at offset on - * vol into buf. Returns the number of bytes read in the ntfs_io struct. - * Returns 0 on success, errno on failure */ -int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset, - ntfs_io *dest) -{ - int rnum, s_vcn, error, clustersizebits; - ntfs_cluster_t cluster, s_cluster, vcn, len; - __s64 l, chunk, copied; - - ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): %s 0x%x bytes at offset " - "0x%Lx %s inode 0x%x, attr type 0x%x.\n", - dest->do_read ? "Read" : "Write", dest->size, offset, - dest->do_read ? "from" : "to", ino->i_number, - attr->type); - l = dest->size; - if (l == 0) - return 0; - if (dest->do_read) { - /* If read _starts_ beyond end of stream, return nothing. */ - if (offset >= attr->size) { - dest->size = 0; - return 0; - } - /* If read _extends_ beyond end of stream, return as much - * initialised data as we have. */ - if (offset + l >= attr->size) - l = dest->size = attr->size - offset; - } else { - /* - * If write extends beyond _allocated_ size, extend attribute, - * updating attr->allocated and attr->size in the process. (AIA) - */ - if ((!attr->resident && offset + l > attr->allocated) || - (attr->resident && offset + l > attr->size)) { - error = ntfs_resize_attr(ino, attr, offset + l); - if (error) - return error; - } - if (!attr->resident) { - /* Has amount of data increased? */ - if (offset + l > attr->size) - attr->size = offset + l; - /* Has amount of initialised data increased? */ - if (offset + l > attr->initialized) { - /* FIXME: Clear the section between the old - * initialised length and the write start. - * (AIA) */ - attr->initialized = offset + l; - } - } - } - if (attr->resident) { - if (dest->do_read) - dest->fn_put(dest, (ntfs_u8*)attr->d.data + offset, l); - else - dest->fn_get((ntfs_u8*)attr->d.data + offset, dest, l); - dest->size = l; - return 0; - } - if (dest->do_read) { - /* Read uninitialized data. */ - if (offset >= attr->initialized) - return ntfs_read_zero(dest, l); - if (offset + l > attr->initialized) { - dest->size = chunk = attr->initialized - offset; - error = ntfs_readwrite_attr(ino, attr, offset, dest); - if (error || (dest->size != chunk && (error = -EIO, 1))) - return error; - dest->size += l - chunk; - return ntfs_read_zero(dest, l - chunk); - } - if (attr->flags & ATTR_IS_COMPRESSED) - return ntfs_read_compressed(ino, attr, offset, dest); - } else { - if (attr->flags & ATTR_IS_COMPRESSED) - return ntfs_write_compressed(ino, attr, offset, dest); - } - vcn = 0; - clustersizebits = ino->vol->cluster_size_bits; - s_vcn = offset >> clustersizebits; - for (rnum = 0; rnum < attr->d.r.len && - vcn + attr->d.r.runlist[rnum].len <= s_vcn; rnum++) - vcn += attr->d.r.runlist[rnum].len; - if (rnum == attr->d.r.len) { - ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): EOPNOTSUPP: " - "inode = 0x%x, rnum = %i, offset = 0x%Lx, vcn = 0x%x, " - "s_vcn = 0x%x.\n", ino->i_number, rnum, offset, vcn, - s_vcn); - dump_runlist(attr->d.r.runlist, attr->d.r.len); - /*FIXME: Should extend runlist. */ - return -EOPNOTSUPP; - } - copied = 0; - while (l) { - s_vcn = offset >> clustersizebits; - cluster = attr->d.r.runlist[rnum].lcn; - len = attr->d.r.runlist[rnum].len; - s_cluster = cluster + s_vcn - vcn; - chunk = ((__s64)(vcn + len) << clustersizebits) - offset; - if (chunk > l) - chunk = l; - dest->size = chunk; - error = ntfs_getput_clusters(ino->vol, s_cluster, offset - - ((__s64)s_vcn << clustersizebits), dest); - if (error) { - ntfs_error("Read/write error.\n"); - dest->size = copied; - return error; - } - l -= chunk; - copied += chunk; - offset += chunk; - if (l && offset >= ((__s64)(vcn + len) << clustersizebits)) { - rnum++; - vcn += len; - cluster = attr->d.r.runlist[rnum].lcn; - len = attr->d.r.runlist[rnum].len; - } - } - dest->size = copied; - return 0; -} - -int ntfs_read_attr(ntfs_inode *ino, int type, char *name, __s64 offset, - ntfs_io *buf) -{ - ntfs_attribute *attr; - - buf->do_read = 1; - attr = ntfs_find_attr(ino, type, name); - if (!attr) { - ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): attr 0x%x not found " - "in inode 0x%x\n", type, ino->i_number); - return -EINVAL; - } - return ntfs_readwrite_attr(ino, attr, offset, buf); -} - -int ntfs_write_attr(ntfs_inode *ino, int type, char *name, __s64 offset, - ntfs_io *buf) -{ - ntfs_attribute *attr; - - buf->do_read = 0; - attr = ntfs_find_attr(ino, type, name); - if (!attr) { - ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): attr 0x%x not found " - "in inode 0x%x\n", type, ino->i_number); - return -EINVAL; - } - return ntfs_readwrite_attr(ino, attr, offset, buf); -} - -/* -2 = error, -1 = hole, >= 0 means real disk cluster (lcn). */ -int ntfs_vcn_to_lcn(ntfs_inode *ino, int vcn) -{ - int rnum; - ntfs_attribute *data; - - data = ntfs_find_attr(ino, ino->vol->at_data, 0); - if (!data || data->resident || data->flags & (ATTR_IS_COMPRESSED | - ATTR_IS_ENCRYPTED)) - return -2; - if (data->size <= (__s64)vcn << ino->vol->cluster_size_bits) - return -2; - if (data->initialized <= (__s64)vcn << ino->vol->cluster_size_bits) - return -1; - for (rnum = 0; rnum < data->d.r.len && - vcn >= data->d.r.runlist[rnum].len; rnum++) - vcn -= data->d.r.runlist[rnum].len; - if (data->d.r.runlist[rnum].lcn >= 0) - return data->d.r.runlist[rnum].lcn + vcn; - return data->d.r.runlist[rnum].lcn + vcn; -} - -static int allocate_store(ntfs_volume *vol, ntfs_disk_inode *store, int count) -{ - int i; - - if (store->count > count) - return 0; - if (store->size < count) { - ntfs_mft_record *n = ntfs_malloc((count + 4) * - sizeof(ntfs_mft_record)); - if (!n) - return -ENOMEM; - if (store->size) { - for (i = 0; i < store->size; i++) - n[i] = store->records[i]; - ntfs_free(store->records); - } - store->size = count + 4; - store->records = n; - } - for (i = store->count; i < count; i++) { - store->records[i].record = ntfs_malloc(vol->mft_record_size); - if (!store->records[i].record) - return -ENOMEM; - store->count++; - } - return 0; -} - -static void deallocate_store(ntfs_disk_inode* store) -{ - int i; - - for (i = 0; i < store->count; i++) - ntfs_free(store->records[i].record); - ntfs_free(store->records); - store->count = store->size = 0; - store->records = 0; -} - -/** - * layout_runs - compress runlist into mapping pairs array - * @attr: attribute containing the runlist to compress - * @rec: destination buffer to hold the mapping pairs array - * @offs: current position in @rec (in/out variable) - * @size: size of the buffer @rec - * - * layout_runs walks the runlist in @attr, compresses it and writes it out the - * resulting mapping pairs array into @rec (up to a maximum of @size bytes are - * written). On entry @offs is the offset in @rec at which to begin writing the - * mapping pairs array. On exit, it contains the offset in @rec of the first - * byte after the end of the mapping pairs array. - */ -static int layout_runs(ntfs_attribute *attr, char *rec, int *offs, int size) -{ - int i, len, offset, coffs; - /* ntfs_cluster_t MUST be signed! (AIA) */ - ntfs_cluster_t cluster, rclus; - ntfs_runlist *rl = attr->d.r.runlist; - cluster = 0; - offset = *offs; - for (i = 0; i < attr->d.r.len; i++) { - /* - * We cheat with this check on the basis that lcn will never - * be less than -1 and the lcn delta will fit in signed - * 32-bits (ntfs_cluster_t). (AIA) - */ - if (rl[i].lcn < (ntfs_cluster_t)-1) { - ntfs_error("layout_runs() encountered an out of bounds " - "cluster delta, lcn = %i.\n", - rl[i].lcn); - return -ERANGE; - } - rclus = rl[i].lcn - cluster; - len = rl[i].len; - rec[offset] = 0; - if (offset + 9 > size) - return -E2BIG; /* It might still fit, but this - * simplifies testing. */ - /* - * Run length is stored as signed number, so deal with it - * properly, i.e. observe that a negative number will have all - * its most significant bits set to 1 but we don't store that - * in the mapping pairs array. We store the smallest type of - * negative number required, thus in the first if we check - * whether len fits inside a signed byte and if so we store it - * as such, the next ifs check for a signed short, then a signed - * 24-bit and finally the full blown signed 32-bit. Same goes - * for rlus below. (AIA) - */ - if (len >= -0x80 && len <= 0x7f) { - NTFS_PUTU8(rec + offset + 1, len & 0xff); - coffs = 1; - } else if (len >= -0x8000 && len <= 0x7fff) { - NTFS_PUTU16(rec + offset + 1, len & 0xffff); - coffs = 2; - } else if (len >= -0x800000 && len <= 0x7fffff) { - NTFS_PUTU24(rec + offset + 1, len & 0xffffff); - coffs = 3; - } else /* if (len >= -0x80000000LL && len <= 0x7fffffff */ { - NTFS_PUTU32(rec + offset + 1, len); - coffs = 4; - } /* else ... FIXME: When len becomes 64-bit we need to extend - * the else if () statements. (AIA) */ - *(rec + offset) |= coffs++; - if (rl[i].lcn == (ntfs_cluster_t)-1) /* Compressed run. */ - /* Nothing */; - else if (rclus >= -0x80 && rclus <= 0x7f) { - *(rec + offset) |= 0x10; - NTFS_PUTS8(rec + offset + coffs, rclus & 0xff); - coffs += 1; - } else if (rclus >= -0x8000 && rclus <= 0x7fff) { - *(rec + offset) |= 0x20; - NTFS_PUTS16(rec + offset + coffs, rclus & 0xffff); - coffs += 2; - } else if (rclus >= -0x800000 && rclus <= 0x7fffff) { - *(rec + offset) |= 0x30; - NTFS_PUTS24(rec + offset + coffs, rclus & 0xffffff); - coffs += 3; - } else /* if (rclus >= -0x80000000LL && rclus <= 0x7fffffff)*/ { - *(rec + offset) |= 0x40; - NTFS_PUTS32(rec + offset + coffs, rclus - /* & 0xffffffffLL */); - coffs += 4; - } /* FIXME: When rclus becomes 64-bit. - else if (rclus >= -0x8000000000 && rclus <= 0x7FFFFFFFFF) { - *(rec + offset) |= 0x50; - NTFS_PUTS40(rec + offset + coffs, rclus & - 0xffffffffffLL); - coffs += 5; - } else if (rclus >= -0x800000000000 && - rclus <= 0x7FFFFFFFFFFF) { - *(rec + offset) |= 0x60; - NTFS_PUTS48(rec + offset + coffs, rclus & - 0xffffffffffffLL); - coffs += 6; - } else if (rclus >= -0x80000000000000 && - rclus <= 0x7FFFFFFFFFFFFF) { - *(rec + offset) |= 0x70; - NTFS_PUTS56(rec + offset + coffs, rclus & - 0xffffffffffffffLL); - coffs += 7; - } else { - *(rec + offset) |= 0x80; - NTFS_PUTS64(rec + offset + coffs, rclus); - coffs += 8; - } */ - offset += coffs; - if (rl[i].lcn) - cluster = rl[i].lcn; - } - if (offset >= size) - return -E2BIG; - /* Terminating null. */ - *(rec + offset++) = 0; - *offs = offset; - return 0; -} - -static void count_runs(ntfs_attribute *attr, char *buf) -{ - ntfs_u32 first, count, last, i; - - first = 0; - for (i = 0, count = 0; i < attr->d.r.len; i++) - count += attr->d.r.runlist[i].len; - last = first + count - 1; - NTFS_PUTU64(buf + 0x10, first); - NTFS_PUTU64(buf + 0x18, last); -} - -/** - * layout_attr - convert in memory attribute to on disk attribute record - * @attr: in memory attribute to convert - * @buf: destination buffer for on disk attribute record - * @size: size of the destination buffer - * @psize: size of converted on disk attribute record (out variable) - * - * layout_attr() takes the attribute @attr and converts it into the appropriate - * on disk structure, writing it into @buf (up to @size bytes are written). - * - * On success we return 0 and set @*psize to the actual byte size of the on- - * disk attribute that was written into @buf. - */ -static int layout_attr(ntfs_attribute *attr, char *buf, int size, int *psize) -{ - int nameoff, hdrsize, asize; - - if (attr->resident) { - nameoff = 0x18; - hdrsize = (nameoff + 2 * attr->namelen + 7) & ~7; - asize = (hdrsize + attr->size + 7) & ~7; - if (size < asize) - return -E2BIG; - NTFS_PUTU32(buf + 0x10, attr->size); - NTFS_PUTU8(buf + 0x16, attr->indexed); - NTFS_PUTU16(buf + 0x14, hdrsize); - if (attr->size) - ntfs_memcpy(buf + hdrsize, attr->d.data, attr->size); - } else { - int error; - - if (attr->flags & ATTR_IS_COMPRESSED) - nameoff = 0x48; - else - nameoff = 0x40; - hdrsize = (nameoff + 2 * attr->namelen + 7) & ~7; - if (size < hdrsize) - return -E2BIG; - /* Make asize point at the end of the attribute record header, - i.e. at the beginning of the mapping pairs array. */ - asize = hdrsize; - error = layout_runs(attr, buf, &asize, size); - /* Now, asize points one byte beyond the end of the mapping - pairs array. */ - if (error) - return error; - /* The next attribute has to begin on 8-byte boundary. */ - asize = (asize + 7) & ~7; - /* FIXME: fragments */ - count_runs(attr, buf); - NTFS_PUTU16(buf + 0x20, hdrsize); - NTFS_PUTU16(buf + 0x22, attr->cengine); - NTFS_PUTU32(buf + 0x24, 0); - NTFS_PUTS64(buf + 0x28, attr->allocated); - NTFS_PUTS64(buf + 0x30, attr->size); - NTFS_PUTS64(buf + 0x38, attr->initialized); - if (attr->flags & ATTR_IS_COMPRESSED) - NTFS_PUTS64(buf + 0x40, attr->compsize); - } - NTFS_PUTU32(buf, attr->type); - NTFS_PUTU32(buf + 4, asize); - NTFS_PUTU8(buf + 8, attr->resident ? 0 : 1); - NTFS_PUTU8(buf + 9, attr->namelen); - NTFS_PUTU16(buf + 0xa, nameoff); - NTFS_PUTU16(buf + 0xc, attr->flags); - NTFS_PUTU16(buf + 0xe, attr->attrno); - if (attr->namelen) - ntfs_memcpy(buf + nameoff, attr->name, 2 * attr->namelen); - *psize = asize; - return 0; -} - -/** - * layout_inode - convert an in-memory inode into on disk mft record(s) - * @ino: in memory inode to convert - * @store: on disk inode, contain buffers for the on disk mft record(s) - * - * layout_inode takes the in memory inode @ino, converts it into a (sequence of) - * mft record(s) and writes them to the appropriate buffers in the @store. - * - * Return 0 on success, - * the required mft record count (>0) if the inode does not fit, - * -ENOMEM if memory allocation problem, or - * -EOPNOTSUP if beyond our capabilities. - * - * TODO: We at the moment do not support extension mft records. (AIA) - */ -int layout_inode(ntfs_inode *ino, ntfs_disk_inode *store) -{ - int offset, i, size, psize, error, count, recno; - ntfs_attribute *attr; - unsigned char *rec; - - error = allocate_store(ino->vol, store, ino->record_count); - if (error) - return error; - size = ino->vol->mft_record_size; - count = i = 0; - do { - if (count < ino->record_count) { - recno = ino->records[count]; - } else { - error = allocate_store(ino->vol, store, count + 1); - if (error) - return error; - recno = -1; - } - /* - * FIXME: We need to support extension records properly. - * At the moment they wouldn't work. Probably would "just" get - * corrupted if we write to them... (AIA) - */ - store->records[count].recno = recno; - rec = store->records[count].record; - count++; - /* Copy mft record header. */ - offset = NTFS_GETU16(ino->attr + 0x14); /* attrs_offset */ - ntfs_memcpy(rec, ino->attr, offset); - /* Copy attributes. */ - while (i < ino->attr_count) { - attr = ino->attrs + i; - error = layout_attr(attr, rec + offset, - size - offset - 8, &psize); - if (error == -E2BIG && offset != NTFS_GETU16(ino->attr - + 0x14)) - break; - if (error) - return error; - offset += psize; - i++; - } - /* Terminating attribute. */ - NTFS_PUTU32(rec + offset, 0xFFFFFFFF); - offset += 4; - NTFS_PUTU32(rec + offset, 0); - offset += 4; - NTFS_PUTU32(rec + 0x18, offset); - } while (i < ino->attr_count || count < ino->record_count); - return count - ino->record_count; -} - -/* - * FIXME: ntfs_update_inode() calls layout_inode() to create the mft record on - * disk structure corresponding to the inode @ino. After that, ntfs_write_attr() - * is called to write out the created mft record to disk. - * We shouldn't need to re-layout every single time we are updating an mft - * record. No wonder the ntfs driver is slow like hell. (AIA) - */ -int ntfs_update_inode(ntfs_inode *ino) -{ - int error, i; - ntfs_disk_inode store; - ntfs_io io; - - ntfs_bzero(&store, sizeof(store)); - error = layout_inode(ino, &store); - if (error == -E2BIG) { - i = ntfs_split_indexroot(ino); - if (i != -ENOTDIR) { - if (!i) - i = layout_inode(ino, &store); - error = i; - } - } - if (error == -E2BIG) { - error = ntfs_attr_allnonresident(ino); - if (!error) - error = layout_inode(ino, &store); - } - if (error > 0) { - /* FIXME: Introduce extension records. */ - error = -E2BIG; - } - if (error) { - if (error == -E2BIG) - ntfs_error("Cannot handle saving inode 0x%x.\n", - ino->i_number); - deallocate_store(&store); - return error; - } - io.fn_get = ntfs_get; - io.fn_put = 0; - for (i = 0; i < store.count; i++) { - error = ntfs_insert_fixups(store.records[i].record, - ino->vol->mft_record_size); - if (error) { - printk(KERN_ALERT "NTFS: ntfs_update_inode() caught " - "corrupt %s mtf record ntfs record " - "header. Refusing to write corrupt " - "data to disk. Unmount and run chkdsk " - "immediately!\n", i ? "extension": - "base"); - deallocate_store(&store); - return -EIO; - } - io.param = store.records[i].record; - io.size = ino->vol->mft_record_size; - error = ntfs_write_attr(ino->vol->mft_ino, ino->vol->at_data, - 0, (__s64)store.records[i].recno << - ino->vol->mft_record_size_bits, &io); - if (error || io.size != ino->vol->mft_record_size) { - /* Big trouble, partially written file. */ - ntfs_error("Please unmount: Write error in inode " - "0x%x\n", ino->i_number); - deallocate_store(&store); - return error ? error : -EIO; - } - } - deallocate_store(&store); - return 0; -} - -void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l) -{ - int head, comp; - int copied = 0; - unsigned char *stop; - int bits; - int tag = 0; - int clear_pos; - - while (1) { - head = NTFS_GETU16(src) & 0xFFF; - /* High bit indicates that compression was performed. */ - comp = NTFS_GETU16(src) & 0x8000; - src += 2; - stop = src + head; - bits = 0; - clear_pos = 0; - if (head == 0) - /* Block is not used. */ - return;/* FIXME: copied */ - if (!comp) { /* uncompressible */ - ntfs_memcpy(dest, src, 0x1000); - dest += 0x1000; - copied += 0x1000; - src += 0x1000; - if (l == copied) - return; - continue; - } - while (src <= stop) { - if (clear_pos > 4096) { - ntfs_error("Error 1 in decompress\n"); - return; - } - if (!bits) { - tag = NTFS_GETU8(src); - bits = 8; - src++; - if (src > stop) - break; - } - if (tag & 1) { - int i, len, delta, code, lmask, dshift; - code = NTFS_GETU16(src); - src += 2; - if (!clear_pos) { - ntfs_error("Error 2 in decompress\n"); - return; - } - for (i = clear_pos - 1, lmask = 0xFFF, - dshift = 12; i >= 0x10; i >>= 1) { - lmask >>= 1; - dshift--; - } - delta = code >> dshift; - len = (code & lmask) + 3; - for (i = 0; i < len; i++) { - dest[clear_pos] = dest[clear_pos - - delta - 1]; - clear_pos++; - copied++; - if (copied==l) - return; - } - } else { - dest[clear_pos++] = NTFS_GETU8(src); - src++; - copied++; - if (copied==l) - return; - } - tag >>= 1; - bits--; - } - dest += clear_pos; - } -} - -/* - * NOTE: Neither of the ntfs_*_bit functions are atomic! But we don't need - * them atomic at present as we never operate on shared/cached bitmaps. - */ -static __inline__ int ntfs_test_bit(unsigned char *byte, const int bit) -{ - return byte[bit >> 3] & (1 << (bit & 7)) ? 1 : 0; -} - -static __inline__ void ntfs_set_bit(unsigned char *byte, const int bit) -{ - byte[bit >> 3] |= 1 << (bit & 7); -} - -static __inline__ void ntfs_clear_bit(unsigned char *byte, const int bit) -{ - byte[bit >> 3] &= ~(1 << (bit & 7)); -} - -static __inline__ int ntfs_test_and_clear_bit(unsigned char *byte, - const int bit) -{ - unsigned char *ptr = byte + (bit >> 3); - int b = 1 << (bit & 7); - int oldbit = *ptr & b ? 1 : 0; - *ptr &= ~b; - return oldbit; -} - -static void dump_runlist(const ntfs_runlist *rl, const int rlen) -{ -#ifdef DEBUG - int i; - ntfs_cluster_t ct; - - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): rlen = %i.\n", rlen); - ntfs_debug(DEBUG_OTHER, "VCN LCN Run length\n"); - for (i = 0, ct = 0; i < rlen; ct += rl[i++].len) { - if (rl[i].lcn == (ntfs_cluster_t)-1) - ntfs_debug(DEBUG_OTHER, "0x%-8x LCN_HOLE 0x%-8x " - "(%s)\n", ct, rl[i].len, rl[i].len ? - "sparse run" : "run list end"); - else - ntfs_debug(DEBUG_OTHER, "0x%-8x 0x%-8x 0x%-8x%s\n", ct, - rl[i].lcn, rl[i].len, rl[i].len && - i + 1 < rlen ? "" : " (run list end)"); - if (!rl[i].len) - break; - } -#endif -} - -/** - * splice_runlists - splice two run lists into one - * @rl1: pointer to address of first run list - * @r1len: number of elementfs in first run list - * @rl2: pointer to second run list - * @r2len: number of elements in second run list - * - * Append the run list @rl2 to the run list *@rl1 and return the result in - * *@rl1 and *@r1len. - * - * Return 0 on success or -errno on error, in which case *@rl1 and *@r1len are - * left untouched. - * - * The only possible error code at the moment is -ENOMEM and only happens if - * there is insufficient memory to allocate the new run list (only happens - * when size of (rl1 + rl2) > allocated size of rl1). - */ -int splice_runlists(ntfs_runlist **rl1, int *r1len, const ntfs_runlist *rl2, - int r2len) -{ - ntfs_runlist *rl; - int rlen, rl_size, rl2_pos; - - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Entering with *r1len = %i, " - "r2len = %i.\n", *r1len, r2len); - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Dumping 1st runlist.\n"); - if (*rl1) - dump_runlist(*rl1, *r1len); - else - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Not present.\n"); - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Dumping 2nd runlist.\n"); - dump_runlist(rl2, r2len); - rlen = *r1len + r2len + 1; - rl_size = (rlen * sizeof(ntfs_runlist) + PAGE_SIZE - 1) & - PAGE_MASK; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): rlen = %i, rl_size = %i.\n", - rlen, rl_size); - /* Do we have enough space? */ - if (rl_size <= ((*r1len * sizeof(ntfs_runlist) + PAGE_SIZE - 1) & - PAGE_MASK)) { - /* Have enough space already. */ - rl = *rl1; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Have enough space " - "already.\n"); - } else { - /* Need more space. Reallocate. */ - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Need more space.\n"); - rl = ntfs_vmalloc(rlen << sizeof(ntfs_runlist)); - if (!rl) - return -ENOMEM; - /* Copy over rl1. */ - ntfs_memcpy(rl, *rl1, *r1len * sizeof(ntfs_runlist)); - ntfs_vfree(*rl1); - *rl1 = rl; - } - /* Reuse rl_size as the current position index into rl. */ - rl_size = *r1len - 1; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): rl_size = %i.\n"); - /* Coalesce neighbouring elements, if present. */ - rl2_pos = 0; - if (rl[rl_size].lcn + rl[rl_size].len == rl2[rl2_pos].lcn) { - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Coalescing adjacent " - "runs.\n"); - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before: " - "rl[rl_size].len = %i.\n", rl[rl_size].len); - rl[rl_size].len += rl2[rl2_pos].len; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After: " - "rl[rl_size].len = %i.\n", rl[rl_size].len); - rl2_pos++; - r2len--; - rlen--; - } - rl_size++; - /* Copy over rl2. */ - ntfs_memcpy(rl + rl_size, rl2 + rl2_pos, r2len * sizeof(ntfs_runlist)); - rlen--; - rl[rlen].lcn = (ntfs_cluster_t)-1; - rl[rlen].len = (ntfs_cluster_t)0; - *r1len = rlen; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Dumping result runlist.\n"); - dump_runlist(*rl1, *r1len); - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Returning with *r1len = " - "%i.\n", rlen); - return 0; -} - -/** - * ntfs_alloc_mft_record - allocate an mft record - * @vol: volume to allocate an mft record on - * @result: the mft record number allocated - * - * Allocate a new mft record on disk. Return 0 on success or -ERRNO on error. - * On success, *@result contains the allocated mft record number. On error, - * *@result is -1UL. - * - * Note, this function doesn't actually set the mft record to be in use. This - * is done by the caller, which at the moment is only ntfs_alloc_inode(). - * - * To find a free mft record, we scan the mft bitmap for a zero bit. To - * optimize this we start scanning at the place where we last stopped and we - * perform wrap around when we reach the end. Note, we do not try to allocate - * mft records below number 24 because numbers 0 to 15 are the defined system - * files anyway and 16 to 24 are special in that they are used for storing - * extension mft records for $MFT's $DATA attribute. This is required to avoid - * the possibility of creating a run list with a circular dependence which once - * written to disk can never be read in again. Windows will only use records - * 16 to 24 for normal files if the volume is completely out of space. We never - * use them which means that when the volume is really out of space we cannot - * create any more files while Windows can still create up to 8 small files. We - * can start doing this at some later time, doesn't matter much for now. - * - * When scanning the mft bitmap, we only search up to the last allocated mft - * record. If there are no free records left in the range 24 to number of - * allocated mft records, then we extend the mft data in order to create free - * mft records. We extend the allocated size of $MFT/$DATA by 16 records at a - * time or one cluster, if cluster size is above 16kiB. If there isn't - * sufficient space to do this, we try to extend by a single mft record or one - * cluster, if cluster size is above mft record size, but we only do this if - * there is enough free space, which we know from the values returned by the - * failed cluster allocation function when we tried to do the first allocation. - * - * No matter how many mft records we allocate, we initialize only the first - * allocated mft record (incrementing mft data size and initialized size) and - * return its number to the caller in @*result, unless there are less than 24 - * mft records, in which case we allocate and initialize mft records until we - * reach record 24 which we consider as the first free mft record for use by - * normal files. - * - * If during any stage we overflow the initialized data in the mft bitmap, we - * extend the initialized size (and data size) by 8 bytes, allocating another - * cluster if required. The bitmap data size has to be at least equal to the - * number of mft records in the mft, but it can be bigger, in which case the - * superflous bits are padded with zeroes. - * - * Thus, when we return successfully (return value 0), we will have: - * - initialized / extended the mft bitmap if necessary, - * - initialized / extended the mft data if necessary, - * - set the bit corresponding to the mft record being allocated in the - * mft bitmap, and we will - * - return the mft record number in @*result. - * - * On error (return value below zero), nothing will have changed. If we had - * changed anything before the error occured, we will have reverted back to - * the starting state before returning to the caller. Thus, except for bugs, - * we should always leave the volume in a consitents state when returning from - * this function. NOTE: Small exception to this is that we set the bit in the - * mft bitmap but we do not mark the mft record in use, which is inconsistent. - * However, the caller will immediately add the wanted attributes to the mft - * record, set it in use and write it out to disk, so there should be no - * problem. - * - * Note, this function cannot make use of most of the normal functions, like - * for example for attribute resizing, etc, because when the run list overflows - * the base mft record and an attribute list is used, it is very important - * that the extension mft records used to store the $DATA attribute of $MFT - * can be reached without having to read the information contained inside - * them, as this would make it impossible to find them in the first place - * after the volume is dismounted. $MFT/$BITMAP probably doesn't need to - * follow this rule because the bitmap is not essential for finding the mft - * records, but on the other hand, handling the bitmap in this special way - * would make life easier because otherwise there might be circular invocations - * of functions when reading the bitmap but if we are careful, we should be - * able to avoid all problems. - * - * FIXME: Don't forget $MftMirr, though this probably belongs in - * ntfs_update_inode() (or even deeper). (AIA) - * - * FIXME: Want finer grained locking. (AIA) - */ -static int ntfs_alloc_mft_record(ntfs_volume *vol, unsigned long *result) -{ - unsigned long nr_mft_records, buf_size, buf_pos, pass_start, pass_end; - unsigned long last_read_pos, mft_rec_size, bit, l; - ntfs_attribute *data, *bmp; - __u8 *buf, *byte, pass, b, have_allocated_mftbmp = 0; - int rlen, rl_size = 0, r2len, rl2_size, old_data_rlen, err = 0; - ntfs_runlist *rl, *rl2; - ntfs_cluster_t lcn = 0, old_data_len; - ntfs_io io; - __s64 ll, old_data_allocated, old_data_initialized, old_data_size; - - *result = -1UL; - /* Allocate a buffer and setup the io structure. */ - buf = (__u8*)__get_free_page(GFP_NOFS); - if (!buf) - return -ENOMEM; - lock_kernel(); - /* Get the $DATA and $BITMAP attributes of $MFT. */ - data = ntfs_find_attr(vol->mft_ino, vol->at_data, 0); - bmp = ntfs_find_attr(vol->mft_ino, vol->at_bitmap, 0); - if (!data || !bmp) { - err = -EINVAL; - goto err_ret; - } - /* Determine the number of allocated mft records in the mft. */ - pass_end = nr_mft_records = data->allocated >> - vol->mft_record_size_bits; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): nr_mft_records = %lu.\n", - nr_mft_records); - /* Make sure we don't overflow the bitmap. */ - l = bmp->initialized << 3; - if (l < nr_mft_records) - // FIXME: It might be a good idea to extend the bitmap instead. - pass_end = l; - pass = 1; - buf_pos = vol->mft_data_pos; - if (buf_pos >= pass_end) { - buf_pos = 24UL; - pass = 2; - } - pass_start = buf_pos; - rl = bmp->d.r.runlist; - rlen = bmp->d.r.len - 1; - lcn = rl[rlen].lcn + rl[rlen].len; - io.fn_put = ntfs_put; - io.fn_get = ntfs_get; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Starting bitmap search.\n"); - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): pass = %i, pass_start = %lu, " - "pass_end = %lu.\n", pass, pass_start, pass_end); - byte = NULL; // FIXME: For debugging only. - /* Loop until a free mft record is found. */ - io.size = (nr_mft_records >> 3) & ~PAGE_MASK; - for (;; io.size = PAGE_SIZE) { - io.param = buf; - io.do_read = 1; - last_read_pos = buf_pos >> 3; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before: " - "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " - "bmp->initialized = 0x%Lx.\n", bmp->allocated, - bmp->size, bmp->initialized); - err = ntfs_readwrite_attr(vol->mft_ino, bmp, last_read_pos, - &io); - if (err) - goto err_ret; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Read %lu bytes.\n", - (unsigned long)io.size); - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After: " - "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " - "bmp->initialized = 0x%Lx.\n", bmp->allocated, - bmp->size, bmp->initialized); - if (!io.size) - goto pass_done; - buf_size = io.size << 3; - bit = buf_pos & 7UL; - buf_pos &= ~7UL; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before loop: " - "buf_size = %lu, buf_pos = %lu, bit = %lu, " - "*byte = 0x%x, b = %u.\n", - buf_size, buf_pos, bit, byte ? *byte : -1, b); - for (; bit < buf_size && bit + buf_pos < pass_end; - bit &= ~7UL, bit += 8UL) { - byte = buf + (bit >> 3); - if (*byte == 0xff) - continue; - b = ffz((unsigned long)*byte); - if (b < (__u8)8 && b >= (bit & 7UL)) { - bit = b + (bit & ~7UL) + buf_pos; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): " - "Found free rec in for loop. " - "bit = %lu\n", bit); - goto found_free_rec; - } - } - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After loop: " - "buf_size = %lu, buf_pos = %lu, bit = %lu, " - "*byte = 0x%x, b = %u.\n", - buf_size, buf_pos, bit, byte ? *byte : -1, b); - buf_pos += buf_size; - if (buf_pos < pass_end) - continue; -pass_done: /* Finished with the current pass. */ - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At pass_done.\n"); - if (pass == 1) { - /* - * Now do pass 2, scanning the first part of the zone - * we omitted in pass 1. - */ - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Done pass " - "1.\n"); - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Pass = 2.\n"); - pass = 2; - pass_end = pass_start; - buf_pos = pass_start = 24UL; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): pass = %i, " - "pass_start = %lu, pass_end = %lu.\n", - pass, pass_start, pass_end); - continue; - } /* pass == 2 */ - /* No free records left. */ - if (bmp->initialized << 3 > nr_mft_records && - bmp->initialized > 3) { - /* - * The mft bitmap is already bigger but the space is - * not covered by mft records, this implies that the - * next records are all free, so we already have found - * a free record. - */ - bit = nr_mft_records; - if (bit < 24UL) - bit = 24UL; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Found free " - "record bit (#1) = 0x%lx.\n", bit); - goto found_free_rec; - } - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Done pass 2.\n"); - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before: " - "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " - "bmp->initialized = 0x%Lx.\n", bmp->allocated, - bmp->size, bmp->initialized); - /* Need to extend the mft bitmap. */ - if (bmp->initialized + 8LL > bmp->allocated) { - ntfs_io io2; - - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Initialized " - "> allocated.\n"); - /* Need to extend bitmap by one more cluster. */ - rl = bmp->d.r.runlist; - rlen = bmp->d.r.len - 1; - lcn = rl[rlen].lcn + rl[rlen].len; - io2.fn_put = ntfs_put; - io2.fn_get = ntfs_get; - io2.param = &b; - io2.size = 1; - io2.do_read = 1; - err = ntfs_readwrite_attr(vol->bitmap, data, lcn >> 3, - &io2); - if (err) - goto err_ret; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Read %lu " - "bytes.\n", (unsigned long)io2.size); - if (io2.size == 1 && b != 0xff) { - __u8 tb = 1 << (lcn & (ntfs_cluster_t)7); - if (!(b & tb)) { - /* Next cluster is free. Allocate it. */ - b |= tb; - io2.param = &b; - io2.do_read = 0; - err = ntfs_readwrite_attr(vol->bitmap, - data, lcn >> 3, &io2); - if (err || io.size != 1) { - if (!err) - err = -EIO; - goto err_ret; - } -append_mftbmp_simple: rl[rlen].len++; - have_allocated_mftbmp |= 1; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ - "(): Appending one " - "cluster to mftbmp.\n"); - } - } - if (!have_allocated_mftbmp) { - /* Allocate a cluster from the DATA_ZONE. */ - ntfs_cluster_t lcn2 = lcn; - ntfs_cluster_t count = 1; - err = ntfs_allocate_clusters(vol, &lcn2, - &count, &rl2, &r2len, - DATA_ZONE); - if (err) - goto err_ret; - if (count != 1 || lcn2 <= 0) { - if (count > 0) { -rl2_dealloc_err_out: if (ntfs_deallocate_clusters( - vol, rl2, r2len)) - ntfs_error(__FUNCTION__ - "(): Cluster " - "deallocation in error " - "code path failed! You " - "should run chkdsk.\n"); - } - ntfs_vfree(rl2); - if (!err) - err = -EINVAL; - goto err_ret; - } - if (lcn2 == lcn) { - ntfs_vfree(rl2); - goto append_mftbmp_simple; - } - /* We need to append a new run. */ - rl_size = (rlen * sizeof(ntfs_runlist) + - PAGE_SIZE - 1) & PAGE_MASK; - /* Reallocate memory if necessary. */ - if ((rlen + 2) * sizeof(ntfs_runlist) >= - rl_size) { - ntfs_runlist *rlt; - - rl_size += PAGE_SIZE; - rlt = ntfs_vmalloc(rl_size); - if (!rlt) { - err = -ENOMEM; - goto rl2_dealloc_err_out; - } - ntfs_memcpy(rlt, rl, rl_size - - PAGE_SIZE); - ntfs_vfree(rl); - bmp->d.r.runlist = rl = rlt; - } - ntfs_vfree(rl2); - rl[rlen].lcn = lcn = lcn2; - rl[rlen].len = count; - bmp->d.r.len = ++rlen; - have_allocated_mftbmp |= 2; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): " - "Adding run to mftbmp. " - "LCN = %i, len = %i\n", lcn, - count); - } - /* - * We now have extended the mft bitmap allocated size - * by one cluster. Reflect this in the attribute. - */ - bmp->allocated += (__s64)vol->cluster_size; - } - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After: " - "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " - "bmp->initialized = 0x%Lx.\n", bmp->allocated, - bmp->size, bmp->initialized); - /* We now have sufficient allocated space. */ - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Now have sufficient " - "allocated space in mftbmp.\n"); - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before: " - "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " - "bmp->initialized = 0x%Lx.\n", bmp->allocated, - bmp->size, bmp->initialized); - buf_pos = bmp->initialized; - bmp->initialized += 8LL; - if (bmp->initialized > bmp->size) - bmp->size = bmp->initialized; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After: " - "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " - "bmp->initialized = 0x%Lx.\n", bmp->allocated, - bmp->size, bmp->initialized); - have_allocated_mftbmp |= 4; - /* Update the mft bitmap attribute value. */ - memset(buf, 0, 8); - io.param = buf; - io.size = 8; - io.do_read = 0; - err = ntfs_readwrite_attr(vol->mft_ino, bmp, buf_pos, &io); - if (err || io.size != 8) { - if (!err) - err = -EIO; - goto shrink_mftbmp_err_ret; - } - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Wrote extended " - "mftbmp bytes %lu.\n", (unsigned long)io.size); - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After write: " - "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " - "bmp->initialized = 0x%Lx.\n", bmp->allocated, - bmp->size, bmp->initialized); - bit = buf_pos << 3; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Found free record " - "bit (#2) = 0x%lx.\n", bit); - goto found_free_rec; - } -found_free_rec: - /* bit is the found free mft record. Allocate it in the mft bitmap. */ - vol->mft_data_pos = bit; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At found_free_rec.\n"); - io.param = buf; - io.size = 1; - io.do_read = 1; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before update: " - "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " - "bmp->initialized = 0x%Lx.\n", bmp->allocated, - bmp->size, bmp->initialized); - err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io); - if (err || io.size != 1) { - if (!err) - err = -EIO; - goto shrink_mftbmp_err_ret; - } - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Read %lu bytes.\n", - (unsigned long)io.size); -#ifdef DEBUG - /* Check our bit is really zero! */ - if (*buf & (1 << (bit & 7))) - BUG(); -#endif - *buf |= 1 << (bit & 7); - io.param = buf; - io.do_read = 0; - err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io); - if (err || io.size != 1) { - if (!err) - err = -EIO; - goto shrink_mftbmp_err_ret; - } - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Wrote %lu bytes.\n", - (unsigned long)io.size); - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After update: " - "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " - "bmp->initialized = 0x%Lx.\n", bmp->allocated, - bmp->size, bmp->initialized); - /* The mft bitmap is now uptodate. Deal with mft data attribute now. */ - ll = (__s64)(bit + 1) << vol->mft_record_size_bits; - if (ll <= data->initialized) { - /* The allocated record is already initialized. We are done! */ - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Allocated mft record " - "already initialized!\n"); - goto done_ret; - } - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Allocated mft record needs " - "to be initialized.\n"); - /* The mft record is outside the initialized data. */ - mft_rec_size = (unsigned long)vol->mft_record_size; - /* Preserve old values for undo purposes. */ - old_data_allocated = data->allocated; - old_data_rlen = data->d.r.len - 1; - old_data_len = data->d.r.runlist[old_data_rlen].len; - /* - * If necessary, extend the mft until it covers the allocated record. - * The loop is only actually used when a freshly formatted volume is - * first written to. But it optimizes away nicely in the common case. - */ - while (ll > data->allocated) { - ntfs_cluster_t lcn2, nr_lcn2, nr, min_nr; - - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Extending mft " - "data allocation, data->allocated = 0x%Lx, " - "data->size = 0x%Lx, data->initialized = " - "0x%Lx.\n", data->allocated, data->size, - data->initialized); - /* Minimum allocation is one mft record worth of clusters. */ - if (mft_rec_size <= vol->cluster_size) - min_nr = (ntfs_cluster_t)1; - else - min_nr = mft_rec_size >> vol->cluster_size_bits; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): min_nr = %i.\n", - min_nr); - /* Allocate 16 mft records worth of clusters. */ - nr = mft_rec_size << 4 >> vol->cluster_size_bits; - if (!nr) - nr = (ntfs_cluster_t)1; - /* Determine the preferred allocation location. */ - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): nr = %i.\n", nr); - rl2 = data->d.r.runlist; - r2len = data->d.r.len; - lcn2 = rl2[r2len - 1].lcn + rl2[r2len - 1].len; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): rl2[r2len - 1].lcn " - "= %i, .len = %i.\n", rl2[r2len - 1].lcn, - rl2[r2len - 1].len); - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): lcn2 = %i, r2len = " - "%i.\n", lcn2, r2len); -retry_mft_data_allocation: - nr_lcn2 = nr; - err = ntfs_allocate_clusters(vol, &lcn2, &nr_lcn2, &rl2, - &r2len, MFT_ZONE); -#ifdef DEBUG - if (!err && nr_lcn2 < min_nr) - /* Allocated less than minimum needed. Weird! */ - BUG(); -#endif - if (err) { - /* - * If there isn't enough space to do the wanted - * allocation, but there is enough space to do a - * minimal allocation, then try that, unless the wanted - * allocation was already the minimal allocation. - */ - if (err == -ENOSPC && nr > min_nr && - nr_lcn2 >= min_nr) { - nr = min_nr; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): " - "Retrying mft data " - "allocation, nr = min_nr = %i" - ".\n", nr); - goto retry_mft_data_allocation; - } - goto undo_mftbmp_alloc_err_ret; - } - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Allocated %i " - "clusters starting at LCN %i.\n", nr_lcn2, - lcn2); - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Allocated " - "runlist:\n"); - dump_runlist(rl2, r2len); - /* Append rl2 to the mft data attribute's run list. */ - err = splice_runlists(&data->d.r.runlist, (int*)&data->d.r.len, - rl2, r2len); - if (err) { - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): " - "splice_runlists failed with error " - "code %i.\n", -err); - goto undo_partial_data_alloc_err_ret; - } - /* Reflect the allocated clusters in the mft allocated data. */ - data->allocated += nr_lcn2 << vol->cluster_size_bits; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After extending mft " - "data allocation, data->allocated = 0x%Lx, " - "data->size = 0x%Lx, data->initialized = " - "0x%Lx.\n", data->allocated, data->size, - data->initialized); - } - /* Prepare a formatted (empty) mft record. */ - memset(buf, 0, mft_rec_size); - ntfs_fill_mft_header(buf, mft_rec_size, 0, 0, 0); - err = ntfs_insert_fixups(buf, mft_rec_size); - if (err) - goto undo_data_alloc_err_ret; - /* - * Extend mft data initialized size to reach the allocated mft record - * and write the formatted mft record buffer to each mft record being - * initialized. Note, that ntfs_readwrite_attr extends both - * data->initialized and data->size, so no need for us to touch them. - */ - old_data_initialized = data->initialized; - old_data_size = data->size; - while (ll > data->initialized) { - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Initializing mft " - "record 0x%Lx.\n", - data->initialized >> vol->mft_record_size_bits); - io.param = buf; - io.size = mft_rec_size; - io.do_read = 0; - err = ntfs_readwrite_attr(vol->mft_ino, data, - data->initialized, &io); - if (err || io.size != mft_rec_size) { - if (!err) - err = -EIO; - goto undo_data_init_err_ret; - } - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Wrote %i bytes to " - "mft data.\n", io.size); - } - /* Update the VFS inode size as well. */ - VFS_I(vol->mft_ino)->i_size = data->size; -#ifdef DEBUG - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After mft record " - "initialization: data->allocated = 0x%Lx, data->size " - "= 0x%Lx, data->initialized = 0x%Lx.\n", - data->allocated, data->size, data->initialized); - /* Sanity checks. */ - if (data->size > data->allocated || data->size < data->initialized || - data->initialized > data->allocated) - BUG(); -#endif -done_ret: - /* Return the number of the allocated mft record. */ - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At done_ret. *result = bit = " - "0x%lx.\n", bit); - *result = bit; - vol->mft_data_pos = bit + 1; -err_ret: - unlock_kernel(); - free_page((unsigned long)buf); - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Syncing inode $MFT.\n"); - if (ntfs_update_inode(vol->mft_ino)) - ntfs_error(__FUNCTION__ "(): Failed to sync inode $MFT. " - "Continuing anyway.\n"); - if (!err) { - ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): Done. Allocated mft " - "record number *result = 0x%lx.\n", *result); - return 0; - } - if (err != -ENOSPC) - ntfs_error(__FUNCTION__ "(): Failed to allocate an mft " - "record. Returning error code %i.\n", -err); - else - ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): Failed to allocate " - "an mft record due to lack of free space.\n"); - return err; -undo_data_init_err_ret: - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At " - "undo_data_init_err_ret.\n"); - data->initialized = old_data_initialized; - data->size = old_data_size; -undo_data_alloc_err_ret: - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At undo_data_alloc_err_ret." - "\n"); - data->allocated = old_data_allocated; -undo_partial_data_alloc_err_ret: - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At " - "undo_partial_data_alloc_err_ret.\n"); - /* Deallocate the clusters. */ - if (ntfs_deallocate_clusters(vol, rl2, r2len)) - ntfs_error(__FUNCTION__ "(): Error deallocating clusters in " - "error code path. You should run chkdsk.\n"); - ntfs_vfree(rl2); - /* Revert the run list back to what it was before. */ - r2len = data->d.r.len; - rl2 = data->d.r.runlist; - rl2[old_data_rlen++].len = old_data_len; - rl2[old_data_rlen].lcn = (ntfs_cluster_t)-1; - rl2[old_data_rlen].len = (ntfs_cluster_t)0; - data->d.r.len = old_data_rlen; - rl2_size = ((old_data_rlen + 1) * sizeof(ntfs_runlist) + PAGE_SIZE - - 1) & PAGE_MASK; - /* Reallocate memory freeing any extra memory allocated. */ - if (rl2_size < ((r2len * sizeof(ntfs_runlist) + PAGE_SIZE - 1) & - PAGE_MASK)) { - rl2 = ntfs_vmalloc(rl2_size); - if (rl2) { - ntfs_memcpy(rl2, data->d.r.runlist, rl2_size); - ntfs_vfree(data->d.r.runlist); - data->d.r.runlist = rl2; - } else - ntfs_error(__FUNCTION__ "(): Error reallocating " - "memory in error code path. This " - "should be harmless.\n"); - } -undo_mftbmp_alloc_err_ret: - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At " - "undo_mftbmp_alloc_err_ret.\n"); - /* Deallocate the allocated bit in the mft bitmap. */ - io.param = buf; - io.size = 1; - io.do_read = 1; - err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io); - if (!err && io.size == 1) { - *buf &= ~(1 << (bit & 7)); - io.param = buf; - io.do_read = 0; - err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io); - } - if (err || io.size != 1) { - if (!err) - err = -EIO; - ntfs_error(__FUNCTION__ "(): Error deallocating mft record in " - "error code path. You should run chkdsk.\n"); - } -shrink_mftbmp_err_ret: - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At shrink_mftbmp_err_ret.\n"); - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): have_allocated_mftbmp = " - "%i.\n", have_allocated_mftbmp); - if (!have_allocated_mftbmp) - goto err_ret; - /* Shrink the mftbmp back to previous size. */ - if (bmp->size == bmp->initialized) - bmp->size -= 8LL; - bmp->initialized -= 8LL; - have_allocated_mftbmp &= ~4; - /* If no allocation occured then we are done. */ - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): have_allocated_mftbmp = " - "%i.\n", have_allocated_mftbmp); - if (!have_allocated_mftbmp) - goto err_ret; - /* Deallocate the allocated cluster. */ - bmp->allocated -= (__s64)vol->cluster_size; - if (ntfs_deallocate_cluster_run(vol, lcn, (ntfs_cluster_t)1)) - ntfs_error(__FUNCTION__ "(): Error deallocating cluster in " - "error code path. You should run chkdsk.\n"); - switch (have_allocated_mftbmp & 3) { - case 1: - /* Delete the last lcn from the last run of mftbmp. */ - rl[rlen - 1].len--; - break; - case 2: - /* Delete the last run of mftbmp. */ - bmp->d.r.len = --rlen; - /* Reallocate memory if necessary. */ - if ((rlen + 1) * sizeof(ntfs_runlist) <= rl_size - PAGE_SIZE) { - ntfs_runlist *rlt; - - rl_size -= PAGE_SIZE; - rlt = ntfs_vmalloc(rl_size); - if (rlt) { - ntfs_memcpy(rlt, rl, rl_size); - ntfs_vfree(rl); - bmp->d.r.runlist = rl = rlt; - } else - ntfs_error(__FUNCTION__ "(): Error " - "reallocating memory in error " - "code path. This should be " - "harmless.\n"); - } - bmp->d.r.runlist[bmp->d.r.len].lcn = (ntfs_cluster_t)-1; - bmp->d.r.runlist[bmp->d.r.len].len = (ntfs_cluster_t)0; - break; - default: - BUG(); - } - goto err_ret; -} - -/* We need 0x48 bytes in total. */ -static int add_standard_information(ntfs_inode *ino) -{ - ntfs_time64_t now; - char data[0x30]; - char *position = data; - ntfs_attribute *si; - - now = ntfs_now(); - NTFS_PUTU64(position + 0x00, now); /* File creation */ - NTFS_PUTU64(position + 0x08, now); /* Last modification */ - NTFS_PUTU64(position + 0x10, now); /* Last mod for MFT */ - NTFS_PUTU64(position + 0x18, now); /* Last access */ - NTFS_PUTU64(position + 0x20, 0); /* MSDOS file perms */ - NTFS_PUTU64(position + 0x28, 0); /* unknown */ - return ntfs_create_attr(ino, ino->vol->at_standard_information, 0, - data, sizeof(data), &si); -} - -static int add_filename(ntfs_inode *ino, ntfs_inode *dir, - const unsigned char *filename, int length, ntfs_u32 flags) -{ - unsigned char *position; - unsigned int size; - ntfs_time64_t now; - int count, error; - unsigned char* data; - ntfs_attribute *fn; - - /* Work out the size. */ - size = 0x42 + 2 * length; - data = ntfs_malloc(size); - if (!data) - return -ENOMEM; - /* Search for a position. */ - position = data; - NTFS_PUTINUM(position, dir); /* Inode num of dir */ - now = ntfs_now(); - NTFS_PUTU64(position + 0x08, now); /* File creation */ - NTFS_PUTU64(position + 0x10, now); /* Last modification */ - NTFS_PUTU64(position + 0x18, now); /* Last mod for MFT */ - NTFS_PUTU64(position + 0x20, now); /* Last access */ - /* FIXME: Get the following two sizes by finding the data attribute - * in ino->attr and copying the corresponding fields from there. - * If no data present then set to zero. In current implementation - * add_data is called after add_filename so zero is correct on - * creation. Need to change when we have hard links / support different - * filename namespaces. (AIA) */ - NTFS_PUTS64(position + 0x28, 0); /* Allocated size */ - NTFS_PUTS64(position + 0x30, 0); /* Data size */ - NTFS_PUTU32(position + 0x38, flags); /* File flags */ - NTFS_PUTU32(position + 0x3c, 0); /* We don't use these - * features yet. */ - NTFS_PUTU8(position + 0x40, length); /* Filename length */ - NTFS_PUTU8(position + 0x41, 0); /* Only long name */ - /* FIXME: This is madness. We are defining the POSIX namespace - * for the filename here which can mean that the file will be - * invisible when in Windows NT/2k! )-: (AIA) */ - position += 0x42; - for (count = 0; count < length; count++) { - NTFS_PUTU16(position + 2 * count, filename[count]); - } - error = ntfs_create_attr(ino, ino->vol->at_file_name, 0, data, size, - &fn); - if (!error) - error = ntfs_dir_add(dir, ino, fn); - ntfs_free(data); - return error; -} - -int add_security(ntfs_inode* ino, ntfs_inode* dir) -{ - int error; - char *buf; - int size; - ntfs_attribute* attr; - ntfs_io io; - ntfs_attribute *se; - - attr = ntfs_find_attr(dir, ino->vol->at_security_descriptor, 0); - if (!attr) - return -EOPNOTSUPP; /* Need security in directory. */ - size = attr->size; - if (size > 512) - return -EOPNOTSUPP; - buf = ntfs_malloc(size); - if (!buf) - return -ENOMEM; - io.fn_get = ntfs_get; - io.fn_put = ntfs_put; - io.param = buf; - io.size = size; - error = ntfs_read_attr(dir, ino->vol->at_security_descriptor, 0, 0,&io); - if (!error && io.size != size) - ntfs_error("wrong size in add_security\n"); - if (error) { - ntfs_free(buf); - return error; - } - /* FIXME: Consider ACL inheritance. */ - error = ntfs_create_attr(ino, ino->vol->at_security_descriptor, - 0, buf, size, &se); - ntfs_free(buf); - return error; -} - -static int add_data(ntfs_inode* ino, unsigned char *data, int length) -{ - ntfs_attribute *da; - - return ntfs_create_attr(ino, ino->vol->at_data, 0, data, length, &da); -} - -/* - * We _could_ use 'dir' to help optimise inode allocation. - * - * FIXME: Need to undo what we do in ntfs_alloc_mft_record if we get an error - * further on in ntfs_alloc_inode. Either fold the two functions to allow - * proper undo or just deallocate the record from the mft bitmap. (AIA) - */ -int ntfs_alloc_inode(ntfs_inode *dir, ntfs_inode *result, const char *filename, - int namelen, ntfs_u32 flags) -{ - ntfs_volume *vol = dir->vol; - int err; - ntfs_u8 buffer[2]; - ntfs_io io; - - err = ntfs_alloc_mft_record(vol, &(result->i_number)); - if (err) { - if (err == -ENOSPC) - ntfs_error(__FUNCTION__ "(): No free inodes.\n"); - return err; - } - /* Get the sequence number. */ - io.fn_put = ntfs_put; - io.fn_get = ntfs_get; - io.param = buffer; - io.size = 2; - err = ntfs_read_attr(vol->mft_ino, vol->at_data, 0, - ((__s64)result->i_number << vol->mft_record_size_bits) - + 0x10, &io); - // FIXME: We are leaving the MFT in inconsistent state! (AIA) - if (err) - return err; - /* Increment the sequence number skipping zero. */ - result->sequence_number = (NTFS_GETU16(buffer) + 1) & 0xffff; - if (!result->sequence_number) - result->sequence_number++; - result->vol = vol; - result->attr_count = 0; - result->attrs = 0; - result->record_count = 1; - result->records = ntfs_calloc(8 * sizeof(int)); - if (!result->records) - goto mem_err_out; - result->records[0] = result->i_number; - result->attr = ntfs_calloc(vol->mft_record_size); - if (!result->attr) { - ntfs_free(result->records); - result->records = NULL; - goto mem_err_out; - } - ntfs_fill_mft_header(result->attr, vol->mft_record_size, - result->sequence_number, 1, 1); - err = add_standard_information(result); - if (!err) - err = add_filename(result, dir, filename, namelen, flags); - if (!err) - err = add_security(result, dir); - // FIXME: We are leaving the MFT in inconsistent state on error! (AIA) - return err; -mem_err_out: - // FIXME: We are leaving the MFT in inconsistent state! (AIA) - result->record_count = 0; - result->attr = NULL; - return -ENOMEM; -} - -int ntfs_alloc_file(ntfs_inode *dir, ntfs_inode *result, char *filename, - int namelen) -{ - int err; - - err = ntfs_alloc_inode(dir, result, filename, namelen, 0); - if (!err) - err = add_data(result, 0, 0); - return err; -} - +/* + * inode.c + * + * Copyright (C) 1995-1999 Martin von Löwis + * Copyright (C) 1996 Albert D. Cahalan + * Copyright (C) 1996-1997 Régis Duchesne + * Copyright (C) 1998 Joseph Malicki + * Copyright (C) 1999 Steve Dodd + * Copyright (C) 2000-2001 Anton Altaparmakov (AIA) + */ +#include "ntfstypes.h" +#include "ntfsendian.h" +#include "struct.h" +#include "inode.h" +#include +#include "macros.h" +#include "attr.h" +#include "super.h" +#include "dir.h" +#include "support.h" +#include "util.h" +#include +#include + +typedef struct { + int recno; + unsigned char *record; +} ntfs_mft_record; + +typedef struct { + int size; + int count; + ntfs_mft_record *records; +} ntfs_disk_inode; + +static void ntfs_fill_mft_header(ntfs_u8 *mft, int rec_size, int seq_no, + int links, int flags) +{ + int fixup_ofs = 0x2a; + int fixup_cnt = rec_size / NTFS_SECTOR_SIZE + 1; + int attr_ofs = (fixup_ofs + 2 * fixup_cnt + 7) & ~7; + + NTFS_PUTU32(mft + 0x00, 0x454c4946); /* FILE */ + NTFS_PUTU16(mft + 0x04, fixup_ofs); /* Offset to fixup. */ + NTFS_PUTU16(mft + 0x06, fixup_cnt); /* Number of fixups. */ + NTFS_PUTU64(mft + 0x08, 0); /* Logical sequence number. */ + NTFS_PUTU16(mft + 0x10, seq_no); /* Sequence number. */ + NTFS_PUTU16(mft + 0x12, links); /* Hard link count. */ + NTFS_PUTU16(mft + 0x14, attr_ofs); /* Offset to attributes. */ + NTFS_PUTU16(mft + 0x16, flags); /* Flags: 1 = In use, + 2 = Directory. */ + NTFS_PUTU32(mft + 0x18, attr_ofs + 8); /* Bytes in use. */ + NTFS_PUTU32(mft + 0x1c, rec_size); /* Total allocated size. */ + NTFS_PUTU64(mft + 0x20, 0); /* Base mft record. */ + NTFS_PUTU16(mft + 0x28, 0); /* Next attr instance. */ + NTFS_PUTU16(mft + fixup_ofs, 1); /* Fixup word. */ + NTFS_PUTU32(mft + attr_ofs, (__u32)-1); /* End of attributes marker. */ +} + +/* + * Search in an inode an attribute by type and name. + * FIXME: Check that when attributes are inserted all attribute list + * attributes are expanded otherwise need to modify this function to deal + * with attribute lists. (AIA) + */ +ntfs_attribute *ntfs_find_attr(ntfs_inode *ino, int type, char *name) +{ + int i; + + if (!ino) { + ntfs_error("ntfs_find_attr: NO INODE!\n"); + return 0; + } + for (i = 0; i < ino->attr_count; i++) { + if (type < ino->attrs[i].type) + return 0; + if (type == ino->attrs[i].type) { + if (!name) { + if (!ino->attrs[i].name) + return ino->attrs + i; + } else if (ino->attrs[i].name && + !ntfs_ua_strncmp(ino->attrs[i].name, name, + strlen(name))) + return ino->attrs + i; + } + } + return 0; +} + +/* + * Insert all attributes from the record mftno of the MFT in the inode ino. + * If mftno is a base mft record we abort as soon as we find the attribute + * list, but only on the first pass. We will get called later when the attribute + * list attribute is being parsed so we need to distinguish the two cases. + * FIXME: We should be performing structural consistency checks. (AIA) + * Return 0 on success or -errno on error. + */ +static int ntfs_insert_mft_attributes(ntfs_inode* ino, char *mft, int mftno) +{ + int i, error, type, len, present = 0; + char *it; + + /* Check for duplicate extension record. */ + for(i = 0; i < ino->record_count; i++) + if (ino->records[i] == mftno) { + if (i) + return 0; + present = 1; + break; + } + if (!present) { + /* (re-)allocate space if necessary. */ + if (ino->record_count % 8 == 0) { + int *new; + + new = ntfs_malloc((ino->record_count + 8) * + sizeof(int)); + if (!new) + return -ENOMEM; + if (ino->records) { + for (i = 0; i < ino->record_count; i++) + new[i] = ino->records[i]; + ntfs_free(ino->records); + } + ino->records = new; + } + ino->records[ino->record_count] = mftno; + ino->record_count++; + } + it = mft + NTFS_GETU16(mft + 0x14); /* mft->attrs_offset */ + do { + type = NTFS_GETU32(it); + len = NTFS_GETU32(it + 4); + if (type != -1) { + error = ntfs_insert_attribute(ino, it); + if (error) + return error; + } + /* If we have just processed the attribute list and this is + * the first time we are parsing this (base) mft record then we + * are done so that the attribute list gets parsed before the + * entries in the base mft record. Otherwise we run into + * problems with encountering attributes out of order and when + * this happens with different attribute extents we die. )-: + * This way we are ok as the attribute list is always sorted + * fully and correctly. (-: */ + if (type == 0x20 && !present) + return 0; + it += len; + } while (type != -1); /* Attribute listing ends with type -1. */ + return 0; +} + +/* + * Insert a single specific attribute from the record mftno of the MFT in the + * inode ino. We disregard the attribute list assuming we have already parsed + * it. + * FIXME: We should be performing structural consistency checks. (AIA) + * Return 0 on success or -errno on error. + */ +static int ntfs_insert_mft_attribute(ntfs_inode* ino, int mftno, + ntfs_u8 *attr) +{ + int i, error, present = 0; + + /* Check for duplicate extension record. */ + for(i = 0; i < ino->record_count; i++) + if (ino->records[i] == mftno) { + present = 1; + break; + } + if (!present) { + /* (re-)allocate space if necessary. */ + if (ino->record_count % 8 == 0) { + int *new; + + new = ntfs_malloc((ino->record_count + 8) * + sizeof(int)); + if (!new) + return -ENOMEM; + if (ino->records) { + for (i = 0; i < ino->record_count; i++) + new[i] = ino->records[i]; + ntfs_free(ino->records); + } + ino->records = new; + } + ino->records[ino->record_count] = mftno; + ino->record_count++; + } + if (NTFS_GETU32(attr) == -1) { + ntfs_debug(DEBUG_FILE3, "ntfs_insert_mft_attribute: attribute " + "type is -1.\n"); + return 0; + } + error = ntfs_insert_attribute(ino, attr); + if (error) + return error; + return 0; +} + +/* Read and insert all the attributes of an 'attribute list' attribute. + * Return the number of remaining bytes in *plen. */ +static int parse_attributes(ntfs_inode *ino, ntfs_u8 *alist, int *plen) +{ + ntfs_u8 *mft, *attr; + int mftno, l, error; + int last_mft = -1; + int len = *plen; + int tries = 0; + + if (!ino->attr) { + ntfs_error("parse_attributes: called on inode 0x%x without a " + "loaded base mft record.\n", ino->i_number); + return -EINVAL; + } + mft = ntfs_malloc(ino->vol->mft_record_size); + if (!mft) + return -ENOMEM; + while (len > 8) { + l = NTFS_GETU16(alist + 4); + if (l > len) + break; + /* Process an attribute description. */ + mftno = NTFS_GETU32(alist + 0x10); + /* FIXME: The mft reference (alist + 0x10) is __s64. + * - Not a problem unless we encounter a huge partition. + * - Should be consistency checking the sequence numbers + * though! This should maybe happen in + * ntfs_read_mft_record() itself and a hotfix could + * then occur there or the user notified to run + * ntfsck. (AIA) */ + if (mftno != ino->i_number && mftno != last_mft) { +continue_after_loading_mft_data: + last_mft = mftno; + error = ntfs_read_mft_record(ino->vol, mftno, mft); + if (error) { + if (error == -EINVAL && !tries) + goto force_load_mft_data; +failed_reading_mft_data: + ntfs_debug(DEBUG_FILE3, "parse_attributes: " + "ntfs_read_mft_record(mftno = 0x%x) " + "failed\n", mftno); + ntfs_free(mft); + return error; + } + } + attr = ntfs_find_attr_in_mft_rec( + ino->vol, /* ntfs volume */ + mftno == ino->i_number ?/* mft record is: */ + ino->attr: /* base record */ + mft, /* extension record */ + NTFS_GETU32(alist + 0), /* type */ + (wchar_t*)(alist + alist[7]), /* name */ + alist[6], /* name length */ + 1, /* ignore case */ + NTFS_GETU16(alist + 24) /* instance number */ + ); + if (!attr) { + ntfs_error("parse_attributes: mft records 0x%x and/or " + "0x%x corrupt!\n", ino->i_number, mftno); + ntfs_free(mft); + return -EINVAL; /* FIXME: Better error code? (AIA) */ + } + error = ntfs_insert_mft_attribute(ino, mftno, attr); + if (error) { + ntfs_debug(DEBUG_FILE3, "parse_attributes: " + "ntfs_insert_mft_attribute(mftno 0x%x, " + "attribute type 0x%x) failed\n", mftno, + NTFS_GETU32(alist + 0)); + ntfs_free(mft); + return error; + } + len -= l; + alist += l; + } + ntfs_free(mft); + *plen = len; + return 0; +force_load_mft_data: +{ + ntfs_u8 *mft2, *attr2; + int mftno2; + int last_mft2 = last_mft; + int len2 = len; + int error2; + int found2 = 0; + ntfs_u8 *alist2 = alist; + /* + * We only get here if $DATA wasn't found in $MFT which only happens + * on volume mount when $MFT has an attribute list and there are + * attributes before $DATA which are inside extent mft records. So + * we just skip forward to the $DATA attribute and read that. Then we + * restart which is safe as an attribute will not be inserted twice. + * + * This still will not fix the case where the attribute list is non- + * resident, larger than 1024 bytes, and the $DATA attribute list entry + * is not in the first 1024 bytes. FIXME: This should be implemented + * somehow! Perhaps by passing special error code up to + * ntfs_load_attributes() so it keeps going trying to get to $DATA + * regardless. Then it would have to restart just like we do here. + */ + mft2 = ntfs_malloc(ino->vol->mft_record_size); + if (!mft2) { + ntfs_free(mft); + return -ENOMEM; + } + ntfs_memcpy(mft2, mft, ino->vol->mft_record_size); + while (len2 > 8) { + l = NTFS_GETU16(alist2 + 4); + if (l > len2) + break; + if (NTFS_GETU32(alist2 + 0x0) < ino->vol->at_data) { + len2 -= l; + alist2 += l; + continue; + } + if (NTFS_GETU32(alist2 + 0x0) > ino->vol->at_data) { + if (found2) + break; + /* Uh-oh! It really isn't there! */ + ntfs_error("Either the $MFT is corrupt or, equally " + "likely, the $MFT is too complex for " + "the current driver to handle. Please " + "email the ntfs maintainer that you " + "saw this message. Thank you.\n"); + goto failed_reading_mft_data; + } + /* Process attribute description. */ + mftno2 = NTFS_GETU32(alist2 + 0x10); + if (mftno2 != ino->i_number && mftno2 != last_mft2) { + last_mft2 = mftno2; + error2 = ntfs_read_mft_record(ino->vol, mftno2, mft2); + if (error2) { + ntfs_debug(DEBUG_FILE3, "parse_attributes: " + "ntfs_read_mft_record(mftno2 = 0x%x) " + "failed\n", mftno2); + ntfs_free(mft2); + goto failed_reading_mft_data; + } + } + attr2 = ntfs_find_attr_in_mft_rec( + ino->vol, /* ntfs volume */ + mftno2 == ino->i_number ?/* mft record is: */ + ino->attr: /* base record */ + mft2, /* extension record */ + NTFS_GETU32(alist2 + 0), /* type */ + (wchar_t*)(alist2 + alist2[7]), /* name */ + alist2[6], /* name length */ + 1, /* ignore case */ + NTFS_GETU16(alist2 + 24) /* instance number */ + ); + if (!attr2) { + ntfs_error("parse_attributes: mft records 0x%x and/or " + "0x%x corrupt!\n", ino->i_number, + mftno2); + ntfs_free(mft2); + goto failed_reading_mft_data; + } + error2 = ntfs_insert_mft_attribute(ino, mftno2, attr2); + if (error2) { + ntfs_debug(DEBUG_FILE3, "parse_attributes: " + "ntfs_insert_mft_attribute(mftno2 0x%x, " + "attribute2 type 0x%x) failed\n", mftno2, + NTFS_GETU32(alist2 + 0)); + ntfs_free(mft2); + goto failed_reading_mft_data; + } + len2 -= l; + alist2 += l; + found2 = 1; + } + ntfs_free(mft2); + tries = 1; + goto continue_after_loading_mft_data; +} +} + +static void ntfs_load_attributes(ntfs_inode *ino) +{ + ntfs_attribute *alist; + int datasize; + int offset, len, delta; + char *buf; + ntfs_volume *vol = ino->vol; + + ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 1\n", ino->i_number); + if (ntfs_insert_mft_attributes(ino, ino->attr, ino->i_number)) + return; + ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 2\n", ino->i_number); + alist = ntfs_find_attr(ino, vol->at_attribute_list, 0); + ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 3\n", ino->i_number); + if (!alist) + return; + ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 4\n", ino->i_number); + datasize = alist->size; + ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: alist->size = 0x%x\n", + ino->i_number, alist->size); + if (alist->resident) { + parse_attributes(ino, alist->d.data, &datasize); + return; + } + ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 5\n", ino->i_number); + buf = ntfs_malloc(1024); + if (!buf) /* FIXME: Should be passing error code to caller. (AIA) */ + return; + delta = 0; + for (offset = 0; datasize; datasize -= len, offset += len) { + ntfs_io io; + + io.fn_put = ntfs_put; + io.fn_get = 0; + io.param = buf + delta; + len = 1024 - delta; + if (len > datasize) + len = datasize; + ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: len = %i\n", + ino->i_number, len); + ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: delta = %i\n", + ino->i_number, delta); + io.size = len; + if (ntfs_read_attr(ino, vol->at_attribute_list, 0, offset, + &io)) + ntfs_error("error in load_attributes\n"); + delta += len; + ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: after += len, " + "delta = %i\n", ino->i_number, delta); + parse_attributes(ino, buf, &delta); + ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: after " + "parse_attr, delta = %i\n", ino->i_number, + delta); + if (delta) + /* Move remaining bytes to buffer start. */ + ntfs_memmove(buf, buf + len - delta, delta); + } + ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 6\n", ino->i_number); + ntfs_free(buf); +} + +int ntfs_init_inode(ntfs_inode *ino, ntfs_volume *vol, int inum) +{ + char *buf; + int error; + + ntfs_debug(DEBUG_FILE1, "Initializing inode 0x%x\n", inum); + ino->i_number = inum; + ino->vol = vol; + ino->attr = buf = ntfs_malloc(vol->mft_record_size); + if (!buf) + return -ENOMEM; + error = ntfs_read_mft_record(vol, inum, ino->attr); + if (error) { + ntfs_debug(DEBUG_OTHER, "Init inode: 0x%x failed\n", inum); + return error; + } + ntfs_debug(DEBUG_FILE2, "Init inode: got mft 0x%x\n", inum); + ino->sequence_number = NTFS_GETU16(buf + 0x10); + ino->attr_count = 0; + ino->record_count = 0; + ino->records = 0; + ino->attrs = 0; + ntfs_load_attributes(ino); + ntfs_debug(DEBUG_FILE2, "Init inode: done 0x%x\n", inum); + return 0; +} + +void ntfs_clear_inode(ntfs_inode *ino) +{ + int i; + if (!ino->attr) { + ntfs_error("ntfs_clear_inode: double free\n"); + return; + } + ntfs_free(ino->attr); + ino->attr = 0; + ntfs_free(ino->records); + ino->records = 0; + for (i = 0; i < ino->attr_count; i++) { + if (ino->attrs[i].name) + ntfs_free(ino->attrs[i].name); + if (ino->attrs[i].resident) { + if (ino->attrs[i].d.data) + ntfs_free(ino->attrs[i].d.data); + } else { + if (ino->attrs[i].d.r.runlist) + ntfs_vfree(ino->attrs[i].d.r.runlist); + } + } + ntfs_free(ino->attrs); + ino->attrs = 0; +} + +/* Check and fixup a MFT record. */ +int ntfs_check_mft_record(ntfs_volume *vol, char *record) +{ + return ntfs_fixup_record(record, "FILE", vol->mft_record_size); +} + +/* Return (in result) the value indicating the next available attribute + * chunk number. Works for inodes w/o extension records only. */ +int ntfs_allocate_attr_number(ntfs_inode *ino, int *result) +{ + if (ino->record_count != 1) + return -EOPNOTSUPP; + *result = NTFS_GETU16(ino->attr + 0x28); + NTFS_PUTU16(ino->attr + 0x28, (*result) + 1); + return 0; +} + +/* Find the location of an attribute in the inode. A name of NULL indicates + * unnamed attributes. Return pointer to attribute or NULL if not found. */ +char *ntfs_get_attr(ntfs_inode *ino, int attr, char *name) +{ + /* Location of first attribute. */ + char *it = ino->attr + NTFS_GETU16(ino->attr + 0x14); + int type; + int len; + + /* Only check for magic DWORD here, fixup should have happened before.*/ + if (!IS_MFT_RECORD(ino->attr)) + return 0; + do { + type = NTFS_GETU32(it); + len = NTFS_GETU16(it + 4); + /* We found the attribute type. Is the name correct, too? */ + if (type == attr) { + int namelen = NTFS_GETU8(it + 9); + char *name_it, *n = name; + /* Match given name and attribute name if present. + Make sure attribute name is Unicode. */ + if (!name) { + goto check_namelen; + } else if (namelen) { + for (name_it = it + NTFS_GETU16(it + 10); + namelen; n++, name_it += 2, namelen--) + if (*name_it != *n || name_it[1]) + break; +check_namelen: + if (!namelen) + break; + } + } + it += len; + } while (type != -1); /* List of attributes ends with type -1. */ + if (type == -1) + return 0; + return it; +} + +__s64 ntfs_get_attr_size(ntfs_inode *ino, int type, char *name) +{ + ntfs_attribute *attr = ntfs_find_attr(ino, type, name); + if (!attr) + return 0; + return + attr->size; +} + +int ntfs_attr_is_resident(ntfs_inode *ino, int type, char *name) +{ + ntfs_attribute *attr = ntfs_find_attr(ino, type, name); + if (!attr) + return 0; + return attr->resident; +} + +/* + * A run is coded as a type indicator, an unsigned length, and a signed cluster + * offset. + * . To save space, length and offset are fields of variable length. The low + * nibble of the type indicates the width of the length :), the high nibble + * the width of the offset. + * . The first offset is relative to cluster 0, later offsets are relative to + * the previous cluster. + * + * This function decodes a run. Length is an output parameter, data and cluster + * are in/out parameters. + */ +int ntfs_decompress_run(unsigned char **data, int *length, + ntfs_cluster_t *cluster, int *ctype) +{ + unsigned char type = *(*data)++; + *ctype = 0; + switch (type & 0xF) { + case 1: + *length = NTFS_GETS8(*data); + break; + case 2: + *length = NTFS_GETS16(*data); + break; + case 3: + *length = NTFS_GETS24(*data); + break; + case 4: + *length = NTFS_GETS32(*data); + break; + /* Note: cases 5-8 are probably pointless to code, since how + * many runs > 4GB of length are there? At the most, cases 5 + * and 6 are probably necessary, and would also require making + * length 64-bit throughout. */ + default: + ntfs_error("Can't decode run type field 0x%x\n", type); + return -1; + } +// ntfs_debug(DEBUG_FILE3, "ntfs_decompress_run: length = 0x%x\n",*length); + if (*length < 0) + { + ntfs_error("Negative run length decoded\n"); + return -1; + } + *data += (type & 0xF); + switch (type & 0xF0) { + case 0: + *ctype = 2; + break; + case 0x10: + *cluster += NTFS_GETS8(*data); + break; + case 0x20: + *cluster += NTFS_GETS16(*data); + break; + case 0x30: + *cluster += NTFS_GETS24(*data); + break; + case 0x40: + *cluster += NTFS_GETS32(*data); + break; +#if 0 /* Keep for future, in case ntfs_cluster_t ever becomes 64bit. */ + case 0x50: + *cluster += NTFS_GETS40(*data); + break; + case 0x60: + *cluster += NTFS_GETS48(*data); + break; + case 0x70: + *cluster += NTFS_GETS56(*data); + break; + case 0x80: + *cluster += NTFS_GETS64(*data); + break; +#endif + default: + ntfs_error("Can't decode run type field 0x%x\n", type); + return -1; + } +// ntfs_debug(DEBUG_FILE3, "ntfs_decompress_run: cluster = 0x%x\n", +// *cluster); + *data += (type >> 4); + return 0; +} + +static void dump_runlist(const ntfs_runlist *rl, const int rlen); + +/* + * FIXME: ntfs_readwrite_attr() has the effect of writing @dest to @offset of + * the attribute value of the attribute @attr in the in memory inode @ino. + * If the attribute value of @attr is non-resident the value's contents at + * @offset are actually written to disk (from @dest). The on disk mft record + * describing the non-resident attribute value is not updated! + * If the attribute value is resident then the value is written only in + * memory. The on disk mft record containing the value is not written to disk. + * A possible fix would be to call ntfs_update_inode() before returning. (AIA) + */ +/* Reads l bytes of the attribute (attr, name) of ino starting at offset on + * vol into buf. Returns the number of bytes read in the ntfs_io struct. + * Returns 0 on success, errno on failure */ +int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset, + ntfs_io *dest) +{ + int rnum, s_vcn, error, clustersizebits; + ntfs_cluster_t cluster, s_cluster, vcn, len; + __s64 l, chunk, copied; + + ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): %s 0x%x bytes at offset " + "0x%Lx %s inode 0x%x, attr type 0x%x.\n", + dest->do_read ? "Read" : "Write", dest->size, offset, + dest->do_read ? "from" : "to", ino->i_number, + attr->type); + l = dest->size; + if (l == 0) + return 0; + if (dest->do_read) { + /* If read _starts_ beyond end of stream, return nothing. */ + if (offset >= attr->size) { + dest->size = 0; + return 0; + } + /* If read _extends_ beyond end of stream, return as much + * initialised data as we have. */ + if (offset + l >= attr->size) + l = dest->size = attr->size - offset; + } else { + /* + * If write extends beyond _allocated_ size, extend attribute, + * updating attr->allocated and attr->size in the process. (AIA) + */ + if ((!attr->resident && offset + l > attr->allocated) || + (attr->resident && offset + l > attr->size)) { + error = ntfs_resize_attr(ino, attr, offset + l); + if (error) + return error; + } + if (!attr->resident) { + /* Has amount of data increased? */ + if (offset + l > attr->size) + attr->size = offset + l; + /* Has amount of initialised data increased? */ + if (offset + l > attr->initialized) { + /* FIXME: Clear the section between the old + * initialised length and the write start. + * (AIA) */ + attr->initialized = offset + l; + } + } + } + if (attr->resident) { + if (dest->do_read) + dest->fn_put(dest, (ntfs_u8*)attr->d.data + offset, l); + else + dest->fn_get((ntfs_u8*)attr->d.data + offset, dest, l); + dest->size = l; + return 0; + } + if (dest->do_read) { + /* Read uninitialized data. */ + if (offset >= attr->initialized) + return ntfs_read_zero(dest, l); + if (offset + l > attr->initialized) { + dest->size = chunk = attr->initialized - offset; + error = ntfs_readwrite_attr(ino, attr, offset, dest); + if (error || (dest->size != chunk && (error = -EIO, 1))) + return error; + dest->size += l - chunk; + return ntfs_read_zero(dest, l - chunk); + } + if (attr->flags & ATTR_IS_COMPRESSED) + return ntfs_read_compressed(ino, attr, offset, dest); + } else { + if (attr->flags & ATTR_IS_COMPRESSED) + return ntfs_write_compressed(ino, attr, offset, dest); + } + vcn = 0; + clustersizebits = ino->vol->cluster_size_bits; + s_vcn = offset >> clustersizebits; + for (rnum = 0; rnum < attr->d.r.len && + vcn + attr->d.r.runlist[rnum].len <= s_vcn; rnum++) + vcn += attr->d.r.runlist[rnum].len; + if (rnum == attr->d.r.len) { + ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): EOPNOTSUPP: " + "inode = 0x%x, rnum = %i, offset = 0x%Lx, vcn = 0x%x, " + "s_vcn = 0x%x.\n", ino->i_number, rnum, offset, vcn, + s_vcn); + dump_runlist(attr->d.r.runlist, attr->d.r.len); + /*FIXME: Should extend runlist. */ + return -EOPNOTSUPP; + } + copied = 0; + while (l) { + s_vcn = offset >> clustersizebits; + cluster = attr->d.r.runlist[rnum].lcn; + len = attr->d.r.runlist[rnum].len; + s_cluster = cluster + s_vcn - vcn; + chunk = ((__s64)(vcn + len) << clustersizebits) - offset; + if (chunk > l) + chunk = l; + dest->size = chunk; + error = ntfs_getput_clusters(ino->vol, s_cluster, offset - + ((__s64)s_vcn << clustersizebits), dest); + if (error) { + ntfs_error("Read/write error.\n"); + dest->size = copied; + return error; + } + l -= chunk; + copied += chunk; + offset += chunk; + if (l && offset >= ((__s64)(vcn + len) << clustersizebits)) { + rnum++; + vcn += len; + cluster = attr->d.r.runlist[rnum].lcn; + len = attr->d.r.runlist[rnum].len; + } + } + dest->size = copied; + return 0; +} + +int ntfs_read_attr(ntfs_inode *ino, int type, char *name, __s64 offset, + ntfs_io *buf) +{ + ntfs_attribute *attr; + + buf->do_read = 1; + attr = ntfs_find_attr(ino, type, name); + if (!attr) { + ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): attr 0x%x not found " + "in inode 0x%x\n", type, ino->i_number); + return -EINVAL; + } + return ntfs_readwrite_attr(ino, attr, offset, buf); +} + +int ntfs_write_attr(ntfs_inode *ino, int type, char *name, __s64 offset, + ntfs_io *buf) +{ + ntfs_attribute *attr; + + buf->do_read = 0; + attr = ntfs_find_attr(ino, type, name); + if (!attr) { + ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): attr 0x%x not found " + "in inode 0x%x\n", type, ino->i_number); + return -EINVAL; + } + return ntfs_readwrite_attr(ino, attr, offset, buf); +} + +/* -2 = error, -1 = hole, >= 0 means real disk cluster (lcn). */ +int ntfs_vcn_to_lcn(ntfs_inode *ino, int vcn) +{ + int rnum; + ntfs_attribute *data; + + data = ntfs_find_attr(ino, ino->vol->at_data, 0); + if (!data || data->resident || data->flags & (ATTR_IS_COMPRESSED | + ATTR_IS_ENCRYPTED)) + return -2; + if (data->size <= (__s64)vcn << ino->vol->cluster_size_bits) + return -2; + if (data->initialized <= (__s64)vcn << ino->vol->cluster_size_bits) + return -1; + for (rnum = 0; rnum < data->d.r.len && + vcn >= data->d.r.runlist[rnum].len; rnum++) + vcn -= data->d.r.runlist[rnum].len; + if (data->d.r.runlist[rnum].lcn >= 0) + return data->d.r.runlist[rnum].lcn + vcn; + return data->d.r.runlist[rnum].lcn + vcn; +} + +static int allocate_store(ntfs_volume *vol, ntfs_disk_inode *store, int count) +{ + int i; + + if (store->count > count) + return 0; + if (store->size < count) { + ntfs_mft_record *n = ntfs_malloc((count + 4) * + sizeof(ntfs_mft_record)); + if (!n) + return -ENOMEM; + if (store->size) { + for (i = 0; i < store->size; i++) + n[i] = store->records[i]; + ntfs_free(store->records); + } + store->size = count + 4; + store->records = n; + } + for (i = store->count; i < count; i++) { + store->records[i].record = ntfs_malloc(vol->mft_record_size); + if (!store->records[i].record) + return -ENOMEM; + store->count++; + } + return 0; +} + +static void deallocate_store(ntfs_disk_inode* store) +{ + int i; + + for (i = 0; i < store->count; i++) + ntfs_free(store->records[i].record); + ntfs_free(store->records); + store->count = store->size = 0; + store->records = 0; +} + +/** + * layout_runs - compress runlist into mapping pairs array + * @attr: attribute containing the runlist to compress + * @rec: destination buffer to hold the mapping pairs array + * @offs: current position in @rec (in/out variable) + * @size: size of the buffer @rec + * + * layout_runs walks the runlist in @attr, compresses it and writes it out the + * resulting mapping pairs array into @rec (up to a maximum of @size bytes are + * written). On entry @offs is the offset in @rec at which to begin writing the + * mapping pairs array. On exit, it contains the offset in @rec of the first + * byte after the end of the mapping pairs array. + */ +static int layout_runs(ntfs_attribute *attr, char *rec, int *offs, int size) +{ + int i, len, offset, coffs; + /* ntfs_cluster_t MUST be signed! (AIA) */ + ntfs_cluster_t cluster, rclus; + ntfs_runlist *rl = attr->d.r.runlist; + cluster = 0; + offset = *offs; + for (i = 0; i < attr->d.r.len; i++) { + /* + * We cheat with this check on the basis that lcn will never + * be less than -1 and the lcn delta will fit in signed + * 32-bits (ntfs_cluster_t). (AIA) + */ + if (rl[i].lcn < (ntfs_cluster_t)-1) { + ntfs_error("layout_runs() encountered an out of bounds " + "cluster delta, lcn = %i.\n", + rl[i].lcn); + return -ERANGE; + } + rclus = rl[i].lcn - cluster; + len = rl[i].len; + rec[offset] = 0; + if (offset + 9 > size) + return -E2BIG; /* It might still fit, but this + * simplifies testing. */ + /* + * Run length is stored as signed number, so deal with it + * properly, i.e. observe that a negative number will have all + * its most significant bits set to 1 but we don't store that + * in the mapping pairs array. We store the smallest type of + * negative number required, thus in the first if we check + * whether len fits inside a signed byte and if so we store it + * as such, the next ifs check for a signed short, then a signed + * 24-bit and finally the full blown signed 32-bit. Same goes + * for rlus below. (AIA) + */ + if (len >= -0x80 && len <= 0x7f) { + NTFS_PUTU8(rec + offset + 1, len & 0xff); + coffs = 1; + } else if (len >= -0x8000 && len <= 0x7fff) { + NTFS_PUTU16(rec + offset + 1, len & 0xffff); + coffs = 2; + } else if (len >= -0x800000 && len <= 0x7fffff) { + NTFS_PUTU24(rec + offset + 1, len & 0xffffff); + coffs = 3; + } else /* if (len >= -0x80000000LL && len <= 0x7fffffff */ { + NTFS_PUTU32(rec + offset + 1, len); + coffs = 4; + } /* else ... FIXME: When len becomes 64-bit we need to extend + * the else if () statements. (AIA) */ + *(rec + offset) |= coffs++; + if (rl[i].lcn == (ntfs_cluster_t)-1) /* Compressed run. */ + /* Nothing */; + else if (rclus >= -0x80 && rclus <= 0x7f) { + *(rec + offset) |= 0x10; + NTFS_PUTS8(rec + offset + coffs, rclus & 0xff); + coffs += 1; + } else if (rclus >= -0x8000 && rclus <= 0x7fff) { + *(rec + offset) |= 0x20; + NTFS_PUTS16(rec + offset + coffs, rclus & 0xffff); + coffs += 2; + } else if (rclus >= -0x800000 && rclus <= 0x7fffff) { + *(rec + offset) |= 0x30; + NTFS_PUTS24(rec + offset + coffs, rclus & 0xffffff); + coffs += 3; + } else /* if (rclus >= -0x80000000LL && rclus <= 0x7fffffff)*/ { + *(rec + offset) |= 0x40; + NTFS_PUTS32(rec + offset + coffs, rclus + /* & 0xffffffffLL */); + coffs += 4; + } /* FIXME: When rclus becomes 64-bit. + else if (rclus >= -0x8000000000 && rclus <= 0x7FFFFFFFFF) { + *(rec + offset) |= 0x50; + NTFS_PUTS40(rec + offset + coffs, rclus & + 0xffffffffffLL); + coffs += 5; + } else if (rclus >= -0x800000000000 && + rclus <= 0x7FFFFFFFFFFF) { + *(rec + offset) |= 0x60; + NTFS_PUTS48(rec + offset + coffs, rclus & + 0xffffffffffffLL); + coffs += 6; + } else if (rclus >= -0x80000000000000 && + rclus <= 0x7FFFFFFFFFFFFF) { + *(rec + offset) |= 0x70; + NTFS_PUTS56(rec + offset + coffs, rclus & + 0xffffffffffffffLL); + coffs += 7; + } else { + *(rec + offset) |= 0x80; + NTFS_PUTS64(rec + offset + coffs, rclus); + coffs += 8; + } */ + offset += coffs; + if (rl[i].lcn) + cluster = rl[i].lcn; + } + if (offset >= size) + return -E2BIG; + /* Terminating null. */ + *(rec + offset++) = 0; + *offs = offset; + return 0; +} + +static void count_runs(ntfs_attribute *attr, char *buf) +{ + ntfs_u32 first, count, last, i; + + first = 0; + for (i = 0, count = 0; i < attr->d.r.len; i++) + count += attr->d.r.runlist[i].len; + last = first + count - 1; + NTFS_PUTU64(buf + 0x10, first); + NTFS_PUTU64(buf + 0x18, last); +} + +/** + * layout_attr - convert in memory attribute to on disk attribute record + * @attr: in memory attribute to convert + * @buf: destination buffer for on disk attribute record + * @size: size of the destination buffer + * @psize: size of converted on disk attribute record (out variable) + * + * layout_attr() takes the attribute @attr and converts it into the appropriate + * on disk structure, writing it into @buf (up to @size bytes are written). + * + * On success we return 0 and set @*psize to the actual byte size of the on- + * disk attribute that was written into @buf. + */ +static int layout_attr(ntfs_attribute *attr, char *buf, int size, int *psize) +{ + int nameoff, hdrsize, asize; + + if (attr->resident) { + nameoff = 0x18; + hdrsize = (nameoff + 2 * attr->namelen + 7) & ~7; + asize = (hdrsize + attr->size + 7) & ~7; + if (size < asize) + return -E2BIG; + NTFS_PUTU32(buf + 0x10, attr->size); + NTFS_PUTU8(buf + 0x16, attr->indexed); + NTFS_PUTU16(buf + 0x14, hdrsize); + if (attr->size) + ntfs_memcpy(buf + hdrsize, attr->d.data, attr->size); + } else { + int error; + + if (attr->flags & ATTR_IS_COMPRESSED) + nameoff = 0x48; + else + nameoff = 0x40; + hdrsize = (nameoff + 2 * attr->namelen + 7) & ~7; + if (size < hdrsize) + return -E2BIG; + /* Make asize point at the end of the attribute record header, + i.e. at the beginning of the mapping pairs array. */ + asize = hdrsize; + error = layout_runs(attr, buf, &asize, size); + /* Now, asize points one byte beyond the end of the mapping + pairs array. */ + if (error) + return error; + /* The next attribute has to begin on 8-byte boundary. */ + asize = (asize + 7) & ~7; + /* FIXME: fragments */ + count_runs(attr, buf); + NTFS_PUTU16(buf + 0x20, hdrsize); + NTFS_PUTU16(buf + 0x22, attr->cengine); + NTFS_PUTU32(buf + 0x24, 0); + NTFS_PUTS64(buf + 0x28, attr->allocated); + NTFS_PUTS64(buf + 0x30, attr->size); + NTFS_PUTS64(buf + 0x38, attr->initialized); + if (attr->flags & ATTR_IS_COMPRESSED) + NTFS_PUTS64(buf + 0x40, attr->compsize); + } + NTFS_PUTU32(buf, attr->type); + NTFS_PUTU32(buf + 4, asize); + NTFS_PUTU8(buf + 8, attr->resident ? 0 : 1); + NTFS_PUTU8(buf + 9, attr->namelen); + NTFS_PUTU16(buf + 0xa, nameoff); + NTFS_PUTU16(buf + 0xc, attr->flags); + NTFS_PUTU16(buf + 0xe, attr->attrno); + if (attr->namelen) + ntfs_memcpy(buf + nameoff, attr->name, 2 * attr->namelen); + *psize = asize; + return 0; +} + +/** + * layout_inode - convert an in-memory inode into on disk mft record(s) + * @ino: in memory inode to convert + * @store: on disk inode, contain buffers for the on disk mft record(s) + * + * layout_inode takes the in memory inode @ino, converts it into a (sequence of) + * mft record(s) and writes them to the appropriate buffers in the @store. + * + * Return 0 on success, + * the required mft record count (>0) if the inode does not fit, + * -ENOMEM if memory allocation problem, or + * -EOPNOTSUP if beyond our capabilities. + * + * TODO: We at the moment do not support extension mft records. (AIA) + */ +int layout_inode(ntfs_inode *ino, ntfs_disk_inode *store) +{ + int offset, i, size, psize, error, count, recno; + ntfs_attribute *attr; + unsigned char *rec; + + error = allocate_store(ino->vol, store, ino->record_count); + if (error) + return error; + size = ino->vol->mft_record_size; + count = i = 0; + do { + if (count < ino->record_count) { + recno = ino->records[count]; + } else { + error = allocate_store(ino->vol, store, count + 1); + if (error) + return error; + recno = -1; + } + /* + * FIXME: We need to support extension records properly. + * At the moment they wouldn't work. Probably would "just" get + * corrupted if we write to them... (AIA) + */ + store->records[count].recno = recno; + rec = store->records[count].record; + count++; + /* Copy mft record header. */ + offset = NTFS_GETU16(ino->attr + 0x14); /* attrs_offset */ + ntfs_memcpy(rec, ino->attr, offset); + /* Copy attributes. */ + while (i < ino->attr_count) { + attr = ino->attrs + i; + error = layout_attr(attr, rec + offset, + size - offset - 8, &psize); + if (error == -E2BIG && offset != NTFS_GETU16(ino->attr + + 0x14)) + break; + if (error) + return error; + offset += psize; + i++; + } + /* Terminating attribute. */ + NTFS_PUTU32(rec + offset, 0xFFFFFFFF); + offset += 4; + NTFS_PUTU32(rec + offset, 0); + offset += 4; + NTFS_PUTU32(rec + 0x18, offset); + } while (i < ino->attr_count || count < ino->record_count); + return count - ino->record_count; +} + +/* + * FIXME: ntfs_update_inode() calls layout_inode() to create the mft record on + * disk structure corresponding to the inode @ino. After that, ntfs_write_attr() + * is called to write out the created mft record to disk. + * We shouldn't need to re-layout every single time we are updating an mft + * record. No wonder the ntfs driver is slow like hell. (AIA) + */ +int ntfs_update_inode(ntfs_inode *ino) +{ + int error, i; + ntfs_disk_inode store; + ntfs_io io; + + ntfs_bzero(&store, sizeof(store)); + error = layout_inode(ino, &store); + if (error == -E2BIG) { + i = ntfs_split_indexroot(ino); + if (i != -ENOTDIR) { + if (!i) + i = layout_inode(ino, &store); + error = i; + } + } + if (error == -E2BIG) { + error = ntfs_attr_allnonresident(ino); + if (!error) + error = layout_inode(ino, &store); + } + if (error > 0) { + /* FIXME: Introduce extension records. */ + error = -E2BIG; + } + if (error) { + if (error == -E2BIG) + ntfs_error("Cannot handle saving inode 0x%x.\n", + ino->i_number); + deallocate_store(&store); + return error; + } + io.fn_get = ntfs_get; + io.fn_put = 0; + for (i = 0; i < store.count; i++) { + error = ntfs_insert_fixups(store.records[i].record, + ino->vol->mft_record_size); + if (error) { + printk(KERN_ALERT "NTFS: ntfs_update_inode() caught " + "corrupt %s mtf record ntfs record " + "header. Refusing to write corrupt " + "data to disk. Unmount and run chkdsk " + "immediately!\n", i ? "extension": + "base"); + deallocate_store(&store); + return -EIO; + } + io.param = store.records[i].record; + io.size = ino->vol->mft_record_size; + error = ntfs_write_attr(ino->vol->mft_ino, ino->vol->at_data, + 0, (__s64)store.records[i].recno << + ino->vol->mft_record_size_bits, &io); + if (error || io.size != ino->vol->mft_record_size) { + /* Big trouble, partially written file. */ + ntfs_error("Please unmount: Write error in inode " + "0x%x\n", ino->i_number); + deallocate_store(&store); + return error ? error : -EIO; + } + } + deallocate_store(&store); + return 0; +} + +void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l) +{ + int head, comp; + int copied = 0; + unsigned char *stop; + int bits; + int tag = 0; + int clear_pos; + + while (1) { + head = NTFS_GETU16(src) & 0xFFF; + /* High bit indicates that compression was performed. */ + comp = NTFS_GETU16(src) & 0x8000; + src += 2; + stop = src + head; + bits = 0; + clear_pos = 0; + if (head == 0) + /* Block is not used. */ + return;/* FIXME: copied */ + if (!comp) { /* uncompressible */ + ntfs_memcpy(dest, src, 0x1000); + dest += 0x1000; + copied += 0x1000; + src += 0x1000; + if (l == copied) + return; + continue; + } + while (src <= stop) { + if (clear_pos > 4096) { + ntfs_error("Error 1 in decompress\n"); + return; + } + if (!bits) { + tag = NTFS_GETU8(src); + bits = 8; + src++; + if (src > stop) + break; + } + if (tag & 1) { + int i, len, delta, code, lmask, dshift; + code = NTFS_GETU16(src); + src += 2; + if (!clear_pos) { + ntfs_error("Error 2 in decompress\n"); + return; + } + for (i = clear_pos - 1, lmask = 0xFFF, + dshift = 12; i >= 0x10; i >>= 1) { + lmask >>= 1; + dshift--; + } + delta = code >> dshift; + len = (code & lmask) + 3; + for (i = 0; i < len; i++) { + dest[clear_pos] = dest[clear_pos - + delta - 1]; + clear_pos++; + copied++; + if (copied==l) + return; + } + } else { + dest[clear_pos++] = NTFS_GETU8(src); + src++; + copied++; + if (copied==l) + return; + } + tag >>= 1; + bits--; + } + dest += clear_pos; + } +} + +/* + * NOTE: Neither of the ntfs_*_bit functions are atomic! But we don't need + * them atomic at present as we never operate on shared/cached bitmaps. + */ +static __inline__ int ntfs_test_bit(unsigned char *byte, const int bit) +{ + return byte[bit >> 3] & (1 << (bit & 7)) ? 1 : 0; +} + +static __inline__ void ntfs_set_bit(unsigned char *byte, const int bit) +{ + byte[bit >> 3] |= 1 << (bit & 7); +} + +static __inline__ void ntfs_clear_bit(unsigned char *byte, const int bit) +{ + byte[bit >> 3] &= ~(1 << (bit & 7)); +} + +static __inline__ int ntfs_test_and_clear_bit(unsigned char *byte, + const int bit) +{ + unsigned char *ptr = byte + (bit >> 3); + int b = 1 << (bit & 7); + int oldbit = *ptr & b ? 1 : 0; + *ptr &= ~b; + return oldbit; +} + +static void dump_runlist(const ntfs_runlist *rl, const int rlen) +{ +#ifdef DEBUG + int i; + ntfs_cluster_t ct; + + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): rlen = %i.\n", rlen); + ntfs_debug(DEBUG_OTHER, "VCN LCN Run length\n"); + for (i = 0, ct = 0; i < rlen; ct += rl[i++].len) { + if (rl[i].lcn == (ntfs_cluster_t)-1) + ntfs_debug(DEBUG_OTHER, "0x%-8x LCN_HOLE 0x%-8x " + "(%s)\n", ct, rl[i].len, rl[i].len ? + "sparse run" : "run list end"); + else + ntfs_debug(DEBUG_OTHER, "0x%-8x 0x%-8x 0x%-8x%s\n", ct, + rl[i].lcn, rl[i].len, rl[i].len && + i + 1 < rlen ? "" : " (run list end)"); + if (!rl[i].len) + break; + } +#endif +} + +/** + * splice_runlists - splice two run lists into one + * @rl1: pointer to address of first run list + * @r1len: number of elementfs in first run list + * @rl2: pointer to second run list + * @r2len: number of elements in second run list + * + * Append the run list @rl2 to the run list *@rl1 and return the result in + * *@rl1 and *@r1len. + * + * Return 0 on success or -errno on error, in which case *@rl1 and *@r1len are + * left untouched. + * + * The only possible error code at the moment is -ENOMEM and only happens if + * there is insufficient memory to allocate the new run list (only happens + * when size of (rl1 + rl2) > allocated size of rl1). + */ +int splice_runlists(ntfs_runlist **rl1, int *r1len, const ntfs_runlist *rl2, + int r2len) +{ + ntfs_runlist *rl; + int rlen, rl_size, rl2_pos; + + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Entering with *r1len = %i, " + "r2len = %i.\n", *r1len, r2len); + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Dumping 1st runlist.\n"); + if (*rl1) + dump_runlist(*rl1, *r1len); + else + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Not present.\n"); + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Dumping 2nd runlist.\n"); + dump_runlist(rl2, r2len); + rlen = *r1len + r2len + 1; + rl_size = (rlen * sizeof(ntfs_runlist) + PAGE_SIZE - 1) & + PAGE_MASK; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): rlen = %i, rl_size = %i.\n", + rlen, rl_size); + /* Do we have enough space? */ + if (rl_size <= ((*r1len * sizeof(ntfs_runlist) + PAGE_SIZE - 1) & + PAGE_MASK)) { + /* Have enough space already. */ + rl = *rl1; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Have enough space " + "already.\n"); + } else { + /* Need more space. Reallocate. */ + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Need more space.\n"); + rl = ntfs_vmalloc(rlen << sizeof(ntfs_runlist)); + if (!rl) + return -ENOMEM; + /* Copy over rl1. */ + ntfs_memcpy(rl, *rl1, *r1len * sizeof(ntfs_runlist)); + ntfs_vfree(*rl1); + *rl1 = rl; + } + /* Reuse rl_size as the current position index into rl. */ + rl_size = *r1len - 1; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): rl_size = %i.\n"); + /* Coalesce neighbouring elements, if present. */ + rl2_pos = 0; + if (rl[rl_size].lcn + rl[rl_size].len == rl2[rl2_pos].lcn) { + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Coalescing adjacent " + "runs.\n"); + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before: " + "rl[rl_size].len = %i.\n", rl[rl_size].len); + rl[rl_size].len += rl2[rl2_pos].len; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After: " + "rl[rl_size].len = %i.\n", rl[rl_size].len); + rl2_pos++; + r2len--; + rlen--; + } + rl_size++; + /* Copy over rl2. */ + ntfs_memcpy(rl + rl_size, rl2 + rl2_pos, r2len * sizeof(ntfs_runlist)); + rlen--; + rl[rlen].lcn = (ntfs_cluster_t)-1; + rl[rlen].len = (ntfs_cluster_t)0; + *r1len = rlen; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Dumping result runlist.\n"); + dump_runlist(*rl1, *r1len); + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Returning with *r1len = " + "%i.\n", rlen); + return 0; +} + +/** + * ntfs_alloc_mft_record - allocate an mft record + * @vol: volume to allocate an mft record on + * @result: the mft record number allocated + * + * Allocate a new mft record on disk. Return 0 on success or -ERRNO on error. + * On success, *@result contains the allocated mft record number. On error, + * *@result is -1UL. + * + * Note, this function doesn't actually set the mft record to be in use. This + * is done by the caller, which at the moment is only ntfs_alloc_inode(). + * + * To find a free mft record, we scan the mft bitmap for a zero bit. To + * optimize this we start scanning at the place where we last stopped and we + * perform wrap around when we reach the end. Note, we do not try to allocate + * mft records below number 24 because numbers 0 to 15 are the defined system + * files anyway and 16 to 24 are special in that they are used for storing + * extension mft records for $MFT's $DATA attribute. This is required to avoid + * the possibility of creating a run list with a circular dependence which once + * written to disk can never be read in again. Windows will only use records + * 16 to 24 for normal files if the volume is completely out of space. We never + * use them which means that when the volume is really out of space we cannot + * create any more files while Windows can still create up to 8 small files. We + * can start doing this at some later time, doesn't matter much for now. + * + * When scanning the mft bitmap, we only search up to the last allocated mft + * record. If there are no free records left in the range 24 to number of + * allocated mft records, then we extend the mft data in order to create free + * mft records. We extend the allocated size of $MFT/$DATA by 16 records at a + * time or one cluster, if cluster size is above 16kiB. If there isn't + * sufficient space to do this, we try to extend by a single mft record or one + * cluster, if cluster size is above mft record size, but we only do this if + * there is enough free space, which we know from the values returned by the + * failed cluster allocation function when we tried to do the first allocation. + * + * No matter how many mft records we allocate, we initialize only the first + * allocated mft record (incrementing mft data size and initialized size) and + * return its number to the caller in @*result, unless there are less than 24 + * mft records, in which case we allocate and initialize mft records until we + * reach record 24 which we consider as the first free mft record for use by + * normal files. + * + * If during any stage we overflow the initialized data in the mft bitmap, we + * extend the initialized size (and data size) by 8 bytes, allocating another + * cluster if required. The bitmap data size has to be at least equal to the + * number of mft records in the mft, but it can be bigger, in which case the + * superflous bits are padded with zeroes. + * + * Thus, when we return successfully (return value 0), we will have: + * - initialized / extended the mft bitmap if necessary, + * - initialized / extended the mft data if necessary, + * - set the bit corresponding to the mft record being allocated in the + * mft bitmap, and we will + * - return the mft record number in @*result. + * + * On error (return value below zero), nothing will have changed. If we had + * changed anything before the error occured, we will have reverted back to + * the starting state before returning to the caller. Thus, except for bugs, + * we should always leave the volume in a consitents state when returning from + * this function. NOTE: Small exception to this is that we set the bit in the + * mft bitmap but we do not mark the mft record in use, which is inconsistent. + * However, the caller will immediately add the wanted attributes to the mft + * record, set it in use and write it out to disk, so there should be no + * problem. + * + * Note, this function cannot make use of most of the normal functions, like + * for example for attribute resizing, etc, because when the run list overflows + * the base mft record and an attribute list is used, it is very important + * that the extension mft records used to store the $DATA attribute of $MFT + * can be reached without having to read the information contained inside + * them, as this would make it impossible to find them in the first place + * after the volume is dismounted. $MFT/$BITMAP probably doesn't need to + * follow this rule because the bitmap is not essential for finding the mft + * records, but on the other hand, handling the bitmap in this special way + * would make life easier because otherwise there might be circular invocations + * of functions when reading the bitmap but if we are careful, we should be + * able to avoid all problems. + * + * FIXME: Don't forget $MftMirr, though this probably belongs in + * ntfs_update_inode() (or even deeper). (AIA) + * + * FIXME: Want finer grained locking. (AIA) + */ +static int ntfs_alloc_mft_record(ntfs_volume *vol, unsigned long *result) +{ + unsigned long nr_mft_records, buf_size, buf_pos, pass_start, pass_end; + unsigned long last_read_pos, mft_rec_size, bit, l; + ntfs_attribute *data, *bmp; + __u8 *buf, *byte, pass, b, have_allocated_mftbmp = 0; + int rlen, rl_size = 0, r2len, rl2_size, old_data_rlen, err = 0; + ntfs_runlist *rl, *rl2; + ntfs_cluster_t lcn = 0, old_data_len; + ntfs_io io; + __s64 ll, old_data_allocated, old_data_initialized, old_data_size; + + *result = -1UL; + /* Allocate a buffer and setup the io structure. */ + buf = (__u8*)__get_free_page(GFP_NOFS); + if (!buf) + return -ENOMEM; + lock_kernel(); + /* Get the $DATA and $BITMAP attributes of $MFT. */ + data = ntfs_find_attr(vol->mft_ino, vol->at_data, 0); + bmp = ntfs_find_attr(vol->mft_ino, vol->at_bitmap, 0); + if (!data || !bmp) { + err = -EINVAL; + goto err_ret; + } + /* Determine the number of allocated mft records in the mft. */ + pass_end = nr_mft_records = data->allocated >> + vol->mft_record_size_bits; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): nr_mft_records = %lu.\n", + nr_mft_records); + /* Make sure we don't overflow the bitmap. */ + l = bmp->initialized << 3; + if (l < nr_mft_records) + // FIXME: It might be a good idea to extend the bitmap instead. + pass_end = l; + pass = 1; + buf_pos = vol->mft_data_pos; + if (buf_pos >= pass_end) { + buf_pos = 24UL; + pass = 2; + } + pass_start = buf_pos; + rl = bmp->d.r.runlist; + rlen = bmp->d.r.len - 1; + lcn = rl[rlen].lcn + rl[rlen].len; + io.fn_put = ntfs_put; + io.fn_get = ntfs_get; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Starting bitmap search.\n"); + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): pass = %i, pass_start = %lu, " + "pass_end = %lu.\n", pass, pass_start, pass_end); + byte = NULL; // FIXME: For debugging only. + /* Loop until a free mft record is found. */ + io.size = (nr_mft_records >> 3) & ~PAGE_MASK; + for (;; io.size = PAGE_SIZE) { + io.param = buf; + io.do_read = 1; + last_read_pos = buf_pos >> 3; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before: " + "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " + "bmp->initialized = 0x%Lx.\n", bmp->allocated, + bmp->size, bmp->initialized); + err = ntfs_readwrite_attr(vol->mft_ino, bmp, last_read_pos, + &io); + if (err) + goto err_ret; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Read %lu bytes.\n", + (unsigned long)io.size); + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After: " + "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " + "bmp->initialized = 0x%Lx.\n", bmp->allocated, + bmp->size, bmp->initialized); + if (!io.size) + goto pass_done; + buf_size = io.size << 3; + bit = buf_pos & 7UL; + buf_pos &= ~7UL; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before loop: " + "buf_size = %lu, buf_pos = %lu, bit = %lu, " + "*byte = 0x%x, b = %u.\n", + buf_size, buf_pos, bit, byte ? *byte : -1, b); + for (; bit < buf_size && bit + buf_pos < pass_end; + bit &= ~7UL, bit += 8UL) { + byte = buf + (bit >> 3); + if (*byte == 0xff) + continue; + b = ffz((unsigned long)*byte); + if (b < (__u8)8 && b >= (bit & 7UL)) { + bit = b + (bit & ~7UL) + buf_pos; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): " + "Found free rec in for loop. " + "bit = %lu\n", bit); + goto found_free_rec; + } + } + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After loop: " + "buf_size = %lu, buf_pos = %lu, bit = %lu, " + "*byte = 0x%x, b = %u.\n", + buf_size, buf_pos, bit, byte ? *byte : -1, b); + buf_pos += buf_size; + if (buf_pos < pass_end) + continue; +pass_done: /* Finished with the current pass. */ + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At pass_done.\n"); + if (pass == 1) { + /* + * Now do pass 2, scanning the first part of the zone + * we omitted in pass 1. + */ + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Done pass " + "1.\n"); + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Pass = 2.\n"); + pass = 2; + pass_end = pass_start; + buf_pos = pass_start = 24UL; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): pass = %i, " + "pass_start = %lu, pass_end = %lu.\n", + pass, pass_start, pass_end); + continue; + } /* pass == 2 */ + /* No free records left. */ + if (bmp->initialized << 3 > nr_mft_records && + bmp->initialized > 3) { + /* + * The mft bitmap is already bigger but the space is + * not covered by mft records, this implies that the + * next records are all free, so we already have found + * a free record. + */ + bit = nr_mft_records; + if (bit < 24UL) + bit = 24UL; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Found free " + "record bit (#1) = 0x%lx.\n", bit); + goto found_free_rec; + } + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Done pass 2.\n"); + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before: " + "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " + "bmp->initialized = 0x%Lx.\n", bmp->allocated, + bmp->size, bmp->initialized); + /* Need to extend the mft bitmap. */ + if (bmp->initialized + 8LL > bmp->allocated) { + ntfs_io io2; + + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Initialized " + "> allocated.\n"); + /* Need to extend bitmap by one more cluster. */ + rl = bmp->d.r.runlist; + rlen = bmp->d.r.len - 1; + lcn = rl[rlen].lcn + rl[rlen].len; + io2.fn_put = ntfs_put; + io2.fn_get = ntfs_get; + io2.param = &b; + io2.size = 1; + io2.do_read = 1; + err = ntfs_readwrite_attr(vol->bitmap, data, lcn >> 3, + &io2); + if (err) + goto err_ret; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Read %lu " + "bytes.\n", (unsigned long)io2.size); + if (io2.size == 1 && b != 0xff) { + __u8 tb = 1 << (lcn & (ntfs_cluster_t)7); + if (!(b & tb)) { + /* Next cluster is free. Allocate it. */ + b |= tb; + io2.param = &b; + io2.do_read = 0; + err = ntfs_readwrite_attr(vol->bitmap, + data, lcn >> 3, &io2); + if (err || io.size != 1) { + if (!err) + err = -EIO; + goto err_ret; + } +append_mftbmp_simple: rl[rlen].len++; + have_allocated_mftbmp |= 1; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ + "(): Appending one " + "cluster to mftbmp.\n"); + } + } + if (!have_allocated_mftbmp) { + /* Allocate a cluster from the DATA_ZONE. */ + ntfs_cluster_t lcn2 = lcn; + ntfs_cluster_t count = 1; + err = ntfs_allocate_clusters(vol, &lcn2, + &count, &rl2, &r2len, + DATA_ZONE); + if (err) + goto err_ret; + if (count != 1 || lcn2 <= 0) { + if (count > 0) { +rl2_dealloc_err_out: if (ntfs_deallocate_clusters( + vol, rl2, r2len)) + ntfs_error(__FUNCTION__ + "(): Cluster " + "deallocation in error " + "code path failed! You " + "should run chkdsk.\n"); + } + ntfs_vfree(rl2); + if (!err) + err = -EINVAL; + goto err_ret; + } + if (lcn2 == lcn) { + ntfs_vfree(rl2); + goto append_mftbmp_simple; + } + /* We need to append a new run. */ + rl_size = (rlen * sizeof(ntfs_runlist) + + PAGE_SIZE - 1) & PAGE_MASK; + /* Reallocate memory if necessary. */ + if ((rlen + 2) * sizeof(ntfs_runlist) >= + rl_size) { + ntfs_runlist *rlt; + + rl_size += PAGE_SIZE; + rlt = ntfs_vmalloc(rl_size); + if (!rlt) { + err = -ENOMEM; + goto rl2_dealloc_err_out; + } + ntfs_memcpy(rlt, rl, rl_size - + PAGE_SIZE); + ntfs_vfree(rl); + bmp->d.r.runlist = rl = rlt; + } + ntfs_vfree(rl2); + rl[rlen].lcn = lcn = lcn2; + rl[rlen].len = count; + bmp->d.r.len = ++rlen; + have_allocated_mftbmp |= 2; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): " + "Adding run to mftbmp. " + "LCN = %i, len = %i\n", lcn, + count); + } + /* + * We now have extended the mft bitmap allocated size + * by one cluster. Reflect this in the attribute. + */ + bmp->allocated += (__s64)vol->cluster_size; + } + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After: " + "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " + "bmp->initialized = 0x%Lx.\n", bmp->allocated, + bmp->size, bmp->initialized); + /* We now have sufficient allocated space. */ + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Now have sufficient " + "allocated space in mftbmp.\n"); + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before: " + "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " + "bmp->initialized = 0x%Lx.\n", bmp->allocated, + bmp->size, bmp->initialized); + buf_pos = bmp->initialized; + bmp->initialized += 8LL; + if (bmp->initialized > bmp->size) + bmp->size = bmp->initialized; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After: " + "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " + "bmp->initialized = 0x%Lx.\n", bmp->allocated, + bmp->size, bmp->initialized); + have_allocated_mftbmp |= 4; + /* Update the mft bitmap attribute value. */ + memset(buf, 0, 8); + io.param = buf; + io.size = 8; + io.do_read = 0; + err = ntfs_readwrite_attr(vol->mft_ino, bmp, buf_pos, &io); + if (err || io.size != 8) { + if (!err) + err = -EIO; + goto shrink_mftbmp_err_ret; + } + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Wrote extended " + "mftbmp bytes %lu.\n", (unsigned long)io.size); + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After write: " + "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " + "bmp->initialized = 0x%Lx.\n", bmp->allocated, + bmp->size, bmp->initialized); + bit = buf_pos << 3; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Found free record " + "bit (#2) = 0x%lx.\n", bit); + goto found_free_rec; + } +found_free_rec: + /* bit is the found free mft record. Allocate it in the mft bitmap. */ + vol->mft_data_pos = bit; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At found_free_rec.\n"); + io.param = buf; + io.size = 1; + io.do_read = 1; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before update: " + "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " + "bmp->initialized = 0x%Lx.\n", bmp->allocated, + bmp->size, bmp->initialized); + err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io); + if (err || io.size != 1) { + if (!err) + err = -EIO; + goto shrink_mftbmp_err_ret; + } + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Read %lu bytes.\n", + (unsigned long)io.size); +#ifdef DEBUG + /* Check our bit is really zero! */ + if (*buf & (1 << (bit & 7))) + BUG(); +#endif + *buf |= 1 << (bit & 7); + io.param = buf; + io.do_read = 0; + err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io); + if (err || io.size != 1) { + if (!err) + err = -EIO; + goto shrink_mftbmp_err_ret; + } + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Wrote %lu bytes.\n", + (unsigned long)io.size); + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After update: " + "bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, " + "bmp->initialized = 0x%Lx.\n", bmp->allocated, + bmp->size, bmp->initialized); + /* The mft bitmap is now uptodate. Deal with mft data attribute now. */ + ll = (__s64)(bit + 1) << vol->mft_record_size_bits; + if (ll <= data->initialized) { + /* The allocated record is already initialized. We are done! */ + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Allocated mft record " + "already initialized!\n"); + goto done_ret; + } + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Allocated mft record needs " + "to be initialized.\n"); + /* The mft record is outside the initialized data. */ + mft_rec_size = (unsigned long)vol->mft_record_size; + /* Preserve old values for undo purposes. */ + old_data_allocated = data->allocated; + old_data_rlen = data->d.r.len - 1; + old_data_len = data->d.r.runlist[old_data_rlen].len; + /* + * If necessary, extend the mft until it covers the allocated record. + * The loop is only actually used when a freshly formatted volume is + * first written to. But it optimizes away nicely in the common case. + */ + while (ll > data->allocated) { + ntfs_cluster_t lcn2, nr_lcn2, nr, min_nr; + + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Extending mft " + "data allocation, data->allocated = 0x%Lx, " + "data->size = 0x%Lx, data->initialized = " + "0x%Lx.\n", data->allocated, data->size, + data->initialized); + /* Minimum allocation is one mft record worth of clusters. */ + if (mft_rec_size <= vol->cluster_size) + min_nr = (ntfs_cluster_t)1; + else + min_nr = mft_rec_size >> vol->cluster_size_bits; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): min_nr = %i.\n", + min_nr); + /* Allocate 16 mft records worth of clusters. */ + nr = mft_rec_size << 4 >> vol->cluster_size_bits; + if (!nr) + nr = (ntfs_cluster_t)1; + /* Determine the preferred allocation location. */ + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): nr = %i.\n", nr); + rl2 = data->d.r.runlist; + r2len = data->d.r.len; + lcn2 = rl2[r2len - 1].lcn + rl2[r2len - 1].len; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): rl2[r2len - 1].lcn " + "= %i, .len = %i.\n", rl2[r2len - 1].lcn, + rl2[r2len - 1].len); + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): lcn2 = %i, r2len = " + "%i.\n", lcn2, r2len); +retry_mft_data_allocation: + nr_lcn2 = nr; + err = ntfs_allocate_clusters(vol, &lcn2, &nr_lcn2, &rl2, + &r2len, MFT_ZONE); +#ifdef DEBUG + if (!err && nr_lcn2 < min_nr) + /* Allocated less than minimum needed. Weird! */ + BUG(); +#endif + if (err) { + /* + * If there isn't enough space to do the wanted + * allocation, but there is enough space to do a + * minimal allocation, then try that, unless the wanted + * allocation was already the minimal allocation. + */ + if (err == -ENOSPC && nr > min_nr && + nr_lcn2 >= min_nr) { + nr = min_nr; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): " + "Retrying mft data " + "allocation, nr = min_nr = %i" + ".\n", nr); + goto retry_mft_data_allocation; + } + goto undo_mftbmp_alloc_err_ret; + } + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Allocated %i " + "clusters starting at LCN %i.\n", nr_lcn2, + lcn2); + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Allocated " + "runlist:\n"); + dump_runlist(rl2, r2len); + /* Append rl2 to the mft data attribute's run list. */ + err = splice_runlists(&data->d.r.runlist, (int*)&data->d.r.len, + rl2, r2len); + if (err) { + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): " + "splice_runlists failed with error " + "code %i.\n", -err); + goto undo_partial_data_alloc_err_ret; + } + /* Reflect the allocated clusters in the mft allocated data. */ + data->allocated += nr_lcn2 << vol->cluster_size_bits; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After extending mft " + "data allocation, data->allocated = 0x%Lx, " + "data->size = 0x%Lx, data->initialized = " + "0x%Lx.\n", data->allocated, data->size, + data->initialized); + } + /* Prepare a formatted (empty) mft record. */ + memset(buf, 0, mft_rec_size); + ntfs_fill_mft_header(buf, mft_rec_size, 0, 0, 0); + err = ntfs_insert_fixups(buf, mft_rec_size); + if (err) + goto undo_data_alloc_err_ret; + /* + * Extend mft data initialized size to reach the allocated mft record + * and write the formatted mft record buffer to each mft record being + * initialized. Note, that ntfs_readwrite_attr extends both + * data->initialized and data->size, so no need for us to touch them. + */ + old_data_initialized = data->initialized; + old_data_size = data->size; + while (ll > data->initialized) { + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Initializing mft " + "record 0x%Lx.\n", + data->initialized >> vol->mft_record_size_bits); + io.param = buf; + io.size = mft_rec_size; + io.do_read = 0; + err = ntfs_readwrite_attr(vol->mft_ino, data, + data->initialized, &io); + if (err || io.size != mft_rec_size) { + if (!err) + err = -EIO; + goto undo_data_init_err_ret; + } + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Wrote %i bytes to " + "mft data.\n", io.size); + } + /* Update the VFS inode size as well. */ + VFS_I(vol->mft_ino)->i_size = data->size; +#ifdef DEBUG + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After mft record " + "initialization: data->allocated = 0x%Lx, data->size " + "= 0x%Lx, data->initialized = 0x%Lx.\n", + data->allocated, data->size, data->initialized); + /* Sanity checks. */ + if (data->size > data->allocated || data->size < data->initialized || + data->initialized > data->allocated) + BUG(); +#endif +done_ret: + /* Return the number of the allocated mft record. */ + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At done_ret. *result = bit = " + "0x%lx.\n", bit); + *result = bit; + vol->mft_data_pos = bit + 1; +err_ret: + unlock_kernel(); + free_page((unsigned long)buf); + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Syncing inode $MFT.\n"); + if (ntfs_update_inode(vol->mft_ino)) + ntfs_error(__FUNCTION__ "(): Failed to sync inode $MFT. " + "Continuing anyway.\n"); + if (!err) { + ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): Done. Allocated mft " + "record number *result = 0x%lx.\n", *result); + return 0; + } + if (err != -ENOSPC) + ntfs_error(__FUNCTION__ "(): Failed to allocate an mft " + "record. Returning error code %i.\n", -err); + else + ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): Failed to allocate " + "an mft record due to lack of free space.\n"); + return err; +undo_data_init_err_ret: + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At " + "undo_data_init_err_ret.\n"); + data->initialized = old_data_initialized; + data->size = old_data_size; +undo_data_alloc_err_ret: + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At undo_data_alloc_err_ret." + "\n"); + data->allocated = old_data_allocated; +undo_partial_data_alloc_err_ret: + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At " + "undo_partial_data_alloc_err_ret.\n"); + /* Deallocate the clusters. */ + if (ntfs_deallocate_clusters(vol, rl2, r2len)) + ntfs_error(__FUNCTION__ "(): Error deallocating clusters in " + "error code path. You should run chkdsk.\n"); + ntfs_vfree(rl2); + /* Revert the run list back to what it was before. */ + r2len = data->d.r.len; + rl2 = data->d.r.runlist; + rl2[old_data_rlen++].len = old_data_len; + rl2[old_data_rlen].lcn = (ntfs_cluster_t)-1; + rl2[old_data_rlen].len = (ntfs_cluster_t)0; + data->d.r.len = old_data_rlen; + rl2_size = ((old_data_rlen + 1) * sizeof(ntfs_runlist) + PAGE_SIZE - + 1) & PAGE_MASK; + /* Reallocate memory freeing any extra memory allocated. */ + if (rl2_size < ((r2len * sizeof(ntfs_runlist) + PAGE_SIZE - 1) & + PAGE_MASK)) { + rl2 = ntfs_vmalloc(rl2_size); + if (rl2) { + ntfs_memcpy(rl2, data->d.r.runlist, rl2_size); + ntfs_vfree(data->d.r.runlist); + data->d.r.runlist = rl2; + } else + ntfs_error(__FUNCTION__ "(): Error reallocating " + "memory in error code path. This " + "should be harmless.\n"); + } +undo_mftbmp_alloc_err_ret: + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At " + "undo_mftbmp_alloc_err_ret.\n"); + /* Deallocate the allocated bit in the mft bitmap. */ + io.param = buf; + io.size = 1; + io.do_read = 1; + err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io); + if (!err && io.size == 1) { + *buf &= ~(1 << (bit & 7)); + io.param = buf; + io.do_read = 0; + err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io); + } + if (err || io.size != 1) { + if (!err) + err = -EIO; + ntfs_error(__FUNCTION__ "(): Error deallocating mft record in " + "error code path. You should run chkdsk.\n"); + } +shrink_mftbmp_err_ret: + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At shrink_mftbmp_err_ret.\n"); + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): have_allocated_mftbmp = " + "%i.\n", have_allocated_mftbmp); + if (!have_allocated_mftbmp) + goto err_ret; + /* Shrink the mftbmp back to previous size. */ + if (bmp->size == bmp->initialized) + bmp->size -= 8LL; + bmp->initialized -= 8LL; + have_allocated_mftbmp &= ~4; + /* If no allocation occured then we are done. */ + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): have_allocated_mftbmp = " + "%i.\n", have_allocated_mftbmp); + if (!have_allocated_mftbmp) + goto err_ret; + /* Deallocate the allocated cluster. */ + bmp->allocated -= (__s64)vol->cluster_size; + if (ntfs_deallocate_cluster_run(vol, lcn, (ntfs_cluster_t)1)) + ntfs_error(__FUNCTION__ "(): Error deallocating cluster in " + "error code path. You should run chkdsk.\n"); + switch (have_allocated_mftbmp & 3) { + case 1: + /* Delete the last lcn from the last run of mftbmp. */ + rl[rlen - 1].len--; + break; + case 2: + /* Delete the last run of mftbmp. */ + bmp->d.r.len = --rlen; + /* Reallocate memory if necessary. */ + if ((rlen + 1) * sizeof(ntfs_runlist) <= rl_size - PAGE_SIZE) { + ntfs_runlist *rlt; + + rl_size -= PAGE_SIZE; + rlt = ntfs_vmalloc(rl_size); + if (rlt) { + ntfs_memcpy(rlt, rl, rl_size); + ntfs_vfree(rl); + bmp->d.r.runlist = rl = rlt; + } else + ntfs_error(__FUNCTION__ "(): Error " + "reallocating memory in error " + "code path. This should be " + "harmless.\n"); + } + bmp->d.r.runlist[bmp->d.r.len].lcn = (ntfs_cluster_t)-1; + bmp->d.r.runlist[bmp->d.r.len].len = (ntfs_cluster_t)0; + break; + default: + BUG(); + } + goto err_ret; +} + +/* We need 0x48 bytes in total. */ +static int add_standard_information(ntfs_inode *ino) +{ + ntfs_time64_t now; + char data[0x30]; + char *position = data; + ntfs_attribute *si; + + now = ntfs_now(); + NTFS_PUTU64(position + 0x00, now); /* File creation */ + NTFS_PUTU64(position + 0x08, now); /* Last modification */ + NTFS_PUTU64(position + 0x10, now); /* Last mod for MFT */ + NTFS_PUTU64(position + 0x18, now); /* Last access */ + NTFS_PUTU64(position + 0x20, 0); /* MSDOS file perms */ + NTFS_PUTU64(position + 0x28, 0); /* unknown */ + return ntfs_create_attr(ino, ino->vol->at_standard_information, 0, + data, sizeof(data), &si); +} + +static int add_filename(ntfs_inode *ino, ntfs_inode *dir, + const unsigned char *filename, int length, ntfs_u32 flags) +{ + unsigned char *position; + unsigned int size; + ntfs_time64_t now; + int count, error; + unsigned char* data; + ntfs_attribute *fn; + + /* Work out the size. */ + size = 0x42 + 2 * length; + data = ntfs_malloc(size); + if (!data) + return -ENOMEM; + /* Search for a position. */ + position = data; + NTFS_PUTINUM(position, dir); /* Inode num of dir */ + now = ntfs_now(); + NTFS_PUTU64(position + 0x08, now); /* File creation */ + NTFS_PUTU64(position + 0x10, now); /* Last modification */ + NTFS_PUTU64(position + 0x18, now); /* Last mod for MFT */ + NTFS_PUTU64(position + 0x20, now); /* Last access */ + /* FIXME: Get the following two sizes by finding the data attribute + * in ino->attr and copying the corresponding fields from there. + * If no data present then set to zero. In current implementation + * add_data is called after add_filename so zero is correct on + * creation. Need to change when we have hard links / support different + * filename namespaces. (AIA) */ + NTFS_PUTS64(position + 0x28, 0); /* Allocated size */ + NTFS_PUTS64(position + 0x30, 0); /* Data size */ + NTFS_PUTU32(position + 0x38, flags); /* File flags */ + NTFS_PUTU32(position + 0x3c, 0); /* We don't use these + * features yet. */ + NTFS_PUTU8(position + 0x40, length); /* Filename length */ + NTFS_PUTU8(position + 0x41, 0); /* Only long name */ + /* FIXME: This is madness. We are defining the POSIX namespace + * for the filename here which can mean that the file will be + * invisible when in Windows NT/2k! )-: (AIA) */ + position += 0x42; + for (count = 0; count < length; count++) { + NTFS_PUTU16(position + 2 * count, filename[count]); + } + error = ntfs_create_attr(ino, ino->vol->at_file_name, 0, data, size, + &fn); + if (!error) + error = ntfs_dir_add(dir, ino, fn); + ntfs_free(data); + return error; +} + +int add_security(ntfs_inode* ino, ntfs_inode* dir) +{ + int error; + char *buf; + int size; + ntfs_attribute* attr; + ntfs_io io; + ntfs_attribute *se; + + attr = ntfs_find_attr(dir, ino->vol->at_security_descriptor, 0); + if (!attr) + return -EOPNOTSUPP; /* Need security in directory. */ + size = attr->size; + if (size > 512) + return -EOPNOTSUPP; + buf = ntfs_malloc(size); + if (!buf) + return -ENOMEM; + io.fn_get = ntfs_get; + io.fn_put = ntfs_put; + io.param = buf; + io.size = size; + error = ntfs_read_attr(dir, ino->vol->at_security_descriptor, 0, 0,&io); + if (!error && io.size != size) + ntfs_error("wrong size in add_security\n"); + if (error) { + ntfs_free(buf); + return error; + } + /* FIXME: Consider ACL inheritance. */ + error = ntfs_create_attr(ino, ino->vol->at_security_descriptor, + 0, buf, size, &se); + ntfs_free(buf); + return error; +} + +static int add_data(ntfs_inode* ino, unsigned char *data, int length) +{ + ntfs_attribute *da; + + return ntfs_create_attr(ino, ino->vol->at_data, 0, data, length, &da); +} + +/* + * We _could_ use 'dir' to help optimise inode allocation. + * + * FIXME: Need to undo what we do in ntfs_alloc_mft_record if we get an error + * further on in ntfs_alloc_inode. Either fold the two functions to allow + * proper undo or just deallocate the record from the mft bitmap. (AIA) + */ +int ntfs_alloc_inode(ntfs_inode *dir, ntfs_inode *result, const char *filename, + int namelen, ntfs_u32 flags) +{ + ntfs_volume *vol = dir->vol; + int err; + ntfs_u8 buffer[2]; + ntfs_io io; + + err = ntfs_alloc_mft_record(vol, &(result->i_number)); + if (err) { + if (err == -ENOSPC) + ntfs_error(__FUNCTION__ "(): No free inodes.\n"); + return err; + } + /* Get the sequence number. */ + io.fn_put = ntfs_put; + io.fn_get = ntfs_get; + io.param = buffer; + io.size = 2; + err = ntfs_read_attr(vol->mft_ino, vol->at_data, 0, + ((__s64)result->i_number << vol->mft_record_size_bits) + + 0x10, &io); + // FIXME: We are leaving the MFT in inconsistent state! (AIA) + if (err) + return err; + /* Increment the sequence number skipping zero. */ + result->sequence_number = (NTFS_GETU16(buffer) + 1) & 0xffff; + if (!result->sequence_number) + result->sequence_number++; + result->vol = vol; + result->attr_count = 0; + result->attrs = 0; + result->record_count = 1; + result->records = ntfs_calloc(8 * sizeof(int)); + if (!result->records) + goto mem_err_out; + result->records[0] = result->i_number; + result->attr = ntfs_calloc(vol->mft_record_size); + if (!result->attr) { + ntfs_free(result->records); + result->records = NULL; + goto mem_err_out; + } + ntfs_fill_mft_header(result->attr, vol->mft_record_size, + result->sequence_number, 1, 1); + err = add_standard_information(result); + if (!err) + err = add_filename(result, dir, filename, namelen, flags); + if (!err) + err = add_security(result, dir); + // FIXME: We are leaving the MFT in inconsistent state on error! (AIA) + return err; +mem_err_out: + // FIXME: We are leaving the MFT in inconsistent state! (AIA) + result->record_count = 0; + result->attr = NULL; + return -ENOMEM; +} + +int ntfs_alloc_file(ntfs_inode *dir, ntfs_inode *result, char *filename, + int namelen) +{ + int err; + + err = ntfs_alloc_inode(dir, result, filename, namelen, 0); + if (!err) + err = add_data(result, 0, 0); + return err; +} +