mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 21:33:00 +00:00
Merge master.kernel.org:/pub/scm/linux/kernel/git/aia21/ntfs-2.6
This commit is contained in:
commit
af6ea9ca23
@ -21,7 +21,7 @@ Overview
|
||||
========
|
||||
|
||||
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
|
||||
from an NTFS volume and ntfsresize which is used to resize an NTFS partition.
|
||||
See the web site for more information.
|
||||
@ -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
|
||||
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":
|
||||
continue: DEFAULT, try to clean-up as much as
|
||||
possible, e.g. marking a corrupt inode as
|
||||
@ -432,6 +439,24 @@ 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:
|
||||
- Improve handling of ntfs volumes with errors.
|
||||
- Fix various bugs and race conditions.
|
||||
|
@ -1,21 +1,18 @@
|
||||
ToDo/Notes:
|
||||
- Find and fix bugs.
|
||||
- Checkpoint or disable the user space journal ($UsnJrnl).
|
||||
- In between ntfs_prepare/commit_write, need exclusion between
|
||||
simultaneous file extensions. Need perhaps an NInoResizeUnderway()
|
||||
flag which we can set in ntfs_prepare_write() and clear again in
|
||||
ntfs_commit_write(). Just have to be careful in readpage/writepage,
|
||||
as well as in truncate, that we play nice... We might need to have
|
||||
a data_size field in the ntfs_inode to store the real attribute
|
||||
length. Also need to be careful with initialized_size extention in
|
||||
simultaneous file extensions. This is given to us by holding i_sem
|
||||
on the inode. The only places in the kernel when a file is resized
|
||||
are prepare/commit write and truncate for both of which i_sem is
|
||||
held. Just have to be careful in readpage/writepage and all other
|
||||
helpers not running under i_sem that we play nice...
|
||||
Also need to be careful with initialized_size extention in
|
||||
ntfs_prepare_write. Basically, just be _very_ careful in this code...
|
||||
OTOH, perhaps i_sem, which is held accross generic_file_write is
|
||||
sufficient for synchronisation here. We then just need to make sure
|
||||
ntfs_readpage/writepage/truncate interoperate properly with us.
|
||||
UPDATE: The above is all ok as it is due to i_sem held. The only
|
||||
thing that needs to be checked is ntfs_writepage() which does not
|
||||
hold i_sem. It cannot change i_size but it needs to cope with a
|
||||
concurrent i_size change.
|
||||
UPDATE: The only things that need to be checked are read/writepage
|
||||
which do not hold i_sem. Note writepage cannot change i_size but it
|
||||
needs to cope with a concurrent i_size change, just like readpage.
|
||||
Also both need to cope with concurrent changes to the other sizes,
|
||||
i.e. initialized/allocated/compressed size, as well.
|
||||
- Implement mft.c::sync_mft_mirror_umount(). We currently will just
|
||||
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
|
||||
@ -25,12 +22,158 @@ ToDo/Notes:
|
||||
- Enable the code for setting the NT4 compatibility flag when we start
|
||||
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
|
||||
compiled without debug. This avoids a possible denial of service
|
||||
attack. Thanks to Carl-Daniel Hailfinger from SuSE for pointing this
|
||||
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.
|
||||
|
||||
@ -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)
|
||||
- Backwards compatibility for gcc-2.95. (Richard Russon)
|
||||
- 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().
|
||||
- Set s_maxbytes to MAX_LFS_FILESIZE to avoid page cache page index
|
||||
overflow on 32-bit architectures.
|
||||
@ -1333,7 +1476,7 @@ tng-0.0.1 - The first useful version.
|
||||
The driver is now actually useful! Yey. (-: It undoubtedly has got bugs
|
||||
though and it doesn't implement accesssing compressed files yet. Also,
|
||||
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
|
||||
system, open, read, mmap and llseek around in files. A big mile stone
|
||||
has been reached!
|
||||
@ -1341,7 +1484,7 @@ tng-0.0.1 - The first useful version.
|
||||
tng-0.0.0 - Initial version tag.
|
||||
|
||||
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
|
||||
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
|
||||
|
@ -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 \
|
||||
unistr.o upcase.o
|
||||
|
||||
EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.22\"
|
||||
EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.23\"
|
||||
|
||||
ifeq ($(CONFIG_NTFS_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
@ -15,5 +15,5 @@ endif
|
||||
ifeq ($(CONFIG_NTFS_RW),y)
|
||||
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
|
||||
|
166
fs/ntfs/aops.c
166
fs/ntfs/aops.c
@ -2,7 +2,7 @@
|
||||
* aops.c - NTFS kernel address space operations and page cache handling.
|
||||
* Part of the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2001-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2001-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2002 Richard Russon
|
||||
*
|
||||
* 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)
|
||||
ni = NTFS_I(page->mapping->host);
|
||||
|
||||
if (likely(uptodate)) {
|
||||
s64 file_ofs;
|
||||
s64 file_ofs, initialized_size;
|
||||
|
||||
set_buffer_uptodate(bh);
|
||||
|
||||
file_ofs = ((s64)page->index << PAGE_CACHE_SHIFT) +
|
||||
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. */
|
||||
if (file_ofs + bh->b_size > ni->initialized_size) {
|
||||
if (file_ofs + bh->b_size > initialized_size) {
|
||||
char *addr;
|
||||
int ofs = 0;
|
||||
|
||||
if (file_ofs < ni->initialized_size)
|
||||
ofs = ni->initialized_size - file_ofs;
|
||||
if (file_ofs < initialized_size)
|
||||
ofs = initialized_size - file_ofs;
|
||||
addr = kmap_atomic(page, KM_BIO_SRC_IRQ);
|
||||
memset(addr + bh_offset(bh) + ofs, 0, bh->b_size - ofs);
|
||||
flush_dcache_page(page);
|
||||
@ -132,7 +135,7 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate)
|
||||
i * rec_size), rec_size);
|
||||
flush_dcache_page(page);
|
||||
kunmap_atomic(addr, KM_BIO_SRC_IRQ);
|
||||
if (likely(!PageError(page) && page_uptodate))
|
||||
if (likely(page_uptodate && !PageError(page)))
|
||||
SetPageUptodate(page);
|
||||
}
|
||||
unlock_page(page);
|
||||
@ -168,6 +171,7 @@ static int ntfs_read_block(struct page *page)
|
||||
runlist_element *rl;
|
||||
struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE];
|
||||
sector_t iblock, lblock, zblock;
|
||||
unsigned long flags;
|
||||
unsigned int blocksize, vcn_ofs;
|
||||
int i, nr;
|
||||
unsigned char blocksize_bits;
|
||||
@ -190,8 +194,10 @@ static int ntfs_read_block(struct page *page)
|
||||
}
|
||||
|
||||
iblock = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
|
||||
read_lock_irqsave(&ni->size_lock, flags);
|
||||
lblock = (ni->allocated_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. */
|
||||
rl = NULL;
|
||||
@ -258,7 +264,8 @@ lock_retry_remap:
|
||||
goto lock_retry_remap;
|
||||
rl = NULL;
|
||||
lcn = err;
|
||||
}
|
||||
} else if (!rl)
|
||||
up_read(&ni->runlist.lock);
|
||||
/* Hard error, zero out region. */
|
||||
bh->b_blocknr = -1;
|
||||
SetPageError(page);
|
||||
@ -341,14 +348,15 @@ handle_zblock:
|
||||
*/
|
||||
static int ntfs_readpage(struct file *file, struct page *page)
|
||||
{
|
||||
loff_t i_size;
|
||||
ntfs_inode *ni, *base_ni;
|
||||
u8 *kaddr;
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
MFT_RECORD *mrec;
|
||||
unsigned long flags;
|
||||
u32 attr_len;
|
||||
int err = 0;
|
||||
|
||||
retry_readpage:
|
||||
BUG_ON(!PageLocked(page));
|
||||
/*
|
||||
* This can potentially happen because we clear PageUptodate() during
|
||||
@ -383,9 +391,9 @@ static int ntfs_readpage(struct file *file, struct page *page)
|
||||
* Attribute is resident, implying it is not compressed or encrypted.
|
||||
* 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
|
||||
* 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);
|
||||
memset(kaddr, 0, PAGE_CACHE_SIZE);
|
||||
flush_dcache_page(page);
|
||||
@ -402,6 +410,14 @@ static int ntfs_readpage(struct file *file, struct page *page)
|
||||
err = PTR_ERR(mrec);
|
||||
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);
|
||||
if (unlikely(!ctx)) {
|
||||
err = -ENOMEM;
|
||||
@ -412,9 +428,10 @@ static int ntfs_readpage(struct file *file, struct page *page)
|
||||
if (unlikely(err))
|
||||
goto put_unm_err_out;
|
||||
attr_len = le32_to_cpu(ctx->attr->data.resident.value_length);
|
||||
i_size = i_size_read(VFS_I(ni));
|
||||
if (unlikely(attr_len > i_size))
|
||||
attr_len = i_size;
|
||||
read_lock_irqsave(&ni->size_lock, flags);
|
||||
if (unlikely(attr_len > ni->initialized_size))
|
||||
attr_len = ni->initialized_size;
|
||||
read_unlock_irqrestore(&ni->size_lock, flags);
|
||||
kaddr = kmap_atomic(page, KM_USER0);
|
||||
/* Copy the data to the page. */
|
||||
memcpy(kaddr, (u8*)ctx->attr +
|
||||
@ -463,12 +480,15 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
|
||||
{
|
||||
VCN vcn;
|
||||
LCN lcn;
|
||||
s64 initialized_size;
|
||||
loff_t i_size;
|
||||
sector_t block, dblock, iblock;
|
||||
struct inode *vi;
|
||||
ntfs_inode *ni;
|
||||
ntfs_volume *vol;
|
||||
runlist_element *rl;
|
||||
struct buffer_head *bh, *head;
|
||||
unsigned long flags;
|
||||
unsigned int blocksize, vcn_ofs;
|
||||
int err;
|
||||
BOOL need_end_writeback;
|
||||
@ -510,11 +530,16 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
|
||||
/* The first block in the page. */
|
||||
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. */
|
||||
dblock = (vi->i_size + blocksize - 1) >> blocksize_bits;
|
||||
dblock = (i_size + blocksize - 1) >> blocksize_bits;
|
||||
|
||||
/* 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
|
||||
@ -559,7 +584,7 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
|
||||
|
||||
/* Make sure we have enough initialized size. */
|
||||
if (unlikely((block >= iblock) &&
|
||||
(ni->initialized_size < vi->i_size))) {
|
||||
(initialized_size < i_size))) {
|
||||
/*
|
||||
* If this page is fully outside initialized size, zero
|
||||
* out all pages between the current initialized size
|
||||
@ -666,7 +691,8 @@ lock_retry_remap:
|
||||
goto lock_retry_remap;
|
||||
rl = NULL;
|
||||
lcn = err;
|
||||
}
|
||||
} else if (!rl)
|
||||
up_read(&ni->runlist.lock);
|
||||
/* Failed to map the buffer, even after retrying. */
|
||||
bh->b_blocknr = -1;
|
||||
ntfs_error(vol->sb, "Failed to write to inode 0x%lx, "
|
||||
@ -801,17 +827,15 @@ static int ntfs_write_mst_block(struct page *page,
|
||||
ntfs_inode *ni = NTFS_I(vi);
|
||||
ntfs_volume *vol = ni->vol;
|
||||
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;
|
||||
ntfs_inode *locked_nis[PAGE_CACHE_SIZE / rec_size];
|
||||
struct buffer_head *bh, *head, *tbh, *rec_start_bh;
|
||||
int max_bhs = PAGE_CACHE_SIZE / bh_size;
|
||||
struct buffer_head *bhs[max_bhs];
|
||||
struct buffer_head *bhs[MAX_BUF_PER_PAGE];
|
||||
runlist_element *rl;
|
||||
int i, nr_locked_nis, nr_recs, nr_bhs, bhs_per_rec, err, err2;
|
||||
unsigned rec_size_bits;
|
||||
int i, nr_locked_nis, nr_recs, nr_bhs, max_bhs, bhs_per_rec, err, err2;
|
||||
unsigned bh_size, rec_size_bits;
|
||||
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 "
|
||||
"0x%lx.", vi->i_ino, ni->type, page->index);
|
||||
@ -826,7 +850,11 @@ static int ntfs_write_mst_block(struct page *page,
|
||||
*/
|
||||
BUG_ON(!(is_mft || S_ISDIR(vi->i_mode) ||
|
||||
(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 > MAX_BUF_PER_PAGE);
|
||||
|
||||
/* Were we called for sync purposes? */
|
||||
sync = (wbc->sync_mode == WB_SYNC_ALL);
|
||||
@ -846,7 +874,7 @@ static int ntfs_write_mst_block(struct page *page,
|
||||
(PAGE_CACHE_SHIFT - bh_size_bits);
|
||||
|
||||
/* 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;
|
||||
err = err2 = nr_bhs = nr_recs = nr_locked_nis = 0;
|
||||
@ -858,6 +886,7 @@ static int ntfs_write_mst_block(struct page *page,
|
||||
if (likely(block < rec_block)) {
|
||||
if (unlikely(block >= dblock)) {
|
||||
clear_buffer_dirty(bh);
|
||||
set_buffer_uptodate(bh);
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
@ -938,8 +967,11 @@ lock_retry_remap:
|
||||
if (err2 == -ENOMEM)
|
||||
page_is_dirty = TRUE;
|
||||
lcn = err2;
|
||||
} else
|
||||
} else {
|
||||
err2 = -EIO;
|
||||
if (!rl)
|
||||
up_read(&ni->runlist.lock);
|
||||
}
|
||||
/* Hard error. Abort writing this record. */
|
||||
if (!err || err == -ENOMEM)
|
||||
err = err2;
|
||||
@ -949,7 +981,8 @@ lock_retry_remap:
|
||||
"attribute type 0x%x) because "
|
||||
"its location on disk could "
|
||||
"not be determined (error "
|
||||
"code %lli).", (s64)block <<
|
||||
"code %lli).",
|
||||
(long long)block <<
|
||||
bh_size_bits >>
|
||||
vol->mft_record_size_bits,
|
||||
ni->mft_no, ni->type,
|
||||
@ -1223,19 +1256,17 @@ done:
|
||||
static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
|
||||
{
|
||||
loff_t i_size;
|
||||
struct inode *vi;
|
||||
ntfs_inode *ni, *base_ni;
|
||||
struct inode *vi = page->mapping->host;
|
||||
ntfs_inode *base_ni = NULL, *ni = NTFS_I(vi);
|
||||
char *kaddr;
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
MFT_RECORD *m;
|
||||
ntfs_attr_search_ctx *ctx = NULL;
|
||||
MFT_RECORD *m = NULL;
|
||||
u32 attr_len;
|
||||
int err;
|
||||
|
||||
retry_writepage:
|
||||
BUG_ON(!PageLocked(page));
|
||||
|
||||
vi = page->mapping->host;
|
||||
i_size = i_size_read(vi);
|
||||
|
||||
/* Is the page fully outside i_size? (truncate in progress) */
|
||||
if (unlikely(page->index >= (i_size + PAGE_CACHE_SIZE - 1) >>
|
||||
PAGE_CACHE_SHIFT)) {
|
||||
@ -1248,8 +1279,6 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
|
||||
ntfs_debug("Write outside i_size - truncated?");
|
||||
return 0;
|
||||
}
|
||||
ni = NTFS_I(vi);
|
||||
|
||||
/* NInoNonResident() == NInoIndexAllocPresent() */
|
||||
if (NInoNonResident(ni)) {
|
||||
/*
|
||||
@ -1326,6 +1355,14 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
|
||||
ctx = NULL;
|
||||
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);
|
||||
if (unlikely(!ctx)) {
|
||||
err = -ENOMEM;
|
||||
@ -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);
|
||||
i_size = i_size_read(VFS_I(ni));
|
||||
kaddr = kmap_atomic(page, KM_USER0);
|
||||
i_size = i_size_read(vi);
|
||||
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;
|
||||
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. */
|
||||
memcpy((u8*)ctx->attr +
|
||||
le16_to_cpu(ctx->attr->data.resident.value_offset),
|
||||
@ -1405,8 +1439,10 @@ err_out:
|
||||
err = 0;
|
||||
} else {
|
||||
ntfs_error(vi->i_sb, "Resident attribute write failed with "
|
||||
"error %i. Setting page error flag.", err);
|
||||
"error %i.", err);
|
||||
SetPageError(page);
|
||||
NVolSetErrors(ni->vol);
|
||||
make_bad_inode(vi);
|
||||
}
|
||||
unlock_page(page);
|
||||
if (ctx)
|
||||
@ -1425,12 +1461,15 @@ static int ntfs_prepare_nonresident_write(struct page *page,
|
||||
{
|
||||
VCN vcn;
|
||||
LCN lcn;
|
||||
s64 initialized_size;
|
||||
loff_t i_size;
|
||||
sector_t block, ablock, iblock;
|
||||
struct inode *vi;
|
||||
ntfs_inode *ni;
|
||||
ntfs_volume *vol;
|
||||
runlist_element *rl;
|
||||
struct buffer_head *bh, *head, *wait[2], **wait_bh = wait;
|
||||
unsigned long flags;
|
||||
unsigned int vcn_ofs, block_start, block_end, blocksize;
|
||||
int err;
|
||||
BOOL is_retry;
|
||||
@ -1462,16 +1501,20 @@ static int ntfs_prepare_nonresident_write(struct page *page,
|
||||
/* The first block in the page. */
|
||||
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
|
||||
* minimum cluster size is 512 bytes, which is equal to the smallest
|
||||
* blocksize.
|
||||
*/
|
||||
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. */
|
||||
iblock = ni->initialized_size >> blocksize_bits;
|
||||
iblock = initialized_size >> blocksize_bits;
|
||||
|
||||
/* Loop through all the buffers in the page. */
|
||||
block_start = 0;
|
||||
@ -1518,7 +1561,7 @@ static int ntfs_prepare_nonresident_write(struct page *page,
|
||||
* request, i.e. block < ablock is true.
|
||||
*/
|
||||
if (unlikely((block >= iblock) &&
|
||||
(ni->initialized_size < vi->i_size))) {
|
||||
(initialized_size < i_size))) {
|
||||
/*
|
||||
* If this page is fully outside initialized size, zero
|
||||
* out all pages between the current initialized size
|
||||
@ -1622,6 +1665,8 @@ lock_retry_remap:
|
||||
"not supported yet. "
|
||||
"Sorry.");
|
||||
err = -EOPNOTSUPP;
|
||||
if (!rl)
|
||||
up_read(&ni->runlist.lock);
|
||||
goto err_out;
|
||||
} else if (!is_retry &&
|
||||
lcn == LCN_RL_NOT_MAPPED) {
|
||||
@ -1636,7 +1681,8 @@ lock_retry_remap:
|
||||
goto lock_retry_remap;
|
||||
rl = NULL;
|
||||
lcn = err;
|
||||
}
|
||||
} else if (!rl)
|
||||
up_read(&ni->runlist.lock);
|
||||
/*
|
||||
* Failed to map the buffer, even after
|
||||
* retrying.
|
||||
@ -1797,6 +1843,7 @@ static int ntfs_prepare_write(struct file *file, struct page *page,
|
||||
unsigned from, unsigned to)
|
||||
{
|
||||
s64 new_size;
|
||||
loff_t i_size;
|
||||
struct inode *vi = page->mapping->host;
|
||||
ntfs_inode *base_ni = NULL, *ni = NTFS_I(vi);
|
||||
ntfs_volume *vol = ni->vol;
|
||||
@ -1868,14 +1915,8 @@ static int ntfs_prepare_write(struct file *file, struct page *page,
|
||||
BUG_ON(page_has_buffers(page));
|
||||
new_size = ((s64)page->index << PAGE_CACHE_SHIFT) + to;
|
||||
/* If we do not need to resize the attribute allocation we are done. */
|
||||
if (new_size <= vi->i_size)
|
||||
if (new_size <= i_size_read(vi))
|
||||
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. */
|
||||
if (!NInoAttr(ni))
|
||||
base_ni = ni;
|
||||
@ -1904,7 +1945,15 @@ static int ntfs_prepare_write(struct file *file, struct page *page,
|
||||
a = ctx->attr;
|
||||
/* The total length of the attribute value. */
|
||||
attr_len = le32_to_cpu(a->data.resident.value_length);
|
||||
BUG_ON(vi->i_size != attr_len);
|
||||
/* 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. */
|
||||
err = ntfs_attr_size_bounds_check(vol, ni->type, new_size);
|
||||
if (unlikely(err)) {
|
||||
@ -1962,6 +2011,7 @@ static int ntfs_prepare_write(struct file *file, struct page *page,
|
||||
}
|
||||
flush_dcache_mft_record_page(ctx->ntfs_ino);
|
||||
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||
done_unm:
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
unmap_mft_record(base_ni);
|
||||
/*
|
||||
@ -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
|
||||
* 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 "
|
||||
"not supported yet. Sorry.");
|
||||
return -EOPNOTSUPP;
|
||||
@ -2183,9 +2233,13 @@ static int ntfs_commit_write(struct file *file, struct page *page,
|
||||
}
|
||||
kunmap_atomic(kaddr, KM_USER0);
|
||||
/* 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;
|
||||
i_size_write(vi, attr_len);
|
||||
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);
|
||||
|
656
fs/ntfs/attrib.c
656
fs/ntfs/attrib.c
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
@ -21,14 +21,88 @@
|
||||
*/
|
||||
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/swap.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "debug.h"
|
||||
#include "layout.h"
|
||||
#include "lcnalloc.h"
|
||||
#include "malloc.h"
|
||||
#include "mft.h"
|
||||
#include "ntfs.h"
|
||||
#include "types.h"
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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 locked for writing.
|
||||
* - This function modifies the runlist.
|
||||
*/
|
||||
int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn)
|
||||
{
|
||||
VCN end_vcn;
|
||||
ntfs_inode *base_ni;
|
||||
MFT_RECORD *m;
|
||||
ATTR_RECORD *a;
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
runlist_element *rl;
|
||||
int err = 0;
|
||||
|
||||
ntfs_debug("Mapping runlist part containing vcn 0x%llx.",
|
||||
(unsigned long long)vcn);
|
||||
if (!NInoAttr(ni))
|
||||
base_ni = ni;
|
||||
else
|
||||
base_ni = ni->ext.base_ntfs_ino;
|
||||
m = map_mft_record(base_ni);
|
||||
if (IS_ERR(m))
|
||||
return PTR_ERR(m);
|
||||
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, vcn, NULL, 0, ctx);
|
||||
if (unlikely(err)) {
|
||||
if (err == -ENOENT)
|
||||
err = -EIO;
|
||||
goto err_out;
|
||||
}
|
||||
a = ctx->attr;
|
||||
/*
|
||||
* 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
|
||||
* we then try to map the already mapped runlist fragment and
|
||||
* 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))
|
||||
err = PTR_ERR(rl);
|
||||
else
|
||||
ni->runlist.rl = rl;
|
||||
err_out:
|
||||
if (likely(ctx))
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
unmap_mft_record(base_ni);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_map_runlist - map (a part of) a runlist of an ntfs inode
|
||||
* @ni: ntfs inode for which to map (part of) a runlist
|
||||
@ -36,73 +110,128 @@
|
||||
*
|
||||
* 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.
|
||||
* - This function takes the lock for writing and modifies the runlist.
|
||||
* - This function takes the runlist lock for writing and modifies the
|
||||
* runlist.
|
||||
*/
|
||||
int ntfs_map_runlist(ntfs_inode *ni, VCN vcn)
|
||||
{
|
||||
ntfs_inode *base_ni;
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
MFT_RECORD *mrec;
|
||||
int err = 0;
|
||||
|
||||
ntfs_debug("Mapping runlist part containing vcn 0x%llx.",
|
||||
(unsigned long long)vcn);
|
||||
|
||||
if (!NInoAttr(ni))
|
||||
base_ni = ni;
|
||||
else
|
||||
base_ni = ni->ext.base_ntfs_ino;
|
||||
|
||||
mrec = map_mft_record(base_ni);
|
||||
if (IS_ERR(mrec))
|
||||
return PTR_ERR(mrec);
|
||||
ctx = ntfs_attr_get_search_ctx(base_ni, mrec);
|
||||
if (unlikely(!ctx)) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
|
||||
CASE_SENSITIVE, vcn, NULL, 0, ctx);
|
||||
if (unlikely(err))
|
||||
goto put_err_out;
|
||||
|
||||
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)) {
|
||||
runlist_element *rl;
|
||||
|
||||
rl = ntfs_mapping_pairs_decompress(ni->vol, ctx->attr,
|
||||
ni->runlist.rl);
|
||||
if (IS_ERR(rl))
|
||||
err = PTR_ERR(rl);
|
||||
else
|
||||
ni->runlist.rl = rl;
|
||||
}
|
||||
LCN_RL_NOT_MAPPED))
|
||||
err = ntfs_map_runlist_nolock(ni, vcn);
|
||||
up_write(&ni->runlist.lock);
|
||||
|
||||
put_err_out:
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
err_out:
|
||||
unmap_mft_record(base_ni);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_find_vcn - find a vcn in the runlist described by an ntfs inode
|
||||
* @ni: ntfs inode describing the runlist to search
|
||||
* @vcn: vcn to find
|
||||
* @need_write: if false, lock for reading and if true, lock for writing
|
||||
* 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
|
||||
* @vcn: vcn to find
|
||||
* @write_locked: true if the runlist is locked for writing
|
||||
*
|
||||
* 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.
|
||||
* 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
|
||||
* runlist is locked for reading. In the error case, the runlist is not left
|
||||
* locked.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
@ -118,34 +247,29 @@ err_out:
|
||||
* -ENOMEM - Not enough memory to map runlist.
|
||||
* -EIO - Critical error (runlist/file is corrupt, i/o error, etc).
|
||||
*
|
||||
* Locking: - The runlist must be unlocked on entry.
|
||||
* - On failing return, the runlist is unlocked.
|
||||
* - On successful return, the runlist is locked. If @need_write us
|
||||
* true, it is locked for writing. Otherwise is is locked for
|
||||
* reading.
|
||||
* 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.
|
||||
*/
|
||||
runlist_element *ntfs_find_vcn(ntfs_inode *ni, const VCN vcn,
|
||||
const BOOL need_write)
|
||||
runlist_element *ntfs_attr_find_vcn_nolock(ntfs_inode *ni, const VCN vcn,
|
||||
const BOOL write_locked)
|
||||
{
|
||||
runlist_element *rl;
|
||||
int err = 0;
|
||||
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,
|
||||
!need_write ? "read" : "writ");
|
||||
write_locked ? "write" : "read");
|
||||
BUG_ON(!ni);
|
||||
BUG_ON(!NInoNonResident(ni));
|
||||
BUG_ON(vcn < 0);
|
||||
lock_retry_remap:
|
||||
if (!need_write)
|
||||
down_read(&ni->runlist.lock);
|
||||
else
|
||||
down_write(&ni->runlist.lock);
|
||||
retry_remap:
|
||||
rl = ni->runlist.rl;
|
||||
if (likely(rl && vcn >= rl[0].vcn)) {
|
||||
while (likely(rl->length)) {
|
||||
if (likely(vcn < rl[1].vcn)) {
|
||||
if (unlikely(vcn < rl[1].vcn)) {
|
||||
if (likely(rl->lcn >= LCN_HOLE)) {
|
||||
ntfs_debug("Done.");
|
||||
return rl;
|
||||
@ -161,30 +285,41 @@ lock_retry_remap:
|
||||
err = -EIO;
|
||||
}
|
||||
}
|
||||
if (!need_write)
|
||||
up_read(&ni->runlist.lock);
|
||||
else
|
||||
up_write(&ni->runlist.lock);
|
||||
if (!err && !is_retry) {
|
||||
/*
|
||||
* The @vcn is in an unmapped region, map the runlist and
|
||||
* 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)) {
|
||||
is_retry = TRUE;
|
||||
goto lock_retry_remap;
|
||||
goto retry_remap;
|
||||
}
|
||||
/*
|
||||
* -EINVAL and -ENOENT coming from a failed mapping attempt are
|
||||
* equivalent to i/o errors for us as they should not happen in
|
||||
* our code paths.
|
||||
* -EINVAL coming from a failed mapping attempt is equivalent
|
||||
* to i/o error for us as it should not happen in our code
|
||||
* paths.
|
||||
*/
|
||||
if (err == -EINVAL || err == -ENOENT)
|
||||
if (err == -EINVAL)
|
||||
err = -EIO;
|
||||
} else if (!err)
|
||||
err = -EIO;
|
||||
ntfs_error(ni->vol->sb, "Failed with error code %i.", err);
|
||||
if (err != -ENOENT)
|
||||
ntfs_error(ni->vol->sb, "Failed with error code %i.", err);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
@ -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,
|
||||
ntfs_inode *ni, MFT_RECORD *mrec)
|
||||
{
|
||||
ctx->mrec = mrec;
|
||||
/* Sanity checks are performed elsewhere. */
|
||||
ctx->attr = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset));
|
||||
ctx->is_first = TRUE;
|
||||
ctx->ntfs_ino = ni;
|
||||
ctx->al_entry = NULL;
|
||||
ctx->base_ntfs_ino = NULL;
|
||||
ctx->base_mrec = NULL;
|
||||
ctx->base_attr = NULL;
|
||||
*ctx = (ntfs_attr_search_ctx) {
|
||||
.mrec = mrec,
|
||||
/* Sanity checks are performed elsewhere. */
|
||||
.attr = (ATTR_RECORD*)((u8*)mrec +
|
||||
le16_to_cpu(mrec->attrs_offset)),
|
||||
.is_first = TRUE,
|
||||
.ntfs_ino = ni,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -945,6 +1079,8 @@ void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef NTFS_RW
|
||||
|
||||
/**
|
||||
* ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file
|
||||
* @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,
|
||||
* Check whether the attribute of @type on the ntfs volume @vol is allowed to
|
||||
* be non-resident. This information is obtained from $AttrDef system file.
|
||||
*
|
||||
* Return 0 if the attribute is allowed to be non-resident, -EPERM if not, or
|
||||
* Return 0 if the attribute is allowed to be non-resident, -EPERM if not, and
|
||||
* -ENOENT if the attribute is not listed in $AttrDef.
|
||||
*/
|
||||
int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPE type)
|
||||
{
|
||||
ATTR_DEF *ad;
|
||||
|
||||
/*
|
||||
* $DATA is always allowed to be non-resident even if $AttrDef does not
|
||||
* specify this in the flags of the $DATA attribute definition record.
|
||||
*/
|
||||
if (type == AT_DATA)
|
||||
return 0;
|
||||
/* Find the attribute definition record in $AttrDef. */
|
||||
ad = ntfs_attr_find_in_attrdef(vol, type);
|
||||
if (unlikely(!ad))
|
||||
return -ENOENT;
|
||||
/* Check the flags and return the result. */
|
||||
if (ad->flags & CAN_BE_NON_RESIDENT)
|
||||
return 0;
|
||||
return -EPERM;
|
||||
if (ad->flags & ATTR_DEF_RESIDENT)
|
||||
return -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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)
|
||||
{
|
||||
if (type != AT_INDEX_ALLOCATION && type != AT_EA)
|
||||
return 0;
|
||||
return -EPERM;
|
||||
if (type == AT_INDEX_ALLOCATION || type == AT_EA)
|
||||
return -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1116,6 +1246,328 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size)
|
||||
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
|
||||
* @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)
|
||||
* byte offset @ofs inside the attribute with the constant byte @val.
|
||||
*
|
||||
* 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
|
||||
* 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)
|
||||
end = ofs + cnt;
|
||||
end_ofs = end & ~PAGE_CACHE_MASK;
|
||||
/* 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.");
|
||||
return -ESPIPE;
|
||||
}
|
||||
@ -1256,3 +1712,5 @@ done:
|
||||
ntfs_debug("Done.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* NTFS_RW */
|
||||
|
@ -2,7 +2,7 @@
|
||||
* attrib.h - Defines for attribute handling in NTFS Linux kernel driver.
|
||||
* Part of the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2001-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2001-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2002 Richard Russon
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
@ -60,10 +60,14 @@ typedef struct {
|
||||
ATTR_RECORD *base_attr;
|
||||
} 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 runlist_element *ntfs_find_vcn(ntfs_inode *ni, const VCN vcn,
|
||||
const BOOL need_write);
|
||||
extern LCN ntfs_attr_vcn_to_lcn_nolock(ntfs_inode *ni, const VCN vcn,
|
||||
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,
|
||||
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,
|
||||
MFT_RECORD *mrec);
|
||||
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,
|
||||
const ATTR_TYPE type, const s64 size);
|
||||
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,
|
||||
|
||||
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,
|
||||
const u8 val);
|
||||
|
||||
#endif /* NTFS_RW */
|
||||
|
||||
#endif /* _LINUX_NTFS_ATTRIB_H */
|
||||
|
@ -96,13 +96,14 @@ void free_compression_buffers(void)
|
||||
/**
|
||||
* 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);
|
||||
unsigned int kp_ofs;
|
||||
|
||||
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
|
||||
* 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)
|
||||
clear_page(kp);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@ -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
|
||||
*/
|
||||
static inline void handle_bounds_compressed_page(ntfs_inode *ni,
|
||||
struct page *page)
|
||||
static inline void handle_bounds_compressed_page(struct page *page,
|
||||
const loff_t i_size, const s64 initialized_size)
|
||||
{
|
||||
if ((page->index >= (ni->initialized_size >> PAGE_CACHE_SHIFT)) &&
|
||||
(ni->initialized_size < VFS_I(ni)->i_size))
|
||||
zero_partial_compressed_page(ni, page);
|
||||
if ((page->index >= (initialized_size >> PAGE_CACHE_SHIFT)) &&
|
||||
(initialized_size < i_size))
|
||||
zero_partial_compressed_page(page, initialized_size);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -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)
|
||||
* @cb_start: compression block to decompress (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 critical section is finished.
|
||||
@ -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,
|
||||
int *dest_ofs, const int dest_max_index, const int dest_max_ofs,
|
||||
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),
|
||||
@ -219,9 +223,6 @@ return_error:
|
||||
spin_unlock(&ntfs_cb_lock);
|
||||
/* Second stage: finalize completed pages. */
|
||||
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++) {
|
||||
int di = completed_pages[i];
|
||||
|
||||
@ -230,7 +231,8 @@ return_error:
|
||||
* If we are outside the initialized size, zero
|
||||
* 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);
|
||||
kunmap(dp);
|
||||
SetPageUptodate(dp);
|
||||
@ -478,12 +480,14 @@ return_overflow:
|
||||
*/
|
||||
int ntfs_read_compressed_block(struct page *page)
|
||||
{
|
||||
loff_t i_size;
|
||||
s64 initialized_size;
|
||||
struct address_space *mapping = page->mapping;
|
||||
ntfs_inode *ni = NTFS_I(mapping->host);
|
||||
ntfs_volume *vol = ni->vol;
|
||||
struct super_block *sb = vol->sb;
|
||||
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;
|
||||
u8 *cb, *cb_pos, *cb_end;
|
||||
struct buffer_head **bhs;
|
||||
@ -552,8 +556,12 @@ int ntfs_read_compressed_block(struct page *page)
|
||||
* The remaining pages need to be allocated and inserted into the page
|
||||
* cache, alignment guarantees keep all the below much simpler. (-8
|
||||
*/
|
||||
max_page = ((VFS_I(ni)->i_size + PAGE_CACHE_SIZE - 1) >>
|
||||
PAGE_CACHE_SHIFT) - offset;
|
||||
read_lock_irqsave(&ni->size_lock, flags);
|
||||
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)
|
||||
max_page = nr_pages;
|
||||
for (i = 0; i < max_page; i++, offset++) {
|
||||
@ -824,7 +832,8 @@ lock_retry_remap:
|
||||
* If we are outside the initialized size, zero
|
||||
* 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);
|
||||
kunmap(page);
|
||||
SetPageUptodate(page);
|
||||
@ -847,7 +856,8 @@ lock_retry_remap:
|
||||
ntfs_debug("Found compressed compression block.");
|
||||
err = ntfs_decompress(pages, &cur_page, &cur_ofs,
|
||||
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
|
||||
* ntfs_decompress().
|
||||
|
@ -164,14 +164,17 @@ void ntfs_debug_dump_runlist(const runlist_element *rl)
|
||||
if (index > -LCN_ENOENT - 1)
|
||||
index = 3;
|
||||
printk(KERN_DEBUG "%-16Lx %s %-16Lx%s\n",
|
||||
(rl + i)->vcn, lcn_str[index],
|
||||
(rl + i)->length, (rl + i)->length ?
|
||||
"" : " (runlist end)");
|
||||
(long long)(rl + i)->vcn, lcn_str[index],
|
||||
(long long)(rl + i)->length,
|
||||
(rl + i)->length ? "" :
|
||||
" (runlist end)");
|
||||
} else
|
||||
printk(KERN_DEBUG "%-16Lx %-16Lx %-16Lx%s\n",
|
||||
(rl + i)->vcn, (rl + i)->lcn,
|
||||
(rl + i)->length, (rl + i)->length ?
|
||||
"" : " (runlist end)");
|
||||
(long long)(rl + i)->vcn,
|
||||
(long long)(rl + i)->lcn,
|
||||
(long long)(rl + i)->length,
|
||||
(rl + i)->length ? "" :
|
||||
" (runlist end)");
|
||||
if (!(rl + i)->length)
|
||||
break;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
@ -183,8 +183,7 @@ found_it:
|
||||
name->len = 0;
|
||||
*res = name;
|
||||
} else {
|
||||
if (name)
|
||||
kfree(name);
|
||||
kfree(name);
|
||||
*res = NULL;
|
||||
}
|
||||
mref = le64_to_cpu(ie->data.dir.indexed_file);
|
||||
@ -444,8 +443,7 @@ found_it2:
|
||||
name->len = 0;
|
||||
*res = name;
|
||||
} else {
|
||||
if (name)
|
||||
kfree(name);
|
||||
kfree(name);
|
||||
*res = NULL;
|
||||
}
|
||||
mref = le64_to_cpu(ie->data.dir.indexed_file);
|
||||
@ -610,7 +608,7 @@ dir_err_out:
|
||||
// TODO: (AIA)
|
||||
// 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
|
||||
// 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
|
||||
@ -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)
|
||||
{
|
||||
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 super_block *sb = vdir->i_sb;
|
||||
ntfs_inode *ndir = NTFS_I(vdir);
|
||||
@ -1122,7 +1120,8 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
||||
vdir->i_ino, fpos);
|
||||
rc = err = 0;
|
||||
/* 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;
|
||||
/* Emulate . and .. for all directories. */
|
||||
if (!fpos) {
|
||||
@ -1264,7 +1263,7 @@ skip_index_root:
|
||||
bmp_mapping = bmp_vi->i_mapping;
|
||||
/* Get the starting bitmap bit position and sanity check it. */
|
||||
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 "
|
||||
"index bitmap size.");
|
||||
goto err_out;
|
||||
@ -1301,7 +1300,7 @@ find_next_index_buffer:
|
||||
goto get_next_bmp_page;
|
||||
}
|
||||
/* 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;
|
||||
ia_pos = (bmp_pos + cur_bmp_pos) <<
|
||||
ndir->itype.index.block_size_bits;
|
||||
@ -1309,7 +1308,8 @@ find_next_index_buffer:
|
||||
ntfs_debug("Handling index buffer 0x%llx.",
|
||||
(unsigned long long)bmp_pos + cur_bmp_pos);
|
||||
/* 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;
|
||||
if (likely(ia_page != NULL)) {
|
||||
unlock_page(ia_page);
|
||||
@ -1441,7 +1441,7 @@ unm_EOD:
|
||||
ntfs_unmap_page(bmp_page);
|
||||
EOD:
|
||||
/* We are finished, set fpos to EOD. */
|
||||
fpos = vdir->i_size + vol->mft_record_size;
|
||||
fpos = i_size + vol->mft_record_size;
|
||||
abort:
|
||||
kfree(name);
|
||||
done:
|
||||
@ -1461,10 +1461,8 @@ err_out:
|
||||
unlock_page(ia_page);
|
||||
ntfs_unmap_page(ia_page);
|
||||
}
|
||||
if (ir)
|
||||
kfree(ir);
|
||||
if (name)
|
||||
kfree(name);
|
||||
kfree(ir);
|
||||
kfree(name);
|
||||
if (ctx)
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
if (m)
|
||||
@ -1495,7 +1493,7 @@ err_out:
|
||||
static int ntfs_dir_open(struct inode *vi, struct file *filp)
|
||||
{
|
||||
if (sizeof(unsigned long) < 8) {
|
||||
if (vi->i_size > MAX_LFS_FILESIZE)
|
||||
if (i_size_read(vi) > MAX_LFS_FILESIZE)
|
||||
return -EFBIG;
|
||||
}
|
||||
return 0;
|
||||
|
@ -47,7 +47,7 @@
|
||||
static int ntfs_file_open(struct inode *vi, struct file *filp)
|
||||
{
|
||||
if (sizeof(unsigned long) < 8) {
|
||||
if (vi->i_size > MAX_LFS_FILESIZE)
|
||||
if (i_size_read(vi) > MAX_LFS_FILESIZE)
|
||||
return -EFBIG;
|
||||
}
|
||||
return generic_file_open(vi, filp);
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
* 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)
|
||||
ntfs_index_context *ictx;
|
||||
|
||||
ictx = kmem_cache_alloc(ntfs_index_ctx_cache, SLAB_NOFS);
|
||||
if (ictx) {
|
||||
ictx->idx_ni = idx_ni;
|
||||
ictx->entry = NULL;
|
||||
ictx->data = NULL;
|
||||
ictx->data_len = 0;
|
||||
ictx->is_in_root = 0;
|
||||
ictx->ir = NULL;
|
||||
ictx->actx = NULL;
|
||||
ictx->base_ni = NULL;
|
||||
ictx->ia = NULL;
|
||||
ictx->page = NULL;
|
||||
}
|
||||
if (ictx)
|
||||
*ictx = (ntfs_index_context){ .idx_ni = idx_ni };
|
||||
return ictx;
|
||||
}
|
||||
|
||||
|
530
fs/ntfs/inode.c
530
fs/ntfs/inode.c
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
* inode.h - Defines for inode structures NTFS Linux kernel driver. Part of
|
||||
* the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2001-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2001-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2002 Richard Russon
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
@ -44,6 +44,7 @@ typedef struct _ntfs_inode ntfs_inode;
|
||||
* fields already provided in the VFS inode.
|
||||
*/
|
||||
struct _ntfs_inode {
|
||||
rwlock_t size_lock; /* Lock serializing access to inode sizes. */
|
||||
s64 initialized_size; /* Copy from the attribute record. */
|
||||
s64 allocated_size; /* Copy from the attribute record. */
|
||||
unsigned long state; /* NTFS specific flags describing this inode.
|
||||
@ -109,7 +110,7 @@ struct _ntfs_inode {
|
||||
u8 block_size_bits; /* Log2 of the above. */
|
||||
u8 vcn_size_bits; /* Log2 of the above. */
|
||||
} 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
|
||||
$DATA. */
|
||||
u32 block_size; /* Size of a compression block
|
||||
@ -165,6 +166,7 @@ typedef enum {
|
||||
NI_Sparse, /* 1: Unnamed data attr is sparse (f).
|
||||
1: Create sparse files by default (d).
|
||||
1: Attribute is sparse (a). */
|
||||
NI_SparseDisabled, /* 1: May not create sparse regions. */
|
||||
NI_TruncateFailed, /* 1: Last ntfs_truncate() call failed. */
|
||||
} ntfs_inode_state_bits;
|
||||
|
||||
@ -217,6 +219,7 @@ NINO_FNS(IndexAllocPresent)
|
||||
NINO_FNS(Compressed)
|
||||
NINO_FNS(Encrypted)
|
||||
NINO_FNS(Sparse)
|
||||
NINO_FNS(SparseDisabled)
|
||||
NINO_FNS(TruncateFailed)
|
||||
|
||||
/*
|
||||
|
@ -2,7 +2,7 @@
|
||||
* layout.h - All NTFS associated on-disk structures. Part of the Linux-NTFS
|
||||
* project.
|
||||
*
|
||||
* Copyright (c) 2001-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2001-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2002 Richard Russon
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
@ -547,26 +547,44 @@ enum {
|
||||
COLLATION_NTOFS_ULONG = const_cpu_to_le32(0x10),
|
||||
COLLATION_NTOFS_SID = const_cpu_to_le32(0x11),
|
||||
COLLATION_NTOFS_SECURITY_HASH = const_cpu_to_le32(0x12),
|
||||
COLLATION_NTOFS_ULONGS = const_cpu_to_le32(0x13)
|
||||
COLLATION_NTOFS_ULONGS = const_cpu_to_le32(0x13),
|
||||
};
|
||||
|
||||
typedef le32 COLLATION_RULE;
|
||||
|
||||
/*
|
||||
* The flags (32-bit) describing attribute properties in the attribute
|
||||
* definition structure. FIXME: This information is from Regis's information
|
||||
* and, according to him, it is not certain and probably incomplete.
|
||||
* The INDEXABLE flag is fairly certainly correct as only the file name
|
||||
* attribute has this flag set and this is the only attribute indexed in NT4.
|
||||
* definition structure. FIXME: This information is based on Regis's
|
||||
* information and, according to him, it is not certain and probably
|
||||
* incomplete. The INDEXABLE flag is fairly certainly correct as only the file
|
||||
* name attribute has this flag set and this is the only attribute indexed in
|
||||
* NT4.
|
||||
*/
|
||||
enum {
|
||||
INDEXABLE = const_cpu_to_le32(0x02), /* Attribute can be
|
||||
indexed. */
|
||||
NEED_TO_REGENERATE = const_cpu_to_le32(0x40), /* Need to regenerate
|
||||
during regeneration
|
||||
phase. */
|
||||
CAN_BE_NON_RESIDENT = const_cpu_to_le32(0x80), /* Attribute can be
|
||||
non-resident. */
|
||||
ATTR_DEF_INDEXABLE = const_cpu_to_le32(0x02), /* Attribute can be
|
||||
indexed. */
|
||||
ATTR_DEF_MULTIPLE = const_cpu_to_le32(0x04), /* Attribute type
|
||||
can be present multiple times in the
|
||||
mft records of an inode. */
|
||||
ATTR_DEF_NOT_ZERO = const_cpu_to_le32(0x08), /* Attribute value
|
||||
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;
|
||||
@ -749,10 +767,11 @@ typedef struct {
|
||||
record header aligned to 8-byte boundary. */
|
||||
/* 34*/ u8 compression_unit; /* The compression unit expressed
|
||||
as the log to the base 2 of the number of
|
||||
clusters in a compression unit. 0 means not
|
||||
compressed. (This effectively limits the
|
||||
clusters in a compression unit. 0 means not
|
||||
compressed. (This effectively limits the
|
||||
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. */
|
||||
/* The sizes below are only used when lowest_vcn is zero, as otherwise it would
|
||||
be difficult to keep them up-to-date.*/
|
||||
@ -772,10 +791,10 @@ typedef struct {
|
||||
data_size. */
|
||||
/* sizeof(uncompressed attr) = 64*/
|
||||
/* 64*/ sle64 compressed_size; /* Byte size of the attribute
|
||||
value after compression. Only present when
|
||||
compressed. Always is a multiple of the
|
||||
cluster size. Represents the actual amount of
|
||||
disk space being used on the disk. */
|
||||
value after compression. Only present when
|
||||
compressed or sparse. Always is a multiple of
|
||||
the cluster size. Represents the actual amount
|
||||
of disk space being used on the disk. */
|
||||
/* sizeof(compressed attr) = 72*/
|
||||
} __attribute__ ((__packed__)) non_resident;
|
||||
} __attribute__ ((__packed__)) data;
|
||||
@ -834,7 +853,7 @@ enum {
|
||||
/* 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
|
||||
index, quota index, one of the security indexes or the encrypting
|
||||
file system related indexes). */
|
||||
filesystem related indexes). */
|
||||
};
|
||||
|
||||
typedef le32 FILE_ATTR_FLAGS;
|
||||
@ -917,20 +936,12 @@ typedef struct {
|
||||
/* 56*/ le64 quota_charged; /* Byte size of the charge to
|
||||
the quota for all streams of the file. Note: Is
|
||||
zero if quotas are disabled. */
|
||||
/* 64*/ le64 usn; /* Last update sequence number
|
||||
of the file. This is a direct index into the
|
||||
change (aka usn) journal file. It is zero if
|
||||
the usn journal is disabled.
|
||||
NOTE: To disable the journal need to delete
|
||||
the journal file itself and to then walk the
|
||||
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. */
|
||||
/* 64*/ leUSN usn; /* Last update sequence number
|
||||
of the file. This is a direct index into the
|
||||
transaction log file ($UsnJrnl). It is zero if
|
||||
the usn journal is disabled or this file has
|
||||
not been subject to logging yet. See usnjrnl.h
|
||||
for details. */
|
||||
} __attribute__ ((__packed__)) v3;
|
||||
/* sizeof() = 72 bytes (NTFS 3.x) */
|
||||
} __attribute__ ((__packed__)) ver;
|
||||
@ -1893,7 +1904,7 @@ enum {
|
||||
VOLUME_FLAGS_MASK = const_cpu_to_le16(0x803f),
|
||||
|
||||
/* 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__));
|
||||
|
||||
typedef le16 VOLUME_FLAGS;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
* 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,
|
||||
if (rl->lcn < 0)
|
||||
continue;
|
||||
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;
|
||||
}
|
||||
ntfs_debug("Done.");
|
||||
@ -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 prev_lcn = 0, prev_run_len = 0, mft_zone_size;
|
||||
s64 clusters;
|
||||
loff_t i_size;
|
||||
struct inode *lcnbmp_vi;
|
||||
runlist_element *rl = NULL;
|
||||
struct address_space *mapping;
|
||||
@ -249,6 +250,7 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn,
|
||||
clusters = count;
|
||||
rlpos = rlsize = 0;
|
||||
mapping = lcnbmp_vi->i_mapping;
|
||||
i_size = i_size_read(lcnbmp_vi);
|
||||
while (1) {
|
||||
ntfs_debug("Start of outer while loop: done_zones 0x%x, "
|
||||
"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,
|
||||
last_read_pos = bmp_pos >> 3;
|
||||
ntfs_debug("last_read_pos 0x%llx.",
|
||||
(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. "
|
||||
"Skipping to zone_pass_done.");
|
||||
goto zone_pass_done;
|
||||
@ -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 = page_address(page) + buf_size;
|
||||
buf_size = PAGE_CACHE_SIZE - buf_size;
|
||||
if (unlikely(last_read_pos + buf_size > lcnbmp_vi->i_size))
|
||||
buf_size = lcnbmp_vi->i_size - last_read_pos;
|
||||
if (unlikely(last_read_pos + buf_size > i_size))
|
||||
buf_size = i_size - last_read_pos;
|
||||
buf_size <<= 3;
|
||||
lcn = bmp_pos & 7;
|
||||
bmp_pos &= ~7;
|
||||
bmp_pos &= ~(LCN)7;
|
||||
ntfs_debug("Before inner while loop: buf_size %i, lcn 0x%llx, "
|
||||
"bmp_pos 0x%llx, need_writeback %i.", buf_size,
|
||||
(unsigned long long)lcn,
|
||||
@ -309,7 +311,7 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn,
|
||||
(unsigned int)*byte);
|
||||
/* Skip full bytes. */
|
||||
if (*byte == 0xff) {
|
||||
lcn = (lcn + 8) & ~7;
|
||||
lcn = (lcn + 8) & ~(LCN)7;
|
||||
ntfs_debug("Continuing while loop 1.");
|
||||
continue;
|
||||
}
|
||||
@ -691,7 +693,7 @@ switch_to_data1_zone: search_zone = 2;
|
||||
if (zone == MFT_ZONE || mft_zone_size <= 0) {
|
||||
ntfs_debug("No free clusters left, going to out.");
|
||||
/* Really no more space left on device. */
|
||||
err = ENOSPC;
|
||||
err = -ENOSPC;
|
||||
goto out;
|
||||
} /* zone == DATA_ZONE && mft_zone_size > 0 */
|
||||
ntfs_debug("Shrinking mft zone.");
|
||||
@ -755,13 +757,13 @@ out:
|
||||
if (rl) {
|
||||
int err2;
|
||||
|
||||
if (err == ENOSPC)
|
||||
if (err == -ENOSPC)
|
||||
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 "
|
||||
"clusters.",
|
||||
(unsigned long long)rl[0].lcn,
|
||||
(unsigned long long)count - clusters);
|
||||
(unsigned long long)(count - clusters));
|
||||
/* Deallocate all allocated clusters. */
|
||||
ntfs_debug("Attempting rollback...");
|
||||
err2 = ntfs_cluster_free_from_rl_nolock(vol, rl);
|
||||
@ -773,10 +775,10 @@ out:
|
||||
}
|
||||
/* Free the runlist. */
|
||||
ntfs_free(rl);
|
||||
} else if (err == ENOSPC)
|
||||
ntfs_debug("No space left at all, err = ENOSPC, "
|
||||
"first free lcn = 0x%llx.",
|
||||
(unsigned long long)vol->data1_zone_pos);
|
||||
} else if (err == -ENOSPC)
|
||||
ntfs_debug("No space left at all, err = -ENOSPC, first free "
|
||||
"lcn = 0x%llx.",
|
||||
(long long)vol->data1_zone_pos);
|
||||
up_write(&vol->lcnbmp_lock);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
@ -846,8 +848,8 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count,
|
||||
|
||||
total_freed = real_freed = 0;
|
||||
|
||||
/* This returns with ni->runlist locked for reading on success. */
|
||||
rl = ntfs_find_vcn(ni, start_vcn, FALSE);
|
||||
down_read(&ni->runlist.lock);
|
||||
rl = ntfs_attr_find_vcn_nolock(ni, start_vcn, FALSE);
|
||||
if (IS_ERR(rl)) {
|
||||
if (!is_rollback)
|
||||
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,
|
||||
ntfs_error(vol->sb, "First runlist element has "
|
||||
"invalid lcn, aborting.");
|
||||
err = -EIO;
|
||||
goto unl_err_out;
|
||||
goto err_out;
|
||||
}
|
||||
/* Find the starting cluster inside the run that needs freeing. */
|
||||
delta = start_vcn - rl->vcn;
|
||||
@ -879,7 +881,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count,
|
||||
if (!is_rollback)
|
||||
ntfs_error(vol->sb, "Failed to clear first run "
|
||||
"(error %i), aborting.", err);
|
||||
goto unl_err_out;
|
||||
goto err_out;
|
||||
}
|
||||
/* We have freed @to_free real clusters. */
|
||||
real_freed = to_free;
|
||||
@ -899,30 +901,15 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count,
|
||||
if (unlikely(rl->lcn < LCN_HOLE)) {
|
||||
VCN vcn;
|
||||
|
||||
/*
|
||||
* Attempt to map runlist, dropping runlist lock for
|
||||
* the duration.
|
||||
*/
|
||||
/* Attempt to map runlist. */
|
||||
vcn = rl->vcn;
|
||||
up_read(&ni->runlist.lock);
|
||||
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);
|
||||
rl = ntfs_attr_find_vcn_nolock(ni, vcn, FALSE);
|
||||
if (IS_ERR(rl)) {
|
||||
err = PTR_ERR(rl);
|
||||
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 "
|
||||
"element.");
|
||||
goto err_out;
|
||||
@ -935,7 +922,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count,
|
||||
(unsigned long long)
|
||||
rl->lcn);
|
||||
err = -EIO;
|
||||
goto unl_err_out;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
/* 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,
|
||||
if (!is_rollback)
|
||||
ntfs_error(vol->sb, "Failed to clear "
|
||||
"subsequent run.");
|
||||
goto unl_err_out;
|
||||
goto err_out;
|
||||
}
|
||||
/* We have freed @to_free real clusters. */
|
||||
real_freed += to_free;
|
||||
@ -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. */
|
||||
ntfs_debug("Done.");
|
||||
return real_freed;
|
||||
unl_err_out:
|
||||
up_read(&ni->runlist.lock);
|
||||
err_out:
|
||||
up_read(&ni->runlist.lock);
|
||||
if (is_rollback)
|
||||
return err;
|
||||
/* If no real clusters were freed, no need to rollback. */
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
@ -410,7 +410,7 @@ err_out:
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* Check the $LogFile journal for consistency and return TRUE if it is
|
||||
@ -443,7 +443,7 @@ BOOL ntfs_check_logfile(struct inode *log_vi)
|
||||
/* An empty $LogFile must have been clean before it got emptied. */
|
||||
if (NVolLogFileEmpty(vol))
|
||||
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. */
|
||||
if (size > MaxLogFileSize)
|
||||
size = MaxLogFileSize;
|
||||
@ -464,7 +464,7 @@ BOOL ntfs_check_logfile(struct inode *log_vi)
|
||||
* optimize log_page_size and log_page_bits into constants.
|
||||
*/
|
||||
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
|
||||
* pages and the minimum number of log record pages.
|
||||
@ -689,7 +689,8 @@ BOOL ntfs_empty_logfile(struct inode *log_vi)
|
||||
if (!NVolLogFileEmpty(vol)) {
|
||||
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)) {
|
||||
ntfs_error(vol->sb, "Failed to fill $LogFile with "
|
||||
"0xff bytes (error code %i).", err);
|
||||
|
227
fs/ntfs/mft.c
227
fs/ntfs/mft.c
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
@ -45,6 +45,7 @@
|
||||
*/
|
||||
static inline MFT_RECORD *map_mft_record_page(ntfs_inode *ni)
|
||||
{
|
||||
loff_t i_size;
|
||||
ntfs_volume *vol = ni->vol;
|
||||
struct inode *mft_vi = vol->mft_ino;
|
||||
struct page *page;
|
||||
@ -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;
|
||||
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. */
|
||||
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 (unlikely(index >= end_index)) {
|
||||
if (index > end_index || (mft_vi->i_size & ~PAGE_CACHE_MASK) <
|
||||
ofs + vol->mft_record_size) {
|
||||
if (index > end_index || (i_size & ~PAGE_CACHE_MASK) < ofs +
|
||||
vol->mft_record_size) {
|
||||
page = ERR_PTR(-ENOENT);
|
||||
ntfs_error(vol->sb, "Attemt to read mft record 0x%lx, "
|
||||
"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,
|
||||
}
|
||||
unmap_mft_record(ni);
|
||||
ntfs_error(base_ni->vol->sb, "Found stale extent mft "
|
||||
"reference! Corrupt file system. "
|
||||
"reference! Corrupt filesystem. "
|
||||
"Run chkdsk.");
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
@ -316,7 +318,7 @@ map_err_out:
|
||||
/* Verify the sequence number if it is present. */
|
||||
if (seq_no && (le16_to_cpu(m->sequence_number) != seq_no)) {
|
||||
ntfs_error(base_ni->vol->sb, "Found stale extent mft "
|
||||
"reference! Corrupt file system. Run chkdsk.");
|
||||
"reference! Corrupt filesystem. Run chkdsk.");
|
||||
destroy_ni = TRUE;
|
||||
m = ERR_PTR(-EIO);
|
||||
goto unm_err_out;
|
||||
@ -946,20 +948,23 @@ BOOL ntfs_may_write_mft_record(ntfs_volume *vol, const unsigned long mft_no,
|
||||
na.name_len = 0;
|
||||
na.type = AT_UNUSED;
|
||||
/*
|
||||
* For inode 0, i.e. $MFT itself, we cannot use ilookup5() from here or
|
||||
* we deadlock because the inode is already locked by the kernel
|
||||
* (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.
|
||||
* Optimize inode 0, i.e. $MFT itself, since we have it in memory and
|
||||
* we get here for it rather often.
|
||||
*/
|
||||
if (!mft_no) {
|
||||
/* Balance the below iput(). */
|
||||
vi = igrab(mft_vi);
|
||||
BUG_ON(vi != mft_vi);
|
||||
} else
|
||||
vi = ilookup5(sb, mft_no, (test_t)ntfs_test_inode, &na);
|
||||
} else {
|
||||
/*
|
||||
* 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) {
|
||||
ntfs_debug("Base inode 0x%lx is in icache.", mft_no);
|
||||
/* The inode is in icache. */
|
||||
@ -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);
|
||||
ntfs_debug("Mft record 0x%lx is an extent record. Looking for base "
|
||||
"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) {
|
||||
/*
|
||||
* 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,
|
||||
ntfs_inode *base_ni)
|
||||
{
|
||||
s64 pass_end, ll, data_pos, pass_start, ofs, bit;
|
||||
unsigned long flags;
|
||||
struct address_space *mftbmp_mapping;
|
||||
u8 *buf, *byte;
|
||||
struct page *page;
|
||||
@ -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
|
||||
* bitmap.
|
||||
*/
|
||||
read_lock_irqsave(&NTFS_I(vol->mft_ino)->size_lock, flags);
|
||||
pass_end = NTFS_I(vol->mft_ino)->allocated_size >>
|
||||
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;
|
||||
read_unlock_irqrestore(&NTFS_I(vol->mftbmp_ino)->size_lock, flags);
|
||||
if (pass_end > ll)
|
||||
pass_end = ll;
|
||||
pass = 1;
|
||||
@ -1263,6 +1279,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
|
||||
{
|
||||
LCN lcn;
|
||||
s64 ll;
|
||||
unsigned long flags;
|
||||
struct page *page;
|
||||
ntfs_inode *mft_ni, *mftbmp_ni;
|
||||
runlist_element *rl, *rl2 = NULL;
|
||||
@ -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
|
||||
* 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) >>
|
||||
vol->cluster_size_bits, TRUE);
|
||||
down_write(&mftbmp_ni->runlist.lock);
|
||||
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)) {
|
||||
up_write(&mftbmp_ni->runlist.lock);
|
||||
ntfs_error(vol->sb, "Failed to determine last allocated "
|
||||
"cluster of mft bitmap attribute.");
|
||||
if (!IS_ERR(rl)) {
|
||||
up_write(&mftbmp_ni->runlist.lock);
|
||||
if (!IS_ERR(rl))
|
||||
ret = -EIO;
|
||||
} else
|
||||
else
|
||||
ret = PTR_ERR(rl);
|
||||
return ret;
|
||||
}
|
||||
@ -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 + rl2->length);
|
||||
/* 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)) {
|
||||
ntfs_error(vol->sb, "Get size for mapping pairs failed for "
|
||||
"mft bitmap attribute extent.");
|
||||
@ -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
|
||||
// record or by starting a new extent in a new mft record or by
|
||||
// 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 "
|
||||
"accomodate extended mft bitmap attribute "
|
||||
"extent. Cannot handle this yet.");
|
||||
@ -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. */
|
||||
ret = ntfs_mapping_pairs_build(vol, (u8*)a +
|
||||
le16_to_cpu(a->data.non_resident.mapping_pairs_offset),
|
||||
mp_size, rl2, ll, NULL);
|
||||
mp_size, rl2, ll, -1, NULL);
|
||||
if (unlikely(ret)) {
|
||||
ntfs_error(vol->sb, "Failed to build mapping pairs array for "
|
||||
"mft bitmap attribute.");
|
||||
@ -1458,9 +1480,11 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
|
||||
}
|
||||
a = ctx->attr;
|
||||
}
|
||||
write_lock_irqsave(&mftbmp_ni->size_lock, flags);
|
||||
mftbmp_ni->allocated_size += vol->cluster_size;
|
||||
a->data.non_resident.allocated_size =
|
||||
cpu_to_sle64(mftbmp_ni->allocated_size);
|
||||
write_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
|
||||
/* Ensure the changes make it to disk. */
|
||||
flush_dcache_mft_record_page(ctx->ntfs_ino);
|
||||
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||
@ -1476,7 +1500,9 @@ restore_undo_alloc:
|
||||
0, ctx)) {
|
||||
ntfs_error(vol->sb, "Failed to find last attribute extent of "
|
||||
"mft bitmap attribute.%s", es);
|
||||
write_lock_irqsave(&mftbmp_ni->size_lock, flags);
|
||||
mftbmp_ni->allocated_size += vol->cluster_size;
|
||||
write_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
unmap_mft_record(mft_ni);
|
||||
up_write(&mftbmp_ni->runlist.lock);
|
||||
@ -1512,7 +1538,7 @@ undo_alloc:
|
||||
a->data.non_resident.mapping_pairs_offset),
|
||||
old_alen - le16_to_cpu(
|
||||
a->data.non_resident.mapping_pairs_offset),
|
||||
rl2, ll, NULL)) {
|
||||
rl2, ll, -1, NULL)) {
|
||||
ntfs_error(vol->sb, "Failed to restore mapping pairs "
|
||||
"array.%s", es);
|
||||
NVolSetErrors(vol);
|
||||
@ -1550,6 +1576,7 @@ undo_alloc:
|
||||
static int ntfs_mft_bitmap_extend_initialized_nolock(ntfs_volume *vol)
|
||||
{
|
||||
s64 old_data_size, old_initialized_size;
|
||||
unsigned long flags;
|
||||
struct inode *mftbmp_vi;
|
||||
ntfs_inode *mft_ni, *mftbmp_ni;
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
@ -1583,7 +1610,8 @@ static int ntfs_mft_bitmap_extend_initialized_nolock(ntfs_volume *vol)
|
||||
goto put_err_out;
|
||||
}
|
||||
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;
|
||||
/*
|
||||
* 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)
|
||||
mftbmp_ni->initialized_size += 8;
|
||||
a->data.non_resident.initialized_size =
|
||||
cpu_to_sle64(mftbmp_ni->initialized_size);
|
||||
if (mftbmp_ni->initialized_size > mftbmp_vi->i_size) {
|
||||
mftbmp_vi->i_size = mftbmp_ni->initialized_size;
|
||||
if (mftbmp_ni->initialized_size > old_data_size) {
|
||||
i_size_write(mftbmp_vi, mftbmp_ni->initialized_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. */
|
||||
flush_dcache_mft_record_page(ctx->ntfs_ino);
|
||||
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||
@ -1636,22 +1665,28 @@ unm_err_out:
|
||||
goto err_out;
|
||||
}
|
||||
a = ctx->attr;
|
||||
write_lock_irqsave(&mftbmp_ni->size_lock, flags);
|
||||
mftbmp_ni->initialized_size = old_initialized_size;
|
||||
a->data.non_resident.initialized_size =
|
||||
cpu_to_sle64(old_initialized_size);
|
||||
if (mftbmp_vi->i_size != old_data_size) {
|
||||
mftbmp_vi->i_size = old_data_size;
|
||||
if (i_size_read(mftbmp_vi) != old_data_size) {
|
||||
i_size_write(mftbmp_vi, 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);
|
||||
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
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, "
|
||||
"data_size 0x%llx, initialized_size 0x%llx.",
|
||||
(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);
|
||||
read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
|
||||
#endif /* DEBUG */
|
||||
err_out:
|
||||
return ret;
|
||||
}
|
||||
@ -1679,7 +1714,8 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
|
||||
{
|
||||
LCN lcn;
|
||||
VCN old_last_vcn;
|
||||
s64 min_nr, nr, ll = 0;
|
||||
s64 min_nr, nr, ll;
|
||||
unsigned long flags;
|
||||
ntfs_inode *mft_ni;
|
||||
runlist_element *rl, *rl2;
|
||||
ntfs_attr_search_ctx *ctx = NULL;
|
||||
@ -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
|
||||
* the mft data attribute. The allocated size of the mft data
|
||||
* 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) >>
|
||||
vol->cluster_size_bits, TRUE);
|
||||
down_write(&mft_ni->runlist.lock);
|
||||
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)) {
|
||||
up_write(&mft_ni->runlist.lock);
|
||||
ntfs_error(vol->sb, "Failed to determine last allocated "
|
||||
"cluster of mft data attribute.");
|
||||
if (!IS_ERR(rl)) {
|
||||
up_write(&mft_ni->runlist.lock);
|
||||
if (!IS_ERR(rl))
|
||||
ret = -EIO;
|
||||
} else
|
||||
else
|
||||
ret = PTR_ERR(rl);
|
||||
return ret;
|
||||
}
|
||||
lcn = rl->lcn + rl->length;
|
||||
ntfs_debug("Last lcn of mft data attribute is 0x%llx.",
|
||||
(long long)lcn);
|
||||
ntfs_debug("Last lcn of mft data attribute is 0x%llx.", (long long)lcn);
|
||||
/* Minimum allocation is one mft record worth of clusters. */
|
||||
min_nr = vol->mft_record_size >> vol->cluster_size_bits;
|
||||
if (!min_nr)
|
||||
@ -1721,12 +1759,13 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
|
||||
if (!nr)
|
||||
nr = min_nr;
|
||||
/* Ensure we do not go above 2^32-1 mft records. */
|
||||
if (unlikely((mft_ni->allocated_size +
|
||||
(nr << vol->cluster_size_bits)) >>
|
||||
read_lock_irqsave(&mft_ni->size_lock, flags);
|
||||
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))) {
|
||||
nr = min_nr;
|
||||
if (unlikely((mft_ni->allocated_size +
|
||||
(nr << vol->cluster_size_bits)) >>
|
||||
if (unlikely((ll + (nr << vol->cluster_size_bits)) >>
|
||||
vol->mft_record_size_bits >= (1ll << 32))) {
|
||||
ntfs_warning(vol->sb, "Cannot allocate mft record "
|
||||
"because the maximum number of inodes "
|
||||
@ -1772,7 +1811,7 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
|
||||
return PTR_ERR(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. */
|
||||
for (; rl[1].length; rl++)
|
||||
;
|
||||
@ -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 + rl2->length);
|
||||
/* 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)) {
|
||||
ntfs_error(vol->sb, "Get size for mapping pairs failed for "
|
||||
"mft data attribute extent.");
|
||||
@ -1832,7 +1871,11 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
|
||||
// moving other attributes out of this mft record.
|
||||
// Note: Use the special reserved mft records and ensure that
|
||||
// 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 "
|
||||
"accomodate extended mft data attribute "
|
||||
"extent. Cannot handle this yet.");
|
||||
@ -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. */
|
||||
ret = ntfs_mapping_pairs_build(vol, (u8*)a +
|
||||
le16_to_cpu(a->data.non_resident.mapping_pairs_offset),
|
||||
mp_size, rl2, ll, NULL);
|
||||
mp_size, rl2, ll, -1, NULL);
|
||||
if (unlikely(ret)) {
|
||||
ntfs_error(vol->sb, "Failed to build mapping pairs array of "
|
||||
"mft data attribute.");
|
||||
@ -1875,9 +1918,11 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
|
||||
}
|
||||
a = ctx->attr;
|
||||
}
|
||||
write_lock_irqsave(&mft_ni->size_lock, flags);
|
||||
mft_ni->allocated_size += nr << vol->cluster_size_bits;
|
||||
a->data.non_resident.allocated_size =
|
||||
cpu_to_sle64(mft_ni->allocated_size);
|
||||
write_unlock_irqrestore(&mft_ni->size_lock, flags);
|
||||
/* Ensure the changes make it to disk. */
|
||||
flush_dcache_mft_record_page(ctx->ntfs_ino);
|
||||
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||
@ -1892,7 +1937,9 @@ restore_undo_alloc:
|
||||
CASE_SENSITIVE, rl[1].vcn, NULL, 0, ctx)) {
|
||||
ntfs_error(vol->sb, "Failed to find last attribute extent of "
|
||||
"mft data attribute.%s", es);
|
||||
write_lock_irqsave(&mft_ni->size_lock, flags);
|
||||
mft_ni->allocated_size += nr << vol->cluster_size_bits;
|
||||
write_unlock_irqrestore(&mft_ni->size_lock, flags);
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
unmap_mft_record(mft_ni);
|
||||
up_write(&mft_ni->runlist.lock);
|
||||
@ -1921,7 +1968,7 @@ undo_alloc:
|
||||
a->data.non_resident.mapping_pairs_offset),
|
||||
old_alen - le16_to_cpu(
|
||||
a->data.non_resident.mapping_pairs_offset),
|
||||
rl2, ll, NULL)) {
|
||||
rl2, ll, -1, NULL)) {
|
||||
ntfs_error(vol->sb, "Failed to restore mapping pairs "
|
||||
"array.%s", es);
|
||||
NVolSetErrors(vol);
|
||||
@ -1991,7 +2038,7 @@ static int ntfs_mft_record_layout(const ntfs_volume *vol, const s64 mft_no,
|
||||
"reports this as corruption, please email "
|
||||
"linux-ntfs-dev@lists.sourceforge.net stating "
|
||||
"that you saw this message and that the "
|
||||
"modified file system created was corrupt. "
|
||||
"modified filesystem created was corrupt. "
|
||||
"Thank you.");
|
||||
}
|
||||
/* 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,
|
||||
*/
|
||||
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 page *page;
|
||||
MFT_RECORD *m;
|
||||
@ -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;
|
||||
ofs = (mft_no << vol->mft_record_size_bits) & ~PAGE_CACHE_MASK;
|
||||
/* 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 || 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 "
|
||||
"record 0x%llx.", (long long)mft_no);
|
||||
return -ENOENT;
|
||||
@ -2188,6 +2237,7 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
|
||||
ntfs_inode *base_ni, MFT_RECORD **mrec)
|
||||
{
|
||||
s64 ll, bit, old_data_initialized, old_data_size;
|
||||
unsigned long flags;
|
||||
struct inode *vi;
|
||||
struct page *page;
|
||||
ntfs_inode *mft_ni, *mftbmp_ni, *ni;
|
||||
@ -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
|
||||
* 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;
|
||||
if (mftbmp_ni->initialized_size << 3 > ll &&
|
||||
mftbmp_ni->initialized_size > 3) {
|
||||
read_unlock_irqrestore(&mft_ni->size_lock, flags);
|
||||
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;
|
||||
if (bit < 24)
|
||||
bit = 24;
|
||||
@ -2254,15 +2308,18 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
|
||||
* mft record that we can allocate.
|
||||
* 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)))
|
||||
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, "
|
||||
"data_size 0x%llx, initialized_size 0x%llx.",
|
||||
(long long)mftbmp_ni->allocated_size,
|
||||
(long long)vol->mftbmp_ino->i_size,
|
||||
(long long)mftbmp_ni->initialized_size);
|
||||
if (mftbmp_ni->initialized_size + 8 > mftbmp_ni->allocated_size) {
|
||||
(long long)old_data_size,
|
||||
(long long)i_size_read(vol->mftbmp_ino),
|
||||
(long long)old_data_initialized);
|
||||
read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
|
||||
if (old_data_initialized + 8 > old_data_size) {
|
||||
/* Need to extend bitmap by one more cluster. */
|
||||
ntfs_debug("mftbmp: initialized_size + 8 > allocated_size.");
|
||||
err = ntfs_mft_bitmap_extend_allocation_nolock(vol);
|
||||
@ -2270,12 +2327,16 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
|
||||
up_write(&vol->mftbmp_lock);
|
||||
goto err_out;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
read_lock_irqsave(&mftbmp_ni->size_lock, flags);
|
||||
ntfs_debug("Status of mftbmp after allocation extension: "
|
||||
"allocated_size 0x%llx, data_size 0x%llx, "
|
||||
"initialized_size 0x%llx.",
|
||||
(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);
|
||||
read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
|
||||
#endif /* DEBUG */
|
||||
}
|
||||
/*
|
||||
* 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,
|
||||
up_write(&vol->mftbmp_lock);
|
||||
goto err_out;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
read_lock_irqsave(&mftbmp_ni->size_lock, flags);
|
||||
ntfs_debug("Status of mftbmp after initialized extention: "
|
||||
"allocated_size 0x%llx, data_size 0x%llx, "
|
||||
"initialized_size 0x%llx.",
|
||||
(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);
|
||||
read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
|
||||
#endif /* DEBUG */
|
||||
ntfs_debug("Found free record (#3), bit 0x%llx.", (long long)bit);
|
||||
found_free_rec:
|
||||
/* @bit is the found free mft record, allocate it in the mft bitmap. */
|
||||
@ -2314,7 +2379,10 @@ have_alloc_rec:
|
||||
* parallel allocation could allocate the same mft record as this one.
|
||||
*/
|
||||
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.");
|
||||
goto mft_rec_already_initialized;
|
||||
}
|
||||
@ -2325,26 +2393,30 @@ have_alloc_rec:
|
||||
* actually traversed more than once when a freshly formatted volume is
|
||||
* 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: "
|
||||
"allocated_size 0x%llx, data_size 0x%llx, "
|
||||
"initialized_size 0x%llx.",
|
||||
(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);
|
||||
while (ll > mft_ni->allocated_size) {
|
||||
read_unlock_irqrestore(&mft_ni->size_lock, flags);
|
||||
err = ntfs_mft_data_extend_allocation_nolock(vol);
|
||||
if (unlikely(err)) {
|
||||
ntfs_error(vol->sb, "Failed to extend mft data "
|
||||
"allocation.");
|
||||
goto undo_mftbmp_alloc_nolock;
|
||||
}
|
||||
read_lock_irqsave(&mft_ni->size_lock, flags);
|
||||
ntfs_debug("Status of mft data after allocation extension: "
|
||||
"allocated_size 0x%llx, data_size 0x%llx, "
|
||||
"initialized_size 0x%llx.",
|
||||
(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);
|
||||
}
|
||||
read_unlock_irqrestore(&mft_ni->size_lock, flags);
|
||||
/*
|
||||
* Extend mft data initialized size (and data size of course) to reach
|
||||
* the allocated mft record, formatting the mft records allong the way.
|
||||
@ -2352,6 +2424,7 @@ have_alloc_rec:
|
||||
* needed by ntfs_mft_record_format(). We will update the attribute
|
||||
* 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_size = vol->mft_ino->i_size;
|
||||
while (ll > mft_ni->initialized_size) {
|
||||
@ -2360,8 +2433,9 @@ have_alloc_rec:
|
||||
new_initialized_size = mft_ni->initialized_size +
|
||||
vol->mft_record_size;
|
||||
mft_no = mft_ni->initialized_size >> vol->mft_record_size_bits;
|
||||
if (new_initialized_size > vol->mft_ino->i_size)
|
||||
vol->mft_ino->i_size = new_initialized_size;
|
||||
if (new_initialized_size > i_size_read(vol->mft_ino))
|
||||
i_size_write(vol->mft_ino, new_initialized_size);
|
||||
write_unlock_irqrestore(&mft_ni->size_lock, flags);
|
||||
ntfs_debug("Initializing mft record 0x%llx.",
|
||||
(long long)mft_no);
|
||||
err = ntfs_mft_record_format(vol, mft_no);
|
||||
@ -2369,8 +2443,10 @@ have_alloc_rec:
|
||||
ntfs_error(vol->sb, "Failed to format mft record.");
|
||||
goto undo_data_init;
|
||||
}
|
||||
write_lock_irqsave(&mft_ni->size_lock, flags);
|
||||
mft_ni->initialized_size = new_initialized_size;
|
||||
}
|
||||
write_unlock_irqrestore(&mft_ni->size_lock, flags);
|
||||
record_formatted = TRUE;
|
||||
/* Update the mft data attribute record to reflect the new sizes. */
|
||||
m = map_mft_record(mft_ni);
|
||||
@ -2396,22 +2472,27 @@ have_alloc_rec:
|
||||
goto undo_data_init;
|
||||
}
|
||||
a = ctx->attr;
|
||||
read_lock_irqsave(&mft_ni->size_lock, flags);
|
||||
a->data.non_resident.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. */
|
||||
flush_dcache_mft_record_page(ctx->ntfs_ino);
|
||||
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
unmap_mft_record(mft_ni);
|
||||
read_lock_irqsave(&mft_ni->size_lock, flags);
|
||||
ntfs_debug("Status of mft data after mft record initialization: "
|
||||
"allocated_size 0x%llx, data_size 0x%llx, "
|
||||
"initialized_size 0x%llx.",
|
||||
(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);
|
||||
BUG_ON(vol->mft_ino->i_size > mft_ni->allocated_size);
|
||||
BUG_ON(mft_ni->initialized_size > vol->mft_ino->i_size);
|
||||
BUG_ON(i_size_read(vol->mft_ino) > mft_ni->allocated_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:
|
||||
/*
|
||||
* We can finally drop the mft bitmap lock as the mft data attribute
|
||||
@ -2652,8 +2733,10 @@ mft_rec_already_initialized:
|
||||
*mrec = m;
|
||||
return ni;
|
||||
undo_data_init:
|
||||
write_lock_irqsave(&mft_ni->size_lock, flags);
|
||||
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;
|
||||
undo_mftbmp_alloc:
|
||||
down_write(&vol->mftbmp_lock);
|
||||
|
@ -153,8 +153,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
|
||||
ntfs_error(vol->sb, "ntfs_iget(0x%lx) failed with "
|
||||
"error code %li.", dent_ino,
|
||||
PTR_ERR(dent_inode));
|
||||
if (name)
|
||||
kfree(name);
|
||||
kfree(name);
|
||||
/* Return the error code. */
|
||||
return (struct dentry *)dent_inode;
|
||||
}
|
||||
@ -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
|
||||
* 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;
|
||||
ntfs_inode *ni = NTFS_I(vi);
|
||||
@ -465,7 +464,7 @@ try_next:
|
||||
*
|
||||
* 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 dentry *dent;
|
||||
@ -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);
|
||||
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 @@
|
||||
* ntfs.h - Defines for NTFS Linux kernel driver. Part of the Linux-NTFS
|
||||
* project.
|
||||
*
|
||||
* Copyright (c) 2001-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2001-2005 Anton Altaparmakov
|
||||
* Copyright (C) 2002 Richard Russon
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
@ -31,6 +31,7 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/nls.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/pagemap.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "volume.h"
|
||||
@ -41,6 +42,9 @@ typedef enum {
|
||||
NTFS_BLOCK_SIZE_BITS = 9,
|
||||
NTFS_SB_MAGIC = 0x5346544e, /* 'NTFS' */
|
||||
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;
|
||||
|
||||
/* Global variables. */
|
||||
@ -65,6 +69,8 @@ extern struct inode_operations ntfs_dir_inode_ops;
|
||||
extern struct file_operations ntfs_empty_file_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
|
||||
* @sb: VFS super block
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* 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,
|
||||
*
|
||||
* As the runlists grow, more memory will be required. To prevent the
|
||||
* 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.
|
||||
*
|
||||
@ -113,8 +113,11 @@ static inline BOOL ntfs_are_rl_mergeable(runlist_element *dst,
|
||||
BUG_ON(!dst);
|
||||
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;
|
||||
}
|
||||
if ((dst->lcn + dst->length) != src->lcn) /* Are the runs contiguous? */
|
||||
return FALSE;
|
||||
if ((dst->vcn + dst->length) != src->vcn) /* Are the runs misaligned? */
|
||||
@ -855,30 +858,42 @@ mpa_err:
|
||||
if (!attr->data.non_resident.lowest_vcn) {
|
||||
VCN max_cluster;
|
||||
|
||||
max_cluster = (sle64_to_cpu(
|
||||
max_cluster = ((sle64_to_cpu(
|
||||
attr->data.non_resident.allocated_size) +
|
||||
vol->cluster_size - 1) >>
|
||||
vol->cluster_size_bits;
|
||||
vol->cluster_size_bits) - 1;
|
||||
/*
|
||||
* 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.
|
||||
* A highest_vcn of zero means this is a single extent
|
||||
* attribute so simply terminate the runlist with LCN_ENOENT).
|
||||
*/
|
||||
if (deltaxcn < --max_cluster) {
|
||||
ntfs_debug("More extents to follow; deltaxcn = 0x%llx, "
|
||||
"max_cluster = 0x%llx",
|
||||
(unsigned long long)deltaxcn,
|
||||
(unsigned long long)max_cluster);
|
||||
rl[rlpos].vcn = vcn;
|
||||
vcn += rl[rlpos].length = max_cluster - deltaxcn;
|
||||
rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
|
||||
rlpos++;
|
||||
} else if (unlikely(deltaxcn > max_cluster)) {
|
||||
ntfs_error(vol->sb, "Corrupt attribute. deltaxcn = "
|
||||
"0x%llx, max_cluster = 0x%llx",
|
||||
(unsigned long long)deltaxcn,
|
||||
(unsigned long long)max_cluster);
|
||||
goto mpa_err;
|
||||
if (deltaxcn) {
|
||||
/*
|
||||
* 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)
|
||||
max_cluster);
|
||||
rl[rlpos].vcn = vcn;
|
||||
vcn += rl[rlpos].length = max_cluster -
|
||||
deltaxcn;
|
||||
rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
|
||||
rlpos++;
|
||||
} else if (unlikely(deltaxcn > max_cluster)) {
|
||||
ntfs_error(vol->sb, "Corrupt attribute. "
|
||||
"deltaxcn = 0x%llx, "
|
||||
"max_cluster = 0x%llx",
|
||||
(unsigned long long)deltaxcn,
|
||||
(unsigned long long)
|
||||
max_cluster);
|
||||
goto mpa_err;
|
||||
}
|
||||
}
|
||||
rl[rlpos].lcn = LCN_ENOENT;
|
||||
} else /* Not the base extent. There may be more extents to follow. */
|
||||
@ -918,17 +933,18 @@ err_out:
|
||||
*
|
||||
* 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.
|
||||
* -2 = LCN_RL_NOT_MAPPED This is part of the runlist which has not been
|
||||
* inserted into the runlist yet.
|
||||
* -3 = LCN_ENOENT There is no such vcn in the attribute.
|
||||
* LCN_HOLE Hole / not allocated on disk.
|
||||
* LCN_RL_NOT_MAPPED This is part of the runlist which has not been
|
||||
* inserted into the runlist yet.
|
||||
* LCN_ENOENT There is no such vcn in the attribute.
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
@ -964,6 +980,39 @@ LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn)
|
||||
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
|
||||
* @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)
|
||||
* ntfs_get_size_for_mapping_pairs - get bytes needed for mapping pairs array
|
||||
* @vol: ntfs volume (needed for the ntfs version)
|
||||
* @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
|
||||
* 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
|
||||
* building the mapping pairs array.
|
||||
*
|
||||
@ -1018,34 +1074,50 @@ static inline int ntfs_get_nr_significant_bytes(const s64 n)
|
||||
* remains locked throughout, and is left locked upon return.
|
||||
*/
|
||||
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;
|
||||
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) {
|
||||
BUG_ON(start_vcn);
|
||||
BUG_ON(first_vcn);
|
||||
BUG_ON(last_vcn > 0);
|
||||
return 1;
|
||||
}
|
||||
/* Skip to runlist element containing @start_vcn. */
|
||||
while (rl->length && start_vcn >= rl[1].vcn)
|
||||
/* Skip to runlist element containing @first_vcn. */
|
||||
while (rl->length && first_vcn >= rl[1].vcn)
|
||||
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;
|
||||
prev_lcn = 0;
|
||||
/* Always need the termining zero byte. */
|
||||
rls = 1;
|
||||
/* Do the first partial run if present. */
|
||||
if (start_vcn > rl->vcn) {
|
||||
s64 delta;
|
||||
if (first_vcn > rl->vcn) {
|
||||
s64 delta, length = rl->length;
|
||||
|
||||
/* 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;
|
||||
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. */
|
||||
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
|
||||
* 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,
|
||||
* 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).
|
||||
*/
|
||||
if (rl->lcn >= 0 || vol->major_ver < 3) {
|
||||
if (likely(rl->lcn >= 0 || vol->major_ver < 3)) {
|
||||
prev_lcn = rl->lcn;
|
||||
if (rl->lcn >= 0)
|
||||
if (likely(rl->lcn >= 0))
|
||||
prev_lcn += delta;
|
||||
/* Change in lcn. */
|
||||
rls += ntfs_get_nr_significant_bytes(prev_lcn);
|
||||
@ -1064,11 +1136,23 @@ int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
|
||||
rl++;
|
||||
}
|
||||
/* Do the full runs. */
|
||||
for (; rl->length; rl++) {
|
||||
if (rl->length < 0 || rl->lcn < LCN_HOLE)
|
||||
for (; rl->length && !the_end; rl++) {
|
||||
s64 length = rl->length;
|
||||
|
||||
if (unlikely(length < 0 || rl->lcn < LCN_HOLE))
|
||||
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. */
|
||||
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
|
||||
* 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,
|
||||
* 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).
|
||||
*/
|
||||
if (rl->lcn >= 0 || vol->major_ver < 3) {
|
||||
if (likely(rl->lcn >= 0 || vol->major_ver < 3)) {
|
||||
/* Change in lcn. */
|
||||
rls += ntfs_get_nr_significant_bytes(rl->lcn -
|
||||
prev_lcn);
|
||||
@ -1119,7 +1203,7 @@ static inline int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max,
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
if (dst > dst_max)
|
||||
if (unlikely(dst > dst_max))
|
||||
goto err_out;
|
||||
*dst++ = l & 0xffll;
|
||||
l >>= 8;
|
||||
@ -1128,12 +1212,12 @@ static inline int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max,
|
||||
j = (n >> 8 * (i - 1)) & 0xff;
|
||||
/* If the sign bit is wrong, we need an extra byte. */
|
||||
if (n < 0 && j >= 0) {
|
||||
if (dst > dst_max)
|
||||
if (unlikely(dst > dst_max))
|
||||
goto err_out;
|
||||
i++;
|
||||
*dst = (s8)-1;
|
||||
} else if (n > 0 && j < 0) {
|
||||
if (dst > dst_max)
|
||||
if (unlikely(dst > dst_max))
|
||||
goto err_out;
|
||||
i++;
|
||||
*dst = (s8)0;
|
||||
@ -1149,13 +1233,18 @@ err_out:
|
||||
* @dst: destination buffer to which to write the mapping pairs array
|
||||
* @dst_len: size of destination buffer @dst in bytes
|
||||
* @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
|
||||
*
|
||||
* 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
|
||||
* bytes and it should be at least equal to the value obtained by calling
|
||||
* ntfs_get_size_for_mapping_pairs().
|
||||
* @first_vcn and finishing with vcn @last_vcn and save the array in @dst.
|
||||
* @dst_len is the size of @dst in bytes and it should be at least equal to the
|
||||
* 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.
|
||||
*
|
||||
@ -1164,7 +1253,7 @@ err_out:
|
||||
* 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
|
||||
* 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
|
||||
* defined:
|
||||
@ -1178,27 +1267,32 @@ err_out:
|
||||
*/
|
||||
int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst,
|
||||
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;
|
||||
s8 *dst_max, *dst_next;
|
||||
int err = -ENOSPC;
|
||||
BOOL the_end = FALSE;
|
||||
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);
|
||||
if (!rl) {
|
||||
BUG_ON(start_vcn);
|
||||
BUG_ON(first_vcn);
|
||||
BUG_ON(last_vcn > 0);
|
||||
if (stop_vcn)
|
||||
*stop_vcn = 0;
|
||||
/* Terminator byte. */
|
||||
*dst = 0;
|
||||
return 0;
|
||||
}
|
||||
/* Skip to runlist element containing @start_vcn. */
|
||||
while (rl->length && start_vcn >= rl[1].vcn)
|
||||
/* Skip to runlist element containing @first_vcn. */
|
||||
while (rl->length && first_vcn >= rl[1].vcn)
|
||||
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;
|
||||
/*
|
||||
* @dst_max is used for bounds checking in
|
||||
@ -1207,17 +1301,27 @@ int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst,
|
||||
dst_max = dst + dst_len - 1;
|
||||
prev_lcn = 0;
|
||||
/* Do the first partial run if present. */
|
||||
if (start_vcn > rl->vcn) {
|
||||
s64 delta;
|
||||
if (first_vcn > rl->vcn) {
|
||||
s64 delta, length = rl->length;
|
||||
|
||||
/* 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;
|
||||
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. */
|
||||
len_len = ntfs_write_significant_bytes(dst + 1, dst_max,
|
||||
rl->length - delta);
|
||||
if (len_len < 0)
|
||||
length - delta);
|
||||
if (unlikely(len_len < 0))
|
||||
goto size_err;
|
||||
/*
|
||||
* 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,
|
||||
* case on NT4. - We assume that we just need to write the lcn
|
||||
* 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;
|
||||
if (rl->lcn >= 0)
|
||||
if (likely(rl->lcn >= 0))
|
||||
prev_lcn += delta;
|
||||
/* Write change in lcn. */
|
||||
lcn_len = ntfs_write_significant_bytes(dst + 1 +
|
||||
len_len, dst_max, prev_lcn);
|
||||
if (lcn_len < 0)
|
||||
if (unlikely(lcn_len < 0))
|
||||
goto size_err;
|
||||
} else
|
||||
lcn_len = 0;
|
||||
dst_next = dst + len_len + lcn_len + 1;
|
||||
if (dst_next > dst_max)
|
||||
if (unlikely(dst_next > dst_max))
|
||||
goto size_err;
|
||||
/* Update header byte. */
|
||||
*dst = lcn_len << 4 | len_len;
|
||||
@ -1250,13 +1354,25 @@ int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst,
|
||||
rl++;
|
||||
}
|
||||
/* Do the full runs. */
|
||||
for (; rl->length; rl++) {
|
||||
if (rl->length < 0 || rl->lcn < LCN_HOLE)
|
||||
for (; rl->length && !the_end; rl++) {
|
||||
s64 length = rl->length;
|
||||
|
||||
if (unlikely(length < 0 || rl->lcn < LCN_HOLE))
|
||||
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. */
|
||||
len_len = ntfs_write_significant_bytes(dst + 1, dst_max,
|
||||
rl->length);
|
||||
if (len_len < 0)
|
||||
length);
|
||||
if (unlikely(len_len < 0))
|
||||
goto size_err;
|
||||
/*
|
||||
* 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,
|
||||
* case on NT4. - We assume that we just need to write the lcn
|
||||
* 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. */
|
||||
lcn_len = ntfs_write_significant_bytes(dst + 1 +
|
||||
len_len, dst_max, rl->lcn - prev_lcn);
|
||||
if (lcn_len < 0)
|
||||
if (unlikely(lcn_len < 0))
|
||||
goto size_err;
|
||||
prev_lcn = rl->lcn;
|
||||
} else
|
||||
lcn_len = 0;
|
||||
dst_next = dst + len_len + lcn_len + 1;
|
||||
if (dst_next > dst_max)
|
||||
if (unlikely(dst_next > dst_max))
|
||||
goto size_err;
|
||||
/* Update header byte. */
|
||||
*dst = lcn_len << 4 | len_len;
|
||||
@ -1436,3 +1552,5 @@ int ntfs_rl_truncate_nolock(const ntfs_volume *vol, runlist *const runlist,
|
||||
ntfs_debug("Done.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* NTFS_RW */
|
||||
|
@ -2,7 +2,7 @@
|
||||
* runlist.h - Defines for runlist handling in NTFS Linux kernel driver.
|
||||
* Part of the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2001-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2001-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2002 Richard Russon
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
@ -66,6 +66,8 @@ typedef enum {
|
||||
LCN_HOLE = -1, /* Keep this as highest value or die! */
|
||||
LCN_RL_NOT_MAPPED = -2,
|
||||
LCN_ENOENT = -3,
|
||||
LCN_ENOMEM = -4,
|
||||
LCN_EIO = -5,
|
||||
} LCN_SPECIAL_VALUES;
|
||||
|
||||
extern runlist_element *ntfs_runlists_merge(runlist_element *drl,
|
||||
@ -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);
|
||||
|
||||
#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,
|
||||
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,
|
||||
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,
|
||||
runlist *const runlist, const s64 new_length);
|
||||
|
||||
#endif /* NTFS_RW */
|
||||
|
||||
#endif /* _LINUX_NTFS_RUNLIST_H */
|
||||
|
692
fs/ntfs/super.c
692
fs/ntfs/super.c
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@
|
||||
* the Linux-NTFS project. Adapted from the old NTFS driver,
|
||||
* Copyright (C) 1997 Martin von Löwis, Régis 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
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
@ -67,7 +67,7 @@ int ntfs_sysctl(int add)
|
||||
return -ENOMEM;
|
||||
#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
|
||||
* non-modular case, THIS_MODULE is NULL, so this is ok.
|
||||
*/
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
* 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)
|
||||
struct timespec ts;
|
||||
|
||||
/* 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
|
||||
* 1-nano-second intervals.
|
||||
|
@ -2,7 +2,7 @@
|
||||
* types.h - Defines for NTFS Linux kernel driver specific types.
|
||||
* 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
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
@ -53,6 +53,14 @@ typedef sle64 leLCN;
|
||||
typedef s64 LSN;
|
||||
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 {
|
||||
FALSE = 0,
|
||||
TRUE = 1
|
||||
|
@ -264,7 +264,7 @@ int ntfs_nlstoucs(const ntfs_volume *vol, const char *ins,
|
||||
|
||||
/* We don't trust outside sources. */
|
||||
if (ins) {
|
||||
ucs = (ntfschar*)kmem_cache_alloc(ntfs_name_cache, SLAB_NOFS);
|
||||
ucs = kmem_cache_alloc(ntfs_name_cache, SLAB_NOFS);
|
||||
if (ucs) {
|
||||
for (i = o = 0; i < ins_len; i += wc_len) {
|
||||
wc_len = nls->char2uni(ins + i, ins_len - i,
|
||||
|
84
fs/ntfs/usnjrnl.c
Normal file
84
fs/ntfs/usnjrnl.c
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 */
|
205
fs/ntfs/usnjrnl.h
Normal file
205
fs/ntfs/usnjrnl.h
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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 @@
|
||||
* volume.h - Defines for volume structures in NTFS Linux kernel driver. Part
|
||||
* of the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2001-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2001-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2002 Richard Russon
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
@ -54,7 +54,7 @@ typedef struct {
|
||||
mode_t dmask; /* The mask for directory
|
||||
permissions. */
|
||||
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. */
|
||||
u16 sector_size; /* in bytes */
|
||||
u8 sector_size_bits; /* log2(sector_size) */
|
||||
@ -125,6 +125,10 @@ typedef struct {
|
||||
/* $Quota stuff is NTFS3.0+ specific. Unused/NULL otherwise. */
|
||||
struct inode *quota_ino; /* The VFS inode of $Quota. */
|
||||
struct inode *quota_q_ino; /* Attribute inode for $Quota/$Q. */
|
||||
/* $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 */
|
||||
struct nls_table *nls_map;
|
||||
} ntfs_volume;
|
||||
@ -141,6 +145,8 @@ typedef enum {
|
||||
file names in WIN32 namespace. */
|
||||
NV_LogFileEmpty, /* 1: $LogFile journal is empty. */
|
||||
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;
|
||||
|
||||
/*
|
||||
@ -167,5 +173,7 @@ NVOL_FNS(ShowSystemFiles)
|
||||
NVOL_FNS(CaseSensitive)
|
||||
NVOL_FNS(LogFileEmpty)
|
||||
NVOL_FNS(QuotaOutOfDate)
|
||||
NVOL_FNS(UsnJrnlStamped)
|
||||
NVOL_FNS(SparseEnabled)
|
||||
|
||||
#endif /* _LINUX_NTFS_VOLUME_H */
|
||||
|
Loading…
Reference in New Issue
Block a user