bk://linux-ntfs.bkbits.net/ntfs-2.6-devel ntfs@flatcap.org|ChangeSet|20041029030852|42020 ntfs # This is a BitKeeper generated diff -Nru style patch. # # ChangeSet # 2004/10/28 16:16:03+01:00 aia21@cantab.net # NTFS: Implement extension of resident files in the regular file write code # paths (fs/ntfs/aops.c::ntfs_{prepare,commit}_write()). At present # this only works until the data attribute becomes too big for the mft # record after which we abort the write returning -EOPNOTSUPP from # ntfs_prepare_write(). # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/aops.c # 2004/10/28 16:15:54+01:00 aia21@cantab.net +209 -179 # Implement extension of resident files in the regular file write code # paths (ntfs_{prepare,commit}_write()). At present this only works # until the data attribute becomes too big for the mft record after # which we abort the write returning -EOPNOTSUPP from ntfs_prepare_write(). # # fs/ntfs/ChangeLog # 2004/10/28 16:15:54+01:00 aia21@cantab.net +5 -0 # Update # # ChangeSet # 2004/10/28 16:07:13+01:00 aia21@cantab.net # NTFS: In fs/ntfs/aops.c::ntfs_writepage(), if t he page is fully outside # i_size, i.e. race with truncate, invalidate the buffers on the page # so that they become freeable and hence the page does not leak. # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/aops.c # 2004/10/28 16:07:03+01:00 aia21@cantab.net +7 -1 # In ntfs_writepage(), if t he page is fully outside i_size, i.e. race # with truncate, invalidate the buffers on the page so that they become # freeable and hence the page does not leak. # # fs/ntfs/ChangeLog # 2004/10/28 16:07:03+01:00 aia21@cantab.net +3 -0 # Update # # ChangeSet # 2004/10/27 13:53:09+01:00 aia21@cantab.net # NTFS: Check for location of attribute name and improve error handling in # general in fs/ntfs/inode.c::ntfs_read_locked_inode() and friends. # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/inode.c # 2004/10/27 13:53:00+01:00 aia21@cantab.net +141 -107 # Check for location of attribute name and improve error handling in # general in fs/ntfs/inode.c::ntfs_read_locked_inode() and friends. # # fs/ntfs/ChangeLog # 2004/10/27 13:53:00+01:00 aia21@cantab.net +2 -0 # Update # # ChangeSet # 2004/10/27 11:41:35+01:00 aia21@cantab.net # NTFS: Ensure the mft record size does not exceed the PAGE_CACHE_SIZE at # mount time as this cannot work with the current implementation. # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/super.c # 2004/10/27 11:38:58+01:00 aia21@cantab.net +20 -8 # Ensure the mft record size does not exceed the PAGE_CACHE_SIZE at # mount time as this cannot work with the current implementation. # # fs/ntfs/ChangeLog # 2004/10/27 11:38:49+01:00 aia21@cantab.net +2 -0 # Update # # ChangeSet # 2004/10/25 11:01:14+01:00 aia21@cantab.net # NTFS: In fs/ntfs/aops.c::mark_ntfs_record_dirty(), take the # mapping->private_lock around the dirtying of the buffer heads # analagous to the way it is done in __set_page_dirty_buffers(). # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/aops.c # 2004/10/25 11:01:06+01:00 aia21@cantab.net +2 -0 # In mark_ntfs_record_dirty(), take the mapping->private_lock around the # dirtying of the buffer heads analagous to the way it is done in # __set_page_dirty_buffers(). # # fs/ntfs/ChangeLog # 2004/10/25 11:01:06+01:00 aia21@cantab.net +3 -0 # Update # # ChangeSet # 2004/10/25 10:54:46+01:00 aia21@cantab.net # NTFS: Add attribute definition handling helpers to fs/ntfs/attrib.[hc]: # ntfs_attr_size_bounds_check(), ntfs_attr_can_be_non_resident(), and # ntfs_attr_can_be_resident(), which in turn use the new private helper # ntfs_attr_find_in_attrdef(). # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/attrib.h # 2004/10/25 10:54:37+01:00 aia21@cantab.net +8 -0 # Add attribute definition handling helpers: ntfs_attr_size_bounds_check(), # ntfs_attr_can_be_non_resident(), and ntfs_attr_can_be_resident(), which in # turn use the new private helper ntfs_attr_find_in_attrdef(). # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/attrib.c # 2004/10/25 10:54:37+01:00 aia21@cantab.net +129 -0 # Add attribute definition handling helpers: ntfs_attr_size_bounds_check(), # ntfs_attr_can_be_non_resident(), and ntfs_attr_can_be_resident(), which in # turn use the new private helper ntfs_attr_find_in_attrdef(). # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/ChangeLog # 2004/10/25 10:54:37+01:00 aia21@cantab.net +4 -0 # Update # # ChangeSet # 2004/10/25 10:49:00+01:00 aia21@cantab.net # NTFS: Fix min_size and max_size definitions in ATTR_DEF structure in # fs/ntfs/layout.h to be signed. # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/layout.h # 2004/10/25 10:48:51+01:00 aia21@cantab.net +2 -2 # Fix min_size and max_size definitions in ATTR_DEF structure to be signed. # # fs/ntfs/ChangeLog # 2004/10/25 10:48:51+01:00 aia21@cantab.net +2 -0 # Update # # ChangeSet # 2004/10/25 10:46:39+01:00 aia21@cantab.net # NTFS: - Change fs/ntfs/inode.c::ntfs_truncate() to return an error code # instead of void and provide a helper ntfs_truncate_vfs() for the # vfs ->truncate method. # - Add a new ntfs inode flag NInoTruncateFailed() and modify # fs/ntfs/inode.c::ntfs_truncate() to set and clear it appropriately. # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/inode.h # 2004/10/25 10:46:30+01:00 aia21@cantab.net +4 -1 # - Change fs/ntfs/inode.c::ntfs_truncate() to return an error code # instead of void and provide a helper ntfs_truncate_vfs() for the # vfs ->truncate method. # - Add a new ntfs inode flag NInoTruncateFailed() and modify # fs/ntfs/inode.c::ntfs_truncate() to set and clear it appropriately. # # fs/ntfs/inode.c # 2004/10/25 10:46:30+01:00 aia21@cantab.net +19 -3 # - Change fs/ntfs/inode.c::ntfs_truncate() to return an error code # instead of void and provide a helper ntfs_truncate_vfs() for the # vfs ->truncate method. # - Add a new ntfs inode flag NInoTruncateFailed() and modify # fs/ntfs/inode.c::ntfs_truncate() to set and clear it appropriately. # # fs/ntfs/file.c # 2004/10/25 10:46:30+01:00 aia21@cantab.net +1 -1 # - Change fs/ntfs/inode.c::ntfs_truncate() to return an error code # instead of void and provide a helper ntfs_truncate_vfs() for the # vfs ->truncate method. # - Add a new ntfs inode flag NInoTruncateFailed() and modify # fs/ntfs/inode.c::ntfs_truncate() to set and clear it appropriately. # # fs/ntfs/ChangeLog # 2004/10/25 10:46:30+01:00 aia21@cantab.net +5 -0 # Update # # ChangeSet # 2004/10/25 10:41:30+01:00 aia21@cantab.net # NTFS: Add fs/ntfs/aops.c to list of providers for ->b_end_io method in # Documentation/filesystems/Locking. # # Signed-off-by: Anton Altaparmakov # # Documentation/filesystems/Locking # 2004/10/25 10:41:20+01:00 aia21@cantab.net +2 -2 # Add fs/ntfs/aops.c to list of providers for ->b_end_io method. # # ChangeSet # 2004/10/21 12:31:59+01:00 aia21@cantab.net # NTFS: Improve error handling in fs/ntfs/inode.c::ntfs_truncate(). # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/inode.c # 2004/10/21 12:31:50+01:00 aia21@cantab.net +29 -20 # Improve error handling in ntfs_truncate(). # # fs/ntfs/Makefile # 2004/10/21 12:31:50+01:00 aia21@cantab.net +1 -1 # Start 2.1.22-WIP. # # fs/ntfs/ChangeLog # 2004/10/21 12:31:50+01:00 aia21@cantab.net +4 -0 # Update # # ChangeSet # 2004/10/20 13:14:44+01:00 aia21@cantab.net # NTFS: Fix two typos in Documentation/filesystems/ntfs.txt. # # Thanks to Richard Russon for pointing them out. # # Signed-off-by: Anton Altaparmakov # # Documentation/filesystems/ntfs.txt # 2004/10/20 13:14:35+01:00 aia21@cantab.net +2 -2 # Fix typos. # diff -Nru a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking --- a/Documentation/filesystems/Locking 2004-10-28 22:32:44 -07:00 +++ b/Documentation/filesystems/Locking 2004-10-28 22:32:44 -07:00 @@ -317,8 +317,8 @@ locking rules: called from interrupts. In other words, extreme care is needed here. bh is locked, but that's all warranties we have here. Currently only RAID1, -highmem and fs/buffer.c are providing these. Block devices call this method -upon the IO completion. +highmem, fs/buffer.c, and fs/ntfs/aops.c are providing these. Block devices +call this method upon the IO completion. --------------------------- block_device_operations ----------------------- prototypes: diff -Nru a/Documentation/filesystems/ntfs.txt b/Documentation/filesystems/ntfs.txt --- a/Documentation/filesystems/ntfs.txt 2004-10-28 22:32:44 -07:00 +++ b/Documentation/filesystems/ntfs.txt 2004-10-28 22:32:44 -07:00 @@ -258,10 +258,10 @@ $ ./ldminfo --dump /dev/hda This would dump the LDM database found on /dev/hda which describes all of your -dinamic disks and all the volumes on them. At the bottom you will see the +dynamic disks and all the volumes on them. At the bottom you will see the VOLUME DEFINITIONS section which is all you really need. You may need to look further above to determine which of the disks in the volume definitions is -which device in Linux. Hint: Run ldminfo on each of your dinamic disks and +which device in Linux. Hint: Run ldminfo on each of your dynamic disks and look at the Disk Id close to the top of the output for each (the PRIVATE HEADER section). You can then find these Disk Ids in the VBLK DATABASE section in the components where you will get the LDM Name for the disk that is found in diff -Nru a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog --- a/fs/ntfs/ChangeLog 2004-10-28 22:32:44 -07:00 +++ b/fs/ntfs/ChangeLog 2004-10-28 22:32:44 -07:00 @@ -21,6 +21,36 @@ - Enable the code for setting the NT4 compatibility flag when we start making NTFS 1.2 specific modifications. +2.1.22-WIP + + - Improve error handling in fs/ntfs/inode.c::ntfs_truncate(). + - Change fs/ntfs/inode.c::ntfs_truncate() to return an error code + instead of void and provide a helper ntfs_truncate_vfs() for the + vfs ->truncate method. + - Add a new ntfs inode flag NInoTruncateFailed() and modify + fs/ntfs/inode.c::ntfs_truncate() to set and clear it appropriately. + - Fix min_size and max_size definitions in ATTR_DEF structure in + fs/ntfs/layout.h to be signed. + - Add attribute definition handling helpers to fs/ntfs/attrib.[hc]: + ntfs_attr_size_bounds_check(), ntfs_attr_can_be_non_resident(), and + ntfs_attr_can_be_resident(), which in turn use the new private helper + ntfs_attr_find_in_attrdef(). + - In fs/ntfs/aops.c::mark_ntfs_record_dirty(), take the + mapping->private_lock around the dirtying of the buffer heads + analagous to the way it is done in __set_page_dirty_buffers(). + - Ensure the mft record size does not exceed the PAGE_CACHE_SIZE at + mount time as this cannot work with the current implementation. + - Check for location of attribute name and improve error handling in + general in fs/ntfs/inode.c::ntfs_read_locked_inode() and friends. + - In fs/ntfs/aops.c::ntfs_writepage(), if t he page is fully outside + i_size, i.e. race with truncate, invalidate the buffers on the page + so that they become freeable and hence the page does not leak. + - Implement extension of resident files in the regular file write code + paths (fs/ntfs/aops.c::ntfs_{prepare,commit}_write()). At present + this only works until the data attribute becomes too big for the mft + record after which we abort the write returning -EOPNOTSUPP from + ntfs_prepare_write(). + 2.1.21 - Fix some races and bugs, rewrite mft write code, add mft allocator. - Implement extent mft record deallocation diff -Nru a/fs/ntfs/Makefile b/fs/ntfs/Makefile --- a/fs/ntfs/Makefile 2004-10-28 22:32:44 -07:00 +++ b/fs/ntfs/Makefile 2004-10-28 22:32:44 -07:00 @@ -6,7 +6,7 @@ index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \ unistr.o upcase.o -EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.21\" +EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.22-WIP\" ifeq ($(CONFIG_NTFS_DEBUG),y) EXTRA_CFLAGS += -DDEBUG diff -Nru a/fs/ntfs/aops.c b/fs/ntfs/aops.c --- a/fs/ntfs/aops.c 2004-10-28 22:32:44 -07:00 +++ b/fs/ntfs/aops.c 2004-10-28 22:32:44 -07:00 @@ -1117,7 +1117,8 @@ * For resident attributes, OTOH, ntfs_writepage() writes the @page by copying * the data to the mft record (which at this stage is most likely in memory). * The mft record is then marked dirty and written out asynchronously via the - * vfs inode dirty code path. + * vfs inode dirty code path for the inode the mft record belongs to or via the + * vm page dirty code path for the page the mft record is in. * * Based on ntfs_readpage() and fs/buffer.c::block_write_full_page(). * @@ -1141,6 +1142,11 @@ /* Is the page fully outside i_size? (truncate in progress) */ if (unlikely(page->index >= (vi->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT)) { + /* + * The page may have dirty, unmapped buffers. Make them + * freeable here, so the page does not leak. + */ + block_invalidatepage(page, 0); unlock_page(page); ntfs_debug("Write outside i_size - truncated?"); return 0; @@ -1195,10 +1201,9 @@ /* Normal data stream. */ return ntfs_write_block(wbc, page); } - /* - * Attribute is resident, implying it is not compressed, encrypted, or - * mst protected. + * Attribute is resident, implying it is not compressed, encrypted, + * sparse, or mst protected. */ BUG_ON(page_has_buffers(page)); BUG_ON(!PageUptodate(page)); @@ -1277,6 +1282,9 @@ * zeroing below is enabled, we MUST move the unlock_page() from above * to after the kunmap_atomic(), i.e. just before the * end_page_writeback(). + * UPDATE: ntfs_prepare/commit_write() do the zeroing on i_size + * increases for resident attributes so those are ok. + * TODO: ntfs_truncate(), others? */ kaddr = kmap_atomic(page, KM_USER0); @@ -1354,7 +1362,6 @@ page->index, from, to); BUG_ON(!NInoNonResident(ni)); - BUG_ON(NInoMstProtected(ni)); blocksize_bits = vi->i_blkbits; blocksize = 1 << blocksize_bits; @@ -1682,8 +1689,8 @@ * ntfs_prepare_write - prepare a page for receiving data * * This is called from generic_file_write() with i_sem held on the inode - * (@page->mapping->host). The @page is locked and kmap()ped so page_address() - * can simply be used. The source data has not yet been copied into the @page. + * (@page->mapping->host). The @page is locked but not kmap()ped. The source + * data has not yet been copied into the @page. * * Need to extend the attribute/fill in holes if necessary, create blocks and * make partially overwritten blocks uptodate, @@ -1693,8 +1700,8 @@ * Return 0 on success or -errno on error. * * Should be using block_prepare_write() [support for sparse files] or - * cont_prepare_write() [no support for sparse files]. Can't do that due to - * ntfs specifics but can look at them for implementation guidancea. + * cont_prepare_write() [no support for sparse files]. Cannot do that due to + * ntfs specifics but can look at them for implementation guidance. * * Note: In the range, @from is inclusive and @to is exclusive, i.e. @from is * the first byte in the page that will be written to and @to is the first byte @@ -1703,18 +1710,40 @@ static int ntfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) { + s64 new_size; struct inode *vi = page->mapping->host; - ntfs_inode *ni = NTFS_I(vi); + ntfs_inode *base_ni = NULL, *ni = NTFS_I(vi); + ntfs_volume *vol = ni->vol; + ntfs_attr_search_ctx *ctx = NULL; + MFT_RECORD *m = NULL; + ATTR_RECORD *a; + u8 *kaddr; + u32 attr_len; + int err; ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index " "0x%lx, from = %u, to = %u.", vi->i_ino, ni->type, page->index, from, to); - BUG_ON(!PageLocked(page)); BUG_ON(from > PAGE_CACHE_SIZE); BUG_ON(to > PAGE_CACHE_SIZE); BUG_ON(from > to); - + BUG_ON(NInoMstProtected(ni)); + /* + * If a previous ntfs_truncate() failed, repeat it and abort if it + * fails again. + */ + if (unlikely(NInoTruncateFailed(ni))) { + down_write(&vi->i_alloc_sem); + err = ntfs_truncate(vi); + up_write(&vi->i_alloc_sem); + if (err || NInoTruncateFailed(ni)) { + if (!err) + err = -EIO; + goto err_out; + } + } + /* If the attribute is not resident, deal with it elsewhere. */ if (NInoNonResident(ni)) { /* * Only unnamed $DATA attributes can be compressed, encrypted, @@ -1743,33 +1772,106 @@ return -EOPNOTSUPP; } } - - // TODO: Implement and remove this check. - if (NInoMstProtected(ni)) { - ntfs_error(vi->i_sb, "Writing to MST protected " - "attributes is not supported yet. " - "Sorry."); - return -EOPNOTSUPP; - } - /* Normal data stream. */ return ntfs_prepare_nonresident_write(page, from, to); } - /* * Attribute is resident, implying it is not compressed, encrypted, or - * mst protected. + * sparse. */ BUG_ON(page_has_buffers(page)); - - /* Do we need to resize the attribute? */ - if (((s64)page->index << PAGE_CACHE_SHIFT) + to > vi->i_size) { - // TODO: Implement resize... - ntfs_error(vi->i_sb, "Writing beyond the existing file size is " - "not supported yet. Sorry."); - return -EOPNOTSUPP; + new_size = ((s64)page->index << PAGE_CACHE_SHIFT) + to; + /* If we do not need to resize the attribute allocation we are done. */ + if (new_size <= vi->i_size) + goto done; + /* Map, pin, and lock the (base) mft record. */ + if (!NInoAttr(ni)) + base_ni = ni; + else + base_ni = ni->ext.base_ntfs_ino; + m = map_mft_record(base_ni); + if (IS_ERR(m)) { + err = PTR_ERR(m); + m = NULL; + ctx = NULL; + goto err_out; + } + ctx = ntfs_attr_get_search_ctx(base_ni, m); + if (unlikely(!ctx)) { + err = -ENOMEM; + goto err_out; + } + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (unlikely(err)) { + if (err == -ENOENT) + err = -EIO; + goto err_out; + } + m = ctx->mrec; + a = ctx->attr; + /* The total length of the attribute value. */ + attr_len = le32_to_cpu(a->data.resident.value_length); + BUG_ON(vi->i_size != attr_len); + /* Check if new size is allowed in $AttrDef. */ + err = ntfs_attr_size_bounds_check(vol, ni->type, new_size); + if (unlikely(err)) { + if (err == -ERANGE) { + ntfs_error(vol->sb, "Write would cause the inode " + "0x%lx to exceed the maximum size for " + "its attribute type (0x%x). Aborting " + "write.", vi->i_ino, + le32_to_cpu(ni->type)); + } else { + ntfs_error(vol->sb, "Inode 0x%lx has unknown " + "attribute type 0x%x. Aborting " + "write.", vi->i_ino, + le32_to_cpu(ni->type)); + err = -EIO; + } + goto err_out2; + } + /* + * Extend the attribute record to be able to store the new attribute + * size. + */ + if (new_size >= vol->mft_record_size || ntfs_attr_record_resize(m, a, + le16_to_cpu(a->data.resident.value_offset) + + new_size)) { + /* Not enough space in the mft record. */ + ntfs_error(vol->sb, "Not enough space in the mft record for " + "the resized attribute value. This is not " + "supported yet. Aborting write."); + err = -EOPNOTSUPP; + goto err_out2; } - + /* + * We have enough space in the mft record to fit the write. This + * implies the attribute is smaller than the mft record and hence the + * attribute must be in a single page and hence page->index must be 0. + */ + BUG_ON(page->index); + /* + * If the beginning of the write is past the old size, enlarge the + * attribute value up to the beginning of the write and fill it with + * zeroes. + */ + if (from > attr_len) { + memset((u8*)a + le16_to_cpu(a->data.resident.value_offset) + + attr_len, 0, from - attr_len); + a->data.resident.value_length = cpu_to_le32(from); + /* Zero the corresponding area in the page as well. */ + if (PageUptodate(page)) { + kaddr = kmap_atomic(page, KM_USER0); + memset(kaddr + attr_len, 0, from - attr_len); + kunmap_atomic(kaddr, KM_USER0); + flush_dcache_page(page); + } + } + flush_dcache_mft_record_page(ctx->ntfs_ino); + mark_mft_record_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(base_ni); /* * Because resident attributes are handled by memcpy() to/from the * corresponding MFT record, and because this form of i/o is byte @@ -1779,26 +1881,30 @@ * generic_file_write() does the copying from userspace. * * We thus defer the uptodate bringing of the page region outside the - * region written to to ntfs_commit_write(). The reason for doing this - * is that we save one round of: - * map_mft_record(), ntfs_attr_get_search_ctx(), - * ntfs_attr_lookup(), kmap_atomic(), kunmap_atomic(), - * ntfs_attr_put_search_ctx(), unmap_mft_record(). - * Which is obviously a very worthwhile save. - * - * Thus we just return success now... + * region written to to ntfs_commit_write(), which makes the code + * simpler and saves one atomic kmap which is good. */ +done: ntfs_debug("Done."); return 0; +err_out: + if (err == -ENOMEM) + ntfs_warning(vi->i_sb, "Error allocating memory required to " + "prepare the write."); + else { + ntfs_error(vi->i_sb, "Resident attribute prepare write failed " + "with error %i.", err); + NVolSetErrors(vol); + make_bad_inode(vi); + } +err_out2: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + if (m) + unmap_mft_record(base_ni); + return err; } -/* - * NOTES: There is a disparity between the apparent need to extend the - * attribute in prepare write but to update i_size only in commit write. - * Need to make sure i_sem protection is sufficient. And if not will need to - * handle this in some way or another. - */ - /** * ntfs_commit_nonresident_write - * @@ -1807,24 +1913,21 @@ unsigned from, unsigned to) { s64 pos = ((s64)page->index << PAGE_CACHE_SHIFT) + to; - struct inode *vi; + struct inode *vi = page->mapping->host; struct buffer_head *bh, *head; unsigned int block_start, block_end, blocksize; BOOL partial; - vi = page->mapping->host; - ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index " "0x%lx, from = %u, to = %u.", vi->i_ino, NTFS_I(vi)->type, page->index, from, to); - blocksize = 1 << vi->i_blkbits; - // FIXME: We need a whole slew of special cases in here for MST - // protected attributes for example. For compressed files, too... + // FIXME: We need a whole slew of special cases in here for compressed + // files for example... // For now, we know ntfs_prepare_write() would have failed so we can't // get here in any of the cases which we have to special case, so we - // are just a ripped off unrolled generic_commit_write() at present. + // are just a ripped off, unrolled generic_commit_write(). bh = head = page_buffers(page); block_start = 0; @@ -1839,24 +1942,22 @@ mark_buffer_dirty(bh); } } while (block_start = block_end, (bh = bh->b_this_page) != head); - /* * If this is a partial write which happened to make all buffers * uptodate then we can optimize away a bogus ->readpage() for the next - * read(). Here we 'discover' whether the page went uptodate as a + * read(). Here we 'discover' whether the page went uptodate as a * result of this (potentially partial) write. */ if (!partial) SetPageUptodate(page); - /* - * Not convinced about this at all. See disparity comment above. For + * Not convinced about this at all. See disparity comment above. For * now we know ntfs_prepare_write() would have failed in the write * exceeds i_size case, so this will never trigger which is fine. */ if (pos > vi->i_size) { ntfs_error(vi->i_sb, "Writing beyond the existing file size is " - "not supported yet. Sorry."); + "not supported yet. Sorry."); return -EOPNOTSUPP; // vi->i_size = pos; // mark_inode_dirty(vi); @@ -1869,118 +1970,73 @@ * ntfs_commit_write - commit the received data * * This is called from generic_file_write() with i_sem held on the inode - * (@page->mapping->host). The @page is locked and kmap()ped so page_address() - * can simply be used. The source data has already been copied into the @page. + * (@page->mapping->host). The @page is locked but not kmap()ped. The source + * data has already been copied into the @page. ntfs_prepare_write() has been + * called before the data copied and it returned success so we can take the + * results of various BUG checks and some error handling for granted. * * Need to mark modified blocks dirty so they get written out later when * ntfs_writepage() is invoked by the VM. * * Return 0 on success or -errno on error. * - * Should be using generic_commit_write(). This marks buffers uptodate and + * Should be using generic_commit_write(). This marks buffers uptodate and * dirty, sets the page uptodate if all buffers in the page are uptodate, and - * updates i_size if the end of io is beyond i_size. In that case, it also - * marks the inode dirty. - We could still use this (obviously except for - * NInoMstProtected() attributes, where we will need to duplicate the core code - * because we need our own async_io completion handler) but we could just do - * the i_size update in prepare write, when we resize the attribute. Then - * we would avoid the i_size update and mark_inode_dirty() happening here. + * updates i_size if the end of io is beyond i_size. In that case, it also + * marks the inode dirty. * - * Can't use generic_commit_write() due to ntfs specialities but can look at + * Cannot use generic_commit_write() due to ntfs specialities but can look at * it for implementation guidance. * * If things have gone as outlined in ntfs_prepare_write(), then we do not * need to do any page content modifications here at all, except in the write * to resident attribute case, where we need to do the uptodate bringing here - * which we combine with the copying into the mft record which means we only - * need to map the mft record and find the attribute record in it only once. + * which we combine with the copying into the mft record which means we save + * one atomic kmap. */ static int ntfs_commit_write(struct file *file, struct page *page, unsigned from, unsigned to) { - s64 attr_pos; - struct inode *vi; - ntfs_inode *ni, *base_ni; + struct inode *vi = page->mapping->host; + ntfs_inode *base_ni, *ni = NTFS_I(vi); char *kaddr, *kattr; ntfs_attr_search_ctx *ctx; MFT_RECORD *m; - u32 attr_len, bytes; + ATTR_RECORD *a; + u32 attr_len; int err; - vi = page->mapping->host; - ni = NTFS_I(vi); - ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index " "0x%lx, from = %u, to = %u.", vi->i_ino, ni->type, page->index, from, to); - + /* If the attribute is not resident, deal with it elsewhere. */ if (NInoNonResident(ni)) { - /* - * Only unnamed $DATA attributes can be compressed, encrypted, - * and/or sparse. - */ + /* Only unnamed $DATA attributes can be compressed/encrypted. */ if (ni->type == AT_DATA && !ni->name_len) { - /* If file is encrypted, deny access, just like NT4. */ + /* Encrypted files need separate handling. */ if (NInoEncrypted(ni)) { - // Should never get here! - ntfs_debug("Denying write access to encrypted " - "file."); - return -EACCES; + // We never get here at present! + BUG(); } /* Compressed data streams are handled in compress.c. */ if (NInoCompressed(ni)) { - // TODO: Implement and replace this check with + // TODO: Implement this! // return ntfs_write_compressed_block(page); - // Should never get here! - ntfs_error(vi->i_sb, "Writing to compressed " - "files is not supported yet. " - "Sorry."); - return -EOPNOTSUPP; - } - // TODO: Implement and remove this check. - if (NInoSparse(ni)) { - // Should never get here! - ntfs_error(vi->i_sb, "Writing to sparse files " - "is not supported yet. Sorry."); - return -EOPNOTSUPP; + // We never get here at present! + BUG(); } } - - // TODO: Implement and remove this check. - if (NInoMstProtected(ni)) { - // Should never get here! - ntfs_error(vi->i_sb, "Writing to MST protected " - "attributes is not supported yet. " - "Sorry."); - return -EOPNOTSUPP; - } - /* Normal data stream. */ return ntfs_commit_nonresident_write(page, from, to); } - /* * Attribute is resident, implying it is not compressed, encrypted, or - * mst protected. + * sparse. */ - - /* Do we need to resize the attribute? */ - if (((s64)page->index << PAGE_CACHE_SHIFT) + to > vi->i_size) { - // TODO: Implement resize... - // pos = ((s64)page->index << PAGE_CACHE_SHIFT) + to; - // vi->i_size = pos; - // mark_inode_dirty(vi); - // Should never get here! - ntfs_error(vi->i_sb, "Writing beyond the existing file size is " - "not supported yet. Sorry."); - return -EOPNOTSUPP; - } - if (!NInoAttr(ni)) base_ni = ni; else base_ni = ni->ext.base_ntfs_ino; - /* Map, pin, and lock the mft record. */ m = map_mft_record(base_ni); if (IS_ERR(m)) { @@ -1996,61 +2052,36 @@ } err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx); - if (unlikely(err)) - goto err_out; - - /* Starting position of the page within the attribute value. */ - attr_pos = page->index << PAGE_CACHE_SHIFT; - - /* The total length of the attribute value. */ - attr_len = le32_to_cpu(ctx->attr->data.resident.value_length); - - if (unlikely(vi->i_size != attr_len)) { - ntfs_error(vi->i_sb, "BUG()! i_size (0x%llx) doesn't match " - "attr_len (0x%x). Aborting write.", vi->i_size, - attr_len); - err = -EIO; + if (unlikely(err)) { + if (err == -ENOENT) + err = -EIO; goto err_out; } - if (unlikely(attr_pos >= attr_len)) { - ntfs_error(vi->i_sb, "BUG()! attr_pos (0x%llx) > attr_len " - "(0x%x). Aborting write.", - (unsigned long long)attr_pos, attr_len); - err = -EIO; - goto err_out; - } - - bytes = attr_len - attr_pos; - if (unlikely(bytes > PAGE_CACHE_SIZE)) - bytes = PAGE_CACHE_SIZE; - - /* - * Calculate the address of the attribute value corresponding to the - * beginning of the current data @page. - */ - kattr = (u8*)ctx->attr + le16_to_cpu( - ctx->attr->data.resident.value_offset) + attr_pos; - + a = ctx->attr; + /* The total length of the attribute value. */ + attr_len = le32_to_cpu(a->data.resident.value_length); + BUG_ON(from > attr_len); + kattr = (u8*)a + le16_to_cpu(a->data.resident.value_offset); kaddr = kmap_atomic(page, KM_USER0); - /* Copy the received data from the page to the mft record. */ memcpy(kattr + from, kaddr + from, to - from); - flush_dcache_mft_record_page(ctx->ntfs_ino); - + /* Update the attribute length if necessary. */ + if (to > attr_len) { + attr_len = to; + a->data.resident.value_length = cpu_to_le32(attr_len); + } + /* + * If the page is not uptodate, bring the out of bounds area(s) + * uptodate by copying data from the mft record to the page. + */ if (!PageUptodate(page)) { - /* - * Bring the out of bounds area(s) uptodate by copying data - * from the mft record to the page. - */ if (from > 0) memcpy(kaddr, kattr, from); - if (to < bytes) - memcpy(kaddr + to, kattr + to, bytes - to); - + if (to < attr_len) + memcpy(kaddr + to, kattr + to, attr_len - to); /* Zero the region outside the end of the attribute value. */ - if (likely(bytes < PAGE_CACHE_SIZE)) - memset(kaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); - + if (attr_len < PAGE_CACHE_SIZE) + memset(kaddr + attr_len, 0, PAGE_CACHE_SIZE - attr_len); /* * The probability of not having done any of the above is * extremely small, so we just flush unconditionally. @@ -2059,10 +2090,14 @@ SetPageUptodate(page); } kunmap_atomic(kaddr, KM_USER0); - + /* Update i_size if necessary. */ + if (vi->i_size < attr_len) { + ni->allocated_size = ni->initialized_size = attr_len; + i_size_write(vi, attr_len); + } /* Mark the mft record dirty, so it gets written back. */ + flush_dcache_mft_record_page(ctx->ntfs_ino); mark_mft_record_dirty(ctx->ntfs_ino); - ntfs_attr_put_search_ctx(ctx); unmap_mft_record(base_ni); ntfs_debug("Done."); @@ -2077,17 +2112,18 @@ "later on by the VM."); /* * Put the page on mapping->dirty_pages, but leave its - * buffer's dirty state as-is. + * buffers' dirty state as-is. */ __set_page_dirty_nobuffers(page); err = 0; } else - ntfs_error(vi->i_sb, "Page is not uptodate. Written " - "data has been lost. )-:"); + ntfs_error(vi->i_sb, "Page is not uptodate. Written " + "data has been lost."); } else { - ntfs_error(vi->i_sb, "Resident attribute write failed with " - "error %i. Setting page error flag.", -err); - SetPageError(page); + ntfs_error(vi->i_sb, "Resident attribute commit write failed " + "with error %i.", err); + NVolSetErrors(ni->vol); + make_bad_inode(vi); } if (ctx) ntfs_attr_put_search_ctx(ctx); @@ -2158,6 +2194,7 @@ } end = ofs + ni->itype.index.block_size; bh_size = ni->vol->sb->s_blocksize; + spin_lock(&page->mapping->private_lock); bh = head = page_buffers(page); do { bh_ofs = bh_offset(bh); @@ -2167,6 +2204,7 @@ break; set_buffer_dirty(bh); } while ((bh = bh->b_this_page) != head); + spin_unlock(&page->mapping->private_lock); __set_page_dirty_nobuffers(page); } diff -Nru a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c --- a/fs/ntfs/attrib.c 2004-10-28 22:32:44 -07:00 +++ b/fs/ntfs/attrib.c 2004-10-28 22:32:44 -07:00 @@ -24,8 +24,10 @@ #include "attrib.h" #include "debug.h" +#include "layout.h" #include "mft.h" #include "ntfs.h" +#include "types.h" /** * ntfs_map_runlist - map (a part of) a runlist of an ntfs inode @@ -947,6 +949,133 @@ unmap_extent_mft_record(ctx->ntfs_ino); kmem_cache_free(ntfs_attr_ctx_cache, ctx); return; +} + +/** + * ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to find + * + * Search for the attribute definition record corresponding to the attribute + * @type in the $AttrDef system file. + * + * Return the attribute type definition record if found and NULL if not found. + */ +static ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, + const ATTR_TYPE type) +{ + ATTR_DEF *ad; + + BUG_ON(!vol->attrdef); + BUG_ON(!type); + for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef < + vol->attrdef_size && ad->type; ++ad) { + /* We have not found it yet, carry on searching. */ + if (likely(le32_to_cpu(ad->type) < le32_to_cpu(type))) + continue; + /* We found the attribute; return it. */ + if (likely(ad->type == type)) + return ad; + /* We have gone too far already. No point in continuing. */ + break; + } + /* Attribute not found. */ + ntfs_debug("Attribute type 0x%x not found in $AttrDef.", + le32_to_cpu(type)); + return NULL; +} + +/** + * ntfs_attr_size_bounds_check - check a size of an attribute type for validity + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to check + * @size: size which to check + * + * Check whether the @size in bytes is valid for an attribute of @type on the + * ntfs volume @vol. This information is obtained from $AttrDef system file. + * + * Return 0 if valid, -ERANGE if not valid, or -ENOENT if the attribute is not + * listed in $AttrDef. + */ +int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPE type, + const s64 size) +{ + ATTR_DEF *ad; + + BUG_ON(size < 0); + /* + * $ATTRIBUTE_LIST has a maximum size of 256kiB, but this is not + * listed in $AttrDef. + */ + if (unlikely(type == AT_ATTRIBUTE_LIST && size > 256 * 1024)) + return -ERANGE; + /* Get the $AttrDef entry for the attribute @type. */ + ad = ntfs_attr_find_in_attrdef(vol, type); + if (unlikely(!ad)) + return -ENOENT; + /* Do the bounds check. */ + if (((sle64_to_cpu(ad->min_size) > 0) && + size < sle64_to_cpu(ad->min_size)) || + ((sle64_to_cpu(ad->max_size) > 0) && size > + sle64_to_cpu(ad->max_size))) + return -ERANGE; + return 0; +} + +/** + * ntfs_attr_can_be_non_resident - check if an attribute can be non-resident + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to check + * + * Check whether the attribute of @type on the ntfs volume @vol is allowed to + * be non-resident. This information is obtained from $AttrDef system file. + * + * Return 0 if the attribute is allowed to be non-resident, -EPERM if not, or + * -ENOENT if the attribute is not listed in $AttrDef. + */ +int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPE type) +{ + ATTR_DEF *ad; + + /* + * $DATA is always allowed to be non-resident even if $AttrDef does not + * specify this in the flags of the $DATA attribute definition record. + */ + if (type == AT_DATA) + return 0; + /* Find the attribute definition record in $AttrDef. */ + ad = ntfs_attr_find_in_attrdef(vol, type); + if (unlikely(!ad)) + return -ENOENT; + /* Check the flags and return the result. */ + if (ad->flags & CAN_BE_NON_RESIDENT) + return 0; + return -EPERM; +} + +/** + * ntfs_attr_can_be_resident - check if an attribute can be resident + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to check + * + * Check whether the attribute of @type on the ntfs volume @vol is allowed to + * be resident. This information is derived from our ntfs knowledge and may + * not be completely accurate, especially when user defined attributes are + * present. Basically we allow everything to be resident except for index + * allocation and $EA attributes. + * + * Return 0 if the attribute is allowed to be non-resident and -EPERM if not. + * + * Warning: In the system file $MFT the attribute $Bitmap must be non-resident + * otherwise windows will not boot (blue screen of death)! We cannot + * check for this here as we do not know which inode's $Bitmap is + * being asked about so the caller needs to special case this. + */ +int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPE type) +{ + if (type != AT_INDEX_ALLOCATION && type != AT_EA) + return 0; + return -EPERM; } /** diff -Nru a/fs/ntfs/attrib.h b/fs/ntfs/attrib.h --- a/fs/ntfs/attrib.h 2004-10-28 22:32:44 -07:00 +++ b/fs/ntfs/attrib.h 2004-10-28 22:32:44 -07:00 @@ -29,6 +29,7 @@ #include "layout.h" #include "inode.h" #include "runlist.h" +#include "volume.h" /** * ntfs_attr_search_ctx - used in attribute search functions @@ -83,6 +84,13 @@ extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec); extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx); + +extern int ntfs_attr_size_bounds_check(const ntfs_volume *vol, + const ATTR_TYPE type, const s64 size); +extern int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, + const ATTR_TYPE type); +extern int ntfs_attr_can_be_resident(const ntfs_volume *vol, + const ATTR_TYPE type); extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size); diff -Nru a/fs/ntfs/file.c b/fs/ntfs/file.c --- a/fs/ntfs/file.c 2004-10-28 22:32:44 -07:00 +++ b/fs/ntfs/file.c 2004-10-28 22:32:44 -07:00 @@ -145,7 +145,7 @@ struct inode_operations ntfs_file_inode_ops = { #ifdef NTFS_RW - .truncate = ntfs_truncate, + .truncate = ntfs_truncate_vfs, .setattr = ntfs_setattr, #endif /* NTFS_RW */ }; diff -Nru a/fs/ntfs/inode.c b/fs/ntfs/inode.c --- a/fs/ntfs/inode.c 2004-10-28 22:32:44 -07:00 +++ b/fs/ntfs/inode.c 2004-10-28 22:32:44 -07:00 @@ -564,13 +564,11 @@ } if (!(m->flags & MFT_RECORD_IN_USE)) { - ntfs_error(vi->i_sb, "Inode is not in use! You should " - "run chkdsk."); + ntfs_error(vi->i_sb, "Inode is not in use!"); goto unm_err_out; } if (m->base_mft_record) { - ntfs_error(vi->i_sb, "Inode is an extent inode! You should " - "run chkdsk."); + ntfs_error(vi->i_sb, "Inode is an extent inode!"); goto unm_err_out; } @@ -667,7 +665,7 @@ if (err) { if (unlikely(err != -ENOENT)) { ntfs_error(vi->i_sb, "Failed to lookup attribute list " - "attribute. You should run chkdsk."); + "attribute."); goto unm_err_out; } } else /* if (!err) */ { @@ -679,9 +677,7 @@ ctx->attr->flags & ATTR_COMPRESSION_MASK || ctx->attr->flags & ATTR_IS_SPARSE) { ntfs_error(vi->i_sb, "Attribute list attribute is " - "compressed/encrypted/sparse. Not " - "allowed. Corrupt inode. You should " - "run chkdsk."); + "compressed/encrypted/sparse."); goto unm_err_out; } /* Now allocate memory for the attribute list. */ @@ -697,9 +693,7 @@ NInoSetAttrListNonResident(ni); if (ctx->attr->data.non_resident.lowest_vcn) { ntfs_error(vi->i_sb, "Attribute list has non " - "zero lowest_vcn. Inode is " - "corrupt. You should run " - "chkdsk."); + "zero lowest_vcn."); goto unm_err_out; } /* @@ -712,10 +706,7 @@ err = PTR_ERR(ni->attr_list_rl.rl); ni->attr_list_rl.rl = NULL; ntfs_error(vi->i_sb, "Mapping pairs " - "decompression failed with " - "error code %i. Corrupt " - "attribute list in inode.", - -err); + "decompression failed."); goto unm_err_out; } /* Now load the attribute list. */ @@ -770,9 +761,18 @@ 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."); + if (unlikely(ctx->attr->non_resident)) { + ntfs_error(vol->sb, "$INDEX_ROOT attribute is not " + "resident."); + goto unm_err_out; + } + /* Ensure the attribute name is placed before the value. */ + if (unlikely(ctx->attr->name_length && + (le16_to_cpu(ctx->attr->name_offset) >= + le16_to_cpu(ctx->attr->data.resident. + value_offset)))) { + ntfs_error(vol->sb, "$INDEX_ROOT attribute name is " + "placed after the attribute value."); goto unm_err_out; } /* @@ -786,8 +786,7 @@ if (ctx->attr->flags & ATTR_IS_ENCRYPTED) { if (ctx->attr->flags & ATTR_COMPRESSION_MASK) { ntfs_error(vi->i_sb, "Found encrypted and " - "compressed attribute. Not " - "allowed."); + "compressed attribute."); goto unm_err_out; } NInoSetEncrypted(ni); @@ -811,12 +810,12 @@ } if (ir->type != AT_FILE_NAME) { ntfs_error(vi->i_sb, "Indexed attribute is not " - "$FILE_NAME. Not allowed."); + "$FILE_NAME."); goto unm_err_out; } if (ir->collation_rule != COLLATION_FILE_NAME) { ntfs_error(vi->i_sb, "Index collation rule is not " - "COLLATION_FILE_NAME. Not allowed."); + "COLLATION_FILE_NAME."); goto unm_err_out; } ni->itype.index.collation_rule = ir->collation_rule; @@ -831,7 +830,7 @@ 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.", + "supported. Sorry.", ni->itype.index.block_size, PAGE_CACHE_SIZE); err = -EOPNOTSUPP; @@ -840,7 +839,7 @@ 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.", + "supported. Sorry.", ni->itype.index.block_size, NTFS_BLOCK_SIZE); err = -EOPNOTSUPP; @@ -883,8 +882,7 @@ if (err == -ENOENT) ntfs_error(vi->i_sb, "$INDEX_ALLOCATION " "attribute is not present but " - "$INDEX_ROOT indicated it " - "is."); + "$INDEX_ROOT indicated it is."); else ntfs_error(vi->i_sb, "Failed to lookup " "$INDEX_ALLOCATION " @@ -896,6 +894,19 @@ "is resident."); goto unm_err_out; } + /* + * Ensure the attribute name is placed before the mapping pairs + * array. + */ + if (unlikely(ctx->attr->name_length && + (le16_to_cpu(ctx->attr->name_offset) >= + le16_to_cpu(ctx->attr->data.non_resident. + mapping_pairs_offset)))) { + ntfs_error(vol->sb, "$INDEX_ALLOCATION attribute name " + "is placed after the mapping pairs " + "array."); + goto unm_err_out; + } if (ctx->attr->flags & ATTR_IS_ENCRYPTED) { ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute " "is encrypted."); @@ -914,8 +925,7 @@ 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."); + "zero lowest_vcn."); goto unm_err_out; } vi->i_size = sle64_to_cpu( @@ -997,8 +1007,7 @@ goto no_data_attr_special_case; // FIXME: File is corrupt! Hot-fix with empty data // attribute if recovery option is set. - ntfs_error(vi->i_sb, "$DATA attribute is " - "missing."); + ntfs_error(vi->i_sb, "$DATA attribute is missing."); goto unm_err_out; } /* Setup the state. */ @@ -1028,10 +1037,8 @@ compression_unit != 4) { ntfs_error(vi->i_sb, "Found " "nonstandard compression unit " - "(%u instead of 4). Cannot " - "handle this. This might " - "indicate corruption so you " - "should run chkdsk.", + "(%u instead of 4). Cannot " + "handle this.", ctx->attr->data.non_resident. compression_unit); err = -EOPNOTSUPP; @@ -1057,8 +1064,7 @@ if (ctx->attr->data.non_resident.lowest_vcn) { ntfs_error(vi->i_sb, "First extent of $DATA " "attribute has non zero " - "lowest_vcn. Inode is corrupt. " - "You should run chkdsk."); + "lowest_vcn."); goto unm_err_out; } /* Setup all the sizes. */ @@ -1127,9 +1133,11 @@ if (m) unmap_mft_record(ni); err_out: - ntfs_error(vi->i_sb, "Failed with error code %i. Marking inode 0x%lx " - "as bad.", -err, vi->i_ino); + ntfs_error(vol->sb, "Failed with error code %i. Marking corrupt " + "inode 0x%lx as bad. Run chkdsk.", err, vi->i_ino); make_bad_inode(vi); + if (err != -EOPNOTSUPP && err != -ENOMEM) + NVolSetErrors(vol); return err; } @@ -1200,15 +1208,21 @@ goto unm_err_out; if (!ctx->attr->non_resident) { + /* Ensure the attribute name is placed before the value. */ + if (unlikely(ctx->attr->name_length && + (le16_to_cpu(ctx->attr->name_offset) >= + le16_to_cpu(ctx->attr->data.resident. + value_offset)))) { + ntfs_error(vol->sb, "Attribute name is placed after " + "the attribute value."); + goto unm_err_out; + } if (NInoMstProtected(ni) || ctx->attr->flags) { ntfs_error(vi->i_sb, "Found mst protected attribute " "or attribute with non-zero flags but " - "the attribute is resident (mft_no " - "0x%lx, type 0x%x, name_len %i). " - "Please report you saw this message " - "to linux-ntfs-dev@lists." - "sourceforge.net", - vi->i_ino, ni->type, ni->name_len); + "the attribute is resident. Please " + "report you saw this message to " + "linux-ntfs-dev@lists.sourceforge.net"); goto unm_err_out; } /* @@ -1219,60 +1233,63 @@ le32_to_cpu(ctx->attr->data.resident.value_length); } else { NInoSetNonResident(ni); + /* + * Ensure the attribute name is placed before the mapping pairs + * array. + */ + if (unlikely(ctx->attr->name_length && + (le16_to_cpu(ctx->attr->name_offset) >= + le16_to_cpu(ctx->attr->data.non_resident. + mapping_pairs_offset)))) { + ntfs_error(vol->sb, "Attribute name is placed after " + "the mapping pairs array."); + goto unm_err_out; + } if (ctx->attr->flags & ATTR_COMPRESSION_MASK) { if (NInoMstProtected(ni)) { ntfs_error(vi->i_sb, "Found mst protected " "attribute but the attribute " - "is compressed (mft_no 0x%lx, " - "type 0x%x, name_len %i). " - "Please report you saw this " - "message to linux-ntfs-dev@" - "lists.sourceforge.net", - vi->i_ino, ni->type, - ni->name_len); + "is compressed. Please report " + "you saw this message to " + "linux-ntfs-dev@lists." + "sourceforge.net"); goto unm_err_out; } NInoSetCompressed(ni); if ((ni->type != AT_DATA) || (ni->type == AT_DATA && ni->name_len)) { - ntfs_error(vi->i_sb, "Found compressed non-" - "data or named data attribute " - "(mft_no 0x%lx, type 0x%x, " - "name_len %i). Please report " + ntfs_error(vi->i_sb, "Found compressed " + "non-data or named data " + "attribute. Please report " "you saw this message to " "linux-ntfs-dev@lists." - "sourceforge.net", - vi->i_ino, ni->type, - ni->name_len); + "sourceforge.net"); goto unm_err_out; } if (vol->cluster_size > 4096) { - ntfs_error(vi->i_sb, "Found " - "compressed attribute but " - "compression is disabled due " - "to cluster size (%i) > 4kiB.", - vol->cluster_size); + ntfs_error(vi->i_sb, "Found compressed " + "attribute but compression is " + "disabled due to cluster size " + "(%i) > 4kiB.", + vol->cluster_size); goto unm_err_out; } if ((ctx->attr->flags & ATTR_COMPRESSION_MASK) != ATTR_IS_COMPRESSED) { ntfs_error(vi->i_sb, "Found unknown " - "compression method or " - "corrupt file."); + "compression method."); goto unm_err_out; } ni->itype.compressed.block_clusters = 1U << ctx->attr->data.non_resident. compression_unit; - if (ctx->attr->data.non_resident.compression_unit != 4) { - ntfs_error(vi->i_sb, "Found " - "nonstandard compression unit " - "(%u instead of 4). Cannot " - "handle this. This might " - "indicate corruption so you " - "should run chkdsk.", - ctx->attr->data.non_resident. - compression_unit); + if (ctx->attr->data.non_resident.compression_unit != + 4) { + ntfs_error(vi->i_sb, "Found nonstandard " + "compression unit (%u instead " + "of 4). Cannot handle this.", + ctx->attr->data.non_resident. + compression_unit); err = -EOPNOTSUPP; goto unm_err_out; } @@ -1292,13 +1309,10 @@ if (NInoMstProtected(ni)) { ntfs_error(vi->i_sb, "Found mst protected " "attribute but the attribute " - "is encrypted (mft_no 0x%lx, " - "type 0x%x, name_len %i). " - "Please report you saw this " - "message to linux-ntfs-dev@" - "lists.sourceforge.net", - vi->i_ino, ni->type, - ni->name_len); + "is encrypted. Please report " + "you saw this message to " + "linux-ntfs-dev@lists." + "sourceforge.net"); goto unm_err_out; } NInoSetEncrypted(ni); @@ -1307,21 +1321,17 @@ if (NInoMstProtected(ni)) { ntfs_error(vi->i_sb, "Found mst protected " "attribute but the attribute " - "is sparse (mft_no 0x%lx, " - "type 0x%x, name_len %i). " - "Please report you saw this " - "message to linux-ntfs-dev@" - "lists.sourceforge.net", - vi->i_ino, ni->type, - ni->name_len); + "is sparse. Please report " + "you saw this message to " + "linux-ntfs-dev@lists." + "sourceforge.net"); goto unm_err_out; } NInoSetSparse(ni); } if (ctx->attr->data.non_resident.lowest_vcn) { ntfs_error(vi->i_sb, "First extent of attribute has " - "non-zero lowest_vcn. Inode is " - "corrupt. You should run chkdsk."); + "non-zero lowest_vcn."); goto unm_err_out; } /* Setup all the sizes. */ @@ -1372,10 +1382,15 @@ 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 " - "attribute inode (mft_no 0x%lx, type 0x%x, name_len " - "%i.", -err, vi->i_ino, ni->type, ni->name_len); + ntfs_error(vol->sb, "Failed with error code %i while reading attribute " + "inode (mft_no 0x%lx, type 0x%x, name_len %i). " + "Marking corrupt inode and base inode 0x%lx as bad. " + "Run chkdsk.", err, vi->i_ino, ni->type, ni->name_len, + base_vi->i_ino); make_bad_inode(vi); + make_bad_inode(base_vi); + if (err != -ENOMEM) + NVolSetErrors(vol); return err; } @@ -1460,16 +1475,24 @@ 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."); + if (unlikely(ctx->attr->non_resident)) { + ntfs_error(vol->sb, "$INDEX_ROOT attribute is not resident."); + goto unm_err_out; + } + /* Ensure the attribute name is placed before the value. */ + if (unlikely(ctx->attr->name_length && + (le16_to_cpu(ctx->attr->name_offset) >= + le16_to_cpu(ctx->attr->data.resident. + value_offset)))) { + ntfs_error(vol->sb, "$INDEX_ROOT attribute name is placed " + "after the attribute value."); 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."); + "root attribute."); goto unm_err_out; } ir = (INDEX_ROOT*)((u8*)ctx->attr + @@ -1485,8 +1508,8 @@ 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)); + ntfs_error(vi->i_sb, "Index type is not 0 (type is 0x%x).", + le32_to_cpu(ir->type)); goto unm_err_out; } ni->itype.index.collation_rule = ir->collation_rule; @@ -1552,6 +1575,16 @@ "resident."); goto unm_err_out; } + /* + * Ensure the attribute name is placed before the mapping pairs array. + */ + if (unlikely(ctx->attr->name_length && (le16_to_cpu( + ctx->attr->name_offset) >= le16_to_cpu( + ctx->attr->data.non_resident.mapping_pairs_offset)))) { + ntfs_error(vol->sb, "$INDEX_ALLOCATION attribute name is " + "placed after the mapping pairs array."); + goto unm_err_out; + } if (ctx->attr->flags & ATTR_IS_ENCRYPTED) { ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is " "encrypted."); @@ -1568,8 +1601,7 @@ } 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."); + "attribute has non zero lowest_vcn."); goto unm_err_out; } vi->i_size = sle64_to_cpu(ctx->attr->data.non_resident.data_size); @@ -1595,16 +1627,16 @@ 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."); + 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); + 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; @@ -1637,9 +1669,11 @@ 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, + "inode (mft_no 0x%lx, name_len %i.", err, vi->i_ino, ni->name_len); make_bad_inode(vi); + if (err != -EOPNOTSUPP && err != -ENOMEM) + NVolSetErrors(vol); return err; } @@ -2263,68 +2297,93 @@ * 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. * + * Returns 0 on success or -errno on error. + * * 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) +int ntfs_truncate(struct inode *vi) { ntfs_inode *ni = NTFS_I(vi); + ntfs_volume *vol = ni->vol; ntfs_attr_search_ctx *ctx; MFT_RECORD *m; + const char *te = " Leaving file length out of sync with i_size."; int err; + ntfs_debug("Entering for inode 0x%lx.", vi->i_ino); BUG_ON(NInoAttr(ni)); BUG_ON(ni->nr_extents < 0); m = map_mft_record(ni); if (IS_ERR(m)) { + err = PTR_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; + "(error code %d).%s", vi->i_ino, err, te); + ctx = NULL; + m = NULL; + goto err_out; } 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; + ntfs_error(vi->i_sb, "Failed to allocate a search context for " + "inode 0x%lx (not enough memory).%s", + vi->i_ino, te); + err = -ENOMEM; + goto err_out; } err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx); if (unlikely(err)) { - if (err == -ENOENT) { + 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 { + 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; + goto err_out; } /* If the size has not changed there is nothing to do. */ if (ntfs_attr_size(ctx->attr) == i_size_read(vi)) - goto out; + goto done; // 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: +done: ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); - return; + ntfs_debug("Done."); + NInoClearTruncateFailed(ni); + return 0; +err_out: + if (err != -ENOMEM) { + NVolSetErrors(vol); + make_bad_inode(vi); + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + if (m) + unmap_mft_record(ni); + NInoSetTruncateFailed(ni); + return err; +} + +/** + * ntfs_truncate_vfs - wrapper for ntfs_truncate() that has no return value + * @vi: inode for which the i_size was changed + * + * Wrapper for ntfs_truncate() that has no return value. + * + * See ntfs_truncate() description above for details. + */ +void ntfs_truncate_vfs(struct inode *vi) { + ntfs_truncate(vi); } /** diff -Nru a/fs/ntfs/inode.h b/fs/ntfs/inode.h --- a/fs/ntfs/inode.h 2004-10-28 22:32:44 -07:00 +++ b/fs/ntfs/inode.h 2004-10-28 22:32:44 -07:00 @@ -165,6 +165,7 @@ NI_Sparse, /* 1: Unnamed data attr is sparse (f). 1: Create sparse files by default (d). 1: Attribute is sparse (a). */ + NI_TruncateFailed, /* 1: Last ntfs_truncate() call failed. */ } ntfs_inode_state_bits; /* @@ -216,6 +217,7 @@ NINO_FNS(Compressed) NINO_FNS(Encrypted) NINO_FNS(Sparse) +NINO_FNS(TruncateFailed) /* * The full structure containing a ntfs_inode and a vfs struct inode. Used for @@ -300,7 +302,8 @@ #ifdef NTFS_RW -extern void ntfs_truncate(struct inode *vi); +extern int ntfs_truncate(struct inode *vi); +extern void ntfs_truncate_vfs(struct inode *vi); extern int ntfs_setattr(struct dentry *dentry, struct iattr *attr); diff -Nru a/fs/ntfs/layout.h b/fs/ntfs/layout.h --- a/fs/ntfs/layout.h 2004-10-28 22:32:44 -07:00 +++ b/fs/ntfs/layout.h 2004-10-28 22:32:44 -07:00 @@ -589,8 +589,8 @@ FIXME: What does it mean? (AIA) */ /* 88*/ COLLATION_RULE collation_rule; /* Default collation rule. */ /* 8c*/ ATTR_DEF_FLAGS flags; /* Flags describing the attribute. */ -/* 90*/ le64 min_size; /* Optional minimum attribute size. */ -/* 98*/ le64 max_size; /* Maximum size of attribute. */ +/* 90*/ sle64 min_size; /* Optional minimum attribute size. */ +/* 98*/ sle64 max_size; /* Maximum size of attribute. */ /* sizeof() = 0xa0 or 160 bytes */ } __attribute__ ((__packed__)) ATTR_DEF; diff -Nru a/fs/ntfs/super.c b/fs/ntfs/super.c --- a/fs/ntfs/super.c 2004-10-28 22:32:44 -07:00 +++ b/fs/ntfs/super.c 2004-10-28 22:32:44 -07:00 @@ -680,7 +680,7 @@ * @b: boot sector to parse * * Parse the ntfs boot sector @b and store all imporant information therein in - * the ntfs super block @vol. Return TRUE on success and FALSE on error. + * the ntfs super block @vol. Return TRUE on success and FALSE on error. */ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b) { @@ -713,12 +713,12 @@ vol->cluster_size_bits, vol->cluster_size_bits); if (vol->sector_size > vol->cluster_size) { ntfs_error(vol->sb, "Sector sizes above the cluster size are " - "not supported. Sorry."); + "not supported. Sorry."); return FALSE; } if (vol->sb->s_blocksize > vol->cluster_size) { ntfs_error(vol->sb, "Cluster sizes smaller than the device " - "sector size are not supported. Sorry."); + "sector size are not supported. Sorry."); return FALSE; } clusters_per_mft_record = b->clusters_per_mft_record; @@ -742,6 +742,18 @@ vol->mft_record_size_mask); ntfs_debug("vol->mft_record_size_bits = %i (0x%x)", vol->mft_record_size_bits, vol->mft_record_size_bits); + /* + * We cannot support mft record sizes above the PAGE_CACHE_SIZE since + * we store $MFT/$DATA, the table of mft records in the page cache. + */ + if (vol->mft_record_size > PAGE_CACHE_SIZE) { + ntfs_error(vol->sb, "Mft record size %i (0x%x) exceeds the " + "page cache size on your system %lu (0x%lx). " + "This is not supported. Sorry.", + vol->mft_record_size, vol->mft_record_size, + PAGE_CACHE_SIZE, PAGE_CACHE_SIZE); + return FALSE; + } clusters_per_index_record = b->clusters_per_index_record; ntfs_debug("clusters_per_index_record = %i (0x%x)", clusters_per_index_record, clusters_per_index_record); @@ -772,7 +784,7 @@ */ ll = sle64_to_cpu(b->number_of_sectors) >> sectors_per_cluster_bits; if ((u64)ll >= 1ULL << 32) { - ntfs_error(vol->sb, "Cannot handle 64-bit clusters. Sorry."); + ntfs_error(vol->sb, "Cannot handle 64-bit clusters. Sorry."); return FALSE; } vol->nr_clusters = ll; @@ -785,8 +797,8 @@ if (sizeof(unsigned long) < 8) { if ((ll << vol->cluster_size_bits) >= (1ULL << 41)) { ntfs_error(vol->sb, "Volume size (%lluTiB) is too " - "large for this architecture. Maximum " - "supported is 2TiB. Sorry.", + "large for this architecture. " + "Maximum supported is 2TiB. Sorry.", (unsigned long long)ll >> (40 - vol->cluster_size_bits)); return FALSE; @@ -794,14 +806,14 @@ } ll = sle64_to_cpu(b->mft_lcn); if (ll >= vol->nr_clusters) { - ntfs_error(vol->sb, "MFT LCN is beyond end of volume. Weird."); + ntfs_error(vol->sb, "MFT LCN is beyond end of volume. Weird."); return FALSE; } vol->mft_lcn = ll; ntfs_debug("vol->mft_lcn = 0x%llx", (long long)vol->mft_lcn); ll = sle64_to_cpu(b->mftmirr_lcn); if (ll >= vol->nr_clusters) { - ntfs_error(vol->sb, "MFTMirr LCN is beyond end of volume. " + ntfs_error(vol->sb, "MFTMirr LCN is beyond end of volume. " "Weird."); return FALSE; }