bk://linux-ntfs.bkbits.net/ntfs-2.6-devel aia21@cantab.net|ChangeSet|20040622084259|22734 aia21 # This is a BitKeeper generated diff -Nru style patch. # # ChangeSet # 2004/06/22 09:42:59+01:00 aia21@cantab.net # NTFS: Forgot the set_page_writeback()/end_page_writeback() in the mst # protected writepage case. # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/aops.c # 2004/06/22 09:42:52+01:00 aia21@cantab.net +49 -7 # Forgot the set_page_writeback()/end_page_writeback() in the mst # protected writepage case. # # ChangeSet # 2004/06/21 15:26:23+01:00 aia21@cantab.net # NTFS: Load the quota file ($Quota) and check if quota tracking is enabled # and if so, mark the quotas out of date. This causes windows to # rescan the volume on boot and update all quota entries. # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/quota.h # 2004/06/21 15:26:15+01:00 aia21@cantab.net +35 -0 # Add quota.h containing declarations for handling of $Quota system file. # The only function at present is ntfs_mark_quotas_out_of_date() which # marks the quotas on the volume out of date. # # fs/ntfs/quota.h # 2004/06/21 15:26:15+01:00 aia21@cantab.net +0 -0 # BitKeeper file /home/src/ntfs-2.6-devel/fs/ntfs/quota.h # # fs/ntfs/quota.c # 2004/06/21 15:26:14+01:00 aia21@cantab.net +115 -0 # Add quota.c containing functions for handling of $Quota system file. # The only function at present is ntfs_mark_quotas_out_of_date() which # marks the quotas on the volume out of date. # # fs/ntfs/super.c # 2004/06/21 15:26:14+01:00 aia21@cantab.net +179 -13 # Load the quota file and mark quotas out of date on read-write (re)mount. # # fs/ntfs/quota.c # 2004/06/21 15:26:14+01:00 aia21@cantab.net +0 -0 # BitKeeper file /home/src/ntfs-2.6-devel/fs/ntfs/quota.c # # fs/ntfs/layout.h # 2004/06/21 15:26:14+01:00 aia21@cantab.net +19 -9 # Add some constants for quota handling and improve some quota related # comments. # # fs/ntfs/Makefile # 2004/06/21 15:26:14+01:00 aia21@cantab.net +1 -1 # Add quota.[hc] to read-write build. # # fs/ntfs/ChangeLog # 2004/06/21 15:26:13+01:00 aia21@cantab.net +3 -0 # Update # # ChangeSet # 2004/06/21 12:35:49+01:00 aia21@cantab.net # NTFS: Add a new type, ntfs_index_context, to allow retrieval of an index # entry using the corresponding index key. To get an index context, # use ntfs_index_ctx_get() and to release it, use ntfs_index_ctx_put(). # This also adds a new slab cache for the index contexts. To lookup a # key in an index inode, use ntfs_index_lookup(). After modifying an # index entry, call ntfs_index_entry_flush_dcache_page() followed by # ntfs_index_entry_mark_dirty() to ensure the changes are written out # to disk. For details see fs/ntfs/index.[hc]. Note, at present, if # an index entry is in the index allocation attribute rather than the # index root attribute it will not be written out (you will get a # warning message about discarded changes instead). # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/index.h # 2004/06/21 12:35:42+01:00 aia21@cantab.net +155 -0 # # fs/ntfs/index.h # 2004/06/21 12:35:42+01:00 aia21@cantab.net +0 -0 # BitKeeper file /home/src/ntfs-2.6-devel/fs/ntfs/index.h # # fs/ntfs/index.c # 2004/06/21 12:35:41+01:00 aia21@cantab.net +459 -0 # # fs/ntfs/super.c # 2004/06/21 12:35:41+01:00 aia21@cantab.net +37 -16 # Add handling of ntfs_index_ctx slab cache and some cleanups. # # fs/ntfs/index.c # 2004/06/21 12:35:41+01:00 aia21@cantab.net +0 -0 # BitKeeper file /home/src/ntfs-2.6-devel/fs/ntfs/index.c # # fs/ntfs/ntfs.h # 2004/06/21 12:35:40+01:00 aia21@cantab.net +2 -0 # Add ntfs_index_ctx_cache declaration. # # fs/ntfs/aops.c # 2004/06/21 12:35:40+01:00 aia21@cantab.net +8 -8 # Add writepage to ntfs_mst_aops and for now display a warning message when it # is called. # # fs/ntfs/Makefile # 2004/06/21 12:35:40+01:00 aia21@cantab.net +1 -1 # Add index.[hc] to build. # # fs/ntfs/ChangeLog # 2004/06/21 12:35:40+01:00 aia21@cantab.net +11 -0 # Update # # ChangeSet # 2004/06/21 12:02:35+01:00 aia21@cantab.net # NTFS: Add framework for generic ntfs collation (fs/ntfs/collation.[hc]). # We have ntfs_is_collation_rule_supported() to check if the collation # rule you want to use is supported and ntfs_collation() which actually # collates two data items. We currently only support COLLATION_BINARY # and COLLATION_NTOFS_ULONG but support for other collation rules will # be added as the need arises. # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/collate.h # 2004/06/21 12:02:27+01:00 aia21@cantab.net +48 -0 # # fs/ntfs/collate.h # 2004/06/21 12:02:27+01:00 aia21@cantab.net +0 -0 # BitKeeper file /home/src/ntfs-2.6-devel/fs/ntfs/collate.h # # fs/ntfs/collate.c # 2004/06/21 12:02:26+01:00 aia21@cantab.net +121 -0 # # fs/ntfs/layout.h # 2004/06/21 12:02:26+01:00 aia21@cantab.net +10 -10 # Update collation comments and hexify constants. # # fs/ntfs/collate.c # 2004/06/21 12:02:26+01:00 aia21@cantab.net +0 -0 # BitKeeper file /home/src/ntfs-2.6-devel/fs/ntfs/collate.c # # fs/ntfs/Makefile # 2004/06/21 12:02:26+01:00 aia21@cantab.net +3 -2 # Add collate.[hc] to build. # # fs/ntfs/ChangeLog # 2004/06/21 12:02:26+01:00 aia21@cantab.net +6 -0 # Update # # ChangeSet # 2004/06/21 11:45:38+01:00 aia21@cantab.net # NTFS: Lock all page cache pages belonging to mst protected attributes while # accessing them to ensure we never see corrupt data while the page is # under writeout. # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/mft.c # 2004/06/21 11:45:31+01:00 aia21@cantab.net +3 -2 # Make sure page is locked. # # fs/ntfs/dir.c # 2004/06/21 11:45:31+01:00 aia21@cantab.net +82 -19 # Lock all page cache pages belonging to mst protected attributes while # accessing them to ensure we never see corrupt data while the page is # under writeout. # # fs/ntfs/ChangeLog # 2004/06/21 11:45:31+01:00 aia21@cantab.net +3 -0 # Update # # ChangeSet # 2004/06/21 11:36:23+01:00 aia21@cantab.net # NTFS: Use case sensitive attribute lookups instead of case insensitive ones. # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/namei.c # 2004/06/21 11:36:16+01:00 aia21@cantab.net +3 -1 # Use case sensitive attribute lookups instead of case insensitive ones. # # fs/ntfs/inode.c # 2004/06/21 11:36:16+01:00 aia21@cantab.net +2 -2 # Use case sensitive attribute lookups instead of case insensitive ones. # # fs/ntfs/attrib.c # 2004/06/21 11:36:16+01:00 aia21@cantab.net +5 -5 # Use case sensitive attribute lookups instead of case insensitive ones. # # fs/ntfs/aops.c # 2004/06/21 11:36:16+01:00 aia21@cantab.net +3 -3 # Use case sensitive attribute lookups instead of case insensitive ones. # # fs/ntfs/ChangeLog # 2004/06/21 11:36:15+01:00 aia21@cantab.net +1 -0 # Update # # fs/ntfs/volume.h # 2004/06/21 10:58:40+01:00 aia21@cantab.net +9 -0 # Add the quota associated inodes and the $Extend inode to the ntfs_volume # structure. # # ChangeSet # 2004/06/21 10:43:59+01:00 aia21@cantab.net # NTFS: Use atomic kmap instead of kmap() in fs/ntfs/aops.c::ntfs_read{page,_block}(). # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/logfile.c # 2004/06/21 10:43:52+01:00 aia21@cantab.net +1 -1 # Fix typo in comments. # # fs/ntfs/aops.c # 2004/06/21 10:43:51+01:00 aia21@cantab.net +14 -9 # Use atomic kmap instead of kmap() in ntfs_read_block() and ntfs_readpage(). # # fs/ntfs/ChangeLog # 2004/06/21 10:43:51+01:00 aia21@cantab.net +2 -0 # Update # # ChangeSet # 2004/06/15 15:27:53+01:00 aia21@cantab.net # NTFS: Ensure that there is no overflow when doing page->index << # PAGE_CACHE_SHIFT by casting page->index to s64 in fs/ntfs/aops.c. # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/aops.c # 2004/06/15 15:27:47+01:00 aia21@cantab.net +5 -4 # Ensure that there is no overflow when doing page->index << # PAGE_CACHE_SHIFT by casting page->index to s64. # # fs/ntfs/ChangeLog # 2004/06/15 15:27:46+01:00 aia21@cantab.net +2 -0 # Update # # ChangeSet # 2004/06/15 14:03:37+01:00 aia21@cantab.net # NTFS: - Add new element itype.index.collation_rule to the ntfs inode # structure and set it appropriately in ntfs_read_locked_inode(). # - Implement a new inode type "index" to allow efficient access to the # indices found in various system files and adapt inode handling # accordingly (fs/ntfs/inode.[hc]). An index inode is essentially an # attribute inode (NInoAttr() is true) with an attribute type of # AT_INDEX_ALLOCATION. As such, it is no longer allowed to call # ntfs_attr_iget() with an attribute type of AT_INDEX_ALLOCATION as # there would be no way to distinguish between normal attribute inodes # and index inodes. The function to obtain an index inode is # ntfs_index_iget() and it uses the helper function # ntfs_read_locked_index_inode(). Note, we do not overload # ntfs_attr_iget() as indices consist of multiple attributes so using # ntfs_attr_iget() to obtain an index inode would be confusing. # # Signed-off-by: Anton Altaparmakov # # fs/ntfs/inode.h # 2004/06/15 14:03:28+01:00 aia21@cantab.net +8 -4 # Add new element itype.index.collation_rule to the ntfs inode structure. # # fs/ntfs/inode.c # 2004/06/15 14:03:28+01:00 aia21@cantab.net +356 -17 # - Add new element itype.index.collation_rule to the ntfs inode # structure and set it appropriately in ntfs_read_locked_inode(). # - Implement a new inode type "index" to allow efficient access to the # indices found in various system files and adapt inode handling # accordingly (fs/ntfs/inode.[hc]). An index inode is essentially an # attribute inode (NInoAttr() is true) with an attribute type of # AT_INDEX_ALLOCATION. As such, it is no longer allowed to call # ntfs_attr_iget() with an attribute type of AT_INDEX_ALLOCATION as # there would be no way to distinguish between normal attribute inodes # and index inodes. The function to obtain an index inode is # ntfs_index_iget() and it uses the helper function # ntfs_read_locked_index_inode(). Note, we do not overload # ntfs_attr_iget() as indices consist of multiple attributes so using # ntfs_attr_iget() to obtain an index inode would be confusing. # # fs/ntfs/Makefile # 2004/06/15 14:03:28+01:00 aia21@cantab.net +1 -1 # Update # # fs/ntfs/ChangeLog # 2004/06/15 14:03:28+01:00 aia21@cantab.net +17 -0 # Update # # ChangeSet # 2004/06/15 10:51:52+01:00 aia21@cantab.net # NTFS: sparse fix: void function with return (value) # # Sparse: # ======= # CHECK fs/ntfs/attrib.c # fs/ntfs/malloc.h:57:15: warning: return expression in void function # [repeated several times] # # From: Mika Kukkonen # Signed-off-by: Randy Dunlap # Signed-off-by: Anton Altaparmakov # # fs/ntfs/malloc.h # 2004/06/15 10:51:46+01:00 aia21@cantab.net +7 -9 # - Do not return a value for void function ntfs_free(). (Mika Kukkonen) # - Cleanup ntfs_malloc_nofs() a little. # diff -Nru a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog --- a/fs/ntfs/ChangeLog 2004-06-23 20:35:05 -07:00 +++ b/fs/ntfs/ChangeLog 2004-06-23 20:35:05 -07:00 @@ -35,6 +35,51 @@ - Enable the code for setting the NT4 compatibility flag when we start making NTFS 1.2 specific modifications. +2.1.15 - . + + - Add new element itype.index.collation_rule to the ntfs inode + structure and set it appropriately in ntfs_read_locked_inode(). + - Implement a new inode type "index" to allow efficient access to the + indices found in various system files and adapt inode handling + accordingly (fs/ntfs/inode.[hc]). An index inode is essentially an + attribute inode (NInoAttr() is true) with an attribute type of + AT_INDEX_ALLOCATION. As such, it is no longer allowed to call + ntfs_attr_iget() with an attribute type of AT_INDEX_ALLOCATION as + there would be no way to distinguish between normal attribute inodes + and index inodes. The function to obtain an index inode is + ntfs_index_iget() and it uses the helper function + ntfs_read_locked_index_inode(). Note, we do not overload + ntfs_attr_iget() as indices consist of multiple attributes so using + ntfs_attr_iget() to obtain an index inode would be confusing. + - Ensure that there is no overflow when doing page->index << + PAGE_CACHE_SHIFT by casting page->index to s64 in fs/ntfs/aops.c. + - Use atomic kmap instead of kmap() in fs/ntfs/aops.c::ntfs_read_page() + and ntfs_read_block(). + - Use case sensitive attribute lookups instead of case insensitive ones. + - Lock all page cache pages belonging to mst protected attributes while + accessing them to ensure we never see corrupt data while the page is + under writeout. + - Add framework for generic ntfs collation (fs/ntfs/collation.[hc]). + We have ntfs_is_collation_rule_supported() to check if the collation + rule you want to use is supported and ntfs_collation() which actually + collates two data items. We currently only support COLLATION_BINARY + and COLLATION_NTOFS_ULONG but support for other collation rules will + be added as the need arises. + - Add a new type, ntfs_index_context, to allow retrieval of an index + entry using the corresponding index key. To get an index context, + use ntfs_index_ctx_get() and to release it, use ntfs_index_ctx_put(). + This also adds a new slab cache for the index contexts. To lookup a + key in an index inode, use ntfs_index_lookup(). After modifying an + index entry, call ntfs_index_entry_flush_dcache_page() followed by + ntfs_index_entry_mark_dirty() to ensure the changes are written out + to disk. For details see fs/ntfs/index.[hc]. Note, at present, if + an index entry is in the index allocation attribute rather than the + index root attribute it will not be written out (you will get a + warning message about discarded changes instead). + - Load the quota file ($Quota) and check if quota tracking is enabled + and if so, mark the quotas out of date. This causes windows to + rescan the volume on boot and update all quota entries. + 2.1.14 - Fix an NFSd caused deadlock reported by several users. - Modify fs/ntfs/ntfs_readdir() to copy the index root attribute value diff -Nru a/fs/ntfs/Makefile b/fs/ntfs/Makefile --- a/fs/ntfs/Makefile 2004-06-23 20:35:05 -07:00 +++ b/fs/ntfs/Makefile 2004-06-23 20:35:05 -07:00 @@ -2,10 +2,11 @@ obj-$(CONFIG_NTFS_FS) += ntfs.o -ntfs-objs := aops.o attrib.o compress.o debug.o dir.o file.o inode.o mft.o \ - mst.o namei.o super.o sysctl.o unistr.o upcase.o +ntfs-objs := aops.o attrib.o collate.o compress.o debug.o dir.o file.o \ + index.o inode.o mft.o mst.o namei.o super.o sysctl.o unistr.o \ + upcase.o -EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.14\" +EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.15-WIP\" ifeq ($(CONFIG_NTFS_DEBUG),y) EXTRA_CFLAGS += -DDEBUG @@ -14,5 +15,5 @@ ifeq ($(CONFIG_NTFS_RW),y) EXTRA_CFLAGS += -DNTFS_RW -ntfs-objs += logfile.o +ntfs-objs += logfile.o quota.o endif diff -Nru a/fs/ntfs/aops.c b/fs/ntfs/aops.c --- a/fs/ntfs/aops.c 2004-06-23 20:35:05 -07:00 +++ b/fs/ntfs/aops.c 2004-06-23 20:35:05 -07:00 @@ -62,7 +62,8 @@ set_buffer_uptodate(bh); - file_ofs = (page->index << PAGE_CACHE_SHIFT) + bh_offset(bh); + file_ofs = ((s64)page->index << PAGE_CACHE_SHIFT) + + bh_offset(bh); /* Check for the current buffer head overflowing. */ if (file_ofs + bh->b_size > ni->initialized_size) { char *addr; @@ -190,7 +191,7 @@ return -ENOMEM; } - iblock = page->index << (PAGE_CACHE_SHIFT - blocksize_bits); + iblock = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits); lblock = (ni->allocated_size + blocksize - 1) >> blocksize_bits; zblock = (ni->initialized_size + blocksize - 1) >> blocksize_bits; @@ -204,6 +205,8 @@ rl = NULL; nr = i = 0; do { + u8 *kaddr; + if (unlikely(buffer_uptodate(bh))) continue; if (unlikely(buffer_mapped(bh))) { @@ -279,9 +282,10 @@ bh->b_blocknr = -1UL; clear_buffer_mapped(bh); handle_zblock: - memset(kmap(page) + i * blocksize, 0, blocksize); + kaddr = kmap_atomic(page, KM_USER0); + memset(kaddr + i * blocksize, 0, blocksize); flush_dcache_page(page); - kunmap(page); + kunmap_atomic(kaddr, KM_USER0); set_buffer_uptodate(bh); } while (i++, iblock++, (bh = bh->b_this_page) != head); @@ -343,7 +347,7 @@ { s64 attr_pos; ntfs_inode *ni, *base_ni; - char *addr; + u8 *kaddr; attr_search_context *ctx; MFT_RECORD *mrec; u32 attr_len; @@ -362,6 +366,7 @@ ni = NTFS_I(page->mapping->host); + /* NInoNonResident() == NInoIndexAllocPresent() */ if (NInoNonResident(ni)) { /* * Only unnamed $DATA attributes can be compressed or @@ -398,7 +403,7 @@ goto unm_err_out; } if (unlikely(!lookup_attr(ni->type, ni->name, ni->name_len, - IGNORE_CASE, 0, NULL, 0, ctx))) { + CASE_SENSITIVE, 0, NULL, 0, ctx))) { err = -ENOENT; goto put_unm_err_out; } @@ -409,22 +414,22 @@ /* The total length of the attribute value. */ attr_len = le32_to_cpu(ctx->attr->data.resident.value_length); - addr = kmap(page); + kaddr = kmap_atomic(page, KM_USER0); /* Copy over in bounds data, zeroing the remainder of the page. */ if (attr_pos < attr_len) { u32 bytes = attr_len - attr_pos; if (bytes > PAGE_CACHE_SIZE) bytes = PAGE_CACHE_SIZE; else if (bytes < PAGE_CACHE_SIZE) - memset(addr + bytes, 0, PAGE_CACHE_SIZE - bytes); + memset(kaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); /* Copy the data to the page. */ - memcpy(addr, attr_pos + (char*)ctx->attr + + memcpy(kaddr, attr_pos + (char*)ctx->attr + le16_to_cpu( ctx->attr->data.resident.value_offset), bytes); } else - memset(addr, 0, PAGE_CACHE_SIZE); + memset(kaddr, 0, PAGE_CACHE_SIZE); flush_dcache_page(page); - kunmap(page); + kunmap_atomic(kaddr, KM_USER0); SetPageUptodate(page); put_unm_err_out: @@ -440,6 +445,7 @@ /** * ntfs_write_block - write a @page to the backing store + * @wbc: writeback control structure * @page: page cache page to write out * * This function is for writing pages belonging to non-resident, non-mst @@ -508,7 +514,7 @@ /* NOTE: Different naming scheme to ntfs_read_block()! */ /* The first block in the page. */ - block = page->index << (PAGE_CACHE_SHIFT - blocksize_bits); + block = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits); /* The first out of bounds block for the data size. */ dblock = (vi->i_size + blocksize - 1) >> blocksize_bits; @@ -768,8 +774,54 @@ } /** + * ntfs_write_mst_block - write a @page to the backing store + * @wbc: writeback control structure + * @page: page cache page to write out + * + * This function is for writing pages belonging to non-resident, mst protected + * attributes to their backing store. The only supported attribute is the + * index allocation attribute. Both directory inodes and index inodes are + * supported. + * + * The page must remain locked for the duration of the write because we apply + * the mst fixups, write, and then undo the fixups, so if we were to unlock the + * page before undoing the fixups, any other user of the page will see the + * page contents as corrupt. + * + * Return 0 on success and -errno on error. + * + * Based on ntfs_write_block(), ntfs_mft_writepage(), and + * write_mft_record_nolock(). + */ +static int ntfs_write_mst_block(struct writeback_control *wbc, + struct page *page) +{ + struct inode *vi; + ntfs_inode *ni; + ntfs_volume *vol; + + vi = page->mapping->host; + ni = NTFS_I(vi); + vol = ni->vol; + ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index " + "0x%lx.", vi->i_ino, ni->type, page->index); + BUG_ON(!NInoNonResident(ni)); + BUG_ON(!NInoMstProtected(ni)); + BUG_ON(!(S_ISDIR(vi->i_mode) || + (NInoAttr(ni) && ni->type == AT_INDEX_ALLOCATION))); + BUG_ON(PageWriteback(page)); + set_page_writeback(page); + unlock_page(page); + end_page_writeback(page); + ntfs_warning(vi->i_sb, "Writing to index allocation attribute is not " + "supported yet. Discarding written data."); + return 0; +} + +/** * ntfs_writepage - write a @page to the backing store * @page: page cache page to write out + * @wbc: writeback control structure * * For non-resident attributes, ntfs_writepage() writes the @page by calling * the ntfs version of the generic block_write_full_page() function, @@ -812,6 +864,7 @@ ni = NTFS_I(vi); + /* NInoNonResident() == NInoIndexAllocPresent() */ if (NInoNonResident(ni)) { /* * Only unnamed $DATA attributes can be compressed, encrypted, @@ -843,7 +896,6 @@ return -EOPNOTSUPP; } } - /* We have to zero every time due to mmap-at-end-of-file. */ if (page->index >= (vi->i_size >> PAGE_CACHE_SHIFT)) { /* The page straddles i_size. */ @@ -853,16 +905,9 @@ flush_dcache_page(page); kunmap_atomic(kaddr, KM_USER0); } - - // TODO: Implement and remove this check. - if (NInoMstProtected(ni)) { - unlock_page(page); - ntfs_error(vi->i_sb, "Writing to MST protected " - "attributes is not supported yet. " - "Sorry."); - return -EOPNOTSUPP; - } - + /* Handle mst protected attributes. */ + if (NInoMstProtected(ni)) + return ntfs_write_mst_block(wbc, page); /* Normal data stream. */ return ntfs_write_block(wbc, page); } @@ -893,7 +938,7 @@ goto err_out; } if (unlikely(!lookup_attr(ni->type, ni->name, ni->name_len, - IGNORE_CASE, 0, NULL, 0, ctx))) { + CASE_SENSITIVE, 0, NULL, 0, ctx))) { err = -ENOENT; goto err_out; } @@ -1042,7 +1087,7 @@ return -ENOMEM; /* The first block in the page. */ - block = page->index << (PAGE_CACHE_SHIFT - blocksize_bits); + block = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits); /* * The first out of bounds block for the allocated size. No need to @@ -1667,7 +1712,7 @@ goto err_out; } if (unlikely(!lookup_attr(ni->type, ni->name, ni->name_len, - IGNORE_CASE, 0, NULL, 0, ctx))) { + CASE_SENSITIVE, 0, NULL, 0, ctx))) { err = -ENOENT; goto err_out; } @@ -1783,7 +1828,7 @@ .prepare_write = ntfs_prepare_write, /* Prepare page and buffers ready to receive data. */ .commit_write = ntfs_commit_write, /* Commit received data. */ -#endif +#endif /* NTFS_RW */ }; /** @@ -1794,4 +1839,7 @@ .readpage = ntfs_readpage, /* Fill page with data. */ .sync_page = block_sync_page, /* Currently, just unplugs the disk request queue. */ +#ifdef NTFS_RW + .writepage = ntfs_writepage, /* Write dirty page to disk. */ +#endif /* NTFS_RW */ }; diff -Nru a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c --- a/fs/ntfs/attrib.c 2004-06-23 20:35:05 -07:00 +++ b/fs/ntfs/attrib.c 2004-06-23 20:35:05 -07:00 @@ -963,7 +963,7 @@ err = -ENOMEM; goto err_out; } - if (!lookup_attr(ni->type, ni->name, ni->name_len, IGNORE_CASE, vcn, + if (!lookup_attr(ni->type, ni->name, ni->name_len, CASE_SENSITIVE, vcn, NULL, 0, ctx)) { put_attr_search_ctx(ctx); err = -ENOENT; @@ -1343,8 +1343,8 @@ * base inode). * * After finishing with the attribute/mft record you need to call - * release_attr_search_ctx() to cleanup the search context (unmapping any - * mapped inodes, etc). + * put_attr_search_ctx() to cleanup the search context (unmapping any mapped + * inodes, etc). * * Return TRUE if the search was successful and FALSE if not. When TRUE, * @ctx->attr is the found attribute and it is in mft record @ctx->mrec. When @@ -1614,8 +1614,8 @@ * continue searches where they were left off at. * * After finishing with the attribute/mft record you need to call - * release_attr_search_ctx() to cleanup the search context (unmapping any - * mapped inodes, etc). + * put_attr_search_ctx() to cleanup the search context (unmapping any mapped + * inodes, etc). * * Return TRUE if the search was successful and FALSE if not. When TRUE, * @ctx->attr is the found attribute and it is in mft record @ctx->mrec. When diff -Nru a/fs/ntfs/collate.c b/fs/ntfs/collate.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/fs/ntfs/collate.c 2004-06-23 20:35:05 -07:00 @@ -0,0 +1,121 @@ +/* + * collate.c - NTFS kernel collation handling. Part of the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ntfs.h" +#include "collate.h" + +static int ntfs_collate_binary(ntfs_volume *vol, + const void *data1, const int data1_len, + const void *data2, const int data2_len) +{ + int rc; + + ntfs_debug("Entering."); + rc = memcmp(data1, data2, min(data1_len, data2_len)); + if (!rc && (data1_len != data2_len)) { + if (data1_len < data2_len) + rc = -1; + else + rc = 1; + } + ntfs_debug("Done, returning %i", rc); + return rc; +} + +static int ntfs_collate_ntofs_ulong(ntfs_volume *vol, + const void *data1, const int data1_len, + const void *data2, const int data2_len) +{ + int rc; + u32 d1, d2; + + ntfs_debug("Entering."); + // FIXME: We don't really want to bug here. + BUG_ON(data1_len != data2_len); + BUG_ON(data1_len != 4); + d1 = le32_to_cpup(data1); + d2 = le32_to_cpup(data2); + if (d1 < d2) + rc = -1; + else { + if (d1 == d2) + rc = 0; + else + rc = 1; + } + ntfs_debug("Done, returning %i", rc); + return rc; +} + +typedef int (*ntfs_collate_func_t)(ntfs_volume *, const void *, const int, + const void *, const int); + +static ntfs_collate_func_t ntfs_do_collate0x0[3] = { + ntfs_collate_binary, + NULL/*ntfs_collate_file_name*/, + NULL/*ntfs_collate_unicode_string*/, +}; + +static ntfs_collate_func_t ntfs_do_collate0x1[4] = { + ntfs_collate_ntofs_ulong, + NULL/*ntfs_collate_ntofs_sid*/, + NULL/*ntfs_collate_ntofs_security_hash*/, + NULL/*ntfs_collate_ntofs_ulongs*/, +}; + +/** + * ntfs_collate - collate two data items using a specified collation rule + * @vol: ntfs volume to which the data items belong + * @cr: collation rule to use when comparing the items + * @data1: first data item to collate + * @data1_len: length in bytes of @data1 + * @data2: second data item to collate + * @data2_len: length in bytes of @data2 + * + * Collate the two data items @data1 and @data2 using the collation rule @cr + * and return -1, 0, ir 1 if @data1 is found, respectively, to collate before, + * to match, or to collate after @data2. + * + * For speed we use the collation rule @cr as an index into two tables of + * function pointers to call the appropriate collation function. + */ +int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr, + const void *data1, const int data1_len, + const void *data2, const int data2_len) { + ntfs_debug("Entering."); + /* + * FIXME: At the moment we only support COLLATION_BINARY and + * COLLATION_NTOFS_ULONG, so we BUG() for everything else for now. + */ + BUG_ON(cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG); + cr = le32_to_cpu(cr); + BUG_ON(cr < 0); + if (cr <= 0x02) + return ntfs_do_collate0x0[cr](vol, data1, data1_len, + data2, data2_len); + BUG_ON(cr < 0x10); + cr -= 0x10; + if (likely(cr <= 3)) + return ntfs_do_collate0x1[cr](vol, data1, data1_len, + data2, data2_len); + BUG(); + return 0; +} diff -Nru a/fs/ntfs/collate.h b/fs/ntfs/collate.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/fs/ntfs/collate.h 2004-06-23 20:35:05 -07:00 @@ -0,0 +1,48 @@ +/* + * collate.h - Defines for NTFS kernel collation handling. Part of the + * Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LINUX_NTFS_COLLATE_H +#define _LINUX_NTFS_COLLATE_H + +#include "types.h" +#include "volume.h" + +static inline BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr) { + /* + * FIXME: At the moment we only support COLLATION_BINARY and + * COLLATION_NTOFS_ULONG, so we return false for everything else for + * now. + */ + if (unlikely(cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG)) + return FALSE; + cr = le32_to_cpu(cr); + if (likely(((cr >= 0) && (cr <= 0x02)) || + ((cr >= 0x10) && (cr <= 0x13)))) + return TRUE; + return FALSE; +} + +extern int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr, + const void *data1, const int data1_len, + const void *data2, const int data2_len); + +#endif /* _LINUX_NTFS_COLLATE_H */ diff -Nru a/fs/ntfs/dir.c b/fs/ntfs/dir.c --- a/fs/ntfs/dir.c 2004-06-23 20:35:05 -07:00 +++ b/fs/ntfs/dir.c 2004-06-23 20:35:05 -07:00 @@ -63,6 +63,14 @@ * This is to avoid polluting the dcache with short file names. We want them to * work but we don't care for how quickly one can access them. This also fixes * the dcache aliasing issues. + * + * Locking: - Caller must hold i_sem on the directory. + * - Each page cache page in the index allocation mapping must be + * locked whilst being accessed otherwise we may find a corrupt + * page due to it being under ->writepage at the moment which + * applies the mst protection fixups before writing out and then + * removes them again after the write is complete after which it + * unlocks the page. */ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, const int uname_len, ntfs_name **res) @@ -83,6 +91,8 @@ u8 *kaddr; ntfs_name *name = NULL; + BUG_ON(!S_ISDIR(VFS_I(dir_ni)->i_mode)); + BUG_ON(NInoAttr(dir_ni)); /* Get hold of the mft record for the directory. */ m = map_mft_record(dir_ni); if (unlikely(IS_ERR(m))) { @@ -309,6 +319,7 @@ err = PTR_ERR(page); goto err_out; } + lock_page(page); kaddr = (u8*)page_address(page); fast_descend_into_child_node: /* Get to the index allocation block. */ @@ -429,6 +440,7 @@ *res = NULL; } mref = le64_to_cpu(ie->data.dir.indexed_file); + unlock_page(page); ntfs_unmap_page(page); return mref; } @@ -461,6 +473,7 @@ "this message to " "linux-ntfs-dev@lists." "sourceforge.net."); + unlock_page(page); ntfs_unmap_page(page); goto dir_err_out; } @@ -543,6 +556,7 @@ vol->cluster_size_bits >> PAGE_CACHE_SHIFT) goto fast_descend_into_child_node; + unlock_page(page); ntfs_unmap_page(page); goto descend_into_child_node; } @@ -557,12 +571,14 @@ * associated with it. */ if (name) { + unlock_page(page); ntfs_unmap_page(page); return name->mref; } ntfs_debug("Entry not found."); err = -ENOENT; unm_err_out: + unlock_page(page); ntfs_unmap_page(page); err_out: if (ctx) @@ -778,6 +794,7 @@ err = PTR_ERR(page); goto err_out; } + lock_page(page); kaddr = (u8*)page_address(page); fast_descend_into_child_node: /* Get to the index allocation block. */ @@ -880,6 +897,7 @@ vol->upcase, vol->upcase_len)) { found_it2: mref = le64_to_cpu(ie->data.dir.indexed_file); + unlock_page(page); ntfs_unmap_page(page); return mref; } @@ -944,6 +962,7 @@ vol->cluster_size_bits >> PAGE_CACHE_SHIFT) goto fast_descend_into_child_node; + unlock_page(page); ntfs_unmap_page(page); goto descend_into_child_node; } @@ -956,6 +975,7 @@ ntfs_debug("Entry not found."); err = -ENOENT; unm_err_out: + unlock_page(page); ntfs_unmap_page(page); err_out: if (ctx) @@ -988,6 +1008,7 @@ * @ndir: ntfs inode of current directory * @index_type: specifies whether @iu is an index root or an index allocation * @iu: index root or index allocation attribute to which @ie belongs + * @ia_page: page in which the index allocation buffer @ie is in resides * @ie: current index entry * @name: buffer to use for the converted name * @dirent: vfs filldir callback context @@ -995,13 +1016,24 @@ * * Convert the Unicode @name to the loaded NLS and pass it to the @filldir * callback. + * + * If @index_type is INDEX_TYPE_ALLOCATION, @ia_page is the locked page + * containing the index allocation block containing the index entry @ie. + * Otherwise, @ia_page is NULL. + * + * Note, we drop (and then reacquire) the page lock on @ia_page across the + * @filldir() call otherwise we would deadlock with NFSd when it calls ->lookup + * since ntfs_lookup() will lock the same page. As an optimization, we do not + * retake the lock if we are returning a non-zero value as ntfs_readdir() + * would need to drop the lock immediately anyway. */ static inline int ntfs_filldir(ntfs_volume *vol, loff_t *fpos, ntfs_inode *ndir, const INDEX_TYPE index_type, - index_union iu, INDEX_ENTRY *ie, u8 *name, - void *dirent, filldir_t filldir) + index_union iu, struct page *ia_page, INDEX_ENTRY *ie, + u8 *name, void *dirent, filldir_t filldir) { - int name_len; + unsigned long mref; + int name_len, rc; unsigned dt_type; FILE_NAME_TYPE_FLAGS name_type; @@ -1039,24 +1071,42 @@ dt_type = DT_DIR; else dt_type = DT_REG; + mref = MREF_LE(ie->data.dir.indexed_file); + /* + * Drop the page lock otherwise we deadlock with NFS when it calls + * ->lookup since ntfs_lookup() will lock the same page. + */ + if (index_type == INDEX_TYPE_ALLOCATION) + unlock_page(ia_page); ntfs_debug("Calling filldir for %s with len %i, fpos 0x%llx, inode " - "0x%lx, DT_%s.", name, name_len, *fpos, - MREF_LE(ie->data.dir.indexed_file), + "0x%lx, DT_%s.", name, name_len, *fpos, mref, dt_type == DT_DIR ? "DIR" : "REG"); - return filldir(dirent, name, name_len, *fpos, - MREF_LE(ie->data.dir.indexed_file), dt_type); + rc = filldir(dirent, name, name_len, *fpos, mref, dt_type); + /* Relock the page but not if we are aborting ->readdir. */ + if (!rc && index_type == INDEX_TYPE_ALLOCATION) + lock_page(ia_page); + return rc; } /* - * VFS calls readdir without BKL but with i_sem held. This protects the VFS - * parts (e.g. ->f_pos and ->i_size, and it also protects against directory - * modifications). - * * We use the same basic approach as the old NTFS driver, i.e. we parse the * index root entries and then the index allocation entries that are marked * as in use in the index bitmap. + * * While this will return the names in random order this doesn't matter for - * readdir but OTOH results in a faster readdir. + * ->readdir but OTOH results in a faster ->readdir. + * + * VFS calls ->readdir without BKL but with i_sem held. This protects the VFS + * parts (e.g. ->f_pos and ->i_size, and it also protects against directory + * modifications). + * + * Locking: - Caller must hold i_sem on the directory. + * - Each page cache page in the index allocation mapping must be + * locked whilst being accessed otherwise we may find a corrupt + * page due to it being under ->writepage at the moment which + * applies the mst protection fixups before writing out and then + * removes them again after the write is complete after which it + * unlocks the page. */ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir) { @@ -1186,8 +1236,8 @@ if (ir_pos > (u8*)ie - (u8*)ir) continue; /* Submit the name to the filldir callback. */ - rc = ntfs_filldir(vol, &fpos, ndir, INDEX_TYPE_ROOT, ir, ie, - name, dirent, filldir); + rc = ntfs_filldir(vol, &fpos, ndir, INDEX_TYPE_ROOT, ir, NULL, + ie, name, dirent, filldir); if (rc) { kfree(ir); goto abort; @@ -1268,8 +1318,10 @@ /* If the current index buffer is in the same page we reuse the page. */ if ((prev_ia_pos & PAGE_CACHE_MASK) != (ia_pos & PAGE_CACHE_MASK)) { prev_ia_pos = ia_pos; - if (likely(ia_page != NULL)) + if (likely(ia_page != NULL)) { + unlock_page(ia_page); ntfs_unmap_page(ia_page); + } /* * Map the page cache page containing the current ia_pos, * reading it from disk if necessary. @@ -1281,6 +1333,7 @@ ia_page = NULL; goto err_out; } + lock_page(ia_page); kaddr = (u8*)page_address(ia_page); } /* Get the current index buffer. */ @@ -1358,10 +1411,16 @@ /* Skip index block entry if continuing previous readdir. */ if (ia_pos - ia_start > (u8*)ie - (u8*)ia) continue; - /* Submit the name to the filldir callback. */ + /* + * Submit the name to the @filldir callback. Note, + * ntfs_filldir() drops the lock on @ia_page but it retakes it + * before returning, unless a non-zero value is returned in + * which case the page is left unlocked. + */ rc = ntfs_filldir(vol, &fpos, ndir, INDEX_TYPE_ALLOCATION, ia, - ie, name, dirent, filldir); + ia_page, ie, name, dirent, filldir); if (rc) { + /* @ia_page is already unlocked in this case. */ ntfs_unmap_page(ia_page); ntfs_unmap_page(bmp_page); goto abort; @@ -1369,8 +1428,10 @@ } goto find_next_index_buffer; unm_EOD: - if (ia_page) + if (ia_page) { + unlock_page(ia_page); ntfs_unmap_page(ia_page); + } ntfs_unmap_page(bmp_page); EOD: /* We are finished, set fpos to EOD. */ @@ -1390,8 +1451,10 @@ err_out: if (bmp_page) ntfs_unmap_page(bmp_page); - if (ia_page) + if (ia_page) { + unlock_page(ia_page); ntfs_unmap_page(ia_page); + } if (ir) kfree(ir); if (name) diff -Nru a/fs/ntfs/index.c b/fs/ntfs/index.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/fs/ntfs/index.c 2004-06-23 20:35:05 -07:00 @@ -0,0 +1,459 @@ +/* + * index.c - NTFS kernel index handling. Part of the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ntfs.h" +#include "collate.h" +#include "index.h" + +/** + * ntfs_index_ctx_get - allocate and initialize a new index context + * @idx_ni: ntfs index inode with which to initialize the context + * + * Allocate a new index context, initialize it with @idx_ni and return it. + * Return NULL if allocation failed. + * + * Locking: Caller must hold i_sem on the index inode. + */ +ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *idx_ni) +{ + ntfs_index_context *ictx; + + ictx = kmem_cache_alloc(ntfs_index_ctx_cache, SLAB_NOFS); + if (ictx) { + ictx->idx_ni = idx_ni; + ictx->entry = NULL; + ictx->data = NULL; + ictx->data_len = 0; + ictx->is_in_root = 0; + ictx->ir = NULL; + ictx->actx = NULL; + ictx->base_ni = NULL; + ictx->ia = NULL; + ictx->page = NULL; + } + return ictx; +} + +/** + * ntfs_index_ctx_put - release an index context + * @ictx: index context to free + * + * Release the index context @ictx, releasing all associated resources. + * + * Locking: Caller must hold i_sem on the index inode. + */ +void ntfs_index_ctx_put(ntfs_index_context *ictx) +{ + if (ictx->entry) { + if (ictx->is_in_root) { + if (ictx->actx) + put_attr_search_ctx(ictx->actx); + if (ictx->base_ni) + unmap_mft_record(ictx->base_ni); + } else { + struct page *page = ictx->page; + if (page) { + BUG_ON(!PageLocked(page)); + unlock_page(page); + ntfs_unmap_page(page); + } + } + } + kmem_cache_free(ntfs_index_ctx_cache, ictx); + return; +} + +/** + * ntfs_index_lookup - find a key in an index and return its index entry + * @key: [IN] key for which to search in the index + * @key_len: [IN] length of @key in bytes + * @ictx: [IN/OUT] context describing the index and the returned entry + * + * Before calling ntfs_index_lookup(), @ictx must have been obtained from a + * call to ntfs_index_ctx_get(). + * + * Look for the @key in the index specified by the index lookup context @ictx. + * ntfs_index_lookup() walks the contents of the index looking for the @key. + * + * If the @key is found in the index, 0 is returned and @ictx is setup to + * describe the index entry containing the matching @key. @ictx->entry is the + * index entry and @ictx->data and @ictx->data_len are the index entry data and + * its length in bytes, respectively. + * + * If the @key is not found in the index, -ENOENT is returned and @ictx is + * setup to describe the index entry whose key collates immediately after the + * search @key, i.e. this is the position in the index at which an index entry + * with a key of @key would need to be inserted. + * + * If an error occurs return the negative error code and @ictx is left + * untouched. + * + * When finished with the entry and its data, call ntfs_index_ctx_put() to free + * the context and other associated resources. + * + * If the index entry was modified, call flush_dcache_index_entry_page() + * immediately after the modification and either ntfs_index_entry_mark_dirty() + * or ntfs_index_entry_write() before the call to ntfs_index_ctx_put() to + * ensure that the changes are written to disk. + * + * Locking: - Caller must hold i_sem on the index inode. + * - Each page cache page in the index allocation mapping must be + * locked whilst being accessed otherwise we may find a corrupt + * page due to it being under ->writepage at the moment which + * applies the mst protection fixups before writing out and then + * removes them again after the write is complete after which it + * unlocks the page. + */ +int ntfs_index_lookup(const void *key, const int key_len, + ntfs_index_context *ictx) +{ + ntfs_inode *idx_ni = ictx->idx_ni; + ntfs_volume *vol = idx_ni->vol; + struct super_block *sb = vol->sb; + ntfs_inode *base_ni = idx_ni->ext.base_ntfs_ino; + MFT_RECORD *m; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_ALLOCATION *ia; + u8 *index_end; + attr_search_context *actx; + int rc, err = 0; + VCN vcn, old_vcn; + struct address_space *ia_mapping; + struct page *page; + u8 *kaddr; + + ntfs_debug("Entering."); + BUG_ON(!NInoAttr(idx_ni)); + BUG_ON(idx_ni->type != AT_INDEX_ALLOCATION); + BUG_ON(idx_ni->nr_extents != -1); + BUG_ON(!base_ni); + BUG_ON(!key); + BUG_ON(key_len <= 0); + if (!ntfs_is_collation_rule_supported( + idx_ni->itype.index.collation_rule)) { + ntfs_error(sb, "Index uses unsupported collation rule 0x%x. " + "Aborting lookup.", le32_to_cpu( + idx_ni->itype.index.collation_rule)); + return -EOPNOTSUPP; + } + /* Get hold of the mft record for the index inode. */ + m = map_mft_record(base_ni); + if (unlikely(IS_ERR(m))) { + ntfs_error(sb, "map_mft_record() failed with error code %ld.", + -PTR_ERR(m)); + return PTR_ERR(m); + } + actx = get_attr_search_ctx(base_ni, m); + if (unlikely(!actx)) { + err = -ENOMEM; + goto err_out; + } + /* Find the index root attribute in the mft record. */ + if (!lookup_attr(AT_INDEX_ROOT, idx_ni->name, idx_ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, actx)) { + ntfs_error(sb, "Index root attribute missing in inode 0x%lx.", + idx_ni->mft_no); + err = -EIO; + goto err_out; + } + /* Get to the index root value (it has been verified in read_inode). */ + ir = (INDEX_ROOT*)((u8*)actx->attr + + le16_to_cpu(actx->attr->data.resident.value_offset)); + index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ir->index + + le32_to_cpu(ir->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + /* Bounds checks. */ + if ((u8*)ie < (u8*)actx->mrec || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->length) > index_end) + goto idx_err_out; + /* + * The last entry cannot contain a key. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ie->flags & INDEX_ENTRY_END) + break; + /* Further bounds checks. */ + if ((u32)sizeof(INDEX_ENTRY_HEADER) + + le16_to_cpu(ie->key_length) > + le16_to_cpu(ie->data.vi.data_offset) || + (u32)le16_to_cpu(ie->data.vi.data_offset) + + le16_to_cpu(ie->data.vi.data_length) > + le16_to_cpu(ie->length)) + goto idx_err_out; + /* If the keys match perfectly, we setup @ictx and return 0. */ + if ((key_len == le16_to_cpu(ie->key_length)) && !memcmp(key, + &ie->key, key_len)) { +ir_done: + ictx->is_in_root = TRUE; + ictx->actx = actx; + ictx->base_ni = base_ni; + ictx->ia = NULL; + ictx->page = NULL; +done: + ictx->entry = ie; + ictx->data = (u8*)ie + + le16_to_cpu(ie->data.vi.data_offset); + ictx->data_len = le16_to_cpu(ie->data.vi.data_length); + ntfs_debug("Done."); + return err; + } + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + rc = ntfs_collate(vol, idx_ni->itype.index.collation_rule, key, + key_len, &ie->key, le16_to_cpu(ie->key_length)); + /* + * If @key collates before the key of the current entry, there + * is definitely no such key in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + /* + * A match should never happen as the memcmp() call should have + * cought it, but we still treat it correctly. + */ + if (!rc) + goto ir_done; + /* The keys are not equal, continue the search. */ + } + /* + * We have finished with this index without success. Check for the + * presence of a child node and if not present setup @ictx and return + * -ENOENT. + */ + if (!(ie->flags & INDEX_ENTRY_NODE)) { + ntfs_debug("Entry not found."); + err = -ENOENT; + goto ir_done; + } /* Child node present, descend into it. */ + /* Consistency check: Verify that an index allocation exists. */ + if (!NInoIndexAllocPresent(idx_ni)) { + ntfs_error(sb, "No index allocation attribute but index entry " + "requires one. Inode 0x%lx is corrupt or " + "driver bug.", idx_ni->mft_no); + err = -EIO; + goto err_out; + } + /* Get the starting vcn of the index_block holding the child node. */ + vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); + ia_mapping = VFS_I(idx_ni)->i_mapping; + /* + * We are done with the index root and the mft record. Release them, + * otherwise we deadlock with ntfs_map_page(). + */ + put_attr_search_ctx(actx); + unmap_mft_record(base_ni); + m = NULL; + actx = NULL; +descend_into_child_node: + /* + * Convert vcn to index into the index allocation attribute in units + * of PAGE_CACHE_SIZE and map the page cache page, reading it from + * disk if necessary. + */ + page = ntfs_map_page(ia_mapping, vcn << + idx_ni->itype.index.vcn_size_bits >> PAGE_CACHE_SHIFT); + if (IS_ERR(page)) { + ntfs_error(sb, "Failed to map index page, error %ld.", + -PTR_ERR(page)); + err = PTR_ERR(page); + goto err_out; + } + lock_page(page); + kaddr = (u8*)page_address(page); +fast_descend_into_child_node: + /* Get to the index allocation block. */ + ia = (INDEX_ALLOCATION*)(kaddr + ((vcn << + idx_ni->itype.index.vcn_size_bits) & ~PAGE_CACHE_MASK)); + /* Bounds checks. */ + if ((u8*)ia < kaddr || (u8*)ia > kaddr + PAGE_CACHE_SIZE) { + ntfs_error(sb, "Out of bounds check failed. Corrupt inode " + "0x%lx or driver bug.", idx_ni->mft_no); + err = -EIO; + goto unm_err_out; + } + if (sle64_to_cpu(ia->index_block_vcn) != vcn) { + ntfs_error(sb, "Actual VCN (0x%llx) of index buffer is " + "different from expected VCN (0x%llx). Inode " + "0x%lx is corrupt or driver bug.", + (unsigned long long) + sle64_to_cpu(ia->index_block_vcn), + (unsigned long long)vcn, idx_ni->mft_no); + err = -EIO; + goto unm_err_out; + } + if (le32_to_cpu(ia->index.allocated_size) + 0x18 != + idx_ni->itype.index.block_size) { + ntfs_error(sb, "Index buffer (VCN 0x%llx) of inode 0x%lx has " + "a size (%u) differing from the index " + "specified size (%u). Inode is corrupt or " + "driver bug.", (unsigned long long)vcn, + idx_ni->mft_no, + le32_to_cpu(ia->index.allocated_size) + 0x18, + idx_ni->itype.index.block_size); + err = -EIO; + goto unm_err_out; + } + index_end = (u8*)ia + idx_ni->itype.index.block_size; + if (index_end > kaddr + PAGE_CACHE_SIZE) { + ntfs_error(sb, "Index buffer (VCN 0x%llx) of inode 0x%lx " + "crosses page boundary. Impossible! Cannot " + "access! This is probably a bug in the " + "driver.", (unsigned long long)vcn, + idx_ni->mft_no); + err = -EIO; + goto unm_err_out; + } + index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); + if (index_end > (u8*)ia + idx_ni->itype.index.block_size) { + ntfs_error(sb, "Size of index buffer (VCN 0x%llx) of inode " + "0x%lx exceeds maximum size.", + (unsigned long long)vcn, idx_ni->mft_no); + err = -EIO; + goto unm_err_out; + } + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ia->index + + le32_to_cpu(ia->index.entries_offset)); + /* + * Iterate similar to above big loop but applied to index buffer, thus + * loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + /* Bounds checks. */ + if ((u8*)ie < (u8*)ia || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->length) > index_end) { + ntfs_error(sb, "Index entry out of bounds in inode " + "0x%lx.", idx_ni->mft_no); + err = -EIO; + goto unm_err_out; + } + /* + * The last entry cannot contain a ket. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ie->flags & INDEX_ENTRY_END) + break; + /* Further bounds checks. */ + if ((u32)sizeof(INDEX_ENTRY_HEADER) + + le16_to_cpu(ie->key_length) > + le16_to_cpu(ie->data.vi.data_offset) || + (u32)le16_to_cpu(ie->data.vi.data_offset) + + le16_to_cpu(ie->data.vi.data_length) > + le16_to_cpu(ie->length)) { + ntfs_error(sb, "Index entry out of bounds in inode " + "0x%lx.", idx_ni->mft_no); + err = -EIO; + goto unm_err_out; + } + /* If the keys match perfectly, we setup @ictx and return 0. */ + if ((key_len == le16_to_cpu(ie->key_length)) && !memcmp(key, + &ie->key, key_len)) { +ia_done: + ictx->is_in_root = FALSE; + ictx->actx = NULL; + ictx->base_ni = NULL; + ictx->ia = ia; + ictx->page = page; + goto done; + } + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + rc = ntfs_collate(vol, idx_ni->itype.index.collation_rule, key, + key_len, &ie->key, le16_to_cpu(ie->key_length)); + /* + * If @key collates before the key of the current entry, there + * is definitely no such key in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + /* + * A match should never happen as the memcmp() call should have + * cought it, but we still treat it correctly. + */ + if (!rc) + goto ia_done; + /* The keys are not equal, continue the search. */ + } + /* + * We have finished with this index buffer without success. Check for + * the presence of a child node and if not present return -ENOENT. + */ + if (!(ie->flags & INDEX_ENTRY_NODE)) { + ntfs_debug("Entry not found."); + err = -ENOENT; + goto ia_done; + } + if ((ia->index.flags & NODE_MASK) == LEAF_NODE) { + ntfs_error(sb, "Index entry with child node found in a leaf " + "node in inode 0x%lx.", idx_ni->mft_no); + err = -EIO; + goto unm_err_out; + } + /* Child node present, descend into it. */ + old_vcn = vcn; + vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); + if (vcn >= 0) { + /* + * If vcn is in the same page cache page as old_vcn we recycle + * the mapped page. + */ + if (old_vcn << vol->cluster_size_bits >> + PAGE_CACHE_SHIFT == vcn << + vol->cluster_size_bits >> + PAGE_CACHE_SHIFT) + goto fast_descend_into_child_node; + unlock_page(page); + ntfs_unmap_page(page); + goto descend_into_child_node; + } + ntfs_error(sb, "Negative child node vcn in inode 0x%lx.", + idx_ni->mft_no); + err = -EIO; +unm_err_out: + unlock_page(page); + ntfs_unmap_page(page); +err_out: + if (actx) + put_attr_search_ctx(actx); + if (m) + unmap_mft_record(base_ni); + return err; +idx_err_out: + ntfs_error(sb, "Corrupt index. Aborting lookup."); + err = -EIO; + goto err_out; +} diff -Nru a/fs/ntfs/index.h b/fs/ntfs/index.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/fs/ntfs/index.h 2004-06-23 20:35:05 -07:00 @@ -0,0 +1,155 @@ +/* + * index.h - Defines for NTFS kernel index handling. Part of the Linux-NTFS + * project. + * + * Copyright (c) 2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LINUX_NTFS_INDEX_H +#define _LINUX_NTFS_INDEX_H + +#include + +#include "types.h" +#include "layout.h" +#include "inode.h" +#include "attrib.h" +#include "mft.h" + +/** + * @idx_ni: index inode containing the @entry described by this context + * @entry: index entry (points into @ir or @ia) + * @data: index entry data (points into @entry) + * @data_len: length in bytes of @data + * @is_in_root: TRUE if @entry is in @ir and FALSE if it is in @ia + * @ir: index root if @is_in_root and NULL otherwise + * @actx: attribute search context if @is_in_root and NULL otherwise + * @base_ni: base inode if @is_in_root and NULL otherwise + * @ia: index block if @is_in_root is FALSE and NULL otherwise + * @page: page if @is_in_root is FALSE and NULL otherwise + * + * @idx_ni is the index inode this context belongs to. + * + * @entry is the index entry described by this context. @data and @data_len + * are the index entry data and its length in bytes, respectively. @data + * simply points into @entry. This is probably what the user is interested in. + * + * If @is_in_root is TRUE, @entry is in the index root attribute @ir described + * by the attribute search context @actx and the base inode @base_ni. @ia and + * @page are NULL in this case. + * + * If @is_in_root is FALSE, @entry is in the index allocation attribute and @ia + * and @page point to the index allocation block and the locked page it is in, + * respectively. @ir, @actx and @base_ni are NULL in this case. + * + * To obtain a context call ntfs_index_ctx_get(). + * + * We use this context to allow ntfs_index_lookup() to return the found index + * @entry and its @data without having to allocate a buffer and copy the @entry + * and/or its @data into it. + * + * When finished with the @entry and its @data, call ntfs_index_ctx_put() to + * free the context and other associated resources. + * + * If the index entry was modified, call flush_dcache_index_entry_page() + * immediately after the modification and either ntfs_index_entry_mark_dirty() + * or ntfs_index_entry_write() before the call to ntfs_index_ctx_put() to + * ensure that the changes are written to disk. + */ +typedef struct { + ntfs_inode *idx_ni; + INDEX_ENTRY *entry; + void *data; + u16 data_len; + BOOL is_in_root; + INDEX_ROOT *ir; + attr_search_context *actx; + ntfs_inode *base_ni; + INDEX_ALLOCATION *ia; + struct page *page; +} ntfs_index_context; + +extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *idx_ni); +extern void ntfs_index_ctx_put(ntfs_index_context *ictx); + +extern int ntfs_index_lookup(const void *key, const int key_len, + ntfs_index_context *ictx); + +#ifdef NTFS_RW + +/** + * ntfs_index_entry_flush_dcache_page - flush_dcache_page() for index entries + * @ictx: ntfs index context describing the index entry + * + * Call flush_dcache_page() for the page in which an index entry resides. + * + * This must be called every time an index entry is modified, just after the + * modification. + * + * If the index entry is in the index root attribute, simply flush the page + * containing the mft record containing the index root attribute. + * + * If the index entry is in an index block belonging to the index allocation + * attribute, simply flush the page cache page containing the index block. + */ +static inline void ntfs_index_entry_flush_dcache_page(ntfs_index_context *ictx) +{ + if (ictx->is_in_root) + flush_dcache_mft_record_page(ictx->actx->ntfs_ino); + else + flush_dcache_page(ictx->page); +} + +/** + * ntfs_index_entry_mark_dirty - mark an index entry dirty + * @ictx: ntfs index context describing the index entry + * + * Mark the index entry described by the index entry context @ictx dirty. + * + * If the index entry is in the index root attribute, simply mark the mft + * record containing the index root attribute dirty. This ensures the mft + * record, and hence the index root attribute, will be written out to disk + * later. + * + * If the index entry is in an index block belonging to the index allocation + * attribute, simply mark the page cache page the index block is in dirty. + * This automatically marks the VFS inode of the ntfs index inode to which the + * index entry belongs dirty, too (I_DIRTY_PAGES) and this in turn ensures the + * page, and hence the dirty index block, will be written out to disk later. + * + * Note, that if an index block is smaller than PAGE_CACHE_SIZE, i.e. if there + * are multiple index blocks in each page cache page, dirtying an index entry + * in one index block will cause all index blocks located in the same page + * cache page to be written out, too but this is a small price to pay + * considering how much more complicated the code would have to be to keep + * track of which index block inside a page is dirty and which is not. And + * anyway, on ia32 architectures index blocks are usually 4kiB in size which is + * the PAGE_CACHE_SIZE and hence this problem does not exist in the majority of + * cases. + */ +static inline void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx) +{ + if (ictx->is_in_root) + mark_mft_record_dirty(ictx->actx->ntfs_ino); + else + __set_page_dirty_nobuffers(ictx->page); +} + +#endif /* NTFS_RW */ + +#endif /* _LINUX_NTFS_INDEX_H */ diff -Nru a/fs/ntfs/inode.c b/fs/ntfs/inode.c --- a/fs/ntfs/inode.c 2004-06-23 20:35:05 -07:00 +++ b/fs/ntfs/inode.c 2004-06-23 20:35:05 -07:00 @@ -134,6 +134,8 @@ typedef int (*set_t)(struct inode *, void *); static int ntfs_read_locked_inode(struct inode *vi); static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi); +static int ntfs_read_locked_index_inode(struct inode *base_vi, + struct inode *vi); /** * ntfs_iget - obtain a struct inode corresponding to a specific normal inode @@ -201,6 +203,9 @@ * initialized, and finally ntfs_read_locked_attr_inode() is called to read the * attribute and fill in the inode structure. * + * Note, for index allocation attributes, you need to use ntfs_index_iget() + * instead of ntfs_attr_iget() as working with indices is a lot more complex. + * * Return the struct inode of the attribute inode on success. Check the return * value with IS_ERR() and if true, the function failed and the error code is * obtained from PTR_ERR(). @@ -212,6 +217,9 @@ ntfs_attr na; int err; + /* Make sure no one calls ntfs_attr_iget() for indices. */ + BUG_ON(type == AT_INDEX_ALLOCATION); + na.mft_no = base_vi->i_ino; na.type = type; na.name = name; @@ -241,6 +249,61 @@ return vi; } +/** + * ntfs_index_iget - obtain a struct inode corresponding to an index + * @base_vi: vfs base inode containing the index related attributes + * @name: Unicode name of the index + * @name_len: length of @name in Unicode characters + * + * Obtain the (fake) struct inode corresponding to the index specified by @name + * and @name_len, which is present in the base mft record specified by the vfs + * inode @base_vi. + * + * If the index inode is in the cache, it is just returned with an increased + * reference count. Otherwise, a new struct inode is allocated and + * initialized, and finally ntfs_read_locked_index_inode() is called to read + * the index related attributes and fill in the inode structure. + * + * Return the struct inode of the index inode on success. Check the return + * value with IS_ERR() and if true, the function failed and the error code is + * obtained from PTR_ERR(). + */ +struct inode *ntfs_index_iget(struct inode *base_vi, ntfschar *name, + u32 name_len) +{ + struct inode *vi; + ntfs_attr na; + int err; + + na.mft_no = base_vi->i_ino; + na.type = AT_INDEX_ALLOCATION; + na.name = name; + na.name_len = name_len; + + vi = iget5_locked(base_vi->i_sb, na.mft_no, (test_t)ntfs_test_inode, + (set_t)ntfs_init_locked_inode, &na); + if (!vi) + return ERR_PTR(-ENOMEM); + + err = 0; + + /* If this is a freshly allocated inode, need to read it now. */ + if (vi->i_state & I_NEW) { + err = ntfs_read_locked_index_inode(base_vi, vi); + unlock_new_inode(vi); + } + /* + * There is no point in keeping bad index inodes around. This also + * simplifies things in that we never need to check for bad index + * inodes elsewhere. + */ + if (err) { + iput(vi); + vi = ERR_PTR(err); + } + return vi; +} + struct inode *ntfs_alloc_big_inode(struct super_block *sb) { ntfs_inode *ni; @@ -319,6 +382,7 @@ ni->itype.index.bmp_ino = NULL; ni->itype.index.block_size = 0; ni->itype.index.vcn_size = 0; + ni->itype.index.collation_rule = 0; ni->itype.index.block_size_bits = 0; ni->itype.index.vcn_size_bits = 0; init_MUTEX(&ni->extent_lock); @@ -438,9 +502,7 @@ * * The only fields in @vi that we need to/can look at when the function is * called are i_sb, pointing to the mounted device's super block, and i_ino, - * the number of the inode to load. If this is a fake inode, i.e. NInoAttr(), - * then the fields type, name, and name_len are also valid, and describe the - * attribute which this fake inode represents. + * the number of the inode to load. * * ntfs_read_locked_inode() maps, pins and locks the mft record number i_ino * for reading and sets up the necessary @vi fields as well as initializing @@ -449,12 +511,12 @@ * Q: What locks are held when the function is called? * A: i_state has I_LOCK set, hence the inode is locked, also * i_count is set to 1, so it is not going to go away - * i_flags is set to 0 and we have no business touching it. Only an ioctl() + * i_flags is set to 0 and we have no business touching it. Only an ioctl() * is allowed to write to them. We should of course be honouring them but * we need to do that using the IS_* macros defined in include/linux/fs.h. * In any case ntfs_read_locked_inode() has nothing to do with i_flags. * - * Return 0 on success and -errno on error. In the error case, the inode will + * Return 0 on success and -errno on error. In the error case, the inode will * have had make_bad_inode() executed on it. */ static int ntfs_read_locked_inode(struct inode *vi) @@ -730,6 +792,7 @@ "COLLATION_FILE_NAME. Not allowed."); goto unm_err_out; } + ni->itype.index.collation_rule = ir->collation_rule; ni->itype.index.block_size = le32_to_cpu(ir->index_block_size); if (ni->itype.index.block_size & (ni->itype.index.block_size - 1)) { @@ -1050,8 +1113,8 @@ * @base_vi: base inode * @vi: attribute inode to read * - * ntfs_read_locked_attr_inode() is called from the ntfs_attr_iget() to read - * the attribute inode described by @vi into memory from the base mft record + * ntfs_read_locked_attr_inode() is called from ntfs_attr_iget() to read the + * attribute inode described by @vi into memory from the base mft record * described by @base_ni. * * ntfs_read_locked_attr_inode() maps, pins and locks the base inode for @@ -1061,6 +1124,9 @@ * Q: What locks are held when the function is called? * A: i_state has I_LOCK set, hence the inode is locked, also * i_count is set to 1, so it is not going to go away + * + * Return 0 on success and -errno on error. In the error case, the inode will + * have had make_bad_inode() executed on it. */ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) { @@ -1103,7 +1169,7 @@ } /* Find the attribute. */ - if (!lookup_attr(ni->type, ni->name, ni->name_len, IGNORE_CASE, 0, + if (!lookup_attr(ni->type, ni->name, ni->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx)) goto unm_err_out; @@ -1288,6 +1354,261 @@ } /** + * ntfs_read_locked_index_inode - read an index inode from its base inode + * @base_vi: base inode + * @vi: index inode to read + * + * ntfs_read_locked_index_inode() is called from ntfs_index_iget() to read the + * index inode described by @vi into memory from the base mft record described + * by @base_ni. + * + * ntfs_read_locked_index_inode() maps, pins and locks the base inode for + * reading and looks up the attributes relating to the index described by @vi + * before setting up the necessary fields in @vi as well as initializing the + * ntfs inode. + * + * Note, index inodes are essentially attribute inodes (NInoAttr() is true) + * with the attribute type set to AT_INDEX_ALLOCATION. Apart from that, they + * are setup like directory inodes since directories are a special case of + * indices ao they need to be treated in much the same way. Most importantly, + * for small indices the index allocation attribute might not actually exist. + * However, the index root attribute always exists but this does not need to + * have an inode associated with it and this is why we define a new inode type + * index. Also, like for directories, we need to have an attribute inode for + * the bitmap attribute corresponding to the index allocation attribute and we + * can store this in the appropriate field of the inode, just like we do for + * normal directory inodes. + * + * Q: What locks are held when the function is called? + * A: i_state has I_LOCK set, hence the inode is locked, also + * i_count is set to 1, so it is not going to go away + * + * Return 0 on success and -errno on error. In the error case, the inode will + * have had make_bad_inode() executed on it. + */ +static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi) +{ + ntfs_volume *vol = NTFS_SB(vi->i_sb); + ntfs_inode *ni, *base_ni, *bni; + struct inode *bvi; + MFT_RECORD *m; + attr_search_context *ctx; + INDEX_ROOT *ir; + u8 *ir_end, *index_end; + int err = 0; + + ntfs_debug("Entering for i_ino 0x%lx.", vi->i_ino); + ntfs_init_big_inode(vi); + ni = NTFS_I(vi); + base_ni = NTFS_I(base_vi); + /* Just mirror the values from the base inode. */ + vi->i_blksize = base_vi->i_blksize; + vi->i_version = base_vi->i_version; + vi->i_uid = base_vi->i_uid; + vi->i_gid = base_vi->i_gid; + vi->i_nlink = base_vi->i_nlink; + vi->i_mtime = base_vi->i_mtime; + vi->i_ctime = base_vi->i_ctime; + vi->i_atime = base_vi->i_atime; + vi->i_generation = ni->seq_no = base_ni->seq_no; + /* Set inode type to zero but preserve permissions. */ + vi->i_mode = base_vi->i_mode & ~S_IFMT; + /* Map the mft record for the base inode. */ + m = map_mft_record(base_ni); + if (IS_ERR(m)) { + err = PTR_ERR(m); + goto err_out; + } + ctx = get_attr_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."); + goto unm_err_out; + } + /* Set up the state. */ + if (ctx->attr->non_resident) { + ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is not resident. " + "Not allowed."); + goto unm_err_out; + } + /* Compressed/encrypted/sparse index root is not allowed. */ + if (ctx->attr->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_ENCRYPTED | + ATTR_IS_SPARSE)) { + ntfs_error(vi->i_sb, "Found compressed/encrypted/sparse index " + "root attribute. Not allowed."); + goto unm_err_out; + } + ir = (INDEX_ROOT*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->data.resident.value_offset)); + ir_end = (u8*)ir + le32_to_cpu(ctx->attr->data.resident.value_length); + if (ir_end > (u8*)ctx->mrec + vol->mft_record_size) { + ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is corrupt."); + goto unm_err_out; + } + index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); + if (index_end > ir_end) { + ntfs_error(vi->i_sb, "Index is corrupt."); + goto unm_err_out; + } + if (ir->type) { + ntfs_error(vi->i_sb, "Index type is not 0 (type is 0x%x). " + "Not allowed.", le32_to_cpu(ir->type)); + goto unm_err_out; + } + ni->itype.index.collation_rule = ir->collation_rule; + ntfs_debug("Index collation rule is 0x%x.", + le32_to_cpu(ir->collation_rule)); + ni->itype.index.block_size = le32_to_cpu(ir->index_block_size); + if (ni->itype.index.block_size & (ni->itype.index.block_size - 1)) { + ntfs_error(vi->i_sb, "Index block size (%u) is not a power of " + "two.", ni->itype.index.block_size); + goto unm_err_out; + } + if (ni->itype.index.block_size > PAGE_CACHE_SIZE) { + ntfs_error(vi->i_sb, "Index block size (%u) > PAGE_CACHE_SIZE " + "(%ld) is not supported. Sorry.", + ni->itype.index.block_size, PAGE_CACHE_SIZE); + err = -EOPNOTSUPP; + goto unm_err_out; + } + if (ni->itype.index.block_size < NTFS_BLOCK_SIZE) { + ntfs_error(vi->i_sb, "Index block size (%u) < NTFS_BLOCK_SIZE " + "(%i) is not supported. Sorry.", + ni->itype.index.block_size, NTFS_BLOCK_SIZE); + err = -EOPNOTSUPP; + goto unm_err_out; + } + ni->itype.index.block_size_bits = ffs(ni->itype.index.block_size) - 1; + /* Determine the size of a vcn in the index. */ + if (vol->cluster_size <= ni->itype.index.block_size) { + ni->itype.index.vcn_size = vol->cluster_size; + ni->itype.index.vcn_size_bits = vol->cluster_size_bits; + } else { + ni->itype.index.vcn_size = vol->sector_size; + ni->itype.index.vcn_size_bits = vol->sector_size_bits; + } + /* Check for presence of index allocation attribute. */ + if (!(ir->index.flags & LARGE_INDEX)) { + /* No index allocation. */ + vi->i_size = ni->initialized_size = ni->allocated_size = 0; + /* We are done with the mft record, so we release it. */ + put_attr_search_ctx(ctx); + unmap_mft_record(base_ni); + m = NULL; + ctx = NULL; + goto skip_large_index_stuff; + } /* LARGE_INDEX: Index allocation present. Setup state. */ + NInoSetIndexAllocPresent(ni); + /* Find index allocation attribute. */ + 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."); + goto unm_err_out; + } + if (!ctx->attr->non_resident) { + ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is " + "resident."); + goto unm_err_out; + } + if (ctx->attr->flags & ATTR_IS_ENCRYPTED) { + ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is " + "encrypted."); + goto unm_err_out; + } + if (ctx->attr->flags & ATTR_IS_SPARSE) { + ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is sparse."); + goto unm_err_out; + } + if (ctx->attr->flags & ATTR_COMPRESSION_MASK) { + ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is " + "compressed."); + goto unm_err_out; + } + if (ctx->attr->data.non_resident.lowest_vcn) { + ntfs_error(vi->i_sb, "First extent of $INDEX_ALLOCATION " + "attribute has non zero lowest_vcn. Inode is " + "corrupt. You should run chkdsk."); + goto unm_err_out; + } + vi->i_size = sle64_to_cpu(ctx->attr->data.non_resident.data_size); + ni->initialized_size = sle64_to_cpu( + ctx->attr->data.non_resident.initialized_size); + ni->allocated_size = sle64_to_cpu( + ctx->attr->data.non_resident.allocated_size); + /* + * We are done with the mft record, so we release it. Otherwise + * we would deadlock in ntfs_attr_iget(). + */ + put_attr_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))) { + ntfs_error(vi->i_sb, "Failed to get bitmap attribute."); + err = PTR_ERR(bvi); + goto unm_err_out; + } + bni = NTFS_I(bvi); + if (NInoCompressed(bni) || NInoEncrypted(bni) || + NInoSparse(bni)) { + ntfs_error(vi->i_sb, "$BITMAP attribute is compressed " + "and/or encrypted and/or sparse."); + goto iput_unm_err_out; + } + /* Consistency check bitmap size vs. index allocation size. */ + if ((bvi->i_size << 3) < (vi->i_size >> + ni->itype.index.block_size_bits)) { + ntfs_error(vi->i_sb, "Index bitmap too small (0x%llx) " + "for index allocation (0x%llx).", + bvi->i_size << 3, vi->i_size); + goto iput_unm_err_out; + } + ni->itype.index.bmp_ino = bvi; +skip_large_index_stuff: + /* Setup the operations for this index inode. */ + vi->i_op = NULL; + vi->i_fop = NULL; + vi->i_mapping->a_ops = &ntfs_mst_aops; + vi->i_blocks = ni->allocated_size >> 9; + + /* + * Make sure the base inode doesn't go away and attach it to the + * index inode. + */ + igrab(base_vi); + ni->ext.base_ntfs_ino = base_ni; + ni->nr_extents = -1; + + ntfs_debug("Done."); + return 0; + +iput_unm_err_out: + iput(bvi); +unm_err_out: + if (!err) + err = -EIO; + if (ctx) + put_attr_search_ctx(ctx); + if (m) + unmap_mft_record(base_ni); +err_out: + ntfs_error(vi->i_sb, "Failed with error code %i while reading index " + "inode (mft_no 0x%lx, name_len %i.", -err, vi->i_ino, + ni->name_len); + make_bad_inode(vi); + return err; +} + +/** * ntfs_read_inode_mount - special read_inode for mount time use only * @vi: inode to read * @@ -1712,21 +2033,39 @@ * The VFS calls ntfs_put_inode() every time the inode reference count (i_count) * is about to be decremented (but before the decrement itself. * - * If the inode @vi is a directory with a single reference, we need to put the - * attribute inode for the directory index bitmap, if it is present, otherwise - * the directory inode would remain pinned for ever (or rather until umount() - * time. + * If the inode @vi is a directory with two references, one of which is being + * dropped, we need to put the attribute inode for the directory index bitmap, + * if it is present, otherwise the directory inode would remain pinned for + * ever. + * + * If the inode @vi is an index inode with only one reference which is being + * dropped, we need to put the attribute inode for the index bitmap, if it is + * present, otherwise the index inode would disappear and the attribute inode + * for the index bitmap would no longer be referenced from anywhere and thus it + * would remain pinned for ever. */ void ntfs_put_inode(struct inode *vi) { - if (S_ISDIR(vi->i_mode) && (atomic_read(&vi->i_count) == 2)) { - ntfs_inode *ni; + ntfs_inode *ni; - ni = NTFS_I(vi); - if (NInoIndexAllocPresent(ni) && ni->itype.index.bmp_ino) { - iput(ni->itype.index.bmp_ino); - ni->itype.index.bmp_ino = NULL; + if (S_ISDIR(vi->i_mode)) { + if (atomic_read(&vi->i_count) == 2) { + ni = NTFS_I(vi); + if (NInoIndexAllocPresent(ni) && + ni->itype.index.bmp_ino) { + iput(ni->itype.index.bmp_ino); + ni->itype.index.bmp_ino = NULL; + } } + return; + } + if (atomic_read(&vi->i_count) != 1) + return; + ni = NTFS_I(vi); + if (NInoAttr(ni) && (ni->type == AT_INDEX_ALLOCATION) && + NInoIndexAllocPresent(ni) && ni->itype.index.bmp_ino) { + iput(ni->itype.index.bmp_ino); + ni->itype.index.bmp_ino = NULL; } return; } @@ -2009,7 +2348,7 @@ goto unm_err_out; } if (unlikely(!lookup_attr(AT_STANDARD_INFORMATION, NULL, 0, - IGNORE_CASE, 0, NULL, 0, ctx))) { + CASE_SENSITIVE, 0, NULL, 0, ctx))) { put_attr_search_ctx(ctx); err = -ENOENT; goto unm_err_out; diff -Nru a/fs/ntfs/inode.h b/fs/ntfs/inode.h --- a/fs/ntfs/inode.h 2004-06-23 20:35:05 -07:00 +++ b/fs/ntfs/inode.h 2004-06-23 20:35:05 -07:00 @@ -90,16 +90,18 @@ u8 *attr_list; /* Attribute list value itself. */ run_list attr_list_rl; /* Run list for the attribute list value. */ union { - struct { /* It is a directory or $MFT. */ + struct { /* It is a directory, $MFT, or an index inode. */ struct inode *bmp_ino; /* Attribute inode for the - directory index $BITMAP. */ + index $BITMAP. */ u32 block_size; /* Size of an index block. */ u32 vcn_size; /* Size of a vcn in this - directory index. */ + index. */ + COLLATION_RULES collation_rule; /* The collation rule + for the index. */ u8 block_size_bits; /* Log2 of the above. */ u8 vcn_size_bits; /* Log2 of the above. */ } index; - struct { /* It is a compressed file or fake inode. */ + struct { /* It is a compressed file or an attribute inode. */ s64 size; /* Copy of compressed_size from $DATA. */ u32 block_size; /* Size of a compression block @@ -260,6 +262,8 @@ extern struct inode *ntfs_iget(struct super_block *sb, unsigned long mft_no); extern struct inode *ntfs_attr_iget(struct inode *base_vi, ATTR_TYPES type, ntfschar *name, u32 name_len); +extern struct inode *ntfs_index_iget(struct inode *base_vi, ntfschar *name, + u32 name_len); extern struct inode *ntfs_alloc_big_inode(struct super_block *sb); extern void ntfs_destroy_big_inode(struct inode *inode); diff -Nru a/fs/ntfs/layout.h b/fs/ntfs/layout.h --- a/fs/ntfs/layout.h 2004-06-23 20:35:05 -07:00 +++ b/fs/ntfs/layout.h 2004-06-23 20:35:05 -07:00 @@ -459,21 +459,21 @@ * equal then the second u32 values would be compared, etc. */ typedef enum { - COLLATION_BINARY = const_cpu_to_le32(0), /* Collate by binary - compare where the first byte is most - significant. */ - COLLATION_FILE_NAME = const_cpu_to_le32(1), /* Collate file names - as Unicode strings. */ - COLLATION_UNICODE_STRING = const_cpu_to_le32(2), /* Collate Unicode + COLLATION_BINARY = const_cpu_to_le32(0x00), /* Collate by + binary compare where the first byte is + most significant. */ + COLLATION_FILE_NAME = const_cpu_to_le32(0x01), /* Collate file + names as Unicode strings. */ + COLLATION_UNICODE_STRING = const_cpu_to_le32(0x02), /* Collate Unicode strings by comparing their binary Unicode values, except that when a character can be uppercased, the upper case value collates before the lower case one. */ - COLLATION_NTOFS_ULONG = const_cpu_to_le32(16), - COLLATION_NTOFS_SID = const_cpu_to_le32(17), - COLLATION_NTOFS_SECURITY_HASH = const_cpu_to_le32(18), - COLLATION_NTOFS_ULONGS = const_cpu_to_le32(19), + COLLATION_NTOFS_ULONG = const_cpu_to_le32(0x10), + COLLATION_NTOFS_SID = const_cpu_to_le32(0x11), + COLLATION_NTOFS_SECURITY_HASH = const_cpu_to_le32(0x12), + COLLATION_NTOFS_ULONGS = const_cpu_to_le32(0x13), } COLLATION_RULES; /* @@ -2019,8 +2019,9 @@ s64 limit; /* Hard quota (-1 if not limited). */ s64 exceeded_time; /* How long the soft quota has been exceeded. */ SID sid; /* The SID of the user/object associated with - this quota entry. Equals zero for the quota - defaults entry. */ + this quota entry. Equals zero for the quota + defaults entry (and in fact on a WinXP + volume, it is not present at all). */ } __attribute__ ((__packed__)) QUOTA_CONTROL_ENTRY; /* @@ -2033,17 +2034,26 @@ } PREDEFINED_OWNER_IDS; /* + * Current constants for quota control entries. + */ +typedef enum { + /* Current version. */ + QUOTA_VERSION = 2, +} QUOTA_CONTROL_ENTRY_CONSTANTS; + +/* * Index entry flags (16-bit). */ typedef enum { - INDEX_ENTRY_NODE = const_cpu_to_le16(1), /* This entry contains a sub-node, - i.e. a reference to an index - block in form of a virtual + INDEX_ENTRY_NODE = const_cpu_to_le16(1), /* This entry contains a + sub-node, i.e. a reference to an + index block in form of a virtual cluster number (see below). */ - INDEX_ENTRY_END = const_cpu_to_le16(2), /* This signifies the last entry in - an index block. The index entry - does not represent a file but it - can point to a sub-node. */ + INDEX_ENTRY_END = const_cpu_to_le16(2), /* This signifies the last + entry in an index block. The + index entry does not represent a + file but it can point to a + sub-node. */ INDEX_ENTRY_SPACE_FILLER = 0xffff, /* Just to force 16-bit width. */ } __attribute__ ((__packed__)) INDEX_ENTRY_FLAGS; diff -Nru a/fs/ntfs/logfile.c b/fs/ntfs/logfile.c --- a/fs/ntfs/logfile.c 2004-06-23 20:35:05 -07:00 +++ b/fs/ntfs/logfile.c 2004-06-23 20:35:05 -07:00 @@ -672,7 +672,7 @@ * @log_vi: struct inode of loaded journal $LogFile to empty * * Empty the contents of the $LogFile journal @log_vi and return TRUE on - * success FALSE on error. + * success and FALSE on error. * * This function assumes that the $LogFile journal has already been consistency * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean() diff -Nru a/fs/ntfs/malloc.h b/fs/ntfs/malloc.h --- a/fs/ntfs/malloc.h 2004-06-23 20:35:05 -07:00 +++ b/fs/ntfs/malloc.h 2004-06-23 20:35:05 -07:00 @@ -37,13 +37,10 @@ static inline void *ntfs_malloc_nofs(unsigned long size) { if (likely(size <= PAGE_SIZE)) { - if (likely(size)) { - /* kmalloc() has per-CPU caches so is faster for now. */ - return kmalloc(PAGE_SIZE, GFP_NOFS); - /* return (void *)__get_free_page(GFP_NOFS | - __GFP_HIGHMEM); */ - } - BUG(); + BUG_ON(!size); + /* kmalloc() has per-CPU caches so is faster for now. */ + return kmalloc(PAGE_SIZE, GFP_NOFS); + /* return (void *)__get_free_page(GFP_NOFS | __GFP_HIGHMEM); */ } if (likely(size >> PAGE_SHIFT < num_physpages)) return __vmalloc(size, GFP_NOFS | __GFP_HIGHMEM, PAGE_KERNEL); @@ -54,8 +51,9 @@ { if (likely(((unsigned long)addr < VMALLOC_START) || ((unsigned long)addr >= VMALLOC_END ))) { - return kfree(addr); - /* return free_page((unsigned long)addr); */ + kfree(addr); + /* free_page((unsigned long)addr); */ + return; } vfree(addr); } diff -Nru a/fs/ntfs/mft.c b/fs/ntfs/mft.c --- a/fs/ntfs/mft.c 2004-06-23 20:35:05 -07:00 +++ b/fs/ntfs/mft.c 2004-06-23 20:35:05 -07:00 @@ -746,8 +746,8 @@ /* * If the ntfs_inode is clean no need to do anything. If it is dirty, * mark it as clean now so that it can be redirtied later on if needed. - * There is no danger of races as as long as the caller is holding the - * locks for the mft record @m and the page it is in. + * There is no danger of races since the caller is holding the locks + * for the mft record @m and the page it is in. */ if (!NInoTestClearDirty(ni)) goto done; @@ -915,6 +915,7 @@ int nr, i, j; BOOL is_dirty = FALSE; + BUG_ON(!PageLocked(page)); BUG_ON(mft_vi != vol->mft_ino); /* The first mft record number in the page. */ mft_no = page->index << (PAGE_CACHE_SHIFT - vol->mft_record_size_bits); diff -Nru a/fs/ntfs/namei.c b/fs/ntfs/namei.c --- a/fs/ntfs/namei.c 2004-06-23 20:35:05 -07:00 +++ b/fs/ntfs/namei.c 2004-06-23 20:35:05 -07:00 @@ -92,6 +92,8 @@ * file name in the WIN32 namespace corresponding to the matched short file * name. We then convert the name to the current NLS code page, and proceed * searching for a dentry with this name, etc, as in case 2), above. + * + * Locking: Caller must hold i_sem on the directory. */ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, struct nameidata *nd) @@ -383,7 +385,7 @@ return ERR_PTR(-ENOMEM); } try_next: - if (unlikely(!lookup_attr(AT_FILE_NAME, NULL, 0, IGNORE_CASE, 0, + if (unlikely(!lookup_attr(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx))) { put_attr_search_ctx(ctx); unmap_mft_record(ni); diff -Nru a/fs/ntfs/ntfs.h b/fs/ntfs/ntfs.h --- a/fs/ntfs/ntfs.h 2004-06-23 20:35:05 -07:00 +++ b/fs/ntfs/ntfs.h 2004-06-23 20:35:05 -07:00 @@ -24,6 +24,7 @@ #ifndef _LINUX_NTFS_H #define _LINUX_NTFS_H +#include #include #include #include @@ -58,6 +59,7 @@ extern kmem_cache_t *ntfs_inode_cache; extern kmem_cache_t *ntfs_big_inode_cache; extern kmem_cache_t *ntfs_attr_ctx_cache; +extern kmem_cache_t *ntfs_index_ctx_cache; /* The various operations structs defined throughout the driver files. */ extern struct super_operations ntfs_sops; diff -Nru a/fs/ntfs/quota.c b/fs/ntfs/quota.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/fs/ntfs/quota.c 2004-06-23 20:35:05 -07:00 @@ -0,0 +1,115 @@ +/* + * quota.c - NTFS kernel quota ($Quota) handling. Part of the Linux-NTFS + * project. + * + * Copyright (c) 2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef NTFS_RW + +#include "ntfs.h" +#include "index.h" +#include "quota.h" + +/** + * ntfs_mark_quotas_out_of_date - mark the quotas out of date on an ntfs volume + * @vol: ntfs volume on which to mark the quotas out of date + * + * Mark the quotas out of date on the ntfs volume @vol and return TRUE on + * success and FALSE on error. + */ +BOOL ntfs_mark_quotas_out_of_date(ntfs_volume *vol) +{ + ntfs_index_context *ictx; + QUOTA_CONTROL_ENTRY *qce; + const u32 qid = QUOTA_DEFAULTS_ID; + int err; + + ntfs_debug("Entering."); + if (NVolQuotaOutOfDate(vol)) + goto done; + if (!vol->quota_ino || !vol->quota_q_ino) { + ntfs_error(vol->sb, "Quota inodes are not open."); + return FALSE; + } + down(&vol->quota_q_ino->i_sem); + ictx = ntfs_index_ctx_get(NTFS_I(vol->quota_q_ino)); + if (!ictx) { + ntfs_error(vol->sb, "Failed to get index context."); + return FALSE; + } + err = ntfs_index_lookup(&qid, sizeof(qid), ictx); + if (err) { + if (err == -ENOENT) + ntfs_error(vol->sb, "Quota defaults entry is not " + "present."); + else + ntfs_error(vol->sb, "Lookup of quota defaults entry " + "failed."); + goto err_out; + } + if (ictx->data_len < offsetof(QUOTA_CONTROL_ENTRY, sid)) { + ntfs_error(vol->sb, "Quota defaults entry size is invalid. " + "Run chkdsk."); + goto err_out; + } + qce = (QUOTA_CONTROL_ENTRY*)ictx->data; + if (le32_to_cpu(qce->version) != QUOTA_VERSION) { + ntfs_error(vol->sb, "Quota defaults entry version 0x%x is not " + "supported.", le32_to_cpu(qce->version)); + goto err_out; + } + ntfs_debug("Quota defaults flags = 0x%x.", le32_to_cpu(qce->flags)); + /* If quotas are already marked out of date, no need to do anything. */ + if (qce->flags & QUOTA_FLAG_OUT_OF_DATE) + goto set_done; + /* + * If quota tracking is neither requested, nor enabled and there are no + * pending deletes, no need to mark the quotas out of date. + */ + if (!(qce->flags & (QUOTA_FLAG_TRACKING_ENABLED | + QUOTA_FLAG_TRACKING_REQUESTED | + QUOTA_FLAG_PENDING_DELETES))) + goto set_done; + /* + * Set the QUOTA_FLAG_OUT_OF_DATE bit thus marking quotas out of date. + * This is verified on WinXP to be sufficient to cause windows to + * rescan the volume on boot and update all quota entries. + */ + qce->flags |= QUOTA_FLAG_OUT_OF_DATE; + /* Ensure the modified flags are written to disk. */ + ntfs_index_entry_flush_dcache_page(ictx); + ntfs_index_entry_mark_dirty(ictx); +set_done: + ntfs_index_ctx_put(ictx); + up(&vol->quota_q_ino->i_sem); + /* + * We set the flag so we do not try to mark the quotas out of date + * again on remount. + */ + NVolSetQuotaOutOfDate(vol); +done: + ntfs_debug("Done."); + return TRUE; +err_out: + ntfs_index_ctx_put(ictx); + up(&vol->quota_q_ino->i_sem); + return FALSE; +} + +#endif /* NTFS_RW */ diff -Nru a/fs/ntfs/quota.h b/fs/ntfs/quota.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/fs/ntfs/quota.h 2004-06-23 20:35:05 -07:00 @@ -0,0 +1,35 @@ +/* + * quota.h - Defines for NTFS kernel quota ($Quota) handling. Part of the + * Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LINUX_NTFS_QUOTA_H +#define _LINUX_NTFS_QUOTA_H + +#ifdef NTFS_RW + +#include "types.h" +#include "volume.h" + +extern BOOL ntfs_mark_quotas_out_of_date(ntfs_volume *vol); + +#endif /* NTFS_RW */ + +#endif /* _LINUX_NTFS_QUOTA_H */ diff -Nru a/fs/ntfs/super.c b/fs/ntfs/super.c --- a/fs/ntfs/super.c 2004-06-23 20:35:05 -07:00 +++ b/fs/ntfs/super.c 2004-06-23 20:35:05 -07:00 @@ -32,6 +32,9 @@ #include "ntfs.h" #include "sysctl.h" #include "logfile.h" +#include "quota.h" +#include "dir.h" +#include "index.h" /* Number of mounted file systems which have compression enabled. */ static unsigned long ntfs_nr_compression_users; @@ -413,7 +416,8 @@ * flags are set. Also, empty the logfile journal as it would become * stale as soon as something is written to the volume and mark the * volume dirty so that chkdsk is run if the volume is not umounted - * cleanly. + * cleanly. Finally, mark the quotas out of date so Windows rescans + * the volume on boot and updates them. * * When remounting read-only, mark the volume clean if no volume errors * have occured. @@ -460,6 +464,12 @@ NVolSetErrors(vol); return -EROFS; } + if (!ntfs_mark_quotas_out_of_date(vol)) { + ntfs_error(sb, "Failed to mark quotas out of date%s", + es); + NVolSetErrors(vol); + return -EROFS; + } } else if (!(sb->s_flags & MS_RDONLY) && (*flags & MS_RDONLY)) { /* Remounting read-only. */ if (!NVolErrors(vol)) { @@ -875,6 +885,7 @@ struct inode *tmp_ino; ntfs_inode *tmp_ni; + ntfs_debug("Entering."); /* Get mft mirror inode. */ tmp_ino = ntfs_iget(vol->sb, FILE_MFTMirr); if (IS_ERR(tmp_ino) || is_bad_inode(tmp_ino)) { @@ -906,6 +917,7 @@ tmp_ni->itype.index.block_size = vol->mft_record_size; tmp_ni->itype.index.block_size_bits = vol->mft_record_size_bits; vol->mftmirr_ino = tmp_ino; + ntfs_debug("Done."); return TRUE; } @@ -1054,6 +1066,76 @@ return TRUE; } +/** + * load_and_init_quota - load and setup the quota file for a volume if present + * @vol: ntfs super block describing device whose quota file to load + * + * Return TRUE on success or FALSE on error. If $Quota is not present, we + * leave vol->quota_ino as NULL and return success. + */ +static BOOL load_and_init_quota(ntfs_volume *vol) +{ + MFT_REF mref; + struct inode *tmp_ino; + ntfs_name *name = NULL; + static const ntfschar Quota[7] = { const_cpu_to_le16('$'), + const_cpu_to_le16('Q'), const_cpu_to_le16('u'), + const_cpu_to_le16('o'), const_cpu_to_le16('t'), + const_cpu_to_le16('a'), const_cpu_to_le16(0) }; + static ntfschar Q[3] = { const_cpu_to_le16('$'), + const_cpu_to_le16('Q'), const_cpu_to_le16(0) }; + + ntfs_debug("Entering."); + /* + * Find the inode number for the quota file by looking up the filename + * $Quota in the extended system files directory $Extend. + */ + down(&vol->extend_ino->i_sem); + mref = ntfs_lookup_inode_by_name(NTFS_I(vol->extend_ino), Quota, 6, + &name); + up(&vol->extend_ino->i_sem); + if (IS_ERR_MREF(mref)) { + /* + * If the file does not exist, quotas are disabled and have + * never been enabled on this volume, just return success. + */ + if (MREF_ERR(mref) == -ENOENT) { + ntfs_debug("$Quota not present. Volume does not have " + "quotas enabled."); + /* + * No need to try to set quotas out of date if they are + * not enabled. + */ + NVolSetQuotaOutOfDate(vol); + return TRUE; + } + /* A real error occured. */ + ntfs_error(vol->sb, "Failed to find inode number for $Quota."); + return FALSE; + } + /* We do not care for the type of match that was found. */ + if (name) + kfree(name); + /* Get the inode. */ + tmp_ino = ntfs_iget(vol->sb, MREF(mref)); + if (IS_ERR(tmp_ino) || is_bad_inode(tmp_ino)) { + if (!IS_ERR(tmp_ino)) + iput(tmp_ino); + ntfs_error(vol->sb, "Failed to load $Quota."); + return FALSE; + } + vol->quota_ino = tmp_ino; + /* Get the $Q index allocation attribute. */ + tmp_ino = ntfs_index_iget(vol->quota_ino, Q, 2); + if (IS_ERR(tmp_ino)) { + ntfs_error(vol->sb, "Failed to load $Quota/$Q index."); + return FALSE; + } + vol->quota_q_ino = tmp_ino; + ntfs_debug("Done."); + return TRUE; +} + #endif /* NTFS_RW */ /** @@ -1436,20 +1518,66 @@ } // FIXME: Initialize security. /* Get the extended system files' directory inode. */ - tmp_ino = ntfs_iget(sb, FILE_Extend); - if (IS_ERR(tmp_ino) || is_bad_inode(tmp_ino)) { - if (!IS_ERR(tmp_ino)) - iput(tmp_ino); + vol->extend_ino = ntfs_iget(sb, FILE_Extend); + if (IS_ERR(vol->extend_ino) || is_bad_inode(vol->extend_ino)) { + if (!IS_ERR(vol->extend_ino)) + iput(vol->extend_ino); ntfs_error(sb, "Failed to load $Extend."); goto iput_sec_err_out; } - // FIXME: Do something. E.g. want to delete the $UsnJrnl if exists. - // Note we might be doing this at the wrong level; we might want to - // d_alloc_root() and then do a "normal" open(2) of $Extend\$UsnJrnl - // rather than using ntfs_iget here, as we don't know the inode number - // for the files in $Extend directory. - iput(tmp_ino); +#ifdef NTFS_RW + /* Find the quota file, load it if present, and set it up. */ + if (!load_and_init_quota(vol)) { + static const char *es1 = "Failed to load $Quota"; + static const char *es2 = ". Run chkdsk."; + + /* If a read-write mount, convert it to a read-only mount. */ + if (!(sb->s_flags & MS_RDONLY)) { + if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO | + ON_ERRORS_CONTINUE))) { + ntfs_error(sb, "%s and neither on_errors=" + "continue nor on_errors=" + "remount-ro was specified%s", + es1, es2); + goto iput_quota_err_out; + } + sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; + ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); + } else + ntfs_warning(sb, "%s. Will not be able to remount " + "read-write%s", es1, es2); + /* This will prevent a read-write remount. */ + NVolSetErrors(vol); + } + /* If (still) a read-write mount, mark the quotas out of date. */ + if (!(sb->s_flags & MS_RDONLY) && + !ntfs_mark_quotas_out_of_date(vol)) { + static const char *es1 = "Failed to mark quotas out of date"; + static const char *es2 = ". Run chkdsk."; + + /* Convert to a read-only mount. */ + if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO | + ON_ERRORS_CONTINUE))) { + ntfs_error(sb, "%s and neither on_errors=continue nor " + "on_errors=remount-ro was specified%s", + es1, es2); + goto iput_quota_err_out; + } + ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); + sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; + NVolSetErrors(vol); + } + // TODO: Delete or checkpoint the $UsnJrnl if it exists. +#endif /* NTFS_RW */ return TRUE; +#ifdef NTFS_RW +iput_quota_err_out: + if (vol->quota_q_ino) + iput(vol->quota_q_ino); + if (vol->quota_ino) + iput(vol->quota_ino); + iput(vol->extend_ino); +#endif /* NTFS_RW */ iput_sec_err_out: iput(vol->secure_ino); iput_root_err_out: @@ -1496,6 +1624,12 @@ /* NTFS 3.0+ specific. */ if (vol->major_ver >= 3) { + if (vol->quota_q_ino) + ntfs_commit_inode(vol->quota_q_ino); + if (vol->quota_ino) + ntfs_commit_inode(vol->quota_ino); + if (vol->extend_ino) + ntfs_commit_inode(vol->extend_ino); if (vol->secure_ino) ntfs_commit_inode(vol->secure_ino); } @@ -1544,6 +1678,20 @@ /* NTFS 3.0+ specific clean up. */ if (vol->major_ver >= 3) { +#ifdef NTFS_RW + if (vol->quota_q_ino) { + iput(vol->quota_q_ino); + vol->quota_q_ino = NULL; + } + if (vol->quota_ino) { + iput(vol->quota_ino); + vol->quota_ino = NULL; + } +#endif /* NTFS_RW */ + if (vol->extend_ino) { + iput(vol->extend_ino); + vol->extend_ino = NULL; + } if (vol->secure_ino) { iput(vol->secure_ino); vol->secure_ino = NULL; @@ -2018,7 +2166,6 @@ init_rwsem(&vol->mftbmp_lock); #ifdef NTFS_RW vol->mftmirr_ino = NULL; - vol->mftmirr_size = 0; vol->logfile_ino = NULL; #endif /* NTFS_RW */ vol->lcnbmp_ino = NULL; @@ -2026,10 +2173,11 @@ vol->vol_ino = NULL; vol->root_ino = NULL; vol->secure_ino = NULL; - vol->uid = vol->gid = 0; - vol->flags = 0; - vol->on_errors = 0; - vol->mft_zone_multiplier = 0; + vol->extend_ino = NULL; +#ifdef NTFS_RW + vol->quota_ino = NULL; + vol->quota_q_ino = NULL; +#endif /* NTFS_RW */ vol->nls_map = NULL; /* @@ -2178,23 +2326,48 @@ } ntfs_error(sb, "Failed to allocate root directory."); /* Clean up after the successful load_system_files() call from above. */ + // TODO: Use ntfs_put_super() instead of repeating all this code... + // FIXME: Should mark the volume clean as the error is most likely + // -ENOMEM. iput(vol->vol_ino); vol->vol_ino = NULL; /* NTFS 3.0+ specific clean up. */ if (vol->major_ver >= 3) { - iput(vol->secure_ino); - vol->secure_ino = NULL; +#ifdef NTFS_RW + if (vol->quota_q_ino) { + iput(vol->quota_q_ino); + vol->quota_q_ino = NULL; + } + if (vol->quota_ino) { + iput(vol->quota_ino); + vol->quota_ino = NULL; + } +#endif /* NTFS_RW */ + if (vol->extend_ino) { + iput(vol->extend_ino); + vol->extend_ino = NULL; + } + if (vol->secure_ino) { + iput(vol->secure_ino); + vol->secure_ino = NULL; + } } iput(vol->root_ino); vol->root_ino = NULL; iput(vol->lcnbmp_ino); vol->lcnbmp_ino = NULL; -#ifdef NTFS_RW - iput(vol->mftmirr_ino); - vol->mftmirr_ino = NULL; -#endif /* NTFS_RW */ iput(vol->mftbmp_ino); vol->mftbmp_ino = NULL; +#ifdef NTFS_RW + if (vol->logfile_ino) { + iput(vol->logfile_ino); + vol->logfile_ino = NULL; + } + if (vol->mftmirr_ino) { + iput(vol->mftmirr_ino); + vol->mftmirr_ino = NULL; + } +#endif /* NTFS_RW */ vol->upcase_len = 0; if (vol->upcase != default_upcase) ntfs_free(vol->upcase); @@ -2220,10 +2393,9 @@ up(&ntfs_lock); iput_tmp_ino_err_out_now: iput(tmp_ino); - if (vol->mft_ino && vol->mft_ino != tmp_ino) { + if (vol->mft_ino && vol->mft_ino != tmp_ino) iput(vol->mft_ino); - vol->mft_ino = NULL; - } + vol->mft_ino = NULL; /* * This is needed to get ntfs_clear_extent_inode() called for each * inode we have ever called ntfs_iget()/iput() on, otherwise we A) @@ -2270,10 +2442,11 @@ } /* - * Slab cache to optimize allocations and deallocations of attribute search - * contexts. + * Slab caches to optimize allocations and deallocations of attribute search + * contexts and index contexts, respectively. */ kmem_cache_t *ntfs_attr_ctx_cache; +kmem_cache_t *ntfs_index_ctx_cache; /* A global default upcase table and a corresponding reference count. */ wchar_t *default_upcase = NULL; @@ -2300,6 +2473,7 @@ }; /* Stable names for the slab caches. */ +static const char ntfs_index_ctx_cache_name[] = "ntfs_index_ctx_cache"; static const char ntfs_attr_ctx_cache_name[] = "ntfs_attr_ctx_cache"; static const char ntfs_name_cache_name[] = "ntfs_name_cache"; static const char ntfs_inode_cache_name[] = "ntfs_inode_cache"; @@ -2326,13 +2500,21 @@ ntfs_debug("Debug messages are enabled."); + ntfs_index_ctx_cache = kmem_cache_create(ntfs_index_ctx_cache_name, + sizeof(ntfs_index_context), 0 /* offset */, + SLAB_HWCACHE_ALIGN, NULL /* ctor */, NULL /* dtor */); + if (!ntfs_index_ctx_cache) { + printk(KERN_CRIT "NTFS: Failed to create %s!\n", + ntfs_index_ctx_cache_name); + goto ictx_err_out; + } ntfs_attr_ctx_cache = kmem_cache_create(ntfs_attr_ctx_cache_name, sizeof(attr_search_context), 0 /* offset */, SLAB_HWCACHE_ALIGN, NULL /* ctor */, NULL /* dtor */); if (!ntfs_attr_ctx_cache) { printk(KERN_CRIT "NTFS: Failed to create %s!\n", ntfs_attr_ctx_cache_name); - goto ctx_err_out; + goto actx_err_out; } ntfs_name_cache = kmem_cache_create(ntfs_name_cache_name, @@ -2385,7 +2567,9 @@ kmem_cache_destroy(ntfs_name_cache); name_err_out: kmem_cache_destroy(ntfs_attr_ctx_cache); -ctx_err_out: +actx_err_out: + kmem_cache_destroy(ntfs_index_ctx_cache); +ictx_err_out: if (!err) { printk(KERN_CRIT "NTFS: Aborting NTFS file system driver " "registration...\n"); @@ -2414,6 +2598,9 @@ if (kmem_cache_destroy(ntfs_attr_ctx_cache) && (err = 1)) printk(KERN_CRIT "NTFS: Failed to destory %s.\n", ntfs_attr_ctx_cache_name); + if (kmem_cache_destroy(ntfs_index_ctx_cache) && (err = 1)) + printk(KERN_CRIT "NTFS: Failed to destory %s.\n", + ntfs_index_ctx_cache_name); if (err) printk(KERN_CRIT "NTFS: This causes memory to leak! There is " "probably a BUG in the driver! Please report " diff -Nru a/fs/ntfs/volume.h b/fs/ntfs/volume.h --- a/fs/ntfs/volume.h 2004-06-23 20:35:05 -07:00 +++ b/fs/ntfs/volume.h 2004-06-23 20:35:05 -07:00 @@ -103,6 +103,13 @@ directory. */ struct inode *secure_ino; /* The VFS inode of $Secure (NTFS3.0+ only, otherwise NULL). */ + struct inode *extend_ino; /* The VFS inode of $Extend (NTFS3.0+ + only, otherwise NULL). */ +#ifdef NTFS_RW + /* $Quota stuff is NTFS3.0+ specific. Unused/NULL otherwise. */ + struct inode *quota_ino; /* The VFS inode of $Quota. */ + struct inode *quota_q_ino; /* Attribute inode for $Quota/$Q. */ +#endif /* NTFS_RW */ struct nls_table *nls_map; } ntfs_volume; @@ -117,6 +124,7 @@ Otherwise be case insensitive and create file names in WIN32 namespace. */ NV_LogFileEmpty, /* 1: $LogFile journal is empty. */ + NV_QuotaOutOfDate, /* 1: $Quota is out of date. */ } ntfs_volume_flags; /* @@ -142,5 +150,6 @@ NVOL_FNS(ShowSystemFiles) NVOL_FNS(CaseSensitive) NVOL_FNS(LogFileEmpty) +NVOL_FNS(QuotaOutOfDate) #endif /* _LINUX_NTFS_VOLUME_H */