X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fntfs%2Finode.c;h=873c25d9830ffb625beb5f3b03f4bf3ca3373bf2;hb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;hp=1abe1b399811dfbd443a8ffff71f5de03b660d02;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 1abe1b399..873c25d98 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -105,8 +105,11 @@ static int ntfs_init_locked_inode(struct inode *vi, ntfs_attr *na) ni->name_len = na->name_len; /* If initializing a normal inode, we are done. */ - if (likely(na->type == AT_UNUSED)) + if (likely(na->type == AT_UNUSED)) { + BUG_ON(na->name); + BUG_ON(na->name_len); return 0; + } /* It is a fake inode. */ NInoSetAttr(ni); @@ -118,15 +121,16 @@ static int ntfs_init_locked_inode(struct inode *vi, ntfs_attr *na) * thus the fraction of named attributes with name != I30 is actually * absolutely tiny. */ - if (na->name && na->name_len && na->name != I30) { + if (na->name_len && na->name != I30) { unsigned int i; + BUG_ON(!na->name); i = na->name_len * sizeof(ntfschar); ni->name = (ntfschar*)kmalloc(i + sizeof(ntfschar), GFP_ATOMIC); if (!ni->name) return -ENOMEM; memcpy(ni->name, na->name, i); - ni->name[i] = cpu_to_le16('\0'); + ni->name[i] = 0; } return 0; } @@ -134,6 +138,8 @@ static int ntfs_init_locked_inode(struct inode *vi, ntfs_attr *na) typedef int (*set_t)(struct inode *, void *); static int ntfs_read_locked_inode(struct inode *vi); static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi); +static int ntfs_read_locked_index_inode(struct inode *base_vi, + struct inode *vi); /** * ntfs_iget - obtain a struct inode corresponding to a specific normal inode @@ -201,17 +207,23 @@ struct inode *ntfs_iget(struct super_block *sb, unsigned long mft_no) * initialized, and finally ntfs_read_locked_attr_inode() is called to read the * attribute and fill in the inode structure. * + * Note, for index allocation attributes, you need to use ntfs_index_iget() + * instead of ntfs_attr_iget() as working with indices is a lot more complex. + * * Return the struct inode of the attribute inode on success. Check the return * value with IS_ERR() and if true, the function failed and the error code is * obtained from PTR_ERR(). */ -struct inode *ntfs_attr_iget(struct inode *base_vi, ATTR_TYPES type, +struct inode *ntfs_attr_iget(struct inode *base_vi, ATTR_TYPE type, ntfschar *name, u32 name_len) { struct inode *vi; ntfs_attr na; int err; + /* Make sure no one calls ntfs_attr_iget() for indices. */ + BUG_ON(type == AT_INDEX_ALLOCATION); + na.mft_no = base_vi->i_ino; na.type = type; na.name = name; @@ -241,6 +253,61 @@ struct inode *ntfs_attr_iget(struct inode *base_vi, ATTR_TYPES type, return vi; } +/** + * ntfs_index_iget - obtain a struct inode corresponding to an index + * @base_vi: vfs base inode containing the index related attributes + * @name: Unicode name of the index + * @name_len: length of @name in Unicode characters + * + * Obtain the (fake) struct inode corresponding to the index specified by @name + * and @name_len, which is present in the base mft record specified by the vfs + * inode @base_vi. + * + * If the index inode is in the cache, it is just returned with an increased + * reference count. Otherwise, a new struct inode is allocated and + * initialized, and finally ntfs_read_locked_index_inode() is called to read + * the index related attributes and fill in the inode structure. + * + * Return the struct inode of the index inode on success. Check the return + * value with IS_ERR() and if true, the function failed and the error code is + * obtained from PTR_ERR(). + */ +struct inode *ntfs_index_iget(struct inode *base_vi, ntfschar *name, + u32 name_len) +{ + struct inode *vi; + ntfs_attr na; + int err; + + na.mft_no = base_vi->i_ino; + na.type = AT_INDEX_ALLOCATION; + na.name = name; + na.name_len = name_len; + + vi = iget5_locked(base_vi->i_sb, na.mft_no, (test_t)ntfs_test_inode, + (set_t)ntfs_init_locked_inode, &na); + if (!vi) + return ERR_PTR(-ENOMEM); + + err = 0; + + /* If this is a freshly allocated inode, need to read it now. */ + if (vi->i_state & I_NEW) { + err = ntfs_read_locked_index_inode(base_vi, vi); + unlock_new_inode(vi); + } + /* + * There is no point in keeping bad index inodes around. This also + * simplifies things in that we never need to check for bad index + * inodes elsewhere. + */ + if (err) { + iput(vi); + vi = ERR_PTR(err); + } + return vi; +} + struct inode *ntfs_alloc_big_inode(struct super_block *sb) { ntfs_inode *ni; @@ -309,16 +376,17 @@ static void __ntfs_init_inode(struct super_block *sb, ntfs_inode *ni) ni->seq_no = 0; atomic_set(&ni->count, 1); ni->vol = NTFS_SB(sb); - init_run_list(&ni->run_list); + init_runlist(&ni->runlist); init_MUTEX(&ni->mrec_lock); ni->page = NULL; ni->page_ofs = 0; ni->attr_list_size = 0; ni->attr_list = NULL; - init_run_list(&ni->attr_list_rl); + init_runlist(&ni->attr_list_rl); ni->itype.index.bmp_ino = NULL; ni->itype.index.block_size = 0; ni->itype.index.vcn_size = 0; + ni->itype.index.collation_rule = 0; ni->itype.index.block_size_bits = 0; ni->itype.index.vcn_size_bits = 0; init_MUTEX(&ni->extent_lock); @@ -364,20 +432,21 @@ inline ntfs_inode *ntfs_new_extent_inode(struct super_block *sb, * Return values: * 1: file is in $Extend directory * 0: file is not in $Extend directory - * -EIO: file is corrupt + * -errno: failed to determine if the file is in the $Extend directory */ -static int ntfs_is_extended_system_file(attr_search_context *ctx) +static int ntfs_is_extended_system_file(ntfs_attr_search_ctx *ctx) { - int nr_links; + int nr_links, err; /* Restart search. */ - reinit_attr_search_ctx(ctx); + ntfs_attr_reinit_search_ctx(ctx); /* Get number of hard links. */ nr_links = le16_to_cpu(ctx->mrec->link_count); /* Loop through all hard links. */ - while (lookup_attr(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) { + while (!(err = ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, + ctx))) { FILE_NAME_ATTR *file_name_attr; ATTR_RECORD *attr = ctx->attr; u8 *p, *p2; @@ -420,7 +489,9 @@ err_corrupt_attr: if (MREF_LE(file_name_attr->parent_directory) == FILE_Extend) return 1; /* YES, it's an extended system file. */ } - if (nr_links) { + if (unlikely(err != -ENOENT)) + return err; + if (unlikely(nr_links)) { ntfs_error(ctx->ntfs_ino->vol->sb, "Inode hard link count " "doesn't match number of name attributes. You " "should run chkdsk."); @@ -438,9 +509,7 @@ err_corrupt_attr: * * The only fields in @vi that we need to/can look at when the function is * called are i_sb, pointing to the mounted device's super block, and i_ino, - * the number of the inode to load. If this is a fake inode, i.e. NInoAttr(), - * then the fields type, name, and name_len are also valid, and describe the - * attribute which this fake inode represents. + * the number of the inode to load. * * ntfs_read_locked_inode() maps, pins and locks the mft record number i_ino * for reading and sets up the necessary @vi fields as well as initializing @@ -449,12 +518,12 @@ err_corrupt_attr: * Q: What locks are held when the function is called? * A: i_state has I_LOCK set, hence the inode is locked, also * i_count is set to 1, so it is not going to go away - * i_flags is set to 0 and we have no business touching it. Only an ioctl() + * i_flags is set to 0 and we have no business touching it. Only an ioctl() * is allowed to write to them. We should of course be honouring them but * we need to do that using the IS_* macros defined in include/linux/fs.h. * In any case ntfs_read_locked_inode() has nothing to do with i_flags. * - * Return 0 on success and -errno on error. In the error case, the inode will + * Return 0 on success and -errno on error. In the error case, the inode will * have had make_bad_inode() executed on it. */ static int ntfs_read_locked_inode(struct inode *vi) @@ -463,7 +532,7 @@ static int ntfs_read_locked_inode(struct inode *vi) ntfs_inode *ni; MFT_RECORD *m; STANDARD_INFORMATION *si; - attr_search_context *ctx; + ntfs_attr_search_ctx *ctx; int err = 0; ntfs_debug("Entering for i_ino 0x%lx.", vi->i_ino); @@ -495,7 +564,7 @@ static int ntfs_read_locked_inode(struct inode *vi) err = PTR_ERR(m); goto err_out; } - ctx = get_attr_search_ctx(ni, m); + ctx = ntfs_attr_get_search_ctx(ni, m); if (!ctx) { err = -ENOMEM; goto unm_err_out; @@ -546,21 +615,25 @@ static int ntfs_read_locked_inode(struct inode *vi) * in fact fail if the standard information is in an extent record, but * I don't think this actually ever happens. */ - if (!lookup_attr(AT_STANDARD_INFORMATION, NULL, 0, 0, 0, NULL, 0, - ctx)) { - /* - * TODO: We should be performing a hot fix here (if the recover - * mount option is set) by creating a new attribute. - */ - ntfs_error(vi->i_sb, "$STANDARD_INFORMATION attribute is " - "missing."); + err = ntfs_attr_lookup(AT_STANDARD_INFORMATION, NULL, 0, 0, 0, NULL, 0, + ctx); + if (unlikely(err)) { + if (err == -ENOENT) { + /* + * TODO: We should be performing a hot fix here (if the + * recover mount option is set) by creating a new + * attribute. + */ + ntfs_error(vi->i_sb, "$STANDARD_INFORMATION attribute " + "is missing."); + } goto unm_err_out; } /* Get the standard information attribute value. */ si = (STANDARD_INFORMATION*)((char*)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset)); - /* Transfer information from the standard information into vfs_ino. */ + /* Transfer information from the standard information into vi. */ /* * Note: The i_?times do not quite map perfectly onto the NTFS times, * but they are close enough, and in the end it doesn't really matter @@ -584,8 +657,15 @@ static int ntfs_read_locked_inode(struct inode *vi) vi->i_atime = ntfs2utc(si->last_access_time); /* Find the attribute list attribute if present. */ - reinit_attr_search_ctx(ctx); - if (lookup_attr(AT_ATTRIBUTE_LIST, NULL, 0, 0, 0, NULL, 0, ctx)) { + ntfs_attr_reinit_search_ctx(ctx); + err = ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, 0, 0, NULL, 0, ctx); + if (err) { + if (unlikely(err != -ENOENT)) { + ntfs_error(vi->i_sb, "Failed to lookup attribute list " + "attribute. You should run chkdsk."); + goto unm_err_out; + } + } else /* if (!err) */ { if (vi->i_ino == FILE_MFT) goto skip_attr_list_load; ntfs_debug("Attribute list found in inode 0x%lx.", vi->i_ino); @@ -600,7 +680,7 @@ static int ntfs_read_locked_inode(struct inode *vi) goto unm_err_out; } /* Now allocate memory for the attribute list. */ - ni->attr_list_size = (u32)attribute_value_length(ctx->attr); + ni->attr_list_size = (u32)ntfs_attr_size(ctx->attr); ni->attr_list = ntfs_malloc_nofs(ni->attr_list_size); if (!ni->attr_list) { ntfs_error(vi->i_sb, "Not enough memory to allocate " @@ -618,7 +698,7 @@ static int ntfs_read_locked_inode(struct inode *vi) goto unm_err_out; } /* - * Setup the run list. No need for locking as we have + * Setup the runlist. No need for locking as we have * exclusive access to the inode at this time. */ ni->attr_list_rl.rl = decompress_mapping_pairs(vol, @@ -671,13 +751,17 @@ skip_attr_list_load: char *ir_end, *index_end; /* It is a directory, find index root attribute. */ - reinit_attr_search_ctx(ctx); - if (!lookup_attr(AT_INDEX_ROOT, I30, 4, CASE_SENSITIVE, 0, - NULL, 0, ctx)) { - // FIXME: File is corrupt! Hot-fix with empty index - // root attribute if recovery option is set. - ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is " - "missing."); + ntfs_attr_reinit_search_ctx(ctx); + err = ntfs_attr_lookup(AT_INDEX_ROOT, I30, 4, CASE_SENSITIVE, + 0, NULL, 0, ctx); + if (unlikely(err)) { + if (err == -ENOENT) { + // FIXME: File is corrupt! Hot-fix with empty + // index root attribute if recovery option is + // set. + ntfs_error(vi->i_sb, "$INDEX_ROOT attribute " + "is missing."); + } goto unm_err_out; } /* Set up the state. */ @@ -730,6 +814,7 @@ skip_attr_list_load: "COLLATION_FILE_NAME. Not allowed."); goto unm_err_out; } + ni->itype.index.collation_rule = ir->collation_rule; ni->itype.index.block_size = le32_to_cpu(ir->index_block_size); if (ni->itype.index.block_size & (ni->itype.index.block_size - 1)) { @@ -778,7 +863,7 @@ skip_attr_list_load: vi->i_size = ni->initialized_size = ni->allocated_size = 0; /* We are done with the mft record, so we release it. */ - put_attr_search_ctx(ctx); + ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); m = NULL; ctx = NULL; @@ -786,12 +871,19 @@ skip_attr_list_load: } /* LARGE_INDEX: Index allocation present. Setup state. */ NInoSetIndexAllocPresent(ni); /* Find index allocation attribute. */ - reinit_attr_search_ctx(ctx); - if (!lookup_attr(AT_INDEX_ALLOCATION, I30, 4, CASE_SENSITIVE, - 0, NULL, 0, ctx)) { - ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute " - "is not present but $INDEX_ROOT " - "indicated it is."); + ntfs_attr_reinit_search_ctx(ctx); + err = ntfs_attr_lookup(AT_INDEX_ALLOCATION, I30, 4, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (unlikely(err)) { + if (err == -ENOENT) + ntfs_error(vi->i_sb, "$INDEX_ALLOCATION " + "attribute is not present but " + "$INDEX_ROOT indicated it " + "is."); + else + ntfs_error(vi->i_sb, "Failed to lookup " + "$INDEX_ALLOCATION " + "attribute."); goto unm_err_out; } if (!ctx->attr->non_resident) { @@ -831,13 +923,13 @@ skip_attr_list_load: * We are done with the mft record, so we release it. Otherwise * we would deadlock in ntfs_attr_iget(). */ - put_attr_search_ctx(ctx); + ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); m = NULL; ctx = NULL; /* Get the index bitmap attribute inode. */ bvi = ntfs_attr_iget(vi, AT_BITMAP, I30, 4); - if (unlikely(IS_ERR(bvi))) { + if (IS_ERR(bvi)) { ntfs_error(vi->i_sb, "Failed to get bitmap attribute."); err = PTR_ERR(bvi); goto unm_err_out; @@ -875,7 +967,7 @@ skip_large_dir_stuff: vi->i_mapping->a_ops = &ntfs_mst_aops; } else { /* It is a file. */ - reinit_attr_search_ctx(ctx); + ntfs_attr_reinit_search_ctx(ctx); /* Setup the data attribute, even if not present. */ ni->type = AT_DATA; @@ -883,9 +975,15 @@ skip_large_dir_stuff: ni->name_len = 0; /* Find first extent of the unnamed data attribute. */ - if (!lookup_attr(AT_DATA, NULL, 0, 0, 0, NULL, 0, ctx)) { + err = ntfs_attr_lookup(AT_DATA, NULL, 0, 0, 0, NULL, 0, ctx); + if (unlikely(err)) { vi->i_size = ni->initialized_size = - ni->allocated_size = 0LL; + ni->allocated_size = 0; + if (err != -ENOENT) { + ntfs_error(vi->i_sb, "Failed to lookup $DATA " + "attribute."); + goto unm_err_out; + } /* * FILE_Secure does not have an unnamed $DATA * attribute, so we special case it here. @@ -996,7 +1094,7 @@ skip_large_dir_stuff: } no_data_attr_special_case: /* We are done with the mft record, so we release it. */ - put_attr_search_ctx(ctx); + ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); m = NULL; ctx = NULL; @@ -1035,7 +1133,7 @@ unm_err_out: if (!err) err = -EIO; if (ctx) - put_attr_search_ctx(ctx); + ntfs_attr_put_search_ctx(ctx); if (m) unmap_mft_record(ni); err_out: @@ -1050,8 +1148,8 @@ err_out: * @base_vi: base inode * @vi: attribute inode to read * - * ntfs_read_locked_attr_inode() is called from the ntfs_attr_iget() to read - * the attribute inode described by @vi into memory from the base mft record + * ntfs_read_locked_attr_inode() is called from ntfs_attr_iget() to read the + * attribute inode described by @vi into memory from the base mft record * described by @base_ni. * * ntfs_read_locked_attr_inode() maps, pins and locks the base inode for @@ -1061,13 +1159,16 @@ err_out: * Q: What locks are held when the function is called? * A: i_state has I_LOCK set, hence the inode is locked, also * i_count is set to 1, so it is not going to go away + * + * Return 0 on success and -errno on error. In the error case, the inode will + * have had make_bad_inode() executed on it. */ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) { ntfs_volume *vol = NTFS_SB(vi->i_sb); ntfs_inode *ni, *base_ni; MFT_RECORD *m; - attr_search_context *ctx; + ntfs_attr_search_ctx *ctx; int err = 0; ntfs_debug("Entering for i_ino 0x%lx.", vi->i_ino); @@ -1096,15 +1197,16 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) err = PTR_ERR(m); goto err_out; } - ctx = get_attr_search_ctx(base_ni, m); + ctx = ntfs_attr_get_search_ctx(base_ni, m); if (!ctx) { err = -ENOMEM; goto unm_err_out; } /* Find the attribute. */ - if (!lookup_attr(ni->type, ni->name, ni->name_len, IGNORE_CASE, 0, - NULL, 0, ctx)) + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (unlikely(err)) goto unm_err_out; if (!ctx->attr->non_resident) { @@ -1267,7 +1369,7 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) ni->ext.base_ntfs_ino = base_ni; ni->nr_extents = -1; - put_attr_search_ctx(ctx); + ntfs_attr_put_search_ctx(ctx); unmap_mft_record(base_ni); ntfs_debug("Done."); @@ -1277,7 +1379,7 @@ unm_err_out: if (!err) err = -EIO; if (ctx) - put_attr_search_ctx(ctx); + ntfs_attr_put_search_ctx(ctx); unmap_mft_record(base_ni); err_out: ntfs_error(vi->i_sb, "Failed with error code %i while reading " @@ -1287,6 +1389,270 @@ err_out: return err; } +/** + * ntfs_read_locked_index_inode - read an index inode from its base inode + * @base_vi: base inode + * @vi: index inode to read + * + * ntfs_read_locked_index_inode() is called from ntfs_index_iget() to read the + * index inode described by @vi into memory from the base mft record described + * by @base_ni. + * + * ntfs_read_locked_index_inode() maps, pins and locks the base inode for + * reading and looks up the attributes relating to the index described by @vi + * before setting up the necessary fields in @vi as well as initializing the + * ntfs inode. + * + * Note, index inodes are essentially attribute inodes (NInoAttr() is true) + * with the attribute type set to AT_INDEX_ALLOCATION. Apart from that, they + * are setup like directory inodes since directories are a special case of + * indices ao they need to be treated in much the same way. Most importantly, + * for small indices the index allocation attribute might not actually exist. + * However, the index root attribute always exists but this does not need to + * have an inode associated with it and this is why we define a new inode type + * index. Also, like for directories, we need to have an attribute inode for + * the bitmap attribute corresponding to the index allocation attribute and we + * can store this in the appropriate field of the inode, just like we do for + * normal directory inodes. + * + * Q: What locks are held when the function is called? + * A: i_state has I_LOCK set, hence the inode is locked, also + * i_count is set to 1, so it is not going to go away + * + * Return 0 on success and -errno on error. In the error case, the inode will + * have had make_bad_inode() executed on it. + */ +static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi) +{ + ntfs_volume *vol = NTFS_SB(vi->i_sb); + ntfs_inode *ni, *base_ni, *bni; + struct inode *bvi; + MFT_RECORD *m; + ntfs_attr_search_ctx *ctx; + INDEX_ROOT *ir; + u8 *ir_end, *index_end; + int err = 0; + + ntfs_debug("Entering for i_ino 0x%lx.", vi->i_ino); + ntfs_init_big_inode(vi); + ni = NTFS_I(vi); + base_ni = NTFS_I(base_vi); + /* Just mirror the values from the base inode. */ + vi->i_blksize = base_vi->i_blksize; + vi->i_version = base_vi->i_version; + vi->i_uid = base_vi->i_uid; + vi->i_gid = base_vi->i_gid; + vi->i_nlink = base_vi->i_nlink; + vi->i_mtime = base_vi->i_mtime; + vi->i_ctime = base_vi->i_ctime; + vi->i_atime = base_vi->i_atime; + vi->i_generation = ni->seq_no = base_ni->seq_no; + /* Set inode type to zero but preserve permissions. */ + vi->i_mode = base_vi->i_mode & ~S_IFMT; + /* Map the mft record for the base inode. */ + m = map_mft_record(base_ni); + if (IS_ERR(m)) { + err = PTR_ERR(m); + goto err_out; + } + ctx = ntfs_attr_get_search_ctx(base_ni, m); + if (!ctx) { + err = -ENOMEM; + goto unm_err_out; + } + /* Find the index root attribute. */ + err = ntfs_attr_lookup(AT_INDEX_ROOT, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (unlikely(err)) { + if (err == -ENOENT) + ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is " + "missing."); + goto unm_err_out; + } + /* Set up the state. */ + if (ctx->attr->non_resident) { + ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is not resident. " + "Not allowed."); + goto unm_err_out; + } + /* Compressed/encrypted/sparse index root is not allowed. */ + if (ctx->attr->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_ENCRYPTED | + ATTR_IS_SPARSE)) { + ntfs_error(vi->i_sb, "Found compressed/encrypted/sparse index " + "root attribute. Not allowed."); + goto unm_err_out; + } + ir = (INDEX_ROOT*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->data.resident.value_offset)); + ir_end = (u8*)ir + le32_to_cpu(ctx->attr->data.resident.value_length); + if (ir_end > (u8*)ctx->mrec + vol->mft_record_size) { + ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is corrupt."); + goto unm_err_out; + } + index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); + if (index_end > ir_end) { + ntfs_error(vi->i_sb, "Index is corrupt."); + goto unm_err_out; + } + if (ir->type) { + ntfs_error(vi->i_sb, "Index type is not 0 (type is 0x%x). " + "Not allowed.", le32_to_cpu(ir->type)); + goto unm_err_out; + } + ni->itype.index.collation_rule = ir->collation_rule; + ntfs_debug("Index collation rule is 0x%x.", + le32_to_cpu(ir->collation_rule)); + ni->itype.index.block_size = le32_to_cpu(ir->index_block_size); + if (ni->itype.index.block_size & (ni->itype.index.block_size - 1)) { + ntfs_error(vi->i_sb, "Index block size (%u) is not a power of " + "two.", ni->itype.index.block_size); + goto unm_err_out; + } + if (ni->itype.index.block_size > PAGE_CACHE_SIZE) { + ntfs_error(vi->i_sb, "Index block size (%u) > PAGE_CACHE_SIZE " + "(%ld) is not supported. Sorry.", + ni->itype.index.block_size, PAGE_CACHE_SIZE); + err = -EOPNOTSUPP; + goto unm_err_out; + } + if (ni->itype.index.block_size < NTFS_BLOCK_SIZE) { + ntfs_error(vi->i_sb, "Index block size (%u) < NTFS_BLOCK_SIZE " + "(%i) is not supported. Sorry.", + ni->itype.index.block_size, NTFS_BLOCK_SIZE); + err = -EOPNOTSUPP; + goto unm_err_out; + } + ni->itype.index.block_size_bits = ffs(ni->itype.index.block_size) - 1; + /* Determine the size of a vcn in the index. */ + if (vol->cluster_size <= ni->itype.index.block_size) { + ni->itype.index.vcn_size = vol->cluster_size; + ni->itype.index.vcn_size_bits = vol->cluster_size_bits; + } else { + ni->itype.index.vcn_size = vol->sector_size; + ni->itype.index.vcn_size_bits = vol->sector_size_bits; + } + /* Check for presence of index allocation attribute. */ + if (!(ir->index.flags & LARGE_INDEX)) { + /* No index allocation. */ + vi->i_size = ni->initialized_size = ni->allocated_size = 0; + /* We are done with the mft record, so we release it. */ + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(base_ni); + m = NULL; + ctx = NULL; + goto skip_large_index_stuff; + } /* LARGE_INDEX: Index allocation present. Setup state. */ + NInoSetIndexAllocPresent(ni); + /* Find index allocation attribute. */ + ntfs_attr_reinit_search_ctx(ctx); + err = ntfs_attr_lookup(AT_INDEX_ALLOCATION, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (unlikely(err)) { + if (err == -ENOENT) + ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is " + "not present but $INDEX_ROOT " + "indicated it is."); + else + ntfs_error(vi->i_sb, "Failed to lookup " + "$INDEX_ALLOCATION attribute."); + goto unm_err_out; + } + if (!ctx->attr->non_resident) { + ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is " + "resident."); + goto unm_err_out; + } + if (ctx->attr->flags & ATTR_IS_ENCRYPTED) { + ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is " + "encrypted."); + goto unm_err_out; + } + if (ctx->attr->flags & ATTR_IS_SPARSE) { + ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is sparse."); + goto unm_err_out; + } + if (ctx->attr->flags & ATTR_COMPRESSION_MASK) { + ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is " + "compressed."); + goto unm_err_out; + } + if (ctx->attr->data.non_resident.lowest_vcn) { + ntfs_error(vi->i_sb, "First extent of $INDEX_ALLOCATION " + "attribute has non zero lowest_vcn. Inode is " + "corrupt. You should run chkdsk."); + goto unm_err_out; + } + vi->i_size = sle64_to_cpu(ctx->attr->data.non_resident.data_size); + ni->initialized_size = sle64_to_cpu( + ctx->attr->data.non_resident.initialized_size); + ni->allocated_size = sle64_to_cpu( + ctx->attr->data.non_resident.allocated_size); + /* + * We are done with the mft record, so we release it. Otherwise + * we would deadlock in ntfs_attr_iget(). + */ + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(base_ni); + m = NULL; + ctx = NULL; + /* Get the index bitmap attribute inode. */ + bvi = ntfs_attr_iget(base_vi, AT_BITMAP, ni->name, ni->name_len); + if (IS_ERR(bvi)) { + ntfs_error(vi->i_sb, "Failed to get bitmap attribute."); + err = PTR_ERR(bvi); + goto unm_err_out; + } + bni = NTFS_I(bvi); + if (NInoCompressed(bni) || NInoEncrypted(bni) || + NInoSparse(bni)) { + ntfs_error(vi->i_sb, "$BITMAP attribute is compressed " + "and/or encrypted and/or sparse."); + goto iput_unm_err_out; + } + /* Consistency check bitmap size vs. index allocation size. */ + if ((bvi->i_size << 3) < (vi->i_size >> + ni->itype.index.block_size_bits)) { + ntfs_error(vi->i_sb, "Index bitmap too small (0x%llx) " + "for index allocation (0x%llx).", + bvi->i_size << 3, vi->i_size); + goto iput_unm_err_out; + } + ni->itype.index.bmp_ino = bvi; +skip_large_index_stuff: + /* Setup the operations for this index inode. */ + vi->i_op = NULL; + vi->i_fop = NULL; + vi->i_mapping->a_ops = &ntfs_mst_aops; + vi->i_blocks = ni->allocated_size >> 9; + + /* + * Make sure the base inode doesn't go away and attach it to the + * index inode. + */ + igrab(base_vi); + ni->ext.base_ntfs_ino = base_ni; + ni->nr_extents = -1; + + ntfs_debug("Done."); + return 0; + +iput_unm_err_out: + iput(bvi); +unm_err_out: + if (!err) + err = -EIO; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + if (m) + unmap_mft_record(base_ni); +err_out: + ntfs_error(vi->i_sb, "Failed with error code %i while reading index " + "inode (mft_no 0x%lx, name_len %i.", -err, vi->i_ino, + ni->name_len); + make_bad_inode(vi); + return err; +} + /** * ntfs_read_inode_mount - special read_inode for mount time use only * @vi: inode to read @@ -1298,16 +1664,16 @@ err_out: * is not initialized and hence we cannot get at the contents of mft records * by calling map_mft_record*(). * - * Further it needs to cope with the circular references problem, i.e. can't + * Further it needs to cope with the circular references problem, i.e. cannot * load any attributes other than $ATTRIBUTE_LIST until $DATA is loaded, because - * we don't know where the other extent mft records are yet and again, because - * we cannot call map_mft_record*() yet. Obviously this applies only when an + * we do not know where the other extent mft records are yet and again, because + * we cannot call map_mft_record*() yet. Obviously this applies only when an * attribute list is actually present in $MFT inode. * * We solve these problems by starting with the $DATA attribute before anything - * else and iterating using lookup_attr($DATA) over all extents. As each extent - * is found, we decompress_mapping_pairs() including the implied - * merge_run_lists(). Each step of the iteration necessarily provides + * else and iterating using ntfs_attr_lookup($DATA) over all extents. As each + * extent is found, we decompress_mapping_pairs() including the implied + * ntfs_merge_runlists(). Each step of the iteration necessarily provides * sufficient information for the next step to complete. * * This should work but there are two possible pit falls (see inline comments @@ -1323,7 +1689,7 @@ int ntfs_read_inode_mount(struct inode *vi) ntfs_inode *ni; MFT_RECORD *m = NULL; ATTR_RECORD *attr; - attr_search_context *ctx; + ntfs_attr_search_ctx *ctx; unsigned int i, nr_blocks; int err; @@ -1398,14 +1764,21 @@ int ntfs_read_inode_mount(struct inode *vi) /* Provides readpage() and sync_page() for map_mft_record(). */ vi->i_mapping->a_ops = &ntfs_mft_aops; - ctx = get_attr_search_ctx(ni, m); + ctx = ntfs_attr_get_search_ctx(ni, m); if (!ctx) { err = -ENOMEM; goto err_out; } /* Find the attribute list attribute if present. */ - if (lookup_attr(AT_ATTRIBUTE_LIST, NULL, 0, 0, 0, NULL, 0, ctx)) { + err = ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, 0, 0, NULL, 0, ctx); + if (err) { + if (unlikely(err != -ENOENT)) { + ntfs_error(sb, "Failed to lookup attribute list " + "attribute. You should run chkdsk."); + goto put_err_out; + } + } else /* if (!err) */ { ATTR_LIST_ENTRY *al_entry, *next_al_entry; u8 *al_end; @@ -1421,7 +1794,7 @@ int ntfs_read_inode_mount(struct inode *vi) goto put_err_out; } /* Now allocate memory for the attribute list. */ - ni->attr_list_size = (u32)attribute_value_length(ctx->attr); + ni->attr_list_size = (u32)ntfs_attr_size(ctx->attr); ni->attr_list = ntfs_malloc_nofs(ni->attr_list_size); if (!ni->attr_list) { ntfs_error(sb, "Not enough memory to allocate buffer " @@ -1436,7 +1809,7 @@ int ntfs_read_inode_mount(struct inode *vi) "You should run chkdsk."); goto put_err_out; } - /* Setup the run list. */ + /* Setup the runlist. */ ni->attr_list_rl.rl = decompress_mapping_pairs(vol, ctx->attr, NULL); if (IS_ERR(ni->attr_list_rl.rl)) { @@ -1534,13 +1907,14 @@ int ntfs_read_inode_mount(struct inode *vi) } } - reinit_attr_search_ctx(ctx); + ntfs_attr_reinit_search_ctx(ctx); /* Now load all attribute extents. */ attr = NULL; next_vcn = last_vcn = highest_vcn = 0; - while (lookup_attr(AT_DATA, NULL, 0, 0, next_vcn, NULL, 0, ctx)) { - run_list_element *nrl; + while (!(err = ntfs_attr_lookup(AT_DATA, NULL, 0, 0, next_vcn, NULL, 0, + ctx))) { + runlist_element *nrl; /* Cache the current attribute. */ attr = ctx->attr; @@ -1564,23 +1938,21 @@ int ntfs_read_inode_mount(struct inode *vi) } /* * Decompress the mapping pairs array of this extent and merge - * the result into the existing run list. No need for locking + * the result into the existing runlist. No need for locking * as we have exclusive access to the inode at this time and we * are a mount in progress task, too. */ - nrl = decompress_mapping_pairs(vol, attr, ni->run_list.rl); + nrl = decompress_mapping_pairs(vol, attr, ni->runlist.rl); if (IS_ERR(nrl)) { ntfs_error(sb, "decompress_mapping_pairs() failed with " "error code %ld. $MFT is corrupt.", PTR_ERR(nrl)); goto put_err_out; } - ni->run_list.rl = nrl; + ni->runlist.rl = nrl; /* Are we in the first extent? */ if (!next_vcn) { - u64 ll; - if (attr->data.non_resident.lowest_vcn) { ntfs_error(sb, "First extent of $DATA " "attribute has non zero " @@ -1599,19 +1971,17 @@ int ntfs_read_inode_mount(struct inode *vi) non_resident.initialized_size); ni->allocated_size = sle64_to_cpu( attr->data.non_resident.allocated_size); - /* Set the number of mft records. */ - ll = vi->i_size >> vol->mft_record_size_bits; /* * Verify the number of mft records does not exceed * 2^32 - 1. */ - if (ll >= (1ULL << 32)) { + if ((vi->i_size >> vol->mft_record_size_bits) >= + (1ULL << 32)) { ntfs_error(sb, "$MFT is too big! Aborting."); goto put_err_out; } - vol->nr_mft_records = ll; /* - * We have got the first extent of the run_list for + * We have got the first extent of the runlist for * $MFT which means it is now relatively safe to call * the normal ntfs_read_inode() function. * Complete reading the inode, this will actually @@ -1638,7 +2008,7 @@ int ntfs_read_inode_mount(struct inode *vi) "saw this message to " "linux-ntfs-dev@lists." "sourceforge.net"); - put_attr_search_ctx(ctx); + ntfs_attr_put_search_ctx(ctx); /* Revert to the safe super operations. */ ntfs_free(m); return -1; @@ -1674,21 +2044,26 @@ int ntfs_read_inode_mount(struct inode *vi) goto put_err_out; } } + if (err != -ENOENT) { + ntfs_error(sb, "Failed to lookup $MFT/$DATA attribute extent. " + "$MFT is corrupt. Run chkdsk."); + goto put_err_out; + } if (!attr) { ntfs_error(sb, "$MFT/$DATA attribute not found. $MFT is " "corrupt. Run chkdsk."); goto put_err_out; } if (highest_vcn && highest_vcn != last_vcn - 1) { - ntfs_error(sb, "Failed to load the complete run list " - "for $MFT/$DATA. Driver bug or " - "corrupt $MFT. Run chkdsk."); + ntfs_error(sb, "Failed to load the complete runlist for " + "$MFT/$DATA. Driver bug or corrupt $MFT. " + "Run chkdsk."); ntfs_debug("highest_vcn = 0x%llx, last_vcn - 1 = 0x%llx", (unsigned long long)highest_vcn, (unsigned long long)last_vcn - 1); goto put_err_out; } - put_attr_search_ctx(ctx); + ntfs_attr_put_search_ctx(ctx); ntfs_debug("Done."); ntfs_free(m); return 0; @@ -1697,7 +2072,7 @@ em_put_err_out: ntfs_error(sb, "Couldn't find first extent of $DATA attribute in " "attribute list. $MFT is corrupt. Run chkdsk."); put_err_out: - put_attr_search_ctx(ctx); + ntfs_attr_put_search_ctx(ctx); err_out: ntfs_error(sb, "Failed. Marking inode as bad."); make_bad_inode(vi); @@ -1712,21 +2087,39 @@ err_out: * The VFS calls ntfs_put_inode() every time the inode reference count (i_count) * is about to be decremented (but before the decrement itself. * - * If the inode @vi is a directory with a single reference, we need to put the - * attribute inode for the directory index bitmap, if it is present, otherwise - * the directory inode would remain pinned for ever (or rather until umount() - * time. + * If the inode @vi is a directory with two references, one of which is being + * dropped, we need to put the attribute inode for the directory index bitmap, + * if it is present, otherwise the directory inode would remain pinned for + * ever. + * + * If the inode @vi is an index inode with only one reference which is being + * dropped, we need to put the attribute inode for the index bitmap, if it is + * present, otherwise the index inode would disappear and the attribute inode + * for the index bitmap would no longer be referenced from anywhere and thus it + * would remain pinned for ever. */ void ntfs_put_inode(struct inode *vi) { - if (S_ISDIR(vi->i_mode) && (atomic_read(&vi->i_count) == 2)) { - ntfs_inode *ni; + ntfs_inode *ni; - ni = NTFS_I(vi); - if (NInoIndexAllocPresent(ni) && ni->itype.index.bmp_ino) { - iput(ni->itype.index.bmp_ino); - ni->itype.index.bmp_ino = NULL; + if (S_ISDIR(vi->i_mode)) { + if (atomic_read(&vi->i_count) == 2) { + ni = NTFS_I(vi); + if (NInoIndexAllocPresent(ni) && + ni->itype.index.bmp_ino) { + iput(ni->itype.index.bmp_ino); + ni->itype.index.bmp_ino = NULL; + } } + return; + } + if (atomic_read(&vi->i_count) != 1) + return; + ni = NTFS_I(vi); + if (NInoAttr(ni) && (ni->type == AT_INDEX_ALLOCATION) && + NInoIndexAllocPresent(ni) && ni->itype.index.bmp_ino) { + iput(ni->itype.index.bmp_ino); + ni->itype.index.bmp_ino = NULL; } return; } @@ -1734,12 +2127,12 @@ void ntfs_put_inode(struct inode *vi) void __ntfs_clear_inode(ntfs_inode *ni) { /* Free all alocated memory. */ - down_write(&ni->run_list.lock); - if (ni->run_list.rl) { - ntfs_free(ni->run_list.rl); - ni->run_list.rl = NULL; + down_write(&ni->runlist.lock); + if (ni->runlist.rl) { + ntfs_free(ni->runlist.rl); + ni->runlist.rl = NULL; } - up_write(&ni->run_list.lock); + up_write(&ni->runlist.lock); if (ni->attr_list) { ntfs_free(ni->attr_list); @@ -1877,16 +2270,71 @@ int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt) * * We don't support i_size changes yet. * - * Called with ->i_sem held. + * The kernel guarantees that @vi is a regular file (S_ISREG() is true) and + * that the change is allowed. + * + * This implies for us that @vi is a file inode rather than a directory, index, + * or attribute inode as well as that @vi is a base inode. + * + * Called with ->i_sem held. In all but one case ->i_alloc_sem is held for + * writing. The only case where ->i_alloc_sem is not held is + * mm/filemap.c::generic_file_buffered_write() where vmtruncate() is called + * with the current i_size as the offset which means that it is a noop as far + * as ntfs_truncate() is concerned. */ void ntfs_truncate(struct inode *vi) { - // TODO: Implement... - ntfs_warning(vi->i_sb, "Eeek: i_size may have changed! If you see " - "this right after a message from " - "ntfs_{prepare,commit}_{,nonresident_}write() then " - "just ignore it. Otherwise it is bad news."); - // TODO: reset i_size now! + ntfs_inode *ni = NTFS_I(vi); + ntfs_attr_search_ctx *ctx; + MFT_RECORD *m; + int err; + + m = map_mft_record(ni); + if (IS_ERR(m)) { + ntfs_error(vi->i_sb, "Failed to map mft record for inode 0x%lx " + "(error code %ld).", vi->i_ino, PTR_ERR(m)); + if (PTR_ERR(m) != ENOMEM) + make_bad_inode(vi); + return; + } + ctx = ntfs_attr_get_search_ctx(ni, m); + if (unlikely(!ctx)) { + ntfs_error(vi->i_sb, "Failed to allocate a search context: " + "Not enough memory"); + // FIXME: We can't report an error code upstream. So what do + // we do?!? make_bad_inode() seems a bit harsh... + unmap_mft_record(ni); + return; + } + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (unlikely(err)) { + if (err == -ENOENT) { + ntfs_error(vi->i_sb, "Open attribute is missing from " + "mft record. Inode 0x%lx is corrupt. " + "Run chkdsk.", vi->i_ino); + make_bad_inode(vi); + } else { + ntfs_error(vi->i_sb, "Failed to lookup attribute in " + "inode 0x%lx (error code %d).", + vi->i_ino, err); + // FIXME: We can't report an error code upstream. So + // what do we do?!? make_bad_inode() seems a bit + // harsh... + } + goto out; + } + /* If the size has not changed there is nothing to do. */ + if (ntfs_attr_size(ctx->attr) == i_size_read(vi)) + goto out; + // TODO: Implement the truncate... + ntfs_error(vi->i_sb, "Inode size has changed but this is not " + "implemented yet. Resetting inode size to old value. " + " This is most likely a bug in the ntfs driver!"); + i_size_write(vi, ntfs_attr_size(ctx->attr)); +out: + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(ni); return; } @@ -1896,67 +2344,62 @@ void ntfs_truncate(struct inode *vi) * @attr: structure describing the attributes and the changes * * We have to trap VFS attempts to truncate the file described by @dentry as - * soon as possible, because we do not implement changes in i_size yet. So we + * soon as possible, because we do not implement changes in i_size yet. So we * abort all i_size changes here. * - * Called with ->i_sem held. + * We also abort all changes of user, group, and mode as we do not implement + * the NTFS ACLs yet. + * + * Called with ->i_sem held. For the ATTR_SIZE (i.e. ->truncate) case, also + * called with ->i_alloc_sem held for writing. * * Basically this is a copy of generic notify_change() and inode_setattr() * functionality, except we intercept and abort changes in i_size. */ int ntfs_setattr(struct dentry *dentry, struct iattr *attr) { - struct inode *vi; + struct inode *vi = dentry->d_inode; int err; unsigned int ia_valid = attr->ia_valid; - vi = dentry->d_inode; - err = inode_change_ok(vi, attr); if (err) return err; - if ((ia_valid & ATTR_UID && attr->ia_uid != vi->i_uid) || - (ia_valid & ATTR_GID && attr->ia_gid != vi->i_gid)) { - err = DQUOT_TRANSFER(vi, attr) ? -EDQUOT : 0; - if (err) - return err; + /* We do not support NTFS ACLs yet. */ + if (ia_valid & (ATTR_UID | ATTR_GID | ATTR_MODE)) { + ntfs_warning(vi->i_sb, "Changes in user/group/mode are not " + "supported yet, ignoring."); + err = -EOPNOTSUPP; + goto out; } - lock_kernel(); - if (ia_valid & ATTR_SIZE) { - ntfs_error(vi->i_sb, "Changes in i_size are not supported " - "yet. Sorry."); - // TODO: Implement... - // err = vmtruncate(vi, attr->ia_size); - err = -EOPNOTSUPP; - if (err) - goto trunc_err; + if (attr->ia_size != i_size_read(vi)) { + ntfs_warning(vi->i_sb, "Changes in inode size are not " + "supported yet, ignoring."); + err = -EOPNOTSUPP; + // TODO: Implement... + // err = vmtruncate(vi, attr->ia_size); + if (err || ia_valid == ATTR_SIZE) + goto out; + } else { + /* + * We skipped the truncate but must still update + * timestamps. + */ + ia_valid |= ATTR_MTIME|ATTR_CTIME; + } } - if (ia_valid & ATTR_UID) - vi->i_uid = attr->ia_uid; - if (ia_valid & ATTR_GID) - vi->i_gid = attr->ia_gid; if (ia_valid & ATTR_ATIME) vi->i_atime = attr->ia_atime; if (ia_valid & ATTR_MTIME) vi->i_mtime = attr->ia_mtime; if (ia_valid & ATTR_CTIME) vi->i_ctime = attr->ia_ctime; - if (ia_valid & ATTR_MODE) { - vi->i_mode = attr->ia_mode; - if (!in_group_p(vi->i_gid) && - !capable(CAP_FSETID)) - vi->i_mode &= ~S_ISGID; - } mark_inode_dirty(vi); - -trunc_err: - - unlock_kernel(); - +out: return err; } @@ -1975,67 +2418,93 @@ trunc_err: * marking the page (and in this case mft record) dirty but we do not implement * this yet as write_mft_record() largely ignores the @sync parameter and * always performs synchronous writes. + * + * Return 0 on success and -errno on error. */ -void ntfs_write_inode(struct inode *vi, int sync) +int ntfs_write_inode(struct inode *vi, int sync) { + sle64 nt; ntfs_inode *ni = NTFS_I(vi); -#if 0 - attr_search_context *ctx; -#endif + ntfs_attr_search_ctx *ctx; MFT_RECORD *m; + STANDARD_INFORMATION *si; int err = 0; + BOOL modified = FALSE; ntfs_debug("Entering for %sinode 0x%lx.", NInoAttr(ni) ? "attr " : "", vi->i_ino); /* * Dirty attribute inodes are written via their real inodes so just - * clean them here. TODO: Take care of access time updates. + * clean them here. Access time updates are taken care off when the + * real inode is written. */ if (NInoAttr(ni)) { NInoClearDirty(ni); - return; + ntfs_debug("Done."); + return 0; } /* Map, pin, and lock the mft record belonging to the inode. */ m = map_mft_record(ni); - if (unlikely(IS_ERR(m))) { + if (IS_ERR(m)) { err = PTR_ERR(m); goto err_out; } -#if 0 - /* Obtain the standard information attribute. */ - ctx = get_attr_search_ctx(ni, m); + /* Update the access times in the standard information attribute. */ + ctx = ntfs_attr_get_search_ctx(ni, m); if (unlikely(!ctx)) { err = -ENOMEM; goto unm_err_out; } - if (unlikely(!lookup_attr(AT_STANDARD_INFORMATION, NULL, 0, - IGNORE_CASE, 0, NULL, 0, ctx))) { - put_attr_search_ctx(ctx); - err = -ENOENT; + err = ntfs_attr_lookup(AT_STANDARD_INFORMATION, NULL, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (unlikely(err)) { + ntfs_attr_put_search_ctx(ctx); goto unm_err_out; } - // TODO: Update the access times in the standard information attribute - // which is now in ctx->attr. - // - Probably want to have use sops->dirty_inode() to set a flag that - // we need to update the times here rather than having to blindly do - // it every time. Or even don't do it here at all and do it in - // sops->dirty_inode() instead. Problem with this would be that - // sops->dirty_inode() must be atomic under certain circumstances - // and mapping mft records and such like is not atomic. - // - For atime updates also need to check whether they are enabled in - // the superblock flags. - ntfs_warning(vi->i_sb, "Access time updates not implement yet."); + si = (STANDARD_INFORMATION*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->data.resident.value_offset)); + /* Update the access times if they have changed. */ + nt = utc2ntfs(vi->i_mtime); + if (si->last_data_change_time != nt) { + ntfs_debug("Updating mtime for inode 0x%lx: old = 0x%llx, " + "new = 0x%llx", vi->i_ino, + sle64_to_cpu(si->last_data_change_time), + sle64_to_cpu(nt)); + si->last_data_change_time = nt; + modified = TRUE; + } + nt = utc2ntfs(vi->i_ctime); + if (si->last_mft_change_time != nt) { + ntfs_debug("Updating ctime for inode 0x%lx: old = 0x%llx, " + "new = 0x%llx", vi->i_ino, + sle64_to_cpu(si->last_mft_change_time), + sle64_to_cpu(nt)); + si->last_mft_change_time = nt; + modified = TRUE; + } + nt = utc2ntfs(vi->i_atime); + if (si->last_access_time != nt) { + ntfs_debug("Updating atime for inode 0x%lx: old = 0x%llx, " + "new = 0x%llx", vi->i_ino, + sle64_to_cpu(si->last_access_time), + sle64_to_cpu(nt)); + si->last_access_time = nt; + modified = TRUE; + } /* - * We just modified the mft record containing the standard information - * attribute. So need to mark the mft record dirty, too, but we do it - * manually so that mark_inode_dirty() is not called again. - * TODO: Only do this if there was a change in any of the times! + * If we just modified the standard information attribute we need to + * mark the mft record it is in dirty. We do this manually so that + * mark_inode_dirty() is not called which would redirty the inode and + * hence result in an infinite loop of trying to write the inode. + * There is no need to mark the base inode nor the base mft record + * dirty, since we are going to write this mft record below in any case + * and the base mft record may actually not have been modified so it + * might not need to be written out. */ - if (!NInoTestSetDirty(ctx->ntfs_ino)) + if (modified && !NInoTestSetDirty(ctx->ntfs_ino)) __set_page_dirty_nobuffers(ctx->ntfs_ino->page); - put_attr_search_ctx(ctx); -#endif - /* Write this base mft record. */ + ntfs_attr_put_search_ctx(ctx); + /* Now the access times are updated, write the base mft record. */ if (NInoDirty(ni)) err = write_mft_record(ni, m, sync); /* Write all attached extent mft records. */ @@ -2052,7 +2521,7 @@ void ntfs_write_inode(struct inode *vi, int sync) MFT_RECORD *tm = map_mft_record(tni); int ret; - if (unlikely(IS_ERR(tm))) { + if (IS_ERR(tm)) { if (!err || err == -ENOMEM) err = PTR_ERR(tm); continue; @@ -2071,11 +2540,9 @@ void ntfs_write_inode(struct inode *vi, int sync) if (unlikely(err)) goto err_out; ntfs_debug("Done."); - return; -#if 0 + return 0; unm_err_out: unmap_mft_record(ni); -#endif err_out: if (err == -ENOMEM) { ntfs_warning(vi->i_sb, "Not enough memory to write inode. " @@ -2087,7 +2554,7 @@ err_out: "as bad. You should run chkdsk.", -err); make_bad_inode(vi); } - return; + return err; } #endif /* NTFS_RW */