Commit 6c8b8509 authored by Anton Altaparmakov's avatar Anton Altaparmakov

Remove all traces of the old ntfs driver.

parent c39e9205
NTFS Overview
=============
Legato Systems, Inc. (http://www.legato.com) have sponsored Anton Altaparmakov
to develop NTFS on Linux since June 2001.
To mount an NTFS volume, use the filesystem type 'ntfs'. The driver
currently works only in read-only mode, with no fault-tolerance supported.
If you enable the dangerous(!) write support, make sure you can recover
from a complete loss of data. Also, download the Linux-NTFS project
distribution from Sourceforge at http://sourceforge.net/projects/linux-ntfs/
and always run the included ntfsfix utility after performing a write to an
NTFS partition from Linux to fix some of the damage done by the Linux NTFS
driver and to schedule an automatic chkdsk when Windows reboots. You should
run ntfsfix _after_ unmounting the partition in Linux but _before_ rebooting
into Windows. During the next reboot into Windows, chkdsk will be run
automatically fixing the remaining damage. If no errors are found it is a
good indication that the driver + ntfsfix together worked to full
satisfaction. (-;
Please note that the experimental write support is limited to Windows NT4 and
earlier versions at the moment.
If you think you have discovered a bug please have look at the "Known bugs"
section below to see whether it isn't known already.
For ftdisk support, limited success was reported with volume sets on top of
the md driver, although mirror and stripe sets should work as well - if the
md driver can be talked into using the same layout as Windows NT. However,
using the md driver will fail if any of your NTFS partitions have an odd
number of sectors.
Supported mount options
=======================
iocharset=name Character set to use when returning file names.
Unlike VFAT, NTFS suppresses names that contain
unconvertible characters. Note that most character
sets contain insufficient characters to represent all
possible Unicode characters that can exist on NTFS. To
be sure you are not missing any files, you are advised
to use the iocharset=utf8 which should be capable of
representing all Unicode characters.
utf8=<bool> Use UTF-8 for converting file names. - It is preferable
to use iocharset=utf8 instead, but if the utf8 NLS is
not available, you can use this utf8 option, which
enables the driver's builtin utf8 conversion functions.
uid=
gid=
umask= These options work as documented in mount(8).
By default, the files are owned by root and
not readable by anyone else.
posix=<bool> If enabled, the file system distinguishes between
upper and lower case. The 8.3 alias names are presented
as hard links instead of being suppressed.
show_sys_files=<bool> If enabled, show all system files as normal files. Note
that $MFT does not appear unless specifically
requested. For example in bash, use: "ls -l \$MFT".
Be careful not to write anything to them or you could
crash the kernel and/or corrupt your file system!
mft_zone_multiplier= Set the MFT zone multiplier for the volume (this
setting is not persistent across mounts and can be
changed from mount to mount but cannot be changed on
remount). Values of 1 to 4 are allowed, 1 being the
default. The MFT zone multiplier determines how much
space is reserved for the MFT on the volume. If all
other space is used up, then the MFT zone will be
shrunk dynamically, so this has no impact on the
amount of free space. However, it can have an impact
on performance by affecting fragmentation of the MFT.
In general use the default. If you have a lot of small
files then use a higher value. The values have the
following meaning:
Value MFT zone size (% of volume size)
1 12.5%
2 25%
3 37.5%
4 50%
Known bugs and (mis-)features
=============================
- Do not use the driver for writing as it corrupts the file system. If you do
use it, get the Linux-NTFS tools and use the ntfsfix utility after
dismounting a partition you wrote to.
- Writing of extension records is not supported properly.
Please send bug reports/comments/feed back/abuse to the Linux-NTFS development
list at sourceforge: linux-ntfs-dev@lists.sourceforge.net
ChangeLog
=========
NTFS 1.1.21:
- Fixed bug with reading $MFT where we try to read higher mft records
before having read the $DATA attribute of $MFT. (Note this is only a
partial solution which will only work in the case that the attribute
list is resident or non-resident but $DATA is in the first 1024
bytes. But this should be enough in the majority of cases. I am not
going to bother fixing the general case until someone finds this to
be a problem for them, which I doubt very much will ever happen...)
- Fixed bogus BUG() call in readdir().
NTFS 1.1.20:
- Fixed two bugs in ntfs_readwrite_attr(). Thanks to Jan Kara for
spotting the out of bounds one.
- Check return value of set_blocksize() in ntfs_read_super() and make
use of get_hardsect_size() to determine the minimum block size.
- Fix return values of ntfs_vcn_to_lcn(). This should stop
peoples start of partition being overwritten at random.
NTFS 1.1.19:
- Fixed ntfs_getdir_unsorted(), ntfs_readdir() and ntfs_printcb() to
cope with arbitrary cluster sizes. Very important for Win2k+. Also,
make them detect directories which are too large and truncate the
enumeration pretending end of directory was reached. Detect more
error conditions and overflows. All this fixes the problem where the
driver could end up in an infinite loop under certain circumstances.
- Fixed potential memory leaks in Unicode conversion functions and
setup correct NULL return values.
NTFS 1.1.18:
- Enhanced & bug fixed cluster deallocation (race fixes, etc.)
- Complete rewrite of cluster allocation, now race free.
- Fixed several bugs in the attribute modification codepaths.
- Hopefully fixed bug where the first sectors of some people's
partitions would be overwritten by the mft. And in general fixed up
mft extension code a bit (still incomplete though).
- Introduce splice_runlist() to allow generic splicing of two run
lists into one.
- MFT zone is now implemented. [Stage 2 of 3; only lack dynamic
growing of mft zone but that is AFAIK not even done by Windows, and
the overhead would be so large that it is probably not worth doing
at all, so Stage 3 might never happen...]
- Complete rewrite of $MFT extension and ntfs inode allocation code.
- Made the NTFS driver initialization string show the compile options
used (i.e. whether read-only or read-write, whether a module, and
whether with debug support).
- Modify ntfs_fill_mft_header() to set all fields and to accept more
arguments.
- Get rid of superfluous add_mft_header().
- Get rid of some unused code.
- Fixed several bugs in and generally cleaned up ntfs_readdir,
ntfs_getdir_unsorted(), and ntfs_printcb. Now they spew out huge
amounts of debug output if debugging is enabled. This will be
removed once I know that this works for everyone.
- ntfs_readdir now shows hidden files. The only files that are now
hidden are the first 16 inodes (i.e. the hard coded system files),
which is consistent with Windows NT4. Using the show_sys_files mount
option, these files are then shown, too.
- Fixed the displaying of the "." and ".." directories. We still cannot
cope with more than 65536 files in a directory index block which is
not a problem and we now cannot cope with more than 32766 directory
index blocks which should not be a problem unless you have a
directory with an insanely large number of files in it. The exact
number depends on the length of the file names of the directory
entries and on the size of the dircetory index blocks.
- Fixed all problems with the last file in a directory (e.g. the last
file should no longer disappear and tab completion should work). If
there are still disappearing files or any other problems with the
last file in a directory, please report them! Thanks.
- Rewrote ntfs_extend_attr() to use the new cluster allocator and the
freshly introduced splice_runlists() function. This simplified
ntfs_extend_attr() a lot which in turn seems to have removed one or
more bugs from it.
- Probably other things I have forgotten... (-;
- Removed dollar signs from the names in the system file enumeration.
Apparently gcc doesn't support dollar signs on PPC architecture.
(Andrzej Krzysztofowicz)
NTFS 1.1.17:
- Fixed system file handling. No longer need to use show_sys_files
option for driver to work fine. System files are now always treated
the same, but without the option, they are made invisible to
directory listings. As a result system files can once again be opened
even without the show_sys_files option. This is important for the
statfs system call to work properly, for example.
- Implemented MFT zone including mount parameter to tune it (just like
in Windows via the registry, only we make it per mount rather than
global for the whole driver, so we are better but we have no way of
storing the value as we don't have a registry so either specify on
each mount or put it in /etc/fstab). [Stage 1 of 3, mount parameter
handling.]
- Fixed fixup functions to handle corruption cases and to return error
codes to the caller.
- Made fixup functions apply hotfixes where sensible. [Stage 1 of 2+,
in memory only.]
- Fixed ommission of "NTFS: " string in ntfs_error() output.
- Fixed stupid if statement bug in unistr.c. Thanks to Yann E. Morin
for spotting it.
- Get rid of all uses of max and min macros. This actually allowed for
optimizing the code in several places so it was a Good Thing(TM).
- Make ntfs use generic_file_open to enforce the O_LARGEFILE flag.
- Detect encrypted files and refuse to access them (return EACCES
error code to user space).
- Fix handling of encrypted & compressed files so that an encrypted
file no longer is considered to be compressed (this was causing
kernel segmentation faults).
NTFS 1.1.16:
- Removed non-functional uni_xlate mount options.
- Clarified the semantics of the utf8 and iocharset mount options.
- Threw out the non-functional mount options for using hard coded
character set conversion. Only kept utf8 one.
- Fixed handling of mount options and proper handling of faulty mount
options on remount.
- Cleaned up character conversion which basically became simplified a
lot due to the removal of the above mentioned mount options.
- Made character conversion to be always consistent. Previously we
could output to the VFS file names which we would then not accept
back from the VFS so in effect we were generating ghost entries in
the directory listings which could not be accessed by any means.
- Simplified time conversion functions drastically without sacrificing
accuracy. (-8
- Fixed a use of a pointer before the check for the pointer being
NULL, reported by the Stanford checker.
- Fixed several missing error checks, reported by the Stanford
checker and fixed by Rasmus Andersen.
NTFS 1.1.15 (changes since kernel 2.4.4's NTFS driver):
- New mount option show_sys_files=<bool> to show all system files as
normal files.
- Support for files and in general any attributes up to the full 2TiB
size supported by the NTFS filesystem. Note we only support up to
32-bits worth of inodes/clusters at this point.
- Support for more than 128kiB sized runlists (using vmalloc_32()
instead of kmalloc()).
- Fixed races in allocation of clusters and mft records.
- Fixed major bugs in attribute handling / searching / collation.
- Fixed major bugs in compressing a run list into a mapping pairs array.
- Fixed major bugs in inode allocation. Especially file create and
mkdir.
- Fixed memory leaks.
- Fixed major bug in inode layout assignment of sequence numbers.
- Lots of other bug fixes I can't think of right now...
- Fixed NULL bug found by the Stanford checker in ntfs_dupuni2map().
- Convert large stack variable to dynamically allocated one in
ntfs_get_free_cluster_count() (found by Stanford checker).
Kernel 2.4.4:
- Started ChangeLog.
...@@ -576,35 +576,6 @@ CONFIG_HPFS_FS ...@@ -576,35 +576,6 @@ CONFIG_HPFS_FS
say M here and read <file:Documentation/modules.txt>. If unsure, say M here and read <file:Documentation/modules.txt>. If unsure,
say N. say N.
CONFIG_NTFS_FS
NTFS is the file system of Microsoft Windows NT. Say Y if you want
to get read access to files on NTFS partitions of your hard drive.
The Linux NTFS driver supports most of the mount options of the VFAT
driver, see <file:Documentation/filesystems/ntfs.txt>. Saying Y here
will give you read-only access to NTFS partitions.
This code is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
The module will be called ntfs.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
CONFIG_NTFS_RW
If you say Y here, you will (maybe) be able to write to NTFS file
systems as well as read from them. The read-write support in NTFS
is far from being complete and is not well tested. If you say Y
here, back up your NTFS volume first, since it will probably get
damaged. Also, download the Linux-NTFS project distribution from
Sourceforge at <http://linux-ntfs.sf.net/> and always run the
included ntfsfix utility after writing to an NTFS partition from
Linux to fix some of the damage done by the driver. You should run
ntfsfix _after_ unmounting the partition in Linux but _before_
rebooting into Windows. When Windows next boots, chkdsk will be
run automatically to fix the remaining damage.
Please note that write support is limited to Windows NT4 and
earlier versions.
If unsure, say N.
CONFIG_SYSV_FS CONFIG_SYSV_FS
SCO, Xenix and Coherent are commercial Unix systems for Intel SCO, Xenix and Coherent are commercial Unix systems for Intel
machines, and Version 7 was used on the DEC PDP-11. Saying Y machines, and Version 7 was used on the DEC PDP-11. Saying Y
......
...@@ -61,8 +61,6 @@ dep_mbool ' JFS statistics' CONFIG_JFS_STATISTICS $CONFIG_JFS_FS ...@@ -61,8 +61,6 @@ dep_mbool ' JFS statistics' CONFIG_JFS_STATISTICS $CONFIG_JFS_FS
tristate 'Minix fs support' CONFIG_MINIX_FS tristate 'Minix fs support' CONFIG_MINIX_FS
tristate 'FreeVxFS file system support (VERITAS VxFS(TM) compatible)' CONFIG_VXFS_FS tristate 'FreeVxFS file system support (VERITAS VxFS(TM) compatible)' CONFIG_VXFS_FS
tristate 'NTFS file system support (read only)' CONFIG_NTFS_FS
dep_mbool ' NTFS write support (DANGEROUS)' CONFIG_NTFS_RW $CONFIG_NTFS_FS $CONFIG_EXPERIMENTAL
tristate 'OS/2 HPFS file system support' CONFIG_HPFS_FS tristate 'OS/2 HPFS file system support' CONFIG_HPFS_FS
......
...@@ -52,7 +52,6 @@ subdir-$(CONFIG_SYSV_FS) += sysv ...@@ -52,7 +52,6 @@ subdir-$(CONFIG_SYSV_FS) += sysv
subdir-$(CONFIG_SMB_FS) += smbfs subdir-$(CONFIG_SMB_FS) += smbfs
subdir-$(CONFIG_NCP_FS) += ncpfs subdir-$(CONFIG_NCP_FS) += ncpfs
subdir-$(CONFIG_HPFS_FS) += hpfs subdir-$(CONFIG_HPFS_FS) += hpfs
subdir-$(CONFIG_NTFS_FS) += ntfs
subdir-$(CONFIG_UFS_FS) += ufs subdir-$(CONFIG_UFS_FS) += ufs
subdir-$(CONFIG_EFS_FS) += efs subdir-$(CONFIG_EFS_FS) += efs
subdir-$(CONFIG_JFFS_FS) += jffs subdir-$(CONFIG_JFFS_FS) += jffs
......
# Rules for making the NTFS driver
O_TARGET := ntfs.o
obj-y := fs.o sysctl.o support.o util.o inode.o dir.o super.o attr.o unistr.o
obj-m := $(O_TARGET)
# New version format started 3 February 2001.
EXTRA_CFLAGS = -DNTFS_VERSION=\"1.1.21\" #-DDEBUG
include $(TOPDIR)/Rules.make
/*
* attr.c
*
* Copyright (C) 1996-1999 Martin von Lwis
* Copyright (C) 1996-1997 Rgis Duchesne
* Copyright (C) 1998 Joseph Malicki
* Copyright (C) 1999 Steve Dodd
* Copyright (C) 2001 Anton Altaparmakov (AIA)
*/
#include "ntfstypes.h"
#include "struct.h"
#include "attr.h"
#include <linux/errno.h>
#include <linux/ntfs_fs.h>
#include "macros.h"
#include "support.h"
#include "util.h"
#include "super.h"
#include "inode.h"
#include "unistr.h"
/**
* ntfs_find_attr_in_mft_rec - find attribute in mft record
* @vol: volume on which attr resides
* @m: mft record to search
* @type: attribute type to find
* @name: attribute name to find (optional, i.e. NULL means don't care)
* @name_len: attribute name length (only needed if @name present)
* @ic: ignore case if 1 or case sensitive if 0 (ignored if @name NULL)
* @instance: instance number to find
*
* Only search the specified mft record and it ignores the presence of an
* attribute list attribute (unless it is the one being searched for,
* obviously, in which case it is returned).
*/
ntfs_u8* ntfs_find_attr_in_mft_rec(ntfs_volume *vol, ntfs_u8 *m, __u32 type,
wchar_t *name, __u32 name_len, int ic, __u16 instance)
{
ntfs_u8 *a;
/* Iterate over attributes in mft record @m. */
a = m + NTFS_GETU16(m + 20); /* attrs_offset */
for (; a >= m && a <= m + vol->mft_record_size;
a += NTFS_GETU32(a + 4 /* length */)) {
/* We catch $END with this more general check, too... */
if (NTFS_GETU32(a + 0 /* type */) > type)
return NULL;
if (!NTFS_GETU32(a + 4 /* length */))
break;
if (NTFS_GETU32(a + 0 /* type */) != type)
continue;
/* If @name is present, compare the two names. */
if (name && !ntfs_are_names_equal(name, name_len, (wchar_t*)
(a + NTFS_GETU16(a + 10 /* name_offset */)),
a[9] /* name_length */, ic, vol->upcase,
vol->upcase_length)) {
register int rc;
rc = ntfs_collate_names(vol->upcase, vol->upcase_length,
name, name_len, (wchar_t*)(a +
NTFS_GETU16(a + 10 /* name_offset */)),
a[9] /* name_length */, 1, 1);
/*
* If @name collates before a->name, there is no
* matching attribute.
*/
if (rc == -1)
return NULL;
/* If the strings are not equal, continue search. */
if (rc)
continue;
rc = ntfs_collate_names(vol->upcase, vol->upcase_length,
name, name_len, (wchar_t*)(a +
NTFS_GETU16(a + 10 /* name_offset */)),
a[9] /* name_length */, 0, 1);
if (rc == -1)
return NULL;
if (rc)
continue;
}
/*
* The names match or @name not present. Check instance number.
* and if it matches we have found the attribute and are done.
*/
if (instance != NTFS_GETU16(a + 14 /* instance */))
continue;
ntfs_debug(DEBUG_FILE3, "ntfs_find_attr_in_mft_record: found: "
"attr type 0x%x, instance number = 0x%x.\n",
NTFS_GETU32(a + 0), instance);
return a;
}
ntfs_error("ntfs_find_attr_in_mft_record: mft record 0x%x is corrupt"
". Run chkdsk.\n", m);
return NULL;
}
/* Look if an attribute already exists in the inode, and if not, create it. */
int ntfs_new_attr(ntfs_inode *ino, int type, void *name, int namelen,
void *value, int value_len, int *pos, int *found)
{
int do_insert = 0;
int i, m;
ntfs_attribute *a;
for (i = 0; i < ino->attr_count; i++)
{
a = ino->attrs + i;
if (a->type < type)
continue;
if (a->type > type) {
do_insert = 1;
break;
}
/* If @name is present, compare the two names. */
if (namelen && !ntfs_are_names_equal((wchar_t*)name, namelen,
a->name, a->namelen /* name_length */,
1 /* ignore case*/, ino->vol->upcase,
ino->vol->upcase_length)) {
register int rc;
rc = ntfs_collate_names(ino->vol->upcase,
ino->vol->upcase_length, a->name,
a->namelen, (wchar_t*)name, namelen,
1 /* ignore case */, 1);
if (rc == -1)
continue;
if (rc == 1) {
do_insert = 1;
break;
}
rc = ntfs_collate_names(ino->vol->upcase,
ino->vol->upcase_length, a->name,
a->namelen, (wchar_t*)name, namelen,
0 /* case sensitive */, 1);
if (rc == -1)
continue;
if (rc == 1) {
do_insert = 1;
break;
}
}
/* Names are equal or no name was asked for. */
/* If a value was specified compare the values. */
if (value_len && a->resident) {
if (!a->resident) {
ntfs_error("ntfs_new_attr: Value specified but "
"attribute non-resident. Bug!\n");
return -EINVAL;
}
m = value_len;
if (m > a->size)
m = a->size;
m = memcmp(value, a->d.data, m);
if (m > 0)
continue;
if (m < 0) {
do_insert = 1;
break;
}
/* Values match until min of value lengths. */
if (value_len > a->size)
continue;
if (value_len < a->size) {
do_insert = 1;
break;
}
}
/* Full match! */
*found = 1;
*pos = i;
return 0;
}
/* Re-allocate space. */
if (ino->attr_count % 8 == 0)
{
ntfs_attribute* new;
new = (ntfs_attribute*)ntfs_malloc((ino->attr_count + 8) *
sizeof(ntfs_attribute));
if (!new)
return -ENOMEM;
if (ino->attrs) {
ntfs_memcpy(new, ino->attrs, ino->attr_count *
sizeof(ntfs_attribute));
ntfs_free(ino->attrs);
}
ino->attrs = new;
}
if (do_insert)
ntfs_memmove(ino->attrs + i + 1, ino->attrs + i,
(ino->attr_count - i) * sizeof(ntfs_attribute));
ino->attr_count++;
ino->attrs[i].type = type;
ino->attrs[i].namelen = namelen;
ino->attrs[i].name = name;
*pos = i;
*found = 0;
return 0;
}
int ntfs_make_attr_resident(ntfs_inode *ino, ntfs_attribute *attr)
{
__s64 size = attr->size;
if (size > 0) {
/* FIXME: read data, free clusters */
return -EOPNOTSUPP;
}
attr->resident = 1;
return 0;
}
/* Store in the inode readable information about a run. */
int ntfs_insert_run(ntfs_attribute *attr, int cnum, ntfs_cluster_t cluster,
int len)
{
/* (re-)allocate space if necessary. */
if ((attr->d.r.len * sizeof(ntfs_runlist)) % PAGE_SIZE == 0) {
ntfs_runlist* new;
unsigned long new_size;
ntfs_debug(DEBUG_MALLOC, "ntfs_insert_run: re-allocating "
"space: old attr->d.r.len = 0x%x\n",
attr->d.r.len);
new_size = attr->d.r.len * sizeof(ntfs_runlist) + PAGE_SIZE;
if ((new_size >> PAGE_SHIFT) > num_physpages) {
ntfs_error("ntfs_insert_run: attempted to allocate "
"more pages than num_physpages."
"This might be a bug or a corrupt"
"file system.\n");
return -1;
}
new = ntfs_vmalloc(new_size);
if (!new) {
ntfs_error("ntfs_insert_run: ntfs_vmalloc(new_size = "
"0x%x) failed\n", new_size);
return -1;
}
if (attr->d.r.runlist) {
ntfs_memcpy(new, attr->d.r.runlist, attr->d.r.len
* sizeof(ntfs_runlist));
ntfs_vfree(attr->d.r.runlist);
}
attr->d.r.runlist = new;
}
if (attr->d.r.len > cnum)
ntfs_memmove(attr->d.r.runlist + cnum + 1,
attr->d.r.runlist + cnum,
(attr->d.r.len - cnum) * sizeof(ntfs_runlist));
attr->d.r.runlist[cnum].lcn = cluster;
attr->d.r.runlist[cnum].len = len;
attr->d.r.len++;
return 0;
}
/**
* ntfs_extend_attr - extend allocated size of an attribute
* @ino: ntfs inode containing the attribute to extend
* @attr: attribute which to extend
* @len: desired new length for @attr (_not_ the amount to extend by)
*
* Extends an attribute. Allocate clusters on the volume which @ino belongs to.
* Extends the run list accordingly, preferably by extending the last run of
* the existing run list, first.
*
* Only modifies attr->allocated, i.e. doesn't touch attr->size, nor
* attr->initialized.
*/
int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, const __s64 len)
{
int rlen, rl2_len, err = 0;
ntfs_cluster_t cluster, clen;
ntfs_runlist *rl, *rl2;
if ((attr->flags & (ATTR_IS_COMPRESSED | ATTR_IS_ENCRYPTED)) ||
ino->record_count > 1)
return -EOPNOTSUPP;
/*
* FIXME: Don't make non-resident if the attribute type is not right.
* For example cannot make index attribute non-resident! (AIA)
*/
if (attr->resident) {
err = ntfs_make_attr_nonresident(ino, attr);
if (err)
return err;
}
if (len <= attr->allocated)
return 0; /* Truly stupid things do sometimes happen. */
rl = attr->d.r.runlist;
rlen = attr->d.r.len;
if (rlen > 0)
cluster = rl[rlen - 1].lcn + rl[rlen - 1].len;
else
/* No preference for allocation space. */
cluster = (ntfs_cluster_t)-1;
/*
* Calculate the extra space we need, and round up to multiple of
* cluster size to get number of new clusters needed.
*/
clen = (len - attr->allocated + ino->vol->cluster_size - 1) >>
ino->vol->cluster_size_bits;
if (!clen)
return 0;
err = ntfs_allocate_clusters(ino->vol, &cluster, &clen, &rl2,
&rl2_len, DATA_ZONE);
if (err)
return err;
attr->allocated += (__s64)clen << ino->vol->cluster_size_bits;
if (rlen > 0) {
err = splice_runlists(&rl, &rlen, rl2, rl2_len);
ntfs_vfree(rl2);
if (err)
return err;
} else {
if (rl)
ntfs_vfree(rl);
rl = rl2;
rlen = rl2_len;
}
attr->d.r.runlist = rl;
attr->d.r.len = rlen;
return 0;
}
int ntfs_make_attr_nonresident(ntfs_inode *ino, ntfs_attribute *attr)
{
int error;
ntfs_io io;
void *data = attr->d.data;
__s64 len = attr->size;
attr->d.r.len = 0;
attr->d.r.runlist = NULL;
attr->resident = 0;
/*
* ->allocated is updated by ntfs_extend_attr(), while ->initialized
* and ->size are updated by ntfs_readwrite_attr(). (AIA)
*/
attr->allocated = attr->initialized = 0;
error = ntfs_extend_attr(ino, attr, len);
if (error)
return error; /* FIXME: On error, restore old values. */
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
io.param = data;
io.size = len;
io.do_read = 0;
return ntfs_readwrite_attr(ino, attr, 0, &io);
}
int ntfs_attr_allnonresident(ntfs_inode *ino)
{
int i, error = 0;
ntfs_volume *vol = ino->vol;
for (i = 0; !error && i < ino->attr_count; i++)
{
if (ino->attrs[i].type != vol->at_security_descriptor &&
ino->attrs[i].type != vol->at_data)
continue;
error = ntfs_make_attr_nonresident(ino, ino->attrs + i);
}
return error;
}
/*
* Resize the attribute to a newsize. attr->allocated and attr->size are
* updated, but attr->initialized is not changed unless it becomes bigger than
* attr->size, in which case it is set to attr->size.
*/
int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 newsize)
{
int error = 0;
__s64 oldsize = attr->size;
int clustersizebits = ino->vol->cluster_size_bits;
int i, count, newcount;
ntfs_runlist *rl, *rlt;
if (newsize == oldsize)
return 0;
if (attr->flags & (ATTR_IS_COMPRESSED | ATTR_IS_ENCRYPTED))
return -EOPNOTSUPP;
if (attr->resident) {
void *v;
if (newsize > ino->vol->mft_record_size) {
error = ntfs_make_attr_nonresident(ino, attr);
if (error)
return error;
return ntfs_resize_attr(ino, attr, newsize);
}
v = attr->d.data;
if (newsize) {
__s64 minsize = newsize;
attr->d.data = ntfs_malloc(newsize);
if (!attr->d.data) {
ntfs_free(v);
return -ENOMEM;
}
if (newsize > oldsize) {
minsize = oldsize;
ntfs_bzero((char*)attr->d.data + oldsize,
newsize - oldsize);
}
ntfs_memcpy((char*)attr->d.data, v, minsize);
} else
attr->d.data = 0;
ntfs_free(v);
attr->size = newsize;
return 0;
}
/* Non-resident attribute. */
rl = attr->d.r.runlist;
if (newsize < oldsize) {
int rl_size;
/*
* FIXME: We might be going awfully wrong for newsize = 0,
* possibly even leaking memory really badly. But considering
* in that case there is more breakage due to -EOPNOTSUPP stuff
* further down the code path, who cares for the moment... (AIA)
*/
for (i = 0, count = 0; i < attr->d.r.len; i++) {
if ((__s64)(count + rl[i].len) << clustersizebits >
newsize) {
i++;
break;
}
count += (int)rl[i].len;
}
newcount = count;
/* Free unused clusters in current run, unless sparse. */
if (rl[--i].lcn != (ntfs_cluster_t)-1) {
ntfs_cluster_t rounded = newsize - ((__s64)count <<
clustersizebits);
rounded = (rounded + ino->vol->cluster_size - 1) >>
clustersizebits;
error = ntfs_deallocate_cluster_run(ino->vol,
rl[i].lcn + rounded,
rl[i].len - rounded);
if (error)
return error; /* FIXME: Incomplete operation. */
rl[i].len = rounded;
newcount = count + rounded;
}
/* Free all other runs. */
i++;
error = ntfs_deallocate_clusters(ino->vol, rl + i,
attr->d.r.len - i);
if (error)
return error; /* FIXME: Incomplete operation. */
/*
* Free space for extra runs in memory if enough memory left
* to do so. FIXME: Only do it if it would free memory. (AIA)
*/
rl_size = ((i + 1) * sizeof(ntfs_runlist) + PAGE_SIZE - 1) &
PAGE_MASK;
if (rl_size < ((attr->d.r.len * sizeof(ntfs_runlist) +
PAGE_SIZE - 1) & PAGE_MASK)) {
rlt = ntfs_vmalloc(rl_size);
if (rlt) {
ntfs_memcpy(rlt, rl, i * sizeof(ntfs_runlist));
ntfs_vfree(rl);
attr->d.r.runlist = rl = rlt;
}
}
rl[i].lcn = (ntfs_cluster_t)-1;
rl[i].len = (ntfs_cluster_t)0;
attr->d.r.len = i;
} else {
error = ntfs_extend_attr(ino, attr, newsize);
if (error)
return error; /* FIXME: Incomplete operation. */
newcount = (newsize + ino->vol->cluster_size - 1) >>
clustersizebits;
}
/* Fill in new sizes. */
attr->allocated = (__s64)newcount << clustersizebits;
attr->size = newsize;
if (attr->initialized > newsize)
attr->initialized = newsize;
if (!newsize)
error = ntfs_make_attr_resident(ino, attr);
return error;
}
int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data,
int dsize, ntfs_attribute **rattr)
{
void *name;
int namelen;
int found, i;
int error;
ntfs_attribute *attr;
if (dsize > ino->vol->mft_record_size)
/* FIXME: Non-resident attributes. */
return -EOPNOTSUPP;
if (aname) {
namelen = strlen(aname);
name = ntfs_malloc(2 * namelen);
if (!name)
return -ENOMEM;
ntfs_ascii2uni(name, aname, namelen);
} else {
name = 0;
namelen = 0;
}
error = ntfs_new_attr(ino, anum, name, namelen, data, dsize, &i,
&found);
if (error || found) {
ntfs_free(name);
return error ? error : -EEXIST;
}
*rattr = attr = ino->attrs + i;
/* Allocate a new number.
* FIXME: Should this happen on inode writeback?
* FIXME: Extension records not supported. */
error = ntfs_allocate_attr_number(ino, &i);
if (error)
return error;
attr->attrno = i;
if (attr->attrno + 1 != NTFS_GETU16(ino->attr + 0x28))
ntfs_error("UH OH! attr->attrno (%i) != NTFS_GETU16(ino->attr "
"+ 0x28) (%i)\n", attr->attrno,
NTFS_GETU16(ino->attr + 0x28));
attr->resident = 1;
attr->flags = 0;
attr->cengine = 0;
attr->size = attr->allocated = attr->initialized = dsize;
/* FIXME: INDEXED information should come from $AttrDef
* Currently, only file names are indexed. As of NTFS v3.0 (Win2k),
* this is no longer true. Different attributes can be indexed now. */
if (anum == ino->vol->at_file_name)
attr->indexed = 1;
else
attr->indexed = 0;
attr->d.data = ntfs_malloc(dsize);
if (!attr->d.data)
return -ENOMEM;
ntfs_memcpy(attr->d.data, data, dsize);
return 0;
}
/*
* Non-resident attributes are stored in runs (intervals of clusters).
*
* This function stores in the inode readable information about a non-resident
* attribute.
*/
static int ntfs_process_runs(ntfs_inode *ino, ntfs_attribute* attr,
unsigned char *data)
{
int startvcn, endvcn;
int vcn, cnum;
ntfs_cluster_t cluster;
int len, ctype;
int er = 0;
startvcn = NTFS_GETS64(data + 0x10);
endvcn = NTFS_GETS64(data + 0x18);
/* Check whether this chunk really belongs to the end. Problem with
* this: this functions can get called on the last extent first, before
* it is called on the other extents in sequence. This happens when the
* base mft record contains the last extent instead of the first one
* and the first extent is stored, like any intermediate extents in
* extension mft records. This would be difficult to allow the way the
* runlist is stored in memory. Thus we fix elsewhere by causing the
* attribute list attribute to be processed immediately when found. The
* extents will then be processed starting with the first one. */
for (cnum = 0, vcn = 0; cnum < attr->d.r.len; cnum++)
vcn += attr->d.r.runlist[cnum].len;
if (vcn != startvcn) {
ntfs_debug(DEBUG_FILE3, "ntfs_process_runs: ino = 0x%x, "
"attr->type = 0x%x, startvcn = 0x%x, endvcn = 0x%x, "
"vcn = 0x%x, cnum = 0x%x\n", ino->i_number, attr->type,
startvcn, endvcn, vcn, cnum);
if (vcn < startvcn) {
ntfs_error("Problem with runlist in extended record\n");
return -1;
}
/* Tried to insert an already inserted runlist. */
return 0;
}
if (!endvcn) {
if (!startvcn) {
/* Allocated length. */
endvcn = NTFS_GETS64(data + 0x28) - 1;
endvcn >>= ino->vol->cluster_size_bits;
} else {
/* This is an extent. Allocated length is not defined!
* Extents must have an endvcn though so this is an
* error. */
ntfs_error("Corrupt attribute extent. (endvcn is "
"missing)\n");
return -1;
}
}
data = data + NTFS_GETU16(data + 0x20);
cnum = attr->d.r.len;
cluster = 0;
for (vcn = startvcn; vcn <= endvcn; vcn += len) {
if (ntfs_decompress_run(&data, &len, &cluster, &ctype)) {
ntfs_debug(DEBUG_FILE3, "ntfs_process_runs: "
"ntfs_decompress_run failed. i_number = 0x%x\n",
ino->i_number);
return -1;
}
if (ctype)
er = ntfs_insert_run(attr, cnum, -1, len);
else
er = ntfs_insert_run(attr, cnum, cluster, len);
if (er)
break;
cnum++;
}
if (er)
ntfs_error("ntfs_process_runs: ntfs_insert_run failed\n");
ntfs_debug(DEBUG_FILE3, "ntfs_process_runs: startvcn = 0x%x, vcn = 0x%x"
", endvcn = 0x%x, cnum = %i\n", startvcn, vcn,
endvcn, cnum);
return er;
}
/* Insert the attribute starting at attr in the inode ino. */
int ntfs_insert_attribute(ntfs_inode *ino, unsigned char *attrdata)
{
int i, found;
int type;
short int *name;
int namelen;
void *data;
ntfs_attribute *attr;
int error;
type = NTFS_GETU32(attrdata);
namelen = NTFS_GETU8(attrdata + 9);
ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute: ino->i_number 0x%x, "
"attr type 0x%x\n", ino->i_number, type);
/* Read the attribute's name if it has one. */
if (!namelen)
name = 0;
else {
/* 1 Unicode character fits in 2 bytes. */
name = ntfs_malloc(2 * namelen);
if (!name)
return -ENOMEM;
ntfs_memcpy(name, attrdata + NTFS_GETU16(attrdata + 10),
2 * namelen);
}
/* If resident look for value, too. */
if (NTFS_GETU8(attrdata + 8) == 0)
error = ntfs_new_attr(ino, type, name, namelen,
attrdata + NTFS_GETU16(attrdata + 0x14),
NTFS_GETU16(attrdata + 0x10), &i, &found);
else
error = ntfs_new_attr(ino, type, name, namelen, NULL, 0, &i,
&found);
if (error) {
ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute: ntfs_new_attr "
"failed.\n");
if (name)
ntfs_free(name);
return error;
}
if (found) {
/* It's already there, if not resident just process the runs. */
if (!ino->attrs[i].resident) {
ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute:"
" processing runs 1.\n");
/* FIXME: Check error code! (AIA) */
ntfs_process_runs(ino, ino->attrs + i, attrdata);
}
return 0;
}
attr = ino->attrs + i;
attr->resident = NTFS_GETU8(attrdata + 8) == 0;
attr->flags = *(__u16*)(attrdata + 0xC);
attr->attrno = NTFS_GETU16(attrdata + 0xE);
if (attr->resident) {
attr->size = NTFS_GETU16(attrdata + 0x10);
data = attrdata + NTFS_GETU16(attrdata + 0x14);
attr->d.data = (void*)ntfs_malloc(attr->size);
if (!attr->d.data)
return -ENOMEM;
ntfs_memcpy(attr->d.data, data, attr->size);
attr->indexed = NTFS_GETU8(attrdata + 0x16);
} else {
attr->allocated = NTFS_GETS64(attrdata + 0x28);
attr->size = NTFS_GETS64(attrdata + 0x30);
attr->initialized = NTFS_GETS64(attrdata + 0x38);
attr->cengine = NTFS_GETU16(attrdata + 0x22);
if (attr->flags & ATTR_IS_COMPRESSED)
attr->compsize = NTFS_GETS64(attrdata + 0x40);
ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute: "
"attr->allocated = 0x%Lx, attr->size = 0x%Lx, "
"attr->initialized = 0x%Lx\n", attr->allocated,
attr->size, attr->initialized);
ino->attrs[i].d.r.runlist = 0;
ino->attrs[i].d.r.len = 0;
ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute: processing "
"runs 2.\n");
/* FIXME: Check error code! (AIA) */
ntfs_process_runs(ino, attr, attrdata);
}
return 0;
}
int ntfs_read_zero(ntfs_io *dest, int size)
{
int i;
char *sparse = ntfs_calloc(512);
if (!sparse)
return -ENOMEM;
i = 512;
while (size) {
if (i > size)
i = size;
dest->fn_put(dest, sparse, i);
size -= i;
}
ntfs_free(sparse);
return 0;
}
/* Process compressed attributes. */
int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
ntfs_io *dest)
{
int error = 0;
int clustersizebits;
int s_vcn, rnum, vcn, got, l1;
__s64 copied, len, chunk, offs1, l, chunk2;
ntfs_cluster_t cluster, cl1;
char *comp = 0, *comp1;
char *decomp = 0;
ntfs_io io;
ntfs_runlist *rl;
l = dest->size;
clustersizebits = ino->vol->cluster_size_bits;
/* Starting cluster of potential chunk. There are three situations:
a) In a large uncompressible or sparse chunk, s_vcn is in the middle
of a run.
b) s_vcn is right on a run border.
c) When several runs make a chunk, s_vcn is before the chunks. */
s_vcn = offset >> clustersizebits;
/* Round down to multiple of 16. */
s_vcn &= ~15;
rl = attr->d.r.runlist;
for (rnum = vcn = 0; rnum < attr->d.r.len && vcn + rl->len <= s_vcn;
rnum++, rl++)
vcn += rl->len;
if (rnum == attr->d.r.len) {
/* Beyond end of file. */
/* FIXME: Check allocated / initialized. */
dest->size = 0;
return 0;
}
io.do_read = 1;
io.fn_put = ntfs_put;
io.fn_get = 0;
cluster = rl->lcn;
len = rl->len;
copied = 0;
while (l) {
chunk = 0;
if (cluster == (ntfs_cluster_t)-1) {
/* Sparse cluster. */
__s64 ll;
if ((len - (s_vcn - vcn)) & 15)
ntfs_error("Unexpected sparse chunk size.");
ll = ((__s64)(vcn + len) << clustersizebits) - offset;
if (ll > l)
ll = l;
chunk = ll;
error = ntfs_read_zero(dest, ll);
if (error)
goto out;
} else if (dest->do_read) {
if (!comp) {
comp = ntfs_malloc(16 << clustersizebits);
if (!comp) {
error = -ENOMEM;
goto out;
}
}
got = 0;
/* We might need to start in the middle of a run. */
cl1 = cluster + s_vcn - vcn;
comp1 = comp;
do {
int delta;
io.param = comp1;
delta = s_vcn - vcn;
if (delta < 0)
delta = 0;
l1 = len - delta;
if (l1 > 16 - got)
l1 = 16 - got;
io.size = (__s64)l1 << clustersizebits;
error = ntfs_getput_clusters(ino->vol, cl1, 0,
&io);
if (error)
goto out;
if (l1 + delta == len) {
rnum++;
rl++;
vcn += len;
cluster = cl1 = rl->lcn;
len = rl->len;
}
got += l1;
comp1 += (__s64)l1 << clustersizebits;
} while (cluster != (ntfs_cluster_t)-1 && got < 16);
/* Until empty run. */
chunk = 16 << clustersizebits;
if (cluster != (ntfs_cluster_t)-1 || got == 16)
/* Uncompressible */
comp1 = comp;
else {
if (!decomp) {
decomp = ntfs_malloc(16 <<
clustersizebits);
if (!decomp) {
error = -ENOMEM;
goto out;
}
}
/* Make sure there are null bytes after the
* last block. */
*(ntfs_u32*)comp1 = 0;
ntfs_decompress(decomp, comp, chunk);
comp1 = decomp;
}
offs1 = offset - ((__s64)s_vcn << clustersizebits);
chunk2 = (16 << clustersizebits) - offs1;
if (chunk2 > l)
chunk2 = l;
if (chunk > chunk2)
chunk = chunk2;
dest->fn_put(dest, comp1 + offs1, chunk);
}
l -= chunk;
copied += chunk;
offset += chunk;
s_vcn = (offset >> clustersizebits) & ~15;
if (l && offset >= ((__s64)(vcn + len) << clustersizebits)) {
rnum++;
rl++;
vcn += len;
cluster = rl->lcn;
len = rl->len;
}
}
out:
if (comp)
ntfs_free(comp);
if (decomp)
ntfs_free(decomp);
dest->size = copied;
return error;
}
int ntfs_write_compressed(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
ntfs_io *dest)
{
return -EOPNOTSUPP;
}
/*
* attr.h - Header file for attr.c
*
* Copyright (C) 1997 Rgis Duchesne
* Copyright (c) 2001 Anton Altaparmakov (AIA)
*/
#include <linux/nls.h>
ntfs_u8* ntfs_find_attr_in_mft_rec(ntfs_volume *vol, ntfs_u8 *m, __u32 type,
wchar_t *name, __u32 name_len, int ic, __u16 instance);
int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, const __s64 len);
int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 newsize);
int ntfs_insert_attribute(ntfs_inode *ino, unsigned char* attrdata);
int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
ntfs_io *dest);
int ntfs_write_compressed(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
ntfs_io *dest);
int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data,
int dsize, ntfs_attribute **rattr);
int ntfs_read_zero(ntfs_io *dest, int size);
int ntfs_make_attr_nonresident(ntfs_inode *ino, ntfs_attribute *attr);
int ntfs_attr_allnonresident(ntfs_inode *ino);
int ntfs_new_attr(ntfs_inode *ino, int type, void *name, int namelen,
void *value, int value_len, int *pos, int *found);
int ntfs_insert_run(ntfs_attribute *attr, int cnum, ntfs_cluster_t cluster,
int len);
/*
* dir.c
*
* Copyright (C) 1995-1997, 1999 Martin von Lwis
* Copyright (C) 1999 Steve Dodd
* Copyright (C) 1999 Joseph Malicki
* Copyright (C) 2001 Anton Altaparmakov (AIA)
*/
#include "ntfstypes.h"
#include "struct.h"
#include "dir.h"
#include "macros.h"
#include <linux/errno.h>
#include "super.h"
#include "inode.h"
#include "attr.h"
#include "support.h"
#include "util.h"
#include <linux/smp_lock.h>
#include <linux/bitops.h>
static char I30[] = "$I30";
/* An index record should start with INDX, and the last word in each block
* should contain the check value. If it passes, the original values need to
* be restored. */
int ntfs_check_index_record(ntfs_inode *ino, char *record)
{
return ntfs_fixup_record(record, "INDX", ino->u.index.recordsize);
}
static inline int ntfs_is_top(ntfs_u64 stack)
{
return stack == 14;
}
static int ntfs_pop(ntfs_u64 *stack)
{
static int width[16] = {1,2,1,3,1,2,1,4,1,2,1,3,1,2,1,-1};
int res = -1;
switch (width[*stack & 15]) {
case 1:
res = (int)((*stack & 15) >> 1);
*stack >>= 4;
break;
case 2:
res = (int)(((*stack & 63) >> 2) + 7);
*stack >>= 6;
break;
case 3:
res = (int)(((*stack & 255) >> 3) + 23);
*stack >>= 8;
break;
case 4:
res = (int)(((*stack & 1023) >> 4) + 55);
*stack >>= 10;
break;
default:
ntfs_error("Unknown encoding\n");
}
return res;
}
static inline unsigned int ntfs_top(void)
{
return 14;
}
static ntfs_u64 ntfs_push(ntfs_u64 stack, int i)
{
if (i < 7)
return (stack << 4) | (i << 1);
if (i < 23)
return (stack << 6) | ((i - 7) << 2) | 1;
if (i < 55)
return (stack << 8) | ((i - 23) << 3) | 3;
if (i < 120)
return (stack << 10) | ((i - 55) << 4) | 7;
ntfs_error("Too many entries\n");
return ~((ntfs_u64)0);
}
#if 0
static void ntfs_display_stack(ntfs_u64 stack)
{
while(!ntfs_is_top(stack))
{
printf("%d ", ntfs_pop(&stack));
}
printf("\n");
}
#endif
/* True if the entry points to another block of entries. */
static inline int ntfs_entry_has_subnodes(char *entry)
{
return (NTFS_GETU16(entry + 0xc) & 1);
}
/* True if it is not the 'end of dir' entry. */
static inline int ntfs_entry_is_used(char *entry)
{
return !(NTFS_GETU16(entry + 0xc) & 2);
}
/*
* Removed RACE for allocating index blocks. But stil not too happy.
* There might be more races afterwards. (AIA)
*/
static int ntfs_allocate_index_block(ntfs_iterate_s *walk)
{
ntfs_attribute *allocation, *bitmap = 0;
int error, size, i, bit;
ntfs_u8 *bmap;
ntfs_io io;
ntfs_volume *vol = walk->dir->vol;
/* Check for allocation attribute. */
allocation = ntfs_find_attr(walk->dir, vol->at_index_allocation, I30);
if (!allocation) {
ntfs_u8 bmp[8];
/* Create index allocation attribute. */
error = ntfs_create_attr(walk->dir, vol->at_index_allocation,
I30, 0, 0, &allocation);
if (error)
goto err_ret;
ntfs_bzero(bmp, sizeof(bmp));
error = ntfs_create_attr(walk->dir, vol->at_bitmap, I30, bmp,
sizeof(bmp), &bitmap);
if (error)
goto err_ret;
} else
bitmap = ntfs_find_attr(walk->dir, vol->at_bitmap, I30);
if (!bitmap) {
ntfs_error("Directory w/o bitmap\n");
error = -EINVAL;
goto err_ret;
}
size = bitmap->size;
bmap = ntfs_malloc(size);
if (!bmap) {
error = -ENOMEM;
goto err_ret;
}
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
try_again:
io.param = bmap;
io.size = size;
error = ntfs_read_attr(walk->dir, vol->at_bitmap, I30, 0, &io);
if (error || (io.size != size && (error = -EIO, 1)))
goto err_fb_out;
/* Allocate a bit. */
for (bit = i = 0; i < size; i++) {
if (bmap[i] == 0xFF)
continue;
bit = ffz(bmap[i]);
if (bit < 8)
break;
}
if (i >= size) {
/* FIXME: Extend bitmap. */
error = -EOPNOTSUPP;
goto err_fb_out;
}
/* Get the byte containing our bit again, now taking the BKL. */
io.param = bmap;
io.size = 1;
lock_kernel();
error = ntfs_read_attr(walk->dir, vol->at_bitmap, I30, i, &io);
if (error || (io.size != 1 && (error = -EIO, 1)))
goto err_unl_out;
if (ntfs_test_and_set_bit(bmap, bit)) {
unlock_kernel();
/* Give other process(es) a chance to finish. */
schedule();
goto try_again;
}
walk->newblock = (i * 8 + bit) * walk->dir->u.index.clusters_per_record;
io.param = bmap;
error = ntfs_write_attr(walk->dir, vol->at_bitmap, I30, i, &io);
if (error || (io.size != size && (error = -EIO, 1)))
goto err_unl_out;
/* Change inode on disk, required when bitmap is resident. */
error = ntfs_update_inode(walk->dir);
if (error)
goto err_unl_out;
unlock_kernel();
ntfs_free(bmap);
/* Check whether record is out of allocated range. */
size = allocation->size;
if (walk->newblock * vol->cluster_size >= size) {
/* Build index record. */
int hsize;
int s1 = walk->dir->u.index.recordsize;
int nr_fix = (s1 >> vol->sector_size) + 1;
char *record = ntfs_malloc(s1);
if (!record) {
error = -ENOMEM;
goto err_ret;
}
ntfs_bzero(record, s1);
/* Magic */
ntfs_memcpy(record, "INDX", 4);
/* Offset to fixups */
NTFS_PUTU16(record + 4, 0x28);
/* Number of fixups. */
NTFS_PUTU16(record + 6, nr_fix);
/* Log file sequence number - We don't do journalling so we
* just set it to zero which should be the Right Thing. (AIA) */
NTFS_PUTU64(record + 8, 0);
/* VCN of buffer */
NTFS_PUTU64(record + 0x10, walk->newblock);
/* Header size. */
hsize = 0x10 + 2 * nr_fix;
hsize = (hsize + 7) & ~7; /* Align. */
NTFS_PUTU16(record + 0x18, hsize);
/* Total size of record. */
NTFS_PUTU32(record + 0x20, s1 - 0x18);
/* Writing the data will extend the attribute. */
io.param = record;
io.size = s1;
io.do_read = 0;
error = ntfs_readwrite_attr(walk->dir, allocation, size, &io);
ntfs_free(record);
if (error || (io.size != s1 && (error = -EIO, 1)))
goto err_ret;
error = ntfs_update_inode(walk->dir);
if (error)
goto err_ret;
}
return 0;
err_unl_out:
unlock_kernel();
err_fb_out:
ntfs_free(bmap);
err_ret:
return error;
}
/* Write an index block (root or allocation) back to storage.
* Used is the total number of bytes in buf, including all headers. */
static int ntfs_index_writeback(ntfs_iterate_s *walk, ntfs_u8 *buf, int block,
int used)
{
ntfs_io io;
int error;
ntfs_attribute *a;
ntfs_volume *vol = walk->dir->vol;
io.fn_put = 0;
io.fn_get = ntfs_get;
io.param = buf;
if (block == -1) { /* Index root. */
NTFS_PUTU16(buf + 0x14, used - 0x10);
/* 0x18 is a copy thereof. */
NTFS_PUTU16(buf + 0x18, used - 0x10);
io.size = used;
error = ntfs_write_attr(walk->dir, vol->at_index_root, I30, 0,
&io);
if (error || (io.size != used && (error = -EIO, 1)))
return error;
/* Shrink if necessary. */
a = ntfs_find_attr(walk->dir, vol->at_index_root, I30);
ntfs_resize_attr(walk->dir, a, used);
} else {
NTFS_PUTU16(buf + 0x1C, used - 0x18);
io.size = walk->dir->u.index.recordsize;
error = ntfs_insert_fixups(buf, io.size);
if (error) {
printk(KERN_ALERT "NTFS: ntfs_index_writeback() caught "
"corrupt index record ntfs record "
"header. Refusing to write corrupt "
"data to disk. Unmount and run chkdsk "
"immediately!\n");
return -EIO;
}
error = ntfs_write_attr(walk->dir, vol->at_index_allocation,
I30, (__s64)block << vol->cluster_size_bits,
&io);
if (error || (io.size != walk->dir->u.index.recordsize &&
(error = -EIO, 1)))
return error;
}
return 0;
}
static int ntfs_split_record(ntfs_iterate_s *walk, char *start, int bsize,
int usize)
{
char *entry, *prev;
ntfs_u8 *newbuf = 0, *middle = 0;
int error, othersize, mlen;
ntfs_io io;
ntfs_volume *vol = walk->dir->vol;
int oldblock;
error = ntfs_allocate_index_block(walk);
if (error)
return error;
/* This should not happen. */
if (walk->block == -1) {
ntfs_error("Trying to split root");
return -EOPNOTSUPP;
}
entry = start + NTFS_GETU16(start + 0x18) + 0x18;
for (prev = entry; entry - start < usize / 2;
entry += NTFS_GETU16(entry + 8))
prev = entry;
newbuf = ntfs_malloc(vol->index_record_size);
if (!newbuf)
return -ENOMEM;
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
io.param = newbuf;
io.size = vol->index_record_size;
/* Read in old header. FIXME: Reading everything is overkill. */
error = ntfs_read_attr(walk->dir, vol->at_index_allocation, I30,
(__s64)walk->newblock << vol->cluster_size_bits, &io);
if (error)
goto out;
if (io.size != vol->index_record_size) {
error = -EIO;
goto out;
}
/* FIXME: Adjust header. */
/* Copy everything from entry to new block. */
othersize = usize - (entry - start);
ntfs_memcpy(newbuf + NTFS_GETU16(newbuf + 0x18) + 0x18, entry,
othersize);
/* Copy flags. */
NTFS_PUTU32(newbuf + 0x24, NTFS_GETU32(start + 0x24));
error = ntfs_index_writeback(walk, newbuf, walk->newblock,
othersize + NTFS_GETU16(newbuf + 0x18) + 0x18);
if (error)
goto out;
/* Move prev to walk. */
mlen = NTFS_GETU16(prev + 0x8);
/* Remember old child node. */
if (ntfs_entry_has_subnodes(prev))
oldblock = NTFS_GETU32(prev + mlen - 8);
else
oldblock = -1;
/* Allow for pointer to subnode. */
middle = ntfs_malloc(ntfs_entry_has_subnodes(prev) ? mlen : mlen + 8);
if (!middle){
error = -ENOMEM;
goto out;
}
ntfs_memcpy(middle, prev, mlen);
/* Set has_subnodes flag. */
NTFS_PUTU8(middle + 0xC, NTFS_GETU8(middle + 0xC) | 1);
/* Middle entry points to block, parent entry will point to newblock. */
NTFS_PUTU64(middle + mlen - 8, walk->block);
if (walk->new_entry)
ntfs_error("Entry not reset");
walk->new_entry = middle;
walk->u.flags |= ITERATE_SPLIT_DONE;
/* Terminate old block. */
othersize = usize - (prev-start);
NTFS_PUTU64(prev, 0);
if (oldblock == -1) {
NTFS_PUTU32(prev + 8, 0x10);
NTFS_PUTU32(prev + 0xC, 2);
othersize += 0x10;
} else {
NTFS_PUTU32(prev + 8, 0x18);
NTFS_PUTU32(prev + 0xC, 3);
NTFS_PUTU64(prev + 0x10, oldblock);
othersize += 0x18;
}
/* Write back original block. */
error = ntfs_index_writeback(walk, start, walk->block, othersize);
out:
if (newbuf)
ntfs_free(newbuf);
if (middle)
ntfs_free(middle);
return error;
}
static int ntfs_dir_insert(ntfs_iterate_s *walk, char *start, char* entry)
{
int blocksize, usedsize, error, offset;
int do_split = 0;
offset = entry - start;
if (walk->block == -1) { /* index root */
blocksize = walk->dir->vol->mft_record_size;
usedsize = NTFS_GETU16(start + 0x14) + 0x10;
} else {
blocksize = walk->dir->u.index.recordsize;
usedsize = NTFS_GETU16(start + 0x1C) + 0x18;
}
if (usedsize + walk->new_entry_size > blocksize) {
char* s1 = ntfs_malloc(blocksize + walk->new_entry_size);
if (!s1)
return -ENOMEM;
ntfs_memcpy(s1, start, usedsize);
do_split = 1;
/* Adjust entry to s1. */
entry = s1 + (entry - start);
start = s1;
}
ntfs_memmove(entry + walk->new_entry_size, entry, usedsize - offset);
ntfs_memcpy(entry, walk->new_entry, walk->new_entry_size);
usedsize += walk->new_entry_size;
ntfs_free(walk->new_entry);
walk->new_entry = 0;
if (do_split) {
error = ntfs_split_record(walk, start, blocksize, usedsize);
ntfs_free(start);
} else {
error = ntfs_index_writeback(walk, start, walk->block,usedsize);
if (error)
return error;
}
return 0;
}
/* Try to split INDEX_ROOT attributes. Return -E2BIG if nothing changed. */
int ntfs_split_indexroot(ntfs_inode *ino)
{
ntfs_attribute *ra;
ntfs_u8 *root = 0, *index = 0;
ntfs_io io;
int error, off, i, bsize, isize;
ntfs_iterate_s walk;
ra = ntfs_find_attr(ino, ino->vol->at_index_root, I30);
if (!ra)
return -ENOTDIR;
bsize = ino->vol->mft_record_size;
root = ntfs_malloc(bsize);
if (!root)
return -E2BIG;
io.fn_put = ntfs_put;
io.param = root;
io.size = bsize;
error = ntfs_read_attr(ino, ino->vol->at_index_root, I30, 0, &io);
if (error)
goto out;
off = 0x20;
/* Count number of entries. */
for (i = 0; ntfs_entry_is_used(root + off); i++)
off += NTFS_GETU16(root + off + 8);
if (i <= 2) {
/* We don't split small index roots. */
error = -E2BIG;
goto out;
}
index = ntfs_malloc(ino->vol->index_record_size);
if (!index) {
error = -ENOMEM;
goto out;
}
walk.dir = ino;
walk.block = -1;
walk.result = walk.new_entry = 0;
walk.name = 0;
error = ntfs_allocate_index_block(&walk);
if (error)
goto out;
/* Write old root to new index block. */
io.param = index;
io.size = ino->vol->index_record_size;
error = ntfs_read_attr(ino, ino->vol->at_index_allocation, I30,
(__s64)walk.newblock << ino->vol->cluster_size_bits, &io);
if (error)
goto out;
isize = NTFS_GETU16(root + 0x18) - 0x10;
ntfs_memcpy(index + NTFS_GETU16(index + 0x18) + 0x18, root+0x20, isize);
/* Copy flags. */
NTFS_PUTU32(index + 0x24, NTFS_GETU32(root + 0x1C));
error = ntfs_index_writeback(&walk, index, walk.newblock,
isize + NTFS_GETU16(index + 0x18) + 0x18);
if (error)
goto out;
/* Mark root as split. */
NTFS_PUTU32(root + 0x1C, 1);
/* Truncate index root. */
NTFS_PUTU64(root + 0x20, 0);
NTFS_PUTU32(root + 0x28, 0x18);
NTFS_PUTU32(root + 0x2C, 3);
NTFS_PUTU64(root + 0x30, walk.newblock);
error = ntfs_index_writeback(&walk, root, -1, 0x38);
out:
ntfs_free(root);
ntfs_free(index);
return error;
}
/* The entry has been found. Copy the result in the caller's buffer */
static int ntfs_copyresult(char *dest, char *source)
{
int length = NTFS_GETU16(source + 8);
ntfs_memcpy(dest, source, length);
return 1;
}
/* Use $UpCase some day. */
static inline unsigned short ntfs_my_toupper(ntfs_volume *vol, ntfs_u16 x)
{
/* We should read any pending rest of $UpCase here. */
if (x >= vol->upcase_length)
return x;
return vol->upcase[x];
}
/* Everything passed in walk and entry. */
static int ntfs_my_strcmp(ntfs_iterate_s *walk, const unsigned char *entry)
{
int lu = *(entry + 0x50);
int i;
ntfs_u16* name = (ntfs_u16*)(entry + 0x52);
ntfs_volume *vol = walk->dir->vol;
for (i = 0; i < lu && i < walk->namelen; i++)
if (ntfs_my_toupper(vol, NTFS_GETU16(name + i)) !=
ntfs_my_toupper(vol, NTFS_GETU16(walk->name + i)))
break;
if (i == lu && i == walk->namelen)
return 0;
if (i == lu)
return 1;
if (i == walk->namelen)
return -1;
if (ntfs_my_toupper(vol, NTFS_GETU16(name + i)) <
ntfs_my_toupper(vol, NTFS_GETU16(walk->name + i)))
return 1;
return -1;
}
/* Necessary forward declaration. */
static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry);
/* Parse a block of entries. Load the block, fix it up, and iterate over the
* entries. The block is given as virtual cluster number. */
static int ntfs_getdir_record(ntfs_iterate_s *walk, int block)
{
int length = walk->dir->u.index.recordsize;
char *record = (char*)ntfs_malloc(length);
char *offset;
int retval,error;
int oldblock;
ntfs_io io;
if (!record)
return -ENOMEM;
io.fn_put = ntfs_put;
io.param = record;
io.size = length;
/* Read the block from the index allocation attribute. */
error = ntfs_read_attr(walk->dir, walk->dir->vol->at_index_allocation,
I30, (__s64)block << walk->dir->vol->cluster_size_bits, &io);
if (error || io.size != length) {
ntfs_error("read failed\n");
ntfs_free(record);
return 0;
}
if (!ntfs_check_index_record(walk->dir, record)) {
ntfs_error("%x is not an index record\n", block);
ntfs_free(record);
return 0;
}
offset = record + NTFS_GETU16(record + 0x18) + 0x18;
oldblock = walk->block;
walk->block = block;
retval = ntfs_getdir_iterate(walk, record, offset);
walk->block = oldblock;
ntfs_free(record);
return retval;
}
/* Go down to the next block of entries. These collate before the current
* entry. */
static int ntfs_descend(ntfs_iterate_s *walk, ntfs_u8 *start, ntfs_u8 *entry)
{
int length = NTFS_GETU16(entry + 8);
int nextblock = NTFS_GETU32(entry + length - 8);
int error;
if (!ntfs_entry_has_subnodes(entry)) {
ntfs_error("illegal ntfs_descend call\n");
return 0;
}
error = ntfs_getdir_record(walk, nextblock);
if (!error && walk->type == DIR_INSERT &&
(walk->u.flags & ITERATE_SPLIT_DONE)) {
/* Split has occurred. Adjust entry, insert new_entry. */
NTFS_PUTU32(entry + length - 8, walk->newblock);
/* Reset flags, as the current block might be split again. */
walk->u.flags &= ~ITERATE_SPLIT_DONE;
error = ntfs_dir_insert(walk, start, entry);
}
return error;
}
static int ntfs_getdir_iterate_byposition(ntfs_iterate_s *walk, char* start,
char *entry)
{
int retval = 0;
int curpos = 0, destpos = 0;
int length;
if (walk->u.pos != 0) {
if (ntfs_is_top(walk->u.pos))
return 0;
destpos = ntfs_pop(&walk->u.pos);
}
while (1) {
if (walk->u.pos == 0) {
if (ntfs_entry_has_subnodes(entry))
ntfs_descend(walk, start, entry);
else
walk->u.pos = ntfs_top();
if (ntfs_is_top(walk->u.pos) &&
!ntfs_entry_is_used(entry))
return 1;
walk->u.pos = ntfs_push(walk->u.pos, curpos);
return 1;
}
if (curpos == destpos) {
if (!ntfs_is_top(walk->u.pos) &&
ntfs_entry_has_subnodes(entry)) {
retval = ntfs_descend(walk, start, entry);
if (retval) {
walk->u.pos = ntfs_push(walk->u.pos,
curpos);
return retval;
}
if (!ntfs_entry_is_used(entry))
return 0;
walk->u.pos = 0;
}
if (ntfs_entry_is_used(entry)) {
retval = ntfs_copyresult(walk->result, entry);
walk->u.pos = 0;
} else {
walk->u.pos = ntfs_top();
return 0;
}
}
curpos++;
if (!ntfs_entry_is_used(entry))
break;
length = NTFS_GETU16(entry + 8);
if (!length) {
ntfs_error("infinite loop\n");
break;
}
entry += length;
}
return -1;
}
/* Iterate over a list of entries, either from an index block, or from the
* index root.
* If searching BY_POSITION, pop the top index from the position. If the
* position stack is empty then, return the item at the index and set the
* position to the next entry. If the position stack is not empty,
* recursively proceed for subnodes. If the entry at the position is the
* 'end of dir' entry, return 'not found' and the empty stack.
* If searching BY_NAME, walk through the items until found or until
* one item is collated after the requested item. In the former case, return
* the result. In the latter case, recursively proceed to the subnodes.
* If 'end of dir' is reached, the name is not in the directory */
static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry)
{
int length;
int cmp;
if (walk->type == BY_POSITION)
return ntfs_getdir_iterate_byposition(walk, start, entry);
do {
/* If the current entry is a real one, compare with the
* requested item. If the current entry is the last item, it
* is always larger than the requested item. */
cmp = ntfs_entry_is_used(entry) ?
ntfs_my_strcmp(walk,entry) : -1;
switch (walk->type) {
case BY_NAME:
switch (cmp) {
case -1:
return ntfs_entry_has_subnodes(entry) ?
ntfs_descend(walk, start, entry) : 0;
case 0:
return ntfs_copyresult(walk->result, entry);
case 1:
break;
}
break;
case DIR_INSERT:
switch (cmp) {
case -1:
return ntfs_entry_has_subnodes(entry) ?
ntfs_descend(walk, start, entry) :
ntfs_dir_insert(walk, start, entry);
case 0:
return -EEXIST;
case 1:
break;
}
break;
default:
ntfs_error("TODO\n"); /* FIXME: ? */
}
if (!ntfs_entry_is_used(entry))
break;
length = NTFS_GETU16(entry + 8);
if (!length) {
ntfs_error("infinite loop\n");
break;
}
entry += length;
} while (1);
return 0;
}
/* Tree walking is done using position numbers. The following numbers have a
* special meaning:
* 0 start (.)
* -1 no more entries
* -2 ..
* All other numbers encode sequences of indices. The sequence a, b, c is
* encoded as <stop><c><b><a>, where <foo> is the encoding of foo. The
* first few integers are encoded as follows:
* 0: 0000 1: 0010 2: 0100 3: 0110
* 4: 1000 5: 1010 6: 1100 stop: 1110
* 7: 000001 8: 000101 9: 001001 10: 001101
* The least significant bits give the width of this encoding, the other bits
* encode the value, starting from the first value of the interval.
* tag width first value last value
* 0 3 0 6
* 01 4 7 22
* 011 5 23 54
* 0111 6 55 119
* More values are hopefully not needed, as the file position has currently
* 64 bits in total. */
/* Find an entry in the directory. Return 0 if not found, otherwise copy the
* entry to the result buffer. */
int ntfs_getdir(ntfs_iterate_s *walk)
{
int length = walk->dir->vol->mft_record_size;
int retval, error;
/* Start at the index root. */
char *root = ntfs_malloc(length);
ntfs_io io;
if (!root)
return -ENOMEM;
io.fn_put = ntfs_put;
io.param = root;
io.size = length;
error = ntfs_read_attr(walk->dir, walk->dir->vol->at_index_root, I30,
0, &io);
if (error) {
ntfs_error("Not a directory\n");
return 0;
}
walk->block = -1;
/* FIXME: Move these to walk. */
walk->dir->u.index.recordsize = NTFS_GETU32(root + 0x8);
walk->dir->u.index.clusters_per_record = NTFS_GETU32(root + 0xC);
/* FIXME: Consistency check. */
/* Skip header. */
retval = ntfs_getdir_iterate(walk, root, root + 0x20);
ntfs_free(root);
return retval;
}
/* Find an entry in the directory by its position stack. Iteration starts
* if the stack is 0, in which case the position is set to the first item
* in the directory. If the position is nonzero, return the item at the
* position and change the position to the next item. The position is -1
* if there are no more items. */
int ntfs_getdir_byposition(ntfs_iterate_s *walk)
{
walk->type = BY_POSITION;
return ntfs_getdir(walk);
}
/* Find an entry in the directory by its name. Return 0 if not found. */
int ntfs_getdir_byname(ntfs_iterate_s *walk)
{
walk->type = BY_NAME;
return ntfs_getdir(walk);
}
int ntfs_getdir_unsorted(ntfs_inode *ino, u32 *p_high, u32 *p_low,
int (*cb)(ntfs_u8 *, void *), void *param)
{
s64 ib_ofs;
char *buf = 0, *entry = 0;
ntfs_attribute *attr;
ntfs_volume *vol;
int byte, bit, err = 0;
u32 start, finish, ibs, max_size;
ntfs_io io;
u8 ibs_bits;
if (!ino) {
ntfs_error(__FUNCTION__ "(): No inode! Returning -EINVAL.\n");
return -EINVAL;
}
vol = ino->vol;
if (!vol) {
ntfs_error(__FUNCTION__ "(): Inode 0x%lx has no volume. "
"Returning -EINVAL.\n", ino->i_number);
return -EINVAL;
}
ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 1: Entering for "
"inode 0x%lx, p_high = 0x%x, p_low = 0x%x.\n",
ino->i_number, *p_high, *p_low);
if (!*p_high) {
/* We are still in the index root. */
buf = ntfs_malloc(io.size = vol->mft_record_size);
if (!buf)
return -ENOMEM;
io.fn_put = ntfs_put;
io.param = buf;
err = ntfs_read_attr(ino, vol->at_index_root, I30, 0, &io);
if (err || !io.size)
goto read_err_ret;
ino->u.index.recordsize = ibs = NTFS_GETU32(buf + 0x8);
ino->u.index.clusters_per_record = NTFS_GETU32(buf + 0xC);
entry = buf + 0x20;
ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 2: In index "
"root.\n");
ibs_bits = ffs(ibs) - 1;
/* Compensate for faked "." and "..". */
start = 2;
} else { /* We are in an index record. */
io.size = ibs = ino->u.index.recordsize;
buf = ntfs_malloc(ibs);
if (!buf)
return -ENOMEM;
ibs_bits = ffs(ibs) - 1;
io.fn_put = ntfs_put;
io.param = buf;
/*
* 0 is index root, index allocation starts at 1 and works in
* units of index block size (ibs).
*/
ib_ofs = (s64)(*p_high - 1) << ibs_bits;
err = ntfs_read_attr(ino, vol->at_index_allocation, I30, ib_ofs,
&io);
if (err || io.size != ibs)
goto read_err_ret;
if (!ntfs_check_index_record(ino, buf)) {
ntfs_error(__FUNCTION__ "(): Index block 0x%x is not "
"an index record. Returning "
"-ENOTDIR.\n", *p_high - 1);
ntfs_free(buf);
return -ENOTDIR;
}
entry = buf + 0x18 + NTFS_GETU16(buf + 0x18);
ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 3: In index "
"allocation.\n");
start = 0;
}
/* Process the entries. */
finish = *p_low;
for (; entry < (buf + ibs) && ntfs_entry_is_used(entry);
entry += NTFS_GETU16(entry + 8)) {
if (start < finish) {
/* Skip entries that were already processed. */
ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 4: "
"Skipping already processed entry "
"p_high 0x%x, p_low 0x%x.\n", *p_high,
start);
start++;
continue;
}
ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 5: "
"Processing entry p_high 0x%x, p_low 0x%x.\n",
*p_high, *p_low);
if ((err = cb(entry, param))) {
/* filldir signalled us to stop. */
ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): "
"Unsorted 6: cb returned %i, "
"returning 0, p_high 0x%x, p_low 0x%x."
"\n", *p_high, *p_low);
ntfs_free(buf);
return 0;
}
++*p_low;
}
ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 7: After processing "
"entries, p_high 0x%x, p_low 0x%x.\n", *p_high, *p_low);
/* We have to locate the next record. */
ntfs_free(buf);
buf = 0;
*p_low = 0;
attr = ntfs_find_attr(ino, vol->at_bitmap, I30);
if (!attr) {
/* Directory does not have index bitmap and index allocation. */
*p_high = 0x7fff;
ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 8: No index "
"allocation. Returning 0, p_high 0x7fff, "
"p_low 0x0.\n");
return 0;
}
max_size = attr->size;
if (max_size > 0x7fff >> 3) {
ntfs_error(__FUNCTION__ "(): Directory too large. Visible "
"length is truncated.\n");
max_size = 0x7fff >> 3;
}
buf = ntfs_malloc(max_size);
if (!buf)
return -ENOMEM;
io.param = buf;
io.size = max_size;
err = ntfs_read_attr(ino, vol->at_bitmap, I30, 0, &io);
if (err || io.size != max_size)
goto read_err_ret;
attr = ntfs_find_attr(ino, vol->at_index_allocation, I30);
if (!attr) {
ntfs_free(buf);
ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 9: Find "
"attr failed. Returning -EIO.\n");
return -EIO;
}
if (attr->resident) {
ntfs_free(buf);
ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 9.5: IA is "
"resident. Not allowed. Returning EINVAL.\n");
return -EINVAL;
}
/* Loop while going through non-allocated index records. */
max_size <<= 3;
while (1) {
if (++*p_high >= 0x7fff) {
ntfs_error(__FUNCTION__ "(): Unsorted 10: Directory "
"inode 0x%lx overflowed the maximum "
"number of index allocation buffers "
"the driver can cope with. Pretending "
"to be at end of directory.\n",
ino->i_number);
goto fake_eod;
}
if (*p_high > max_size || (s64)*p_high << ibs_bits >
attr->initialized) {
fake_eod:
/* No more index records. */
*p_high = 0x7fff;
*p_low = 0;
ntfs_free(buf);
ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted "
"10.5: No more index records. "
"Returning 0, p_high 0x7fff, p_low "
"0.\n");
return 0;
}
byte = (ntfs_cluster_t)(*p_high - 1);
bit = 1 << (byte & 7);
byte >>= 3;
if ((buf[byte] & bit))
break;
};
ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 11: Done. "
"Returning 0, p_high 0x%x, p_low 0x%x.\n", *p_high,
*p_low);
ntfs_free(buf);
return 0;
read_err_ret:
if (!err)
err = -EIO;
ntfs_error(__FUNCTION__ "(): Read failed. Returning error code %i.\n",
err);
ntfs_free(buf);
return err;
}
int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name)
{
ntfs_iterate_s walk;
int nsize, esize;
ntfs_u8* entry, *ndata;
int error;
walk.type = DIR_INSERT;
walk.dir = dir;
walk.u.flags = 0;
nsize = name->size;
ndata = name->d.data;
walk.name = (ntfs_u16*)(ndata + 0x42);
walk.namelen = NTFS_GETU8(ndata + 0x40);
walk.new_entry_size = esize = (nsize + 0x10 + 7) & ~7;
walk.new_entry = entry = ntfs_malloc(esize);
if (!entry)
return -ENOMEM;
NTFS_PUTINUM(entry, new);
NTFS_PUTU16(entry + 0x8, esize); /* Size of entry. */
NTFS_PUTU16(entry + 0xA, nsize); /* Size of original name attribute. */
NTFS_PUTU16(entry + 0xC, 0); /* Flags. */
NTFS_PUTU16(entry + 0xE, 0); /* Reserved. */
ntfs_memcpy(entry + 0x10, ndata, nsize);
ntfs_bzero(entry + 0x10 + nsize, esize - 0x10 - nsize);
error = ntfs_getdir(&walk);
if (walk.new_entry)
ntfs_free(walk.new_entry);
return error;
}
#if 0
int ntfs_dir_add1(ntfs_inode *dir, const char* name, int namelen,
ntfs_inode *ino)
{
ntfs_iterate_s walk;
int error;
int nsize;
char *entry;
ntfs_attribute *name_attr;
error = ntfs_decodeuni(dir->vol, name, namelen, &walk.name,
&walk.namelen);
if (error)
return error;
/* FIXME: Set flags. */
walk.type = DIR_INSERT;
walk.dir = dir;
/* walk.new = ino; */
/* Prepare new entry. */
/* Round up to a multiple of 8. */
walk.new_entry_size = nsize = ((0x52 + 2 * walk.namelen + 7) / 8) * 8;
walk.new_entry = entry = ntfs_malloc(nsize);
if (!entry)
return -ENOMEM;
ntfs_bzero(entry, nsize);
NTFS_PUTINUM(entry, ino);
NTFS_PUTU16(entry + 8, nsize);
NTFS_PUTU16(entry + 0xA, 0x42 + 2 * namelen); /* FIXME: Size of name
* attribute. */
NTFS_PUTU32(entry + 0xC, 0); /* FIXME: D-F? */
name_attr = ntfs_find_attr(ino, vol->at_file_name, 0);
/* FIXME: multiple names */
if (!name_attr || !name_attr->resident)
return -EIDRM;
/* Directory, file stamps, sizes, filename. */
ntfs_memcpy(entry + 0x10, name_attr->d.data, 0x42 + 2 * namelen);
error = ntfs_getdir(&walk);
ntfs_free(walk.name);
return error;
}
#endif
/* Fills out and creates an INDEX_ROOT attribute. */
int ntfs_add_index_root(ntfs_inode *ino, int type)
{
ntfs_attribute *da;
ntfs_u8 data[0x30]; /* 0x20 header, 0x10 last entry. */
char name[10];
NTFS_PUTU32(data, type);
/* Collation rule. 1 == COLLATION_FILENAME */
NTFS_PUTU32(data + 4, 1);
NTFS_PUTU32(data + 8, ino->vol->index_record_size);
NTFS_PUTU32(data + 0xC, ino->vol->index_clusters_per_record);
/* Byte offset to first INDEX_ENTRY. */
NTFS_PUTU32(data + 0x10, 0x10);
/* Size of entries, including header. */
NTFS_PUTU32(data + 0x14, 0x20);
NTFS_PUTU32(data + 0x18, 0x20);
/* No index allocation, yet. */
NTFS_PUTU32(data + 0x1C, 0);
/* Add last entry. */
/* Indexed MFT record. */
NTFS_PUTU64(data + 0x20, 0);
/* Size of entry. */
NTFS_PUTU32(data + 0x28, 0x10);
/* Flags: Last entry, no child nodes. */
NTFS_PUTU32(data + 0x2C, 2);
/* Compute name. */
ntfs_indexname(name, type);
return ntfs_create_attr(ino, ino->vol->at_index_root, name,
data, sizeof(data), &da);
}
int ntfs_mkdir(ntfs_inode *dir, const char *name, int namelen,
ntfs_inode *result)
{
int error;
error = ntfs_alloc_inode(dir, result, name, namelen, NTFS_AFLAG_DIR);
if (error)
goto out;
error = ntfs_add_index_root(result, 0x30);
if (error)
goto out;
/* Set directory bit. */
result->attr[0x16] |= 2;
error = ntfs_update_inode(dir);
if (error)
goto out;
error = ntfs_update_inode(result);
if (error)
goto out;
out:
return error;
}
/*
* dir.h - Header file for dir.c
*
* Copyright (C) 1997 Rgis Duchesne
*/
#define ITERATE_SPLIT_DONE 1
enum ntfs_iterate_e {
BY_POSITION,
BY_NAME,
DIR_INSERT
};
/* not all fields are used for all operations */
typedef struct ntfs_iterate_s {
enum ntfs_iterate_e type;
ntfs_inode *dir;
union{
ntfs_u64 pos;
int flags;
}u;
char *result; /* pointer to entry if found */
ntfs_u16* name;
int namelen;
int block; /* current index record */
int newblock; /* index record created in a split */
char *new_entry;
int new_entry_size;
/*ntfs_inode* new;*/
} ntfs_iterate_s;
int ntfs_getdir_unsorted(ntfs_inode *ino, ntfs_u32 *p_high, ntfs_u32* p_low,
int (*cb)(ntfs_u8*, void*), void *param);
int ntfs_getdir_byname(ntfs_iterate_s *walk);
int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name);
int ntfs_check_index_record(ntfs_inode *ino, char *record);
int ntfs_getdir_byposition(ntfs_iterate_s *walk);
int ntfs_mkdir(ntfs_inode* dir,const char* name,int namelen, ntfs_inode *ino);
int ntfs_split_indexroot(ntfs_inode *ino);
int ntfs_add_index_root(ntfs_inode *ino, int type);
/*
* fs.c - NTFS driver for Linux 2.4.x
*
* Legato Systems, Inc. (http://www.legato.com) have sponsored Anton
* Altaparmakov to develop NTFS on Linux since June 2001.
*
* Copyright (C) 1995-1997, 1999 Martin von Lwis
* Copyright (C) 1996 Richard Russon
* Copyright (C) 1996-1997 Rgis Duchesne
* Copyright (C) 2000-2001, Anton Altaparmakov (AIA)
*/
#include <linux/config.h>
#include <linux/errno.h>
#include "ntfstypes.h"
#include "struct.h"
#include "util.h"
#include "inode.h"
#include "super.h"
#include "dir.h"
#include "support.h"
#include "macros.h"
#include "sysctl.h"
#include "attr.h"
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/locks.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <linux/blkdev.h>
#include <asm/page.h>
#include <linux/nls.h>
#include <linux/ntfs_fs.h>
/* Forward declarations. */
static struct inode_operations ntfs_dir_inode_operations;
static struct file_operations ntfs_dir_operations;
#define ITEM_SIZE 2040
/* Io functions to user space. */
static void ntfs_putuser(ntfs_io* dest, void *src, ntfs_size_t len)
{
copy_to_user(dest->param, src, len);
dest->param += len;
}
#ifdef CONFIG_NTFS_RW
struct ntfs_getuser_update_vm_s {
const char *user;
struct inode *ino;
loff_t off;
};
static void ntfs_getuser_update_vm(void *dest, ntfs_io *src, ntfs_size_t len)
{
struct ntfs_getuser_update_vm_s *p = src->param;
copy_from_user(dest, p->user, len);
p->user += len;
p->off += len;
}
#endif
/* loff_t is 64 bit signed, so is cool. */
static ssize_t ntfs_read(struct file *filp, char *buf, size_t count,loff_t *off)
{
int error;
ntfs_io io;
ntfs_attribute *attr;
ntfs_inode *ino = NTFS_I(filp->f_dentry->d_inode);
/* Inode is not properly initialized. */
if (!ino)
return -EINVAL;
ntfs_debug(DEBUG_OTHER, "ntfs_read %x, %Lx, %x ->",
(unsigned)ino->i_number, (unsigned long long)*off,
(unsigned)count);
attr = ntfs_find_attr(ino, ino->vol->at_data, NULL);
/* Inode has no unnamed data attribute. */
if (!attr) {
ntfs_debug(DEBUG_OTHER, "ntfs_read: $DATA not found!\n");
return -EINVAL;
}
if (attr->flags & ATTR_IS_ENCRYPTED)
return -EACCES;
/* Read the data. */
io.fn_put = ntfs_putuser;
io.fn_get = 0;
io.param = buf;
io.size = count;
error = ntfs_read_attr(ino, ino->vol->at_data, NULL, *off, &io);
if (error && !io.size) {
ntfs_debug(DEBUG_OTHER, "ntfs_read: read_attr failed with "
"error %i, io size %u.\n", error, io.size);
return error;
}
*off += io.size;
ntfs_debug(DEBUG_OTHER, "ntfs_read: finished. read %u bytes.\n",
io.size);
return io.size;
}
#ifdef CONFIG_NTFS_RW
static ssize_t ntfs_write(struct file *filp, const char *buf, size_t count,
loff_t *pos)
{
int err;
struct inode *vfs_ino = filp->f_dentry->d_inode;
ntfs_inode *ntfs_ino = NTFS_I(vfs_ino);
ntfs_attribute *data;
ntfs_io io;
struct ntfs_getuser_update_vm_s param;
if (!ntfs_ino)
return -EINVAL;
ntfs_debug(DEBUG_LINUX, __FUNCTION__ "(): Entering for inode 0x%lx, "
"*pos 0x%Lx, count 0x%x.\n", ntfs_ino->i_number, *pos,
count);
/* Allows to lock fs ro at any time. */
if (vfs_ino->i_sb->s_flags & MS_RDONLY)
return -EROFS;
data = ntfs_find_attr(ntfs_ino, ntfs_ino->vol->at_data, NULL);
if (!data)
return -EINVAL;
/* Evaluating O_APPEND is the file system's job... */
if (filp->f_flags & O_APPEND)
*pos = vfs_ino->i_size;
if (!data->resident && *pos + count > data->allocated) {
err = ntfs_extend_attr(ntfs_ino, data, *pos + count);
if (err < 0)
return err;
}
param.user = buf;
param.ino = vfs_ino;
param.off = *pos;
io.fn_put = 0;
io.fn_get = ntfs_getuser_update_vm;
io.param = &param;
io.size = count;
io.do_read = 0;
err = ntfs_readwrite_attr(ntfs_ino, data, *pos, &io);
ntfs_debug(DEBUG_LINUX, __FUNCTION__ "(): Returning %i\n", -err);
if (!err) {
*pos += io.size;
if (*pos > vfs_ino->i_size)
vfs_ino->i_size = *pos;
mark_inode_dirty(vfs_ino);
return io.size;
}
return err;
}
#endif
struct ntfs_filldir {
struct inode *dir;
filldir_t filldir;
unsigned int type;
u32 ph, pl;
void *dirent;
char *name;
int namelen;
int ret_code;
};
static int ntfs_printcb(ntfs_u8 *entry, void *param)
{
unsigned long inum = NTFS_GETU64(entry) & 0xffffffffffff;
struct ntfs_filldir *nf = param;
u32 flags = NTFS_GETU32(entry + 0x48);
char show_sys_files = 0;
u8 name_len = NTFS_GETU8(entry + 0x50);
u8 name_type = NTFS_GETU8(entry + 0x51);
int err;
unsigned file_type;
switch (nf->type) {
case ngt_dos:
/* Don't display long names. */
if (!(name_type & 2))
return 0;
break;
case ngt_nt:
/* Don't display short-only names. */
if ((name_type & 3) == 2)
return 0;
break;
case ngt_posix:
break;
case ngt_full:
show_sys_files = 1;
break;
default:
BUG();
}
err = ntfs_encodeuni(NTFS_INO2VOL(nf->dir), (ntfs_u16*)(entry + 0x52),
name_len, &nf->name, &nf->namelen);
if (err) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Skipping "
"unrepresentable file.\n");
err = 0;
goto err_ret;
}
if (!show_sys_files && inum < 0x10UL) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Skipping system "
"file (%s).\n", nf->name);
err = 0;
goto err_ret;
}
/* Do not return ".", as this is faked. */
if (nf->namelen == 1 && nf->name[0] == '.') {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Skipping \".\"\n");
err = 0;
goto err_ret;
}
nf->name[nf->namelen] = 0;
if (flags & 0x10000000) /* FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT */
file_type = DT_DIR;
else
file_type = DT_REG;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Calling filldir for %s with "
"len %i, f_pos 0x%Lx, inode %lu, %s.\n",
nf->name, nf->namelen, (loff_t)(nf->ph << 16) | nf->pl,
inum, file_type == DT_DIR ? "DT_DIR" : "DT_REG");
/*
* Userspace side of filldir expects an off_t rather than an loff_t.
* And it also doesn't like the most significant bit being set as it
* then considers the value to be negative. Thus this implementation
* limits the number of index records to 32766, which should be plenty.
*/
err = nf->filldir(nf->dirent, nf->name, nf->namelen,
(loff_t)(nf->ph << 16) | nf->pl, inum, file_type);
if (err)
nf->ret_code = err;
err_ret:
nf->namelen = 0;
ntfs_free(nf->name);
nf->name = NULL;
return err;
}
/*
* readdir returns '.', then '..', then the directory entries in sequence.
* As the root directory contains an entry for itself, '.' is not emulated for
* the root directory.
*/
static int ntfs_readdir(struct file* filp, void *dirent, filldir_t filldir)
{
struct inode *dir = filp->f_dentry->d_inode;
int err;
struct ntfs_filldir cb;
cb.ret_code = 0;
cb.pl = filp->f_pos & 0xffff;
cb.ph = (filp->f_pos >> 16) & 0x7fff;
filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Entering for inode %lu, "
"f_pos 0x%Lx, i_mode 0x%x, i_count %lu.\n", dir->i_ino,
filp->f_pos, (unsigned int)dir->i_mode,
atomic_read(&dir->i_count));
if (!cb.ph) {
/* Start of directory. Emulate "." and "..". */
if (!cb.pl) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Calling "
"filldir for . with len 1, f_pos 0x%Lx, "
"inode %lu, DT_DIR.\n", filp->f_pos,
dir->i_ino);
cb.ret_code = filldir(dirent, ".", 1, filp->f_pos,
dir->i_ino, DT_DIR);
if (cb.ret_code)
goto done;
cb.pl++;
filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl;
}
if (cb.pl == (u32)1) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Calling "
"filldir for .. with len 2, f_pos 0x%Lx, "
"inode %lu, DT_DIR.\n", filp->f_pos,
parent_ino(filp->f_dentry));
cb.ret_code = filldir(dirent, "..", 2, filp->f_pos,
parent_ino(filp->f_dentry), DT_DIR);
if (cb.ret_code)
goto done;
cb.pl++;
filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl;
}
} else if (cb.ph >= 0x7fff)
/* End of directory. */
goto done;
cb.dir = dir;
cb.filldir = filldir;
cb.dirent = dirent;
cb.type = NTFS_INO2VOL(dir)->ngt;
do {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Looking for next "
"file using ntfs_getdir_unsorted(), f_pos "
"0x%Lx.\n", (loff_t)(cb.ph << 16) | cb.pl);
err = ntfs_getdir_unsorted(NTFS_I(dir), &cb.ph, &cb.pl,
ntfs_printcb, &cb);
} while (!err && !cb.ret_code && cb.ph < 0x7fff);
filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After ntfs_getdir_unsorted()"
" calls, f_pos 0x%Lx.\n", filp->f_pos);
if (!err) {
done:
#ifdef DEBUG
if (!cb.ret_code)
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): EOD, f_pos "
"0x%Lx, returning 0.\n", filp->f_pos);
else
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): filldir "
"returned %i, returning 0, f_pos "
"0x%Lx.\n", cb.ret_code, filp->f_pos);
#endif
return 0;
}
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Returning %i, f_pos 0x%Lx.\n",
err, filp->f_pos);
return err;
}
/* Copied from vfat driver. */
static int simple_getbool(char *s, int *setval)
{
if (s) {
if (!strcmp(s, "1") || !strcmp(s, "yes") || !strcmp(s, "true"))
*setval = 1;
else if (!strcmp(s, "0") || !strcmp(s, "no") ||
!strcmp(s, "false"))
*setval = 0;
else
return 0;
} else
*setval = 1;
return 1;
}
/*
* This needs to be outside parse_options() otherwise a remount will reset
* these unintentionally.
*/
static void init_ntfs_super_block(ntfs_volume* vol)
{
vol->uid = vol->gid = 0;
vol->umask = 0077;
vol->ngt = ngt_nt;
vol->nls_map = (void*)-1;
vol->mft_zone_multiplier = -1;
}
/* Parse the (re)mount options. */
static int parse_options(ntfs_volume *vol, char *opt)
{
char *value; /* Defaults if not specified and !remount. */
ntfs_uid_t uid = -1; /* 0, root user only */
ntfs_gid_t gid = -1; /* 0, root user only */
int umask = -1; /* 0077, owner access only */
unsigned int ngt = -1; /* ngt_nt */
void *nls_map = NULL; /* Try to load the default NLS. */
int use_utf8 = -1; /* If no NLS specified and loading the default
NLS failed use utf8. */
int mft_zone_mul = -1; /* 1 */
if (!opt)
goto done;
for (opt = strtok(opt, ","); opt; opt = strtok(NULL, ",")) {
if ((value = strchr(opt, '=')) != NULL)
*value ++= '\0';
if (strcmp(opt, "uid") == 0) {
if (!value || !*value)
goto needs_arg;
uid = simple_strtoul(value, &value, 0);
if (*value) {
printk(KERN_ERR "NTFS: uid invalid argument\n");
return 0;
}
} else if (strcmp(opt, "gid") == 0) {
if (!value || !*value)
goto needs_arg;
gid = simple_strtoul(value, &value, 0);
if (*value) {
printk(KERN_ERR "NTFS: gid invalid argument\n");
return 0;
}
} else if (strcmp(opt, "umask") == 0) {
if (!value || !*value)
goto needs_arg;
umask = simple_strtoul(value, &value, 0);
if (*value) {
printk(KERN_ERR "NTFS: umask invalid "
"argument\n");
return 0;
}
} else if (strcmp(opt, "mft_zone_multiplier") == 0) {
unsigned long ul;
if (!value || !*value)
goto needs_arg;
ul = simple_strtoul(value, &value, 0);
if (*value) {
printk(KERN_ERR "NTFS: mft_zone_multiplier "
"invalid argument\n");
return 0;
}
if (ul >= 1 && ul <= 4)
mft_zone_mul = ul;
else {
mft_zone_mul = 1;
printk(KERN_WARNING "NTFS: mft_zone_multiplier "
"out of range. Setting to 1.\n");
}
} else if (strcmp(opt, "posix") == 0) {
int val;
if (!value || !*value)
goto needs_arg;
if (!simple_getbool(value, &val))
goto needs_bool;
ngt = val ? ngt_posix : ngt_nt;
} else if (strcmp(opt, "show_sys_files") == 0) {
int val = 0;
if (!value || !*value)
val = 1;
else if (!simple_getbool(value, &val))
goto needs_bool;
ngt = val ? ngt_full : ngt_nt;
} else if (strcmp(opt, "iocharset") == 0) {
if (!value || !*value)
goto needs_arg;
nls_map = load_nls(value);
if (!nls_map) {
printk(KERN_ERR "NTFS: charset not found");
return 0;
}
} else if (strcmp(opt, "utf8") == 0) {
int val = 0;
if (!value || !*value)
val = 1;
else if (!simple_getbool(value, &val))
goto needs_bool;
use_utf8 = val;
} else {
printk(KERN_ERR "NTFS: unkown option '%s'\n", opt);
return 0;
}
}
done:
if (use_utf8 == -1) {
/* utf8 was not specified at all. */
if (!nls_map) {
/*
* No NLS was specified. If first mount, load the
* default NLS, otherwise don't change the NLS setting.
*/
if (vol->nls_map == (void*)-1)
vol->nls_map = load_nls_default();
} else {
/* If an NLS was already loaded, unload it first. */
if (vol->nls_map && vol->nls_map != (void*)-1)
unload_nls(vol->nls_map);
/* Use the specified NLS. */
vol->nls_map = nls_map;
}
} else {
/* utf8 was specified. */
if (use_utf8 && nls_map) {
unload_nls(nls_map);
printk(KERN_ERR "NTFS: utf8 cannot be combined with "
"iocharset.\n");
return 0;
}
/* If an NLS was already loaded, unload it first. */
if (vol->nls_map && vol->nls_map != (void*)-1)
unload_nls(vol->nls_map);
if (!use_utf8) {
/* utf8 was specified as false. */
if (!nls_map)
/* No NLS was specified, load the default. */
vol->nls_map = load_nls_default();
else
/* Use the specified NLS. */
vol->nls_map = nls_map;
} else
/* utf8 was specified as true. */
vol->nls_map = NULL;
}
if (uid != -1)
vol->uid = uid;
if (gid != -1)
vol->gid = gid;
if (umask != -1)
vol->umask = (ntmode_t)umask;
if (ngt != -1)
vol->ngt = ngt;
if (mft_zone_mul != -1) {
/* mft_zone_multiplier was specified. */
if (vol->mft_zone_multiplier != -1) {
/* This is a remount, ignore a change and warn user. */
if (vol->mft_zone_multiplier != mft_zone_mul)
printk(KERN_WARNING "NTFS: Ignoring changes in "
"mft_zone_multiplier on "
"remount. If you want to "
"change this you need to "
"umount and mount again.\n");
} else
/* Use the specified multiplier. */
vol->mft_zone_multiplier = mft_zone_mul;
} else if (vol->mft_zone_multiplier == -1)
/* No multiplier specified and first mount, so set default. */
vol->mft_zone_multiplier = 1;
return 1;
needs_arg:
printk(KERN_ERR "NTFS: %s needs an argument", opt);
return 0;
needs_bool:
printk(KERN_ERR "NTFS: %s needs boolean argument", opt);
return 0;
}
static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *d)
{
struct inode *res = 0;
char *item = 0;
ntfs_iterate_s walk;
int err;
ntfs_debug(DEBUG_NAME1, __FUNCTION__ "(): Looking up %s in directory "
"ino 0x%x.\n", d->d_name.name, (unsigned)dir->i_ino);
walk.name = NULL;
walk.namelen = 0;
/* Convert to wide string. */
lock_kernel();
err = ntfs_decodeuni(NTFS_INO2VOL(dir), (char*)d->d_name.name,
d->d_name.len, &walk.name, &walk.namelen);
if (err)
goto err_ret;
item = ntfs_malloc(ITEM_SIZE);
if (!item) {
err = -ENOMEM;
goto err_ret;
}
/* ntfs_getdir will place the directory entry into item, and the first
* long long is the MFT record number. */
walk.type = BY_NAME;
walk.dir = NTFS_I(dir);
walk.result = item;
if (ntfs_getdir_byname(&walk))
res = iget(dir->i_sb, NTFS_GETU32(item));
d_add(d, res);
ntfs_free(item);
ntfs_free(walk.name);
unlock_kernel();
/* Always return success, the dcache will handle negative entries. */
return NULL;
err_ret:
ntfs_free(walk.name);
unlock_kernel();
return ERR_PTR(err);
}
static struct file_operations ntfs_file_operations = {
llseek: generic_file_llseek,
read: ntfs_read,
#ifdef CONFIG_NTFS_RW
write: ntfs_write,
#endif
open: generic_file_open,
};
static struct inode_operations ntfs_inode_operations;
#ifdef CONFIG_NTFS_RW
static int ntfs_create(struct inode* dir, struct dentry *d, int mode)
{
struct inode *r = 0;
ntfs_inode *ino = 0;
ntfs_volume *vol;
int error = 0;
ntfs_attribute *si;
lock_kernel();
r = new_inode(dir->i_sb);
if (!r) {
error = -ENOMEM;
goto fail;
}
ntfs_debug(DEBUG_OTHER, "ntfs_create %s\n", d->d_name.name);
vol = NTFS_INO2VOL(dir);
ino = NTFS_I(r);
ino->u.index.recordsize = 0;
ino->u.index.clusters_per_record = 0;
error = ntfs_alloc_file(NTFS_I(dir), ino, (char*)d->d_name.name,
d->d_name.len);
if (error) {
ntfs_error("ntfs_alloc_file FAILED: error = %i", error);
goto fail;
}
/* Not doing this one was causing a huge amount of corruption! Now the
* bugger bytes the dust! (-8 (AIA) */
r->i_ino = ino->i_number;
error = ntfs_update_inode(ino);
if (error)
goto fail;
error = ntfs_update_inode(NTFS_I(dir));
if (error)
goto fail;
r->i_uid = vol->uid;
r->i_gid = vol->gid;
/* FIXME: dirty? dev? */
/* Get the file modification times from the standard information. */
si = ntfs_find_attr(ino, vol->at_standard_information, NULL);
if (si) {
char *attr = si->d.data;
r->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18));
r->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr));
r->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8));
}
/* It's not a directory */
r->i_op = &ntfs_inode_operations;
r->i_fop = &ntfs_file_operations;
r->i_mode = S_IFREG | S_IRUGO;
#ifdef CONFIG_NTFS_RW
r->i_mode |= S_IWUGO;
#endif
r->i_mode &= ~vol->umask;
unlock_kernel();
insert_inode_hash(r);
d_instantiate(d, r);
return 0;
fail:
unlock_kernel();
if (r)
iput(r);
return error;
}
static int _linux_ntfs_mkdir(struct inode *dir, struct dentry* d, int mode)
{
int error;
struct inode *r = 0;
ntfs_volume *vol;
ntfs_inode *ino;
ntfs_attribute *si;
lock_kernel();
ntfs_debug (DEBUG_DIR1, "mkdir %s in %x\n", d->d_name.name, dir->i_ino);
error = -ENAMETOOLONG;
if (d->d_name.len > /* FIXME: */ 255)
goto out;
error = -EIO;
r = new_inode(dir->i_sb);
if (!r)
goto out;
vol = NTFS_INO2VOL(dir);
ino = NTFS_I(r);
error = ntfs_mkdir(NTFS_I(dir), d->d_name.name, d->d_name.len,
ino);
if (error)
goto out;
/* Not doing this one was causing a huge amount of corruption! Now the
* bugger bytes the dust! (-8 (AIA) */
r->i_ino = ino->i_number;
r->i_uid = vol->uid;
r->i_gid = vol->gid;
si = ntfs_find_attr(ino, vol->at_standard_information, NULL);
if (si) {
char *attr = si->d.data;
r->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18));
r->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr));
r->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8));
}
/* It's a directory. */
r->i_op = &ntfs_dir_inode_operations;
r->i_fop = &ntfs_dir_operations;
r->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
#ifdef CONFIG_NTFS_RW
r->i_mode |= S_IWUGO;
#endif
r->i_mode &= ~vol->umask;
insert_inode_hash(r);
d_instantiate(d, r);
error = 0;
out:
ntfs_debug (DEBUG_DIR1, "mkdir returns %d\n", error);
unlock_kernel();
return error;
}
#endif
static struct file_operations ntfs_dir_operations = {
read: generic_read_dir,
readdir: ntfs_readdir,
};
static struct inode_operations ntfs_dir_inode_operations = {
lookup: ntfs_lookup,
#ifdef CONFIG_NTFS_RW
create: ntfs_create,
mkdir: _linux_ntfs_mkdir,
#endif
};
/* ntfs_read_inode() is called by the Virtual File System (the kernel layer
* that deals with filesystems) when iget is called requesting an inode not
* already present in the inode table. Typically filesystems have separate
* inode_operations for directories, files and symlinks. */
static void ntfs_read_inode(struct inode* inode)
{
ntfs_volume *vol;
ntfs_inode *ino;
ntfs_attribute *data;
ntfs_attribute *si;
vol = NTFS_INO2VOL(inode);
inode->i_mode = 0;
ntfs_debug(DEBUG_OTHER, "ntfs_read_inode 0x%lx\n", inode->i_ino);
switch (inode->i_ino) {
/* Those are loaded special files. */
case FILE_Mft:
if (!vol->mft_ino || ((vol->ino_flags & 1) == 0))
goto sys_file_error;
ntfs_memcpy(NTFS_I(inode), vol->mft_ino, sizeof(ntfs_inode));
ino = vol->mft_ino;
vol->mft_ino = NTFS_I(inode);
vol->ino_flags &= ~1;
ntfs_free(ino);
ino = vol->mft_ino;
ntfs_debug(DEBUG_OTHER, "Opening $MFT!\n");
break;
case FILE_MftMirr:
if (!vol->mftmirr || ((vol->ino_flags & 2) == 0))
goto sys_file_error;
ntfs_memcpy(NTFS_I(inode), vol->mftmirr, sizeof(ntfs_inode));
ino = vol->mftmirr;
vol->mftmirr = NTFS_I(inode);
vol->ino_flags &= ~2;
ntfs_free(ino);
ino = vol->mftmirr;
ntfs_debug(DEBUG_OTHER, "Opening $MFTMirr!\n");
break;
case FILE_BitMap:
if (!vol->bitmap || ((vol->ino_flags & 4) == 0))
goto sys_file_error;
ntfs_memcpy(NTFS_I(inode), vol->bitmap, sizeof(ntfs_inode));
ino = vol->bitmap;
vol->bitmap = NTFS_I(inode);
vol->ino_flags &= ~4;
ntfs_free(ino);
ino = vol->bitmap;
ntfs_debug(DEBUG_OTHER, "Opening $Bitmap!\n");
break;
case FILE_LogFile ... FILE_AttrDef:
/* No need to log root directory accesses. */
case FILE_Boot ... FILE_UpCase:
ntfs_debug(DEBUG_OTHER, "Opening system file %i!\n",
inode->i_ino);
default:
ino = NTFS_I(inode);
ino->u.index.recordsize = 0;
ino->u.index.clusters_per_record = 0;
if (ntfs_init_inode(ino, NTFS_INO2VOL(inode), inode->i_ino)) {
ntfs_debug(DEBUG_OTHER, "NTFS: Error loading inode "
"0x%x\n", (unsigned int)inode->i_ino);
return;
}
}
/* Set uid/gid from mount options */
inode->i_uid = vol->uid;
inode->i_gid = vol->gid;
inode->i_nlink = 1;
/* Use the size of the data attribute as file size */
data = ntfs_find_attr(ino, vol->at_data, NULL);
if (!data)
inode->i_size = 0;
else
inode->i_size = data->size;
/* Get the file modification times from the standard information. */
si = ntfs_find_attr(ino, vol->at_standard_information, NULL);
if (si) {
char *attr = si->d.data;
inode->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18));
inode->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr));
inode->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8));
}
/* If it has an index root, it's a directory. */
if (ntfs_find_attr(ino, vol->at_index_root, "$I30")) {
ntfs_attribute *at;
at = ntfs_find_attr(ino, vol->at_index_allocation, "$I30");
inode->i_size = at ? at->size : 0;
inode->i_op = &ntfs_dir_inode_operations;
inode->i_fop = &ntfs_dir_operations;
inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
} else {
inode->i_op = &ntfs_inode_operations;
inode->i_fop = &ntfs_file_operations;
inode->i_mode = S_IFREG | S_IRUGO;
}
#ifdef CONFIG_NTFS_RW
if (!data || !(data->flags & (ATTR_IS_COMPRESSED | ATTR_IS_ENCRYPTED)))
inode->i_mode |= S_IWUGO;
#endif
inode->i_mode &= ~vol->umask;
return;
sys_file_error:
ntfs_error("Critical error. Tried to call ntfs_read_inode() before we "
"have completed read_super() or VFS error.\n");
// FIXME: Should we panic() at this stage?
}
#ifdef CONFIG_NTFS_RW
static void ntfs_write_inode(struct inode *ino, int unused)
{
lock_kernel();
ntfs_debug(DEBUG_LINUX, "ntfs_write_inode 0x%x\n", ino->i_ino);
ntfs_update_inode(NTFS_I(ino));
unlock_kernel();
}
#endif
static void _ntfs_clear_inode(struct inode *inode)
{
ntfs_inode *ino;
ntfs_volume *vol;
lock_kernel();
ntfs_debug(DEBUG_OTHER, "_ntfs_clear_inode 0x%x\n", inode->i_ino);
vol = NTFS_INO2VOL(inode);
if (!vol)
ntfs_error("_ntfs_clear_inode: vol = NTFS_INO2VOL(inode) is "
"NULL.\n");
switch (inode->i_ino) {
case FILE_Mft:
if (vol->mft_ino && ((vol->ino_flags & 1) == 0)) {
ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode));
ntfs_memcpy(ino, NTFS_I(inode), sizeof(ntfs_inode));
vol->mft_ino = ino;
vol->ino_flags |= 1;
goto unl_out;
}
break;
case FILE_MftMirr:
if (vol->mftmirr && ((vol->ino_flags & 2) == 0)) {
ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode));
ntfs_memcpy(ino, NTFS_I(inode), sizeof(ntfs_inode));
vol->mftmirr = ino;
vol->ino_flags |= 2;
goto unl_out;
}
break;
case FILE_BitMap:
if (vol->bitmap && ((vol->ino_flags & 4) == 0)) {
ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode));
ntfs_memcpy(ino, NTFS_I(inode), sizeof(ntfs_inode));
vol->bitmap = ino;
vol->ino_flags |= 4;
goto unl_out;
}
break;
default:
/* Nothing. Just clear the inode and exit. */
}
ntfs_clear_inode(NTFS_I(inode));
unl_out:
unlock_kernel();
return;
}
/* Called when umounting a filesystem by do_umount() in fs/super.c. */
static void ntfs_put_super(struct super_block *sb)
{
ntfs_volume *vol;
ntfs_debug(DEBUG_OTHER, "ntfs_put_super\n");
vol = NTFS_SB2VOL(sb);
ntfs_release_volume(vol);
if (vol->nls_map)
unload_nls(vol->nls_map);
ntfs_debug(DEBUG_OTHER, "ntfs_put_super: done\n");
}
/* Called by the kernel when asking for stats. */
static int ntfs_statfs(struct super_block *sb, struct statfs *sf)
{
struct inode *mft;
ntfs_volume *vol;
__s64 size;
int error;
ntfs_debug(DEBUG_OTHER, "ntfs_statfs\n");
vol = NTFS_SB2VOL(sb);
sf->f_type = NTFS_SUPER_MAGIC;
sf->f_bsize = vol->cluster_size;
error = ntfs_get_volumesize(NTFS_SB2VOL(sb), &size);
if (error)
return error;
sf->f_blocks = size; /* Volumesize is in clusters. */
size = (__s64)ntfs_get_free_cluster_count(vol->bitmap);
/* Just say zero if the call failed. */
if (size < 0LL)
size = 0;
sf->f_bfree = sf->f_bavail = size;
ntfs_debug(DEBUG_OTHER, "ntfs_statfs: calling mft = iget(sb, "
"FILE_Mft)\n");
mft = iget(sb, FILE_Mft);
ntfs_debug(DEBUG_OTHER, "ntfs_statfs: iget(sb, FILE_Mft) returned "
"0x%x\n", mft);
if (!mft)
return -EIO;
sf->f_files = mft->i_size >> vol->mft_record_size_bits;
ntfs_debug(DEBUG_OTHER, "ntfs_statfs: calling iput(mft)\n");
iput(mft);
/* Should be read from volume. */
sf->f_namelen = 255;
return 0;
}
/* Called when remounting a filesystem by do_remount_sb() in fs/super.c. */
static int ntfs_remount_fs(struct super_block *sb, int *flags, char *options)
{
if (!parse_options(NTFS_SB2VOL(sb), options))
return -EINVAL;
return 0;
}
/* Define the super block operation that are implemented */
static kmem_cache_t * ntfs_inode_cachep;
static struct inode *__ntfs_alloc_inode(struct super_block *sb)
{
struct ntfs_i *ei;
ei = (struct ntfs_i *)kmem_cache_alloc(ntfs_inode_cachep, SLAB_KERNEL);
if (!ei)
return NULL;
return &ei->vfs_inode;
}
static void ntfs_destroy_inode(struct inode *inode)
{
kmem_cache_free(ntfs_inode_cachep, ntfs_i(inode));
}
static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
{
struct ntfs_i *ei = (struct ntfs_i *) foo;
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
SLAB_CTOR_CONSTRUCTOR)
inode_init_once(&ei->vfs_inode);
}
static int init_inodecache(void)
{
ntfs_inode_cachep = kmem_cache_create("ntfs_inode_cache",
sizeof(struct ntfs_i),
0, SLAB_HWCACHE_ALIGN,
init_once, NULL);
if (ntfs_inode_cachep == NULL)
return -ENOMEM;
return 0;
}
static void destroy_inodecache(void)
{
if (kmem_cache_destroy(ntfs_inode_cachep))
printk(KERN_INFO "ntfs_inode_cache: not all structures were freed\n");
}
static struct super_operations ntfs_super_operations = {
alloc_inode: __ntfs_alloc_inode,
destroy_inode: ntfs_destroy_inode,
read_inode: ntfs_read_inode,
#ifdef CONFIG_NTFS_RW
write_inode: ntfs_write_inode,
#endif
put_super: ntfs_put_super,
statfs: ntfs_statfs,
remount_fs: ntfs_remount_fs,
clear_inode: _ntfs_clear_inode,
};
/**
* is_boot_sector_ntfs - check an NTFS boot sector for validity
* @b: buffer containing bootsector to check
*
* Check whether @b contains a valid NTFS boot sector.
* Return 1 if @b is a valid NTFS bootsector or 0 if not.
*/
static int is_boot_sector_ntfs(ntfs_u8 *b)
{
ntfs_u32 i;
/* FIXME: We don't use checksumming yet as NT4(SP6a) doesn't either...
* But we might as well have the code ready to do it. (AIA) */
#if 0
/* Calculate the checksum. */
if (b < b + 0x50) {
ntfs_u32 *u;
ntfs_u32 *bi = (ntfs_u32 *)(b + 0x50);
for (u = bi, i = 0; u < bi; ++u)
i += NTFS_GETU32(*u);
}
#endif
/* Check magic is "NTFS " */
if (b[3] != 0x4e) goto not_ntfs;
if (b[4] != 0x54) goto not_ntfs;
if (b[5] != 0x46) goto not_ntfs;
if (b[6] != 0x53) goto not_ntfs;
for (i = 7; i < 0xb; ++i)
if (b[i] != 0x20) goto not_ntfs;
/* Check bytes per sector value is between 512 and 4096. */
if (b[0xb] != 0) goto not_ntfs;
if (b[0xc] > 0x10) goto not_ntfs;
/* Check sectors per cluster value is valid. */
switch (b[0xd]) {
case 1: case 2: case 4: case 8: case 16:
case 32: case 64: case 128:
break;
default:
goto not_ntfs;
}
/* Check reserved sectors value and four other fields are zero. */
for (i = 0xe; i < 0x15; ++i)
if (b[i] != 0) goto not_ntfs;
if (b[0x16] != 0) goto not_ntfs;
if (b[0x17] != 0) goto not_ntfs;
for (i = 0x20; i < 0x24; ++i)
if (b[i] != 0) goto not_ntfs;
/* Check clusters per file record segment value is valid. */
if (b[0x40] < 0xe1 || b[0x40] > 0xf7) {
switch (b[0x40]) {
case 1: case 2: case 4: case 8: case 16: case 32: case 64:
break;
default:
goto not_ntfs;
}
}
/* Check clusters per index block value is valid. */
if (b[0x44] < 0xe1 || b[0x44] > 0xf7) {
switch (b[0x44]) {
case 1: case 2: case 4: case 8: case 16: case 32: case 64:
break;
default:
goto not_ntfs;
}
}
return 1;
not_ntfs:
return 0;
}
/* Called to mount a filesystem by read_super() in fs/super.c.
* Return a super block, the main structure of a filesystem.
*
* NOTE : Don't store a pointer to an option, as the page containing the
* options is freed after ntfs_read_super() returns.
*
* NOTE : A context switch can happen in kernel code only if the code blocks
* (= calls schedule() in kernel/sched.c). */
static int ntfs_fill_super(struct super_block *sb, void *options, int silent)
{
ntfs_volume *vol;
struct buffer_head *bh;
int i, to_read, blocksize;
ntfs_debug(DEBUG_OTHER, "ntfs_read_super\n");
vol = NTFS_SB2VOL(sb);
init_ntfs_super_block(vol);
if (!parse_options(vol, (char*)options))
goto ntfs_read_super_vol;
blocksize = sb_min_blocksize(sb, 512);
if (!blocksize) {
ntfs_error("Unable to set blocksize.\n");
goto ntfs_read_super_vol;
}
/* Read the super block (boot block). */
if (!(bh = sb_bread(sb, 0))) {
ntfs_error("Reading super block failed\n");
goto ntfs_read_super_unl;
}
ntfs_debug(DEBUG_OTHER, "Done reading boot block\n");
/* Check for valid 'NTFS' boot sector. */
if (!is_boot_sector_ntfs(bh->b_data)) {
ntfs_debug(DEBUG_OTHER, "Not a NTFS volume\n");
bforget(bh);
goto ntfs_read_super_unl;
}
ntfs_debug(DEBUG_OTHER, "Going to init volume\n");
if (ntfs_init_volume(vol, bh->b_data) < 0) {
ntfs_debug(DEBUG_OTHER, "Init volume failed.\n");
bforget(bh);
goto ntfs_read_super_unl;
}
ntfs_debug(DEBUG_OTHER, "$Mft at cluster 0x%lx\n", vol->mft_lcn);
brelse(bh);
NTFS_SB(vol) = sb;
if (vol->cluster_size > PAGE_SIZE) {
ntfs_error("Partition cluster size is not supported yet (it "
"is > max kernel blocksize).\n");
goto ntfs_read_super_unl;
}
ntfs_debug(DEBUG_OTHER, "Done to init volume\n");
/* Inform the kernel that a device block is a NTFS cluster. */
if (!sb_set_blocksize(sb, vol->cluster_size)) {
ntfs_error("Cluster size too small for device.\n");
goto ntfs_read_super_unl;
}
ntfs_debug(DEBUG_OTHER, "set_blocksize\n");
/* Allocate an MFT record (MFT record can be smaller than a cluster). */
i = vol->cluster_size;
if (i < vol->mft_record_size)
i = vol->mft_record_size;
if (!(vol->mft = ntfs_malloc(i)))
goto ntfs_read_super_unl;
/* Read at least the MFT record for $Mft. */
to_read = vol->mft_clusters_per_record;
if (to_read < 1)
to_read = 1;
for (i = 0; i < to_read; i++) {
if (!(bh = sb_bread(sb, vol->mft_lcn + i))) {
ntfs_error("Could not read $Mft record 0\n");
goto ntfs_read_super_mft;
}
ntfs_memcpy(vol->mft + ((__s64)i << vol->cluster_size_bits),
bh->b_data, vol->cluster_size);
brelse(bh);
ntfs_debug(DEBUG_OTHER, "Read cluster 0x%x\n",
vol->mft_lcn + i);
}
/* Check and fixup this MFT record */
if (!ntfs_check_mft_record(vol, vol->mft)){
ntfs_error("Invalid $Mft record 0\n");
goto ntfs_read_super_mft;
}
/* Inform the kernel about which super operations are available. */
sb->s_op = &ntfs_super_operations;
sb->s_magic = NTFS_SUPER_MAGIC;
sb->s_maxbytes = MAX_LFS_FILESIZE;
ntfs_debug(DEBUG_OTHER, "Reading special files\n");
if (ntfs_load_special_files(vol)) {
ntfs_error("Error loading special files\n");
goto ntfs_read_super_mft;
}
ntfs_debug(DEBUG_OTHER, "Getting RootDir\n");
/* Get the root directory. */
if (!(sb->s_root = d_alloc_root(iget(sb, FILE_root)))) {
ntfs_error("Could not get root dir inode\n");
goto ntfs_read_super_mft;
}
return 0;
ntfs_read_super_mft:
ntfs_free(vol->mft);
ntfs_read_super_unl:
ntfs_read_super_vol:
ntfs_debug(DEBUG_OTHER, "read_super: done\n");
return -EINVAL;
}
/* Define the filesystem */
static struct super_block *ntfs_get_sb(struct file_system_type *fs_type,
int flags, char *dev_name, void *data)
{
return get_sb_bdev(fs_type, flags, dev_name, data, ntfs_fill_super);
}
static struct file_system_type ntfs_fs_type = {
owner: THIS_MODULE,
name: "ntfs",
get_sb: ntfs_get_sb,
fs_flags: FS_REQUIRES_DEV,
};
static int __init init_ntfs_fs(void)
{
int err;
/* Comment this if you trust klogd. There are reasons not to trust it */
#if defined(DEBUG) && !defined(MODULE)
console_verbose();
#endif
printk(KERN_NOTICE "NTFS driver v" NTFS_VERSION " [Flags: R/"
#ifdef CONFIG_NTFS_RW
"W"
#else
"O"
#endif
#ifdef DEBUG
" DEBUG"
#endif
#ifdef MODULE
" MODULE"
#endif
"]\n");
SYSCTL(1);
ntfs_debug(DEBUG_OTHER, "registering %s\n", ntfs_fs_type.name);
err = init_inodecache();
if (err)
goto out1;
err = register_filesystem(&ntfs_fs_type);
if (err)
goto out;
return 0;
out:
destroy_inodecache();
out1:
SYSCTL(0);
return err;
}
static void __exit exit_ntfs_fs(void)
{
SYSCTL(0);
ntfs_debug(DEBUG_OTHER, "unregistering %s\n", ntfs_fs_type.name);
unregister_filesystem(&ntfs_fs_type);
destroy_inodecache();
}
EXPORT_NO_SYMBOLS;
/*
* Not strictly true. The driver was written originally by Martin von Lwis.
* I am just maintaining and rewriting it.
*/
MODULE_AUTHOR("Anton Altaparmakov <aia21@cus.cam.ac.uk>");
MODULE_DESCRIPTION("Linux NTFS driver");
MODULE_LICENSE("GPL");
#ifdef DEBUG
MODULE_PARM(ntdebug, "i");
MODULE_PARM_DESC(ntdebug, "Debug level");
#endif
module_init(init_ntfs_fs)
module_exit(exit_ntfs_fs)
/*
* inode.c
*
* Copyright (C) 1995-1999 Martin von Lwis
* Copyright (C) 1996 Albert D. Cahalan
* Copyright (C) 1996-1997 Rgis Duchesne
* Copyright (C) 1998 Joseph Malicki
* Copyright (C) 1999 Steve Dodd
* Copyright (C) 2000-2001 Anton Altaparmakov (AIA)
*/
#include "ntfstypes.h"
#include "ntfsendian.h"
#include "struct.h"
#include "inode.h"
#include <linux/errno.h>
#include "macros.h"
#include "attr.h"
#include "super.h"
#include "dir.h"
#include "support.h"
#include "util.h"
#include <linux/ntfs_fs.h>
#include <linux/smp_lock.h>
typedef struct {
int recno;
unsigned char *record;
} ntfs_mft_record;
typedef struct {
int size;
int count;
ntfs_mft_record *records;
} ntfs_disk_inode;
static void ntfs_fill_mft_header(ntfs_u8 *mft, int rec_size, int seq_no,
int links, int flags)
{
int fixup_ofs = 0x2a;
int fixup_cnt = rec_size / NTFS_SECTOR_SIZE + 1;
int attr_ofs = (fixup_ofs + 2 * fixup_cnt + 7) & ~7;
NTFS_PUTU32(mft + 0x00, 0x454c4946); /* FILE */
NTFS_PUTU16(mft + 0x04, fixup_ofs); /* Offset to fixup. */
NTFS_PUTU16(mft + 0x06, fixup_cnt); /* Number of fixups. */
NTFS_PUTU64(mft + 0x08, 0); /* Logical sequence number. */
NTFS_PUTU16(mft + 0x10, seq_no); /* Sequence number. */
NTFS_PUTU16(mft + 0x12, links); /* Hard link count. */
NTFS_PUTU16(mft + 0x14, attr_ofs); /* Offset to attributes. */
NTFS_PUTU16(mft + 0x16, flags); /* Flags: 1 = In use,
2 = Directory. */
NTFS_PUTU32(mft + 0x18, attr_ofs + 8); /* Bytes in use. */
NTFS_PUTU32(mft + 0x1c, rec_size); /* Total allocated size. */
NTFS_PUTU64(mft + 0x20, 0); /* Base mft record. */
NTFS_PUTU16(mft + 0x28, 0); /* Next attr instance. */
NTFS_PUTU16(mft + fixup_ofs, 1); /* Fixup word. */
NTFS_PUTU32(mft + attr_ofs, (__u32)-1); /* End of attributes marker. */
}
/*
* Search in an inode an attribute by type and name.
* FIXME: Check that when attributes are inserted all attribute list
* attributes are expanded otherwise need to modify this function to deal
* with attribute lists. (AIA)
*/
ntfs_attribute *ntfs_find_attr(ntfs_inode *ino, int type, char *name)
{
int i;
if (!ino) {
ntfs_error("ntfs_find_attr: NO INODE!\n");
return 0;
}
for (i = 0; i < ino->attr_count; i++) {
if (type < ino->attrs[i].type)
return 0;
if (type == ino->attrs[i].type) {
if (!name) {
if (!ino->attrs[i].name)
return ino->attrs + i;
} else if (ino->attrs[i].name &&
!ntfs_ua_strncmp(ino->attrs[i].name, name,
strlen(name)))
return ino->attrs + i;
}
}
return 0;
}
/*
* Insert all attributes from the record mftno of the MFT in the inode ino.
* If mftno is a base mft record we abort as soon as we find the attribute
* list, but only on the first pass. We will get called later when the attribute
* list attribute is being parsed so we need to distinguish the two cases.
* FIXME: We should be performing structural consistency checks. (AIA)
* Return 0 on success or -errno on error.
*/
static int ntfs_insert_mft_attributes(ntfs_inode* ino, char *mft, int mftno)
{
int i, error, type, len, present = 0;
char *it;
/* Check for duplicate extension record. */
for(i = 0; i < ino->record_count; i++)
if (ino->records[i] == mftno) {
if (i)
return 0;
present = 1;
break;
}
if (!present) {
/* (re-)allocate space if necessary. */
if (ino->record_count % 8 == 0) {
int *new;
new = ntfs_malloc((ino->record_count + 8) *
sizeof(int));
if (!new)
return -ENOMEM;
if (ino->records) {
for (i = 0; i < ino->record_count; i++)
new[i] = ino->records[i];
ntfs_free(ino->records);
}
ino->records = new;
}
ino->records[ino->record_count] = mftno;
ino->record_count++;
}
it = mft + NTFS_GETU16(mft + 0x14); /* mft->attrs_offset */
do {
type = NTFS_GETU32(it);
len = NTFS_GETU32(it + 4);
if (type != -1) {
error = ntfs_insert_attribute(ino, it);
if (error)
return error;
}
/* If we have just processed the attribute list and this is
* the first time we are parsing this (base) mft record then we
* are done so that the attribute list gets parsed before the
* entries in the base mft record. Otherwise we run into
* problems with encountering attributes out of order and when
* this happens with different attribute extents we die. )-:
* This way we are ok as the attribute list is always sorted
* fully and correctly. (-: */
if (type == 0x20 && !present)
return 0;
it += len;
} while (type != -1); /* Attribute listing ends with type -1. */
return 0;
}
/*
* Insert a single specific attribute from the record mftno of the MFT in the
* inode ino. We disregard the attribute list assuming we have already parsed
* it.
* FIXME: We should be performing structural consistency checks. (AIA)
* Return 0 on success or -errno on error.
*/
static int ntfs_insert_mft_attribute(ntfs_inode* ino, int mftno,
ntfs_u8 *attr)
{
int i, error, present = 0;
/* Check for duplicate extension record. */
for(i = 0; i < ino->record_count; i++)
if (ino->records[i] == mftno) {
present = 1;
break;
}
if (!present) {
/* (re-)allocate space if necessary. */
if (ino->record_count % 8 == 0) {
int *new;
new = ntfs_malloc((ino->record_count + 8) *
sizeof(int));
if (!new)
return -ENOMEM;
if (ino->records) {
for (i = 0; i < ino->record_count; i++)
new[i] = ino->records[i];
ntfs_free(ino->records);
}
ino->records = new;
}
ino->records[ino->record_count] = mftno;
ino->record_count++;
}
if (NTFS_GETU32(attr) == -1) {
ntfs_debug(DEBUG_FILE3, "ntfs_insert_mft_attribute: attribute "
"type is -1.\n");
return 0;
}
error = ntfs_insert_attribute(ino, attr);
if (error)
return error;
return 0;
}
/* Read and insert all the attributes of an 'attribute list' attribute.
* Return the number of remaining bytes in *plen. */
static int parse_attributes(ntfs_inode *ino, ntfs_u8 *alist, int *plen)
{
ntfs_u8 *mft, *attr;
int mftno, l, error;
int last_mft = -1;
int len = *plen;
int tries = 0;
if (!ino->attr) {
ntfs_error("parse_attributes: called on inode 0x%x without a "
"loaded base mft record.\n", ino->i_number);
return -EINVAL;
}
mft = ntfs_malloc(ino->vol->mft_record_size);
if (!mft)
return -ENOMEM;
while (len > 8) {
l = NTFS_GETU16(alist + 4);
if (l > len)
break;
/* Process an attribute description. */
mftno = NTFS_GETU32(alist + 0x10);
/* FIXME: The mft reference (alist + 0x10) is __s64.
* - Not a problem unless we encounter a huge partition.
* - Should be consistency checking the sequence numbers
* though! This should maybe happen in
* ntfs_read_mft_record() itself and a hotfix could
* then occur there or the user notified to run
* ntfsck. (AIA) */
if (mftno != ino->i_number && mftno != last_mft) {
continue_after_loading_mft_data:
last_mft = mftno;
error = ntfs_read_mft_record(ino->vol, mftno, mft);
if (error) {
if (error == -EINVAL && !tries)
goto force_load_mft_data;
failed_reading_mft_data:
ntfs_debug(DEBUG_FILE3, "parse_attributes: "
"ntfs_read_mft_record(mftno = 0x%x) "
"failed\n", mftno);
ntfs_free(mft);
return error;
}
}
attr = ntfs_find_attr_in_mft_rec(
ino->vol, /* ntfs volume */
mftno == ino->i_number ?/* mft record is: */
ino->attr: /* base record */
mft, /* extension record */
NTFS_GETU32(alist + 0), /* type */
(wchar_t*)(alist + alist[7]), /* name */
alist[6], /* name length */
1, /* ignore case */
NTFS_GETU16(alist + 24) /* instance number */
);
if (!attr) {
ntfs_error("parse_attributes: mft records 0x%x and/or "
"0x%x corrupt!\n", ino->i_number, mftno);
ntfs_free(mft);
return -EINVAL; /* FIXME: Better error code? (AIA) */
}
error = ntfs_insert_mft_attribute(ino, mftno, attr);
if (error) {
ntfs_debug(DEBUG_FILE3, "parse_attributes: "
"ntfs_insert_mft_attribute(mftno 0x%x, "
"attribute type 0x%x) failed\n", mftno,
NTFS_GETU32(alist + 0));
ntfs_free(mft);
return error;
}
len -= l;
alist += l;
}
ntfs_free(mft);
*plen = len;
return 0;
force_load_mft_data:
{
ntfs_u8 *mft2, *attr2;
int mftno2;
int last_mft2 = last_mft;
int len2 = len;
int error2;
int found2 = 0;
ntfs_u8 *alist2 = alist;
/*
* We only get here if $DATA wasn't found in $MFT which only happens
* on volume mount when $MFT has an attribute list and there are
* attributes before $DATA which are inside extent mft records. So
* we just skip forward to the $DATA attribute and read that. Then we
* restart which is safe as an attribute will not be inserted twice.
*
* This still will not fix the case where the attribute list is non-
* resident, larger than 1024 bytes, and the $DATA attribute list entry
* is not in the first 1024 bytes. FIXME: This should be implemented
* somehow! Perhaps by passing special error code up to
* ntfs_load_attributes() so it keeps going trying to get to $DATA
* regardless. Then it would have to restart just like we do here.
*/
mft2 = ntfs_malloc(ino->vol->mft_record_size);
if (!mft2) {
ntfs_free(mft);
return -ENOMEM;
}
ntfs_memcpy(mft2, mft, ino->vol->mft_record_size);
while (len2 > 8) {
l = NTFS_GETU16(alist2 + 4);
if (l > len2)
break;
if (NTFS_GETU32(alist2 + 0x0) < ino->vol->at_data) {
len2 -= l;
alist2 += l;
continue;
}
if (NTFS_GETU32(alist2 + 0x0) > ino->vol->at_data) {
if (found2)
break;
/* Uh-oh! It really isn't there! */
ntfs_error("Either the $MFT is corrupt or, equally "
"likely, the $MFT is too complex for "
"the current driver to handle. Please "
"email the ntfs maintainer that you "
"saw this message. Thank you.\n");
goto failed_reading_mft_data;
}
/* Process attribute description. */
mftno2 = NTFS_GETU32(alist2 + 0x10);
if (mftno2 != ino->i_number && mftno2 != last_mft2) {
last_mft2 = mftno2;
error2 = ntfs_read_mft_record(ino->vol, mftno2, mft2);
if (error2) {
ntfs_debug(DEBUG_FILE3, "parse_attributes: "
"ntfs_read_mft_record(mftno2 = 0x%x) "
"failed\n", mftno2);
ntfs_free(mft2);
goto failed_reading_mft_data;
}
}
attr2 = ntfs_find_attr_in_mft_rec(
ino->vol, /* ntfs volume */
mftno2 == ino->i_number ?/* mft record is: */
ino->attr: /* base record */
mft2, /* extension record */
NTFS_GETU32(alist2 + 0), /* type */
(wchar_t*)(alist2 + alist2[7]), /* name */
alist2[6], /* name length */
1, /* ignore case */
NTFS_GETU16(alist2 + 24) /* instance number */
);
if (!attr2) {
ntfs_error("parse_attributes: mft records 0x%x and/or "
"0x%x corrupt!\n", ino->i_number,
mftno2);
ntfs_free(mft2);
goto failed_reading_mft_data;
}
error2 = ntfs_insert_mft_attribute(ino, mftno2, attr2);
if (error2) {
ntfs_debug(DEBUG_FILE3, "parse_attributes: "
"ntfs_insert_mft_attribute(mftno2 0x%x, "
"attribute2 type 0x%x) failed\n", mftno2,
NTFS_GETU32(alist2 + 0));
ntfs_free(mft2);
goto failed_reading_mft_data;
}
len2 -= l;
alist2 += l;
found2 = 1;
}
ntfs_free(mft2);
tries = 1;
goto continue_after_loading_mft_data;
}
}
static void ntfs_load_attributes(ntfs_inode *ino)
{
ntfs_attribute *alist;
int datasize;
int offset, len, delta;
char *buf;
ntfs_volume *vol = ino->vol;
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 1\n", ino->i_number);
if (ntfs_insert_mft_attributes(ino, ino->attr, ino->i_number))
return;
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 2\n", ino->i_number);
alist = ntfs_find_attr(ino, vol->at_attribute_list, 0);
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 3\n", ino->i_number);
if (!alist)
return;
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 4\n", ino->i_number);
datasize = alist->size;
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: alist->size = 0x%x\n",
ino->i_number, alist->size);
if (alist->resident) {
parse_attributes(ino, alist->d.data, &datasize);
return;
}
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 5\n", ino->i_number);
buf = ntfs_malloc(1024);
if (!buf) /* FIXME: Should be passing error code to caller. (AIA) */
return;
delta = 0;
for (offset = 0; datasize; datasize -= len, offset += len) {
ntfs_io io;
io.fn_put = ntfs_put;
io.fn_get = 0;
io.param = buf + delta;
len = 1024 - delta;
if (len > datasize)
len = datasize;
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: len = %i\n",
ino->i_number, len);
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: delta = %i\n",
ino->i_number, delta);
io.size = len;
if (ntfs_read_attr(ino, vol->at_attribute_list, 0, offset,
&io))
ntfs_error("error in load_attributes\n");
delta += len;
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: after += len, "
"delta = %i\n", ino->i_number, delta);
parse_attributes(ino, buf, &delta);
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: after "
"parse_attr, delta = %i\n", ino->i_number,
delta);
if (delta)
/* Move remaining bytes to buffer start. */
ntfs_memmove(buf, buf + len - delta, delta);
}
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 6\n", ino->i_number);
ntfs_free(buf);
}
int ntfs_init_inode(ntfs_inode *ino, ntfs_volume *vol, int inum)
{
char *buf;
int error;
ntfs_debug(DEBUG_FILE1, "Initializing inode 0x%x\n", inum);
ino->i_number = inum;
ino->vol = vol;
ino->attr = buf = ntfs_malloc(vol->mft_record_size);
if (!buf)
return -ENOMEM;
error = ntfs_read_mft_record(vol, inum, ino->attr);
if (error) {
ntfs_debug(DEBUG_OTHER, "Init inode: 0x%x failed\n", inum);
return error;
}
ntfs_debug(DEBUG_FILE2, "Init inode: got mft 0x%x\n", inum);
ino->sequence_number = NTFS_GETU16(buf + 0x10);
ino->attr_count = 0;
ino->record_count = 0;
ino->records = 0;
ino->attrs = 0;
ntfs_load_attributes(ino);
ntfs_debug(DEBUG_FILE2, "Init inode: done 0x%x\n", inum);
return 0;
}
void ntfs_clear_inode(ntfs_inode *ino)
{
int i;
if (!ino->attr) {
ntfs_error("ntfs_clear_inode: double free\n");
return;
}
ntfs_free(ino->attr);
ino->attr = 0;
ntfs_free(ino->records);
ino->records = 0;
for (i = 0; i < ino->attr_count; i++) {
if (ino->attrs[i].name)
ntfs_free(ino->attrs[i].name);
if (ino->attrs[i].resident) {
if (ino->attrs[i].d.data)
ntfs_free(ino->attrs[i].d.data);
} else {
if (ino->attrs[i].d.r.runlist)
ntfs_vfree(ino->attrs[i].d.r.runlist);
}
}
ntfs_free(ino->attrs);
ino->attrs = 0;
}
/* Check and fixup a MFT record. */
int ntfs_check_mft_record(ntfs_volume *vol, char *record)
{
return ntfs_fixup_record(record, "FILE", vol->mft_record_size);
}
/* Return (in result) the value indicating the next available attribute
* chunk number. Works for inodes w/o extension records only. */
int ntfs_allocate_attr_number(ntfs_inode *ino, int *result)
{
if (ino->record_count != 1)
return -EOPNOTSUPP;
*result = NTFS_GETU16(ino->attr + 0x28);
NTFS_PUTU16(ino->attr + 0x28, (*result) + 1);
return 0;
}
/* Find the location of an attribute in the inode. A name of NULL indicates
* unnamed attributes. Return pointer to attribute or NULL if not found. */
char *ntfs_get_attr(ntfs_inode *ino, int attr, char *name)
{
/* Location of first attribute. */
char *it = ino->attr + NTFS_GETU16(ino->attr + 0x14);
int type;
int len;
/* Only check for magic DWORD here, fixup should have happened before.*/
if (!IS_MFT_RECORD(ino->attr))
return 0;
do {
type = NTFS_GETU32(it);
len = NTFS_GETU16(it + 4);
/* We found the attribute type. Is the name correct, too? */
if (type == attr) {
int namelen = NTFS_GETU8(it + 9);
char *name_it, *n = name;
/* Match given name and attribute name if present.
Make sure attribute name is Unicode. */
if (!name) {
goto check_namelen;
} else if (namelen) {
for (name_it = it + NTFS_GETU16(it + 10);
namelen; n++, name_it += 2, namelen--)
if (*name_it != *n || name_it[1])
break;
check_namelen:
if (!namelen)
break;
}
}
it += len;
} while (type != -1); /* List of attributes ends with type -1. */
if (type == -1)
return 0;
return it;
}
__s64 ntfs_get_attr_size(ntfs_inode *ino, int type, char *name)
{
ntfs_attribute *attr = ntfs_find_attr(ino, type, name);
if (!attr)
return 0;
return
attr->size;
}
int ntfs_attr_is_resident(ntfs_inode *ino, int type, char *name)
{
ntfs_attribute *attr = ntfs_find_attr(ino, type, name);
if (!attr)
return 0;
return attr->resident;
}
/*
* A run is coded as a type indicator, an unsigned length, and a signed cluster
* offset.
* . To save space, length and offset are fields of variable length. The low
* nibble of the type indicates the width of the length :), the high nibble
* the width of the offset.
* . The first offset is relative to cluster 0, later offsets are relative to
* the previous cluster.
*
* This function decodes a run. Length is an output parameter, data and cluster
* are in/out parameters.
*/
int ntfs_decompress_run(unsigned char **data, int *length,
ntfs_cluster_t *cluster, int *ctype)
{
unsigned char type = *(*data)++;
*ctype = 0;
switch (type & 0xF) {
case 1:
*length = NTFS_GETS8(*data);
break;
case 2:
*length = NTFS_GETS16(*data);
break;
case 3:
*length = NTFS_GETS24(*data);
break;
case 4:
*length = NTFS_GETS32(*data);
break;
/* Note: cases 5-8 are probably pointless to code, since how
* many runs > 4GB of length are there? At the most, cases 5
* and 6 are probably necessary, and would also require making
* length 64-bit throughout. */
default:
ntfs_error("Can't decode run type field 0x%x\n", type);
return -1;
}
// ntfs_debug(DEBUG_FILE3, "ntfs_decompress_run: length = 0x%x\n",*length);
if (*length < 0)
{
ntfs_error("Negative run length decoded\n");
return -1;
}
*data += (type & 0xF);
switch (type & 0xF0) {
case 0:
*ctype = 2;
break;
case 0x10:
*cluster += NTFS_GETS8(*data);
break;
case 0x20:
*cluster += NTFS_GETS16(*data);
break;
case 0x30:
*cluster += NTFS_GETS24(*data);
break;
case 0x40:
*cluster += NTFS_GETS32(*data);
break;
#if 0 /* Keep for future, in case ntfs_cluster_t ever becomes 64bit. */
case 0x50:
*cluster += NTFS_GETS40(*data);
break;
case 0x60:
*cluster += NTFS_GETS48(*data);
break;
case 0x70:
*cluster += NTFS_GETS56(*data);
break;
case 0x80:
*cluster += NTFS_GETS64(*data);
break;
#endif
default:
ntfs_error("Can't decode run type field 0x%x\n", type);
return -1;
}
// ntfs_debug(DEBUG_FILE3, "ntfs_decompress_run: cluster = 0x%x\n",
// *cluster);
*data += (type >> 4);
return 0;
}
static void dump_runlist(const ntfs_runlist *rl, const int rlen);
/*
* FIXME: ntfs_readwrite_attr() has the effect of writing @dest to @offset of
* the attribute value of the attribute @attr in the in memory inode @ino.
* If the attribute value of @attr is non-resident the value's contents at
* @offset are actually written to disk (from @dest). The on disk mft record
* describing the non-resident attribute value is not updated!
* If the attribute value is resident then the value is written only in
* memory. The on disk mft record containing the value is not written to disk.
* A possible fix would be to call ntfs_update_inode() before returning. (AIA)
*/
/* Reads l bytes of the attribute (attr, name) of ino starting at offset on
* vol into buf. Returns the number of bytes read in the ntfs_io struct.
* Returns 0 on success, errno on failure */
int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
ntfs_io *dest)
{
int rnum, s_vcn, error, clustersizebits;
ntfs_cluster_t cluster, s_cluster, vcn, len;
__s64 l, chunk, copied;
ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): %s 0x%x bytes at offset "
"0x%Lx %s inode 0x%x, attr type 0x%x.\n",
dest->do_read ? "Read" : "Write", dest->size, offset,
dest->do_read ? "from" : "to", ino->i_number,
attr->type);
l = dest->size;
if (l == 0)
return 0;
if (dest->do_read) {
/* If read _starts_ beyond end of stream, return nothing. */
if (offset >= attr->size) {
dest->size = 0;
return 0;
}
/* If read _extends_ beyond end of stream, return as much
* initialised data as we have. */
if (offset + l >= attr->size)
l = dest->size = attr->size - offset;
} else {
/*
* If write extends beyond _allocated_ size, extend attribute,
* updating attr->allocated and attr->size in the process. (AIA)
*/
if ((!attr->resident && offset + l > attr->allocated) ||
(attr->resident && offset + l > attr->size)) {
error = ntfs_resize_attr(ino, attr, offset + l);
if (error)
return error;
}
if (!attr->resident) {
/* Has amount of data increased? */
if (offset + l > attr->size)
attr->size = offset + l;
/* Has amount of initialised data increased? */
if (offset + l > attr->initialized) {
/* FIXME: Clear the section between the old
* initialised length and the write start.
* (AIA) */
attr->initialized = offset + l;
}
}
}
if (attr->resident) {
if (dest->do_read)
dest->fn_put(dest, (ntfs_u8*)attr->d.data + offset, l);
else
dest->fn_get((ntfs_u8*)attr->d.data + offset, dest, l);
dest->size = l;
return 0;
}
if (dest->do_read) {
/* Read uninitialized data. */
if (offset >= attr->initialized)
return ntfs_read_zero(dest, l);
if (offset + l > attr->initialized) {
dest->size = chunk = attr->initialized - offset;
error = ntfs_readwrite_attr(ino, attr, offset, dest);
if (error || (dest->size != chunk && (error = -EIO, 1)))
return error;
dest->size += l - chunk;
return ntfs_read_zero(dest, l - chunk);
}
if (attr->flags & ATTR_IS_COMPRESSED)
return ntfs_read_compressed(ino, attr, offset, dest);
} else {
if (attr->flags & ATTR_IS_COMPRESSED)
return ntfs_write_compressed(ino, attr, offset, dest);
}
vcn = 0;
clustersizebits = ino->vol->cluster_size_bits;
s_vcn = offset >> clustersizebits;
for (rnum = 0; rnum < attr->d.r.len &&
vcn + attr->d.r.runlist[rnum].len <= s_vcn; rnum++)
vcn += attr->d.r.runlist[rnum].len;
if (rnum == attr->d.r.len) {
ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): EOPNOTSUPP: "
"inode = 0x%x, rnum = %i, offset = 0x%Lx, vcn = 0x%x, "
"s_vcn = 0x%x.\n", ino->i_number, rnum, offset, vcn,
s_vcn);
dump_runlist(attr->d.r.runlist, attr->d.r.len);
/*FIXME: Should extend runlist. */
return -EOPNOTSUPP;
}
copied = 0;
while (l) {
s_vcn = offset >> clustersizebits;
cluster = attr->d.r.runlist[rnum].lcn;
len = attr->d.r.runlist[rnum].len;
s_cluster = cluster + s_vcn - vcn;
chunk = ((__s64)(vcn + len) << clustersizebits) - offset;
if (chunk > l)
chunk = l;
dest->size = chunk;
error = ntfs_getput_clusters(ino->vol, s_cluster, offset -
((__s64)s_vcn << clustersizebits), dest);
if (error) {
ntfs_error("Read/write error.\n");
dest->size = copied;
return error;
}
l -= chunk;
copied += chunk;
offset += chunk;
if (l && offset >= ((__s64)(vcn + len) << clustersizebits)) {
rnum++;
vcn += len;
cluster = attr->d.r.runlist[rnum].lcn;
len = attr->d.r.runlist[rnum].len;
}
}
dest->size = copied;
return 0;
}
int ntfs_read_attr(ntfs_inode *ino, int type, char *name, __s64 offset,
ntfs_io *buf)
{
ntfs_attribute *attr;
buf->do_read = 1;
attr = ntfs_find_attr(ino, type, name);
if (!attr) {
ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): attr 0x%x not found "
"in inode 0x%x\n", type, ino->i_number);
return -EINVAL;
}
return ntfs_readwrite_attr(ino, attr, offset, buf);
}
int ntfs_write_attr(ntfs_inode *ino, int type, char *name, __s64 offset,
ntfs_io *buf)
{
ntfs_attribute *attr;
buf->do_read = 0;
attr = ntfs_find_attr(ino, type, name);
if (!attr) {
ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): attr 0x%x not found "
"in inode 0x%x\n", type, ino->i_number);
return -EINVAL;
}
return ntfs_readwrite_attr(ino, attr, offset, buf);
}
/* -2 = error, -1 = hole, >= 0 means real disk cluster (lcn). */
int ntfs_vcn_to_lcn(ntfs_inode *ino, int vcn)
{
int rnum;
ntfs_attribute *data;
data = ntfs_find_attr(ino, ino->vol->at_data, 0);
if (!data || data->resident || data->flags & (ATTR_IS_COMPRESSED |
ATTR_IS_ENCRYPTED))
return -2;
if (data->size <= (__s64)vcn << ino->vol->cluster_size_bits)
return -2;
if (data->initialized <= (__s64)vcn << ino->vol->cluster_size_bits)
return -1;
for (rnum = 0; rnum < data->d.r.len &&
vcn >= data->d.r.runlist[rnum].len; rnum++)
vcn -= data->d.r.runlist[rnum].len;
if (data->d.r.runlist[rnum].lcn >= 0)
return data->d.r.runlist[rnum].lcn + vcn;
return data->d.r.runlist[rnum].lcn + vcn;
}
static int allocate_store(ntfs_volume *vol, ntfs_disk_inode *store, int count)
{
int i;
if (store->count > count)
return 0;
if (store->size < count) {
ntfs_mft_record *n = ntfs_malloc((count + 4) *
sizeof(ntfs_mft_record));
if (!n)
return -ENOMEM;
if (store->size) {
for (i = 0; i < store->size; i++)
n[i] = store->records[i];
ntfs_free(store->records);
}
store->size = count + 4;
store->records = n;
}
for (i = store->count; i < count; i++) {
store->records[i].record = ntfs_malloc(vol->mft_record_size);
if (!store->records[i].record)
return -ENOMEM;
store->count++;
}
return 0;
}
static void deallocate_store(ntfs_disk_inode* store)
{
int i;
for (i = 0; i < store->count; i++)
ntfs_free(store->records[i].record);
ntfs_free(store->records);
store->count = store->size = 0;
store->records = 0;
}
/**
* layout_runs - compress runlist into mapping pairs array
* @attr: attribute containing the runlist to compress
* @rec: destination buffer to hold the mapping pairs array
* @offs: current position in @rec (in/out variable)
* @size: size of the buffer @rec
*
* layout_runs walks the runlist in @attr, compresses it and writes it out the
* resulting mapping pairs array into @rec (up to a maximum of @size bytes are
* written). On entry @offs is the offset in @rec at which to begin writing the
* mapping pairs array. On exit, it contains the offset in @rec of the first
* byte after the end of the mapping pairs array.
*/
static int layout_runs(ntfs_attribute *attr, char *rec, int *offs, int size)
{
int i, len, offset, coffs;
/* ntfs_cluster_t MUST be signed! (AIA) */
ntfs_cluster_t cluster, rclus;
ntfs_runlist *rl = attr->d.r.runlist;
cluster = 0;
offset = *offs;
for (i = 0; i < attr->d.r.len; i++) {
/*
* We cheat with this check on the basis that lcn will never
* be less than -1 and the lcn delta will fit in signed
* 32-bits (ntfs_cluster_t). (AIA)
*/
if (rl[i].lcn < (ntfs_cluster_t)-1) {
ntfs_error("layout_runs() encountered an out of bounds "
"cluster delta, lcn = %i.\n",
rl[i].lcn);
return -ERANGE;
}
rclus = rl[i].lcn - cluster;
len = rl[i].len;
rec[offset] = 0;
if (offset + 9 > size)
return -E2BIG; /* It might still fit, but this
* simplifies testing. */
/*
* Run length is stored as signed number, so deal with it
* properly, i.e. observe that a negative number will have all
* its most significant bits set to 1 but we don't store that
* in the mapping pairs array. We store the smallest type of
* negative number required, thus in the first if we check
* whether len fits inside a signed byte and if so we store it
* as such, the next ifs check for a signed short, then a signed
* 24-bit and finally the full blown signed 32-bit. Same goes
* for rlus below. (AIA)
*/
if (len >= -0x80 && len <= 0x7f) {
NTFS_PUTU8(rec + offset + 1, len & 0xff);
coffs = 1;
} else if (len >= -0x8000 && len <= 0x7fff) {
NTFS_PUTU16(rec + offset + 1, len & 0xffff);
coffs = 2;
} else if (len >= -0x800000 && len <= 0x7fffff) {
NTFS_PUTU24(rec + offset + 1, len & 0xffffff);
coffs = 3;
} else /* if (len >= -0x80000000LL && len <= 0x7fffffff */ {
NTFS_PUTU32(rec + offset + 1, len);
coffs = 4;
} /* else ... FIXME: When len becomes 64-bit we need to extend
* the else if () statements. (AIA) */
*(rec + offset) |= coffs++;
if (rl[i].lcn == (ntfs_cluster_t)-1) /* Compressed run. */
/* Nothing */;
else if (rclus >= -0x80 && rclus <= 0x7f) {
*(rec + offset) |= 0x10;
NTFS_PUTS8(rec + offset + coffs, rclus & 0xff);
coffs += 1;
} else if (rclus >= -0x8000 && rclus <= 0x7fff) {
*(rec + offset) |= 0x20;
NTFS_PUTS16(rec + offset + coffs, rclus & 0xffff);
coffs += 2;
} else if (rclus >= -0x800000 && rclus <= 0x7fffff) {
*(rec + offset) |= 0x30;
NTFS_PUTS24(rec + offset + coffs, rclus & 0xffffff);
coffs += 3;
} else /* if (rclus >= -0x80000000LL && rclus <= 0x7fffffff)*/ {
*(rec + offset) |= 0x40;
NTFS_PUTS32(rec + offset + coffs, rclus
/* & 0xffffffffLL */);
coffs += 4;
} /* FIXME: When rclus becomes 64-bit.
else if (rclus >= -0x8000000000 && rclus <= 0x7FFFFFFFFF) {
*(rec + offset) |= 0x50;
NTFS_PUTS40(rec + offset + coffs, rclus &
0xffffffffffLL);
coffs += 5;
} else if (rclus >= -0x800000000000 &&
rclus <= 0x7FFFFFFFFFFF) {
*(rec + offset) |= 0x60;
NTFS_PUTS48(rec + offset + coffs, rclus &
0xffffffffffffLL);
coffs += 6;
} else if (rclus >= -0x80000000000000 &&
rclus <= 0x7FFFFFFFFFFFFF) {
*(rec + offset) |= 0x70;
NTFS_PUTS56(rec + offset + coffs, rclus &
0xffffffffffffffLL);
coffs += 7;
} else {
*(rec + offset) |= 0x80;
NTFS_PUTS64(rec + offset + coffs, rclus);
coffs += 8;
} */
offset += coffs;
if (rl[i].lcn)
cluster = rl[i].lcn;
}
if (offset >= size)
return -E2BIG;
/* Terminating null. */
*(rec + offset++) = 0;
*offs = offset;
return 0;
}
static void count_runs(ntfs_attribute *attr, char *buf)
{
ntfs_u32 first, count, last, i;
first = 0;
for (i = 0, count = 0; i < attr->d.r.len; i++)
count += attr->d.r.runlist[i].len;
last = first + count - 1;
NTFS_PUTU64(buf + 0x10, first);
NTFS_PUTU64(buf + 0x18, last);
}
/**
* layout_attr - convert in memory attribute to on disk attribute record
* @attr: in memory attribute to convert
* @buf: destination buffer for on disk attribute record
* @size: size of the destination buffer
* @psize: size of converted on disk attribute record (out variable)
*
* layout_attr() takes the attribute @attr and converts it into the appropriate
* on disk structure, writing it into @buf (up to @size bytes are written).
*
* On success we return 0 and set @*psize to the actual byte size of the on-
* disk attribute that was written into @buf.
*/
static int layout_attr(ntfs_attribute *attr, char *buf, int size, int *psize)
{
int nameoff, hdrsize, asize;
if (attr->resident) {
nameoff = 0x18;
hdrsize = (nameoff + 2 * attr->namelen + 7) & ~7;
asize = (hdrsize + attr->size + 7) & ~7;
if (size < asize)
return -E2BIG;
NTFS_PUTU32(buf + 0x10, attr->size);
NTFS_PUTU8(buf + 0x16, attr->indexed);
NTFS_PUTU16(buf + 0x14, hdrsize);
if (attr->size)
ntfs_memcpy(buf + hdrsize, attr->d.data, attr->size);
} else {
int error;
if (attr->flags & ATTR_IS_COMPRESSED)
nameoff = 0x48;
else
nameoff = 0x40;
hdrsize = (nameoff + 2 * attr->namelen + 7) & ~7;
if (size < hdrsize)
return -E2BIG;
/* Make asize point at the end of the attribute record header,
i.e. at the beginning of the mapping pairs array. */
asize = hdrsize;
error = layout_runs(attr, buf, &asize, size);
/* Now, asize points one byte beyond the end of the mapping
pairs array. */
if (error)
return error;
/* The next attribute has to begin on 8-byte boundary. */
asize = (asize + 7) & ~7;
/* FIXME: fragments */
count_runs(attr, buf);
NTFS_PUTU16(buf + 0x20, hdrsize);
NTFS_PUTU16(buf + 0x22, attr->cengine);
NTFS_PUTU32(buf + 0x24, 0);
NTFS_PUTS64(buf + 0x28, attr->allocated);
NTFS_PUTS64(buf + 0x30, attr->size);
NTFS_PUTS64(buf + 0x38, attr->initialized);
if (attr->flags & ATTR_IS_COMPRESSED)
NTFS_PUTS64(buf + 0x40, attr->compsize);
}
NTFS_PUTU32(buf, attr->type);
NTFS_PUTU32(buf + 4, asize);
NTFS_PUTU8(buf + 8, attr->resident ? 0 : 1);
NTFS_PUTU8(buf + 9, attr->namelen);
NTFS_PUTU16(buf + 0xa, nameoff);
NTFS_PUTU16(buf + 0xc, attr->flags);
NTFS_PUTU16(buf + 0xe, attr->attrno);
if (attr->namelen)
ntfs_memcpy(buf + nameoff, attr->name, 2 * attr->namelen);
*psize = asize;
return 0;
}
/**
* layout_inode - convert an in-memory inode into on disk mft record(s)
* @ino: in memory inode to convert
* @store: on disk inode, contain buffers for the on disk mft record(s)
*
* layout_inode takes the in memory inode @ino, converts it into a (sequence of)
* mft record(s) and writes them to the appropriate buffers in the @store.
*
* Return 0 on success,
* the required mft record count (>0) if the inode does not fit,
* -ENOMEM if memory allocation problem, or
* -EOPNOTSUP if beyond our capabilities.
*
* TODO: We at the moment do not support extension mft records. (AIA)
*/
int layout_inode(ntfs_inode *ino, ntfs_disk_inode *store)
{
int offset, i, size, psize, error, count, recno;
ntfs_attribute *attr;
unsigned char *rec;
error = allocate_store(ino->vol, store, ino->record_count);
if (error)
return error;
size = ino->vol->mft_record_size;
count = i = 0;
do {
if (count < ino->record_count) {
recno = ino->records[count];
} else {
error = allocate_store(ino->vol, store, count + 1);
if (error)
return error;
recno = -1;
}
/*
* FIXME: We need to support extension records properly.
* At the moment they wouldn't work. Probably would "just" get
* corrupted if we write to them... (AIA)
*/
store->records[count].recno = recno;
rec = store->records[count].record;
count++;
/* Copy mft record header. */
offset = NTFS_GETU16(ino->attr + 0x14); /* attrs_offset */
ntfs_memcpy(rec, ino->attr, offset);
/* Copy attributes. */
while (i < ino->attr_count) {
attr = ino->attrs + i;
error = layout_attr(attr, rec + offset,
size - offset - 8, &psize);
if (error == -E2BIG && offset != NTFS_GETU16(ino->attr
+ 0x14))
break;
if (error)
return error;
offset += psize;
i++;
}
/* Terminating attribute. */
NTFS_PUTU32(rec + offset, 0xFFFFFFFF);
offset += 4;
NTFS_PUTU32(rec + offset, 0);
offset += 4;
NTFS_PUTU32(rec + 0x18, offset);
} while (i < ino->attr_count || count < ino->record_count);
return count - ino->record_count;
}
/*
* FIXME: ntfs_update_inode() calls layout_inode() to create the mft record on
* disk structure corresponding to the inode @ino. After that, ntfs_write_attr()
* is called to write out the created mft record to disk.
* We shouldn't need to re-layout every single time we are updating an mft
* record. No wonder the ntfs driver is slow like hell. (AIA)
*/
int ntfs_update_inode(ntfs_inode *ino)
{
int error, i;
ntfs_disk_inode store;
ntfs_io io;
ntfs_bzero(&store, sizeof(store));
error = layout_inode(ino, &store);
if (error == -E2BIG) {
i = ntfs_split_indexroot(ino);
if (i != -ENOTDIR) {
if (!i)
i = layout_inode(ino, &store);
error = i;
}
}
if (error == -E2BIG) {
error = ntfs_attr_allnonresident(ino);
if (!error)
error = layout_inode(ino, &store);
}
if (error > 0) {
/* FIXME: Introduce extension records. */
error = -E2BIG;
}
if (error) {
if (error == -E2BIG)
ntfs_error("Cannot handle saving inode 0x%x.\n",
ino->i_number);
deallocate_store(&store);
return error;
}
io.fn_get = ntfs_get;
io.fn_put = 0;
for (i = 0; i < store.count; i++) {
error = ntfs_insert_fixups(store.records[i].record,
ino->vol->mft_record_size);
if (error) {
printk(KERN_ALERT "NTFS: ntfs_update_inode() caught "
"corrupt %s mtf record ntfs record "
"header. Refusing to write corrupt "
"data to disk. Unmount and run chkdsk "
"immediately!\n", i ? "extension":
"base");
deallocate_store(&store);
return -EIO;
}
io.param = store.records[i].record;
io.size = ino->vol->mft_record_size;
error = ntfs_write_attr(ino->vol->mft_ino, ino->vol->at_data,
0, (__s64)store.records[i].recno <<
ino->vol->mft_record_size_bits, &io);
if (error || io.size != ino->vol->mft_record_size) {
/* Big trouble, partially written file. */
ntfs_error("Please unmount: Write error in inode "
"0x%x\n", ino->i_number);
deallocate_store(&store);
return error ? error : -EIO;
}
}
deallocate_store(&store);
return 0;
}
void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l)
{
int head, comp;
int copied = 0;
unsigned char *stop;
int bits;
int tag = 0;
int clear_pos;
while (1) {
head = NTFS_GETU16(src) & 0xFFF;
/* High bit indicates that compression was performed. */
comp = NTFS_GETU16(src) & 0x8000;
src += 2;
stop = src + head;
bits = 0;
clear_pos = 0;
if (head == 0)
/* Block is not used. */
return;/* FIXME: copied */
if (!comp) { /* uncompressible */
ntfs_memcpy(dest, src, 0x1000);
dest += 0x1000;
copied += 0x1000;
src += 0x1000;
if (l == copied)
return;
continue;
}
while (src <= stop) {
if (clear_pos > 4096) {
ntfs_error("Error 1 in decompress\n");
return;
}
if (!bits) {
tag = NTFS_GETU8(src);
bits = 8;
src++;
if (src > stop)
break;
}
if (tag & 1) {
int i, len, delta, code, lmask, dshift;
code = NTFS_GETU16(src);
src += 2;
if (!clear_pos) {
ntfs_error("Error 2 in decompress\n");
return;
}
for (i = clear_pos - 1, lmask = 0xFFF,
dshift = 12; i >= 0x10; i >>= 1) {
lmask >>= 1;
dshift--;
}
delta = code >> dshift;
len = (code & lmask) + 3;
for (i = 0; i < len; i++) {
dest[clear_pos] = dest[clear_pos -
delta - 1];
clear_pos++;
copied++;
if (copied==l)
return;
}
} else {
dest[clear_pos++] = NTFS_GETU8(src);
src++;
copied++;
if (copied==l)
return;
}
tag >>= 1;
bits--;
}
dest += clear_pos;
}
}
/*
* NOTE: Neither of the ntfs_*_bit functions are atomic! But we don't need
* them atomic at present as we never operate on shared/cached bitmaps.
*/
static __inline__ int ntfs_test_bit(unsigned char *byte, const int bit)
{
return byte[bit >> 3] & (1 << (bit & 7)) ? 1 : 0;
}
static __inline__ void ntfs_set_bit(unsigned char *byte, const int bit)
{
byte[bit >> 3] |= 1 << (bit & 7);
}
static __inline__ void ntfs_clear_bit(unsigned char *byte, const int bit)
{
byte[bit >> 3] &= ~(1 << (bit & 7));
}
static __inline__ int ntfs_test_and_clear_bit(unsigned char *byte,
const int bit)
{
unsigned char *ptr = byte + (bit >> 3);
int b = 1 << (bit & 7);
int oldbit = *ptr & b ? 1 : 0;
*ptr &= ~b;
return oldbit;
}
static void dump_runlist(const ntfs_runlist *rl, const int rlen)
{
#ifdef DEBUG
int i;
ntfs_cluster_t ct;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): rlen = %i.\n", rlen);
ntfs_debug(DEBUG_OTHER, "VCN LCN Run length\n");
for (i = 0, ct = 0; i < rlen; ct += rl[i++].len) {
if (rl[i].lcn == (ntfs_cluster_t)-1)
ntfs_debug(DEBUG_OTHER, "0x%-8x LCN_HOLE 0x%-8x "
"(%s)\n", ct, rl[i].len, rl[i].len ?
"sparse run" : "run list end");
else
ntfs_debug(DEBUG_OTHER, "0x%-8x 0x%-8x 0x%-8x%s\n", ct,
rl[i].lcn, rl[i].len, rl[i].len &&
i + 1 < rlen ? "" : " (run list end)");
if (!rl[i].len)
break;
}
#endif
}
/**
* splice_runlists - splice two run lists into one
* @rl1: pointer to address of first run list
* @r1len: number of elementfs in first run list
* @rl2: pointer to second run list
* @r2len: number of elements in second run list
*
* Append the run list @rl2 to the run list *@rl1 and return the result in
* *@rl1 and *@r1len.
*
* Return 0 on success or -errno on error, in which case *@rl1 and *@r1len are
* left untouched.
*
* The only possible error code at the moment is -ENOMEM and only happens if
* there is insufficient memory to allocate the new run list (only happens
* when size of (rl1 + rl2) > allocated size of rl1).
*/
int splice_runlists(ntfs_runlist **rl1, int *r1len, const ntfs_runlist *rl2,
int r2len)
{
ntfs_runlist *rl;
int rlen, rl_size, rl2_pos;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Entering with *r1len = %i, "
"r2len = %i.\n", *r1len, r2len);
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Dumping 1st runlist.\n");
if (*rl1)
dump_runlist(*rl1, *r1len);
else
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Not present.\n");
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Dumping 2nd runlist.\n");
dump_runlist(rl2, r2len);
rlen = *r1len + r2len + 1;
rl_size = (rlen * sizeof(ntfs_runlist) + PAGE_SIZE - 1) &
PAGE_MASK;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): rlen = %i, rl_size = %i.\n",
rlen, rl_size);
/* Do we have enough space? */
if (rl_size <= ((*r1len * sizeof(ntfs_runlist) + PAGE_SIZE - 1) &
PAGE_MASK)) {
/* Have enough space already. */
rl = *rl1;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Have enough space "
"already.\n");
} else {
/* Need more space. Reallocate. */
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Need more space.\n");
rl = ntfs_vmalloc(rlen << sizeof(ntfs_runlist));
if (!rl)
return -ENOMEM;
/* Copy over rl1. */
ntfs_memcpy(rl, *rl1, *r1len * sizeof(ntfs_runlist));
ntfs_vfree(*rl1);
*rl1 = rl;
}
/* Reuse rl_size as the current position index into rl. */
rl_size = *r1len - 1;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): rl_size = %i.\n");
/* Coalesce neighbouring elements, if present. */
rl2_pos = 0;
if (rl[rl_size].lcn + rl[rl_size].len == rl2[rl2_pos].lcn) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Coalescing adjacent "
"runs.\n");
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before: "
"rl[rl_size].len = %i.\n", rl[rl_size].len);
rl[rl_size].len += rl2[rl2_pos].len;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After: "
"rl[rl_size].len = %i.\n", rl[rl_size].len);
rl2_pos++;
r2len--;
rlen--;
}
rl_size++;
/* Copy over rl2. */
ntfs_memcpy(rl + rl_size, rl2 + rl2_pos, r2len * sizeof(ntfs_runlist));
rlen--;
rl[rlen].lcn = (ntfs_cluster_t)-1;
rl[rlen].len = (ntfs_cluster_t)0;
*r1len = rlen;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Dumping result runlist.\n");
dump_runlist(*rl1, *r1len);
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Returning with *r1len = "
"%i.\n", rlen);
return 0;
}
/**
* ntfs_alloc_mft_record - allocate an mft record
* @vol: volume to allocate an mft record on
* @result: the mft record number allocated
*
* Allocate a new mft record on disk. Return 0 on success or -ERRNO on error.
* On success, *@result contains the allocated mft record number. On error,
* *@result is -1UL.
*
* Note, this function doesn't actually set the mft record to be in use. This
* is done by the caller, which at the moment is only ntfs_alloc_inode().
*
* To find a free mft record, we scan the mft bitmap for a zero bit. To
* optimize this we start scanning at the place where we last stopped and we
* perform wrap around when we reach the end. Note, we do not try to allocate
* mft records below number 24 because numbers 0 to 15 are the defined system
* files anyway and 16 to 24 are special in that they are used for storing
* extension mft records for $MFT's $DATA attribute. This is required to avoid
* the possibility of creating a run list with a circular dependence which once
* written to disk can never be read in again. Windows will only use records
* 16 to 24 for normal files if the volume is completely out of space. We never
* use them which means that when the volume is really out of space we cannot
* create any more files while Windows can still create up to 8 small files. We
* can start doing this at some later time, doesn't matter much for now.
*
* When scanning the mft bitmap, we only search up to the last allocated mft
* record. If there are no free records left in the range 24 to number of
* allocated mft records, then we extend the mft data in order to create free
* mft records. We extend the allocated size of $MFT/$DATA by 16 records at a
* time or one cluster, if cluster size is above 16kiB. If there isn't
* sufficient space to do this, we try to extend by a single mft record or one
* cluster, if cluster size is above mft record size, but we only do this if
* there is enough free space, which we know from the values returned by the
* failed cluster allocation function when we tried to do the first allocation.
*
* No matter how many mft records we allocate, we initialize only the first
* allocated mft record (incrementing mft data size and initialized size) and
* return its number to the caller in @*result, unless there are less than 24
* mft records, in which case we allocate and initialize mft records until we
* reach record 24 which we consider as the first free mft record for use by
* normal files.
*
* If during any stage we overflow the initialized data in the mft bitmap, we
* extend the initialized size (and data size) by 8 bytes, allocating another
* cluster if required. The bitmap data size has to be at least equal to the
* number of mft records in the mft, but it can be bigger, in which case the
* superflous bits are padded with zeroes.
*
* Thus, when we return successfully (return value 0), we will have:
* - initialized / extended the mft bitmap if necessary,
* - initialized / extended the mft data if necessary,
* - set the bit corresponding to the mft record being allocated in the
* mft bitmap, and we will
* - return the mft record number in @*result.
*
* On error (return value below zero), nothing will have changed. If we had
* changed anything before the error occured, we will have reverted back to
* the starting state before returning to the caller. Thus, except for bugs,
* we should always leave the volume in a consitents state when returning from
* this function. NOTE: Small exception to this is that we set the bit in the
* mft bitmap but we do not mark the mft record in use, which is inconsistent.
* However, the caller will immediately add the wanted attributes to the mft
* record, set it in use and write it out to disk, so there should be no
* problem.
*
* Note, this function cannot make use of most of the normal functions, like
* for example for attribute resizing, etc, because when the run list overflows
* the base mft record and an attribute list is used, it is very important
* that the extension mft records used to store the $DATA attribute of $MFT
* can be reached without having to read the information contained inside
* them, as this would make it impossible to find them in the first place
* after the volume is dismounted. $MFT/$BITMAP probably doesn't need to
* follow this rule because the bitmap is not essential for finding the mft
* records, but on the other hand, handling the bitmap in this special way
* would make life easier because otherwise there might be circular invocations
* of functions when reading the bitmap but if we are careful, we should be
* able to avoid all problems.
*
* FIXME: Don't forget $MftMirr, though this probably belongs in
* ntfs_update_inode() (or even deeper). (AIA)
*
* FIXME: Want finer grained locking. (AIA)
*/
static int ntfs_alloc_mft_record(ntfs_volume *vol, unsigned long *result)
{
unsigned long nr_mft_records, buf_size, buf_pos, pass_start, pass_end;
unsigned long last_read_pos, mft_rec_size, bit, l;
ntfs_attribute *data, *bmp;
__u8 *buf, *byte, pass, b, have_allocated_mftbmp = 0;
int rlen, rl_size = 0, r2len, rl2_size, old_data_rlen, err = 0;
ntfs_runlist *rl, *rl2;
ntfs_cluster_t lcn = 0, old_data_len;
ntfs_io io;
__s64 ll, old_data_allocated, old_data_initialized, old_data_size;
*result = -1UL;
/* Allocate a buffer and setup the io structure. */
buf = (__u8*)__get_free_page(GFP_NOFS);
if (!buf)
return -ENOMEM;
lock_kernel();
/* Get the $DATA and $BITMAP attributes of $MFT. */
data = ntfs_find_attr(vol->mft_ino, vol->at_data, 0);
bmp = ntfs_find_attr(vol->mft_ino, vol->at_bitmap, 0);
if (!data || !bmp) {
err = -EINVAL;
goto err_ret;
}
/* Determine the number of allocated mft records in the mft. */
pass_end = nr_mft_records = data->allocated >>
vol->mft_record_size_bits;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): nr_mft_records = %lu.\n",
nr_mft_records);
/* Make sure we don't overflow the bitmap. */
l = bmp->initialized << 3;
if (l < nr_mft_records)
// FIXME: It might be a good idea to extend the bitmap instead.
pass_end = l;
pass = 1;
buf_pos = vol->mft_data_pos;
if (buf_pos >= pass_end) {
buf_pos = 24UL;
pass = 2;
}
pass_start = buf_pos;
rl = bmp->d.r.runlist;
rlen = bmp->d.r.len - 1;
lcn = rl[rlen].lcn + rl[rlen].len;
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Starting bitmap search.\n");
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): pass = %i, pass_start = %lu, "
"pass_end = %lu.\n", pass, pass_start, pass_end);
byte = NULL; // FIXME: For debugging only.
/* Loop until a free mft record is found. */
io.size = (nr_mft_records >> 3) & ~PAGE_MASK;
for (;; io.size = PAGE_SIZE) {
io.param = buf;
io.do_read = 1;
last_read_pos = buf_pos >> 3;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before: "
"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
"bmp->initialized = 0x%Lx.\n", bmp->allocated,
bmp->size, bmp->initialized);
err = ntfs_readwrite_attr(vol->mft_ino, bmp, last_read_pos,
&io);
if (err)
goto err_ret;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Read %lu bytes.\n",
(unsigned long)io.size);
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After: "
"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
"bmp->initialized = 0x%Lx.\n", bmp->allocated,
bmp->size, bmp->initialized);
if (!io.size)
goto pass_done;
buf_size = io.size << 3;
bit = buf_pos & 7UL;
buf_pos &= ~7UL;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before loop: "
"buf_size = %lu, buf_pos = %lu, bit = %lu, "
"*byte = 0x%x, b = %u.\n",
buf_size, buf_pos, bit, byte ? *byte : -1, b);
for (; bit < buf_size && bit + buf_pos < pass_end;
bit &= ~7UL, bit += 8UL) {
byte = buf + (bit >> 3);
if (*byte == 0xff)
continue;
b = ffz((unsigned long)*byte);
if (b < (__u8)8 && b >= (bit & 7UL)) {
bit = b + (bit & ~7UL) + buf_pos;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"Found free rec in for loop. "
"bit = %lu\n", bit);
goto found_free_rec;
}
}
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After loop: "
"buf_size = %lu, buf_pos = %lu, bit = %lu, "
"*byte = 0x%x, b = %u.\n",
buf_size, buf_pos, bit, byte ? *byte : -1, b);
buf_pos += buf_size;
if (buf_pos < pass_end)
continue;
pass_done: /* Finished with the current pass. */
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At pass_done.\n");
if (pass == 1) {
/*
* Now do pass 2, scanning the first part of the zone
* we omitted in pass 1.
*/
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Done pass "
"1.\n");
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Pass = 2.\n");
pass = 2;
pass_end = pass_start;
buf_pos = pass_start = 24UL;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): pass = %i, "
"pass_start = %lu, pass_end = %lu.\n",
pass, pass_start, pass_end);
continue;
} /* pass == 2 */
/* No free records left. */
if (bmp->initialized << 3 > nr_mft_records &&
bmp->initialized > 3) {
/*
* The mft bitmap is already bigger but the space is
* not covered by mft records, this implies that the
* next records are all free, so we already have found
* a free record.
*/
bit = nr_mft_records;
if (bit < 24UL)
bit = 24UL;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Found free "
"record bit (#1) = 0x%lx.\n", bit);
goto found_free_rec;
}
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Done pass 2.\n");
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before: "
"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
"bmp->initialized = 0x%Lx.\n", bmp->allocated,
bmp->size, bmp->initialized);
/* Need to extend the mft bitmap. */
if (bmp->initialized + 8LL > bmp->allocated) {
ntfs_io io2;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Initialized "
"> allocated.\n");
/* Need to extend bitmap by one more cluster. */
rl = bmp->d.r.runlist;
rlen = bmp->d.r.len - 1;
lcn = rl[rlen].lcn + rl[rlen].len;
io2.fn_put = ntfs_put;
io2.fn_get = ntfs_get;
io2.param = &b;
io2.size = 1;
io2.do_read = 1;
err = ntfs_readwrite_attr(vol->bitmap, data, lcn >> 3,
&io2);
if (err)
goto err_ret;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Read %lu "
"bytes.\n", (unsigned long)io2.size);
if (io2.size == 1 && b != 0xff) {
__u8 tb = 1 << (lcn & (ntfs_cluster_t)7);
if (!(b & tb)) {
/* Next cluster is free. Allocate it. */
b |= tb;
io2.param = &b;
io2.do_read = 0;
err = ntfs_readwrite_attr(vol->bitmap,
data, lcn >> 3, &io2);
if (err || io.size != 1) {
if (!err)
err = -EIO;
goto err_ret;
}
append_mftbmp_simple: rl[rlen].len++;
have_allocated_mftbmp |= 1;
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): Appending one "
"cluster to mftbmp.\n");
}
}
if (!have_allocated_mftbmp) {
/* Allocate a cluster from the DATA_ZONE. */
ntfs_cluster_t lcn2 = lcn;
ntfs_cluster_t count = 1;
err = ntfs_allocate_clusters(vol, &lcn2,
&count, &rl2, &r2len,
DATA_ZONE);
if (err)
goto err_ret;
if (count != 1 || lcn2 <= 0) {
if (count > 0) {
rl2_dealloc_err_out: if (ntfs_deallocate_clusters(
vol, rl2, r2len))
ntfs_error(__FUNCTION__
"(): Cluster "
"deallocation in error "
"code path failed! You "
"should run chkdsk.\n");
}
ntfs_vfree(rl2);
if (!err)
err = -EINVAL;
goto err_ret;
}
if (lcn2 == lcn) {
ntfs_vfree(rl2);
goto append_mftbmp_simple;
}
/* We need to append a new run. */
rl_size = (rlen * sizeof(ntfs_runlist) +
PAGE_SIZE - 1) & PAGE_MASK;
/* Reallocate memory if necessary. */
if ((rlen + 2) * sizeof(ntfs_runlist) >=
rl_size) {
ntfs_runlist *rlt;
rl_size += PAGE_SIZE;
rlt = ntfs_vmalloc(rl_size);
if (!rlt) {
err = -ENOMEM;
goto rl2_dealloc_err_out;
}
ntfs_memcpy(rlt, rl, rl_size -
PAGE_SIZE);
ntfs_vfree(rl);
bmp->d.r.runlist = rl = rlt;
}
ntfs_vfree(rl2);
rl[rlen].lcn = lcn = lcn2;
rl[rlen].len = count;
bmp->d.r.len = ++rlen;
have_allocated_mftbmp |= 2;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"Adding run to mftbmp. "
"LCN = %i, len = %i\n", lcn,
count);
}
/*
* We now have extended the mft bitmap allocated size
* by one cluster. Reflect this in the attribute.
*/
bmp->allocated += (__s64)vol->cluster_size;
}
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After: "
"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
"bmp->initialized = 0x%Lx.\n", bmp->allocated,
bmp->size, bmp->initialized);
/* We now have sufficient allocated space. */
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Now have sufficient "
"allocated space in mftbmp.\n");
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before: "
"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
"bmp->initialized = 0x%Lx.\n", bmp->allocated,
bmp->size, bmp->initialized);
buf_pos = bmp->initialized;
bmp->initialized += 8LL;
if (bmp->initialized > bmp->size)
bmp->size = bmp->initialized;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After: "
"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
"bmp->initialized = 0x%Lx.\n", bmp->allocated,
bmp->size, bmp->initialized);
have_allocated_mftbmp |= 4;
/* Update the mft bitmap attribute value. */
memset(buf, 0, 8);
io.param = buf;
io.size = 8;
io.do_read = 0;
err = ntfs_readwrite_attr(vol->mft_ino, bmp, buf_pos, &io);
if (err || io.size != 8) {
if (!err)
err = -EIO;
goto shrink_mftbmp_err_ret;
}
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Wrote extended "
"mftbmp bytes %lu.\n", (unsigned long)io.size);
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After write: "
"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
"bmp->initialized = 0x%Lx.\n", bmp->allocated,
bmp->size, bmp->initialized);
bit = buf_pos << 3;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Found free record "
"bit (#2) = 0x%lx.\n", bit);
goto found_free_rec;
}
found_free_rec:
/* bit is the found free mft record. Allocate it in the mft bitmap. */
vol->mft_data_pos = bit;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At found_free_rec.\n");
io.param = buf;
io.size = 1;
io.do_read = 1;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before update: "
"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
"bmp->initialized = 0x%Lx.\n", bmp->allocated,
bmp->size, bmp->initialized);
err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io);
if (err || io.size != 1) {
if (!err)
err = -EIO;
goto shrink_mftbmp_err_ret;
}
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Read %lu bytes.\n",
(unsigned long)io.size);
#ifdef DEBUG
/* Check our bit is really zero! */
if (*buf & (1 << (bit & 7)))
BUG();
#endif
*buf |= 1 << (bit & 7);
io.param = buf;
io.do_read = 0;
err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io);
if (err || io.size != 1) {
if (!err)
err = -EIO;
goto shrink_mftbmp_err_ret;
}
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Wrote %lu bytes.\n",
(unsigned long)io.size);
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After update: "
"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
"bmp->initialized = 0x%Lx.\n", bmp->allocated,
bmp->size, bmp->initialized);
/* The mft bitmap is now uptodate. Deal with mft data attribute now. */
ll = (__s64)(bit + 1) << vol->mft_record_size_bits;
if (ll <= data->initialized) {
/* The allocated record is already initialized. We are done! */
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Allocated mft record "
"already initialized!\n");
goto done_ret;
}
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Allocated mft record needs "
"to be initialized.\n");
/* The mft record is outside the initialized data. */
mft_rec_size = (unsigned long)vol->mft_record_size;
/* Preserve old values for undo purposes. */
old_data_allocated = data->allocated;
old_data_rlen = data->d.r.len - 1;
old_data_len = data->d.r.runlist[old_data_rlen].len;
/*
* If necessary, extend the mft until it covers the allocated record.
* The loop is only actually used when a freshly formatted volume is
* first written to. But it optimizes away nicely in the common case.
*/
while (ll > data->allocated) {
ntfs_cluster_t lcn2, nr_lcn2, nr, min_nr;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Extending mft "
"data allocation, data->allocated = 0x%Lx, "
"data->size = 0x%Lx, data->initialized = "
"0x%Lx.\n", data->allocated, data->size,
data->initialized);
/* Minimum allocation is one mft record worth of clusters. */
if (mft_rec_size <= vol->cluster_size)
min_nr = (ntfs_cluster_t)1;
else
min_nr = mft_rec_size >> vol->cluster_size_bits;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): min_nr = %i.\n",
min_nr);
/* Allocate 16 mft records worth of clusters. */
nr = mft_rec_size << 4 >> vol->cluster_size_bits;
if (!nr)
nr = (ntfs_cluster_t)1;
/* Determine the preferred allocation location. */
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): nr = %i.\n", nr);
rl2 = data->d.r.runlist;
r2len = data->d.r.len;
lcn2 = rl2[r2len - 1].lcn + rl2[r2len - 1].len;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): rl2[r2len - 1].lcn "
"= %i, .len = %i.\n", rl2[r2len - 1].lcn,
rl2[r2len - 1].len);
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): lcn2 = %i, r2len = "
"%i.\n", lcn2, r2len);
retry_mft_data_allocation:
nr_lcn2 = nr;
err = ntfs_allocate_clusters(vol, &lcn2, &nr_lcn2, &rl2,
&r2len, MFT_ZONE);
#ifdef DEBUG
if (!err && nr_lcn2 < min_nr)
/* Allocated less than minimum needed. Weird! */
BUG();
#endif
if (err) {
/*
* If there isn't enough space to do the wanted
* allocation, but there is enough space to do a
* minimal allocation, then try that, unless the wanted
* allocation was already the minimal allocation.
*/
if (err == -ENOSPC && nr > min_nr &&
nr_lcn2 >= min_nr) {
nr = min_nr;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"Retrying mft data "
"allocation, nr = min_nr = %i"
".\n", nr);
goto retry_mft_data_allocation;
}
goto undo_mftbmp_alloc_err_ret;
}
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Allocated %i "
"clusters starting at LCN %i.\n", nr_lcn2,
lcn2);
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Allocated "
"runlist:\n");
dump_runlist(rl2, r2len);
/* Append rl2 to the mft data attribute's run list. */
err = splice_runlists(&data->d.r.runlist, (int*)&data->d.r.len,
rl2, r2len);
if (err) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"splice_runlists failed with error "
"code %i.\n", -err);
goto undo_partial_data_alloc_err_ret;
}
/* Reflect the allocated clusters in the mft allocated data. */
data->allocated += nr_lcn2 << vol->cluster_size_bits;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After extending mft "
"data allocation, data->allocated = 0x%Lx, "
"data->size = 0x%Lx, data->initialized = "
"0x%Lx.\n", data->allocated, data->size,
data->initialized);
}
/* Prepare a formatted (empty) mft record. */
memset(buf, 0, mft_rec_size);
ntfs_fill_mft_header(buf, mft_rec_size, 0, 0, 0);
err = ntfs_insert_fixups(buf, mft_rec_size);
if (err)
goto undo_data_alloc_err_ret;
/*
* Extend mft data initialized size to reach the allocated mft record
* and write the formatted mft record buffer to each mft record being
* initialized. Note, that ntfs_readwrite_attr extends both
* data->initialized and data->size, so no need for us to touch them.
*/
old_data_initialized = data->initialized;
old_data_size = data->size;
while (ll > data->initialized) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Initializing mft "
"record 0x%Lx.\n",
data->initialized >> vol->mft_record_size_bits);
io.param = buf;
io.size = mft_rec_size;
io.do_read = 0;
err = ntfs_readwrite_attr(vol->mft_ino, data,
data->initialized, &io);
if (err || io.size != mft_rec_size) {
if (!err)
err = -EIO;
goto undo_data_init_err_ret;
}
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Wrote %i bytes to "
"mft data.\n", io.size);
}
/* Update the VFS inode size as well. */
VFS_I(vol->mft_ino)->i_size = data->size;
#ifdef DEBUG
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After mft record "
"initialization: data->allocated = 0x%Lx, data->size "
"= 0x%Lx, data->initialized = 0x%Lx.\n",
data->allocated, data->size, data->initialized);
/* Sanity checks. */
if (data->size > data->allocated || data->size < data->initialized ||
data->initialized > data->allocated)
BUG();
#endif
done_ret:
/* Return the number of the allocated mft record. */
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At done_ret. *result = bit = "
"0x%lx.\n", bit);
*result = bit;
vol->mft_data_pos = bit + 1;
err_ret:
unlock_kernel();
free_page((unsigned long)buf);
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Syncing inode $MFT.\n");
if (ntfs_update_inode(vol->mft_ino))
ntfs_error(__FUNCTION__ "(): Failed to sync inode $MFT. "
"Continuing anyway.\n");
if (!err) {
ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): Done. Allocated mft "
"record number *result = 0x%lx.\n", *result);
return 0;
}
if (err != -ENOSPC)
ntfs_error(__FUNCTION__ "(): Failed to allocate an mft "
"record. Returning error code %i.\n", -err);
else
ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): Failed to allocate "
"an mft record due to lack of free space.\n");
return err;
undo_data_init_err_ret:
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At "
"undo_data_init_err_ret.\n");
data->initialized = old_data_initialized;
data->size = old_data_size;
undo_data_alloc_err_ret:
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At undo_data_alloc_err_ret."
"\n");
data->allocated = old_data_allocated;
undo_partial_data_alloc_err_ret:
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At "
"undo_partial_data_alloc_err_ret.\n");
/* Deallocate the clusters. */
if (ntfs_deallocate_clusters(vol, rl2, r2len))
ntfs_error(__FUNCTION__ "(): Error deallocating clusters in "
"error code path. You should run chkdsk.\n");
ntfs_vfree(rl2);
/* Revert the run list back to what it was before. */
r2len = data->d.r.len;
rl2 = data->d.r.runlist;
rl2[old_data_rlen++].len = old_data_len;
rl2[old_data_rlen].lcn = (ntfs_cluster_t)-1;
rl2[old_data_rlen].len = (ntfs_cluster_t)0;
data->d.r.len = old_data_rlen;
rl2_size = ((old_data_rlen + 1) * sizeof(ntfs_runlist) + PAGE_SIZE -
1) & PAGE_MASK;
/* Reallocate memory freeing any extra memory allocated. */
if (rl2_size < ((r2len * sizeof(ntfs_runlist) + PAGE_SIZE - 1) &
PAGE_MASK)) {
rl2 = ntfs_vmalloc(rl2_size);
if (rl2) {
ntfs_memcpy(rl2, data->d.r.runlist, rl2_size);
ntfs_vfree(data->d.r.runlist);
data->d.r.runlist = rl2;
} else
ntfs_error(__FUNCTION__ "(): Error reallocating "
"memory in error code path. This "
"should be harmless.\n");
}
undo_mftbmp_alloc_err_ret:
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At "
"undo_mftbmp_alloc_err_ret.\n");
/* Deallocate the allocated bit in the mft bitmap. */
io.param = buf;
io.size = 1;
io.do_read = 1;
err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io);
if (!err && io.size == 1) {
*buf &= ~(1 << (bit & 7));
io.param = buf;
io.do_read = 0;
err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io);
}
if (err || io.size != 1) {
if (!err)
err = -EIO;
ntfs_error(__FUNCTION__ "(): Error deallocating mft record in "
"error code path. You should run chkdsk.\n");
}
shrink_mftbmp_err_ret:
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At shrink_mftbmp_err_ret.\n");
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): have_allocated_mftbmp = "
"%i.\n", have_allocated_mftbmp);
if (!have_allocated_mftbmp)
goto err_ret;
/* Shrink the mftbmp back to previous size. */
if (bmp->size == bmp->initialized)
bmp->size -= 8LL;
bmp->initialized -= 8LL;
have_allocated_mftbmp &= ~4;
/* If no allocation occured then we are done. */
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): have_allocated_mftbmp = "
"%i.\n", have_allocated_mftbmp);
if (!have_allocated_mftbmp)
goto err_ret;
/* Deallocate the allocated cluster. */
bmp->allocated -= (__s64)vol->cluster_size;
if (ntfs_deallocate_cluster_run(vol, lcn, (ntfs_cluster_t)1))
ntfs_error(__FUNCTION__ "(): Error deallocating cluster in "
"error code path. You should run chkdsk.\n");
switch (have_allocated_mftbmp & 3) {
case 1:
/* Delete the last lcn from the last run of mftbmp. */
rl[rlen - 1].len--;
break;
case 2:
/* Delete the last run of mftbmp. */
bmp->d.r.len = --rlen;
/* Reallocate memory if necessary. */
if ((rlen + 1) * sizeof(ntfs_runlist) <= rl_size - PAGE_SIZE) {
ntfs_runlist *rlt;
rl_size -= PAGE_SIZE;
rlt = ntfs_vmalloc(rl_size);
if (rlt) {
ntfs_memcpy(rlt, rl, rl_size);
ntfs_vfree(rl);
bmp->d.r.runlist = rl = rlt;
} else
ntfs_error(__FUNCTION__ "(): Error "
"reallocating memory in error "
"code path. This should be "
"harmless.\n");
}
bmp->d.r.runlist[bmp->d.r.len].lcn = (ntfs_cluster_t)-1;
bmp->d.r.runlist[bmp->d.r.len].len = (ntfs_cluster_t)0;
break;
default:
BUG();
}
goto err_ret;
}
/* We need 0x48 bytes in total. */
static int add_standard_information(ntfs_inode *ino)
{
ntfs_time64_t now;
char data[0x30];
char *position = data;
ntfs_attribute *si;
now = ntfs_now();
NTFS_PUTU64(position + 0x00, now); /* File creation */
NTFS_PUTU64(position + 0x08, now); /* Last modification */
NTFS_PUTU64(position + 0x10, now); /* Last mod for MFT */
NTFS_PUTU64(position + 0x18, now); /* Last access */
NTFS_PUTU64(position + 0x20, 0); /* MSDOS file perms */
NTFS_PUTU64(position + 0x28, 0); /* unknown */
return ntfs_create_attr(ino, ino->vol->at_standard_information, 0,
data, sizeof(data), &si);
}
static int add_filename(ntfs_inode *ino, ntfs_inode *dir,
const unsigned char *filename, int length, ntfs_u32 flags)
{
unsigned char *position;
unsigned int size;
ntfs_time64_t now;
int count, error;
unsigned char* data;
ntfs_attribute *fn;
/* Work out the size. */
size = 0x42 + 2 * length;
data = ntfs_malloc(size);
if (!data)
return -ENOMEM;
/* Search for a position. */
position = data;
NTFS_PUTINUM(position, dir); /* Inode num of dir */
now = ntfs_now();
NTFS_PUTU64(position + 0x08, now); /* File creation */
NTFS_PUTU64(position + 0x10, now); /* Last modification */
NTFS_PUTU64(position + 0x18, now); /* Last mod for MFT */
NTFS_PUTU64(position + 0x20, now); /* Last access */
/* FIXME: Get the following two sizes by finding the data attribute
* in ino->attr and copying the corresponding fields from there.
* If no data present then set to zero. In current implementation
* add_data is called after add_filename so zero is correct on
* creation. Need to change when we have hard links / support different
* filename namespaces. (AIA) */
NTFS_PUTS64(position + 0x28, 0); /* Allocated size */
NTFS_PUTS64(position + 0x30, 0); /* Data size */
NTFS_PUTU32(position + 0x38, flags); /* File flags */
NTFS_PUTU32(position + 0x3c, 0); /* We don't use these
* features yet. */
NTFS_PUTU8(position + 0x40, length); /* Filename length */
NTFS_PUTU8(position + 0x41, 0); /* Only long name */
/* FIXME: This is madness. We are defining the POSIX namespace
* for the filename here which can mean that the file will be
* invisible when in Windows NT/2k! )-: (AIA) */
position += 0x42;
for (count = 0; count < length; count++) {
NTFS_PUTU16(position + 2 * count, filename[count]);
}
error = ntfs_create_attr(ino, ino->vol->at_file_name, 0, data, size,
&fn);
if (!error)
error = ntfs_dir_add(dir, ino, fn);
ntfs_free(data);
return error;
}
int add_security(ntfs_inode* ino, ntfs_inode* dir)
{
int error;
char *buf;
int size;
ntfs_attribute* attr;
ntfs_io io;
ntfs_attribute *se;
attr = ntfs_find_attr(dir, ino->vol->at_security_descriptor, 0);
if (!attr)
return -EOPNOTSUPP; /* Need security in directory. */
size = attr->size;
if (size > 512)
return -EOPNOTSUPP;
buf = ntfs_malloc(size);
if (!buf)
return -ENOMEM;
io.fn_get = ntfs_get;
io.fn_put = ntfs_put;
io.param = buf;
io.size = size;
error = ntfs_read_attr(dir, ino->vol->at_security_descriptor, 0, 0,&io);
if (!error && io.size != size)
ntfs_error("wrong size in add_security\n");
if (error) {
ntfs_free(buf);
return error;
}
/* FIXME: Consider ACL inheritance. */
error = ntfs_create_attr(ino, ino->vol->at_security_descriptor,
0, buf, size, &se);
ntfs_free(buf);
return error;
}
static int add_data(ntfs_inode* ino, unsigned char *data, int length)
{
ntfs_attribute *da;
return ntfs_create_attr(ino, ino->vol->at_data, 0, data, length, &da);
}
/*
* We _could_ use 'dir' to help optimise inode allocation.
*
* FIXME: Need to undo what we do in ntfs_alloc_mft_record if we get an error
* further on in ntfs_alloc_inode. Either fold the two functions to allow
* proper undo or just deallocate the record from the mft bitmap. (AIA)
*/
int ntfs_alloc_inode(ntfs_inode *dir, ntfs_inode *result, const char *filename,
int namelen, ntfs_u32 flags)
{
ntfs_volume *vol = dir->vol;
int err;
ntfs_u8 buffer[2];
ntfs_io io;
err = ntfs_alloc_mft_record(vol, &(result->i_number));
if (err) {
if (err == -ENOSPC)
ntfs_error(__FUNCTION__ "(): No free inodes.\n");
return err;
}
/* Get the sequence number. */
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
io.param = buffer;
io.size = 2;
err = ntfs_read_attr(vol->mft_ino, vol->at_data, 0,
((__s64)result->i_number << vol->mft_record_size_bits)
+ 0x10, &io);
// FIXME: We are leaving the MFT in inconsistent state! (AIA)
if (err)
return err;
/* Increment the sequence number skipping zero. */
result->sequence_number = (NTFS_GETU16(buffer) + 1) & 0xffff;
if (!result->sequence_number)
result->sequence_number++;
result->vol = vol;
result->attr_count = 0;
result->attrs = 0;
result->record_count = 1;
result->records = ntfs_calloc(8 * sizeof(int));
if (!result->records)
goto mem_err_out;
result->records[0] = result->i_number;
result->attr = ntfs_calloc(vol->mft_record_size);
if (!result->attr) {
ntfs_free(result->records);
result->records = NULL;
goto mem_err_out;
}
ntfs_fill_mft_header(result->attr, vol->mft_record_size,
result->sequence_number, 1, 1);
err = add_standard_information(result);
if (!err)
err = add_filename(result, dir, filename, namelen, flags);
if (!err)
err = add_security(result, dir);
// FIXME: We are leaving the MFT in inconsistent state on error! (AIA)
return err;
mem_err_out:
// FIXME: We are leaving the MFT in inconsistent state! (AIA)
result->record_count = 0;
result->attr = NULL;
return -ENOMEM;
}
int ntfs_alloc_file(ntfs_inode *dir, ntfs_inode *result, char *filename,
int namelen)
{
int err;
err = ntfs_alloc_inode(dir, result, filename, namelen, 0);
if (!err)
err = add_data(result, 0, 0);
return err;
}
/*
* inode.h - Header file for inode.c
*
* Copyright (C) 1997 Rgis Duchesne
* Copyright (C) 1998 Martin von Lwis
* Copyright (c) 2001 Anton Altparmakov (AIA)
*/
ntfs_attribute *ntfs_find_attr(ntfs_inode *ino, int type, char *name);
int ntfs_read_attr(ntfs_inode *ino, int type, char *name, __s64 offset,
ntfs_io *buf);
int ntfs_write_attr(ntfs_inode *ino, int type, char *name, __s64 offset,
ntfs_io *buf);
int ntfs_init_inode(ntfs_inode *ino, ntfs_volume *vol, int inum);
void ntfs_clear_inode(ntfs_inode *ino);
int ntfs_check_mft_record(ntfs_volume *vol, char *record);
int ntfs_alloc_inode(ntfs_inode *dir, ntfs_inode *result, const char *filename,
int namelen, ntfs_u32);
int ntfs_alloc_file(ntfs_inode *dir, ntfs_inode *result, char *filename,
int namelen);
int ntfs_update_inode(ntfs_inode *ino);
int ntfs_vcn_to_lcn(ntfs_inode *ino, int vcn);
int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
ntfs_io *dest);
int ntfs_allocate_attr_number(ntfs_inode *ino, int *result);
int ntfs_decompress_run(unsigned char **data, int *length,
ntfs_cluster_t *cluster, int *ctype);
void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l);
int splice_runlists(ntfs_runlist **rl1, int *r1len, const ntfs_runlist *rl2,
int r2len);
/*
* NOTE: Neither of the ntfs_*_bit functions are atomic! But we don't need
* them atomic at present as we never operate on shared/cached bitmaps.
*/
static __inline__ int ntfs_test_and_set_bit(unsigned char *byte, const int bit)
{
unsigned char *ptr = byte + (bit >> 3);
int b = 1 << (bit & 7);
int oldbit = *ptr & b ? 1 : 0;
*ptr |= b;
return oldbit;
}
/*
* macros.h
*
* Copyright (C) 1995 Martin von Lwis
* Copyright (C) 1996 Rgis Duchesne
* Copyright (c) 2001 Anton Altaparmakov
*/
#include <linux/ntfs_fs_i.h>
#include <linux/fs.h>
#include <asm/page.h>
#define NTFS_FD(vol) ((vol)->u.fd)
#define NTFS_SB(vol) ((struct super_block*)(vol)->sb)
#define NTFS_SB2VOL(sb) (&(sb)->u.ntfs_sb)
#define NTFS_INO2VOL(ino) (&((ino)->i_sb->u.ntfs_sb))
static inline struct ntfs_i *ntfs_i(struct inode *inode)
{
return list_entry(inode, struct ntfs_i, vfs_inode);
}
#define NTFS_I(ino) (&ntfs_i(ino)->n)
static inline struct inode *VFS_I(struct ntfs_inode_info *ntfs_ino)
{
return &list_entry(ntfs_ino, struct ntfs_i, n)->vfs_inode;
}
#define IS_MAGIC(a,b) (*(int*)(a) == *(int*)(b))
#define IS_MFT_RECORD(a) IS_MAGIC((a),"FILE")
#define IS_INDEX_RECORD(a) IS_MAGIC((a),"INDX")
/* 'NTFS' in little endian */
#define NTFS_SUPER_MAGIC 0x5346544E
#define NTFS_AFLAG_RO 1
#define NTFS_AFLAG_HIDDEN 2
#define NTFS_AFLAG_SYSTEM 4
#define NTFS_AFLAG_ARCHIVE 20
#define NTFS_AFLAG_COMPRESSED 0x800
#define NTFS_AFLAG_DIR 0x10000000
/*
* ntfsendian.h
*
* Copyright (C) 1998, 1999 Martin von Lwis
* Copyright (C) 1998 Joseph Malicki
* Copyright (C) 1999 Werner Seiler
* Copyright (C) 2001 Anton Altaparmakov (AIA)
*/
#include <asm/byteorder.h>
#define CPU_TO_LE16(a) __cpu_to_le16(a)
#define CPU_TO_LE32(a) __cpu_to_le32(a)
#define CPU_TO_LE64(a) __cpu_to_le64(a)
#define LE16_TO_CPU(a) __cpu_to_le16(a)
#define LE32_TO_CPU(a) __cpu_to_le32(a)
#define LE64_TO_CPU(a) __cpu_to_le64(a)
#define NTFS_GETU8(p) (*(ntfs_u8*)(p))
#define NTFS_GETU16(p) ((ntfs_u16)LE16_TO_CPU(*(ntfs_u16*)(p)))
#define NTFS_GETU24(p) ((ntfs_u32)NTFS_GETU16(p) | \
((ntfs_u32)NTFS_GETU8(((char*)(p)) + 2) << 16))
#define NTFS_GETU32(p) ((ntfs_u32)LE32_TO_CPU(*(ntfs_u32*)(p)))
#define NTFS_GETU40(p) ((ntfs_u64)NTFS_GETU32(p) | \
(((ntfs_u64)NTFS_GETU8(((char*)(p)) + 4)) << 32))
#define NTFS_GETU48(p) ((ntfs_u64)NTFS_GETU32(p) | \
(((ntfs_u64)NTFS_GETU16(((char*)(p)) + 4)) << 32))
#define NTFS_GETU56(p) ((ntfs_u64)NTFS_GETU32(p) | \
(((ntfs_u64)NTFS_GETU24(((char*)(p)) + 4)) << 32))
#define NTFS_GETU64(p) ((ntfs_u64)LE64_TO_CPU(*(ntfs_u64*)(p)))
/* Macros writing unsigned integers */
#define NTFS_PUTU8(p,v) ((*(ntfs_u8*)(p)) = (v))
#define NTFS_PUTU16(p,v) ((*(ntfs_u16*)(p)) = CPU_TO_LE16(v))
#define NTFS_PUTU24(p,v) NTFS_PUTU16(p, (v) & 0xFFFF);\
NTFS_PUTU8(((char*)(p)) + 2, (v) >> 16)
#define NTFS_PUTU32(p,v) ((*(ntfs_u32*)(p)) = CPU_TO_LE32(v))
#define NTFS_PUTU64(p,v) ((*(ntfs_u64*)(p)) = CPU_TO_LE64(v))
/* Macros reading signed integers */
#define NTFS_GETS8(p) ((*(ntfs_s8*)(p)))
#define NTFS_GETS16(p) ((ntfs_s16)LE16_TO_CPU(*(short*)(p)))
#define NTFS_GETS24(p) (NTFS_GETU24(p) < 0x800000 ? \
(int)NTFS_GETU24(p) : \
(int)(NTFS_GETU24(p) - 0x1000000))
#define NTFS_GETS32(p) ((ntfs_s32)LE32_TO_CPU(*(int*)(p)))
#define NTFS_GETS40(p) (((ntfs_s64)NTFS_GETU32(p)) | \
(((ntfs_s64)NTFS_GETS8(((char*)(p)) + 4)) << 32))
#define NTFS_GETS48(p) (((ntfs_s64)NTFS_GETU32(p)) | \
(((ntfs_s64)NTFS_GETS16(((char*)(p)) + 4)) << 32))
#define NTFS_GETS56(p) (((ntfs_s64)NTFS_GETU32(p)) | \
(((ntfs_s64)NTFS_GETS24(((char*)(p)) + 4)) << 32))
#define NTFS_GETS64(p) ((ntfs_s64)NTFS_GETU64(p))
#define NTFS_PUTS8(p,v) NTFS_PUTU8(p,v)
#define NTFS_PUTS16(p,v) NTFS_PUTU16(p,v)
#define NTFS_PUTS24(p,v) NTFS_PUTU24(p,v)
#define NTFS_PUTS32(p,v) NTFS_PUTU32(p,v)
#define NTFS_PUTS64(p,v) NTFS_PUTU64(p,v)
/*
* ntfstypes.h - This file defines four things:
* - Generic platform independent fixed-size types (e.g. ntfs_u32).
* - Specific fixed-size types (e.g. ntfs_offset_t).
* - Macros that read and write those types from and to byte arrays.
* - Types derived from OS specific ones.
*
* Copyright (C) 1996, 1998, 1999 Martin von Lwis
* Copyright (C) 2001 Anton Altaparmakov (AIA)
*/
#include <linux/fs.h>
#include "ntfsendian.h"
#include <asm/types.h>
/* Integral types */
#ifndef NTFS_INTEGRAL_TYPES
#define NTFS_INTEGRAL_TYPES
typedef u8 ntfs_u8;
typedef u16 ntfs_u16;
typedef u32 ntfs_u32;
typedef u64 ntfs_u64;
typedef s8 ntfs_s8;
typedef s16 ntfs_s16;
typedef s32 ntfs_s32;
typedef s64 ntfs_s64;
#endif
/* Unicode character type */
#ifndef NTFS_WCHAR_T
#define NTFS_WCHAR_T
typedef u16 ntfs_wchar_t;
#endif
/* File offset */
#ifndef NTFS_OFFSET_T
#define NTFS_OFFSET_T
typedef s64 ntfs_offset_t;
#endif
/* UTC */
#ifndef NTFS_TIME64_T
#define NTFS_TIME64_T
typedef u64 ntfs_time64_t;
#endif
/*
* This is really signed long long. So we support only volumes up to 2Tb. This
* is ok as Win2k also only uses 32-bits to store clusters.
* Whatever you do keep this a SIGNED value or a lot of NTFS users with
* corrupted filesystems will lynch you! It causes massive fs corruption when
* unsigned due to the nature of many checks relying on being performed on
* signed quantities. (AIA)
*/
#ifndef NTFS_CLUSTER_T
#define NTFS_CLUSTER_T
typedef s32 ntfs_cluster_t;
#endif
/* Architecture independent macros. */
/* PUTU32 would not clear all bytes. */
#define NTFS_PUTINUM(p,i) NTFS_PUTU64(p, i->i_number); \
NTFS_PUTU16(((char*)p) + 6, i->sequence_number)
/* System dependent types. */
#include <asm/posix_types.h>
#ifndef NTMODE_T
#define NTMODE_T
typedef __kernel_mode_t ntmode_t;
#endif
#ifndef NTFS_UID_T
#define NTFS_UID_T
typedef uid_t ntfs_uid_t;
#endif
#ifndef NTFS_GID_T
#define NTFS_GID_T
typedef gid_t ntfs_gid_t;
#endif
#ifndef NTFS_SIZE_T
#define NTFS_SIZE_T
typedef __kernel_size_t ntfs_size_t;
#endif
#ifndef NTFS_TIME_T
#define NTFS_TIME_T
typedef __kernel_time_t ntfs_time_t;
#endif
/*
* struct.h - Structure definitions
*
* Copyright (C) 1997 Rgis Duchesne
* Copyright (C) 2000-2001 Anton Altaparmakov (AIA)
*/
#include <linux/ntfs_fs.h>
/* Necessary forward definition. */
struct ntfs_inode;
/* Which files should be returned from a director listing. */
#define ngt_dos 1 /* only short names, no system files */
#define ngt_nt 2 /* only long names, all-uppercase becomes
* all-lowercase, no system files */
#define ngt_posix 3 /* all names except system files */
#define ngt_full 4 /* all entries */
typedef struct ntfs_sb_info ntfs_volume;
typedef struct {
ntfs_cluster_t lcn;
ntfs_cluster_t len;
} ntfs_runlist;
typedef struct ntfs_attribute {
int type;
ntfs_u16 *name;
int namelen;
int attrno;
__s64 size, allocated, initialized, compsize;
ATTR_FLAGS flags;
__u8 resident, indexed;
int cengine;
union {
void *data; /* if resident */
struct {
ntfs_runlist *runlist;
unsigned long len;
} r;
} d;
} ntfs_attribute;
typedef struct ntfs_inode_info ntfs_inode;
/* Structure to define IO to user buffer. do_read means that the destination
* has to be written using fn_put, do_write means that the destination has to
* read using fn_get. So, do_read is from a user's point of view, while put and
* get are from the driver's point of view. The first argument is always the
* destination of the IO. */
typedef struct ntfs_io{
int do_read;
void (*fn_put)(struct ntfs_io *dest, void *buf, ntfs_size_t);
void (*fn_get)(void *buf, struct ntfs_io *src, ntfs_size_t len);
void *param;
unsigned long size;
} ntfs_io;
#if 0
typedef struct {
ntfs_volume *vol;
ntfs_inode *ino;
int type;
char *name;
int mftno;
int start_vcn;
} ntfs_attrlist_item;
#endif
/*
* super.c
*
* Copyright (C) 1995-1997, 1999 Martin von Lwis
* Copyright (C) 1996-1997 Rgis Duchesne
* Copyright (C) 1999 Steve Dodd
* Copyright (C) 2000-2001 Anton Altparmakov (AIA)
*/
#include <linux/ntfs_fs.h>
#include <linux/errno.h>
#include <linux/bitops.h>
#include <linux/module.h>
#include "ntfstypes.h"
#include "struct.h"
#include "super.h"
#include "macros.h"
#include "inode.h"
#include "support.h"
#include "util.h"
#include <linux/smp_lock.h>
/* All important structures in NTFS use 2 consistency checks:
* . a magic structure identifier (FILE, INDX, RSTR, RCRD...)
* . a fixup technique : the last word of each sector (called a fixup) of a
* structure's record should end with the word at offset <n> of the first
* sector, and if it is the case, must be replaced with the words following
* <n>. The value of <n> and the number of fixups is taken from the fields
* at the offsets 4 and 6. Note that the sector size is defined as
* NTFS_SECTOR_SIZE and not as the hardware sector size (this is concordant
* with what the Windows NTFS driver does).
*
* This function performs these 2 checks, and _fails_ if:
* . the input size is invalid
* . the fixup header is invalid
* . the size does not match the number of sectors
* . the magic identifier is wrong
* . a fixup is invalid
*/
int ntfs_fixup_record(char *record, char *magic, int size)
{
int start, count, offset;
ntfs_u16 fixup;
if (!IS_MAGIC(record, magic))
return 0;
start = NTFS_GETU16(record + 4);
count = NTFS_GETU16(record + 6) - 1;
if (size & (NTFS_SECTOR_SIZE - 1) || start & 1 ||
start + count * 2 > size || size >> 9 != count) {
if (size <= 0)
printk(KERN_ERR "NTFS: BUG: ntfs_fixup_record() got "
"zero size! Please report this to "
"linux-ntfs-dev@lists.sf.net\n");
return 0;
}
fixup = NTFS_GETU16(record + start);
start += 2;
offset = NTFS_SECTOR_SIZE - 2;
while (count--) {
if (NTFS_GETU16(record + offset) != fixup)
return 0;
NTFS_PUTU16(record + offset, NTFS_GETU16(record + start));
start += 2;
offset += NTFS_SECTOR_SIZE;
}
return 1;
}
/*
* Get vital informations about the ntfs partition from the boot sector.
* Return 0 on success or -1 on error.
*/
int ntfs_init_volume(ntfs_volume *vol, char *boot)
{
int sectors_per_cluster_bits;
__s64 ll;
ntfs_cluster_t mft_zone_size, tc;
/* System defined default values, in case we don't load $AttrDef. */
vol->at_standard_information = 0x10;
vol->at_attribute_list = 0x20;
vol->at_file_name = 0x30;
vol->at_volume_version = 0x40;
vol->at_security_descriptor = 0x50;
vol->at_volume_name = 0x60;
vol->at_volume_information = 0x70;
vol->at_data = 0x80;
vol->at_index_root = 0x90;
vol->at_index_allocation = 0xA0;
vol->at_bitmap = 0xB0;
vol->at_symlink = 0xC0;
/* Sector size. */
vol->sector_size = NTFS_GETU16(boot + 0xB);
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->sector_size = 0x%x\n",
vol->sector_size);
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: sectors_per_cluster = "
"0x%x\n", NTFS_GETU8(boot + 0xD));
sectors_per_cluster_bits = ffs(NTFS_GETU8(boot + 0xD)) - 1;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: sectors_per_cluster_bits "
"= 0x%x\n", sectors_per_cluster_bits);
vol->mft_clusters_per_record = NTFS_GETS8(boot + 0x40);
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_clusters_per_record"
" = 0x%x\n", vol->mft_clusters_per_record);
vol->index_clusters_per_record = NTFS_GETS8(boot + 0x44);
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: "
"vol->index_clusters_per_record = 0x%x\n",
vol->index_clusters_per_record);
vol->cluster_size = vol->sector_size << sectors_per_cluster_bits;
vol->cluster_size_bits = ffs(vol->cluster_size) - 1;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->cluster_size = 0x%x\n",
vol->cluster_size);
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->cluster_size_bits = "
"0x%x\n", vol->cluster_size_bits);
if (vol->mft_clusters_per_record > 0)
vol->mft_record_size = vol->cluster_size <<
(ffs(vol->mft_clusters_per_record) - 1);
else
/*
* When mft_record_size < cluster_size, mft_clusters_per_record
* = -log2(mft_record_size) bytes. mft_record_size normaly is
* 1024 bytes, which is encoded as 0xF6 (-10 in decimal).
*/
vol->mft_record_size = 1 << -vol->mft_clusters_per_record;
vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_record_size = 0x%x"
"\n", vol->mft_record_size);
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_record_size_bits = "
"0x%x\n", vol->mft_record_size_bits);
if (vol->index_clusters_per_record > 0)
vol->index_record_size = vol->cluster_size <<
(ffs(vol->index_clusters_per_record) - 1);
else
/*
* When index_record_size < cluster_size,
* index_clusters_per_record = -log2(index_record_size) bytes.
* index_record_size normaly equals 4096 bytes, which is
* encoded as 0xF4 (-12 in decimal).
*/
vol->index_record_size = 1 << -vol->index_clusters_per_record;
vol->index_record_size_bits = ffs(vol->index_record_size) - 1;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->index_record_size = "
"0x%x\n", vol->index_record_size);
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->index_record_size_bits "
"= 0x%x\n", vol->index_record_size_bits);
/*
* Get the size of the volume in clusters (ofs 0x28 is nr_sectors) and
* check for 64-bit-ness. Windows currently only uses 32 bits to save
* the clusters so we do the same as it is much faster on 32-bit CPUs.
*/
ll = NTFS_GETS64(boot + 0x28) >> sectors_per_cluster_bits;
if (ll >= (__s64)1 << 31) {
ntfs_error("Cannot handle 64-bit clusters. Please inform "
"linux-ntfs-dev@lists.sf.net that you got this "
"error.\n");
return -1;
}
vol->nr_clusters = (ntfs_cluster_t)ll;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->nr_clusters = 0x%x\n",
vol->nr_clusters);
vol->mft_lcn = (ntfs_cluster_t)NTFS_GETS64(boot + 0x30);
vol->mft_mirr_lcn = (ntfs_cluster_t)NTFS_GETS64(boot + 0x38);
/* Determine MFT zone size. */
mft_zone_size = vol->nr_clusters;
switch (vol->mft_zone_multiplier) { /* % of volume size in clusters */
case 4:
mft_zone_size >>= 1; /* 50% */
break;
case 3:
mft_zone_size = mft_zone_size * 3 >> 3; /* 37.5% */
break;
case 2:
mft_zone_size >>= 2; /* 25% */
break;
/* case 1: */
default:
mft_zone_size >>= 3; /* 12.5% */
break;
}
/* Setup mft zone. */
vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_zone_pos = %x\n",
vol->mft_zone_pos);
/*
* Calculate the mft_lcn for an unmodified NTFS volume (see mkntfs
* source) and if the actual mft_lcn is in the expected place or even
* further to the front of the volume, extend the mft_zone to cover the
* beginning of the volume as well. This is in order to protect the
* area reserved for the mft bitmap as well within the mft_zone itself.
* On non-standard volumes we don't protect it as well as the overhead
* would be higher than the speed increase we would get by doing it.
*/
tc = (8192 + 2 * vol->cluster_size - 1) / vol->cluster_size;
if (tc * vol->cluster_size < 16 * 1024)
tc = (16 * 1024 + vol->cluster_size - 1) / vol->cluster_size;
if (vol->mft_zone_start <= tc)
vol->mft_zone_start = (ntfs_cluster_t)0;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_zone_start = %x\n",
vol->mft_zone_start);
/*
* Need to cap the mft zone on non-standard volumes so that it does
* not point outside the boundaries of the volume, we do this by
* halving the zone size until we are inside the volume.
*/
vol->mft_zone_end = vol->mft_lcn + mft_zone_size;
while (vol->mft_zone_end >= vol->nr_clusters) {
mft_zone_size >>= 1;
vol->mft_zone_end = vol->mft_lcn + mft_zone_size;
}
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_zone_end = %x\n",
vol->mft_zone_end);
/*
* Set the current position within each data zone to the start of the
* respective zone.
*/
vol->data1_zone_pos = vol->mft_zone_end;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->data1_zone_pos = %x\n",
vol->data1_zone_pos);
vol->data2_zone_pos = (ntfs_cluster_t)0;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->data2_zone_pos = %x\n",
vol->data2_zone_pos);
/* Set the mft data allocation position to mft record 24. */
vol->mft_data_pos = 24UL;
/* This will be initialized later. */
vol->upcase = 0;
vol->upcase_length = 0;
vol->mft_ino = 0;
return 0;
}
static void ntfs_init_upcase(ntfs_inode *upcase)
{
ntfs_io io;
#define UPCASE_LENGTH 256
upcase->vol->upcase = ntfs_malloc(UPCASE_LENGTH << 1);
if (!upcase->vol->upcase)
return;
io.fn_put = ntfs_put;
io.fn_get = 0;
io.param = (char*)upcase->vol->upcase;
io.size = UPCASE_LENGTH << 1;
ntfs_read_attr(upcase, upcase->vol->at_data, 0, 0, &io);
upcase->vol->upcase_length = io.size >> 1;
}
static int process_attrdef(ntfs_inode* attrdef, ntfs_u8* def)
{
int type = NTFS_GETU32(def+0x80);
int check_type = 0;
ntfs_volume *vol = attrdef->vol;
ntfs_u16* name = (ntfs_u16*)def;
if (!type) {
ntfs_debug(DEBUG_OTHER, "process_atrdef: finished processing "
"and returning 1\n");
return 1;
}
if (ntfs_ua_strncmp(name, "$STANDARD_INFORMATION", 64) == 0) {
vol->at_standard_information = type;
check_type = 0x10;
} else if (ntfs_ua_strncmp(name, "$ATTRIBUTE_LIST", 64) == 0) {
vol->at_attribute_list = type;
check_type = 0x20;
} else if (ntfs_ua_strncmp(name, "$FILE_NAME", 64) == 0) {
vol->at_file_name = type;
check_type = 0x30;
} else if (ntfs_ua_strncmp(name, "$VOLUME_VERSION", 64) == 0) {
vol->at_volume_version = type;
check_type = 0x40;
} else if (ntfs_ua_strncmp(name, "$SECURITY_DESCRIPTOR", 64) == 0) {
vol->at_security_descriptor = type;
check_type = 0x50;
} else if (ntfs_ua_strncmp(name, "$VOLUME_NAME", 64) == 0) {
vol->at_volume_name = type;
check_type = 0x60;
} else if (ntfs_ua_strncmp(name, "$VOLUME_INFORMATION", 64) == 0) {
vol->at_volume_information = type;
check_type = 0x70;
} else if (ntfs_ua_strncmp(name, "$DATA", 64) == 0) {
vol->at_data = type;
check_type = 0x80;
} else if (ntfs_ua_strncmp(name, "$INDEX_ROOT", 64) == 0) {
vol->at_index_root = type;
check_type = 0x90;
} else if (ntfs_ua_strncmp(name, "$INDEX_ALLOCATION", 64) == 0) {
vol->at_index_allocation = type;
check_type = 0xA0;
} else if (ntfs_ua_strncmp(name, "$BITMAP", 64) == 0) {
vol->at_bitmap = type;
check_type = 0xB0;
} else if (ntfs_ua_strncmp(name, "$SYMBOLIC_LINK", 64) == 0 ||
ntfs_ua_strncmp(name, "$REPARSE_POINT", 64) == 0) {
vol->at_symlink = type;
check_type = 0xC0;
}
if (check_type && check_type != type) {
ntfs_error("process_attrdef: unexpected type 0x%x for 0x%x\n",
type, check_type);
return -EINVAL;
}
ntfs_debug(DEBUG_OTHER, "process_attrdef: found %s attribute of type "
"0x%x\n", check_type ? "known" : "unknown", type);
return 0;
}
int ntfs_init_attrdef(ntfs_inode* attrdef)
{
ntfs_u8 *buf;
ntfs_io io;
__s64 offset;
unsigned i;
int error;
ntfs_attribute *data;
ntfs_debug(DEBUG_BSD, "Entered ntfs_init_attrdef()\n");
buf = ntfs_malloc(4050); /* 90*45 */
if (!buf)
return -ENOMEM;
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
io.do_read = 1;
offset = 0;
data = ntfs_find_attr(attrdef, attrdef->vol->at_data, 0);
ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() after call to "
"ntfs_find_attr.\n");
if (!data) {
ntfs_free(buf);
return -EINVAL;
}
do {
io.param = buf;
io.size = 4050;
ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() going to call "
"ntfs_readwrite_attr.\n");
error = ntfs_readwrite_attr(attrdef, data, offset, &io);
ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() after call to "
"ntfs_readwrite_attr.\n");
for (i = 0; !error && i <= io.size - 0xA0; i += 0xA0) {
ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() going "
"to call process_attrdef.\n");
error = process_attrdef(attrdef, buf + i);
ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() after "
"call to process_attrdef.\n");
}
offset += 4096;
} while (!error && io.size);
ntfs_debug(DEBUG_BSD, "Exiting ntfs_init_attrdef()\n");
ntfs_free(buf);
return error == 1 ? 0 : error;
}
/* ntfs_get_version will determine the NTFS version of the volume and will
* return the version in a BCD format, with the MSB being the major version
* number and the LSB the minor one. Otherwise return <0 on error.
* Example: version 3.1 will be returned as 0x0301. This has the obvious
* limitation of not coping with version numbers above 0x80 but that shouldn't
* be a problem... */
int ntfs_get_version(ntfs_inode* volume)
{
ntfs_attribute *volinfo;
volinfo = ntfs_find_attr(volume, volume->vol->at_volume_information, 0);
if (!volinfo)
return -EINVAL;
if (!volinfo->resident) {
ntfs_error("Volume information attribute is not resident!\n");
return -EINVAL;
}
return ((ntfs_u8*)volinfo->d.data)[8] << 8 |
((ntfs_u8*)volinfo->d.data)[9];
}
int ntfs_load_special_files(ntfs_volume *vol)
{
int error;
ntfs_inode upcase, attrdef, volume;
vol->mft_ino = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode));
vol->mftmirr = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode));
vol->bitmap = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode));
vol->ino_flags = 4 | 2 | 1;
error = -ENOMEM;
ntfs_debug(DEBUG_BSD, "Going to load MFT\n");
if (!vol->mft_ino || (error = ntfs_init_inode(vol->mft_ino, vol,
FILE_Mft))) {
ntfs_error("Problem loading MFT\n");
return error;
}
ntfs_debug(DEBUG_BSD, "Going to load MIRR\n");
if ((error = ntfs_init_inode(vol->mftmirr, vol, FILE_MftMirr))) {
ntfs_error("Problem %d loading MFTMirr\n", error);
return error;
}
ntfs_debug(DEBUG_BSD, "Going to load BITMAP\n");
if ((error = ntfs_init_inode(vol->bitmap, vol, FILE_BitMap))) {
ntfs_error("Problem loading Bitmap\n");
return error;
}
ntfs_debug(DEBUG_BSD, "Going to load UPCASE\n");
error = ntfs_init_inode(&upcase, vol, FILE_UpCase);
if (error)
return error;
ntfs_init_upcase(&upcase);
ntfs_clear_inode(&upcase);
ntfs_debug(DEBUG_BSD, "Going to load ATTRDEF\n");
error = ntfs_init_inode(&attrdef, vol, FILE_AttrDef);
if (error)
return error;
error = ntfs_init_attrdef(&attrdef);
ntfs_clear_inode(&attrdef);
if (error)
return error;
/* Check for NTFS version and if Win2k version (ie. 3.0+) do not allow
* write access since the driver write support is broken. */
ntfs_debug(DEBUG_BSD, "Going to load VOLUME\n");
error = ntfs_init_inode(&volume, vol, FILE_Volume);
if (error)
return error;
if ((error = ntfs_get_version(&volume)) >= 0x0300 &&
!(NTFS_SB(vol)->s_flags & MS_RDONLY)) {
NTFS_SB(vol)->s_flags |= MS_RDONLY;
ntfs_error("Warning! NTFS volume version is Win2k+: Mounting "
"read-only\n");
}
ntfs_clear_inode(&volume);
if (error < 0)
return error;
ntfs_debug(DEBUG_BSD, "NTFS volume is v%d.%d\n", error >> 8,
error & 0xff);
return 0;
}
int ntfs_release_volume(ntfs_volume *vol)
{
if (((vol->ino_flags & 1) == 1) && vol->mft_ino) {
ntfs_clear_inode(vol->mft_ino);
ntfs_free(vol->mft_ino);
vol->mft_ino = 0;
}
if (((vol->ino_flags & 2) == 2) && vol->mftmirr) {
ntfs_clear_inode(vol->mftmirr);
ntfs_free(vol->mftmirr);
vol->mftmirr = 0;
}
if (((vol->ino_flags & 4) == 4) && vol->bitmap) {
ntfs_clear_inode(vol->bitmap);
ntfs_free(vol->bitmap);
vol->bitmap = 0;
}
ntfs_free(vol->mft);
ntfs_free(vol->upcase);
return 0;
}
/*
* Writes the volume size (units of clusters) into vol_size.
* Returns 0 if successful or error.
*/
int ntfs_get_volumesize(ntfs_volume *vol, ntfs_s64 *vol_size)
{
ntfs_io io;
char *cluster0;
if (!vol_size)
return -EFAULT;
cluster0 = ntfs_malloc(vol->cluster_size);
if (!cluster0)
return -ENOMEM;
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
io.param = cluster0;
io.do_read = 1;
io.size = vol->cluster_size;
ntfs_getput_clusters(vol, 0, 0, &io);
*vol_size = NTFS_GETU64(cluster0 + 0x28) >>
(ffs(NTFS_GETU8(cluster0 + 0xD)) - 1);
ntfs_free(cluster0);
return 0;
}
static int nc[16]={4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0};
int ntfs_get_free_cluster_count(ntfs_inode *bitmap)
{
ntfs_io io;
int offset, error, clusters;
unsigned char *bits = ntfs_malloc(2048);
if (!bits)
return -ENOMEM;
offset = clusters = 0;
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
while (1) {
register int i;
io.param = bits;
io.size = 2048;
error = ntfs_read_attr(bitmap, bitmap->vol->at_data, 0, offset,
&io);
if (error || io.size == 0)
break;
/* I never thought I would do loop unrolling some day */
for (i = 0; i < io.size - 8; ) {
clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
}
while (i < io.size) {
clusters += nc[bits[i] >> 4];
clusters += nc[bits[i++] & 0xF];
}
offset += io.size;
}
ntfs_free(bits);
return clusters;
}
/*
* Insert the fixups for the record. The number and location of the fixes
* is obtained from the record header but we double check with @rec_size and
* use that as the upper boundary, if necessary overwriting the count value in
* the record header.
*
* We return 0 on success or -1 if fixup header indicated the beginning of the
* update sequence array to be beyond the valid limit.
*/
int ntfs_insert_fixups(unsigned char *rec, int rec_size)
{
int first;
int count;
int offset = -2;
ntfs_u16 fix;
first = NTFS_GETU16(rec + 4);
count = (rec_size >> NTFS_SECTOR_BITS) + 1;
if (first + count * 2 > NTFS_SECTOR_SIZE - 2) {
printk(KERN_CRIT "NTFS: ntfs_insert_fixups() detected corrupt "
"NTFS record update sequence array position. - "
"Cannot hotfix.\n");
return -1;
}
if (count != NTFS_GETU16(rec + 6)) {
printk(KERN_ERR "NTFS: ntfs_insert_fixups() detected corrupt "
"NTFS record update sequence array size. - "
"Applying hotfix.\n");
NTFS_PUTU16(rec + 6, count);
}
fix = (NTFS_GETU16(rec + first) + 1) & 0xffff;
if (fix == 0xffff || !fix)
fix = 1;
NTFS_PUTU16(rec + first, fix);
count--;
while (count--) {
first += 2;
offset += NTFS_SECTOR_SIZE;
NTFS_PUTU16(rec + first, NTFS_GETU16(rec + offset));
NTFS_PUTU16(rec + offset, fix);
}
return 0;
}
/**
* ntfs_allocate_clusters - allocate logical clusters on an ntfs volume
* @vol: volume on which to allocate clusters
* @location: preferred location for first allocated cluster
* @count: number of clusters to allocate
* @rl: address of pointer in which to return the allocated run list
* @rl_len: the number of elements returned in @*rl
*
* Allocate @*count clusters (LCNs), preferably beginning at @*location in the
* bitmap of the volume @vol. If @*location is -1, it does not matter where the
* clusters are. @rl is the address of a ntfs_runlist pointer which this
* function will allocate and fill with the runlist of the allocated clusters.
* It is the callers responsibility to ntfs_vfree() @*rl after she is finished
* with it. If the function was not successful, @*rl will be set to NULL.
* @*rl_len will contain the number of ntfs_runlist elements in @*rl or 0 if
* @*rl is NULL.
*
* Return 0 on success, or -errno on error. On success, @*location and @*count
* say what was really allocated. On -ENOSPC, @*location and @*count say what
* could have been allocated. If nothing could be allocated or a different
* error occured, @*location = -1 and @*count = 0.
*
* There are two data zones. First is the area between the end of the mft zone
* and the end of the volume, and second is the area between the start of the
* volume and the start of the mft zone. On unmodified/standard volumes, the
* second mft zone doesn't exist due to the mft zone being expanded to cover
* the start of volume in order to reserve space for the mft bitmap attribute.
*
* This is not the prettiest function but the complexity stems from the need of
* implementing the mft vs data zoned approach and from the fact that we have
* access to the lcn bitmap in portions of PAGE_SIZE bytes at a time, so we
* need to cope with crossing over boundaries of two pages. Further, the fact
* that the allocator allows for caller supplied hints as to the location of
* where allocation should begin and the fact that the allocator keeps track of
* where in the data zones the next natural allocation should occur, contribute
* to the complexity of the function. But it should all be worthwhile, because
* this allocator should: 1) be a full implementation of the MFT zone approach
* used by Windows, 2) cause reduction in fragmentation as much as possible,
* and 3) be speedy in allocations (the code is not optimized for speed, but
* the algorithm is, so further speed improvements are probably possible).
*
* FIXME: Really need finer-grained locking but this will do for the moment. I
* just want to kill all races and have a working allocator. When that is done,
* we can beautify... (AIA)
*
* FIXME: We should be monitoring cluster allocation and increment the MFT zone
* size dynamically but this is something for the future. We will just cause
* heavier fragmentation by not doing it and I am not even sure Windows would
* grow the MFT zone dynamically, so might even be correct not doing this. The
* overhead in doing dynamic MFT zone expansion would be very large and unlikely
* worth the effort. (AIA)
*
* TODO: I have added in double the required zone position pointer wrap around
* logic which can be optimized to having only one of the two logic sets.
* However, having the double logic will work fine, but if we have only one of
* the sets and we get it wrong somewhere, then we get into trouble, so
* removing the duplicate logic requires _very_ careful consideration of _all_
* possible code paths. So at least for now, I am leaving the double logic -
* better safe than sorry... (AIA)
*/
int ntfs_allocate_clusters(ntfs_volume *vol, ntfs_cluster_t *location,
ntfs_cluster_t *count, ntfs_runlist **rl, int *rl_len,
const NTFS_CLUSTER_ALLOCATION_ZONES zone)
{
ntfs_runlist *rl2 = NULL, *rlt;
ntfs_attribute *data;
ntfs_cluster_t buf_pos, zone_start, zone_end, mft_zone_size;
ntfs_cluster_t lcn, last_read_pos, prev_lcn = (ntfs_cluster_t)0;
ntfs_cluster_t initial_location, prev_run_len = (ntfs_cluster_t)0;
ntfs_cluster_t clusters = (ntfs_cluster_t)0;
unsigned char *buf, *byte, bit, search_zone, done_zones;
unsigned char pass, need_writeback;
int rlpos = 0, rlsize, buf_size, err = 0;
ntfs_io io;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Entering with *location = "
"0x%x, *count = 0x%x, zone = %s_ZONE.\n", *location,
*count, zone == DATA_ZONE ? "DATA" : "MFT");
buf = (char*)__get_free_page(GFP_NOFS);
if (!buf) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Returning "
"-ENOMEM.\n");
return -ENOMEM;
}
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
lock_kernel();
/* Get the $DATA attribute of $Bitmap. */
data = ntfs_find_attr(vol->bitmap, vol->at_data, 0);
if (!data) {
err = -EINVAL;
goto err_ret;
}
/*
* If no specific location was requested, use the current data zone
* position, otherwise use the requested location but make sure it lies
* outside the mft zone. Also set done_zones to 0 (no zones done) and
* pass depending on whether we are starting inside a zone (1) or
* at the beginning of a zone (2). If requesting from the MFT_ZONE, then
* we either start at the current position within the mft zone or at the
* specified position and if the latter is out of bounds then we start
* at the beginning of the MFT_ZONE.
*/
done_zones = 0;
pass = 1;
/*
* zone_start and zone_end are the current search range. search_zone
* is 1 for mft zone, 2 for data zone 1 (end of mft zone till end of
* volume) and 4 for data zone 2 (start of volume till start of mft
* zone).
*/
zone_start = *location;
if (zone_start < 0) {
if (zone == DATA_ZONE)
zone_start = vol->data1_zone_pos;
else
zone_start = vol->mft_zone_pos;
if (!zone_start)
/*
* Zone starts at beginning of volume which means a
* single pass is sufficient.
*/
pass = 2;
} else if (zone_start >= vol->mft_zone_start && zone_start <
vol->mft_zone_end && zone == DATA_ZONE) {
zone_start = vol->mft_zone_end;
pass = 2;
} else if ((zone_start < vol->mft_zone_start || zone_start >=
vol->mft_zone_end) && zone == MFT_ZONE) {
zone_start = vol->mft_lcn;
if (!vol->mft_zone_end)
zone_start = (ntfs_cluster_t)0;
pass = 2;
}
if (zone == DATA_ZONE) {
/* Skip searching the mft zone. */
done_zones |= 1;
if (zone_start >= vol->mft_zone_end) {
zone_end = vol->nr_clusters;
search_zone = 2;
} else {
zone_end = vol->mft_zone_start;
search_zone = 4;
}
} else /* if (zone == MFT_ZONE) */ {
zone_end = vol->mft_zone_end;
search_zone = 1;
}
/*
* buf_pos is the current bit position inside the bitmap. We use
* initial_location to determine whether or not to do a zone switch.
*/
buf_pos = initial_location = zone_start;
/* Loop until all clusters are allocated, i.e. clusters == 0. */
clusters = *count;
rlpos = rlsize = 0;
if (*count <= 0) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): *count <= 0, "
"returning -EINVAL.\n");
err = -EINVAL;
goto err_ret;
}
while (1) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Start of outer while "
"loop: done_zones = 0x%x, search_zone = %i, "
"pass = %i, zone_start = 0x%x, zone_end = "
"0x%x, initial_location = 0x%x, buf_pos = "
"0x%x, rlpos = %i, rlsize = %i.\n",
done_zones, search_zone, pass, zone_start,
zone_end, initial_location, buf_pos, rlpos,
rlsize);
/* Loop until we run out of free clusters. */
io.param = buf;
io.size = PAGE_SIZE;
io.do_read = 1;
last_read_pos = buf_pos >> 3;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): last_read_pos = "
"0x%x.\n", last_read_pos);
err = ntfs_readwrite_attr(vol->bitmap, data, last_read_pos,
&io);
if (err) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"ntfs_read_attr failed with error "
"code %i, going to err_ret.\n", -err);
goto err_ret;
}
if (!io.size) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): !io.size, "
"going to zone_pass_done.\n");
goto zone_pass_done;
}
buf_size = io.size << 3;
lcn = buf_pos & 7;
buf_pos &= ~7;
need_writeback = 0;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before inner while "
"loop: buf_size = 0x%x, lcn = 0x%x, buf_pos = "
"0x%x, need_writeback = %i.\n", buf_size, lcn,
buf_pos, need_writeback);
while (lcn < buf_size && lcn + buf_pos < zone_end) {
byte = buf + (lcn >> 3);
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): In inner "
"while loop: buf_size = 0x%x, lcn = "
"0x%x, buf_pos = 0x%x, need_writeback "
"= %i, byte ofs = 0x%x, *byte = "
"0x%x.\n", buf_size, lcn, buf_pos,
need_writeback, lcn >> 3, *byte);
/* Skip full bytes. */
if (*byte == 0xff) {
lcn += 8;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"continuing while loop 1.\n");
continue;
}
bit = 1 << (lcn & 7);
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): bit = %i.\n",
bit);
/* If the bit is already set, go onto the next one. */
if (*byte & bit) {
lcn++;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"continuing while loop 2.\n");
continue;
}
/* Allocate the bitmap bit. */
*byte |= bit;
/* We need to write this bitmap buffer back to disk! */
need_writeback = 1;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): *byte = "
"0x%x, need_writeback = %i.\n", *byte,
need_writeback);
/* Reallocate memory if necessary. */
if ((rlpos + 2) * sizeof(ntfs_runlist) >= rlsize) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"Reallocating space.\n");
/* Setup first free bit return value. */
if (!rl2) {
*location = lcn + buf_pos;
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): *location = "
"0x%x.\n", *location);
}
rlsize += PAGE_SIZE;
rlt = ntfs_vmalloc(rlsize);
if (!rlt) {
err = -ENOMEM;
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): Failed to "
"allocate memory, "
"returning -ENOMEM, "
"going to "
"wb_err_ret.\n");
goto wb_err_ret;
}
if (rl2) {
ntfs_memcpy(rlt, rl2, rlsize -
PAGE_SIZE);
ntfs_vfree(rl2);
}
rl2 = rlt;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"Reallocated memory, rlsize = "
"0x%x.\n", rlsize);
}
/*
* Coalesce with previous run if adjacent LCNs.
* Otherwise, append a new run.
*/
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Adding run "
"(lcn 0x%x, len 0x%x), prev_lcn = "
"0x%x, lcn = 0x%x, buf_pos = 0x%x, "
"prev_run_len = 0x%x, rlpos = %i.\n",
lcn + buf_pos, 1, prev_lcn, lcn,
buf_pos, prev_run_len, rlpos);
if (prev_lcn == lcn + buf_pos - prev_run_len && rlpos) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"Coalescing to run (lcn 0x%x, "
"len 0x%x).\n",
rl2[rlpos - 1].lcn,
rl2[rlpos - 1].len);
rl2[rlpos - 1].len = ++prev_run_len;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"Run now (lcn 0x%x, len 0x%x), "
"prev_run_len = 0x%x.\n",
rl2[rlpos - 1].lcn,
rl2[rlpos - 1].len,
prev_run_len);
} else {
if (rlpos)
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): Adding new run, "
"(previous run lcn "
"0x%x, len 0x%x).\n",
rl2[rlpos - 1].lcn,
rl2[rlpos - 1].len);
else
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): Adding new run, "
"is first run.\n");
rl2[rlpos].lcn = prev_lcn = lcn + buf_pos;
rl2[rlpos].len = prev_run_len =
(ntfs_cluster_t)1;
rlpos++;
}
/* Done? */
if (!--clusters) {
ntfs_cluster_t tc;
/*
* Update the current zone position. Positions
* of already scanned zones have been updated
* during the respective zone switches.
*/
tc = lcn + buf_pos + 1;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"Done. Updating current zone "
"position, tc = 0x%x, "
"search_zone = %i.\n", tc,
search_zone);
switch (search_zone) {
case 1:
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): Before checks, "
"vol->mft_zone_pos = "
"0x%x.\n",
vol->mft_zone_pos);
if (tc >= vol->mft_zone_end) {
vol->mft_zone_pos =
vol->mft_lcn;
if (!vol->mft_zone_end)
vol->mft_zone_pos =
(ntfs_cluster_t)0;
} else if ((initial_location >=
vol->mft_zone_pos ||
tc > vol->mft_zone_pos)
&& tc >= vol->mft_lcn)
vol->mft_zone_pos = tc;
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): After checks, "
"vol->mft_zone_pos = "
"0x%x.\n",
vol->mft_zone_pos);
break;
case 2:
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): Before checks, "
"vol->data1_zone_pos = "
"0x%x.\n",
vol->data1_zone_pos);
if (tc >= vol->nr_clusters)
vol->data1_zone_pos =
vol->mft_zone_end;
else if ((initial_location >=
vol->data1_zone_pos ||
tc > vol->data1_zone_pos)
&& tc >= vol->mft_zone_end)
vol->data1_zone_pos = tc;
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): After checks, "
"vol->data1_zone_pos = "
"0x%x.\n",
vol->data1_zone_pos);
break;
case 4:
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): Before checks, "
"vol->data2_zone_pos = "
"0x%x.\n",
vol->data2_zone_pos);
if (tc >= vol->mft_zone_start)
vol->data2_zone_pos =
(ntfs_cluster_t)0;
else if (initial_location >=
vol->data2_zone_pos ||
tc > vol->data2_zone_pos)
vol->data2_zone_pos = tc;
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): After checks, "
"vol->data2_zone_pos = "
"0x%x.\n",
vol->data2_zone_pos);
break;
default:
BUG();
}
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"Going to done_ret.\n");
goto done_ret;
}
lcn++;
}
buf_pos += buf_size;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After inner while "
"loop: buf_size = 0x%x, lcn = 0x%x, buf_pos = "
"0x%x, need_writeback = %i.\n", buf_size, lcn,
buf_pos, need_writeback);
if (need_writeback) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Writing "
"back.\n");
need_writeback = 0;
io.param = buf;
io.do_read = 0;
err = ntfs_readwrite_attr(vol->bitmap, data,
last_read_pos, &io);
if (err) {
ntfs_error(__FUNCTION__ "(): Bitmap writeback "
"failed in read next buffer "
"code path with error code "
"%i.\n", -err);
goto err_ret;
}
}
if (buf_pos < zone_end) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Continuing "
"outer while loop, buf_pos = 0x%x, "
"zone_end = 0x%x.\n", buf_pos,
zone_end);
continue;
}
zone_pass_done: /* Finished with the current zone pass. */
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At zone_pass_done, "
"pass = %i.\n", pass);
if (pass == 1) {
/*
* Now do pass 2, scanning the first part of the zone
* we omitted in pass 1.
*/
pass = 2;
zone_end = zone_start;
switch (search_zone) {
case 1: /* mft_zone */
zone_start = vol->mft_zone_start;
break;
case 2: /* data1_zone */
zone_start = vol->mft_zone_end;
break;
case 4: /* data2_zone */
zone_start = (ntfs_cluster_t)0;
break;
default:
BUG();
}
/* Sanity check. */
if (zone_end < zone_start)
zone_end = zone_start;
buf_pos = zone_start;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Continuing "
"outer while loop, pass = 2, "
"zone_start = 0x%x, zone_end = 0x%x, "
"buf_pos = 0x%x.\n");
continue;
} /* pass == 2 */
done_zones_check:
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At done_zones_check, "
"search_zone = %i, done_zones before = 0x%x, "
"done_zones after = 0x%x.\n",
search_zone, done_zones, done_zones |
search_zone);
done_zones |= search_zone;
if (done_zones < 7) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Switching "
"zone.\n");
/* Now switch to the next zone we haven't done yet. */
pass = 1;
switch (search_zone) {
case 1:
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"Switching from mft zone to "
"data1 zone.\n");
/* Update mft zone position. */
if (rlpos) {
ntfs_cluster_t tc;
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): Before checks, "
"vol->mft_zone_pos = "
"0x%x.\n",
vol->mft_zone_pos);
tc = rl2[rlpos - 1].lcn +
rl2[rlpos - 1].len;
if (tc >= vol->mft_zone_end) {
vol->mft_zone_pos =
vol->mft_lcn;
if (!vol->mft_zone_end)
vol->mft_zone_pos =
(ntfs_cluster_t)0;
} else if ((initial_location >=
vol->mft_zone_pos ||
tc > vol->mft_zone_pos)
&& tc >= vol->mft_lcn)
vol->mft_zone_pos = tc;
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): After checks, "
"vol->mft_zone_pos = "
"0x%x.\n",
vol->mft_zone_pos);
}
/* Switch from mft zone to data1 zone. */
switch_to_data1_zone: search_zone = 2;
zone_start = initial_location =
vol->data1_zone_pos;
zone_end = vol->nr_clusters;
if (zone_start == vol->mft_zone_end)
pass = 2;
if (zone_start >= zone_end) {
vol->data1_zone_pos = zone_start =
vol->mft_zone_end;
pass = 2;
}
break;
case 2:
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"Switching from data1 zone to "
"data2 zone.\n");
/* Update data1 zone position. */
if (rlpos) {
ntfs_cluster_t tc;
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): Before checks, "
"vol->data1_zone_pos = "
"0x%x.\n",
vol->data1_zone_pos);
tc = rl2[rlpos - 1].lcn +
rl2[rlpos - 1].len;
if (tc >= vol->nr_clusters)
vol->data1_zone_pos =
vol->mft_zone_end;
else if ((initial_location >=
vol->data1_zone_pos ||
tc > vol->data1_zone_pos)
&& tc >= vol->mft_zone_end)
vol->data1_zone_pos = tc;
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): After checks, "
"vol->data1_zone_pos = "
"0x%x.\n",
vol->data1_zone_pos);
}
/* Switch from data1 zone to data2 zone. */
search_zone = 4;
zone_start = initial_location =
vol->data2_zone_pos;
zone_end = vol->mft_zone_start;
if (!zone_start)
pass = 2;
if (zone_start >= zone_end) {
vol->data2_zone_pos = zone_start =
initial_location =
(ntfs_cluster_t)0;
pass = 2;
}
break;
case 4:
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"Switching from data2 zone to "
"data1 zone.\n");
/* Update data2 zone position. */
if (rlpos) {
ntfs_cluster_t tc;
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): Before checks, "
"vol->data2_zone_pos = "
"0x%x.\n",
vol->data2_zone_pos);
tc = rl2[rlpos - 1].lcn +
rl2[rlpos - 1].len;
if (tc >= vol->mft_zone_start)
vol->data2_zone_pos =
(ntfs_cluster_t)0;
else if (initial_location >=
vol->data2_zone_pos ||
tc > vol->data2_zone_pos)
vol->data2_zone_pos = tc;
ntfs_debug(DEBUG_OTHER, __FUNCTION__
"(): After checks, "
"vol->data2_zone_pos = "
"0x%x.\n",
vol->data2_zone_pos);
}
/* Switch from data2 zone to data1 zone. */
goto switch_to_data1_zone; /* See above. */
default:
BUG();
}
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After zone "
"switch, search_zone = %i, pass = %i, "
"initial_location = 0x%x, zone_start "
"= 0x%x, zone_end = 0x%x.\n",
search_zone, pass, initial_location,
zone_start, zone_end);
buf_pos = zone_start;
if (zone_start == zone_end) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
"Empty zone, going to "
"done_zones_check.\n");
/* Empty zone. Don't bother searching it. */
goto done_zones_check;
}
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Continuing "
"outer while loop.\n");
continue;
} /* done_zones == 7 */
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): All zones are "
"finished.\n");
/*
* All zones are finished! If DATA_ZONE, shrink mft zone. If
* MFT_ZONE, we have really run out of space.
*/
mft_zone_size = vol->mft_zone_end - vol->mft_zone_start;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): vol->mft_zone_start "
"= 0x%x, vol->mft_zone_end = 0x%x, "
"mft_zone_size = 0x%x.\n", vol->mft_zone_start,
vol->mft_zone_end, mft_zone_size);
if (zone == MFT_ZONE || mft_zone_size <= (ntfs_cluster_t)0) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): No free "
"clusters left, returning -ENOSPC, "
"going to fail_ret.\n");
/* Really no more space left on device. */
err = -ENOSPC;
goto fail_ret;
} /* zone == DATA_ZONE && mft_zone_size > 0 */
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Shrinking mft "
"zone.\n");
zone_end = vol->mft_zone_end;
mft_zone_size >>= 1;
if (mft_zone_size > (ntfs_cluster_t)0)
vol->mft_zone_end = vol->mft_zone_start + mft_zone_size;
else /* mft zone and data2 zone no longer exist. */
vol->data2_zone_pos = vol->mft_zone_start =
vol->mft_zone_end = (ntfs_cluster_t)0;
if (vol->mft_zone_pos >= vol->mft_zone_end) {
vol->mft_zone_pos = vol->mft_lcn;
if (!vol->mft_zone_end)
vol->mft_zone_pos = (ntfs_cluster_t)0;
}
buf_pos = zone_start = initial_location =
vol->data1_zone_pos = vol->mft_zone_end;
search_zone = 2;
pass = 2;
done_zones &= ~2;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After shrinking mft "
"zone, mft_zone_size = 0x%x, "
"vol->mft_zone_start = 0x%x, vol->mft_zone_end "
"= 0x%x, vol->mft_zone_pos = 0x%x, search_zone "
"= 2, pass = 2, dones_zones = 0x%x, zone_start "
"= 0x%x, zone_end = 0x%x, vol->data1_zone_pos "
"= 0x%x, continuing outer while loop.\n",
mft_zone_size, vol->mft_zone_start,
vol->mft_zone_end, vol->mft_zone_pos,
search_zone, pass, done_zones, zone_start,
zone_end, vol->data1_zone_pos);
}
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After outer while loop.\n");
done_ret:
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At done_ret.\n");
rl2[rlpos].lcn = (ntfs_cluster_t)-1;
rl2[rlpos].len = (ntfs_cluster_t)0;
*rl = rl2;
*rl_len = rlpos;
if (need_writeback) {
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Writing back.\n");
need_writeback = 0;
io.param = buf;
io.do_read = 0;
err = ntfs_readwrite_attr(vol->bitmap, data, last_read_pos,
&io);
if (err) {
ntfs_error(__FUNCTION__ "(): Bitmap writeback failed "
"in done code path with error code "
"%i.\n", -err);
goto err_ret;
}
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Wrote 0x%Lx bytes.\n",
io.size);
}
done_fail_ret:
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At done_fail_ret (follows "
"done_ret).\n");
unlock_kernel();
free_page((unsigned long)buf);
if (err)
ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): Failed to allocate "
"clusters. Returning with error code %i.\n",
-err);
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Syncing $Bitmap inode.\n");
if (ntfs_update_inode(vol->bitmap))
ntfs_error(__FUNCTION__ "(): Failed to sync inode $Bitmap. "
"Continuing anyway.\n");
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Returning with code %i.\n",
err);
return err;
fail_ret:
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At fail_ret.\n");
if (rl2) {
if (err == -ENOSPC) {
/* Return first free lcn and count of free clusters. */
*location = rl2[0].lcn;
*count -= clusters;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): err = "
"-ENOSPC, *location = 0x%x, *count = "
"0x%x.\n", *location, *count);
}
/* Deallocate all allocated clusters. */
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Deallocating "
"allocated clusters.\n");
ntfs_deallocate_clusters(vol, rl2, rlpos);
/* Free the runlist. */
ntfs_vfree(rl2);
} else {
if (err == -ENOSPC) {
/* Nothing free at all. */
*location = vol->data1_zone_pos; /* Irrelevant... */
*count = 0;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): No space "
"left at all, err = -ENOSPC, *location "
"= 0x%x, *count = 0.\n", *location);
}
}
*rl = NULL;
*rl_len = 0;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): *rl = NULL, *rl_len = 0, "
"going to done_fail_ret.\n");
goto done_fail_ret;
wb_err_ret:
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At wb_err_ret.\n");
if (need_writeback) {
int __err;
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Writing back.\n");
io.param = buf;
io.do_read = 0;
__err = ntfs_readwrite_attr(vol->bitmap, data, last_read_pos,
&io);
if (__err)
ntfs_error(__FUNCTION__ "(): Bitmap writeback failed "
"in error code path with error code "
"%i.\n", -__err);
need_writeback = 0;
}
err_ret:
ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At err_ret, *location = -1, "
"*count = 0, going to fail_ret.\n");
*location = -1;
*count = 0;
goto fail_ret;
}
/*
* IMPORTANT: Caller has to hold big kernel lock or the race monster will come
* to get you! (-;
* TODO: Need our own lock for bitmap accesses but BKL is more secure for now,
* considering we might not have covered all places with a lock yet. In that
* case the BKL offers a one way exclusion which is better than no exclusion
* at all... (AIA)
*/
static int ntfs_clear_bitrange(ntfs_inode *bitmap,
const ntfs_cluster_t start_bit, const ntfs_cluster_t count)
{
ntfs_cluster_t buf_size, bit, nr_bits = count;
unsigned char *buf, *byte;
int err;
ntfs_io io;
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
/* Calculate the required buffer size in bytes. */
buf_size = (ntfs_cluster_t)((start_bit & 7) + nr_bits + 7) >> 3;
if (buf_size <= (ntfs_cluster_t)(64 * 1024))
buf = ntfs_malloc(buf_size);
else
buf = ntfs_vmalloc(buf_size);
if (!buf)
return -ENOMEM;
/* Read the bitmap from the data attribute. */
io.param = byte = buf;
io.size = buf_size;
err = ntfs_read_attr(bitmap, bitmap->vol->at_data, 0, start_bit >> 3,
&io);
if (err || io.size != buf_size)
goto err_out;
/* Now clear the bits in the read bitmap. */
bit = start_bit & 7;
while (bit && nr_bits) { /* Process first partial byte, if present. */
*byte &= ~(1 << bit++);
nr_bits--;
bit &= 7;
if (!bit)
byte++;
}
while (nr_bits >= 8) { /* Process full bytes. */
*byte = 0;
nr_bits -= 8;
byte++;
}
bit = 0;
while (nr_bits) { /* Process last partial byte, if present. */
*byte &= ~(1 << bit);
nr_bits--;
bit++;
}
/* Write the modified bitmap back to disk. */
io.param = buf;
io.size = buf_size;
err = ntfs_write_attr(bitmap, bitmap->vol->at_data, 0, start_bit >> 3,
&io);
err_out:
if (buf_size <= (ntfs_cluster_t)(64 * 1024))
ntfs_free(buf);
else
ntfs_vfree(buf);
if (!err && io.size != buf_size)
err = -EIO;
return err;
}
/*
* See comments for lack of zone adjustments below in the description of the
* function ntfs_deallocate_clusters().
*/
int ntfs_deallocate_cluster_run(const ntfs_volume *vol,
const ntfs_cluster_t lcn, const ntfs_cluster_t len)
{
int err;
lock_kernel();
err = ntfs_clear_bitrange(vol->bitmap, lcn, len);
unlock_kernel();
return err;
}
/*
* This is inefficient, but logically trivial, so will do for now. Note, we
* do not touch the mft nor the data zones here because we want to minimize
* recycling of clusters to enhance the chances of data being undeleteable.
* Also we don't want the overhead. Instead we do one additional sweep of the
* current data zone during cluster allocation to check for freed clusters.
*/
int ntfs_deallocate_clusters(const ntfs_volume *vol, const ntfs_runlist *rl,
const int rl_len)
{
int i, err;
lock_kernel();
for (i = err = 0; i < rl_len && !err; i++)
err = ntfs_clear_bitrange(vol->bitmap, rl[i].lcn, rl[i].len);
unlock_kernel();
return err;
}
/*
* super.h - Header file for super.c
*
* Copyright (C) 1995-1997 Martin von Lwis
* Copyright (C) 1996-1997 Rgis Duchesne
* Copyright (c) 2001 Anton Altaparmakov
*/
int ntfs_get_free_cluster_count(ntfs_inode *bitmap);
int ntfs_get_volumesize(ntfs_volume *vol, __s64 *vol_size);
int ntfs_init_volume(ntfs_volume *vol, char *boot);
int ntfs_load_special_files(ntfs_volume *vol);
int ntfs_release_volume(ntfs_volume *vol);
int ntfs_insert_fixups(unsigned char *rec, int rec_size);
int ntfs_fixup_record(char *record, char *magic, int size);
int ntfs_allocate_clusters(ntfs_volume *vol, ntfs_cluster_t *location,
ntfs_cluster_t *count, ntfs_runlist **rl, int *rl_len,
const NTFS_CLUSTER_ALLOCATION_ZONES zone);
int ntfs_deallocate_cluster_run(const ntfs_volume *vol,
const ntfs_cluster_t lcn, const ntfs_cluster_t len);
int ntfs_deallocate_clusters(const ntfs_volume *vol, const ntfs_runlist *rl,
const int rl_len);
/*
* support.c - Specific support functions
*
* Copyright (C) 1997 Martin von Lwis
* Copyright (C) 1997 Rgis Duchesne
* Copyright (C) 2001 Anton Altaparmakov (AIA)
*/
#include "ntfstypes.h"
#include "struct.h"
#include "support.h"
#include <stdarg.h>
#include <linux/slab.h>
#include <linux/locks.h>
#include <linux/fs.h>
#include "util.h"
#include "inode.h"
#include "macros.h"
#include <linux/nls.h>
static char print_buf[1024];
#ifdef DEBUG
#include "sysctl.h"
#include <linux/kernel.h>
/* Debugging output */
void ntfs_debug(int mask, const char *fmt, ...)
{
va_list ap;
/* Filter it with the debugging level required */
if (ntdebug & mask) {
va_start(ap,fmt);
strcpy(print_buf, KERN_DEBUG "NTFS: ");
vsprintf(print_buf + 9, fmt, ap);
printk(print_buf);
va_end(ap);
}
}
#ifndef ntfs_malloc
/* Verbose kmalloc */
void *ntfs_malloc(int size)
{
void *ret;
ret = kmalloc(size, GFP_KERNEL);
ntfs_debug(DEBUG_MALLOC, "Allocating %x at %p\n", size, ret);
return ret;
}
#endif
#ifndef ntfs_free
/* Verbose kfree() */
void ntfs_free(void *block)
{
ntfs_debug(DEBUG_MALLOC, "Freeing memory at %p\n", block);
kfree(block);
}
#endif
#else /* End of DEBUG functions. Normal ones below... */
#ifndef ntfs_malloc
void *ntfs_malloc(int size)
{
return kmalloc(size, GFP_KERNEL);
}
#endif
#ifndef ntfs_free
void ntfs_free(void *block)
{
kfree(block);
}
#endif
#endif /* DEBUG */
void ntfs_bzero(void *s, int n)
{
memset(s, 0, n);
}
/* These functions deliberately return no value. It is dest, anyway,
and not used anywhere in the NTFS code. */
void ntfs_memcpy(void *dest, const void *src, ntfs_size_t n)
{
memcpy(dest, src, n);
}
void ntfs_memmove(void *dest, const void *src, ntfs_size_t n)
{
memmove(dest, src, n);
}
/* Warn that an error occurred. */
void ntfs_error(const char *fmt,...)
{
va_list ap;
va_start(ap, fmt);
strcpy(print_buf, KERN_ERR "NTFS: ");
vsprintf(print_buf + 9, fmt, ap);
printk(print_buf);
va_end(ap);
}
int ntfs_read_mft_record(ntfs_volume *vol, int mftno, char *buf)
{
int error;
ntfs_io io;
ntfs_debug(DEBUG_OTHER, "read_mft_record 0x%x\n", mftno);
if (mftno == FILE_Mft)
{
ntfs_memcpy(buf, vol->mft, vol->mft_record_size);
return 0;
}
if (!vol->mft_ino)
{
printk(KERN_ERR "NTFS: mft_ino is NULL. Something is terribly "
"wrong here!\n");
return -ENODATA;
}
io.fn_put = ntfs_put;
io.fn_get = 0;
io.param = buf;
io.size = vol->mft_record_size;
ntfs_debug(DEBUG_OTHER, "read_mft_record: calling ntfs_read_attr with: "
"mftno = 0x%x, vol->mft_record_size_bits = 0x%x, "
"mftno << vol->mft_record_size_bits = 0x%Lx\n", mftno,
vol->mft_record_size_bits,
(__s64)mftno << vol->mft_record_size_bits);
error = ntfs_read_attr(vol->mft_ino, vol->at_data, NULL,
(__s64)mftno << vol->mft_record_size_bits, &io);
if (error || (io.size != vol->mft_record_size)) {
ntfs_debug(DEBUG_OTHER, "read_mft_record: read 0x%x failed "
"(%d,%d,%d)\n", mftno, error, io.size,
vol->mft_record_size);
return error ? error : -ENODATA;
}
ntfs_debug(DEBUG_OTHER, "read_mft_record: finished read 0x%x\n", mftno);
if (!ntfs_check_mft_record(vol, buf)) {
/* FIXME: This is incomplete behaviour. We might be able to
* recover at this stage. ntfs_check_mft_record() is too
* conservative at aborting it's operations. It is OK for
* now as we just can't handle some on disk structures
* this way. (AIA) */
printk(KERN_WARNING "NTFS: Invalid MFT record for 0x%x\n", mftno);
return -EIO;
}
ntfs_debug(DEBUG_OTHER, "read_mft_record: Done 0x%x\n", mftno);
return 0;
}
int ntfs_getput_clusters(ntfs_volume *vol, int cluster, ntfs_size_t start_offs,
ntfs_io *buf)
{
struct super_block *sb = NTFS_SB(vol);
struct buffer_head *bh;
int length = buf->size;
int error = 0;
ntfs_size_t to_copy;
ntfs_debug(DEBUG_OTHER, "%s_clusters %d %d %d\n",
buf->do_read ? "get" : "put", cluster, start_offs, length);
to_copy = vol->cluster_size - start_offs;
while (length) {
if (!(bh = sb_bread(sb, cluster))) {
ntfs_debug(DEBUG_OTHER, "%s failed\n",
buf->do_read ? "Reading" : "Writing");
error = -EIO;
goto error_ret;
}
if (to_copy > length)
to_copy = length;
lock_buffer(bh);
if (buf->do_read) {
buf->fn_put(buf, bh->b_data + start_offs, to_copy);
unlock_buffer(bh);
} else {
buf->fn_get(bh->b_data + start_offs, buf, to_copy);
mark_buffer_dirty(bh);
unlock_buffer(bh);
/*
* Note: We treat synchronous IO on a per volume basis
* disregarding flags of individual inodes. This can
* lead to some strange write ordering effects upon a
* remount with a change in the sync flag but it should
* not break anything. [Except if the system crashes
* at that point in time but there would be more thigs
* to worry about than that in that case...]. (AIA)
*/
if (sb->s_flags & MS_SYNCHRONOUS) {
ll_rw_block(WRITE, 1, &bh);
wait_on_buffer(bh);
if (buffer_req(bh) && !buffer_uptodate(bh)) {
printk(KERN_ERR "IO error syncing NTFS "
"cluster [%s:%i]\n",
sb->s_id, cluster);
brelse(bh);
error = -EIO;
goto error_ret;
}
}
}
brelse(bh);
length -= to_copy;
start_offs = 0;
to_copy = vol->cluster_size;
cluster++;
}
error_ret:
return error;
}
ntfs_time64_t ntfs_now(void)
{
return ntfs_unixutc2ntutc(CURRENT_TIME);
}
int ntfs_dupuni2map(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
int *out_len)
{
int i, o, chl, chi;
char *result, *buf, charbuf[NLS_MAX_CHARSET_SIZE];
struct nls_table *nls = vol->nls_map;
result = ntfs_malloc(in_len + 1);
if (!result)
return -ENOMEM;
*out_len = in_len;
for (i = o = 0; i < in_len; i++) {
/* FIXME: Byte order? */
wchar_t uni = in[i];
if ((chl = nls->uni2char(uni, charbuf,
NLS_MAX_CHARSET_SIZE)) > 0) {
/* Adjust result buffer. */
if (chl > 1) {
buf = ntfs_malloc(*out_len + chl - 1);
if (!buf) {
i = -ENOMEM;
goto err_ret;
}
memcpy(buf, result, o);
ntfs_free(result);
result = buf;
*out_len += (chl - 1);
}
for (chi = 0; chi < chl; chi++)
result[o++] = charbuf[chi];
} else {
/* Invalid character. */
printk(KERN_ERR "NTFS: Unicode name contains a "
"character that cannot be converted "
"to chosen character set. Remount "
"with utf8 encoding and this should "
"work.\n");
i = -EILSEQ;
goto err_ret;
}
}
result[*out_len] = '\0';
*out = result;
return 0;
err_ret:
ntfs_free(result);
*out_len = 0;
*out = NULL;
return i;
}
int ntfs_dupmap2uni(ntfs_volume *vol, char* in, int in_len, ntfs_u16 **out,
int *out_len)
{
int i, o;
ntfs_u16 *result;
struct nls_table *nls = vol->nls_map;
*out = result = ntfs_malloc(2 * in_len);
if (!result) {
*out_len = 0;
return -ENOMEM;
}
*out_len = in_len;
for (i = o = 0; i < in_len; i++, o++) {
wchar_t uni;
int charlen;
charlen = nls->char2uni(&in[i], in_len - i, &uni);
if (charlen < 0) {
i = charlen;
goto err_ret;
}
*out_len -= charlen - 1;
i += charlen - 1;
/* FIXME: Byte order? */
result[o] = uni;
if (!result[o]) {
i = -EILSEQ;
goto err_ret;
}
}
return 0;
err_ret:
printk(KERN_ERR "NTFS: Name contains a character that cannot be "
"converted to Unicode.\n");
ntfs_free(result);
*out_len = 0;
*out = NULL;
return i;
}
/*
* support.h - Header file for specific support.c
*
* Copyright (C) 1997 Rgis Duchesne
* Copyright (c) 2001 Anton Altaparmakov (AIA)
*/
/* Debug levels */
#define DEBUG_OTHER 1
#define DEBUG_MALLOC 2
#define DEBUG_BSD 4
#define DEBUG_LINUX 8
#define DEBUG_DIR1 16
#define DEBUG_DIR2 32
#define DEBUG_DIR3 64
#define DEBUG_FILE1 128
#define DEBUG_FILE2 256
#define DEBUG_FILE3 512
#define DEBUG_NAME1 1024
#define DEBUG_NAME2 2048
#ifdef DEBUG
void ntfs_debug(int mask, const char *fmt, ...);
#else
#define ntfs_debug(mask, fmt...) do {} while (0)
#endif
#include <linux/slab.h>
#include <linux/vmalloc.h>
#define ntfs_malloc(size) kmalloc(size, GFP_KERNEL)
#define ntfs_free(ptr) kfree(ptr)
#define ntfs_vmalloc(size) vmalloc_32(size)
#define ntfs_vfree(ptr) vfree(ptr)
void ntfs_bzero(void *s, int n);
void ntfs_memcpy(void *dest, const void *src, ntfs_size_t n);
void ntfs_memmove(void *dest, const void *src, ntfs_size_t n);
void ntfs_error(const char *fmt,...);
int ntfs_read_mft_record(ntfs_volume *vol, int mftno, char *buf);
int ntfs_getput_clusters(ntfs_volume *pvol, int cluster, ntfs_size_t offs,
ntfs_io *buf);
ntfs_time64_t ntfs_now(void);
int ntfs_dupuni2map(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
int *out_len);
int ntfs_dupmap2uni(ntfs_volume *vol, char* in, int in_len, ntfs_u16 **out,
int *out_len);
/*
* sysctl.c - System control stuff
*
* Copyright (C) 1997 Martin von Lwis
* Copyright (C) 1997 Rgis Duchesne
*/
#include "sysctl.h"
#ifdef DEBUG
#include <linux/locks.h>
#include <linux/sysctl.h>
int ntdebug = 0;
/* Add or remove the debug sysctl
* Is this really the only file system with sysctls ?
*/
void ntfs_sysctl(int add)
{
#define FS_NTFS 1
/* Definition of the sysctl */
static ctl_table ntfs_sysctls[]={
{FS_NTFS, /* ID */
"ntfs-debug", /* name in /proc */
&ntdebug,sizeof(ntdebug), /* data ptr, data size */
0644, /* mode */
0, /* child */
proc_dointvec, /* proc handler */
0, /* strategy */
0, /* proc control block */
0,0}, /* extra */
{0}
};
/* Define the parent file : /proc/sys/fs */
static ctl_table sysctls_root[]={
{CTL_FS,
"fs",
NULL,0,
0555,
ntfs_sysctls},
{0}
};
static struct ctl_table_header *sysctls_root_header = NULL;
if(add){
if(!sysctls_root_header)
sysctls_root_header = register_sysctl_table(sysctls_root, 0);
} else if(sysctls_root_header) {
unregister_sysctl_table(sysctls_root_header);
sysctls_root_header = NULL;
}
}
#endif /* DEBUG */
/*
* sysctl.h - Header file for sysctl.c
*
* Copyright (C) 1997 Martin von Lwis
* Copyright (C) 1997 Rgis Duchesne
*/
#ifdef DEBUG
extern int ntdebug;
void ntfs_sysctl(int add);
#define SYSCTL(x) ntfs_sysctl(x)
#else
#define SYSCTL(x)
#endif /* DEBUG */
/*
* unistr.c - Unicode string handling. Part of the Linux-NTFS project.
*
* Copyright (c) 2000,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/string.h>
#include <linux/fs.h>
#include <asm/byteorder.h>
#include "unistr.h"
#include "macros.h"
/*
* This is used by the name collation functions to quickly determine what
* characters are (in)valid.
*/
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.
*/
int ntfs_are_names_equal(wchar_t *s1, size_t s1_len,
wchar_t *s2, size_t s2_len, int ic,
wchar_t *upcase, __u32 upcase_size)
{
if (s1_len != s2_len)
return 0;
if (!ic)
return memcmp(s1, s2, s1_len << 1) ? 0: 1;
return ntfs_wcsncasecmp(s1, s2, s1_len, upcase, upcase_size) ? 0: 1;
}
/**
* ntfs_collate_names - collate two Unicode names
* @upcase: upcase table (ignored if @ic is CASE_SENSITIVE)
* @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE)
* @name1: first Unicode name to compare
* @name2: second Unicode name to compare
* @ic: either CASE_SENSITIVE or IGNORE_CASE
* @err_val: if @name1 contains an invalid character return this value
*
* 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
* @ec if an invalid character is encountered in @name1 during the comparison.
*
* The following characters are considered invalid: '"', '*', '<', '>' and '?'.
*/
int ntfs_collate_names(wchar_t *upcase, __u32 upcase_len,
wchar_t *name1, __u32 name1_len,
wchar_t *name2, __u32 name2_len,
int ic, int err_val)
{
__u32 cnt, min_len;
wchar_t c1, c2;
min_len = name1_len;
if (min_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;
++name1;
++name2;
}
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_wcsncasecmp - 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_wcsncasecmp(wchar_t *s1, wchar_t *s2, size_t n,
wchar_t *upcase, __u32 upcase_size)
{
wchar_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;
}
/*
* unistr.h - Exports for unicode string handling. Part of the Linux-NTFS
* project.
*
* Copyright (c) 2000,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_UNISTR_H
#define _LINUX_NTFS_UNISTR_H
#include <linux/types.h>
#include <linux/nls.h>
extern const __u8 legal_ansi_char_array[0x40];
int ntfs_are_names_equal(wchar_t *s1, size_t s1_len,
wchar_t *s2, size_t s2_len, int ic,
wchar_t *upcase, __u32 upcase_size);
int ntfs_collate_names(wchar_t *upcase, __u32 upcase_len,
wchar_t *name1, __u32 name1_len,
wchar_t *name2, __u32 name2_len,
int ic, int err_val);
int ntfs_wcsncasecmp(wchar_t *s1, wchar_t *s2, size_t n,
wchar_t *upcase, __u32 upcase_size);
#endif /* defined _LINUX_NTFS_UNISTR_H */
/*
* util.c - Miscellaneous support
*
* Copyright (C) 1997,1999 Martin von Lwis
* Copyright (C) 1997 Rgis Duchesne
* Copyright (C) 2001 Anton Altaparmakov (AIA)
*
* The utf8 routines are copied from Python wstrop module.
*/
#include "ntfstypes.h"
#include "struct.h"
#include "util.h"
#include <linux/string.h>
#include <linux/errno.h>
#include <asm/div64.h> /* For do_div(). */
#include "support.h"
/*
* Converts a single wide character to a sequence of utf8 bytes.
* The character is represented in host byte order.
* Returns the number of bytes, or 0 on error.
*/
static int to_utf8(ntfs_u16 c, unsigned char *buf)
{
if (c == 0)
return 0; /* No support for embedded 0 runes. */
if (c < 0x80) {
if (buf)
buf[0] = (unsigned char)c;
return 1;
}
if (c < 0x800) {
if (buf) {
buf[0] = 0xc0 | (c >> 6);
buf[1] = 0x80 | (c & 0x3f);
}
return 2;
}
/* c < 0x10000 */
if (buf) {
buf[0] = 0xe0 | (c >> 12);
buf[1] = 0x80 | ((c >> 6) & 0x3f);
buf[2] = 0x80 | (c & 0x3f);
}
return 3;
}
/*
* Decodes a sequence of utf8 bytes into a single wide character.
* The character is returned in host byte order.
* Returns the number of bytes consumed, or 0 on error.
*/
static int from_utf8(const unsigned char *str, ntfs_u16 *c)
{
int l = 0, i;
if (*str < 0x80) {
*c = *str;
return 1;
}
if (*str < 0xc0) /* Lead byte must not be 10xxxxxx. */
return 0; /* Is c0 a possible lead byte? */
if (*str < 0xe0) { /* 110xxxxx */
*c = *str & 0x1f;
l = 2;
} else if (*str < 0xf0) { /* 1110xxxx */
*c = *str & 0xf;
l = 3;
} else if (*str < 0xf8) { /* 11110xxx */
*c = *str & 7;
l = 4;
} else /* We don't support characters above 0xFFFF in NTFS. */
return 0;
for (i = 1; i < l; i++) {
/* All other bytes must be 10xxxxxx. */
if ((str[i] & 0xc0) != 0x80)
return 0;
*c <<= 6;
*c |= str[i] & 0x3f;
}
return l;
}
/*
* Converts wide string to UTF-8. Expects two in- and two out-parameters.
* Returns 0 on success, or error code.
* The caller has to free the result string.
*/
static int ntfs_dupuni2utf8(ntfs_u16 *in, int in_len, char **out, int *out_len)
{
int i, tmp;
int len8;
unsigned char *result;
ntfs_debug(DEBUG_NAME1, "converting l = %d\n", in_len);
/* Count the length of the resulting UTF-8. */
for (i = len8 = 0; i < in_len; i++) {
tmp = to_utf8(NTFS_GETU16(in + i), 0);
if (!tmp)
/* Invalid character. */
return -EILSEQ;
len8 += tmp;
}
*out = result = ntfs_malloc(len8 + 1); /* allow for zero-termination */
if (!result)
return -ENOMEM;
result[len8] = '\0';
*out_len = len8;
for (i = len8 = 0; i < in_len; i++)
len8 += to_utf8(NTFS_GETU16(in + i), result + len8);
ntfs_debug(DEBUG_NAME1, "result %p:%s\n", result, result);
return 0;
}
/*
* Converts an UTF-8 sequence to a wide string. Same conventions as the
* previous function.
*/
static int ntfs_duputf82uni(unsigned char* in, int in_len, ntfs_u16** out,
int *out_len)
{
int i, tmp;
int len16;
ntfs_u16* result;
ntfs_u16 wtmp;
for (i = len16 = 0; i < in_len; i += tmp, len16++) {
tmp = from_utf8(in + i, &wtmp);
if (!tmp)
return -EILSEQ;
}
*out = result = ntfs_malloc(2 * (len16 + 1));
if (!result)
return -ENOMEM;
result[len16] = 0;
*out_len = len16;
for (i = len16 = 0; i < in_len; i += tmp, len16++) {
tmp = from_utf8(in + i, &wtmp);
NTFS_PUTU16(result + len16, wtmp);
}
return 0;
}
/* Encodings dispatchers. */
int ntfs_encodeuni(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
int *out_len)
{
if (vol->nls_map)
return ntfs_dupuni2map(vol, in, in_len, out, out_len);
else
return ntfs_dupuni2utf8(in, in_len, out, out_len);
}
int ntfs_decodeuni(ntfs_volume *vol, char *in, int in_len, ntfs_u16 **out,
int *out_len)
{
if (vol->nls_map)
return ntfs_dupmap2uni(vol, in, in_len, out, out_len);
else
return ntfs_duputf82uni(in, in_len, out, out_len);
}
/* Same address space copies. */
void ntfs_put(ntfs_io *dest, void *src, ntfs_size_t n)
{
ntfs_memcpy(dest->param, src, n);
((char*)dest->param) += n;
}
void ntfs_get(void* dest, ntfs_io *src, ntfs_size_t n)
{
ntfs_memcpy(dest, src->param, n);
((char*)src->param) += n;
}
void *ntfs_calloc(int size)
{
void *result = ntfs_malloc(size);
if (result)
ntfs_bzero(result, size);
return result;
}
/* Copy len ascii characters from from to to. :) */
void ntfs_ascii2uni(short int *to, char *from, int len)
{
int i;
for (i = 0; i < len; i++)
NTFS_PUTU16(to + i, from[i]);
to[i] = 0;
}
/* strncmp for Unicode strings. */
int ntfs_uni_strncmp(short int* a, short int *b, int n)
{
int i;
for(i = 0; i < n; i++)
{
if (NTFS_GETU16(a + i) < NTFS_GETU16(b + i))
return -1;
if (NTFS_GETU16(b + i) < NTFS_GETU16(a + i))
return 1;
if (NTFS_GETU16(a + i) == 0)
break;
}
return 0;
}
/* strncmp between Unicode and ASCII strings. */
int ntfs_ua_strncmp(short int* a, char* b, int n)
{
int i;
for (i = 0; i < n; i++) {
if(NTFS_GETU16(a + i) < b[i])
return -1;
if(b[i] < NTFS_GETU16(a + i))
return 1;
if (b[i] == 0)
return 0;
}
return 0;
}
#define NTFS_TIME_OFFSET ((ntfs_time64_t)(369*365 + 89) * 24 * 3600 * 10000000)
/* Convert the NT UTC (based 1.1.1601, in hundred nanosecond units)
* into Unix UTC (based 1.1.1970, in seconds). */
ntfs_time_t ntfs_ntutc2unixutc(ntfs_time64_t ntutc)
{
/* Subtract the NTFS time offset, then convert to 1s intervals. */
ntfs_time64_t t = ntutc - NTFS_TIME_OFFSET;
do_div(t, 10000000);
return (ntfs_time_t)t;
}
/* Convert the Unix UTC into NT UTC. */
ntfs_time64_t ntfs_unixutc2ntutc(ntfs_time_t t)
{
/* Convert to 100ns intervals and then add the NTFS time offset. */
return (ntfs_time64_t)t * 10000000 + NTFS_TIME_OFFSET;
}
#undef NTFS_TIME_OFFSET
/* Fill index name. */
void ntfs_indexname(char *buf, int type)
{
char hex[] = "0123456789ABCDEF";
int index;
*buf++ = '$';
*buf++ = 'I';
for (index = 24; index > 0; index -= 4)
if ((0xF << index) & type)
break;
while (index >= 0) {
*buf++ = hex[(type >> index) & 0xF];
index -= 4;
}
*buf = '\0';
}
/*
* util.h - Header file for util.c
*
* Copyright (C) 1997 Rgis Duchesne
* Copyright (C) 2001 Anton Altaparmakov (AIA)
*/
/* The first 16 inodes correspond to NTFS special files. */
typedef enum {
FILE_Mft = 0,
FILE_MftMirr = 1,
FILE_LogFile = 2,
FILE_Volume = 3,
FILE_AttrDef = 4,
FILE_root = 5,
FILE_BitMap = 6,
FILE_Boot = 7,
FILE_BadClus = 8,
FILE_Secure = 9,
FILE_UpCase = 10,
FILE_Extend = 11,
FILE_Reserved12 = 12,
FILE_Reserved13 = 13,
FILE_Reserved14 = 14,
FILE_Reserved15 = 15,
} NTFS_SYSTEM_FILES;
/* Memory management */
void *ntfs_calloc(int size);
/* String operations */
/* Copy Unicode <-> ASCII */
void ntfs_ascii2uni(short int *to, char *from, int len);
/* Comparison */
int ntfs_uni_strncmp(short int* a, short int *b, int n);
int ntfs_ua_strncmp(short int* a, char* b, int n);
/* Same address space copies */
void ntfs_put(ntfs_io *dest, void *src, ntfs_size_t n);
void ntfs_get(void* dest, ntfs_io *src, ntfs_size_t n);
/* Charset conversion */
int ntfs_encodeuni(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
int *out_len);
int ntfs_decodeuni(ntfs_volume *vol, char *in, int in_len, ntfs_u16 **out,
int *out_len);
/* Time conversion */
/* NT <-> Unix */
ntfs_time_t ntfs_ntutc2unixutc(ntfs_time64_t ntutc);
ntfs_time64_t ntfs_unixutc2ntutc(ntfs_time_t t);
/* Attribute names */
void ntfs_indexname(char *buf, int type);
...@@ -652,7 +652,6 @@ struct quota_mount_options ...@@ -652,7 +652,6 @@ struct quota_mount_options
#include <linux/ext2_fs_sb.h> #include <linux/ext2_fs_sb.h>
#include <linux/ext3_fs_sb.h> #include <linux/ext3_fs_sb.h>
#include <linux/hpfs_fs_sb.h> #include <linux/hpfs_fs_sb.h>
#include <linux/ntfs_fs_sb.h>
#include <linux/msdos_fs_sb.h> #include <linux/msdos_fs_sb.h>
#include <linux/iso_fs_sb.h> #include <linux/iso_fs_sb.h>
#include <linux/nfs_fs_sb.h> #include <linux/nfs_fs_sb.h>
...@@ -710,7 +709,6 @@ struct super_block { ...@@ -710,7 +709,6 @@ struct super_block {
struct ext2_sb_info ext2_sb; struct ext2_sb_info ext2_sb;
struct ext3_sb_info ext3_sb; struct ext3_sb_info ext3_sb;
struct hpfs_sb_info hpfs_sb; struct hpfs_sb_info hpfs_sb;
struct ntfs_sb_info ntfs_sb;
struct msdos_sb_info msdos_sb; struct msdos_sb_info msdos_sb;
struct isofs_sb_info isofs_sb; struct isofs_sb_info isofs_sb;
struct nfs_sb_info nfs_sb; struct nfs_sb_info nfs_sb;
......
#ifndef _LINUX_NTFS_FS_H
#define _LINUX_NTFS_FS_H
#include <asm/byteorder.h>
#define NTFS_SECTOR_BITS 9
#define NTFS_SECTOR_SIZE 512
/*
* Attribute flags (16-bit).
*/
typedef enum {
ATTR_IS_COMPRESSED = __constant_cpu_to_le16(0x0001),
ATTR_COMPRESSION_MASK = __constant_cpu_to_le16(0x00ff),
/* Compression method mask. Also,
* first illegal value. */
ATTR_IS_ENCRYPTED = __constant_cpu_to_le16(0x4000),
ATTR_IS_SPARSE = __constant_cpu_to_le16(0x8000),
} __attribute__ ((__packed__)) ATTR_FLAGS;
/*
* The two zones from which to allocate clusters.
*/
typedef enum {
MFT_ZONE,
DATA_ZONE
} NTFS_CLUSTER_ALLOCATION_ZONES;
#endif
#ifndef _LINUX_NTFS_FS_I_H
#define _LINUX_NTFS_FS_I_H
#include <linux/types.h>
/* Forward declarations, to keep number of mutual includes low */
struct ntfs_attribute;
struct ntfs_sb_info;
/* Duplicate definitions from ntfs/ntfstypes.h */
#ifndef NTFS_INTEGRAL_TYPES
#define NTFS_INTEGRAL_TYPES
typedef u8 ntfs_u8;
typedef u16 ntfs_u16;
typedef u32 ntfs_u32;
typedef u64 ntfs_u64;
typedef s8 ntfs_s8;
typedef s16 ntfs_s16;
typedef s32 ntfs_s32;
typedef s64 ntfs_s64;
#endif
#ifndef NTMODE_T
#define NTMODE_T
typedef __kernel_mode_t ntmode_t;
#endif
#ifndef NTFS_UID_T
#define NTFS_UID_T
typedef uid_t ntfs_uid_t;
#endif
#ifndef NTFS_GID_T
#define NTFS_GID_T
typedef gid_t ntfs_gid_t;
#endif
#ifndef NTFS_SIZE_T
#define NTFS_SIZE_T
typedef __kernel_size_t ntfs_size_t;
#endif
#ifndef NTFS_TIME_T
#define NTFS_TIME_T
typedef __kernel_time_t ntfs_time_t;
#endif
/* unicode character type */
#ifndef NTFS_WCHAR_T
#define NTFS_WCHAR_T
typedef u16 ntfs_wchar_t;
#endif
/* file offset */
#ifndef NTFS_OFFSET_T
#define NTFS_OFFSET_T
typedef s64 ntfs_offset_t;
#endif
/* UTC */
#ifndef NTFS_TIME64_T
#define NTFS_TIME64_T
typedef u64 ntfs_time64_t;
#endif
/*
* This is really signed long long. So we support only volumes up to 2Tb. This
* is ok as Win2k also only uses 32-bits to store clusters.
* Whatever you do keep this a SIGNED value or a lot of NTFS users with
* corrupted filesystems will lynch you! It causes massive fs corruption when
* unsigned due to the nature of many checks relying on being performed on
* signed quantities. (AIA)
*/
#ifndef NTFS_CLUSTER_T
#define NTFS_CLUSTER_T
typedef s32 ntfs_cluster_t;
#endif
/* Definition of the NTFS in-memory inode structure. */
struct ntfs_inode_info {
struct ntfs_sb_info *vol;
unsigned long i_number; /* Should be really 48 bits. */
__u16 sequence_number; /* The current sequence number. */
unsigned char *attr; /* Array of the attributes. */
int attr_count; /* Size of attrs[]. */
struct ntfs_attribute *attrs;
int record_count; /* Size of records[]. */
int *records; /* Array of the record numbers of the $Mft whose
* attributes have been inserted in the inode. */
union {
struct {
int recordsize;
int clusters_per_record;
} index;
} u;
};
/* this is a kludge */
struct ntfs_i {
struct ntfs_inode_info n;
struct inode vfs_inode;
};
#endif
#ifndef _LINUX_NTFS_FS_SB_H
#define _LINUX_NTFS_FS_SB_H
#include <linux/ntfs_fs_i.h>
struct ntfs_sb_info{
/* Configuration provided by user at mount time. */
ntfs_uid_t uid;
ntfs_gid_t gid;
ntmode_t umask;
void *nls_map;
unsigned int ngt;
char mft_zone_multiplier;
unsigned long mft_data_pos;
ntfs_cluster_t mft_zone_pos;
ntfs_cluster_t mft_zone_start;
ntfs_cluster_t mft_zone_end;
ntfs_cluster_t data1_zone_pos;
ntfs_cluster_t data2_zone_pos;
/* Configuration provided by user with the ntfstools.
* FIXME: This is no longer possible. What is this good for? (AIA) */
ntfs_size_t partition_bias; /* For access to underlying device. */
/* Attribute definitions. */
ntfs_u32 at_standard_information;
ntfs_u32 at_attribute_list;
ntfs_u32 at_file_name;
ntfs_u32 at_volume_version;
ntfs_u32 at_security_descriptor;
ntfs_u32 at_volume_name;
ntfs_u32 at_volume_information;
ntfs_u32 at_data;
ntfs_u32 at_index_root;
ntfs_u32 at_index_allocation;
ntfs_u32 at_bitmap;
ntfs_u32 at_symlink; /* aka SYMBOLIC_LINK or REPARSE_POINT */
/* Data read / calculated from the boot file. */
int sector_size;
int cluster_size;
int cluster_size_bits;
int mft_clusters_per_record;
int mft_record_size;
int mft_record_size_bits;
int index_clusters_per_record;
int index_record_size;
int index_record_size_bits;
ntfs_cluster_t nr_clusters;
ntfs_cluster_t mft_lcn;
ntfs_cluster_t mft_mirr_lcn;
/* Data read from special files. */
unsigned char *mft;
unsigned short *upcase;
unsigned int upcase_length;
/* Inodes we always hold onto. */
struct ntfs_inode_info *mft_ino;
struct ntfs_inode_info *mftmirr;
struct ntfs_inode_info *bitmap;
struct super_block *sb;
unsigned char ino_flags;
};
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment