Added a branch of linux-ntfs based on kernel 2.6.0test11, this can be used as a FOUNDATION for building a STABLE read/write ntfs driver. check out http://linux-ntfs.sf.net for details on this driver.

svn path=/trunk/; revision=6860
This commit is contained in:
Richard Campbell 2003-12-04 18:03:56 +00:00
parent f0d7ce034e
commit 8a1c4241be
29 changed files with 15739 additions and 0 deletions

View file

@ -0,0 +1,802 @@
ToDo:
- Find and fix bugs.
- Enable NFS exporting of NTFS.
- Implement aops->set_page_dirty() in order to take control of buffer
dirtying. Not having it means if page_has_buffers(), all buffers
will be dirtied with the page. And if not they won't be. That is
fine for the moment but will break once we enable metadata updates.
- Implement sops->dirty_inode() to implement {a,m,c} time updates and
such things.
- Implement sops->write_inode().
- 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
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.
2.1.5 - Fix minor bug in attribute list attribute handling.
- Fix bug in attribute list handling. Actually it is not as much a bug
as too much protection in that we were not allowing attribute lists
which waste space on disk while Windows XP clearly allows it and in
fact creates such attribute lists so our driver was failing.
- Update NTFS documentation ready for 2.6 kernel release.
2.1.4 - Reduce compiler requirements.
- Remove all uses of unnamed structs and unions in the driver to make
old and newer gcc versions happy. Makes it a bit uglier IMO but at
least people will stop hassling me about it.
2.1.3 - Important bug fixes in corner cases.
- super.c::parse_ntfs_boot_sector(): Correct the check for 64-bit
clusters. (Philipp Thomas)
- attrib.c::load_attribute_list(): Fix bug when initialized_size is a
multiple of the block_size but not the cluster size. (Szabolcs
Szakacsits <szaka@sienet.hu>)
2.1.2 - Important bug fixes aleviating the hangs in statfs.
- Fix buggy free cluster and free inode determination logic.
2.1.1 - Minor updates.
- Add handling for initialized_size != data_size in compressed files.
- Reduce function local stack usage from 0x3d4 bytes to just noise in
fs/ntfs/upcase.c. (Randy Dunlap <rddunlap@osdl.ord>)
- Remove compiler warnings for newer gcc.
2.1.0 - First steps towards write support: implement file overwrite.
- Add configuration option for developmental write support with an
appropriately scary configuration help text.
- Initial implementation of fs/ntfs/aops.c::ntfs_writepage() and its
helper fs/ntfs/aops.c::ntfs_write_block(). This enables mmap(2) based
overwriting of existing files on ntfs. Note: Resident files are
only written into memory, and not written out to disk at present, so
avoid writing to files smaller than about 1kiB.
- Initial implementation of fs/ntfs/aops.c::ntfs_prepare_write(), its
helper fs/ntfs/aops.c::ntfs_prepare_nonresident_write() and their
counterparts, fs/ntfs/aops.c::ntfs_commit_write(), and
fs/ntfs/aops.c::ntfs_commit_nonresident_write(), respectively. Also,
add generic_file_write() to the ntfs file operations (fs/ntfs/file.c).
This enables write(2) based overwriting of existing files on ntfs.
Note: As with mmap(2) based overwriting, resident files are only
written into memory, and not written out to disk at present, so avoid
writing to files smaller than about 1kiB.
- Implement ->truncate (fs/ntfs/inode.c::ntfs_truncate()) and
->setattr() (fs/ntfs/inode.c::ntfs_setattr()) inode operations for
files with the purpose of intercepting and aborting all i_size
changes which we do not support yet. ntfs_truncate() actually only
emits a warning message but AFAICS our interception of i_size changes
elsewhere means ntfs_truncate() never gets called for i_size changes.
It is only called from generic_file_write() when we fail in
ntfs_prepare_{,nonresident_}write() in order to discard any
instantiated buffers beyond i_size. Thus i_size is not actually
changed so our warning message is enough. Unfortunately it is not
possible to easily determine if i_size is being changed or not hence
we just emit an appropriately worded error message.
2.0.25 - Small bug fixes and cleanups.
- Unlock the page in an out of memory error code path in
fs/ntfs/aops.c::ntfs_read_block().
- If fs/ntfs/aops.c::ntfs_read_page() is called on an uptodate page,
just unlock the page and return. (This can happen due to ->writepage
clearing PageUptodate() during write out of MstProtected()
attributes.
- Remove leaked write code again.
2.0.24 - Cleanups.
- Treat BUG_ON() as ASSERT() not VERIFY(), i.e. do not use side effects
inside BUG_ON(). (Adam J. Richter)
- Split logical OR expressions inside BUG_ON() into individual BUG_ON()
calls for improved debugging. (Adam J. Richter)
- Add errors flag to the ntfs volume state, accessed via
NVol{,Set,Clear}Errors(vol).
- Do not allow read-write remounts of read-only volumes with errors.
- Clarify comment for ntfs file operation sendfile which was added by
Christoph Hellwig a while ago (just using generic_file_sendfile())
to say that ntfs ->sendfile is only used for the case where the
source data is on the ntfs partition and the destination is
somewhere else, i.e. nothing we need to concern ourselves with.
- Add generic_file_write() as our ntfs file write operation.
2.0.23 - Major bug fixes (races, deadlocks, non-i386 architectures).
- Massive internal locking changes to mft record locking. Fixes lock
recursion and replaces the mrec_lock read/write semaphore with a
mutex. Also removes the now superfluous mft_count. This fixes several
race conditions and deadlocks, especially in the future write code.
- Fix ntfs over loopback for compressed files by adding an
optimization barrier. (gcc was screwing up otherwise ?)
- Miscellaneous cleanups all over the code and a fix or two in error
handling code paths.
Thanks go to Christoph Hellwig for pointing out the following two:
- Remove now unused function fs/ntfs/malloc.h::vmalloc_nofs().
- Fix ntfs_free() for ia64 and parisc by checking for VMALLOC_END, too.
2.0.22 - Cleanups, mainly to ntfs_readdir(), and use C99 initializers.
- Change fs/ntfs/dir.c::ntfs_reddir() to only read/write ->f_pos once
at entry/exit respectively.
- Use C99 initializers for structures.
- Remove unused variable blocks from fs/ntfs/aops.c::ntfs_read_block().
2.0.21 - Check for, and refuse to work with too large files/directories/volumes.
- Limit volume size at mount time to 2TiB on architectures where
unsigned long is 32-bits (fs/ntfs/super.c::parse_ntfs_boot_sector()).
This is the most we can do without overflowing the 32-bit limit of
the block device size imposed on us by sb_bread() and sb_getblk()
for the time being.
- Limit file/directory size at open() time to 16TiB on architectures
where unsigned long is 32-bits (fs/ntfs/file.c::ntfs_file_open() and
fs/ntfs/dir.c::ntfs_dir_open()). This is the most we can do without
overflowing the page cache page index.
2.0.20 - Support non-resident directory index bitmaps, fix page leak in readdir.
- Move the directory index bitmap to use an attribute inode instead of
having special fields for it inside the ntfs inode structure. This
means that the index bitmaps now use the page cache for i/o, too,
and also as a side effect we get support for non-resident index
bitmaps for free.
- Simplify/cleanup error handling in fs/ntfs/dir.c::ntfs_readdir() and
fix a page leak that manifested itself in some cases.
- Add fs/ntfs/inode.c::ntfs_put_inode(), which we need to release the
index bitmap inode on the final iput().
2.0.19 - Fix race condition, improvements, and optimizations in i/o interface.
- Apply block optimization added to fs/ntfs/aops.c::ntfs_read_block()
to fs/ntfs/compress.c::ntfs_file_read_compressed_block() as well.
- Drop the "file" from ntfs_file_read_compressed_block().
- Rename fs/ntfs/aops.c::ntfs_enb_buffer_read_async() to
ntfs_end_buffer_async_read() (more like the fs/buffer.c counterpart).
- Update ntfs_end_buffer_async_read() with the improved logic from
its updated counterpart fs/buffer.c::end_buffer_async_read(). Apply
further logic improvements to better determine when we set PageError.
- Update submission of buffers in fs/ntfs/aops.c::ntfs_read_block() to
check for the buffers being uptodate first in line with the updated
fs/buffer.c::block_read_full_page(). This plugs a small race
condition.
2.0.18 - Fix race condition in reading of compressed files.
- There was a narrow window between checking a buffer head for being
uptodate and locking it in ntfs_file_read_compressed_block(). We now
lock the buffer and then check whether it is uptodate or not.
2.0.17 - Cleanups and optimizations - shrinking the ToDo list.
- Modify fs/ntfs/inode.c::ntfs_read_locked_inode() to return an error
code and update callers, i.e. ntfs_iget(), to pass that error code
up instead of just using -EIO.
- Modifications to super.c to ensure that both mount and remount
cannot set any write related options when the driver is compiled
read-only.
- Optimize block resolution in fs/ntfs/aops.c::ntfs_read_block() to
cache the current run list element. This should improve performance
when reading very large and/or very fragmented data.
2.0.16 - Convert access to $MFT/$BITMAP to attribute inode API.
- Fix a stupid bug introduced in 2.0.15 where we were unmapping the
wrong inode in fs/ntfs/inode.c::ntfs_attr_iget().
- Fix debugging check in fs/ntfs/aops.c::ntfs_read_block().
- Convert $MFT/$BITMAP access to attribute inode API and remove all
remnants of the ugly mftbmp address space and operations hack. This
means we finally have only one readpage function as well as only one
async io completion handler. Yey! The mft bitmap is now just an
attribute inode and is accessed from vol->mftbmp_ino just as if it
were a normal file. Fake inodes rule. (-:
2.0.15 - Fake inodes based attribute i/o via the pagecache, fixes and cleanups.
- Fix silly bug in fs/ntfs/super.c::parse_options() which was causing
remounts to fail when the partition had an entry in /etc/fstab and
the entry specified the nls= option.
- Apply same macro magic used in fs/ntfs/inode.h to fs/ntfs/volume.h to
expand all the helper functions NVolFoo(), NVolSetFoo(), and
NVolClearFoo().
- Move copyright statement from driver initialisation message to
module description (fs/super.c). This makes the initialisation
message fit on one line and fits in better with rest of kernel.
- Update fs/ntfs/attrib.c::map_run_list() to work on both real and
attribute inodes, and both for files and directories.
- Implement fake attribute inodes allowing all attribute i/o to go via
the page cache and to use all the normal vfs/mm functionality:
- Add ntfs_attr_iget() and its helper ntfs_read_locked_attr_inode()
to fs/ntfs/inode.c.
- Add needed cleanup code to ntfs_clear_big_inode().
- Merge address space operations for files and directories (aops.c),
now just have ntfs_aops:
- Rename:
end_buffer_read_attr_async() -> ntfs_end_buffer_read_async(),
ntfs_attr_read_block() -> ntfs_read_block(),
ntfs_file_read_page() -> ntfs_readpage().
- Rewrite fs/ntfs/aops.c::ntfs_readpage() to work on both real and
attribute inodes, and both for files and directories.
- Remove obsolete fs/ntfs/aops.c::ntfs_mst_readpage().
2.0.14 - Run list merging code cleanup, minor locking changes, typo fixes.
- Change fs/ntfs/super.c::ntfs_statfs() to not rely on BKL by moving
the locking out of super.c::get_nr_free_mft_records() and taking and
dropping the mftbmp_lock rw_semaphore in ntfs_statfs() itself.
- Bring attribute run list merging code (fs/ntfs/attrib.c) in sync with
current userspace ntfs library code. This means that if a merge
fails the original run lists are always left unmodified instead of
being silently corrupted.
- Misc typo fixes.
2.0.13 - Use iget5_locked() in preparation for fake inodes and small cleanups.
- Remove nr_mft_bits and the now superfluous union with nr_mft_records
from ntfs_volume structure.
- Remove nr_lcn_bits and the now superfluous union with nr_clusters
from ntfs_volume structure.
- Use iget5_locked() and friends instead of conventional iget(). Wrap
the call in fs/ntfs/inode.c::ntfs_iget() and update callers of iget()
to use ntfs_iget(). Leave only one iget() call at mount time so we
don't need an ntfs_iget_mount().
- Change fs/ntfs/inode.c::ntfs_new_extent_inode() to take mft_no as an
additional argument.
2.0.12 - Initial cleanup of address space operations following 2.0.11 changes.
- Merge fs/ntfs/aops.c::end_buffer_read_mst_async() and
fs/ntfs/aops.c::end_buffer_read_file_async() into one function
fs/ntfs/aops.c::end_buffer_read_attr_async() using NInoMstProtected()
to determine whether to apply mst fixups or not.
- Above change allows merging fs/ntfs/aops.c::ntfs_file_read_block()
and fs/ntfs/aops.c::ntfs_mst_readpage() into one function
fs/ntfs/aops.c::ntfs_attr_read_block(). Also, create a tiny wrapper
fs/ntfs/aops.c::ntfs_mst_readpage() to transform the parameters from
the VFS readpage function prototype to the ntfs_attr_read_block()
function prototype.
2.0.11 - Initial preparations for fake inode based attribute i/o.
- Move definition of ntfs_inode_state_bits to fs/ntfs/inode.h and
do some macro magic (adapted from include/linux/buffer_head.h) to
expand all the helper functions NInoFoo(), NInoSetFoo(), and
NInoClearFoo().
- Add new flag to ntfs_inode_state_bits: NI_Sparse.
- Add new fields to ntfs_inode structure to allow use of fake inodes
for attribute i/o: type, name, name_len. Also add new state bits:
NI_Attr, which, if set, indicates the inode is a fake inode, and
NI_MstProtected, which, if set, indicates the attribute uses multi
sector transfer protection, i.e. fixups need to be applied after
reads and before/after writes.
- Rename fs/ntfs/inode.c::ntfs_{new,clear,destroy}_inode() to
ntfs_{new,clear,destroy}_extent_inode() and update callers.
- Use ntfs_clear_extent_inode() in fs/ntfs/inode.c::__ntfs_clear_inode()
instead of ntfs_destroy_extent_inode().
- Cleanup memory deallocations in {__,}ntfs_clear_{,big_}inode().
- Make all operations on ntfs inode state bits use the NIno* functions.
- Set up the new ntfs inode fields and state bits in
fs/ntfs/inode.c::ntfs_read_inode() and add appropriate cleanup of
allocated memory to __ntfs_clear_inode().
- Cleanup ntfs_inode structure a bit for better ordering of elements
w.r.t. their size to allow better packing of the structure in memory.
2.0.10 - There can only be 2^32 - 1 inodes on an NTFS volume.
- Add check at mount time to verify that the number of inodes on the
volume does not exceed 2^32 - 1, which is the maximum allowed for
NTFS according to Microsoft.
- Change mft_no member of ntfs_inode structure to be unsigned long.
Update all users. This makes ntfs_inode->mft_no just a copy of struct
inode->i_ino. But we can't just always use struct inode->i_ino and
remove mft_no because extent inodes do not have an attached struct
inode.
2.0.9 - Decompression engine now uses a single buffer and other cleanups.
- Change decompression engine to use a single buffer protected by a
spin lock instead of per-CPU buffers. (Rusty Russell)
- Do not update cb_pos when handling a partial final page during
decompression of a sparse compression block, as the value is later
reset without being read/used. (Rusty Russell)
- Switch to using the new KM_BIO_SRC_IRQ for atomic kmap()s. (Andrew
Morton)
- Change buffer size in ntfs_readdir()/ntfs_filldir() to use
NLS_MAX_CHARSET_SIZE which makes the buffers almost 1kiB each but
it also makes everything safer so it is a good thing.
- Miscellaneous minor cleanups to comments.
2.0.8 - Major updates for handling of case sensitivity and dcache aliasing.
Big thanks go to Al Viro and other inhabitants of #kernel for investing
their time to discuss the case sensitivity and dcache aliasing issues.
- Remove unused source file fs/ntfs/attraops.c.
- Remove show_inodes mount option(s), thus dropping support for
displaying of short file names.
- Remove deprecated mount option posix.
- Restore show_sys_files mount option.
- Add new mount option case_sensitive, to determine if the driver
treats file names as case sensitive or not. If case sensitive, create
file names in the POSIX namespace. Otherwise create file names in the
LONG/WIN32 namespace. Note, files remain accessible via their short
file name, if it exists.
- Remove really dumb logic bug in boot sector recovery code.
- Fix dcache aliasing issues wrt short/long file names via changes
to fs/ntfs/dir.c::ntfs_lookup_inode_by_name() and
fs/ntfs/namei.c::ntfs_lookup():
- Add additional argument to ntfs_lookup_inode_by_name() in which we
return information about the matching file name if the case is not
matching or the match is a short file name. See comments above the
function definition for details.
- Change ntfs_lookup() to only create dcache entries for the correctly
cased file name and only for the WIN32 namespace counterpart of DOS
namespace file names. This ensures we have only one dentry per
directory and also removes all dcache aliasing issues between short
and long file names once we add write support. See comments above
function for details.
- Fix potential 1 byte overflow in fs/ntfs/unistr.c::ntfs_ucstonls().
2.0.7 - Minor cleanups and updates for changes in core kernel code.
- Remove much of the NULL struct element initializers.
- Various updates to make compatible with recent kernels.
- Remove defines of MAX_BUF_PER_PAGE and include linux/buffer_head.h
in fs/ntfs/ntfs.h instead.
- Remove no longer needed KERNEL_VERSION checks. We are now in the
kernel proper so they are no longer needed.
2.0.6 - Major bugfix to make compatible with other kernel changes.
- Initialize the mftbmp address space properly now that there are more
fields in the struct address_space. This was leading to hangs and
oopses on umount since 2.5.12 because of changes to other parts of
the kernel. We probably want a kernel generic init_address_space()
function...
- Drop BKL from ntfs_readdir() after consultation with Al Viro. The
only caller of ->readdir() is vfs_readdir() which holds i_sem during
the call, and i_sem is sufficient protection against changes in the
directory inode (including ->i_size).
- Use generic_file_llseek() for directories (as opposed to
default_llseek()) as this downs i_sem instead of the BKL which is
what we now need for exclusion against ->f_pos changes considering we
no longer take the BKL in ntfs_readdir().
2.0.5 - Major bugfix. Buffer overflow in extent inode handling.
- No need to set old blocksize in super.c::ntfs_fill_super() as the
VFS does so via invocation of deactivate_super() calling
fs->fill_super() calling block_kill_super() which does it.
- BKL moved from VFS into dir.c::ntfs_readdir(). (Linus Torvalds)
-> Do we really need it? I don't think so as we have exclusion on
the directory ntfs_inode rw_semaphore mrec_lock. We mmight have to
move the ->f_pos accesses under the mrec_lock though. Check this...
- Fix really, really, really stupid buffer overflow in extent inode
handling in mft.c::map_extent_mft_record().
2.0.4 - Cleanups and updates for kernel 2.5.11.
- Add documentation on how to use the MD driver to be able to use NTFS
stripe and volume sets in Linux and generally cleanup documentation
a bit.
Remove all uses of kdev_t in favour of struct block_device *:
- Change compress.c::ntfs_file_read_compressed_block() to use
sb_getblk() instead of getblk().
- Change super.c::ntfs_fill_super() to use bdev_hardsect_size() instead
of get_hardsect_size().
- No need to get old blocksize in super.c::ntfs_fill_super() as
fs/super.c::get_sb_bdev() already does this.
- Set bh->b_bdev instead of bh->b_dev throughout aops.c.
2.0.3 - Small bug fixes, cleanups, and performance improvements.
- Remove some dead code from mft.c.
- Optimize readpage and read_block functions throughout aops.c so that
only initialized blocks are read. Non-initialized ones have their
buffer head mapped, zeroed, and set up to date, without scheduling
any i/o. Thanks to Al Viro for advice on how to avoid the device i/o.
Thanks go to Andrew Morton for spotting the below:
- Fix buglet in allocate_compression_buffers() error code path.
- Call flush_dcache_page() after modifying page cache page contents in
ntfs_file_readpage().
- Check for existence of page buffers throughout aops.c before calling
create_empty_buffers(). This happens when an I/O error occurs and the
read is retried. (It also happens once writing is implemented so that
needed doing anyway but I had left it for later...)
- Don't BUG_ON() uptodate and/or mapped buffers throughout aops.c in
readpage and read_block functions. Reasoning same as above (i.e. I/O
error retries and future write code paths.)
2.0.2 - Minor updates and cleanups.
- Cleanup: rename mst.c::__post_read_mst_fixup to post_write_mst_fixup
and cleanup the code a bit, removing the unused size parameter.
- Change default fmask to 0177 and update documentation.
- Change attrib.c::get_attr_search_ctx() to return the search context
directly instead of taking the address of a pointer. A return value
of NULL means the allocation failed. Updated all callers
appropriately.
- Update to 2.5.9 kernel (preserving backwards compatibility) by
replacing all occurences of page->buffers with page_buffers(page).
- Fix minor bugs in run list merging, also minor cleanup.
- Updates to bootsector layout and mft mirror contents descriptions.
- Small bug fix in error detection in unistr.c and some cleanups.
- Grow name buffer allocations in unistr.c in aligned mutlipled of 64
bytes.
2.0.1 - Minor updates.
- Make default umask correspond to documentation.
- Improve documentation.
- Set default mode to include execute bit. The {u,f,d}mask can be used
to take it away if desired. This allows binaries to be executed from
a mounted ntfs partition.
2.0.0 - New version number. Remove TNG from the name. Now in the kernel.
- Add kill_super, just keeping up with the vfs changes in the kernel.
- Repeat some changes from tng-0.0.8 that somehow got lost on the way
from the CVS import into BitKeeper.
- Begin to implement proper handling of allocated_size vs
initialized_size vs data_size (i.e. i_size). Done are
mft.c::ntfs_mft_readpage(), aops.c::end_buffer_read_index_async(),
and attrib.c::load_attribute_list().
- Lock the run list in attrib.c::load_attribute_list() while using it.
- Fix memory leak in ntfs_file_read_compressed_block() and generally
clean up compress.c a little, removing some uncommented/unused debug
code.
- Tidy up dir.c a little bit.
- Don't bother getting the run list in inode.c::ntfs_read_inode().
- Merge mft.c::ntfs_mft_readpage() and aops.c::ntfs_index_readpage()
creating aops.c::ntfs_mst_readpage(), improving the handling of
holes and overflow in the process and implementing the correct
equivalent of ntfs_file_get_block() in ntfs_mst_readpage() itself.
I am aiming for correctness at the moment. Modularisation can come
later.
- Rename aops.c::end_buffer_read_index_async() to
end_buffer_read_mst_async() and optimize the overflow checking and
handling.
- Use the host of the mftbmp address space mapping to hold the ntfs
volume. This is needed so the async i/o completion handler can
retrieve a pointer to the volume. Hopefully this will not cause
problems elsewhere in the kernel... Otherwise will need to use a
fake inode.
- Complete implementation of proper handling of allocated_size vs
initialized_size vs data_size (i.e. i_size) in whole driver.
Basically aops.c is now completely rewritten.
- Change NTFS driver name to just NTFS and set version number to 2.0.0
to make a clear distinction from the old driver which is still on
version 1.1.22.
tng-0.0.8 - 08/03/2002 - Now using BitKeeper, http://linux-ntfs.bkbits.net/
- Replace bdevname(sb->s_dev) with sb->s_id.
- Remove now superfluous new-line characters in all callers of
ntfs_debug().
- Apply kludge in ntfs_read_inode(), setting i_nlink to 1 for
directories. Without this the "find" utility gets very upset which is
fair enough as Linux/Unix do not support directory hard links.
- Further run list 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
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.
- Cleanup upcase loading code to use ntfs_(un)map_page().
- Disable/reenable preemtion in critical sections of compession engine.
- Replace device size determination in ntfs_fill_super() with
sb->s_bdev->bd_inode->i_size (in bytes) and remove now superfluous
function super.c::get_nr_blocks().
- Implement a mount time option (show_inodes) allowing choice of which
types of inode names readdir() returns and modify ntfs_filldir()
accordingly. There are several parameters to show_inodes:
system: system files
win32: long file names (including POSIX file names) [DEFAULT]
long: same as win32
dos: short file names only (excluding POSIX file names)
short: same as dos
posix: same as both win32 and dos
all: all file names
Note that the options are additive, i.e. specifying:
-o show_inodes=system,show_inodes=win32,show_inodes=dos
is the same as specifying:
-o show_inodes=all
Note that the "posix" and "all" options will show all directory
names, BUT the link count on each directory inode entry is set to 1,
due to Linux not supporting directory hard links. This may well
confuse some userspace applications, since the directory names will
have the same inode numbers. Thus it is NOT advisable to use the
"posix" or "all" options. We provide them only for completeness sake.
- Add copies of allocated_size, initialized_size, and compressed_size to
the ntfs inode structure and set them up in
inode.c::ntfs_read_inode(). These reflect the unnamed data attribute
for files and the index allocation attribute for directories.
- Add copies of allocated_size and initialized_size to ntfs inode for
$BITMAP attribute of large directories and set them up in
inode.c::ntfs_read_inode().
- Add copies of allocated_size and initialized_size to ntfs volume for
$BITMAP attribute of $MFT and set them up in
super.c::load_system_files().
- Parse deprecated ntfs driver options (iocharset, show_sys_files,
posix, and utf8) and tell user what the new options to use are. Note
we still do support them but they will be removed with kernel 2.7.x.
- Change all occurences of integer long long printf formatting to hex
as printk() will not support long long integer format if/when the
div64 patch goes into the kernel.
- Make slab caches have stable names and change the names to what they
were intended to be. These changes are required/made possible by the
new slab cache name handling which removes the length limitation by
requiring the caller of kmem_cache_create() to supply a stable name
which is then referenced but not copied.
- Rename run_list structure to run_list_element and create a new
run_list structure containing a pointer to a run_list_element
structure and a read/write semaphore. Adapt all users of run lists
to new scheme and take and release the lock as needed. This fixes a
nasty race as the run_list changes even when inodes are locked for
reading and even when the inode isn't locked at all, so we really
needed the serialization. We use a semaphore rather than a spinlock
as memory allocations can sleep and doing everything GFP_ATOMIC
would be silly.
- Cleanup read_inode() removing all code checking for lowest_vcn != 0.
This can never happen due to the nature of lookup_attr() and how we
support attribute lists. If it did happen it would imply the inode
being corrupt.
- Check for lowest_vcn != 0 in ntfs_read_inode() and mark the inode as
bad if found.
- Update to 2.5.6-pre2 changes in struct address_space.
- Use parent_ino() when accessing d_parent inode number in dir.c.
- Import Sourceforge CVS repository into BitKeeper repository:
http://linux-ntfs.bkbits.net/ntfs-tng-2.5
- Update fs/Makefile, fs/Config.help, fs/Config.in, and
Documentation/filesystems/ntfs.txt for NTFS TNG.
- Create kernel configuration option controlling whether debugging
is enabled or not.
- Add the required export of end_buffer_io_sync() from the patches
directory to the kernel code.
- Update inode.c::ntfs_show_options() with show_inodes mount option.
- Update errors mount option.
tng-0.0.7 - 13/02/2002 - The driver is now feature complete for read-only!
- Cleanup mft.c and it's debug/error output in particular. Fix a minor
bug in mapping of extent inodes. Update all the comments to fit all
the recent code changes.
- Modify vcn_to_lcn() to cope with entirely unmapped run lists.
- Cleanups in compress.c, mostly comments and folding help.
- Implement attrib.c::map_run_list() as a generic helper.
- Make compress.c::ntfs_file_read_compressed_block() use map_run_list()
thus making code shorter and enabling attribute list support.
- Cleanup incorrect use of [su]64 with %L printf format specifier in
all source files. Type casts to [unsigned] long long added to correct
the mismatches (important for architectures which have long long not
being 64 bits).
- Merge async io completion handlers for directory indexes and $MFT
data into one by setting the index_block_size{_bits} of the ntfs
inode for $MFT to the mft_record_size{_bits} of the ntfs_volume.
- Cleanup aops.c, update comments.
- Make ntfs_file_get_block() use map_run_list() so all files now
support attribute lists.
- Make ntfs_dir_readpage() almost verbatim copy of
block_read_full_page() by using ntfs_file_get_block() with only real
difference being the use of our own async io completion handler
rather than the default one, thus reducing the amount of code and
automatically enabling attribute list support for directory indices.
- Fix bug in load_attribute_list() - forgot to call brelse in error
code path.
- Change parameters to find_attr() and lookup_attr(). We no longer
pass in the upcase table and its length. These can be gotten from
ctx->ntfs_ino->vol->upcase{_len}. Update all callers.
- Cleanups in attrib.c.
- Implement merging of run lists, attrib.c::merge_run_lists() and its
helpers. (Richard Russon)
- Attribute lists part 2, attribute extents and multi part run lists:
enable proper support for LCN_RL_NOT_MAPPED and automatic mapping of
further run list parts via attrib.c::map_run_list().
- Tiny endianness bug fix in decompress_mapping_pairs().
tng-0.0.6 - Encrypted directories, bug fixes, cleanups, debugging enhancements.
- Enable encrypted directories. (Their index root is marked encrypted
to indicate that new files in that directory should be created
encrypted.)
- Fix bug in NInoBmpNonResident() macro. (Cut and paste error.)
- Enable $Extend system directory. Most (if not all) extended system
files do not have unnamed data attributes so ntfs_read_inode() had to
special case them but that is ok, as the special casing recovery
happens inside an error code path so there is zero slow down in the
normal fast path. The special casing is done by introducing a new
function inode.c::ntfs_is_extended_system_file() which checks if any
of the hard links in the inode point to $Extend as being their parent
directory and if they do we assume this is an extended system file.
- Create a sysctl/proc interface to allow {dis,en}abling of debug output
when compiled with -DDEBUG. Default is debug messages to be disabled.
To enable them, one writes a non-zero value to /proc/sys/fs/ntfs-debug
(if /proc is enabled) or uses sysctl(2) to effect the same (if sysctl
interface is enabled). Inspired by old ntfs driver.
- Add debug_msgs insmod/kernel boot parameter to set whether debug
messages are {dis,en}abled. This is useful to enable debug messages
during ntfs initialization and is the only way to activate debugging
when the sysctl interface is not enabled.
- Cleanup debug output in various places.
- Remove all dollar signs ($) from the source (except comments) to
enable compilation on architectures whose gcc compiler does not
support dollar signs in the names of variables/constants. Attribute
types now start with AT_ instead of $ and $I30 is now just I30.
- Cleanup ntfs_lookup() and add consistency check of sequence numbers.
- Load complete run list for $MFT/$BITMAP during mount and cleanup
access functions. This means we now cope with $MFT/$BITMAP being
spread accross several mft records.
- Disable modification of mft_zone_multiplier on remount. We can always
reenable this later on if we really want to, but we will need to make
sure we readjust the mft_zone size / layout accordingly.
tng-0.0.5 - Modernize for 2.5.x and further in line-ing with Al Viro's comments.
- Use sb_set_blocksize() instead of set_blocksize() and verify the
return value.
- Use sb_bread() instead of bread() throughout.
- Add index_vcn_size{_bits} to ntfs_inode structure to store the size
of a directory index block vcn. Apply resulting simplifications in
dir.c everywhere.
- Fix a small bug somewhere (but forgot what it was).
- Change ntfs_{debug,error,warning} to enable gcc to do type checking
on the printf-format parameter list and fix bugs reported by gcc
as a result. (Richard Russon)
- Move inode allocation strategy to Al's new stuff but maintain the
divorce of ntfs_inode from struct inode. To achieve this we have two
separate slab caches, one for big ntfs inodes containing a struct
inode and pure ntfs inodes and at the same time fix some faulty
error code paths in ntfs_read_inode().
- Show mount options in proc (inode.c::ntfs_show_options()).
tng-0.0.4 - Big changes, getting in line with Al Viro's comments.
- Modified (un)map_mft_record functions to be common for read and write
case. To specify which is which, added extra parameter at front of
parameter list. Pass either READ or WRITE to this, each has the
obvious meaning.
- General cleanups to allow for easier folding in vi.
- attrib.c::decompress_mapping_pairs() now accepts the old run list
argument, and invokes attrib.c::merge_run_lists() to merge the old
and the new run lists.
- Removed attrib.c::find_first_attr().
- Implemented loading of attribute list and complete run list for $MFT.
This means we now cope with $MFT being spread across several mft
records.
- Adapt to 2.5.2-pre9 and the changed create_empty_buffers() syntax.
- Adapt major/minor/kdev_t/[bk]devname stuff to new 2.5.x kernels.
- Make ntfs_volume be allocated via kmalloc() instead of using a slab
cache. There are too little ntfs_volume structures at any one time
to justify a private slab cache.
- Fix bogus kmap() use in async io completion. Now use kmap_atomic().
Use KM_BIO_IRQ on advice from IRC/kernel...
- Use ntfs_map_page() in map_mft_record() and create ->readpage method
for reading $MFT (ntfs_mft_readpage). In the process create dedicated
address space operations (ntfs_mft_aops) for $MFT inode mapping. Also
removed the now superfluous exports from the kernel core patch.
- Fix a bug where kfree() was used insted of ntfs_free().
- Change map_mft_record() to take ntfs_inode as argument instead of
vfs inode. Dito for unmap_mft_record(). Adapt all callers.
- Add pointer to ntfs_volume to ntfs_inode.
- Add mft record number and sequence number to ntfs_inode. Stop using
i_ino and i_generation for in-driver purposes.
- Implement attrib.c::merge_run_lists(). (Richard Russon)
- Remove use of proper inodes by extent inodes. Move i_ino and
i_generation to ntfs_inode to do this. Apply simplifications that
result and remove iget_no_wait(), etc.
- Pass ntfs_inode everywhere in the driver (used to be struct inode).
- Add reference counting in ntfs_inode for the ntfs inode itself and
for the mapped mft record.
- Extend mft record mapping so we can (un)map extent mft records (new
functions (un)map_extent_mft_record), and so mappings are reference
counted and don't have to happen twice if already mapped - just ref
count increases.
- Add -o iocharset as alias to -o nls for backwards compatibility.
- The latest core patch is now tiny. In fact just a single additional
export is necessary over the base kernel.
tng-0.0.3 - Cleanups, enhancements, bug fixes.
- Work on attrib.c::decompress_mapping_pairs() to detect base extents
and setup the run list appropriately using knowledge provided by the
sizes in the base attribute record.
- Balance the get_/put_attr_search_ctx() calls so we don't leak memory
any more.
- Introduce ntfs_malloc_nofs() and ntfs_free() to allocate/free a single
page or use vmalloc depending on the amount of memory requested.
- Cleanup error output. The __FUNCTION__ "(): " is now added
automatically. Introduced a new header file debug.h to support this
and also moved ntfs_debug() function into it.
- Make reading of compressed files more intelligent and especially get
rid of the vmalloc_nofs() from readpage(). This now uses per CPU
buffers (allocated at first mount with cluster size <= 4kiB and
deallocated on last umount with cluster size <= 4kiB), and
asynchronous io for the compressed data using a list of buffer heads.
Er, we use synchronous io as async io only works on whole pages
covered by buffers and not on individual buffer heads...
- Bug fix for reading compressed files with sparse compression blocks.
tng-0.0.2 - Now handles larger/fragmented/compressed volumes/files/dirs.
- Fixed handling of directories when cluster size exceeds index block
size.
- Hide DOS only name space directory entries from readdir() but allow
them in lookup(). This should fix the problem that Linux doesn't
support directory hard links, while still allowing access to entries
via their short file name. This also has the benefit of mimicking
what Windows users are used to, so it is the ideal solution.
- Implemented sync_page everywhere so no more hangs in D state when
waiting for a page.
- Stop using bforget() in favour of brelse().
- Stop locking buffers unnecessarily.
- Implemented compressed files (inode->mapping contains uncompressed
data, raw compressed data is currently bread() into a vmalloc()ed
memory buffer).
- Enable compressed directories. (Their index root is marked compressed
to indicate that new files in that directory should be created
compressed.)
- Use vsnprintf rather than vsprintf in the ntfs_error and ntfs_warning
functions. (Thanks to Will Dyson for pointing this out.)
- Moved the ntfs_inode and ntfs_volume (the former ntfs_inode_info and
ntfs_sb_info) out of the common inode and super_block structures and
started using the generic_ip and generic_sbp pointers instead. This
makes ntfs entirely private with respect to the kernel tree.
- Detect compiler version and abort with error message if gcc less than
2.96 is used.
- Fix bug in name comparison function in unistr.c.
- Implement attribute lists part 1, the infrastructure: search contexts
and operations, find_external_attr(), lookup_attr()) and make the
code use the infrastructure.
- Fix stupid buffer overflow bug that became apparent on larger run
list containing attributes.
- Fix bugs in readdir() that became apparent on larger directories.
The driver is now really useful and survives the test
find . -type f -exec md5sum "{}" \;
without any error messages on a over 1GiB sized partition with >16k
files on it, including compressed files and directories and many files
and directories with attribute lists.
tng-0.0.1 - The first useful version.
- Added ntfs_lookup().
- Added default upcase generation and handling.
- Added compile options to be shown on module init.
- Many bug fixes that were "hidden" before.
- Update to latest kernel.
- Added ntfs_readdir().
- Added file operations for mmap(), read(), open() and llseek(). We just
use the generic ones. The whole point of going through implementing
readpage() methods and where possible get_block() call backs is that
this allows us to make use of the generic high level methods provided
by the kernel.
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
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!
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
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
working nicely, too. Proof of inode metadata in the page cache and non-
resident file unnamed stream data in the page cache concepts is thus
complete.

View file

@ -0,0 +1,16 @@
# Rules for making the NTFS driver.
obj-$(CONFIG_NTFS_FS) += ntfs.o
ntfs-objs := aops.o attrib.o compress.o debug.o dir.o file.o inode.o mft.o \
mst.o namei.o super.o sysctl.o time.o unistr.o upcase.o
EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.5\"
ifeq ($(CONFIG_NTFS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
ifeq ($(CONFIG_NTFS_RW),y)
EXTRA_CFLAGS += -DNTFS_RW
endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,106 @@
/*
* attrib.h - Defines for attribute handling in NTFS Linux kernel driver.
* Part of the Linux-NTFS project.
*
* Copyright (c) 2001-2003 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon
*
* 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_ATTRIB_H
#define _LINUX_NTFS_ATTRIB_H
#include <linux/fs.h>
#include "endian.h"
#include "types.h"
#include "layout.h"
static inline void init_run_list(run_list *rl)
{
rl->rl = NULL;
init_rwsem(&rl->lock);
}
typedef enum {
LCN_HOLE = -1, /* Keep this as highest value or die! */
LCN_RL_NOT_MAPPED = -2,
LCN_ENOENT = -3,
LCN_EINVAL = -4,
} LCN_SPECIAL_VALUES;
/**
* attr_search_context - used in attribute search functions
* @mrec: buffer containing mft record to search
* @attr: attribute record in @mrec where to begin/continue search
* @is_first: if true lookup_attr() begins search with @attr, else after @attr
*
* Structure must be initialized to zero before the first call to one of the
* attribute search functions. Initialize @mrec to point to the mft record to
* search, and @attr to point to the first attribute within @mrec (not necessary
* if calling the _first() functions), and set @is_first to TRUE (not necessary
* if calling the _first() functions).
*
* If @is_first is TRUE, the search begins with @attr. If @is_first is FALSE,
* the search begins after @attr. This is so that, after the first call to one
* of the search attribute functions, we can call the function again, without
* any modification of the search context, to automagically get the next
* matching attribute.
*/
typedef struct {
MFT_RECORD *mrec;
ATTR_RECORD *attr;
BOOL is_first;
ntfs_inode *ntfs_ino;
ATTR_LIST_ENTRY *al_entry;
ntfs_inode *base_ntfs_ino;
MFT_RECORD *base_mrec;
ATTR_RECORD *base_attr;
} attr_search_context;
extern run_list_element *decompress_mapping_pairs(const ntfs_volume *vol,
const ATTR_RECORD *attr, run_list_element *old_rl);
extern int map_run_list(ntfs_inode *ni, VCN vcn);
extern LCN vcn_to_lcn(const run_list_element *rl, const VCN vcn);
extern BOOL find_attr(const ATTR_TYPES type, const uchar_t *name,
const u32 name_len, const IGNORE_CASE_BOOL ic, const u8 *val,
const u32 val_len, attr_search_context *ctx);
BOOL lookup_attr(const ATTR_TYPES type, const uchar_t *name, const u32 name_len,
const IGNORE_CASE_BOOL ic, const VCN lowest_vcn, const u8 *val,
const u32 val_len, attr_search_context *ctx);
extern int load_attribute_list(ntfs_volume *vol, run_list *rl, u8 *al_start,
const s64 size, const s64 initialized_size);
static inline s64 attribute_value_length(const ATTR_RECORD *a)
{
if (!a->non_resident)
return (s64)le32_to_cpu(a->data.resident.value_length);
return sle64_to_cpu(a->data.non_resident.data_size);
}
extern void reinit_attr_search_ctx(attr_search_context *ctx);
extern attr_search_context *get_attr_search_ctx(ntfs_inode *ni,
MFT_RECORD *mrec);
extern void put_attr_search_ctx(attr_search_context *ctx);
#endif /* _LINUX_NTFS_ATTRIB_H */

View file

@ -0,0 +1,945 @@
/**
* compress.c - NTFS kernel compressed attributes handling.
* Part of the Linux-NTFS project.
*
* Copyright (c) 2001-2003 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program/include file is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the Linux-NTFS
* distribution in the file COPYING); if not, write to the Free Software
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/fs.h>
#include <linux/buffer_head.h>
#include "ntfs.h"
/**
* ntfs_compression_constants - enum of constants used in the compression code
*/
typedef enum {
/* Token types and access mask. */
NTFS_SYMBOL_TOKEN = 0,
NTFS_PHRASE_TOKEN = 1,
NTFS_TOKEN_MASK = 1,
/* Compression sub-block constants. */
NTFS_SB_SIZE_MASK = 0x0fff,
NTFS_SB_SIZE = 0x1000,
NTFS_SB_IS_COMPRESSED = 0x8000,
/*
* The maximum compression block size is by definition 16 * the cluster
* size, with the maximum supported cluster size being 4kiB. Thus the
* maximum compression buffer size is 64kiB, so we use this when
* initializing the compression buffer.
*/
NTFS_MAX_CB_SIZE = 64 * 1024,
} ntfs_compression_constants;
/**
* ntfs_compression_buffer - one buffer for the decompression engine
*/
static u8 *ntfs_compression_buffer = NULL;
/**
* ntfs_cb_lock - spinlock which protects ntfs_compression_buffer
*/
static spinlock_t ntfs_cb_lock = SPIN_LOCK_UNLOCKED;
/**
* allocate_compression_buffers - allocate the decompression buffers
*
* Caller has to hold the ntfs_lock semaphore.
*
* Return 0 on success or -ENOMEM if the allocations failed.
*/
int allocate_compression_buffers(void)
{
BUG_ON(ntfs_compression_buffer);
ntfs_compression_buffer = vmalloc(NTFS_MAX_CB_SIZE);
if (!ntfs_compression_buffer)
return -ENOMEM;
return 0;
}
/**
* free_compression_buffers - free the decompression buffers
*
* Caller has to hold the ntfs_lock semaphore.
*/
void free_compression_buffers(void)
{
BUG_ON(!ntfs_compression_buffer);
vfree(ntfs_compression_buffer);
ntfs_compression_buffer = NULL;
}
/**
* zero_partial_compressed_page - zero out of bounds compressed page region
*/
static void zero_partial_compressed_page(ntfs_inode *ni, struct page *page)
{
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) {
/*
* FIXME: Using clear_page() will become wrong when we get
* PAGE_CACHE_SIZE != PAGE_SIZE but for now there is no problem.
*/
clear_page(kp);
return;
}
kp_ofs = ni->initialized_size & ~PAGE_CACHE_MASK;
memset(kp + kp_ofs, 0, PAGE_CACHE_SIZE - kp_ofs);
return;
}
/**
* 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)
{
if ((page->index >= (ni->initialized_size >> PAGE_CACHE_SHIFT)) &&
(ni->initialized_size < VFS_I(ni)->i_size))
zero_partial_compressed_page(ni, page);
return;
}
/**
* ntfs_decompress - decompress a compression block into an array of pages
* @dest_pages: destination array of pages
* @dest_index: current index into @dest_pages (IN/OUT)
* @dest_ofs: current offset within @dest_pages[@dest_index] (IN/OUT)
* @dest_max_index: maximum index into @dest_pages (IN)
* @dest_max_ofs: maximum offset within @dest_pages[@dest_max_index] (IN)
* @xpage: the target page (-1 if none) (IN)
* @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)
*
* The caller must have disabled preemption. ntfs_decompress() reenables it when
* the critical section is finished.
*
* This decompresses the compression block @cb_start into the array of
* destination pages @dest_pages starting at index @dest_index into @dest_pages
* and at offset @dest_pos into the page @dest_pages[@dest_index].
*
* When the page @dest_pages[@xpage] is completed, @xpage_done is set to 1.
* If xpage is -1 or @xpage has not been completed, @xpage_done is not modified.
*
* @cb_start is a pointer to the compression block which needs decompressing
* and @cb_size is the size of @cb_start in bytes (8-64kiB).
*
* Return 0 if success or -EOVERFLOW on error in the compressed stream.
* @xpage_done indicates whether the target page (@dest_pages[@xpage]) was
* completed during the decompression of the compression block (@cb_start).
*
* Warning: This function *REQUIRES* PAGE_CACHE_SIZE >= 4096 or it will blow up
* unpredicatbly! You have been warned!
*
* Note to hackers: This function may not sleep until it has finished accessing
* the compression block @cb_start as it is a per-CPU buffer.
*/
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)
{
/*
* Pointers into the compressed data, i.e. the compression block (cb),
* and the therein contained sub-blocks (sb).
*/
u8 *cb_end = cb_start + cb_size; /* End of cb. */
u8 *cb = cb_start; /* Current position in cb. */
u8 *cb_sb_start = cb; /* Beginning of the current sb in the cb. */
u8 *cb_sb_end; /* End of current sb / beginning of next sb. */
/* Variables for uncompressed data / destination. */
struct page *dp; /* Current destination page being worked on. */
u8 *dp_addr; /* Current pointer into dp. */
u8 *dp_sb_start; /* Start of current sub-block in dp. */
u8 *dp_sb_end; /* End of current sb in dp (dp_sb_start +
NTFS_SB_SIZE). */
u16 do_sb_start; /* @dest_ofs when starting this sub-block. */
u16 do_sb_end; /* @dest_ofs of end of this sb (do_sb_start +
NTFS_SB_SIZE). */
/* Variables for tag and token parsing. */
u8 tag; /* Current tag. */
int token; /* Loop counter for the eight tokens in tag. */
/* Need this because we can't sleep, so need two stages. */
int completed_pages[dest_max_index - *dest_index + 1];
int nr_completed_pages = 0;
/* Default error code. */
int err = -EOVERFLOW;
ntfs_debug("Entering, cb_size = 0x%x.", cb_size);
do_next_sb:
ntfs_debug("Beginning sub-block at offset = 0x%x in the cb.",
cb - cb_start);
/* Have we reached the end of the compression block? */
if (cb == cb_end || !le16_to_cpup((u16*)cb)) {
int i;
ntfs_debug("Completed. Returning success (0).");
err = 0;
return_error:
/* We can sleep from now on, so we drop lock. */
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];
dp = dest_pages[di];
/*
* If we are outside the initialized size, zero
* the out of bounds page range.
*/
handle_bounds_compressed_page(ni, dp);
flush_dcache_page(dp);
kunmap(dp);
SetPageUptodate(dp);
unlock_page(dp);
if (di == xpage)
*xpage_done = 1;
else
page_cache_release(dp);
dest_pages[di] = NULL;
}
}
return err;
}
/* Setup offsets for the current sub-block destination. */
do_sb_start = *dest_ofs;
do_sb_end = do_sb_start + NTFS_SB_SIZE;
/* Check that we are still within allowed boundaries. */
if (*dest_index == dest_max_index && do_sb_end > dest_max_ofs)
goto return_overflow;
/* Does the minimum size of a compressed sb overflow valid range? */
if (cb + 6 > cb_end)
goto return_overflow;
/* Setup the current sub-block source pointers and validate range. */
cb_sb_start = cb;
cb_sb_end = cb_sb_start + (le16_to_cpup((u16*)cb) & NTFS_SB_SIZE_MASK)
+ 3;
if (cb_sb_end > cb_end)
goto return_overflow;
/* Get the current destination page. */
dp = dest_pages[*dest_index];
if (!dp) {
/* No page present. Skip decompression of this sub-block. */
cb = cb_sb_end;
/* Advance destination position to next sub-block. */
*dest_ofs = (*dest_ofs + NTFS_SB_SIZE) & ~PAGE_CACHE_MASK;
if (!*dest_ofs && (++*dest_index > dest_max_index))
goto return_overflow;
goto do_next_sb;
}
/* We have a valid destination page. Setup the destination pointers. */
dp_addr = (u8*)page_address(dp) + do_sb_start;
/* Now, we are ready to process the current sub-block (sb). */
if (!(le16_to_cpup((u16*)cb) & NTFS_SB_IS_COMPRESSED)) {
ntfs_debug("Found uncompressed sub-block.");
/* This sb is not compressed, just copy it into destination. */
/* Advance source position to first data byte. */
cb += 2;
/* An uncompressed sb must be full size. */
if (cb_sb_end - cb != NTFS_SB_SIZE)
goto return_overflow;
/* Copy the block and advance the source position. */
memcpy(dp_addr, cb, NTFS_SB_SIZE);
cb += NTFS_SB_SIZE;
/* Advance destination position to next sub-block. */
*dest_ofs += NTFS_SB_SIZE;
if (!(*dest_ofs &= ~PAGE_CACHE_MASK)) {
finalize_page:
/*
* First stage: add current page index to array of
* completed pages.
*/
completed_pages[nr_completed_pages++] = *dest_index;
if (++*dest_index > dest_max_index)
goto return_overflow;
}
goto do_next_sb;
}
ntfs_debug("Found compressed sub-block.");
/* This sb is compressed, decompress it into destination. */
/* Setup destination pointers. */
dp_sb_start = dp_addr;
dp_sb_end = dp_sb_start + NTFS_SB_SIZE;
/* Forward to the first tag in the sub-block. */
cb += 2;
do_next_tag:
if (cb == cb_sb_end) {
/* Check if the decompressed sub-block was not full-length. */
if (dp_addr < dp_sb_end) {
int nr_bytes = do_sb_end - *dest_ofs;
ntfs_debug("Filling incomplete sub-block with "
"zeroes.");
/* Zero remainder and update destination position. */
memset(dp_addr, 0, nr_bytes);
*dest_ofs += nr_bytes;
}
/* We have finished the current sub-block. */
if (!(*dest_ofs &= ~PAGE_CACHE_MASK))
goto finalize_page;
goto do_next_sb;
}
/* Check we are still in range. */
if (cb > cb_sb_end || dp_addr > dp_sb_end)
goto return_overflow;
/* Get the next tag and advance to first token. */
tag = *cb++;
/* Parse the eight tokens described by the tag. */
for (token = 0; token < 8; token++, tag >>= 1) {
u16 lg, pt, length, max_non_overlap;
register u16 i;
u8 *dp_back_addr;
/* Check if we are done / still in range. */
if (cb >= cb_sb_end || dp_addr > dp_sb_end)
break;
/* Determine token type and parse appropriately.*/
if ((tag & NTFS_TOKEN_MASK) == NTFS_SYMBOL_TOKEN) {
/*
* We have a symbol token, copy the symbol across, and
* advance the source and destination positions.
*/
*dp_addr++ = *cb++;
++*dest_ofs;
/* Continue with the next token. */
continue;
}
/*
* We have a phrase token. Make sure it is not the first tag in
* the sb as this is illegal and would confuse the code below.
*/
if (dp_addr == dp_sb_start)
goto return_overflow;
/*
* Determine the number of bytes to go back (p) and the number
* of bytes to copy (l). We use an optimized algorithm in which
* we first calculate log2(current destination position in sb),
* which allows determination of l and p in O(1) rather than
* O(n). We just need an arch-optimized log2() function now.
*/
lg = 0;
for (i = *dest_ofs - do_sb_start - 1; i >= 0x10; i >>= 1)
lg++;
/* Get the phrase token into i. */
pt = le16_to_cpup((u16*)cb);
/*
* Calculate starting position of the byte sequence in
* the destination using the fact that p = (pt >> (12 - lg)) + 1
* and make sure we don't go too far back.
*/
dp_back_addr = dp_addr - (pt >> (12 - lg)) - 1;
if (dp_back_addr < dp_sb_start)
goto return_overflow;
/* Now calculate the length of the byte sequence. */
length = (pt & (0xfff >> lg)) + 3;
/* Advance destination position and verify it is in range. */
*dest_ofs += length;
if (*dest_ofs > do_sb_end)
goto return_overflow;
/* The number of non-overlapping bytes. */
max_non_overlap = dp_addr - dp_back_addr;
if (length <= max_non_overlap) {
/* The byte sequence doesn't overlap, just copy it. */
memcpy(dp_addr, dp_back_addr, length);
/* Advance destination pointer. */
dp_addr += length;
} else {
/*
* The byte sequence does overlap, copy non-overlapping
* part and then do a slow byte by byte copy for the
* overlapping part. Also, advance the destination
* pointer.
*/
memcpy(dp_addr, dp_back_addr, max_non_overlap);
dp_addr += max_non_overlap;
dp_back_addr += max_non_overlap;
length -= max_non_overlap;
while (length--)
*dp_addr++ = *dp_back_addr++;
}
/* Advance source position and continue with the next token. */
cb += 2;
}
/* No tokens left in the current tag. Continue with the next tag. */
goto do_next_tag;
return_overflow:
ntfs_error(NULL, "Failed. Returning -EOVERFLOW.\n");
goto return_error;
}
/**
* ntfs_read_compressed_block - read a compressed block into the page cache
* @page: locked page in the compression block(s) we need to read
*
* When we are called the page has already been verified to be locked and the
* attribute is known to be non-resident, not encrypted, but compressed.
*
* 1. Determine which compression block(s) @page is in.
* 2. Get hold of all pages corresponding to this/these compression block(s).
* 3. Read the (first) compression block.
* 4. Decompress it into the corresponding pages.
* 5. Throw the compressed data away and proceed to 3. for the next compression
* block or return success if no more compression blocks left.
*
* Warning: We have to be careful what we do about existing pages. They might
* have been written to so that we would lose data if we were to just overwrite
* them with the out-of-date uncompressed data.
*
* FIXME: For PAGE_CACHE_SIZE > cb_size we are not doing the Right Thing(TM) at
* the end of the file I think. We need to detect this case and zero the out
* of bounds remainder of the page in question and mark it as handled. At the
* moment we would just return -EIO on such a page. This bug will only become
* apparent if pages are above 8kiB and the NTFS volume only uses 512 byte
* clusters so is probably not going to be seen by anyone. Still this should
* be fixed. (AIA)
*
* FIXME: Again for PAGE_CACHE_SIZE > cb_size we are screwing up both in
* handling sparse and compressed cbs. (AIA)
*
* FIXME: At the moment we don't do any zeroing out in the case that
* initialized_size is less than data_size. This should be safe because of the
* nature of the compression algorithm used. Just in case we check and output
* an error message in read inode if the two sizes are not equal for a
* compressed file. (AIA)
*/
int ntfs_read_compressed_block(struct page *page)
{
struct address_space *mapping = page->mapping;
ntfs_inode *ni = NTFS_I(mapping->host);
ntfs_volume *vol = ni->vol;
struct super_block *sb = vol->sb;
run_list_element *rl;
unsigned long block_size = sb->s_blocksize;
unsigned char block_size_bits = sb->s_blocksize_bits;
u8 *cb, *cb_pos, *cb_end;
struct buffer_head **bhs;
unsigned long offset, index = page->index;
u32 cb_size = ni->itype.compressed.block_size;
u64 cb_size_mask = cb_size - 1UL;
VCN vcn;
LCN lcn;
/* The first wanted vcn (minimum alignment is PAGE_CACHE_SIZE). */
VCN start_vcn = (((s64)index << PAGE_CACHE_SHIFT) & ~cb_size_mask) >>
vol->cluster_size_bits;
/*
* The first vcn after the last wanted vcn (minumum alignment is again
* PAGE_CACHE_SIZE.
*/
VCN end_vcn = ((((s64)(index + 1UL) << PAGE_CACHE_SHIFT) + cb_size - 1)
& ~cb_size_mask) >> vol->cluster_size_bits;
/* Number of compression blocks (cbs) in the wanted vcn range. */
unsigned int nr_cbs = (end_vcn - start_vcn) << vol->cluster_size_bits
>> ni->itype.compressed.block_size_bits;
/*
* Number of pages required to store the uncompressed data from all
* compression blocks (cbs) overlapping @page. Due to alignment
* guarantees of start_vcn and end_vcn, no need to round up here.
*/
unsigned int nr_pages = (end_vcn - start_vcn) <<
vol->cluster_size_bits >> PAGE_CACHE_SHIFT;
unsigned int xpage, max_page, cur_page, cur_ofs, i;
unsigned int cb_clusters, cb_max_ofs;
int block, max_block, cb_max_page, bhs_size, nr_bhs, err = 0;
struct page **pages;
unsigned char xpage_done = 0;
ntfs_debug("Entering, page->index = 0x%lx, cb_size = 0x%x, nr_pages = "
"%i.", index, cb_size, nr_pages);
/*
* Bad things happen if we get here for anything that is not an
* unnamed $DATA attribute.
*/
BUG_ON(ni->type != AT_DATA);
BUG_ON(ni->name_len);
pages = kmalloc(nr_pages * sizeof(struct page *), GFP_NOFS);
/* Allocate memory to store the buffer heads we need. */
bhs_size = cb_size / block_size * sizeof(struct buffer_head *);
bhs = kmalloc(bhs_size, GFP_NOFS);
if (unlikely(!pages || !bhs)) {
kfree(bhs);
kfree(pages);
SetPageError(page);
unlock_page(page);
ntfs_error(vol->sb, "Failed to allocate internal buffers.");
return -ENOMEM;
}
/*
* We have already been given one page, this is the one we must do.
* Once again, the alignment guarantees keep it simple.
*/
offset = start_vcn << vol->cluster_size_bits >> PAGE_CACHE_SHIFT;
xpage = index - offset;
pages[xpage] = 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;
if (nr_pages < max_page)
max_page = nr_pages;
for (i = 0; i < max_page; i++, offset++) {
if (i != xpage)
pages[i] = grab_cache_page_nowait(mapping, offset);
page = pages[i];
if (page) {
/*
* We only (re)read the page if it isn't already read
* in and/or dirty or we would be losing data or at
* least wasting our time.
*/
if (!PageDirty(page) && (!PageUptodate(page) ||
PageError(page))) {
ClearPageError(page);
kmap(page);
continue;
}
unlock_page(page);
page_cache_release(page);
pages[i] = NULL;
}
}
/*
* We have the run list, and all the destination pages we need to fill.
* Now read the first compression block.
*/
cur_page = 0;
cur_ofs = 0;
cb_clusters = ni->itype.compressed.block_clusters;
do_next_cb:
nr_cbs--;
nr_bhs = 0;
/* Read all cb buffer heads one cluster at a time. */
rl = NULL;
for (vcn = start_vcn, start_vcn += cb_clusters; vcn < start_vcn;
vcn++) {
BOOL is_retry = FALSE;
if (!rl) {
lock_retry_remap:
down_read(&ni->run_list.lock);
rl = ni->run_list.rl;
}
if (likely(rl != NULL)) {
/* Seek to element containing target vcn. */
while (rl->length && rl[1].vcn <= vcn)
rl++;
lcn = vcn_to_lcn(rl, vcn);
} else
lcn = (LCN)LCN_RL_NOT_MAPPED;
ntfs_debug("Reading vcn = 0x%Lx, lcn = 0x%Lx.",
(long long)vcn, (long long)lcn);
if (lcn < 0) {
/*
* When we reach the first sparse cluster we have
* finished with the cb.
*/
if (lcn == LCN_HOLE)
break;
if (is_retry || lcn != LCN_RL_NOT_MAPPED)
goto rl_err;
is_retry = TRUE;
/*
* Attempt to map run list, dropping lock for the
* duration.
*/
up_read(&ni->run_list.lock);
if (!map_run_list(ni, vcn))
goto lock_retry_remap;
goto map_rl_err;
}
block = lcn << vol->cluster_size_bits >> block_size_bits;
/* Read the lcn from device in chunks of block_size bytes. */
max_block = block + (vol->cluster_size >> block_size_bits);
do {
ntfs_debug("block = 0x%x.", block);
if (unlikely(!(bhs[nr_bhs] = sb_getblk(sb, block))))
goto getblk_err;
nr_bhs++;
} while (++block < max_block);
}
/* Release the lock if we took it. */
if (rl)
up_read(&ni->run_list.lock);
/* Setup and initiate io on all buffer heads. */
for (i = 0; i < nr_bhs; i++) {
struct buffer_head *tbh = bhs[i];
if (unlikely(test_set_buffer_locked(tbh)))
continue;
if (unlikely(buffer_uptodate(tbh))) {
unlock_buffer(tbh);
continue;
}
atomic_inc(&tbh->b_count);
tbh->b_end_io = end_buffer_read_sync;
submit_bh(READ, tbh);
}
/* Wait for io completion on all buffer heads. */
for (i = 0; i < nr_bhs; i++) {
struct buffer_head *tbh = bhs[i];
if (buffer_uptodate(tbh))
continue;
wait_on_buffer(tbh);
/*
* We need an optimization barrier here, otherwise we start
* hitting the below fixup code when accessing a loopback
* mounted ntfs partition. This indicates either there is a
* race condition in the loop driver or, more likely, gcc
* overoptimises the code without the barrier and it doesn't
* do the Right Thing(TM).
*/
barrier();
if (unlikely(!buffer_uptodate(tbh))) {
ntfs_warning(vol->sb, "Buffer is unlocked but not "
"uptodate! Unplugging the disk queue "
"and rescheduling.");
get_bh(tbh);
blk_run_queues();
schedule();
put_bh(tbh);
if (unlikely(!buffer_uptodate(tbh)))
goto read_err;
ntfs_warning(vol->sb, "Buffer is now uptodate. Good.");
}
}
/*
* Get the compression buffer. We must not sleep any more
* until we are finished with it.
*/
spin_lock(&ntfs_cb_lock);
cb = ntfs_compression_buffer;
BUG_ON(!cb);
cb_pos = cb;
cb_end = cb + cb_size;
/* Copy the buffer heads into the contiguous buffer. */
for (i = 0; i < nr_bhs; i++) {
memcpy(cb_pos, bhs[i]->b_data, block_size);
cb_pos += block_size;
}
/* Just a precaution. */
if (cb_pos + 2 <= cb + cb_size)
*(u16*)cb_pos = 0;
/* Reset cb_pos back to the beginning. */
cb_pos = cb;
/* We now have both source (if present) and destination. */
ntfs_debug("Successfully read the compression block.");
/* The last page and maximum offset within it for the current cb. */
cb_max_page = (cur_page << PAGE_CACHE_SHIFT) + cur_ofs + cb_size;
cb_max_ofs = cb_max_page & ~PAGE_CACHE_MASK;
cb_max_page >>= PAGE_CACHE_SHIFT;
/* Catch end of file inside a compression block. */
if (cb_max_page > max_page)
cb_max_page = max_page;
if (vcn == start_vcn - cb_clusters) {
/* Sparse cb, zero out page range overlapping the cb. */
ntfs_debug("Found sparse compression block.");
/* We can sleep from now on, so we drop lock. */
spin_unlock(&ntfs_cb_lock);
if (cb_max_ofs)
cb_max_page--;
for (; cur_page < cb_max_page; cur_page++) {
page = pages[cur_page];
if (page) {
/*
* FIXME: Using clear_page() will become wrong
* when we get PAGE_CACHE_SIZE != PAGE_SIZE but
* for now there is no problem.
*/
if (likely(!cur_ofs))
clear_page(page_address(page));
else
memset(page_address(page) + cur_ofs, 0,
PAGE_CACHE_SIZE -
cur_ofs);
flush_dcache_page(page);
kunmap(page);
SetPageUptodate(page);
unlock_page(page);
if (cur_page == xpage)
xpage_done = 1;
else
page_cache_release(page);
pages[cur_page] = NULL;
}
cb_pos += PAGE_CACHE_SIZE - cur_ofs;
cur_ofs = 0;
if (cb_pos >= cb_end)
break;
}
/* If we have a partial final page, deal with it now. */
if (cb_max_ofs && cb_pos < cb_end) {
page = pages[cur_page];
if (page)
memset(page_address(page) + cur_ofs, 0,
cb_max_ofs - cur_ofs);
/*
* No need to update cb_pos at this stage:
* cb_pos += cb_max_ofs - cur_ofs;
*/
cur_ofs = cb_max_ofs;
}
} else if (vcn == start_vcn) {
/* We can't sleep so we need two stages. */
unsigned int cur2_page = cur_page;
unsigned int cur_ofs2 = cur_ofs;
u8 *cb_pos2 = cb_pos;
ntfs_debug("Found uncompressed compression block.");
/* Uncompressed cb, copy it to the destination pages. */
/*
* TODO: As a big optimization, we could detect this case
* before we read all the pages and use block_read_full_page()
* on all full pages instead (we still have to treat partial
* pages especially but at least we are getting rid of the
* synchronous io for the majority of pages.
* Or if we choose not to do the read-ahead/-behind stuff, we
* could just return block_read_full_page(pages[xpage]) as long
* as PAGE_CACHE_SIZE <= cb_size.
*/
if (cb_max_ofs)
cb_max_page--;
/* First stage: copy data into destination pages. */
for (; cur_page < cb_max_page; cur_page++) {
page = pages[cur_page];
if (page)
memcpy(page_address(page) + cur_ofs, cb_pos,
PAGE_CACHE_SIZE - cur_ofs);
cb_pos += PAGE_CACHE_SIZE - cur_ofs;
cur_ofs = 0;
if (cb_pos >= cb_end)
break;
}
/* If we have a partial final page, deal with it now. */
if (cb_max_ofs && cb_pos < cb_end) {
page = pages[cur_page];
if (page)
memcpy(page_address(page) + cur_ofs, cb_pos,
cb_max_ofs - cur_ofs);
cb_pos += cb_max_ofs - cur_ofs;
cur_ofs = cb_max_ofs;
}
/* We can sleep from now on, so drop lock. */
spin_unlock(&ntfs_cb_lock);
/* Second stage: finalize pages. */
for (; cur2_page < cb_max_page; cur2_page++) {
page = pages[cur2_page];
if (page) {
/*
* If we are outside the initialized size, zero
* the out of bounds page range.
*/
handle_bounds_compressed_page(ni, page);
flush_dcache_page(page);
kunmap(page);
SetPageUptodate(page);
unlock_page(page);
if (cur2_page == xpage)
xpage_done = 1;
else
page_cache_release(page);
pages[cur2_page] = NULL;
}
cb_pos2 += PAGE_CACHE_SIZE - cur_ofs2;
cur_ofs2 = 0;
if (cb_pos2 >= cb_end)
break;
}
} else {
/* Compressed cb, decompress it into the destination page(s). */
unsigned int prev_cur_page = cur_page;
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));
/*
* We can sleep from now on, lock already dropped by
* ntfs_decompress().
*/
if (err) {
ntfs_error(vol->sb, "ntfs_decompress() failed in inode "
"0x%lx with error code %i. Skipping "
"this compression block.\n",
ni->mft_no, -err);
/* Release the unfinished pages. */
for (; prev_cur_page < cur_page; prev_cur_page++) {
page = pages[prev_cur_page];
if (page) {
if (prev_cur_page == xpage &&
!xpage_done)
SetPageError(page);
flush_dcache_page(page);
kunmap(page);
unlock_page(page);
if (prev_cur_page != xpage)
page_cache_release(page);
pages[prev_cur_page] = NULL;
}
}
}
}
/* Release the buffer heads. */
for (i = 0; i < nr_bhs; i++)
brelse(bhs[i]);
/* Do we have more work to do? */
if (nr_cbs)
goto do_next_cb;
/* We no longer need the list of buffer heads. */
kfree(bhs);
/* Clean up if we have any pages left. Should never happen. */
for (cur_page = 0; cur_page < max_page; cur_page++) {
page = pages[cur_page];
if (page) {
ntfs_error(vol->sb, "Still have pages left! "
"Terminating them with extreme "
"prejudice.");
if (cur_page == xpage && !xpage_done)
SetPageError(page);
flush_dcache_page(page);
kunmap(page);
unlock_page(page);
if (cur_page != xpage)
page_cache_release(page);
pages[cur_page] = NULL;
}
}
/* We no longer need the list of pages. */
kfree(pages);
/* If we have completed the requested page, we return success. */
if (likely(xpage_done))
return 0;
ntfs_debug("Failed. Returning error code %s.", err == -EOVERFLOW ?
"EOVERFLOW" : (!err ? "EIO" : "unkown error"));
return err < 0 ? err : -EIO;
read_err:
ntfs_error(vol->sb, "IO error while reading compressed data.");
/* Release the buffer heads. */
for (i = 0; i < nr_bhs; i++)
brelse(bhs[i]);
goto err_out;
map_rl_err:
ntfs_error(vol->sb, "map_run_list() failed. Cannot read compression "
"block.");
goto err_out;
rl_err:
up_read(&ni->run_list.lock);
ntfs_error(vol->sb, "vcn_to_lcn() failed. Cannot read compression "
"block.");
goto err_out;
getblk_err:
up_read(&ni->run_list.lock);
ntfs_error(vol->sb, "getblk() failed. Cannot read compression block.");
err_out:
kfree(bhs);
for (i = cur_page; i < max_page; i++) {
page = pages[i];
if (page) {
if (i == xpage && !xpage_done)
SetPageError(page);
flush_dcache_page(page);
kunmap(page);
unlock_page(page);
if (i != xpage)
page_cache_release(page);
}
}
kfree(pages);
return -EIO;
}

View file

@ -0,0 +1,175 @@
/*
* debug.c - NTFS kernel debug support. Part of the Linux-NTFS project.
*
* Copyright (c) 2001,2002 Anton Altaparmakov.
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program/include file is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the Linux-NTFS
* distribution in the file COPYING); if not, write to the Free Software
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "debug.h"
/*
* A static buffer to hold the error string being displayed and a spinlock
* to protect concurrent accesses to it.
*/
static char err_buf[1024];
static spinlock_t err_buf_lock = SPIN_LOCK_UNLOCKED;
/**
* __ntfs_warning - output a warning to the syslog
* @function: name of function outputting the warning
* @sb: super block of mounted ntfs filesystem
* @fmt: warning string containing format specifications
* @...: a variable number of arguments specified in @fmt
*
* Outputs a warning to the syslog for the mounted ntfs filesystem described
* by @sb.
*
* @fmt and the corresponding @... is printf style format string containing
* the warning string and the corresponding format arguments, respectively.
*
* @function is the name of the function from which __ntfs_warning is being
* called.
*
* Note, you should be using debug.h::ntfs_warning(@sb, @fmt, @...) instead
* as this provides the @function parameter automatically.
*/
void __ntfs_warning(const char *function, const struct super_block *sb,
const char *fmt, ...)
{
va_list args;
int flen = 0;
if (function)
flen = strlen(function);
spin_lock(&err_buf_lock);
va_start(args, fmt);
vsnprintf(err_buf, sizeof(err_buf), fmt, args);
va_end(args);
if (sb)
printk(KERN_ERR "NTFS-fs warning (device %s): %s(): %s\n",
sb->s_id, flen ? function : "", err_buf);
else
printk(KERN_ERR "NTFS-fs warning: %s(): %s\n",
flen ? function : "", err_buf);
spin_unlock(&err_buf_lock);
}
/**
* __ntfs_error - output an error to the syslog
* @function: name of function outputting the error
* @sb: super block of mounted ntfs filesystem
* @fmt: error string containing format specifications
* @...: a variable number of arguments specified in @fmt
*
* Outputs an error to the syslog for the mounted ntfs filesystem described
* by @sb.
*
* @fmt and the corresponding @... is printf style format string containing
* the error string and the corresponding format arguments, respectively.
*
* @function is the name of the function from which __ntfs_error is being
* called.
*
* Note, you should be using debug.h::ntfs_error(@sb, @fmt, @...) instead
* as this provides the @function parameter automatically.
*/
void __ntfs_error(const char *function, const struct super_block *sb,
const char *fmt, ...)
{
va_list args;
int flen = 0;
if (function)
flen = strlen(function);
spin_lock(&err_buf_lock);
va_start(args, fmt);
vsnprintf(err_buf, sizeof(err_buf), fmt, args);
va_end(args);
if (sb)
printk(KERN_ERR "NTFS-fs error (device %s): %s(): %s\n",
sb->s_id, flen ? function : "", err_buf);
else
printk(KERN_ERR "NTFS-fs error: %s(): %s\n",
flen ? function : "", err_buf);
spin_unlock(&err_buf_lock);
}
#ifdef DEBUG
/* If 1, output debug messages, and if 0, don't. */
int debug_msgs = 0;
void __ntfs_debug (const char *file, int line, const char *function,
const char *fmt, ...)
{
va_list args;
int flen = 0;
if (!debug_msgs)
return;
if (function)
flen = strlen(function);
spin_lock(&err_buf_lock);
va_start(args, fmt);
vsnprintf(err_buf, sizeof(err_buf), fmt, args);
va_end(args);
printk(KERN_DEBUG "NTFS-fs DEBUG (%s, %d): %s: %s\n",
file, line, flen ? function : "", err_buf);
spin_unlock(&err_buf_lock);
}
/* Dump a run list. Caller has to provide synchronisation for @rl. */
void ntfs_debug_dump_runlist(const run_list_element *rl)
{
int i;
const char *lcn_str[5] = { "LCN_HOLE ", "LCN_RL_NOT_MAPPED",
"LCN_ENOENT ", "LCN_EINVAL ",
"LCN_unknown " };
if (!debug_msgs)
return;
printk(KERN_DEBUG "NTFS-fs DEBUG: Dumping run list (values "
"in hex):\n");
if (!rl) {
printk(KERN_DEBUG "Run list not present.\n");
return;
}
printk(KERN_DEBUG "VCN LCN Run length\n");
for (i = 0; ; i++) {
LCN lcn = (rl + i)->lcn;
if (lcn < (LCN)0) {
int index = -lcn - 1;
if (index > -LCN_EINVAL - 1)
index = 4;
printk(KERN_DEBUG "%-16Lx %s %-16Lx%s\n",
(rl + i)->vcn, lcn_str[index],
(rl + i)->length, (rl + i)->length ?
"" : " (run list end)");
} else
printk(KERN_DEBUG "%-16Lx %-16Lx %-16Lx%s\n",
(rl + i)->vcn, (rl + i)->lcn,
(rl + i)->length, (rl + i)->length ?
"" : " (run list end)");
if (!(rl + i)->length)
break;
}
}
#endif

View file

@ -0,0 +1,72 @@
/*
* debug.h - NTFS kernel debug support. Part of the Linux-NTFS project.
*
* Copyright (c) 2001,2002 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_DEBUG_H
#define _LINUX_NTFS_DEBUG_H
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <linux/fs.h>
#include "inode.h"
#include "attrib.h"
#ifdef DEBUG
extern int debug_msgs;
#if 0 /* Fool kernel-doc since it doesn't do macros yet */
/**
* ntfs_debug - write a debug level message to syslog
* @f: a printf format string containing the message
* @...: the variables to substitute into @f
*
* ntfs_debug() writes a DEBUG level message to the syslog but only if the
* driver was compiled with -DDEBUG. Otherwise, the call turns into a NOP.
*/
static void ntfs_debug(const char *f, ...);
#endif
extern void __ntfs_debug (const char *file, int line, const char *function,
const char *format, ...) __attribute__ ((format (printf, 4, 5)));
#define ntfs_debug(f, a...) \
__ntfs_debug(__FILE__, __LINE__, __FUNCTION__, f, ##a)
extern void ntfs_debug_dump_runlist(const run_list_element *rl);
#else /* !DEBUG */
#define ntfs_debug(f, a...) do {} while (0)
#define ntfs_debug_dump_runlist(rl) do {} while (0)
#endif /* !DEBUG */
extern void __ntfs_warning(const char *function, const struct super_block *sb,
const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
#define ntfs_warning(sb, f, a...) __ntfs_warning(__FUNCTION__, sb, f, ##a)
extern void __ntfs_error(const char *function, const struct super_block *sb,
const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
#define ntfs_error(sb, f, a...) __ntfs_error(__FUNCTION__, sb, f, ##a)
#endif /* _LINUX_NTFS_DEBUG_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,47 @@
/*
* dir.h - Defines for directory handling in NTFS Linux kernel driver. Part of
* the Linux-NTFS project.
*
* Copyright (c) 2002 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_DIR_H
#define _LINUX_NTFS_DIR_H
#include "layout.h"
/*
* ntfs_name is used to return the file name to the caller of
* ntfs_lookup_inode_by_name() in order for the caller (namei.c::ntfs_lookup())
* to be able to deal with dcache aliasing issues.
*/
typedef struct {
MFT_REF mref;
FILE_NAME_TYPE_FLAGS type;
u8 len;
uchar_t name[0];
} __attribute__ ((__packed__)) ntfs_name;
/* The little endian Unicode string $I30 as a global constant. */
extern uchar_t I30[5];
extern MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni,
const uchar_t *uname, const int uname_len, ntfs_name **res);
#endif /* _LINUX_NTFS_FS_DIR_H */

View file

@ -0,0 +1,48 @@
/*
* endian.h - Defines for endianness handling in NTFS Linux kernel driver.
* Part of the Linux-NTFS project.
*
* Copyright (c) 2001 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_ENDIAN_H
#define _LINUX_NTFS_ENDIAN_H
#include <asm/byteorder.h>
/*
* Signed endianness conversion defines.
*/
#define sle16_to_cpu(x) ((s16)__le16_to_cpu((s16)(x)))
#define sle32_to_cpu(x) ((s32)__le32_to_cpu((s32)(x)))
#define sle64_to_cpu(x) ((s64)__le64_to_cpu((s64)(x)))
#define sle16_to_cpup(x) ((s16)__le16_to_cpu(*(s16*)(x)))
#define sle32_to_cpup(x) ((s32)__le32_to_cpu(*(s32*)(x)))
#define sle64_to_cpup(x) ((s64)__le64_to_cpu(*(s64*)(x)))
#define cpu_to_sle16(x) ((s16)__cpu_to_le16((s16)(x)))
#define cpu_to_sle32(x) ((s32)__cpu_to_le32((s32)(x)))
#define cpu_to_sle64(x) ((s64)__cpu_to_le64((s64)(x)))
#define cpu_to_sle16p(x) ((s16)__cpu_to_le16(*(s16*)(x)))
#define cpu_to_sle32p(x) ((s32)__cpu_to_le32(*(s32*)(x)))
#define cpu_to_sle64p(x) ((s64)__cpu_to_le64(*(s64*)(x)))
#endif /* _LINUX_NTFS_ENDIAN_H */

View file

@ -0,0 +1,76 @@
/*
* file.c - NTFS kernel file operations. Part of the Linux-NTFS project.
*
* Copyright (c) 2001 Anton Altaparmakov.
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program/include file is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the Linux-NTFS
* distribution in the file COPYING); if not, write to the Free Software
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ntfs.h"
/**
* ntfs_file_open - called when an inode is about to be opened
* @vi: inode to be opened
* @filp: file structure describing the inode
*
* Limit file size to the page cache limit on architectures where unsigned long
* is 32-bits. This is the most we can do for now without overflowing the page
* cache page index. Doing it this way means we don't run into problems because
* of existing too large files. It would be better to allow the user to read
* the beginning of the file but I doubt very much anyone is going to hit this
* check on a 32-bit architecture, so there is no point in adding the extra
* complexity required to support this.
*
* On 64-bit architectures, the check is hopefully optimized away by the
* compiler.
*
* After the check passes, just call generic_file_open() to do its work.
*/
static int ntfs_file_open(struct inode *vi, struct file *filp)
{
if (sizeof(unsigned long) < 8) {
if (vi->i_size > MAX_LFS_FILESIZE)
return -EFBIG;
}
return generic_file_open(vi, filp);
}
struct file_operations ntfs_file_ops = {
.llseek = generic_file_llseek, /* Seek inside file. */
.read = generic_file_read, /* Read from file. */
#ifdef NTFS_RW
.write = generic_file_write, /* Write to a file. */
#endif
.mmap = generic_file_mmap, /* Mmap file. */
.sendfile = generic_file_sendfile,/* Zero-copy data send with the
data source being on the
ntfs partition. We don't
need to care about the data
destination. */
.open = ntfs_file_open, /* Open file. */
};
struct inode_operations ntfs_file_inode_ops = {
#ifdef NTFS_RW
.truncate = ntfs_truncate,
.setattr = ntfs_setattr,
#endif
};
struct file_operations ntfs_empty_file_ops = {};
struct inode_operations ntfs_empty_inode_ops = {};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,251 @@
/*
* inode.h - Defines for inode structures NTFS Linux kernel driver. Part of
* the Linux-NTFS project.
*
* Copyright (c) 2001-2003 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon
*
* 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_INODE_H
#define _LINUX_NTFS_INODE_H
#include <linux/seq_file.h>
#include "layout.h"
#include "volume.h"
typedef struct _ntfs_inode ntfs_inode;
/*
* The NTFS in-memory inode structure. It is just used as an extension to the
* fields already provided in the VFS inode.
*/
struct _ntfs_inode {
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.
See ntfs_inode_state_bits below. */
unsigned long mft_no; /* Number of the mft record / inode. */
u16 seq_no; /* Sequence number of the mft record. */
atomic_t count; /* Inode reference count for book keeping. */
ntfs_volume *vol; /* Pointer to the ntfs volume of this inode. */
/*
* If NInoAttr() is true, the below fields describe the attribute which
* this fake inode belongs to. The actual inode of this attribute is
* pointed to by base_ntfs_ino and nr_extents is always set to -1 (see
* below). For real inodes, we also set the type (AT_DATA for files and
* AT_INDEX_ALLOCATION for directories), with the name = NULL and
* name_len = 0 for files and name = I30 (global constant) and
* name_len = 4 for directories.
*/
ATTR_TYPES type; /* Attribute type of this fake inode. */
uchar_t *name; /* Attribute name of this fake inode. */
u32 name_len; /* Attribute name length of this fake inode. */
run_list run_list; /* If state has the NI_NonResident bit set,
the run list of the unnamed data attribute
(if a file) or of the index allocation
attribute (directory) or of the attribute
described by the fake inode (if NInoAttr()).
If run_list.rl is NULL, the run list has not
been read in yet or has been unmapped. If
NI_NonResident is clear, the attribute is
resident (file and fake inode) or there is
no $I30 index allocation attribute
(small directory). In the latter case
run_list.rl is always NULL.*/
/*
* The following fields are only valid for real inodes and extent
* inodes.
*/
struct semaphore mrec_lock; /* Lock for serializing access to the
mft record belonging to this inode. */
struct page *page; /* The page containing the mft record of the
inode. This should only be touched by the
(un)map_mft_record*() functions. */
int page_ofs; /* Offset into the page at which the mft record
begins. This should only be touched by the
(un)map_mft_record*() functions. */
/*
* Attribute list support (only for use by the attribute lookup
* functions). Setup during read_inode for all inodes with attribute
* lists. Only valid if NI_AttrList is set in state, and attr_list_rl is
* further only valid if NI_AttrListNonResident is set.
*/
u32 attr_list_size; /* Length of attribute list value in bytes. */
u8 *attr_list; /* Attribute list value itself. */
run_list attr_list_rl; /* Run list for the attribute list value. */
union {
struct { /* It is a directory or $MFT. */
struct inode *bmp_ino; /* Attribute inode for the
directory index $BITMAP. */
u32 block_size; /* Size of an index block. */
u32 vcn_size; /* Size of a vcn in this
directory index. */
u8 block_size_bits; /* Log2 of the above. */
u8 vcn_size_bits; /* Log2 of the above. */
} index;
struct { /* It is a compressed file or fake inode. */
s64 size; /* Copy of compressed_size from
$DATA. */
u32 block_size; /* Size of a compression block
(cb). */
u8 block_size_bits; /* Log2 of the size of a cb. */
u8 block_clusters; /* Number of clusters per cb. */
} compressed;
} itype;
struct semaphore extent_lock; /* Lock for accessing/modifying the
below . */
s32 nr_extents; /* For a base mft record, the number of attached extent
inodes (0 if none), for extent records and for fake
inodes describing an attribute this is -1. */
union { /* This union is only used if nr_extents != 0. */
ntfs_inode **extent_ntfs_inos; /* For nr_extents > 0, array of
the ntfs inodes of the extent
mft records belonging to
this base inode which have
been loaded. */
ntfs_inode *base_ntfs_ino; /* For nr_extents == -1, the
ntfs inode of the base mft
record. For fake inodes, the
real (base) inode to which
the attribute belongs. */
} ext;
};
/*
* Defined bits for the state field in the ntfs_inode structure.
* (f) = files only, (d) = directories only, (a) = attributes/fake inodes only
*/
typedef enum {
NI_Dirty, /* 1: Mft record needs to be written to disk. */
NI_AttrList, /* 1: Mft record contains an attribute list. */
NI_AttrListNonResident, /* 1: Attribute list is non-resident. Implies
NI_AttrList is set. */
NI_Attr, /* 1: Fake inode for attribute i/o.
0: Real inode or extent inode. */
NI_MstProtected, /* 1: Attribute is protected by MST fixups.
0: Attribute is not protected by fixups. */
NI_NonResident, /* 1: Unnamed data attr is non-resident (f).
1: Attribute is non-resident (a). */
NI_IndexAllocPresent = NI_NonResident, /* 1: $I30 index alloc attr is
present (d). */
NI_Compressed, /* 1: Unnamed data attr is compressed (f).
1: Create compressed files by default (d).
1: Attribute is compressed (a). */
NI_Encrypted, /* 1: Unnamed data attr is encrypted (f).
1: Create encrypted files by default (d).
1: Attribute is encrypted (a). */
NI_Sparse, /* 1: Unnamed data attr is sparse (f).
1: Create sparse files by default (d).
1: Attribute is sparse (a). */
} ntfs_inode_state_bits;
/*
* NOTE: We should be adding dirty mft records to a list somewhere and they
* should be independent of the (ntfs/vfs) inode structure so that an inode can
* be removed but the record can be left dirty for syncing later.
*/
/*
* Macro tricks to expand the NInoFoo(), NInoSetFoo(), and NInoClearFoo()
* functions.
*/
#define NINO_FNS(flag) \
static inline int NIno##flag(ntfs_inode *ni) \
{ \
return test_bit(NI_##flag, &(ni)->state); \
} \
static inline void NInoSet##flag(ntfs_inode *ni) \
{ \
set_bit(NI_##flag, &(ni)->state); \
} \
static inline void NInoClear##flag(ntfs_inode *ni) \
{ \
clear_bit(NI_##flag, &(ni)->state); \
}
/* Emit the ntfs inode bitops functions. */
NINO_FNS(Dirty)
NINO_FNS(AttrList)
NINO_FNS(AttrListNonResident)
NINO_FNS(Attr)
NINO_FNS(MstProtected)
NINO_FNS(NonResident)
NINO_FNS(IndexAllocPresent)
NINO_FNS(Compressed)
NINO_FNS(Encrypted)
NINO_FNS(Sparse)
/*
* The full structure containing a ntfs_inode and a vfs struct inode. Used for
* all real and fake inodes but not for extent inodes which lack the vfs struct
* inode.
*/
typedef struct {
ntfs_inode ntfs_inode;
struct inode vfs_inode; /* The vfs inode structure. */
} big_ntfs_inode;
/**
* NTFS_I - return the ntfs inode given a vfs inode
* @inode: VFS inode
*
* NTFS_I() returns the ntfs inode associated with the VFS @inode.
*/
static inline ntfs_inode *NTFS_I(struct inode *inode)
{
return (ntfs_inode *)list_entry(inode, big_ntfs_inode, vfs_inode);
}
static inline struct inode *VFS_I(ntfs_inode *ni)
{
return &((big_ntfs_inode *)ni)->vfs_inode;
}
extern struct inode *ntfs_iget(struct super_block *sb, unsigned long mft_no);
extern struct inode *ntfs_attr_iget(struct inode *base_vi, ATTR_TYPES type,
uchar_t *name, u32 name_len);
extern struct inode *ntfs_alloc_big_inode(struct super_block *sb);
extern void ntfs_destroy_big_inode(struct inode *inode);
extern void ntfs_clear_big_inode(struct inode *vi);
extern ntfs_inode *ntfs_new_extent_inode(struct super_block *sb,
unsigned long mft_no);
extern void ntfs_clear_extent_inode(ntfs_inode *ni);
extern void ntfs_read_inode_mount(struct inode *vi);
extern void ntfs_dirty_inode(struct inode *vi);
extern void ntfs_put_inode(struct inode *vi);
extern int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt);
#ifdef NTFS_RW
extern void ntfs_truncate(struct inode *vi);
extern int ntfs_setattr(struct dentry *dentry, struct iattr *attr);
#endif
#endif /* _LINUX_NTFS_FS_INODE_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,64 @@
/*
* malloc.h - NTFS kernel memory handling. Part of the Linux-NTFS project.
*
* Copyright (c) 2001,2002 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_MALLOC_H
#define _LINUX_NTFS_MALLOC_H
#include <linux/vmalloc.h>
#include <linux/slab.h>
/**
* ntfs_malloc_nofs - allocate memory in multiples of pages
* @size number of bytes to allocate
*
* Allocates @size bytes of memory, rounded up to multiples of PAGE_SIZE and
* returns a pointer to the allocated memory.
*
* If there was insufficient memory to complete the request, return NULL.
*/
static inline void *ntfs_malloc_nofs(unsigned long size)
{
if (likely(size <= PAGE_SIZE)) {
if (likely(size)) {
/* kmalloc() has per-CPU caches so if faster for now. */
return kmalloc(PAGE_SIZE, GFP_NOFS);
/* return (void *)__get_free_page(GFP_NOFS |
__GFP_HIGHMEM); */
}
BUG();
}
if (likely(size >> PAGE_SHIFT < num_physpages))
return __vmalloc(size, GFP_NOFS | __GFP_HIGHMEM, PAGE_KERNEL);
return NULL;
}
static inline void ntfs_free(void *addr)
{
if (likely(((unsigned long)addr < VMALLOC_START) ||
((unsigned long)addr >= VMALLOC_END ))) {
return kfree(addr);
/* return free_page((unsigned long)addr); */
}
vfree(addr);
}
#endif /* _LINUX_NTFS_MALLOC_H */

View file

@ -0,0 +1,432 @@
/**
* mft.c - NTFS kernel mft record operations. Part of the Linux-NTFS project.
*
* Copyright (c) 2001-2003 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program/include file is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the Linux-NTFS
* distribution in the file COPYING); if not, write to the Free Software
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/swap.h>
#include "ntfs.h"
/**
* __format_mft_record - initialize an empty mft record
* @m: mapped, pinned and locked for writing mft record
* @size: size of the mft record
* @rec_no: mft record number / inode number
*
* Private function to initialize an empty mft record. Use one of the two
* provided format_mft_record() functions instead.
*/
static void __format_mft_record(MFT_RECORD *m, const int size,
const unsigned long rec_no)
{
ATTR_RECORD *a;
memset(m, 0, size);
m->magic = magic_FILE;
/* Aligned to 2-byte boundary. */
m->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD) + 1) & ~1);
m->usa_count = cpu_to_le16(size / NTFS_BLOCK_SIZE + 1);
/* Set the update sequence number to 1. */
*(u16*)((char*)m + ((sizeof(MFT_RECORD) + 1) & ~1)) = cpu_to_le16(1);
m->lsn = cpu_to_le64(0LL);
m->sequence_number = cpu_to_le16(1);
m->link_count = cpu_to_le16(0);
/* Aligned to 8-byte boundary. */
m->attrs_offset = cpu_to_le16((le16_to_cpu(m->usa_ofs) +
(le16_to_cpu(m->usa_count) << 1) + 7) & ~7);
m->flags = cpu_to_le16(0);
/*
* Using attrs_offset plus eight bytes (for the termination attribute),
* aligned to 8-byte boundary.
*/
m->bytes_in_use = cpu_to_le32((le16_to_cpu(m->attrs_offset) + 8 + 7) &
~7);
m->bytes_allocated = cpu_to_le32(size);
m->base_mft_record = cpu_to_le64((MFT_REF)0);
m->next_attr_instance = cpu_to_le16(0);
a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset));
a->type = AT_END;
a->length = cpu_to_le32(0);
}
/**
* format_mft_record - initialize an empty mft record
* @ni: ntfs inode of mft record
* @mft_rec: mapped, pinned and locked mft record (optional)
*
* Initialize an empty mft record. This is used when extending the MFT.
*
* If @mft_rec is NULL, we call map_mft_record() to obtain the
* record and we unmap it again when finished.
*
* We return 0 on success or -errno on error.
*/
int format_mft_record(ntfs_inode *ni, MFT_RECORD *mft_rec)
{
MFT_RECORD *m;
if (mft_rec)
m = mft_rec;
else {
m = map_mft_record(ni);
if (IS_ERR(m))
return PTR_ERR(m);
}
__format_mft_record(m, ni->vol->mft_record_size, ni->mft_no);
if (!mft_rec) {
// FIXME: Need to set the mft record dirty!
unmap_mft_record(ni);
}
return 0;
}
/**
* ntfs_readpage - external declaration, function is in fs/ntfs/aops.c
*/
extern int ntfs_readpage(struct file *, struct page *);
/**
* ntfs_mft_aops - address space operations for access to $MFT
*
* Address space operations for access to $MFT. This allows us to simply use
* ntfs_map_page() in map_mft_record_page().
*/
struct address_space_operations ntfs_mft_aops = {
.readpage = ntfs_readpage, /* Fill page with data. */
.sync_page = block_sync_page, /* Currently, just unplugs the
disk request queue. */
};
/**
* map_mft_record_page - map the page in which a specific mft record resides
* @ni: ntfs inode whose mft record page to map
*
* This maps the page in which the mft record of the ntfs inode @ni is situated
* and returns a pointer to the mft record within the mapped page.
*
* Return value needs to be checked with IS_ERR() and if that is true PTR_ERR()
* contains the negative error code returned.
*/
static inline MFT_RECORD *map_mft_record_page(ntfs_inode *ni)
{
ntfs_volume *vol = ni->vol;
struct inode *mft_vi = vol->mft_ino;
struct page *page;
unsigned long index, ofs, end_index;
BUG_ON(ni->page);
/*
* The index into the page cache and the offset within the page cache
* page of the wanted mft record. FIXME: We need to check for
* overflowing the unsigned long, but I don't think we would ever get
* here if the volume was that big...
*/
index = ni->mft_no << vol->mft_record_size_bits >> PAGE_CACHE_SHIFT;
ofs = (ni->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;
/* 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) {
page = ERR_PTR(-ENOENT);
goto err_out;
}
}
/* Read, map, and pin the page. */
page = ntfs_map_page(mft_vi->i_mapping, index);
if (likely(!IS_ERR(page))) {
ni->page = page;
ni->page_ofs = ofs;
return page_address(page) + ofs;
}
err_out:
ni->page = NULL;
ni->page_ofs = 0;
ntfs_error(vol->sb, "Failed with error code %lu.", -PTR_ERR(page));
return (void*)page;
}
/**
* map_mft_record - map, pin and lock an mft record
* @ni: ntfs inode whose MFT record to map
*
* First, take the mrec_lock semaphore. We might now be sleeping, while waiting
* for the semaphore if it was already locked by someone else.
*
* The page of the record is mapped using map_mft_record_page() before being
* returned to the caller.
*
* This in turn uses ntfs_map_page() to get the page containing the wanted mft
* record (it in turn calls read_cache_page() which reads it in from disk if
* necessary, increments the use count on the page so that it cannot disappear
* under us and returns a reference to the page cache page).
*
* If read_cache_page() invokes ntfs_readpage() to load the page from disk, it
* sets PG_locked and clears PG_uptodate on the page. Once I/O has completed
* and the post-read mst fixups on each mft record in the page have been
* performed, the page gets PG_uptodate set and PG_locked cleared (this is done
* in our asynchronous I/O completion handler end_buffer_read_mft_async()).
* ntfs_map_page() waits for PG_locked to become clear and checks if
* PG_uptodate is set and returns an error code if not. This provides
* sufficient protection against races when reading/using the page.
*
* However there is the write mapping to think about. Doing the above described
* checking here will be fine, because when initiating the write we will set
* PG_locked and clear PG_uptodate making sure nobody is touching the page
* contents. Doing the locking this way means that the commit to disk code in
* the page cache code paths is automatically sufficiently locked with us as
* we will not touch a page that has been locked or is not uptodate. The only
* locking problem then is them locking the page while we are accessing it.
*
* So that code will end up having to own the mrec_lock of all mft
* records/inodes present in the page before I/O can proceed. In that case we
* wouldn't need to bother with PG_locked and PG_uptodate as nobody will be
* accessing anything without owning the mrec_lock semaphore. But we do need
* to use them because of the read_cache_page() invocation and the code becomes
* so much simpler this way that it is well worth it.
*
* The mft record is now ours and we return a pointer to it. You need to check
* the returned pointer with IS_ERR() and if that is true, PTR_ERR() will return
* the error code.
*
* NOTE: Caller is responsible for setting the mft record dirty before calling
* unmap_mft_record(). This is obviously only necessary if the caller really
* modified the mft record...
* Q: Do we want to recycle one of the VFS inode state bits instead?
* A: No, the inode ones mean we want to change the mft record, not we want to
* write it out.
*/
MFT_RECORD *map_mft_record(ntfs_inode *ni)
{
MFT_RECORD *m;
ntfs_debug("Entering for mft_no 0x%lx.", ni->mft_no);
/* Make sure the ntfs inode doesn't go away. */
atomic_inc(&ni->count);
/* Serialize access to this mft record. */
down(&ni->mrec_lock);
m = map_mft_record_page(ni);
if (likely(!IS_ERR(m)))
return m;
up(&ni->mrec_lock);
atomic_dec(&ni->count);
ntfs_error(ni->vol->sb, "Failed with error code %lu.", -PTR_ERR(m));
return m;
}
/**
* unmap_mft_record_page - unmap the page in which a specific mft record resides
* @ni: ntfs inode whose mft record page to unmap
*
* This unmaps the page in which the mft record of the ntfs inode @ni is
* situated and returns. This is a NOOP if highmem is not configured.
*
* The unmap happens via ntfs_unmap_page() which in turn decrements the use
* count on the page thus releasing it from the pinned state.
*
* We do not actually unmap the page from memory of course, as that will be
* done by the page cache code itself when memory pressure increases or
* whatever.
*/
static inline void unmap_mft_record_page(ntfs_inode *ni)
{
BUG_ON(!ni->page);
// TODO: If dirty, blah...
ntfs_unmap_page(ni->page);
ni->page = NULL;
ni->page_ofs = 0;
return;
}
/**
* unmap_mft_record - release a mapped mft record
* @ni: ntfs inode whose MFT record to unmap
*
* We release the page mapping and the mrec_lock mutex which unmaps the mft
* record and releases it for others to get hold of. We also release the ntfs
* inode by decrementing the ntfs inode reference count.
*
* NOTE: If caller has modified the mft record, it is imperative to set the mft
* record dirty BEFORE calling unmap_mft_record().
*/
void unmap_mft_record(ntfs_inode *ni)
{
struct page *page = ni->page;
BUG_ON(!page);
ntfs_debug("Entering for mft_no 0x%lx.", ni->mft_no);
unmap_mft_record_page(ni);
up(&ni->mrec_lock);
atomic_dec(&ni->count);
/*
* If pure ntfs_inode, i.e. no vfs inode attached, we leave it to
* ntfs_clear_extent_inode() in the extent inode case, and to the
* caller in the non-extent, yet pure ntfs inode case, to do the actual
* tear down of all structures and freeing of all allocated memory.
*/
return;
}
/**
* map_extent_mft_record - load an extent inode and attach it to its base
* @base_ni: base ntfs inode
* @mref: mft reference of the extent inode to load (in little endian)
* @ntfs_ino: on successful return, pointer to the ntfs_inode structure
*
* Load the extent mft record @mref and attach it to its base inode @base_ni.
* Return the mapped extent mft record if IS_ERR(result) is false. Otherwise
* PTR_ERR(result) gives the negative error code.
*
* On successful return, @ntfs_ino contains a pointer to the ntfs_inode
* structure of the mapped extent inode.
*/
MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref,
ntfs_inode **ntfs_ino)
{
MFT_RECORD *m;
ntfs_inode *ni = NULL;
ntfs_inode **extent_nis = NULL;
int i;
unsigned long mft_no = MREF_LE(mref);
u16 seq_no = MSEQNO_LE(mref);
BOOL destroy_ni = FALSE;
ntfs_debug("Mapping extent mft record 0x%lx (base mft record 0x%lx).",
mft_no, base_ni->mft_no);
/* Make sure the base ntfs inode doesn't go away. */
atomic_inc(&base_ni->count);
/*
* Check if this extent inode has already been added to the base inode,
* in which case just return it. If not found, add it to the base
* inode before returning it.
*/
down(&base_ni->extent_lock);
if (base_ni->nr_extents > 0) {
extent_nis = base_ni->ext.extent_ntfs_inos;
for (i = 0; i < base_ni->nr_extents; i++) {
if (mft_no != extent_nis[i]->mft_no)
continue;
ni = extent_nis[i];
/* Make sure the ntfs inode doesn't go away. */
atomic_inc(&ni->count);
break;
}
}
if (likely(ni != NULL)) {
up(&base_ni->extent_lock);
atomic_dec(&base_ni->count);
/* We found the record; just have to map and return it. */
m = map_mft_record(ni);
/* map_mft_record() has incremented this on success. */
atomic_dec(&ni->count);
if (likely(!IS_ERR(m))) {
/* Verify the sequence number. */
if (likely(le16_to_cpu(m->sequence_number) == seq_no)) {
ntfs_debug("Done 1.");
*ntfs_ino = ni;
return m;
}
unmap_mft_record(ni);
ntfs_error(base_ni->vol->sb, "Found stale extent mft "
"reference! Corrupt file system. "
"Run chkdsk.");
return ERR_PTR(-EIO);
}
map_err_out:
ntfs_error(base_ni->vol->sb, "Failed to map extent "
"mft record, error code %ld.", -PTR_ERR(m));
return m;
}
/* Record wasn't there. Get a new ntfs inode and initialize it. */
ni = ntfs_new_extent_inode(base_ni->vol->sb, mft_no);
if (unlikely(!ni)) {
up(&base_ni->extent_lock);
atomic_dec(&base_ni->count);
return ERR_PTR(-ENOMEM);
}
ni->vol = base_ni->vol;
ni->seq_no = seq_no;
ni->nr_extents = -1;
ni->ext.base_ntfs_ino = base_ni;
/* Now map the record. */
m = map_mft_record(ni);
if (unlikely(IS_ERR(m))) {
up(&base_ni->extent_lock);
atomic_dec(&base_ni->count);
ntfs_clear_extent_inode(ni);
goto map_err_out;
}
/* Verify the sequence number. */
if (unlikely(le16_to_cpu(m->sequence_number) != seq_no)) {
ntfs_error(base_ni->vol->sb, "Found stale extent mft "
"reference! Corrupt file system. Run chkdsk.");
destroy_ni = TRUE;
m = ERR_PTR(-EIO);
goto unm_err_out;
}
/* Attach extent inode to base inode, reallocating memory if needed. */
if (!(base_ni->nr_extents & 3)) {
ntfs_inode **tmp;
int new_size = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *);
tmp = (ntfs_inode **)kmalloc(new_size, GFP_NOFS);
if (unlikely(!tmp)) {
ntfs_error(base_ni->vol->sb, "Failed to allocate "
"internal buffer.");
destroy_ni = TRUE;
m = ERR_PTR(-ENOMEM);
goto unm_err_out;
}
if (base_ni->ext.extent_ntfs_inos) {
memcpy(tmp, base_ni->ext.extent_ntfs_inos, new_size -
4 * sizeof(ntfs_inode *));
kfree(base_ni->ext.extent_ntfs_inos);
}
base_ni->ext.extent_ntfs_inos = tmp;
}
base_ni->ext.extent_ntfs_inos[base_ni->nr_extents++] = ni;
up(&base_ni->extent_lock);
atomic_dec(&base_ni->count);
ntfs_debug("Done 2.");
*ntfs_ino = ni;
return m;
unm_err_out:
unmap_mft_record(ni);
up(&base_ni->extent_lock);
atomic_dec(&base_ni->count);
/*
* If the extent inode was not attached to the base inode we need to
* release it or we will leak memory.
*/
if (destroy_ni)
ntfs_clear_extent_inode(ni);
return m;
}

View file

@ -0,0 +1,61 @@
/*
* mft.h - Defines for mft record handling in NTFS Linux kernel driver.
* Part of the Linux-NTFS project.
*
* Copyright (c) 2001,2002 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_MFT_H
#define _LINUX_NTFS_MFT_H
#include <linux/fs.h>
#include "inode.h"
extern int format_mft_record(ntfs_inode *ni, MFT_RECORD *m);
//extern int format_mft_record2(struct super_block *vfs_sb,
// const unsigned long inum, MFT_RECORD *m);
extern MFT_RECORD *map_mft_record(ntfs_inode *ni);
extern void unmap_mft_record(ntfs_inode *ni);
extern MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref,
ntfs_inode **ntfs_ino);
static inline void unmap_extent_mft_record(ntfs_inode *ni)
{
unmap_mft_record(ni);
return;
}
/*
* flush_dcache_mft_record_page - flush_dcache_page() for mft records
* @ni: ntfs inode structure of mft record
*
* Call flush_dcache_page() for the page in which an mft record resides.
*
* This must be called every time an mft record is modified, just after the
* modification.
*/
static inline void flush_dcache_mft_record_page(ntfs_inode *ni)
{
flush_dcache_page(ni->page);
}
#endif /* _LINUX_NTFS_MFT_H */

View file

@ -0,0 +1,202 @@
/*
* mst.c - NTFS multi sector transfer protection handling code. Part of the
* Linux-NTFS project.
*
* Copyright (c) 2001 Anton Altaparmakov.
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program/include file is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the Linux-NTFS
* distribution in the file COPYING); if not, write to the Free Software
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ntfs.h"
/**
* post_read_mst_fixup - deprotect multi sector transfer protected data
* @b: pointer to the data to deprotect
* @size: size in bytes of @b
*
* Perform the necessary post read multi sector transfer fixup and detect the
* presence of incomplete multi sector transfers. - In that case, overwrite the
* magic of the ntfs record header being processed with "BAAD" (in memory only!)
* and abort processing.
*
* Return 0 on success and -EINVAL on error ("BAAD" magic will be present).
*
* NOTE: We consider the absence / invalidity of an update sequence array to
* mean that the structure is not protected at all and hence doesn't need to
* be fixed up. Thus, we return success and not failure in this case. This is
* in contrast to pre_write_mst_fixup(), see below.
*/
int post_read_mst_fixup(NTFS_RECORD *b, const u32 size)
{
u16 usa_ofs, usa_count, usn;
u16 *usa_pos, *data_pos;
/* Setup the variables. */
usa_ofs = le16_to_cpu(b->usa_ofs);
/* Decrement usa_count to get number of fixups. */
usa_count = le16_to_cpu(b->usa_count) - 1;
/* Size and alignment checks. */
if ( size & (NTFS_BLOCK_SIZE - 1) ||
usa_ofs & 1 ||
usa_ofs + (usa_count * 2) > size ||
(size >> NTFS_BLOCK_SIZE_BITS) != usa_count)
return 0;
/* Position of usn in update sequence array. */
usa_pos = (u16*)b + usa_ofs/sizeof(u16);
/*
* The update sequence number which has to be equal to each of the
* u16 values before they are fixed up. Note no need to care for
* endianness since we are comparing and moving data for on disk
* structures which means the data is consistent. - If it is
* consistenty the wrong endianness it doesn't make any difference.
*/
usn = *usa_pos;
/*
* Position in protected data of first u16 that needs fixing up.
*/
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
/*
* Check for incomplete multi sector transfer(s).
*/
while (usa_count--) {
if (*data_pos != usn) {
/*
* Incomplete multi sector transfer detected! )-:
* Set the magic to "BAAD" and return failure.
* Note that magic_BAAD is already converted to le32.
*/
b->magic = magic_BAAD;
return -EINVAL;
}
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
}
/* Re-setup the variables. */
usa_count = le16_to_cpu(b->usa_count) - 1;
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
/* Fixup all sectors. */
while (usa_count--) {
/*
* Increment position in usa and restore original data from
* the usa into the data buffer.
*/
*data_pos = *(++usa_pos);
/* Increment position in data as well. */
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
}
return 0;
}
/**
* pre_write_mst_fixup - apply multi sector transfer protection
* @b: pointer to the data to protect
* @size: size in bytes of @b
*
* Perform the necessary pre write multi sector transfer fixup on the data
* pointer to by @b of @size.
*
* Return 0 if fixup applied (success) or -EINVAL if no fixup was performed
* (assumed not needed). This is in contrast to post_read_mst_fixup() above.
*
* NOTE: We consider the absence / invalidity of an update sequence array to
* mean that the structure is not subject to protection and hence doesn't need
* to be fixed up. This means that you have to create a valid update sequence
* array header in the ntfs record before calling this function, otherwise it
* will fail (the header needs to contain the position of the update sequence
* array together with the number of elements in the array). You also need to
* initialise the update sequence number before calling this function
* otherwise a random word will be used (whatever was in the record at that
* position at that time).
*/
int pre_write_mst_fixup(NTFS_RECORD *b, const u32 size)
{
u16 usa_ofs, usa_count, usn;
u16 *usa_pos, *data_pos;
/* Sanity check + only fixup if it makes sense. */
if (!b || is_baad_record(b->magic) || is_hole_record(b->magic))
return -EINVAL;
/* Setup the variables. */
usa_ofs = le16_to_cpu(b->usa_ofs);
/* Decrement usa_count to get number of fixups. */
usa_count = le16_to_cpu(b->usa_count) - 1;
/* Size and alignment checks. */
if ( size & (NTFS_BLOCK_SIZE - 1) ||
usa_ofs & 1 ||
usa_ofs + (usa_count * 2) > size ||
(size >> NTFS_BLOCK_SIZE_BITS) != usa_count)
return -EINVAL;
/* Position of usn in update sequence array. */
usa_pos = (u16*)((u8*)b + usa_ofs);
/*
* Cyclically increment the update sequence number
* (skipping 0 and -1, i.e. 0xffff).
*/
usn = le16_to_cpup(usa_pos) + 1;
if (usn == 0xffff || !usn)
usn = 1;
usn = cpu_to_le16(usn);
*usa_pos = usn;
/* Position in data of first u16 that needs fixing up. */
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
/* Fixup all sectors. */
while (usa_count--) {
/*
* Increment the position in the usa and save the
* original data from the data buffer into the usa.
*/
*(++usa_pos) = *data_pos;
/* Apply fixup to data. */
*data_pos = usn;
/* Increment position in data as well. */
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
}
return 0;
}
/**
* post_write_mst_fixup - fast deprotect multi sector transfer protected data
* @b: pointer to the data to deprotect
*
* Perform the necessary post write multi sector transfer fixup, not checking
* for any errors, because we assume we have just used pre_write_mst_fixup(),
* thus the data will be fine or we would never have gotten here.
*/
void post_write_mst_fixup(NTFS_RECORD *b)
{
u16 *usa_pos, *data_pos;
u16 usa_ofs = le16_to_cpu(b->usa_ofs);
u16 usa_count = le16_to_cpu(b->usa_count) - 1;
/* Position of usn in update sequence array. */
usa_pos = (u16*)b + usa_ofs/sizeof(u16);
/* Position in protected data of first u16 that needs fixing up. */
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
/* Fixup all sectors. */
while (usa_count--) {
/*
* Increment position in usa and restore original data from
* the usa into the data buffer.
*/
*data_pos = *(++usa_pos);
/* Increment position in data as well. */
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
}
}

View file

@ -0,0 +1,297 @@
/*
* namei.c - NTFS kernel directory inode operations. Part of the Linux-NTFS
* project.
*
* Copyright (c) 2001-2003 Anton Altaparmakov
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program/include file is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the Linux-NTFS
* distribution in the file COPYING); if not, write to the Free Software
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/dcache.h>
#include "ntfs.h"
#include "dir.h"
/**
* ntfs_lookup - find the inode represented by a dentry in a directory inode
* @dir_ino: directory inode in which to look for the inode
* @dent: dentry representing the inode to look for
* @nd: lookup nameidata
*
* In short, ntfs_lookup() looks for the inode represented by the dentry @dent
* in the directory inode @dir_ino and if found attaches the inode to the
* dentry @dent.
*
* In more detail, the dentry @dent specifies which inode to look for by
* supplying the name of the inode in @dent->d_name.name. ntfs_lookup()
* converts the name to Unicode and walks the contents of the directory inode
* @dir_ino looking for the converted Unicode name. If the name is found in the
* directory, the corresponding inode is loaded by calling ntfs_iget() on its
* inode number and the inode is associated with the dentry @dent via a call to
* d_add().
*
* If the name is not found in the directory, a NULL inode is inserted into the
* dentry @dent. The dentry is then termed a negative dentry.
*
* Only if an actual error occurs, do we return an error via ERR_PTR().
*
* In order to handle the case insensitivity issues of NTFS with regards to the
* dcache and the dcache requiring only one dentry per directory, we deal with
* dentry aliases that only differ in case in ->ntfs_lookup() while maintining
* a case sensitive dcache. This means that we get the full benefit of dcache
* speed when the file/directory is looked up with the same case as returned by
* ->ntfs_readdir() but that a lookup for any other case (or for the short file
* name) will not find anything in dcache and will enter ->ntfs_lookup()
* instead, where we search the directory for a fully matching file name
* (including case) and if that is not found, we search for a file name that
* matches with different case and if that has non-POSIX semantics we return
* that. We actually do only one search (case sensitive) and keep tabs on
* whether we have found a case insensitive match in the process.
*
* To simplify matters for us, we do not treat the short vs long filenames as
* two hard links but instead if the lookup matches a short filename, we
* return the dentry for the corresponding long filename instead.
*
* There are three cases we need to distinguish here:
*
* 1) @dent perfectly matches (i.e. including case) a directory entry with a
* file name in the WIN32 or POSIX namespaces. In this case
* ntfs_lookup_inode_by_name() will return with name set to NULL and we
* just d_add() @dent.
* 2) @dent matches (not including case) a directory entry with a file name in
* the WIN32 namespace. In this case ntfs_lookup_inode_by_name() will return
* with name set to point to a kmalloc()ed ntfs_name structure containing
* the properly cased little endian Unicode name. We convert the name to the
* current NLS code page, search if a dentry with this name already exists
* and if so return that instead of @dent. The VFS will then destroy the old
* @dent and use the one we returned. If a dentry is not found, we allocate
* a new one, d_add() it, and return it as above.
* 3) @dent matches either perfectly or not (i.e. we don't care about case) a
* directory entry with a file name in the DOS namespace. In this case
* ntfs_lookup_inode_by_name() will return with name set to point to a
* kmalloc()ed ntfs_name structure containing the mft reference (cpu endian)
* of the inode. We use the mft reference to read the inode and to find the
* file name in the WIN32 namespace corresponding to the matched short file
* name. We then convert the name to the current NLS code page, and proceed
* searching for a dentry with this name, etc, as in case 2), above.
*/
static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, struct nameidata *nd)
{
ntfs_volume *vol = NTFS_SB(dir_ino->i_sb);
struct inode *dent_inode;
uchar_t *uname;
ntfs_name *name = NULL;
MFT_REF mref;
unsigned long dent_ino;
int uname_len;
ntfs_debug("Looking up %s in directory inode 0x%lx.",
dent->d_name.name, dir_ino->i_ino);
/* Convert the name of the dentry to Unicode. */
uname_len = ntfs_nlstoucs(vol, dent->d_name.name, dent->d_name.len,
&uname);
if (uname_len < 0) {
ntfs_error(vol->sb, "Failed to convert name to Unicode.");
return ERR_PTR(uname_len);
}
mref = ntfs_lookup_inode_by_name(NTFS_I(dir_ino), uname, uname_len,
&name);
kmem_cache_free(ntfs_name_cache, uname);
if (!IS_ERR_MREF(mref)) {
dent_ino = MREF(mref);
ntfs_debug("Found inode 0x%lx. Calling ntfs_iget.", dent_ino);
dent_inode = ntfs_iget(vol->sb, dent_ino);
if (likely(!IS_ERR(dent_inode))) {
/* Consistency check. */
if (MSEQNO(mref) == NTFS_I(dent_inode)->seq_no ||
dent_ino == FILE_MFT) {
/* Perfect WIN32/POSIX match. -- Case 1. */
if (!name) {
d_add(dent, dent_inode);
ntfs_debug("Done.");
return NULL;
}
/*
* We are too indented. Handle imperfect
* matches and short file names further below.
*/
goto handle_name;
}
ntfs_error(vol->sb, "Found stale reference to inode "
"0x%lx (reference sequence number = "
"0x%x, inode sequence number = 0x%x, "
"returning -EIO. Run chkdsk.",
dent_ino, MSEQNO(mref),
NTFS_I(dent_inode)->seq_no);
iput(dent_inode);
dent_inode = ERR_PTR(-EIO);
} else
ntfs_error(vol->sb, "ntfs_iget(0x%lx) failed with "
"error code %li.", dent_ino,
PTR_ERR(dent_inode));
if (name)
kfree(name);
/* Return the error code. */
return (struct dentry *)dent_inode;
}
/* It is guaranteed that name is no longer allocated at this point. */
if (MREF_ERR(mref) == -ENOENT) {
ntfs_debug("Entry was not found, adding negative dentry.");
/* The dcache will handle negative entries. */
d_add(dent, NULL);
ntfs_debug("Done.");
return NULL;
}
ntfs_error(vol->sb, "ntfs_lookup_ino_by_name() failed with error "
"code %i.", -MREF_ERR(mref));
return ERR_PTR(MREF_ERR(mref));
// TODO: Consider moving this lot to a separate function! (AIA)
handle_name:
{
struct dentry *real_dent;
MFT_RECORD *m;
attr_search_context *ctx;
ntfs_inode *ni = NTFS_I(dent_inode);
int err;
struct qstr nls_name;
nls_name.name = NULL;
if (name->type != FILE_NAME_DOS) { /* Case 2. */
nls_name.len = (unsigned)ntfs_ucstonls(vol,
(uchar_t*)&name->name, name->len,
(unsigned char**)&nls_name.name,
name->len * 3 + 1);
kfree(name);
} else /* if (name->type == FILE_NAME_DOS) */ { /* Case 3. */
FILE_NAME_ATTR *fn;
kfree(name);
/* Find the WIN32 name corresponding to the matched DOS name. */
ni = NTFS_I(dent_inode);
m = map_mft_record(ni);
if (IS_ERR(m)) {
err = PTR_ERR(m);
m = NULL;
ctx = NULL;
goto err_out;
}
ctx = get_attr_search_ctx(ni, m);
if (!ctx) {
err = -ENOMEM;
goto err_out;
}
do {
ATTR_RECORD *a;
u32 val_len;
if (!lookup_attr(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0,
ctx)) {
ntfs_error(vol->sb, "Inode corrupt: No WIN32 "
"namespace counterpart to DOS "
"file name. Run chkdsk.");
err = -EIO;
goto err_out;
}
/* Consistency checks. */
a = ctx->attr;
if (a->non_resident || a->flags)
goto eio_err_out;
val_len = le32_to_cpu(a->data.resident.value_length);
if (le16_to_cpu(a->data.resident.value_offset) +
val_len > le32_to_cpu(a->length))
goto eio_err_out;
fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + le16_to_cpu(
ctx->attr->data.resident.value_offset));
if ((u32)(fn->file_name_length * sizeof(uchar_t) +
sizeof(FILE_NAME_ATTR)) > val_len)
goto eio_err_out;
} while (fn->file_name_type != FILE_NAME_WIN32);
/* Convert the found WIN32 name to current NLS code page. */
nls_name.len = (unsigned)ntfs_ucstonls(vol,
(uchar_t*)&fn->file_name, fn->file_name_length,
(unsigned char**)&nls_name.name,
fn->file_name_length * 3 + 1);
put_attr_search_ctx(ctx);
unmap_mft_record(ni);
}
m = NULL;
ctx = NULL;
/* Check if a conversion error occurred. */
if ((signed)nls_name.len < 0) {
err = (signed)nls_name.len;
goto err_out;
}
nls_name.hash = full_name_hash(nls_name.name, nls_name.len);
/*
* Note: No need for dent->d_lock lock as i_sem is held on the
* parent inode.
*/
/* Does a dentry matching the nls_name exist already? */
real_dent = d_lookup(dent->d_parent, &nls_name);
/* If not, create it now. */
if (!real_dent) {
real_dent = d_alloc(dent->d_parent, &nls_name);
kfree(nls_name.name);
if (!real_dent) {
err = -ENOMEM;
goto err_out;
}
d_add(real_dent, dent_inode);
return real_dent;
}
kfree(nls_name.name);
/* Matching dentry exists, check if it is negative. */
if (real_dent->d_inode) {
BUG_ON(real_dent->d_inode != dent_inode);
/*
* Already have the inode and the dentry attached, decrement
* the reference count to balance the ntfs_iget() we did
* earlier on.
*/
iput(dent_inode);
return real_dent;
}
/* Negative dentry: instantiate it. */
d_instantiate(real_dent, dent_inode);
return real_dent;
eio_err_out:
ntfs_error(vol->sb, "Illegal file name attribute. Run chkdsk.");
err = -EIO;
err_out:
if (ctx)
put_attr_search_ctx(ctx);
if (m)
unmap_mft_record(ni);
iput(dent_inode);
return ERR_PTR(err);
}
}
/*
* Inode operations for directories.
*/
struct inode_operations ntfs_dir_inode_ops = {
.lookup = ntfs_lookup, /* VFS: Lookup directory. */
};

View file

@ -0,0 +1,217 @@
/*
* ntfs.h - Defines for NTFS Linux kernel driver. Part of the Linux-NTFS
* project.
*
* Copyright (c) 2001,2002 Anton Altaparmakov.
* Copyright (C) 2002 Richard Russon.
*
* 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_H
#define _LINUX_NTFS_H
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/compiler.h>
#include <linux/fs.h>
#include <linux/buffer_head.h>
#include <linux/nls.h>
#include <linux/pagemap.h>
#include <linux/smp.h>
#include <asm/atomic.h>
#include "types.h"
#include "debug.h"
#include "malloc.h"
#include "endian.h"
#include "volume.h"
#include "inode.h"
#include "layout.h"
#include "attrib.h"
#include "mft.h"
typedef enum {
NTFS_BLOCK_SIZE = 512,
NTFS_BLOCK_SIZE_BITS = 9,
NTFS_SB_MAGIC = 0x5346544e, /* 'NTFS' */
NTFS_MAX_NAME_LEN = 255,
} NTFS_CONSTANTS;
/* Global variables. */
/* Slab caches (from super.c). */
extern kmem_cache_t *ntfs_name_cache;
extern kmem_cache_t *ntfs_inode_cache;
extern kmem_cache_t *ntfs_big_inode_cache;
extern kmem_cache_t *ntfs_attr_ctx_cache;
/* The various operations structs defined throughout the driver files. */
extern struct super_operations ntfs_sops;
extern struct super_operations ntfs_mount_sops;
extern struct address_space_operations ntfs_aops;
extern struct address_space_operations ntfs_mft_aops;
extern struct file_operations ntfs_file_ops;
extern struct inode_operations ntfs_file_inode_ops;
extern struct file_operations ntfs_dir_ops;
extern struct inode_operations ntfs_dir_inode_ops;
extern struct file_operations ntfs_empty_file_ops;
extern struct inode_operations ntfs_empty_inode_ops;
/* Generic macro to convert pointers to values for comparison purposes. */
#ifndef p2n
#define p2n(p) ((ptrdiff_t)((ptrdiff_t*)(p)))
#endif
/**
* NTFS_SB - return the ntfs volume given a vfs super block
* @sb: VFS super block
*
* NTFS_SB() returns the ntfs volume associated with the VFS super block @sb.
*/
static inline ntfs_volume *NTFS_SB(struct super_block *sb)
{
return sb->s_fs_info;
}
/**
* ntfs_unmap_page - release a page that was mapped using ntfs_map_page()
* @page: the page to release
*
* Unpin, unmap and release a page that was obtained from ntfs_map_page().
*/
static inline void ntfs_unmap_page(struct page *page)
{
kunmap(page);
page_cache_release(page);
}
/**
* ntfs_map_page - map a page into accessible memory, reading it if necessary
* @mapping: address space for which to obtain the page
* @index: index into the page cache for @mapping of the page to map
*
* Read a page from the page cache of the address space @mapping at position
* @index, where @index is in units of PAGE_CACHE_SIZE, and not in bytes.
*
* If the page is not in memory it is loaded from disk first using the readpage
* method defined in the address space operations of @mapping and the page is
* added to the page cache of @mapping in the process.
*
* If the page is in high memory it is mapped into memory directly addressible
* by the kernel.
*
* Finally the page count is incremented, thus pinning the page into place.
*
* The above means that page_address(page) can be used on all pages obtained
* with ntfs_map_page() to get the kernel virtual address of the page.
*
* When finished with the page, the caller has to call ntfs_unmap_page() to
* unpin, unmap and release the page.
*
* Note this does not grant exclusive access. If such is desired, the caller
* must provide it independently of the ntfs_{un}map_page() calls by using
* a {rw_}semaphore or other means of serialization. A spin lock cannot be
* used as ntfs_map_page() can block.
*
* The unlocked and uptodate page is returned on success or an encoded error
* on failure. Caller has to test for error using the IS_ERR() macro on the
* return value. If that evaluates to TRUE, the negative error code can be
* obtained using PTR_ERR() on the return value of ntfs_map_page().
*/
static inline struct page *ntfs_map_page(struct address_space *mapping,
unsigned long index)
{
struct page *page = read_cache_page(mapping, index,
(filler_t*)mapping->a_ops->readpage, NULL);
if (!IS_ERR(page)) {
wait_on_page_locked(page);
kmap(page);
if (PageUptodate(page) && !PageError(page))
return page;
ntfs_unmap_page(page);
return ERR_PTR(-EIO);
}
return page;
}
/* Declarations of functions and global variables. */
/* From fs/ntfs/compress.c */
extern int ntfs_read_compressed_block(struct page *page);
/* From fs/ntfs/super.c */
#define default_upcase_len 0x10000
extern wchar_t *default_upcase;
extern unsigned long ntfs_nr_upcase_users;
extern unsigned long ntfs_nr_mounts;
extern struct semaphore ntfs_lock;
typedef struct {
int val;
char *str;
} option_t;
extern const option_t on_errors_arr[];
/* From fs/ntfs/compress.c */
extern int allocate_compression_buffers(void);
extern void free_compression_buffers(void);
/* From fs/ntfs/mst.c */
extern int post_read_mst_fixup(NTFS_RECORD *b, const u32 size);
extern int pre_write_mst_fixup(NTFS_RECORD *b, const u32 size);
extern void post_write_mst_fixup(NTFS_RECORD *b);
/* From fs/ntfs/time.c */
extern inline s64 utc2ntfs(const time_t time);
extern inline s64 get_current_ntfs_time(void);
extern inline time_t ntfs2utc(const s64 time);
/* From fs/ntfs/unistr.c */
extern BOOL ntfs_are_names_equal(const uchar_t *s1, size_t s1_len,
const uchar_t *s2, size_t s2_len,
const IGNORE_CASE_BOOL ic,
const uchar_t *upcase, const u32 upcase_size);
extern int ntfs_collate_names(const uchar_t *name1, const u32 name1_len,
const uchar_t *name2, const u32 name2_len,
const int err_val, const IGNORE_CASE_BOOL ic,
const uchar_t *upcase, const u32 upcase_len);
extern int ntfs_ucsncmp(const uchar_t *s1, const uchar_t *s2, size_t n);
extern int ntfs_ucsncasecmp(const uchar_t *s1, const uchar_t *s2, size_t n,
const uchar_t *upcase, const u32 upcase_size);
extern void ntfs_upcase_name(uchar_t *name, u32 name_len,
const uchar_t *upcase, const u32 upcase_len);
extern void ntfs_file_upcase_value(FILE_NAME_ATTR *file_name_attr,
const uchar_t *upcase, const u32 upcase_len);
extern int ntfs_file_compare_values(FILE_NAME_ATTR *file_name_attr1,
FILE_NAME_ATTR *file_name_attr2,
const int err_val, const IGNORE_CASE_BOOL ic,
const uchar_t *upcase, const u32 upcase_len);
extern int ntfs_nlstoucs(const ntfs_volume *vol, const char *ins,
const int ins_len, uchar_t **outs);
extern int ntfs_ucstonls(const ntfs_volume *vol, const uchar_t *ins,
const int ins_len, unsigned char **outs, int outs_len);
/* From fs/ntfs/upcase.c */
extern uchar_t *generate_default_upcase(void);
#endif /* _LINUX_NTFS_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,86 @@
/*
* sysctl.c - Code for sysctl handling in NTFS Linux kernel driver. Part of
* the Linux-NTFS project. Adapted from the old NTFS driver,
* Copyright (C) 1997 Martin von Löwis, Régis Duchesne.
*
* Copyright (c) 2002 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 DEBUG
#include <linux/module.h>
#ifdef CONFIG_SYSCTL
#include <linux/proc_fs.h>
#include <linux/sysctl.h>
#include "sysctl.h"
#include "debug.h"
#define FS_NTFS 1
/* Definition of the ntfs sysctl. */
static ctl_table ntfs_sysctls[] = {
{ FS_NTFS, "ntfs-debug", /* Binary and text IDs. */
&debug_msgs,sizeof(debug_msgs), /* Data pointer and size. */
0644, NULL, &proc_dointvec }, /* Mode, child, proc handler. */
{ 0 }
};
/* Define the parent directory /proc/sys/fs. */
static ctl_table sysctls_root[] = {
{ CTL_FS, "fs", NULL, 0, 0555, ntfs_sysctls },
{ 0 }
};
/* Storage for the sysctls header. */
static struct ctl_table_header *sysctls_root_table = NULL;
/**
* ntfs_sysctl - add or remove the debug sysctl
* @add: add (1) or remove (0) the sysctl
*
* Add or remove the debug sysctl. Return 0 on success or -errno on error.
*/
int ntfs_sysctl(int add)
{
if (add) {
BUG_ON(sysctls_root_table);
sysctls_root_table = register_sysctl_table(sysctls_root, 0);
if (!sysctls_root_table)
return -ENOMEM;
#ifdef CONFIG_PROC_FS
/*
* If the proc file system 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.
*/
ntfs_sysctls[0].de->owner = THIS_MODULE;
#endif
} else {
BUG_ON(!sysctls_root_table);
unregister_sysctl_table(sysctls_root_table);
sysctls_root_table = NULL;
}
return 0;
}
#endif /* CONFIG_SYSCTL */
#endif /* DEBUG */

View file

@ -0,0 +1,43 @@
/*
* sysctl.h - Defines for sysctl handling in NTFS Linux kernel driver. Part of
* the Linux-NTFS project. Adapted from the old NTFS driver,
* Copyright (C) 1997 Martin von Löwis, Régis Duchesne.
*
* Copyright (c) 2002 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_SYSCTL_H
#define _LINUX_NTFS_SYSCTL_H
#include <linux/config.h>
#if (DEBUG && CONFIG_SYSCTL)
extern int ntfs_sysctl(int add);
#else
/* Just return success. */
static inline int ntfs_sysctl(int add)
{
return 0;
}
#endif /* DEBUG && CONFIG_SYSCTL */
#endif /* _LINUX_NTFS_SYSCTL_H */

View file

@ -0,0 +1,82 @@
/*
* time.c - NTFS time conversion functions. Part of the Linux-NTFS project.
*
* Copyright (c) 2001 Anton Altaparmakov.
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program/include file is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the Linux-NTFS
* distribution in the file COPYING); if not, write to the Free Software
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/sched.h> /* For CURRENT_TIME. */
#include <asm/div64.h> /* For do_div(). */
#include "ntfs.h"
#define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000)
/**
* utc2ntfs - convert Linux time to NTFS time
* @time: Linux time to convert to NTFS
*
* Convert the Linux time @time to its corresponding NTFS time and return that
* in little endian format.
*
* Linux stores time in a long at present and measures it as the number of
* 1-second intervals since 1st January 1970, 00:00:00 UTC.
*
* NTFS uses Microsoft's standard time format which is stored in a s64 and is
* measured as the number of 100 nano-second intervals since 1st January 1601,
* 00:00:00 UTC.
*/
inline s64 utc2ntfs(const time_t time)
{
/* Convert to 100ns intervals and then add the NTFS time offset. */
return cpu_to_sle64((s64)time * 10000000 + NTFS_TIME_OFFSET);
}
/**
* get_current_ntfs_time - get the current time in little endian NTFS format
*
* Get the current time from the Linux kernel, convert it to its corresponding
* NTFS time and return that in little endian format.
*/
inline s64 get_current_ntfs_time(void)
{
/* ignores leap second */
return utc2ntfs(get_seconds()) + xtime.tv_nsec/1000;
}
/**
* ntfs2utc - convert NTFS time to Linux time
* @time: NTFS time (little endian) to convert to Linux
*
* Convert the little endian NTFS time @time to its corresponding Linux time
* and return that in cpu format.
*
* Linux stores time in a long at present and measures it as the number of
* 1-second intervals since 1st January 1970, 00:00:00 UTC.
*
* NTFS uses Microsoft's standard time format which is stored in a s64 and is
* measured as the number of 100 nano-second intervals since 1st January 1601,
* 00:00:00 UTC.
*/
inline time_t ntfs2utc(const s64 time)
{
/* Subtract the NTFS time offset, then convert to 1s intervals. */
s64 t = sle64_to_cpu(time) - NTFS_TIME_OFFSET;
do_div(t, 10000000);
return (time_t)t;
}

View file

@ -0,0 +1,84 @@
/*
* types.h - Defines for NTFS Linux kernel driver specific types.
* Part of the Linux-NTFS project.
*
* Copyright (c) 2001,2002 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_TYPES_H
#define _LINUX_NTFS_TYPES_H
#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
#define SN(X) X /* Struct Name */
#define SC(P,N) P.N /* ShortCut: Prefix, Name */
#else
#define SN(X)
#define SC(P,N) N
#endif
/* 2-byte Unicode character type. */
typedef u16 uchar_t;
#define UCHAR_T_SIZE_BITS 1
/*
* Clusters are signed 64-bit values on NTFS volumes. We define two types, LCN
* and VCN, to allow for type checking and better code readability.
*/
typedef s64 VCN;
typedef s64 LCN;
/**
* run_list_element - in memory vcn to lcn mapping array element
* @vcn: starting vcn of the current array element
* @lcn: starting lcn of the current array element
* @length: length in clusters of the current array element
*
* The last vcn (in fact the last vcn + 1) is reached when length == 0.
*
* When lcn == -1 this means that the count vcns starting at vcn are not
* physically allocated (i.e. this is a hole / data is sparse).
*/
typedef struct { /* In memory vcn to lcn mapping structure element. */
VCN vcn; /* vcn = Starting virtual cluster number. */
LCN lcn; /* lcn = Starting logical cluster number. */
s64 length; /* Run length in clusters. */
} run_list_element;
/**
* run_list - in memory vcn to lcn mapping array including a read/write lock
* @rl: pointer to an array of run list elements
* @lock: read/write spinlock for serializing access to @rl
*
*/
typedef struct {
run_list_element *rl;
struct rw_semaphore lock;
} run_list;
typedef enum {
FALSE = 0,
TRUE = 1
} BOOL;
typedef enum {
CASE_SENSITIVE = 0,
IGNORE_CASE = 1,
} IGNORE_CASE_BOOL;
#endif /* _LINUX_NTFS_TYPES_H */

View file

@ -0,0 +1,383 @@
/*
* unistr.c - NTFS Unicode string handling. Part of the Linux-NTFS project.
*
* Copyright (c) 2001-2003 Anton Altaparmakov
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program/include file is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the Linux-NTFS
* distribution in the file COPYING); if not, write to the Free Software
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ntfs.h"
/*
* IMPORTANT
* =========
*
* All these routines assume that the Unicode characters are in little endian
* encoding inside the strings!!!
*/
/*
* This is used by the name collation functions to quickly determine what
* characters are (in)valid.
*/
static const u8 legal_ansi_char_array[0x40] = {
0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x17, 0x07, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17,
0x17, 0x17, 0x18, 0x16, 0x16, 0x17, 0x07, 0x00,
0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
0x17, 0x17, 0x04, 0x16, 0x18, 0x16, 0x18, 0x18,
};
/**
* ntfs_are_names_equal - compare two Unicode names for equality
* @s1: name to compare to @s2
* @s1_len: length in Unicode characters of @s1
* @s2: name to compare to @s1
* @s2_len: length in Unicode characters of @s2
* @ic: ignore case bool
* @upcase: upcase table (only if @ic == IGNORE_CASE)
* @upcase_size: length in Unicode characters of @upcase (if present)
*
* Compare the names @s1 and @s2 and return TRUE (1) if the names are
* identical, or FALSE (0) if they are not identical. If @ic is IGNORE_CASE,
* the @upcase table is used to performa a case insensitive comparison.
*/
BOOL ntfs_are_names_equal(const uchar_t *s1, size_t s1_len,
const uchar_t *s2, size_t s2_len,
const IGNORE_CASE_BOOL ic,
const uchar_t *upcase, const u32 upcase_size)
{
if (s1_len != s2_len)
return FALSE;
if (ic == CASE_SENSITIVE)
return !ntfs_ucsncmp(s1, s2, s1_len);
return !ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size);
}
/**
* ntfs_collate_names - collate two Unicode names
* @name1: first Unicode name to compare
* @name2: second Unicode name to compare
* @err_val: if @name1 contains an invalid character return this value
* @ic: either CASE_SENSITIVE or IGNORE_CASE
* @upcase: upcase table (ignored if @ic is CASE_SENSITIVE)
* @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE)
*
* ntfs_collate_names collates two Unicode names and returns:
*
* -1 if the first name collates before the second one,
* 0 if the names match,
* 1 if the second name collates before the first one, or
* @err_val if an invalid character is found in @name1 during the comparison.
*
* The following characters are considered invalid: '"', '*', '<', '>' and '?'.
*/
int ntfs_collate_names(const uchar_t *name1, const u32 name1_len,
const uchar_t *name2, const u32 name2_len,
const int err_val, const IGNORE_CASE_BOOL ic,
const uchar_t *upcase, const u32 upcase_len)
{
u32 cnt, min_len;
uchar_t c1, c2;
min_len = name1_len;
if (name1_len > name2_len)
min_len = name2_len;
for (cnt = 0; cnt < min_len; ++cnt) {
c1 = le16_to_cpu(*name1++);
c2 = le16_to_cpu(*name2++);
if (ic) {
if (c1 < upcase_len)
c1 = le16_to_cpu(upcase[c1]);
if (c2 < upcase_len)
c2 = le16_to_cpu(upcase[c2]);
}
if (c1 < 64 && legal_ansi_char_array[c1] & 8)
return err_val;
if (c1 < c2)
return -1;
if (c1 > c2)
return 1;
}
if (name1_len < name2_len)
return -1;
if (name1_len == name2_len)
return 0;
/* name1_len > name2_len */
c1 = le16_to_cpu(*name1);
if (c1 < 64 && legal_ansi_char_array[c1] & 8)
return err_val;
return 1;
}
/**
* ntfs_ucsncmp - compare two little endian Unicode strings
* @s1: first string
* @s2: second string
* @n: maximum unicode characters to compare
*
* Compare the first @n characters of the Unicode strings @s1 and @s2,
* The strings in little endian format and appropriate le16_to_cpu()
* conversion is performed on non-little endian machines.
*
* The function returns an integer less than, equal to, or greater than zero
* if @s1 (or the first @n Unicode characters thereof) is found, respectively,
* to be less than, to match, or be greater than @s2.
*/
int ntfs_ucsncmp(const uchar_t *s1, const uchar_t *s2, size_t n)
{
uchar_t c1, c2;
size_t i;
for (i = 0; i < n; ++i) {
c1 = le16_to_cpu(s1[i]);
c2 = le16_to_cpu(s2[i]);
if (c1 < c2)
return -1;
if (c1 > c2)
return 1;
if (!c1)
break;
}
return 0;
}
/**
* ntfs_ucsncasecmp - compare two little endian Unicode strings, ignoring case
* @s1: first string
* @s2: second string
* @n: maximum unicode characters to compare
* @upcase: upcase table
* @upcase_size: upcase table size in Unicode characters
*
* Compare the first @n characters of the Unicode strings @s1 and @s2,
* ignoring case. The strings in little endian format and appropriate
* le16_to_cpu() conversion is performed on non-little endian machines.
*
* Each character is uppercased using the @upcase table before the comparison.
*
* The function returns an integer less than, equal to, or greater than zero
* if @s1 (or the first @n Unicode characters thereof) is found, respectively,
* to be less than, to match, or be greater than @s2.
*/
int ntfs_ucsncasecmp(const uchar_t *s1, const uchar_t *s2, size_t n,
const uchar_t *upcase, const u32 upcase_size)
{
uchar_t c1, c2;
size_t i;
for (i = 0; i < n; ++i) {
if ((c1 = le16_to_cpu(s1[i])) < upcase_size)
c1 = le16_to_cpu(upcase[c1]);
if ((c2 = le16_to_cpu(s2[i])) < upcase_size)
c2 = le16_to_cpu(upcase[c2]);
if (c1 < c2)
return -1;
if (c1 > c2)
return 1;
if (!c1)
break;
}
return 0;
}
void ntfs_upcase_name(uchar_t *name, u32 name_len, const uchar_t *upcase,
const u32 upcase_len)
{
u32 i;
uchar_t u;
for (i = 0; i < name_len; i++)
if ((u = le16_to_cpu(name[i])) < upcase_len)
name[i] = upcase[u];
}
void ntfs_file_upcase_value(FILE_NAME_ATTR *file_name_attr,
const uchar_t *upcase, const u32 upcase_len)
{
ntfs_upcase_name((uchar_t*)&file_name_attr->file_name,
file_name_attr->file_name_length, upcase, upcase_len);
}
int ntfs_file_compare_values(FILE_NAME_ATTR *file_name_attr1,
FILE_NAME_ATTR *file_name_attr2,
const int err_val, const IGNORE_CASE_BOOL ic,
const uchar_t *upcase, const u32 upcase_len)
{
return ntfs_collate_names((uchar_t*)&file_name_attr1->file_name,
file_name_attr1->file_name_length,
(uchar_t*)&file_name_attr2->file_name,
file_name_attr2->file_name_length,
err_val, ic, upcase, upcase_len);
}
/**
* ntfs_nlstoucs - convert NLS string to little endian Unicode string
* @vol: ntfs volume which we are working with
* @ins: input NLS string buffer
* @ins_len: length of input string in bytes
* @outs: on return contains the allocated output Unicode string buffer
*
* Convert the input string @ins, which is in whatever format the loaded NLS
* map dictates, into a little endian, 2-byte Unicode string.
*
* This function allocates the string and the caller is responsible for
* calling kmem_cache_free(ntfs_name_cache, @outs); when finished with it.
*
* On success the function returns the number of Unicode characters written to
* the output string *@outs (>= 0), not counting the terminating Unicode NULL
* character. *@outs is set to the allocated output string buffer.
*
* On error, a negative number corresponding to the error code is returned. In
* that case the output string is not allocated. Both *@outs and *@outs_len
* are then undefined.
*
* This might look a bit odd due to fast path optimization...
*/
int ntfs_nlstoucs(const ntfs_volume *vol, const char *ins,
const int ins_len, uchar_t **outs)
{
struct nls_table *nls = vol->nls_map;
uchar_t *ucs;
wchar_t wc;
int i, o, wc_len;
/* We don't trust outside sources. */
if (ins) {
ucs = (uchar_t*)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,
&wc);
if (wc_len >= 0) {
if (wc) {
ucs[o++] = cpu_to_le16(wc);
continue;
} /* else (!wc) */
break;
} /* else (wc_len < 0) */
goto conversion_err;
}
ucs[o] = cpu_to_le16('\0');
*outs = ucs;
return o;
} /* else (!ucs) */
ntfs_error(vol->sb, "Failed to allocate name from "
"ntfs_name_cache!");
return -ENOMEM;
} /* else (!ins) */
ntfs_error(NULL, "Received NULL pointer.");
return -EINVAL;
conversion_err:
ntfs_error(vol->sb, "Name using character set %s contains characters "
"that cannot be converted to Unicode.", nls->charset);
kmem_cache_free(ntfs_name_cache, ucs);
return -EILSEQ;
}
/**
* ntfs_ucstonls - convert little endian Unicode string to NLS string
* @vol: ntfs volume which we are working with
* @ins: input Unicode string buffer
* @ins_len: length of input string in Unicode characters
* @outs: on return contains the (allocated) output NLS string buffer
* @outs_len: length of output string buffer in bytes
*
* Convert the input little endian, 2-byte Unicode string @ins, of length
* @ins_len into the string format dictated by the loaded NLS.
*
* If @outs is NULL, this function allocates the string and the caller is
* responsible for calling kfree(@outs); when finished with it.
*
* On success the function returns the number of bytes written to the output
* string *@outs (>= 0), not counting the terminating NULL byte. If the output
* string buffer was allocated, *@outs is set to it.
*
* On error, a negative number corresponding to the error code is returned. In
* that case the output string is not allocated. The contents of *@outs are
* then undefined.
*
* This might look a bit odd due to fast path optimization...
*/
int ntfs_ucstonls(const ntfs_volume *vol, const uchar_t *ins,
const int ins_len, unsigned char **outs, int outs_len)
{
struct nls_table *nls = vol->nls_map;
unsigned char *ns;
int i, o, ns_len, wc;
/* We don't trust outside sources. */
if (ins) {
ns = *outs;
ns_len = outs_len;
if (ns && !ns_len) {
wc = -ENAMETOOLONG;
goto conversion_err;
}
if (!ns) {
ns_len = ins_len * NLS_MAX_CHARSET_SIZE;
ns = (unsigned char*)kmalloc(ns_len + 1, GFP_NOFS);
if (!ns)
goto mem_err_out;
}
for (i = o = 0; i < ins_len; i++) {
retry: wc = nls->uni2char(le16_to_cpu(ins[i]), ns + o,
ns_len - o);
if (wc > 0) {
o += wc;
continue;
} else if (!wc)
break;
else if (wc == -ENAMETOOLONG && ns != *outs) {
unsigned char *tc;
/* Grow in multiples of 64 bytes. */
tc = (unsigned char*)kmalloc((ns_len + 64) &
~63, GFP_NOFS);
if (tc) {
memcpy(tc, ns, ns_len);
ns_len = ((ns_len + 64) & ~63) - 1;
kfree(ns);
ns = tc;
goto retry;
} /* No memory so goto conversion_error; */
} /* wc < 0, real error. */
goto conversion_err;
}
ns[o] = '\0';
*outs = ns;
return o;
} /* else (!ins) */
ntfs_error(vol->sb, "Received NULL pointer.");
return -EINVAL;
conversion_err:
ntfs_error(vol->sb, "Unicode name contains characters that cannot be "
"converted to character set %s.", nls->charset);
if (ns != *outs)
kfree(ns);
if (wc != -ENAMETOOLONG)
wc = -EILSEQ;
return wc;
mem_err_out:
ntfs_error(vol->sb, "Failed to allocate name!");
return -ENOMEM;
}

View file

@ -0,0 +1,90 @@
/*
* upcase.c - Generate the full NTFS Unicode upcase table in little endian.
* Part of the Linux-NTFS project.
*
* Copyright (c) 2001 Richard Russon <ntfs@flatcap.org>
* Copyright (c) 2001-2003 Anton Altaparmakov
*
* Modified for mkntfs inclusion 9 June 2001 by Anton Altaparmakov.
* Modified for kernel inclusion 10 September 2001 by Anton Altparmakov.
*
* This program 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 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 source
* in the file COPYING); if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ntfs.h"
uchar_t *generate_default_upcase(void)
{
static const int uc_run_table[][3] = { /* Start, End, Add */
{0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74},
{0x00E0, 0x00F7, -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76, 86},
{0x00F8, 0x00FF, -32}, {0x0561, 0x0587, -48}, {0x1F76, 0x1F78, 100},
{0x0256, 0x0258, -205}, {0x1F00, 0x1F08, 8}, {0x1F78, 0x1F7A, 128},
{0x028A, 0x028C, -217}, {0x1F10, 0x1F16, 8}, {0x1F7A, 0x1F7C, 112},
{0x03AC, 0x03AD, -38}, {0x1F20, 0x1F28, 8}, {0x1F7C, 0x1F7E, 126},
{0x03AD, 0x03B0, -37}, {0x1F30, 0x1F38, 8}, {0x1FB0, 0x1FB2, 8},
{0x03B1, 0x03C2, -32}, {0x1F40, 0x1F46, 8}, {0x1FD0, 0x1FD2, 8},
{0x03C2, 0x03C3, -31}, {0x1F51, 0x1F52, 8}, {0x1FE0, 0x1FE2, 8},
{0x03C3, 0x03CC, -32}, {0x1F53, 0x1F54, 8}, {0x1FE5, 0x1FE6, 7},
{0x03CC, 0x03CD, -64}, {0x1F55, 0x1F56, 8}, {0x2170, 0x2180, -16},
{0x03CD, 0x03CF, -63}, {0x1F57, 0x1F58, 8}, {0x24D0, 0x24EA, -26},
{0x0430, 0x0450, -32}, {0x1F60, 0x1F68, 8}, {0xFF41, 0xFF5B, -32},
{0}
};
static const int uc_dup_table[][2] = { /* Start, End */
{0x0100, 0x012F}, {0x01A0, 0x01A6}, {0x03E2, 0x03EF}, {0x04CB, 0x04CC},
{0x0132, 0x0137}, {0x01B3, 0x01B7}, {0x0460, 0x0481}, {0x04D0, 0x04EB},
{0x0139, 0x0149}, {0x01CD, 0x01DD}, {0x0490, 0x04BF}, {0x04EE, 0x04F5},
{0x014A, 0x0178}, {0x01DE, 0x01EF}, {0x04BF, 0x04BF}, {0x04F8, 0x04F9},
{0x0179, 0x017E}, {0x01F4, 0x01F5}, {0x04C1, 0x04C4}, {0x1E00, 0x1E95},
{0x018B, 0x018B}, {0x01FA, 0x0218}, {0x04C7, 0x04C8}, {0x1EA0, 0x1EF9},
{0}
};
static const int uc_word_table[][2] = { /* Offset, Value */
{0x00FF, 0x0178}, {0x01AD, 0x01AC}, {0x01F3, 0x01F1}, {0x0269, 0x0196},
{0x0183, 0x0182}, {0x01B0, 0x01AF}, {0x0253, 0x0181}, {0x026F, 0x019C},
{0x0185, 0x0184}, {0x01B9, 0x01B8}, {0x0254, 0x0186}, {0x0272, 0x019D},
{0x0188, 0x0187}, {0x01BD, 0x01BC}, {0x0259, 0x018F}, {0x0275, 0x019F},
{0x018C, 0x018B}, {0x01C6, 0x01C4}, {0x025B, 0x0190}, {0x0283, 0x01A9},
{0x0192, 0x0191}, {0x01C9, 0x01C7}, {0x0260, 0x0193}, {0x0288, 0x01AE},
{0x0199, 0x0198}, {0x01CC, 0x01CA}, {0x0263, 0x0194}, {0x0292, 0x01B7},
{0x01A8, 0x01A7}, {0x01DD, 0x018E}, {0x0268, 0x0197},
{0}
};
int i, r;
uchar_t *uc;
uc = ntfs_malloc_nofs(default_upcase_len * sizeof(uchar_t));
if (!uc)
return uc;
memset(uc, 0, default_upcase_len * sizeof(uchar_t));
for (i = 0; i < default_upcase_len; i++)
uc[i] = cpu_to_le16(i);
for (r = 0; uc_run_table[r][0]; r++)
for (i = uc_run_table[r][0]; i < uc_run_table[r][1]; i++)
uc[i] = cpu_to_le16((le16_to_cpu(uc[i]) +
uc_run_table[r][2]));
for (r = 0; uc_dup_table[r][0]; r++)
for (i = uc_dup_table[r][0]; i < uc_dup_table[r][1]; i += 2)
uc[i + 1] = cpu_to_le16(le16_to_cpu(uc[i + 1]) - 1);
for (r = 0; uc_word_table[r][0]; r++)
uc[uc_word_table[r][0]] = cpu_to_le16(uc_word_table[r][1]);
return uc;
}

View file

@ -0,0 +1,136 @@
/*
* volume.h - Defines for volume structures in NTFS Linux kernel driver. Part
* of the Linux-NTFS project.
*
* Copyright (c) 2001,2002 Anton Altaparmakov.
* Copyright (c) 2002 Richard Russon.
*
* 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_VOLUME_H
#define _LINUX_NTFS_VOLUME_H
#include "types.h"
/*
* The NTFS in memory super block structure.
*/
typedef struct {
/*
* FIXME: Reorder to have commonly used together element within the
* same cache line, aiming at a cache line size of 32 bytes. Aim for
* 64 bytes for less commonly used together elements. Put most commonly
* used elements to front of structure. Obviously do this only when the
* structure has stabilized... (AIA)
*/
/* Device specifics. */
struct super_block *sb; /* Pointer back to the super_block,
so we don't have to get the offset
every time. */
LCN nr_blocks; /* Number of NTFS_BLOCK_SIZE bytes
sized blocks on the device. */
/* Configuration provided by user at mount time. */
unsigned long flags; /* Miscellaneous flags, see above. */
uid_t uid; /* uid that files will be mounted as. */
gid_t gid; /* gid that files will be mounted as. */
mode_t fmask; /* The mask for file permissions. */
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. */
/* NTFS bootsector provided information. */
u16 sector_size; /* in bytes */
u8 sector_size_bits; /* log2(sector_size) */
u32 cluster_size; /* in bytes */
u32 cluster_size_mask; /* cluster_size - 1 */
u8 cluster_size_bits; /* log2(cluster_size) */
u32 mft_record_size; /* in bytes */
u32 mft_record_size_mask; /* mft_record_size - 1 */
u8 mft_record_size_bits; /* log2(mft_record_size) */
u32 index_record_size; /* in bytes */
u32 index_record_size_mask; /* index_record_size - 1 */
u8 index_record_size_bits; /* log2(index_record_size) */
LCN nr_clusters; /* Volume size in clusters == number of
bits in lcn bitmap. */
LCN mft_lcn; /* Cluster location of mft data. */
LCN mftmirr_lcn; /* Cluster location of copy of mft. */
u64 serial_no; /* The volume serial number. */
/* Mount specific NTFS information. */
u32 upcase_len; /* Number of entries in upcase[]. */
uchar_t *upcase; /* The upcase table. */
LCN mft_zone_start; /* First cluster of the mft zone. */
LCN mft_zone_end; /* First cluster beyond the mft zone. */
struct inode *mft_ino; /* The VFS inode of $MFT. */
struct inode *mftbmp_ino; /* Attribute inode for $MFT/$BITMAP. */
struct rw_semaphore mftbmp_lock; /* Lock for serializing accesses to the
mft record bitmap ($MFT/$BITMAP). */
unsigned long nr_mft_records; /* Number of mft records == number of
bits in mft bitmap. */
struct inode *mftmirr_ino; /* The VFS inode of $MFTMirr. */
struct inode *lcnbmp_ino; /* The VFS inode of $Bitmap. */
struct rw_semaphore lcnbmp_lock; /* Lock for serializing accesses to the
cluster bitmap ($Bitmap/$DATA). */
struct inode *vol_ino; /* The VFS inode of $Volume. */
unsigned long vol_flags; /* Volume flags (VOLUME_*). */
u8 major_ver; /* Ntfs major version of volume. */
u8 minor_ver; /* Ntfs minor version of volume. */
struct inode *root_ino; /* The VFS inode of the root
directory. */
struct inode *secure_ino; /* The VFS inode of $Secure (NTFS3.0+
only, otherwise NULL). */
struct nls_table *nls_map;
} ntfs_volume;
/*
* Defined bits for the flags field in the ntfs_volume structure.
*/
typedef enum {
NV_Errors, /* 1: Volume has errors, prevent remount rw. */
NV_ShowSystemFiles, /* 1: Return system files in ntfs_readdir(). */
NV_CaseSensitive, /* 1: Treat file names as case sensitive and
create filenames in the POSIX namespace.
Otherwise be case insensitive and create
file names in WIN32 namespace. */
} ntfs_volume_flags;
/*
* Macro tricks to expand the NVolFoo(), NVolSetFoo(), and NVolClearFoo()
* functions.
*/
#define NVOL_FNS(flag) \
static inline int NVol##flag(ntfs_volume *vol) \
{ \
return test_bit(NV_##flag, &(vol)->flags); \
} \
static inline void NVolSet##flag(ntfs_volume *vol) \
{ \
set_bit(NV_##flag, &(vol)->flags); \
} \
static inline void NVolClear##flag(ntfs_volume *vol) \
{ \
clear_bit(NV_##flag, &(vol)->flags); \
}
/* Emit the ntfs volume bitops functions. */
NVOL_FNS(Errors)
NVOL_FNS(ShowSystemFiles)
NVOL_FNS(CaseSensitive)
#endif /* _LINUX_NTFS_VOLUME_H */