X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fntfs%2Finode.c;h=873c25d9830ffb625beb5f3b03f4bf3ca3373bf2;hb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;hp=686d5bbb52a54ebaa34d4f10a8c797e98b3e4abd;hpb=a2c21200f1c81b08cb55e417b68150bba439b646;p=linux-2.6.git diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 686d5bbb5..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; } @@ -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 */