Commit af6ea9ca authored by Linus Torvalds's avatar Linus Torvalds

Merge master.kernel.org:/pub/scm/linux/kernel/git/aia21/ntfs-2.6

parents 1fa4aad4 c5147207
...@@ -21,7 +21,7 @@ Overview ...@@ -21,7 +21,7 @@ Overview
======== ========
Linux-NTFS comes with a number of user-space programs known as ntfsprogs. Linux-NTFS comes with a number of user-space programs known as ntfsprogs.
These include mkntfs, a full-featured ntfs file system format utility, These include mkntfs, a full-featured ntfs filesystem format utility,
ntfsundelete used for recovering files that were unintentionally deleted ntfsundelete used for recovering files that were unintentionally deleted
from an NTFS volume and ntfsresize which is used to resize an NTFS partition. from an NTFS volume and ntfsresize which is used to resize an NTFS partition.
See the web site for more information. See the web site for more information.
...@@ -149,7 +149,14 @@ case_sensitive=<BOOL> If case_sensitive is specified, treat all file names as ...@@ -149,7 +149,14 @@ case_sensitive=<BOOL> If case_sensitive is specified, treat all file names as
name, if it exists. If case_sensitive, you will need name, if it exists. If case_sensitive, you will need
to provide the correct case of the short file name. to provide the correct case of the short file name.
errors=opt What to do when critical file system errors are found. disable_sparse=<BOOL> If disable_sparse is specified, creation of sparse
regions, i.e. holes, inside files is disabled for the
volume (for the duration of this mount only). By
default, creation of sparse regions is enabled, which
is consistent with the behaviour of traditional Unix
filesystems.
errors=opt What to do when critical filesystem errors are found.
Following values can be used for "opt": Following values can be used for "opt":
continue: DEFAULT, try to clean-up as much as continue: DEFAULT, try to clean-up as much as
possible, e.g. marking a corrupt inode as possible, e.g. marking a corrupt inode as
...@@ -432,6 +439,24 @@ ChangeLog ...@@ -432,6 +439,24 @@ ChangeLog
Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog. Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.
2.1.23:
- Stamp the user space journal, aka transaction log, aka $UsnJrnl, if
it is present and active thus telling Windows and applications using
the transaction log that changes can have happened on the volume
which are not recorded in $UsnJrnl.
- Detect the case when Windows has been hibernated (suspended to disk)
and if this is the case do not allow (re)mounting read-write to
prevent data corruption when you boot back into the suspended
Windows session.
- Implement extension of resident files using the normal file write
code paths, i.e. most very small files can be extended to be a little
bit bigger but not by much.
- Add new mount option "disable_sparse". (See list of mount options
above for details.)
- Improve handling of ntfs volumes with errors and strange boot sectors
in particular.
- Fix various bugs including a nasty deadlock that appeared in recent
kernels (around 2.6.11-2.6.12 timeframe).
2.1.22: 2.1.22:
- Improve handling of ntfs volumes with errors. - Improve handling of ntfs volumes with errors.
- Fix various bugs and race conditions. - Fix various bugs and race conditions.
......
ToDo/Notes: ToDo/Notes:
- Find and fix bugs. - Find and fix bugs.
- Checkpoint or disable the user space journal ($UsnJrnl).
- In between ntfs_prepare/commit_write, need exclusion between - In between ntfs_prepare/commit_write, need exclusion between
simultaneous file extensions. Need perhaps an NInoResizeUnderway() simultaneous file extensions. This is given to us by holding i_sem
flag which we can set in ntfs_prepare_write() and clear again in on the inode. The only places in the kernel when a file is resized
ntfs_commit_write(). Just have to be careful in readpage/writepage, are prepare/commit write and truncate for both of which i_sem is
as well as in truncate, that we play nice... We might need to have held. Just have to be careful in readpage/writepage and all other
a data_size field in the ntfs_inode to store the real attribute helpers not running under i_sem that we play nice...
length. Also need to be careful with initialized_size extention in Also need to be careful with initialized_size extention in
ntfs_prepare_write. Basically, just be _very_ careful in this code... ntfs_prepare_write. Basically, just be _very_ careful in this code...
OTOH, perhaps i_sem, which is held accross generic_file_write is UPDATE: The only things that need to be checked are read/writepage
sufficient for synchronisation here. We then just need to make sure which do not hold i_sem. Note writepage cannot change i_size but it
ntfs_readpage/writepage/truncate interoperate properly with us. needs to cope with a concurrent i_size change, just like readpage.
UPDATE: The above is all ok as it is due to i_sem held. The only Also both need to cope with concurrent changes to the other sizes,
thing that needs to be checked is ntfs_writepage() which does not i.e. initialized/allocated/compressed size, as well.
hold i_sem. It cannot change i_size but it needs to cope with a
concurrent i_size change.
- Implement mft.c::sync_mft_mirror_umount(). We currently will just - Implement mft.c::sync_mft_mirror_umount(). We currently will just
leave the volume dirty on umount if the final iput(vol->mft_ino) leave the volume dirty on umount if the final iput(vol->mft_ino)
causes a write of any mirrored mft records due to the mft mirror causes a write of any mirrored mft records due to the mft mirror
...@@ -25,12 +22,158 @@ ToDo/Notes: ...@@ -25,12 +22,158 @@ ToDo/Notes:
- Enable the code for setting the NT4 compatibility flag when we start - Enable the code for setting the NT4 compatibility flag when we start
making NTFS 1.2 specific modifications. making NTFS 1.2 specific modifications.
2.1.23-WIP 2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements...
- Add printk rate limiting for ntfs_warning() and ntfs_error() when - Add printk rate limiting for ntfs_warning() and ntfs_error() when
compiled without debug. This avoids a possible denial of service compiled without debug. This avoids a possible denial of service
attack. Thanks to Carl-Daniel Hailfinger from SuSE for pointing this attack. Thanks to Carl-Daniel Hailfinger from SuSE for pointing this
out. out.
- Fix compilation warnings on ia64. (Randy Dunlap)
- Use i_size_{read,write}() instead of reading i_size by hand and cache
the value where apropriate.
- Add size_lock to the ntfs_inode structure. This is an rw spinlock
and it locks against access to the inode sizes. Note, ->size_lock
is also accessed from irq context so you must use the _irqsave and
_irqrestore lock and unlock functions, respectively. Protect all
accesses to allocated_size, initialized_size, and compressed_size.
- Minor optimization to fs/ntfs/super.c::ntfs_statfs() and its helpers.
- Implement extension of resident files in the regular file write code
paths (fs/ntfs/aops.c::ntfs_{prepare,commit}_write()). At present
this only works until the data attribute becomes too big for the mft
record after which we abort the write returning -EOPNOTSUPP from
ntfs_prepare_write().
- Add disable_sparse mount option together with a per volume sparse
enable bit which is set appropriately and a per inode sparse disable
bit which is preset on some system file inodes as appropriate.
- Enforce that sparse support is disabled on NTFS volumes pre 3.0.
- Fix a bug in fs/ntfs/runlist.c::ntfs_mapping_pairs_decompress() in
the creation of the unmapped runlist element for the base attribute
extent.
- Split ntfs_map_runlist() into ntfs_map_runlist() and a non-locking
helper ntfs_map_runlist_nolock() which is used by ntfs_map_runlist().
This allows us to map runlist fragments with the runlist lock already
held without having to drop and reacquire it around the call. Adapt
all callers.
- Change ntfs_find_vcn() to ntfs_find_vcn_nolock() which takes a locked
runlist. This allows us to find runlist elements with the runlist
lock already held without having to drop and reacquire it around the
call. Adapt all callers.
- Change time to u64 in time.h::ntfs2utc() as it otherwise generates a
warning in the do_div() call on sparc32. Thanks to Meelis Roos for
the report and analysis of the warning.
- Fix a nasty runlist merge bug when merging two holes.
- Set the ntfs_inode->allocated_size to the real allocated size in the
mft record for resident attributes (fs/ntfs/inode.c).
- Small readability cleanup to use "a" instead of "ctx->attr"
everywhere (fs/ntfs/inode.c).
- Make fs/ntfs/namei.c::ntfs_get_{parent,dentry} static and move the
definition of ntfs_export_ops from fs/ntfs/super.c to namei.c. Also,
declare ntfs_export_ops in fs/ntfs/ntfs.h.
- Correct sparse file handling. The compressed values need to be
checked and set in the ntfs inode as done for compressed files and
the compressed size needs to be used for vfs inode->i_blocks instead
of the allocated size, again, as done for compressed files.
- Add AT_EA in addition to AT_DATA to whitelist for being allowed to be
non-resident in fs/ntfs/attrib.c::ntfs_attr_can_be_non_resident().
- Add fs/ntfs/attrib.c::ntfs_attr_vcn_to_lcn_nolock() used by the new
write code.
- Fix bug in fs/ntfs/attrib.c::ntfs_find_vcn_nolock() where after
dropping the read lock and taking the write lock we were not checking
whether someone else did not already do the work we wanted to do.
- Rename fs/ntfs/attrib.c::ntfs_find_vcn_nolock() to
ntfs_attr_find_vcn_nolock() and update all callers.
- Add fs/ntfs/attrib.[hc]::ntfs_attr_make_non_resident().
- Fix sign of various error return values to be negative in
fs/ntfs/lcnalloc.c.
- Modify ->readpage and ->writepage (fs/ntfs/aops.c) so they detect and
handle the case where an attribute is converted from resident to
non-resident by a concurrent file write.
- Remove checks for NULL before calling kfree() since kfree() does the
checking itself. (Jesper Juhl)
- Some utilities modify the boot sector but do not update the checksum.
Thus, relax the checking in fs/ntfs/super.c::is_boot_sector_ntfs() to
only emit a warning when the checksum is incorrect rather than
refusing the mount. Thanks to Bernd Casimir for pointing this
problem out.
- Update attribute definition handling.
- Add NTFS_MAX_CLUSTER_SIZE and NTFS_MAX_PAGES_PER_CLUSTER constants.
- Use NTFS_MAX_CLUSTER_SIZE in super.c instead of hard coding 0x10000.
- Use MAX_BUF_PER_PAGE instead of variable sized array allocation for
better code generation and one less sparse warning in fs/ntfs/aops.c.
- Remove spurious void pointer casts from fs/ntfs/. (Pekka Enberg)
- Use C99 style structure initialization after memory allocation where
possible (fs/ntfs/{attrib.c,index.c,super.c}). Thanks to Al Viro and
Pekka Enberg.
- Stamp the transaction log ($UsnJrnl), aka user space journal, if it
is active on the volume and we are mounting read-write or remounting
from read-only to read-write.
- Fix a bug in address space operations error recovery code paths where
if the runlist was not mapped at all and a mapping error occured we
would leave the runlist locked on exit to the function so that the
next access to the same file would try to take the lock and deadlock.
- Detect the case when Windows has been suspended to disk on the volume
to be mounted and if this is the case do not allow (re)mounting
read-write. This is done by parsing hiberfil.sys if present.
- Fix several occurences of a bug where we would perform 'var & ~const'
with a 64-bit variable and a int, i.e. 32-bit, constant. This causes
the higher order 32-bits of the 64-bit variable to be zeroed. To fix
this cast the 'const' to the same 64-bit type as 'var'.
- Change the runlist terminator of the newly allocated cluster(s) to
LCN_ENOENT in ntfs_attr_make_non_resident(). Otherwise the runlist
code gets confused.
- Add an extra parameter @last_vcn to ntfs_get_size_for_mapping_pairs()
and ntfs_mapping_pairs_build() to allow the runlist encoding to be
partial which is desirable when filling holes in sparse attributes.
Update all callers.
- Change ntfs_map_runlist_nolock() to only decompress the mapping pairs
if the requested vcn is inside it. Otherwise we get into problems
when we try to map an out of bounds vcn because we then try to map
the already mapped runlist fragment which causes
ntfs_mapping_pairs_decompress() to fail and return error. Update
ntfs_attr_find_vcn_nolock() accordingly.
- Fix a nasty deadlock that appeared in recent kernels.
The situation: VFS inode X on a mounted ntfs volume is dirty. For
same inode X, the ntfs_inode is dirty and thus corresponding on-disk
inode, i.e. mft record, which is in a dirty PAGE_CACHE_PAGE belonging
to the table of inodes, i.e. $MFT, inode 0.
What happens:
Process 1: sys_sync()/umount()/whatever... calls
__sync_single_inode() for $MFT -> do_writepages() -> write_page for
the dirty page containing the on-disk inode X, the page is now locked
-> ntfs_write_mst_block() which clears PageUptodate() on the page to
prevent anyone else getting hold of it whilst it does the write out.
This is necessary as the on-disk inode needs "fixups" applied before
the write to disk which are removed again after the write and
PageUptodate is then set again. It then analyses the page looking
for dirty on-disk inodes and when it finds one it calls
ntfs_may_write_mft_record() to see if it is safe to write this
on-disk inode. This then calls ilookup5() to check if the
corresponding VFS inode is in icache(). This in turn calls ifind()
which waits on the inode lock via wait_on_inode whilst holding the
global inode_lock.
Process 2: pdflush results in a call to __sync_single_inode for the
same VFS inode X on the ntfs volume. This locks the inode (I_LOCK)
then calls write-inode -> ntfs_write_inode -> map_mft_record() ->
read_cache_page() for the page (in page cache of table of inodes
$MFT, inode 0) containing the on-disk inode. This page has
PageUptodate() clear because of Process 1 (see above) so
read_cache_page() blocks when it tries to take the page lock for the
page so it can call ntfs_read_page().
Thus Process 1 is holding the page lock on the page containing the
on-disk inode X and it is waiting on the inode X to be unlocked in
ifind() so it can write the page out and then unlock the page.
And Process 2 is holding the inode lock on inode X and is waiting for
the page to be unlocked so it can call ntfs_readpage() or discover
that Process 1 set PageUptodate() again and use the page.
Thus we have a deadlock due to ifind() waiting on the inode lock.
The solution: The fix is to use the newly introduced
ilookup5_nowait() which does not wait on the inode's lock and hence
avoids the deadlock. This is safe as we do not care about the VFS
inode and only use the fact that it is in the VFS inode cache and the
fact that the vfs and ntfs inodes are one struct in memory to find
the ntfs inode in memory if present. Also, the ntfs inode has its
own locking so it does not matter if the vfs inode is locked.
2.1.22 - Many bug and race fixes and error handling improvements. 2.1.22 - Many bug and race fixes and error handling improvements.
...@@ -1037,7 +1180,7 @@ tng-0.0.8 - 08/03/2002 - Now using BitKeeper, http://linux-ntfs.bkbits.net/ ...@@ -1037,7 +1180,7 @@ tng-0.0.8 - 08/03/2002 - Now using BitKeeper, http://linux-ntfs.bkbits.net/
- Further runlist merging work. (Richard Russon) - Further runlist merging work. (Richard Russon)
- Backwards compatibility for gcc-2.95. (Richard Russon) - Backwards compatibility for gcc-2.95. (Richard Russon)
- Update to kernel 2.5.5-pre1 and rediff the now tiny patch. - Update to kernel 2.5.5-pre1 and rediff the now tiny patch.
- Convert to new file system declaration using ->ntfs_get_sb() and - Convert to new filesystem declaration using ->ntfs_get_sb() and
replacing ntfs_read_super() with ntfs_fill_super(). replacing ntfs_read_super() with ntfs_fill_super().
- Set s_maxbytes to MAX_LFS_FILESIZE to avoid page cache page index - Set s_maxbytes to MAX_LFS_FILESIZE to avoid page cache page index
overflow on 32-bit architectures. overflow on 32-bit architectures.
...@@ -1333,7 +1476,7 @@ tng-0.0.1 - The first useful version. ...@@ -1333,7 +1476,7 @@ tng-0.0.1 - The first useful version.
The driver is now actually useful! Yey. (-: It undoubtedly has got bugs The driver is now actually useful! Yey. (-: It undoubtedly has got bugs
though and it doesn't implement accesssing compressed files yet. Also, though and it doesn't implement accesssing compressed files yet. Also,
accessing files with attribute list attributes is not implemented yet accessing files with attribute list attributes is not implemented yet
either. But for small or simple file systems it should work and allow either. But for small or simple filesystems it should work and allow
you to list directories, use stat on directory entries and the file you to list directories, use stat on directory entries and the file
system, open, read, mmap and llseek around in files. A big mile stone system, open, read, mmap and llseek around in files. A big mile stone
has been reached! has been reached!
...@@ -1341,7 +1484,7 @@ tng-0.0.1 - The first useful version. ...@@ -1341,7 +1484,7 @@ tng-0.0.1 - The first useful version.
tng-0.0.0 - Initial version tag. tng-0.0.0 - Initial version tag.
Initial driver implementation. The driver can mount and umount simple Initial driver implementation. The driver can mount and umount simple
NTFS file systems (i.e. ones without attribute lists in the system NTFS filesystems (i.e. ones without attribute lists in the system
files). If the mount fails there might be problems in the error handling files). If the mount fails there might be problems in the error handling
code paths, so be warned. Otherwise it seems to be loading the system code paths, so be warned. Otherwise it seems to be loading the system
files nicely and the mft record read mapping/unmapping seems to be files nicely and the mft record read mapping/unmapping seems to be
......
...@@ -6,7 +6,7 @@ ntfs-objs := aops.o attrib.o collate.o compress.o debug.o dir.o file.o \ ...@@ -6,7 +6,7 @@ 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 runlist.o super.o sysctl.o \ index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \
unistr.o upcase.o unistr.o upcase.o
EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.22\" EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.23\"
ifeq ($(CONFIG_NTFS_DEBUG),y) ifeq ($(CONFIG_NTFS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG EXTRA_CFLAGS += -DDEBUG
...@@ -15,5 +15,5 @@ endif ...@@ -15,5 +15,5 @@ endif
ifeq ($(CONFIG_NTFS_RW),y) ifeq ($(CONFIG_NTFS_RW),y)
EXTRA_CFLAGS += -DNTFS_RW EXTRA_CFLAGS += -DNTFS_RW
ntfs-objs += bitmap.o lcnalloc.o logfile.o quota.o ntfs-objs += bitmap.o lcnalloc.o logfile.o quota.o usnjrnl.o
endif endif
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* aops.c - NTFS kernel address space operations and page cache handling. * aops.c - NTFS kernel address space operations and page cache handling.
* Part of the Linux-NTFS project. * Part of the Linux-NTFS project.
* *
* Copyright (c) 2001-2004 Anton Altaparmakov * Copyright (c) 2001-2005 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon * Copyright (c) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
...@@ -66,19 +66,22 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate) ...@@ -66,19 +66,22 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate)
ni = NTFS_I(page->mapping->host); ni = NTFS_I(page->mapping->host);
if (likely(uptodate)) { if (likely(uptodate)) {
s64 file_ofs; s64 file_ofs, initialized_size;
set_buffer_uptodate(bh); set_buffer_uptodate(bh);
file_ofs = ((s64)page->index << PAGE_CACHE_SHIFT) + file_ofs = ((s64)page->index << PAGE_CACHE_SHIFT) +
bh_offset(bh); bh_offset(bh);
read_lock_irqsave(&ni->size_lock, flags);
initialized_size = ni->initialized_size;
read_unlock_irqrestore(&ni->size_lock, flags);
/* Check for the current buffer head overflowing. */ /* Check for the current buffer head overflowing. */
if (file_ofs + bh->b_size > ni->initialized_size) { if (file_ofs + bh->b_size > initialized_size) {
char *addr; char *addr;
int ofs = 0; int ofs = 0;
if (file_ofs < ni->initialized_size) if (file_ofs < initialized_size)
ofs = ni->initialized_size - file_ofs; ofs = initialized_size - file_ofs;
addr = kmap_atomic(page, KM_BIO_SRC_IRQ); addr = kmap_atomic(page, KM_BIO_SRC_IRQ);
memset(addr + bh_offset(bh) + ofs, 0, bh->b_size - ofs); memset(addr + bh_offset(bh) + ofs, 0, bh->b_size - ofs);
flush_dcache_page(page); flush_dcache_page(page);
...@@ -132,7 +135,7 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate) ...@@ -132,7 +135,7 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate)
i * rec_size), rec_size); i * rec_size), rec_size);
flush_dcache_page(page); flush_dcache_page(page);
kunmap_atomic(addr, KM_BIO_SRC_IRQ); kunmap_atomic(addr, KM_BIO_SRC_IRQ);
if (likely(!PageError(page) && page_uptodate)) if (likely(page_uptodate && !PageError(page)))
SetPageUptodate(page); SetPageUptodate(page);
} }
unlock_page(page); unlock_page(page);
...@@ -168,6 +171,7 @@ static int ntfs_read_block(struct page *page) ...@@ -168,6 +171,7 @@ static int ntfs_read_block(struct page *page)
runlist_element *rl; runlist_element *rl;
struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE]; struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE];
sector_t iblock, lblock, zblock; sector_t iblock, lblock, zblock;
unsigned long flags;
unsigned int blocksize, vcn_ofs; unsigned int blocksize, vcn_ofs;
int i, nr; int i, nr;
unsigned char blocksize_bits; unsigned char blocksize_bits;
...@@ -190,8 +194,10 @@ static int ntfs_read_block(struct page *page) ...@@ -190,8 +194,10 @@ static int ntfs_read_block(struct page *page)
} }
iblock = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits); iblock = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
read_lock_irqsave(&ni->size_lock, flags);
lblock = (ni->allocated_size + blocksize - 1) >> blocksize_bits; lblock = (ni->allocated_size + blocksize - 1) >> blocksize_bits;
zblock = (ni->initialized_size + blocksize - 1) >> blocksize_bits; zblock = (ni->initialized_size + blocksize - 1) >> blocksize_bits;
read_unlock_irqrestore(&ni->size_lock, flags);
/* Loop through all the buffers in the page. */ /* Loop through all the buffers in the page. */
rl = NULL; rl = NULL;
...@@ -258,7 +264,8 @@ static int ntfs_read_block(struct page *page) ...@@ -258,7 +264,8 @@ static int ntfs_read_block(struct page *page)
goto lock_retry_remap; goto lock_retry_remap;
rl = NULL; rl = NULL;
lcn = err; lcn = err;
} } else if (!rl)
up_read(&ni->runlist.lock);
/* Hard error, zero out region. */ /* Hard error, zero out region. */
bh->b_blocknr = -1; bh->b_blocknr = -1;
SetPageError(page); SetPageError(page);
...@@ -341,14 +348,15 @@ static int ntfs_read_block(struct page *page) ...@@ -341,14 +348,15 @@ static int ntfs_read_block(struct page *page)
*/ */
static int ntfs_readpage(struct file *file, struct page *page) static int ntfs_readpage(struct file *file, struct page *page)
{ {
loff_t i_size;
ntfs_inode *ni, *base_ni; ntfs_inode *ni, *base_ni;
u8 *kaddr; u8 *kaddr;
ntfs_attr_search_ctx *ctx; ntfs_attr_search_ctx *ctx;
MFT_RECORD *mrec; MFT_RECORD *mrec;
unsigned long flags;
u32 attr_len; u32 attr_len;
int err = 0; int err = 0;
retry_readpage:
BUG_ON(!PageLocked(page)); BUG_ON(!PageLocked(page));
/* /*
* This can potentially happen because we clear PageUptodate() during * This can potentially happen because we clear PageUptodate() during
...@@ -383,9 +391,9 @@ static int ntfs_readpage(struct file *file, struct page *page) ...@@ -383,9 +391,9 @@ static int ntfs_readpage(struct file *file, struct page *page)
* Attribute is resident, implying it is not compressed or encrypted. * Attribute is resident, implying it is not compressed or encrypted.
* This also means the attribute is smaller than an mft record and * This also means the attribute is smaller than an mft record and
* hence smaller than a page, so can simply zero out any pages with * hence smaller than a page, so can simply zero out any pages with
* index above 0. We can also do this if the file size is 0. * index above 0.
*/ */
if (unlikely(page->index > 0 || !i_size_read(VFS_I(ni)))) { if (unlikely(page->index > 0)) {
kaddr = kmap_atomic(page, KM_USER0); kaddr = kmap_atomic(page, KM_USER0);
memset(kaddr, 0, PAGE_CACHE_SIZE); memset(kaddr, 0, PAGE_CACHE_SIZE);
flush_dcache_page(page); flush_dcache_page(page);
...@@ -402,6 +410,14 @@ static int ntfs_readpage(struct file *file, struct page *page) ...@@ -402,6 +410,14 @@ static int ntfs_readpage(struct file *file, struct page *page)
err = PTR_ERR(mrec); err = PTR_ERR(mrec);
goto err_out; goto err_out;
} }
/*
* If a parallel write made the attribute non-resident, drop the mft
* record and retry the readpage.
*/
if (unlikely(NInoNonResident(ni))) {
unmap_mft_record(base_ni);
goto retry_readpage;
}
ctx = ntfs_attr_get_search_ctx(base_ni, mrec); ctx = ntfs_attr_get_search_ctx(base_ni, mrec);
if (unlikely(!ctx)) { if (unlikely(!ctx)) {
err = -ENOMEM; err = -ENOMEM;
...@@ -412,9 +428,10 @@ static int ntfs_readpage(struct file *file, struct page *page) ...@@ -412,9 +428,10 @@ static int ntfs_readpage(struct file *file, struct page *page)
if (unlikely(err)) if (unlikely(err))
goto put_unm_err_out; goto put_unm_err_out;
attr_len = le32_to_cpu(ctx->attr->data.resident.value_length); attr_len = le32_to_cpu(ctx->attr->data.resident.value_length);
i_size = i_size_read(VFS_I(ni)); read_lock_irqsave(&ni->size_lock, flags);
if (unlikely(attr_len > i_size)) if (unlikely(attr_len > ni->initialized_size))
attr_len = i_size; attr_len = ni->initialized_size;
read_unlock_irqrestore(&ni->size_lock, flags);
kaddr = kmap_atomic(page, KM_USER0); kaddr = kmap_atomic(page, KM_USER0);
/* Copy the data to the page. */ /* Copy the data to the page. */
memcpy(kaddr, (u8*)ctx->attr + memcpy(kaddr, (u8*)ctx->attr +
...@@ -463,12 +480,15 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc) ...@@ -463,12 +480,15 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
{ {
VCN vcn; VCN vcn;
LCN lcn; LCN lcn;
s64 initialized_size;
loff_t i_size;
sector_t block, dblock, iblock; sector_t block, dblock, iblock;
struct inode *vi; struct inode *vi;
ntfs_inode *ni; ntfs_inode *ni;
ntfs_volume *vol; ntfs_volume *vol;
runlist_element *rl; runlist_element *rl;
struct buffer_head *bh, *head; struct buffer_head *bh, *head;
unsigned long flags;
unsigned int blocksize, vcn_ofs; unsigned int blocksize, vcn_ofs;
int err; int err;
BOOL need_end_writeback; BOOL need_end_writeback;
...@@ -510,11 +530,16 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc) ...@@ -510,11 +530,16 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
/* The first block in the page. */ /* The first block in the page. */
block = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits); block = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
read_lock_irqsave(&ni->size_lock, flags);
i_size = i_size_read(vi);
initialized_size = ni->initialized_size;
read_unlock_irqrestore(&ni->size_lock, flags);
/* The first out of bounds block for the data size. */ /* The first out of bounds block for the data size. */
dblock = (vi->i_size + blocksize - 1) >> blocksize_bits; dblock = (i_size + blocksize - 1) >> blocksize_bits;
/* The last (fully or partially) initialized block. */ /* The last (fully or partially) initialized block. */
iblock = ni->initialized_size >> blocksize_bits; iblock = initialized_size >> blocksize_bits;
/* /*
* Be very careful. We have no exclusion from __set_page_dirty_buffers * Be very careful. We have no exclusion from __set_page_dirty_buffers
...@@ -559,7 +584,7 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc) ...@@ -559,7 +584,7 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
/* Make sure we have enough initialized size. */ /* Make sure we have enough initialized size. */
if (unlikely((block >= iblock) && if (unlikely((block >= iblock) &&
(ni->initialized_size < vi->i_size))) { (initialized_size < i_size))) {
/* /*
* If this page is fully outside initialized size, zero * If this page is fully outside initialized size, zero
* out all pages between the current initialized size * out all pages between the current initialized size
...@@ -666,7 +691,8 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc) ...@@ -666,7 +691,8 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
goto lock_retry_remap; goto lock_retry_remap;
rl = NULL; rl = NULL;
lcn = err; lcn = err;
} } else if (!rl)
up_read(&ni->runlist.lock);
/* Failed to map the buffer, even after retrying. */ /* Failed to map the buffer, even after retrying. */
bh->b_blocknr = -1; bh->b_blocknr = -1;
ntfs_error(vol->sb, "Failed to write to inode 0x%lx, " ntfs_error(vol->sb, "Failed to write to inode 0x%lx, "
...@@ -801,17 +827,15 @@ static int ntfs_write_mst_block(struct page *page, ...@@ -801,17 +827,15 @@ static int ntfs_write_mst_block(struct page *page,
ntfs_inode *ni = NTFS_I(vi); ntfs_inode *ni = NTFS_I(vi);
ntfs_volume *vol = ni->vol; ntfs_volume *vol = ni->vol;
u8 *kaddr; u8 *kaddr;
unsigned char bh_size_bits = vi->i_blkbits;
unsigned int bh_size = 1 << bh_size_bits;
unsigned int rec_size = ni->itype.index.block_size; unsigned int rec_size = ni->itype.index.block_size;
ntfs_inode *locked_nis[PAGE_CACHE_SIZE / rec_size]; ntfs_inode *locked_nis[PAGE_CACHE_SIZE / rec_size];
struct buffer_head *bh, *head, *tbh, *rec_start_bh; struct buffer_head *bh, *head, *tbh, *rec_start_bh;
int max_bhs = PAGE_CACHE_SIZE / bh_size; struct buffer_head *bhs[MAX_BUF_PER_PAGE];
struct buffer_head *bhs[max_bhs];
runlist_element *rl; runlist_element *rl;
int i, nr_locked_nis, nr_recs, nr_bhs, bhs_per_rec, err, err2; int i, nr_locked_nis, nr_recs, nr_bhs, max_bhs, bhs_per_rec, err, err2;
unsigned rec_size_bits; unsigned bh_size, rec_size_bits;
BOOL sync, is_mft, page_is_dirty, rec_is_dirty; BOOL sync, is_mft, page_is_dirty, rec_is_dirty;
unsigned char bh_size_bits;
ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index " ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
"0x%lx.", vi->i_ino, ni->type, page->index); "0x%lx.", vi->i_ino, ni->type, page->index);
...@@ -826,7 +850,11 @@ static int ntfs_write_mst_block(struct page *page, ...@@ -826,7 +850,11 @@ static int ntfs_write_mst_block(struct page *page,
*/ */
BUG_ON(!(is_mft || S_ISDIR(vi->i_mode) || BUG_ON(!(is_mft || S_ISDIR(vi->i_mode) ||
(NInoAttr(ni) && ni->type == AT_INDEX_ALLOCATION))); (NInoAttr(ni) && ni->type == AT_INDEX_ALLOCATION)));
bh_size_bits = vi->i_blkbits;
bh_size = 1 << bh_size_bits;
max_bhs = PAGE_CACHE_SIZE / bh_size;
BUG_ON(!max_bhs); BUG_ON(!max_bhs);
BUG_ON(max_bhs > MAX_BUF_PER_PAGE);
/* Were we called for sync purposes? */ /* Were we called for sync purposes? */
sync = (wbc->sync_mode == WB_SYNC_ALL); sync = (wbc->sync_mode == WB_SYNC_ALL);
...@@ -846,7 +874,7 @@ static int ntfs_write_mst_block(struct page *page, ...@@ -846,7 +874,7 @@ static int ntfs_write_mst_block(struct page *page,
(PAGE_CACHE_SHIFT - bh_size_bits); (PAGE_CACHE_SHIFT - bh_size_bits);
/* The first out of bounds block for the data size. */ /* The first out of bounds block for the data size. */
dblock = (vi->i_size + bh_size - 1) >> bh_size_bits; dblock = (i_size_read(vi) + bh_size - 1) >> bh_size_bits;
rl = NULL; rl = NULL;
err = err2 = nr_bhs = nr_recs = nr_locked_nis = 0; err = err2 = nr_bhs = nr_recs = nr_locked_nis = 0;
...@@ -858,6 +886,7 @@ static int ntfs_write_mst_block(struct page *page, ...@@ -858,6 +886,7 @@ static int ntfs_write_mst_block(struct page *page,
if (likely(block < rec_block)) { if (likely(block < rec_block)) {
if (unlikely(block >= dblock)) { if (unlikely(block >= dblock)) {
clear_buffer_dirty(bh); clear_buffer_dirty(bh);
set_buffer_uptodate(bh);
continue; continue;
} }
/* /*
...@@ -938,8 +967,11 @@ static int ntfs_write_mst_block(struct page *page, ...@@ -938,8 +967,11 @@ static int ntfs_write_mst_block(struct page *page,
if (err2 == -ENOMEM) if (err2 == -ENOMEM)
page_is_dirty = TRUE; page_is_dirty = TRUE;
lcn = err2; lcn = err2;
} else } else {
err2 = -EIO; err2 = -EIO;
if (!rl)
up_read(&ni->runlist.lock);
}
/* Hard error. Abort writing this record. */ /* Hard error. Abort writing this record. */
if (!err || err == -ENOMEM) if (!err || err == -ENOMEM)
err = err2; err = err2;
...@@ -949,7 +981,8 @@ static int ntfs_write_mst_block(struct page *page, ...@@ -949,7 +981,8 @@ static int ntfs_write_mst_block(struct page *page,
"attribute type 0x%x) because " "attribute type 0x%x) because "
"its location on disk could " "its location on disk could "
"not be determined (error " "not be determined (error "
"code %lli).", (s64)block << "code %lli).",
(long long)block <<
bh_size_bits >> bh_size_bits >>
vol->mft_record_size_bits, vol->mft_record_size_bits,
ni->mft_no, ni->type, ni->mft_no, ni->type,
...@@ -1223,19 +1256,17 @@ static int ntfs_write_mst_block(struct page *page, ...@@ -1223,19 +1256,17 @@ static int ntfs_write_mst_block(struct page *page,
static int ntfs_writepage(struct page *page, struct writeback_control *wbc) static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
{ {
loff_t i_size; loff_t i_size;
struct inode *vi; struct inode *vi = page->mapping->host;
ntfs_inode *ni, *base_ni; ntfs_inode *base_ni = NULL, *ni = NTFS_I(vi);
char *kaddr; char *kaddr;
ntfs_attr_search_ctx *ctx; ntfs_attr_search_ctx *ctx = NULL;
MFT_RECORD *m; MFT_RECORD *m = NULL;
u32 attr_len; u32 attr_len;
int err; int err;
retry_writepage:
BUG_ON(!PageLocked(page)); BUG_ON(!PageLocked(page));
vi = page->mapping->host;
i_size = i_size_read(vi); i_size = i_size_read(vi);
/* Is the page fully outside i_size? (truncate in progress) */ /* Is the page fully outside i_size? (truncate in progress) */
if (unlikely(page->index >= (i_size + PAGE_CACHE_SIZE - 1) >> if (unlikely(page->index >= (i_size + PAGE_CACHE_SIZE - 1) >>
PAGE_CACHE_SHIFT)) { PAGE_CACHE_SHIFT)) {
...@@ -1248,8 +1279,6 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc) ...@@ -1248,8 +1279,6 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
ntfs_debug("Write outside i_size - truncated?"); ntfs_debug("Write outside i_size - truncated?");
return 0; return 0;
} }
ni = NTFS_I(vi);
/* NInoNonResident() == NInoIndexAllocPresent() */ /* NInoNonResident() == NInoIndexAllocPresent() */
if (NInoNonResident(ni)) { if (NInoNonResident(ni)) {
/* /*
...@@ -1326,6 +1355,14 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc) ...@@ -1326,6 +1355,14 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
ctx = NULL; ctx = NULL;
goto err_out; goto err_out;
} }
/*
* If a parallel write made the attribute non-resident, drop the mft
* record and retry the writepage.
*/
if (unlikely(NInoNonResident(ni))) {
unmap_mft_record(base_ni);
goto retry_writepage;
}
ctx = ntfs_attr_get_search_ctx(base_ni, m); ctx = ntfs_attr_get_search_ctx(base_ni, m);
if (unlikely(!ctx)) { if (unlikely(!ctx)) {
err = -ENOMEM; err = -ENOMEM;
...@@ -1367,15 +1404,12 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc) ...@@ -1367,15 +1404,12 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
*/ */
attr_len = le32_to_cpu(ctx->attr->data.resident.value_length); attr_len = le32_to_cpu(ctx->attr->data.resident.value_length);
i_size = i_size_read(VFS_I(ni)); i_size = i_size_read(vi);
kaddr = kmap_atomic(page, KM_USER0);
if (unlikely(attr_len > i_size)) { if (unlikely(attr_len > i_size)) {
/* Zero out of bounds area in the mft record. */
memset((u8*)ctx->attr + le16_to_cpu(
ctx->attr->data.resident.value_offset) +
i_size, 0, attr_len - i_size);
attr_len = i_size; attr_len = i_size;
ctx->attr->data.resident.value_length = cpu_to_le32(attr_len);
} }
kaddr = kmap_atomic(page, KM_USER0);
/* Copy the data from the page to the mft record. */ /* Copy the data from the page to the mft record. */
memcpy((u8*)ctx->attr + memcpy((u8*)ctx->attr +
le16_to_cpu(ctx->attr->data.resident.value_offset), le16_to_cpu(ctx->attr->data.resident.value_offset),
...@@ -1405,8 +1439,10 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc) ...@@ -1405,8 +1439,10 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
err = 0; err = 0;
} else { } else {
ntfs_error(vi->i_sb, "Resident attribute write failed with " ntfs_error(vi->i_sb, "Resident attribute write failed with "
"error %i. Setting page error flag.", err); "error %i.", err);
SetPageError(page); SetPageError(page);
NVolSetErrors(ni->vol);
make_bad_inode(vi);
} }
unlock_page(page); unlock_page(page);
if (ctx) if (ctx)
...@@ -1425,12 +1461,15 @@ static int ntfs_prepare_nonresident_write(struct page *page, ...@@ -1425,12 +1461,15 @@ static int ntfs_prepare_nonresident_write(struct page *page,
{ {
VCN vcn; VCN vcn;
LCN lcn; LCN lcn;
s64 initialized_size;
loff_t i_size;
sector_t block, ablock, iblock; sector_t block, ablock, iblock;
struct inode *vi; struct inode *vi;
ntfs_inode *ni; ntfs_inode *ni;
ntfs_volume *vol; ntfs_volume *vol;
runlist_element *rl; runlist_element *rl;
struct buffer_head *bh, *head, *wait[2], **wait_bh = wait; struct buffer_head *bh, *head, *wait[2], **wait_bh = wait;
unsigned long flags;
unsigned int vcn_ofs, block_start, block_end, blocksize; unsigned int vcn_ofs, block_start, block_end, blocksize;
int err; int err;
BOOL is_retry; BOOL is_retry;
...@@ -1462,6 +1501,7 @@ static int ntfs_prepare_nonresident_write(struct page *page, ...@@ -1462,6 +1501,7 @@ static int ntfs_prepare_nonresident_write(struct page *page,
/* The first block in the page. */ /* The first block in the page. */
block = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits); block = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
read_lock_irqsave(&ni->size_lock, flags);
/* /*
* The first out of bounds block for the allocated size. No need to * The first out of bounds block for the allocated size. No need to
* round up as allocated_size is in multiples of cluster size and the * round up as allocated_size is in multiples of cluster size and the
...@@ -1469,9 +1509,12 @@ static int ntfs_prepare_nonresident_write(struct page *page, ...@@ -1469,9 +1509,12 @@ static int ntfs_prepare_nonresident_write(struct page *page,
* blocksize. * blocksize.
*/ */
ablock = ni->allocated_size >> blocksize_bits; ablock = ni->allocated_size >> blocksize_bits;
i_size = i_size_read(vi);
initialized_size = ni->initialized_size;
read_unlock_irqrestore(&ni->size_lock, flags);
/* The last (fully or partially) initialized block. */ /* The last (fully or partially) initialized block. */
iblock = ni->initialized_size >> blocksize_bits; iblock = initialized_size >> blocksize_bits;
/* Loop through all the buffers in the page. */ /* Loop through all the buffers in the page. */
block_start = 0; block_start = 0;
...@@ -1518,7 +1561,7 @@ static int ntfs_prepare_nonresident_write(struct page *page, ...@@ -1518,7 +1561,7 @@ static int ntfs_prepare_nonresident_write(struct page *page,
* request, i.e. block < ablock is true. * request, i.e. block < ablock is true.
*/ */
if (unlikely((block >= iblock) && if (unlikely((block >= iblock) &&
(ni->initialized_size < vi->i_size))) { (initialized_size < i_size))) {
/* /*
* If this page is fully outside initialized size, zero * If this page is fully outside initialized size, zero
* out all pages between the current initialized size * out all pages between the current initialized size
...@@ -1622,6 +1665,8 @@ static int ntfs_prepare_nonresident_write(struct page *page, ...@@ -1622,6 +1665,8 @@ static int ntfs_prepare_nonresident_write(struct page *page,
"not supported yet. " "not supported yet. "
"Sorry."); "Sorry.");
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
if (!rl)
up_read(&ni->runlist.lock);
goto err_out; goto err_out;
} else if (!is_retry && } else if (!is_retry &&
lcn == LCN_RL_NOT_MAPPED) { lcn == LCN_RL_NOT_MAPPED) {
...@@ -1636,7 +1681,8 @@ static int ntfs_prepare_nonresident_write(struct page *page, ...@@ -1636,7 +1681,8 @@ static int ntfs_prepare_nonresident_write(struct page *page,
goto lock_retry_remap; goto lock_retry_remap;
rl = NULL; rl = NULL;
lcn = err; lcn = err;
} } else if (!rl)
up_read(&ni->runlist.lock);
/* /*
* Failed to map the buffer, even after * Failed to map the buffer, even after
* retrying. * retrying.
...@@ -1797,6 +1843,7 @@ static int ntfs_prepare_write(struct file *file, struct page *page, ...@@ -1797,6 +1843,7 @@ static int ntfs_prepare_write(struct file *file, struct page *page,
unsigned from, unsigned to) unsigned from, unsigned to)
{ {
s64 new_size; s64 new_size;
loff_t i_size;
struct inode *vi = page->mapping->host; struct inode *vi = page->mapping->host;
ntfs_inode *base_ni = NULL, *ni = NTFS_I(vi); ntfs_inode *base_ni = NULL, *ni = NTFS_I(vi);
ntfs_volume *vol = ni->vol; ntfs_volume *vol = ni->vol;
...@@ -1868,14 +1915,8 @@ static int ntfs_prepare_write(struct file *file, struct page *page, ...@@ -1868,14 +1915,8 @@ static int ntfs_prepare_write(struct file *file, struct page *page,
BUG_ON(page_has_buffers(page)); BUG_ON(page_has_buffers(page));
new_size = ((s64)page->index << PAGE_CACHE_SHIFT) + to; new_size = ((s64)page->index << PAGE_CACHE_SHIFT) + to;
/* If we do not need to resize the attribute allocation we are done. */ /* If we do not need to resize the attribute allocation we are done. */
if (new_size <= vi->i_size) if (new_size <= i_size_read(vi))
goto done; goto done;
// FIXME: We abort for now as this code is not safe.
ntfs_error(vi->i_sb, "Changing the file size is not supported yet. "
"Sorry.");
return -EOPNOTSUPP;
/* Map, pin, and lock the (base) mft record. */ /* Map, pin, and lock the (base) mft record. */
if (!NInoAttr(ni)) if (!NInoAttr(ni))
base_ni = ni; base_ni = ni;
...@@ -1904,7 +1945,15 @@ static int ntfs_prepare_write(struct file *file, struct page *page, ...@@ -1904,7 +1945,15 @@ static int ntfs_prepare_write(struct file *file, struct page *page,
a = ctx->attr; a = ctx->attr;
/* The total length of the attribute value. */ /* The total length of the attribute value. */
attr_len = le32_to_cpu(a->data.resident.value_length); attr_len = le32_to_cpu(a->data.resident.value_length);
BUG_ON(vi->i_size != attr_len); /* Fix an eventual previous failure of ntfs_commit_write(). */
i_size = i_size_read(vi);
if (unlikely(attr_len > i_size)) {
attr_len = i_size;
a->data.resident.value_length = cpu_to_le32(attr_len);
}
/* If we do not need to resize the attribute allocation we are done. */
if (new_size <= attr_len)
goto done_unm;
/* Check if new size is allowed in $AttrDef. */ /* Check if new size is allowed in $AttrDef. */
err = ntfs_attr_size_bounds_check(vol, ni->type, new_size); err = ntfs_attr_size_bounds_check(vol, ni->type, new_size);
if (unlikely(err)) { if (unlikely(err)) {
...@@ -1962,6 +2011,7 @@ static int ntfs_prepare_write(struct file *file, struct page *page, ...@@ -1962,6 +2011,7 @@ static int ntfs_prepare_write(struct file *file, struct page *page,
} }
flush_dcache_mft_record_page(ctx->ntfs_ino); flush_dcache_mft_record_page(ctx->ntfs_ino);
mark_mft_record_dirty(ctx->ntfs_ino); mark_mft_record_dirty(ctx->ntfs_ino);
done_unm:
ntfs_attr_put_search_ctx(ctx); ntfs_attr_put_search_ctx(ctx);
unmap_mft_record(base_ni); unmap_mft_record(base_ni);
/* /*
...@@ -2047,7 +2097,7 @@ static int ntfs_commit_nonresident_write(struct page *page, ...@@ -2047,7 +2097,7 @@ static int ntfs_commit_nonresident_write(struct page *page,
* now we know ntfs_prepare_write() would have failed in the write * now we know ntfs_prepare_write() would have failed in the write
* exceeds i_size case, so this will never trigger which is fine. * exceeds i_size case, so this will never trigger which is fine.
*/ */
if (pos > vi->i_size) { if (pos > i_size_read(vi)) {
ntfs_error(vi->i_sb, "Writing beyond the existing file size is " ntfs_error(vi->i_sb, "Writing beyond the existing file size is "
"not supported yet. Sorry."); "not supported yet. Sorry.");
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -2183,9 +2233,13 @@ static int ntfs_commit_write(struct file *file, struct page *page, ...@@ -2183,9 +2233,13 @@ static int ntfs_commit_write(struct file *file, struct page *page,
} }
kunmap_atomic(kaddr, KM_USER0); kunmap_atomic(kaddr, KM_USER0);
/* Update i_size if necessary. */ /* Update i_size if necessary. */
if (vi->i_size < attr_len) { if (i_size_read(vi) < attr_len) {
unsigned long flags;
write_lock_irqsave(&ni->size_lock, flags);
ni->allocated_size = ni->initialized_size = attr_len; ni->allocated_size = ni->initialized_size = attr_len;
i_size_write(vi, attr_len); i_size_write(vi, attr_len);
write_unlock_irqrestore(&ni->size_lock, flags);
} }
/* Mark the mft record dirty, so it gets written back. */ /* Mark the mft record dirty, so it gets written back. */
flush_dcache_mft_record_page(ctx->ntfs_ino); flush_dcache_mft_record_page(ctx->ntfs_ino);
......
/** /**
* attrib.c - NTFS attribute operations. Part of the Linux-NTFS project. * attrib.c - NTFS attribute operations. Part of the Linux-NTFS project.
* *
* Copyright (c) 2001-2004 Anton Altaparmakov * Copyright (c) 2001-2005 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon * Copyright (c) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
...@@ -21,88 +21,217 @@ ...@@ -21,88 +21,217 @@
*/ */
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/swap.h>
#include "attrib.h" #include "attrib.h"
#include "debug.h" #include "debug.h"
#include "layout.h" #include "layout.h"
#include "lcnalloc.h"
#include "malloc.h"
#include "mft.h" #include "mft.h"
#include "ntfs.h" #include "ntfs.h"
#include "types.h" #include "types.h"
/** /**
* ntfs_map_runlist - map (a part of) a runlist of an ntfs inode * ntfs_map_runlist_nolock - map (a part of) a runlist of an ntfs inode
* @ni: ntfs inode for which to map (part of) a runlist * @ni: ntfs inode for which to map (part of) a runlist
* @vcn: map runlist part containing this vcn * @vcn: map runlist part containing this vcn
* *
* Map the part of a runlist containing the @vcn of the ntfs inode @ni. * Map the part of a runlist containing the @vcn of the ntfs inode @ni.
* *
* Return 0 on success and -errno on error. * Return 0 on success and -errno on error. There is one special error code
* which is not an error as such. This is -ENOENT. It means that @vcn is out
* of bounds of the runlist.
* *
* Locking: - The runlist must be unlocked on entry and is unlocked on return. * Locking: - The runlist must be locked for writing.
* - This function takes the lock for writing and modifies the runlist. * - This function modifies the runlist.
*/ */
int ntfs_map_runlist(ntfs_inode *ni, VCN vcn) int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn)
{ {
VCN end_vcn;
ntfs_inode *base_ni; ntfs_inode *base_ni;
MFT_RECORD *m;
ATTR_RECORD *a;
ntfs_attr_search_ctx *ctx; ntfs_attr_search_ctx *ctx;
MFT_RECORD *mrec; runlist_element *rl;
int err = 0; int err = 0;
ntfs_debug("Mapping runlist part containing vcn 0x%llx.", ntfs_debug("Mapping runlist part containing vcn 0x%llx.",
(unsigned long long)vcn); (unsigned long long)vcn);
if (!NInoAttr(ni)) if (!NInoAttr(ni))
base_ni = ni; base_ni = ni;
else else
base_ni = ni->ext.base_ntfs_ino; base_ni = ni->ext.base_ntfs_ino;
m = map_mft_record(base_ni);
mrec = map_mft_record(base_ni); if (IS_ERR(m))
if (IS_ERR(mrec)) return PTR_ERR(m);
return PTR_ERR(mrec); ctx = ntfs_attr_get_search_ctx(base_ni, m);
ctx = ntfs_attr_get_search_ctx(base_ni, mrec);
if (unlikely(!ctx)) { if (unlikely(!ctx)) {
err = -ENOMEM; err = -ENOMEM;
goto err_out; goto err_out;
} }
err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
CASE_SENSITIVE, vcn, NULL, 0, ctx); CASE_SENSITIVE, vcn, NULL, 0, ctx);
if (unlikely(err)) if (unlikely(err)) {
goto put_err_out; if (err == -ENOENT)
err = -EIO;
down_write(&ni->runlist.lock); goto err_out;
/* Make sure someone else didn't do the work while we were sleeping. */ }
if (likely(ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn) <= a = ctx->attr;
LCN_RL_NOT_MAPPED)) { /*
runlist_element *rl; * Only decompress the mapping pairs if @vcn is inside it. Otherwise
* we get into problems when we try to map an out of bounds vcn because
rl = ntfs_mapping_pairs_decompress(ni->vol, ctx->attr, * we then try to map the already mapped runlist fragment and
ni->runlist.rl); * ntfs_mapping_pairs_decompress() fails.
*/
end_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn) + 1;
if (unlikely(!a->data.non_resident.lowest_vcn && end_vcn <= 1))
end_vcn = ni->allocated_size >> ni->vol->cluster_size_bits;
if (unlikely(vcn >= end_vcn)) {
err = -ENOENT;
goto err_out;
}
rl = ntfs_mapping_pairs_decompress(ni->vol, a, ni->runlist.rl);
if (IS_ERR(rl)) if (IS_ERR(rl))
err = PTR_ERR(rl); err = PTR_ERR(rl);
else else
ni->runlist.rl = rl; ni->runlist.rl = rl;
}
up_write(&ni->runlist.lock);
put_err_out:
ntfs_attr_put_search_ctx(ctx);
err_out: err_out:
if (likely(ctx))
ntfs_attr_put_search_ctx(ctx);
unmap_mft_record(base_ni); unmap_mft_record(base_ni);
return err; return err;
} }
/** /**
* ntfs_find_vcn - find a vcn in the runlist described by an ntfs inode * ntfs_map_runlist - map (a part of) a runlist of an ntfs inode
* @ni: ntfs inode for which to map (part of) a runlist
* @vcn: map runlist part containing this vcn
*
* Map the part of a runlist containing the @vcn of the ntfs inode @ni.
*
* Return 0 on success and -errno on error. There is one special error code
* which is not an error as such. This is -ENOENT. It means that @vcn is out
* of bounds of the runlist.
*
* Locking: - The runlist must be unlocked on entry and is unlocked on return.
* - This function takes the runlist lock for writing and modifies the
* runlist.
*/
int ntfs_map_runlist(ntfs_inode *ni, VCN vcn)
{
int err = 0;
down_write(&ni->runlist.lock);
/* Make sure someone else didn't do the work while we were sleeping. */
if (likely(ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn) <=
LCN_RL_NOT_MAPPED))
err = ntfs_map_runlist_nolock(ni, vcn);
up_write(&ni->runlist.lock);
return err;
}
/**
* ntfs_attr_vcn_to_lcn_nolock - convert a vcn into a lcn given an ntfs inode
* @ni: ntfs inode of the attribute whose runlist to search
* @vcn: vcn to convert
* @write_locked: true if the runlist is locked for writing
*
* Find the virtual cluster number @vcn in the runlist of the ntfs attribute
* described by the ntfs inode @ni and return the corresponding logical cluster
* number (lcn).
*
* If the @vcn is not mapped yet, the attempt is made to map the attribute
* extent containing the @vcn and the vcn to lcn conversion is retried.
*
* If @write_locked is true the caller has locked the runlist for writing and
* if false for reading.
*
* Since lcns must be >= 0, we use negative return codes with special meaning:
*
* Return code Meaning / Description
* ==========================================
* LCN_HOLE Hole / not allocated on disk.
* LCN_ENOENT There is no such vcn in the runlist, i.e. @vcn is out of bounds.
* LCN_ENOMEM Not enough memory to map runlist.
* LCN_EIO Critical error (runlist/file is corrupt, i/o error, etc).
*
* Locking: - The runlist must be locked on entry and is left locked on return.
* - If @write_locked is FALSE, i.e. the runlist is locked for reading,
* the lock may be dropped inside the function so you cannot rely on
* the runlist still being the same when this function returns.
*/
LCN ntfs_attr_vcn_to_lcn_nolock(ntfs_inode *ni, const VCN vcn,
const BOOL write_locked)
{
LCN lcn;
BOOL is_retry = FALSE;
ntfs_debug("Entering for i_ino 0x%lx, vcn 0x%llx, %s_locked.",
ni->mft_no, (unsigned long long)vcn,
write_locked ? "write" : "read");
BUG_ON(!ni);
BUG_ON(!NInoNonResident(ni));
BUG_ON(vcn < 0);
retry_remap:
/* Convert vcn to lcn. If that fails map the runlist and retry once. */
lcn = ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn);
if (likely(lcn >= LCN_HOLE)) {
ntfs_debug("Done, lcn 0x%llx.", (long long)lcn);
return lcn;
}
if (lcn != LCN_RL_NOT_MAPPED) {
if (lcn != LCN_ENOENT)
lcn = LCN_EIO;
} else if (!is_retry) {
int err;
if (!write_locked) {
up_read(&ni->runlist.lock);
down_write(&ni->runlist.lock);
if (unlikely(ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn) !=
LCN_RL_NOT_MAPPED)) {
up_write(&ni->runlist.lock);
down_read(&ni->runlist.lock);
goto retry_remap;
}
}
err = ntfs_map_runlist_nolock(ni, vcn);
if (!write_locked) {
up_write(&ni->runlist.lock);
down_read(&ni->runlist.lock);
}
if (likely(!err)) {
is_retry = TRUE;
goto retry_remap;
}
if (err == -ENOENT)
lcn = LCN_ENOENT;
else if (err == -ENOMEM)
lcn = LCN_ENOMEM;
else
lcn = LCN_EIO;
}
if (lcn != LCN_ENOENT)
ntfs_error(ni->vol->sb, "Failed with error code %lli.",
(long long)lcn);
return lcn;
}
/**
* ntfs_attr_find_vcn_nolock - find a vcn in the runlist of an ntfs inode
* @ni: ntfs inode describing the runlist to search * @ni: ntfs inode describing the runlist to search
* @vcn: vcn to find * @vcn: vcn to find
* @need_write: if false, lock for reading and if true, lock for writing * @write_locked: true if the runlist is locked for writing
* *
* Find the virtual cluster number @vcn in the runlist described by the ntfs * Find the virtual cluster number @vcn in the runlist described by the ntfs
* inode @ni and return the address of the runlist element containing the @vcn. * inode @ni and return the address of the runlist element containing the @vcn.
* The runlist is left locked and the caller has to unlock it. If @need_write *
* is true, the runlist is locked for writing and if @need_write is false, the * If the @vcn is not mapped yet, the attempt is made to map the attribute
* runlist is locked for reading. In the error case, the runlist is not left * extent containing the @vcn and the vcn to lcn conversion is retried.
* locked. *
* If @write_locked is true the caller has locked the runlist for writing and
* if false for reading.
* *
* Note you need to distinguish between the lcn of the returned runlist element * Note you need to distinguish between the lcn of the returned runlist element
* being >= 0 and LCN_HOLE. In the later case you have to return zeroes on * being >= 0 and LCN_HOLE. In the later case you have to return zeroes on
...@@ -118,34 +247,29 @@ int ntfs_map_runlist(ntfs_inode *ni, VCN vcn) ...@@ -118,34 +247,29 @@ int ntfs_map_runlist(ntfs_inode *ni, VCN vcn)
* -ENOMEM - Not enough memory to map runlist. * -ENOMEM - Not enough memory to map runlist.
* -EIO - Critical error (runlist/file is corrupt, i/o error, etc). * -EIO - Critical error (runlist/file is corrupt, i/o error, etc).
* *
* Locking: - The runlist must be unlocked on entry. * Locking: - The runlist must be locked on entry and is left locked on return.
* - On failing return, the runlist is unlocked. * - If @write_locked is FALSE, i.e. the runlist is locked for reading,
* - On successful return, the runlist is locked. If @need_write us * the lock may be dropped inside the function so you cannot rely on
* true, it is locked for writing. Otherwise is is locked for * the runlist still being the same when this function returns.
* reading.
*/ */
runlist_element *ntfs_find_vcn(ntfs_inode *ni, const VCN vcn, runlist_element *ntfs_attr_find_vcn_nolock(ntfs_inode *ni, const VCN vcn,
const BOOL need_write) const BOOL write_locked)
{ {
runlist_element *rl; runlist_element *rl;
int err = 0; int err = 0;
BOOL is_retry = FALSE; BOOL is_retry = FALSE;
ntfs_debug("Entering for i_ino 0x%lx, vcn 0x%llx, lock for %sing.", ntfs_debug("Entering for i_ino 0x%lx, vcn 0x%llx, %s_locked.",
ni->mft_no, (unsigned long long)vcn, ni->mft_no, (unsigned long long)vcn,
!need_write ? "read" : "writ"); write_locked ? "write" : "read");
BUG_ON(!ni); BUG_ON(!ni);
BUG_ON(!NInoNonResident(ni)); BUG_ON(!NInoNonResident(ni));
BUG_ON(vcn < 0); BUG_ON(vcn < 0);
lock_retry_remap: retry_remap:
if (!need_write)
down_read(&ni->runlist.lock);
else
down_write(&ni->runlist.lock);
rl = ni->runlist.rl; rl = ni->runlist.rl;
if (likely(rl && vcn >= rl[0].vcn)) { if (likely(rl && vcn >= rl[0].vcn)) {
while (likely(rl->length)) { while (likely(rl->length)) {
if (likely(vcn < rl[1].vcn)) { if (unlikely(vcn < rl[1].vcn)) {
if (likely(rl->lcn >= LCN_HOLE)) { if (likely(rl->lcn >= LCN_HOLE)) {
ntfs_debug("Done."); ntfs_debug("Done.");
return rl; return rl;
...@@ -161,29 +285,40 @@ runlist_element *ntfs_find_vcn(ntfs_inode *ni, const VCN vcn, ...@@ -161,29 +285,40 @@ runlist_element *ntfs_find_vcn(ntfs_inode *ni, const VCN vcn,
err = -EIO; err = -EIO;
} }
} }
if (!need_write)
up_read(&ni->runlist.lock);
else
up_write(&ni->runlist.lock);
if (!err && !is_retry) { if (!err && !is_retry) {
/* /*
* The @vcn is in an unmapped region, map the runlist and * The @vcn is in an unmapped region, map the runlist and
* retry. * retry.
*/ */
err = ntfs_map_runlist(ni, vcn); if (!write_locked) {
up_read(&ni->runlist.lock);
down_write(&ni->runlist.lock);
if (unlikely(ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn) !=
LCN_RL_NOT_MAPPED)) {
up_write(&ni->runlist.lock);
down_read(&ni->runlist.lock);
goto retry_remap;
}
}
err = ntfs_map_runlist_nolock(ni, vcn);
if (!write_locked) {
up_write(&ni->runlist.lock);
down_read(&ni->runlist.lock);
}
if (likely(!err)) { if (likely(!err)) {
is_retry = TRUE; is_retry = TRUE;
goto lock_retry_remap; goto retry_remap;
} }
/* /*
* -EINVAL and -ENOENT coming from a failed mapping attempt are * -EINVAL coming from a failed mapping attempt is equivalent
* equivalent to i/o errors for us as they should not happen in * to i/o error for us as it should not happen in our code
* our code paths. * paths.
*/ */
if (err == -EINVAL || err == -ENOENT) if (err == -EINVAL)
err = -EIO; err = -EIO;
} else if (!err) } else if (!err)
err = -EIO; err = -EIO;
if (err != -ENOENT)
ntfs_error(ni->vol->sb, "Failed with error code %i.", err); ntfs_error(ni->vol->sb, "Failed with error code %i.", err);
return ERR_PTR(err); return ERR_PTR(err);
} }
...@@ -870,15 +1005,14 @@ int ntfs_attr_lookup(const ATTR_TYPE type, const ntfschar *name, ...@@ -870,15 +1005,14 @@ int ntfs_attr_lookup(const ATTR_TYPE type, const ntfschar *name,
static inline void ntfs_attr_init_search_ctx(ntfs_attr_search_ctx *ctx, static inline void ntfs_attr_init_search_ctx(ntfs_attr_search_ctx *ctx,
ntfs_inode *ni, MFT_RECORD *mrec) ntfs_inode *ni, MFT_RECORD *mrec)
{ {
ctx->mrec = mrec; *ctx = (ntfs_attr_search_ctx) {
.mrec = mrec,
/* Sanity checks are performed elsewhere. */ /* Sanity checks are performed elsewhere. */
ctx->attr = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset)); .attr = (ATTR_RECORD*)((u8*)mrec +
ctx->is_first = TRUE; le16_to_cpu(mrec->attrs_offset)),
ctx->ntfs_ino = ni; .is_first = TRUE,
ctx->al_entry = NULL; .ntfs_ino = ni,
ctx->base_ntfs_ino = NULL; };
ctx->base_mrec = NULL;
ctx->base_attr = NULL;
} }
/** /**
...@@ -945,6 +1079,8 @@ void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx) ...@@ -945,6 +1079,8 @@ void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx)
return; return;
} }
#ifdef NTFS_RW
/** /**
* ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file * ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file
* @vol: ntfs volume to which the attribute belongs * @vol: ntfs volume to which the attribute belongs
...@@ -1024,27 +1160,21 @@ int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPE type, ...@@ -1024,27 +1160,21 @@ int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPE type,
* Check whether the attribute of @type on the ntfs volume @vol is allowed to * Check whether the attribute of @type on the ntfs volume @vol is allowed to
* be non-resident. This information is obtained from $AttrDef system file. * be non-resident. This information is obtained from $AttrDef system file.
* *
* Return 0 if the attribute is allowed to be non-resident, -EPERM if not, or * Return 0 if the attribute is allowed to be non-resident, -EPERM if not, and
* -ENOENT if the attribute is not listed in $AttrDef. * -ENOENT if the attribute is not listed in $AttrDef.
*/ */
int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPE type) int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPE type)
{ {
ATTR_DEF *ad; ATTR_DEF *ad;
/*
* $DATA is always allowed to be non-resident even if $AttrDef does not
* specify this in the flags of the $DATA attribute definition record.
*/
if (type == AT_DATA)
return 0;
/* Find the attribute definition record in $AttrDef. */ /* Find the attribute definition record in $AttrDef. */
ad = ntfs_attr_find_in_attrdef(vol, type); ad = ntfs_attr_find_in_attrdef(vol, type);
if (unlikely(!ad)) if (unlikely(!ad))
return -ENOENT; return -ENOENT;
/* Check the flags and return the result. */ /* Check the flags and return the result. */
if (ad->flags & CAN_BE_NON_RESIDENT) if (ad->flags & ATTR_DEF_RESIDENT)
return 0;
return -EPERM; return -EPERM;
return 0;
} }
/** /**
...@@ -1067,9 +1197,9 @@ int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPE type) ...@@ -1067,9 +1197,9 @@ int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPE type)
*/ */
int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPE type) int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPE type)
{ {
if (type != AT_INDEX_ALLOCATION && type != AT_EA) if (type == AT_INDEX_ALLOCATION || type == AT_EA)
return 0;
return -EPERM; return -EPERM;
return 0;
} }
/** /**
...@@ -1116,6 +1246,328 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) ...@@ -1116,6 +1246,328 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size)
return 0; return 0;
} }
/**
* ntfs_attr_make_non_resident - convert a resident to a non-resident attribute
* @ni: ntfs inode describing the attribute to convert
*
* Convert the resident ntfs attribute described by the ntfs inode @ni to a
* non-resident one.
*
* Return 0 on success and -errno on error. The following error return codes
* are defined:
* -EPERM - The attribute is not allowed to be non-resident.
* -ENOMEM - Not enough memory.
* -ENOSPC - Not enough disk space.
* -EINVAL - Attribute not defined on the volume.
* -EIO - I/o error or other error.
* Note that -ENOSPC is also returned in the case that there is not enough
* space in the mft record to do the conversion. This can happen when the mft
* record is already very full. The caller is responsible for trying to make
* space in the mft record and trying again. FIXME: Do we need a separate
* error return code for this kind of -ENOSPC or is it always worth trying
* again in case the attribute may then fit in a resident state so no need to
* make it non-resident at all? Ho-hum... (AIA)
*
* NOTE to self: No changes in the attribute list are required to move from
* a resident to a non-resident attribute.
*
* Locking: - The caller must hold i_sem on the inode.
*/
int ntfs_attr_make_non_resident(ntfs_inode *ni)
{
s64 new_size;
struct inode *vi = VFS_I(ni);
ntfs_volume *vol = ni->vol;
ntfs_inode *base_ni;
MFT_RECORD *m;
ATTR_RECORD *a;
ntfs_attr_search_ctx *ctx;
struct page *page;
runlist_element *rl;
u8 *kaddr;
unsigned long flags;
int mp_size, mp_ofs, name_ofs, arec_size, err, err2;
u32 attr_size;
u8 old_res_attr_flags;
/* Check that the attribute is allowed to be non-resident. */
err = ntfs_attr_can_be_non_resident(vol, ni->type);
if (unlikely(err)) {
if (err == -EPERM)
ntfs_debug("Attribute is not allowed to be "
"non-resident.");
else
ntfs_debug("Attribute not defined on the NTFS "
"volume!");
return err;
}
/*
* The size needs to be aligned to a cluster boundary for allocation
* purposes.
*/
new_size = (i_size_read(vi) + vol->cluster_size - 1) &
~(vol->cluster_size - 1);
if (new_size > 0) {
runlist_element *rl2;
/*
* Will need the page later and since the page lock nests
* outside all ntfs locks, we need to get the page now.
*/
page = find_or_create_page(vi->i_mapping, 0,
mapping_gfp_mask(vi->i_mapping));
if (unlikely(!page))
return -ENOMEM;
/* Start by allocating clusters to hold the attribute value. */
rl = ntfs_cluster_alloc(vol, 0, new_size >>
vol->cluster_size_bits, -1, DATA_ZONE);
if (IS_ERR(rl)) {
err = PTR_ERR(rl);
ntfs_debug("Failed to allocate cluster%s, error code "
"%i.", (new_size >>
vol->cluster_size_bits) > 1 ? "s" : "",
err);
goto page_err_out;
}
/* Change the runlist terminator to LCN_ENOENT. */
rl2 = rl;
while (rl2->length)
rl2++;
BUG_ON(rl2->lcn != LCN_RL_NOT_MAPPED);
rl2->lcn = LCN_ENOENT;
} else {
rl = NULL;
page = NULL;
}
/* Determine the size of the mapping pairs array. */
mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0, -1);
if (unlikely(mp_size < 0)) {
err = mp_size;
ntfs_debug("Failed to get size for mapping pairs array, error "
"code %i.", err);
goto rl_err_out;
}
down_write(&ni->runlist.lock);
if (!NInoAttr(ni))
base_ni = ni;
else
base_ni = ni->ext.base_ntfs_ino;
m = map_mft_record(base_ni);
if (IS_ERR(m)) {
err = PTR_ERR(m);
m = NULL;
ctx = NULL;
goto err_out;
}
ctx = ntfs_attr_get_search_ctx(base_ni, m);
if (unlikely(!ctx)) {
err = -ENOMEM;
goto err_out;
}
err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
CASE_SENSITIVE, 0, NULL, 0, ctx);
if (unlikely(err)) {
if (err == -ENOENT)
err = -EIO;
goto err_out;
}
m = ctx->mrec;
a = ctx->attr;
BUG_ON(NInoNonResident(ni));
BUG_ON(a->non_resident);
/*
* Calculate new offsets for the name and the mapping pairs array.
* We assume the attribute is not compressed or sparse.
*/
name_ofs = (offsetof(ATTR_REC,
data.non_resident.compressed_size) + 7) & ~7;
mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7;
/*
* Determine the size of the resident part of the now non-resident
* attribute record.
*/
arec_size = (mp_ofs + mp_size + 7) & ~7;
/*
* If the page is not uptodate bring it uptodate by copying from the
* attribute value.
*/
attr_size = le32_to_cpu(a->data.resident.value_length);
BUG_ON(attr_size != i_size_read(vi));
if (page && !PageUptodate(page)) {
kaddr = kmap_atomic(page, KM_USER0);
memcpy(kaddr, (u8*)a +
le16_to_cpu(a->data.resident.value_offset),
attr_size);
memset(kaddr + attr_size, 0, PAGE_CACHE_SIZE - attr_size);
kunmap_atomic(kaddr, KM_USER0);
flush_dcache_page(page);
SetPageUptodate(page);
}
/* Backup the attribute flag. */
old_res_attr_flags = a->data.resident.flags;
/* Resize the resident part of the attribute record. */
err = ntfs_attr_record_resize(m, a, arec_size);
if (unlikely(err))
goto err_out;
/*
* Convert the resident part of the attribute record to describe a
* non-resident attribute.
*/
a->non_resident = 1;
/* Move the attribute name if it exists and update the offset. */
if (a->name_length)
memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset),
a->name_length * sizeof(ntfschar));
a->name_offset = cpu_to_le16(name_ofs);
/*
* FIXME: For now just clear all of these as we do not support them
* when writing.
*/
a->flags &= cpu_to_le16(0xffff & ~le16_to_cpu(ATTR_IS_SPARSE |
ATTR_IS_ENCRYPTED | ATTR_COMPRESSION_MASK));
/* Setup the fields specific to non-resident attributes. */
a->data.non_resident.lowest_vcn = 0;
a->data.non_resident.highest_vcn = cpu_to_sle64((new_size - 1) >>
vol->cluster_size_bits);
a->data.non_resident.mapping_pairs_offset = cpu_to_le16(mp_ofs);
a->data.non_resident.compression_unit = 0;
memset(&a->data.non_resident.reserved, 0,
sizeof(a->data.non_resident.reserved));
a->data.non_resident.allocated_size = cpu_to_sle64(new_size);
a->data.non_resident.data_size =
a->data.non_resident.initialized_size =
cpu_to_sle64(attr_size);
/* Generate the mapping pairs array into the attribute record. */
err = ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs,
arec_size - mp_ofs, rl, 0, -1, NULL);
if (unlikely(err)) {
ntfs_debug("Failed to build mapping pairs, error code %i.",
err);
goto undo_err_out;
}
/* Setup the in-memory attribute structure to be non-resident. */
/*
* FIXME: For now just clear all of these as we do not support them
* when writing.
*/
NInoClearSparse(ni);
NInoClearEncrypted(ni);
NInoClearCompressed(ni);
ni->runlist.rl = rl;
write_lock_irqsave(&ni->size_lock, flags);
ni->allocated_size = new_size;
write_unlock_irqrestore(&ni->size_lock, flags);
/*
* This needs to be last since the address space operations ->readpage
* and ->writepage can run concurrently with us as they are not
* serialized on i_sem. Note, we are not allowed to fail once we flip
* this switch, which is another reason to do this last.
*/
NInoSetNonResident(ni);
/* Mark the mft record dirty, so it gets written back. */
flush_dcache_mft_record_page(ctx->ntfs_ino);
mark_mft_record_dirty(ctx->ntfs_ino);
ntfs_attr_put_search_ctx(ctx);
unmap_mft_record(base_ni);
up_write(&ni->runlist.lock);
if (page) {
set_page_dirty(page);
unlock_page(page);
mark_page_accessed(page);
page_cache_release(page);
}
ntfs_debug("Done.");
return 0;
undo_err_out:
/* Convert the attribute back into a resident attribute. */
a->non_resident = 0;
/* Move the attribute name if it exists and update the offset. */
name_ofs = (offsetof(ATTR_RECORD, data.resident.reserved) +
sizeof(a->data.resident.reserved) + 7) & ~7;
if (a->name_length)
memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset),
a->name_length * sizeof(ntfschar));
mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7;
a->name_offset = cpu_to_le16(name_ofs);
arec_size = (mp_ofs + attr_size + 7) & ~7;
/* Resize the resident part of the attribute record. */
err2 = ntfs_attr_record_resize(m, a, arec_size);
if (unlikely(err2)) {
/*
* This cannot happen (well if memory corruption is at work it
* could happen in theory), but deal with it as well as we can.
* If the old size is too small, truncate the attribute,
* otherwise simply give it a larger allocated size.
* FIXME: Should check whether chkdsk complains when the
* allocated size is much bigger than the resident value size.
*/
arec_size = le32_to_cpu(a->length);
if ((mp_ofs + attr_size) > arec_size) {
err2 = attr_size;
attr_size = arec_size - mp_ofs;
ntfs_error(vol->sb, "Failed to undo partial resident "
"to non-resident attribute "
"conversion. Truncating inode 0x%lx, "
"attribute type 0x%x from %i bytes to "
"%i bytes to maintain metadata "
"consistency. THIS MEANS YOU ARE "
"LOSING %i BYTES DATA FROM THIS %s.",
vi->i_ino,
(unsigned)le32_to_cpu(ni->type),
err2, attr_size, err2 - attr_size,
((ni->type == AT_DATA) &&
!ni->name_len) ? "FILE": "ATTRIBUTE");
write_lock_irqsave(&ni->size_lock, flags);
ni->initialized_size = attr_size;
i_size_write(vi, attr_size);
write_unlock_irqrestore(&ni->size_lock, flags);
}
}
/* Setup the fields specific to resident attributes. */
a->data.resident.value_length = cpu_to_le32(attr_size);
a->data.resident.value_offset = cpu_to_le16(mp_ofs);
a->data.resident.flags = old_res_attr_flags;
memset(&a->data.resident.reserved, 0,
sizeof(a->data.resident.reserved));
/* Copy the data from the page back to the attribute value. */
if (page) {
kaddr = kmap_atomic(page, KM_USER0);
memcpy((u8*)a + mp_ofs, kaddr, attr_size);
kunmap_atomic(kaddr, KM_USER0);
}
/* Setup the allocated size in the ntfs inode in case it changed. */
write_lock_irqsave(&ni->size_lock, flags);
ni->allocated_size = arec_size - mp_ofs;
write_unlock_irqrestore(&ni->size_lock, flags);
/* Mark the mft record dirty, so it gets written back. */
flush_dcache_mft_record_page(ctx->ntfs_ino);
mark_mft_record_dirty(ctx->ntfs_ino);
err_out:
if (ctx)
ntfs_attr_put_search_ctx(ctx);
if (m)
unmap_mft_record(base_ni);
ni->runlist.rl = NULL;
up_write(&ni->runlist.lock);
rl_err_out:
if (rl) {
if (ntfs_cluster_free_from_rl(vol, rl) < 0) {
ntfs_error(vol->sb, "Failed to release allocated "
"cluster(s) in error code path. Run "
"chkdsk to recover the lost "
"cluster(s).");
NVolSetErrors(vol);
}
ntfs_free(rl);
page_err_out:
unlock_page(page);
page_cache_release(page);
}
if (err == -EINVAL)
err = -EIO;
return err;
}
/** /**
* ntfs_attr_set - fill (a part of) an attribute with a byte * ntfs_attr_set - fill (a part of) an attribute with a byte
* @ni: ntfs inode describing the attribute to fill * @ni: ntfs inode describing the attribute to fill
...@@ -1127,6 +1579,10 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) ...@@ -1127,6 +1579,10 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size)
* byte offset @ofs inside the attribute with the constant byte @val. * byte offset @ofs inside the attribute with the constant byte @val.
* *
* This function is effectively like memset() applied to an ntfs attribute. * This function is effectively like memset() applied to an ntfs attribute.
* Note thie function actually only operates on the page cache pages belonging
* to the ntfs attribute and it marks them dirty after doing the memset().
* Thus it relies on the vm dirty page write code paths to cause the modified
* pages to be written to the mft record/disk.
* *
* Return 0 on success and -errno on error. An error code of -ESPIPE means * Return 0 on success and -errno on error. An error code of -ESPIPE means
* that @ofs + @cnt were outside the end of the attribute and no write was * that @ofs + @cnt were outside the end of the attribute and no write was
...@@ -1155,7 +1611,7 @@ int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, const u8 val) ...@@ -1155,7 +1611,7 @@ int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, const u8 val)
end = ofs + cnt; end = ofs + cnt;
end_ofs = end & ~PAGE_CACHE_MASK; end_ofs = end & ~PAGE_CACHE_MASK;
/* If the end is outside the inode size return -ESPIPE. */ /* If the end is outside the inode size return -ESPIPE. */
if (unlikely(end > VFS_I(ni)->i_size)) { if (unlikely(end > i_size_read(VFS_I(ni)))) {
ntfs_error(vol->sb, "Request exceeds end of attribute."); ntfs_error(vol->sb, "Request exceeds end of attribute.");
return -ESPIPE; return -ESPIPE;
} }
...@@ -1256,3 +1712,5 @@ int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, const u8 val) ...@@ -1256,3 +1712,5 @@ int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, const u8 val)
ntfs_debug("Done."); ntfs_debug("Done.");
return 0; return 0;
} }
#endif /* NTFS_RW */
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* attrib.h - Defines for attribute handling in NTFS Linux kernel driver. * attrib.h - Defines for attribute handling in NTFS Linux kernel driver.
* Part of the Linux-NTFS project. * Part of the Linux-NTFS project.
* *
* Copyright (c) 2001-2004 Anton Altaparmakov * Copyright (c) 2001-2005 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon * Copyright (c) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
...@@ -60,10 +60,14 @@ typedef struct { ...@@ -60,10 +60,14 @@ typedef struct {
ATTR_RECORD *base_attr; ATTR_RECORD *base_attr;
} ntfs_attr_search_ctx; } ntfs_attr_search_ctx;
extern int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn);
extern int ntfs_map_runlist(ntfs_inode *ni, VCN vcn); extern int ntfs_map_runlist(ntfs_inode *ni, VCN vcn);
extern runlist_element *ntfs_find_vcn(ntfs_inode *ni, const VCN vcn, extern LCN ntfs_attr_vcn_to_lcn_nolock(ntfs_inode *ni, const VCN vcn,
const BOOL need_write); const BOOL write_locked);
extern runlist_element *ntfs_attr_find_vcn_nolock(ntfs_inode *ni,
const VCN vcn, const BOOL write_locked);
int ntfs_attr_lookup(const ATTR_TYPE type, const ntfschar *name, int ntfs_attr_lookup(const ATTR_TYPE type, const ntfschar *name,
const u32 name_len, const IGNORE_CASE_BOOL ic, const u32 name_len, const IGNORE_CASE_BOOL ic,
...@@ -85,6 +89,8 @@ extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, ...@@ -85,6 +89,8 @@ extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni,
MFT_RECORD *mrec); MFT_RECORD *mrec);
extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx); extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx);
#ifdef NTFS_RW
extern int ntfs_attr_size_bounds_check(const ntfs_volume *vol, extern int ntfs_attr_size_bounds_check(const ntfs_volume *vol,
const ATTR_TYPE type, const s64 size); const ATTR_TYPE type, const s64 size);
extern int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, extern int ntfs_attr_can_be_non_resident(const ntfs_volume *vol,
...@@ -94,7 +100,11 @@ extern int ntfs_attr_can_be_resident(const ntfs_volume *vol, ...@@ -94,7 +100,11 @@ extern int ntfs_attr_can_be_resident(const ntfs_volume *vol,
extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size); extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size);
extern int ntfs_attr_make_non_resident(ntfs_inode *ni);
extern int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, extern int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt,
const u8 val); const u8 val);
#endif /* NTFS_RW */
#endif /* _LINUX_NTFS_ATTRIB_H */ #endif /* _LINUX_NTFS_ATTRIB_H */
...@@ -96,13 +96,14 @@ void free_compression_buffers(void) ...@@ -96,13 +96,14 @@ void free_compression_buffers(void)
/** /**
* zero_partial_compressed_page - zero out of bounds compressed page region * zero_partial_compressed_page - zero out of bounds compressed page region
*/ */
static void zero_partial_compressed_page(ntfs_inode *ni, struct page *page) static void zero_partial_compressed_page(struct page *page,
const s64 initialized_size)
{ {
u8 *kp = page_address(page); u8 *kp = page_address(page);
unsigned int kp_ofs; unsigned int kp_ofs;
ntfs_debug("Zeroing page region outside initialized size."); ntfs_debug("Zeroing page region outside initialized size.");
if (((s64)page->index << PAGE_CACHE_SHIFT) >= ni->initialized_size) { if (((s64)page->index << PAGE_CACHE_SHIFT) >= initialized_size) {
/* /*
* FIXME: Using clear_page() will become wrong when we get * FIXME: Using clear_page() will become wrong when we get
* PAGE_CACHE_SIZE != PAGE_SIZE but for now there is no problem. * PAGE_CACHE_SIZE != PAGE_SIZE but for now there is no problem.
...@@ -110,7 +111,7 @@ static void zero_partial_compressed_page(ntfs_inode *ni, struct page *page) ...@@ -110,7 +111,7 @@ static void zero_partial_compressed_page(ntfs_inode *ni, struct page *page)
clear_page(kp); clear_page(kp);
return; return;
} }
kp_ofs = ni->initialized_size & ~PAGE_CACHE_MASK; kp_ofs = initialized_size & ~PAGE_CACHE_MASK;
memset(kp + kp_ofs, 0, PAGE_CACHE_SIZE - kp_ofs); memset(kp + kp_ofs, 0, PAGE_CACHE_SIZE - kp_ofs);
return; return;
} }
...@@ -118,12 +119,12 @@ static void zero_partial_compressed_page(ntfs_inode *ni, struct page *page) ...@@ -118,12 +119,12 @@ static void zero_partial_compressed_page(ntfs_inode *ni, struct page *page)
/** /**
* handle_bounds_compressed_page - test for&handle out of bounds compressed page * handle_bounds_compressed_page - test for&handle out of bounds compressed page
*/ */
static inline void handle_bounds_compressed_page(ntfs_inode *ni, static inline void handle_bounds_compressed_page(struct page *page,
struct page *page) const loff_t i_size, const s64 initialized_size)
{ {
if ((page->index >= (ni->initialized_size >> PAGE_CACHE_SHIFT)) && if ((page->index >= (initialized_size >> PAGE_CACHE_SHIFT)) &&
(ni->initialized_size < VFS_I(ni)->i_size)) (initialized_size < i_size))
zero_partial_compressed_page(ni, page); zero_partial_compressed_page(page, initialized_size);
return; return;
} }
...@@ -138,6 +139,8 @@ static inline void handle_bounds_compressed_page(ntfs_inode *ni, ...@@ -138,6 +139,8 @@ static inline void handle_bounds_compressed_page(ntfs_inode *ni,
* @xpage_done: set to 1 if xpage was completed successfully (IN/OUT) * @xpage_done: set to 1 if xpage was completed successfully (IN/OUT)
* @cb_start: compression block to decompress (IN) * @cb_start: compression block to decompress (IN)
* @cb_size: size of compression block @cb_start in bytes (IN) * @cb_size: size of compression block @cb_start in bytes (IN)
* @i_size: file size when we started the read (IN)
* @initialized_size: initialized file size when we started the read (IN)
* *
* The caller must have disabled preemption. ntfs_decompress() reenables it when * The caller must have disabled preemption. ntfs_decompress() reenables it when
* the critical section is finished. * the critical section is finished.
...@@ -165,7 +168,8 @@ static inline void handle_bounds_compressed_page(ntfs_inode *ni, ...@@ -165,7 +168,8 @@ static inline void handle_bounds_compressed_page(ntfs_inode *ni,
static int ntfs_decompress(struct page *dest_pages[], int *dest_index, static int ntfs_decompress(struct page *dest_pages[], int *dest_index,
int *dest_ofs, const int dest_max_index, const int dest_max_ofs, int *dest_ofs, const int dest_max_index, const int dest_max_ofs,
const int xpage, char *xpage_done, u8 *const cb_start, const int xpage, char *xpage_done, u8 *const cb_start,
const u32 cb_size) const u32 cb_size, const loff_t i_size,
const s64 initialized_size)
{ {
/* /*
* Pointers into the compressed data, i.e. the compression block (cb), * Pointers into the compressed data, i.e. the compression block (cb),
...@@ -219,9 +223,6 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index, ...@@ -219,9 +223,6 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index,
spin_unlock(&ntfs_cb_lock); spin_unlock(&ntfs_cb_lock);
/* Second stage: finalize completed pages. */ /* Second stage: finalize completed pages. */
if (nr_completed_pages > 0) { if (nr_completed_pages > 0) {
struct page *page = dest_pages[completed_pages[0]];
ntfs_inode *ni = NTFS_I(page->mapping->host);
for (i = 0; i < nr_completed_pages; i++) { for (i = 0; i < nr_completed_pages; i++) {
int di = completed_pages[i]; int di = completed_pages[i];
...@@ -230,7 +231,8 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index, ...@@ -230,7 +231,8 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index,
* If we are outside the initialized size, zero * If we are outside the initialized size, zero
* the out of bounds page range. * the out of bounds page range.
*/ */
handle_bounds_compressed_page(ni, dp); handle_bounds_compressed_page(dp, i_size,
initialized_size);
flush_dcache_page(dp); flush_dcache_page(dp);
kunmap(dp); kunmap(dp);
SetPageUptodate(dp); SetPageUptodate(dp);
...@@ -478,12 +480,14 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index, ...@@ -478,12 +480,14 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index,
*/ */
int ntfs_read_compressed_block(struct page *page) int ntfs_read_compressed_block(struct page *page)
{ {
loff_t i_size;
s64 initialized_size;
struct address_space *mapping = page->mapping; struct address_space *mapping = page->mapping;
ntfs_inode *ni = NTFS_I(mapping->host); ntfs_inode *ni = NTFS_I(mapping->host);
ntfs_volume *vol = ni->vol; ntfs_volume *vol = ni->vol;
struct super_block *sb = vol->sb; struct super_block *sb = vol->sb;
runlist_element *rl; runlist_element *rl;
unsigned long block_size = sb->s_blocksize; unsigned long flags, block_size = sb->s_blocksize;
unsigned char block_size_bits = sb->s_blocksize_bits; unsigned char block_size_bits = sb->s_blocksize_bits;
u8 *cb, *cb_pos, *cb_end; u8 *cb, *cb_pos, *cb_end;
struct buffer_head **bhs; struct buffer_head **bhs;
...@@ -552,8 +556,12 @@ int ntfs_read_compressed_block(struct page *page) ...@@ -552,8 +556,12 @@ int ntfs_read_compressed_block(struct page *page)
* The remaining pages need to be allocated and inserted into the page * The remaining pages need to be allocated and inserted into the page
* cache, alignment guarantees keep all the below much simpler. (-8 * cache, alignment guarantees keep all the below much simpler. (-8
*/ */
max_page = ((VFS_I(ni)->i_size + PAGE_CACHE_SIZE - 1) >> read_lock_irqsave(&ni->size_lock, flags);
PAGE_CACHE_SHIFT) - offset; i_size = i_size_read(VFS_I(ni));
initialized_size = ni->initialized_size;
read_unlock_irqrestore(&ni->size_lock, flags);
max_page = ((i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT) -
offset;
if (nr_pages < max_page) if (nr_pages < max_page)
max_page = nr_pages; max_page = nr_pages;
for (i = 0; i < max_page; i++, offset++) { for (i = 0; i < max_page; i++, offset++) {
...@@ -824,7 +832,8 @@ int ntfs_read_compressed_block(struct page *page) ...@@ -824,7 +832,8 @@ int ntfs_read_compressed_block(struct page *page)
* If we are outside the initialized size, zero * If we are outside the initialized size, zero
* the out of bounds page range. * the out of bounds page range.
*/ */
handle_bounds_compressed_page(ni, page); handle_bounds_compressed_page(page, i_size,
initialized_size);
flush_dcache_page(page); flush_dcache_page(page);
kunmap(page); kunmap(page);
SetPageUptodate(page); SetPageUptodate(page);
...@@ -847,7 +856,8 @@ int ntfs_read_compressed_block(struct page *page) ...@@ -847,7 +856,8 @@ int ntfs_read_compressed_block(struct page *page)
ntfs_debug("Found compressed compression block."); ntfs_debug("Found compressed compression block.");
err = ntfs_decompress(pages, &cur_page, &cur_ofs, err = ntfs_decompress(pages, &cur_page, &cur_ofs,
cb_max_page, cb_max_ofs, xpage, &xpage_done, cb_max_page, cb_max_ofs, xpage, &xpage_done,
cb_pos, cb_size - (cb_pos - cb)); cb_pos, cb_size - (cb_pos - cb), i_size,
initialized_size);
/* /*
* We can sleep from now on, lock already dropped by * We can sleep from now on, lock already dropped by
* ntfs_decompress(). * ntfs_decompress().
......
...@@ -164,14 +164,17 @@ void ntfs_debug_dump_runlist(const runlist_element *rl) ...@@ -164,14 +164,17 @@ void ntfs_debug_dump_runlist(const runlist_element *rl)
if (index > -LCN_ENOENT - 1) if (index > -LCN_ENOENT - 1)
index = 3; index = 3;
printk(KERN_DEBUG "%-16Lx %s %-16Lx%s\n", printk(KERN_DEBUG "%-16Lx %s %-16Lx%s\n",
(rl + i)->vcn, lcn_str[index], (long long)(rl + i)->vcn, lcn_str[index],
(rl + i)->length, (rl + i)->length ? (long long)(rl + i)->length,
"" : " (runlist end)"); (rl + i)->length ? "" :
" (runlist end)");
} else } else
printk(KERN_DEBUG "%-16Lx %-16Lx %-16Lx%s\n", printk(KERN_DEBUG "%-16Lx %-16Lx %-16Lx%s\n",
(rl + i)->vcn, (rl + i)->lcn, (long long)(rl + i)->vcn,
(rl + i)->length, (rl + i)->length ? (long long)(rl + i)->lcn,
"" : " (runlist end)"); (long long)(rl + i)->length,
(rl + i)->length ? "" :
" (runlist end)");
if (!(rl + i)->length) if (!(rl + i)->length)
break; break;
} }
......
/** /**
* dir.c - NTFS kernel directory operations. Part of the Linux-NTFS project. * dir.c - NTFS kernel directory operations. Part of the Linux-NTFS project.
* *
* Copyright (c) 2001-2004 Anton Altaparmakov * Copyright (c) 2001-2005 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon * Copyright (c) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
...@@ -183,7 +183,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -183,7 +183,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
name->len = 0; name->len = 0;
*res = name; *res = name;
} else { } else {
if (name)
kfree(name); kfree(name);
*res = NULL; *res = NULL;
} }
...@@ -444,7 +443,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -444,7 +443,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
name->len = 0; name->len = 0;
*res = name; *res = name;
} else { } else {
if (name)
kfree(name); kfree(name);
*res = NULL; *res = NULL;
} }
...@@ -610,7 +608,7 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -610,7 +608,7 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
// TODO: (AIA) // TODO: (AIA)
// The algorithm embedded in this code will be required for the time when we // The algorithm embedded in this code will be required for the time when we
// want to support adding of entries to directories, where we require correct // want to support adding of entries to directories, where we require correct
// collation of file names in order not to cause corruption of the file system. // collation of file names in order not to cause corruption of the filesystem.
/** /**
* ntfs_lookup_inode_by_name - find an inode in a directory given its name * ntfs_lookup_inode_by_name - find an inode in a directory given its name
...@@ -1101,7 +1099,7 @@ static inline int ntfs_filldir(ntfs_volume *vol, loff_t fpos, ...@@ -1101,7 +1099,7 @@ static inline int ntfs_filldir(ntfs_volume *vol, loff_t fpos,
static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir) static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{ {
s64 ia_pos, ia_start, prev_ia_pos, bmp_pos; s64 ia_pos, ia_start, prev_ia_pos, bmp_pos;
loff_t fpos; loff_t fpos, i_size;
struct inode *bmp_vi, *vdir = filp->f_dentry->d_inode; struct inode *bmp_vi, *vdir = filp->f_dentry->d_inode;
struct super_block *sb = vdir->i_sb; struct super_block *sb = vdir->i_sb;
ntfs_inode *ndir = NTFS_I(vdir); ntfs_inode *ndir = NTFS_I(vdir);
...@@ -1122,7 +1120,8 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -1122,7 +1120,8 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
vdir->i_ino, fpos); vdir->i_ino, fpos);
rc = err = 0; rc = err = 0;
/* Are we at end of dir yet? */ /* Are we at end of dir yet? */
if (fpos >= vdir->i_size + vol->mft_record_size) i_size = i_size_read(vdir);
if (fpos >= i_size + vol->mft_record_size)
goto done; goto done;
/* Emulate . and .. for all directories. */ /* Emulate . and .. for all directories. */
if (!fpos) { if (!fpos) {
...@@ -1264,7 +1263,7 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -1264,7 +1263,7 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
bmp_mapping = bmp_vi->i_mapping; bmp_mapping = bmp_vi->i_mapping;
/* Get the starting bitmap bit position and sanity check it. */ /* Get the starting bitmap bit position and sanity check it. */
bmp_pos = ia_pos >> ndir->itype.index.block_size_bits; bmp_pos = ia_pos >> ndir->itype.index.block_size_bits;
if (unlikely(bmp_pos >> 3 >= bmp_vi->i_size)) { if (unlikely(bmp_pos >> 3 >= i_size_read(bmp_vi))) {
ntfs_error(sb, "Current index allocation position exceeds " ntfs_error(sb, "Current index allocation position exceeds "
"index bitmap size."); "index bitmap size.");
goto err_out; goto err_out;
...@@ -1301,7 +1300,7 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -1301,7 +1300,7 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
goto get_next_bmp_page; goto get_next_bmp_page;
} }
/* If we have reached the end of the bitmap, we are done. */ /* If we have reached the end of the bitmap, we are done. */
if (unlikely(((bmp_pos + cur_bmp_pos) >> 3) >= vdir->i_size)) if (unlikely(((bmp_pos + cur_bmp_pos) >> 3) >= i_size))
goto unm_EOD; goto unm_EOD;
ia_pos = (bmp_pos + cur_bmp_pos) << ia_pos = (bmp_pos + cur_bmp_pos) <<
ndir->itype.index.block_size_bits; ndir->itype.index.block_size_bits;
...@@ -1309,7 +1308,8 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -1309,7 +1308,8 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
ntfs_debug("Handling index buffer 0x%llx.", ntfs_debug("Handling index buffer 0x%llx.",
(unsigned long long)bmp_pos + cur_bmp_pos); (unsigned long long)bmp_pos + cur_bmp_pos);
/* If the current index buffer is in the same page we reuse the page. */ /* 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)) { if ((prev_ia_pos & (s64)PAGE_CACHE_MASK) !=
(ia_pos & (s64)PAGE_CACHE_MASK)) {
prev_ia_pos = ia_pos; prev_ia_pos = ia_pos;
if (likely(ia_page != NULL)) { if (likely(ia_page != NULL)) {
unlock_page(ia_page); unlock_page(ia_page);
...@@ -1441,7 +1441,7 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -1441,7 +1441,7 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
ntfs_unmap_page(bmp_page); ntfs_unmap_page(bmp_page);
EOD: EOD:
/* We are finished, set fpos to EOD. */ /* We are finished, set fpos to EOD. */
fpos = vdir->i_size + vol->mft_record_size; fpos = i_size + vol->mft_record_size;
abort: abort:
kfree(name); kfree(name);
done: done:
...@@ -1461,9 +1461,7 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -1461,9 +1461,7 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
unlock_page(ia_page); unlock_page(ia_page);
ntfs_unmap_page(ia_page); ntfs_unmap_page(ia_page);
} }
if (ir)
kfree(ir); kfree(ir);
if (name)
kfree(name); kfree(name);
if (ctx) if (ctx)
ntfs_attr_put_search_ctx(ctx); ntfs_attr_put_search_ctx(ctx);
...@@ -1495,7 +1493,7 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -1495,7 +1493,7 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
static int ntfs_dir_open(struct inode *vi, struct file *filp) static int ntfs_dir_open(struct inode *vi, struct file *filp)
{ {
if (sizeof(unsigned long) < 8) { if (sizeof(unsigned long) < 8) {
if (vi->i_size > MAX_LFS_FILESIZE) if (i_size_read(vi) > MAX_LFS_FILESIZE)
return -EFBIG; return -EFBIG;
} }
return 0; return 0;
......
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
static int ntfs_file_open(struct inode *vi, struct file *filp) static int ntfs_file_open(struct inode *vi, struct file *filp)
{ {
if (sizeof(unsigned long) < 8) { if (sizeof(unsigned long) < 8) {
if (vi->i_size > MAX_LFS_FILESIZE) if (i_size_read(vi) > MAX_LFS_FILESIZE)
return -EFBIG; return -EFBIG;
} }
return generic_file_open(vi, filp); return generic_file_open(vi, filp);
......
/* /*
* index.c - NTFS kernel index handling. Part of the Linux-NTFS project. * index.c - NTFS kernel index handling. Part of the Linux-NTFS project.
* *
* Copyright (c) 2004 Anton Altaparmakov * Copyright (c) 2004-2005 Anton Altaparmakov
* *
* This program/include file is free software; you can redistribute it and/or * 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 * modify it under the terms of the GNU General Public License as published
...@@ -39,18 +39,8 @@ ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *idx_ni) ...@@ -39,18 +39,8 @@ ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *idx_ni)
ntfs_index_context *ictx; ntfs_index_context *ictx;
ictx = kmem_cache_alloc(ntfs_index_ctx_cache, SLAB_NOFS); ictx = kmem_cache_alloc(ntfs_index_ctx_cache, SLAB_NOFS);
if (ictx) { if (ictx)
ictx->idx_ni = idx_ni; *ictx = (ntfs_index_context){ .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; return ictx;
} }
......
/** /**
* inode.c - NTFS kernel inode handling. Part of the Linux-NTFS project. * inode.c - NTFS kernel inode handling. Part of the Linux-NTFS project.
* *
* Copyright (c) 2001-2004 Anton Altaparmakov * Copyright (c) 2001-2005 Anton Altaparmakov
* *
* This program/include file is free software; you can redistribute it and/or * 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 * modify it under the terms of the GNU General Public License as published
...@@ -174,7 +174,7 @@ struct inode *ntfs_iget(struct super_block *sb, unsigned long mft_no) ...@@ -174,7 +174,7 @@ struct inode *ntfs_iget(struct super_block *sb, unsigned long mft_no)
vi = iget5_locked(sb, mft_no, (test_t)ntfs_test_inode, vi = iget5_locked(sb, mft_no, (test_t)ntfs_test_inode,
(set_t)ntfs_init_locked_inode, &na); (set_t)ntfs_init_locked_inode, &na);
if (!vi) if (unlikely(!vi))
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
err = 0; err = 0;
...@@ -188,7 +188,7 @@ struct inode *ntfs_iget(struct super_block *sb, unsigned long mft_no) ...@@ -188,7 +188,7 @@ struct inode *ntfs_iget(struct super_block *sb, unsigned long mft_no)
* There is no point in keeping bad inodes around if the failure was * There is no point in keeping bad inodes around if the failure was
* due to ENOMEM. We want to be able to retry again later. * due to ENOMEM. We want to be able to retry again later.
*/ */
if (err == -ENOMEM) { if (unlikely(err == -ENOMEM)) {
iput(vi); iput(vi);
vi = ERR_PTR(err); vi = ERR_PTR(err);
} }
...@@ -235,7 +235,7 @@ struct inode *ntfs_attr_iget(struct inode *base_vi, ATTR_TYPE type, ...@@ -235,7 +235,7 @@ struct inode *ntfs_attr_iget(struct inode *base_vi, ATTR_TYPE type,
vi = iget5_locked(base_vi->i_sb, na.mft_no, (test_t)ntfs_test_inode, vi = iget5_locked(base_vi->i_sb, na.mft_no, (test_t)ntfs_test_inode,
(set_t)ntfs_init_locked_inode, &na); (set_t)ntfs_init_locked_inode, &na);
if (!vi) if (unlikely(!vi))
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
err = 0; err = 0;
...@@ -250,7 +250,7 @@ struct inode *ntfs_attr_iget(struct inode *base_vi, ATTR_TYPE type, ...@@ -250,7 +250,7 @@ struct inode *ntfs_attr_iget(struct inode *base_vi, ATTR_TYPE type,
* simplifies things in that we never need to check for bad attribute * simplifies things in that we never need to check for bad attribute
* inodes elsewhere. * inodes elsewhere.
*/ */
if (err) { if (unlikely(err)) {
iput(vi); iput(vi);
vi = ERR_PTR(err); vi = ERR_PTR(err);
} }
...@@ -290,7 +290,7 @@ struct inode *ntfs_index_iget(struct inode *base_vi, ntfschar *name, ...@@ -290,7 +290,7 @@ struct inode *ntfs_index_iget(struct inode *base_vi, ntfschar *name,
vi = iget5_locked(base_vi->i_sb, na.mft_no, (test_t)ntfs_test_inode, vi = iget5_locked(base_vi->i_sb, na.mft_no, (test_t)ntfs_test_inode,
(set_t)ntfs_init_locked_inode, &na); (set_t)ntfs_init_locked_inode, &na);
if (!vi) if (unlikely(!vi))
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
err = 0; err = 0;
...@@ -305,7 +305,7 @@ struct inode *ntfs_index_iget(struct inode *base_vi, ntfschar *name, ...@@ -305,7 +305,7 @@ struct inode *ntfs_index_iget(struct inode *base_vi, ntfschar *name,
* simplifies things in that we never need to check for bad index * simplifies things in that we never need to check for bad index
* inodes elsewhere. * inodes elsewhere.
*/ */
if (err) { if (unlikely(err)) {
iput(vi); iput(vi);
vi = ERR_PTR(err); vi = ERR_PTR(err);
} }
...@@ -317,8 +317,7 @@ struct inode *ntfs_alloc_big_inode(struct super_block *sb) ...@@ -317,8 +317,7 @@ struct inode *ntfs_alloc_big_inode(struct super_block *sb)
ntfs_inode *ni; ntfs_inode *ni;
ntfs_debug("Entering."); ntfs_debug("Entering.");
ni = (ntfs_inode *)kmem_cache_alloc(ntfs_big_inode_cache, ni = kmem_cache_alloc(ntfs_big_inode_cache, SLAB_NOFS);
SLAB_NOFS);
if (likely(ni != NULL)) { if (likely(ni != NULL)) {
ni->state = 0; ni->state = 0;
return VFS_I(ni); return VFS_I(ni);
...@@ -343,7 +342,7 @@ static inline ntfs_inode *ntfs_alloc_extent_inode(void) ...@@ -343,7 +342,7 @@ static inline ntfs_inode *ntfs_alloc_extent_inode(void)
ntfs_inode *ni; ntfs_inode *ni;
ntfs_debug("Entering."); ntfs_debug("Entering.");
ni = (ntfs_inode *)kmem_cache_alloc(ntfs_inode_cache, SLAB_NOFS); ni = kmem_cache_alloc(ntfs_inode_cache, SLAB_NOFS);
if (likely(ni != NULL)) { if (likely(ni != NULL)) {
ni->state = 0; ni->state = 0;
return ni; return ni;
...@@ -376,6 +375,7 @@ static void ntfs_destroy_extent_inode(ntfs_inode *ni) ...@@ -376,6 +375,7 @@ static void ntfs_destroy_extent_inode(ntfs_inode *ni)
void __ntfs_init_inode(struct super_block *sb, ntfs_inode *ni) void __ntfs_init_inode(struct super_block *sb, ntfs_inode *ni)
{ {
ntfs_debug("Entering."); ntfs_debug("Entering.");
rwlock_init(&ni->size_lock);
ni->initialized_size = ni->allocated_size = 0; ni->initialized_size = ni->allocated_size = 0;
ni->seq_no = 0; ni->seq_no = 0;
atomic_set(&ni->count, 1); atomic_set(&ni->count, 1);
...@@ -524,6 +524,7 @@ static int ntfs_read_locked_inode(struct inode *vi) ...@@ -524,6 +524,7 @@ static int ntfs_read_locked_inode(struct inode *vi)
ntfs_volume *vol = NTFS_SB(vi->i_sb); ntfs_volume *vol = NTFS_SB(vi->i_sb);
ntfs_inode *ni; ntfs_inode *ni;
MFT_RECORD *m; MFT_RECORD *m;
ATTR_RECORD *a;
STANDARD_INFORMATION *si; STANDARD_INFORMATION *si;
ntfs_attr_search_ctx *ctx; ntfs_attr_search_ctx *ctx;
int err = 0; int err = 0;
...@@ -632,9 +633,10 @@ static int ntfs_read_locked_inode(struct inode *vi) ...@@ -632,9 +633,10 @@ static int ntfs_read_locked_inode(struct inode *vi)
} }
goto unm_err_out; goto unm_err_out;
} }
a = ctx->attr;
/* Get the standard information attribute value. */ /* Get the standard information attribute value. */
si = (STANDARD_INFORMATION*)((char*)ctx->attr + si = (STANDARD_INFORMATION*)((u8*)a +
le16_to_cpu(ctx->attr->data.resident.value_offset)); le16_to_cpu(a->data.resident.value_offset));
/* Transfer information from the standard information into vi. */ /* Transfer information from the standard information into vi. */
/* /*
...@@ -673,15 +675,16 @@ static int ntfs_read_locked_inode(struct inode *vi) ...@@ -673,15 +675,16 @@ static int ntfs_read_locked_inode(struct inode *vi)
goto skip_attr_list_load; goto skip_attr_list_load;
ntfs_debug("Attribute list found in inode 0x%lx.", vi->i_ino); ntfs_debug("Attribute list found in inode 0x%lx.", vi->i_ino);
NInoSetAttrList(ni); NInoSetAttrList(ni);
if (ctx->attr->flags & ATTR_IS_ENCRYPTED || a = ctx->attr;
ctx->attr->flags & ATTR_COMPRESSION_MASK || if (a->flags & ATTR_IS_ENCRYPTED ||
ctx->attr->flags & ATTR_IS_SPARSE) { a->flags & ATTR_COMPRESSION_MASK ||
a->flags & ATTR_IS_SPARSE) {
ntfs_error(vi->i_sb, "Attribute list attribute is " ntfs_error(vi->i_sb, "Attribute list attribute is "
"compressed/encrypted/sparse."); "compressed/encrypted/sparse.");
goto unm_err_out; goto unm_err_out;
} }
/* Now allocate memory for the attribute list. */ /* Now allocate memory for the attribute list. */
ni->attr_list_size = (u32)ntfs_attr_size(ctx->attr); ni->attr_list_size = (u32)ntfs_attr_size(a);
ni->attr_list = ntfs_malloc_nofs(ni->attr_list_size); ni->attr_list = ntfs_malloc_nofs(ni->attr_list_size);
if (!ni->attr_list) { if (!ni->attr_list) {
ntfs_error(vi->i_sb, "Not enough memory to allocate " ntfs_error(vi->i_sb, "Not enough memory to allocate "
...@@ -689,9 +692,9 @@ static int ntfs_read_locked_inode(struct inode *vi) ...@@ -689,9 +692,9 @@ static int ntfs_read_locked_inode(struct inode *vi)
err = -ENOMEM; err = -ENOMEM;
goto unm_err_out; goto unm_err_out;
} }
if (ctx->attr->non_resident) { if (a->non_resident) {
NInoSetAttrListNonResident(ni); NInoSetAttrListNonResident(ni);
if (ctx->attr->data.non_resident.lowest_vcn) { if (a->data.non_resident.lowest_vcn) {
ntfs_error(vi->i_sb, "Attribute list has non " ntfs_error(vi->i_sb, "Attribute list has non "
"zero lowest_vcn."); "zero lowest_vcn.");
goto unm_err_out; goto unm_err_out;
...@@ -701,7 +704,7 @@ static int ntfs_read_locked_inode(struct inode *vi) ...@@ -701,7 +704,7 @@ static int ntfs_read_locked_inode(struct inode *vi)
* exclusive access to the inode at this time. * exclusive access to the inode at this time.
*/ */
ni->attr_list_rl.rl = ntfs_mapping_pairs_decompress(vol, ni->attr_list_rl.rl = ntfs_mapping_pairs_decompress(vol,
ctx->attr, NULL); a, NULL);
if (IS_ERR(ni->attr_list_rl.rl)) { if (IS_ERR(ni->attr_list_rl.rl)) {
err = PTR_ERR(ni->attr_list_rl.rl); err = PTR_ERR(ni->attr_list_rl.rl);
ni->attr_list_rl.rl = NULL; ni->attr_list_rl.rl = NULL;
...@@ -712,27 +715,26 @@ static int ntfs_read_locked_inode(struct inode *vi) ...@@ -712,27 +715,26 @@ static int ntfs_read_locked_inode(struct inode *vi)
/* Now load the attribute list. */ /* Now load the attribute list. */
if ((err = load_attribute_list(vol, &ni->attr_list_rl, if ((err = load_attribute_list(vol, &ni->attr_list_rl,
ni->attr_list, ni->attr_list_size, ni->attr_list, ni->attr_list_size,
sle64_to_cpu(ctx->attr->data. sle64_to_cpu(a->data.non_resident.
non_resident.initialized_size)))) { initialized_size)))) {
ntfs_error(vi->i_sb, "Failed to load " ntfs_error(vi->i_sb, "Failed to load "
"attribute list attribute."); "attribute list attribute.");
goto unm_err_out; goto unm_err_out;
} }
} else /* if (!ctx.attr->non_resident) */ { } else /* if (!a->non_resident) */ {
if ((u8*)ctx->attr + le16_to_cpu( if ((u8*)a + le16_to_cpu(a->data.resident.value_offset)
ctx->attr->data.resident.value_offset) + + le32_to_cpu(
le32_to_cpu( a->data.resident.value_length) >
ctx->attr->data.resident.value_length) >
(u8*)ctx->mrec + vol->mft_record_size) { (u8*)ctx->mrec + vol->mft_record_size) {
ntfs_error(vi->i_sb, "Corrupt attribute list " ntfs_error(vi->i_sb, "Corrupt attribute list "
"in inode."); "in inode.");
goto unm_err_out; goto unm_err_out;
} }
/* Now copy the attribute list. */ /* Now copy the attribute list. */
memcpy(ni->attr_list, (u8*)ctx->attr + le16_to_cpu( memcpy(ni->attr_list, (u8*)a + le16_to_cpu(
ctx->attr->data.resident.value_offset), a->data.resident.value_offset),
le32_to_cpu( le32_to_cpu(
ctx->attr->data.resident.value_length)); a->data.resident.value_length));
} }
} }
skip_attr_list_load: skip_attr_list_load:
...@@ -741,10 +743,11 @@ static int ntfs_read_locked_inode(struct inode *vi) ...@@ -741,10 +743,11 @@ static int ntfs_read_locked_inode(struct inode *vi)
* in ntfs_ino->attr_list and it is ntfs_ino->attr_list_size bytes. * in ntfs_ino->attr_list and it is ntfs_ino->attr_list_size bytes.
*/ */
if (S_ISDIR(vi->i_mode)) { if (S_ISDIR(vi->i_mode)) {
loff_t bvi_size;
struct inode *bvi; struct inode *bvi;
ntfs_inode *bni; ntfs_inode *bni;
INDEX_ROOT *ir; INDEX_ROOT *ir;
char *ir_end, *index_end; u8 *ir_end, *index_end;
/* It is a directory, find index root attribute. */ /* It is a directory, find index root attribute. */
ntfs_attr_reinit_search_ctx(ctx); ntfs_attr_reinit_search_ctx(ctx);
...@@ -760,17 +763,16 @@ static int ntfs_read_locked_inode(struct inode *vi) ...@@ -760,17 +763,16 @@ static int ntfs_read_locked_inode(struct inode *vi)
} }
goto unm_err_out; goto unm_err_out;
} }
a = ctx->attr;
/* Set up the state. */ /* Set up the state. */
if (unlikely(ctx->attr->non_resident)) { if (unlikely(a->non_resident)) {
ntfs_error(vol->sb, "$INDEX_ROOT attribute is not " ntfs_error(vol->sb, "$INDEX_ROOT attribute is not "
"resident."); "resident.");
goto unm_err_out; goto unm_err_out;
} }
/* Ensure the attribute name is placed before the value. */ /* Ensure the attribute name is placed before the value. */
if (unlikely(ctx->attr->name_length && if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >=
(le16_to_cpu(ctx->attr->name_offset) >= le16_to_cpu(a->data.resident.value_offset)))) {
le16_to_cpu(ctx->attr->data.resident.
value_offset)))) {
ntfs_error(vol->sb, "$INDEX_ROOT attribute name is " ntfs_error(vol->sb, "$INDEX_ROOT attribute name is "
"placed after the attribute value."); "placed after the attribute value.");
goto unm_err_out; goto unm_err_out;
...@@ -781,28 +783,27 @@ static int ntfs_read_locked_inode(struct inode *vi) ...@@ -781,28 +783,27 @@ static int ntfs_read_locked_inode(struct inode *vi)
* encrypted. However index root cannot be both compressed and * encrypted. However index root cannot be both compressed and
* encrypted. * encrypted.
*/ */
if (ctx->attr->flags & ATTR_COMPRESSION_MASK) if (a->flags & ATTR_COMPRESSION_MASK)
NInoSetCompressed(ni); NInoSetCompressed(ni);
if (ctx->attr->flags & ATTR_IS_ENCRYPTED) { if (a->flags & ATTR_IS_ENCRYPTED) {
if (ctx->attr->flags & ATTR_COMPRESSION_MASK) { if (a->flags & ATTR_COMPRESSION_MASK) {
ntfs_error(vi->i_sb, "Found encrypted and " ntfs_error(vi->i_sb, "Found encrypted and "
"compressed attribute."); "compressed attribute.");
goto unm_err_out; goto unm_err_out;
} }
NInoSetEncrypted(ni); NInoSetEncrypted(ni);
} }
if (ctx->attr->flags & ATTR_IS_SPARSE) if (a->flags & ATTR_IS_SPARSE)
NInoSetSparse(ni); NInoSetSparse(ni);
ir = (INDEX_ROOT*)((char*)ctx->attr + le16_to_cpu( ir = (INDEX_ROOT*)((u8*)a +
ctx->attr->data.resident.value_offset)); le16_to_cpu(a->data.resident.value_offset));
ir_end = (char*)ir + le32_to_cpu( ir_end = (u8*)ir + le32_to_cpu(a->data.resident.value_length);
ctx->attr->data.resident.value_length); if (ir_end > (u8*)ctx->mrec + vol->mft_record_size) {
if (ir_end > (char*)ctx->mrec + vol->mft_record_size) {
ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is " ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is "
"corrupt."); "corrupt.");
goto unm_err_out; goto unm_err_out;
} }
index_end = (char*)&ir->index + index_end = (u8*)&ir->index +
le32_to_cpu(ir->index.index_length); le32_to_cpu(ir->index.index_length);
if (index_end > ir_end) { if (index_end > ir_end) {
ntfs_error(vi->i_sb, "Directory index is corrupt."); ntfs_error(vi->i_sb, "Directory index is corrupt.");
...@@ -889,7 +890,8 @@ static int ntfs_read_locked_inode(struct inode *vi) ...@@ -889,7 +890,8 @@ static int ntfs_read_locked_inode(struct inode *vi)
"attribute."); "attribute.");
goto unm_err_out; goto unm_err_out;
} }
if (!ctx->attr->non_resident) { a = ctx->attr;
if (!a->non_resident) {
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute " ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute "
"is resident."); "is resident.");
goto unm_err_out; goto unm_err_out;
...@@ -898,42 +900,40 @@ static int ntfs_read_locked_inode(struct inode *vi) ...@@ -898,42 +900,40 @@ static int ntfs_read_locked_inode(struct inode *vi)
* Ensure the attribute name is placed before the mapping pairs * Ensure the attribute name is placed before the mapping pairs
* array. * array.
*/ */
if (unlikely(ctx->attr->name_length && if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >=
(le16_to_cpu(ctx->attr->name_offset) >= le16_to_cpu(
le16_to_cpu(ctx->attr->data.non_resident. a->data.non_resident.mapping_pairs_offset)))) {
mapping_pairs_offset)))) {
ntfs_error(vol->sb, "$INDEX_ALLOCATION attribute name " ntfs_error(vol->sb, "$INDEX_ALLOCATION attribute name "
"is placed after the mapping pairs " "is placed after the mapping pairs "
"array."); "array.");
goto unm_err_out; goto unm_err_out;
} }
if (ctx->attr->flags & ATTR_IS_ENCRYPTED) { if (a->flags & ATTR_IS_ENCRYPTED) {
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute " ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute "
"is encrypted."); "is encrypted.");
goto unm_err_out; goto unm_err_out;
} }
if (ctx->attr->flags & ATTR_IS_SPARSE) { if (a->flags & ATTR_IS_SPARSE) {
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute " ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute "
"is sparse."); "is sparse.");
goto unm_err_out; goto unm_err_out;
} }
if (ctx->attr->flags & ATTR_COMPRESSION_MASK) { if (a->flags & ATTR_COMPRESSION_MASK) {
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute " ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute "
"is compressed."); "is compressed.");
goto unm_err_out; goto unm_err_out;
} }
if (ctx->attr->data.non_resident.lowest_vcn) { if (a->data.non_resident.lowest_vcn) {
ntfs_error(vi->i_sb, "First extent of " ntfs_error(vi->i_sb, "First extent of "
"$INDEX_ALLOCATION attribute has non " "$INDEX_ALLOCATION attribute has non "
"zero lowest_vcn."); "zero lowest_vcn.");
goto unm_err_out; goto unm_err_out;
} }
vi->i_size = sle64_to_cpu( vi->i_size = sle64_to_cpu(a->data.non_resident.data_size);
ctx->attr->data.non_resident.data_size);
ni->initialized_size = sle64_to_cpu( ni->initialized_size = sle64_to_cpu(
ctx->attr->data.non_resident.initialized_size); a->data.non_resident.initialized_size);
ni->allocated_size = sle64_to_cpu( ni->allocated_size = sle64_to_cpu(
ctx->attr->data.non_resident.allocated_size); a->data.non_resident.allocated_size);
/* /*
* We are done with the mft record, so we release it. Otherwise * We are done with the mft record, so we release it. Otherwise
* we would deadlock in ntfs_attr_iget(). * we would deadlock in ntfs_attr_iget().
...@@ -958,11 +958,12 @@ static int ntfs_read_locked_inode(struct inode *vi) ...@@ -958,11 +958,12 @@ static int ntfs_read_locked_inode(struct inode *vi)
goto unm_err_out; goto unm_err_out;
} }
/* Consistency check bitmap size vs. index allocation size. */ /* Consistency check bitmap size vs. index allocation size. */
if ((bvi->i_size << 3) < (vi->i_size >> bvi_size = i_size_read(bvi);
if ((bvi_size << 3) < (vi->i_size >>
ni->itype.index.block_size_bits)) { ni->itype.index.block_size_bits)) {
ntfs_error(vi->i_sb, "Index bitmap too small (0x%llx) " ntfs_error(vi->i_sb, "Index bitmap too small (0x%llx) "
"for index allocation (0x%llx).", "for index allocation (0x%llx).",
bvi->i_size << 3, vi->i_size); bvi_size << 3, vi->i_size);
goto unm_err_out; goto unm_err_out;
} }
skip_large_dir_stuff: skip_large_dir_stuff:
...@@ -1010,87 +1011,92 @@ static int ntfs_read_locked_inode(struct inode *vi) ...@@ -1010,87 +1011,92 @@ static int ntfs_read_locked_inode(struct inode *vi)
ntfs_error(vi->i_sb, "$DATA attribute is missing."); ntfs_error(vi->i_sb, "$DATA attribute is missing.");
goto unm_err_out; goto unm_err_out;
} }
a = ctx->attr;
/* Setup the state. */ /* Setup the state. */
if (ctx->attr->non_resident) { if (a->non_resident) {
NInoSetNonResident(ni); NInoSetNonResident(ni);
if (ctx->attr->flags & ATTR_COMPRESSION_MASK) { if (a->flags & (ATTR_COMPRESSION_MASK |
ATTR_IS_SPARSE)) {
if (a->flags & ATTR_COMPRESSION_MASK) {
NInoSetCompressed(ni); NInoSetCompressed(ni);
if (vol->cluster_size > 4096) { if (vol->cluster_size > 4096) {
ntfs_error(vi->i_sb, "Found " ntfs_error(vi->i_sb, "Found "
"compressed data but " "compressed data but "
"compression is disabled due " "compression is "
"to cluster size (%i) > 4kiB.", "disabled due to "
"cluster size (%i) > "
"4kiB.",
vol->cluster_size); vol->cluster_size);
goto unm_err_out; goto unm_err_out;
} }
if ((ctx->attr->flags & ATTR_COMPRESSION_MASK) if ((a->flags & ATTR_COMPRESSION_MASK)
!= ATTR_IS_COMPRESSED) { != ATTR_IS_COMPRESSED) {
ntfs_error(vi->i_sb, "Found " ntfs_error(vi->i_sb, "Found "
"unknown compression method or " "unknown compression "
"corrupt file."); "method or corrupt "
"file.");
goto unm_err_out; goto unm_err_out;
} }
ni->itype.compressed.block_clusters = 1U << }
ctx->attr->data.non_resident. if (a->flags & ATTR_IS_SPARSE)
compression_unit; NInoSetSparse(ni);
if (ctx->attr->data.non_resident. if (a->data.non_resident.compression_unit !=
compression_unit != 4) { 4) {
ntfs_error(vi->i_sb, "Found " ntfs_error(vi->i_sb, "Found "
"nonstandard compression unit " "nonstandard compression unit "
"(%u instead of 4). Cannot " "(%u instead of 4). Cannot "
"handle this.", "handle this.",
ctx->attr->data.non_resident. a->data.non_resident.
compression_unit); compression_unit);
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto unm_err_out; goto unm_err_out;
} }
ni->itype.compressed.block_clusters = 1U <<
a->data.non_resident.
compression_unit;
ni->itype.compressed.block_size = 1U << ( ni->itype.compressed.block_size = 1U << (
ctx->attr->data.non_resident. a->data.non_resident.
compression_unit + compression_unit +
vol->cluster_size_bits); vol->cluster_size_bits);
ni->itype.compressed.block_size_bits = ffs( ni->itype.compressed.block_size_bits = ffs(
ni->itype.compressed.block_size) - 1; ni->itype.compressed.
block_size) - 1;
ni->itype.compressed.size = sle64_to_cpu(
a->data.non_resident.
compressed_size);
} }
if (ctx->attr->flags & ATTR_IS_ENCRYPTED) { if (a->flags & ATTR_IS_ENCRYPTED) {
if (ctx->attr->flags & ATTR_COMPRESSION_MASK) { if (a->flags & ATTR_COMPRESSION_MASK) {
ntfs_error(vi->i_sb, "Found encrypted " ntfs_error(vi->i_sb, "Found encrypted "
"and compressed data."); "and compressed data.");
goto unm_err_out; goto unm_err_out;
} }
NInoSetEncrypted(ni); NInoSetEncrypted(ni);
} }
if (ctx->attr->flags & ATTR_IS_SPARSE) if (a->data.non_resident.lowest_vcn) {
NInoSetSparse(ni);
if (ctx->attr->data.non_resident.lowest_vcn) {
ntfs_error(vi->i_sb, "First extent of $DATA " ntfs_error(vi->i_sb, "First extent of $DATA "
"attribute has non zero " "attribute has non zero "
"lowest_vcn."); "lowest_vcn.");
goto unm_err_out; goto unm_err_out;
} }
/* Setup all the sizes. */
vi->i_size = sle64_to_cpu( vi->i_size = sle64_to_cpu(
ctx->attr->data.non_resident.data_size); a->data.non_resident.data_size);
ni->initialized_size = sle64_to_cpu( ni->initialized_size = sle64_to_cpu(
ctx->attr->data.non_resident. a->data.non_resident.initialized_size);
initialized_size);
ni->allocated_size = sle64_to_cpu( ni->allocated_size = sle64_to_cpu(
ctx->attr->data.non_resident. a->data.non_resident.allocated_size);
allocated_size);
if (NInoCompressed(ni)) {
ni->itype.compressed.size = sle64_to_cpu(
ctx->attr->data.non_resident.
compressed_size);
}
} else { /* Resident attribute. */ } else { /* Resident attribute. */
/* vi->i_size = ni->initialized_size = le32_to_cpu(
* Make all sizes equal for simplicity in read code a->data.resident.value_length);
* paths. FIXME: Need to keep this in mind when ni->allocated_size = le32_to_cpu(a->length) -
* converting to non-resident attribute in write code le16_to_cpu(
* path. (Probably only affects truncate().) a->data.resident.value_offset);
*/ if (vi->i_size > ni->allocated_size) {
vi->i_size = ni->initialized_size = ni->allocated_size = ntfs_error(vi->i_sb, "Resident data attribute "
le32_to_cpu( "is corrupt (size exceeds "
ctx->attr->data.resident.value_length); "allocation).");
goto unm_err_out;
}
} }
no_data_attr_special_case: no_data_attr_special_case:
/* We are done with the mft record, so we release it. */ /* We are done with the mft record, so we release it. */
...@@ -1117,11 +1123,10 @@ static int ntfs_read_locked_inode(struct inode *vi) ...@@ -1117,11 +1123,10 @@ static int ntfs_read_locked_inode(struct inode *vi)
* sizes of all non-resident attributes present to give us the Linux * sizes of all non-resident attributes present to give us the Linux
* correct size that should go into i_blocks (after division by 512). * correct size that should go into i_blocks (after division by 512).
*/ */
if (S_ISDIR(vi->i_mode) || !NInoCompressed(ni)) if (S_ISREG(vi->i_mode) && (NInoCompressed(ni) || NInoSparse(ni)))
vi->i_blocks = ni->allocated_size >> 9;
else
vi->i_blocks = ni->itype.compressed.size >> 9; vi->i_blocks = ni->itype.compressed.size >> 9;
else
vi->i_blocks = ni->allocated_size >> 9;
ntfs_debug("Done."); ntfs_debug("Done.");
return 0; return 0;
...@@ -1166,6 +1171,7 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) ...@@ -1166,6 +1171,7 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
ntfs_volume *vol = NTFS_SB(vi->i_sb); ntfs_volume *vol = NTFS_SB(vi->i_sb);
ntfs_inode *ni, *base_ni; ntfs_inode *ni, *base_ni;
MFT_RECORD *m; MFT_RECORD *m;
ATTR_RECORD *a;
ntfs_attr_search_ctx *ctx; ntfs_attr_search_ctx *ctx;
int err = 0; int err = 0;
...@@ -1200,24 +1206,21 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) ...@@ -1200,24 +1206,21 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
err = -ENOMEM; err = -ENOMEM;
goto unm_err_out; goto unm_err_out;
} }
/* Find the attribute. */ /* Find the attribute. */
err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
CASE_SENSITIVE, 0, NULL, 0, ctx); CASE_SENSITIVE, 0, NULL, 0, ctx);
if (unlikely(err)) if (unlikely(err))
goto unm_err_out; goto unm_err_out;
a = ctx->attr;
if (!ctx->attr->non_resident) { if (!a->non_resident) {
/* Ensure the attribute name is placed before the value. */ /* Ensure the attribute name is placed before the value. */
if (unlikely(ctx->attr->name_length && if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >=
(le16_to_cpu(ctx->attr->name_offset) >= le16_to_cpu(a->data.resident.value_offset)))) {
le16_to_cpu(ctx->attr->data.resident.
value_offset)))) {
ntfs_error(vol->sb, "Attribute name is placed after " ntfs_error(vol->sb, "Attribute name is placed after "
"the attribute value."); "the attribute value.");
goto unm_err_out; goto unm_err_out;
} }
if (NInoMstProtected(ni) || ctx->attr->flags) { if (NInoMstProtected(ni) || a->flags) {
ntfs_error(vi->i_sb, "Found mst protected attribute " ntfs_error(vi->i_sb, "Found mst protected attribute "
"or attribute with non-zero flags but " "or attribute with non-zero flags but "
"the attribute is resident. Please " "the attribute is resident. Please "
...@@ -1225,85 +1228,95 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) ...@@ -1225,85 +1228,95 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
"linux-ntfs-dev@lists.sourceforge.net"); "linux-ntfs-dev@lists.sourceforge.net");
goto unm_err_out; goto unm_err_out;
} }
/* vi->i_size = ni->initialized_size = le32_to_cpu(
* Resident attribute. Make all sizes equal for simplicity in a->data.resident.value_length);
* read code paths. ni->allocated_size = le32_to_cpu(a->length) -
*/ le16_to_cpu(a->data.resident.value_offset);
vi->i_size = ni->initialized_size = ni->allocated_size = if (vi->i_size > ni->allocated_size) {
le32_to_cpu(ctx->attr->data.resident.value_length); ntfs_error(vi->i_sb, "Resident attribute is corrupt "
"(size exceeds allocation).");
goto unm_err_out;
}
} else { } else {
NInoSetNonResident(ni); NInoSetNonResident(ni);
/* /*
* Ensure the attribute name is placed before the mapping pairs * Ensure the attribute name is placed before the mapping pairs
* array. * array.
*/ */
if (unlikely(ctx->attr->name_length && if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >=
(le16_to_cpu(ctx->attr->name_offset) >= le16_to_cpu(
le16_to_cpu(ctx->attr->data.non_resident. a->data.non_resident.mapping_pairs_offset)))) {
mapping_pairs_offset)))) {
ntfs_error(vol->sb, "Attribute name is placed after " ntfs_error(vol->sb, "Attribute name is placed after "
"the mapping pairs array."); "the mapping pairs array.");
goto unm_err_out; goto unm_err_out;
} }
if (ctx->attr->flags & ATTR_COMPRESSION_MASK) { if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) {
if (NInoMstProtected(ni)) { if (a->flags & ATTR_COMPRESSION_MASK) {
ntfs_error(vi->i_sb, "Found mst protected "
"attribute but the attribute "
"is compressed. Please report "
"you saw this message to "
"linux-ntfs-dev@lists."
"sourceforge.net");
goto unm_err_out;
}
NInoSetCompressed(ni); NInoSetCompressed(ni);
if ((ni->type != AT_DATA) || (ni->type == AT_DATA && if ((ni->type != AT_DATA) || (ni->type ==
ni->name_len)) { AT_DATA && ni->name_len)) {
ntfs_error(vi->i_sb, "Found compressed " ntfs_error(vi->i_sb, "Found compressed "
"non-data or named data " "non-data or named "
"attribute. Please report " "data attribute. "
"you saw this message to " "Please report you "
"saw this message to "
"linux-ntfs-dev@lists." "linux-ntfs-dev@lists."
"sourceforge.net"); "sourceforge.net");
goto unm_err_out; goto unm_err_out;
} }
if (vol->cluster_size > 4096) { if (vol->cluster_size > 4096) {
ntfs_error(vi->i_sb, "Found compressed " ntfs_error(vi->i_sb, "Found compressed "
"attribute but compression is " "attribute but "
"disabled due to cluster size " "compression is "
"(%i) > 4kiB.", "disabled due to "
"cluster size (%i) > "
"4kiB.",
vol->cluster_size); vol->cluster_size);
goto unm_err_out; goto unm_err_out;
} }
if ((ctx->attr->flags & ATTR_COMPRESSION_MASK) if ((a->flags & ATTR_COMPRESSION_MASK) !=
!= ATTR_IS_COMPRESSED) { ATTR_IS_COMPRESSED) {
ntfs_error(vi->i_sb, "Found unknown " ntfs_error(vi->i_sb, "Found unknown "
"compression method."); "compression method.");
goto unm_err_out; goto unm_err_out;
} }
ni->itype.compressed.block_clusters = 1U << }
ctx->attr->data.non_resident. if (NInoMstProtected(ni)) {
compression_unit; ntfs_error(vi->i_sb, "Found mst protected "
if (ctx->attr->data.non_resident.compression_unit != "attribute but the attribute "
4) { "is %s. Please report you "
"saw this message to "
"linux-ntfs-dev@lists."
"sourceforge.net",
NInoCompressed(ni) ?
"compressed" : "sparse");
goto unm_err_out;
}
if (a->flags & ATTR_IS_SPARSE)
NInoSetSparse(ni);
if (a->data.non_resident.compression_unit != 4) {
ntfs_error(vi->i_sb, "Found nonstandard " ntfs_error(vi->i_sb, "Found nonstandard "
"compression unit (%u instead " "compression unit (%u instead "
"of 4). Cannot handle this.", "of 4). Cannot handle this.",
ctx->attr->data.non_resident. a->data.non_resident.
compression_unit); compression_unit);
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto unm_err_out; goto unm_err_out;
} }
ni->itype.compressed.block_clusters = 1U <<
a->data.non_resident.compression_unit;
ni->itype.compressed.block_size = 1U << ( ni->itype.compressed.block_size = 1U << (
ctx->attr->data.non_resident. a->data.non_resident.compression_unit +
compression_unit +
vol->cluster_size_bits); vol->cluster_size_bits);
ni->itype.compressed.block_size_bits = ffs( ni->itype.compressed.block_size_bits = ffs(
ni->itype.compressed.block_size) - 1; ni->itype.compressed.block_size) - 1;
ni->itype.compressed.size = sle64_to_cpu(
a->data.non_resident.compressed_size);
} }
if (ctx->attr->flags & ATTR_IS_ENCRYPTED) { if (a->flags & ATTR_IS_ENCRYPTED) {
if (ctx->attr->flags & ATTR_COMPRESSION_MASK) { if (a->flags & ATTR_COMPRESSION_MASK) {
ntfs_error(vi->i_sb, "Found encrypted " ntfs_error(vi->i_sb, "Found encrypted and "
"and compressed data."); "compressed data.");
goto unm_err_out; goto unm_err_out;
} }
if (NInoMstProtected(ni)) { if (NInoMstProtected(ni)) {
...@@ -1317,37 +1330,17 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) ...@@ -1317,37 +1330,17 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
} }
NInoSetEncrypted(ni); NInoSetEncrypted(ni);
} }
if (ctx->attr->flags & ATTR_IS_SPARSE) { if (a->data.non_resident.lowest_vcn) {
if (NInoMstProtected(ni)) {
ntfs_error(vi->i_sb, "Found mst protected "
"attribute but the attribute "
"is sparse. Please report "
"you saw this message to "
"linux-ntfs-dev@lists."
"sourceforge.net");
goto unm_err_out;
}
NInoSetSparse(ni);
}
if (ctx->attr->data.non_resident.lowest_vcn) {
ntfs_error(vi->i_sb, "First extent of attribute has " ntfs_error(vi->i_sb, "First extent of attribute has "
"non-zero lowest_vcn."); "non-zero lowest_vcn.");
goto unm_err_out; goto unm_err_out;
} }
/* Setup all the sizes. */ vi->i_size = sle64_to_cpu(a->data.non_resident.data_size);
vi->i_size = sle64_to_cpu(
ctx->attr->data.non_resident.data_size);
ni->initialized_size = sle64_to_cpu( ni->initialized_size = sle64_to_cpu(
ctx->attr->data.non_resident.initialized_size); a->data.non_resident.initialized_size);
ni->allocated_size = sle64_to_cpu( ni->allocated_size = sle64_to_cpu(
ctx->attr->data.non_resident.allocated_size); a->data.non_resident.allocated_size);
if (NInoCompressed(ni)) {
ni->itype.compressed.size = sle64_to_cpu(
ctx->attr->data.non_resident.
compressed_size);
}
} }
/* Setup the operations for this attribute inode. */ /* Setup the operations for this attribute inode. */
vi->i_op = NULL; vi->i_op = NULL;
vi->i_fop = NULL; vi->i_fop = NULL;
...@@ -1355,12 +1348,10 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) ...@@ -1355,12 +1348,10 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
vi->i_mapping->a_ops = &ntfs_mst_aops; vi->i_mapping->a_ops = &ntfs_mst_aops;
else else
vi->i_mapping->a_ops = &ntfs_aops; vi->i_mapping->a_ops = &ntfs_aops;
if (NInoCompressed(ni) || NInoSparse(ni))
if (!NInoCompressed(ni))
vi->i_blocks = ni->allocated_size >> 9;
else
vi->i_blocks = ni->itype.compressed.size >> 9; vi->i_blocks = ni->itype.compressed.size >> 9;
else
vi->i_blocks = ni->allocated_size >> 9;
/* /*
* Make sure the base inode doesn't go away and attach it to the * Make sure the base inode doesn't go away and attach it to the
* attribute inode. * attribute inode.
...@@ -1429,10 +1420,12 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) ...@@ -1429,10 +1420,12 @@ 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) static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
{ {
loff_t bvi_size;
ntfs_volume *vol = NTFS_SB(vi->i_sb); ntfs_volume *vol = NTFS_SB(vi->i_sb);
ntfs_inode *ni, *base_ni, *bni; ntfs_inode *ni, *base_ni, *bni;
struct inode *bvi; struct inode *bvi;
MFT_RECORD *m; MFT_RECORD *m;
ATTR_RECORD *a;
ntfs_attr_search_ctx *ctx; ntfs_attr_search_ctx *ctx;
INDEX_ROOT *ir; INDEX_ROOT *ir;
u8 *ir_end, *index_end; u8 *ir_end, *index_end;
...@@ -1474,30 +1467,28 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi) ...@@ -1474,30 +1467,28 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
"missing."); "missing.");
goto unm_err_out; goto unm_err_out;
} }
a = ctx->attr;
/* Set up the state. */ /* Set up the state. */
if (unlikely(ctx->attr->non_resident)) { if (unlikely(a->non_resident)) {
ntfs_error(vol->sb, "$INDEX_ROOT attribute is not resident."); ntfs_error(vol->sb, "$INDEX_ROOT attribute is not resident.");
goto unm_err_out; goto unm_err_out;
} }
/* Ensure the attribute name is placed before the value. */ /* Ensure the attribute name is placed before the value. */
if (unlikely(ctx->attr->name_length && if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >=
(le16_to_cpu(ctx->attr->name_offset) >= le16_to_cpu(a->data.resident.value_offset)))) {
le16_to_cpu(ctx->attr->data.resident.
value_offset)))) {
ntfs_error(vol->sb, "$INDEX_ROOT attribute name is placed " ntfs_error(vol->sb, "$INDEX_ROOT attribute name is placed "
"after the attribute value."); "after the attribute value.");
goto unm_err_out; goto unm_err_out;
} }
/* Compressed/encrypted/sparse index root is not allowed. */ /* Compressed/encrypted/sparse index root is not allowed. */
if (ctx->attr->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_ENCRYPTED | if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_ENCRYPTED |
ATTR_IS_SPARSE)) { ATTR_IS_SPARSE)) {
ntfs_error(vi->i_sb, "Found compressed/encrypted/sparse index " ntfs_error(vi->i_sb, "Found compressed/encrypted/sparse index "
"root attribute."); "root attribute.");
goto unm_err_out; goto unm_err_out;
} }
ir = (INDEX_ROOT*)((u8*)ctx->attr + ir = (INDEX_ROOT*)((u8*)a + le16_to_cpu(a->data.resident.value_offset));
le16_to_cpu(ctx->attr->data.resident.value_offset)); ir_end = (u8*)ir + le32_to_cpu(a->data.resident.value_length);
ir_end = (u8*)ir + le32_to_cpu(ctx->attr->data.resident.value_length);
if (ir_end > (u8*)ctx->mrec + vol->mft_record_size) { if (ir_end > (u8*)ctx->mrec + vol->mft_record_size) {
ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is corrupt."); ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is corrupt.");
goto unm_err_out; goto unm_err_out;
...@@ -1570,7 +1561,7 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi) ...@@ -1570,7 +1561,7 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
"$INDEX_ALLOCATION attribute."); "$INDEX_ALLOCATION attribute.");
goto unm_err_out; goto unm_err_out;
} }
if (!ctx->attr->non_resident) { if (!a->non_resident) {
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is " ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is "
"resident."); "resident.");
goto unm_err_out; goto unm_err_out;
...@@ -1578,37 +1569,36 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi) ...@@ -1578,37 +1569,36 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
/* /*
* Ensure the attribute name is placed before the mapping pairs array. * Ensure the attribute name is placed before the mapping pairs array.
*/ */
if (unlikely(ctx->attr->name_length && (le16_to_cpu( if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >=
ctx->attr->name_offset) >= le16_to_cpu( le16_to_cpu(
ctx->attr->data.non_resident.mapping_pairs_offset)))) { a->data.non_resident.mapping_pairs_offset)))) {
ntfs_error(vol->sb, "$INDEX_ALLOCATION attribute name is " ntfs_error(vol->sb, "$INDEX_ALLOCATION attribute name is "
"placed after the mapping pairs array."); "placed after the mapping pairs array.");
goto unm_err_out; goto unm_err_out;
} }
if (ctx->attr->flags & ATTR_IS_ENCRYPTED) { if (a->flags & ATTR_IS_ENCRYPTED) {
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is " ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is "
"encrypted."); "encrypted.");
goto unm_err_out; goto unm_err_out;
} }
if (ctx->attr->flags & ATTR_IS_SPARSE) { if (a->flags & ATTR_IS_SPARSE) {
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is sparse."); ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is sparse.");
goto unm_err_out; goto unm_err_out;
} }
if (ctx->attr->flags & ATTR_COMPRESSION_MASK) { if (a->flags & ATTR_COMPRESSION_MASK) {
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is " ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is "
"compressed."); "compressed.");
goto unm_err_out; goto unm_err_out;
} }
if (ctx->attr->data.non_resident.lowest_vcn) { if (a->data.non_resident.lowest_vcn) {
ntfs_error(vi->i_sb, "First extent of $INDEX_ALLOCATION " ntfs_error(vi->i_sb, "First extent of $INDEX_ALLOCATION "
"attribute has non zero lowest_vcn."); "attribute has non zero lowest_vcn.");
goto unm_err_out; goto unm_err_out;
} }
vi->i_size = sle64_to_cpu(ctx->attr->data.non_resident.data_size); vi->i_size = sle64_to_cpu(a->data.non_resident.data_size);
ni->initialized_size = sle64_to_cpu( ni->initialized_size = sle64_to_cpu(
ctx->attr->data.non_resident.initialized_size); a->data.non_resident.initialized_size);
ni->allocated_size = sle64_to_cpu( ni->allocated_size = sle64_to_cpu(a->data.non_resident.allocated_size);
ctx->attr->data.non_resident.allocated_size);
/* /*
* We are done with the mft record, so we release it. Otherwise * We are done with the mft record, so we release it. Otherwise
* we would deadlock in ntfs_attr_iget(). * we would deadlock in ntfs_attr_iget().
...@@ -1632,10 +1622,10 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi) ...@@ -1632,10 +1622,10 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
goto iput_unm_err_out; goto iput_unm_err_out;
} }
/* Consistency check bitmap size vs. index allocation size. */ /* Consistency check bitmap size vs. index allocation size. */
if ((bvi->i_size << 3) < (vi->i_size >> bvi_size = i_size_read(bvi);
ni->itype.index.block_size_bits)) { if ((bvi_size << 3) < (vi->i_size >> ni->itype.index.block_size_bits)) {
ntfs_error(vi->i_sb, "Index bitmap too small (0x%llx) for " ntfs_error(vi->i_sb, "Index bitmap too small (0x%llx) for "
"index allocation (0x%llx).", bvi->i_size << 3, "index allocation (0x%llx).", bvi_size << 3,
vi->i_size); vi->i_size);
goto iput_unm_err_out; goto iput_unm_err_out;
} }
...@@ -1646,7 +1636,6 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi) ...@@ -1646,7 +1636,6 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
vi->i_fop = NULL; vi->i_fop = NULL;
vi->i_mapping->a_ops = &ntfs_mst_aops; vi->i_mapping->a_ops = &ntfs_mst_aops;
vi->i_blocks = ni->allocated_size >> 9; vi->i_blocks = ni->allocated_size >> 9;
/* /*
* Make sure the base inode doesn't go away and attach it to the * Make sure the base inode doesn't go away and attach it to the
* index inode. * index inode.
...@@ -1712,7 +1701,7 @@ int ntfs_read_inode_mount(struct inode *vi) ...@@ -1712,7 +1701,7 @@ int ntfs_read_inode_mount(struct inode *vi)
struct buffer_head *bh; struct buffer_head *bh;
ntfs_inode *ni; ntfs_inode *ni;
MFT_RECORD *m = NULL; MFT_RECORD *m = NULL;
ATTR_RECORD *attr; ATTR_RECORD *a;
ntfs_attr_search_ctx *ctx; ntfs_attr_search_ctx *ctx;
unsigned int i, nr_blocks; unsigned int i, nr_blocks;
int err; int err;
...@@ -1727,10 +1716,10 @@ int ntfs_read_inode_mount(struct inode *vi) ...@@ -1727,10 +1716,10 @@ int ntfs_read_inode_mount(struct inode *vi)
/* Setup the data attribute. It is special as it is mst protected. */ /* Setup the data attribute. It is special as it is mst protected. */
NInoSetNonResident(ni); NInoSetNonResident(ni);
NInoSetMstProtected(ni); NInoSetMstProtected(ni);
NInoSetSparseDisabled(ni);
ni->type = AT_DATA; ni->type = AT_DATA;
ni->name = NULL; ni->name = NULL;
ni->name_len = 0; ni->name_len = 0;
/* /*
* This sets up our little cheat allowing us to reuse the async read io * This sets up our little cheat allowing us to reuse the async read io
* completion handler for directories. * completion handler for directories.
...@@ -1808,9 +1797,10 @@ int ntfs_read_inode_mount(struct inode *vi) ...@@ -1808,9 +1797,10 @@ int ntfs_read_inode_mount(struct inode *vi)
ntfs_debug("Attribute list attribute found in $MFT."); ntfs_debug("Attribute list attribute found in $MFT.");
NInoSetAttrList(ni); NInoSetAttrList(ni);
if (ctx->attr->flags & ATTR_IS_ENCRYPTED || a = ctx->attr;
ctx->attr->flags & ATTR_COMPRESSION_MASK || if (a->flags & ATTR_IS_ENCRYPTED ||
ctx->attr->flags & ATTR_IS_SPARSE) { a->flags & ATTR_COMPRESSION_MASK ||
a->flags & ATTR_IS_SPARSE) {
ntfs_error(sb, "Attribute list attribute is " ntfs_error(sb, "Attribute list attribute is "
"compressed/encrypted/sparse. Not " "compressed/encrypted/sparse. Not "
"allowed. $MFT is corrupt. You should " "allowed. $MFT is corrupt. You should "
...@@ -1818,16 +1808,16 @@ int ntfs_read_inode_mount(struct inode *vi) ...@@ -1818,16 +1808,16 @@ int ntfs_read_inode_mount(struct inode *vi)
goto put_err_out; goto put_err_out;
} }
/* Now allocate memory for the attribute list. */ /* Now allocate memory for the attribute list. */
ni->attr_list_size = (u32)ntfs_attr_size(ctx->attr); ni->attr_list_size = (u32)ntfs_attr_size(a);
ni->attr_list = ntfs_malloc_nofs(ni->attr_list_size); ni->attr_list = ntfs_malloc_nofs(ni->attr_list_size);
if (!ni->attr_list) { if (!ni->attr_list) {
ntfs_error(sb, "Not enough memory to allocate buffer " ntfs_error(sb, "Not enough memory to allocate buffer "
"for attribute list."); "for attribute list.");
goto put_err_out; goto put_err_out;
} }
if (ctx->attr->non_resident) { if (a->non_resident) {
NInoSetAttrListNonResident(ni); NInoSetAttrListNonResident(ni);
if (ctx->attr->data.non_resident.lowest_vcn) { if (a->data.non_resident.lowest_vcn) {
ntfs_error(sb, "Attribute list has non zero " ntfs_error(sb, "Attribute list has non zero "
"lowest_vcn. $MFT is corrupt. " "lowest_vcn. $MFT is corrupt. "
"You should run chkdsk."); "You should run chkdsk.");
...@@ -1835,7 +1825,7 @@ int ntfs_read_inode_mount(struct inode *vi) ...@@ -1835,7 +1825,7 @@ int ntfs_read_inode_mount(struct inode *vi)
} }
/* Setup the runlist. */ /* Setup the runlist. */
ni->attr_list_rl.rl = ntfs_mapping_pairs_decompress(vol, ni->attr_list_rl.rl = ntfs_mapping_pairs_decompress(vol,
ctx->attr, NULL); a, NULL);
if (IS_ERR(ni->attr_list_rl.rl)) { if (IS_ERR(ni->attr_list_rl.rl)) {
err = PTR_ERR(ni->attr_list_rl.rl); err = PTR_ERR(ni->attr_list_rl.rl);
ni->attr_list_rl.rl = NULL; ni->attr_list_rl.rl = NULL;
...@@ -1847,7 +1837,7 @@ int ntfs_read_inode_mount(struct inode *vi) ...@@ -1847,7 +1837,7 @@ int ntfs_read_inode_mount(struct inode *vi)
/* Now load the attribute list. */ /* Now load the attribute list. */
if ((err = load_attribute_list(vol, &ni->attr_list_rl, if ((err = load_attribute_list(vol, &ni->attr_list_rl,
ni->attr_list, ni->attr_list_size, ni->attr_list, ni->attr_list_size,
sle64_to_cpu(ctx->attr->data. sle64_to_cpu(a->data.
non_resident.initialized_size)))) { non_resident.initialized_size)))) {
ntfs_error(sb, "Failed to load attribute list " ntfs_error(sb, "Failed to load attribute list "
"attribute with error code %i.", "attribute with error code %i.",
...@@ -1855,20 +1845,20 @@ int ntfs_read_inode_mount(struct inode *vi) ...@@ -1855,20 +1845,20 @@ int ntfs_read_inode_mount(struct inode *vi)
goto put_err_out; goto put_err_out;
} }
} else /* if (!ctx.attr->non_resident) */ { } else /* if (!ctx.attr->non_resident) */ {
if ((u8*)ctx->attr + le16_to_cpu( if ((u8*)a + le16_to_cpu(
ctx->attr->data.resident.value_offset) + a->data.resident.value_offset) +
le32_to_cpu( le32_to_cpu(
ctx->attr->data.resident.value_length) > a->data.resident.value_length) >
(u8*)ctx->mrec + vol->mft_record_size) { (u8*)ctx->mrec + vol->mft_record_size) {
ntfs_error(sb, "Corrupt attribute list " ntfs_error(sb, "Corrupt attribute list "
"attribute."); "attribute.");
goto put_err_out; goto put_err_out;
} }
/* Now copy the attribute list. */ /* Now copy the attribute list. */
memcpy(ni->attr_list, (u8*)ctx->attr + le16_to_cpu( memcpy(ni->attr_list, (u8*)a + le16_to_cpu(
ctx->attr->data.resident.value_offset), a->data.resident.value_offset),
le32_to_cpu( le32_to_cpu(
ctx->attr->data.resident.value_length)); a->data.resident.value_length));
} }
/* The attribute list is now setup in memory. */ /* The attribute list is now setup in memory. */
/* /*
...@@ -1934,25 +1924,25 @@ int ntfs_read_inode_mount(struct inode *vi) ...@@ -1934,25 +1924,25 @@ int ntfs_read_inode_mount(struct inode *vi)
ntfs_attr_reinit_search_ctx(ctx); ntfs_attr_reinit_search_ctx(ctx);
/* Now load all attribute extents. */ /* Now load all attribute extents. */
attr = NULL; a = NULL;
next_vcn = last_vcn = highest_vcn = 0; next_vcn = last_vcn = highest_vcn = 0;
while (!(err = ntfs_attr_lookup(AT_DATA, NULL, 0, 0, next_vcn, NULL, 0, while (!(err = ntfs_attr_lookup(AT_DATA, NULL, 0, 0, next_vcn, NULL, 0,
ctx))) { ctx))) {
runlist_element *nrl; runlist_element *nrl;
/* Cache the current attribute. */ /* Cache the current attribute. */
attr = ctx->attr; a = ctx->attr;
/* $MFT must be non-resident. */ /* $MFT must be non-resident. */
if (!attr->non_resident) { if (!a->non_resident) {
ntfs_error(sb, "$MFT must be non-resident but a " ntfs_error(sb, "$MFT must be non-resident but a "
"resident extent was found. $MFT is " "resident extent was found. $MFT is "
"corrupt. Run chkdsk."); "corrupt. Run chkdsk.");
goto put_err_out; goto put_err_out;
} }
/* $MFT must be uncompressed and unencrypted. */ /* $MFT must be uncompressed and unencrypted. */
if (attr->flags & ATTR_COMPRESSION_MASK || if (a->flags & ATTR_COMPRESSION_MASK ||
attr->flags & ATTR_IS_ENCRYPTED || a->flags & ATTR_IS_ENCRYPTED ||
attr->flags & ATTR_IS_SPARSE) { a->flags & ATTR_IS_SPARSE) {
ntfs_error(sb, "$MFT must be uncompressed, " ntfs_error(sb, "$MFT must be uncompressed, "
"non-sparse, and unencrypted but a " "non-sparse, and unencrypted but a "
"compressed/sparse/encrypted extent " "compressed/sparse/encrypted extent "
...@@ -1966,7 +1956,7 @@ int ntfs_read_inode_mount(struct inode *vi) ...@@ -1966,7 +1956,7 @@ int ntfs_read_inode_mount(struct inode *vi)
* as we have exclusive access to the inode at this time and we * as we have exclusive access to the inode at this time and we
* are a mount in progress task, too. * are a mount in progress task, too.
*/ */
nrl = ntfs_mapping_pairs_decompress(vol, attr, ni->runlist.rl); nrl = ntfs_mapping_pairs_decompress(vol, a, ni->runlist.rl);
if (IS_ERR(nrl)) { if (IS_ERR(nrl)) {
ntfs_error(sb, "ntfs_mapping_pairs_decompress() " ntfs_error(sb, "ntfs_mapping_pairs_decompress() "
"failed with error code %ld. $MFT is " "failed with error code %ld. $MFT is "
...@@ -1977,7 +1967,7 @@ int ntfs_read_inode_mount(struct inode *vi) ...@@ -1977,7 +1967,7 @@ int ntfs_read_inode_mount(struct inode *vi)
/* Are we in the first extent? */ /* Are we in the first extent? */
if (!next_vcn) { if (!next_vcn) {
if (attr->data.non_resident.lowest_vcn) { if (a->data.non_resident.lowest_vcn) {
ntfs_error(sb, "First extent of $DATA " ntfs_error(sb, "First extent of $DATA "
"attribute has non zero " "attribute has non zero "
"lowest_vcn. $MFT is corrupt. " "lowest_vcn. $MFT is corrupt. "
...@@ -1986,15 +1976,15 @@ int ntfs_read_inode_mount(struct inode *vi) ...@@ -1986,15 +1976,15 @@ int ntfs_read_inode_mount(struct inode *vi)
} }
/* Get the last vcn in the $DATA attribute. */ /* Get the last vcn in the $DATA attribute. */
last_vcn = sle64_to_cpu( last_vcn = sle64_to_cpu(
attr->data.non_resident.allocated_size) a->data.non_resident.allocated_size)
>> vol->cluster_size_bits; >> vol->cluster_size_bits;
/* Fill in the inode size. */ /* Fill in the inode size. */
vi->i_size = sle64_to_cpu( vi->i_size = sle64_to_cpu(
attr->data.non_resident.data_size); a->data.non_resident.data_size);
ni->initialized_size = sle64_to_cpu(attr->data. ni->initialized_size = sle64_to_cpu(
non_resident.initialized_size); a->data.non_resident.initialized_size);
ni->allocated_size = sle64_to_cpu( ni->allocated_size = sle64_to_cpu(
attr->data.non_resident.allocated_size); a->data.non_resident.allocated_size);
/* /*
* Verify the number of mft records does not exceed * Verify the number of mft records does not exceed
* 2^32 - 1. * 2^32 - 1.
...@@ -2051,7 +2041,7 @@ int ntfs_read_inode_mount(struct inode *vi) ...@@ -2051,7 +2041,7 @@ int ntfs_read_inode_mount(struct inode *vi)
} }
/* Get the lowest vcn for the next extent. */ /* Get the lowest vcn for the next extent. */
highest_vcn = sle64_to_cpu(attr->data.non_resident.highest_vcn); highest_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn);
next_vcn = highest_vcn + 1; next_vcn = highest_vcn + 1;
/* Only one extent or error, which we catch below. */ /* Only one extent or error, which we catch below. */
...@@ -2060,7 +2050,7 @@ int ntfs_read_inode_mount(struct inode *vi) ...@@ -2060,7 +2050,7 @@ int ntfs_read_inode_mount(struct inode *vi)
/* Avoid endless loops due to corruption. */ /* Avoid endless loops due to corruption. */
if (next_vcn < sle64_to_cpu( if (next_vcn < sle64_to_cpu(
attr->data.non_resident.lowest_vcn)) { a->data.non_resident.lowest_vcn)) {
ntfs_error(sb, "$MFT has corrupt attribute list " ntfs_error(sb, "$MFT has corrupt attribute list "
"attribute. Run chkdsk."); "attribute. Run chkdsk.");
goto put_err_out; goto put_err_out;
...@@ -2071,7 +2061,7 @@ int ntfs_read_inode_mount(struct inode *vi) ...@@ -2071,7 +2061,7 @@ int ntfs_read_inode_mount(struct inode *vi)
"$MFT is corrupt. Run chkdsk."); "$MFT is corrupt. Run chkdsk.");
goto put_err_out; goto put_err_out;
} }
if (!attr) { if (!a) {
ntfs_error(sb, "$MFT/$DATA attribute not found. $MFT is " ntfs_error(sb, "$MFT/$DATA attribute not found. $MFT is "
"corrupt. Run chkdsk."); "corrupt. Run chkdsk.");
goto put_err_out; goto put_err_out;
...@@ -2275,6 +2265,8 @@ int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt) ...@@ -2275,6 +2265,8 @@ int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt)
seq_printf(sf, ",case_sensitive"); seq_printf(sf, ",case_sensitive");
if (NVolShowSystemFiles(vol)) if (NVolShowSystemFiles(vol))
seq_printf(sf, ",show_sys_files"); seq_printf(sf, ",show_sys_files");
if (!NVolSparseEnabled(vol))
seq_printf(sf, ",disable_sparse");
for (i = 0; on_errors_arr[i].val; i++) { for (i = 0; on_errors_arr[i].val; i++) {
if (on_errors_arr[i].val & vol->on_errors) if (on_errors_arr[i].val & vol->on_errors)
seq_printf(sf, ",errors=%s", on_errors_arr[i].str); seq_printf(sf, ",errors=%s", on_errors_arr[i].str);
...@@ -2311,6 +2303,7 @@ int ntfs_truncate(struct inode *vi) ...@@ -2311,6 +2303,7 @@ int ntfs_truncate(struct inode *vi)
ntfs_volume *vol = ni->vol; ntfs_volume *vol = ni->vol;
ntfs_attr_search_ctx *ctx; ntfs_attr_search_ctx *ctx;
MFT_RECORD *m; MFT_RECORD *m;
ATTR_RECORD *a;
const char *te = " Leaving file length out of sync with i_size."; const char *te = " Leaving file length out of sync with i_size.";
int err; int err;
...@@ -2347,14 +2340,15 @@ int ntfs_truncate(struct inode *vi) ...@@ -2347,14 +2340,15 @@ int ntfs_truncate(struct inode *vi)
vi->i_ino, err); vi->i_ino, err);
goto err_out; goto err_out;
} }
a = ctx->attr;
/* If the size has not changed there is nothing to do. */ /* If the size has not changed there is nothing to do. */
if (ntfs_attr_size(ctx->attr) == i_size_read(vi)) if (ntfs_attr_size(a) == i_size_read(vi))
goto done; goto done;
// TODO: Implement the truncate... // TODO: Implement the truncate...
ntfs_error(vi->i_sb, "Inode size has changed but this is not " ntfs_error(vi->i_sb, "Inode size has changed but this is not "
"implemented yet. Resetting inode size to old value. " "implemented yet. Resetting inode size to old value. "
" This is most likely a bug in the ntfs driver!"); " This is most likely a bug in the ntfs driver!");
i_size_write(vi, ntfs_attr_size(ctx->attr)); i_size_write(vi, ntfs_attr_size(a));
done: done:
ntfs_attr_put_search_ctx(ctx); ntfs_attr_put_search_ctx(ctx);
unmap_mft_record(ni); unmap_mft_record(ni);
...@@ -2515,18 +2509,18 @@ int ntfs_write_inode(struct inode *vi, int sync) ...@@ -2515,18 +2509,18 @@ int ntfs_write_inode(struct inode *vi, int sync)
nt = utc2ntfs(vi->i_mtime); nt = utc2ntfs(vi->i_mtime);
if (si->last_data_change_time != nt) { if (si->last_data_change_time != nt) {
ntfs_debug("Updating mtime for inode 0x%lx: old = 0x%llx, " ntfs_debug("Updating mtime for inode 0x%lx: old = 0x%llx, "
"new = 0x%llx", vi->i_ino, "new = 0x%llx", vi->i_ino, (long long)
sle64_to_cpu(si->last_data_change_time), sle64_to_cpu(si->last_data_change_time),
sle64_to_cpu(nt)); (long long)sle64_to_cpu(nt));
si->last_data_change_time = nt; si->last_data_change_time = nt;
modified = TRUE; modified = TRUE;
} }
nt = utc2ntfs(vi->i_ctime); nt = utc2ntfs(vi->i_ctime);
if (si->last_mft_change_time != nt) { if (si->last_mft_change_time != nt) {
ntfs_debug("Updating ctime for inode 0x%lx: old = 0x%llx, " ntfs_debug("Updating ctime for inode 0x%lx: old = 0x%llx, "
"new = 0x%llx", vi->i_ino, "new = 0x%llx", vi->i_ino, (long long)
sle64_to_cpu(si->last_mft_change_time), sle64_to_cpu(si->last_mft_change_time),
sle64_to_cpu(nt)); (long long)sle64_to_cpu(nt));
si->last_mft_change_time = nt; si->last_mft_change_time = nt;
modified = TRUE; modified = TRUE;
} }
...@@ -2534,8 +2528,8 @@ int ntfs_write_inode(struct inode *vi, int sync) ...@@ -2534,8 +2528,8 @@ int ntfs_write_inode(struct inode *vi, int sync)
if (si->last_access_time != nt) { if (si->last_access_time != nt) {
ntfs_debug("Updating atime for inode 0x%lx: old = 0x%llx, " ntfs_debug("Updating atime for inode 0x%lx: old = 0x%llx, "
"new = 0x%llx", vi->i_ino, "new = 0x%llx", vi->i_ino,
sle64_to_cpu(si->last_access_time), (long long)sle64_to_cpu(si->last_access_time),
sle64_to_cpu(nt)); (long long)sle64_to_cpu(nt));
si->last_access_time = nt; si->last_access_time = nt;
modified = TRUE; modified = TRUE;
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* inode.h - Defines for inode structures NTFS Linux kernel driver. Part of * inode.h - Defines for inode structures NTFS Linux kernel driver. Part of
* the Linux-NTFS project. * the Linux-NTFS project.
* *
* Copyright (c) 2001-2004 Anton Altaparmakov * Copyright (c) 2001-2005 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon * Copyright (c) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
...@@ -44,6 +44,7 @@ typedef struct _ntfs_inode ntfs_inode; ...@@ -44,6 +44,7 @@ typedef struct _ntfs_inode ntfs_inode;
* fields already provided in the VFS inode. * fields already provided in the VFS inode.
*/ */
struct _ntfs_inode { struct _ntfs_inode {
rwlock_t size_lock; /* Lock serializing access to inode sizes. */
s64 initialized_size; /* Copy from the attribute record. */ s64 initialized_size; /* Copy from the attribute record. */
s64 allocated_size; /* Copy from the attribute record. */ s64 allocated_size; /* Copy from the attribute record. */
unsigned long state; /* NTFS specific flags describing this inode. unsigned long state; /* NTFS specific flags describing this inode.
...@@ -109,7 +110,7 @@ struct _ntfs_inode { ...@@ -109,7 +110,7 @@ struct _ntfs_inode {
u8 block_size_bits; /* Log2 of the above. */ u8 block_size_bits; /* Log2 of the above. */
u8 vcn_size_bits; /* Log2 of the above. */ u8 vcn_size_bits; /* Log2 of the above. */
} index; } index;
struct { /* It is a compressed file or an attribute inode. */ struct { /* It is a compressed/sparse file/attribute inode. */
s64 size; /* Copy of compressed_size from s64 size; /* Copy of compressed_size from
$DATA. */ $DATA. */
u32 block_size; /* Size of a compression block u32 block_size; /* Size of a compression block
...@@ -165,6 +166,7 @@ typedef enum { ...@@ -165,6 +166,7 @@ typedef enum {
NI_Sparse, /* 1: Unnamed data attr is sparse (f). NI_Sparse, /* 1: Unnamed data attr is sparse (f).
1: Create sparse files by default (d). 1: Create sparse files by default (d).
1: Attribute is sparse (a). */ 1: Attribute is sparse (a). */
NI_SparseDisabled, /* 1: May not create sparse regions. */
NI_TruncateFailed, /* 1: Last ntfs_truncate() call failed. */ NI_TruncateFailed, /* 1: Last ntfs_truncate() call failed. */
} ntfs_inode_state_bits; } ntfs_inode_state_bits;
...@@ -217,6 +219,7 @@ NINO_FNS(IndexAllocPresent) ...@@ -217,6 +219,7 @@ NINO_FNS(IndexAllocPresent)
NINO_FNS(Compressed) NINO_FNS(Compressed)
NINO_FNS(Encrypted) NINO_FNS(Encrypted)
NINO_FNS(Sparse) NINO_FNS(Sparse)
NINO_FNS(SparseDisabled)
NINO_FNS(TruncateFailed) NINO_FNS(TruncateFailed)
/* /*
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* layout.h - All NTFS associated on-disk structures. Part of the Linux-NTFS * layout.h - All NTFS associated on-disk structures. Part of the Linux-NTFS
* project. * project.
* *
* Copyright (c) 2001-2004 Anton Altaparmakov * Copyright (c) 2001-2005 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon * Copyright (c) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
...@@ -547,26 +547,44 @@ enum { ...@@ -547,26 +547,44 @@ enum {
COLLATION_NTOFS_ULONG = const_cpu_to_le32(0x10), COLLATION_NTOFS_ULONG = const_cpu_to_le32(0x10),
COLLATION_NTOFS_SID = const_cpu_to_le32(0x11), COLLATION_NTOFS_SID = const_cpu_to_le32(0x11),
COLLATION_NTOFS_SECURITY_HASH = const_cpu_to_le32(0x12), COLLATION_NTOFS_SECURITY_HASH = const_cpu_to_le32(0x12),
COLLATION_NTOFS_ULONGS = const_cpu_to_le32(0x13) COLLATION_NTOFS_ULONGS = const_cpu_to_le32(0x13),
}; };
typedef le32 COLLATION_RULE; typedef le32 COLLATION_RULE;
/* /*
* The flags (32-bit) describing attribute properties in the attribute * The flags (32-bit) describing attribute properties in the attribute
* definition structure. FIXME: This information is from Regis's information * definition structure. FIXME: This information is based on Regis's
* and, according to him, it is not certain and probably incomplete. * information and, according to him, it is not certain and probably
* The INDEXABLE flag is fairly certainly correct as only the file name * incomplete. The INDEXABLE flag is fairly certainly correct as only the file
* attribute has this flag set and this is the only attribute indexed in NT4. * name attribute has this flag set and this is the only attribute indexed in
* NT4.
*/ */
enum { enum {
INDEXABLE = const_cpu_to_le32(0x02), /* Attribute can be ATTR_DEF_INDEXABLE = const_cpu_to_le32(0x02), /* Attribute can be
indexed. */ indexed. */
NEED_TO_REGENERATE = const_cpu_to_le32(0x40), /* Need to regenerate ATTR_DEF_MULTIPLE = const_cpu_to_le32(0x04), /* Attribute type
during regeneration can be present multiple times in the
phase. */ mft records of an inode. */
CAN_BE_NON_RESIDENT = const_cpu_to_le32(0x80), /* Attribute can be ATTR_DEF_NOT_ZERO = const_cpu_to_le32(0x08), /* Attribute value
non-resident. */ must contain at least one non-zero
byte. */
ATTR_DEF_INDEXED_UNIQUE = const_cpu_to_le32(0x10), /* Attribute must be
indexed and the attribute value must be
unique for the attribute type in all of
the mft records of an inode. */
ATTR_DEF_NAMED_UNIQUE = const_cpu_to_le32(0x20), /* Attribute must be
named and the name must be unique for
the attribute type in all of the mft
records of an inode. */
ATTR_DEF_RESIDENT = const_cpu_to_le32(0x40), /* Attribute must be
resident. */
ATTR_DEF_ALWAYS_LOG = const_cpu_to_le32(0x80), /* Always log
modifications to this attribute,
regardless of whether it is resident or
non-resident. Without this, only log
modifications if the attribute is
resident. */
}; };
typedef le32 ATTR_DEF_FLAGS; typedef le32 ATTR_DEF_FLAGS;
...@@ -752,7 +770,8 @@ typedef struct { ...@@ -752,7 +770,8 @@ typedef struct {
clusters in a compression unit. 0 means not clusters in a compression unit. 0 means not
compressed. (This effectively limits the compressed. (This effectively limits the
compression unit size to be a power of two compression unit size to be a power of two
clusters.) WinNT4 only uses a value of 4. */ clusters.) WinNT4 only uses a value of 4.
Sparse files also have this set to 4. */
/* 35*/ u8 reserved[5]; /* Align to 8-byte boundary. */ /* 35*/ u8 reserved[5]; /* Align to 8-byte boundary. */
/* The sizes below are only used when lowest_vcn is zero, as otherwise it would /* The sizes below are only used when lowest_vcn is zero, as otherwise it would
be difficult to keep them up-to-date.*/ be difficult to keep them up-to-date.*/
...@@ -773,9 +792,9 @@ typedef struct { ...@@ -773,9 +792,9 @@ typedef struct {
/* sizeof(uncompressed attr) = 64*/ /* sizeof(uncompressed attr) = 64*/
/* 64*/ sle64 compressed_size; /* Byte size of the attribute /* 64*/ sle64 compressed_size; /* Byte size of the attribute
value after compression. Only present when value after compression. Only present when
compressed. Always is a multiple of the compressed or sparse. Always is a multiple of
cluster size. Represents the actual amount of the cluster size. Represents the actual amount
disk space being used on the disk. */ of disk space being used on the disk. */
/* sizeof(compressed attr) = 72*/ /* sizeof(compressed attr) = 72*/
} __attribute__ ((__packed__)) non_resident; } __attribute__ ((__packed__)) non_resident;
} __attribute__ ((__packed__)) data; } __attribute__ ((__packed__)) data;
...@@ -834,7 +853,7 @@ enum { ...@@ -834,7 +853,7 @@ enum {
/* Note, this is a copy of the corresponding bit from the mft record, /* Note, this is a copy of the corresponding bit from the mft record,
telling us whether this file has a view index present (eg. object id telling us whether this file has a view index present (eg. object id
index, quota index, one of the security indexes or the encrypting index, quota index, one of the security indexes or the encrypting
file system related indexes). */ filesystem related indexes). */
}; };
typedef le32 FILE_ATTR_FLAGS; typedef le32 FILE_ATTR_FLAGS;
...@@ -917,20 +936,12 @@ typedef struct { ...@@ -917,20 +936,12 @@ typedef struct {
/* 56*/ le64 quota_charged; /* Byte size of the charge to /* 56*/ le64 quota_charged; /* Byte size of the charge to
the quota for all streams of the file. Note: Is the quota for all streams of the file. Note: Is
zero if quotas are disabled. */ zero if quotas are disabled. */
/* 64*/ le64 usn; /* Last update sequence number /* 64*/ leUSN usn; /* Last update sequence number
of the file. This is a direct index into the of the file. This is a direct index into the
change (aka usn) journal file. It is zero if transaction log file ($UsnJrnl). It is zero if
the usn journal is disabled. the usn journal is disabled or this file has
NOTE: To disable the journal need to delete not been subject to logging yet. See usnjrnl.h
the journal file itself and to then walk the for details. */
whole mft and set all Usn entries in all mft
records to zero! (This can take a while!)
The journal is FILE_Extend/$UsnJrnl. Win2k
will recreate the journal and initiate
logging if necessary when mounting the
partition. This, in contrast to disabling the
journal is a very fast process, so the user
won't even notice it. */
} __attribute__ ((__packed__)) v3; } __attribute__ ((__packed__)) v3;
/* sizeof() = 72 bytes (NTFS 3.x) */ /* sizeof() = 72 bytes (NTFS 3.x) */
} __attribute__ ((__packed__)) ver; } __attribute__ ((__packed__)) ver;
...@@ -1893,7 +1904,7 @@ enum { ...@@ -1893,7 +1904,7 @@ enum {
VOLUME_FLAGS_MASK = const_cpu_to_le16(0x803f), VOLUME_FLAGS_MASK = const_cpu_to_le16(0x803f),
/* To make our life easier when checking if we must mount read-only. */ /* To make our life easier when checking if we must mount read-only. */
VOLUME_MUST_MOUNT_RO_MASK = const_cpu_to_le16(0x8037), VOLUME_MUST_MOUNT_RO_MASK = const_cpu_to_le16(0x8027),
} __attribute__ ((__packed__)); } __attribute__ ((__packed__));
typedef le16 VOLUME_FLAGS; typedef le16 VOLUME_FLAGS;
......
/* /*
* lcnalloc.c - Cluster (de)allocation code. Part of the Linux-NTFS project. * lcnalloc.c - Cluster (de)allocation code. Part of the Linux-NTFS project.
* *
* Copyright (c) 2004 Anton Altaparmakov * Copyright (c) 2004-2005 Anton Altaparmakov
* *
* This program/include file is free software; you can redistribute it and/or * 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 * modify it under the terms of the GNU General Public License as published
...@@ -60,7 +60,7 @@ int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol, ...@@ -60,7 +60,7 @@ int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol,
if (rl->lcn < 0) if (rl->lcn < 0)
continue; continue;
err = ntfs_bitmap_clear_run(lcnbmp_vi, rl->lcn, rl->length); err = ntfs_bitmap_clear_run(lcnbmp_vi, rl->lcn, rl->length);
if (unlikely(err && (!ret || ret == ENOMEM) && ret != err)) if (unlikely(err && (!ret || ret == -ENOMEM) && ret != err))
ret = err; ret = err;
} }
ntfs_debug("Done."); ntfs_debug("Done.");
...@@ -140,6 +140,7 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn, ...@@ -140,6 +140,7 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn,
LCN zone_start, zone_end, bmp_pos, bmp_initial_pos, last_read_pos, lcn; LCN zone_start, zone_end, bmp_pos, bmp_initial_pos, last_read_pos, lcn;
LCN prev_lcn = 0, prev_run_len = 0, mft_zone_size; LCN prev_lcn = 0, prev_run_len = 0, mft_zone_size;
s64 clusters; s64 clusters;
loff_t i_size;
struct inode *lcnbmp_vi; struct inode *lcnbmp_vi;
runlist_element *rl = NULL; runlist_element *rl = NULL;
struct address_space *mapping; struct address_space *mapping;
...@@ -249,6 +250,7 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn, ...@@ -249,6 +250,7 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn,
clusters = count; clusters = count;
rlpos = rlsize = 0; rlpos = rlsize = 0;
mapping = lcnbmp_vi->i_mapping; mapping = lcnbmp_vi->i_mapping;
i_size = i_size_read(lcnbmp_vi);
while (1) { while (1) {
ntfs_debug("Start of outer while loop: done_zones 0x%x, " ntfs_debug("Start of outer while loop: done_zones 0x%x, "
"search_zone %i, pass %i, zone_start 0x%llx, " "search_zone %i, pass %i, zone_start 0x%llx, "
...@@ -263,7 +265,7 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn, ...@@ -263,7 +265,7 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn,
last_read_pos = bmp_pos >> 3; last_read_pos = bmp_pos >> 3;
ntfs_debug("last_read_pos 0x%llx.", ntfs_debug("last_read_pos 0x%llx.",
(unsigned long long)last_read_pos); (unsigned long long)last_read_pos);
if (last_read_pos > lcnbmp_vi->i_size) { if (last_read_pos > i_size) {
ntfs_debug("End of attribute reached. " ntfs_debug("End of attribute reached. "
"Skipping to zone_pass_done."); "Skipping to zone_pass_done.");
goto zone_pass_done; goto zone_pass_done;
...@@ -287,11 +289,11 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn, ...@@ -287,11 +289,11 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn,
buf_size = last_read_pos & ~PAGE_CACHE_MASK; buf_size = last_read_pos & ~PAGE_CACHE_MASK;
buf = page_address(page) + buf_size; buf = page_address(page) + buf_size;
buf_size = PAGE_CACHE_SIZE - buf_size; buf_size = PAGE_CACHE_SIZE - buf_size;
if (unlikely(last_read_pos + buf_size > lcnbmp_vi->i_size)) if (unlikely(last_read_pos + buf_size > i_size))
buf_size = lcnbmp_vi->i_size - last_read_pos; buf_size = i_size - last_read_pos;
buf_size <<= 3; buf_size <<= 3;
lcn = bmp_pos & 7; lcn = bmp_pos & 7;
bmp_pos &= ~7; bmp_pos &= ~(LCN)7;
ntfs_debug("Before inner while loop: buf_size %i, lcn 0x%llx, " ntfs_debug("Before inner while loop: buf_size %i, lcn 0x%llx, "
"bmp_pos 0x%llx, need_writeback %i.", buf_size, "bmp_pos 0x%llx, need_writeback %i.", buf_size,
(unsigned long long)lcn, (unsigned long long)lcn,
...@@ -309,7 +311,7 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn, ...@@ -309,7 +311,7 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn,
(unsigned int)*byte); (unsigned int)*byte);
/* Skip full bytes. */ /* Skip full bytes. */
if (*byte == 0xff) { if (*byte == 0xff) {
lcn = (lcn + 8) & ~7; lcn = (lcn + 8) & ~(LCN)7;
ntfs_debug("Continuing while loop 1."); ntfs_debug("Continuing while loop 1.");
continue; continue;
} }
...@@ -691,7 +693,7 @@ switch_to_data1_zone: search_zone = 2; ...@@ -691,7 +693,7 @@ switch_to_data1_zone: search_zone = 2;
if (zone == MFT_ZONE || mft_zone_size <= 0) { if (zone == MFT_ZONE || mft_zone_size <= 0) {
ntfs_debug("No free clusters left, going to out."); ntfs_debug("No free clusters left, going to out.");
/* Really no more space left on device. */ /* Really no more space left on device. */
err = ENOSPC; err = -ENOSPC;
goto out; goto out;
} /* zone == DATA_ZONE && mft_zone_size > 0 */ } /* zone == DATA_ZONE && mft_zone_size > 0 */
ntfs_debug("Shrinking mft zone."); ntfs_debug("Shrinking mft zone.");
...@@ -755,13 +757,13 @@ switch_to_data1_zone: search_zone = 2; ...@@ -755,13 +757,13 @@ switch_to_data1_zone: search_zone = 2;
if (rl) { if (rl) {
int err2; int err2;
if (err == ENOSPC) if (err == -ENOSPC)
ntfs_debug("Not enough space to complete allocation, " ntfs_debug("Not enough space to complete allocation, "
"err ENOSPC, first free lcn 0x%llx, " "err -ENOSPC, first free lcn 0x%llx, "
"could allocate up to 0x%llx " "could allocate up to 0x%llx "
"clusters.", "clusters.",
(unsigned long long)rl[0].lcn, (unsigned long long)rl[0].lcn,
(unsigned long long)count - clusters); (unsigned long long)(count - clusters));
/* Deallocate all allocated clusters. */ /* Deallocate all allocated clusters. */
ntfs_debug("Attempting rollback..."); ntfs_debug("Attempting rollback...");
err2 = ntfs_cluster_free_from_rl_nolock(vol, rl); err2 = ntfs_cluster_free_from_rl_nolock(vol, rl);
...@@ -773,10 +775,10 @@ switch_to_data1_zone: search_zone = 2; ...@@ -773,10 +775,10 @@ switch_to_data1_zone: search_zone = 2;
} }
/* Free the runlist. */ /* Free the runlist. */
ntfs_free(rl); ntfs_free(rl);
} else if (err == ENOSPC) } else if (err == -ENOSPC)
ntfs_debug("No space left at all, err = ENOSPC, " ntfs_debug("No space left at all, err = -ENOSPC, first free "
"first free lcn = 0x%llx.", "lcn = 0x%llx.",
(unsigned long long)vol->data1_zone_pos); (long long)vol->data1_zone_pos);
up_write(&vol->lcnbmp_lock); up_write(&vol->lcnbmp_lock);
return ERR_PTR(err); return ERR_PTR(err);
} }
...@@ -846,8 +848,8 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, ...@@ -846,8 +848,8 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count,
total_freed = real_freed = 0; total_freed = real_freed = 0;
/* This returns with ni->runlist locked for reading on success. */ down_read(&ni->runlist.lock);
rl = ntfs_find_vcn(ni, start_vcn, FALSE); rl = ntfs_attr_find_vcn_nolock(ni, start_vcn, FALSE);
if (IS_ERR(rl)) { if (IS_ERR(rl)) {
if (!is_rollback) if (!is_rollback)
ntfs_error(vol->sb, "Failed to find first runlist " ntfs_error(vol->sb, "Failed to find first runlist "
...@@ -861,7 +863,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, ...@@ -861,7 +863,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count,
ntfs_error(vol->sb, "First runlist element has " ntfs_error(vol->sb, "First runlist element has "
"invalid lcn, aborting."); "invalid lcn, aborting.");
err = -EIO; err = -EIO;
goto unl_err_out; goto err_out;
} }
/* Find the starting cluster inside the run that needs freeing. */ /* Find the starting cluster inside the run that needs freeing. */
delta = start_vcn - rl->vcn; delta = start_vcn - rl->vcn;
...@@ -879,7 +881,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, ...@@ -879,7 +881,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count,
if (!is_rollback) if (!is_rollback)
ntfs_error(vol->sb, "Failed to clear first run " ntfs_error(vol->sb, "Failed to clear first run "
"(error %i), aborting.", err); "(error %i), aborting.", err);
goto unl_err_out; goto err_out;
} }
/* We have freed @to_free real clusters. */ /* We have freed @to_free real clusters. */
real_freed = to_free; real_freed = to_free;
...@@ -899,30 +901,15 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, ...@@ -899,30 +901,15 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count,
if (unlikely(rl->lcn < LCN_HOLE)) { if (unlikely(rl->lcn < LCN_HOLE)) {
VCN vcn; VCN vcn;
/* /* Attempt to map runlist. */
* Attempt to map runlist, dropping runlist lock for
* the duration.
*/
vcn = rl->vcn; vcn = rl->vcn;
up_read(&ni->runlist.lock); rl = ntfs_attr_find_vcn_nolock(ni, vcn, FALSE);
err = ntfs_map_runlist(ni, vcn);
if (err) {
if (!is_rollback)
ntfs_error(vol->sb, "Failed to map "
"runlist fragment.");
if (err == -EINVAL || err == -ENOENT)
err = -EIO;
goto err_out;
}
/*
* This returns with ni->runlist locked for reading on
* success.
*/
rl = ntfs_find_vcn(ni, vcn, FALSE);
if (IS_ERR(rl)) { if (IS_ERR(rl)) {
err = PTR_ERR(rl); err = PTR_ERR(rl);
if (!is_rollback) if (!is_rollback)
ntfs_error(vol->sb, "Failed to find " ntfs_error(vol->sb, "Failed to map "
"runlist fragment or "
"failed to find "
"subsequent runlist " "subsequent runlist "
"element."); "element.");
goto err_out; goto err_out;
...@@ -935,7 +922,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, ...@@ -935,7 +922,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count,
(unsigned long long) (unsigned long long)
rl->lcn); rl->lcn);
err = -EIO; err = -EIO;
goto unl_err_out; goto err_out;
} }
} }
/* The number of clusters in this run that need freeing. */ /* The number of clusters in this run that need freeing. */
...@@ -951,7 +938,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, ...@@ -951,7 +938,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count,
if (!is_rollback) if (!is_rollback)
ntfs_error(vol->sb, "Failed to clear " ntfs_error(vol->sb, "Failed to clear "
"subsequent run."); "subsequent run.");
goto unl_err_out; goto err_out;
} }
/* We have freed @to_free real clusters. */ /* We have freed @to_free real clusters. */
real_freed += to_free; real_freed += to_free;
...@@ -972,9 +959,8 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, ...@@ -972,9 +959,8 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count,
/* We are done. Return the number of actually freed clusters. */ /* We are done. Return the number of actually freed clusters. */
ntfs_debug("Done."); ntfs_debug("Done.");
return real_freed; return real_freed;
unl_err_out:
up_read(&ni->runlist.lock);
err_out: err_out:
up_read(&ni->runlist.lock);
if (is_rollback) if (is_rollback)
return err; return err;
/* If no real clusters were freed, no need to rollback. */ /* If no real clusters were freed, no need to rollback. */
......
/* /*
* logfile.c - NTFS kernel journal handling. Part of the Linux-NTFS project. * logfile.c - NTFS kernel journal handling. Part of the Linux-NTFS project.
* *
* Copyright (c) 2002-2004 Anton Altaparmakov * Copyright (c) 2002-2005 Anton Altaparmakov
* *
* This program/include file is free software; you can redistribute it and/or * 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 * modify it under the terms of the GNU General Public License as published
...@@ -410,7 +410,7 @@ static BOOL ntfs_check_and_load_restart_page(struct inode *vi, ...@@ -410,7 +410,7 @@ static BOOL ntfs_check_and_load_restart_page(struct inode *vi,
} }
/** /**
* ntfs_ckeck_logfile - check in the journal if the volume is consistent * ntfs_check_logfile - check the journal for consistency
* @log_vi: struct inode of loaded journal $LogFile to check * @log_vi: struct inode of loaded journal $LogFile to check
* *
* Check the $LogFile journal for consistency and return TRUE if it is * Check the $LogFile journal for consistency and return TRUE if it is
...@@ -443,7 +443,7 @@ BOOL ntfs_check_logfile(struct inode *log_vi) ...@@ -443,7 +443,7 @@ BOOL ntfs_check_logfile(struct inode *log_vi)
/* An empty $LogFile must have been clean before it got emptied. */ /* An empty $LogFile must have been clean before it got emptied. */
if (NVolLogFileEmpty(vol)) if (NVolLogFileEmpty(vol))
goto is_empty; goto is_empty;
size = log_vi->i_size; size = i_size_read(log_vi);
/* Make sure the file doesn't exceed the maximum allowed size. */ /* Make sure the file doesn't exceed the maximum allowed size. */
if (size > MaxLogFileSize) if (size > MaxLogFileSize)
size = MaxLogFileSize; size = MaxLogFileSize;
...@@ -464,7 +464,7 @@ BOOL ntfs_check_logfile(struct inode *log_vi) ...@@ -464,7 +464,7 @@ BOOL ntfs_check_logfile(struct inode *log_vi)
* optimize log_page_size and log_page_bits into constants. * optimize log_page_size and log_page_bits into constants.
*/ */
log_page_bits = generic_ffs(log_page_size) - 1; log_page_bits = generic_ffs(log_page_size) - 1;
size &= ~(log_page_size - 1); size &= ~(s64)(log_page_size - 1);
/* /*
* Ensure the log file is big enough to store at least the two restart * Ensure the log file is big enough to store at least the two restart
* pages and the minimum number of log record pages. * pages and the minimum number of log record pages.
...@@ -689,7 +689,8 @@ BOOL ntfs_empty_logfile(struct inode *log_vi) ...@@ -689,7 +689,8 @@ BOOL ntfs_empty_logfile(struct inode *log_vi)
if (!NVolLogFileEmpty(vol)) { if (!NVolLogFileEmpty(vol)) {
int err; int err;
err = ntfs_attr_set(NTFS_I(log_vi), 0, log_vi->i_size, 0xff); err = ntfs_attr_set(NTFS_I(log_vi), 0, i_size_read(log_vi),
0xff);
if (unlikely(err)) { if (unlikely(err)) {
ntfs_error(vol->sb, "Failed to fill $LogFile with " ntfs_error(vol->sb, "Failed to fill $LogFile with "
"0xff bytes (error code %i).", err); "0xff bytes (error code %i).", err);
......
/** /**
* mft.c - NTFS kernel mft record operations. Part of the Linux-NTFS project. * mft.c - NTFS kernel mft record operations. Part of the Linux-NTFS project.
* *
* Copyright (c) 2001-2004 Anton Altaparmakov * Copyright (c) 2001-2005 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon * Copyright (c) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
*/ */
static inline MFT_RECORD *map_mft_record_page(ntfs_inode *ni) static inline MFT_RECORD *map_mft_record_page(ntfs_inode *ni)
{ {
loff_t i_size;
ntfs_volume *vol = ni->vol; ntfs_volume *vol = ni->vol;
struct inode *mft_vi = vol->mft_ino; struct inode *mft_vi = vol->mft_ino;
struct page *page; struct page *page;
...@@ -60,13 +61,14 @@ static inline MFT_RECORD *map_mft_record_page(ntfs_inode *ni) ...@@ -60,13 +61,14 @@ static inline MFT_RECORD *map_mft_record_page(ntfs_inode *ni)
index = ni->mft_no << vol->mft_record_size_bits >> PAGE_CACHE_SHIFT; index = ni->mft_no << vol->mft_record_size_bits >> PAGE_CACHE_SHIFT;
ofs = (ni->mft_no << vol->mft_record_size_bits) & ~PAGE_CACHE_MASK; ofs = (ni->mft_no << vol->mft_record_size_bits) & ~PAGE_CACHE_MASK;
i_size = i_size_read(mft_vi);
/* The maximum valid index into the page cache for $MFT's data. */ /* The maximum valid index into the page cache for $MFT's data. */
end_index = mft_vi->i_size >> PAGE_CACHE_SHIFT; end_index = i_size >> PAGE_CACHE_SHIFT;
/* If the wanted index is out of bounds the mft record doesn't exist. */ /* If the wanted index is out of bounds the mft record doesn't exist. */
if (unlikely(index >= end_index)) { if (unlikely(index >= end_index)) {
if (index > end_index || (mft_vi->i_size & ~PAGE_CACHE_MASK) < if (index > end_index || (i_size & ~PAGE_CACHE_MASK) < ofs +
ofs + vol->mft_record_size) { vol->mft_record_size) {
page = ERR_PTR(-ENOENT); page = ERR_PTR(-ENOENT);
ntfs_error(vol->sb, "Attemt to read mft record 0x%lx, " ntfs_error(vol->sb, "Attemt to read mft record 0x%lx, "
"which is beyond the end of the mft. " "which is beyond the end of the mft. "
...@@ -285,7 +287,7 @@ MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref, ...@@ -285,7 +287,7 @@ MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref,
} }
unmap_mft_record(ni); unmap_mft_record(ni);
ntfs_error(base_ni->vol->sb, "Found stale extent mft " ntfs_error(base_ni->vol->sb, "Found stale extent mft "
"reference! Corrupt file system. " "reference! Corrupt filesystem. "
"Run chkdsk."); "Run chkdsk.");
return ERR_PTR(-EIO); return ERR_PTR(-EIO);
} }
...@@ -316,7 +318,7 @@ MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref, ...@@ -316,7 +318,7 @@ MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref,
/* Verify the sequence number if it is present. */ /* Verify the sequence number if it is present. */
if (seq_no && (le16_to_cpu(m->sequence_number) != seq_no)) { if (seq_no && (le16_to_cpu(m->sequence_number) != seq_no)) {
ntfs_error(base_ni->vol->sb, "Found stale extent mft " ntfs_error(base_ni->vol->sb, "Found stale extent mft "
"reference! Corrupt file system. Run chkdsk."); "reference! Corrupt filesystem. Run chkdsk.");
destroy_ni = TRUE; destroy_ni = TRUE;
m = ERR_PTR(-EIO); m = ERR_PTR(-EIO);
goto unm_err_out; goto unm_err_out;
...@@ -946,20 +948,23 @@ BOOL ntfs_may_write_mft_record(ntfs_volume *vol, const unsigned long mft_no, ...@@ -946,20 +948,23 @@ BOOL ntfs_may_write_mft_record(ntfs_volume *vol, const unsigned long mft_no,
na.name_len = 0; na.name_len = 0;
na.type = AT_UNUSED; na.type = AT_UNUSED;
/* /*
* For inode 0, i.e. $MFT itself, we cannot use ilookup5() from here or * Optimize inode 0, i.e. $MFT itself, since we have it in memory and
* we deadlock because the inode is already locked by the kernel * we get here for it rather often.
* (fs/fs-writeback.c::__sync_single_inode()) and ilookup5() waits
* until the inode is unlocked before returning it and it never gets
* unlocked because ntfs_should_write_mft_record() never returns. )-:
* Fortunately, we have inode 0 pinned in icache for the duration of
* the mount so we can access it directly.
*/ */
if (!mft_no) { if (!mft_no) {
/* Balance the below iput(). */ /* Balance the below iput(). */
vi = igrab(mft_vi); vi = igrab(mft_vi);
BUG_ON(vi != mft_vi); BUG_ON(vi != mft_vi);
} else } else {
vi = ilookup5(sb, mft_no, (test_t)ntfs_test_inode, &na); /*
* Have to use ilookup5_nowait() since ilookup5() waits for the
* inode lock which causes ntfs to deadlock when a concurrent
* inode write via the inode dirty code paths and the page
* dirty code path of the inode dirty code path when writing
* $MFT occurs.
*/
vi = ilookup5_nowait(sb, mft_no, (test_t)ntfs_test_inode, &na);
}
if (vi) { if (vi) {
ntfs_debug("Base inode 0x%lx is in icache.", mft_no); ntfs_debug("Base inode 0x%lx is in icache.", mft_no);
/* The inode is in icache. */ /* The inode is in icache. */
...@@ -1014,7 +1019,13 @@ BOOL ntfs_may_write_mft_record(ntfs_volume *vol, const unsigned long mft_no, ...@@ -1014,7 +1019,13 @@ BOOL ntfs_may_write_mft_record(ntfs_volume *vol, const unsigned long mft_no,
na.mft_no = MREF_LE(m->base_mft_record); na.mft_no = MREF_LE(m->base_mft_record);
ntfs_debug("Mft record 0x%lx is an extent record. Looking for base " ntfs_debug("Mft record 0x%lx is an extent record. Looking for base "
"inode 0x%lx in icache.", mft_no, na.mft_no); "inode 0x%lx in icache.", mft_no, na.mft_no);
vi = ilookup5(sb, na.mft_no, (test_t)ntfs_test_inode, &na); if (!na.mft_no) {
/* Balance the below iput(). */
vi = igrab(mft_vi);
BUG_ON(vi != mft_vi);
} else
vi = ilookup5_nowait(sb, na.mft_no, (test_t)ntfs_test_inode,
&na);
if (!vi) { if (!vi) {
/* /*
* The base inode is not in icache, write this extent mft * The base inode is not in icache, write this extent mft
...@@ -1121,6 +1132,7 @@ static int ntfs_mft_bitmap_find_and_alloc_free_rec_nolock(ntfs_volume *vol, ...@@ -1121,6 +1132,7 @@ static int ntfs_mft_bitmap_find_and_alloc_free_rec_nolock(ntfs_volume *vol,
ntfs_inode *base_ni) ntfs_inode *base_ni)
{ {
s64 pass_end, ll, data_pos, pass_start, ofs, bit; s64 pass_end, ll, data_pos, pass_start, ofs, bit;
unsigned long flags;
struct address_space *mftbmp_mapping; struct address_space *mftbmp_mapping;
u8 *buf, *byte; u8 *buf, *byte;
struct page *page; struct page *page;
...@@ -1134,9 +1146,13 @@ static int ntfs_mft_bitmap_find_and_alloc_free_rec_nolock(ntfs_volume *vol, ...@@ -1134,9 +1146,13 @@ static int ntfs_mft_bitmap_find_and_alloc_free_rec_nolock(ntfs_volume *vol,
* Set the end of the pass making sure we do not overflow the mft * Set the end of the pass making sure we do not overflow the mft
* bitmap. * bitmap.
*/ */
read_lock_irqsave(&NTFS_I(vol->mft_ino)->size_lock, flags);
pass_end = NTFS_I(vol->mft_ino)->allocated_size >> pass_end = NTFS_I(vol->mft_ino)->allocated_size >>
vol->mft_record_size_bits; vol->mft_record_size_bits;
read_unlock_irqrestore(&NTFS_I(vol->mft_ino)->size_lock, flags);
read_lock_irqsave(&NTFS_I(vol->mftbmp_ino)->size_lock, flags);
ll = NTFS_I(vol->mftbmp_ino)->initialized_size << 3; ll = NTFS_I(vol->mftbmp_ino)->initialized_size << 3;
read_unlock_irqrestore(&NTFS_I(vol->mftbmp_ino)->size_lock, flags);
if (pass_end > ll) if (pass_end > ll)
pass_end = ll; pass_end = ll;
pass = 1; pass = 1;
...@@ -1263,6 +1279,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1263,6 +1279,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
{ {
LCN lcn; LCN lcn;
s64 ll; s64 ll;
unsigned long flags;
struct page *page; struct page *page;
ntfs_inode *mft_ni, *mftbmp_ni; ntfs_inode *mft_ni, *mftbmp_ni;
runlist_element *rl, *rl2 = NULL; runlist_element *rl, *rl2 = NULL;
...@@ -1284,17 +1301,20 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1284,17 +1301,20 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
/* /*
* Determine the last lcn of the mft bitmap. The allocated size of the * Determine the last lcn of the mft bitmap. The allocated size of the
* mft bitmap cannot be zero so we are ok to do this. * mft bitmap cannot be zero so we are ok to do this.
* ntfs_find_vcn() returns the runlist locked on success.
*/ */
rl = ntfs_find_vcn(mftbmp_ni, (mftbmp_ni->allocated_size - 1) >> down_write(&mftbmp_ni->runlist.lock);
vol->cluster_size_bits, TRUE); read_lock_irqsave(&mftbmp_ni->size_lock, flags);
ll = mftbmp_ni->allocated_size;
read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
rl = ntfs_attr_find_vcn_nolock(mftbmp_ni,
(ll - 1) >> vol->cluster_size_bits, TRUE);
if (unlikely(IS_ERR(rl) || !rl->length || rl->lcn < 0)) { if (unlikely(IS_ERR(rl) || !rl->length || rl->lcn < 0)) {
up_write(&mftbmp_ni->runlist.lock);
ntfs_error(vol->sb, "Failed to determine last allocated " ntfs_error(vol->sb, "Failed to determine last allocated "
"cluster of mft bitmap attribute."); "cluster of mft bitmap attribute.");
if (!IS_ERR(rl)) { if (!IS_ERR(rl))
up_write(&mftbmp_ni->runlist.lock);
ret = -EIO; ret = -EIO;
} else else
ret = PTR_ERR(rl); ret = PTR_ERR(rl);
return ret; return ret;
} }
...@@ -1396,7 +1416,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1396,7 +1416,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
BUG_ON(ll < rl2->vcn); BUG_ON(ll < rl2->vcn);
BUG_ON(ll >= rl2->vcn + rl2->length); BUG_ON(ll >= rl2->vcn + rl2->length);
/* Get the size for the new mapping pairs array for this extent. */ /* Get the size for the new mapping pairs array for this extent. */
mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll); mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, -1);
if (unlikely(mp_size <= 0)) { if (unlikely(mp_size <= 0)) {
ntfs_error(vol->sb, "Get size for mapping pairs failed for " ntfs_error(vol->sb, "Get size for mapping pairs failed for "
"mft bitmap attribute extent."); "mft bitmap attribute extent.");
...@@ -1418,6 +1438,8 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1418,6 +1438,8 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
// TODO: Deal with this by moving this extent to a new mft // TODO: Deal with this by moving this extent to a new mft
// record or by starting a new extent in a new mft record or by // record or by starting a new extent in a new mft record or by
// moving other attributes out of this mft record. // moving other attributes out of this mft record.
// Note: It will need to be a special mft record and if none of
// those are available it gets rather complicated...
ntfs_error(vol->sb, "Not enough space in this mft record to " ntfs_error(vol->sb, "Not enough space in this mft record to "
"accomodate extended mft bitmap attribute " "accomodate extended mft bitmap attribute "
"extent. Cannot handle this yet."); "extent. Cannot handle this yet.");
...@@ -1428,7 +1450,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1428,7 +1450,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
/* Generate the mapping pairs array directly into the attr record. */ /* Generate the mapping pairs array directly into the attr record. */
ret = ntfs_mapping_pairs_build(vol, (u8*)a + ret = ntfs_mapping_pairs_build(vol, (u8*)a +
le16_to_cpu(a->data.non_resident.mapping_pairs_offset), le16_to_cpu(a->data.non_resident.mapping_pairs_offset),
mp_size, rl2, ll, NULL); mp_size, rl2, ll, -1, NULL);
if (unlikely(ret)) { if (unlikely(ret)) {
ntfs_error(vol->sb, "Failed to build mapping pairs array for " ntfs_error(vol->sb, "Failed to build mapping pairs array for "
"mft bitmap attribute."); "mft bitmap attribute.");
...@@ -1458,9 +1480,11 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1458,9 +1480,11 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
} }
a = ctx->attr; a = ctx->attr;
} }
write_lock_irqsave(&mftbmp_ni->size_lock, flags);
mftbmp_ni->allocated_size += vol->cluster_size; mftbmp_ni->allocated_size += vol->cluster_size;
a->data.non_resident.allocated_size = a->data.non_resident.allocated_size =
cpu_to_sle64(mftbmp_ni->allocated_size); cpu_to_sle64(mftbmp_ni->allocated_size);
write_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
/* Ensure the changes make it to disk. */ /* Ensure the changes make it to disk. */
flush_dcache_mft_record_page(ctx->ntfs_ino); flush_dcache_mft_record_page(ctx->ntfs_ino);
mark_mft_record_dirty(ctx->ntfs_ino); mark_mft_record_dirty(ctx->ntfs_ino);
...@@ -1476,7 +1500,9 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1476,7 +1500,9 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
0, ctx)) { 0, ctx)) {
ntfs_error(vol->sb, "Failed to find last attribute extent of " ntfs_error(vol->sb, "Failed to find last attribute extent of "
"mft bitmap attribute.%s", es); "mft bitmap attribute.%s", es);
write_lock_irqsave(&mftbmp_ni->size_lock, flags);
mftbmp_ni->allocated_size += vol->cluster_size; mftbmp_ni->allocated_size += vol->cluster_size;
write_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
ntfs_attr_put_search_ctx(ctx); ntfs_attr_put_search_ctx(ctx);
unmap_mft_record(mft_ni); unmap_mft_record(mft_ni);
up_write(&mftbmp_ni->runlist.lock); up_write(&mftbmp_ni->runlist.lock);
...@@ -1512,7 +1538,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1512,7 +1538,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
a->data.non_resident.mapping_pairs_offset), a->data.non_resident.mapping_pairs_offset),
old_alen - le16_to_cpu( old_alen - le16_to_cpu(
a->data.non_resident.mapping_pairs_offset), a->data.non_resident.mapping_pairs_offset),
rl2, ll, NULL)) { rl2, ll, -1, NULL)) {
ntfs_error(vol->sb, "Failed to restore mapping pairs " ntfs_error(vol->sb, "Failed to restore mapping pairs "
"array.%s", es); "array.%s", es);
NVolSetErrors(vol); NVolSetErrors(vol);
...@@ -1550,6 +1576,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1550,6 +1576,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
static int ntfs_mft_bitmap_extend_initialized_nolock(ntfs_volume *vol) static int ntfs_mft_bitmap_extend_initialized_nolock(ntfs_volume *vol)
{ {
s64 old_data_size, old_initialized_size; s64 old_data_size, old_initialized_size;
unsigned long flags;
struct inode *mftbmp_vi; struct inode *mftbmp_vi;
ntfs_inode *mft_ni, *mftbmp_ni; ntfs_inode *mft_ni, *mftbmp_ni;
ntfs_attr_search_ctx *ctx; ntfs_attr_search_ctx *ctx;
...@@ -1583,7 +1610,8 @@ static int ntfs_mft_bitmap_extend_initialized_nolock(ntfs_volume *vol) ...@@ -1583,7 +1610,8 @@ static int ntfs_mft_bitmap_extend_initialized_nolock(ntfs_volume *vol)
goto put_err_out; goto put_err_out;
} }
a = ctx->attr; a = ctx->attr;
old_data_size = mftbmp_vi->i_size; write_lock_irqsave(&mftbmp_ni->size_lock, flags);
old_data_size = i_size_read(mftbmp_vi);
old_initialized_size = mftbmp_ni->initialized_size; old_initialized_size = mftbmp_ni->initialized_size;
/* /*
* We can simply update the initialized_size before filling the space * We can simply update the initialized_size before filling the space
...@@ -1593,11 +1621,12 @@ static int ntfs_mft_bitmap_extend_initialized_nolock(ntfs_volume *vol) ...@@ -1593,11 +1621,12 @@ static int ntfs_mft_bitmap_extend_initialized_nolock(ntfs_volume *vol)
mftbmp_ni->initialized_size += 8; mftbmp_ni->initialized_size += 8;
a->data.non_resident.initialized_size = a->data.non_resident.initialized_size =
cpu_to_sle64(mftbmp_ni->initialized_size); cpu_to_sle64(mftbmp_ni->initialized_size);
if (mftbmp_ni->initialized_size > mftbmp_vi->i_size) { if (mftbmp_ni->initialized_size > old_data_size) {
mftbmp_vi->i_size = mftbmp_ni->initialized_size; i_size_write(mftbmp_vi, mftbmp_ni->initialized_size);
a->data.non_resident.data_size = a->data.non_resident.data_size =
cpu_to_sle64(mftbmp_vi->i_size); cpu_to_sle64(mftbmp_ni->initialized_size);
} }
write_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
/* Ensure the changes make it to disk. */ /* Ensure the changes make it to disk. */
flush_dcache_mft_record_page(ctx->ntfs_ino); flush_dcache_mft_record_page(ctx->ntfs_ino);
mark_mft_record_dirty(ctx->ntfs_ino); mark_mft_record_dirty(ctx->ntfs_ino);
...@@ -1636,22 +1665,28 @@ static int ntfs_mft_bitmap_extend_initialized_nolock(ntfs_volume *vol) ...@@ -1636,22 +1665,28 @@ static int ntfs_mft_bitmap_extend_initialized_nolock(ntfs_volume *vol)
goto err_out; goto err_out;
} }
a = ctx->attr; a = ctx->attr;
write_lock_irqsave(&mftbmp_ni->size_lock, flags);
mftbmp_ni->initialized_size = old_initialized_size; mftbmp_ni->initialized_size = old_initialized_size;
a->data.non_resident.initialized_size = a->data.non_resident.initialized_size =
cpu_to_sle64(old_initialized_size); cpu_to_sle64(old_initialized_size);
if (mftbmp_vi->i_size != old_data_size) { if (i_size_read(mftbmp_vi) != old_data_size) {
mftbmp_vi->i_size = old_data_size; i_size_write(mftbmp_vi, old_data_size);
a->data.non_resident.data_size = cpu_to_sle64(old_data_size); a->data.non_resident.data_size = cpu_to_sle64(old_data_size);
} }
write_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
flush_dcache_mft_record_page(ctx->ntfs_ino); flush_dcache_mft_record_page(ctx->ntfs_ino);
mark_mft_record_dirty(ctx->ntfs_ino); mark_mft_record_dirty(ctx->ntfs_ino);
ntfs_attr_put_search_ctx(ctx); ntfs_attr_put_search_ctx(ctx);
unmap_mft_record(mft_ni); unmap_mft_record(mft_ni);
#ifdef DEBUG
read_lock_irqsave(&mftbmp_ni->size_lock, flags);
ntfs_debug("Restored status of mftbmp: allocated_size 0x%llx, " ntfs_debug("Restored status of mftbmp: allocated_size 0x%llx, "
"data_size 0x%llx, initialized_size 0x%llx.", "data_size 0x%llx, initialized_size 0x%llx.",
(long long)mftbmp_ni->allocated_size, (long long)mftbmp_ni->allocated_size,
(long long)mftbmp_vi->i_size, (long long)i_size_read(mftbmp_vi),
(long long)mftbmp_ni->initialized_size); (long long)mftbmp_ni->initialized_size);
read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
#endif /* DEBUG */
err_out: err_out:
return ret; return ret;
} }
...@@ -1679,7 +1714,8 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1679,7 +1714,8 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
{ {
LCN lcn; LCN lcn;
VCN old_last_vcn; VCN old_last_vcn;
s64 min_nr, nr, ll = 0; s64 min_nr, nr, ll;
unsigned long flags;
ntfs_inode *mft_ni; ntfs_inode *mft_ni;
runlist_element *rl, *rl2; runlist_element *rl, *rl2;
ntfs_attr_search_ctx *ctx = NULL; ntfs_attr_search_ctx *ctx = NULL;
...@@ -1695,23 +1731,25 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1695,23 +1731,25 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
* Determine the preferred allocation location, i.e. the last lcn of * Determine the preferred allocation location, i.e. the last lcn of
* the mft data attribute. The allocated size of the mft data * the mft data attribute. The allocated size of the mft data
* attribute cannot be zero so we are ok to do this. * attribute cannot be zero so we are ok to do this.
* ntfs_find_vcn() returns the runlist locked on success.
*/ */
rl = ntfs_find_vcn(mft_ni, (mft_ni->allocated_size - 1) >> down_write(&mft_ni->runlist.lock);
vol->cluster_size_bits, TRUE); read_lock_irqsave(&mft_ni->size_lock, flags);
ll = mft_ni->allocated_size;
read_unlock_irqrestore(&mft_ni->size_lock, flags);
rl = ntfs_attr_find_vcn_nolock(mft_ni,
(ll - 1) >> vol->cluster_size_bits, TRUE);
if (unlikely(IS_ERR(rl) || !rl->length || rl->lcn < 0)) { if (unlikely(IS_ERR(rl) || !rl->length || rl->lcn < 0)) {
up_write(&mft_ni->runlist.lock);
ntfs_error(vol->sb, "Failed to determine last allocated " ntfs_error(vol->sb, "Failed to determine last allocated "
"cluster of mft data attribute."); "cluster of mft data attribute.");
if (!IS_ERR(rl)) { if (!IS_ERR(rl))
up_write(&mft_ni->runlist.lock);
ret = -EIO; ret = -EIO;
} else else
ret = PTR_ERR(rl); ret = PTR_ERR(rl);
return ret; return ret;
} }
lcn = rl->lcn + rl->length; lcn = rl->lcn + rl->length;
ntfs_debug("Last lcn of mft data attribute is 0x%llx.", ntfs_debug("Last lcn of mft data attribute is 0x%llx.", (long long)lcn);
(long long)lcn);
/* Minimum allocation is one mft record worth of clusters. */ /* Minimum allocation is one mft record worth of clusters. */
min_nr = vol->mft_record_size >> vol->cluster_size_bits; min_nr = vol->mft_record_size >> vol->cluster_size_bits;
if (!min_nr) if (!min_nr)
...@@ -1721,12 +1759,13 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1721,12 +1759,13 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
if (!nr) if (!nr)
nr = min_nr; nr = min_nr;
/* Ensure we do not go above 2^32-1 mft records. */ /* Ensure we do not go above 2^32-1 mft records. */
if (unlikely((mft_ni->allocated_size + read_lock_irqsave(&mft_ni->size_lock, flags);
(nr << vol->cluster_size_bits)) >> ll = mft_ni->allocated_size;
read_unlock_irqrestore(&mft_ni->size_lock, flags);
if (unlikely((ll + (nr << vol->cluster_size_bits)) >>
vol->mft_record_size_bits >= (1ll << 32))) { vol->mft_record_size_bits >= (1ll << 32))) {
nr = min_nr; nr = min_nr;
if (unlikely((mft_ni->allocated_size + if (unlikely((ll + (nr << vol->cluster_size_bits)) >>
(nr << vol->cluster_size_bits)) >>
vol->mft_record_size_bits >= (1ll << 32))) { vol->mft_record_size_bits >= (1ll << 32))) {
ntfs_warning(vol->sb, "Cannot allocate mft record " ntfs_warning(vol->sb, "Cannot allocate mft record "
"because the maximum number of inodes " "because the maximum number of inodes "
...@@ -1772,7 +1811,7 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1772,7 +1811,7 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
return PTR_ERR(rl); return PTR_ERR(rl);
} }
mft_ni->runlist.rl = rl; mft_ni->runlist.rl = rl;
ntfs_debug("Allocated %lli clusters.", nr); ntfs_debug("Allocated %lli clusters.", (long long)nr);
/* Find the last run in the new runlist. */ /* Find the last run in the new runlist. */
for (; rl[1].length; rl++) for (; rl[1].length; rl++)
; ;
...@@ -1808,7 +1847,7 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1808,7 +1847,7 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
BUG_ON(ll < rl2->vcn); BUG_ON(ll < rl2->vcn);
BUG_ON(ll >= rl2->vcn + rl2->length); BUG_ON(ll >= rl2->vcn + rl2->length);
/* Get the size for the new mapping pairs array for this extent. */ /* Get the size for the new mapping pairs array for this extent. */
mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll); mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, -1);
if (unlikely(mp_size <= 0)) { if (unlikely(mp_size <= 0)) {
ntfs_error(vol->sb, "Get size for mapping pairs failed for " ntfs_error(vol->sb, "Get size for mapping pairs failed for "
"mft data attribute extent."); "mft data attribute extent.");
...@@ -1832,7 +1871,11 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1832,7 +1871,11 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
// moving other attributes out of this mft record. // moving other attributes out of this mft record.
// Note: Use the special reserved mft records and ensure that // Note: Use the special reserved mft records and ensure that
// this extent is not required to find the mft record in // this extent is not required to find the mft record in
// question. // question. If no free special records left we would need to
// move an existing record away, insert ours in its place, and
// then place the moved record into the newly allocated space
// and we would then need to update all references to this mft
// record appropriately. This is rather complicated...
ntfs_error(vol->sb, "Not enough space in this mft record to " ntfs_error(vol->sb, "Not enough space in this mft record to "
"accomodate extended mft data attribute " "accomodate extended mft data attribute "
"extent. Cannot handle this yet."); "extent. Cannot handle this yet.");
...@@ -1843,7 +1886,7 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1843,7 +1886,7 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
/* Generate the mapping pairs array directly into the attr record. */ /* Generate the mapping pairs array directly into the attr record. */
ret = ntfs_mapping_pairs_build(vol, (u8*)a + ret = ntfs_mapping_pairs_build(vol, (u8*)a +
le16_to_cpu(a->data.non_resident.mapping_pairs_offset), le16_to_cpu(a->data.non_resident.mapping_pairs_offset),
mp_size, rl2, ll, NULL); mp_size, rl2, ll, -1, NULL);
if (unlikely(ret)) { if (unlikely(ret)) {
ntfs_error(vol->sb, "Failed to build mapping pairs array of " ntfs_error(vol->sb, "Failed to build mapping pairs array of "
"mft data attribute."); "mft data attribute.");
...@@ -1875,9 +1918,11 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1875,9 +1918,11 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
} }
a = ctx->attr; a = ctx->attr;
} }
write_lock_irqsave(&mft_ni->size_lock, flags);
mft_ni->allocated_size += nr << vol->cluster_size_bits; mft_ni->allocated_size += nr << vol->cluster_size_bits;
a->data.non_resident.allocated_size = a->data.non_resident.allocated_size =
cpu_to_sle64(mft_ni->allocated_size); cpu_to_sle64(mft_ni->allocated_size);
write_unlock_irqrestore(&mft_ni->size_lock, flags);
/* Ensure the changes make it to disk. */ /* Ensure the changes make it to disk. */
flush_dcache_mft_record_page(ctx->ntfs_ino); flush_dcache_mft_record_page(ctx->ntfs_ino);
mark_mft_record_dirty(ctx->ntfs_ino); mark_mft_record_dirty(ctx->ntfs_ino);
...@@ -1892,7 +1937,9 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1892,7 +1937,9 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
CASE_SENSITIVE, rl[1].vcn, NULL, 0, ctx)) { CASE_SENSITIVE, rl[1].vcn, NULL, 0, ctx)) {
ntfs_error(vol->sb, "Failed to find last attribute extent of " ntfs_error(vol->sb, "Failed to find last attribute extent of "
"mft data attribute.%s", es); "mft data attribute.%s", es);
write_lock_irqsave(&mft_ni->size_lock, flags);
mft_ni->allocated_size += nr << vol->cluster_size_bits; mft_ni->allocated_size += nr << vol->cluster_size_bits;
write_unlock_irqrestore(&mft_ni->size_lock, flags);
ntfs_attr_put_search_ctx(ctx); ntfs_attr_put_search_ctx(ctx);
unmap_mft_record(mft_ni); unmap_mft_record(mft_ni);
up_write(&mft_ni->runlist.lock); up_write(&mft_ni->runlist.lock);
...@@ -1921,7 +1968,7 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1921,7 +1968,7 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
a->data.non_resident.mapping_pairs_offset), a->data.non_resident.mapping_pairs_offset),
old_alen - le16_to_cpu( old_alen - le16_to_cpu(
a->data.non_resident.mapping_pairs_offset), a->data.non_resident.mapping_pairs_offset),
rl2, ll, NULL)) { rl2, ll, -1, NULL)) {
ntfs_error(vol->sb, "Failed to restore mapping pairs " ntfs_error(vol->sb, "Failed to restore mapping pairs "
"array.%s", es); "array.%s", es);
NVolSetErrors(vol); NVolSetErrors(vol);
...@@ -1991,7 +2038,7 @@ static int ntfs_mft_record_layout(const ntfs_volume *vol, const s64 mft_no, ...@@ -1991,7 +2038,7 @@ static int ntfs_mft_record_layout(const ntfs_volume *vol, const s64 mft_no,
"reports this as corruption, please email " "reports this as corruption, please email "
"linux-ntfs-dev@lists.sourceforge.net stating " "linux-ntfs-dev@lists.sourceforge.net stating "
"that you saw this message and that the " "that you saw this message and that the "
"modified file system created was corrupt. " "modified filesystem created was corrupt. "
"Thank you."); "Thank you.");
} }
/* Set the update sequence number to 1. */ /* Set the update sequence number to 1. */
...@@ -2036,6 +2083,7 @@ static int ntfs_mft_record_layout(const ntfs_volume *vol, const s64 mft_no, ...@@ -2036,6 +2083,7 @@ static int ntfs_mft_record_layout(const ntfs_volume *vol, const s64 mft_no,
*/ */
static int ntfs_mft_record_format(const ntfs_volume *vol, const s64 mft_no) static int ntfs_mft_record_format(const ntfs_volume *vol, const s64 mft_no)
{ {
loff_t i_size;
struct inode *mft_vi = vol->mft_ino; struct inode *mft_vi = vol->mft_ino;
struct page *page; struct page *page;
MFT_RECORD *m; MFT_RECORD *m;
...@@ -2051,10 +2099,11 @@ static int ntfs_mft_record_format(const ntfs_volume *vol, const s64 mft_no) ...@@ -2051,10 +2099,11 @@ static int ntfs_mft_record_format(const ntfs_volume *vol, const s64 mft_no)
index = mft_no << vol->mft_record_size_bits >> PAGE_CACHE_SHIFT; index = mft_no << vol->mft_record_size_bits >> PAGE_CACHE_SHIFT;
ofs = (mft_no << vol->mft_record_size_bits) & ~PAGE_CACHE_MASK; ofs = (mft_no << vol->mft_record_size_bits) & ~PAGE_CACHE_MASK;
/* The maximum valid index into the page cache for $MFT's data. */ /* The maximum valid index into the page cache for $MFT's data. */
end_index = mft_vi->i_size >> PAGE_CACHE_SHIFT; i_size = i_size_read(mft_vi);
end_index = i_size >> PAGE_CACHE_SHIFT;
if (unlikely(index >= end_index)) { if (unlikely(index >= end_index)) {
if (unlikely(index > end_index || ofs + vol->mft_record_size >= if (unlikely(index > end_index || ofs + vol->mft_record_size >=
(mft_vi->i_size & ~PAGE_CACHE_MASK))) { (i_size & ~PAGE_CACHE_MASK))) {
ntfs_error(vol->sb, "Tried to format non-existing mft " ntfs_error(vol->sb, "Tried to format non-existing mft "
"record 0x%llx.", (long long)mft_no); "record 0x%llx.", (long long)mft_no);
return -ENOENT; return -ENOENT;
...@@ -2188,6 +2237,7 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode, ...@@ -2188,6 +2237,7 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
ntfs_inode *base_ni, MFT_RECORD **mrec) ntfs_inode *base_ni, MFT_RECORD **mrec)
{ {
s64 ll, bit, old_data_initialized, old_data_size; s64 ll, bit, old_data_initialized, old_data_size;
unsigned long flags;
struct inode *vi; struct inode *vi;
struct page *page; struct page *page;
ntfs_inode *mft_ni, *mftbmp_ni, *ni; ntfs_inode *mft_ni, *mftbmp_ni, *ni;
...@@ -2237,9 +2287,13 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode, ...@@ -2237,9 +2287,13 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
* the first 24 mft records as they are special and whilst they may not * the first 24 mft records as they are special and whilst they may not
* be in use, we do not allocate from them. * be in use, we do not allocate from them.
*/ */
read_lock_irqsave(&mft_ni->size_lock, flags);
ll = mft_ni->initialized_size >> vol->mft_record_size_bits; ll = mft_ni->initialized_size >> vol->mft_record_size_bits;
if (mftbmp_ni->initialized_size << 3 > ll && read_unlock_irqrestore(&mft_ni->size_lock, flags);
mftbmp_ni->initialized_size > 3) { read_lock_irqsave(&mftbmp_ni->size_lock, flags);
old_data_initialized = mftbmp_ni->initialized_size;
read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
if (old_data_initialized << 3 > ll && old_data_initialized > 3) {
bit = ll; bit = ll;
if (bit < 24) if (bit < 24)
bit = 24; bit = 24;
...@@ -2254,15 +2308,18 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode, ...@@ -2254,15 +2308,18 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
* mft record that we can allocate. * mft record that we can allocate.
* Note: The smallest mft record we allocate is mft record 24. * Note: The smallest mft record we allocate is mft record 24.
*/ */
bit = mftbmp_ni->initialized_size << 3; bit = old_data_initialized << 3;
if (unlikely(bit >= (1ll << 32))) if (unlikely(bit >= (1ll << 32)))
goto max_err_out; goto max_err_out;
read_lock_irqsave(&mftbmp_ni->size_lock, flags);
old_data_size = mftbmp_ni->allocated_size;
ntfs_debug("Status of mftbmp before extension: allocated_size 0x%llx, " ntfs_debug("Status of mftbmp before extension: allocated_size 0x%llx, "
"data_size 0x%llx, initialized_size 0x%llx.", "data_size 0x%llx, initialized_size 0x%llx.",
(long long)mftbmp_ni->allocated_size, (long long)old_data_size,
(long long)vol->mftbmp_ino->i_size, (long long)i_size_read(vol->mftbmp_ino),
(long long)mftbmp_ni->initialized_size); (long long)old_data_initialized);
if (mftbmp_ni->initialized_size + 8 > mftbmp_ni->allocated_size) { read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
if (old_data_initialized + 8 > old_data_size) {
/* Need to extend bitmap by one more cluster. */ /* Need to extend bitmap by one more cluster. */
ntfs_debug("mftbmp: initialized_size + 8 > allocated_size."); ntfs_debug("mftbmp: initialized_size + 8 > allocated_size.");
err = ntfs_mft_bitmap_extend_allocation_nolock(vol); err = ntfs_mft_bitmap_extend_allocation_nolock(vol);
...@@ -2270,12 +2327,16 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode, ...@@ -2270,12 +2327,16 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
up_write(&vol->mftbmp_lock); up_write(&vol->mftbmp_lock);
goto err_out; goto err_out;
} }
#ifdef DEBUG
read_lock_irqsave(&mftbmp_ni->size_lock, flags);
ntfs_debug("Status of mftbmp after allocation extension: " ntfs_debug("Status of mftbmp after allocation extension: "
"allocated_size 0x%llx, data_size 0x%llx, " "allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.", "initialized_size 0x%llx.",
(long long)mftbmp_ni->allocated_size, (long long)mftbmp_ni->allocated_size,
(long long)vol->mftbmp_ino->i_size, (long long)i_size_read(vol->mftbmp_ino),
(long long)mftbmp_ni->initialized_size); (long long)mftbmp_ni->initialized_size);
read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
#endif /* DEBUG */
} }
/* /*
* We now have sufficient allocated space, extend the initialized_size * We now have sufficient allocated space, extend the initialized_size
...@@ -2287,12 +2348,16 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode, ...@@ -2287,12 +2348,16 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
up_write(&vol->mftbmp_lock); up_write(&vol->mftbmp_lock);
goto err_out; goto err_out;
} }
#ifdef DEBUG
read_lock_irqsave(&mftbmp_ni->size_lock, flags);
ntfs_debug("Status of mftbmp after initialized extention: " ntfs_debug("Status of mftbmp after initialized extention: "
"allocated_size 0x%llx, data_size 0x%llx, " "allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.", "initialized_size 0x%llx.",
(long long)mftbmp_ni->allocated_size, (long long)mftbmp_ni->allocated_size,
(long long)vol->mftbmp_ino->i_size, (long long)i_size_read(vol->mftbmp_ino),
(long long)mftbmp_ni->initialized_size); (long long)mftbmp_ni->initialized_size);
read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
#endif /* DEBUG */
ntfs_debug("Found free record (#3), bit 0x%llx.", (long long)bit); ntfs_debug("Found free record (#3), bit 0x%llx.", (long long)bit);
found_free_rec: found_free_rec:
/* @bit is the found free mft record, allocate it in the mft bitmap. */ /* @bit is the found free mft record, allocate it in the mft bitmap. */
...@@ -2314,7 +2379,10 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode, ...@@ -2314,7 +2379,10 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
* parallel allocation could allocate the same mft record as this one. * parallel allocation could allocate the same mft record as this one.
*/ */
ll = (bit + 1) << vol->mft_record_size_bits; ll = (bit + 1) << vol->mft_record_size_bits;
if (ll <= mft_ni->initialized_size) { read_lock_irqsave(&mft_ni->size_lock, flags);
old_data_initialized = mft_ni->initialized_size;
read_unlock_irqrestore(&mft_ni->size_lock, flags);
if (ll <= old_data_initialized) {
ntfs_debug("Allocated mft record already initialized."); ntfs_debug("Allocated mft record already initialized.");
goto mft_rec_already_initialized; goto mft_rec_already_initialized;
} }
...@@ -2325,26 +2393,30 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode, ...@@ -2325,26 +2393,30 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
* actually traversed more than once when a freshly formatted volume is * actually traversed more than once when a freshly formatted volume is
* first written to so it optimizes away nicely in the common case. * first written to so it optimizes away nicely in the common case.
*/ */
read_lock_irqsave(&mft_ni->size_lock, flags);
ntfs_debug("Status of mft data before extension: " ntfs_debug("Status of mft data before extension: "
"allocated_size 0x%llx, data_size 0x%llx, " "allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.", "initialized_size 0x%llx.",
(long long)mft_ni->allocated_size, (long long)mft_ni->allocated_size,
(long long)vol->mft_ino->i_size, (long long)i_size_read(vol->mft_ino),
(long long)mft_ni->initialized_size); (long long)mft_ni->initialized_size);
while (ll > mft_ni->allocated_size) { while (ll > mft_ni->allocated_size) {
read_unlock_irqrestore(&mft_ni->size_lock, flags);
err = ntfs_mft_data_extend_allocation_nolock(vol); err = ntfs_mft_data_extend_allocation_nolock(vol);
if (unlikely(err)) { if (unlikely(err)) {
ntfs_error(vol->sb, "Failed to extend mft data " ntfs_error(vol->sb, "Failed to extend mft data "
"allocation."); "allocation.");
goto undo_mftbmp_alloc_nolock; goto undo_mftbmp_alloc_nolock;
} }
read_lock_irqsave(&mft_ni->size_lock, flags);
ntfs_debug("Status of mft data after allocation extension: " ntfs_debug("Status of mft data after allocation extension: "
"allocated_size 0x%llx, data_size 0x%llx, " "allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.", "initialized_size 0x%llx.",
(long long)mft_ni->allocated_size, (long long)mft_ni->allocated_size,
(long long)vol->mft_ino->i_size, (long long)i_size_read(vol->mft_ino),
(long long)mft_ni->initialized_size); (long long)mft_ni->initialized_size);
} }
read_unlock_irqrestore(&mft_ni->size_lock, flags);
/* /*
* Extend mft data initialized size (and data size of course) to reach * Extend mft data initialized size (and data size of course) to reach
* the allocated mft record, formatting the mft records allong the way. * the allocated mft record, formatting the mft records allong the way.
...@@ -2352,6 +2424,7 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode, ...@@ -2352,6 +2424,7 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
* needed by ntfs_mft_record_format(). We will update the attribute * needed by ntfs_mft_record_format(). We will update the attribute
* record itself in one fell swoop later on. * record itself in one fell swoop later on.
*/ */
write_lock_irqsave(&mft_ni->size_lock, flags);
old_data_initialized = mft_ni->initialized_size; old_data_initialized = mft_ni->initialized_size;
old_data_size = vol->mft_ino->i_size; old_data_size = vol->mft_ino->i_size;
while (ll > mft_ni->initialized_size) { while (ll > mft_ni->initialized_size) {
...@@ -2360,8 +2433,9 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode, ...@@ -2360,8 +2433,9 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
new_initialized_size = mft_ni->initialized_size + new_initialized_size = mft_ni->initialized_size +
vol->mft_record_size; vol->mft_record_size;
mft_no = mft_ni->initialized_size >> vol->mft_record_size_bits; mft_no = mft_ni->initialized_size >> vol->mft_record_size_bits;
if (new_initialized_size > vol->mft_ino->i_size) if (new_initialized_size > i_size_read(vol->mft_ino))
vol->mft_ino->i_size = new_initialized_size; i_size_write(vol->mft_ino, new_initialized_size);
write_unlock_irqrestore(&mft_ni->size_lock, flags);
ntfs_debug("Initializing mft record 0x%llx.", ntfs_debug("Initializing mft record 0x%llx.",
(long long)mft_no); (long long)mft_no);
err = ntfs_mft_record_format(vol, mft_no); err = ntfs_mft_record_format(vol, mft_no);
...@@ -2369,8 +2443,10 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode, ...@@ -2369,8 +2443,10 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
ntfs_error(vol->sb, "Failed to format mft record."); ntfs_error(vol->sb, "Failed to format mft record.");
goto undo_data_init; goto undo_data_init;
} }
write_lock_irqsave(&mft_ni->size_lock, flags);
mft_ni->initialized_size = new_initialized_size; mft_ni->initialized_size = new_initialized_size;
} }
write_unlock_irqrestore(&mft_ni->size_lock, flags);
record_formatted = TRUE; record_formatted = TRUE;
/* Update the mft data attribute record to reflect the new sizes. */ /* Update the mft data attribute record to reflect the new sizes. */
m = map_mft_record(mft_ni); m = map_mft_record(mft_ni);
...@@ -2396,22 +2472,27 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode, ...@@ -2396,22 +2472,27 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
goto undo_data_init; goto undo_data_init;
} }
a = ctx->attr; a = ctx->attr;
read_lock_irqsave(&mft_ni->size_lock, flags);
a->data.non_resident.initialized_size = a->data.non_resident.initialized_size =
cpu_to_sle64(mft_ni->initialized_size); cpu_to_sle64(mft_ni->initialized_size);
a->data.non_resident.data_size = cpu_to_sle64(vol->mft_ino->i_size); a->data.non_resident.data_size =
cpu_to_sle64(i_size_read(vol->mft_ino));
read_unlock_irqrestore(&mft_ni->size_lock, flags);
/* Ensure the changes make it to disk. */ /* Ensure the changes make it to disk. */
flush_dcache_mft_record_page(ctx->ntfs_ino); flush_dcache_mft_record_page(ctx->ntfs_ino);
mark_mft_record_dirty(ctx->ntfs_ino); mark_mft_record_dirty(ctx->ntfs_ino);
ntfs_attr_put_search_ctx(ctx); ntfs_attr_put_search_ctx(ctx);
unmap_mft_record(mft_ni); unmap_mft_record(mft_ni);
read_lock_irqsave(&mft_ni->size_lock, flags);
ntfs_debug("Status of mft data after mft record initialization: " ntfs_debug("Status of mft data after mft record initialization: "
"allocated_size 0x%llx, data_size 0x%llx, " "allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.", "initialized_size 0x%llx.",
(long long)mft_ni->allocated_size, (long long)mft_ni->allocated_size,
(long long)vol->mft_ino->i_size, (long long)i_size_read(vol->mft_ino),
(long long)mft_ni->initialized_size); (long long)mft_ni->initialized_size);
BUG_ON(vol->mft_ino->i_size > mft_ni->allocated_size); BUG_ON(i_size_read(vol->mft_ino) > mft_ni->allocated_size);
BUG_ON(mft_ni->initialized_size > vol->mft_ino->i_size); BUG_ON(mft_ni->initialized_size > i_size_read(vol->mft_ino));
read_unlock_irqrestore(&mft_ni->size_lock, flags);
mft_rec_already_initialized: mft_rec_already_initialized:
/* /*
* We can finally drop the mft bitmap lock as the mft data attribute * We can finally drop the mft bitmap lock as the mft data attribute
...@@ -2652,8 +2733,10 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode, ...@@ -2652,8 +2733,10 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
*mrec = m; *mrec = m;
return ni; return ni;
undo_data_init: undo_data_init:
write_lock_irqsave(&mft_ni->size_lock, flags);
mft_ni->initialized_size = old_data_initialized; mft_ni->initialized_size = old_data_initialized;
vol->mft_ino->i_size = old_data_size; i_size_write(vol->mft_ino, old_data_size);
write_unlock_irqrestore(&mft_ni->size_lock, flags);
goto undo_mftbmp_alloc_nolock; goto undo_mftbmp_alloc_nolock;
undo_mftbmp_alloc: undo_mftbmp_alloc:
down_write(&vol->mftbmp_lock); down_write(&vol->mftbmp_lock);
......
...@@ -153,7 +153,6 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, ...@@ -153,7 +153,6 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
ntfs_error(vol->sb, "ntfs_iget(0x%lx) failed with " ntfs_error(vol->sb, "ntfs_iget(0x%lx) failed with "
"error code %li.", dent_ino, "error code %li.", dent_ino,
PTR_ERR(dent_inode)); PTR_ERR(dent_inode));
if (name)
kfree(name); kfree(name);
/* Return the error code. */ /* Return the error code. */
return (struct dentry *)dent_inode; return (struct dentry *)dent_inode;
...@@ -380,7 +379,7 @@ struct inode_operations ntfs_dir_inode_ops = { ...@@ -380,7 +379,7 @@ struct inode_operations ntfs_dir_inode_ops = {
* Return the dentry of the parent directory on success or the error code on * Return the dentry of the parent directory on success or the error code on
* error (IS_ERR() is true). * error (IS_ERR() is true).
*/ */
struct dentry *ntfs_get_parent(struct dentry *child_dent) static struct dentry *ntfs_get_parent(struct dentry *child_dent)
{ {
struct inode *vi = child_dent->d_inode; struct inode *vi = child_dent->d_inode;
ntfs_inode *ni = NTFS_I(vi); ntfs_inode *ni = NTFS_I(vi);
...@@ -465,7 +464,7 @@ struct dentry *ntfs_get_parent(struct dentry *child_dent) ...@@ -465,7 +464,7 @@ struct dentry *ntfs_get_parent(struct dentry *child_dent)
* *
* Return the dentry on success or the error code on error (IS_ERR() is true). * Return the dentry on success or the error code on error (IS_ERR() is true).
*/ */
struct dentry *ntfs_get_dentry(struct super_block *sb, void *fh) static struct dentry *ntfs_get_dentry(struct super_block *sb, void *fh)
{ {
struct inode *vi; struct inode *vi;
struct dentry *dent; struct dentry *dent;
...@@ -496,3 +495,30 @@ struct dentry *ntfs_get_dentry(struct super_block *sb, void *fh) ...@@ -496,3 +495,30 @@ struct dentry *ntfs_get_dentry(struct super_block *sb, void *fh)
ntfs_debug("Done for inode 0x%lx, generation 0x%x.", ino, gen); ntfs_debug("Done for inode 0x%lx, generation 0x%x.", ino, gen);
return dent; return dent;
} }
/**
* Export operations allowing NFS exporting of mounted NTFS partitions.
*
* We use the default ->decode_fh() and ->encode_fh() for now. Note that they
* use 32 bits to store the inode number which is an unsigned long so on 64-bit
* architectures is usually 64 bits so it would all fail horribly on huge
* volumes. I guess we need to define our own encode and decode fh functions
* that store 64-bit inode numbers at some point but for now we will ignore the
* problem...
*
* We also use the default ->get_name() helper (used by ->decode_fh() via
* fs/exportfs/expfs.c::find_exported_dentry()) as that is completely fs
* independent.
*
* The default ->get_parent() just returns -EACCES so we have to provide our
* own and the default ->get_dentry() is incompatible with NTFS due to not
* allowing the inode number 0 which is used in NTFS for the system file $MFT
* and due to using iget() whereas NTFS needs ntfs_iget().
*/
struct export_operations ntfs_export_ops = {
.get_parent = ntfs_get_parent, /* Find the parent of a given
directory. */
.get_dentry = ntfs_get_dentry, /* Find a dentry for the inode
given a file handle
sub-fragment. */
};
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* ntfs.h - Defines for NTFS Linux kernel driver. Part of the Linux-NTFS * ntfs.h - Defines for NTFS Linux kernel driver. Part of the Linux-NTFS
* project. * project.
* *
* Copyright (c) 2001-2004 Anton Altaparmakov * Copyright (c) 2001-2005 Anton Altaparmakov
* Copyright (C) 2002 Richard Russon * Copyright (C) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/nls.h> #include <linux/nls.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/pagemap.h>
#include "types.h" #include "types.h"
#include "volume.h" #include "volume.h"
...@@ -41,6 +42,9 @@ typedef enum { ...@@ -41,6 +42,9 @@ typedef enum {
NTFS_BLOCK_SIZE_BITS = 9, NTFS_BLOCK_SIZE_BITS = 9,
NTFS_SB_MAGIC = 0x5346544e, /* 'NTFS' */ NTFS_SB_MAGIC = 0x5346544e, /* 'NTFS' */
NTFS_MAX_NAME_LEN = 255, NTFS_MAX_NAME_LEN = 255,
NTFS_MAX_ATTR_NAME_LEN = 255,
NTFS_MAX_CLUSTER_SIZE = 64 * 1024, /* 64kiB */
NTFS_MAX_PAGES_PER_CLUSTER = NTFS_MAX_CLUSTER_SIZE / PAGE_CACHE_SIZE,
} NTFS_CONSTANTS; } NTFS_CONSTANTS;
/* Global variables. */ /* Global variables. */
...@@ -65,6 +69,8 @@ extern struct inode_operations ntfs_dir_inode_ops; ...@@ -65,6 +69,8 @@ extern struct inode_operations ntfs_dir_inode_ops;
extern struct file_operations ntfs_empty_file_ops; extern struct file_operations ntfs_empty_file_ops;
extern struct inode_operations ntfs_empty_inode_ops; extern struct inode_operations ntfs_empty_inode_ops;
extern struct export_operations ntfs_export_ops;
/** /**
* NTFS_SB - return the ntfs volume given a vfs super block * NTFS_SB - return the ntfs volume given a vfs super block
* @sb: VFS super block * @sb: VFS super block
......
/** /**
* runlist.c - NTFS runlist handling code. Part of the Linux-NTFS project. * runlist.c - NTFS runlist handling code. Part of the Linux-NTFS project.
* *
* Copyright (c) 2001-2004 Anton Altaparmakov * Copyright (c) 2001-2005 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon * Copyright (c) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
...@@ -59,7 +59,7 @@ static inline void ntfs_rl_mc(runlist_element *dstbase, int dst, ...@@ -59,7 +59,7 @@ static inline void ntfs_rl_mc(runlist_element *dstbase, int dst,
* *
* As the runlists grow, more memory will be required. To prevent the * As the runlists grow, more memory will be required. To prevent the
* kernel having to allocate and reallocate large numbers of small bits of * kernel having to allocate and reallocate large numbers of small bits of
* memory, this function returns and entire page of memory. * memory, this function returns an entire page of memory.
* *
* It is up to the caller to serialize access to the runlist @rl. * It is up to the caller to serialize access to the runlist @rl.
* *
...@@ -113,8 +113,11 @@ static inline BOOL ntfs_are_rl_mergeable(runlist_element *dst, ...@@ -113,8 +113,11 @@ static inline BOOL ntfs_are_rl_mergeable(runlist_element *dst,
BUG_ON(!dst); BUG_ON(!dst);
BUG_ON(!src); BUG_ON(!src);
if ((dst->lcn < 0) || (src->lcn < 0)) /* Are we merging holes? */ if ((dst->lcn < 0) || (src->lcn < 0)) { /* Are we merging holes? */
if (dst->lcn == LCN_HOLE && src->lcn == LCN_HOLE)
return TRUE;
return FALSE; return FALSE;
}
if ((dst->lcn + dst->length) != src->lcn) /* Are the runs contiguous? */ if ((dst->lcn + dst->length) != src->lcn) /* Are the runs contiguous? */
return FALSE; return FALSE;
if ((dst->vcn + dst->length) != src->vcn) /* Are the runs misaligned? */ if ((dst->vcn + dst->length) != src->vcn) /* Are the runs misaligned? */
...@@ -855,31 +858,43 @@ runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, ...@@ -855,31 +858,43 @@ runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol,
if (!attr->data.non_resident.lowest_vcn) { if (!attr->data.non_resident.lowest_vcn) {
VCN max_cluster; VCN max_cluster;
max_cluster = (sle64_to_cpu( max_cluster = ((sle64_to_cpu(
attr->data.non_resident.allocated_size) + attr->data.non_resident.allocated_size) +
vol->cluster_size - 1) >> vol->cluster_size - 1) >>
vol->cluster_size_bits; vol->cluster_size_bits) - 1;
/* /*
* If there is a difference between the highest_vcn and the * A highest_vcn of zero means this is a single extent
* highest cluster, the runlist is either corrupt or, more * attribute so simply terminate the runlist with LCN_ENOENT).
* likely, there are more extents following this one.
*/ */
if (deltaxcn < --max_cluster) { if (deltaxcn) {
ntfs_debug("More extents to follow; deltaxcn = 0x%llx, " /*
"max_cluster = 0x%llx", * If there is a difference between the highest_vcn and
* the highest cluster, the runlist is either corrupt
* or, more likely, there are more extents following
* this one.
*/
if (deltaxcn < max_cluster) {
ntfs_debug("More extents to follow; deltaxcn "
"= 0x%llx, max_cluster = "
"0x%llx",
(unsigned long long)deltaxcn, (unsigned long long)deltaxcn,
(unsigned long long)max_cluster); (unsigned long long)
max_cluster);
rl[rlpos].vcn = vcn; rl[rlpos].vcn = vcn;
vcn += rl[rlpos].length = max_cluster - deltaxcn; vcn += rl[rlpos].length = max_cluster -
deltaxcn;
rl[rlpos].lcn = LCN_RL_NOT_MAPPED; rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
rlpos++; rlpos++;
} else if (unlikely(deltaxcn > max_cluster)) { } else if (unlikely(deltaxcn > max_cluster)) {
ntfs_error(vol->sb, "Corrupt attribute. deltaxcn = " ntfs_error(vol->sb, "Corrupt attribute. "
"0x%llx, max_cluster = 0x%llx", "deltaxcn = 0x%llx, "
"max_cluster = 0x%llx",
(unsigned long long)deltaxcn, (unsigned long long)deltaxcn,
(unsigned long long)max_cluster); (unsigned long long)
max_cluster);
goto mpa_err; goto mpa_err;
} }
}
rl[rlpos].lcn = LCN_ENOENT; rl[rlpos].lcn = LCN_ENOENT;
} else /* Not the base extent. There may be more extents to follow. */ } else /* Not the base extent. There may be more extents to follow. */
rl[rlpos].lcn = LCN_RL_NOT_MAPPED; rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
...@@ -918,17 +933,18 @@ runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, ...@@ -918,17 +933,18 @@ runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol,
* *
* It is up to the caller to serialize access to the runlist @rl. * It is up to the caller to serialize access to the runlist @rl.
* *
* Since lcns must be >= 0, we use negative return values with special meaning: * Since lcns must be >= 0, we use negative return codes with special meaning:
* *
* Return value Meaning / Description * Return code Meaning / Description
* ================================================== * ==================================================
* -1 = LCN_HOLE Hole / not allocated on disk. * LCN_HOLE Hole / not allocated on disk.
* -2 = LCN_RL_NOT_MAPPED This is part of the runlist which has not been * LCN_RL_NOT_MAPPED This is part of the runlist which has not been
* inserted into the runlist yet. * inserted into the runlist yet.
* -3 = LCN_ENOENT There is no such vcn in the attribute. * LCN_ENOENT There is no such vcn in the attribute.
* *
* Locking: - The caller must have locked the runlist (for reading or writing). * Locking: - The caller must have locked the runlist (for reading or writing).
* - This function does not touch the lock. * - This function does not touch the lock, nor does it modify the
* runlist.
*/ */
LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn) LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn)
{ {
...@@ -964,6 +980,39 @@ LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn) ...@@ -964,6 +980,39 @@ LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn)
return LCN_ENOENT; return LCN_ENOENT;
} }
#ifdef NTFS_RW
/**
* ntfs_rl_find_vcn_nolock - find a vcn in a runlist
* @rl: runlist to search
* @vcn: vcn to find
*
* Find the virtual cluster number @vcn in the runlist @rl and return the
* address of the runlist element containing the @vcn on success.
*
* Return NULL if @rl is NULL or @vcn is in an unmapped part/out of bounds of
* the runlist.
*
* Locking: The runlist must be locked on entry.
*/
runlist_element *ntfs_rl_find_vcn_nolock(runlist_element *rl, const VCN vcn)
{
BUG_ON(vcn < 0);
if (unlikely(!rl || vcn < rl[0].vcn))
return NULL;
while (likely(rl->length)) {
if (unlikely(vcn < rl[1].vcn)) {
if (likely(rl->lcn >= LCN_HOLE))
return rl;
return NULL;
}
rl++;
}
if (likely(rl->lcn == LCN_ENOENT))
return rl;
return NULL;
}
/** /**
* ntfs_get_nr_significant_bytes - get number of bytes needed to store a number * ntfs_get_nr_significant_bytes - get number of bytes needed to store a number
* @n: number for which to get the number of bytes for * @n: number for which to get the number of bytes for
...@@ -999,10 +1048,17 @@ static inline int ntfs_get_nr_significant_bytes(const s64 n) ...@@ -999,10 +1048,17 @@ static inline int ntfs_get_nr_significant_bytes(const s64 n)
* ntfs_get_size_for_mapping_pairs - get bytes needed for mapping pairs array * ntfs_get_size_for_mapping_pairs - get bytes needed for mapping pairs array
* @vol: ntfs volume (needed for the ntfs version) * @vol: ntfs volume (needed for the ntfs version)
* @rl: locked runlist to determine the size of the mapping pairs of * @rl: locked runlist to determine the size of the mapping pairs of
* @start_vcn: vcn at which to start the mapping pairs array * @first_vcn: first vcn which to include in the mapping pairs array
* @last_vcn: last vcn which to include in the mapping pairs array
* *
* Walk the locked runlist @rl and calculate the size in bytes of the mapping * Walk the locked runlist @rl and calculate the size in bytes of the mapping
* pairs array corresponding to the runlist @rl, starting at vcn @start_vcn. * pairs array corresponding to the runlist @rl, starting at vcn @first_vcn and
* finishing with vcn @last_vcn.
*
* A @last_vcn of -1 means end of runlist and in that case the size of the
* mapping pairs array corresponding to the runlist starting at vcn @first_vcn
* and finishing at the end of the runlist is determined.
*
* This for example allows us to allocate a buffer of the right size when * This for example allows us to allocate a buffer of the right size when
* building the mapping pairs array. * building the mapping pairs array.
* *
...@@ -1018,34 +1074,50 @@ static inline int ntfs_get_nr_significant_bytes(const s64 n) ...@@ -1018,34 +1074,50 @@ static inline int ntfs_get_nr_significant_bytes(const s64 n)
* remains locked throughout, and is left locked upon return. * remains locked throughout, and is left locked upon return.
*/ */
int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
const runlist_element *rl, const VCN start_vcn) const runlist_element *rl, const VCN first_vcn,
const VCN last_vcn)
{ {
LCN prev_lcn; LCN prev_lcn;
int rls; int rls;
BOOL the_end = FALSE;
BUG_ON(start_vcn < 0); BUG_ON(first_vcn < 0);
BUG_ON(last_vcn < -1);
BUG_ON(last_vcn >= 0 && first_vcn > last_vcn);
if (!rl) { if (!rl) {
BUG_ON(start_vcn); BUG_ON(first_vcn);
BUG_ON(last_vcn > 0);
return 1; return 1;
} }
/* Skip to runlist element containing @start_vcn. */ /* Skip to runlist element containing @first_vcn. */
while (rl->length && start_vcn >= rl[1].vcn) while (rl->length && first_vcn >= rl[1].vcn)
rl++; rl++;
if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn) if (unlikely((!rl->length && first_vcn > rl->vcn) ||
first_vcn < rl->vcn))
return -EINVAL; return -EINVAL;
prev_lcn = 0; prev_lcn = 0;
/* Always need the termining zero byte. */ /* Always need the termining zero byte. */
rls = 1; rls = 1;
/* Do the first partial run if present. */ /* Do the first partial run if present. */
if (start_vcn > rl->vcn) { if (first_vcn > rl->vcn) {
s64 delta; s64 delta, length = rl->length;
/* We know rl->length != 0 already. */ /* We know rl->length != 0 already. */
if (rl->length < 0 || rl->lcn < LCN_HOLE) if (unlikely(length < 0 || rl->lcn < LCN_HOLE))
goto err_out; goto err_out;
delta = start_vcn - rl->vcn; /*
* If @stop_vcn is given and finishes inside this run, cap the
* run length.
*/
if (unlikely(last_vcn >= 0 && rl[1].vcn > last_vcn)) {
s64 s1 = last_vcn + 1;
if (unlikely(rl[1].vcn > s1))
length = s1 - rl->vcn;
the_end = TRUE;
}
delta = first_vcn - rl->vcn;
/* Header byte + length. */ /* Header byte + length. */
rls += 1 + ntfs_get_nr_significant_bytes(rl->length - delta); rls += 1 + ntfs_get_nr_significant_bytes(length - delta);
/* /*
* If the logical cluster number (lcn) denotes a hole and we * If the logical cluster number (lcn) denotes a hole and we
* are on NTFS 3.0+, we don't store it at all, i.e. we need * are on NTFS 3.0+, we don't store it at all, i.e. we need
...@@ -1053,9 +1125,9 @@ int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, ...@@ -1053,9 +1125,9 @@ int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
* Note: this assumes that on NTFS 1.2-, holes are stored with * Note: this assumes that on NTFS 1.2-, holes are stored with
* an lcn of -1 and not a delta_lcn of -1 (unless both are -1). * an lcn of -1 and not a delta_lcn of -1 (unless both are -1).
*/ */
if (rl->lcn >= 0 || vol->major_ver < 3) { if (likely(rl->lcn >= 0 || vol->major_ver < 3)) {
prev_lcn = rl->lcn; prev_lcn = rl->lcn;
if (rl->lcn >= 0) if (likely(rl->lcn >= 0))
prev_lcn += delta; prev_lcn += delta;
/* Change in lcn. */ /* Change in lcn. */
rls += ntfs_get_nr_significant_bytes(prev_lcn); rls += ntfs_get_nr_significant_bytes(prev_lcn);
...@@ -1064,11 +1136,23 @@ int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, ...@@ -1064,11 +1136,23 @@ int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
rl++; rl++;
} }
/* Do the full runs. */ /* Do the full runs. */
for (; rl->length; rl++) { for (; rl->length && !the_end; rl++) {
if (rl->length < 0 || rl->lcn < LCN_HOLE) s64 length = rl->length;
if (unlikely(length < 0 || rl->lcn < LCN_HOLE))
goto err_out; goto err_out;
/*
* If @stop_vcn is given and finishes inside this run, cap the
* run length.
*/
if (unlikely(last_vcn >= 0 && rl[1].vcn > last_vcn)) {
s64 s1 = last_vcn + 1;
if (unlikely(rl[1].vcn > s1))
length = s1 - rl->vcn;
the_end = TRUE;
}
/* Header byte + length. */ /* Header byte + length. */
rls += 1 + ntfs_get_nr_significant_bytes(rl->length); rls += 1 + ntfs_get_nr_significant_bytes(length);
/* /*
* If the logical cluster number (lcn) denotes a hole and we * If the logical cluster number (lcn) denotes a hole and we
* are on NTFS 3.0+, we don't store it at all, i.e. we need * are on NTFS 3.0+, we don't store it at all, i.e. we need
...@@ -1076,7 +1160,7 @@ int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, ...@@ -1076,7 +1160,7 @@ int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
* Note: this assumes that on NTFS 1.2-, holes are stored with * Note: this assumes that on NTFS 1.2-, holes are stored with
* an lcn of -1 and not a delta_lcn of -1 (unless both are -1). * an lcn of -1 and not a delta_lcn of -1 (unless both are -1).
*/ */
if (rl->lcn >= 0 || vol->major_ver < 3) { if (likely(rl->lcn >= 0 || vol->major_ver < 3)) {
/* Change in lcn. */ /* Change in lcn. */
rls += ntfs_get_nr_significant_bytes(rl->lcn - rls += ntfs_get_nr_significant_bytes(rl->lcn -
prev_lcn); prev_lcn);
...@@ -1119,7 +1203,7 @@ static inline int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max, ...@@ -1119,7 +1203,7 @@ static inline int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max,
i = 0; i = 0;
do { do {
if (dst > dst_max) if (unlikely(dst > dst_max))
goto err_out; goto err_out;
*dst++ = l & 0xffll; *dst++ = l & 0xffll;
l >>= 8; l >>= 8;
...@@ -1128,12 +1212,12 @@ static inline int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max, ...@@ -1128,12 +1212,12 @@ static inline int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max,
j = (n >> 8 * (i - 1)) & 0xff; j = (n >> 8 * (i - 1)) & 0xff;
/* If the sign bit is wrong, we need an extra byte. */ /* If the sign bit is wrong, we need an extra byte. */
if (n < 0 && j >= 0) { if (n < 0 && j >= 0) {
if (dst > dst_max) if (unlikely(dst > dst_max))
goto err_out; goto err_out;
i++; i++;
*dst = (s8)-1; *dst = (s8)-1;
} else if (n > 0 && j < 0) { } else if (n > 0 && j < 0) {
if (dst > dst_max) if (unlikely(dst > dst_max))
goto err_out; goto err_out;
i++; i++;
*dst = (s8)0; *dst = (s8)0;
...@@ -1149,13 +1233,18 @@ static inline int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max, ...@@ -1149,13 +1233,18 @@ static inline int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max,
* @dst: destination buffer to which to write the mapping pairs array * @dst: destination buffer to which to write the mapping pairs array
* @dst_len: size of destination buffer @dst in bytes * @dst_len: size of destination buffer @dst in bytes
* @rl: locked runlist for which to build the mapping pairs array * @rl: locked runlist for which to build the mapping pairs array
* @start_vcn: vcn at which to start the mapping pairs array * @first_vcn: first vcn which to include in the mapping pairs array
* @last_vcn: last vcn which to include in the mapping pairs array
* @stop_vcn: first vcn outside destination buffer on success or -ENOSPC * @stop_vcn: first vcn outside destination buffer on success or -ENOSPC
* *
* Create the mapping pairs array from the locked runlist @rl, starting at vcn * Create the mapping pairs array from the locked runlist @rl, starting at vcn
* @start_vcn and save the array in @dst. @dst_len is the size of @dst in * @first_vcn and finishing with vcn @last_vcn and save the array in @dst.
* bytes and it should be at least equal to the value obtained by calling * @dst_len is the size of @dst in bytes and it should be at least equal to the
* ntfs_get_size_for_mapping_pairs(). * value obtained by calling ntfs_get_size_for_mapping_pairs().
*
* A @last_vcn of -1 means end of runlist and in that case the mapping pairs
* array corresponding to the runlist starting at vcn @first_vcn and finishing
* at the end of the runlist is created.
* *
* If @rl is NULL, just write a single terminator byte to @dst. * If @rl is NULL, just write a single terminator byte to @dst.
* *
...@@ -1164,7 +1253,7 @@ static inline int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max, ...@@ -1164,7 +1253,7 @@ static inline int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max,
* been filled with all the mapping pairs that will fit, thus it can be treated * been filled with all the mapping pairs that will fit, thus it can be treated
* as partial success, in that a new attribute extent needs to be created or * as partial success, in that a new attribute extent needs to be created or
* the next extent has to be used and the mapping pairs build has to be * the next extent has to be used and the mapping pairs build has to be
* continued with @start_vcn set to *@stop_vcn. * continued with @first_vcn set to *@stop_vcn.
* *
* Return 0 on success and -errno on error. The following error codes are * Return 0 on success and -errno on error. The following error codes are
* defined: * defined:
...@@ -1178,27 +1267,32 @@ static inline int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max, ...@@ -1178,27 +1267,32 @@ static inline int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max,
*/ */
int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst, int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst,
const int dst_len, const runlist_element *rl, const int dst_len, const runlist_element *rl,
const VCN start_vcn, VCN *const stop_vcn) const VCN first_vcn, const VCN last_vcn, VCN *const stop_vcn)
{ {
LCN prev_lcn; LCN prev_lcn;
s8 *dst_max, *dst_next; s8 *dst_max, *dst_next;
int err = -ENOSPC; int err = -ENOSPC;
BOOL the_end = FALSE;
s8 len_len, lcn_len; s8 len_len, lcn_len;
BUG_ON(start_vcn < 0); BUG_ON(first_vcn < 0);
BUG_ON(last_vcn < -1);
BUG_ON(last_vcn >= 0 && first_vcn > last_vcn);
BUG_ON(dst_len < 1); BUG_ON(dst_len < 1);
if (!rl) { if (!rl) {
BUG_ON(start_vcn); BUG_ON(first_vcn);
BUG_ON(last_vcn > 0);
if (stop_vcn) if (stop_vcn)
*stop_vcn = 0; *stop_vcn = 0;
/* Terminator byte. */ /* Terminator byte. */
*dst = 0; *dst = 0;
return 0; return 0;
} }
/* Skip to runlist element containing @start_vcn. */ /* Skip to runlist element containing @first_vcn. */
while (rl->length && start_vcn >= rl[1].vcn) while (rl->length && first_vcn >= rl[1].vcn)
rl++; rl++;
if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn) if (unlikely((!rl->length && first_vcn > rl->vcn) ||
first_vcn < rl->vcn))
return -EINVAL; return -EINVAL;
/* /*
* @dst_max is used for bounds checking in * @dst_max is used for bounds checking in
...@@ -1207,17 +1301,27 @@ int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst, ...@@ -1207,17 +1301,27 @@ int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst,
dst_max = dst + dst_len - 1; dst_max = dst + dst_len - 1;
prev_lcn = 0; prev_lcn = 0;
/* Do the first partial run if present. */ /* Do the first partial run if present. */
if (start_vcn > rl->vcn) { if (first_vcn > rl->vcn) {
s64 delta; s64 delta, length = rl->length;
/* We know rl->length != 0 already. */ /* We know rl->length != 0 already. */
if (rl->length < 0 || rl->lcn < LCN_HOLE) if (unlikely(length < 0 || rl->lcn < LCN_HOLE))
goto err_out; goto err_out;
delta = start_vcn - rl->vcn; /*
* If @stop_vcn is given and finishes inside this run, cap the
* run length.
*/
if (unlikely(last_vcn >= 0 && rl[1].vcn > last_vcn)) {
s64 s1 = last_vcn + 1;
if (unlikely(rl[1].vcn > s1))
length = s1 - rl->vcn;
the_end = TRUE;
}
delta = first_vcn - rl->vcn;
/* Write length. */ /* Write length. */
len_len = ntfs_write_significant_bytes(dst + 1, dst_max, len_len = ntfs_write_significant_bytes(dst + 1, dst_max,
rl->length - delta); length - delta);
if (len_len < 0) if (unlikely(len_len < 0))
goto size_err; goto size_err;
/* /*
* If the logical cluster number (lcn) denotes a hole and we * If the logical cluster number (lcn) denotes a hole and we
...@@ -1228,19 +1332,19 @@ int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst, ...@@ -1228,19 +1332,19 @@ int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst,
* case on NT4. - We assume that we just need to write the lcn * case on NT4. - We assume that we just need to write the lcn
* change until someone tells us otherwise... (AIA) * change until someone tells us otherwise... (AIA)
*/ */
if (rl->lcn >= 0 || vol->major_ver < 3) { if (likely(rl->lcn >= 0 || vol->major_ver < 3)) {
prev_lcn = rl->lcn; prev_lcn = rl->lcn;
if (rl->lcn >= 0) if (likely(rl->lcn >= 0))
prev_lcn += delta; prev_lcn += delta;
/* Write change in lcn. */ /* Write change in lcn. */
lcn_len = ntfs_write_significant_bytes(dst + 1 + lcn_len = ntfs_write_significant_bytes(dst + 1 +
len_len, dst_max, prev_lcn); len_len, dst_max, prev_lcn);
if (lcn_len < 0) if (unlikely(lcn_len < 0))
goto size_err; goto size_err;
} else } else
lcn_len = 0; lcn_len = 0;
dst_next = dst + len_len + lcn_len + 1; dst_next = dst + len_len + lcn_len + 1;
if (dst_next > dst_max) if (unlikely(dst_next > dst_max))
goto size_err; goto size_err;
/* Update header byte. */ /* Update header byte. */
*dst = lcn_len << 4 | len_len; *dst = lcn_len << 4 | len_len;
...@@ -1250,13 +1354,25 @@ int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst, ...@@ -1250,13 +1354,25 @@ int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst,
rl++; rl++;
} }
/* Do the full runs. */ /* Do the full runs. */
for (; rl->length; rl++) { for (; rl->length && !the_end; rl++) {
if (rl->length < 0 || rl->lcn < LCN_HOLE) s64 length = rl->length;
if (unlikely(length < 0 || rl->lcn < LCN_HOLE))
goto err_out; goto err_out;
/*
* If @stop_vcn is given and finishes inside this run, cap the
* run length.
*/
if (unlikely(last_vcn >= 0 && rl[1].vcn > last_vcn)) {
s64 s1 = last_vcn + 1;
if (unlikely(rl[1].vcn > s1))
length = s1 - rl->vcn;
the_end = TRUE;
}
/* Write length. */ /* Write length. */
len_len = ntfs_write_significant_bytes(dst + 1, dst_max, len_len = ntfs_write_significant_bytes(dst + 1, dst_max,
rl->length); length);
if (len_len < 0) if (unlikely(len_len < 0))
goto size_err; goto size_err;
/* /*
* If the logical cluster number (lcn) denotes a hole and we * If the logical cluster number (lcn) denotes a hole and we
...@@ -1267,17 +1383,17 @@ int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst, ...@@ -1267,17 +1383,17 @@ int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst,
* case on NT4. - We assume that we just need to write the lcn * case on NT4. - We assume that we just need to write the lcn
* change until someone tells us otherwise... (AIA) * change until someone tells us otherwise... (AIA)
*/ */
if (rl->lcn >= 0 || vol->major_ver < 3) { if (likely(rl->lcn >= 0 || vol->major_ver < 3)) {
/* Write change in lcn. */ /* Write change in lcn. */
lcn_len = ntfs_write_significant_bytes(dst + 1 + lcn_len = ntfs_write_significant_bytes(dst + 1 +
len_len, dst_max, rl->lcn - prev_lcn); len_len, dst_max, rl->lcn - prev_lcn);
if (lcn_len < 0) if (unlikely(lcn_len < 0))
goto size_err; goto size_err;
prev_lcn = rl->lcn; prev_lcn = rl->lcn;
} else } else
lcn_len = 0; lcn_len = 0;
dst_next = dst + len_len + lcn_len + 1; dst_next = dst + len_len + lcn_len + 1;
if (dst_next > dst_max) if (unlikely(dst_next > dst_max))
goto size_err; goto size_err;
/* Update header byte. */ /* Update header byte. */
*dst = lcn_len << 4 | len_len; *dst = lcn_len << 4 | len_len;
...@@ -1436,3 +1552,5 @@ int ntfs_rl_truncate_nolock(const ntfs_volume *vol, runlist *const runlist, ...@@ -1436,3 +1552,5 @@ int ntfs_rl_truncate_nolock(const ntfs_volume *vol, runlist *const runlist,
ntfs_debug("Done."); ntfs_debug("Done.");
return 0; return 0;
} }
#endif /* NTFS_RW */
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* runlist.h - Defines for runlist handling in NTFS Linux kernel driver. * runlist.h - Defines for runlist handling in NTFS Linux kernel driver.
* Part of the Linux-NTFS project. * Part of the Linux-NTFS project.
* *
* Copyright (c) 2001-2004 Anton Altaparmakov * Copyright (c) 2001-2005 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon * Copyright (c) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
...@@ -66,6 +66,8 @@ typedef enum { ...@@ -66,6 +66,8 @@ typedef enum {
LCN_HOLE = -1, /* Keep this as highest value or die! */ LCN_HOLE = -1, /* Keep this as highest value or die! */
LCN_RL_NOT_MAPPED = -2, LCN_RL_NOT_MAPPED = -2,
LCN_ENOENT = -3, LCN_ENOENT = -3,
LCN_ENOMEM = -4,
LCN_EIO = -5,
} LCN_SPECIAL_VALUES; } LCN_SPECIAL_VALUES;
extern runlist_element *ntfs_runlists_merge(runlist_element *drl, extern runlist_element *ntfs_runlists_merge(runlist_element *drl,
...@@ -76,14 +78,22 @@ extern runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, ...@@ -76,14 +78,22 @@ extern runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol,
extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn); extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn);
#ifdef NTFS_RW
extern runlist_element *ntfs_rl_find_vcn_nolock(runlist_element *rl,
const VCN vcn);
extern int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, extern int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
const runlist_element *rl, const VCN start_vcn); const runlist_element *rl, const VCN first_vcn,
const VCN last_vcn);
extern int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst, extern int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst,
const int dst_len, const runlist_element *rl, const int dst_len, const runlist_element *rl,
const VCN start_vcn, VCN *const stop_vcn); const VCN first_vcn, const VCN last_vcn, VCN *const stop_vcn);
extern int ntfs_rl_truncate_nolock(const ntfs_volume *vol, extern int ntfs_rl_truncate_nolock(const ntfs_volume *vol,
runlist *const runlist, const s64 new_length); runlist *const runlist, const s64 new_length);
#endif /* NTFS_RW */
#endif /* _LINUX_NTFS_RUNLIST_H */ #endif /* _LINUX_NTFS_RUNLIST_H */
/* /*
* super.c - NTFS kernel super block handling. Part of the Linux-NTFS project. * super.c - NTFS kernel super block handling. Part of the Linux-NTFS project.
* *
* Copyright (c) 2001-2004 Anton Altaparmakov * Copyright (c) 2001-2005 Anton Altaparmakov
* Copyright (c) 2001,2002 Richard Russon * Copyright (c) 2001,2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
...@@ -34,14 +34,16 @@ ...@@ -34,14 +34,16 @@
#include "sysctl.h" #include "sysctl.h"
#include "logfile.h" #include "logfile.h"
#include "quota.h" #include "quota.h"
#include "usnjrnl.h"
#include "dir.h" #include "dir.h"
#include "debug.h" #include "debug.h"
#include "index.h" #include "index.h"
#include "aops.h" #include "aops.h"
#include "layout.h"
#include "malloc.h" #include "malloc.h"
#include "ntfs.h" #include "ntfs.h"
/* Number of mounted file systems which have compression enabled. */ /* Number of mounted filesystems which have compression enabled. */
static unsigned long ntfs_nr_compression_users; static unsigned long ntfs_nr_compression_users;
/* A global default upcase table and a corresponding reference count. */ /* A global default upcase table and a corresponding reference count. */
...@@ -102,7 +104,7 @@ static BOOL parse_options(ntfs_volume *vol, char *opt) ...@@ -102,7 +104,7 @@ static BOOL parse_options(ntfs_volume *vol, char *opt)
gid_t gid = (gid_t)-1; gid_t gid = (gid_t)-1;
mode_t fmask = (mode_t)-1, dmask = (mode_t)-1; mode_t fmask = (mode_t)-1, dmask = (mode_t)-1;
int mft_zone_multiplier = -1, on_errors = -1; int mft_zone_multiplier = -1, on_errors = -1;
int show_sys_files = -1, case_sensitive = -1; int show_sys_files = -1, case_sensitive = -1, disable_sparse = -1;
struct nls_table *nls_map = NULL, *old_nls; struct nls_table *nls_map = NULL, *old_nls;
/* I am lazy... (-8 */ /* I am lazy... (-8 */
...@@ -162,6 +164,7 @@ static BOOL parse_options(ntfs_volume *vol, char *opt) ...@@ -162,6 +164,7 @@ static BOOL parse_options(ntfs_volume *vol, char *opt)
else NTFS_GETOPT_WITH_DEFAULT("sloppy", sloppy, TRUE) else NTFS_GETOPT_WITH_DEFAULT("sloppy", sloppy, TRUE)
else NTFS_GETOPT_BOOL("show_sys_files", show_sys_files) else NTFS_GETOPT_BOOL("show_sys_files", show_sys_files)
else NTFS_GETOPT_BOOL("case_sensitive", case_sensitive) else NTFS_GETOPT_BOOL("case_sensitive", case_sensitive)
else NTFS_GETOPT_BOOL("disable_sparse", disable_sparse)
else NTFS_GETOPT_OPTIONS_ARRAY("errors", on_errors, else NTFS_GETOPT_OPTIONS_ARRAY("errors", on_errors,
on_errors_arr) on_errors_arr)
else if (!strcmp(p, "posix") || !strcmp(p, "show_inodes")) else if (!strcmp(p, "posix") || !strcmp(p, "show_inodes"))
...@@ -291,6 +294,21 @@ static BOOL parse_options(ntfs_volume *vol, char *opt) ...@@ -291,6 +294,21 @@ static BOOL parse_options(ntfs_volume *vol, char *opt)
else else
NVolClearCaseSensitive(vol); NVolClearCaseSensitive(vol);
} }
if (disable_sparse != -1) {
if (disable_sparse)
NVolClearSparseEnabled(vol);
else {
if (!NVolSparseEnabled(vol) &&
vol->major_ver && vol->major_ver < 3)
ntfs_warning(vol->sb, "Not enabling sparse "
"support due to NTFS volume "
"version %i.%i (need at least "
"version 3.0).", vol->major_ver,
vol->minor_ver);
else
NVolSetSparseEnabled(vol);
}
}
return TRUE; return TRUE;
needs_arg: needs_arg:
ntfs_error(vol->sb, "The %s option requires an argument.", p); ntfs_error(vol->sb, "The %s option requires an argument.", p);
...@@ -480,6 +498,12 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt) ...@@ -480,6 +498,12 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt)
NVolSetErrors(vol); NVolSetErrors(vol);
return -EROFS; return -EROFS;
} }
if (!ntfs_stamp_usnjrnl(vol)) {
ntfs_error(sb, "Failed to stamp transation log "
"($UsnJrnl)%s", es);
NVolSetErrors(vol);
return -EROFS;
}
} else if (!(sb->s_flags & MS_RDONLY) && (*flags & MS_RDONLY)) { } else if (!(sb->s_flags & MS_RDONLY) && (*flags & MS_RDONLY)) {
/* Remounting read-only. */ /* Remounting read-only. */
if (!NVolErrors(vol)) { if (!NVolErrors(vol)) {
...@@ -516,16 +540,19 @@ static BOOL is_boot_sector_ntfs(const struct super_block *sb, ...@@ -516,16 +540,19 @@ static BOOL is_boot_sector_ntfs(const struct super_block *sb,
{ {
/* /*
* Check that checksum == sum of u32 values from b to the checksum * Check that checksum == sum of u32 values from b to the checksum
* field. If checksum is zero, no checking is done. * field. If checksum is zero, no checking is done. We will work when
* the checksum test fails, since some utilities update the boot sector
* ignoring the checksum which leaves the checksum out-of-date. We
* report a warning if this is the case.
*/ */
if ((void*)b < (void*)&b->checksum && b->checksum) { if ((void*)b < (void*)&b->checksum && b->checksum && !silent) {
le32 *u; le32 *u;
u32 i; u32 i;
for (i = 0, u = (le32*)b; u < (le32*)(&b->checksum); ++u) for (i = 0, u = (le32*)b; u < (le32*)(&b->checksum); ++u)
i += le32_to_cpup(u); i += le32_to_cpup(u);
if (le32_to_cpu(b->checksum) != i) if (le32_to_cpu(b->checksum) != i)
goto not_ntfs; ntfs_warning(sb, "Invalid boot sector checksum.");
} }
/* Check OEMidentifier is "NTFS " */ /* Check OEMidentifier is "NTFS " */
if (b->oem_id != magicNTFS) if (b->oem_id != magicNTFS)
...@@ -541,9 +568,9 @@ static BOOL is_boot_sector_ntfs(const struct super_block *sb, ...@@ -541,9 +568,9 @@ static BOOL is_boot_sector_ntfs(const struct super_block *sb,
default: default:
goto not_ntfs; goto not_ntfs;
} }
/* Check the cluster size is not above 65536 bytes. */ /* Check the cluster size is not above the maximum (64kiB). */
if ((u32)le16_to_cpu(b->bpb.bytes_per_sector) * if ((u32)le16_to_cpu(b->bpb.bytes_per_sector) *
b->bpb.sectors_per_cluster > 0x10000) b->bpb.sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE)
goto not_ntfs; goto not_ntfs;
/* Check reserved/unused fields are really zero. */ /* Check reserved/unused fields are really zero. */
if (le16_to_cpu(b->bpb.reserved_sectors) || if (le16_to_cpu(b->bpb.reserved_sectors) ||
...@@ -575,7 +602,7 @@ static BOOL is_boot_sector_ntfs(const struct super_block *sb, ...@@ -575,7 +602,7 @@ static BOOL is_boot_sector_ntfs(const struct super_block *sb,
* many BIOSes will refuse to boot from a bootsector if the magic is * many BIOSes will refuse to boot from a bootsector if the magic is
* incorrect, so we emit a warning. * incorrect, so we emit a warning.
*/ */
if (!silent && b->end_of_sector_marker != cpu_to_le16(0xaa55)) if (!silent && b->end_of_sector_marker != const_cpu_to_le16(0xaa55))
ntfs_warning(sb, "Invalid end of sector marker."); ntfs_warning(sb, "Invalid end of sector marker.");
return TRUE; return TRUE;
not_ntfs: not_ntfs:
...@@ -967,6 +994,7 @@ static BOOL load_and_init_mft_mirror(ntfs_volume *vol) ...@@ -967,6 +994,7 @@ static BOOL load_and_init_mft_mirror(ntfs_volume *vol)
tmp_ni = NTFS_I(tmp_ino); tmp_ni = NTFS_I(tmp_ino);
/* The $MFTMirr, like the $MFT is multi sector transfer protected. */ /* The $MFTMirr, like the $MFT is multi sector transfer protected. */
NInoSetMstProtected(tmp_ni); NInoSetMstProtected(tmp_ni);
NInoSetSparseDisabled(tmp_ni);
/* /*
* Set up our little cheat allowing us to reuse the async read io * Set up our little cheat allowing us to reuse the async read io
* completion handler for directories. * completion handler for directories.
...@@ -990,12 +1018,12 @@ static BOOL load_and_init_mft_mirror(ntfs_volume *vol) ...@@ -990,12 +1018,12 @@ static BOOL load_and_init_mft_mirror(ntfs_volume *vol)
*/ */
static BOOL check_mft_mirror(ntfs_volume *vol) static BOOL check_mft_mirror(ntfs_volume *vol)
{ {
unsigned long index;
struct super_block *sb = vol->sb; struct super_block *sb = vol->sb;
ntfs_inode *mirr_ni; ntfs_inode *mirr_ni;
struct page *mft_page, *mirr_page; struct page *mft_page, *mirr_page;
u8 *kmft, *kmirr; u8 *kmft, *kmirr;
runlist_element *rl, rl2[2]; runlist_element *rl, rl2[2];
pgoff_t index;
int mrecs_per_page, i; int mrecs_per_page, i;
ntfs_debug("Entering."); ntfs_debug("Entering.");
...@@ -1122,11 +1150,130 @@ static BOOL load_and_check_logfile(ntfs_volume *vol) ...@@ -1122,11 +1150,130 @@ static BOOL load_and_check_logfile(ntfs_volume *vol)
/* ntfs_check_logfile() will have displayed error output. */ /* ntfs_check_logfile() will have displayed error output. */
return FALSE; return FALSE;
} }
NInoSetSparseDisabled(NTFS_I(tmp_ino));
vol->logfile_ino = tmp_ino; vol->logfile_ino = tmp_ino;
ntfs_debug("Done."); ntfs_debug("Done.");
return TRUE; return TRUE;
} }
#define NTFS_HIBERFIL_HEADER_SIZE 4096
/**
* check_windows_hibernation_status - check if Windows is suspended on a volume
* @vol: ntfs super block of device to check
*
* Check if Windows is hibernated on the ntfs volume @vol. This is done by
* looking for the file hiberfil.sys in the root directory of the volume. If
* the file is not present Windows is definitely not suspended.
*
* If hiberfil.sys exists and is less than 4kiB in size it means Windows is
* definitely suspended (this volume is not the system volume). Caveat: on a
* system with many volumes it is possible that the < 4kiB check is bogus but
* for now this should do fine.
*
* If hiberfil.sys exists and is larger than 4kiB in size, we need to read the
* hiberfil header (which is the first 4kiB). If this begins with "hibr",
* Windows is definitely suspended. If it is completely full of zeroes,
* Windows is definitely not hibernated. Any other case is treated as if
* Windows is suspended. This caters for the above mentioned caveat of a
* system with many volumes where no "hibr" magic would be present and there is
* no zero header.
*
* Return 0 if Windows is not hibernated on the volume, >0 if Windows is
* hibernated on the volume, and -errno on error.
*/
static int check_windows_hibernation_status(ntfs_volume *vol)
{
MFT_REF mref;
struct inode *vi;
ntfs_inode *ni;
struct page *page;
u32 *kaddr, *kend;
ntfs_name *name = NULL;
int ret = 1;
static const ntfschar hiberfil[13] = { const_cpu_to_le16('h'),
const_cpu_to_le16('i'), const_cpu_to_le16('b'),
const_cpu_to_le16('e'), const_cpu_to_le16('r'),
const_cpu_to_le16('f'), const_cpu_to_le16('i'),
const_cpu_to_le16('l'), const_cpu_to_le16('.'),
const_cpu_to_le16('s'), const_cpu_to_le16('y'),
const_cpu_to_le16('s'), 0 };
ntfs_debug("Entering.");
/*
* Find the inode number for the hibernation file by looking up the
* filename hiberfil.sys in the root directory.
*/
down(&vol->root_ino->i_sem);
mref = ntfs_lookup_inode_by_name(NTFS_I(vol->root_ino), hiberfil, 12,
&name);
up(&vol->root_ino->i_sem);
if (IS_ERR_MREF(mref)) {
ret = MREF_ERR(mref);
/* If the file does not exist, Windows is not hibernated. */
if (ret == -ENOENT) {
ntfs_debug("hiberfil.sys not present. Windows is not "
"hibernated on the volume.");
return 0;
}
/* A real error occured. */
ntfs_error(vol->sb, "Failed to find inode number for "
"hiberfil.sys.");
return ret;
}
/* We do not care for the type of match that was found. */
kfree(name);
/* Get the inode. */
vi = ntfs_iget(vol->sb, MREF(mref));
if (IS_ERR(vi) || is_bad_inode(vi)) {
if (!IS_ERR(vi))
iput(vi);
ntfs_error(vol->sb, "Failed to load hiberfil.sys.");
return IS_ERR(vi) ? PTR_ERR(vi) : -EIO;
}
if (unlikely(i_size_read(vi) < NTFS_HIBERFIL_HEADER_SIZE)) {
ntfs_debug("hiberfil.sys is smaller than 4kiB (0x%llx). "
"Windows is hibernated on the volume. This "
"is not the system volume.", i_size_read(vi));
goto iput_out;
}
ni = NTFS_I(vi);
page = ntfs_map_page(vi->i_mapping, 0);
if (IS_ERR(page)) {
ntfs_error(vol->sb, "Failed to read from hiberfil.sys.");
ret = PTR_ERR(page);
goto iput_out;
}
kaddr = (u32*)page_address(page);
if (*(le32*)kaddr == const_cpu_to_le32(0x72626968)/*'hibr'*/) {
ntfs_debug("Magic \"hibr\" found in hiberfil.sys. Windows is "
"hibernated on the volume. This is the "
"system volume.");
goto unm_iput_out;
}
kend = kaddr + NTFS_HIBERFIL_HEADER_SIZE/sizeof(*kaddr);
do {
if (unlikely(*kaddr)) {
ntfs_debug("hiberfil.sys is larger than 4kiB "
"(0x%llx), does not contain the "
"\"hibr\" magic, and does not have a "
"zero header. Windows is hibernated "
"on the volume. This is not the "
"system volume.", i_size_read(vi));
goto unm_iput_out;
}
} while (++kaddr < kend);
ntfs_debug("hiberfil.sys contains a zero header. Windows is not "
"hibernated on the volume. This is the system "
"volume.");
ret = 0;
unm_iput_out:
ntfs_unmap_page(page);
iput_out:
iput(vi);
return ret;
}
/** /**
* load_and_init_quota - load and setup the quota file for a volume if present * 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 * @vol: ntfs super block describing device whose quota file to load
...@@ -1175,7 +1322,6 @@ static BOOL load_and_init_quota(ntfs_volume *vol) ...@@ -1175,7 +1322,6 @@ static BOOL load_and_init_quota(ntfs_volume *vol)
return FALSE; return FALSE;
} }
/* We do not care for the type of match that was found. */ /* We do not care for the type of match that was found. */
if (name)
kfree(name); kfree(name);
/* Get the inode. */ /* Get the inode. */
tmp_ino = ntfs_iget(vol->sb, MREF(mref)); tmp_ino = ntfs_iget(vol->sb, MREF(mref));
...@@ -1197,6 +1343,167 @@ static BOOL load_and_init_quota(ntfs_volume *vol) ...@@ -1197,6 +1343,167 @@ static BOOL load_and_init_quota(ntfs_volume *vol)
return TRUE; return TRUE;
} }
/**
* load_and_init_usnjrnl - load and setup the transaction log if present
* @vol: ntfs super block describing device whose usnjrnl file to load
*
* Return TRUE on success or FALSE on error.
*
* If $UsnJrnl is not present or in the process of being disabled, we set
* NVolUsnJrnlStamped() and return success.
*
* If the $UsnJrnl $DATA/$J attribute has a size equal to the lowest valid usn,
* i.e. transaction logging has only just been enabled or the journal has been
* stamped and nothing has been logged since, we also set NVolUsnJrnlStamped()
* and return success.
*/
static BOOL load_and_init_usnjrnl(ntfs_volume *vol)
{
MFT_REF mref;
struct inode *tmp_ino;
ntfs_inode *tmp_ni;
struct page *page;
ntfs_name *name = NULL;
USN_HEADER *uh;
static const ntfschar UsnJrnl[9] = { const_cpu_to_le16('$'),
const_cpu_to_le16('U'), const_cpu_to_le16('s'),
const_cpu_to_le16('n'), const_cpu_to_le16('J'),
const_cpu_to_le16('r'), const_cpu_to_le16('n'),
const_cpu_to_le16('l'), 0 };
static ntfschar Max[5] = { const_cpu_to_le16('$'),
const_cpu_to_le16('M'), const_cpu_to_le16('a'),
const_cpu_to_le16('x'), 0 };
static ntfschar J[3] = { const_cpu_to_le16('$'),
const_cpu_to_le16('J'), 0 };
ntfs_debug("Entering.");
/*
* Find the inode number for the transaction log file by looking up the
* filename $UsnJrnl in the extended system files directory $Extend.
*/
down(&vol->extend_ino->i_sem);
mref = ntfs_lookup_inode_by_name(NTFS_I(vol->extend_ino), UsnJrnl, 8,
&name);
up(&vol->extend_ino->i_sem);
if (IS_ERR_MREF(mref)) {
/*
* If the file does not exist, transaction logging is disabled,
* just return success.
*/
if (MREF_ERR(mref) == -ENOENT) {
ntfs_debug("$UsnJrnl not present. Volume does not "
"have transaction logging enabled.");
not_enabled:
/*
* No need to try to stamp the transaction log if
* transaction logging is not enabled.
*/
NVolSetUsnJrnlStamped(vol);
return TRUE;
}
/* A real error occured. */
ntfs_error(vol->sb, "Failed to find inode number for "
"$UsnJrnl.");
return FALSE;
}
/* We do not care for the type of match that was found. */
kfree(name);
/* Get the inode. */
tmp_ino = ntfs_iget(vol->sb, MREF(mref));
if (unlikely(IS_ERR(tmp_ino) || is_bad_inode(tmp_ino))) {
if (!IS_ERR(tmp_ino))
iput(tmp_ino);
ntfs_error(vol->sb, "Failed to load $UsnJrnl.");
return FALSE;
}
vol->usnjrnl_ino = tmp_ino;
/*
* If the transaction log is in the process of being deleted, we can
* ignore it.
*/
if (unlikely(vol->vol_flags & VOLUME_DELETE_USN_UNDERWAY)) {
ntfs_debug("$UsnJrnl in the process of being disabled. "
"Volume does not have transaction logging "
"enabled.");
goto not_enabled;
}
/* Get the $DATA/$Max attribute. */
tmp_ino = ntfs_attr_iget(vol->usnjrnl_ino, AT_DATA, Max, 4);
if (IS_ERR(tmp_ino)) {
ntfs_error(vol->sb, "Failed to load $UsnJrnl/$DATA/$Max "
"attribute.");
return FALSE;
}
vol->usnjrnl_max_ino = tmp_ino;
if (unlikely(i_size_read(tmp_ino) < sizeof(USN_HEADER))) {
ntfs_error(vol->sb, "Found corrupt $UsnJrnl/$DATA/$Max "
"attribute (size is 0x%llx but should be at "
"least 0x%x bytes).", i_size_read(tmp_ino),
sizeof(USN_HEADER));
return FALSE;
}
/* Get the $DATA/$J attribute. */
tmp_ino = ntfs_attr_iget(vol->usnjrnl_ino, AT_DATA, J, 2);
if (IS_ERR(tmp_ino)) {
ntfs_error(vol->sb, "Failed to load $UsnJrnl/$DATA/$J "
"attribute.");
return FALSE;
}
vol->usnjrnl_j_ino = tmp_ino;
/* Verify $J is non-resident and sparse. */
tmp_ni = NTFS_I(vol->usnjrnl_j_ino);
if (unlikely(!NInoNonResident(tmp_ni) || !NInoSparse(tmp_ni))) {
ntfs_error(vol->sb, "$UsnJrnl/$DATA/$J attribute is resident "
"and/or not sparse.");
return FALSE;
}
/* Read the USN_HEADER from $DATA/$Max. */
page = ntfs_map_page(vol->usnjrnl_max_ino->i_mapping, 0);
if (IS_ERR(page)) {
ntfs_error(vol->sb, "Failed to read from $UsnJrnl/$DATA/$Max "
"attribute.");
return FALSE;
}
uh = (USN_HEADER*)page_address(page);
/* Sanity check the $Max. */
if (unlikely(sle64_to_cpu(uh->allocation_delta) >
sle64_to_cpu(uh->maximum_size))) {
ntfs_error(vol->sb, "Allocation delta (0x%llx) exceeds "
"maximum size (0x%llx). $UsnJrnl is corrupt.",
(long long)sle64_to_cpu(uh->allocation_delta),
(long long)sle64_to_cpu(uh->maximum_size));
ntfs_unmap_page(page);
return FALSE;
}
/*
* If the transaction log has been stamped and nothing has been written
* to it since, we do not need to stamp it.
*/
if (unlikely(sle64_to_cpu(uh->lowest_valid_usn) >=
i_size_read(vol->usnjrnl_j_ino))) {
if (likely(sle64_to_cpu(uh->lowest_valid_usn) ==
i_size_read(vol->usnjrnl_j_ino))) {
ntfs_unmap_page(page);
ntfs_debug("$UsnJrnl is enabled but nothing has been "
"logged since it was last stamped. "
"Treating this as if the volume does "
"not have transaction logging "
"enabled.");
goto not_enabled;
}
ntfs_error(vol->sb, "$UsnJrnl has lowest valid usn (0x%llx) "
"which is out of bounds (0x%llx). $UsnJrnl "
"is corrupt.",
(long long)sle64_to_cpu(uh->lowest_valid_usn),
i_size_read(vol->usnjrnl_j_ino));
ntfs_unmap_page(page);
return FALSE;
}
ntfs_unmap_page(page);
ntfs_debug("Done.");
return TRUE;
}
/** /**
* load_and_init_attrdef - load the attribute definitions table for a volume * load_and_init_attrdef - load the attribute definitions table for a volume
* @vol: ntfs super block describing device whose attrdef to load * @vol: ntfs super block describing device whose attrdef to load
...@@ -1205,10 +1512,11 @@ static BOOL load_and_init_quota(ntfs_volume *vol) ...@@ -1205,10 +1512,11 @@ static BOOL load_and_init_quota(ntfs_volume *vol)
*/ */
static BOOL load_and_init_attrdef(ntfs_volume *vol) static BOOL load_and_init_attrdef(ntfs_volume *vol)
{ {
loff_t i_size;
struct super_block *sb = vol->sb; struct super_block *sb = vol->sb;
struct inode *ino; struct inode *ino;
struct page *page; struct page *page;
unsigned long index, max_index; pgoff_t index, max_index;
unsigned int size; unsigned int size;
ntfs_debug("Entering."); ntfs_debug("Entering.");
...@@ -1219,14 +1527,16 @@ static BOOL load_and_init_attrdef(ntfs_volume *vol) ...@@ -1219,14 +1527,16 @@ static BOOL load_and_init_attrdef(ntfs_volume *vol)
iput(ino); iput(ino);
goto failed; goto failed;
} }
NInoSetSparseDisabled(NTFS_I(ino));
/* The size of FILE_AttrDef must be above 0 and fit inside 31 bits. */ /* The size of FILE_AttrDef must be above 0 and fit inside 31 bits. */
if (!ino->i_size || ino->i_size > 0x7fffffff) i_size = i_size_read(ino);
if (i_size <= 0 || i_size > 0x7fffffff)
goto iput_failed; goto iput_failed;
vol->attrdef = (ATTR_DEF*)ntfs_malloc_nofs(ino->i_size); vol->attrdef = (ATTR_DEF*)ntfs_malloc_nofs(i_size);
if (!vol->attrdef) if (!vol->attrdef)
goto iput_failed; goto iput_failed;
index = 0; index = 0;
max_index = ino->i_size >> PAGE_CACHE_SHIFT; max_index = i_size >> PAGE_CACHE_SHIFT;
size = PAGE_CACHE_SIZE; size = PAGE_CACHE_SIZE;
while (index < max_index) { while (index < max_index) {
/* Read the attrdef table and copy it into the linear buffer. */ /* Read the attrdef table and copy it into the linear buffer. */
...@@ -1239,12 +1549,12 @@ static BOOL load_and_init_attrdef(ntfs_volume *vol) ...@@ -1239,12 +1549,12 @@ static BOOL load_and_init_attrdef(ntfs_volume *vol)
ntfs_unmap_page(page); ntfs_unmap_page(page);
}; };
if (size == PAGE_CACHE_SIZE) { if (size == PAGE_CACHE_SIZE) {
size = ino->i_size & ~PAGE_CACHE_MASK; size = i_size & ~PAGE_CACHE_MASK;
if (size) if (size)
goto read_partial_attrdef_page; goto read_partial_attrdef_page;
} }
vol->attrdef_size = ino->i_size; vol->attrdef_size = i_size;
ntfs_debug("Read %llu bytes from $AttrDef.", ino->i_size); ntfs_debug("Read %llu bytes from $AttrDef.", i_size);
iput(ino); iput(ino);
return TRUE; return TRUE;
free_iput_failed: free_iput_failed:
...@@ -1267,10 +1577,11 @@ static BOOL load_and_init_attrdef(ntfs_volume *vol) ...@@ -1267,10 +1577,11 @@ static BOOL load_and_init_attrdef(ntfs_volume *vol)
*/ */
static BOOL load_and_init_upcase(ntfs_volume *vol) static BOOL load_and_init_upcase(ntfs_volume *vol)
{ {
loff_t i_size;
struct super_block *sb = vol->sb; struct super_block *sb = vol->sb;
struct inode *ino; struct inode *ino;
struct page *page; struct page *page;
unsigned long index, max_index; pgoff_t index, max_index;
unsigned int size; unsigned int size;
int i, max; int i, max;
...@@ -1286,14 +1597,15 @@ static BOOL load_and_init_upcase(ntfs_volume *vol) ...@@ -1286,14 +1597,15 @@ static BOOL load_and_init_upcase(ntfs_volume *vol)
* The upcase size must not be above 64k Unicode characters, must not * The upcase size must not be above 64k Unicode characters, must not
* be zero and must be a multiple of sizeof(ntfschar). * be zero and must be a multiple of sizeof(ntfschar).
*/ */
if (!ino->i_size || ino->i_size & (sizeof(ntfschar) - 1) || i_size = i_size_read(ino);
ino->i_size > 64ULL * 1024 * sizeof(ntfschar)) if (!i_size || i_size & (sizeof(ntfschar) - 1) ||
i_size > 64ULL * 1024 * sizeof(ntfschar))
goto iput_upcase_failed; goto iput_upcase_failed;
vol->upcase = (ntfschar*)ntfs_malloc_nofs(ino->i_size); vol->upcase = (ntfschar*)ntfs_malloc_nofs(i_size);
if (!vol->upcase) if (!vol->upcase)
goto iput_upcase_failed; goto iput_upcase_failed;
index = 0; index = 0;
max_index = ino->i_size >> PAGE_CACHE_SHIFT; max_index = i_size >> PAGE_CACHE_SHIFT;
size = PAGE_CACHE_SIZE; size = PAGE_CACHE_SIZE;
while (index < max_index) { while (index < max_index) {
/* Read the upcase table and copy it into the linear buffer. */ /* Read the upcase table and copy it into the linear buffer. */
...@@ -1306,13 +1618,13 @@ static BOOL load_and_init_upcase(ntfs_volume *vol) ...@@ -1306,13 +1618,13 @@ static BOOL load_and_init_upcase(ntfs_volume *vol)
ntfs_unmap_page(page); ntfs_unmap_page(page);
}; };
if (size == PAGE_CACHE_SIZE) { if (size == PAGE_CACHE_SIZE) {
size = ino->i_size & ~PAGE_CACHE_MASK; size = i_size & ~PAGE_CACHE_MASK;
if (size) if (size)
goto read_partial_upcase_page; goto read_partial_upcase_page;
} }
vol->upcase_len = ino->i_size >> UCHAR_T_SIZE_BITS; vol->upcase_len = i_size >> UCHAR_T_SIZE_BITS;
ntfs_debug("Read %llu bytes from $UpCase (expected %zu bytes).", ntfs_debug("Read %llu bytes from $UpCase (expected %zu bytes).",
ino->i_size, 64 * 1024 * sizeof(ntfschar)); i_size, 64 * 1024 * sizeof(ntfschar));
iput(ino); iput(ino);
down(&ntfs_lock); down(&ntfs_lock);
if (!default_upcase) { if (!default_upcase) {
...@@ -1376,6 +1688,9 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1376,6 +1688,9 @@ static BOOL load_system_files(ntfs_volume *vol)
MFT_RECORD *m; MFT_RECORD *m;
VOLUME_INFORMATION *vi; VOLUME_INFORMATION *vi;
ntfs_attr_search_ctx *ctx; ntfs_attr_search_ctx *ctx;
#ifdef NTFS_RW
int err;
#endif /* NTFS_RW */
ntfs_debug("Entering."); ntfs_debug("Entering.");
#ifdef NTFS_RW #ifdef NTFS_RW
...@@ -1435,7 +1750,8 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1435,7 +1750,8 @@ static BOOL load_system_files(ntfs_volume *vol)
iput(vol->lcnbmp_ino); iput(vol->lcnbmp_ino);
goto bitmap_failed; goto bitmap_failed;
} }
if ((vol->nr_clusters + 7) >> 3 > vol->lcnbmp_ino->i_size) { NInoSetSparseDisabled(NTFS_I(vol->lcnbmp_ino));
if ((vol->nr_clusters + 7) >> 3 > i_size_read(vol->lcnbmp_ino)) {
iput(vol->lcnbmp_ino); iput(vol->lcnbmp_ino);
bitmap_failed: bitmap_failed:
ntfs_error(sb, "Failed to load $Bitmap."); ntfs_error(sb, "Failed to load $Bitmap.");
...@@ -1486,6 +1802,12 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1486,6 +1802,12 @@ static BOOL load_system_files(ntfs_volume *vol)
unmap_mft_record(NTFS_I(vol->vol_ino)); unmap_mft_record(NTFS_I(vol->vol_ino));
printk(KERN_INFO "NTFS volume version %i.%i.\n", vol->major_ver, printk(KERN_INFO "NTFS volume version %i.%i.\n", vol->major_ver,
vol->minor_ver); vol->minor_ver);
if (vol->major_ver < 3 && NVolSparseEnabled(vol)) {
ntfs_warning(vol->sb, "Disabling sparse support due to NTFS "
"volume version %i.%i (need at least version "
"3.0).", vol->major_ver, vol->minor_ver);
NVolClearSparseEnabled(vol);
}
#ifdef NTFS_RW #ifdef NTFS_RW
/* Make sure that no unsupported volume flags are set. */ /* Make sure that no unsupported volume flags are set. */
if (vol->vol_flags & VOLUME_MUST_MOUNT_RO_MASK) { if (vol->vol_flags & VOLUME_MUST_MOUNT_RO_MASK) {
...@@ -1545,6 +1867,50 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1545,6 +1867,50 @@ static BOOL load_system_files(ntfs_volume *vol)
/* This will prevent a read-write remount. */ /* This will prevent a read-write remount. */
NVolSetErrors(vol); NVolSetErrors(vol);
} }
#endif /* NTFS_RW */
/* Get the root directory inode so we can do path lookups. */
vol->root_ino = ntfs_iget(sb, FILE_root);
if (IS_ERR(vol->root_ino) || is_bad_inode(vol->root_ino)) {
if (!IS_ERR(vol->root_ino))
iput(vol->root_ino);
ntfs_error(sb, "Failed to load root directory.");
goto iput_logfile_err_out;
}
#ifdef NTFS_RW
/*
* Check if Windows is suspended to disk on the target volume. If it
* is hibernated, we must not write *anything* to the disk so set
* NVolErrors() without setting the dirty volume flag and mount
* read-only. This will prevent read-write remounting and it will also
* prevent all writes.
*/
err = check_windows_hibernation_status(vol);
if (unlikely(err)) {
static const char *es1a = "Failed to determine if Windows is "
"hibernated";
static const char *es1b = "Windows is hibernated";
static const char *es2 = ". Run chkdsk.";
const char *es1;
es1 = err < 0 ? es1a : es1b;
/* 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_root_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 volume dirty. */ /* If (still) a read-write mount, mark the volume dirty. */
if (!(sb->s_flags & MS_RDONLY) && if (!(sb->s_flags & MS_RDONLY) &&
ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY)) { ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY)) {
...@@ -1558,7 +1924,7 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1558,7 +1924,7 @@ static BOOL load_system_files(ntfs_volume *vol)
ntfs_error(sb, "%s and neither on_errors=continue nor " ntfs_error(sb, "%s and neither on_errors=continue nor "
"on_errors=remount-ro was specified%s", "on_errors=remount-ro was specified%s",
es1, es2); es1, es2);
goto iput_logfile_err_out; goto iput_root_err_out;
} }
ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); ntfs_error(sb, "%s. Mounting read-only%s", es1, es2);
sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
...@@ -1585,7 +1951,7 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1585,7 +1951,7 @@ static BOOL load_system_files(ntfs_volume *vol)
ntfs_error(sb, "%s and neither on_errors=continue nor " ntfs_error(sb, "%s and neither on_errors=continue nor "
"on_errors=remount-ro was specified%s", "on_errors=remount-ro was specified%s",
es1, es2); es1, es2);
goto iput_logfile_err_out; goto iput_root_err_out;
} }
ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); ntfs_error(sb, "%s. Mounting read-only%s", es1, es2);
sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
...@@ -1604,23 +1970,15 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1604,23 +1970,15 @@ static BOOL load_system_files(ntfs_volume *vol)
ntfs_error(sb, "%s and neither on_errors=continue nor " ntfs_error(sb, "%s and neither on_errors=continue nor "
"on_errors=remount-ro was specified%s", "on_errors=remount-ro was specified%s",
es1, es2); es1, es2);
goto iput_logfile_err_out; goto iput_root_err_out;
} }
ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); ntfs_error(sb, "%s. Mounting read-only%s", es1, es2);
sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
NVolSetErrors(vol); NVolSetErrors(vol);
} }
#endif /* NTFS_RW */ #endif /* NTFS_RW */
/* Get the root directory inode. */
vol->root_ino = ntfs_iget(sb, FILE_root);
if (IS_ERR(vol->root_ino) || is_bad_inode(vol->root_ino)) {
if (!IS_ERR(vol->root_ino))
iput(vol->root_ino);
ntfs_error(sb, "Failed to load root directory.");
goto iput_logfile_err_out;
}
/* If on NTFS versions before 3.0, we are done. */ /* If on NTFS versions before 3.0, we are done. */
if (vol->major_ver < 3) if (unlikely(vol->major_ver < 3))
return TRUE; return TRUE;
/* NTFS 3.0+ specific initialization. */ /* NTFS 3.0+ specific initialization. */
/* Get the security descriptors inode. */ /* Get the security descriptors inode. */
...@@ -1631,7 +1989,7 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1631,7 +1989,7 @@ static BOOL load_system_files(ntfs_volume *vol)
ntfs_error(sb, "Failed to load $Secure."); ntfs_error(sb, "Failed to load $Secure.");
goto iput_root_err_out; goto iput_root_err_out;
} }
// FIXME: Initialize security. // TODO: Initialize security.
/* Get the extended system files' directory inode. */ /* Get the extended system files' directory inode. */
vol->extend_ino = ntfs_iget(sb, FILE_Extend); 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) || is_bad_inode(vol->extend_ino)) {
...@@ -1682,10 +2040,60 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1682,10 +2040,60 @@ static BOOL load_system_files(ntfs_volume *vol)
sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
NVolSetErrors(vol); NVolSetErrors(vol);
} }
// TODO: Delete or checkpoint the $UsnJrnl if it exists. /*
* Find the transaction log file ($UsnJrnl), load it if present, check
* it, and set it up.
*/
if (!load_and_init_usnjrnl(vol)) {
static const char *es1 = "Failed to load $UsnJrnl";
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_usnjrnl_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, stamp the transaction log. */
if (!(sb->s_flags & MS_RDONLY) && !ntfs_stamp_usnjrnl(vol)) {
static const char *es1 = "Failed to stamp transaction log "
"($UsnJrnl)";
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_usnjrnl_err_out;
}
ntfs_error(sb, "%s. Mounting read-only%s", es1, es2);
sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
NVolSetErrors(vol);
}
#endif /* NTFS_RW */ #endif /* NTFS_RW */
return TRUE; return TRUE;
#ifdef NTFS_RW #ifdef NTFS_RW
iput_usnjrnl_err_out:
if (vol->usnjrnl_j_ino)
iput(vol->usnjrnl_j_ino);
if (vol->usnjrnl_max_ino)
iput(vol->usnjrnl_max_ino);
if (vol->usnjrnl_ino)
iput(vol->usnjrnl_ino);
iput_quota_err_out: iput_quota_err_out:
if (vol->quota_q_ino) if (vol->quota_q_ino)
iput(vol->quota_q_ino); iput(vol->quota_q_ino);
...@@ -1759,6 +2167,12 @@ static void ntfs_put_super(struct super_block *sb) ...@@ -1759,6 +2167,12 @@ static void ntfs_put_super(struct super_block *sb)
/* NTFS 3.0+ specific. */ /* NTFS 3.0+ specific. */
if (vol->major_ver >= 3) { if (vol->major_ver >= 3) {
if (vol->usnjrnl_j_ino)
ntfs_commit_inode(vol->usnjrnl_j_ino);
if (vol->usnjrnl_max_ino)
ntfs_commit_inode(vol->usnjrnl_max_ino);
if (vol->usnjrnl_ino)
ntfs_commit_inode(vol->usnjrnl_ino);
if (vol->quota_q_ino) if (vol->quota_q_ino)
ntfs_commit_inode(vol->quota_q_ino); ntfs_commit_inode(vol->quota_q_ino);
if (vol->quota_ino) if (vol->quota_ino)
...@@ -1814,6 +2228,18 @@ static void ntfs_put_super(struct super_block *sb) ...@@ -1814,6 +2228,18 @@ static void ntfs_put_super(struct super_block *sb)
/* NTFS 3.0+ specific clean up. */ /* NTFS 3.0+ specific clean up. */
if (vol->major_ver >= 3) { if (vol->major_ver >= 3) {
#ifdef NTFS_RW #ifdef NTFS_RW
if (vol->usnjrnl_j_ino) {
iput(vol->usnjrnl_j_ino);
vol->usnjrnl_j_ino = NULL;
}
if (vol->usnjrnl_max_ino) {
iput(vol->usnjrnl_max_ino);
vol->usnjrnl_max_ino = NULL;
}
if (vol->usnjrnl_ino) {
iput(vol->usnjrnl_ino);
vol->usnjrnl_ino = NULL;
}
if (vol->quota_q_ino) { if (vol->quota_q_ino) {
iput(vol->quota_q_ino); iput(vol->quota_q_ino);
vol->quota_q_ino = NULL; vol->quota_q_ino = NULL;
...@@ -1959,8 +2385,7 @@ static s64 get_nr_free_clusters(ntfs_volume *vol) ...@@ -1959,8 +2385,7 @@ static s64 get_nr_free_clusters(ntfs_volume *vol)
struct address_space *mapping = vol->lcnbmp_ino->i_mapping; struct address_space *mapping = vol->lcnbmp_ino->i_mapping;
filler_t *readpage = (filler_t*)mapping->a_ops->readpage; filler_t *readpage = (filler_t*)mapping->a_ops->readpage;
struct page *page; struct page *page;
unsigned long index, max_index; pgoff_t index, max_index;
unsigned int max_size;
ntfs_debug("Entering."); ntfs_debug("Entering.");
/* Serialize accesses to the cluster bitmap. */ /* Serialize accesses to the cluster bitmap. */
...@@ -1972,11 +2397,10 @@ static s64 get_nr_free_clusters(ntfs_volume *vol) ...@@ -1972,11 +2397,10 @@ static s64 get_nr_free_clusters(ntfs_volume *vol)
*/ */
max_index = (((vol->nr_clusters + 7) >> 3) + PAGE_CACHE_SIZE - 1) >> max_index = (((vol->nr_clusters + 7) >> 3) + PAGE_CACHE_SIZE - 1) >>
PAGE_CACHE_SHIFT; PAGE_CACHE_SHIFT;
/* Use multiples of 4 bytes. */ /* Use multiples of 4 bytes, thus max_size is PAGE_CACHE_SIZE / 4. */
max_size = PAGE_CACHE_SIZE >> 2; ntfs_debug("Reading $Bitmap, max_index = 0x%lx, max_size = 0x%lx.",
ntfs_debug("Reading $Bitmap, max_index = 0x%lx, max_size = 0x%x.", max_index, PAGE_CACHE_SIZE / 4);
max_index, max_size); for (index = 0; index < max_index; index++) {
for (index = 0UL; index < max_index; index++) {
unsigned int i; unsigned int i;
/* /*
* Read the page from page cache, getting it from backing store * Read the page from page cache, getting it from backing store
...@@ -2008,7 +2432,7 @@ static s64 get_nr_free_clusters(ntfs_volume *vol) ...@@ -2008,7 +2432,7 @@ static s64 get_nr_free_clusters(ntfs_volume *vol)
* the result as all out of range bytes are set to zero by * the result as all out of range bytes are set to zero by
* ntfs_readpage(). * ntfs_readpage().
*/ */
for (i = 0; i < max_size; i++) for (i = 0; i < PAGE_CACHE_SIZE / 4; i++)
nr_free -= (s64)hweight32(kaddr[i]); nr_free -= (s64)hweight32(kaddr[i]);
kunmap_atomic(kaddr, KM_USER0); kunmap_atomic(kaddr, KM_USER0);
page_cache_release(page); page_cache_release(page);
...@@ -2031,6 +2455,8 @@ static s64 get_nr_free_clusters(ntfs_volume *vol) ...@@ -2031,6 +2455,8 @@ static s64 get_nr_free_clusters(ntfs_volume *vol)
/** /**
* __get_nr_free_mft_records - return the number of free inodes on a volume * __get_nr_free_mft_records - return the number of free inodes on a volume
* @vol: ntfs volume for which to obtain free inode count * @vol: ntfs volume for which to obtain free inode count
* @nr_free: number of mft records in filesystem
* @max_index: maximum number of pages containing set bits
* *
* Calculate the number of free mft records (inodes) on the mounted NTFS * Calculate the number of free mft records (inodes) on the mounted NTFS
* volume @vol. We actually calculate the number of mft records in use instead * volume @vol. We actually calculate the number of mft records in use instead
...@@ -2043,32 +2469,20 @@ static s64 get_nr_free_clusters(ntfs_volume *vol) ...@@ -2043,32 +2469,20 @@ static s64 get_nr_free_clusters(ntfs_volume *vol)
* *
* NOTE: Caller must hold mftbmp_lock rw_semaphore for reading or writing. * NOTE: Caller must hold mftbmp_lock rw_semaphore for reading or writing.
*/ */
static unsigned long __get_nr_free_mft_records(ntfs_volume *vol) static unsigned long __get_nr_free_mft_records(ntfs_volume *vol,
s64 nr_free, const pgoff_t max_index)
{ {
s64 nr_free;
u32 *kaddr; u32 *kaddr;
struct address_space *mapping = vol->mftbmp_ino->i_mapping; struct address_space *mapping = vol->mftbmp_ino->i_mapping;
filler_t *readpage = (filler_t*)mapping->a_ops->readpage; filler_t *readpage = (filler_t*)mapping->a_ops->readpage;
struct page *page; struct page *page;
unsigned long index, max_index; pgoff_t index;
unsigned int max_size;
ntfs_debug("Entering."); ntfs_debug("Entering.");
/* Number of mft records in file system (at this point in time). */ /* Use multiples of 4 bytes, thus max_size is PAGE_CACHE_SIZE / 4. */
nr_free = vol->mft_ino->i_size >> vol->mft_record_size_bits;
/*
* Convert the maximum number of set bits into bytes rounded up, then
* convert into multiples of PAGE_CACHE_SIZE, rounding up so that if we
* have one full and one partial page max_index = 2.
*/
max_index = ((((NTFS_I(vol->mft_ino)->initialized_size >>
vol->mft_record_size_bits) + 7) >> 3) +
PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
/* Use multiples of 4 bytes. */
max_size = PAGE_CACHE_SIZE >> 2;
ntfs_debug("Reading $MFT/$BITMAP, max_index = 0x%lx, max_size = " ntfs_debug("Reading $MFT/$BITMAP, max_index = 0x%lx, max_size = "
"0x%x.", max_index, max_size); "0x%lx.", max_index, PAGE_CACHE_SIZE / 4);
for (index = 0UL; index < max_index; index++) { for (index = 0; index < max_index; index++) {
unsigned int i; unsigned int i;
/* /*
* Read the page from page cache, getting it from backing store * Read the page from page cache, getting it from backing store
...@@ -2100,7 +2514,7 @@ static unsigned long __get_nr_free_mft_records(ntfs_volume *vol) ...@@ -2100,7 +2514,7 @@ static unsigned long __get_nr_free_mft_records(ntfs_volume *vol)
* the result as all out of range bytes are set to zero by * the result as all out of range bytes are set to zero by
* ntfs_readpage(). * ntfs_readpage().
*/ */
for (i = 0; i < max_size; i++) for (i = 0; i < PAGE_CACHE_SIZE / 4; i++)
nr_free -= (s64)hweight32(kaddr[i]); nr_free -= (s64)hweight32(kaddr[i]);
kunmap_atomic(kaddr, KM_USER0); kunmap_atomic(kaddr, KM_USER0);
page_cache_release(page); page_cache_release(page);
...@@ -2134,8 +2548,11 @@ static unsigned long __get_nr_free_mft_records(ntfs_volume *vol) ...@@ -2134,8 +2548,11 @@ static unsigned long __get_nr_free_mft_records(ntfs_volume *vol)
*/ */
static int ntfs_statfs(struct super_block *sb, struct kstatfs *sfs) static int ntfs_statfs(struct super_block *sb, struct kstatfs *sfs)
{ {
ntfs_volume *vol = NTFS_SB(sb);
s64 size; s64 size;
ntfs_volume *vol = NTFS_SB(sb);
ntfs_inode *mft_ni = NTFS_I(vol->mft_ino);
pgoff_t max_index;
unsigned long flags;
ntfs_debug("Entering."); ntfs_debug("Entering.");
/* Type of filesystem. */ /* Type of filesystem. */
...@@ -2143,13 +2560,13 @@ static int ntfs_statfs(struct super_block *sb, struct kstatfs *sfs) ...@@ -2143,13 +2560,13 @@ static int ntfs_statfs(struct super_block *sb, struct kstatfs *sfs)
/* Optimal transfer block size. */ /* Optimal transfer block size. */
sfs->f_bsize = PAGE_CACHE_SIZE; sfs->f_bsize = PAGE_CACHE_SIZE;
/* /*
* Total data blocks in file system in units of f_bsize and since * Total data blocks in filesystem in units of f_bsize and since
* inodes are also stored in data blocs ($MFT is a file) this is just * inodes are also stored in data blocs ($MFT is a file) this is just
* the total clusters. * the total clusters.
*/ */
sfs->f_blocks = vol->nr_clusters << vol->cluster_size_bits >> sfs->f_blocks = vol->nr_clusters << vol->cluster_size_bits >>
PAGE_CACHE_SHIFT; PAGE_CACHE_SHIFT;
/* Free data blocks in file system in units of f_bsize. */ /* Free data blocks in filesystem in units of f_bsize. */
size = get_nr_free_clusters(vol) << vol->cluster_size_bits >> size = get_nr_free_clusters(vol) << vol->cluster_size_bits >>
PAGE_CACHE_SHIFT; PAGE_CACHE_SHIFT;
if (size < 0LL) if (size < 0LL)
...@@ -2158,17 +2575,27 @@ static int ntfs_statfs(struct super_block *sb, struct kstatfs *sfs) ...@@ -2158,17 +2575,27 @@ static int ntfs_statfs(struct super_block *sb, struct kstatfs *sfs)
sfs->f_bavail = sfs->f_bfree = size; sfs->f_bavail = sfs->f_bfree = size;
/* Serialize accesses to the inode bitmap. */ /* Serialize accesses to the inode bitmap. */
down_read(&vol->mftbmp_lock); down_read(&vol->mftbmp_lock);
/* Number of inodes in file system (at this point in time). */ read_lock_irqsave(&mft_ni->size_lock, flags);
sfs->f_files = vol->mft_ino->i_size >> vol->mft_record_size_bits; size = i_size_read(vol->mft_ino) >> vol->mft_record_size_bits;
/*
* Convert the maximum number of set bits into bytes rounded up, then
* convert into multiples of PAGE_CACHE_SIZE, rounding up so that if we
* have one full and one partial page max_index = 2.
*/
max_index = ((((mft_ni->initialized_size >> vol->mft_record_size_bits)
+ 7) >> 3) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
read_unlock_irqrestore(&mft_ni->size_lock, flags);
/* Number of inodes in filesystem (at this point in time). */
sfs->f_files = size;
/* Free inodes in fs (based on current total count). */ /* Free inodes in fs (based on current total count). */
sfs->f_ffree = __get_nr_free_mft_records(vol); sfs->f_ffree = __get_nr_free_mft_records(vol, size, max_index);
up_read(&vol->mftbmp_lock); up_read(&vol->mftbmp_lock);
/* /*
* File system id. This is extremely *nix flavour dependent and even * File system id. This is extremely *nix flavour dependent and even
* within Linux itself all fs do their own thing. I interpret this to * within Linux itself all fs do their own thing. I interpret this to
* mean a unique id associated with the mounted fs and not the id * mean a unique id associated with the mounted fs and not the id
* associated with the file system driver, the latter is already given * associated with the filesystem driver, the latter is already given
* by the file system type in sfs->f_type. Thus we use the 64-bit * by the filesystem type in sfs->f_type. Thus we use the 64-bit
* volume serial number splitting it into two 32-bit parts. We enter * volume serial number splitting it into two 32-bit parts. We enter
* the least significant 32-bits in f_fsid[0] and the most significant * the least significant 32-bits in f_fsid[0] and the most significant
* 32-bits in f_fsid[1]. * 32-bits in f_fsid[1].
...@@ -2219,53 +2646,19 @@ static struct super_operations ntfs_sops = { ...@@ -2219,53 +2646,19 @@ static struct super_operations ntfs_sops = {
proc. */ proc. */
}; };
/**
* Declarations for NTFS specific export operations (fs/ntfs/namei.c).
*/
extern struct dentry *ntfs_get_parent(struct dentry *child_dent);
extern struct dentry *ntfs_get_dentry(struct super_block *sb, void *fh);
/**
* Export operations allowing NFS exporting of mounted NTFS partitions.
*
* We use the default ->decode_fh() and ->encode_fh() for now. Note that they
* use 32 bits to store the inode number which is an unsigned long so on 64-bit
* architectures is usually 64 bits so it would all fail horribly on huge
* volumes. I guess we need to define our own encode and decode fh functions
* that store 64-bit inode numbers at some point but for now we will ignore the
* problem...
*
* We also use the default ->get_name() helper (used by ->decode_fh() via
* fs/exportfs/expfs.c::find_exported_dentry()) as that is completely fs
* independent.
*
* The default ->get_parent() just returns -EACCES so we have to provide our
* own and the default ->get_dentry() is incompatible with NTFS due to not
* allowing the inode number 0 which is used in NTFS for the system file $MFT
* and due to using iget() whereas NTFS needs ntfs_iget().
*/
static struct export_operations ntfs_export_ops = {
.get_parent = ntfs_get_parent, /* Find the parent of a given
directory. */
.get_dentry = ntfs_get_dentry, /* Find a dentry for the inode
given a file handle
sub-fragment. */
};
/** /**
* ntfs_fill_super - mount an ntfs files system * ntfs_fill_super - mount an ntfs filesystem
* @sb: super block of ntfs file system to mount * @sb: super block of ntfs filesystem to mount
* @opt: string containing the mount options * @opt: string containing the mount options
* @silent: silence error output * @silent: silence error output
* *
* ntfs_fill_super() is called by the VFS to mount the device described by @sb * ntfs_fill_super() is called by the VFS to mount the device described by @sb
* with the mount otions in @data with the NTFS file system. * with the mount otions in @data with the NTFS filesystem.
* *
* If @silent is true, remain silent even if errors are detected. This is used * If @silent is true, remain silent even if errors are detected. This is used
* during bootup, when the kernel tries to mount the root file system with all * during bootup, when the kernel tries to mount the root filesystem with all
* registered file systems one after the other until one succeeds. This implies * registered filesystems one after the other until one succeeds. This implies
* that all file systems except the correct one will quite correctly and * that all filesystems except the correct one will quite correctly and
* expectedly return an error, but nobody wants to see error messages when in * expectedly return an error, but nobody wants to see error messages when in
* fact this is what is supposed to happen. * fact this is what is supposed to happen.
* *
...@@ -2292,39 +2685,25 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) ...@@ -2292,39 +2685,25 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
return -ENOMEM; return -ENOMEM;
} }
/* Initialize ntfs_volume structure. */ /* Initialize ntfs_volume structure. */
memset(vol, 0, sizeof(ntfs_volume)); *vol = (ntfs_volume) {
vol->sb = sb; .sb = sb,
vol->upcase = NULL;
vol->attrdef = NULL;
vol->mft_ino = NULL;
vol->mftbmp_ino = NULL;
init_rwsem(&vol->mftbmp_lock);
#ifdef NTFS_RW
vol->mftmirr_ino = NULL;
vol->logfile_ino = NULL;
#endif /* NTFS_RW */
vol->lcnbmp_ino = NULL;
init_rwsem(&vol->lcnbmp_lock);
vol->vol_ino = NULL;
vol->root_ino = NULL;
vol->secure_ino = NULL;
vol->extend_ino = NULL;
#ifdef NTFS_RW
vol->quota_ino = NULL;
vol->quota_q_ino = NULL;
#endif /* NTFS_RW */
vol->nls_map = NULL;
/* /*
* Default is group and other don't have any access to files or * Default is group and other don't have any access to files or
* directories while owner has full access. Further, files by default * directories while owner has full access. Further, files by
* are not executable but directories are of course browseable. * default are not executable but directories are of course
* browseable.
*/ */
vol->fmask = 0177; .fmask = 0177,
vol->dmask = 0077; .dmask = 0077,
};
init_rwsem(&vol->mftbmp_lock);
init_rwsem(&vol->lcnbmp_lock);
unlock_kernel(); unlock_kernel();
/* By default, enable sparse support. */
NVolSetSparseEnabled(vol);
/* Important to get the mount options dealt with now. */ /* Important to get the mount options dealt with now. */
if (!parse_options(vol, (char*)opt)) if (!parse_options(vol, (char*)opt))
goto err_out_now; goto err_out_now;
...@@ -2347,7 +2726,8 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) ...@@ -2347,7 +2726,8 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
} }
/* Get the size of the device in units of NTFS_BLOCK_SIZE bytes. */ /* Get the size of the device in units of NTFS_BLOCK_SIZE bytes. */
vol->nr_blocks = sb->s_bdev->bd_inode->i_size >> NTFS_BLOCK_SIZE_BITS; vol->nr_blocks = i_size_read(sb->s_bdev->bd_inode) >>
NTFS_BLOCK_SIZE_BITS;
/* Read the boot sector and return unlocked buffer head to it. */ /* Read the boot sector and return unlocked buffer head to it. */
if (!(bh = read_ntfs_boot_sector(sb, silent))) { if (!(bh = read_ntfs_boot_sector(sb, silent))) {
...@@ -2476,6 +2856,18 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) ...@@ -2476,6 +2856,18 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
/* NTFS 3.0+ specific clean up. */ /* NTFS 3.0+ specific clean up. */
if (vol->major_ver >= 3) { if (vol->major_ver >= 3) {
#ifdef NTFS_RW #ifdef NTFS_RW
if (vol->usnjrnl_j_ino) {
iput(vol->usnjrnl_j_ino);
vol->usnjrnl_j_ino = NULL;
}
if (vol->usnjrnl_max_ino) {
iput(vol->usnjrnl_max_ino);
vol->usnjrnl_max_ino = NULL;
}
if (vol->usnjrnl_ino) {
iput(vol->usnjrnl_ino);
vol->usnjrnl_ino = NULL;
}
if (vol->quota_q_ino) { if (vol->quota_q_ino) {
iput(vol->quota_q_ino); iput(vol->quota_q_ino);
vol->quota_q_ino = NULL; vol->quota_q_ino = NULL;
...@@ -2581,7 +2973,7 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) ...@@ -2581,7 +2973,7 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
*/ */
kmem_cache_t *ntfs_name_cache; kmem_cache_t *ntfs_name_cache;
/* Slab caches for efficient allocation/deallocation of of inodes. */ /* Slab caches for efficient allocation/deallocation of inodes. */
kmem_cache_t *ntfs_inode_cache; kmem_cache_t *ntfs_inode_cache;
kmem_cache_t *ntfs_big_inode_cache; kmem_cache_t *ntfs_big_inode_cache;
...@@ -2705,7 +3097,7 @@ static int __init init_ntfs_fs(void) ...@@ -2705,7 +3097,7 @@ static int __init init_ntfs_fs(void)
ntfs_debug("NTFS driver registered successfully."); ntfs_debug("NTFS driver registered successfully.");
return 0; /* Success! */ return 0; /* Success! */
} }
printk(KERN_CRIT "NTFS: Failed to register NTFS file system driver!\n"); printk(KERN_CRIT "NTFS: Failed to register NTFS filesystem driver!\n");
sysctl_err_out: sysctl_err_out:
kmem_cache_destroy(ntfs_big_inode_cache); kmem_cache_destroy(ntfs_big_inode_cache);
...@@ -2719,7 +3111,7 @@ static int __init init_ntfs_fs(void) ...@@ -2719,7 +3111,7 @@ static int __init init_ntfs_fs(void)
kmem_cache_destroy(ntfs_index_ctx_cache); kmem_cache_destroy(ntfs_index_ctx_cache);
ictx_err_out: ictx_err_out:
if (!err) { if (!err) {
printk(KERN_CRIT "NTFS: Aborting NTFS file system driver " printk(KERN_CRIT "NTFS: Aborting NTFS filesystem driver "
"registration...\n"); "registration...\n");
err = -ENOMEM; err = -ENOMEM;
} }
...@@ -2759,7 +3151,7 @@ static void __exit exit_ntfs_fs(void) ...@@ -2759,7 +3151,7 @@ static void __exit exit_ntfs_fs(void)
} }
MODULE_AUTHOR("Anton Altaparmakov <aia21@cantab.net>"); MODULE_AUTHOR("Anton Altaparmakov <aia21@cantab.net>");
MODULE_DESCRIPTION("NTFS 1.2/3.x driver - Copyright (c) 2001-2004 Anton Altaparmakov"); MODULE_DESCRIPTION("NTFS 1.2/3.x driver - Copyright (c) 2001-2005 Anton Altaparmakov");
MODULE_VERSION(NTFS_VERSION); MODULE_VERSION(NTFS_VERSION);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
#ifdef DEBUG #ifdef DEBUG
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* the Linux-NTFS project. Adapted from the old NTFS driver, * the Linux-NTFS project. Adapted from the old NTFS driver,
* Copyright (C) 1997 Martin von Lwis, Rgis Duchesne * Copyright (C) 1997 Martin von Lwis, Rgis Duchesne
* *
* Copyright (c) 2002-2004 Anton Altaparmakov * Copyright (c) 2002-2005 Anton Altaparmakov
* *
* This program/include file is free software; you can redistribute it and/or * 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 * modify it under the terms of the GNU General Public License as published
...@@ -67,7 +67,7 @@ int ntfs_sysctl(int add) ...@@ -67,7 +67,7 @@ int ntfs_sysctl(int add)
return -ENOMEM; return -ENOMEM;
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
/* /*
* If the proc file system is in use and we are a module, need * If the proc filesystem is in use and we are a module, need
* to set the owner of our proc entry to our module. In the * to set the owner of our proc entry to our module. In the
* non-modular case, THIS_MODULE is NULL, so this is ok. * non-modular case, THIS_MODULE is NULL, so this is ok.
*/ */
......
/* /*
* time.h - NTFS time conversion functions. Part of the Linux-NTFS project. * time.h - NTFS time conversion functions. Part of the Linux-NTFS project.
* *
* Copyright (c) 2001-2004 Anton Altaparmakov * Copyright (c) 2001-2005 Anton Altaparmakov
* *
* This program/include file is free software; you can redistribute it and/or * 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 * modify it under the terms of the GNU General Public License as published
...@@ -87,7 +87,7 @@ static inline struct timespec ntfs2utc(const sle64 time) ...@@ -87,7 +87,7 @@ static inline struct timespec ntfs2utc(const sle64 time)
struct timespec ts; struct timespec ts;
/* Subtract the NTFS time offset. */ /* Subtract the NTFS time offset. */
s64 t = sle64_to_cpu(time) - NTFS_TIME_OFFSET; u64 t = (u64)(sle64_to_cpu(time) - NTFS_TIME_OFFSET);
/* /*
* Convert the time to 1-second intervals and the remainder to * Convert the time to 1-second intervals and the remainder to
* 1-nano-second intervals. * 1-nano-second intervals.
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* types.h - Defines for NTFS Linux kernel driver specific types. * types.h - Defines for NTFS Linux kernel driver specific types.
* Part of the Linux-NTFS project. * Part of the Linux-NTFS project.
* *
* Copyright (c) 2001-2004 Anton Altaparmakov * Copyright (c) 2001-2005 Anton Altaparmakov
* *
* This program/include file is free software; you can redistribute it and/or * 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 * modify it under the terms of the GNU General Public License as published
...@@ -53,6 +53,14 @@ typedef sle64 leLCN; ...@@ -53,6 +53,14 @@ typedef sle64 leLCN;
typedef s64 LSN; typedef s64 LSN;
typedef sle64 leLSN; typedef sle64 leLSN;
/*
* The NTFS transaction log $UsnJrnl uses usn which are signed 64-bit values.
* We define our own type USN, to allow for type checking and better code
* readability.
*/
typedef s64 USN;
typedef sle64 leUSN;
typedef enum { typedef enum {
FALSE = 0, FALSE = 0,
TRUE = 1 TRUE = 1
......
...@@ -264,7 +264,7 @@ int ntfs_nlstoucs(const ntfs_volume *vol, const char *ins, ...@@ -264,7 +264,7 @@ int ntfs_nlstoucs(const ntfs_volume *vol, const char *ins,
/* We don't trust outside sources. */ /* We don't trust outside sources. */
if (ins) { if (ins) {
ucs = (ntfschar*)kmem_cache_alloc(ntfs_name_cache, SLAB_NOFS); ucs = kmem_cache_alloc(ntfs_name_cache, SLAB_NOFS);
if (ucs) { if (ucs) {
for (i = o = 0; i < ins_len; i += wc_len) { for (i = o = 0; i < ins_len; i += wc_len) {
wc_len = nls->char2uni(ins + i, ins_len - i, wc_len = nls->char2uni(ins + i, ins_len - i,
......
/*
* usnjrnl.h - NTFS kernel transaction log ($UsnJrnl) handling. Part of the
* Linux-NTFS project.
*
* Copyright (c) 2005 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 <linux/fs.h>
#include <linux/highmem.h>
#include <linux/mm.h>
#include "aops.h"
#include "debug.h"
#include "endian.h"
#include "time.h"
#include "types.h"
#include "usnjrnl.h"
#include "volume.h"
/**
* ntfs_stamp_usnjrnl - stamp the transaction log ($UsnJrnl) on an ntfs volume
* @vol: ntfs volume on which to stamp the transaction log
*
* Stamp the transaction log ($UsnJrnl) on the ntfs volume @vol and return
* TRUE on success and FALSE on error.
*
* This function assumes that the transaction log has already been loaded and
* consistency checked by a call to fs/ntfs/super.c::load_and_init_usnjrnl().
*/
BOOL ntfs_stamp_usnjrnl(ntfs_volume *vol)
{
ntfs_debug("Entering.");
if (likely(!NVolUsnJrnlStamped(vol))) {
sle64 stamp;
struct page *page;
USN_HEADER *uh;
page = ntfs_map_page(vol->usnjrnl_max_ino->i_mapping, 0);
if (IS_ERR(page)) {
ntfs_error(vol->sb, "Failed to read from "
"$UsnJrnl/$DATA/$Max attribute.");
return FALSE;
}
uh = (USN_HEADER*)page_address(page);
stamp = get_current_ntfs_time();
ntfs_debug("Stamping transaction log ($UsnJrnl): old "
"journal_id 0x%llx, old lowest_valid_usn "
"0x%llx, new journal_id 0x%llx, new "
"lowest_valid_usn 0x%llx.",
(long long)sle64_to_cpu(uh->journal_id),
(long long)sle64_to_cpu(uh->lowest_valid_usn),
(long long)sle64_to_cpu(stamp),
i_size_read(vol->usnjrnl_j_ino));
uh->lowest_valid_usn =
cpu_to_sle64(i_size_read(vol->usnjrnl_j_ino));
uh->journal_id = stamp;
flush_dcache_page(page);
set_page_dirty(page);
ntfs_unmap_page(page);
/* Set the flag so we do not have to do it again on remount. */
NVolSetUsnJrnlStamped(vol);
}
ntfs_debug("Done.");
return TRUE;
}
#endif /* NTFS_RW */
/*
* usnjrnl.h - Defines for NTFS kernel transaction log ($UsnJrnl) handling.
* Part of the Linux-NTFS project.
*
* Copyright (c) 2005 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_USNJRNL_H
#define _LINUX_NTFS_USNJRNL_H
#ifdef NTFS_RW
#include "types.h"
#include "endian.h"
#include "layout.h"
#include "volume.h"
/*
* Transaction log ($UsnJrnl) organization:
*
* The transaction log records whenever a file is modified in any way. So for
* example it will record that file "blah" was written to at a particular time
* but not what was written. If will record that a file was deleted or
* created, that a file was truncated, etc. See below for all the reason
* codes used.
*
* The transaction log is in the $Extend directory which is in the root
* directory of each volume. If it is not present it means transaction
* logging is disabled. If it is present it means transaction logging is
* either enabled or in the process of being disabled in which case we can
* ignore it as it will go away as soon as Windows gets its hands on it.
*
* To determine whether the transaction logging is enabled or in the process
* of being disabled, need to check the volume flags in the
* $VOLUME_INFORMATION attribute in the $Volume system file (which is present
* in the root directory and has a fixed mft record number, see layout.h).
* If the flag VOLUME_DELETE_USN_UNDERWAY is set it means the transaction log
* is in the process of being disabled and if this flag is clear it means the
* transaction log is enabled.
*
* The transaction log consists of two parts; the $DATA/$Max attribute as well
* as the $DATA/$J attribute. $Max is a header describing the transaction
* log whilst $J is the transaction log data itself as a sequence of variable
* sized USN_RECORDs (see below for all the structures).
*
* We do not care about transaction logging at this point in time but we still
* need to let windows know that the transaction log is out of date. To do
* this we need to stamp the transaction log. This involves setting the
* lowest_valid_usn field in the $DATA/$Max attribute to the usn to be used
* for the next added USN_RECORD to the $DATA/$J attribute as well as
* generating a new journal_id in $DATA/$Max.
*
* The journal_id is as of the current version (2.0) of the transaction log
* simply the 64-bit timestamp of when the journal was either created or last
* stamped.
*
* To determine the next usn there are two ways. The first is to parse
* $DATA/$J and to find the last USN_RECORD in it and to add its record_length
* to its usn (which is the byte offset in the $DATA/$J attribute). The
* second is simply to take the data size of the attribute. Since the usns
* are simply byte offsets into $DATA/$J, this is exactly the next usn. For
* obvious reasons we use the second method as it is much simpler and faster.
*
* As an aside, note that to actually disable the transaction log, one would
* need to set the VOLUME_DELETE_USN_UNDERWAY flag (see above), then go
* through all the mft records on the volume and set the usn field in their
* $STANDARD_INFORMATION attribute to zero. Once that is done, one would need
* to delete the transaction log file, i.e. \$Extent\$UsnJrnl, and finally,
* one would need to clear the VOLUME_DELETE_USN_UNDERWAY flag.
*
* Note that if a volume is unmounted whilst the transaction log is being
* disabled, the process will continue the next time the volume is mounted.
* This is why we can safely mount read-write when we see a transaction log
* in the process of being deleted.
*/
/* Some $UsnJrnl related constants. */
#define UsnJrnlMajorVer 2
#define UsnJrnlMinorVer 0
/*
* $DATA/$Max attribute. This is (always?) resident and has a fixed size of
* 32 bytes. It contains the header describing the transaction log.
*/
typedef struct {
/*Ofs*/
/* 0*/sle64 maximum_size; /* The maximum on-disk size of the $DATA/$J
attribute. */
/* 8*/sle64 allocation_delta; /* Number of bytes by which to increase the
size of the $DATA/$J attribute. */
/*0x10*/sle64 journal_id; /* Current id of the transaction log. */
/*0x18*/leUSN lowest_valid_usn; /* Lowest valid usn in $DATA/$J for the
current journal_id. */
/* sizeof() = 32 (0x20) bytes */
} __attribute__ ((__packed__)) USN_HEADER;
/*
* Reason flags (32-bit). Cumulative flags describing the change(s) to the
* file since it was last opened. I think the names speak for themselves but
* if you disagree check out the descriptions in the Linux NTFS project NTFS
* documentation: http://linux-ntfs.sourceforge.net/ntfs/files/usnjrnl.html
*/
enum {
USN_REASON_DATA_OVERWRITE = const_cpu_to_le32(0x00000001),
USN_REASON_DATA_EXTEND = const_cpu_to_le32(0x00000002),
USN_REASON_DATA_TRUNCATION = const_cpu_to_le32(0x00000004),
USN_REASON_NAMED_DATA_OVERWRITE = const_cpu_to_le32(0x00000010),
USN_REASON_NAMED_DATA_EXTEND = const_cpu_to_le32(0x00000020),
USN_REASON_NAMED_DATA_TRUNCATION= const_cpu_to_le32(0x00000040),
USN_REASON_FILE_CREATE = const_cpu_to_le32(0x00000100),
USN_REASON_FILE_DELETE = const_cpu_to_le32(0x00000200),
USN_REASON_EA_CHANGE = const_cpu_to_le32(0x00000400),
USN_REASON_SECURITY_CHANGE = const_cpu_to_le32(0x00000800),
USN_REASON_RENAME_OLD_NAME = const_cpu_to_le32(0x00001000),
USN_REASON_RENAME_NEW_NAME = const_cpu_to_le32(0x00002000),
USN_REASON_INDEXABLE_CHANGE = const_cpu_to_le32(0x00004000),
USN_REASON_BASIC_INFO_CHANGE = const_cpu_to_le32(0x00008000),
USN_REASON_HARD_LINK_CHANGE = const_cpu_to_le32(0x00010000),
USN_REASON_COMPRESSION_CHANGE = const_cpu_to_le32(0x00020000),
USN_REASON_ENCRYPTION_CHANGE = const_cpu_to_le32(0x00040000),
USN_REASON_OBJECT_ID_CHANGE = const_cpu_to_le32(0x00080000),
USN_REASON_REPARSE_POINT_CHANGE = const_cpu_to_le32(0x00100000),
USN_REASON_STREAM_CHANGE = const_cpu_to_le32(0x00200000),
USN_REASON_CLOSE = const_cpu_to_le32(0x80000000),
};
typedef le32 USN_REASON_FLAGS;
/*
* Source info flags (32-bit). Information about the source of the change(s)
* to the file. For detailed descriptions of what these mean, see the Linux
* NTFS project NTFS documentation:
* http://linux-ntfs.sourceforge.net/ntfs/files/usnjrnl.html
*/
enum {
USN_SOURCE_DATA_MANAGEMENT = const_cpu_to_le32(0x00000001),
USN_SOURCE_AUXILIARY_DATA = const_cpu_to_le32(0x00000002),
USN_SOURCE_REPLICATION_MANAGEMENT = const_cpu_to_le32(0x00000004),
};
typedef le32 USN_SOURCE_INFO_FLAGS;
/*
* $DATA/$J attribute. This is always non-resident, is marked as sparse, and
* is of variabled size. It consists of a sequence of variable size
* USN_RECORDS. The minimum allocated_size is allocation_delta as
* specified in $DATA/$Max. When the maximum_size specified in $DATA/$Max is
* exceeded by more than allocation_delta bytes, allocation_delta bytes are
* allocated and appended to the $DATA/$J attribute and an equal number of
* bytes at the beginning of the attribute are freed and made sparse. Note the
* making sparse only happens at volume checkpoints and hence the actual
* $DATA/$J size can exceed maximum_size + allocation_delta temporarily.
*/
typedef struct {
/*Ofs*/
/* 0*/le32 length; /* Byte size of this record (8-byte
aligned). */
/* 4*/le16 major_ver; /* Major version of the transaction log used
for this record. */
/* 6*/le16 minor_ver; /* Minor version of the transaction log used
for this record. */
/* 8*/leMFT_REF mft_reference;/* The mft reference of the file (or
directory) described by this record. */
/*0x10*/leMFT_REF parent_directory;/* The mft reference of the parent
directory of the file described by this
record. */
/*0x18*/leUSN usn; /* The usn of this record. Equals the offset
within the $DATA/$J attribute. */
/*0x20*/sle64 time; /* Time when this record was created. */
/*0x28*/USN_REASON_FLAGS reason;/* Reason flags (see above). */
/*0x2c*/USN_SOURCE_INFO_FLAGS source_info;/* Source info flags (see above). */
/*0x30*/le32 security_id; /* File security_id copied from
$STANDARD_INFORMATION. */
/*0x34*/FILE_ATTR_FLAGS file_attributes; /* File attributes copied from
$STANDARD_INFORMATION or $FILE_NAME (not
sure which). */
/*0x38*/le16 file_name_size; /* Size of the file name in bytes. */
/*0x3a*/le16 file_name_offset; /* Offset to the file name in bytes from the
start of this record. */
/*0x3c*/ntfschar file_name[0]; /* Use when creating only. When reading use
file_name_offset to determine the location
of the name. */
/* sizeof() = 60 (0x3c) bytes */
} __attribute__ ((__packed__)) USN_RECORD;
extern BOOL ntfs_stamp_usnjrnl(ntfs_volume *vol);
#endif /* NTFS_RW */
#endif /* _LINUX_NTFS_USNJRNL_H */
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* volume.h - Defines for volume structures in NTFS Linux kernel driver. Part * volume.h - Defines for volume structures in NTFS Linux kernel driver. Part
* of the Linux-NTFS project. * of the Linux-NTFS project.
* *
* Copyright (c) 2001-2004 Anton Altaparmakov * Copyright (c) 2001-2005 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon * Copyright (c) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
...@@ -54,7 +54,7 @@ typedef struct { ...@@ -54,7 +54,7 @@ typedef struct {
mode_t dmask; /* The mask for directory mode_t dmask; /* The mask for directory
permissions. */ permissions. */
u8 mft_zone_multiplier; /* Initial mft zone multiplier. */ u8 mft_zone_multiplier; /* Initial mft zone multiplier. */
u8 on_errors; /* What to do on file system errors. */ u8 on_errors; /* What to do on filesystem errors. */
/* NTFS bootsector provided information. */ /* NTFS bootsector provided information. */
u16 sector_size; /* in bytes */ u16 sector_size; /* in bytes */
u8 sector_size_bits; /* log2(sector_size) */ u8 sector_size_bits; /* log2(sector_size) */
...@@ -125,6 +125,10 @@ typedef struct { ...@@ -125,6 +125,10 @@ typedef struct {
/* $Quota stuff is NTFS3.0+ specific. Unused/NULL otherwise. */ /* $Quota stuff is NTFS3.0+ specific. Unused/NULL otherwise. */
struct inode *quota_ino; /* The VFS inode of $Quota. */ struct inode *quota_ino; /* The VFS inode of $Quota. */
struct inode *quota_q_ino; /* Attribute inode for $Quota/$Q. */ struct inode *quota_q_ino; /* Attribute inode for $Quota/$Q. */
/* $UsnJrnl stuff is NTFS3.0+ specific. Unused/NULL otherwise. */
struct inode *usnjrnl_ino; /* The VFS inode of $UsnJrnl. */
struct inode *usnjrnl_max_ino; /* Attribute inode for $UsnJrnl/$Max. */
struct inode *usnjrnl_j_ino; /* Attribute inode for $UsnJrnl/$J. */
#endif /* NTFS_RW */ #endif /* NTFS_RW */
struct nls_table *nls_map; struct nls_table *nls_map;
} ntfs_volume; } ntfs_volume;
...@@ -141,6 +145,8 @@ typedef enum { ...@@ -141,6 +145,8 @@ typedef enum {
file names in WIN32 namespace. */ file names in WIN32 namespace. */
NV_LogFileEmpty, /* 1: $LogFile journal is empty. */ NV_LogFileEmpty, /* 1: $LogFile journal is empty. */
NV_QuotaOutOfDate, /* 1: $Quota is out of date. */ NV_QuotaOutOfDate, /* 1: $Quota is out of date. */
NV_UsnJrnlStamped, /* 1: $UsnJrnl has been stamped. */
NV_SparseEnabled, /* 1: May create sparse files. */
} ntfs_volume_flags; } ntfs_volume_flags;
/* /*
...@@ -167,5 +173,7 @@ NVOL_FNS(ShowSystemFiles) ...@@ -167,5 +173,7 @@ NVOL_FNS(ShowSystemFiles)
NVOL_FNS(CaseSensitive) NVOL_FNS(CaseSensitive)
NVOL_FNS(LogFileEmpty) NVOL_FNS(LogFileEmpty)
NVOL_FNS(QuotaOutOfDate) NVOL_FNS(QuotaOutOfDate)
NVOL_FNS(UsnJrnlStamped)
NVOL_FNS(SparseEnabled)
#endif /* _LINUX_NTFS_VOLUME_H */ #endif /* _LINUX_NTFS_VOLUME_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment