vserver 1.9.3
[linux-2.6.git] / fs / ntfs / inode.c
index 686d5bb..873c25d 100644 (file)
@@ -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;
 }
@@ -210,7 +214,7 @@ struct inode *ntfs_iget(struct super_block *sb, unsigned long mft_no)
  * 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;
@@ -372,13 +376,13 @@ 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;
@@ -428,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;
@@ -484,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.");
@@ -525,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);
@@ -557,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;
@@ -608,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
@@ -646,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);
@@ -662,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 "
@@ -680,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,
@@ -733,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. */
@@ -841,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;
@@ -849,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) {
@@ -894,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;
@@ -938,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;
@@ -946,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.
@@ -1059,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;
@@ -1098,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:
@@ -1133,7 +1168,7 @@ 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);
@@ -1162,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, CASE_SENSITIVE, 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) {
@@ -1333,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.");
@@ -1343,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 "
@@ -1392,7 +1428,7 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
        ntfs_inode *ni, *base_ni, *bni;
        struct inode *bvi;
        MFT_RECORD *m;
-       attr_search_context *ctx;
+       ntfs_attr_search_ctx *ctx;
        INDEX_ROOT *ir;
        u8 *ir_end, *index_end;
        int err = 0;
@@ -1419,15 +1455,18 @@ static int ntfs_read_locked_index_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 index root attribute. */
-       if (!lookup_attr(AT_INDEX_ROOT, ni->name, ni->name_len, CASE_SENSITIVE,
-                       0, NULL, 0, ctx)) {
-               ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is missing.");
+       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. */
@@ -1497,7 +1536,7 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
                /* No index allocation. */
                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(base_ni);
                m = NULL;
                ctx = NULL;
@@ -1505,11 +1544,17 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
        } /* LARGE_INDEX:  Index allocation present.  Setup state. */
        NInoSetIndexAllocPresent(ni);
        /* Find index allocation attribute. */
-       reinit_attr_search_ctx(ctx);
-       if (!lookup_attr(AT_INDEX_ALLOCATION, ni->name, ni->name_len,
-                       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, 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) {
@@ -1546,13 +1591,13 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
         * 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(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 (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;
@@ -1597,7 +1642,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(base_ni);
 err_out:
@@ -1619,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
@@ -1644,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;
 
@@ -1719,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;
 
@@ -1742,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 "
@@ -1757,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)) {
@@ -1855,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;
@@ -1885,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 "
@@ -1920,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
@@ -1959,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;
@@ -1995,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;
@@ -2018,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);
@@ -2073,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);
@@ -2216,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;
 }
 
@@ -2235,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;
 }
 
@@ -2314,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,
-                       CASE_SENSITIVE, 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. */
@@ -2391,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;
@@ -2410,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.  "
@@ -2426,7 +2554,7 @@ err_out:
                                "as bad.  You should run chkdsk.", -err);
                make_bad_inode(vi);
        }
-       return;
+       return err;
 }
 
 #endif /* NTFS_RW */