/* * 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; }