diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 00000000..acc1a924 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,24 @@ +Makefile +Makefile.in +aclocal.m4 +autom4te.cache +conf.libgnutlstest +config.guess +config.h +config.h.in +config.log +config.status +config.sub +configure +libtool +ntfsprogs.spec +stamp-h1 +depcomp +ltmain.sh +mkinstalldirs +compile +install-sh +stamp-h.in +ltconfig +missing +INSTALL diff --git a/CREDITS b/CREDITS index 844a147e..82cc1246 100644 --- a/CREDITS +++ b/CREDITS @@ -38,4 +38,24 @@ Ismail Donmez Laszlo Dvornik Pallaghy Ajtony Szabolcs Szakacsits +Alexei Alexandrov +Albert D. Cahalan +Russ Christensen +Pete Curran +Andras Erdei +Matthew J. Fanto +Marcin GibuÅ‚a +Christophe Grenier +Ian Jackson +Carmelo Kintana +Jan Kratochvil +Lode Leroy +David Martínez Moreno +Giang Nguyen +Leonard NorrgÃ¥rd +Holger Ohmacht +Per Olofsson +Yuri Per +Richard Russon +Erik Sørnes diff --git a/Makefile.am b/Makefile.am index f1ff59cc..10b07706 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,9 +24,20 @@ MAINTAINERCLEANFILES=\ $(srcdir)/m4/lt~obsolete.m4 \ $(srcdir)/m4/ltoptions.m4 -SUBDIRS = include libfuse-lite libntfs-3g src +SUBDIRS = doc include libfuse-lite libntfs-3g ntfsprogs src doc_DATA = README dist-hook: $(MKDIR_P) "$(distdir)/m4" + +libtool: $(LIBTOOL_DEPS) + $(SHELL) ./config.status --recheck + +strip: + (cd ntfsprogs && $(MAKE) strip) || exit 1; + +extra: extras + +extras: libs + (cd ntfsprogs && $(MAKE) extras) || exit 1; diff --git a/README b/README index 12328b3c..42a601d2 100644 --- a/README +++ b/README @@ -66,3 +66,40 @@ line at the END(!) of the /etc/fstab file: /dev/sda1 /mnt/windows ntfs-3g defaults 0 0 +NTFS UTILITIES +============== + +The ntfsprogs includes utilities for doing all required tasks to NTFS +partitions. In general, just run a utility without any command line +options to display the version number and usage syntax. + +The following utilities are so far implemented: + +ntfsfix - Attempt to fix an NTFS partition and force Windows to check NTFS. + +mkntfs - Format a partition with the NTFS filesystem. See man 8 mkntfs for +command line options. + +ntfslabel - Display/change the label of an NTFS partition. See man 8 ntfslabel +for details. + +ntfsundelete - Recover deleted files from an NTFS volume. See man 8 +ntfsundelete for more details. + +ntfsresize - Resize NTFS volumes. See man 8 ntfsresize for details. + +ntfsclone - Efficiently create/restore an image of an NTFS partition. See +man 8 ntfsclone for details. + +ntfscluster - Locate the owner of any given sector or cluster on an NTFS +partition. See man 8 ntfscluster for details. + +ntfsinfo - Show some information about an NTFS partition or one of the files +or directories within it. See man 8 ntfsinfo for details. + +ntfsls - List information about files in a directory residing on an NTFS +partition. See man 8 ntfsls for details. + +ntfscat - Concatenate files and print their contents on the standard output. + +ntfscp - Overwrite files on an NTFS partition. diff --git a/TODO.include b/TODO.include new file mode 100644 index 00000000..5e8dba03 --- /dev/null +++ b/TODO.include @@ -0,0 +1,11 @@ +Add usnjrnl.h (copy from kernel driver): + +- describe the $UsnJrnl on disk structures + +Finish logfile.h: in particular, add: + +- more about the $LogFile on disk structures + +Remove ifdef HAVE_CONFIG_H and make config.h compulsory. Further, place +config.h in include/ntfs/config.h at least when distributing (make install, +make dist, make rpm). diff --git a/TODO.libntfs b/TODO.libntfs new file mode 100644 index 00000000..3c303b1f --- /dev/null +++ b/TODO.libntfs @@ -0,0 +1,65 @@ +******************** +* libntfs-gnomevfs * +******************** + +- be generic filter: provide ntfs_device_operations from parent GnomeVFSHandle + + +*********** +* libntfs * +*********** + +***************** +* HIGH priority * +***************** + +- complete ntfs_index_{add_filename,rm} + +- move ntfsdecrypt to library. + +- complete the implementation of ntfs_attr_truncate() (for compressed files) + +- add write of compressed attributes + +- detect presence of usnjrnl and if present and mounting rw, re-stamp it (see + current ntfs driver for example in 2.6.15 kernel) + +- fix support for large sector sizes by changing the logical device block size + to the sector size in the ntfs volume (it ought to work without doing this as + we do byte-aligned i/o anyway) but it would be good to change in case a + previous mount left the block size to larger value and we expect 512 bytes as + that would be incredibly inefficient + +******************* +* MEDIUM priority * +******************* + +- create API reference book template (cf. linux kernel) +- enable automatic creation of API reference + +- ntfs_index_add_filename to more generic ntfs_index_add + +- write API for conventional high level file access. + +- implement loads of utilities a-la ntfsrm, ntfsmkdir, etc... + +- implement an ntfs shell where can use the above much faster with caching, + probably extending the library in the process + +**************** +* LOW priority * +**************** + +- Do we attach attributes (ntfs_attr) to the corresponding ntfs_inode? Now we + just atach the inode to the attribute and expect the user to not shoot + themselves in the foot. + +- add ACL read/write support to library + +- add MS BackupAPI to library + +- add volume resizing support to library + +- add defrag API to library + +- write utilities for all of the above diff --git a/TODO.ntfsprogs b/TODO.ntfsprogs new file mode 100644 index 00000000..456794b7 --- /dev/null +++ b/TODO.ntfsprogs @@ -0,0 +1,128 @@ +Please keep in alphabetical order so utilities are easier to find. + +Thanks, + Anton + + +********** +* mkntfs * +********** + +- Correct support for creating volumes with larger sector sizes (mft record + size, cluster size, and index block size must be >= sector size), so for 1k, + 2k, and 4k sectors, we need to set the default mft record, cluster, and index + block size to be at least the sector size. +- Correct the odd last partition sector not being accessible under 2.4 kernels + by setting the device block size to the sector size (default is 1k on 2.4 + kernels and they can't cope with partial sectors). +- Got a report that creating a floppy with mkntfs failed. Difference between + this floppy and the floppy created by the special tool found on the net was + said to be that the bitmap is 256kib on the special floppy while mkntfs will + make it much smaller. Need to verify this and experiment with the bitmap + size to make it work. Note, reporter was using win2k. + + +************* +* ntfsclone * +************* + +- get rid of the unneeded lseek()'s during reads/writes (probably it + doesn't improve performance much, or any at all) +- catch if source and dest are the same +- disable consistency check for --metadata (e.g. if the check is crashing) +- option: --inode +- option: --data +- metadata cloning: skip more non-needed inodes +- manual: document LFS issues (smbfs' lfs option, nfs) +- manual: mention optimized seeks +- manual: optimal backup if disks have bad sectors +- manual: ntfsclone guarantees the restored image works only + if one restores to the exactly same partition. For example, + one can not copy system partition to a different partition: + minimum "hidden sectors" field and BOOT.INI need modifications. + We could do these adjustments optionally. +- check if kernel block size = GCD(page size, device size) makes + effect on performance (Al Viro says no) +- check whether the O_WRONLY -> O_RDWR change made effect on performance + + +*********** +* ntfscmp * +*********** + +- compare mft record headers +- exit status is 0 if inputs are the same, 1 if different, other if trouble +- optionally ignore less interesting fields (e.g. attribute instance) +- new option: --metadata mode +- unnamed resident attributes with same type are ignored +- code cleanup, remove many cross-util duplicates +- handle deleted records +- performance: special handling for sparse files + + +********** +* ntfscp * +********** + +- add ability to copy multiply files at once. +- add ability to create new files. + + +*********** +* ntfsfix * +*********** + +- Cleanup to use ntfs_attr_* API for editing $MFTMirr, $Volume, and $LogFile. + This has the immediate benefit of enabling attribute list support and making + the code simpler. +- On ntfs 3.0+ volumes need to disable the usn journal if it is active. This + means deleting file $UsnJrnl from /$Extend directory. +- On ntfs 3.0+ volumes need to mark the quota out of date? - Probably, but + it shouldn't cause any corruption not doing so for the moment so this is + not a showstopper bug for the first release. (AIA) + + +************* +* ntfslabel * +************* + +- Support ioctls for kernel driver and ntfsmount for reading/changing the label. + + +************* +* ntfsmount * +************* + +- Cache opened inodes for faster access. + + +************** +* ntfsresize * +************** + +High priority + - move ntfs consistency check to libntfs (for ntfsck, ntfsclone, etc) + - use different exit codes (e.g. corrupt volume detected, unsupported case, + bad sectors, etc) + +Medium priority + - cope with the rare, unsupported cases, see man ntfsresize 'KNOWN ISSUES' + - save $Bitmap if it was modified and an error occures (e.g. bad sector). + - handle signals (^C, etc) + +Low priority + - fully support disks with bad sectors (attrlist attr, unknown bad sectors) + - move volume start + + +**************** +* ntfsundelete * +**************** + +- undelete by name rather than inode number +- support for compressed files +- support for internationalisation +- recover by type? +- mass undelete (using wildcards) +- display parent directory +- name "" to MFTn diff --git a/configure.ac b/configure.ac index 09303c51..d3dd69c0 100644 --- a/configure.ac +++ b/configure.ac @@ -3,6 +3,7 @@ # compilation. # # Copyright (c) 2000-2006 Anton Altaparmakov +# Copyright (c) 2003 Jan Kratochvil # Copyright (c) 2005-2009 Szabolcs Szakacsits # Copyright (C) 2007-2008 Alon Bar-Lev # @@ -127,6 +128,12 @@ AC_ARG_ENABLE( [enable_device_default_io_ops="yes"] ) +AC_ARG_ENABLE(crypto, + AS_HELP_STRING(--enable-crypto,enable crypto related code and utilities + (default=no)), , + enable_crypto=no +) + AC_ARG_ENABLE( [nfconv], [AS_HELP_STRING([--disable-nfconv],[disable the 'nfconv' patch, which adds support for Unicode normalization form conversion when built on Mac OS X @<:@default=enabled for Mac OS X@:>@])], @@ -153,6 +160,9 @@ ifdef( [AC_PROG_LIBTOOL] ) +AC_PROG_INSTALL +PKG_PROG_PKG_CONFIG + AC_PATH_PROG([MV], [mv]) AC_PATH_PROG([RM], [rm]) AC_PATH_PROG([SED], [sed]) @@ -256,6 +266,41 @@ else FUSE_LIB_PATH=`$PKG_CONFIG --libs-only-L fuse | sed -e 's,/[/]*,/,g' -e 's,[ ]*$,,'` fi +# Autodetect whether we can build crypto stuff or not. +compile_crypto=false +if test "$enable_crypto" != "no"; then + have_libgcrypt=false + AM_PATH_LIBGCRYPT(1.2.0, [ have_libgcrypt=true ], + [ + if test "$enable_crypto" = "yes"; then + AC_MSG_ERROR([ntfsprogs crypto code requires the gcrypt library.]) + else + AC_MSG_WARN([ntfsprogs crypto code requires the gcrypt library.]) + fi + ]) + have_libgnutls=false + PKG_CHECK_MODULES(GNUTLS_MODULE, gnutls >= 1.2.8, [ have_libgnutls=true ], + [ + if test "$enable_crypto" = "yes"; then + AC_MSG_ERROR([ntfsprogs crypto code requires the gnutls library.]) + else + AC_MSG_WARN([ntfsprogs crypto code requires the gnutls library.]) + fi + ]) + if test "$have_libgcrypt" = "true"; then + if test "$have_libgnutls" = "true"; then + compile_crypto=true + fi + fi +fi +AM_CONDITIONAL(ENABLE_CRYPTO, $compile_crypto) + +# add --with-extra-includes and --with-extra-libs switch to ./configure +all_libraries="$all_libraries $USER_LDFLAGS" +all_includes="$all_includes $USER_INCLUDES" +AC_SUBST(all_includes) +AC_SUBST(all_libraries) + # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([ctype.h fcntl.h libgen.h libintl.h limits.h locale.h \ @@ -264,7 +309,7 @@ AC_CHECK_HEADERS([ctype.h fcntl.h libgen.h libintl.h limits.h locale.h \ endian.h byteswap.h sys/byteorder.h sys/endian.h sys/param.h \ sys/ioctl.h sys/mkdev.h sys/mount.h sys/stat.h sys/types.h sys/vfs.h \ sys/statvfs.h sys/sysmacros.h linux/major.h linux/fd.h linux/hdreg.h \ - machine/endian.h windows.h syslog.h]) + machine/endian.h gcrypt.h windows.h gnutls/pkcs12.h syslog.h]) # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL @@ -280,6 +325,7 @@ AC_C_BIGENDIAN( ] , ) +AC_C_CONST AC_C_INLINE AC_TYPE_OFF_T AC_TYPE_SIZE_T @@ -413,13 +459,29 @@ fi # generate files AC_CONFIG_FILES([ Makefile + doc/Makefile include/Makefile include/fuse-lite/Makefile + include/ntfs/Makefile include/ntfs-3g/Makefile libfuse-lite/Makefile libntfs-3g/Makefile libntfs-3g/libntfs-3g.pc libntfs-3g/libntfs-3g.script.so + ntfsprogs/Makefile + ntfsprogs/mkntfs.8 + ntfsprogs/ntfscat.8 + ntfsprogs/ntfsclone.8 + ntfsprogs/ntfscluster.8 + ntfsprogs/ntfscmp.8 + ntfsprogs/ntfscp.8 + ntfsprogs/ntfsfix.8 + ntfsprogs/ntfsinfo.8 + ntfsprogs/ntfslabel.8 + ntfsprogs/ntfsls.8 + ntfsprogs/ntfsprogs.8 + ntfsprogs/ntfsresize.8 + ntfsprogs/ntfsundelete.8 src/Makefile src/ntfs-3g.8 src/ntfs-3g.probe.8 diff --git a/doc/.cvsignore b/doc/.cvsignore new file mode 100644 index 00000000..282522db --- /dev/null +++ b/doc/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/doc/CodingStyle b/doc/CodingStyle new file mode 100644 index 00000000..8dc07d1d --- /dev/null +++ b/doc/CodingStyle @@ -0,0 +1,24 @@ + +The standard K&R coding style is used in this project and the guidelines +outlined in the Linux kernel CodingStyle document (found in the latest Linux +kernel main directory in Documentation/CodingStyle) should be adhered to as +strictly as possible. + +Special notes: + + Capitals are used when declaring NTFS on-disk structures which you + can't just go ahead modifying without getting killed (in a bug sense, + not literally...). In memory structures are named with lower case as + usual. + + For styles that are not explicitly defined in the document or in the + kernel one, check/read the kernel and/or libntfs source. Some parts + of the kernel might use unwanted coding styles but usually, e.g. + linux/kernel/* + has what Linus prefers/writes and most developers follow. + + The latest Linux kernel contains the script Lindent in the scripts/ + directory which formats the source the most preferred way. Moreover, + the below patch will be applied to the official kernel tree as well: + http://lkml.org/lkml/2004/1/30/180 + Check indent(1) for what the used/unused (default) parameters mean. diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 00000000..725ac632 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,12 @@ +EXTRA_DIST = \ + CodingStyle \ + attribute_definitions \ + attributes.txt \ + compression.txt \ + template.c \ + template.h \ + tunable_settings \ + system_files.txt \ + system_security_descriptors.txt + +MAINTAINERCLEANFILES = Makefile.in diff --git a/doc/attribute_definitions b/doc/attribute_definitions new file mode 100644 index 00000000..45b4d2c4 --- /dev/null +++ b/doc/attribute_definitions @@ -0,0 +1,129 @@ +/* All values are as in Windows NT4 SP6a. */ + +__u16 name[64] = "$STANDARD_INFORMATION" +__u32 type = 0x10 +__u32 unknown[2] = 0, 0 +__u32 flags = 0x40 +__u64 min_size = 0x30 +__u64 max_size = 0x30, in Win2k: 0x48 + +__u16 name[64] = "$ATTRIBUTE_LIST" +__u32 type = 0x20 +__u32 unknown[2] = 0, 0 +__u32 flags = 0x80 +__u64 min_size = 0 +__u64 max_size = -1 + +__u16 name[64] = "$FILE_NAME" +__u32 type = 0x30 +__u32 unknown[2] = 0, 0 +__u32 flags = 0x42 +__u64 min_size = 0x44 +__u64 max_size = 0x242 + +/* The $volume_version attribute has never been observed in the field. It + * probably never was used and was hence replaced by the $object_id in + * Windows 2000. */ +__u16 name[64] = "$VOLUME_VERSION" in Win2k: "$OBJECT_ID" +__u32 type = 0x40 +__u32 unknown[2] = 0, 0 +__u32 flags = 0x40 +__u64 min_size = 0x8 in Win2k: 0 +__u64 max_size = 0x8 in Win2k: 0x100 + +__u16 name[64] = "$SECURITY_DESCRIPTOR" +__u32 type = 0x50 +__u32 unknown[2] = 0, 0 +__u32 flags = 0x80 +__u64 min_size = 0 +__u64 max_size = -1 + +__u16 name[64] = "$VOLUME_NAME" +__u32 type = 0x60 +__u32 unknown[2] = 0,0 +__u32 flags = 0x40 +__u64 min_size = 0x2 +__u64 max_size = 0x100 + +__u16 name[64] = "$VOLUME_INFORMATION" +__u32 type = 0x70 +__u32 unknown[2] = 0, 0 +__u32 flags = 0x40 +__u64 min_size = 0xc +__u64 max_size = 0xc + +__u16 name[64] = "$DATA" +__u32 type = 0x80 +__u32 unknown[2] = 0, 0 +__u32 flags = 0 +__u64 min_size = 0 +__u64 max_size = -1 + +__u16 name[64] = "$INDEX_ROOT" +__u32 type = 0x90 +__u32 unknown[2] = 0, 0 +__u32 flags = 0x40 +__u64 min_size = 0 +__u64 max_size = -1 + +__u16 name[64] = "$INDEX_ALLOCATION" +__u32 type = 0xa0 +__u32 unknown[2] = 0,0 +__u32 flags = 0x80 +__u64 min_size = 0 +__u64 max_size = -1 + +__u16 name[64] = "$BITMAP" +__u32 type = 0xb0 +__u32 unknown[2] = 0, 0 +__u32 flags = 0x80 +__u64 min_size = 0 +__u64 max_size = -1 + +/* The $symbolic_link attribute has never been observed in the field. It + * probably never was used and was hence replaced by the $reparse_point in + * Windows 2000. */ +__u16 name[64] = "$SYMBOLIC_LINK" in Win2k: "$REPARSE_POINT" +__u32 type = 0xc0 +__u32 unknown[2] = 0, 0 +__u32 flags = 0x80 +__u64 min_size = 0 +__u64 max_size = -1 in Win2k: 0x4000 + +__u16 name[64] = "$EA_INFORMATION" +__u32 type = 0xd0 +__u32 unknown[2] = 0, 0 +__u32 flags = 0x40 +__u64 min_size = 0x8 +__u64 max_size = 0x8 + +__u16 name[64] = "$EA" +__u32 type = 0xe0 +__u32 unknown[2] = 0, 0 +__u32 flags = 0 +__u64 min_size = 0 +__u64 max_size = 0x10000 + +/* + * Sequence terminates here with a record all of whose fields are zero, even + * though the size of the $AttrDef data attribute is much larger (36000 bytes, + * i.e. in theory 225 attribute definitions of 160 bytes each but in practice + * only until we reach an all zero record). + * + * The following only applies to Windows 2000 and replaces the above comment. + */ + +__u16 name[64] = "$LOGGED_UTILITY_STREAM" +__u32 type = 0x100 +__u32 unknown[2] = 0, 0 +__u32 flags = 0x80 +__u64 min_size = 0 +__u64 max_size = 0x10000 + +/* + * This is terminated by a single record all of whose fields are zero. This + * also finishes the $AttrDef data attribute, i.e. the attribute size is the + * correct size of the sequence of attribute definitions (2560 bytes, i.e. + * 16 attribute definitions of 160 bytes each). + */ + diff --git a/doc/attributes.txt b/doc/attributes.txt new file mode 100644 index 00000000..94fec5f0 --- /dev/null +++ b/doc/attributes.txt @@ -0,0 +1,111 @@ +Notes on the storage of attributes. +=================================== + +Resident attributes +=================== + +Resident attributes are small enough to be stored in the mft record itself. + +When an attribute becomes too big to fit in the mft record or when the number +of attributes grows so large that there is no more space in the mft record, the +largest attribute(s) is(are) made non-resident. (Note, that some attributes +cannot be non-resident.) + +Non-resident attributes +======================= + +Non-resident attributes contain only their non-resident attribute header in the +mft record and the actual attribute value is stored anywhere on the volume +(but not in other mft records). + +The value of the attribute is saved as a sequence of clusters, named virtual +clusters, beginning at virtual cluster number (vcn) zero. Each vcn corresponds +to a cluster on the volume, or a logical cluster number (lcn). The lcns on the +volume begin with lcn zero for the very first cluster on the volume. + +The location of the attribute value is described by the mapping pairs array +present in the non-resident attribute header structure. The mapping pairs array +contains, in compressed form, the mapping between the attribute's vcns and the +corresponding lcns on the volume. + +When the mapping pairs array of a non-resident attribute becomes too large to +fit in the mft record or when there are so many attributes in the mft record +that, even though all attributes that can be non-resident have been made +non-resident, they still do not fit in the mft record, the larger non-resident +attributes are moved away from the mft record to make space. + +This is done by moving whole non-resident attribute header structures to other +mft records and/or by splitting the mapping pairs array of attributes into +several non-resident attribute headers, each containing a chunk of the +original mapping pairs array. These non-resident attribute headers each +describing the same attribute value (but different parts of it) are called +extents. + +Attribute list attribute +======================== + +The attribute list attribute is then added to the base mft record to describe +the location of each attribute. + +The attribute list attribute lists all attributes that belong to this mft +record (with the exception of itself). Each entry in the attribute list +describes the attribute listed and in which mft record its attribute header can +be found. For resident attributes this will be the same number as the base mft +record in which the attribute list attribute is located itself. For non-resident +attributes, this will be another mft record, called an extension mft record. +Naturally, all extension mft records point back to their base mft record. + +Only one attribute is stored in an extension mft record, even if the attribute +is very small. At least this is the case with Windows NT4 SP6a driver. + +Should the mapping pairs array of an attribute become so large as to not fit +into an extenstion mft record, even though its attribute is the only attribute +in this extension record, then the attribute is splitt into several extents. +The first extent starts at vcn 0 and has its lowest vcn value set to zero and +continues up to its highest vcn value. This is determined by splitting up the +mapping pairs array into chunks which just fit into an extension mft record +each. Thus the first mapping pairs array chunk will determine the value of +the highest vcn for the first extent. The attribute list will contain an entry +for this extent. Then, a second extent is created which has its lowest vcn +value set to the highest vcn of the previous extent + 1 and the next chunk of +the mapping pairs array is inserted into this extent. Again, an entry for this +extent is placed into the attribute list, and so on, until the whole +non-resident attribute's mapping pairs array has been accomodated. + +Should the attribute list become too big to fit inside the base mft record it +is made non-resident. However, it's maximum value size is determined by the +Windows cache manager to be 256kb (the size of a VACB). Also, the mapping pairs +array of the attribute list has to fit inside the base mft record, as an +attribute list can't be described by itself and attribute lists are not allowed to be nested. + +Compressed attributes +===================== + +The attribute value of each $DATA attribute can be compressed to save space. +If this is the case, the ATTR_IS_COMPRESSED flag is set. + +See the discussion on compression in include/attrib.h for more details. +FIXME: The discussion belongs here! (AIA) + +Sparse attributes (NTFS 3.0+) +============================= + +The attribute value of each $DATA attribute can be sparse to save space. +If this is the case, the ATTR_IS_SPARSE flag is set. Note, that compressed +attributes are by definition sparse, as well as compressed, without having the +ATTR_IS_SPARSE flag set. + +See the discussion on compression in include/attrib.h for more details. +FIXME: The discussion belongs here! (AIA) + +Encrypted attributes (NTFS 3.0+) +================================ + +Since NTFS 3.0, the attribute value of each $DATA attribute can be encrypted, +to protect the contents from spying eyes. If this is the case, the +ATTR_IS_ENCRYPTED flag is set. + +FIXME: Write notes on attribute encryption. The discussions from the articles +"Inside the Encrypting File System" in Windows NT magazine (?) are very good +starting points. (AIA) + diff --git a/doc/compression.txt b/doc/compression.txt new file mode 100644 index 00000000..5da5238f --- /dev/null +++ b/doc/compression.txt @@ -0,0 +1,153 @@ + +Description of the NTFS (de)compression algorithm (based on a modified LZ77 +algorithm) + +Copyright (c) 2001 Anton Altaparmakov + +This document is published under the GNU General Public License. + +Credits: This is based on notes taken from various places (most notably from +Regis Duchesne's NTFS documentation and from various LZ77 descriptions) and +further refined by looking at a few compressed streams to figure out some +uncertainties. + +Note: You should also read the runlist description with regards to compression +in linux-ntfs/include/layout.h. Just search for "Attribute compression". +FIXME: Should merge the info from there into this document some time. + +Compressed data is organized in logical "compression" blocks (cb). Each cb has +a size (cb_size) of 2^compression_unit clusters. In all versions of Windows, +NTFS (NT/2k/XP, NTFS 1.2-3.1), the only valid compression_unit is 4, IOW, each +cb is 2^4 = 16 clusters in size. + +We detect and warn about a compression_unit != 4 but we try to decompress the +data anyway. + +Compression is only supported for cluster sizes between 512 and 4096. Thus a +cb can be between 8 and 64kiB in size. + +Each cb is independent of the other cbs and is thus the minimal unit we have +to parse even if we wanted to decompress only one byte. + +Also, a cb can be totally uncompressed and this would be indicated as a sparse +cb in the runlist. + +Thus, we need to look at the runlist of the compressed data stream, starting +at the beginning of the first cb overlapping @page. So we convert the page +offset into units of clusters (vcn), and round the vcn down to a mutliple of +cb_size clusters. + +We then scan the runlist for the appropriate position. Based on what we find +there, we decide how to proceed. + +If the cb is not compressed at all, and covers the whole of @page, we pretend +to be accessing an uncompressed file, so we fall back to what we do in +aops.c::ntfs_file_readpage(), i.e. we do: + return block_read_full_page(page, ntfs_file_get_block); + +If the cb is completely sparse, and covers the whole of @page, we can just +zero out @page and complete the io (set @page up-to-date, unlock it, and +finally return 0). + +In all other cases we initiate the decompression engine, but first some more +on the compression algorithm. + +Before compression the data of each cb is further divided into 4kiB blocks, we +call them "sub compression" blocks (sb), each including a header specifying +its compressed length. So we could just scan the cb for the first sb +overlapping @page and skip the sbs before that, or we could decompress the +whole cb injecting the superfluous decompressed pages into the page cache as a +form of read ahead (this is what zisofs does for example). + +In either case, we then need to read and decompress all sbs overlapping @page, +potentially having to decompress one or more other cbs, too. + +As soon as @page is completed we could either stop or continue until we finish +the current cb, injecting pages as we go along (again following the zisofs +example). + +Because the sbs follow each other directly, we need to actually read in the +whole cb in order to be able to scan through the cb to find the first sb +overlapping @page, so it does make sense to follow the zisofs approach of +decompressing the whole cb and injecting pages as we go along. So all +discussion from now on will assume that we are going to do that. Although it +might make sense not to decompress any sbs locate before @page because this +would be a kind of "read-behind" which is probably silly, unless someone is +reading the file backwards. Performing read-ahead by decompressing all sbs +following @page OTOH, is very likely to be a good idea. + +So, we read the whole cb from disk and start at the first sb. + +As mentioned above, each sb is started with a header. The header is 16 bits of +which the lower twelve bits (i.e. bits 0 to 11) are the length (L) - 3 of the +sb (including the two bytes for the header itself, or L - 1 not counting the +two bytes for the header). The higher four bits are set to 1011 (0xb) by the +compressor for a compressed block, or to 0000 for an uncompressed block, but +the decompressor only checks the most significant bit taking a 1 to signify a +compressed block, and a 0 an uncompressed block. + +So from the header we know how many compressed bytes we need to decompress to +obtain the next 4kiB of uncompressed data and if we didn't want to decompress +this sb we could just seek to the next next one using the length read from the +header. We could then continue seeking until we reach the first sb overlapping +@page. + +In either case, we will reach a sb which we want to decompress. + +Having dealt with the 16-bit header of the sb, we now have length bytes of +compressed data to decompress. This compressed stream is further split into +tokens which are organized into groups of eight tokens. Each token group (tg) +starts with a tag byte, which is an eight bit bitmap, the bits specifying the +type of each of the following eight tokens. The least significant bit (LSB) +corresponds to the first token and the most significant bit (MSB) corresponds +to the last token. + +The two types of tokens are symbol tokens, specified by a zero bit, and phrase +tokens, specified by a set bit. + +A symbol token (st) is a single byte and is to be taken literally and copied +into the sliding window (the decompressed data). + +A phrase token (pt) is a pointer back into the sliding window (in bytes), +together with a length (again in bytes), starting at the byte the back pointer +is pointing to. Thus a phrase token defines a sequence of bytes in the sliding +window which need to be copied at the current position into the sliding window +(the decompressed data stream). + +Each pt consists of 2 bytes split into the back pointer (p) and the length (l), +each of variable bit width (but the sum of the widths of p and l is fixed at +16 bits). p is at least 4 bits and l is at most 12 bits. + +The most significant bits contain the back pointer (p), while the least +significant bits contain the length (l). + +l is actually stored as the number of bytes minus 3 (unsigned) as anything +shorter than that would be at least as long as the 2 bytes needed for the +actual pt, so no compression would be achieved. + +p is stored as the positive number of bytes minus 1 (unsigned) as going zero +bytes back is meaningless. + +Note that decompression has to occur byte by byte, as it is possible that some +of the bytes pointed to by the pt will only be generated in the sliding window +as the byte sequence pointed to by the pt is being copied into it! + +To give a concrete example: a block full of the letter A would be compressed +by storing the byte A once as a symbol token, followed by a single phrase +token with back pointer -1 (p = 0, therefore go back by -(0 + 1) bytes) and +length 4095 (l=0xffc, therefore length 0xffc + 3 bytes). + +The widths of p and l are determined from the current position within the +decompressed data (cur_pos). We don't actually care about the widths as such +however, but instead we want the mask (l_mask) with which to AND the pt to +obtain l, and the number of bits (p_shift) by which to right shift the pt to +obtain p. These are determined using the following algorithm: + +for (i = cur_pos, l_mask = 0xfff, p_shift = 12; i >= 0x10; i >>= 1) { + l_mask >>= 1; + p_shift--; +} + +Note, that as usual in NTFS, the sb header, as well as each pt, are stored in +little endian format. + diff --git a/doc/encryption.txt b/doc/encryption.txt new file mode 100644 index 00000000..e69de29b diff --git a/doc/system_files.txt b/doc/system_files.txt new file mode 100644 index 00000000..9a6cf279 --- /dev/null +++ b/doc/system_files.txt @@ -0,0 +1,41 @@ +System files mft record numbers. All these files are always marked as used +in the bitmap attribute of the mft; presumably in order to avoid accidental +allocation for random other mft records. Also, the sequence number for each +of the system files is always equal to their mft record number and it is +never modified. (Only $MFT has a sequence number of 1, rather than 0.) + +FILE_$MFT = 0, /* Master file table (mft). Data attribute + contains the entries and bitmap attribute + records which ones are in use (bit==1). */ +FILE_$MFTMirr = 1, /* Mft mirror (copy of first four mft records) + in data attribute. */ +FILE_$LogFile = 2, /* Journalling log in data attribute. */ +FILE_$Volume = 3, /* Volume name attribute and volume information + attribute (flags and ntfs version). Windows + refers to this file as volume DASD (Direct + Access Storage Device). */ +FILE_$AttrDef = 4, /* Array of attribute definitions in data + attribute. */ +FILE_$root = 5, /* Root directory. */ +FILE_$Bitmap = 6, /* Allocation bitmap of all clusters (lcns) in + data attribute. */ +FILE_$Boot = 7, /* Boot sector (always at cluster 0) in data + attribute. */ +FILE_$BadClus = 8, /* Contains all bad clusters in the non-resident + data attribute. */ +FILE_$Secure = 9, /* Shared security descriptors in data attribute + and two indexes into the descriptors. + Appeared in Windows 2000. Before that, this + file was named $Quota but was unused. */ +FILE_$UpCase = 10, /* Uppercase equivalents of all 65536 Unicode + characters in data attribute. */ +FILE_$Extend = 11, /* Directory containing other system files (eg. + $ObjId, $Quota, $Reparse and $UsnJrnl). This + is new to NTFS3.0. */ +FILE_reserved12 = 12, /* Reserved for future use (records 12-15). */ +FILE_reserved13 = 13, +FILE_reserved14 = 14, +FILE_reserved15 = 15, +FILE_first_user = 16, /* First user file, used as test limit for + whether to allow opening a file or not. */ + diff --git a/doc/system_security_descriptors.txt b/doc/system_security_descriptors.txt new file mode 100644 index 00000000..2003bb46 --- /dev/null +++ b/doc/system_security_descriptors.txt @@ -0,0 +1,33 @@ +$SD attribute value for the system files on an NTFS 1.2 volume: + +$MFT, $MFTMirr, $LogFile, $AttrDef, $Bitmap, $Boot, $BadClus, and $UpCase: + +sd: 1, 0, 0x8004, 0x00000048, 0x00000058, 0x00000000, 0x00000014; +sd.dacl.acl: 2, 0, 0x0034, 0x0002, 0x0000; +sd.dacl.acl.ace1: 0, 0, 0x0014, 0x00120089; +sd.dacl.acl.ace1.sid: 1, 1, 0, 0, 0, 0, 0, 5, 0x00000012; +sd.dacl.acl.ace2: 0, 0, 0x0018, 0x00120089; +sd.dacl.acl.ace2.sid: 1, 2, 0, 0, 0, 0, 0, 5, 0x00000020, 0x00000220; +sd.owner.sid: 1, 2, 0, 0, 0, 0, 0, 5, 0x00000020, 0x00000220; +sd.group.sid: 1, 2, 0, 0, 0, 0, 0, 5, 0x00000020, 0x00000220; + +$Volume, $Quota, and system files 0xb-0xf: + +sd: 1, 0, 0x8004, 0x00000048, 0x00000058, 0x00000000, 0x00000014; +sd.dacl.acl: 2, 0, 0x0034, 0x0002, 0x0000; +sd.dacl.acl.ace1: 0, 0, 0x0014, 0x0012019f; +sd.dacl.acl.ace1.sid: 1, 1, 0, 0, 0, 0, 0, 5, 0x00000012; +sd.dacl.acl.ace2: 0, 0, 0x0018, 0x0012019f; +sd.dacl.acl.ace2.sid: 1, 2, 0, 0, 0, 0, 0, 5, 0x00000020, 0x00000220; +sd.owner.sid: 1, 2, 0, 0, 0, 0, 0, 5, 0x00000020, 0x00000220; +sd.group.sid: 1, 2, 0, 0, 0, 0, 0, 5, 0x00000020, 0x00000220; + +. (root directory) + +sd: 1, 0, 0x8004, 0x00000030, 0x00000040, 0x00000000, 0x00000014; +sd.dacl.acl: 2, 0, 0x001c, 0x0001, 0x0000; +sd.dacl.acl.ace1: 0, 3, 0x0014, 0x001f01ff; +sd.dacl.acl.ace1.sid: 1, 1, 0, 0, 0, 0, 0, 1, 0x00000000; +sd.owner.sid: 1, 2, 0, 0, 0, 0, 0, 5, 0x00000020, 0x00000220; +sd.group.sid: 1, 2, 0, 0, 0, 0, 0, 5, 0x00000020, 0x00000220; + diff --git a/doc/template.c b/doc/template.c new file mode 100644 index 00000000..062116b4 --- /dev/null +++ b/doc/template.c @@ -0,0 +1,47 @@ +const char *EXEC_NAME = ""; +const char *EXEC_VERSION= "0.0.1"; +/* + * EXEC_NAME - Part of the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * + * Short description here. + * + * Anton Altaparmakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program (in the main directory of the Linux-NTFS distribution + * in the file COPYING); if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * WARNING: This program might not work on architectures which do not allow + * unaligned access. For those, the program would need to start using + * get/put_unaligned macros (#include ), but not doing it yet, + * since NTFS really mostly applies to ia32 only, which does allow unaligned + * accesses. We might not actually have a problem though, since the structs are + * defined as being packed so that might be enough for gcc to insert the + * correct code. + * + * If anyone using a non-little endian and/or an aligned access only CPU tries + * this program please let me know whether it works or not! + * + * Anton Altaparmakov + */ + +int main(int argc, char **argv) +{ + return 0; +} + diff --git a/doc/template.h b/doc/template.h new file mode 100644 index 00000000..9d48a6e2 --- /dev/null +++ b/doc/template.h @@ -0,0 +1,26 @@ +/* + * NAME.h - Description. Part of the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef NTFS_NAME_H +#define NTFS_NAME_H + +#endif /* defined NTFS_NAME_H */ + diff --git a/doc/tunable_settings b/doc/tunable_settings new file mode 100644 index 00000000..8181d290 --- /dev/null +++ b/doc/tunable_settings @@ -0,0 +1,31 @@ +The following settings can be tuned in Windows NT in the registry under the key +\Registry\Machine\System\CurrentControlSet\Control\FileSystem. + +NtfsDisable8dot3NameCreation default is to enable 8.3 creation + +NtfsAllowExtendedCharacterIn8dot3Name default is to disallow extended chars + +NtfsDisableLastAccessUpdate default is enable the last acc. update + +__u32 NtfsMftZoneReservation: + If not present set the variable _NtfsMftZoneMultiplier to 1. + If = 0 or > 4, again set the variable _NtfsMftZoneMultiplier to 1. + Otherwise, set the variable _NtfsMftZoneMultiplier to the value of + NtfsMftZoneReservation. + + Value = Space % of volume reserved for MftZone + 1 = 12.5% + 2 = 25% + 3 = 37.5% + 4 = 50% + +The zone multiplier is ONLY read accessed when mount_volume is called and +when deallocate_clusters is called. +The zone multiplier is ONLY write accessed when the driver initializes. + +Win2k adds: + +NtfsQuotaNotifyRate ? + +NtfsEncryptionService ? + diff --git a/getgccver b/getgccver new file mode 100755 index 00000000..ccae864a --- /dev/null +++ b/getgccver @@ -0,0 +1,27 @@ +#!/bin/sh + +if test -z "$1"; then + echo "This program is only to be run by the ./configure script." + exit 1 +fi + +# Get the gcc version. Can't do this in configure.ac as automake refuses to +# preserve the square brackets while generating the configure script. +ver_str=`$1 -dumpversion 2> /dev/null | head -n 1` + +case $2 in +version) + echo ${ver_str} + ;; +major) + echo `echo ${ver_str} | cut -d'.' -f1 | sed s/"^\([0-9]*\).*$"/"\1"/` + ;; +minor) + echo `echo ${ver_str} | cut -d'.' -f2 | sed s/"^\([0-9]*\).*$"/"\1"/` + ;; +*) + echo "This program is only to be run by the ./configure script." + exit 1 + ;; +esac +exit 0 diff --git a/include/.cvsignore b/include/.cvsignore new file mode 100644 index 00000000..282522db --- /dev/null +++ b/include/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/include/Makefile.am b/include/Makefile.am index 0fa17025..e8766d30 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,4 +1,4 @@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in -SUBDIRS = ntfs-3g fuse-lite +SUBDIRS = ntfs ntfs-3g fuse-lite diff --git a/include/ntfs/.cvsignore b/include/ntfs/.cvsignore new file mode 100644 index 00000000..282522db --- /dev/null +++ b/include/ntfs/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/include/ntfs/Makefile.am b/include/ntfs/Makefile.am new file mode 100644 index 00000000..2e01a294 --- /dev/null +++ b/include/ntfs/Makefile.am @@ -0,0 +1,11 @@ + +linux_ntfsincludedir = $(includedir)/ntfs + +noinst_HEADERS = \ + gnome-vfs-method.h \ + gnome-vfs-module.h \ + list.h \ + rich.h \ + tree.h + +MAINTAINERCLEANFILES = Makefile.in diff --git a/include/ntfs/gnome-vfs-method.h b/include/ntfs/gnome-vfs-method.h new file mode 100644 index 00000000..d83b86ff --- /dev/null +++ b/include/ntfs/gnome-vfs-method.h @@ -0,0 +1,43 @@ +/* + * gnome-vfs-method.h - Export for Gnome-VFS init/shutdown implementation of + * interface to libntfs. Par of the Linux-NTFS project. + * + * Copyright (c) 2002-2003 Jan Kratochvil + * Copyright (c) 2000-2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_GNOME_VFS_METHOD_H +#define _NTFS_GNOME_VFS_METHOD_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +G_BEGIN_DECLS + +GnomeVFSMethod *libntfs_gnomevfs_method_init(const gchar *method_name, + const gchar *args); + +void libntfs_gnomevfs_method_shutdown(void); + +G_END_DECLS + +#endif /* _NTFS_GNOME_VFS_METHOD_H */ + diff --git a/include/ntfs/gnome-vfs-module.h b/include/ntfs/gnome-vfs-module.h new file mode 100644 index 00000000..bd27f713 --- /dev/null +++ b/include/ntfs/gnome-vfs-module.h @@ -0,0 +1,42 @@ +/* + * gnome-vfs-module.h - Exports for Gnome-VFS init/shutdown implementation of + * interface to libntfs. Part of the Linux-NTFS project. + * + * Copyright (c) 2003 Jan Kratochvil + * Copyright (c) 2000-2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_GNOME_VFS_MODULE_H +#define _NTFS_GNOME_VFS_MODULE_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +G_BEGIN_DECLS + +G_LOCK_EXTERN(libntfs); + +#define libntfs_newn(objp, n) ((objp) = g_new(typeof(*(objp)), (n))) +#define libntfs_new(objp) (libntfs_newn((objp), 1)) +#define LIBNTFS_MEMZERO(objp) (memset((objp), 0, sizeof(*(objp)))) + +G_END_DECLS + +#endif /* _NTFS_GNOME_VFS_MODULE_H */ + diff --git a/include/ntfs/list.h b/include/ntfs/list.h new file mode 100644 index 00000000..9af28aa5 --- /dev/null +++ b/include/ntfs/list.h @@ -0,0 +1,192 @@ +/* + * list.h - Linked list implementation. Part of the Linux-NTFS project. + * + * Copyright (c) 2000-2002 Anton Altaparmakov and others + * + * 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 _NTFS_LIST_H +#define _NTFS_LIST_H + +/** + * struct list_head - Simple doubly linked list implementation. + * + * Copied from Linux kernel 2.4.2-ac18 into Linux-NTFS (with minor + * modifications). - AIA + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/** + * __list_add - Insert a new entry between two known consecutive entries. + * @new: + * @prev: + * @next: + * + * This is only for internal list manipulation where we know the prev/next + * entries already! + */ +static __inline__ void __list_add(struct list_head * new, + struct list_head * prev, struct list_head * next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static __inline__ void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/** + * __list_del - + * @prev: + * @next: + * + * Delete a list entry by making the prev/next entries point to each other. + * + * This is only for internal list manipulation where we know the prev/next + * entries already! + */ +static __inline__ void __list_del(struct list_head * prev, + struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * + * Note: list_empty on entry does not return true after this, the entry is in + * an undefined state. + */ +static __inline__ void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static __inline__ void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static __inline__ int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static __inline__ void list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +#endif /* defined _NTFS_LIST_H */ + diff --git a/include/ntfs/rich.h b/include/ntfs/rich.h new file mode 100644 index 00000000..b40a7a4f --- /dev/null +++ b/include/ntfs/rich.h @@ -0,0 +1,40 @@ +/* + * rich.h - Temporary junk file. Part of the Linux-NTFS project. + * + * Copyright (c) 2004-2005 Richard Russon + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_RICH_H_ +#define _NTFS_RICH_H_ + +#include "layout.h" +#include "attrib.h" +#include "bitmap.h" + +#define ROUND_UP(num,bound) (((num)+((bound)-1)) & ~((bound)-1)) +#define ROUND_DOWN(num,bound) ((num) & ~((bound)-1)) +#define ATTR_SIZE(s) ROUND_UP(s,8) + +ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx); +ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft); +int utils_free_non_residents3(struct ntfs_bmp *bmp, ntfs_inode *inode, ATTR_RECORD *attr); +int utils_free_non_residents2(ntfs_inode *inode, struct ntfs_bmp *bmp); +void ntfs_name_print(ntfschar *name, int name_len); + +#endif /* _NTFS_RICH_H_ */ + diff --git a/include/ntfs/tree.h b/include/ntfs/tree.h new file mode 100644 index 00000000..4e2745c4 --- /dev/null +++ b/include/ntfs/tree.h @@ -0,0 +1,82 @@ +/* + * tree.h - Directory tree handling code. Part of the Linux-NTFS project. + * + * Copyright (c) 2004-2005 Richard Russon + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_TREE_H_ +#define _NTFS_TREE_H_ + +#include "layout.h" +#include "volume.h" + +struct ntfs_dir; + +/** + * struct ntfs_dt - + */ +struct ntfs_dt { + struct ntfs_dir *dir; + struct ntfs_dt *parent; + u8 *data; + int data_len; + int child_count; + INDEX_ENTRY **children; + struct ntfs_dt **sub_nodes; + ntfs_inode **inodes; + VCN vcn; + INDEX_HEADER *header; + BOOL changed; +}; + + +void ntfs_dt_free(struct ntfs_dt *dt); +int ntfs_dt_rollback(struct ntfs_dt *dt); +int ntfs_dt_commit(struct ntfs_dt *dt); +BOOL ntfs_dt_create_children2(struct ntfs_dt *dt, int count); +BOOL ntfs_dt_resize_children3(struct ntfs_dt *dt, int new); +int ntfs_dt_root_count(struct ntfs_dt *dt); +int ntfs_dt_alloc_count(struct ntfs_dt *dt); +int ntfs_dt_initialise2(ntfs_volume *vol, struct ntfs_dt *dt); +struct ntfs_dt * ntfs_dt_create(struct ntfs_dir *dir, struct ntfs_dt *parent, VCN vcn); +MFT_REF ntfs_dt_find(struct ntfs_dt *dt, ntfschar *name, int name_len); +struct ntfs_dt * ntfs_dt_find2(struct ntfs_dt *dt, ntfschar *name, int name_len, int *index_num); +struct ntfs_dt * ntfs_dt_find3(struct ntfs_dt *dt, ntfschar *name, int name_len, int *index_num); +struct ntfs_dt * ntfs_dt_find4(struct ntfs_dt *dt, ntfschar *name, int name_len, int *index_num); +void ntfs_dt_find_all(struct ntfs_dt *dt); +int ntfs_dt_find_parent(struct ntfs_dt *dt); +BOOL ntfs_dt_isroot(struct ntfs_dt *dt); +int ntfs_dt_root_freespace(struct ntfs_dt *dt); +int ntfs_dt_alloc_freespace(struct ntfs_dt *dt); +int ntfs_dt_transfer(struct ntfs_dt *old, struct ntfs_dt *new, int start, int count); +int ntfs_dt_alloc_insert(struct ntfs_dt *dt, INDEX_ENTRY *first, int count); +INDEX_ENTRY * ntfs_dt_alloc_insert2(struct ntfs_dt *dt, int before, int count, int bytes); +int ntfs_dt_root_insert(struct ntfs_dt *dt, INDEX_ENTRY *first, int count); +int ntfs_dt_alloc_remove2(struct ntfs_dt *dt, int start, int count); +int ntfs_dt_root_remove2(struct ntfs_dt *dt, int start, int count); +int ntfs_dt_transfer2(struct ntfs_dt *old, struct ntfs_dt *new, int start, int count); +int ntfs_dt_root_replace(struct ntfs_dt *del, int del_num, INDEX_ENTRY *del_ie, INDEX_ENTRY *suc_ie); +BOOL ntfs_dt_alloc_replace(struct ntfs_dt *del, int del_num, INDEX_ENTRY *del_ie, INDEX_ENTRY *suc_ie); +BOOL ntfs_dt_root_remove(struct ntfs_dt *del, int del_num); +BOOL ntfs_dt_alloc_remove(struct ntfs_dt *del, int del_num); +int ntfs_dt_alloc_add(struct ntfs_dt *parent, int index_num, INDEX_ENTRY *ie, struct ntfs_dt *child); +int ntfs_dt_root_add(struct ntfs_dt *parent, int index_num, INDEX_ENTRY *ie, struct ntfs_dt *child); +int ntfs_dt_add2(INDEX_ENTRY *ie, struct ntfs_dt *suc, int suc_num, struct ntfs_dt *ded); + +#endif /* _NTFS_TREE_H_ */ + diff --git a/libntfs/.cvsignore b/libntfs/.cvsignore new file mode 100644 index 00000000..0843e907 --- /dev/null +++ b/libntfs/.cvsignore @@ -0,0 +1,9 @@ +.deps +.libs +Makefile +Makefile.in +libntfs-gnomevfs.8 +libntfs-gnomevfs.la +libntfs.conf +libntfs.la +*.lo diff --git a/libntfs/Makefile.am b/libntfs/Makefile.am new file mode 100644 index 00000000..f99b40fd --- /dev/null +++ b/libntfs/Makefile.am @@ -0,0 +1,59 @@ +# +# Before making a release, the LTVERSION string should be modified. +# The string is of the form CURRENT:REVISION:AGE. +# +# CURRENT (C) +# The most recent interface number that this library implements. +# +# REVISION (R) +# The implementation number that this library implements. +# +# AGE (A) +# The difference between the newest and oldest interfaces that this +# library implements. In other works, the library implements all the +# interface numbers in the range from number 'CURRENT - AGE' to +# 'CURRENT'. +# +# This means that: +# +# - If interfaces have been changed or added, but binary compatibility has +# been preserved, change to C+1:0:A+1 +# +# - If binary compatibility has been broken (eg removed or changed +# interfaces) change to C+1:0:0 +# +# - If the interface is the same as the previous version, change to C:R+1:A +# + +# For LTVERSION_LIBNTFS see configure.ac! +LTVERSION_LIBNTFS_GNOMEVFS = 1:0:0 + + +linux_ntfsincludedir = -I$(top_srcdir)/include/ntfs + +if ENABLE_GNOME_VFS + +gnomevfsmoduleslibdir = $(libdir)/gnome-vfs-2.0/modules +gnomevfsmoduleslib_LTLIBRARIES = libntfs-gnomevfs.la + +gnomevfsmodulesconfdir = $(sysconfdir)/gnome-vfs-2.0/modules +gnomevfsmodulesconf_DATA = libntfs.conf + +endif + +libntfs_gnomevfs_la_LDFLAGS = -version-info $(LTVERSION_LIBNTFS_GNOMEVFS) +libntfs_gnomevfs_la_LIBS = $(NTFS_3G_MODULE_LIBS) $(LIBNTFS_GNOMEVFS_LIBS) +libntfs_gnomevfs_la_CFLAGS = $(NTFS_3G_MODULE_CFLAGS) $(LIBNTFS_GNOMEVFS_CFLAGS) +libntfs_gnomevfs_la_SOURCES = \ + gnome-vfs-method.c \ + gnome-vfs-module.c + +man_MANS = libntfs-gnomevfs.8 + +AM_CPPFLAGS = $(linux_ntfsincludedir) $(all_includes) + +EXTRA_DIST = libntfs.conf.in + +MAINTAINERCLEANFILES = Makefile.in + +libs: $(lib_LTLIBRARIES) diff --git a/libntfs/gnome-vfs-method.c b/libntfs/gnome-vfs-method.c new file mode 100644 index 00000000..ce2e93e4 --- /dev/null +++ b/libntfs/gnome-vfs-method.c @@ -0,0 +1,888 @@ +/* + * gnome-vfs-method.c - Gnome-VFS init/shutdown implementation of interface to + * libntfs. Part of the Linux-NTFS project. + * + * Copyright (c) 2003 Jan Kratochvil + * Copyright (c) 2003-2006 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 "config.h" + +#undef FALSE +#undef TRUE +#include /* for 'FALSE'/'TRUE' libntfs definition */ +#define FALSE FALSE +#define TRUE TRUE + +#include "gnome-vfs-method.h" /* self */ +#include +#include +#include "gnome-vfs-module.h" +#include +#ifdef HAVE_STRING_H +#include +#endif +#include + +#include +#include + +static GnomeVFSMethod GnomeVFSMethod_static; +G_LOCK_DEFINE_STATIC(GnomeVFSMethod_static); + +/* map: (gchar *)method_name -> (struct method_name_info *) */ +static GHashTable *method_name_hash; +G_LOCK_DEFINE_STATIC(method_name_hash); + +struct method_name_info { + gchar *args; +}; + +static void method_name_hash_key_destroy_func(gchar *key) +{ + g_return_if_fail(key != NULL); + + g_free(key); +} + +static void method_name_hash_value_destroy_func(struct method_name_info *value) +{ + g_return_if_fail(value != NULL); + + g_free(value->args); + g_free(value); +} + +static void method_name_hash_init(void) +{ + G_LOCK(method_name_hash); + if (!method_name_hash) { + method_name_hash = g_hash_table_new_full( + g_str_hash, /* hash_func */ + g_str_equal, /* key_equal_func */ + (GDestroyNotify) method_name_hash_key_destroy_func, /* key_destroy_func */ + (GDestroyNotify) method_name_hash_value_destroy_func); /* value_destroy_func */ + } + G_UNLOCK(method_name_hash); +} + +/* + * map: (gchar *)uri_parent_string "method_name:uri_parent" -> (ntfs_volume *) + */ +static GHashTable *uri_parent_string_hash; +G_LOCK_DEFINE_STATIC(uri_parent_string_hash); + +static void uri_parent_string_hash_key_destroy_func(gchar *key) +{ + g_return_if_fail(key != NULL); + + g_free(key); +} + +static void uri_parent_string_hash_value_destroy_func(ntfs_volume *value) +{ + g_return_if_fail(value != NULL); + + ntfs_umount( /* errors ignored */ + value, /* vol */ + TRUE); /* force; possibly loose modifications */ +} + +static void uri_parent_string_hash_init(void) +{ + G_LOCK(uri_parent_string_hash); + if (!uri_parent_string_hash) { + uri_parent_string_hash = g_hash_table_new_full( + g_str_hash, /* hash_func */ + g_str_equal, /* key_equal_func */ + (GDestroyNotify) uri_parent_string_hash_key_destroy_func, /* key_destroy_func */ + (GDestroyNotify) uri_parent_string_hash_value_destroy_func); /* value_destroy_func */ + } + G_UNLOCK(uri_parent_string_hash); +} + +static GnomeVFSResult libntfs_gnomevfs_uri_parent_init( + ntfs_volume **volume_return, GnomeVFSURI *uri) +{ + gchar *uri_parent_string; + gchar *uri_parent_string_parent; + ntfs_volume *volume; + + g_return_val_if_fail(uri != NULL, GNOME_VFS_ERROR_INVALID_URI); + g_return_val_if_fail(volume_return != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + + uri_parent_string_hash_init(); + + if (!uri->parent) + return GNOME_VFS_ERROR_INVALID_URI; + if (!uri->text) /* not needed here but we don't permit non-specific fs-image reference */ + return GNOME_VFS_ERROR_INVALID_URI; + uri_parent_string_parent = gnome_vfs_uri_to_string(uri->parent, + GNOME_VFS_URI_HIDE_NONE); + g_assert(uri_parent_string_parent != NULL); + + uri_parent_string = g_strdup_printf("%s:%s", uri->method_string, + uri_parent_string_parent); + g_assert(uri_parent_string != NULL); + + G_LOCK(uri_parent_string_hash); + volume = g_hash_table_lookup(uri_parent_string_hash, uri_parent_string); + G_UNLOCK(uri_parent_string_hash); + if (!volume) { + struct method_name_info *method_name_info; + + G_LOCK(method_name_hash); + method_name_info = g_hash_table_lookup(method_name_hash, + uri->method_string); + G_UNLOCK(method_name_hash); + if (!method_name_info) { + /* should not happend */ + g_return_val_if_reached(GNOME_VFS_ERROR_INVALID_URI); + } + + /* TODO: Generic GnomeVFS filter. */ + if (strcmp(uri->parent->method_string, "file")) { + g_free(uri_parent_string); + return GNOME_VFS_ERROR_INVALID_URI; + } + + if (!(volume = ntfs_mount(uri->parent->text, MS_RDONLY))) { + g_free(uri_parent_string); + return GNOME_VFS_ERROR_WRONG_FORMAT; + } + + G_LOCK(uri_parent_string_hash); + g_hash_table_insert(uri_parent_string_hash, + g_strdup(uri_parent_string), volume); + G_UNLOCK(uri_parent_string_hash); + } + g_free(uri_parent_string); + + *volume_return = volume; + return GNOME_VFS_OK; +} + +static GnomeVFSResult inode_open_by_pathname(ntfs_inode **inode_return, + ntfs_volume *volume, const gchar *pathname) +{ + MFT_REF mref; + ntfs_inode *inode; + gchar *pathname_parse, *pathname_next; + int errint; + + g_return_val_if_fail(inode_return != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + g_return_val_if_fail(volume != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS); + g_return_val_if_fail(pathname != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS); + + pathname = g_path_skip_root(pathname); + pathname_parse = g_alloca(strlen(pathname) + 1); + strcpy(pathname_parse, pathname); + mref = FILE_root; + for (;;) { + ntfschar *pathname_parse_ucs2; + gchar *pathname_parse_unescaped; + int i; + + G_LOCK(libntfs); + inode = ntfs_inode_open(volume, mref); + G_UNLOCK(libntfs); + if (!inode) + return GNOME_VFS_ERROR_NOT_FOUND; + if (!*pathname_parse) { + *inode_return = inode; + return GNOME_VFS_OK; + } + for (pathname_next = pathname_parse; *pathname_next && + *pathname_next != G_DIR_SEPARATOR; pathname_next++) ; + if (*pathname_next) { + /* terminate current path element */ + *pathname_next++ = '\0'; + } + while (*pathname_next == G_DIR_SEPARATOR) + pathname_next++; + /* FIXME: Is 'pathname' utf8? */ + pathname_parse_unescaped = gnome_vfs_unescape_string( + pathname_parse, NULL); /* illegal_characters */ + libntfs_newn(pathname_parse_ucs2, + strlen(pathname_parse_unescaped) + 1); + for (i = 0; pathname_parse_unescaped[i]; i++) + pathname_parse_ucs2[i] = pathname_parse_unescaped[i]; + pathname_parse_ucs2[i] = 0; + g_free(pathname_parse_unescaped); + G_LOCK(libntfs); + mref = ntfs_inode_lookup_by_name(inode, pathname_parse_ucs2, i); + G_UNLOCK(libntfs); + g_free(pathname_parse_ucs2); + if ((MFT_REF)-1 == mref) + return GNOME_VFS_ERROR_NOT_FOUND; + G_LOCK(libntfs); + errint = ntfs_inode_close(inode); + G_UNLOCK(libntfs); + if (errint) + g_return_val_if_reached(GNOME_VFS_ERROR_INTERNAL); + pathname_parse = pathname_next; + } + /* NOTREACHED */ +} + +struct libntfs_directory { + ntfs_inode *inode; + GList *file_info_list; /* of (GnomeVFSFileInfo *); last item has ->data == NULL */ +}; + +static GnomeVFSResult libntfs_gnomevfs_open_directory(GnomeVFSMethod *method, + GnomeVFSMethodHandle **method_handle, GnomeVFSURI *uri, + GnomeVFSFileInfoOptions options __attribute__((unused)), + GnomeVFSContext *context __attribute__((unused))) +{ + GnomeVFSResult errvfsresult; + ntfs_volume *volume; + ntfs_inode *inode; + struct libntfs_directory *libntfs_directory; + + g_return_val_if_fail(method == &GnomeVFSMethod_static, + GNOME_VFS_ERROR_BAD_PARAMETERS); + g_return_val_if_fail(method_handle != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + + if (GNOME_VFS_OK != (errvfsresult = + libntfs_gnomevfs_uri_parent_init(&volume, uri))) + return errvfsresult; + + if (GNOME_VFS_OK != (errvfsresult = inode_open_by_pathname(&inode, + volume, uri->text))) + return errvfsresult; + + libntfs_new(libntfs_directory); + libntfs_directory->inode = inode; + libntfs_directory->file_info_list = NULL; + + *method_handle = (GnomeVFSMethodHandle *)libntfs_directory; + return errvfsresult; +} + +static GnomeVFSResult libntfs_gnomevfs_close_directory(GnomeVFSMethod *method, + GnomeVFSMethodHandle *method_handle, + GnomeVFSContext *context __attribute__((unused))) +{ + struct libntfs_directory *libntfs_directory; + int errint; + + g_return_val_if_fail(method == &GnomeVFSMethod_static, + GNOME_VFS_ERROR_BAD_PARAMETERS); + libntfs_directory = (struct libntfs_directory *)method_handle; + g_return_val_if_fail(libntfs_directory != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + + G_LOCK(libntfs); + errint = ntfs_inode_close(libntfs_directory->inode); + G_UNLOCK(libntfs); + if (errint) + g_return_val_if_reached(GNOME_VFS_ERROR_INTERNAL); + + if (libntfs_directory->file_info_list) { + GList *last_l; + + /* + * Prevent gnome_vfs_file_info_list_free() and its + * gnome_vfs_file_info_unref() on the last 'file_info_list' + * items as it is EOF with NULL '->data'. + */ + last_l = g_list_last(libntfs_directory->file_info_list); + g_assert(last_l->data == NULL); + libntfs_directory->file_info_list = g_list_delete_link( + libntfs_directory->file_info_list, last_l); + gnome_vfs_file_info_list_free( + libntfs_directory->file_info_list); + } + + g_free(libntfs_directory); + + return GNOME_VFS_OK; +} + +static gchar *libntfs_ntfscharo_utf8(const ntfschar *name, const int name_len) +{ + GString *gstring; + int i; + + gstring = g_string_sized_new(name_len); + for (i = 0; i < name_len; i++) + gstring = g_string_append_unichar(gstring, name[i]); + return g_string_free(gstring, /* returns utf8-formatted string */ + FALSE); /* free_segment */ +} + +/* + * Do not lock 'libntfs' here as we are already locked inside ntfs_readdir(). + */ +static int libntfs_gnomevfs_read_directory_filldir( + struct libntfs_directory *libntfs_directory /* dirent */, + const ntfschar *name, const int name_len, + const int name_type __attribute__((unused)), + const s64 pos, const MFT_REF mref, const unsigned dt_type) +{ + GnomeVFSFileInfo *file_info; + + g_return_val_if_fail(libntfs_directory != NULL, -1); + g_return_val_if_fail(name != NULL, -1); + g_return_val_if_fail(name_len >= 0, -1); + g_return_val_if_fail(pos >= 0, -1); + + /* system directory; FIXME: What is its proper identification? */ + if (name_len > 0 && name[0] == '$') + return 0; /* continue traversal */ + + file_info = gnome_vfs_file_info_new(); + file_info->name = libntfs_ntfscharo_utf8(name, name_len); + file_info->valid_fields = 0; + + switch (dt_type) { + case NTFS_DT_FIFO: + file_info->type = GNOME_VFS_FILE_TYPE_FIFO; + break; + case NTFS_DT_CHR: + file_info->type = GNOME_VFS_FILE_TYPE_CHARACTER_DEVICE; + break; + case NTFS_DT_DIR: + file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY; + break; + case NTFS_DT_BLK: + file_info->type = GNOME_VFS_FILE_TYPE_BLOCK_DEVICE; + break; + case NTFS_DT_REG: + file_info->type = GNOME_VFS_FILE_TYPE_REGULAR; + break; + case NTFS_DT_LNK: + file_info->type = GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK; + break; + case NTFS_DT_SOCK: + file_info->type = GNOME_VFS_FILE_TYPE_SOCKET; + break; + /* FIXME: What is 'NTFS_DT_WHT'? */ + default: + file_info->type = GNOME_VFS_FILE_TYPE_UNKNOWN; + } + if (file_info->type != GNOME_VFS_FILE_TYPE_UNKNOWN) + file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE; + + /* Detect 'file_info->size': */ + if (file_info->type == GNOME_VFS_FILE_TYPE_REGULAR) { + ntfs_inode *inode; + + inode = ntfs_inode_open(libntfs_directory->inode->vol, mref); + /* FIXME: Check failed 'inode' open. */ + if (inode) { + ntfs_attr *attr; + int errint; + + attr = ntfs_attr_open(inode, /* ni */ + AT_DATA, /* type */ + AT_UNNAMED, /* name */ + 0); /* name_len */ + /* FIXME: Check failed 'attr' open. */ + if (attr) { + /* FIXME: Is 'data_size' the right field? */ + file_info->size = attr->data_size; + file_info->valid_fields |= + GNOME_VFS_FILE_INFO_FIELDS_SIZE; + ntfs_attr_close(attr); + } + errint = ntfs_inode_close(inode); + /* FIXME: Check 'errint'. */ + } + } + + libntfs_directory->file_info_list = g_list_prepend( + libntfs_directory->file_info_list, file_info); + + return 0; /* continue traversal */ +} + +static GnomeVFSResult libntfs_gnomevfs_read_directory(GnomeVFSMethod *method, + GnomeVFSMethodHandle *method_handle, + GnomeVFSFileInfo *file_info, + GnomeVFSContext *context __attribute__((unused))) +{ + GnomeVFSResult errvfsresult; + struct libntfs_directory *libntfs_directory; + + g_return_val_if_fail(method == &GnomeVFSMethod_static, + GNOME_VFS_ERROR_BAD_PARAMETERS); + libntfs_directory = (struct libntfs_directory *)method_handle; + g_return_val_if_fail(libntfs_directory != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + g_return_val_if_fail(file_info != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS); + + if (!libntfs_directory->file_info_list) { + int errint; + s64 pos; + + pos = 0; /* read from the start; incl. "." and ".." entries */ + G_LOCK(libntfs); + errint = ntfs_readdir(libntfs_directory->inode, /* dir_ni */ + &pos, /* pos */ + libntfs_directory, /* dirent */ + (ntfs_filldir_t)libntfs_gnomevfs_read_directory_filldir); /* filldir */ + G_UNLOCK(libntfs); + if (errint) + return GNOME_VFS_ERROR_INTERNAL; + + libntfs_directory->file_info_list = g_list_prepend( + libntfs_directory->file_info_list, NULL); /* EOF */ + libntfs_directory->file_info_list = g_list_reverse( + libntfs_directory->file_info_list); + } + + if (!libntfs_directory->file_info_list->data) { + g_assert(libntfs_directory->file_info_list->next == NULL); + /* + * Do not clear the list to leave us stuck at EOF - GnomeVFS + * behaves that way. + */ + errvfsresult = GNOME_VFS_ERROR_EOF; + } else { + /* Cut first list item. */ + gnome_vfs_file_info_copy(file_info, /* dest */ + libntfs_directory->file_info_list->data); /* src */ + gnome_vfs_file_info_unref( + libntfs_directory->file_info_list->data); + libntfs_directory->file_info_list = g_list_delete_link( + libntfs_directory->file_info_list, + libntfs_directory->file_info_list); + errvfsresult = GNOME_VFS_OK; + } + return errvfsresult; +} + +struct libntfs_file { + ntfs_inode *inode; + ntfs_attr *attr; + s64 pos; +}; + +static GnomeVFSResult libntfs_open_attr(struct libntfs_file *libntfs_file) +{ + g_return_val_if_fail(libntfs_file != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + g_return_val_if_fail(libntfs_file->inode != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + + if (!libntfs_file->attr) { + G_LOCK(libntfs); + libntfs_file->attr = ntfs_attr_open( + libntfs_file->inode, /* ni */ + AT_DATA, /* type */ + AT_UNNAMED, /* name */ + 0); /* name_len */ + G_UNLOCK(libntfs); + if (!libntfs_file->attr) + return GNOME_VFS_ERROR_BAD_FILE; + libntfs_file->pos = 0; + } + + return GNOME_VFS_OK; +} + +static GnomeVFSResult libntfs_gnomevfs_open(GnomeVFSMethod *method, + GnomeVFSMethodHandle **method_handle_return, GnomeVFSURI *uri, + GnomeVFSOpenMode mode, + GnomeVFSContext *context __attribute__((unused))) +{ + GnomeVFSResult errvfsresult; + ntfs_volume *volume; + ntfs_inode *inode; + struct libntfs_file *libntfs_file; + + g_return_val_if_fail(method == &GnomeVFSMethod_static, + GNOME_VFS_ERROR_BAD_PARAMETERS); + g_return_val_if_fail(method_handle_return != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + + if (GNOME_VFS_OK != (errvfsresult = + libntfs_gnomevfs_uri_parent_init(&volume, uri))) + return errvfsresult; + + if (mode & GNOME_VFS_OPEN_WRITE) + return GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM; + + if (GNOME_VFS_OK != (errvfsresult = + inode_open_by_pathname(&inode, volume, uri->text))) + return errvfsresult; + + libntfs_new(libntfs_file); + libntfs_file->inode = inode; + libntfs_file->attr = NULL; + + *method_handle_return = (GnomeVFSMethodHandle *)libntfs_file; + return errvfsresult; +} + +static GnomeVFSResult libntfs_gnomevfs_create(GnomeVFSMethod *method, + GnomeVFSMethodHandle **method_handle_return, GnomeVFSURI *uri, + GnomeVFSOpenMode mode __attribute__((unused)), + gboolean exclusive __attribute__((unused)), + guint perm __attribute__((unused)), + GnomeVFSContext *context __attribute__((unused))) +{ + GnomeVFSResult errvfsresult; + ntfs_volume *volume; + + g_return_val_if_fail(method == &GnomeVFSMethod_static, + GNOME_VFS_ERROR_BAD_PARAMETERS); + g_return_val_if_fail(method_handle_return != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + + if (GNOME_VFS_OK != (errvfsresult = + libntfs_gnomevfs_uri_parent_init(&volume, uri))) + return errvfsresult; + + return GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM; +} + +static GnomeVFSResult libntfs_gnomevfs_close(GnomeVFSMethod *method, + GnomeVFSMethodHandle *method_handle, + GnomeVFSContext *context __attribute__((unused))) +{ + struct libntfs_file *libntfs_file; + int errint; + + g_return_val_if_fail(method == &GnomeVFSMethod_static, + GNOME_VFS_ERROR_BAD_PARAMETERS); + libntfs_file = (struct libntfs_file *) method_handle; + g_return_val_if_fail(libntfs_file != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + + if (libntfs_file->attr) { + G_LOCK(libntfs); + ntfs_attr_close(libntfs_file->attr); + G_UNLOCK(libntfs); + } + G_LOCK(libntfs); + errint = ntfs_inode_close(libntfs_file->inode); + G_UNLOCK(libntfs); + if (errint) + g_return_val_if_reached(GNOME_VFS_ERROR_INTERNAL); + + g_free(libntfs_file); + + return GNOME_VFS_OK; +} + +static GnomeVFSResult libntfs_gnomevfs_read(GnomeVFSMethod *method, + GnomeVFSMethodHandle *method_handle, gpointer buffer, + GnomeVFSFileSize num_bytes, GnomeVFSFileSize *bytes_read_return, + GnomeVFSContext *context __attribute__((unused))) +{ + GnomeVFSResult errvfsresult; + struct libntfs_file *libntfs_file; + s64 count_s64, got; + + g_return_val_if_fail(method == &GnomeVFSMethod_static, + GNOME_VFS_ERROR_BAD_PARAMETERS); + libntfs_file = (struct libntfs_file *)method_handle; + g_return_val_if_fail(libntfs_file != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + g_return_val_if_fail(buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS); + g_return_val_if_fail(bytes_read_return != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + + if (GNOME_VFS_OK != (errvfsresult = libntfs_open_attr(libntfs_file))) + return errvfsresult; + + count_s64 = num_bytes; + g_assert((GnomeVFSFileSize)count_s64 == num_bytes); + G_LOCK(libntfs); + got = ntfs_attr_pread(libntfs_file->attr, libntfs_file->pos, count_s64, + buffer); + G_UNLOCK(libntfs); + if (got == -1) + return GNOME_VFS_ERROR_IO; + + libntfs_file->pos += got; + *bytes_read_return = got; + g_assert((s64)*bytes_read_return == got); + + return GNOME_VFS_OK; +} + +static GnomeVFSResult libntfs_gnomevfs_seek(GnomeVFSMethod *method, + GnomeVFSMethodHandle *method_handle, + GnomeVFSSeekPosition whence, GnomeVFSFileOffset offset, + GnomeVFSContext *context __attribute__((unused))) +{ + GnomeVFSResult errvfsresult; + struct libntfs_file *libntfs_file; + + g_return_val_if_fail(method == &GnomeVFSMethod_static, + GNOME_VFS_ERROR_BAD_PARAMETERS); + libntfs_file = (struct libntfs_file *)method_handle; + g_return_val_if_fail(libntfs_file != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + + if (GNOME_VFS_OK != (errvfsresult = libntfs_open_attr(libntfs_file))) + return errvfsresult; + + switch (whence) { + case GNOME_VFS_SEEK_START: + libntfs_file->pos = offset; + break; + case GNOME_VFS_SEEK_CURRENT: + libntfs_file->pos += offset; + break; + case GNOME_VFS_SEEK_END: + /* FIXME: NOT IMPLEMENTED YET */ + g_return_val_if_reached(GNOME_VFS_ERROR_BAD_PARAMETERS); + default: + g_assert_not_reached(); + } + + return GNOME_VFS_OK; +} + +static GnomeVFSResult libntfs_gnomevfs_tell(GnomeVFSMethod *method, + GnomeVFSMethodHandle *method_handle, + GnomeVFSFileSize *offset_return) +{ + GnomeVFSResult errvfsresult; + struct libntfs_file *libntfs_file; + + g_return_val_if_fail(method == &GnomeVFSMethod_static, + GNOME_VFS_ERROR_BAD_PARAMETERS); + libntfs_file = (struct libntfs_file *)method_handle; + g_return_val_if_fail(libntfs_file != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + g_return_val_if_fail(offset_return != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + + if (GNOME_VFS_OK != (errvfsresult = libntfs_open_attr(libntfs_file))) + return errvfsresult; + + *offset_return = libntfs_file->pos; + g_assert((s64)*offset_return == libntfs_file->pos); + + return errvfsresult; +} + +static gboolean libntfs_gnomevfs_is_local(GnomeVFSMethod *method, + const GnomeVFSURI *uri) +{ + g_return_val_if_fail(method == &GnomeVFSMethod_static, + GNOME_VFS_ERROR_BAD_PARAMETERS); + g_return_val_if_fail(uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS); + + return gnome_vfs_uri_is_local(uri->parent); +} + +static GnomeVFSResult libntfs_gnomevfs_get_file_info_from_handle( + GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, + GnomeVFSFileInfo *file_info, + GnomeVFSFileInfoOptions options __attribute__((unused)), + GnomeVFSContext *context __attribute__((unused))) +{ + GnomeVFSResult errvfsresult; + struct libntfs_file *libntfs_file; + + g_return_val_if_fail(method == &GnomeVFSMethod_static, + GNOME_VFS_ERROR_BAD_PARAMETERS); + libntfs_file = (struct libntfs_file *)method_handle; + g_return_val_if_fail(libntfs_file != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + g_return_val_if_fail(file_info != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS); + /* handle 'options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE'? */ + + file_info->valid_fields = 0; + /* FIXME: It is complicated to read filename of open 'ntfs_inode'. */ + file_info->name = NULL; + + if (GNOME_VFS_OK != (errvfsresult = libntfs_open_attr(libntfs_file))) { + /* Assume we are directory: */ + file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY; + /* + * Do not: file_info->valid_fields |= + * GNOME_VFS_FILE_INFO_FIELDS_TYPE; + * as gnome-vfs-xfer.c/copy_items() does not check + * 'GNOME_VFS_FILE_INFO_FIELDS_TYPE' and we are just bluffing + * we know it. + */ + return GNOME_VFS_OK; + } + + /* FIXME: Is 'data_size' the right field? */ + file_info->size = libntfs_file->attr->data_size; + file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_SIZE; + + /* + * FIXME: We do not really know the type of 'libntfs_file' but + * gnome-vfs-xfer.c/copy_items() requires 'GNOME_VFS_FILE_TYPE_REGULAR' + * to copy it. + */ + file_info->type = GNOME_VFS_FILE_TYPE_REGULAR; + /* + * Do not: file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_TYPE; + * as gnome-vfs-xfer.c/copy_items() does not check + * 'GNOME_VFS_FILE_INFO_FIELDS_TYPE' and we are just bluffing we know + * it. + */ + + return errvfsresult; +} + +static GnomeVFSResult libntfs_gnomevfs_get_file_info(GnomeVFSMethod *method, + GnomeVFSURI *uri, GnomeVFSFileInfo *file_info, + GnomeVFSFileInfoOptions options, GnomeVFSContext *context) +{ + GnomeVFSResult errvfsresult; + GnomeVFSMethodHandle *method_handle; + + g_return_val_if_fail(method == &GnomeVFSMethod_static, + GNOME_VFS_ERROR_BAD_PARAMETERS); + g_return_val_if_fail(file_info != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS); + /* handle 'options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE'? */ + + if (GNOME_VFS_OK != (errvfsresult = + libntfs_gnomevfs_open(method, &method_handle, uri, + GNOME_VFS_OPEN_READ, context))) + return errvfsresult; + if (GNOME_VFS_OK != (errvfsresult = + libntfs_gnomevfs_get_file_info_from_handle(method, + method_handle, file_info, options, context))) + return errvfsresult; + if (GNOME_VFS_OK != (errvfsresult = + libntfs_gnomevfs_close(method, method_handle, context))) + return errvfsresult; + + return GNOME_VFS_OK; +} + +static GnomeVFSResult libntfs_gnomevfs_check_same_fs(GnomeVFSMethod *method, + GnomeVFSURI *a, GnomeVFSURI *b, gboolean *same_fs_return, + GnomeVFSContext *context __attribute__((unused))) +{ + ntfs_volume *volume_a; + ntfs_volume *volume_b; + GnomeVFSResult errvfsresult; + + g_return_val_if_fail(method == &GnomeVFSMethod_static, + GNOME_VFS_ERROR_BAD_PARAMETERS); + g_return_val_if_fail(same_fs_return != NULL, + GNOME_VFS_ERROR_BAD_PARAMETERS); + + errvfsresult = libntfs_gnomevfs_uri_parent_init(&volume_a, a); + g_return_val_if_fail(errvfsresult == GNOME_VFS_OK, errvfsresult); + + errvfsresult = libntfs_gnomevfs_uri_parent_init(&volume_b, b); + g_return_val_if_fail(errvfsresult == GNOME_VFS_OK, errvfsresult); + + *same_fs_return = (volume_a == volume_b); + + return GNOME_VFS_OK; +} + +/** + * libntfs_gnomevfs_init: + * + * Returns: Initialized structure of #GnomeVFSMethod with static methods of + * libntfs-gnomevfs. + */ +GnomeVFSMethod *libntfs_gnomevfs_method_init(const gchar *method_name, + const gchar *args) +{ + struct method_name_info *method_name_info; + + g_return_val_if_fail(method_name != NULL, NULL); + /* 'args' may be NULL if not supplied. */ + + method_name_hash_init(); + + G_LOCK(method_name_hash); + method_name_info = g_hash_table_lookup(method_name_hash, method_name); + if (method_name_info && strcmp(method_name_info->args, args)) + method_name_info = NULL; + G_UNLOCK(method_name_hash); + if (!method_name_info) { + libntfs_new(method_name_info); + method_name_info->args = g_strdup(args); + G_LOCK(method_name_hash); + g_hash_table_replace(method_name_hash, g_strdup(method_name), + method_name_info); + G_UNLOCK(method_name_hash); + } + + G_LOCK(GnomeVFSMethod_static); + LIBNTFS_MEMZERO(&GnomeVFSMethod_static); + GnomeVFSMethod_static.method_table_size = sizeof(GnomeVFSMethod_static); + GnomeVFSMethod_static.open = libntfs_gnomevfs_open; /* mandatory */ + GnomeVFSMethod_static.create = libntfs_gnomevfs_create; /* mandatory */ + GnomeVFSMethod_static.close = libntfs_gnomevfs_close; + GnomeVFSMethod_static.read = libntfs_gnomevfs_read; + GnomeVFSMethod_static.seek = libntfs_gnomevfs_seek; + GnomeVFSMethod_static.tell = libntfs_gnomevfs_tell; + GnomeVFSMethod_static.open_directory = libntfs_gnomevfs_open_directory; + GnomeVFSMethod_static.close_directory = + libntfs_gnomevfs_close_directory; + GnomeVFSMethod_static.read_directory = libntfs_gnomevfs_read_directory; + GnomeVFSMethod_static.get_file_info = + libntfs_gnomevfs_get_file_info; /* mandatory */ + GnomeVFSMethod_static.get_file_info_from_handle = + libntfs_gnomevfs_get_file_info_from_handle; + GnomeVFSMethod_static.is_local = + libntfs_gnomevfs_is_local; /* mandatory */ + GnomeVFSMethod_static.check_same_fs = libntfs_gnomevfs_check_same_fs; + /* TODO: GnomeVFSMethodFindDirectoryFunc find_directory; */ + /* TODO: GnomeVFSMethodFileControlFunc file_control; */ + /* R/W: GnomeVFSMethodCreateSymbolicLinkFunc create_symbolic_link; */ + /* R/W: GnomeVFSMethodMonitorAddFunc monitor_add; */ + /* R/W: GnomeVFSMethodMonitorCancelFunc monitor_cancel; */ + /* R/W: GnomeVFSMethod_static.write; */ + /* R/W: GnomeVFSMethod_static.truncate_handle; */ + /* R/W: GnomeVFSMethod_static.make_directory; */ + /* R/W: GnomeVFSMethod_static.remove_directory; */ + /* R/W: GnomeVFSMethod_static.move; */ + /* R/W: GnomeVFSMethod_static.unlink; */ + /* R/W: GnomeVFSMethod_static.set_file_info; */ + /* R/W: GnomeVFSMethod_static.truncate; */ + G_UNLOCK(GnomeVFSMethod_static); + + return &GnomeVFSMethod_static; +} + +/** + * libntfs_gnomevfs_method_shutdown: + * + * Shutdowns libntfs-gnomevfs successfuly flushing all caches. + * + * Sad note about gnome-vfs-2.1.5 is that it never calls this function. :-) + */ +void libntfs_gnomevfs_method_shutdown(void) +{ + uri_parent_string_hash_init(); + G_LOCK(uri_parent_string_hash); + g_hash_table_destroy(uri_parent_string_hash); + uri_parent_string_hash = NULL; + G_UNLOCK(uri_parent_string_hash); + + method_name_hash_init(); + G_LOCK(method_name_hash); + g_hash_table_destroy(method_name_hash); + method_name_hash = NULL; + G_UNLOCK(method_name_hash); +} + diff --git a/libntfs/gnome-vfs-module.c b/libntfs/gnome-vfs-module.c new file mode 100644 index 00000000..8f09563f --- /dev/null +++ b/libntfs/gnome-vfs-module.c @@ -0,0 +1,77 @@ +/* + * gnome-vfs-module.c - Gnome-VFS init/shutdown implementation of interface to + * libntfs. Part of the Linux-NTFS project. + * + * Copyright (c) 2003 Jan Kratochvil + * Copyright (c) 2003 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include "gnome-vfs-method.h" +#include +#include +#include /* for g_atexit() */ + +/* Filesystem-module-scope lock for _any_ libntfs access. */ +G_LOCK_DEFINE(libntfs); + +static void vfs_module_shutdown_atexit(void); + +/** + * vfs_module_init: + * @method_name: FIXME + * @args: FIXME + * + * FIXME + * + * Returns: FIXME + */ +GnomeVFSMethod *vfs_module_init(const char *method_name, const char *args) +{ + GnomeVFSMethod *libntfs_gnomevfs_method_ptr; + + g_return_val_if_fail(method_name != NULL, NULL); + /* 'args' may be NULL if not supplied. */ + + libntfs_gnomevfs_method_ptr = libntfs_gnomevfs_method_init(method_name, + args); + + g_atexit(vfs_module_shutdown_atexit); + + return libntfs_gnomevfs_method_ptr; +} + +/** + * vfs_module_shutdown: + */ +void vfs_module_shutdown(GnomeVFSMethod *method __attribute__((unused))) +{ + /* + * 'method' may be NULL if we are called from + * vfs_module_shutdown_atexit(). + */ + + libntfs_gnomevfs_method_shutdown(); +} + +static void vfs_module_shutdown_atexit(void) +{ + vfs_module_shutdown(NULL); +} + diff --git a/libntfs/libntfs-gnomevfs.8.in b/libntfs/libntfs-gnomevfs.8.in new file mode 100644 index 00000000..11a87c81 --- /dev/null +++ b/libntfs/libntfs-gnomevfs.8.in @@ -0,0 +1,110 @@ +.\" -*- nroff -*- +.\" Copyright (c) 2003 Anton Altaparmakov. All Rights Reserved. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSPROGS 8 "November 2003" "Linux-NTFS version @VERSION@" +.SH NAME +libntfs-gnomevfs \- Module for GNOME VFS that allows access to NTFS filesystems. +.SH OVERVIEW +The GNOME virtual filesystem (VFS) provides universal access to different filesystems. +The +.BR libntfs-gnomevfs +module enables GNOME VFS aware clients to seamlessly utilize the NTFS library +.BR libntfs-3g . + +So you can access an NTFS filesystem without needing to use the NTFS utilities themselves +(at least in theory anyway). In practice this is probably more useful for programs and +programmers to make using +.BR libntfs-3g +easier, more generic, and to allow easier debugging of +.BR libntfs-3g . + +.SH Examples +.SS Prerequisites +.PP +To be able to follow these examples you will need to have installed the test utilities +from the gnome-vfs-2.4.x package. The easiest way to do this is to download and compile +the gnome-vfs-2 package, e.g. download from: + +http://ftp.gnome.org/pub/GNOME/desktop/2.4/2.4.0/sources/gnome-vfs-2.4.0.tar.gz + +Then run ./configure followed by make and make install (as root). This will install +it into /usr/local so it should not conflict with your existing installation from +rpm or deb packages which will be in /usr. + +Note you may also need to add /usr/local/lib to /etc/ld.so.conf and then run ldconfig +(as root) to let your system see the installed gnome-vfs-2.4.x libraries. + +Then run ./configure followed by make and make install (as root) in the main +.BR ntfsprogs +directory to build and install the +.BR libntfs-gnomevfs +module. + +.SS Copying a file from an NTFS partition +.PP +To copy the file autoexec.bat from the main directory of an NTFS partition (/dev/hda1) +to the /tmp directory on your system you could run: + +/path/to/gnome-vfs-2.4.x/test/test-xfer file:///dev/hda1#libntfs:/autoexec.bat /tmp/autoexec.bat + +To copy a file from a directory inside the NTFS partition you would just specify the full path. +So for example to copy the file win.ini from the Windows directory you would run: + +/path/to/gnome-vfs-2.4.x/test/test-xfer file:///dev/hda1#libntfs:/Windows/win.ini /tmp/win.ini + +.SS Shell access to an NTFS partition +.PP +For debugging it is most useful to be able to do various things to the NTFS partition while it +is being operated upon by +.BR libntfs-3g . +This is achieved using the test-shell utility (from the gnome-vfs-2.4.x package) by running: +/path/to/gnome-vfs-2.4.x/test/test-shell + +This drops you into the GNOME VFS shell from where you can now cd into the NTFS partition (/dev/hda1) +by typing: cd file:///dev/hda1#libntfs:/ + +You are now in the root directory of the NTFS partition. The first thing you will probably want to +do is to type "ls" to display the directory contents. + +You could then change directories using the "cd" command, e.g. to enter the Windows directory you +would type: cd Windows + +You can then open files, seek inside files, read from files (write is not enabled at present), etc +thus exercising large portions of the NTFS library. + +Use the "help" command while in the shell to see the available commands. + +.SH BUGS +.PP +No bugs are known but there are several limitations at the moment: + +You cannot get information about files other than what the "ls" command +in the test-shell can give you, i.e. the "info" command in the test-shell +does not work. + +Further access to the partition is read-only and hence you cannot write +to files. This will be changed in the future once the module has had +more wide testing. + +There may be other limitations and possibly bugs. Please report any +problems to the NTFS mailing list: linux-ntfs-dev@lists.sourceforge.net + +.SH AUTHORS +.PP +The +.BR libntfs-gnomevfs +module was written by Jan Kratochvil. This man page was written by Anton Altaparmakov. + +.SH AVAILABILITY +The +.BR ntfsprogs +package which contains the +.BR libntfs-gnomevfs +module can be downloaded from http://www.linux-ntfs.org/content/view/19/37/ +.BR +These manual pages can be viewed online at http://man.linux-ntfs.org/ntfsprogs.8.html + +.SH SEE ALSO +.BR ntfsprogs (8) + diff --git a/libntfs/libntfs.conf.in b/libntfs/libntfs.conf.in new file mode 100644 index 00000000..e69de29b diff --git a/libntfs/rich.c b/libntfs/rich.c new file mode 100644 index 00000000..1921e9dc --- /dev/null +++ b/libntfs/rich.c @@ -0,0 +1,215 @@ +/** + * rich.c - Temporary junk file. Part of the Linux-NTFS project. + * + * Copyright (c) 2004-2005 Richard Russon + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef NTFS_RICH + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "rich.h" +#include "layout.h" +#include "logging.h" + +/** + * find_attribute - Find an attribute of the given type + * @type: An attribute type, e.g. AT_FILE_NAME + * @ctx: A search context, created using ntfs_get_attr_search_ctx + * + * Using the search context to keep track, find the first/next occurrence of a + * given attribute type. + * + * N.B. This will return a pointer into @mft. As long as the search context + * has been created without an inode, it won't overflow the buffer. + * + * Return: Pointer Success, an attribute was found + * NULL Error, no matching attributes were found + */ +ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx) +{ + if (!ctx) { + errno = EINVAL; + return NULL; + } + + ntfs_log_trace ("\n"); + if (ntfs_attr_lookup(type, NULL, 0, 0, 0, NULL, 0, ctx) != 0) { + ntfs_log_debug("find_attribute didn't find an attribute of type: 0x%02x.\n", type); + return NULL; /* None / no more of that type */ + } + + ntfs_log_debug("find_attribute found an attribute of type: 0x%02x.\n", type); + return ctx->attr; +} + +/** + * find_first_attribute - Find the first attribute of a given type + * @type: An attribute type, e.g. AT_FILE_NAME + * @mft: A buffer containing a raw MFT record + * + * Search through a raw MFT record for an attribute of a given type. + * The return value is a pointer into the MFT record that was supplied. + * + * N.B. This will return a pointer into @mft. The pointer won't stray outside + * the buffer, since we created the search context without an inode. + * + * Return: Pointer Success, an attribute was found + * NULL Error, no matching attributes were found + */ +ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *rec; + + if (!mft) { + errno = EINVAL; + return NULL; + } + + ntfs_log_trace ("\n"); + ctx = ntfs_attr_get_search_ctx(NULL, mft); + if (!ctx) { + //XXX ntfs_log_error("Couldn't create a search context.\n"); + return NULL; + } + + rec = find_attribute(type, ctx); + ntfs_attr_put_search_ctx(ctx); + if (rec) + ntfs_log_debug("find_first_attribute: found attr of type 0x%02x.\n", type); + else + ntfs_log_debug("find_first_attribute: didn't find attr of type 0x%02x.\n", type); + return rec; +} + +/** + * ntfs_name_print - Send a Unicode name to the debug log + * @name: + * @name_len: + * + * Description... + * + * Returns: + */ +void ntfs_name_print(ntfschar *name, int name_len) +{ + char *buffer = NULL; + + if (name_len) { + ntfs_ucstombs(name, name_len, &buffer, 0); + ntfs_log_debug("%s", buffer); + free(buffer); + } else { + ntfs_log_debug("!"); + } +} + +/** + * utils_free_non_residents3 - Free all the non-resident attributes of an inode + * @bmp: + * @inode: + * @attr: + * + * Description... + * + * Returns: + */ +int utils_free_non_residents3(struct ntfs_bmp *bmp, ntfs_inode *inode, ATTR_RECORD *attr) +{ + ntfs_attr *na; + runlist_element *rl; + LCN size; + LCN count; + + if (!bmp) + return 1; + if (!inode) + return 1; + if (!attr) + return 1; + if (!attr->non_resident) + return 0; + + ntfs_log_trace ("\n"); + na = ntfs_attr_open(inode, attr->type, NULL, 0); + if (!na) + return 1; + + ntfs_attr_map_whole_runlist(na); + rl = na->rl; + size = na->allocated_size >> inode->vol->cluster_size_bits; + for (count = 0; count < size; count += rl->length, rl++) { + if (ntfs_bmp_set_range(bmp, rl->lcn, rl->length, 0) < 0) { + ntfs_log_warning("set range : %lld - %lld FAILED\n", rl->lcn, rl->lcn+rl->length-1); + } + } + ntfs_attr_close(na); + + return 0; +} + +/** + * utils_free_non_residents2 - Find all the non-resident attributes of an inode + * @inode: + * @bmp: + * + * Description... + * + * Returns: + */ +int utils_free_non_residents2(ntfs_inode *inode, struct ntfs_bmp *bmp) +{ + ntfs_attr_search_ctx *ctx; + + if (!inode) + return -1; + if (!bmp) + return -1; + + ntfs_log_trace ("\n"); + ctx = ntfs_attr_get_search_ctx(NULL, inode->mrec); + if (!ctx) { + ntfs_log_info("can't create a search context\n"); + return -1; + } + + while (ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx) == 0) { + utils_free_non_residents3(bmp, inode, ctx->attr); + } + + ntfs_attr_put_search_ctx(ctx); + return 0; +} + + +#endif /* NTFS_RICH */ + diff --git a/libntfs/tree.c b/libntfs/tree.c new file mode 100644 index 00000000..9dc5ef87 --- /dev/null +++ b/libntfs/tree.c @@ -0,0 +1,2366 @@ +/** + * tree.c - Directory tree handling code. Part of the Linux-NTFS project. + * + * Copyright (c) 2004-2005 Richard Russon + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef NTFS_RICH + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "volume.h" +#include "dir.h" +#include "tree.h" +#include "bitmap.h" +#include "index.h" +#include "inode.h" +#include "logging.h" +#include "rich.h" + +/** + * ntfs_dt_free - Destroy a directory-tree object + * @dt: + * + * Description... + * + * Returns: + */ +void ntfs_dt_free(struct ntfs_dt *dt) +{ + int i; + + if (!dt) + return; + + ntfs_log_trace ("dt %p, children %d, dir %lld\n", dt, dt->child_count, MREF(dt->dir->mft_num)); + + for (i = 0; i < dt->child_count; i++) { + ntfs_dt_free(dt->sub_nodes[i]); + ntfs_inode_close2(dt->inodes[i]); + } + + free(dt->sub_nodes); + free(dt->children); + free(dt->inodes); + free(dt->data); // XXX is this always ours? + free(dt); +} + +/** + * ntfs_dt_rollback - Discard the in-memory directory-tree changes + * @dt: + * + * Description... + * + * Returns: + */ +int ntfs_dt_rollback(struct ntfs_dt *dt) +{ + int i; + + if (!dt) + return 0; + if (dt->child_count == 0) // No children or nothing mapped + return 0; + + ntfs_log_trace ("dt %p, children %d, dir %lld\n", dt, dt->child_count, MREF(dt->dir->mft_num)); + + if (dt->changed) { + // We can't trust anything below us in the tree + for (i = 0; i < dt->child_count; i++) { + ntfs_dt_free(dt->sub_nodes[i]); + ntfs_inode_close2(dt->inodes[i]); + } + + dt->child_count = 0; + + free(dt->data); + free(dt->children); + free(dt->sub_nodes); + free(dt->inodes); + + dt->data = NULL; + dt->children = NULL; + dt->sub_nodes = NULL; + dt->inodes = NULL; + } else { + // This node is OK, check the su-nodes + for (i = 0; i < dt->child_count; i++) { + if (ntfs_dt_rollback(dt->sub_nodes[i])) { + ntfs_inode_close2(dt->inodes[i]); + // Child was changed so unmap it + dt->sub_nodes[i] = NULL; + dt->inodes[i] = NULL; + } + } + } + + return (dt->child_count == 0); +} + +/** + * ntfs_dt_commit - Write to disk the in-memory directory-tree changes + * @dt: + * + * Description... + * + * Returns: + */ +int ntfs_dt_commit(struct ntfs_dt *dt) +{ + ntfs_volume *vol; + ntfs_attr *attr; + struct ntfs_dir *dir; + int i; + int size; + + if (!dt) + return 0; + + ntfs_log_trace ("dt %p, children %d, dir %lld\n", dt, dt->child_count, MREF(dt->dir->mft_num)); + + dir = dt->dir; + if (!dir) + return -1; + + vol = dir->vol; // cluster size + + if (dt->changed) { + if (dt->parent) { + ntfs_log_debug("commit dt (alloc)\n"); + attr = dt->dir->ialloc; + size = dt->dir->index_size; + //utils_dump_mem(dt->data, 0, size, DM_DEFAULTS); + ntfs_attr_mst_pwrite(attr, dt->vcn * vol->cluster_size, 1, size, dt->data); // XXX retval + } else { + ntfs_log_debug("commit dt (root)\n"); + attr = dt->dir->iroot; + size = dt->data_len; + //utils_dump_mem(dt->data, 0, size, DM_DEFAULTS); + ntfs_attr_pwrite(attr, 0, size, dt->data); // XXX retval + } + + ntfs_log_warning("\tntfs_attr_pwrite(vcn %lld)\n", dt->vcn); + + dt->changed = FALSE; + } else { + //ntfs_log_debug("\tdt is clean\n"); + } + + for (i = 0; i < dt->child_count; i++) { + if ((dt->inodes[i]) && (NInoDirty(dt->inodes[i]))) { + //utils_dump_mem(dt->inodes[i]->mrec, 0, vol->mft_record_size, DM_DEFAULTS); + ntfs_inode_sync(dt->inodes[i]); + ntfs_log_warning("\tntfs_inode_sync %llu\n", dt->inodes[i]->mft_no); + } + + if (ntfs_dt_commit(dt->sub_nodes[i]) < 0) + return -1; + } + + return 0; +} + +/** + * ntfs_dt_create_children2 - Allocate space for the directory-tree's children + * @dt: + * @count: + * + * Description... + * + * Returns: + */ +BOOL ntfs_dt_create_children2(struct ntfs_dt *dt, int count) +{ + // XXX calculate for 2K and 4K indexes max and min filenames (inc/exc VCN) + + int old = (dt->child_count + 0x1e) & ~0x1f; + int new = (count + 0x1f) & ~0x1f; + + if (old == new) + return TRUE; + + ntfs_log_trace ("\n"); + dt->children = realloc(dt->children, new * sizeof(*dt->children)); + dt->sub_nodes = realloc(dt->sub_nodes, new * sizeof(*dt->sub_nodes)); + dt->inodes = realloc(dt->inodes, new * sizeof(*dt->inodes)); + + if (!dt->children || !dt->sub_nodes || !dt->inodes) + return FALSE; // dt->child_count = -1 ? + + memset((u8*)dt->children + old, 0, (new - old) * sizeof(*dt->children)); + memset((u8*)dt->sub_nodes + old, 0, (new - old) * sizeof(*dt->sub_nodes)); + memset((u8*)dt->inodes + old, 0, (new - old) * sizeof(*dt->inodes)); + + return TRUE; +} + +/** + * ntfs_dt_resize_children3 - Resize a directory-tree's children arrays + * @dt: + * @new: + * + * Description... + * + * Returns: + */ +BOOL ntfs_dt_resize_children3(struct ntfs_dt *dt, int new) +{ + int old; + + // XXX calculate for 2K and 4K indexes max and min filenames (inc/exc VCN) + // XXX assumption: sizeof(*dt->children) == sizeof(*dt->sub_nodes) == sizeof(*dt->inodes) + // XXX put back blocking factor + + if (!dt) + return FALSE; + + old = dt->child_count; + if (old == new) + return TRUE; + + ntfs_log_trace ("dt %p, mft %lld, old %d, new %d\n", dt, MREF(dt->dir->mft_num), old, new); + dt->child_count = new; + + old *= sizeof(*dt->children); + new *= sizeof(*dt->children); + + dt->children = realloc(dt->children, new); + dt->sub_nodes = realloc(dt->sub_nodes, new); + dt->inodes = realloc(dt->inodes, new); + + if (!dt->children || !dt->sub_nodes || !dt->inodes) + return FALSE; + + if (new > old) { + memset((u8*)dt->children + old, 0, (new - old)); + memset((u8*)dt->sub_nodes + old, 0, (new - old)); + memset((u8*)dt->inodes + old, 0, (new - old)); + } + + return TRUE; +} + +/** + * ntfs_dt_root_count - Count the index entries in an index root + * @dt: + * + * Description... + * + * Returns: + */ +int ntfs_dt_root_count(struct ntfs_dt *dt) +{ + u8 *buffer = NULL; + u8 *ptr = NULL; + VCN vcn; + s64 size = 0; + char *name = NULL; + + INDEX_ROOT *root; + INDEX_HEADER *header; + INDEX_ENTRY *entry; + + if (!dt) + return -1; + + ntfs_log_trace ("\n"); + buffer = dt->data; + size = dt->data_len; + + //utils_dump_mem(buffer, 0, size, DM_DEFAULTS); + + root = (INDEX_ROOT*) buffer; + if (root->type != AT_FILE_NAME) + return -1; + + header = (INDEX_HEADER*) (buffer + 0x10); + if (header->index_length > size) + return -1; + + dt->child_count = 0; + ptr = buffer + header->entries_offset + 0x10; + + while (ptr < (buffer + size)) { + entry = (INDEX_ENTRY*) ptr; + + ntfs_dt_resize_children3(dt, dt->child_count + 1); // XXX retval + + if (entry->flags & INDEX_ENTRY_NODE) { + vcn = ntfs_ie_get_vcn((INDEX_ENTRY*) ptr); + //ntfs_log_debug("VCN %lld\n", vcn); + } + + if (!(entry->flags & INDEX_ENTRY_END)) { + ntfs_ucstombs(entry->key.file_name.file_name, entry->key.file_name.file_name_length, &name, 0); + //ntfs_log_debug("\tinode %8lld %s\n", MREF(entry->indexed_file), name); + free(name); + name = NULL; + } + + //ntfs_log_debug("CC[%d] = %p\n", dt->child_count-1, entry); + dt->children[dt->child_count-1] = entry; + + ptr += entry->length; + } + + //ntfs_log_debug("count = %d\n\n", dt->child_count); + + return dt->child_count; +} + +/** + * ntfs_dt_alloc_count - Count the index entries in an index allocation + * @dt: + * + * Description... + * + * Returns: + */ +int ntfs_dt_alloc_count(struct ntfs_dt *dt) +{ + u8 *buffer = NULL; + u8 *ptr = NULL; + VCN vcn; + s64 size = 0; + char *name = NULL; + + INDEX_BLOCK *block; + INDEX_ENTRY *entry; + + if (!dt) + return -1; + + ntfs_log_trace ("\n"); + buffer = dt->data; + size = dt->data_len; + + //utils_dump_mem(buffer, 0, 128, DM_DEFAULTS); + + block = (INDEX_BLOCK*) buffer; + //ntfs_log_debug("INDX %lld\n", block->index_block_vcn); + + ptr = buffer + 0x18 + block->index.entries_offset; + + //ntfs_log_debug("block size %d\n", block->index.index_length); + dt->child_count = 0; + //ntfs_log_debug("start = 0x%02X, end = 0x%02X\n", 0x18 + block->index.entries_offset, 0x18 + block->index.index_length); + while (ptr < (buffer + 0x18 + block->index.index_length)) { + entry = (INDEX_ENTRY*) ptr; + + ntfs_dt_resize_children3(dt, dt->child_count + 1); // XXX retval + + if (entry->flags & INDEX_ENTRY_NODE) { + vcn = ntfs_ie_get_vcn((INDEX_ENTRY*) ptr); + //ntfs_log_debug("\tVCN %lld\n", vcn); + } + + dt->children[dt->child_count-1] = entry; + + if (entry->flags & INDEX_ENTRY_END) { + break; + } else { + ntfs_ucstombs(entry->key.file_name.file_name, entry->key.file_name.file_name_length, &name, 0); + //ntfs_log_debug("\tinode %8lld %s\n", MREF(entry->indexed_file), name); + free(name); + name = NULL; + } + + ptr += entry->length; + } + //ntfs_log_debug("count = %d\n", dt->child_count); + + return dt->child_count; +} + +/** + * ntfs_dt_initialise2 - Setup a directory-tree object + * @vol: + * @dt: + * + * Description... + * + * Returns: + */ +int ntfs_dt_initialise2(ntfs_volume *vol, struct ntfs_dt *dt) +{ + INDEX_ALLOCATION *alloc; + INDEX_ENTRY *entry; + + if (!vol) + return 1; + if (!dt) + return 1; + + ntfs_log_trace ("\n"); + memset(dt->data, 0, dt->data_len); + + alloc = (INDEX_ALLOCATION*) dt->data; + + alloc->magic = magic_INDX; + alloc->usa_ofs = 0x28; + alloc->usa_count = (dt->data_len >> vol->sector_size_bits) + 1; + alloc->lsn = 0; + alloc->index_block_vcn = 0; + + alloc->index.entries_offset = 0x28; + alloc->index.index_length = 0x10 + 0x28; + alloc->index.allocated_size = dt->data_len - 0x18; + alloc->index.flags = 0; + + entry = (INDEX_ENTRY*) (dt->data + 0x40); + + entry->indexed_file = 0; + entry->length = 0x10; + entry->key_length = 0; + entry->flags = INDEX_ENTRY_END; + + ntfs_dt_resize_children3(dt, 1); // XXX retval + + dt->children[0] = entry; + + return 0; +} + +/** + * ntfs_dt_create - Create a representation of a directory index + * @dir: + * @parent: + * @vcn: + * + * Description... + * + * Returns: + */ +struct ntfs_dt * ntfs_dt_create(struct ntfs_dir *dir, struct ntfs_dt *parent, VCN vcn) +{ + struct ntfs_dt *dt = NULL; + //int i; + + if (!dir) + return NULL; + + dt = calloc(1, sizeof(*dt)); + if (!dt) + return NULL; + + ntfs_log_trace ("\n"); + dt->dir = dir; + dt->parent = parent; + dt->child_count = 0; + dt->children = NULL; + dt->sub_nodes = NULL; + dt->inodes = NULL; + dt->vcn = vcn; + dt->changed = FALSE; + + if (parent) { + //ntfs_log_debug("alloc a = %lld\n", dir->ialloc->allocated_size); + //ntfs_log_debug("alloc d = %lld\n", dir->ialloc->data_size); + //ntfs_log_debug("alloc i = %lld\n", dir->ialloc->initialized_size); + //ntfs_log_debug("vcn = %lld\n", vcn); + + dt->data_len = dt->dir->index_size; + //ntfs_log_debug("parent size = %d\n", dt->data_len); + dt->data = malloc(dt->data_len); + + if (vcn >= 0) { + //ntfs_log_debug("%lld\n", ntfs_attr_mst_pread(dir->ialloc, vcn*512, 1, dt->data_len, dt->data)); + ntfs_attr_mst_pread(dir->ialloc, vcn*512, 1, dt->data_len, dt->data); + } else { + ntfs_dt_initialise2(dir->vol, dt); + } + + //utils_dump_mem(dt->data, 0, dt->data_len, DM_DEFAULTS); + //ntfs_log_debug("\n"); + + ntfs_dt_alloc_count(dt); + + dt->header = &((INDEX_BLOCK*)dt->data)->index; + //ntfs_log_debug("USA = %d\n", ((INDEX_BLOCK*)dt->data)->usa_count); + +#if 0 + for (i = 0; i < dt->child_count; i++) { + INDEX_ENTRY *ie = dt->children[i]; + + ntfs_log_debug("%d\n", ((u8*)ie) - dt->data); + if (ie->flags & INDEX_ENTRY_END) + ntfs_log_debug("IE (%d)\n", ie->length); + else + ntfs_log_debug("IE %lld (%d)\n", MREF(ie->key.file_name.parent_directory), ie->length); + utils_dump_mem(ie, 0, ie->length, DM_DEFAULTS); + ntfs_log_debug("\n"); + } +#endif + } else { + //ntfs_log_debug("root a = %lld\n", dir->iroot->allocated_size); + //ntfs_log_debug("root d = %lld\n", dir->iroot->data_size); + //ntfs_log_debug("root i = %lld\n", dir->iroot->initialized_size); + + dt->data_len = dir->iroot->allocated_size; + dt->data = malloc(dt->data_len); + //ntfs_log_debug("%lld\n", ntfs_attr_pread(dir->iroot, 0, dt->data_len, dt->data)); + ntfs_attr_pread(dir->iroot, 0, dt->data_len, dt->data); + //utils_dump_mem(dt->data, 0, dt->data_len, DM_DEFAULTS); + //ntfs_log_debug("\n"); + + ntfs_dt_root_count(dt); + + dt->header = &((INDEX_ROOT*)dt->data)->index; + //dt->data_len = ((INDEX_ROOT*)dt->data)->index_block_size; + //ntfs_log_debug("IBS = %d\n", ((INDEX_ROOT*)dt->data)->index_block_size); + +#if 0 + for (i = 0; i < dt->child_count; i++) { + INDEX_ENTRY *ie = dt->children[i]; + + ntfs_log_debug("%d\n", ((u8*)ie) - dt->data); + if (ie->flags & INDEX_ENTRY_END) + ntfs_log_debug("IE (%d)\n", ie->length); + else + ntfs_log_debug("IE %lld (%d)\n", MREF(ie->key.file_name.parent_directory), ie->length); + utils_dump_mem(ie, 0, ie->length, DM_DEFAULTS); + ntfs_log_debug("\n"); + } +#endif + } + //ntfs_log_debug("index_header (%d,%d)\n", dt->header->index_length, dt->header->allocated_size); + + return dt; +} + +/** + * ntfs_dt_find - Find an index entry by name + * @dt: + * @name: + * @name_len: + * + * find dt by name, return MFT_REF + * maps dt's as necessary + */ +MFT_REF ntfs_dt_find(struct ntfs_dt *dt, ntfschar *name, int name_len) +{ + MFT_REF res = -1; + INDEX_ENTRY *ie; + struct ntfs_dt *sub; + VCN vcn; + int i; + int r; + + if (!dt || !name) + return -1; + + ntfs_log_trace ("\n"); + /* + * State Children Action + * ------------------------------------------- + * collates after - keep searching + * match name - return MREF + * collates before no return -1 + * collates before yes map & recurse + * end marker no return -1 + * end marker yes map & recurse + */ + + //ntfs_log_debug("child_count = %d\n", dt->child_count); + for (i = 0; i < dt->child_count; i++) { + ie = dt->children[i]; + + if (ie->flags & INDEX_ENTRY_END) { + r = -1; + } else { + //ntfs_log_debug("\t"); ntfs_name_print(ie->key.file_name.file_name, ie->key.file_name.file_name_length); ntfs_log_debug("\n"); + r = ntfs_names_collate(name, name_len, + ie->key.file_name.file_name, + ie->key.file_name.file_name_length, + 2, IGNORE_CASE, + dt->dir->vol->upcase, + dt->dir->vol->upcase_len); + } + + //ntfs_log_debug("%d, %d\n", i, r); + + if (r == 1) { + //ntfs_log_debug("keep searching\n"); + continue; + } else if (r == 0) { + res = MREF(ie->indexed_file); + //ntfs_log_debug("match %lld\n", res); + } else if (r == -1) { + if (ie->flags & INDEX_ENTRY_NODE) { + //ntfs_log_debug("map & recurse\n"); + //ntfs_log_debug("sub %p\n", dt->sub_nodes); + if (!dt->sub_nodes[i]) { + vcn = ntfs_ie_get_vcn(ie); + //ntfs_log_debug("vcn = %lld\n", vcn); + sub = ntfs_dt_create(dt->dir, dt, vcn); + dt->sub_nodes[i] = sub; + } + res = ntfs_dt_find(dt->sub_nodes[i], name, name_len); + } else { + //ntfs_log_debug("ENOENT\n"); + } + } else { + ntfs_log_debug("error collating name\n"); + } + break; + } + + return res; +} + +/** + * ntfs_dt_find2 - Find an index entry by name + * @dt: + * @name: + * @name_len: + * @index_num: + * + * find dt by name, returns dt and index + * maps dt's as necessary + */ +struct ntfs_dt * ntfs_dt_find2(struct ntfs_dt *dt, ntfschar *name, int name_len, int *index_num) +{ + struct ntfs_dt *res = NULL; + INDEX_ENTRY *ie; + VCN vcn; + int i; + int r; + + if (!dt || !name) + return NULL; + ntfs_log_trace ("dt %p, mft %llu, name %p%d\n", dt, MREF(dt->dir->mft_num), name, name_len); + + //ntfs_log_debug("searching for: "); ntfs_name_print(name, name_len); ntfs_log_debug("\n"); + + //utils_dump_mem(dt->data, 0, 256, DM_DEFAULTS); + + // XXX default index_num to -1 + + /* + * State Children Action + * ------------------------------------------- + * collates after - keep searching + * match name - return MREF + * collates before no return -1 + * collates before yes map & recurse + * end marker no return -1 + * end marker yes map & recurse + */ + + //ntfs_log_debug("child_count = %d\n", dt->child_count); + for (i = 0; i < dt->child_count; i++) { + ie = dt->children[i]; + + if (ie->flags & INDEX_ENTRY_END) { + r = -1; + } else { + //ntfs_log_debug("\t"); ntfs_name_print(ie->key.file_name.file_name, ie->key.file_name.file_name_length); ntfs_log_debug("\n"); + //utils_dump_mem(name, 0, name_len * 2, DM_DEFAULTS); + //utils_dump_mem(ie->key.file_name.file_name, 0, ie->key.file_name.file_name_length * 2, DM_DEFAULTS); + r = ntfs_names_collate(name, name_len, + ie->key.file_name.file_name, + ie->key.file_name.file_name_length, + 2, IGNORE_CASE, + dt->dir->vol->upcase, + dt->dir->vol->upcase_len); + } + + //ntfs_log_debug("%d, %d\n", i, r); + + if (r == 1) { + //ntfs_log_debug("keep searching\n"); + continue; + } else if (r == 0) { + res = dt; + //ntfs_log_debug("match %p\n", res); + if (index_num) + *index_num = i; + } else if ((r == -1) && (ie->flags & INDEX_ENTRY_NODE)) { + //ntfs_log_debug("recurse\n"); + if (!dt->sub_nodes[i]) { + vcn = ntfs_ie_get_vcn(ie); + //ntfs_log_debug("vcn = %lld\n", vcn); + dt->sub_nodes[i] = ntfs_dt_create(dt->dir, dt, vcn); + } + res = ntfs_dt_find2(dt->sub_nodes[i], name, name_len, index_num); + } else { + //ntfs_log_debug("error collating name\n"); + } + break; + } + + return res; +} + +/** + * ntfs_dt_find3 - Find an index entry by name + * @dt: + * @name: + * @name_len: + * @index_num: + * + * find dt by name, returns dt and index + * does not map new dt's + */ +struct ntfs_dt * ntfs_dt_find3(struct ntfs_dt *dt, ntfschar *name, int name_len, int *index_num) +{ + struct ntfs_dt *res = NULL; + INDEX_ENTRY *ie; + int i; + int r; + + if (!dt || !name) + return NULL; + ntfs_log_trace ("\n"); + + //ntfs_log_debug("child_count = %d\n", dt->child_count); + for (i = 0; i < dt->child_count; i++) { + ie = dt->children[i]; + + if (ie->flags & INDEX_ENTRY_END) { + r = -1; + } else { + //ntfs_log_debug("\t"); ntfs_name_print(ie->key.file_name.file_name, ie->key.file_name.file_name_length); ntfs_log_debug("\n"); + r = ntfs_names_collate(name, name_len, + ie->key.file_name.file_name, + ie->key.file_name.file_name_length, + 2, IGNORE_CASE, + dt->dir->vol->upcase, + dt->dir->vol->upcase_len); + } + + //ntfs_log_debug("%d, %d\n", i, r); + + if (r == 1) { + //ntfs_log_debug("keep searching\n"); + continue; + } else if (r == 0) { + res = dt; + //ntfs_log_debug("match %p\n", res); + if (index_num) + *index_num = i; + } else if (r == -1) { + if (ie->flags & INDEX_ENTRY_NODE) { + //ntfs_log_debug("recurse\n"); + res = ntfs_dt_find3(dt->sub_nodes[i], name, name_len, index_num); + } else { + //ntfs_log_debug("no match\n"); + res = dt; + if (index_num) + *index_num = i; + } + } else { + ntfs_log_debug("error collating name\n"); + } + break; + } + + return res; +} + +/** + * ntfs_dt_find4 - Find an index entry by name + * @dt: + * @name: + * @name_len: + * @index_num: + * + * find successor to specified name, returns dt and index + * maps dt's as necessary + */ +struct ntfs_dt * ntfs_dt_find4(struct ntfs_dt *dt, ntfschar *name, int name_len, int *index_num) +{ + struct ntfs_dt *res = NULL; + struct ntfs_dt *sub = NULL; + INDEX_ENTRY *ie; + VCN vcn; + int i; + int r; + + if (!dt || !name) + return NULL; + ntfs_log_trace ("\n"); + + //ntfs_log_debug("child_count = %d\n", dt->child_count); + for (i = 0; i < dt->child_count; i++) { + ie = dt->children[i]; + + //ntfs_log_debug("ie->flags = %d\n", ie->flags); + if (ie->flags & INDEX_ENTRY_END) { + r = -1; + } else { + //ntfs_log_debug("\t"); ntfs_name_print(ie->key.file_name.file_name, ie->key.file_name.file_name_length); ntfs_log_debug("\n"); + r = ntfs_names_collate(name, name_len, + ie->key.file_name.file_name, + ie->key.file_name.file_name_length, + 2, IGNORE_CASE, + dt->dir->vol->upcase, + dt->dir->vol->upcase_len); + } + + //ntfs_log_debug("%d, %d\n", i, r); + + if (r == 1) { + //ntfs_log_debug("keep searching\n"); + } else if (r == 0) { + //res = dt; + //ntfs_log_debug("match\n"); + // ignore + } else if (r == -1) { + if (ie->flags & INDEX_ENTRY_NODE) { + //ntfs_log_debug("recurse\n"); + if (!dt->sub_nodes[i]) { + vcn = ntfs_ie_get_vcn(ie); + //ntfs_log_debug("vcn = %lld\n", vcn); + sub = ntfs_dt_create(dt->dir, dt, vcn); + dt->sub_nodes[i] = sub; + } + res = ntfs_dt_find4(dt->sub_nodes[i], name, name_len, index_num); + } else { + //ntfs_log_debug("no match\n"); + res = dt; + if (index_num) + *index_num = i; + } + break; + } else { + ntfs_log_debug("error collating name\n"); + } + //break; + } + + return res; +} + +/** + * ntfs_dt_find_all - Recurse the directory-tree, mapping all elements + * @dt: + * + * maps all dt's into memory + */ +void ntfs_dt_find_all(struct ntfs_dt *dt) +{ + INDEX_ENTRY *ie; + VCN vcn; + int i; + + if (!dt) + return; + ntfs_log_trace ("\n"); + + for (i = 0; i < dt->child_count; i++) { + ie = dt->children[i]; + + if (ie->flags & INDEX_ENTRY_NODE) { + if (!dt->sub_nodes[i]) { + vcn = ntfs_ie_get_vcn(ie); + dt->sub_nodes[i] = ntfs_dt_create(dt->dir, dt, vcn); + } + ntfs_dt_find_all(dt->sub_nodes[i]); + } + } +} + +/** + * ntfs_dt_find_parent - Find the index of ourself in the parent's array + * @dt: + * + * Description... + * + * Returns: + */ +int ntfs_dt_find_parent(struct ntfs_dt *dt) +{ + int i; + struct ntfs_dt *parent; + + if (!dt) + return -1; + ntfs_log_trace ("\n"); + + parent = dt->parent; + if (!parent) + return -1; + + for (i = 0; i < parent->child_count; i++) + if (parent->sub_nodes[i] == dt) + return i; + + return -1; +} + +/** + * ntfs_dt_isroot - Does this directory-tree represent an index root + * @dt: + * + * Description... + * + * Returns: + */ +BOOL ntfs_dt_isroot(struct ntfs_dt *dt) +{ + if (!dt) + return FALSE; + ntfs_log_trace ("\n"); + return (dt->parent == NULL); +} + +/** + * ntfs_dt_root_freespace - Give the free space (bytes) in an index root + * @dt: + * + * Description... + * + * Returns: + */ +int ntfs_dt_root_freespace(struct ntfs_dt *dt) +{ + int recsize; + int inuse; + MFT_RECORD *mrec; + + if (!dt) + return -1; + ntfs_log_trace ("\n"); + + recsize = dt->dir->inode->vol->mft_record_size; + + mrec = (MFT_RECORD*) dt->dir->inode->mrec; + inuse = mrec->bytes_in_use; + + return recsize - inuse; +} + +/** + * ntfs_dt_alloc_freespace - Give the free space (bytes) in an index allocation + * @dt: + * + * Description... + * + * Returns: + */ +int ntfs_dt_alloc_freespace(struct ntfs_dt *dt) +{ + int recsize; + int inuse; + INDEX_BLOCK *block; + + if (!dt) + return -1; + ntfs_log_trace ("\n"); + + recsize = dt->dir->index_size; + + block = (INDEX_BLOCK*) dt->data; + inuse = block->index.index_length + 24; + + return recsize - inuse; +} + +/** + * ntfs_dt_transfer - Transfer several index entries between directory-trees + * @old: + * @new: + * @start: + * @count: + * + * Description... + * + * Returns: + */ +int ntfs_dt_transfer(struct ntfs_dt *old, struct ntfs_dt *new, int start, int count) +{ + int i; + int need; + int space; + INDEX_ENTRY *mov_ie; + u8 *src; + u8 *dst; + int len; + int insert; + //FILE_NAME_ATTR *file; + ntfs_log_trace ("\n"); + + //XXX check len > 0 + + if (!old || !new) + return -1; + + if ((start < 0) || ((start+count) >= old->child_count)) + return -1; + + //ntfs_log_debug("\n"); + ntfs_log_debug("Transferring children\n"); + + need = 0; + for (i = start; i < (start+count+1); i++) { + mov_ie = old->children[i]; + need += mov_ie->length; + //file = &mov_ie->key.file_name; ntfs_log_debug("\ttrn name: "); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_debug("\n"); + } + + if (ntfs_dt_isroot(new)) + space = ntfs_dt_root_freespace(new); + else + space = ntfs_dt_alloc_freespace(new); + + // XXX if this is an index root, it'll go badly wrong + // restrict to allocs only? + + ntfs_log_debug("\tneed = %d\n", need); + ntfs_log_debug("\tspace = %d\n", space); + + if (space < need) + return -1; + + if (new->child_count == 1) { + i = -1; + } else { + ntfschar *n1, *n2; + int l1, l2; + + n1 = new->children[0]->key.file_name.file_name; + l1 = new->children[0]->key.file_name.file_name_length; + + n2 = old->children[start]->key.file_name.file_name; + l2 = old->children[start]->key.file_name.file_name_length; + + i = ntfs_names_collate(n1, l1, n2, l2, + 2, IGNORE_CASE, + old->dir->vol->upcase, + old->dir->vol->upcase_len); + } + + if ((i == 0) || (i == 2)) + return -1; + + // determine the insertion point + if (i == 1) + insert = 0; + else + insert = new->child_count-1; + + src = (u8*) new->children[insert]; + dst = src + need; + len = (u8*) new->children[new->child_count-1] + new->children[new->child_count-1]->length - src; + + //ntfs_log_debug("src = %d, dst = %d, len = %d\n", src - new->data, dst - new->data, len); + memmove(dst, src, len); + + dst = src; + src = (u8*) old->children[start]; + len = need; + + memcpy(dst, src, len); + + src = (u8*) old->children[start+count-1]; + dst = (u8*) old->children[start]; + len = (u8*) old->children[old->child_count-1] + old->children[old->child_count-1]->length - src; + + //ntfs_log_debug("src = %d, dst = %d, len = %d\n", src - old->data, dst - old->data, len); + memmove(dst, src, len); + + dst += len; + len = old->data + old->dir->index_size - dst; + + //ntfs_log_debug("dst = %d, len = %d\n", dst - old->data, len); + memset(dst, 0, len); + + if (!ntfs_dt_resize_children3(new, new->child_count + count)) + return -1; + + src = (u8*) &old->sub_nodes[start+count-1]; + dst = (u8*) &old->sub_nodes[start]; + len = (old->child_count - start - count + 1) * sizeof(struct ntfs_dt*); + + memmove(dst, src, len); + + src = (u8*) &new->sub_nodes[insert]; + dst = (u8*) &new->sub_nodes[insert+count-1]; + len = (new->child_count - insert - count + 1) * sizeof(struct ntfs_dt*); + + memmove(dst, src, len); + + if (!ntfs_dt_resize_children3(old, old->child_count - count)) + return -1; + + src = (u8*) new->children[0]; + for (i = 0; i < new->child_count; i++) { + new->children[i] = (INDEX_ENTRY*) src; + src += new->children[i]->length; + } + + src = (u8*) old->children[0]; + for (i = 0; i < old->child_count; i++) { + old->children[i] = (INDEX_ENTRY*) src; + src += old->children[i]->length; + } + + old->header->index_length -= need; + new->header->index_length += need; + + // resize children and sub_nodes + // memmove keys in new + // memcpy old to new + // memmove keys in old + // rebuild old/new children/sub_nodes without destroying tree + // update old/new headers + + old->changed = TRUE; + new->changed = TRUE; + + ntfs_log_debug("Modified: inode %lld, $INDEX_ALLOCATION vcn %lld-%lld\n", old->dir->inode->mft_no, old->vcn, old->vcn + (old->dir->index_size>>9) - 1); + ntfs_log_debug("Modified: inode %lld, $INDEX_ALLOCATION vcn %lld-%lld\n", new->dir->inode->mft_no, new->vcn, new->vcn + (new->dir->index_size>>9) - 1); + + return 0; +} + +/** + * ntfs_dt_alloc_insert - Insert an index entry into an index allocation + * @dt: + * @first: + * @count: + * + * Description... + * + * Returns: + */ +int ntfs_dt_alloc_insert(struct ntfs_dt *dt, INDEX_ENTRY *first, int count) +{ + // XXX don't bother measuring, just subtract the children pointers + + int i; + int need; + INDEX_ENTRY *ie; + INDEX_ALLOCATION *alloc; + u8 *src; + u8 *dst; + int len; + + if (!dt) + return 1; + if (!first) + return 1; + ntfs_log_trace ("\n"); + + need = 0; + ie = first; + for (i = 0; i < count; i++) { + need += ie->length; + ie = (INDEX_ENTRY*) ((u8*)ie + ie->length); + } + + ntfs_log_debug("alloc insert %d bytes\n", need); + + alloc = (INDEX_ALLOCATION*) dt->data; + ntfs_log_debug("entries_offset = %d\n", alloc->index.entries_offset); + ntfs_log_debug("index_length = %d\n", alloc->index.index_length); + ntfs_log_debug("allocated_size = %d\n", alloc->index.allocated_size); + + ntfs_log_debug("insert has %d children\n", dt->child_count); + ntfs_log_debug("children = %p\n", dt->children); + //utils_dump_mem(dt->data, 0, 128, DM_DEFAULTS); + + ie = dt->children[dt->child_count-1]; + + ntfs_log_debug("last child = %p (%ld)\n", ie, (long)ie - (long)dt->data); + ntfs_log_debug("size = %d\n", ie->length); + + src = (u8*) ie; + dst = src + need; + len = ie->length; + + memmove(dst, src, len); + + src = (u8*) first; + dst = (u8*) ie; + len = need; + + memcpy(dst, src, len); + + // use create children + // measure need and update children list + // adjust headers + + //utils_dump_mem(dt->data, 0, 256, DM_DEFAULTS); + return 0; +} + +/** + * ntfs_dt_alloc_insert2 - Insert an index entry into an index allocation + * @dt: + * @before: + * @count: + * @bytes: + * + * Description... + * + * Returns: + */ +INDEX_ENTRY * ntfs_dt_alloc_insert2(struct ntfs_dt *dt, int before, int count, int bytes) +{ + int space; + u8 *src; + u8 *dst; + int len; + + // XXX don't bother measuring, just subtract the children pointers + + if (!dt) + return NULL; + if (before < 0) + return NULL; + if (count < 1) + return NULL; + if (bytes < 1) + return NULL; + ntfs_log_trace ("\n"); + + // check alloc has enough space + space = ntfs_dt_alloc_freespace(dt); + if (bytes > space) + return NULL; + + // move data + src = (u8*) dt->children[before]; + dst = src + bytes; + len = dt->header->index_length - ((int)dt->children[before] - (int)dt->data) + 24; + + //ntfs_log_debug("%d, %d, %d\n", (int)src - (int)dt->data, (int)dst - (int)dt->data, len); + + memmove(dst, src, len); + memset(dst, 0, bytes); + + // resize arrays + ntfs_dt_resize_children3(dt, dt->child_count + count); + + // move keys (children) + src = (u8*) (dt->children + before); + dst = src + (count * sizeof(u8*)); + len = (dt->child_count - count - before) * sizeof(u8*); + + memmove(dst, src, len); + memset(src, 0, count * sizeof(u8*)); + + // move keys (inodes) + src = (u8*) (dt->inodes + before); + dst = src + (count * sizeof(u8*)); + len = (dt->child_count - count - before) * sizeof(u8*); + + memmove(dst, src, len); + memset(src, 0, count * sizeof(u8*)); + + // move keys (sub_nodes) + src = (u8*) (dt->sub_nodes + before); + dst = src + (count * sizeof(u8*)); + len = (dt->child_count - count - before) * sizeof(u8*); + + memmove(dst, src, len); + memset(src, 0, count * sizeof(u8*)); + + return NULL; +} + +/** + * ntfs_dt_root_insert - Insert an index entry into an index root + * @dt: + * @first: + * @count: + * + * Description... + * + * Returns: + */ +int ntfs_dt_root_insert(struct ntfs_dt *dt, INDEX_ENTRY *first, int count) +{ + if (!dt) + return 1; + if (!first) + return 1; + ntfs_log_trace ("\n"); + + return count; +} + +/** + * ntfs_dt_alloc_remove2 - Remove an index entry from an index allocation + * @dt: + * @start: + * @count: + * + * Description... + * + * Returns: + */ +int ntfs_dt_alloc_remove2(struct ntfs_dt *dt, int start, int count) +{ + int i; + int size; + + if (!dt) + return 1; + ntfs_log_trace ("\n"); + + size = 0; + for (i = start; i < (start+count); i++) { + size += dt->children[i]->length; + } + + return start + count; +} + +/** + * ntfs_dt_root_remove2 - Remove an index entry from an index root + * @dt: + * @start: + * @count: + * + * Description... + * + * Returns: + */ +int ntfs_dt_root_remove2(struct ntfs_dt *dt, int start, int count) +{ + int i; + int size; + + if (!dt) + return -1; + if ((start < 0) || (start >= dt->child_count)) + return -1; + if ((count < 1) || ((start + count - 1) >= dt->child_count)) + return -1; + ntfs_log_trace ("\n"); + + ntfs_log_debug("s c/t %d %d/%d\n", start, count, dt->child_count); + + size = 0; + for (i = start; i < (start + count); i++) + size += dt->children[i]->length; + ntfs_log_debug("size1 = %d\n", size); + + size = (int) dt->children[start+count] - (int) dt->children[start]; + ntfs_log_debug("size2 = %d\n", size); + + size = (int) dt->children[start+count-1] - (int) dt->children[start] + dt->children[start+count-1]->length; + ntfs_log_debug("size3 = %d\n", size); + + // XXX what shall we do with the inodes? + // transfer them to the dir (commit them for now) + // are they _our_ responsibility? probably not + + // rearrange arrays + // shrink attribute + + ntfs_dt_resize_children3(dt, dt->child_count - count); + + ntfs_log_debug("ntfs_dt_root_remove2\n"); + return dt->child_count; +} + +/** + * ntfs_dt_transfer2 - Transfer several index entries between directory-trees + * @old: + * @new: + * @start: + * @count: + * + * Description... + * + * Returns: + */ +int ntfs_dt_transfer2(struct ntfs_dt *old, struct ntfs_dt *new, int start, int count) +{ + int i; + int need; + int space; + INDEX_ENTRY *mov_ie; + u8 *src; + u8 *dst; + int len; + int insert; + //FILE_NAME_ATTR *file; + + if (!old || !new) + return -1; + + if ((start < 0) || (count < 0)) + return -1; + + if ((start + count) >= old->child_count) + return -1; + ntfs_log_trace ("\n"); + + //ntfs_log_debug("\n"); + ntfs_log_debug("Transferring children\n"); + + need = 0; + for (i = start; i < (start+count); i++) { + mov_ie = old->children[i]; + need += mov_ie->length; + //file = &mov_ie->key.file_name; ntfs_log_debug("\ttrn name: "); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_debug("\n"); + } + + if (ntfs_dt_isroot(new)) + space = ntfs_dt_root_freespace(new); + else + space = ntfs_dt_alloc_freespace(new); + + ntfs_log_debug("\tneed = %d\n", need); + ntfs_log_debug("\tspace = %d\n", space); + + if (need > space) + return -1; + + if (ntfs_dt_isroot(new)) + ntfs_dt_root_insert(new, old->children[0], count); + else + ntfs_dt_alloc_insert2(new, 0, count, need); + + if (ntfs_dt_isroot(old)) + ntfs_dt_root_remove2(old, 0, count); + else + ntfs_dt_alloc_remove2(old, 0, count); + + if (1) return -1; + if (0) ntfs_dt_alloc_insert(NULL, NULL, 0); + + if (new->child_count == 1) { + i = -1; + } else { + ntfschar *n1, *n2; + int l1, l2; + + n1 = new->children[0]->key.file_name.file_name; + l1 = new->children[0]->key.file_name.file_name_length; + + n2 = old->children[start]->key.file_name.file_name; + l2 = old->children[start]->key.file_name.file_name_length; + + i = ntfs_names_collate(n1, l1, n2, l2, + 2, IGNORE_CASE, + old->dir->vol->upcase, + old->dir->vol->upcase_len); + } + + if ((i == 0) || (i == 2)) + return -1; + + // determine the insertion point + if (i == 1) + insert = 0; + else + insert = new->child_count-1; + + src = (u8*) new->children[insert]; + dst = src + need; + len = (u8*) new->children[new->child_count-1] + new->children[new->child_count-1]->length - src; + + //ntfs_log_debug("src = %d, dst = %d, len = %d\n", src - new->data, dst - new->data, len); + memmove(dst, src, len); + + dst = src; + src = (u8*) old->children[start]; + len = need; + + memcpy(dst, src, len); + + src = (u8*) old->children[start+count-1]; + dst = (u8*) old->children[start]; + len = (u8*) old->children[old->child_count-1] + old->children[old->child_count-1]->length - src; + + //ntfs_log_debug("src = %d, dst = %d, len = %d\n", src - old->data, dst - old->data, len); + memmove(dst, src, len); + + dst += len; + len = old->data + old->dir->index_size - dst; + + //ntfs_log_debug("dst = %d, len = %d\n", dst - old->data, len); + memset(dst, 0, len); + + if (!ntfs_dt_resize_children3(new, new->child_count + count)) + return -1; + + src = (u8*) &old->sub_nodes[start+count-1]; + dst = (u8*) &old->sub_nodes[start]; + len = (old->child_count - start - count + 1) * sizeof(struct ntfs_dt*); + + memmove(dst, src, len); + + src = (u8*) &new->sub_nodes[insert]; + dst = (u8*) &new->sub_nodes[insert+count-1]; + len = (new->child_count - insert - count + 1) * sizeof(struct ntfs_dt*); + + memmove(dst, src, len); + + if (!ntfs_dt_resize_children3(old, old->child_count - count)) + return -1; + + src = (u8*) new->children[0]; + for (i = 0; i < new->child_count; i++) { + new->children[i] = (INDEX_ENTRY*) src; + src += new->children[i]->length; + } + + src = (u8*) old->children[0]; + for (i = 0; i < old->child_count; i++) { + old->children[i] = (INDEX_ENTRY*) src; + src += old->children[i]->length; + } + + old->header->index_length -= need; + new->header->index_length += need; + + // resize children and sub_nodes + // memmove keys in new + // memcpy old to new + // memmove keys in old + // rebuild old/new children/sub_nodes without destroying tree + // update old/new headers + + old->changed = TRUE; + new->changed = TRUE; + + ntfs_log_debug("Modified: inode %lld, $INDEX_ALLOCATION vcn %lld-%lld\n", old->dir->inode->mft_no, old->vcn, old->vcn + (old->dir->index_size>>9) - 1); + ntfs_log_debug("Modified: inode %lld, $INDEX_ALLOCATION vcn %lld-%lld\n", new->dir->inode->mft_no, new->vcn, new->vcn + (new->dir->index_size>>9) - 1); + + return 0; +} + +/** + * ntfs_dt_root_replace - Replace an index entry in an index root + * @del: + * @del_num: + * @del_ie: + * @suc_ie: + * + * Description... + * + * Returns: + */ +int ntfs_dt_root_replace(struct ntfs_dt *del, int del_num, INDEX_ENTRY *del_ie, INDEX_ENTRY *suc_ie) +{ + u8 *src; + u8 *dst; + u8 *attr; + int len; + int i; + + if (!del || !del_ie || !suc_ie) + return FALSE; + ntfs_log_trace ("\n"); + + //utils_dump_mem(del->data, 0, del->data_len, DM_DEFAULTS); + //ntfs_log_debug("\n"); + + attr = malloc(del->data_len + suc_ie->length - del_ie->length); + + dst = attr; + src = del->data; + len = (u8*) del_ie - del->data; + + memcpy(dst, src, len); + + dst += len; + src = (u8*) suc_ie; + len = suc_ie->length; + + memcpy(dst, src, len); + + dst += len; + src = (u8*) del_ie + del_ie->length; + len = del->data_len + (del->data - (u8*) del_ie) - del_ie->length; + + memcpy(dst, src, len); + + src = (u8*) del->data; + dst = attr; + + len = suc_ie->length - del_ie->length; + free(del->data); + del->data = attr; + del->data_len += len; + del->header = (INDEX_HEADER*) (del->data + 0x10); + del->header->index_length += len; + del->header->allocated_size += len; + + ntfs_mft_resize_resident(del->dir->inode, AT_INDEX_ROOT, NTFS_INDEX_I30, 4, del->data, del->data_len); + + //utils_dump_mem(attr, 0, del->data_len, DM_DEFAULTS); + + //ntfs_log_debug("\n"); + //ntfs_log_debug("Adjust children\n"); + //for (i = 0; i < del->child_count; i++) + // ntfs_log_debug("\tChild %d %p %d\n", i, del->children[i], del->children[i]->flags); + //ntfs_log_debug("\n"); + + //ntfs_log_debug("src = %p, dst = %p, len = %d\n", src, dst, len); fflush (stdout); + + for (i = 0; i < del->child_count; i++) + del->children[i] = (INDEX_ENTRY*) (dst + ((u8*) del->children[i] - src)); + + for (i = del_num+1; i < del->child_count; i++) + del->children[i] = (INDEX_ENTRY*) ((u8*) del->children[i] + len); + + //for (i = 0; i < del->child_count; i++) + // ntfs_log_debug("\tChild %d %p %d\n", i, del->children[i], del->children[i]->flags); + //ntfs_log_debug("\n"); + + //utils_dump_mem(del->data, 0, del->data_len, DM_DEFAULTS); + //ntfs_log_debug("\n"); + + del->changed = TRUE; + + ntfs_log_debug("Modified: inode %lld, $INDEX_ROOT\n", del->dir->inode->mft_no); + return TRUE; +} + +/** + * ntfs_dt_alloc_replace - Replace an index entry in an index allocation + * @del: + * @del_num: + * @del_ie: + * @suc_ie: + * + * Description... + * + * Returns: + */ +BOOL ntfs_dt_alloc_replace(struct ntfs_dt *del, int del_num, INDEX_ENTRY *del_ie, INDEX_ENTRY *suc_ie) +{ + u8 *src; + u8 *dst; + int len; + int i; + + if (!del || !del_ie || !suc_ie) + return FALSE; + ntfs_log_trace ("\n"); + + //utils_dump_mem(del->data, 0, del->data_len, DM_DEFAULTS); + + src = (u8*) del_ie + del_ie->length; + dst = (u8*) del_ie + suc_ie->length; + len = del->header->index_length + 24 + (del->data - src); + //ntfs_log_debug("src = %d\n", src - del->data); + //ntfs_log_debug("dst = %d\n", dst - del->data); + //ntfs_log_debug("len = %d\n", len); + + if (src != dst) + memmove(dst, src, len); + + src = (u8*) suc_ie; + dst = (u8*) del_ie; + len = suc_ie->length; + + memcpy(dst, src, len); + + //utils_dump_mem(del->data, 0, del->data_len, DM_DEFAULTS); + + del->header->index_length += suc_ie->length - del_ie->length; + + dst = del->data + del->header->index_length + 24; + len = del->data_len - del->header->index_length - 24; + + memset(dst, 0, len); + + //for (i = 0; i < del->child_count; i++) + // ntfs_log_debug("Child %d %p\n", i, del->children[i]); + //ntfs_log_debug("\n"); + + len = suc_ie->length - del_ie->length; + //ntfs_log_debug("len = %d\n", len); + + for (i = del_num+1; i < del->child_count; i++) + del->children[i] = (INDEX_ENTRY*) ((u8*) del->children[i] + len); + + //for (i = 0; i < del->child_count; i++) + // ntfs_log_debug("Child %d %p\n", i, del->children[i]); + //ntfs_log_debug("\n"); + + //utils_dump_mem(del->data, 0, del->data_len, DM_DEFAULTS); + + del->changed = TRUE; + + ntfs_log_debug("Modified: inode %lld, $INDEX_ALLOCATION vcn %lld-%lld\n", del->dir->inode->mft_no, del->vcn, del->vcn + (del->dir->index_size>>9) - 1); + return TRUE; +} + +/** + * ntfs_dt_root_remove - Remove an index entry from an index root + * @del: + * @del_num: + * + * Description... + * + * Returns: + */ +BOOL ntfs_dt_root_remove(struct ntfs_dt *del, int del_num) +{ + INDEX_ENTRY *del_ie = NULL; + u8 *src; + u8 *dst; + u8 *old; + int len; + int del_len; + int i; + //int off; + + if (!del) + return FALSE; + ntfs_log_trace ("\n"); + + //utils_dump_mem(del->data, 0, del->header->index_length+16, DM_RED); + //ntfs_log_debug("\n"); + +#if 0 + off = (u8*) del->children[0] - del->data; + for (i = 0; i < del->child_count; i++) { + del_ie = del->children[i]; + + ntfs_log_debug("%2d %4d ", i+1, off); + off += del_ie->length; + + if (del_ie->flags & INDEX_ENTRY_END) { + ntfs_log_debug("END (%d)\n", del_ie->length); + break; + } + + ntfs_name_print(del_ie->key.file_name.file_name, del_ie->key.file_name.file_name_length); + ntfs_log_debug(" (%d)\n", del_ie->length); + } + ntfs_log_debug("total = %d\n", off); +#endif + + del_ie = del->children[del_num]; + del_len = del_ie->length; + + src = (u8*) del_ie + del_len; + dst = (u8*) del_ie; + len = del->header->index_length + 16 - (src - del->data); + + //ntfs_log_debug("src = %d\n", src - del->data); + //ntfs_log_debug("dst = %d\n", dst - del->data); + //ntfs_log_debug("len = %d\n", len); + + memmove(dst, src, len); + + del->data_len -= del_len; + del->child_count--; + + del->header->index_length = del->data_len - 16; + del->header->allocated_size = del->data_len - 16; + + ntfs_mft_resize_resident(del->dir->inode, AT_INDEX_ROOT, NTFS_INDEX_I30, 4, del->data, del->data_len); + old = del->data; + del->data = realloc(del->data, del->data_len); + del->header = (INDEX_HEADER*) (del->data + 0x10); + + //utils_dump_mem(del->data, 0, del->data_len, DM_GREEN | DM_RED); + + src = (u8*) (&del->children[del_num+1]); + dst = (u8*) (&del->children[del_num]); + len = (del->child_count - del_num) * sizeof(INDEX_ENTRY*); + + //ntfs_log_debug("src = %d\n", src - (u8*) del->children); + //ntfs_log_debug("dst = %d\n", dst - (u8*) del->children); + //ntfs_log_debug("len = %d\n", len); + + memmove(dst, src, len); + + src = (u8*) (&del->sub_nodes[del_num+1]); + dst = (u8*) (&del->sub_nodes[del_num]); + len = (del->child_count - del_num) * sizeof(struct ntfs_dt*); + + //ntfs_log_debug("src = %d\n", src - (u8*) del->children); + //ntfs_log_debug("dst = %d\n", dst - (u8*) del->children); + //ntfs_log_debug("len = %d\n", len); + + memmove(dst, src, len); + + //ntfs_log_debug("del_num = %d\n", del_num); + for (i = 0; i < del->child_count; i++) + del->children[i] = (INDEX_ENTRY*) ((u8*) del->children[i] - old + del->data); + for (i = del_num; i < del->child_count; i++) + del->children[i] = (INDEX_ENTRY*) ((u8*) del->children[i] - del_len); + + if (!ntfs_dt_create_children2(del, del->child_count)) + return FALSE; + +#if 0 + off = (u8*) del->children[0] - del->data; + for (i = 0; i < del->child_count; i++) { + del_ie = del->children[i]; + + ntfs_log_debug("%2d %4d ", i+1, off); + off += del_len; + + if (del_ie->flags & INDEX_ENTRY_END) { + ntfs_log_debug("END (%d)\n", del_len); + break; + } + + ntfs_name_print(del_ie->key.file_name.file_name, del_ie->key.file_name.file_name_length); + ntfs_log_debug(" (%d)\n", del_len); + } + ntfs_log_debug("total = %d\n", off); +#endif + + //utils_dump_mem(del->data, 0, del->header->index_length+16, DM_DEFAULTS); + + del->changed = TRUE; + + ntfs_log_debug("Modified: inode %lld, $INDEX_ROOT\n", del->dir->inode->mft_no); + return TRUE; +} + +/** + * ntfs_dt_alloc_remove - Remove an index entry from an index allocation + * @del: + * @del_num: + * + * Description... + * + * Returns: + */ +BOOL ntfs_dt_alloc_remove(struct ntfs_dt *del, int del_num) +{ + INDEX_ENTRY *del_ie = NULL; + u8 *dst; + u8 *src; + int len; + int i; + //int off; + + if (!del) + return FALSE; + ntfs_log_trace ("\n"); + +#if 0 + off = (u8*)del->children[0] - del->data; + for (i = 0; i < del->child_count; i++) { + del_ie = del->children[i]; + + ntfs_log_debug("%2d %4d ", i, off); + off += del_ie->length; + + if (del_ie->flags & INDEX_ENTRY_END) { + ntfs_log_debug("END (%d)\n", del_ie->length); + break; + } + + ntfs_name_print(del_ie->key.file_name.file_name, del_ie->key.file_name.file_name_length); + ntfs_log_debug(" (%d)\n", del_ie->length); + } + ntfs_log_debug("total = %d\n", off); + ntfs_log_debug("\n"); +#endif + + //utils_dump_mem(del->data, 0, del->data_len, DM_DEFAULTS); + //ntfs_log_debug("\n"); + + del_ie = del->children[del_num]; + + src = (u8*) del_ie + del_ie->length; + dst = (u8*) del_ie; + len = del->header->index_length + 24 - (src - del->data); + + //ntfs_log_debug("src = %d\n", src - del->data); + //ntfs_log_debug("dst = %d\n", dst - del->data); + //ntfs_log_debug("len = %d\n", len); + + memmove(dst, src, len); + + del->header->index_length -= src - dst; + del->child_count--; + + dst += len; + len = del->data_len - del->header->index_length - 24; + + //ntfs_log_debug("dst = %d\n", dst - del->data); + //ntfs_log_debug("len = %d\n", len); + + memset(dst, 0, len); + + src = (u8*) (&del->children[del_num+1]); + dst = (u8*) (&del->children[del_num]); + len = (del->child_count - del_num) * sizeof(INDEX_ENTRY*); + + //ntfs_log_debug("src = %d\n", src - (u8*) del->children); + //ntfs_log_debug("dst = %d\n", dst - (u8*) del->children); + //ntfs_log_debug("len = %d\n", len); + + memmove(dst, src, len); + + src = (u8*) (&del->sub_nodes[del_num+1]); + dst = (u8*) (&del->sub_nodes[del_num]); + len = (del->child_count - del_num) * sizeof(struct ntfs_dt*); + + //ntfs_log_debug("src = %d\n", src - (u8*) del->children); + //ntfs_log_debug("dst = %d\n", dst - (u8*) del->children); + //ntfs_log_debug("len = %d\n", len); + + memmove(dst, src, len); + + //ntfs_log_debug("del_num = %d\n", del_num); + for (i = del_num; i < del->child_count; i++) + del->children[i] = (INDEX_ENTRY*) ((u8*) del->children[i] - del_ie->length); + + if (!ntfs_dt_create_children2(del, del->child_count)) + return FALSE; + + //utils_dump_mem(del->data, 0, del->data_len, DM_DEFAULTS); + +#if 0 + off = (u8*)del->children[0] - del->data; + for (i = 0; i < del->child_count; i++) { + del_ie = del->children[i]; + + ntfs_log_debug("%2d %4d ", i, off); + off += del_ie->length; + + if (del_ie->flags & INDEX_ENTRY_END) { + ntfs_log_debug("END (%d)\n", del_ie->length); + break; + } + + ntfs_name_print(del_ie->key.file_name.file_name, del_ie->key.file_name.file_name_length); + ntfs_log_debug(" (%d)\n", del_ie->length); + } + ntfs_log_debug("total = %d\n", off); + ntfs_log_debug("\n"); +#endif + + del->changed = TRUE; + + ntfs_log_debug("Modified: inode %lld, $INDEX_ALLOCATION vcn %lld-%lld\n", del->dir->inode->mft_no, del->vcn, del->vcn + (del->dir->index_size>>9) - 1); + + if (del->child_count < 2) { + ntfs_log_debug("indx is empty\n"); + ntfs_bmp_set_range(del->dir->bitmap, del->vcn, 1, 0); + } + + return TRUE; +} + +/** + * ntfs_dt_alloc_add - Add an index entry to an index allocation + * @parent: + * @index_num: + * @ie: + * @child: + * + * Description... + * + * Returns: + */ +int ntfs_dt_alloc_add(struct ntfs_dt *parent, int index_num, INDEX_ENTRY *ie, struct ntfs_dt *child) +{ + INDEX_BLOCK *block; + INDEX_ENTRY *entry; + int need; + int space; + u8 *src; + u8 *dst; + int len; + + if (!parent || !ie) + return 0; + ntfs_log_trace ("\n"); + + block = (INDEX_BLOCK*) parent->data; + + need = ie->length; + space = parent->data_len - block->index.index_length - 24; + + ntfs_log_debug("need %d, have %d\n", need, space); + if (need > space) { + ntfs_log_debug("no room\n"); + return 0; + } + + //utils_dump_mem(parent->data, 0, parent->data_len, DM_DEFAULTS); + //ntfs_log_debug("\n"); + + src = (u8*) parent->children[index_num]; + dst = src + need; + len = parent->data + parent->data_len - src - space; + //ntfs_log_debug("src = %d\n", src - parent->data); + //ntfs_log_debug("dst = %d\n", dst - parent->data); + //ntfs_log_debug("len = %d\n", len); + + memmove(dst, src, len); + + dst = src; + src = (u8*) ie; + len = need; + + memcpy(dst, src, len); + + block->index.index_length += len; + + dst = parent->data + block->index.index_length + 24; + len = parent->data_len - block->index.index_length - 24; + + memset(dst, 0, len); + + //realloc children, sub_nodes + ntfs_dt_create_children2(parent, parent->child_count + 1); + + // regen children pointers + parent->child_count = 0; + + src = parent->data + 0x18 + parent->header->entries_offset; + len = parent->data_len - 0x18 - parent->header->entries_offset; + + while (src < (parent->data + parent->data_len)) { + entry = (INDEX_ENTRY*) src; + + parent->children[parent->child_count] = entry; + parent->child_count++; + + if (entry->flags & INDEX_ENTRY_END) + break; + + src += entry->length; + } + ntfs_log_debug("count = %d\n", parent->child_count); + + src = (u8*) &parent->sub_nodes[index_num+parent->child_count-1]; + dst = (u8*) &parent->sub_nodes[index_num]; + len = (parent->child_count - index_num - 1) * sizeof(struct ntfs_dt*); + + memmove(dst, src, len); + + //insert sub_node pointer + parent->sub_nodes[index_num] = child; + + //utils_dump_mem(parent->data, 0, parent->data_len, DM_DEFAULTS); + //ntfs_log_debug("\n"); + return 0; +} + +/** + * ntfs_dt_root_add - Add an index entry to an index root + * @parent: + * @index_num: + * @ie: + * @child: + * + * Description... + * + * Returns: + */ +int ntfs_dt_root_add(struct ntfs_dt *parent, int index_num, INDEX_ENTRY *ie, struct ntfs_dt *child) +{ + INDEX_ROOT *root; + INDEX_ENTRY *entry; + int need; + int space; + u8 *attr; + u8 *src; + u8 *dst; + int len; + + if (!parent || !ie) + return -1; + ntfs_log_trace ("\n"); + + root = (INDEX_ROOT*) parent->data; + + //utils_dump_mem(parent->data, 0, parent->data_len, DM_DEFAULTS); + //ntfs_log_debug("\n"); + + need = ie->length; + space = ntfs_mft_free_space(parent->dir); + + ntfs_log_debug("need %d, have %d\n", need, space); + if (need > space) { + ntfs_log_debug("no room\n"); + return -1; + } + + attr = malloc(parent->data_len + need); + + src = parent->data; + dst = attr; + len = root->index.entries_offset + 16; + + memcpy(dst, src, len); + + dst += len; + src = (u8*) ie; + len = ie->length; + + memcpy(dst, src, len); + + dst += len; + src = (u8*) parent->children[index_num]; + len = parent->data + parent->data_len - src; + + memcpy(dst, src, len); + + free(parent->data); + parent->data = attr; + parent->data_len += need; + + ntfs_log_debug("parent data len = %d\n", parent->data_len); + + root = (INDEX_ROOT*) parent->data; + root->index.index_length = parent->data_len - 16; + root->index.allocated_size = parent->data_len - 16; + + //utils_dump_mem(parent->data, 0, parent->data_len, DM_DEFAULTS); + //ntfs_log_debug("\n"); + + ntfs_mft_resize_resident(parent->dir->inode, AT_INDEX_ROOT, NTFS_INDEX_I30, 4, parent->data, parent->data_len); + parent->changed = TRUE; + + //realloc children, sub_nodes + ntfs_dt_create_children2(parent, parent->child_count + 1); + + // regen children pointers + parent->child_count = 0; + + src = parent->data + 0x18 + parent->header->entries_offset; + len = parent->data_len - 0x18 - parent->header->entries_offset; + + // XXX can we rebase the children more simply? (in alloc_add too) + while (src < (parent->data + parent->data_len)) { + entry = (INDEX_ENTRY*) src; + + parent->children[parent->child_count] = entry; + parent->child_count++; + + if (entry->flags & INDEX_ENTRY_END) + break; + + src += entry->length; + } + ntfs_log_debug("count = %d\n", parent->child_count); + + src = (u8*) &parent->sub_nodes[index_num+parent->child_count-1]; + dst = (u8*) &parent->sub_nodes[index_num]; + len = (parent->child_count - index_num - 1) * sizeof(struct ntfs_dt*); + + memmove(dst, src, len); + + //insert sub_node pointer + parent->sub_nodes[index_num] = child; + + return 0; +} + +/** + * ntfs_dt_add2 - Add an index entry to a directory-tree + * @ie: + * @suc: + * @suc_num: + * @ded: + * + * Description... + * + * Returns: + */ +int ntfs_dt_add2(INDEX_ENTRY *ie, struct ntfs_dt *suc, int suc_num, struct ntfs_dt *ded) +{ + int need; + int space; + int median; + struct ntfs_dt *new = NULL; + struct ntfs_dt *chl; + INDEX_ENTRY *med_ie = NULL; + //FILE_NAME_ATTR *file; + VCN vcn = 0; + //int i; + + if (!ie || !suc) + return -1; + ntfs_log_trace ("\n"); + + ntfs_log_debug("Add key to leaf\n"); + + //utils_dump_mem(suc->data, 0, suc->data_len, DM_DEFAULTS); + + chl = NULL; +ascend: + //XXX replace with while/break? + +#if 0 + for (; ded; ded = ded->sub_nodes[0]) { + ntfs_log_debug("\tded vcn = %lld\n", ded->vcn); + } +#endif + + /* + * ADD + * room in current node? + * yes, add, done + * no, split, ascend + */ + need = ie->length; + + if (ntfs_dt_isroot(suc)) + space = ntfs_dt_root_freespace(suc); + else + space = ntfs_dt_alloc_freespace(suc); + + ntfs_log_debug("\tneed %d\n", need); + ntfs_log_debug("\tspace %d\n", space); + + if (space >= need) { + //ntfs_log_critical("index = %d\n", suc_num); + //ntfs_log_debug("prev inode = %p\n", suc->inodes[suc_num-1]); + //ntfs_log_debug("curr inode = %p\n", suc->inodes[suc_num]); + ntfs_log_debug("count = %d\n", suc->child_count); + if (ntfs_dt_isroot(suc)) + ntfs_dt_root_add(suc, suc_num, ie, chl); + else + ntfs_dt_alloc_add(suc, suc_num, ie, chl); + ntfs_log_debug("count = %d\n", suc->child_count); + //goto done; + return suc_num; //XXX this is probably off-by-one + } + + /* + * SPLIT + * any dead? + * yes reuse + * no alloc + */ + if (ded) { + new = ded; + vcn = ded->vcn; + ded = ded->sub_nodes[0]; + ntfs_log_debug("\treusing vcn %lld\n", new->vcn); + } else { + ntfs_mft_add_index(suc->dir); + /* + * ALLOC + * any unused records? + * yes, enable first + * no, extend + */ + /* + * ENABLE + * modify bitmap + * init indx record + */ + /* + * EXTEND + * room in bitmap + * yes, do nothing + * no, extend bitmap + * extend alloc + */ + /* + * EXTEND BITMAP + * extend bitmap + * init bitmap + */ + } + + //ntfs_log_debug("\tnode has %d children\n", suc->child_count); + + // initialise new node + // XXX ntfs_dt_initialise(new, vcn); + + goto done; + + // find median key + median = (suc->child_count+1) / 2; + med_ie = ntfs_ie_copy(suc->children[median]); + //file = &med_ie->key.file_name; ntfs_log_debug("\tmed name: "); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_debug("\n"); + + ntfs_ie_free(med_ie); + med_ie = NULL; + + //ntfs_log_debug("suc key count = %d\n", suc->child_count); + //ntfs_log_debug("new key count = %d\n", new->child_count); + + //ntfs_log_debug("median's child = %p\n", suc->sub_nodes[median]); + // need to pass the child when ascending + chl = suc->sub_nodes[median]; + + // transfer keys + if (ntfs_dt_transfer(suc, new, 0, median-1) < 0) + goto done; + + //ntfs_log_debug("suc key count = %d\n", suc->child_count); + //ntfs_log_debug("new key count = %d\n", new->child_count); + + //file = &suc->children[0]->key.file_name; ntfs_log_debug("\tmed name: "); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_debug("\n"); + + // can this be a root node? + if (ntfs_dt_isroot(suc)) + ntfs_dt_root_remove(suc, 0); + else + ntfs_dt_alloc_remove(suc, 0); + + //file = &suc->children[0]->key.file_name; ntfs_log_debug("\tmed name: "); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_debug("\n"); + //ntfs_log_debug("suc key count = %d\n", suc->child_count); + //ntfs_log_debug("new key count = %d\n", new->child_count); + + // remove the median key + + // split when median has children + // median child given to new ! + // median child is new + // ascend + + med_ie = ntfs_ie_set_vcn(med_ie, new->vcn); + if (!med_ie) + goto done; + + //ntfs_log_debug("median child = %lld\n", ntfs_ie_get_vcn(med_ie)); + //ntfs_log_debug("new's vcn = %lld\n", new->vcn); + + // adjust parents + // attach new to median + // escape clause for root node? + // goto ascend + + // ie = insert + // child = child + // suc = successor + // suc_num = insert point + + ie = med_ie; + suc = suc->parent; + suc_num = 0; + + //ntfs_log_debug("\n"); + ntfs_log_debug("Ascend\n"); + goto ascend; +done: + return 0; +} + + +#endif /* NTFS_RICH */ + diff --git a/ntfsprogs.spec.in b/ntfsprogs.spec.in new file mode 100644 index 00000000..f5374642 --- /dev/null +++ b/ntfsprogs.spec.in @@ -0,0 +1,206 @@ +%define name @PACKAGE@ +%define ver @VERSION@ +%define rel 1 +%define prefix /usr +%define bindir /usr/bin +%define sbindir /usr/sbin +%define mandir /usr/share/man + + +Summary : NTFS filesystem libraries and utilities +Name : %{name} +Version : %{ver} +Release : %{rel} +Source : http://prdownloads.sf.net/linux-ntfs/ntfsprogs-%{ver}.tar.gz +Buildroot : %{_tmppath}/%{name}-root +Packager : Anton Altaparmakov +License : GPL +Group : System Environment/Base +%description +The Linux-NTFS project (http://www.linux-ntfs.org/) aims to bring full support +for the NTFS filesystem to the Linux operating system. The ntfsprogs package +currently consists of a static library and utilities such as mkntfs, ntfscat, +ntfsls, ntfsresize, and ntfsundelete (for a full list of included utilities +see man 8 ntfsprogs after installation). + + +%package gnomevfs +Summary : NTFS GNOME virtual filesystem module +Group : System Environment/Base +Requires : ntfsprogs = %{ver}-%{rel} +BuildRequires : glib2-devel +Requires : glib2 +BuildRequires : gnome-vfs2-devel +Requires : gnome-vfs2 +%description gnomevfs +This package contains the NTFS GNOME virtual filesystem (VFS) module which +allows GNOME VFS clients to seamlessly utilize the NTFS library (libntfs). + + +%package fuse +Summary : NTFS FUSE module (ntfsmount) +Group : System Environment/Base +Requires : ntfsprogs = %{ver}-%{rel} +Requires : fuse >= 2.3.0 +%description fuse +This package contains the ntfsmount utility which is an NTFS filesystem in +userspace (FUSE) module allowing users to mount an ntfs filesystem from +userspace and accessing it using the functionality provided by the NTFS +library (libntfs). + + +%package devel +Summary : files required to compile software that uses libntfs +Group : Development/System +Requires : ntfsprogs = %{ver}-%{rel} +%description devel +This package includes the header files and libraries needed to link software +with the NTFS library (libntfs). + + +%prep +%setup + +%build +if [ -n "$LINGUAS" ]; then unset LINGUAS; fi +%configure --enable-gnome-vfs --enable-fuse-module +make + + +%install +rm -rf "$RPM_BUILD_ROOT" +make DESTDIR="$RPM_BUILD_ROOT" install-strip + + +%clean +rm -rf "$RPM_BUILD_ROOT" + + +%post +/sbin/ldconfig + + +%postun +/sbin/ldconfig + + +%files +%defattr(-,root,root) +%doc AUTHORS COPYING CREDITS ChangeLog INSTALL NEWS README TODO.include TODO.libntfs TODO.ntfsprogs doc/CodingStyle doc/attribute_definitions doc/attributes.txt doc/compression.txt doc/tunable_settings doc/template.c doc/template.h doc/system_files.txt doc/system_security_descriptors.txt +%{bindir}/ntfs[^m][^o]* +%{sbindir}/* +/sbin/mkfs.ntfs +%{mandir}/man8/mkntfs.8* +%{mandir}/man8/mkfs.ntfs.8* +%{mandir}/man8/ntfs[^m][^o]*.8* +%{_libdir}/libntfs.*so* + +%files gnomevfs +%defattr(-,root,root) +%{mandir}/man8/libntfs-gnomevfs.8* +%{_libdir}/gnome-vfs-2.0/modules/libntfs-gnomevfs.*so* +%config %{_sysconfdir}/gnome-vfs-2.0/modules/libntfs.conf + +%files fuse +%defattr(-,root,root) +%{bindir}/ntfsmount* +/sbin/mount.ntfs-fuse +%{mandir}/man8/ntfsmount.8* +%{mandir}/man8/mount.ntfs-fuse.8* + +%files devel +%defattr(-,root,root) +%{_includedir}/* +%{_libdir}/libntfs.*a* +%{_libdir}/gnome-vfs-2.0/modules/libntfs-gnomevfs.*a* + +%changelog +* Fri Oct 7 2005 Anton Altaparmakov +- Fix the file distribution after new binaries have been added as they were + ending up in the wrong rpms. + +* Mon Aug 15 2005 Szabolcs Szakacsits +- Add mkfs.ntfs. + +* Mon Jul 18 2005 Anton Altaparmakov +- Add ntfsmount fuse module in separate rpm ntfsprogs-fuse. + +* Wed Mar 10 2004 Anton Altaparmakov +- Cleanup descriptions ready for 1.9.0 release. + +* Mon Jan 19 2004 Anton Altaparmakov +- Add %config to tell rpm that libntfs.conf is a config file. + +* Thu Nov 6 2003 Anton Altaparmakov +- update description (it was getting too long) +- merge libntfs-gnomevfs + +* Fri Oct 19 2003 Richard Russon +- added the new utility ntfscat + +* Tue Sep 30 2003 Anton Altaparmakov +- added the new utilities, ntfsclone, ntfscluster, ntfsinfo, ntfsls. + +* Sat Jan 18 2003 Anton Altaparmakov +- renamed to ntfsprogs.spec.in +- change source tar ball name to ntfsprogs + +* Tue Dec 10 2002 Anton Altaparmakov +- added ntfsresize + +* Wed Jul 18 2002 Richard Russon +- added ntfsundelete +- change TODO names + +* Wed Jul 3 2002 Anton Altaparmakov +- update my email address + +* Mon Jun 3 2002 Anton Altaparmakov +- update %doc with new TODO files + +* Tue Apr 12 2002 Anton Altaparmakov +- update %description text for ntfslabel + +* Tue Mar 12 2002 Anton Altaparmakov +- update %description text + +* Sat Jan 26 2002 Anton Altaparmakov +- update %description text +- make dependencies pick the right version automatically + +* Thu Jan 10 2002 Anton Altaparmakov +- add dependency on linux-ntfs to linux-ntfs-devel +- update %description text + +* Fri Nov 09 2001 Anton Altaparmakov +- update %description text +- (re)enable installation of shared libraries + +* Wed Aug 22 2001 Anton Altaparmakov +- update %description text + +* Thu Aug 2 2001 Anton Altaparmakov +- update %description text + +* Wed Jul 25 2001 Anton Altaparmakov +- include sbin install path (mkntfs now is in sbin) + +* Tue Jul 24 2001 Anton Altaparmakov +- update %description text + +* Mon Jun 11 2001 Anton Altaparmakov +- remove duplicate %configure options +- remove shared library installation as shared libraries are disabled by +default + +* Sun Jun 10 2001 Anton Altaparmakov +- add man pages stuff +- update info text +- add new doc/ stuff +- modify installation to do install-strip instead of install followed by manual +stripping +- update download URL to be the fast sourceforge http download server + +* Fri Feb 2 2001 Anton Altaparmakov +- started changelog + diff --git a/ntfsprogs/.cvsignore b/ntfsprogs/.cvsignore new file mode 100644 index 00000000..d31d1c5a --- /dev/null +++ b/ntfsprogs/.cvsignore @@ -0,0 +1,25 @@ +.deps +.libs +Makefile +Makefile.in +mkntfs +ntfscat +ntfsclone +ntfscluster +ntfscmp +ntfscp +ntfsdecrypt +ntfsdump_logfile +ntfsfix +ntfsinfo +ntfslabel +ntfsls +ntfsmftalloc +ntfsmount +ntfsmove +ntfsresize +ntfsrm +ntfstruncate +ntfsundelete +ntfswipe +*.8 diff --git a/ntfsprogs/Makefile.am b/ntfsprogs/Makefile.am new file mode 100644 index 00000000..b258a6ab --- /dev/null +++ b/ntfsprogs/Makefile.am @@ -0,0 +1,150 @@ +if REALLYSTATIC +AM_LIBS = +AM_LFLAGS = -static +STATIC_LINK = $(CC) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +else +AM_LIBS = $(NTFS_3G_MODULE_LIBS) +AM_CFLAGS = $(NTFS_3G_MODULE_CFLAGS) +AM_LFLAGS = $(all_libraries) +LIBTOOL_LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +endif + +# Workaround to make REALLYSTATIC work with automake 1.5. +LINK=$(STATIC_LINK) $(LIBTOOL_LINK) + +bin_PROGRAMS = ntfsfix ntfsinfo ntfscluster ntfsls ntfscat ntfscmp +sbin_PROGRAMS = mkntfs ntfslabel ntfsundelete ntfsresize ntfsclone \ + ntfscp +EXTRA_PROGRAMS = ntfsdump_logfile ntfswipe ntfstruncate ntfsmove \ + ntfsmftalloc + +man_MANS = mkntfs.8 ntfsfix.8 ntfslabel.8 ntfsinfo.8 \ + ntfsundelete.8 ntfsresize.8 ntfsprogs.8 ntfsls.8 \ + ntfsclone.8 ntfscluster.8 ntfscat.8 ntfscp.8 \ + ntfscmp.8 +EXTRA_MANS = + +CLEANFILES = $(EXTRA_PROGRAMS) + +MAINTAINERCLEANFILES = Makefile.in + +linux_ntfsincludedir = -I$(top_srcdir)/include/ntfs + +if ENABLE_CRYPTO +EXTRA_PROGRAMS += ntfsdecrypt +endif + +if ENABLE_RICH +EXTRA_PROGRAMS += ntfsrm +endif + +# Set the include path. +AM_CPPFLAGS = -I$(top_srcdir)/include/ntfs $(all_includes) + +ntfsfix_SOURCES = ntfsfix.c utils.c utils.h +ntfsfix_LDADD = $(AM_LIBS) +ntfsfix_LDFLAGS = $(AM_LFLAGS) + +mkntfs_SOURCES = attrdef.c attrdef.h upcase.c upcase.h boot.c boot.h sd.c sd.h mkntfs.c utils.c utils.h +mkntfs_LDADD = $(AM_LIBS) +mkntfs_LDFLAGS = $(AM_LFLAGS) + +ntfslabel_SOURCES = ntfslabel.c utils.c utils.h +ntfslabel_LDADD = $(AM_LIBS) +ntfslabel_LDFLAGS = $(AM_LFLAGS) + +ntfsinfo_SOURCES = ntfsinfo.c utils.c utils.h +ntfsinfo_LDADD = $(AM_LIBS) +ntfsinfo_LDFLAGS = $(AM_LFLAGS) + +ntfsundelete_SOURCES = ntfsundelete.c ntfsundelete.h utils.c utils.h +ntfsundelete_LDADD = $(AM_LIBS) +ntfsundelete_LDFLAGS = $(AM_LFLAGS) + +ntfsresize_SOURCES = ntfsresize.c utils.c utils.h +ntfsresize_LDADD = $(AM_LIBS) +ntfsresize_LDFLAGS = $(AM_LFLAGS) + +ntfsclone_SOURCES = ntfsclone.c utils.c utils.h +ntfsclone_LDADD = $(AM_LIBS) +ntfsclone_LDFLAGS = $(AM_LFLAGS) + +ntfscluster_SOURCES = ntfscluster.c ntfscluster.h cluster.c cluster.h utils.c utils.h +ntfscluster_LDADD = $(AM_LIBS) +ntfscluster_LDFLAGS = $(AM_LFLAGS) + +ntfsls_SOURCES = ntfsls.c utils.c utils.h +ntfsls_LDADD = $(AM_LIBS) +ntfsls_LDFLAGS = $(AM_LFLAGS) + +ntfscat_SOURCES = ntfscat.c ntfscat.h utils.c utils.h +ntfscat_LDADD = $(AM_LIBS) +ntfscat_LDFLAGS = $(AM_LFLAGS) + +ntfscp_SOURCES = ntfscp.c utils.c utils.h +ntfscp_LDADD = $(AM_LIBS) +ntfscp_LDFLAGS = $(AM_LFLAGS) + +ntfscmp_SOURCES = ntfscmp.c utils.c utils.h +ntfscmp_LDADD = $(AM_LIBS) +ntfscmp_LDFLAGS = $(AM_LFLAGS) + +# We don't distribute these + +if ENABLE_RICH +ntfsrm_SOURCES = ntfsrm.c ntfsrm.h utils.c utils.h +ntfsrm_LDADD = $(AM_LIBS) +ntfsrm_LDFLAGS = $(AM_LFLAGS) +endif + +ntfstruncate_SOURCES = attrdef.c ntfstruncate.c utils.c utils.h +ntfstruncate_LDADD = $(AM_LIBS) +ntfstruncate_LDFLAGS = $(AM_LFLAGS) + +ntfsmftalloc_SOURCES = ntfsmftalloc.c utils.c utils.h +ntfsmftalloc_LDADD = $(AM_LIBS) +ntfsmftalloc_LDFLAGS = $(AM_LFLAGS) + +ntfsmove_SOURCES = ntfsmove.c ntfsmove.h utils.c utils.h +ntfsmove_LDADD = $(AM_LIBS) +ntfsmove_LDFLAGS = $(AM_LFLAGS) + +ntfswipe_SOURCES = ntfswipe.c ntfswipe.h utils.c utils.h +ntfswipe_LDADD = $(AM_LIBS) +ntfswipe_LDFLAGS = $(AM_LFLAGS) + +ntfsdump_logfile_SOURCES= ntfsdump_logfile.c +ntfsdump_logfile_LDADD = $(AM_LIBS) +ntfsdump_logfile_LDFLAGS= $(AM_LFLAGS) + +if ENABLE_CRYPTO +ntfsdecrypt_SOURCES = ntfsdecrypt.c utils.c utils.h +ntfsdecrypt_LDADD = $(AM_LIBS) +ntfsdecrypt_LDFLAGS = $(AM_LFLAGS) -lgcrypt -lgnutls +endif + +# Extra targets + +strip: $(bin_PROGRAMS) $(sbin_PROGRAMS) + $(STRIP) $^ + +libs: + (cd ../libntfs && $(MAKE) libs) || exit 1; + +extra: extras + +extras: libs $(EXTRA_PROGRAMS) + +# mkfs.ntfs[.8] hard link + +install-exec-hook: + $(INSTALL) -d $(DESTDIR)/sbin + $(LN_S) -f $(sbindir)/mkntfs $(DESTDIR)/sbin/mkfs.ntfs + +install-data-hook: + $(INSTALL) -d $(DESTDIR)$(man8dir) + $(LN_S) -f mkntfs.8 $(DESTDIR)$(man8dir)/mkfs.ntfs.8 + +uninstall-local: + $(RM) -f $(DESTDIR)/sbin/mkfs.ntfs + $(RM) -f $(DESTDIR)$(man8dir)/mkfs.ntfs.8 diff --git a/ntfsprogs/attrdef.c b/ntfsprogs/attrdef.c new file mode 100644 index 00000000..4ffa5eeb --- /dev/null +++ b/ntfsprogs/attrdef.c @@ -0,0 +1,322 @@ +/** + * attrdef_ntfs12_array + */ +const unsigned char attrdef_ntfs12_array[2400] = { + 36, 0, 83, 0, 84, 0, 65, 0, 78, 0, 68, 0, 65, 0, 82, 0, + 68, 0, 95, 0, 73, 0, 78, 0, 70, 0, 79, 0, 82, 0, 77, 0, + 65, 0, 84, 0, 73, 0, 79, 0, 78, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, + 48, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, + 36, 0, 65, 0, 84, 0, 84, 0, 82, 0, 73, 0, 66, 0, 85, 0, + 84, 0, 69, 0, 95, 0, 76, 0, 73, 0, 83, 0, 84, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 36, 0, 70, 0, 73, 0, 76, 0, 69, 0, 95, 0, 78, 0, 65, 0, + 77, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, + 68, 0, 0, 0, 0, 0, 0, 0, 66, 2, 0, 0, 0, 0, 0, 0, + 36, 0, 86, 0, 79, 0, 76, 0, 85, 0, 77, 0, 69, 0, 95, 0, + 86, 0, 69, 0, 82, 0, 83, 0, 73, 0, 79, 0, 78, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, + 8, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, + 36, 0, 83, 0, 69, 0, 67, 0, 85, 0, 82, 0, 73, 0, 84, 0, + 89, 0, 95, 0, 68, 0, 69, 0, 83, 0, 67, 0, 82, 0, 73, 0, + 80, 0, 84, 0, 79, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 36, 0, 86, 0, 79, 0, 76, 0, 85, 0, 77, 0, 69, 0, 95, 0, + 78, 0, 65, 0, 77, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 36, 0, 86, 0, 79, 0, 76, 0, 85, 0, 77, 0, 69, 0, 95, 0, + 73, 0, 78, 0, 70, 0, 79, 0, 82, 0, 77, 0, 65, 0, 84, 0, + 73, 0, 79, 0, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, + 12, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, + 36, 0, 68, 0, 65, 0, 84, 0, 65, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 36, 0, 73, 0, 78, 0, 68, 0, 69, 0, 88, 0, 95, 0, 82, 0, + 79, 0, 79, 0, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 36, 0, 73, 0, 78, 0, 68, 0, 69, 0, 88, 0, 95, 0, 65, 0, + 76, 0, 76, 0, 79, 0, 67, 0, 65, 0, 84, 0, 73, 0, 79, 0, + 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 36, 0, 66, 0, 73, 0, 84, 0, 77, 0, 65, 0, 80, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 36, 0, 83, 0, 89, 0, 77, 0, 66, 0, 79, 0, 76, 0, 73, 0, + 67, 0, 95, 0, 76, 0, 73, 0, 78, 0, 75, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 36, 0, 69, 0, 65, 0, 95, 0, 73, 0, 78, 0, 70, 0, 79, 0, + 82, 0, 77, 0, 65, 0, 84, 0, 73, 0, 79, 0, 78, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, + 8, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, + 36, 0, 69, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/** + * attrdef_ntfs3x_array + */ +const unsigned char attrdef_ntfs3x_array[2560] = { +0x24, 0x00, 0x53, 0x00, 0x54, 0x00, 0x41, 0x00, 0x4E, 0x00, 0x44, 0x00, 0x41, 0x00, 0x52, 0x00, +0x44, 0x00, 0x5F, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00, 0x52, 0x00, 0x4D, 0x00, +0x41, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4F, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x41, 0x00, 0x54, 0x00, 0x54, 0x00, 0x52, 0x00, 0x49, 0x00, 0x42, 0x00, 0x55, 0x00, +0x54, 0x00, 0x45, 0x00, 0x5F, 0x00, 0x4C, 0x00, 0x49, 0x00, 0x53, 0x00, 0x54, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4C, 0x00, 0x45, 0x00, 0x5F, 0x00, 0x4E, 0x00, 0x41, 0x00, +0x4D, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, +0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x4F, 0x00, 0x42, 0x00, 0x4A, 0x00, 0x45, 0x00, 0x43, 0x00, 0x54, 0x00, 0x5F, 0x00, +0x49, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x53, 0x00, 0x45, 0x00, 0x43, 0x00, 0x55, 0x00, 0x52, 0x00, 0x49, 0x00, 0x54, 0x00, +0x59, 0x00, 0x5F, 0x00, 0x44, 0x00, 0x45, 0x00, 0x53, 0x00, 0x43, 0x00, 0x52, 0x00, 0x49, 0x00, +0x50, 0x00, 0x54, 0x00, 0x4F, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x56, 0x00, 0x4F, 0x00, 0x4C, 0x00, 0x55, 0x00, 0x4D, 0x00, 0x45, 0x00, 0x5F, 0x00, +0x4E, 0x00, 0x41, 0x00, 0x4D, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x56, 0x00, 0x4F, 0x00, 0x4C, 0x00, 0x55, 0x00, 0x4D, 0x00, 0x45, 0x00, 0x5F, 0x00, +0x49, 0x00, 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00, 0x52, 0x00, 0x4D, 0x00, 0x41, 0x00, 0x54, 0x00, +0x49, 0x00, 0x4F, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x44, 0x00, 0x45, 0x00, 0x58, 0x00, 0x5F, 0x00, 0x52, 0x00, +0x4F, 0x00, 0x4F, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x44, 0x00, 0x45, 0x00, 0x58, 0x00, 0x5F, 0x00, 0x41, 0x00, +0x4C, 0x00, 0x4C, 0x00, 0x4F, 0x00, 0x43, 0x00, 0x41, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4F, 0x00, +0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x42, 0x00, 0x49, 0x00, 0x54, 0x00, 0x4D, 0x00, 0x41, 0x00, 0x50, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x52, 0x00, 0x45, 0x00, 0x50, 0x00, 0x41, 0x00, 0x52, 0x00, 0x53, 0x00, 0x45, 0x00, +0x5F, 0x00, 0x50, 0x00, 0x4F, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x45, 0x00, 0x41, 0x00, 0x5F, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00, +0x52, 0x00, 0x4D, 0x00, 0x41, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4F, 0x00, 0x4E, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x45, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x4C, 0x00, 0x4F, 0x00, 0x47, 0x00, 0x47, 0x00, 0x45, 0x00, 0x44, 0x00, 0x5F, 0x00, +0x55, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4C, 0x00, 0x49, 0x00, 0x54, 0x00, 0x59, 0x00, 0x5F, 0x00, +0x53, 0x00, 0x54, 0x00, 0x52, 0x00, 0x45, 0x00, 0x41, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + diff --git a/ntfsprogs/attrdef.h b/ntfsprogs/attrdef.h new file mode 100644 index 00000000..09740fab --- /dev/null +++ b/ntfsprogs/attrdef.h @@ -0,0 +1,8 @@ +#ifndef _NTFS_ATTRDEF_H_ +#define _NTFS_ATTRDEF_H_ + +extern const unsigned char attrdef_ntfs12_array[2400]; +extern const unsigned char attrdef_ntfs3x_array[2560]; + +#endif /* _NTFS_ATTRDEF_H_ */ + diff --git a/ntfsprogs/boot.c b/ntfsprogs/boot.c new file mode 100644 index 00000000..f0535ffd --- /dev/null +++ b/ntfsprogs/boot.c @@ -0,0 +1,222 @@ +/** + * boot_array - the first 3429 bytes of $Boot + * The first 3429 bytes of $Boot. The rest is just zero. Total 8192 bytes. + */ +const unsigned char boot_array[3429] = { +235, 91, 144, 78, 84, 70, 83, 32, 32, 32, 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 51, 192, +142, 208, 188, 0, 124, 251, 184, 192, 7, 142, 216, 199, 6, 84, 0, 0, + 0, 199, 6, 86, 0, 0, 0, 199, 6, 91, 0, 16, 0, 184, 0, 13, +142, 192, 43, 219, 232, 7, 0, 104, 0, 13, 104, 102, 2, 203, 80, 83, + 81, 82, 6, 102, 161, 84, 0, 102, 3, 6, 28, 0, 102, 51, 210, 102, + 15, 183, 14, 24, 0, 102, 247, 241, 254, 194, 136, 22, 90, 0, 102, 139, +208, 102, 193, 234, 16, 247, 54, 26, 0, 136, 22, 37, 0, 163, 88, 0, +161, 24, 0, 42, 6, 90, 0, 64, 59, 6, 91, 0, 118, 3, 161, 91, + 0, 80, 180, 2, 139, 22, 88, 0, 177, 6, 210, 230, 10, 54, 90, 0, +139, 202, 134, 233, 138, 54, 37, 0, 178, 128, 205, 19, 88, 114, 42, 1, + 6, 84, 0, 131, 22, 86, 0, 0, 41, 6, 91, 0, 118, 11, 193, 224, + 5, 140, 194, 3, 208, 142, 194, 235, 138, 7, 90, 89, 91, 88, 195, 190, + 89, 1, 235, 8, 190, 227, 1, 235, 3, 190, 57, 1, 232, 9, 0, 190, +173, 1, 232, 3, 0, 251, 235, 254, 172, 60, 0, 116, 9, 180, 14, 187, + 7, 0, 205, 16, 235, 242, 195, 29, 0, 65, 32, 100, 105, 115, 107, 32, +114, 101, 97, 100, 32, 101, 114, 114, 111, 114, 32, 111, 99, 99, 117, 114, +114, 101, 100, 46, 13, 10, 0, 41, 0, 65, 32, 107, 101, 114, 110, 101, +108, 32, 102, 105, 108, 101, 32, 105, 115, 32, 109, 105, 115, 115, 105, 110, +103, 32, 102, 114, 111, 109, 32, 116, 104, 101, 32, 100, 105, 115, 107, 46, + 13, 10, 0, 37, 0, 65, 32, 107, 101, 114, 110, 101, 108, 32, 102, 105, +108, 101, 32, 105, 115, 32, 116, 111, 111, 32, 100, 105, 115, 99, 111, 110, +116, 105, 103, 117, 111, 117, 115, 46, 13, 10, 0, 51, 0, 73, 110, 115, +101, 114, 116, 32, 97, 32, 115, 121, 115, 116, 101, 109, 32, 100, 105, 115, +107, 101, 116, 116, 101, 32, 97, 110, 100, 32, 114, 101, 115, 116, 97, 114, +116, 13, 10, 116, 104, 101, 32, 115, 121, 115, 116, 101, 109, 46, 13, 10, + 0, 23, 0, 92, 78, 84, 76, 68, 82, 32, 105, 115, 32, 99, 111, 109, +112, 114, 101, 115, 115, 101, 100, 46, 13, 10, 0, 0, 0, 0, 85, 170, + 5, 0, 78, 0, 84, 0, 76, 0, 68, 0, 82, 0, 4, 0, 36, 0, + 73, 0, 51, 0, 48, 0, 0, 224, 0, 0, 0, 48, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 140, 200, 142, 216, 193, 224, 4, 250, 139, 224, +251, 102, 15, 183, 6, 11, 0, 102, 15, 182, 30, 13, 0, 102, 247, 227, +102, 163, 78, 2, 102, 139, 14, 64, 0, 128, 249, 0, 15, 143, 14, 0, +246, 217, 102, 184, 1, 0, 0, 0, 102, 211, 224, 235, 8, 144, 102, 161, + 78, 2, 102, 247, 225, 102, 163, 82, 2, 102, 15, 183, 30, 11, 0, 102, + 51, 210, 102, 247, 243, 102, 163, 86, 2, 232, 44, 4, 102, 139, 14, 74, + 2, 102, 137, 14, 34, 2, 102, 3, 14, 82, 2, 102, 137, 14, 38, 2, +102, 3, 14, 82, 2, 102, 137, 14, 42, 2, 102, 3, 14, 82, 2, 102, +137, 14, 58, 2, 102, 3, 14, 82, 2, 102, 137, 14, 66, 2, 102, 184, +144, 0, 0, 0, 102, 139, 14, 34, 2, 232, 65, 9, 102, 11, 192, 15, +132, 22, 254, 102, 163, 46, 2, 102, 184, 160, 0, 0, 0, 102, 139, 14, + 38, 2, 232, 40, 9, 102, 163, 50, 2, 102, 184, 176, 0, 0, 0, 102, +139, 14, 42, 2, 232, 22, 9, 102, 163, 54, 2, 102, 161, 46, 2, 102, + 11, 192, 15, 132, 227, 253, 103, 128, 120, 8, 0, 15, 133, 218, 253, 103, +102, 141, 80, 16, 103, 3, 66, 4, 103, 102, 15, 182, 72, 12, 102, 137, + 14, 94, 2, 103, 102, 139, 72, 8, 102, 137, 14, 90, 2, 102, 161, 90, + 2, 102, 15, 183, 14, 11, 0, 102, 51, 210, 102, 247, 241, 102, 163, 98, + 2, 102, 161, 66, 2, 102, 3, 6, 90, 2, 102, 163, 70, 2, 102, 131, + 62, 50, 2, 0, 15, 132, 25, 0, 102, 131, 62, 54, 2, 0, 15, 132, +135, 253, 102, 139, 30, 54, 2, 30, 7, 102, 139, 62, 70, 2, 232, 177, + 1, 102, 15, 183, 14, 0, 2, 102, 184, 2, 2, 0, 0, 232, 153, 6, +102, 11, 192, 15, 132, 88, 253, 103, 102, 139, 0, 30, 7, 102, 139, 62, + 58, 2, 232, 209, 4, 102, 161, 58, 2, 102, 187, 128, 0, 0, 0, 102, +185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, 203, 0, 102, 11, +192, 15, 132, 42, 253, 103, 102, 15, 183, 88, 12, 102, 129, 227, 255, 0, + 0, 0, 15, 133, 30, 253, 102, 139, 216, 104, 0, 32, 7, 102, 43, 255, +232, 79, 1, 138, 22, 36, 0, 184, 232, 3, 142, 192, 141, 54, 11, 0, + 43, 192, 104, 0, 32, 80, 203, 80, 83, 81, 82, 6, 255, 54, 91, 0, +255, 54, 84, 0, 255, 54, 86, 0, 139, 195, 193, 232, 4, 140, 193, 3, +193, 37, 255, 15, 45, 0, 16, 247, 216, 139, 14, 91, 0, 193, 225, 5, + 81, 59, 193, 118, 2, 139, 193, 80, 193, 232, 5, 163, 91, 0, 232, 61, +252, 88, 89, 43, 200, 118, 11, 140, 194, 3, 208, 142, 194, 184, 0, 16, +235, 222, 143, 6, 86, 0, 143, 6, 84, 0, 143, 6, 91, 0, 7, 90, + 89, 91, 88, 195, 6, 30, 102, 96, 102, 139, 218, 102, 15, 182, 14, 13, + 0, 102, 247, 225, 102, 163, 84, 0, 102, 139, 195, 102, 247, 225, 163, 91, + 0, 139, 223, 131, 227, 15, 140, 192, 102, 193, 239, 4, 3, 199, 80, 7, +232, 116, 255, 102, 97, 144, 31, 7, 195, 103, 3, 64, 20, 103, 102, 131, + 56, 255, 15, 132, 76, 0, 103, 102, 57, 24, 15, 133, 51, 0, 102, 11, +201, 15, 133, 10, 0, 103, 128, 120, 9, 0, 15, 133, 35, 0, 195, 103, + 58, 72, 9, 15, 133, 26, 0, 102, 139, 240, 103, 3, 112, 10, 232, 61, + 5, 102, 81, 30, 7, 102, 139, 250, 243, 167, 102, 89, 15, 133, 1, 0, +195, 103, 102, 131, 120, 4, 0, 15, 132, 7, 0, 103, 102, 3, 64, 4, +235, 171, 102, 43, 192, 195, 102, 139, 243, 232, 18, 5, 103, 102, 3, 0, +103, 247, 64, 12, 2, 0, 15, 133, 52, 0, 103, 102, 141, 80, 16, 103, + 58, 74, 64, 15, 133, 24, 0, 103, 102, 141, 114, 66, 232, 239, 4, 102, + 81, 30, 7, 102, 139, 251, 243, 167, 102, 89, 15, 133, 1, 0, 195, 103, +131, 120, 8, 0, 15, 132, 6, 0, 103, 3, 64, 8, 235, 194, 102, 51, +192, 195, 103, 128, 123, 8, 0, 15, 133, 28, 0, 6, 30, 102, 96, 103, +102, 141, 83, 16, 103, 102, 139, 10, 102, 139, 243, 103, 3, 114, 4, 243, +164, 102, 97, 144, 31, 7, 195, 103, 102, 141, 83, 16, 103, 102, 139, 74, + 8, 102, 65, 102, 43, 192, 232, 1, 0, 195, 6, 30, 102, 96, 103, 128, +123, 8, 1, 15, 132, 3, 0, 233, 127, 251, 102, 131, 249, 0, 15, 133, + 6, 0, 102, 97, 144, 31, 7, 195, 102, 83, 102, 80, 102, 81, 102, 87, + 6, 232, 87, 3, 102, 139, 209, 7, 102, 95, 102, 89, 102, 59, 202, 15, +141, 3, 0, 102, 139, 209, 232, 171, 254, 102, 43, 202, 102, 139, 218, 102, +139, 194, 102, 15, 182, 22, 13, 0, 102, 247, 226, 102, 15, 183, 22, 11, + 0, 102, 247, 226, 102, 3, 248, 102, 88, 102, 3, 195, 102, 91, 235, 170, + 6, 30, 102, 96, 103, 128, 123, 8, 1, 15, 132, 3, 0, 233, 25, 251, +102, 131, 249, 0, 15, 133, 6, 0, 102, 97, 144, 31, 7, 195, 102, 83, +102, 80, 102, 81, 102, 87, 6, 102, 81, 102, 51, 210, 102, 15, 182, 14, + 13, 0, 102, 247, 241, 102, 82, 232, 225, 2, 102, 15, 182, 30, 13, 0, +102, 247, 227, 102, 90, 102, 3, 194, 102, 80, 102, 15, 182, 6, 13, 0, +102, 247, 225, 102, 139, 208, 102, 88, 102, 89, 7, 102, 95, 102, 89, 102, + 59, 202, 15, 141, 3, 0, 102, 139, 209, 102, 163, 84, 0, 137, 22, 91, + 0, 6, 30, 102, 96, 139, 223, 131, 227, 15, 140, 192, 102, 193, 239, 4, + 3, 199, 80, 7, 232, 160, 253, 102, 97, 144, 31, 7, 102, 43, 202, 102, +139, 218, 102, 139, 194, 102, 15, 183, 22, 11, 0, 102, 247, 226, 102, 3, +248, 102, 88, 102, 3, 195, 102, 91, 233, 101, 255, 6, 30, 102, 96, 38, +103, 102, 15, 183, 95, 4, 38, 103, 102, 15, 183, 79, 6, 102, 11, 201, + 15, 132, 101, 250, 102, 3, 223, 102, 131, 195, 2, 102, 129, 199, 254, 1, + 0, 0, 102, 73, 102, 11, 201, 15, 132, 23, 0, 38, 103, 139, 3, 38, +103, 137, 7, 102, 131, 195, 2, 102, 129, 199, 0, 2, 0, 0, 102, 73, +235, 226, 102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 184, 1, 0, + 0, 0, 102, 163, 30, 2, 102, 161, 26, 2, 102, 3, 6, 82, 2, 102, +163, 74, 2, 102, 161, 48, 0, 102, 15, 182, 30, 13, 0, 102, 247, 227, +102, 163, 84, 0, 102, 161, 86, 2, 163, 91, 0, 102, 139, 30, 26, 2, + 30, 7, 232, 242, 252, 102, 15, 183, 251, 232, 111, 255, 102, 161, 26, 2, +102, 187, 32, 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, + 0, 0, 232, 100, 253, 102, 11, 192, 15, 132, 87, 0, 102, 139, 216, 30, + 7, 102, 139, 62, 22, 2, 232, 249, 253, 102, 139, 30, 22, 2, 103, 102, +129, 59, 128, 0, 0, 0, 15, 132, 6, 0, 103, 3, 91, 4, 235, 238, +103, 102, 129, 59, 128, 0, 0, 0, 15, 133, 39, 0, 102, 83, 103, 102, +139, 67, 16, 102, 139, 62, 74, 2, 30, 7, 232, 9, 1, 102, 91, 102, +161, 82, 2, 102, 1, 6, 74, 2, 102, 255, 6, 30, 2, 103, 3, 91, + 4, 235, 205, 102, 97, 144, 31, 7, 195, 102, 139, 208, 102, 139, 14, 30, + 2, 102, 161, 26, 2, 102, 82, 102, 80, 102, 81, 102, 82, 102, 187, 128, + 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, +215, 252, 102, 11, 192, 15, 132, 64, 249, 102, 139, 216, 102, 88, 232, 42, + 1, 102, 11, 192, 15, 132, 7, 0, 102, 91, 102, 91, 102, 91, 195, 102, + 89, 102, 88, 102, 90, 102, 3, 6, 82, 2, 226, 185, 102, 51, 192, 195, + 6, 30, 102, 96, 102, 80, 102, 81, 102, 51, 210, 102, 15, 182, 30, 13, + 0, 102, 247, 243, 102, 82, 232, 144, 255, 102, 11, 192, 15, 132, 249, 248, +102, 15, 182, 30, 13, 0, 102, 247, 227, 102, 90, 102, 3, 194, 102, 163, + 84, 0, 102, 89, 102, 15, 182, 30, 13, 0, 102, 59, 203, 15, 142, 19, + 0, 137, 30, 91, 0, 102, 43, 203, 102, 88, 102, 3, 195, 102, 80, 102, + 81, 235, 20, 144, 102, 88, 102, 3, 193, 102, 80, 137, 14, 91, 0, 102, +185, 0, 0, 0, 0, 102, 81, 6, 102, 87, 139, 223, 131, 227, 15, 140, +192, 102, 193, 239, 4, 3, 199, 80, 7, 232, 155, 251, 102, 95, 7, 102, + 3, 62, 78, 2, 102, 89, 102, 88, 102, 131, 249, 0, 15, 143, 116, 255, +102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 247, 38, 86, 2, 102, +139, 14, 86, 2, 232, 89, 255, 232, 241, 253, 102, 97, 144, 31, 7, 195, + 6, 30, 102, 96, 102, 247, 38, 98, 2, 102, 139, 30, 50, 2, 102, 139, + 14, 98, 2, 30, 7, 102, 139, 62, 66, 2, 232, 35, 253, 232, 203, 253, +102, 97, 144, 31, 7, 195, 102, 80, 102, 83, 102, 81, 102, 139, 30, 70, + 2, 102, 139, 200, 102, 193, 232, 3, 102, 131, 225, 7, 102, 3, 216, 102, +184, 1, 0, 0, 0, 102, 211, 224, 103, 132, 3, 15, 132, 4, 0, 248, +235, 2, 144, 249, 102, 89, 102, 91, 102, 88, 195, 103, 128, 123, 8, 1, + 15, 132, 4, 0, 102, 43, 192, 195, 103, 102, 141, 115, 16, 103, 102, 139, + 86, 8, 102, 59, 194, 15, 135, 11, 0, 103, 102, 139, 22, 102, 59, 194, + 15, 131, 4, 0, 102, 43, 192, 195, 103, 3, 94, 16, 102, 43, 246, 103, +128, 59, 0, 15, 132, 62, 0, 232, 129, 0, 102, 3, 241, 232, 57, 0, +102, 3, 202, 102, 59, 193, 15, 140, 33, 0, 102, 139, 209, 102, 80, 103, +102, 15, 182, 11, 102, 139, 193, 102, 131, 224, 15, 102, 193, 233, 4, 102, + 3, 217, 102, 3, 216, 102, 67, 102, 88, 235, 196, 102, 43, 200, 102, 43, +194, 102, 3, 198, 195, 102, 43, 192, 195, 102, 43, 201, 103, 138, 11, 128, +225, 15, 102, 131, 249, 0, 15, 133, 4, 0, 102, 43, 201, 195, 102, 83, +102, 82, 102, 3, 217, 103, 102, 15, 190, 19, 102, 73, 102, 75, 102, 131, +249, 0, 15, 132, 13, 0, 102, 193, 226, 8, 103, 138, 19, 102, 75, 102, + 73, 235, 235, 102, 139, 202, 102, 90, 102, 91, 195, 102, 83, 102, 82, 102, + 43, 210, 103, 138, 19, 102, 131, 226, 15, 102, 43, 201, 103, 138, 11, 192, +233, 4, 102, 131, 249, 0, 15, 133, 8, 0, 102, 43, 201, 102, 90, 102, + 91, 195, 102, 3, 218, 102, 3, 217, 103, 102, 15, 190, 19, 102, 73, 102, + 75, 102, 131, 249, 0, 15, 132, 13, 0, 102, 193, 226, 8, 103, 138, 19, +102, 75, 102, 73, 235, 235, 102, 139, 202, 102, 90, 102, 91, 195, 102, 11, +201, 15, 133, 1, 0, 195, 102, 81, 102, 86, 103, 131, 62, 97, 15, 140, + 12, 0, 103, 131, 62, 122, 15, 143, 4, 0, 103, 131, 46, 32, 102, 131, +198, 2, 226, 230, 102, 94, 102, 89, 195, 102, 80, 102, 81, 102, 139, 208, +102, 161, 46, 2, 103, 102, 141, 88, 16, 103, 3, 67, 4, 103, 102, 141, + 64, 16, 102, 139, 218, 232, 158, 250, 102, 11, 192, 15, 132, 5, 0, 102, + 89, 102, 89, 195, 102, 161, 50, 2, 102, 11, 192, 15, 133, 8, 0, 102, + 89, 102, 89, 102, 51, 192, 195, 102, 139, 22, 50, 2, 103, 102, 141, 82, + 16, 103, 102, 139, 66, 8, 102, 64, 102, 139, 30, 78, 2, 102, 247, 227, +102, 51, 210, 102, 247, 54, 90, 2, 102, 80, 102, 88, 102, 11, 192, 15, +132, 48, 0, 102, 72, 102, 80, 232, 28, 254, 114, 238, 232, 241, 253, 102, + 90, 102, 89, 102, 91, 102, 83, 102, 81, 102, 82, 102, 161, 66, 2, 103, +102, 141, 64, 24, 232, 47, 250, 102, 11, 192, 116, 206, 102, 89, 102, 89, +102, 89, 195, 102, 89, 102, 89, 102, 51, 192, 195, 6, 30, 102, 96, 102, +139, 54, 66, 2, 102, 185, 32, 0, 0, 0, 102, 247, 193, 3, 0, 0, + 0, 15, 133, 3, 0, 232, 13, 0, 102, 173, 232, 105, 0, 226, 235, 102, + 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 51, 192, 102, 51, 219, 176, + 13, 180, 14, 187, 7, 0, 205, 16, 176, 10, 180, 14, 187, 7, 0, 205, + 16, 102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 11, 201, 15, 133, + 9, 0, 232, 208, 255, 102, 97, 144, 31, 7, 195, 102, 51, 192, 102, 51, +219, 173, 180, 14, 187, 7, 0, 205, 16, 226, 240, 232, 183, 255, 102, 97, +144, 31, 7, 195, 96, 172, 60, 0, 116, 9, 180, 14, 187, 7, 0, 205, + 16, 235, 242, 97, 144, 195, 6, 30, 102, 96, 102, 185, 8, 0, 0, 0, +102, 139, 208, 102, 131, 226, 15, 102, 82, 102, 193, 232, 4, 226, 241, 102, +185, 8, 0, 0, 0, 102, 88, 102, 131, 248, 9, 15, 143, 7, 0, 102, +131, 192, 48, 235, 9, 144, 102, 131, 232, 10, 102, 131, 192, 65, 102, 51, +219, 180, 14, 187, 7, 0, 205, 16, 226, 219, 176, 32, 180, 14, 187, 7, + 0, 205, 16, 102, 97, 144, 31, 7, 232, 96, 0, 195, 6, 30, 102, 96, +102, 190, 22, 13, 0, 0, 232, 79, 245, 102, 97, 144, 31, 7, 195, 6, + 30, 102, 96, 102, 190, 38, 13, 0, 0, 232, 60, 245, 102, 97, 144, 31, + 7, 195, 6, 30, 102, 96, 102, 190, 54, 13, 0, 0, 232, 41, 245, 102, + 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 190, 70, 13, 0, 0, 232, + 22, 245, 102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 190, 86, 13, + 0, 0, 232, 3, 245, 102, 97, 144, 31, 7, 195, 102, 80, 102, 184, 0, + 0, 245, 255, 102, 64, 102, 11, 192, 117, 249, 102, 88, 195, 102, 81, 102, + 80, 102, 184, 5, 0, 0, 0, 30, 7, 102, 139, 249, 232, 71, 252, 102, +139, 193, 102, 91, 102, 83, 102, 15, 183, 14, 12, 2, 102, 186, 14, 2, + 0, 0, 232, 68, 248, 102, 91, 102, 89, 102, 11, 192, 15, 133, 47, 0, +102, 139, 193, 102, 139, 203, 102, 80, 102, 83, 232, 35, 0, 102, 91, 102, + 95, 102, 11, 192, 15, 132, 23, 0, 30, 7, 232, 9, 252, 102, 139, 199, +102, 15, 183, 14, 12, 2, 102, 186, 14, 2, 0, 0, 232, 10, 248, 195, +102, 81, 102, 187, 32, 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, + 0, 0, 0, 0, 232, 242, 247, 102, 11, 192, 15, 132, 82, 0, 102, 139, +216, 30, 7, 102, 139, 62, 22, 2, 232, 135, 248, 30, 7, 102, 139, 30, + 22, 2, 102, 89, 38, 102, 57, 15, 15, 132, 46, 0, 38, 102, 131, 63, +255, 15, 132, 45, 0, 38, 131, 127, 4, 0, 15, 132, 36, 0, 38, 102, + 15, 183, 71, 4, 3, 216, 139, 195, 37, 0, 128, 116, 215, 140, 192, 5, + 0, 8, 142, 192, 129, 227, 255, 127, 235, 202, 38, 102, 139, 71, 16, 195, +102, 89, 102, 51, 192, 195, 68, 101, 98, 117, 103, 32, 80, 111, 105, 110, +116, 32, 48, 13, 10, 0, 68, 101, 98, 117, 103, 32, 80, 111, 105, 110, +116, 32, 49, 13, 10, 0, 68, 101, 98, 117, 103, 32, 80, 111, 105, 110, +116, 32, 50, 13, 10, 0, 68, 101, 98, 117, 103, 32, 80, 111, 105, 110, +116, 32, 51, 13, 10, 0, 68, 101, 98, 117, 103, 32, 80, 111, 105, 110, +116, 32, 52, 13, 10 +}; + diff --git a/ntfsprogs/boot.h b/ntfsprogs/boot.h new file mode 100644 index 00000000..74e9ba12 --- /dev/null +++ b/ntfsprogs/boot.h @@ -0,0 +1,7 @@ +#ifndef _NTFS_BOOT_H_ +#define _NTFS_BOOT_H_ + +extern const unsigned char boot_array[3429]; + +#endif /* _NTFS_BOOT_H_ */ + diff --git a/ntfsprogs/cluster.c b/ntfsprogs/cluster.c new file mode 100644 index 00000000..98fdcf42 --- /dev/null +++ b/ntfsprogs/cluster.c @@ -0,0 +1,119 @@ +/** + * cluster - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2003 Richard Russon + * + * This function will locate the owner of any given sector or cluster range. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 "config.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include + +#include "cluster.h" +#include "utils.h" + +/** + * cluster_find + */ +int cluster_find(ntfs_volume *vol, LCN c_begin, LCN c_end, cluster_cb *cb, void *data) +{ + int j; + int result = -1; + struct mft_search_ctx *m_ctx = NULL; + ntfs_attr_search_ctx *a_ctx = NULL; + ATTR_RECORD *rec; + runlist *runs; + + if (!vol || !cb) + return -1; + + m_ctx = mft_get_search_ctx(vol); + m_ctx->flags_search = FEMR_IN_USE | FEMR_BASE_RECORD; + + while (mft_next_record(m_ctx) == 0) { + + if (!(m_ctx->flags_match & FEMR_BASE_RECORD)) + continue; + + ntfs_log_verbose("Inode: %llu\n", (unsigned long long) + m_ctx->inode->mft_no); + + a_ctx = ntfs_attr_get_search_ctx(m_ctx->inode, NULL); + + while ((rec = find_attribute(AT_UNUSED, a_ctx))) { + + if (!rec->non_resident) { + ntfs_log_verbose("0x%02x skipped - attr is resident\n", a_ctx->attr->type); + continue; + } + + runs = ntfs_mapping_pairs_decompress(vol, a_ctx->attr, NULL); + if (!runs) { + ntfs_log_error("Couldn't read the data runs.\n"); + goto done; + } + + ntfs_log_verbose("\t[0x%02X]\n", a_ctx->attr->type); + + ntfs_log_verbose("\t\tVCN\tLCN\tLength\n"); + for (j = 0; runs[j].length > 0; j++) { + LCN a_begin = runs[j].lcn; + LCN a_end = a_begin + runs[j].length - 1; + + if (a_begin < 0) + continue; // sparse, discontiguous, etc + + ntfs_log_verbose("\t\t%lld\t%lld-%lld (%lld)\n", + (long long)runs[j].vcn, + (long long)runs[j].lcn, + (long long)(runs[j].lcn + + runs[j].length - 1), + (long long)runs[j].length); + //dprint list + + if ((a_begin > c_end) || (a_end < c_begin)) + continue; // before or after search range + + if ((*cb) (m_ctx->inode, a_ctx->attr, runs+j, data)) + return 1; + } + } + + ntfs_attr_put_search_ctx(a_ctx); + a_ctx = NULL; + } + + result = 0; +done: + ntfs_attr_put_search_ctx(a_ctx); + mft_put_search_ctx(m_ctx); + + return result; +} + diff --git a/ntfsprogs/cluster.h b/ntfsprogs/cluster.h new file mode 100644 index 00000000..47d3edd8 --- /dev/null +++ b/ntfsprogs/cluster.h @@ -0,0 +1,39 @@ +/* + * cluster - Part of the Linux-NTFS project. + * + * Copyright (c) 2003 Richard Russon + * + * This function will locate the owner of any given sector or cluster range. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 _CLUSTER_H_ +#define _CLUSTER_H_ + +#include +#include + +typedef struct { + int x; +} ntfs_cluster; + +typedef int (cluster_cb)(ntfs_inode *ino, ATTR_RECORD *attr, runlist_element *run, void *data); + +int cluster_find(ntfs_volume *vol, LCN c_begin, LCN c_end, cluster_cb *cb, void *data); + +#endif /* _CLUSTER_H_ */ + diff --git a/ntfsprogs/mkntfs.8.in b/ntfsprogs/mkntfs.8.in new file mode 100644 index 00000000..38c13021 --- /dev/null +++ b/ntfsprogs/mkntfs.8.in @@ -0,0 +1,300 @@ +.\" Copyright (c) 2001\-2006 Anton Altaparmakov. +.\" Copyright (c) 2005 Richard Russon. +.\" Copyright (c) 2005\-2006 Szabolcs Szakacsits. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH MKNTFS 8 "January 2006" "ntfsprogs @VERSION@" +.SH NAME +mkntfs \- create an NTFS file system +.SH SYNOPSIS +.B mkntfs +[\fIoptions\fR] \fIdevice \fR[\fInumber\-of\-sectors\fR] +.PP +.B mkntfs +[ +.B \-C +] +[ +.B \-c +.I cluster\-size +] +[ +.B \-F +] +[ +.B \-f +] +[ +.B \-H +.I heads +] +[ +.B \-h +] +[ +.B \-I +] +[ +.B \-L +.I volume\-label +] +[ +.B \-l +] +[ +.B \-n +] +[ +.B \-N +.I ntfs\-version +] +[ +.B \-p +.I part\-start\-sect +] +[ +.B \-Q +] +[ +.B \-q +] +[ +.B \-S +.I sectors\-per\-track +] +[ +.B \-s +.I sector\-size +] +[ +.B \-T +] +[ +.B \-V +] +[ +.B \-v +] +[ +.B \-z +.I mft\-zone\-multiplier +] +[ +.B \-\-debug +] +.I device +[ +.I number\-of\-sectors +] +.SH DESCRIPTION +.B mkntfs +is used to create an NTFS file system on a device (usually a disk partition) +or file. +.I device +is the special file corresponding to the device (e.g +.IR /dev/hdXX ). +.I number\-of\-sectors +is the number of blocks on the device. If omitted, +.B mkntfs +automagically figures the file system size. +.SH OPTIONS +Below is a summary of all the options that +.B mkntfs +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.SS Basic options +.TP +\fB\-f\fR, \fB\-\-fast\fR, \fB\-Q\fR, \fB\-\-quick\fR +Perform quick (fast) format. This will skip both zeroing of the volume and bad +sector checking. +.TP +\fB\-L\fR, \fB\-\-label\fR STRING +Set the volume label for the filesystem. +.TP +\fB\-C\fR, \fB\-\-enable\-compression\fR +Enable compression on the volume. +.TP +\fB\-c\fR, \fB\-\-cluster\-size\fR BYTES +Specify the size of clusters in bytes. Valid cluster size values are powers of +two, with at least 256, and at most 65536 bytes per cluster. If omitted, +.B mkntfs +determines the +.I cluster\-size +from the volume size. The value is determined as follows: +.TS +box; +lB lB lB +l l r. +Volume size Default cluster size +0 \- 512MB 512 bytes +512MB \- 1GB 1024 bytes +1GB \- 2GB 2048 bytes +2GB + 4096 bytes +.TE +.sp +.sp +Note that the default cluster size is set to be at least equal to the sector +size as a cluster cannot be smaller than a sector. Also, note that values +greater than 4096 have the side effect that compression is disabled on the +volume (due to limitations in the NTFS compression algorithm currently in use +by Windows). +.TP +\fB\-N\fR, \fB\-\-ntfs\-version\fR STRING +Select the version of NTFS you wish to create. This can be "1.2" +(Windows NT 4.0) or "3.1" (Windows XP, Server 2003 and Vista). +Versions are upwards compatible and Windows 2000, which uses version "3.0", +can read/write both. + +If this option is omitted then version "3.1" is used. +.TP +\fB\-n\fR, \fB\-\-no\-action\fR +Causes +.B mkntfs +to not actually create a filesystem, but display what it would do if it were +to create a filesystem. All steps of the format are carried out except the +actual writing to the device. +.SS Advanced options +.TP +\fB\-s\fR, \fB\-\-sector\-size\fR BYTES +Specify the size of sectors in bytes. Valid sector size values are 256, 512, +1024, 2048 and 4096 bytes per sector. If omitted, +.B mkntfs +attempts to determine the +.I sector\-size +automatically and if that fails a default of 512 bytes per sector is used. +.TP +\fB\-p\fR, \fB\-\-partition\-start\fR SECTOR +Specify the partition start sector. The maximum is 4294967295 (2^32\-1). If +omitted, +.B mkntfs +attempts to determine +.I part\-start\-sect +automatically and if that fails a default of 0 is used. Note that +.I part\-start\-sect +is required for Windows to be able to boot from the created volume. +.TP +\fB\-H\fR, \fB\-\-heads\fR NUM +Specify the number of heads. The maximum is 65535 (0xffff). If omitted, +.B mkntfs +attempts to determine the number of +.I heads +automatically and if that fails a default of 0 is used. Note that +.I heads +is required for Windows to be able to boot from the created volume. +.TP +\fB\-S\fR, \fB\-\-sectors\-per\-track\fR NUM +Specify the number of sectors per track. The maximum is 65535 (0xffff). If +omitted, +.B mkntfs +attempts to determine the number of +.I sectors\-per\-track +automatically and if that fails a default of 0 is used. Note that +.I sectors\-per\-track +is required for Windows to be able to boot from the created volume. +.TP +\fB\-z\fR, \fB\-\-mft\-zone\-multiplier\fR NUM +Set the MFT zone multiplier, which determines the size of the MFT zone to use +on the volume. The MFT zone is the area at the beginning of the volume reserved +for the master file table (MFT), which stores the on disk inodes (MFT records). +It is noteworthy that small files are stored entirely within the inode; +thus, if you expect to use the volume for storing large numbers of very small +files, it is useful to set the zone multiplier to a higher value. Note, that +the MFT zone is resized on the fly as required during operation of the NTFS +driver but choosing a good value will reduce fragmentation. Valid values +are 1, 2, 3 and 4. The values have the following meaning: +.TS +box; +lB lB +lB lB +c l. +MFT zone MFT zone size +multiplier (% of volume size) +1 12.5% (default) +2 25.0% +3 37.5% +4 50.0% +.TE +.sp +.TP +\fB\-T\fR, \fB\-\-zero\-time\fR +Fake the time to be 00:00:00 UTC, Jan 1, 1970 instead of the current system +time. This is only really useful for debugging purposes. +.TP +\fB\-I\fR, \fB\-\-no\-indexing\fR +Disable content indexing on the volume. (This is only meaningful on +Windows 2000 and later. Windows NT 4.0 and earlier ignore this as they do +not implement content indexing at all.) +.TP +\fB\-F\fR, \fB\-\-force\fR +Force +.B mkntfs +to run, even if the specified +.I device +is not a block special device, or appears to be mounted. +.SS Output options +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Quiet execution; only errors are written to stderr, no output to stdout +occurs at all. Useful if +.B mkntfs +is run in a script. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Verbose execution. +.TP +\fB\-\-debug\fR +Really verbose execution; includes the verbose output from the +.B \-v +option as well as additional output useful for debugging +.B mkntfs. +.SS Help options +.TP +\fB\-V\fR, \fB\-\-version\fR +Print the version number of +.B mkntfs +and exit. +.TP +\fB\-l\fR, \fB\-\-license\fR +Print the licensing information of +.B mkntfs +and exit. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.SH BUGS +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +linux\-ntfs\-dev@lists.sourceforge.net +.hy +.SH AUTHORS +.B mkntfs +was written by Anton Altaparmakov, Richard Russon, Erik Sornes and Szabolcs Szakacsits. +.SH AVAILABILITY +.B mkntfs +is part of the +.B ntfsprogs +package and is available from: +.br +.nh +http://www.linux\-ntfs.org/content/view/19/37 +.hy +.sp +The manual pages are available online at: +.br +.nh +http://man.linux-ntfs.org/ +.hy +.SH SEE ALSO +.BR badblocks (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/mkntfs.c b/ntfsprogs/mkntfs.c new file mode 100644 index 00000000..467083b2 --- /dev/null +++ b/ntfsprogs/mkntfs.c @@ -0,0 +1,5203 @@ +/** + * mkntfs - Part of the Linux-NTFS project. + * + * Copyright (c) 2000-2006 Anton Altaparmakov + * Copyright (c) 2001-2005 Richard Russon + * Copyright (c) 2002-2006 Szabolcs Szakacsits + * Copyright (c) 2005 Erik Sornes + * + * This utility will create an NTFS 1.2 or 3.1 volume on a user + * specified (block) device. + * + * Some things (option handling and determination of mount status) have been + * adapted from e2fsprogs-1.19 and lib/ext2fs/ismounted.c and misc/mke2fs.c in + * particular. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS source + * in the file COPYING); if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * WARNING: This program might not work on architectures which do not allow + * unaligned access. For those, the program would need to start using + * get/put_unaligned macros (#include ), but not doing it yet, + * since NTFS really mostly applies to ia32 only, which does allow unaligned + * accesses. We might not actually have a problem though, since the structs are + * defined as being packed so that might be enough for gcc to insert the + * correct code. + * + * If anyone using a non-little endian and/or an aligned access only CPU tries + * this program please let me know whether it works or not! + * + * Anton Altaparmakov + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDARG_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_TIME_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_LIBGEN_H +#include +#endif + +#ifdef HAVE_GETOPT_H +#include +#else + extern char *optarg; + extern int optind; +#endif + +#ifdef HAVE_LINUX_MAJOR_H +# include +# ifndef MAJOR +# define MAJOR(dev) ((dev) >> 8) +# define MINOR(dev) ((dev) & 0xff) +# endif +# ifndef IDE_DISK_MAJOR +# ifndef IDE0_MAJOR +# define IDE0_MAJOR 3 +# define IDE1_MAJOR 22 +# define IDE2_MAJOR 33 +# define IDE3_MAJOR 34 +# define IDE4_MAJOR 56 +# define IDE5_MAJOR 57 +# define IDE6_MAJOR 88 +# define IDE7_MAJOR 89 +# define IDE8_MAJOR 90 +# define IDE9_MAJOR 91 +# endif +# define IDE_DISK_MAJOR(M) \ + ((M) == IDE0_MAJOR || (M) == IDE1_MAJOR || \ + (M) == IDE2_MAJOR || (M) == IDE3_MAJOR || \ + (M) == IDE4_MAJOR || (M) == IDE5_MAJOR || \ + (M) == IDE6_MAJOR || (M) == IDE7_MAJOR || \ + (M) == IDE8_MAJOR || (M) == IDE9_MAJOR) +# endif +# ifndef SCSI_DISK_MAJOR +# ifndef SCSI_DISK0_MAJOR +# define SCSI_DISK0_MAJOR 8 +# define SCSI_DISK1_MAJOR 65 +# define SCSI_DISK7_MAJOR 71 +# endif +# define SCSI_DISK_MAJOR(M) \ + ((M) == SCSI_DISK0_MAJOR || \ + ((M) >= SCSI_DISK1_MAJOR && \ + (M) <= SCSI_DISK7_MAJOR)) +# endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "sd.h" +#include "upcase.h" +#include "boot.h" +#include "attrdef.h" +/* #include "version.h" */ + +#ifdef NO_NTFS_DEVICE_DEFAULT_IO_OPS +#error "No default device io operations! Cannot build mkntfs. \ +You need to run ./configure without the --disable-default-device-io-ops \ +switch if you want to be able to build the NTFS utilities." +#endif + +/* Page size on ia32. Can change to 8192 on Alpha. */ +#define NTFS_PAGE_SIZE 4096 + +static char EXEC_NAME[] = "mkntfs"; + +/** + * global variables + */ +static u8 *g_buf = NULL; +static int g_mft_bitmap_byte_size = 0; +static u8 *g_mft_bitmap = NULL; +static int g_lcn_bitmap_byte_size = 0; +static u8 *g_lcn_bitmap = NULL; +static runlist *g_rl_mft = NULL; +static runlist *g_rl_mft_bmp = NULL; +static runlist *g_rl_mftmirr = NULL; +static runlist *g_rl_logfile = NULL; +static runlist *g_rl_boot = NULL; +static runlist *g_rl_bad = NULL; +static INDEX_ALLOCATION *g_index_block = NULL; +static ntfs_volume *g_vol = NULL; +static int g_mft_size = 0; +static long long g_mft_lcn = 0; /* lcn of $MFT, $DATA attribute */ +static long long g_mftmirr_lcn = 0; /* lcn of $MFTMirr, $DATA */ +static long long g_logfile_lcn = 0; /* lcn of $LogFile, $DATA */ +static int g_logfile_size = 0; /* in bytes, determined from volume_size */ +static long long g_mft_zone_end = 0; /* Determined from volume_size and mft_zone_multiplier, in clusters */ +static long long g_num_bad_blocks = 0; /* Number of bad clusters */ +static long long *g_bad_blocks = NULL; /* Array of bad clusters */ + +/** + * struct mkntfs_options + */ +static struct mkntfs_options { + char *dev_name; /* Name of the device, or file, to use */ + BOOL enable_compression; /* -C, enables compression of all files on the volume by default. */ + BOOL quick_format; /* -f or -Q, fast format, don't zero the volume first. */ + BOOL force; /* -F, force fs creation. */ + long heads; /* -H, number of heads on device */ + BOOL disable_indexing; /* -I, disables indexing of file contents on the volume by default. */ + BOOL no_action; /* -n, do not write to device, only display what would be done. */ + long long part_start_sect; /* -p, start sector of partition on parent device */ + long sector_size; /* -s, in bytes, power of 2, default is 512 bytes. */ + long sectors_per_track; /* -S, number of sectors per track on device */ + BOOL use_epoch_time; /* -T, fake the time to be 00:00:00 UTC, Jan 1, 1970. */ + long mft_zone_multiplier; /* -z, value from 1 to 4. Default is 1. */ + long long num_sectors; /* size of device in sectors */ + long cluster_size; /* -c, format with this cluster-size */ + u8 ver_major; /* -N, ntfs version to create */ + u8 ver_minor; + char *label; /* -L, volume label */ +} opts; + + +/** + * mkntfs_license + */ +static void mkntfs_license(void) +{ + ntfs_log_info("%s", ntfs_gpl); +} + +/** + * mkntfs_usage + */ +static void mkntfs_usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device [number-of-sectors]\n" + "\n" + "Basic options:\n" + " -f, --fast Perform a quick format\n" + " -Q, --quick Perform a quick format\n" + " -L, --label STRING Set the volume label\n" + " -C, --enable-compression Enable compression on the volume\n" + " -c, --cluster-size BYTES Specify the cluster size for the volume\n" + " -I, --no-indexing Disable indexing on the volume\n" + " -n, --no-action Do not write to disk\n" + "\n" + "Advanced options:\n" + " -s, --sector-size BYTES Specify the sector size for the device\n" + " -p, --partition-start SECTOR Specify the partition start sector\n" + " -H, --heads NUM Specify the number of heads\n" + " -S, --sectors-per-track NUM Specify the number of sectors per track\n" + " -z, --mft-zone-multiplier NUM Set the MFT zone multiplier\n" + " -T, --zero-time Fake the time to be 00:00 UTC, Jan 1, 1970\n" + " -N, --ntfs-version VERSION NTFS version: 3.1 (default) or 1.2 (old)\n" + " -F, --force Force execution despite errors\n" + "\n" + "Output options:\n" + " -q, --quiet Quiet execution\n" + " -v, --verbose Verbose execution\n" + " --debug Very verbose execution\n" + "\n" + "Help options:\n" + " -V, --version Display version\n" + " -l, --license Display licensing information\n" + " -h, --help Display this help\n" + "\n", basename(EXEC_NAME)); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * mkntfs_version + */ +static void mkntfs_version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g)\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Create an NTFS volume on a user specified (block) device.\n\n"); + ntfs_log_info("Copyright (c) 2000-2006 Anton Altaparmakov\n"); + ntfs_log_info("Copyright (c) 2001-2005 Richard Russon\n"); + ntfs_log_info("Copyright (c) 2002-2006 Szabolcs Szakacsits\n"); + ntfs_log_info("Copyright (c) 2005 Erik Sornes\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + + +/** + * mkntfs_parse_long + */ +static BOOL mkntfs_parse_long(const char *string, const char *name, long *num) +{ + char *end = NULL; + long tmp; + + if (!string || !name || !num) + return FALSE; + + if (*num >= 0) { + ntfs_log_error("You may only specify the %s once.\n", name); + return FALSE; + } + + tmp = strtol(string, &end, 0); + if (end && *end) { + ntfs_log_error("Cannot understand the %s '%s'.\n", name, string); + return FALSE; + } else { + *num = tmp; + return TRUE; + } +} + +/** + * mkntfs_parse_llong + */ +static BOOL mkntfs_parse_llong(const char *string, const char *name, long long *num) +{ + char *end = NULL; + long long tmp; + + if (!string || !name || !num) + return FALSE; + + if (*num >= 0) { + ntfs_log_error("You may only specify the %s once.\n", name); + return FALSE; + } + + tmp = strtoll(string, &end, 0); + if (end && *end) { + ntfs_log_error("Cannot understand the %s '%s'.\n", name, string); + return FALSE; + } else { + *num = tmp; + return TRUE; + } +} + +/** + * mkntfs_init_options + */ +static void mkntfs_init_options(struct mkntfs_options *opts2) +{ + if (!opts2) + return; + + memset(opts2, 0, sizeof(*opts2)); + + /* Mark all the numeric options as "unset". */ + opts2->cluster_size = -1; + opts2->heads = -1; + opts2->mft_zone_multiplier = -1; + opts2->num_sectors = -1; + opts2->part_start_sect = -1; + opts2->sector_size = -1; + opts2->sectors_per_track = -1; +} + +/** + * mkntfs_parse_options + */ +static BOOL mkntfs_parse_options(int argc, char *argv[], struct mkntfs_options *opts2) +{ + static const char *sopt = "-c:CfFhH:IlL:nN:p:qQs:S:TvVz:"; + static const struct option lopt[] = { + { "cluster-size", required_argument, NULL, 'c' }, + { "debug", no_argument, NULL, 'Z' }, + { "enable-compression", no_argument, NULL, 'C' }, + { "fast", no_argument, NULL, 'f' }, + { "force", no_argument, NULL, 'F' }, + { "heads", required_argument, NULL, 'H' }, + { "help", no_argument, NULL, 'h' }, + { "label", required_argument, NULL, 'L' }, + { "license", no_argument, NULL, 'l' }, + { "mft-zone-multiplier",required_argument, NULL, 'z' }, + { "no-action", no_argument, NULL, 'n' }, + { "no-indexing", no_argument, NULL, 'I' }, + { "ntfs-version", required_argument, NULL, 'N' }, + { "partition-start", required_argument, NULL, 'p' }, + { "quick", no_argument, NULL, 'Q' }, + { "quiet", no_argument, NULL, 'q' }, + { "sector-size", required_argument, NULL, 's' }, + { "sectors-per-track", required_argument, NULL, 'S' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { "zero-time", no_argument, NULL, 'T' }, + { NULL, 0, NULL, 0 } + }; + + int c = -1; + int lic = 0; + int err = 0; + int ver = 0; + + if (!argv || !opts2) { + ntfs_log_error("Internal error: invalid parameters to mkntfs_options.\n"); + return FALSE; + } + + opterr = 0; /* We'll handle the errors, thank you. */ + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A device, or a number of sectors */ + if (!opts2->dev_name) + opts2->dev_name = argv[optind-1]; + else if (!mkntfs_parse_llong(optarg, "number of sectors", &opts2->num_sectors)) + err++; + break; + case 'C': + opts2->enable_compression = TRUE; + break; + case 'c': + if (!mkntfs_parse_long(optarg, "cluster size", &opts2->cluster_size)) + err++; + break; + case 'F': + opts2->force = TRUE; + break; + case 'f': /* fast */ + case 'Q': /* quick */ + opts2->quick_format = TRUE; + break; + case 'H': + if (!mkntfs_parse_long(optarg, "heads", &opts2->heads)) + err++; + break; + case 'h': + err++; /* display help */ + break; + case 'I': + opts2->disable_indexing = TRUE; + break; + case 'L': + if (!opts2->label) { + opts2->label = argv[optind-1]; + } else { + ntfs_log_error("You may only specify the label once.\n"); + err++; + } + break; + case 'l': + lic++; /* display the license */ + break; + case 'n': + opts2->no_action = TRUE; + break; + case 'N': /* ntfs-version */ + if ((opts2->ver_major == 0) && (opts2->ver_minor == 0)) { + if (strcmp(optarg , "1.2") == 0) { + opts2->ver_major = 1; + opts2->ver_minor = 2; +/* + FIXME: version 3.0 was not checked + } else if (strcmp(optarg , "3.0") == 0) { + opts2->ver_major = 3; + opts2->ver_minor = 0; +*/ } else if (strcmp(optarg , "3.1") == 0) { + opts2->ver_major = 3; + opts2->ver_minor = 1; + } else { + ntfs_log_error("NTFS version '%s' is invalid.\n", optarg); + err++; + } + } else { + ntfs_log_error("You may only specify the NTFS version once.\n"); + err++; + } + break; + case 'p': + if (!mkntfs_parse_llong(optarg, "partition start", &opts2->part_start_sect)) + err++; + break; + case 'q': + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_PROGRESS); + break; + case 's': + if (!mkntfs_parse_long(optarg, "sector size", &opts2->sector_size)) + err++; + break; + case 'S': + if (!mkntfs_parse_long(optarg, "sectors per track", &opts2->sectors_per_track)) + err++; + break; + case 'T': + opts2->use_epoch_time = TRUE; + break; + case 'v': + ntfs_log_set_levels(NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_PROGRESS); + break; + case 'V': + ver++; /* display version info */ + break; + case 'Z': /* debug - turn on everything */ + ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_QUIET); + break; + case 'z': + if (!mkntfs_parse_long(optarg, "mft zone multiplier", &opts2->mft_zone_multiplier)) + err++; + break; + default: + if (ntfs_log_parse_option (argv[optind-1])) + break; + if (((optopt == 'c') || (optopt == 'H') || + (optopt == 'L') || (optopt == 'p') || + (optopt == 's') || (optopt == 'S') || + (optopt == 'N') || (optopt == 'z')) && + (!optarg)) { + ntfs_log_error("Option '%s' requires an argument.\n", argv[optind-1]); + } else if (optopt != '?') { + ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); + } + err++; + break; + } + } + + if (!err && !ver && !lic) { + if (opts2->dev_name == NULL) { + if (argc > 1) + ntfs_log_error("You must specify a device.\n"); + err++; + } + } + + if (ver) + mkntfs_version(); + if (lic) + mkntfs_license(); + if (err) + mkntfs_usage(); + + return (!err && !ver && !lic); +} + + +/** + * mkntfs_time + */ +static struct timespec mkntfs_time(void) +{ + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = 0; + if (!opts.use_epoch_time) + ts.tv_sec = time(NULL); + return ts; +} + +/** + * mkntfs_calloc + */ +static void *ntfs_calloc(size_t nmemb, size_t size) +{ + void *p; + + p = calloc(nmemb, size); + if (!p) + ntfs_log_perror("Failed to calloc() %lld bytes", + (long long)nmemb * size); + return p; +} + +/** + * append_to_bad_blocks + */ +static BOOL append_to_bad_blocks(unsigned long long block) +{ + long long *new_buf; + + if (!(g_num_bad_blocks & 15)) { + new_buf = realloc(g_bad_blocks, (g_num_bad_blocks + 16) * + sizeof(long long)); + if (!new_buf) { + ntfs_log_perror("Reallocating memory for bad blocks " + "list failed"); + return FALSE; + } + g_bad_blocks = new_buf; + } + g_bad_blocks[g_num_bad_blocks++] = block; + return TRUE; +} + +/** + * mkntfs_write + */ +static long long mkntfs_write(struct ntfs_device *dev, + const void *b, long long count) +{ + long long bytes_written, total; + int retry; + + if (opts.no_action) + return count; + total = 0LL; + retry = 0; + do { + bytes_written = dev->d_ops->write(dev, b, count); + if (bytes_written == -1LL) { + retry = errno; + ntfs_log_perror("Error writing to %s", dev->d_name); + errno = retry; + return bytes_written; + } else if (!bytes_written) { + retry++; + } else { + count -= bytes_written; + total += bytes_written; + } + } while (count && retry < 3); + if (count) + ntfs_log_error("Failed to complete writing to %s after three retries." + "\n", dev->d_name); + return total; +} + +/** + * ntfs_rlwrite - Write to disk the clusters contained in the runlist @rl + * taking the data from @val. Take @val_len bytes from @val and pad the + * rest with zeroes. + * + * If the @rl specifies a completely sparse file, @val is allowed to be NULL. + * + * @inited_size if not NULL points to an output variable which will contain + * the actual number of bytes written to disk. I.e. this will not include + * sparse bytes for example. + * + * Return the number of bytes written (minus padding) or -1 on error. Errno + * will be set to the error code. + */ +static s64 ntfs_rlwrite(struct ntfs_device *dev, const runlist *rl, + const u8 *val, const s64 val_len, s64 *inited_size) +{ + s64 bytes_written, total, length, delta; + int retry, i; + + if (inited_size) + *inited_size = 0LL; + if (opts.no_action) + return val_len; + total = 0LL; + delta = 0LL; + for (i = 0; rl[i].length; i++) { + length = rl[i].length * g_vol->cluster_size; + /* Don't write sparse runs. */ + if (rl[i].lcn == -1) { + total += length; + if (!val) + continue; + /* TODO: Check that *val is really zero at pos and len. */ + continue; + } + /* + * Break up the write into the real data write and then a write + * of zeroes between the end of the real data and the end of + * the (last) run. + */ + if (total + length > val_len) { + delta = length; + length = val_len - total; + delta -= length; + } + if (dev->d_ops->seek(dev, rl[i].lcn * g_vol->cluster_size, + SEEK_SET) == (off_t)-1) + return -1LL; + retry = 0; + do { + bytes_written = dev->d_ops->write(dev, val + total, + length); + if (bytes_written == -1LL) { + retry = errno; + ntfs_log_perror("Error writing to %s", + dev->d_name); + errno = retry; + return bytes_written; + } + if (bytes_written) { + length -= bytes_written; + total += bytes_written; + if (inited_size) + *inited_size += bytes_written; + } else { + retry++; + } + } while (length && retry < 3); + if (length) { + ntfs_log_error("Failed to complete writing to %s after three " + "retries.\n", dev->d_name); + return total; + } + } + if (delta) { + int eo; + char *b = ntfs_calloc(1, delta); + if (!b) + return -1; + bytes_written = mkntfs_write(dev, b, delta); + eo = errno; + free(b); + errno = eo; + if (bytes_written == -1LL) + return bytes_written; + } + return total; +} + + +/** + * dump_resident_attr_val + */ +static void dump_resident_attr_val(ATTR_TYPES type, char *val, u32 val_len) +{ + const char *don_t_know = "Don't know what to do with this attribute " + "type yet."; + const char *skip = "Skipping display of $%s attribute value.\n"; + const char *todo = "This is still work in progress."; + char *b = NULL; + int i, j; + + switch (type) { + case AT_STANDARD_INFORMATION: + /* TODO */ + printf("%s\n", todo); + return; + case AT_ATTRIBUTE_LIST: + /* TODO */ + printf("%s\n", todo); + return; + case AT_FILE_NAME: + /* TODO */ + printf("%s\n", todo); + return; + case AT_OBJECT_ID: + /* TODO */ + printf("%s\n", todo); + return; + case AT_SECURITY_DESCRIPTOR: + /* TODO */ + printf("%s\n", todo); + return; + case AT_VOLUME_NAME: + printf("Volume name length = %i\n", (unsigned int)val_len); + if (val_len) { + i = ntfs_ucstombs((ntfschar*)val, val_len, &b, 0); + if (i < 0) + printf("Volume name contains non-displayable " + "Unicode characters.\n"); + printf("Volume name = %s\n", b); + free(b); + } + return; + case AT_VOLUME_INFORMATION: +#define VOL_INF(x) ((VOLUME_INFORMATION *)(x)) + printf("NTFS version %i.%i\n", VOL_INF(val)->major_ver, + VOL_INF(val)->minor_ver); + i = VOL_INF(val)->flags; +#undef VOL_INF + printf("Volume flags = 0x%x: ", i); + if (!i) { + printf("NONE\n"); + return; + } + j = 0; + if (i & VOLUME_MODIFIED_BY_CHKDSK) { + printf("VOLUME_MODIFIED_BY_CHKDSK"); + j = 1; + } + if (i & VOLUME_REPAIR_OBJECT_ID) { + if (j) + printf(" | "); + printf("VOLUME_REPAIR_OBJECT_ID"); + j = 1; + } + if (i & VOLUME_DELETE_USN_UNDERWAY) { + if (j) + printf(" | "); + printf("VOLUME_DELETE_USN_UNDERWAY"); + j = 1; + } + if (i & VOLUME_MOUNTED_ON_NT4) { + if (j) + printf(" | "); + printf("VOLUME_MOUNTED_ON_NT4"); + j = 1; + } + if (i & VOLUME_UPGRADE_ON_MOUNT) { + if (j) + printf(" | "); + printf("VOLUME_UPGRADE_ON_MOUNT"); + j = 1; + } + if (i & VOLUME_RESIZE_LOG_FILE) { + if (j) + printf(" | "); + printf("VOLUME_RESIZE_LOG_FILE"); + j = 1; + } + if (i & VOLUME_IS_DIRTY) { + if (j) + printf(" | "); + printf("VOLUME_IS_DIRTY"); + j = 1; + } + printf("\n"); + return; + case AT_DATA: + printf(skip, "DATA"); + return; + case AT_INDEX_ROOT: + /* TODO */ + printf("collation_rule %u\n", le32_to_cpu(((INDEX_ROOT*)val)->collation_rule)); + printf("index.entries_offset %u\n", le32_to_cpu(((INDEX_ROOT*)val)->index.entries_offset)); + printf("index.index_length %u\n", le32_to_cpu(((INDEX_ROOT*)val)->index.index_length)); + printf("%s\n", todo); + return; + case AT_INDEX_ALLOCATION: + /* TODO */ + printf("%s\n", todo); + return; + case AT_BITMAP: + printf(skip, "BITMAP"); + return; + case AT_REPARSE_POINT: + /* TODO */ + printf("%s\n", todo); + return; + case AT_EA_INFORMATION: + /* TODO */ + printf("%s\n", don_t_know); + return; + case AT_EA: + /* TODO */ + printf("%s\n", don_t_know); + return; + case AT_LOGGED_UTILITY_STREAM: + /* TODO */ + printf("%s\n", don_t_know); + return; + default: + i = le32_to_cpu(type); + printf("Cannot display unknown %s defined attribute type 0x%x" + ".\n", (u32)i >= + le32_to_cpu(AT_FIRST_USER_DEFINED_ATTRIBUTE) ? + "user" : "system", i); + } +} + +/** + * dump_resident_attr + */ +static void dump_resident_attr(ATTR_RECORD *a) +{ + int i; + + i = le32_to_cpu(a->value_length); + printf("Attribute value length = %u (0x%x)\n", i, i); + i = le16_to_cpu(a->value_offset); + printf("Attribute value offset = %u (0x%x)\n", i, i); + i = a->resident_flags; + printf("Resident flags = 0x%x: ", i); + if (!i) + printf("NONE\n"); + else if (i & ~RESIDENT_ATTR_IS_INDEXED) + printf("UNKNOWN FLAG(S)\n"); + else + printf("RESIDENT_ATTR_IS_INDEXED\n"); + dump_resident_attr_val(a->type, (char*)a + le16_to_cpu(a->value_offset), + le32_to_cpu(a->value_length)); +} + +/** + * dump_mapping_pairs_array + */ +static void dump_mapping_pairs_array(char *b __attribute__((unused)), + unsigned int max_len __attribute__((unused))) +{ + /* TODO */ + return; +} + +/** + * dump_non_resident_attr + */ +static void dump_non_resident_attr(ATTR_RECORD *a) +{ + s64 l; + int i; + + l = sle64_to_cpu(a->lowest_vcn); + printf("Lowest VCN = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + l = sle64_to_cpu(a->highest_vcn); + printf("Highest VCN = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + printf("Mapping pairs array offset = 0x%x\n", + le16_to_cpu(a->mapping_pairs_offset)); + printf("Compression unit = 0x%x: %sCOMPRESSED\n", a->compression_unit, + a->compression_unit ? "" : "NOT "); + if (sle64_to_cpu(a->lowest_vcn)) + printf("Attribute is not the first extent. The following " + "sizes are meaningless:\n"); + l = sle64_to_cpu(a->allocated_size); + printf("Allocated size = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + l = sle64_to_cpu(a->data_size); + printf("Data size = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + l = sle64_to_cpu(a->initialized_size); + printf("Initialized size = %lli (0x%llx)\n", + (long long)l, (unsigned long long)l); + if (a->flags & ATTR_COMPRESSION_MASK) { + l = sle64_to_cpu(a->compressed_size); + printf("Compressed size = %lli (0x%llx)\n", + (long long)l, (unsigned long long)l); + } + i = le16_to_cpu(a->mapping_pairs_offset); + dump_mapping_pairs_array((char*)a + i, le32_to_cpu(a->length) - i); +} + +/** + * dump_attr_record + */ +static void dump_attr_record(ATTR_RECORD *a) +{ + unsigned int u; + char s[0x200]; + int i; + + printf("-- Beginning dump of attribute record. --\n"); + if (a->type == AT_END) { + printf("Attribute type = 0x%x ($END)\n", + (unsigned int)const_le32_to_cpu(AT_END)); + u = le32_to_cpu(a->length); + printf("Length of resident part = %u (0x%x)\n", u, u); + return; + } + u = le32_to_cpu(a->type); + for (i = 0; g_vol->attrdef[i].type; i++) + if (le32_to_cpu(g_vol->attrdef[i].type) >= u) + break; + if (g_vol->attrdef[i].type) { +#if 0 + printf("type = 0x%x\n", le32_to_cpu(g_vol->attrdef[i].type)); + { char *p = (char*)g_vol->attrdef[i].name; + printf("name = %c%c%c%c%c\n", *p, p[1], p[2], p[3], p[4]); + } +#endif + if (ntfs_ucstombs(g_vol->attrdef[i].name, 0x40, (char**)&s, sizeof(s)) < 0) { + ntfs_log_error("Could not convert Unicode string to single " + "byte string in current locale.\n"); + strncpy(s, "Error converting Unicode string", + sizeof(s)); + } + } else { + strncpy(s, "UNKNOWN_TYPE", sizeof(s)); + } + printf("Attribute type = 0x%x (%s)\n", u, s); + u = le32_to_cpu(a->length); + printf("Length of resident part = %u (0x%x)\n", u, u); + printf("Attribute is %sresident\n", a->non_resident ? "non-" : ""); + printf("Name length = %u unicode characters\n", a->name_length); + printf("Name offset = %u (0x%x)\n", cpu_to_le16(a->name_offset), + cpu_to_le16(a->name_offset)); + u = a->flags; + if (a->name_length) { + if (ntfs_ucstombs((ntfschar*)((char*)a + cpu_to_le16(a->name_offset)), + min(sizeof(s), a->name_length + 1U), (char**)&s, sizeof(s)) < 0) { + ntfs_log_error("Could not convert Unicode string to single " + "byte string in current locale.\n"); + strncpy(s, "Error converting Unicode string", + sizeof(s)); + } + printf("Name = %s\n", s); + } + printf("Attribute flags = 0x%x: ", le16_to_cpu(u)); + if (!u) { + printf("NONE"); + } else { + int first = TRUE; + if (u & ATTR_COMPRESSION_MASK) { + if (u & ATTR_IS_COMPRESSED) { + printf("ATTR_IS_COMPRESSED"); + first = FALSE; + } + if ((u & ATTR_COMPRESSION_MASK) & ~ATTR_IS_COMPRESSED) { + if (!first) + printf(" | "); + else + first = FALSE; + printf("ATTR_UNKNOWN_COMPRESSION"); + } + } + if (u & ATTR_IS_ENCRYPTED) { + if (!first) + printf(" | "); + else + first = FALSE; + printf("ATTR_IS_ENCRYPTED"); + } + if (u & ATTR_IS_SPARSE) { + if (!first) + printf(" | "); + else + first = FALSE; + printf("ATTR_IS_SPARSE"); + } + } + printf("\n"); + printf("Attribute instance = %u\n", le16_to_cpu(a->instance)); + if (a->non_resident) { + dump_non_resident_attr(a); + } else { + dump_resident_attr(a); + } +} + +/** + * dump_mft_record + */ +__attribute__((unused)) +static void dump_mft_record(MFT_RECORD *m) +{ + ATTR_RECORD *a; + unsigned int u; + MFT_REF r; + + printf("-- Beginning dump of mft record. --\n"); + u = le32_to_cpu(m->magic); + printf("Mft record signature (magic) = %c%c%c%c\n", u & 0xff, + u >> 8 & 0xff, u >> 16 & 0xff, u >> 24 & 0xff); + u = le16_to_cpu(m->usa_ofs); + printf("Update sequence array offset = %u (0x%x)\n", u, u); + printf("Update sequence array size = %u\n", le16_to_cpu(m->usa_count)); + printf("$LogFile sequence number (lsn) = %llu\n", + (unsigned long long)le64_to_cpu(m->lsn)); + printf("Sequence number = %u\n", le16_to_cpu(m->sequence_number)); + printf("Reference (hard link) count = %u\n", + le16_to_cpu(m->link_count)); + u = le16_to_cpu(m->attrs_offset); + printf("First attribute offset = %u (0x%x)\n", u, u); + printf("Flags = %u: ", le16_to_cpu(m->flags)); + if (m->flags & MFT_RECORD_IN_USE) + printf("MFT_RECORD_IN_USE"); + else + printf("MFT_RECORD_NOT_IN_USE"); + if (m->flags & MFT_RECORD_IS_DIRECTORY) + printf(" | MFT_RECORD_IS_DIRECTORY"); + printf("\n"); + u = le32_to_cpu(m->bytes_in_use); + printf("Bytes in use = %u (0x%x)\n", u, u); + u = le32_to_cpu(m->bytes_allocated); + printf("Bytes allocated = %u (0x%x)\n", u, u); + r = le64_to_cpu(m->base_mft_record); + printf("Base mft record reference:\n\tMft record number = %llu\n\t" + "Sequence number = %u\n", (unsigned long long)MREF(r), + MSEQNO(r)); + printf("Next attribute instance = %u\n", + le16_to_cpu(m->next_attr_instance)); + a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); + printf("-- Beginning dump of attributes within mft record. --\n"); + while ((char*)a < (char*)m + le32_to_cpu(m->bytes_in_use)) { + dump_attr_record(a); + if (a->type == AT_END) + break; + a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); + }; + printf("-- End of attributes. --\n"); +} + + +/** + * make_room_for_attribute - make room for an attribute inside an mft record + * @m: mft record + * @pos: position at which to make space + * @size: byte size to make available at this position + * + * @pos points to the attribute in front of which we want to make space. + * + * Return 0 on success or -errno on error. Possible error codes are: + * + * -ENOSPC There is not enough space available to complete + * operation. The caller has to make space before calling + * this. + * -EINVAL Can only occur if mkntfs was compiled with -DDEBUG. Means + * the input parameters were faulty. + */ +static int make_room_for_attribute(MFT_RECORD *m, char *pos, const u32 size) +{ + u32 biu; + + if (!size) + return 0; +#ifdef DEBUG + /* + * Rigorous consistency checks. Always return -EINVAL even if more + * appropriate codes exist for simplicity of parsing the return value. + */ + if (size != ((size + 7) & ~7)) { + ntfs_log_error("make_room_for_attribute() received non 8-byte aligned " + "size.\n"); + return -EINVAL; + } + if (!m || !pos) + return -EINVAL; + if (pos < (char*)m || pos + size < (char*)m || + pos > (char*)m + le32_to_cpu(m->bytes_allocated) || + pos + size > (char*)m + le32_to_cpu(m->bytes_allocated)) + return -EINVAL; + /* The -8 is for the attribute terminator. */ + if (pos - (char*)m > (int)le32_to_cpu(m->bytes_in_use) - 8) + return -EINVAL; +#endif + biu = le32_to_cpu(m->bytes_in_use); + /* Do we have enough space? */ + if (biu + size > le32_to_cpu(m->bytes_allocated)) + return -ENOSPC; + /* Move everything after pos to pos + size. */ + memmove(pos + size, pos, biu - (pos - (char*)m)); + /* Update mft record. */ + m->bytes_in_use = cpu_to_le32(biu + size); + return 0; +} + +/** + * deallocate_scattered_clusters + */ +static void deallocate_scattered_clusters(const runlist *rl) +{ + LCN j; + int i; + + if (!rl) + return; + /* Iterate over all runs in the runlist @rl. */ + for (i = 0; rl[i].length; i++) { + /* Skip sparse runs. */ + if (rl[i].lcn == -1LL) + continue; + /* Deallocate the current run. */ + for (j = rl[i].lcn; j < rl[i].lcn + rl[i].length; j++) + ntfs_bit_set(g_lcn_bitmap, j, 0); + } +} + +/** + * allocate_scattered_clusters + * @clusters: Amount of clusters to allocate. + * + * Allocate @clusters and create a runlist of the allocated clusters. + * + * Return the allocated runlist. Caller has to free the runlist when finished + * with it. + * + * On error return NULL and errno is set to the error code. + * + * TODO: We should be returning the size as well, but for mkntfs this is not + * necessary. + */ +static runlist * allocate_scattered_clusters(s64 clusters) +{ + runlist *rl = NULL, *rlt; + VCN vcn = 0LL; + LCN lcn, end, prev_lcn = 0LL; + int rlpos = 0; + int rlsize = 0; + s64 prev_run_len = 0LL; + char bit; + + end = g_vol->nr_clusters; + /* Loop until all clusters are allocated. */ + while (clusters) { + /* Loop in current zone until we run out of free clusters. */ + for (lcn = g_mft_zone_end; lcn < end; lcn++) { + bit = ntfs_bit_get_and_set(g_lcn_bitmap, lcn, 1); + if (bit) + continue; + /* + * Reallocate memory if necessary. Make sure we have + * enough for the terminator entry as well. + */ + if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) { + rlsize += 4096; /* PAGE_SIZE */ + rlt = realloc(rl, rlsize); + if (!rlt) + goto err_end; + rl = rlt; + } + /* Coalesce with previous run if adjacent LCNs. */ + if (prev_lcn == lcn - prev_run_len) { + rl[rlpos - 1].length = ++prev_run_len; + vcn++; + } else { + rl[rlpos].vcn = vcn++; + rl[rlpos].lcn = lcn; + prev_lcn = lcn; + rl[rlpos].length = 1LL; + prev_run_len = 1LL; + rlpos++; + } + /* Done? */ + if (!--clusters) { + /* Add terminator element and return. */ + rl[rlpos].vcn = vcn; + rl[rlpos].lcn = 0LL; + rl[rlpos].length = 0LL; + return rl; + } + + } + /* Switch to next zone, decreasing mft zone by factor 2. */ + end = g_mft_zone_end; + g_mft_zone_end >>= 1; + /* Have we run out of space on the volume? */ + if (g_mft_zone_end <= 0) + goto err_end; + } + return rl; +err_end: + if (rl) { + /* Add terminator element. */ + rl[rlpos].vcn = vcn; + rl[rlpos].lcn = -1LL; + rl[rlpos].length = 0LL; + /* Deallocate all allocated clusters. */ + deallocate_scattered_clusters(rl); + /* Free the runlist. */ + free(rl); + } + return NULL; +} + +/** + * ntfs_attr_find - find (next) attribute in mft record + * @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 or CASE_SENSITIVE (ignored if @name not present) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * You shouldn't need to call this function directly. Use lookup_attr() instead. + * + * ntfs_attr_find() takes a search context @ctx as parameter and searches the + * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an + * attribute of @type, optionally @name and @val. If found, ntfs_attr_find() + * returns 0 and @ctx->attr will point to the found attribute. + * + * If not found, ntfs_attr_find() returns -1, with errno set to ENOENT and + * @ctx->attr will point to the attribute before which the attribute being + * searched for would need to be inserted if such an action were to be desired. + * + * On actual error, ntfs_attr_find() returns -1 with errno set to the error + * code but not to ENOENT. In this case @ctx->attr is undefined and in + * particular do not rely on it not changing. + * + * If @ctx->is_first is TRUE, the search begins with @ctx->attr itself. If it + * is FALSE, the search begins after @ctx->attr. + * + * If @type is AT_UNUSED, return the first found attribute, i.e. one can + * enumerate all attributes by setting @type to AT_UNUSED and then calling + * ntfs_attr_find() repeatedly until it returns -1 with errno set to ENOENT to + * indicate that there are no more entries. During the enumeration, each + * successful call of ntfs_attr_find() will return the next attribute in the + * mft record @ctx->mrec. + * + * If @type is AT_END, seek to the end and return -1 with errno set to ENOENT. + * AT_END is not a valid attribute, its length is zero for example, thus it is + * safer to return error instead of success in this case. This also allows us + * to interoperate cleanly with ntfs_external_attr_find(). + * + * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present + * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, + * match both named and unnamed attributes. + * + * If @ic is IGNORE_CASE, the @name comparison is not case sensitive and + * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record + * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at + * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case + * sensitive. When @name is present, @name_len is the @name length in Unicode + * characters. + * + * If @name is not present (NULL), we assume that the unnamed attribute is + * being searched for. + * + * Finally, the resident attribute value @val is looked for, if present. + * If @val is not present (NULL), @val_len is ignored. + * + * ntfs_attr_find() only searches the specified mft record and it ignores the + * presence of an attribute list attribute (unless it is the one being searched + * for, obviously). If you need to take attribute lists into consideration, use + * ntfs_attr_lookup() instead (see below). This also means that you cannot use + * ntfs_attr_find() to search for extent records of non-resident attributes, as + * extents with lowest_vcn != 0 are usually described by the attribute list + * attribute only. - Note that it is possible that the first extent is only in + * the attribute list while the last extent is in the base mft record, so don't + * rely on being able to find the first extent in the base mft record. + * + * Warning: Never use @val when looking for attribute types which can be + * non-resident as this most likely will result in a crash! + */ +static int mkntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx) +{ + ATTR_RECORD *a; + ntfschar *upcase = g_vol->upcase; + u32 upcase_len = g_vol->upcase_len; + + /* + * Iterate over attributes in mft record starting at @ctx->attr, or the + * attribute following that, if @ctx->is_first is TRUE. + */ + if (ctx->is_first) { + a = ctx->attr; + ctx->is_first = FALSE; + } else { + a = (ATTR_RECORD*)((char*)ctx->attr + + le32_to_cpu(ctx->attr->length)); + } + for (;; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) { + if (p2n(a) < p2n(ctx->mrec) || (char*)a > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_allocated)) + break; + ctx->attr = a; + if (((type != AT_UNUSED) && (le32_to_cpu(a->type) > + le32_to_cpu(type))) || + (a->type == AT_END)) { + errno = ENOENT; + return -1; + } + if (!a->length) + break; + /* If this is an enumeration return this attribute. */ + if (type == AT_UNUSED) + return 0; + if (a->type != type) + continue; + /* + * If @name is AT_UNNAMED we want an unnamed attribute. + * If @name is present, compare the two names. + * Otherwise, match any attribute. + */ + if (name == AT_UNNAMED) { + /* The search failed if the found attribute is named. */ + if (a->name_length) { + errno = ENOENT; + return -1; + } + } else if (name && !ntfs_names_are_equal(name, name_len, + (ntfschar*)((char*)a + le16_to_cpu(a->name_offset)), + a->name_length, ic, upcase, upcase_len)) { + int rc; + + rc = ntfs_names_full_collate(name, name_len, + (ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)), + a->name_length, IGNORE_CASE, + upcase, upcase_len); + /* + * If @name collates before a->name, there is no + * matching attribute. + */ + if (rc == -1) { + errno = ENOENT; + return -1; + } + /* If the strings are not equal, continue search. */ + if (rc) + continue; + rc = ntfs_names_full_collate(name, name_len, + (ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)), + a->name_length, CASE_SENSITIVE, + upcase, upcase_len); + if (rc == -1) { + errno = ENOENT; + return -1; + } + if (rc) + continue; + } + /* + * The names match or @name not present and attribute is + * unnamed. If no @val specified, we have found the attribute + * and are done. + */ + if (!val) { + return 0; + /* @val is present; compare values. */ + } else { + int rc; + + rc = memcmp(val, (char*)a +le16_to_cpu(a->value_offset), + min(val_len, + le32_to_cpu(a->value_length))); + /* + * If @val collates before the current attribute's + * value, there is no matching attribute. + */ + if (!rc) { + u32 avl; + avl = le32_to_cpu(a->value_length); + if (val_len == avl) + return 0; + if (val_len < avl) { + errno = ENOENT; + return -1; + } + } else if (rc < 0) { + errno = ENOENT; + return -1; + } + } + } + ntfs_log_trace("File is corrupt. Run chkdsk.\n"); + errno = EIO; + return -1; +} + +/** + * ntfs_attr_lookup - find an attribute in an ntfs inode + * @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 or CASE_SENSITIVE (ignored if @name not present) + * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must + * be the base mft record and @ctx must have been obtained from a call to + * ntfs_attr_get_search_ctx(). + * + * This function transparently handles attribute lists and @ctx is used to + * continue searches where they were left off at. + * + * If @type is AT_UNUSED, return the first found attribute, i.e. one can + * enumerate all attributes by setting @type to AT_UNUSED and then calling + * ntfs_attr_lookup() repeatedly until it returns -1 with errno set to ENOENT + * to indicate that there are no more entries. During the enumeration, each + * successful call of ntfs_attr_lookup() will return the next attribute, with + * the current attribute being described by the search context @ctx. + * + * If @type is AT_END, seek to the end of the base mft record ignoring the + * attribute list completely and return -1 with errno set to ENOENT. AT_END is + * not a valid attribute, its length is zero for example, thus it is safer to + * return error instead of success in this case. It should never be needed to + * do this, but we implement the functionality because it allows for simpler + * code inside ntfs_external_attr_find(). + * + * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present + * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, + * match both named and unnamed attributes. + * + * After finishing with the attribute/mft record you need to call + * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any + * mapped extent inodes, etc). + * + * Return 0 if the search was successful and -1 if not, with errno set to the + * error code. + * + * On success, @ctx->attr is the found attribute, it is in mft record + * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this + * attribute with @ctx->base_* being the base mft record to which @ctx->attr + * belongs. If no attribute list attribute is present @ctx->al_entry and + * @ctx->base_* are NULL. + * + * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the + * attribute which collates just after the attribute being searched for in the + * base ntfs inode, i.e. if one wants to add the attribute to the mft record + * this is the correct place to insert it into, and if there is not enough + * space, the attribute should be placed in an extent mft record. + * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list + * at which the new attribute's attribute list entry should be inserted. The + * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. + * The only exception to this is when @type is AT_END, in which case + * @ctx->al_entry is set to NULL also (see above). + * + * The following error codes are defined: + * ENOENT Attribute not found, not an error as such. + * EINVAL Invalid arguments. + * EIO I/O error or corrupt data structures found. + * ENOMEM Not enough memory to allocate necessary buffers. + */ +static int mkntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const VCN lowest_vcn __attribute__((unused)), const u8 *val, + const u32 val_len, ntfs_attr_search_ctx *ctx) +{ + ntfs_inode *base_ni; + + if (!ctx || !ctx->mrec || !ctx->attr) { + errno = EINVAL; + return -1; + } + if (ctx->base_ntfs_ino) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + if (!base_ni || !NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST) + return mkntfs_attr_find(type, name, name_len, ic, val, val_len, + ctx); + errno = EOPNOTSUPP; + return -1; +} + +/** + * insert_positioned_attr_in_mft_record + * Create a non-resident attribute with a predefined on disk location + * specified by the runlist @rl. The clusters specified by @rl are assumed to + * be allocated already. + * + * Return 0 on success and -errno on error. + */ +static int insert_positioned_attr_in_mft_record(MFT_RECORD *m, + const ATTR_TYPES type, const char *name, u32 name_len, + const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, + const runlist *rl, const u8 *val, const s64 val_len) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + u16 hdr_size; + int asize, mpa_size, err, i; + s64 bw = 0, inited_size; + VCN highest_vcn; + ntfschar *uname = NULL; + int uname_len = 0; + /* + if (base record) + attr_lookup(); + else + */ + + uname = ntfs_str2ucs(name, &uname_len); + if (!uname) + return -errno; + + /* Check if the attribute is already there. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to allocate attribute search context.\n"); + err = -ENOMEM; + goto err_out; + } + if (ic == IGNORE_CASE) { + ntfs_log_error("FIXME: Hit unimplemented code path #1.\n"); + err = -EOPNOTSUPP; + goto err_out; + } + if (!mkntfs_attr_lookup(type, uname, uname_len, ic, 0, NULL, 0, ctx)) { + err = -EEXIST; + goto err_out; + } + if (errno != ENOENT) { + ntfs_log_error("Corrupt inode.\n"); + err = -errno; + goto err_out; + } + a = ctx->attr; + if (flags & ATTR_COMPRESSION_MASK) { + ntfs_log_error("Compressed attributes not supported yet.\n"); + /* FIXME: Compress attribute into a temporary buffer, set */ + /* val accordingly and save the compressed size. */ + err = -EOPNOTSUPP; + goto err_out; + } + if (flags & (ATTR_IS_ENCRYPTED || ATTR_IS_SPARSE)) { + ntfs_log_error("Encrypted/sparse attributes not supported yet.\n"); + err = -EOPNOTSUPP; + goto err_out; + } + if (flags & ATTR_COMPRESSION_MASK) { + hdr_size = 72; + /* FIXME: This compression stuff is all wrong. Never mind for */ + /* now. (AIA) */ + if (val_len) + mpa_size = 0; /* get_size_for_compressed_mapping_pairs(rl); */ + else + mpa_size = 0; + } else { + hdr_size = 64; + if (val_len) { + mpa_size = ntfs_get_size_for_mapping_pairs(g_vol, rl, 0, INT_MAX); + if (mpa_size < 0) { + err = -errno; + ntfs_log_error("Failed to get size for mapping " + "pairs.\n"); + goto err_out; + } + } else { + mpa_size = 0; + } + } + /* Mapping pairs array and next attribute must be 8-byte aligned. */ + asize = (((int)hdr_size + ((name_len + 7) & ~7) + mpa_size) + 7) & ~7; + /* Get the highest vcn. */ + for (i = 0, highest_vcn = 0LL; rl[i].length; i++) + highest_vcn += rl[i].length; + /* Does the value fit inside the allocated size? */ + if (highest_vcn * g_vol->cluster_size < val_len) { + ntfs_log_error("BUG: Allocated size is smaller than data size!\n"); + err = -EINVAL; + goto err_out; + } + err = make_room_for_attribute(m, (char*)a, asize); + if (err == -ENOSPC) { + /* + * FIXME: Make space! (AIA) + * can we make it non-resident? if yes, do that. + * does it fit now? yes -> do it. + * m's $DATA or $BITMAP+$INDEX_ALLOCATION resident? + * yes -> make non-resident + * does it fit now? yes -> do it. + * make all attributes non-resident + * does it fit now? yes -> do it. + * m is a base record? yes -> allocate extension record + * does the new attribute fit in there? yes -> do it. + * split up runlist into extents and place each in an extension + * record. + * FIXME: the check for needing extension records should be + * earlier on as it is very quick: asize > m->bytes_allocated? + */ + err = -EOPNOTSUPP; + goto err_out; +#ifdef DEBUG + } else if (err == -EINVAL) { + ntfs_log_error("BUG(): in insert_positioned_attribute_in_mft_" + "record(): make_room_for_attribute() returned " + "error: EINVAL!\n"); + goto err_out; +#endif + } + a->type = type; + a->length = cpu_to_le32(asize); + a->non_resident = 1; + a->name_length = name_len; + a->name_offset = cpu_to_le16(hdr_size); + a->flags = flags; + a->instance = m->next_attr_instance; + m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) + + 1) & 0xffff); + a->lowest_vcn = cpu_to_le64(0); + a->highest_vcn = cpu_to_le64(highest_vcn - 1LL); + a->mapping_pairs_offset = cpu_to_le16(hdr_size + ((name_len + 7) & ~7)); + memset(a->reserved1, 0, sizeof(a->reserved1)); + /* FIXME: Allocated size depends on compression. */ + a->allocated_size = cpu_to_le64(highest_vcn * g_vol->cluster_size); + a->data_size = cpu_to_le64(val_len); + if (name_len) + memcpy((char*)a + hdr_size, uname, name_len << 1); + if (flags & ATTR_COMPRESSION_MASK) { + if (flags & ATTR_COMPRESSION_MASK & ~ATTR_IS_COMPRESSED) { + ntfs_log_error("Unknown compression format. Reverting to " + "standard compression.\n"); + a->flags &= ~ATTR_COMPRESSION_MASK; + a->flags |= ATTR_IS_COMPRESSED; + } + a->compression_unit = 4; + inited_size = val_len; + /* FIXME: Set the compressed size. */ + a->compressed_size = cpu_to_le64(0); + /* FIXME: Write out the compressed data. */ + /* FIXME: err = build_mapping_pairs_compressed(); */ + err = -EOPNOTSUPP; + } else { + a->compression_unit = 0; + bw = ntfs_rlwrite(g_vol->dev, rl, val, val_len, &inited_size); + if (bw != val_len) { + ntfs_log_error("Error writing non-resident attribute value.\n"); + return -errno; + } + err = ntfs_mapping_pairs_build(g_vol, (u8*)a + hdr_size + + ((name_len + 7) & ~7), mpa_size, rl, 0, NULL); + } + a->initialized_size = cpu_to_le64(inited_size); + if (err < 0 || bw != val_len) { + /* FIXME: Handle error. */ + /* deallocate clusters */ + /* remove attribute */ + if (err >= 0) + err = -EIO; + ntfs_log_error("insert_positioned_attr_in_mft_record failed with " + "error %i.\n", err < 0 ? err : (int)bw); + } +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + ntfs_ucsfree(uname); + return err; +} + +/** + * insert_non_resident_attr_in_mft_record + * + * Return 0 on success and -errno on error. + */ +static int insert_non_resident_attr_in_mft_record(MFT_RECORD *m, + const ATTR_TYPES type, const char *name, u32 name_len, + const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, + const u8 *val, const s64 val_len) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + u16 hdr_size; + int asize, mpa_size, err, i; + runlist *rl = NULL; + s64 bw = 0; + ntfschar *uname = NULL; + int uname_len = 0; + /* + if (base record) + attr_lookup(); + else + */ + + uname = ntfs_str2ucs(name, &uname_len); + if (!uname) + return -errno; + + /* Check if the attribute is already there. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to allocate attribute search context.\n"); + err = -ENOMEM; + goto err_out; + } + if (ic == IGNORE_CASE) { + ntfs_log_error("FIXME: Hit unimplemented code path #2.\n"); + err = -EOPNOTSUPP; + goto err_out; + } + if (!mkntfs_attr_lookup(type, uname, uname_len, ic, 0, NULL, 0, ctx)) { + err = -EEXIST; + goto err_out; + } + if (errno != ENOENT) { + ntfs_log_error("Corrupt inode.\n"); + err = -errno; + goto err_out; + } + a = ctx->attr; + if (flags & ATTR_COMPRESSION_MASK) { + ntfs_log_error("Compressed attributes not supported yet.\n"); + /* FIXME: Compress attribute into a temporary buffer, set */ + /* val accordingly and save the compressed size. */ + err = -EOPNOTSUPP; + goto err_out; + } + if (flags & (ATTR_IS_ENCRYPTED || ATTR_IS_SPARSE)) { + ntfs_log_error("Encrypted/sparse attributes not supported yet.\n"); + err = -EOPNOTSUPP; + goto err_out; + } + if (val_len) { + rl = allocate_scattered_clusters((val_len + + g_vol->cluster_size - 1) / g_vol->cluster_size); + if (!rl) { + err = -errno; + ntfs_log_perror("Failed to allocate scattered clusters"); + goto err_out; + } + } else { + rl = NULL; + } + if (flags & ATTR_COMPRESSION_MASK) { + hdr_size = 72; + /* FIXME: This compression stuff is all wrong. Never mind for */ + /* now. (AIA) */ + if (val_len) + mpa_size = 0; /* get_size_for_compressed_mapping_pairs(rl); */ + else + mpa_size = 0; + } else { + hdr_size = 64; + if (val_len) { + mpa_size = ntfs_get_size_for_mapping_pairs(g_vol, rl, 0, INT_MAX); + if (mpa_size < 0) { + err = -errno; + ntfs_log_error("Failed to get size for mapping " + "pairs.\n"); + goto err_out; + } + } else { + mpa_size = 0; + } + } + /* Mapping pairs array and next attribute must be 8-byte aligned. */ + asize = (((int)hdr_size + ((name_len + 7) & ~7) + mpa_size) + 7) & ~7; + err = make_room_for_attribute(m, (char*)a, asize); + if (err == -ENOSPC) { + /* + * FIXME: Make space! (AIA) + * can we make it non-resident? if yes, do that. + * does it fit now? yes -> do it. + * m's $DATA or $BITMAP+$INDEX_ALLOCATION resident? + * yes -> make non-resident + * does it fit now? yes -> do it. + * make all attributes non-resident + * does it fit now? yes -> do it. + * m is a base record? yes -> allocate extension record + * does the new attribute fit in there? yes -> do it. + * split up runlist into extents and place each in an extension + * record. + * FIXME: the check for needing extension records should be + * earlier on as it is very quick: asize > m->bytes_allocated? + */ + err = -EOPNOTSUPP; + goto err_out; +#ifdef DEBUG + } else if (err == -EINVAL) { + ntfs_log_error("BUG(): in insert_non_resident_attribute_in_" + "mft_record(): make_room_for_attribute() " + "returned error: EINVAL!\n"); + goto err_out; +#endif + } + a->type = type; + a->length = cpu_to_le32(asize); + a->non_resident = 1; + a->name_length = name_len; + a->name_offset = cpu_to_le16(hdr_size); + a->flags = flags; + a->instance = m->next_attr_instance; + m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) + + 1) & 0xffff); + a->lowest_vcn = cpu_to_le64(0); + for (i = 0; rl[i].length; i++) + ; + a->highest_vcn = cpu_to_le64(rl[i].vcn - 1); + a->mapping_pairs_offset = cpu_to_le16(hdr_size + ((name_len + 7) & ~7)); + memset(a->reserved1, 0, sizeof(a->reserved1)); + /* FIXME: Allocated size depends on compression. */ + a->allocated_size = cpu_to_le64((val_len + (g_vol->cluster_size - 1)) & + ~(g_vol->cluster_size - 1)); + a->data_size = cpu_to_le64(val_len); + a->initialized_size = cpu_to_le64(val_len); + if (name_len) + memcpy((char*)a + hdr_size, uname, name_len << 1); + if (flags & ATTR_COMPRESSION_MASK) { + if (flags & ATTR_COMPRESSION_MASK & ~ATTR_IS_COMPRESSED) { + ntfs_log_error("Unknown compression format. Reverting to " + "standard compression.\n"); + a->flags &= ~ATTR_COMPRESSION_MASK; + a->flags |= ATTR_IS_COMPRESSED; + } + a->compression_unit = 4; + /* FIXME: Set the compressed size. */ + a->compressed_size = cpu_to_le64(0); + /* FIXME: Write out the compressed data. */ + /* FIXME: err = build_mapping_pairs_compressed(); */ + err = -EOPNOTSUPP; + } else { + a->compression_unit = 0; + bw = ntfs_rlwrite(g_vol->dev, rl, val, val_len, NULL); + if (bw != val_len) { + ntfs_log_error("Error writing non-resident attribute value.\n"); + return -errno; + } + err = ntfs_mapping_pairs_build(g_vol, (u8*)a + hdr_size + + ((name_len + 7) & ~7), mpa_size, rl, 0, NULL); + } + if (err < 0 || bw != val_len) { + /* FIXME: Handle error. */ + /* deallocate clusters */ + /* remove attribute */ + if (err >= 0) + err = -EIO; + ntfs_log_error("insert_non_resident_attr_in_mft_record failed with " + "error %lld.\n", (long long) (err < 0 ? err : bw)); + } +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + ntfs_ucsfree(uname); + free(rl); + return err; +} + +/** + * insert_resident_attr_in_mft_record + * + * Return 0 on success and -errno on error. + */ +static int insert_resident_attr_in_mft_record(MFT_RECORD *m, + const ATTR_TYPES type, const char *name, u32 name_len, + const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, + const RESIDENT_ATTR_FLAGS res_flags, + const u8 *val, const u32 val_len) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + int asize, err; + ntfschar *uname = NULL; + int uname_len = 0; + /* + if (base record) + mkntfs_attr_lookup(); + else + */ + + uname = ntfs_str2ucs(name, &uname_len); + if (!uname) + return -errno; + + /* Check if the attribute is already there. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to allocate attribute search context.\n"); + err = -ENOMEM; + goto err_out; + } + if (ic == IGNORE_CASE) { + ntfs_log_error("FIXME: Hit unimplemented code path #3.\n"); + err = -EOPNOTSUPP; + goto err_out; + } + if (!mkntfs_attr_lookup(type, uname, uname_len, ic, 0, val, val_len, + ctx)) { + err = -EEXIST; + goto err_out; + } + if (errno != ENOENT) { + ntfs_log_error("Corrupt inode.\n"); + err = -errno; + goto err_out; + } + a = ctx->attr; + /* sizeof(resident attribute record header) == 24 */ + asize = ((24 + ((name_len + 7) & ~7) + val_len) + 7) & ~7; + err = make_room_for_attribute(m, (char*)a, asize); + if (err == -ENOSPC) { + /* + * FIXME: Make space! (AIA) + * can we make it non-resident? if yes, do that. + * does it fit now? yes -> do it. + * m's $DATA or $BITMAP+$INDEX_ALLOCATION resident? + * yes -> make non-resident + * does it fit now? yes -> do it. + * make all attributes non-resident + * does it fit now? yes -> do it. + * m is a base record? yes -> allocate extension record + * does the new attribute fit in there? yes -> do it. + * split up runlist into extents and place each in an extension + * record. + * FIXME: the check for needing extension records should be + * earlier on as it is very quick: asize > m->bytes_allocated? + */ + err = -EOPNOTSUPP; + goto err_out; + } +#ifdef DEBUG + if (err == -EINVAL) { + ntfs_log_error("BUG(): in insert_resident_attribute_in_mft_" + "record(): make_room_for_attribute() returned " + "error: EINVAL!\n"); + goto err_out; + } +#endif + a->type = type; + a->length = cpu_to_le32(asize); + a->non_resident = 0; + a->name_length = name_len; + a->name_offset = const_cpu_to_le16(24); + a->flags = flags; + a->instance = m->next_attr_instance; + m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) + + 1) & 0xffff); + a->value_length = cpu_to_le32(val_len); + a->value_offset = cpu_to_le16(24 + ((name_len + 7) & ~7)); + a->resident_flags = res_flags; + a->reservedR = 0; + if (name_len) + memcpy((char*)a + 24, uname, name_len << 1); + if (val_len) + memcpy((char*)a + le16_to_cpu(a->value_offset), val, val_len); +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + ntfs_ucsfree(uname); + return err; +} + + +/** + * add_attr_std_info + * + * Return 0 on success or -errno on error. + */ +static int add_attr_std_info(MFT_RECORD *m, const FILE_ATTR_FLAGS flags, + u32 security_id) +{ + STANDARD_INFORMATION si; + int err, sd_size; + + sd_size = 48; + + si.creation_time = timespec2ntfs(mkntfs_time()); + si.last_data_change_time = si.creation_time; + si.last_mft_change_time = si.creation_time; + si.last_access_time = si.creation_time; + si.file_attributes = flags; /* already LE */ + if (g_vol->major_ver < 3) { + memset(&si.reserved12, 0, sizeof(si.reserved12)); + } else { + si.maximum_versions = cpu_to_le32(0); + si.version_number = cpu_to_le32(0); + si.class_id = cpu_to_le32(0); + si.security_id = security_id; + if (si.security_id != 0) + sd_size = 72; + /* FIXME: $Quota support... */ + si.owner_id = cpu_to_le32(0); + si.quota_charged = cpu_to_le64(0ULL); + /* FIXME: $UsnJrnl support... Not needed on fresh w2k3-volume */ + si.usn = cpu_to_le64(0ULL); + } + /* NTFS 1.2: size of si = 48, NTFS 3.[01]: size of si = 72 */ + err = insert_resident_attr_in_mft_record(m, AT_STANDARD_INFORMATION, + NULL, 0, 0, 0, 0, (u8*)&si, sd_size); + if (err < 0) + ntfs_log_perror("add_attr_std_info failed"); + return err; +} + +/** + * add_attr_file_name + * + * Return 0 on success or -errno on error. + */ +static int add_attr_file_name(MFT_RECORD *m, const MFT_REF parent_dir, + const s64 allocated_size, const s64 data_size, + const FILE_ATTR_FLAGS flags, const u16 packed_ea_size, + const u32 reparse_point_tag, const char *file_name, + const FILE_NAME_TYPE_FLAGS file_name_type) +{ + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *si; + FILE_NAME_ATTR *fn; + int i, fn_size; + ntfschar *uname; + + /* Check if the attribute is already there. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to allocate attribute search context.\n"); + return -ENOMEM; + } + if (mkntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, 0, + ctx)) { + int eo = errno; + ntfs_log_error("BUG: Standard information attribute not present in " + "file record\n"); + ntfs_attr_put_search_ctx(ctx); + return -eo; + } + si = (STANDARD_INFORMATION*)((char*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + i = (strlen(file_name) + 1) * sizeof(ntfschar); + fn_size = sizeof(FILE_NAME_ATTR) + i; + fn = malloc(fn_size); + if (!fn) { + ntfs_attr_put_search_ctx(ctx); + return -errno; + } + fn->parent_directory = parent_dir; + + fn->creation_time = si->creation_time; + fn->last_data_change_time = si->last_data_change_time; + fn->last_mft_change_time = si->last_mft_change_time; + fn->last_access_time = si->last_access_time; + ntfs_attr_put_search_ctx(ctx); + + fn->allocated_size = cpu_to_le64(allocated_size); + fn->data_size = cpu_to_le64(data_size); + fn->file_attributes = flags; + /* These are in a union so can't have both. */ + if (packed_ea_size && reparse_point_tag) { + free(fn); + return -EINVAL; + } + if (packed_ea_size) { + fn->packed_ea_size = cpu_to_le16(packed_ea_size); + fn->reserved = cpu_to_le16(0); + } else { + fn->reparse_point_tag = cpu_to_le32(reparse_point_tag); + } + fn->file_name_type = file_name_type; + uname = fn->file_name; + i = ntfs_mbstoucs_libntfscompat(file_name, &uname, i); + if (i < 1) { + free(fn); + return -EINVAL; + } + if (i > 0xff) { + free(fn); + return -ENAMETOOLONG; + } + /* No terminating null in file names. */ + fn->file_name_length = i; + fn_size = sizeof(FILE_NAME_ATTR) + i * sizeof(ntfschar); + i = insert_resident_attr_in_mft_record(m, AT_FILE_NAME, NULL, 0, 0, + 0, RESIDENT_ATTR_IS_INDEXED, (u8*)fn, fn_size); + free(fn); + if (i < 0) + ntfs_log_error("add_attr_file_name failed: %s\n", strerror(-i)); + return i; +} + +/** + * add_attr_sd + * + * Create the security descriptor attribute adding the security descriptor @sd + * of length @sd_len to the mft record @m. + * + * Return 0 on success or -errno on error. + */ +static int add_attr_sd(MFT_RECORD *m, const u8 *sd, const s64 sd_len) +{ + int err; + + /* Does it fit? NO: create non-resident. YES: create resident. */ + if (le32_to_cpu(m->bytes_in_use) + 24 + sd_len > + le32_to_cpu(m->bytes_allocated)) + err = insert_non_resident_attr_in_mft_record(m, + AT_SECURITY_DESCRIPTOR, NULL, 0, 0, 0, sd, + sd_len); + else + err = insert_resident_attr_in_mft_record(m, + AT_SECURITY_DESCRIPTOR, NULL, 0, 0, 0, 0, sd, + sd_len); + if (err < 0) + ntfs_log_error("add_attr_sd failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_data + * + * Return 0 on success or -errno on error. + */ +static int add_attr_data(MFT_RECORD *m, const char *name, const u32 name_len, + const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, + const u8 *val, const s64 val_len) +{ + int err; + + /* + * Does it fit? NO: create non-resident. YES: create resident. + * + * FIXME: Introduced arbitrary limit of mft record allocated size - 512. + * This is to get around the problem that if $Bitmap/$DATA becomes too + * big, but is just small enough to be resident, we would make it + * resident, and later run out of space when creating the other + * attributes and this would cause us to abort as making resident + * attributes non-resident is not supported yet. + * The proper fix is to support making resident attribute non-resident. + */ + if (le32_to_cpu(m->bytes_in_use) + 24 + val_len > + min(le32_to_cpu(m->bytes_allocated), + le32_to_cpu(m->bytes_allocated) - 512)) + err = insert_non_resident_attr_in_mft_record(m, AT_DATA, name, + name_len, ic, flags, val, val_len); + else + err = insert_resident_attr_in_mft_record(m, AT_DATA, name, + name_len, ic, flags, 0, val, val_len); + + if (err < 0) + ntfs_log_error("add_attr_data failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_data_positioned + * + * Create a non-resident data attribute with a predefined on disk location + * specified by the runlist @rl. The clusters specified by @rl are assumed to + * be allocated already. + * + * Return 0 on success or -errno on error. + */ +static int add_attr_data_positioned(MFT_RECORD *m, const char *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const ATTR_FLAGS flags, const runlist *rl, + const u8 *val, const s64 val_len) +{ + int err; + + err = insert_positioned_attr_in_mft_record(m, AT_DATA, name, name_len, + ic, flags, rl, val, val_len); + if (err < 0) + ntfs_log_error("add_attr_data_positioned failed: %s\n", + strerror(-err)); + return err; +} + +/** + * add_attr_vol_name + * + * Create volume name attribute specifying the volume name @vol_name as a null + * terminated char string of length @vol_name_len (number of characters not + * including the terminating null), which is converted internally to a little + * endian ntfschar string. The name is at least 1 character long and at most + * 0xff characters long (not counting the terminating null). + * + * Return 0 on success or -errno on error. + */ +static int add_attr_vol_name(MFT_RECORD *m, const char *vol_name, + const int vol_name_len __attribute__((unused))) +{ + ntfschar *uname = NULL; + int uname_len = 0; + int i; + + if (vol_name) { + uname_len = ntfs_mbstoucs_libntfscompat(vol_name, &uname, 0); + if (uname_len < 0) + return -errno; + if (uname_len > 0xff) { + free(uname); + return -ENAMETOOLONG; + } + } + i = insert_resident_attr_in_mft_record(m, AT_VOLUME_NAME, NULL, 0, 0, + 0, 0, (u8*)uname, uname_len*sizeof(ntfschar)); + free(uname); + if (i < 0) + ntfs_log_error("add_attr_vol_name failed: %s\n", strerror(-i)); + return i; +} + +/** + * add_attr_vol_info + * + * Return 0 on success or -errno on error. + */ +static int add_attr_vol_info(MFT_RECORD *m, const VOLUME_FLAGS flags, + const u8 major_ver, const u8 minor_ver) +{ + VOLUME_INFORMATION vi; + int err; + + memset(&vi, 0, sizeof(vi)); + vi.major_ver = major_ver; + vi.minor_ver = minor_ver; + vi.flags = flags & VOLUME_FLAGS_MASK; + err = insert_resident_attr_in_mft_record(m, AT_VOLUME_INFORMATION, NULL, + 0, 0, 0, 0, (u8*)&vi, sizeof(vi)); + if (err < 0) + ntfs_log_error("add_attr_vol_info failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_index_root + * + * Return 0 on success or -errno on error. + */ +static int add_attr_index_root(MFT_RECORD *m, const char *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const ATTR_TYPES indexed_attr_type, + const COLLATION_RULES collation_rule, + const u32 index_block_size) +{ + INDEX_ROOT *r; + INDEX_ENTRY_HEADER *e; + int err, val_len; + + val_len = sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY_HEADER); + r = malloc(val_len); + if (!r) + return -errno; + r->type = (indexed_attr_type == AT_FILE_NAME) ? AT_FILE_NAME : 0; + if (indexed_attr_type == AT_FILE_NAME && + collation_rule != COLLATION_FILE_NAME) { + free(r); + ntfs_log_error("add_attr_index_root: indexed attribute is $FILE_NAME " + "but collation rule is not COLLATION_FILE_NAME.\n"); + return -EINVAL; + } + r->collation_rule = collation_rule; + r->index_block_size = cpu_to_le32(index_block_size); + if (index_block_size >= g_vol->cluster_size) { + if (index_block_size % g_vol->cluster_size) { + ntfs_log_error("add_attr_index_root: index block size is not " + "a multiple of the cluster size.\n"); + free(r); + return -EINVAL; + } + r->clusters_per_index_block = index_block_size / + g_vol->cluster_size; + } else { /* if (g_vol->cluster_size > index_block_size) */ + if (index_block_size & (index_block_size - 1)) { + ntfs_log_error("add_attr_index_root: index block size is not " + "a power of 2.\n"); + free(r); + return -EINVAL; + } + if (index_block_size < (u32)opts.sector_size) { + ntfs_log_error("add_attr_index_root: index block size is " + "smaller than the sector size.\n"); + free(r); + return -EINVAL; + } + r->clusters_per_index_block = index_block_size / + opts.sector_size; + } + memset(&r->reserved, 0, sizeof(r->reserved)); + r->index.entries_offset = const_cpu_to_le32(sizeof(INDEX_HEADER)); + r->index.index_length = const_cpu_to_le32(sizeof(INDEX_HEADER) + + sizeof(INDEX_ENTRY_HEADER)); + r->index.allocated_size = r->index.index_length; + r->index.ih_flags = SMALL_INDEX; + memset(&r->index.reserved, 0, sizeof(r->index.reserved)); + e = (INDEX_ENTRY_HEADER*)((u8*)&r->index + + le32_to_cpu(r->index.entries_offset)); + /* + * No matter whether this is a file index or a view as this is a + * termination entry, hence no key value / data is associated with it + * at all. Thus, we just need the union to be all zero. + */ + e->indexed_file = const_cpu_to_le64(0LL); + e->length = const_cpu_to_le16(sizeof(INDEX_ENTRY_HEADER)); + e->key_length = const_cpu_to_le16(0); + e->flags = INDEX_ENTRY_END; + e->reserved = const_cpu_to_le16(0); + err = insert_resident_attr_in_mft_record(m, AT_INDEX_ROOT, name, + name_len, ic, 0, 0, (u8*)r, val_len); + free(r); + if (err < 0) + ntfs_log_error("add_attr_index_root failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_index_alloc + * + * Return 0 on success or -errno on error. + */ +static int add_attr_index_alloc(MFT_RECORD *m, const char *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const u8 *index_alloc_val, const u32 index_alloc_val_len) +{ + int err; + + err = insert_non_resident_attr_in_mft_record(m, AT_INDEX_ALLOCATION, + name, name_len, ic, 0, index_alloc_val, + index_alloc_val_len); + if (err < 0) + ntfs_log_error("add_attr_index_alloc failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_bitmap + * + * Return 0 on success or -errno on error. + */ +static int add_attr_bitmap(MFT_RECORD *m, const char *name, const u32 name_len, + const IGNORE_CASE_BOOL ic, const u8 *bitmap, + const u32 bitmap_len) +{ + int err; + + /* Does it fit? NO: create non-resident. YES: create resident. */ + if (le32_to_cpu(m->bytes_in_use) + 24 + bitmap_len > + le32_to_cpu(m->bytes_allocated)) + err = insert_non_resident_attr_in_mft_record(m, AT_BITMAP, name, + name_len, ic, 0, bitmap, bitmap_len); + else + err = insert_resident_attr_in_mft_record(m, AT_BITMAP, name, + name_len, ic, 0, 0, bitmap, bitmap_len); + + if (err < 0) + ntfs_log_error("add_attr_bitmap failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_bitmap_positioned + * + * Create a non-resident bitmap attribute with a predefined on disk location + * specified by the runlist @rl. The clusters specified by @rl are assumed to + * be allocated already. + * + * Return 0 on success or -errno on error. + */ +static int add_attr_bitmap_positioned(MFT_RECORD *m, const char *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const runlist *rl, const u8 *bitmap, const u32 bitmap_len) +{ + int err; + + err = insert_positioned_attr_in_mft_record(m, AT_BITMAP, name, name_len, + ic, 0, rl, bitmap, bitmap_len); + if (err < 0) + ntfs_log_error("add_attr_bitmap_positioned failed: %s\n", + strerror(-err)); + return err; +} + + +/** + * upgrade_to_large_index + * + * Create bitmap and index allocation attributes, modify index root + * attribute accordingly and move all of the index entries from the index root + * into the index allocation. + * + * Return 0 on success or -errno on error. + */ +static int upgrade_to_large_index(MFT_RECORD *m, const char *name, + u32 name_len, const IGNORE_CASE_BOOL ic, + INDEX_ALLOCATION **idx) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + INDEX_ROOT *r; + INDEX_ENTRY *re; + INDEX_ALLOCATION *ia_val = NULL; + ntfschar *uname = NULL; + int uname_len = 0; + u8 bmp[8]; + char *re_start, *re_end; + int i, err, index_block_size; + + uname = ntfs_str2ucs(name, &uname_len); + if (!uname) + return -errno; + + /* Find the index root attribute. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to allocate attribute search context.\n"); + ntfs_ucsfree(uname); + return -ENOMEM; + } + if (ic == IGNORE_CASE) { + ntfs_log_error("FIXME: Hit unimplemented code path #4.\n"); + err = -EOPNOTSUPP; + ntfs_ucsfree(uname); + goto err_out; + } + err = mkntfs_attr_lookup(AT_INDEX_ROOT, uname, uname_len, ic, 0, NULL, 0, + ctx); + ntfs_ucsfree(uname); + if (err) { + err = -ENOTDIR; + goto err_out; + } + a = ctx->attr; + if (a->non_resident || a->flags) { + err = -EINVAL; + goto err_out; + } + ntfs_attr_put_search_ctx(ctx); + r = (INDEX_ROOT*)((char*)a + le16_to_cpu(a->value_offset)); + re_end = (char*)r + le32_to_cpu(a->value_length); + re_start = (char*)&r->index + le32_to_cpu(r->index.entries_offset); + re = (INDEX_ENTRY*)re_start; + index_block_size = le32_to_cpu(r->index_block_size); + memset(bmp, 0, sizeof(bmp)); + ntfs_bit_set(bmp, 0ULL, 1); + /* Bitmap has to be at least 8 bytes in size. */ + err = add_attr_bitmap(m, name, name_len, ic, bmp, sizeof(bmp)); + if (err) + goto err_out; + ia_val = ntfs_calloc(1, index_block_size); + if (!ia_val) { + err = -errno; + goto err_out; + } + /* Setup header. */ + ia_val->magic = magic_INDX; + ia_val->usa_ofs = cpu_to_le16(sizeof(INDEX_ALLOCATION)); + if (index_block_size >= NTFS_BLOCK_SIZE) { + ia_val->usa_count = cpu_to_le16(index_block_size / + NTFS_BLOCK_SIZE + 1); + } else { + ia_val->usa_count = cpu_to_le16(1); + ntfs_log_error("Sector size is bigger than index block size. " + "Setting usa_count to 1. If Windows chkdsk " + "reports this as corruption, please email %s " + "stating that you saw this message and that " + "the filesystem created was corrupt. " + "Thank you.", NTFS_DEV_LIST); + } + /* Set USN to 1. */ + *(u16*)((char*)ia_val + le16_to_cpu(ia_val->usa_ofs)) = + cpu_to_le16(1); + ia_val->lsn = cpu_to_le64(0); + ia_val->index_block_vcn = cpu_to_le64(0); + ia_val->index.ih_flags = LEAF_NODE; + /* Align to 8-byte boundary. */ + ia_val->index.entries_offset = cpu_to_le32((sizeof(INDEX_HEADER) + + le16_to_cpu(ia_val->usa_count) * 2 + 7) & ~7); + ia_val->index.allocated_size = cpu_to_le32(index_block_size - + (sizeof(INDEX_ALLOCATION) - sizeof(INDEX_HEADER))); + /* Find the last entry in the index root and save it in re. */ + while ((char*)re < re_end && !(re->ie_flags & INDEX_ENTRY_END)) { + /* Next entry in index root. */ + re = (INDEX_ENTRY*)((char*)re + le16_to_cpu(re->length)); + } + /* Copy all the entries including the termination entry. */ + i = (char*)re - re_start + le16_to_cpu(re->length); + memcpy((char*)&ia_val->index + + le32_to_cpu(ia_val->index.entries_offset), re_start, i); + /* Finish setting up index allocation. */ + ia_val->index.index_length = cpu_to_le32(i + + le32_to_cpu(ia_val->index.entries_offset)); + /* Move the termination entry forward to the beginning if necessary. */ + if ((char*)re > re_start) { + memmove(re_start, (char*)re, le16_to_cpu(re->length)); + re = (INDEX_ENTRY*)re_start; + } + /* Now fixup empty index root with pointer to index allocation VCN 0. */ + r->index.ih_flags = LARGE_INDEX; + re->ie_flags |= INDEX_ENTRY_NODE; + if (le16_to_cpu(re->length) < sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN)) + re->length = cpu_to_le16(le16_to_cpu(re->length) + sizeof(VCN)); + r->index.index_length = cpu_to_le32(le32_to_cpu(r->index.entries_offset) + + le16_to_cpu(re->length)); + r->index.allocated_size = r->index.index_length; + /* Resize index root attribute. */ + if (ntfs_resident_attr_value_resize(m, a, sizeof(INDEX_ROOT) - + sizeof(INDEX_HEADER) + + le32_to_cpu(r->index.allocated_size))) { + /* TODO: Remove the added bitmap! */ + /* Revert index root from index allocation. */ + err = -errno; + goto err_out; + } + /* Set VCN pointer to 0LL. */ + *(VCN*)((char*)re + cpu_to_le16(re->length) - sizeof(VCN)) = + cpu_to_le64(0); + err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)ia_val, index_block_size); + if (err) { + err = -errno; + ntfs_log_error("ntfs_mst_pre_write_fixup() failed in " + "upgrade_to_large_index.\n"); + goto err_out; + } + err = add_attr_index_alloc(m, name, name_len, ic, (u8*)ia_val, + index_block_size); + ntfs_mst_post_write_fixup((NTFS_RECORD*)ia_val); + if (err) { + /* TODO: Remove the added bitmap! */ + /* Revert index root from index allocation. */ + goto err_out; + } + *idx = ia_val; + return 0; +err_out: + ntfs_attr_put_search_ctx(ctx); + free(ia_val); + return err; +} + +/** + * make_room_for_index_entry_in_index_block + * + * Create space of @size bytes at position @pos inside the index block @idx. + * + * Return 0 on success or -errno on error. + */ +static int make_room_for_index_entry_in_index_block(INDEX_BLOCK *idx, + INDEX_ENTRY *pos, u32 size) +{ + u32 biu; + + if (!size) + return 0; +#ifdef DEBUG + /* + * Rigorous consistency checks. Always return -EINVAL even if more + * appropriate codes exist for simplicity of parsing the return value. + */ + if (size != ((size + 7) & ~7)) { + ntfs_log_error("make_room_for_index_entry_in_index_block() received " + "non 8-byte aligned size.\n"); + return -EINVAL; + } + if (!idx || !pos) + return -EINVAL; + if ((char*)pos < (char*)idx || (char*)pos + size < (char*)idx || + (char*)pos > (char*)idx + sizeof(INDEX_BLOCK) - + sizeof(INDEX_HEADER) + + le32_to_cpu(idx->index.allocated_size) || + (char*)pos + size > (char*)idx + sizeof(INDEX_BLOCK) - + sizeof(INDEX_HEADER) + + le32_to_cpu(idx->index.allocated_size)) + return -EINVAL; + /* The - sizeof(INDEX_ENTRY_HEADER) is for the index terminator. */ + if ((char*)pos - (char*)&idx->index > + (int)le32_to_cpu(idx->index.index_length) + - (int)sizeof(INDEX_ENTRY_HEADER)) + return -EINVAL; +#endif + biu = le32_to_cpu(idx->index.index_length); + /* Do we have enough space? */ + if (biu + size > le32_to_cpu(idx->index.allocated_size)) + return -ENOSPC; + /* Move everything after pos to pos + size. */ + memmove((char*)pos + size, (char*)pos, biu - ((char*)pos - + (char*)&idx->index)); + /* Update index block. */ + idx->index.index_length = cpu_to_le32(biu + size); + return 0; +} + +/** + * ntfs_index_keys_compare + * + * not all types of COLLATION_RULES supported yet... + * added as needed.. (remove this comment when all are added) + */ +static int ntfs_index_keys_compare(u8 *key1, u8 *key2, int key1_length, + int key2_length, COLLATION_RULES collation_rule) +{ + u32 u1, u2; + int i; + + if (collation_rule == COLLATION_NTOFS_ULONG) { + /* i.e. $SII or $QUOTA-$Q */ + u1 = le32_to_cpup(key1); + u2 = le32_to_cpup(key2); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + /* u1 == u2 */ + return 0; + } + if (collation_rule == COLLATION_NTOFS_ULONGS) { + /* i.e $OBJID-$O */ + i = 0; + while (i < min(key1_length, key2_length)) { + u1 = le32_to_cpup(key1 + i); + u2 = le32_to_cpup(key2 + i); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + /* u1 == u2 */ + i += sizeof(u32); + } + if (key1_length < key2_length) + return -1; + if (key1_length > key2_length) + return 1; + return 0; + } + if (collation_rule == COLLATION_NTOFS_SECURITY_HASH) { + /* i.e. $SDH */ + u1 = le32_to_cpu(((SDH_INDEX_KEY*)key1)->hash); + u2 = le32_to_cpu(((SDH_INDEX_KEY*)key2)->hash); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + /* u1 == u2 */ + u1 = le32_to_cpu(((SDH_INDEX_KEY*)key1)->security_id); + u2 = le32_to_cpu(((SDH_INDEX_KEY*)key2)->security_id); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + return 0; + } + if (collation_rule == COLLATION_NTOFS_SID) { + /* i.e. $QUOTA-O */ + i = memcmp(key1, key2, min(key1_length, key2_length)); + if (!i) { + if (key1_length < key2_length) + return -1; + if (key1_length > key2_length) + return 1; + } + return i; + } + ntfs_log_critical("ntfs_index_keys_compare called without supported " + "collation rule.\n"); + return 0; /* Claim they're equal. What else can we do? */ +} + +/** + * insert_index_entry_in_res_dir_index + * + * i.e. insert an index_entry in some named index_root + * simplified search method, works for mkntfs + */ +static int insert_index_entry_in_res_dir_index(INDEX_ENTRY *idx, u32 idx_size, + MFT_RECORD *m, ntfschar *name, u32 name_size, ATTR_TYPES type) +{ + ntfs_attr_search_ctx *ctx; + INDEX_HEADER *idx_header; + INDEX_ENTRY *idx_entry, *idx_end; + ATTR_RECORD *a; + COLLATION_RULES collation_rule; + int err, i; + + err = 0; + /* does it fit ?*/ + if (g_vol->mft_record_size > idx_size + le32_to_cpu(m->bytes_allocated)) + return -ENOSPC; + /* find the INDEX_ROOT attribute:*/ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to allocate attribute search " + "context.\n"); + err = -ENOMEM; + goto err_out; + } + if (mkntfs_attr_lookup(AT_INDEX_ROOT, name, name_size, 0, 0, NULL, 0, + ctx)) { + err = -EEXIST; + goto err_out; + } + /* found attribute */ + a = (ATTR_RECORD*)ctx->attr; + collation_rule = ((INDEX_ROOT*)((u8*)a + + le16_to_cpu(a->value_offset)))->collation_rule; + idx_header = (INDEX_HEADER*)((u8*)a + le16_to_cpu(a->value_offset) + + 0x10); + idx_entry = (INDEX_ENTRY*)((u8*)idx_header + + le32_to_cpu(idx_header->entries_offset)); + idx_end = (INDEX_ENTRY*)((u8*)idx_entry + + le32_to_cpu(idx_header->index_length)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + if (type == AT_FILE_NAME) { + while (((u8*)idx_entry < (u8*)idx_end) && + !(idx_entry->ie_flags & INDEX_ENTRY_END)) { + /* + i = ntfs_file_values_compare(&idx->key.file_name, + &idx_entry->key.file_name, 1, + IGNORE_CASE, g_vol->upcase, + g_vol->upcase_len); + */ + i = ntfs_names_full_collate(idx->key.file_name.file_name, idx->key.file_name.file_name_length, + idx_entry->key.file_name.file_name, idx_entry->key.file_name.file_name_length, + IGNORE_CASE, g_vol->upcase, + g_vol->upcase_len); + /* + * If @file_name collates before ie->key.file_name, + * there is no matching index entry. + */ + if (i == -1) + break; + /* If file names are not equal, continue search. */ + if (i) + goto do_next; + if (idx->key.file_name.file_name_type != + FILE_NAME_POSIX || + idx_entry->key.file_name.file_name_type + != FILE_NAME_POSIX) + return -EEXIST; + /* + i = ntfs_file_values_compare(&idx->key.file_name, + &idx_entry->key.file_name, 1, + CASE_SENSITIVE, g_vol->upcase, + g_vol->upcase_len); + */ + i = ntfs_names_full_collate(idx->key.file_name.file_name, idx->key.file_name.file_name_length, + idx_entry->key.file_name.file_name, idx_entry->key.file_name.file_name_length, + CASE_SENSITIVE, g_vol->upcase, + g_vol->upcase_len); + if (!i) + return -EEXIST; + if (i == -1) + break; +do_next: + idx_entry = (INDEX_ENTRY*)((u8*)idx_entry + + le16_to_cpu(idx_entry->length)); + } + } else if (type == AT_UNUSED) { /* case view */ + while (((u8*)idx_entry < (u8*)idx_end) && + !(idx_entry->ie_flags & INDEX_ENTRY_END)) { + i = ntfs_index_keys_compare((u8*)idx + 0x10, + (u8*)idx_entry + 0x10, + le16_to_cpu(idx->key_length), + le16_to_cpu(idx_entry->key_length), + collation_rule); + if (!i) + return -EEXIST; + if (i == -1) + break; + idx_entry = (INDEX_ENTRY*)((u8*)idx_entry + + le16_to_cpu(idx_entry->length)); + } + } else { + return -EINVAL; + } + memmove((u8*)idx_entry + idx_size, (u8*)idx_entry, + le32_to_cpu(m->bytes_in_use) - + ((u8*)idx_entry - (u8*)m)); + memcpy((u8*)idx_entry, (u8*)idx, idx_size); + /* Adjust various offsets, etc... */ + m->bytes_in_use = cpu_to_le32(le32_to_cpu(m->bytes_in_use) + idx_size); + a->length = cpu_to_le32(le32_to_cpu(a->length) + idx_size); + a->value_length = cpu_to_le32(le32_to_cpu(a->value_length) + idx_size); + idx_header->index_length = cpu_to_le32( + le32_to_cpu(idx_header->index_length) + idx_size); + idx_header->allocated_size = cpu_to_le32( + le32_to_cpu(idx_header->allocated_size) + idx_size); +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + return err; +} + +/** + * initialize_secure + * + * initializes $Secure's $SDH and $SII indexes from $SDS datastream + */ +static int initialize_secure(char *sds, u32 sds_size, MFT_RECORD *m) +{ + int err, sdh_size, sii_size; + SECURITY_DESCRIPTOR_HEADER *sds_header; + INDEX_ENTRY *idx_entry_sdh, *idx_entry_sii; + SDH_INDEX_DATA *sdh_data; + SII_INDEX_DATA *sii_data; + + sds_header = (SECURITY_DESCRIPTOR_HEADER*)sds; + sdh_size = sizeof(INDEX_ENTRY_HEADER); + sdh_size += sizeof(SDH_INDEX_KEY) + sizeof(SDH_INDEX_DATA); + sii_size = sizeof(INDEX_ENTRY_HEADER); + sii_size += sizeof(SII_INDEX_KEY) + sizeof(SII_INDEX_DATA); + idx_entry_sdh = ntfs_calloc(1, sizeof(INDEX_ENTRY)); + if (!idx_entry_sdh) + return -errno; + idx_entry_sii = ntfs_calloc(1, sizeof(INDEX_ENTRY)); + if (!idx_entry_sii) { + free(idx_entry_sdh); + return -errno; + } + err = 0; + + while ((char*)sds_header < (char*)sds + sds_size) { + if (!sds_header->length) + break; + /* SDH index entry */ + idx_entry_sdh->data_offset = const_cpu_to_le16(0x18); + idx_entry_sdh->data_length = const_cpu_to_le16(0x14); + idx_entry_sdh->reservedV = const_cpu_to_le32(0x00); + idx_entry_sdh->length = const_cpu_to_le16(0x30); + idx_entry_sdh->key_length = const_cpu_to_le16(0x08); + idx_entry_sdh->ie_flags = const_cpu_to_le16(0x00); + idx_entry_sdh->reserved = const_cpu_to_le16(0x00); + idx_entry_sdh->key.sdh.hash = sds_header->hash; + idx_entry_sdh->key.sdh.security_id = sds_header->security_id; + sdh_data = (SDH_INDEX_DATA*)((u8*)idx_entry_sdh + + le16_to_cpu(idx_entry_sdh->data_offset)); + sdh_data->hash = sds_header->hash; + sdh_data->security_id = sds_header->security_id; + sdh_data->offset = sds_header->offset; + sdh_data->length = sds_header->length; + sdh_data->reserved_II = const_cpu_to_le32(0x00490049); + + /* SII index entry */ + idx_entry_sii->data_offset = const_cpu_to_le16(0x14); + idx_entry_sii->data_length = const_cpu_to_le16(0x14); + idx_entry_sii->reservedV = const_cpu_to_le32(0x00); + idx_entry_sii->length = const_cpu_to_le16(0x28); + idx_entry_sii->key_length = const_cpu_to_le16(0x04); + idx_entry_sii->ie_flags = const_cpu_to_le16(0x00); + idx_entry_sii->reserved = const_cpu_to_le16(0x00); + idx_entry_sii->key.sii.security_id = sds_header->security_id; + sii_data = (SII_INDEX_DATA*)((u8*)idx_entry_sii + + le16_to_cpu(idx_entry_sii->data_offset)); + sii_data->hash = sds_header->hash; + sii_data->security_id = sds_header->security_id; + sii_data->offset = sds_header->offset; + sii_data->length = sds_header->length; + if ((err = insert_index_entry_in_res_dir_index(idx_entry_sdh, + sdh_size, m, NTFS_INDEX_SDH, 4, AT_UNUSED))) + break; + if ((err = insert_index_entry_in_res_dir_index(idx_entry_sii, + sii_size, m, NTFS_INDEX_SII, 4, AT_UNUSED))) + break; + sds_header = (SECURITY_DESCRIPTOR_HEADER*)((u8*)sds_header + + ((le32_to_cpu(sds_header->length) + 15) & ~15)); + } + free(idx_entry_sdh); + free(idx_entry_sii); + return err; +} + +/** + * initialize_quota + * + * initialize $Quota with the default quota index-entries. + */ +static int initialize_quota(MFT_RECORD *m) +{ + int o_size, q1_size, q2_size, err; + INDEX_ENTRY *idx_entry_o, *idx_entry_q1, *idx_entry_q2; + QUOTA_O_INDEX_DATA *idx_entry_o_data; + QUOTA_CONTROL_ENTRY *idx_entry_q1_data, *idx_entry_q2_data; + + err = 0; + /* q index entry num 1 */ + q1_size = 0x48; + idx_entry_q1 = ntfs_calloc(1, q1_size); + if (!idx_entry_q1) + return errno; + idx_entry_q1->data_offset = const_cpu_to_le16(0x14); + idx_entry_q1->data_length = const_cpu_to_le16(0x30); + idx_entry_q1->reservedV = const_cpu_to_le32(0x00); + idx_entry_q1->length = const_cpu_to_le16(0x48); + idx_entry_q1->key_length = const_cpu_to_le16(0x04); + idx_entry_q1->ie_flags = const_cpu_to_le16(0x00); + idx_entry_q1->reserved = const_cpu_to_le16(0x00); + idx_entry_q1->key.owner_id = const_cpu_to_le32(0x01); + idx_entry_q1_data = (QUOTA_CONTROL_ENTRY*)((char*)idx_entry_q1 + + le16_to_cpu(idx_entry_q1->data_offset)); + idx_entry_q1_data->version = const_cpu_to_le32(0x02); + idx_entry_q1_data->flags = QUOTA_FLAG_DEFAULT_LIMITS; + if (g_vol->minor_ver == 0) + idx_entry_q1_data->flags |= QUOTA_FLAG_OUT_OF_DATE; + idx_entry_q1_data->bytes_used = const_cpu_to_le64(0x00); + idx_entry_q1_data->change_time = timespec2ntfs(mkntfs_time()); + idx_entry_q1_data->threshold = const_cpu_to_le64((s64)-1); + idx_entry_q1_data->limit = const_cpu_to_le64((s64)-1); + idx_entry_q1_data->exceeded_time = const_cpu_to_le64(0x00); + err = insert_index_entry_in_res_dir_index(idx_entry_q1, q1_size, m, + NTFS_INDEX_Q, 2, AT_UNUSED); + free(idx_entry_q1); + if (err) + return err; + /* q index entry num 2 */ + q2_size = 0x58; + idx_entry_q2 = ntfs_calloc(1, q2_size); + if (!idx_entry_q2) + return errno; + idx_entry_q2->data_offset = const_cpu_to_le16(0x14); + idx_entry_q2->data_length = const_cpu_to_le16(0x40); + idx_entry_q2->reservedV = const_cpu_to_le32(0x00); + idx_entry_q2->length = const_cpu_to_le16(0x58); + idx_entry_q2->key_length = const_cpu_to_le16(0x04); + idx_entry_q2->ie_flags = const_cpu_to_le16(0x00); + idx_entry_q2->reserved = const_cpu_to_le16(0x00); + idx_entry_q2->key.owner_id = QUOTA_FIRST_USER_ID; + idx_entry_q2_data = (QUOTA_CONTROL_ENTRY*)((char*)idx_entry_q2 + + le16_to_cpu(idx_entry_q2->data_offset)); + idx_entry_q2_data->version = const_cpu_to_le32(0x02); + idx_entry_q2_data->flags = QUOTA_FLAG_DEFAULT_LIMITS; + idx_entry_q2_data->bytes_used = const_cpu_to_le64(0x00); + idx_entry_q2_data->change_time = timespec2ntfs(mkntfs_time());; + idx_entry_q2_data->threshold = const_cpu_to_le64((s64)-1); + idx_entry_q2_data->limit = const_cpu_to_le64((s64)-1); + idx_entry_q2_data->exceeded_time = const_cpu_to_le64(0x00); + idx_entry_q2_data->sid.revision = 1; + idx_entry_q2_data->sid.sub_authority_count = 2; + idx_entry_q2_data->sid.identifier_authority.high_part = + const_cpu_to_le16(0x0000); + idx_entry_q2_data->sid.identifier_authority.low_part = + const_cpu_to_le32(0x05000000); + idx_entry_q2_data->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + idx_entry_q2_data->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + err = insert_index_entry_in_res_dir_index(idx_entry_q2, q2_size, m, + NTFS_INDEX_Q, 2, AT_UNUSED); + free(idx_entry_q2); + if (err) + return err; + o_size = 0x28; + idx_entry_o = ntfs_calloc(1, o_size); + if (!idx_entry_o) + return errno; + idx_entry_o->data_offset = const_cpu_to_le16(0x20); + idx_entry_o->data_length = const_cpu_to_le16(0x04); + idx_entry_o->reservedV = const_cpu_to_le32(0x00); + idx_entry_o->length = const_cpu_to_le16(0x28); + idx_entry_o->key_length = const_cpu_to_le16(0x10); + idx_entry_o->ie_flags = const_cpu_to_le16(0x00); + idx_entry_o->reserved = const_cpu_to_le16(0x00); + idx_entry_o->key.sid.revision = 0x01; + idx_entry_o->key.sid.sub_authority_count = 0x02; + idx_entry_o->key.sid.identifier_authority.high_part = + const_cpu_to_le16(0x0000); + idx_entry_o->key.sid.identifier_authority.low_part = + const_cpu_to_le32(0x05000000); + idx_entry_o->key.sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + idx_entry_o->key.sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + idx_entry_o_data = (QUOTA_O_INDEX_DATA*)((char*)idx_entry_o + + le16_to_cpu(idx_entry_o->data_offset)); + idx_entry_o_data->owner_id = QUOTA_FIRST_USER_ID; + /* 20 00 00 00 padding after here on ntfs 3.1. 3.0 is unchecked. */ + idx_entry_o_data->unknown = const_cpu_to_le32(32); + err = insert_index_entry_in_res_dir_index(idx_entry_o, o_size, m, + NTFS_INDEX_O, 2, AT_UNUSED); + free(idx_entry_o); + + return err; +} + +/** + * insert_file_link_in_dir_index + * + * Insert the fully completed FILE_NAME_ATTR @file_name which is inside + * the file with mft reference @file_ref into the index (allocation) block + * @idx (which belongs to @file_ref's parent directory). + * + * Return 0 on success or -errno on error. + */ +static int insert_file_link_in_dir_index(INDEX_BLOCK *idx, MFT_REF file_ref, + FILE_NAME_ATTR *file_name, u32 file_name_size) +{ + int err, i; + INDEX_ENTRY *ie; + char *index_end; + + /* + * Lookup dir entry @file_name in dir @idx to determine correct + * insertion location. FIXME: Using a very oversimplified lookup + * method which is sufficient for mkntfs but no good whatsoever in + * real world scenario. (AIA) + */ + + index_end = (char*)&idx->index + le32_to_cpu(idx->index.index_length); + ie = (INDEX_ENTRY*)((char*)&idx->index + + le32_to_cpu(idx->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + while ((char*)ie < index_end && !(ie->ie_flags & INDEX_ENTRY_END)) { +#if 0 +#ifdef DEBUG + ntfs_log_debug("file_name_attr1->file_name_length = %i\n", + file_name->file_name_length); + if (file_name->file_name_length) { + char *__buf = NULL; + i = ntfs_ucstombs((ntfschar*)&file_name->file_name, + file_name->file_name_length, &__buf, 0); + if (i < 0) + ntfs_log_debug("Name contains non-displayable " + "Unicode characters.\n"); + ntfs_log_debug("file_name_attr1->file_name = %s\n", __buf); + free(__buf); + } + ntfs_log_debug("file_name_attr2->file_name_length = %i\n", + ie->key.file_name.file_name_length); + if (ie->key.file_name.file_name_length) { + char *__buf = NULL; + i = ntfs_ucstombs(ie->key.file_name.file_name, + ie->key.file_name.file_name_length + 1, &__buf, 0); + if (i < 0) + ntfs_log_debug("Name contains non-displayable " + "Unicode characters.\n"); + ntfs_log_debug("file_name_attr2->file_name = %s\n", __buf); + free(__buf); + } +#endif +#endif + /* + i = ntfs_file_values_compare(file_name, + (FILE_NAME_ATTR*)&ie->key.file_name, 1, + IGNORE_CASE, g_vol->upcase, g_vol->upcase_len); + */ + i = ntfs_names_full_collate(file_name->file_name, file_name->file_name_length, + ((FILE_NAME_ATTR*)&ie->key.file_name)->file_name, ((FILE_NAME_ATTR*)&ie->key.file_name)->file_name_length, + IGNORE_CASE, g_vol->upcase, g_vol->upcase_len); + /* + * If @file_name collates before ie->key.file_name, there is no + * matching index entry. + */ + if (i == -1) + break; + /* If file names are not equal, continue search. */ + if (i) + goto do_next; + /* File names are equal when compared ignoring case. */ + /* + * If BOTH file names are in the POSIX namespace, do a case + * sensitive comparison as well. Otherwise the names match so + * we return -EEXIST. FIXME: There are problems with this in a + * real world scenario, when one is POSIX and one isn't, but + * fine for mkntfs where we don't use POSIX namespace at all + * and hence this following code is luxury. (AIA) + */ + if (file_name->file_name_type != FILE_NAME_POSIX || + ie->key.file_name.file_name_type != FILE_NAME_POSIX) + return -EEXIST; + /* + i = ntfs_file_values_compare(file_name, + (FILE_NAME_ATTR*)&ie->key.file_name, 1, + CASE_SENSITIVE, g_vol->upcase, g_vol->upcase_len); + */ + i = ntfs_names_full_collate(file_name->file_name, file_name->file_name_length, + ((FILE_NAME_ATTR*)&ie->key.file_name)->file_name, ((FILE_NAME_ATTR*)&ie->key.file_name)->file_name_length, + CASE_SENSITIVE, g_vol->upcase, g_vol->upcase_len); + if (i == -1) + break; + /* Complete match. Bugger. Can't insert. */ + if (!i) + return -EEXIST; +do_next: +#ifdef DEBUG + /* Next entry. */ + if (!ie->length) { + ntfs_log_debug("BUG: ie->length is zero, breaking out of loop.\n"); + break; + } +#endif + ie = (INDEX_ENTRY*)((char*)ie + le16_to_cpu(ie->length)); + }; + i = (sizeof(INDEX_ENTRY_HEADER) + file_name_size + 7) & ~7; + err = make_room_for_index_entry_in_index_block(idx, ie, i); + if (err) { + ntfs_log_error("make_room_for_index_entry_in_index_block failed: " + "%s\n", strerror(-err)); + return err; + } + /* Create entry in place and copy file name attribute value. */ + ie->indexed_file = file_ref; + ie->length = cpu_to_le16(i); + ie->key_length = cpu_to_le16(file_name_size); + ie->ie_flags = cpu_to_le16(0); + ie->reserved = cpu_to_le16(0); + memcpy((char*)&ie->key.file_name, (char*)file_name, file_name_size); + return 0; +} + +/** + * create_hardlink_res + * + * Create a file_name_attribute in the mft record @m_file which points to the + * parent directory with mft reference @ref_parent. + * + * Then, insert an index entry with this file_name_attribute in the index + * root @idx of the index_root attribute of the parent directory. + * + * @ref_file is the mft reference of @m_file. + * + * Return 0 on success or -errno on error. + */ +static int create_hardlink_res(MFT_RECORD *m_parent, const MFT_REF ref_parent, + MFT_RECORD *m_file, const MFT_REF ref_file, + const s64 allocated_size, const s64 data_size, + const FILE_ATTR_FLAGS flags, const u16 packed_ea_size, + const u32 reparse_point_tag, const char *file_name, + const FILE_NAME_TYPE_FLAGS file_name_type) +{ + FILE_NAME_ATTR *fn; + int i, fn_size, idx_size; + INDEX_ENTRY *idx_entry_new; + ntfschar *uname; + + /* Create the file_name attribute. */ + i = (strlen(file_name) + 1) * sizeof(ntfschar); + fn_size = sizeof(FILE_NAME_ATTR) + i; + fn = malloc(fn_size); + if (!fn) + return -errno; + fn->parent_directory = ref_parent; + /* FIXME: copy the creation_time from the std info */ + fn->creation_time = timespec2ntfs(mkntfs_time()); + fn->last_data_change_time = fn->creation_time; + fn->last_mft_change_time = fn->creation_time; + fn->last_access_time = fn->creation_time; + fn->allocated_size = cpu_to_le64(allocated_size); + fn->data_size = cpu_to_le64(data_size); + fn->file_attributes = flags; + /* These are in a union so can't have both. */ + if (packed_ea_size && reparse_point_tag) { + free(fn); + return -EINVAL; + } + if (packed_ea_size) { + free(fn); + return -EINVAL; + } + if (packed_ea_size) { + fn->packed_ea_size = cpu_to_le16(packed_ea_size); + fn->reserved = cpu_to_le16(0); + } else { + fn->reparse_point_tag = cpu_to_le32(reparse_point_tag); + } + fn->file_name_type = file_name_type; + uname = fn->file_name; + i = ntfs_mbstoucs_libntfscompat(file_name, &uname, i); + if (i < 1) { + free(fn); + return -EINVAL; + } + if (i > 0xff) { + free(fn); + return -ENAMETOOLONG; + } + /* No terminating null in file names. */ + fn->file_name_length = i; + fn_size = sizeof(FILE_NAME_ATTR) + i * sizeof(ntfschar); + /* Increment the link count of @m_file. */ + i = le16_to_cpu(m_file->link_count); + if (i == 0xffff) { + ntfs_log_error("Too many hardlinks present already.\n"); + free(fn); + return -EINVAL; + } + m_file->link_count = cpu_to_le16(i + 1); + /* Add the file_name to @m_file. */ + i = insert_resident_attr_in_mft_record(m_file, AT_FILE_NAME, NULL, 0, 0, + 0, RESIDENT_ATTR_IS_INDEXED, (u8*)fn, fn_size); + if (i < 0) { + ntfs_log_error("create_hardlink failed adding file name attribute: " + "%s\n", strerror(-i)); + free(fn); + /* Undo link count increment. */ + m_file->link_count = cpu_to_le16( + le16_to_cpu(m_file->link_count) - 1); + return i; + } + /* Insert the index entry for file_name in @idx. */ + idx_size = (fn_size + 7) & ~7; + idx_entry_new = ntfs_calloc(1, idx_size + 0x10); + if (!idx_entry_new) + return -errno; + idx_entry_new->indexed_file = ref_file; + idx_entry_new->length = cpu_to_le16(idx_size + 0x10); + idx_entry_new->key_length = cpu_to_le16(fn_size); + memcpy((u8*)idx_entry_new + 0x10, (u8*)fn, fn_size); + i = insert_index_entry_in_res_dir_index(idx_entry_new, idx_size + 0x10, + m_parent, NTFS_INDEX_I30, 4, AT_FILE_NAME); + if (i < 0) { + ntfs_log_error("create_hardlink failed inserting index entry: %s\n", + strerror(-i)); + /* FIXME: Remove the file name attribute from @m_file. */ + free(idx_entry_new); + free(fn); + /* Undo link count increment. */ + m_file->link_count = cpu_to_le16( + le16_to_cpu(m_file->link_count) - 1); + return i; + } + free(idx_entry_new); + free(fn); + return 0; +} + +/** + * create_hardlink + * + * Create a file_name_attribute in the mft record @m_file which points to the + * parent directory with mft reference @ref_parent. + * + * Then, insert an index entry with this file_name_attribute in the index + * block @idx of the index allocation attribute of the parent directory. + * + * @ref_file is the mft reference of @m_file. + * + * Return 0 on success or -errno on error. + */ +static int create_hardlink(INDEX_BLOCK *idx, const MFT_REF ref_parent, + MFT_RECORD *m_file, const MFT_REF ref_file, + const s64 allocated_size, const s64 data_size, + const FILE_ATTR_FLAGS flags, const u16 packed_ea_size, + const u32 reparse_point_tag, const char *file_name, + const FILE_NAME_TYPE_FLAGS file_name_type) +{ + FILE_NAME_ATTR *fn; + int i, fn_size; + ntfschar *uname; + + /* Create the file_name attribute. */ + i = (strlen(file_name) + 1) * sizeof(ntfschar); + fn_size = sizeof(FILE_NAME_ATTR) + i; + fn = malloc(fn_size); + if (!fn) + return -errno; + fn->parent_directory = ref_parent; + /* FIXME: Is this correct? Or do we have to copy the creation_time */ + /* from the std info? */ + fn->creation_time = timespec2ntfs(mkntfs_time()); + fn->last_data_change_time = fn->creation_time; + fn->last_mft_change_time = fn->creation_time; + fn->last_access_time = fn->creation_time; + fn->allocated_size = cpu_to_le64(allocated_size); + fn->data_size = cpu_to_le64(data_size); + fn->file_attributes = flags; + /* These are in a union so can't have both. */ + if (packed_ea_size && reparse_point_tag) { + free(fn); + return -EINVAL; + } + if (packed_ea_size) { + fn->packed_ea_size = cpu_to_le16(packed_ea_size); + fn->reserved = cpu_to_le16(0); + } else { + fn->reparse_point_tag = cpu_to_le32(reparse_point_tag); + } + fn->file_name_type = file_name_type; + uname = fn->file_name; + i = ntfs_mbstoucs_libntfscompat(file_name, &uname, i); + if (i < 1) { + free(fn); + return -EINVAL; + } + if (i > 0xff) { + free(fn); + return -ENAMETOOLONG; + } + /* No terminating null in file names. */ + fn->file_name_length = i; + fn_size = sizeof(FILE_NAME_ATTR) + i * sizeof(ntfschar); + /* Increment the link count of @m_file. */ + i = le16_to_cpu(m_file->link_count); + if (i == 0xffff) { + ntfs_log_error("Too many hardlinks present already.\n"); + free(fn); + return -EINVAL; + } + m_file->link_count = cpu_to_le16(i + 1); + /* Add the file_name to @m_file. */ + i = insert_resident_attr_in_mft_record(m_file, AT_FILE_NAME, NULL, 0, 0, + 0, RESIDENT_ATTR_IS_INDEXED, (u8*)fn, fn_size); + if (i < 0) { + ntfs_log_error("create_hardlink failed adding file name attribute: " + "%s\n", strerror(-i)); + free(fn); + /* Undo link count increment. */ + m_file->link_count = cpu_to_le16( + le16_to_cpu(m_file->link_count) - 1); + return i; + } + /* Insert the index entry for file_name in @idx. */ + i = insert_file_link_in_dir_index(idx, ref_file, fn, fn_size); + if (i < 0) { + ntfs_log_error("create_hardlink failed inserting index entry: %s\n", + strerror(-i)); + /* FIXME: Remove the file name attribute from @m_file. */ + free(fn); + /* Undo link count increment. */ + m_file->link_count = cpu_to_le16( + le16_to_cpu(m_file->link_count) - 1); + return i; + } + free(fn); + return 0; +} + +/** + * mkntfs_cleanup + */ +static void mkntfs_cleanup(void) +{ + /* Close the volume */ + if (g_vol) { + if (g_vol->dev) { + if (NDevOpen(g_vol->dev) && g_vol->dev->d_ops->close(g_vol->dev)) + ntfs_log_perror("Warning: Could not close %s", g_vol->dev->d_name); + ntfs_device_free(g_vol->dev); + } + free(g_vol->vol_name); + free(g_vol->attrdef); + free(g_vol->upcase); + free(g_vol); + g_vol = NULL; + } + + /* Free any memory we've used */ + free(g_bad_blocks); g_bad_blocks = NULL; + free(g_buf); g_buf = NULL; + free(g_index_block); g_index_block = NULL; + free(g_lcn_bitmap); g_lcn_bitmap = NULL; + free(g_mft_bitmap); g_mft_bitmap = NULL; + free(g_rl_bad); g_rl_bad = NULL; + free(g_rl_boot); g_rl_boot = NULL; + free(g_rl_logfile); g_rl_logfile = NULL; + free(g_rl_mft); g_rl_mft = NULL; + free(g_rl_mft_bmp); g_rl_mft_bmp = NULL; + free(g_rl_mftmirr); g_rl_mftmirr = NULL; +} + + +/** + * mkntfs_open_partition - + */ +static BOOL mkntfs_open_partition(ntfs_volume *vol) +{ + BOOL result = FALSE; + int i; + struct stat sbuf; + unsigned long mnt_flags; + + /* + * Allocate and initialize an ntfs device structure and attach it to + * the volume. + */ + vol->dev = ntfs_device_alloc(opts.dev_name, 0, &ntfs_device_default_io_ops, NULL); + if (!vol->dev) { + ntfs_log_perror("Could not create device"); + goto done; + } + + /* Open the device for reading or reading and writing. */ + if (opts.no_action) { + ntfs_log_quiet("Running in READ-ONLY mode!\n"); + i = O_RDONLY; + } else { + i = O_RDWR; + } + if (vol->dev->d_ops->open(vol->dev, i)) { + if (errno == ENOENT) + ntfs_log_error("The device doesn't exist; did you specify it correctly?\n"); + else + ntfs_log_perror("Could not open %s", vol->dev->d_name); + goto done; + } + /* Verify we are dealing with a block device. */ + if (vol->dev->d_ops->stat(vol->dev, &sbuf)) { + ntfs_log_perror("Error getting information about %s", vol->dev->d_name); + goto done; + } + + if (!S_ISBLK(sbuf.st_mode)) { + ntfs_log_error("%s is not a block device.\n", vol->dev->d_name); + if (!opts.force) { + ntfs_log_error("Refusing to make a filesystem here!\n"); + goto done; + } + if (!opts.num_sectors) { + if (!sbuf.st_size && !sbuf.st_blocks) { + ntfs_log_error("You must specify the number of sectors.\n"); + goto done; + } + if (opts.sector_size) { + if (sbuf.st_size) + opts.num_sectors = sbuf.st_size / opts.sector_size; + else + opts.num_sectors = ((s64)sbuf.st_blocks << 9) / opts.sector_size; + } else { + if (sbuf.st_size) + opts.num_sectors = sbuf.st_size / 512; + else + opts.num_sectors = sbuf.st_blocks; + opts.sector_size = 512; + } + } + ntfs_log_warning("mkntfs forced anyway.\n"); +#ifdef HAVE_LINUX_MAJOR_H + } else if ((IDE_DISK_MAJOR(MAJOR(sbuf.st_rdev)) && + MINOR(sbuf.st_rdev) % 64 == 0) || + (SCSI_DISK_MAJOR(MAJOR(sbuf.st_rdev)) && + MINOR(sbuf.st_rdev) % 16 == 0)) { + ntfs_log_error("%s is entire device, not just one partition.\n", vol->dev->d_name); + if (!opts.force) { + ntfs_log_error("Refusing to make a filesystem here!\n"); + goto done; + } + ntfs_log_warning("mkntfs forced anyway.\n"); +#endif + } + /* Make sure the file system is not mounted. */ + if (ntfs_check_if_mounted(vol->dev->d_name, &mnt_flags)) { + ntfs_log_perror("Failed to determine whether %s is mounted", vol->dev->d_name); + } else if (mnt_flags & NTFS_MF_MOUNTED) { + ntfs_log_error("%s is mounted.\n", vol->dev->d_name); + if (!opts.force) { + ntfs_log_error("Refusing to make a filesystem here!\n"); + goto done; + } + ntfs_log_warning("mkntfs forced anyway. Hope /etc/mtab is incorrect.\n"); + } + result = TRUE; +done: + return result; +} + +/** + * mkntfs_get_page_size - detect the system's memory page size. + */ +static long mkntfs_get_page_size() +{ + long page_size; +#ifdef _SC_PAGESIZE + page_size = sysconf(_SC_PAGESIZE); + if (page_size < 0) +#endif +#ifdef _SC_PAGE_SIZE + page_size = sysconf(_SC_PAGE_SIZE); + if (page_size < 0) +#endif + { + ntfs_log_warning("Failed to determine system page size. " + "Assuming safe default of 4096 bytes.\n"); + return 4096; + } + ntfs_log_debug("System page size is %li bytes.\n", page_size); + return page_size; +} + +/** + * mkntfs_override_vol_params - + */ +static BOOL mkntfs_override_vol_params(ntfs_volume *vol) +{ + s64 volume_size; + long page_size; + int i; + BOOL winboot = TRUE; + + /* If user didn't specify the sector size, determine it now. */ + if (opts.sector_size < 0) { + opts.sector_size = ntfs_device_sector_size_get(vol->dev); + if (opts.sector_size < 0) { + ntfs_log_warning("The sector size was not specified " + "for %s and it could not be obtained " + "automatically. It has been set to 512 " + "bytes.\n", vol->dev->d_name); + opts.sector_size = 512; + } + } + /* Validate sector size. */ + if ((opts.sector_size - 1) & opts.sector_size) { + ntfs_log_error("The sector size is invalid. It must be a " + "power of two, e.g. 512, 1024.\n"); + return FALSE; + } + if (opts.sector_size < 256 || opts.sector_size > 4096) { + ntfs_log_error("The sector size is invalid. The minimum size " + "is 256 bytes and the maximum is 4096 bytes.\n"); + return FALSE; + } + ntfs_log_debug("sector size = %ld bytes\n", opts.sector_size); + /* Now set the device block size to the sector size. */ + if (ntfs_device_block_size_set(vol->dev, opts.sector_size)) + ntfs_log_debug("Failed to set the device block size to the " + "sector size. This may cause problems when " + "creating the backup boot sector and also may " + "affect performance but should be harmless " + "otherwise. Error: %s\n", strerror(errno)); + /* If user didn't specify the number of sectors, determine it now. */ + if (opts.num_sectors < 0) { + opts.num_sectors = ntfs_device_size_get(vol->dev, + opts.sector_size); + if (opts.num_sectors <= 0) { + ntfs_log_error("Couldn't determine the size of %s. " + "Please specify the number of sectors " + "manually.\n", vol->dev->d_name); + return FALSE; + } + } + ntfs_log_debug("number of sectors = %lld (0x%llx)\n", opts.num_sectors, + opts.num_sectors); + /* + * Reserve the last sector for the backup boot sector unless the + * sector size is less than 512 bytes in which case reserve 512 bytes + * worth of sectors. + */ + i = 1; + if (opts.sector_size < 512) + i = 512 / opts.sector_size; + opts.num_sectors -= i; + /* If user didn't specify the partition start sector, determine it. */ + if (opts.part_start_sect < 0) { + opts.part_start_sect = ntfs_device_partition_start_sector_get( + vol->dev); + if (opts.part_start_sect < 0) { + ntfs_log_warning("The partition start sector was not " + "specified for %s and it could not be obtained " + "automatically. It has been set to 0.\n", + vol->dev->d_name); + opts.part_start_sect = 0; + winboot = FALSE; + } else if (opts.part_start_sect >> 32) { + ntfs_log_warning("The partition start sector specified " + "for %s and the automatically determined value " + "is too large. It has been set to 0.\n", + vol->dev->d_name); + opts.part_start_sect = 0; + winboot = FALSE; + } + } else if (opts.part_start_sect >> 32) { + ntfs_log_error("Invalid partition start sector. Maximum is " + "4294967295 (2^32-1).\n"); + return FALSE; + } + /* If user didn't specify the sectors per track, determine it now. */ + if (opts.sectors_per_track < 0) { + opts.sectors_per_track = ntfs_device_sectors_per_track_get( + vol->dev); + if (opts.sectors_per_track < 0) { + ntfs_log_warning("The number of sectors per track was " + "not specified for %s and it could not be " + "obtained automatically. It has been set to " + "0.\n", vol->dev->d_name); + opts.sectors_per_track = 0; + winboot = FALSE; + } else if (opts.sectors_per_track > 65535) { + ntfs_log_warning("The number of sectors per track was " + "not specified for %s and the automatically " + "determined value is too large. It has been " + "set to 0.\n", vol->dev->d_name); + opts.sectors_per_track = 0; + winboot = FALSE; + } + } else if (opts.sectors_per_track > 65535) { + ntfs_log_error("Invalid number of sectors per track. Maximum " + "is 65535.\n"); + return FALSE; + } + /* If user didn't specify the number of heads, determine it now. */ + if (opts.heads < 0) { + opts.heads = ntfs_device_heads_get(vol->dev); + if (opts.heads < 0) { + ntfs_log_warning("The number of heads was not " + "specified for %s and it could not be obtained " + "automatically. It has been set to 0.\n", + vol->dev->d_name); + opts.heads = 0; + winboot = FALSE; + } else if (opts.heads > 65535) { + ntfs_log_warning("The number of heads was not " + "specified for %s and the automatically " + "determined value is too large. It has been " + "set to 0.\n", vol->dev->d_name); + opts.heads = 0; + winboot = FALSE; + } + } else if (opts.heads > 65535) { + ntfs_log_error("Invalid number of heads. Maximum is 65535.\n"); + return FALSE; + } + volume_size = opts.num_sectors * opts.sector_size; + /* Validate volume size. */ + if (volume_size < (1 << 20)) { /* 1MiB */ + ntfs_log_error("Device is too small (%llikiB). Minimum NTFS " + "volume size is 1MiB.\n", volume_size / 1024); + return FALSE; + } + ntfs_log_debug("volume size = %llikiB\n", volume_size / 1024); + /* If user didn't specify the cluster size, determine it now. */ + if (!vol->cluster_size) { + if (volume_size <= 512LL << 20) /* <= 512MB */ + vol->cluster_size = 512; + else if (volume_size <= 1LL << 30) /* ]512MB-1GB] */ + vol->cluster_size = 1024; + else if (volume_size <= 2LL << 30) /* ]1GB-2GB] */ + vol->cluster_size = 2048; + else + vol->cluster_size = 4096; + /* For small volumes on devices with large sector sizes. */ + if (vol->cluster_size < (u32)opts.sector_size) + vol->cluster_size = opts.sector_size; + /* + * For huge volumes, grow the cluster size until the number of + * clusters fits into 32 bits or the cluster size exceeds the + * maximum limit of 64kiB. + */ + while (volume_size >> (ffs(vol->cluster_size) - 1 + 32)) { + vol->cluster_size <<= 1; + if (vol->cluster_size > 65535) { + ntfs_log_error("Device is too large to hold an " + "NTFS volume (maximum size is " + "256TiB).\n"); + return FALSE; + } + } + ntfs_log_quiet("Cluster size has been automatically set to %d " + "bytes.\n", vol->cluster_size); + } + /* Validate cluster size. */ + if (vol->cluster_size & (vol->cluster_size - 1)) { + ntfs_log_error("The cluster size is invalid. It must be a " + "power of two, e.g. 1024, 4096.\n"); + return FALSE; + } + if (vol->cluster_size < (u32)opts.sector_size) { + ntfs_log_error("The cluster size is invalid. It must be equal " + "to, or larger than, the sector size.\n"); + return FALSE; + } + if (vol->cluster_size > 128 * (u32)opts.sector_size) { + ntfs_log_error("The cluster size is invalid. It cannot be " + "more that 128 times the size of the sector " + "size.\n"); + return FALSE; + } + if (vol->cluster_size > 65536) { + ntfs_log_error("The cluster size is invalid. The maximum " + "cluster size is 65536 bytes (64kiB).\n"); + return FALSE; + } + vol->cluster_size_bits = ffs(vol->cluster_size) - 1; + ntfs_log_debug("cluster size = %u bytes\n", + (unsigned int)vol->cluster_size); + if (vol->cluster_size > 4096) { + if (opts.enable_compression) { + if (!opts.force) { + ntfs_log_error("Windows cannot use compression " + "when the cluster size is " + "larger than 4096 bytes.\n"); + return FALSE; + } + opts.enable_compression = 0; + } + ntfs_log_warning("Windows cannot use compression when the " + "cluster size is larger than 4096 bytes. " + "Compression has been disabled for this " + "volume.\n"); + } + vol->nr_clusters = volume_size / vol->cluster_size; + /* + * Check the cluster_size and num_sectors for consistency with + * sector_size and num_sectors. And check both of these for consistency + * with volume_size. + */ + if ((vol->nr_clusters != ((opts.num_sectors * opts.sector_size) / + vol->cluster_size) || + (volume_size / opts.sector_size) != opts.num_sectors || + (volume_size / vol->cluster_size) != + vol->nr_clusters)) { + /* XXX is this code reachable? */ + ntfs_log_error("Illegal combination of volume/cluster/sector " + "size and/or cluster/sector number.\n"); + return FALSE; + } + ntfs_log_debug("number of clusters = %llu (0x%llx)\n", + vol->nr_clusters, vol->nr_clusters); + /* Number of clusters must fit within 32 bits (Win2k limitation). */ + if (vol->nr_clusters >> 32) { + if (vol->cluster_size >= 65536) { + ntfs_log_error("Device is too large to hold an NTFS " + "volume (maximum size is 256TiB).\n"); + return FALSE; + } + ntfs_log_error("Number of clusters exceeds 32 bits. Please " + "try again with a larger\ncluster size or " + "leave the cluster size unspecified and the " + "smallest possible cluster size for the size " + "of the device will be used.\n"); + return FALSE; + } + page_size = mkntfs_get_page_size(); + /* + * Set the mft record size. By default this is 1024 but it has to be + * at least as big as a sector and not bigger than a page on the system + * or the NTFS kernel driver will not be able to mount the volume. + * TODO: The mft record size should be user specifiable just like the + * "inode size" can be specified on other Linux/Unix file systems. + */ + vol->mft_record_size = 1024; + if (vol->mft_record_size < (u32)opts.sector_size) + vol->mft_record_size = opts.sector_size; + if (vol->mft_record_size > (unsigned long)page_size) + ntfs_log_warning("Mft record size (%u bytes) exceeds system " + "page size (%li bytes). You will not be able " + "to mount this volume using the NTFS kernel " + "driver.\n", (unsigned)vol->mft_record_size, + page_size); + vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1; + ntfs_log_debug("mft record size = %u bytes\n", + (unsigned)vol->mft_record_size); + /* + * Set the index record size. By default this is 4096 but it has to be + * at least as big as a sector and not bigger than a page on the system + * or the NTFS kernel driver will not be able to mount the volume. + * FIXME: Should we make the index record size to be user specifiable? + */ + vol->indx_record_size = 4096; + if (vol->indx_record_size < (u32)opts.sector_size) + vol->indx_record_size = opts.sector_size; + if (vol->indx_record_size > (unsigned long)page_size) + ntfs_log_warning("Index record size (%u bytes) exceeds system " + "page size (%li bytes). You will not be able " + "to mount this volume using the NTFS kernel " + "driver.\n", vol->indx_record_size, page_size); + vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1; + ntfs_log_debug("index record size = %u bytes\n", + (unsigned)vol->indx_record_size); + if (!winboot) { + ntfs_log_warning("To boot from a device, Windows needs the " + "'partition start sector', the 'sectors per " + "track' and the 'number of heads' to be " + "set.\n"); + ntfs_log_warning("Windows will not be able to boot from this " + "device.\n"); + } + return TRUE; +} + +/** + * mkntfs_initialize_bitmaps - + */ +static BOOL mkntfs_initialize_bitmaps(void) +{ + u64 i; + int mft_bitmap_size; + + /* Determine lcn bitmap byte size and allocate it. */ + g_lcn_bitmap_byte_size = (g_vol->nr_clusters + 7) >> 3; + /* Needs to be multiple of 8 bytes. */ + g_lcn_bitmap_byte_size = (g_lcn_bitmap_byte_size + 7) & ~7; + i = (g_lcn_bitmap_byte_size + g_vol->cluster_size - 1) & + ~(g_vol->cluster_size - 1); + ntfs_log_debug("g_lcn_bitmap_byte_size = %i, allocated = %llu\n", + g_lcn_bitmap_byte_size, i); + g_lcn_bitmap = ntfs_calloc(1, g_lcn_bitmap_byte_size); + if (!g_lcn_bitmap) + return FALSE; + /* + * $Bitmap can overlap the end of the volume. Any bits in this region + * must be set. This region also encompasses the backup boot sector. + */ + for (i = g_vol->nr_clusters; i < (u64)g_lcn_bitmap_byte_size << 3; i++) + ntfs_bit_set(g_lcn_bitmap, i, 1); + /* + * Determine mft_size: (16 (1.2) or 27 (3.0+) mft records) or + * one cluster, whichever is bigger. + */ + if (g_vol->major_ver >= 3) + g_mft_size = 27; + else + g_mft_size = 16; + g_mft_size *= g_vol->mft_record_size; + if (g_mft_size < (s32)g_vol->cluster_size) + g_mft_size = g_vol->cluster_size; + ntfs_log_debug("MFT size = %i (0x%x) bytes\n", g_mft_size, g_mft_size); + /* Determine mft bitmap size and allocate it. */ + mft_bitmap_size = g_mft_size / g_vol->mft_record_size; + /* Convert to bytes, at least one. */ + g_mft_bitmap_byte_size = (mft_bitmap_size + 7) >> 3; + /* Mft bitmap is allocated in multiples of 8 bytes. */ + g_mft_bitmap_byte_size = (g_mft_bitmap_byte_size + 7) & ~7; + ntfs_log_debug("mft_bitmap_size = %i, g_mft_bitmap_byte_size = %i\n", + mft_bitmap_size, g_mft_bitmap_byte_size); + g_mft_bitmap = ntfs_calloc(1, g_mft_bitmap_byte_size); + if (!g_mft_bitmap) + return FALSE; + /* Create runlist for mft bitmap. */ + g_rl_mft_bmp = malloc(2 * sizeof(runlist)); + if (!g_rl_mft_bmp) { + ntfs_log_perror("Failed to allocate internal buffer"); + return FALSE; + } + g_rl_mft_bmp[0].vcn = 0LL; + /* Mft bitmap is right after $Boot's data. */ + i = (8192 + g_vol->cluster_size - 1) / g_vol->cluster_size; + g_rl_mft_bmp[0].lcn = i; + /* + * Size is always one cluster, even though valid data size and + * initialized data size are only 8 bytes. + */ + g_rl_mft_bmp[1].vcn = 1LL; + g_rl_mft_bmp[0].length = 1LL; + g_rl_mft_bmp[1].lcn = -1LL; + g_rl_mft_bmp[1].length = 0LL; + /* Allocate cluster for mft bitmap. */ + ntfs_bit_set(g_lcn_bitmap, i, 1); + return TRUE; +} + +/** + * mkntfs_initialize_rl_mft - + */ +static BOOL mkntfs_initialize_rl_mft(void) +{ + int i, j; + + /* If user didn't specify the mft lcn, determine it now. */ + if (!g_mft_lcn) { + /* + * We start at the higher value out of 16kiB and just after the + * mft bitmap. + */ + g_mft_lcn = g_rl_mft_bmp[0].lcn + g_rl_mft_bmp[0].length; + if (g_mft_lcn * g_vol->cluster_size < 16 * 1024) + g_mft_lcn = (16 * 1024 + g_vol->cluster_size - 1) / + g_vol->cluster_size; + } + ntfs_log_debug("$MFT logical cluster number = 0x%llx\n", g_mft_lcn); + /* Determine MFT zone size. */ + g_mft_zone_end = g_vol->nr_clusters; + switch (opts.mft_zone_multiplier) { /* % of volume size in clusters */ + case 4: + g_mft_zone_end = g_mft_zone_end >> 1; /* 50% */ + break; + case 3: + g_mft_zone_end = g_mft_zone_end * 3 >> 3;/* 37.5% */ + break; + case 2: + g_mft_zone_end = g_mft_zone_end >> 2; /* 25% */ + break; + case 1: + default: + g_mft_zone_end = g_mft_zone_end >> 3; /* 12.5% */ + break; + } + ntfs_log_debug("MFT zone size = %lldkiB\n", g_mft_zone_end << + g_vol->cluster_size_bits >> 10 /* >> 10 == / 1024 */); + /* + * The mft zone begins with the mft data attribute, not at the beginning + * of the device. + */ + g_mft_zone_end += g_mft_lcn; + /* Create runlist for mft. */ + g_rl_mft = malloc(2 * sizeof(runlist)); + if (!g_rl_mft) { + ntfs_log_perror("Failed to allocate internal buffer"); + return FALSE; + } + g_rl_mft[0].vcn = 0LL; + g_rl_mft[0].lcn = g_mft_lcn; + /* rounded up division by cluster size */ + j = (g_mft_size + g_vol->cluster_size - 1) / g_vol->cluster_size; + g_rl_mft[1].vcn = j; + g_rl_mft[0].length = j; + g_rl_mft[1].lcn = -1LL; + g_rl_mft[1].length = 0LL; + /* Allocate clusters for mft. */ + for (i = 0; i < j; i++) + ntfs_bit_set(g_lcn_bitmap, g_mft_lcn + i, 1); + /* Determine mftmirr_lcn (middle of volume). */ + g_mftmirr_lcn = (opts.num_sectors * opts.sector_size >> 1) + / g_vol->cluster_size; + ntfs_log_debug("$MFTMirr logical cluster number = 0x%llx\n", + g_mftmirr_lcn); + /* Create runlist for mft mirror. */ + g_rl_mftmirr = malloc(2 * sizeof(runlist)); + if (!g_rl_mftmirr) { + ntfs_log_perror("Failed to allocate internal buffer"); + return FALSE; + } + g_rl_mftmirr[0].vcn = 0LL; + g_rl_mftmirr[0].lcn = g_mftmirr_lcn; + /* + * The mft mirror is either 4kb (the first four records) or one cluster + * in size, which ever is bigger. In either case, it contains a + * byte-for-byte identical copy of the beginning of the mft (i.e. either + * the first four records (4kb) or the first cluster worth of records, + * whichever is bigger). + */ + j = (4 * g_vol->mft_record_size + g_vol->cluster_size - 1) / g_vol->cluster_size; + g_rl_mftmirr[1].vcn = j; + g_rl_mftmirr[0].length = j; + g_rl_mftmirr[1].lcn = -1LL; + g_rl_mftmirr[1].length = 0LL; + /* Allocate clusters for mft mirror. */ + for (i = 0; i < j; i++) + ntfs_bit_set(g_lcn_bitmap, g_mftmirr_lcn + i, 1); + g_logfile_lcn = g_mftmirr_lcn + j; + ntfs_log_debug("$LogFile logical cluster number = 0x%llx\n", + g_logfile_lcn); + return TRUE; +} + +/** + * mkntfs_initialize_rl_logfile - + */ +static BOOL mkntfs_initialize_rl_logfile(void) +{ + int i, j; + u64 volume_size; + + /* Create runlist for log file. */ + g_rl_logfile = malloc(2 * sizeof(runlist)); + if (!g_rl_logfile) { + ntfs_log_perror("Failed to allocate internal buffer"); + return FALSE; + } + + volume_size = g_vol->nr_clusters << g_vol->cluster_size_bits; + + g_rl_logfile[0].vcn = 0LL; + g_rl_logfile[0].lcn = g_logfile_lcn; + /* + * Determine logfile_size from volume_size (rounded up to a cluster), + * making sure it does not overflow the end of the volume. + */ + if (volume_size < 2048LL * 1024) /* < 2MiB */ + g_logfile_size = 256LL * 1024; /* -> 256kiB */ + else if (volume_size < 4000000LL) /* < 4MB */ + g_logfile_size = 512LL * 1024; /* -> 512kiB */ + else if (volume_size <= 200LL * 1024 * 1024) /* < 200MiB */ + g_logfile_size = 2048LL * 1024; /* -> 2MiB */ + else if (g_vol->major_ver < 3) { + if (volume_size >= 400LL << 20) /* > 400MiB */ + g_logfile_size = 4 << 20; /* -> 4MiB */ + else + g_logfile_size = (volume_size / 100) & + ~(g_vol->cluster_size - 1); + } else { + /* + * FIXME: The $LogFile size is 64 MiB upwards from 12GiB but + * the "200" divider below apparently approximates "100" or + * some other value as the volume size decreases. For example: + * Volume size LogFile size Ratio + * 8799808 46048 191.100 + * 8603248 45072 190.877 + * 7341704 38768 189.375 + * 6144828 32784 187.433 + * 4192932 23024 182.111 + */ + if (volume_size >= 12LL << 30) /* > 12GiB */ + g_logfile_size = 64 << 20; /* -> 64MiB */ + else + g_logfile_size = (volume_size / 200) & + ~(g_vol->cluster_size - 1); + } + j = g_logfile_size / g_vol->cluster_size; + while (g_rl_logfile[0].lcn + j >= g_vol->nr_clusters) { + /* + * $Logfile would overflow volume. Need to make it smaller than + * the standard size. It's ok as we are creating a non-standard + * volume anyway if it is that small. + */ + g_logfile_size >>= 1; + j = g_logfile_size / g_vol->cluster_size; + } + g_logfile_size = (g_logfile_size + g_vol->cluster_size - 1) & + ~(g_vol->cluster_size - 1); + ntfs_log_debug("$LogFile (journal) size = %ikiB\n", + g_logfile_size / 1024); + /* + * FIXME: The 256kiB limit is arbitrary. Should find out what the real + * minimum requirement for Windows is so it doesn't blue screen. + */ + if (g_logfile_size < 256 << 10) { + ntfs_log_error("$LogFile would be created with invalid size. " + "This is not allowed as it would cause Windows " + "to blue screen and during boot.\n"); + return FALSE; + } + g_rl_logfile[1].vcn = j; + g_rl_logfile[0].length = j; + g_rl_logfile[1].lcn = -1LL; + g_rl_logfile[1].length = 0LL; + /* Allocate clusters for log file. */ + for (i = 0; i < j; i++) + ntfs_bit_set(g_lcn_bitmap, g_logfile_lcn + i, 1); + return TRUE; +} + +/** + * mkntfs_initialize_rl_boot - + */ +static BOOL mkntfs_initialize_rl_boot(void) +{ + int i, j; + /* Create runlist for $Boot. */ + g_rl_boot = malloc(2 * sizeof(runlist)); + if (!g_rl_boot) { + ntfs_log_perror("Failed to allocate internal buffer"); + return FALSE; + } + g_rl_boot[0].vcn = 0LL; + g_rl_boot[0].lcn = 0LL; + /* + * $Boot is always 8192 (0x2000) bytes or 1 cluster, whichever is + * bigger. + */ + j = (8192 + g_vol->cluster_size - 1) / g_vol->cluster_size; + g_rl_boot[1].vcn = j; + g_rl_boot[0].length = j; + g_rl_boot[1].lcn = -1LL; + g_rl_boot[1].length = 0LL; + /* Allocate clusters for $Boot. */ + for (i = 0; i < j; i++) + ntfs_bit_set(g_lcn_bitmap, 0LL + i, 1); + return TRUE; +} + +/** + * mkntfs_initialize_rl_bad - + */ +static BOOL mkntfs_initialize_rl_bad(void) +{ + /* Create runlist for $BadClus, $DATA named stream $Bad. */ + g_rl_bad = malloc(2 * sizeof(runlist)); + if (!g_rl_bad) { + ntfs_log_perror("Failed to allocate internal buffer"); + return FALSE; + } + g_rl_bad[0].vcn = 0LL; + g_rl_bad[0].lcn = -1LL; + /* + * $BadClus named stream $Bad contains the whole volume as a single + * sparse runlist entry. + */ + g_rl_bad[1].vcn = g_vol->nr_clusters; + g_rl_bad[0].length = g_vol->nr_clusters; + g_rl_bad[1].lcn = -1LL; + g_rl_bad[1].length = 0LL; + + /* TODO: Mark bad blocks as such. */ + return TRUE; +} + +/** + * mkntfs_fill_device_with_zeroes - + */ +static BOOL mkntfs_fill_device_with_zeroes(void) +{ + /* + * If not quick format, fill the device with 0s. + * FIXME: Except bad blocks! (AIA) + */ + int i; + ssize_t bw; + unsigned long long position, mid_clust; + float progress_inc = (float)g_vol->nr_clusters / 100; + u64 volume_size; + + volume_size = g_vol->nr_clusters << g_vol->cluster_size_bits; + + ntfs_log_progress("Initializing device with zeroes: 0%%"); + mid_clust = (volume_size >> 1) / g_vol->cluster_size; + for (position = 0; position < (unsigned long long)g_vol->nr_clusters; + position++) { + if (!(position % (int)(progress_inc+1))) { + ntfs_log_progress("\b\b\b\b%3.0f%%", position / + progress_inc); + } + bw = mkntfs_write(g_vol->dev, g_buf, g_vol->cluster_size); + if (bw != (ssize_t)g_vol->cluster_size) { + if (bw != -1 || errno != EIO) { + ntfs_log_error("This should not happen.\n"); + return FALSE; + } + if (!position) { + ntfs_log_error("Error: Cluster zero is bad. " + "Cannot create NTFS file " + "system.\n"); + return FALSE; + } + if (position == mid_clust && + (g_vol->major_ver < 1 || + (g_vol->major_ver == 1 && + g_vol->minor_ver < 2))) { + ntfs_log_error("Error: Bad cluster found in " + "location reserved for system " + "file $Boot.\n"); + return FALSE; + } + /* Add the baddie to our bad blocks list. */ + if (!append_to_bad_blocks(position)) + return FALSE; + ntfs_log_quiet("\nFound bad cluster (%lld). Adding to " + "list of bad blocks.\nInitializing " + "device with zeroes: %3.0f%%", position, + position / progress_inc); + /* Seek to next cluster. */ + g_vol->dev->d_ops->seek(g_vol->dev, + ((off_t)position + 1) * + g_vol->cluster_size, SEEK_SET); + } + } + ntfs_log_progress("\b\b\b\b100%%"); + position = (volume_size & (g_vol->cluster_size - 1)) / + opts.sector_size; + for (i = 0; (unsigned long)i < position; i++) { + bw = mkntfs_write(g_vol->dev, g_buf, opts.sector_size); + if (bw != opts.sector_size) { + if (bw != -1 || errno != EIO) { + ntfs_log_error("This should not happen.\n"); + return FALSE; + } else if (i + 1ull == position && + (g_vol->major_ver >= 2 || + (g_vol->major_ver == 1 && + g_vol->minor_ver >= 2))) { + ntfs_log_error("Error: Bad cluster found in " + "location reserved for system " + "file $Boot.\n"); + return FALSE; + } + /* Seek to next sector. */ + g_vol->dev->d_ops->seek(g_vol->dev, + opts.sector_size, SEEK_CUR); + } + } + ntfs_log_progress(" - Done.\n"); + return TRUE; +} + +/** + * mkntfs_sync_index_record + * + * (ERSO) made a function out of this, but the reason for doing that + * disappeared during coding.... + */ +static BOOL mkntfs_sync_index_record(INDEX_ALLOCATION* idx, MFT_RECORD* m, + ntfschar* name, u32 name_len) +{ + int i, err; + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + long long lw; + runlist *rl_index = NULL; + + i = 5 * sizeof(ntfschar); + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_perror("Failed to allocate attribute search context"); + return FALSE; + } + /* FIXME: This should be IGNORE_CASE! */ + if (mkntfs_attr_lookup(AT_INDEX_ALLOCATION, name, name_len, 0, 0, + NULL, 0, ctx)) { + ntfs_attr_put_search_ctx(ctx); + ntfs_log_error("BUG: $INDEX_ALLOCATION attribute not found.\n"); + return FALSE; + } + a = ctx->attr; + rl_index = ntfs_mapping_pairs_decompress(g_vol, a, NULL); + if (!rl_index) { + ntfs_attr_put_search_ctx(ctx); + ntfs_log_error("Failed to decompress runlist of $INDEX_ALLOCATION " + "attribute.\n"); + return FALSE; + } + if (sle64_to_cpu(a->initialized_size) < i) { + ntfs_attr_put_search_ctx(ctx); + free(rl_index); + ntfs_log_error("BUG: $INDEX_ALLOCATION attribute too short.\n"); + return FALSE; + } + ntfs_attr_put_search_ctx(ctx); + i = sizeof(INDEX_BLOCK) - sizeof(INDEX_HEADER) + + le32_to_cpu(idx->index.allocated_size); + err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)idx, i); + if (err) { + free(rl_index); + ntfs_log_error("ntfs_mst_pre_write_fixup() failed while " + "syncing index block.\n"); + return FALSE; + } + lw = ntfs_rlwrite(g_vol->dev, rl_index, (u8*)idx, i, NULL); + free(rl_index); + if (lw != i) { + ntfs_log_error("Error writing $INDEX_ALLOCATION.\n"); + return FALSE; + } + /* No more changes to @idx below here so no need for fixup: */ + /* ntfs_mst_post_write_fixup((NTFS_RECORD*)idx); */ + return TRUE; +} + + +/** + * create_file_volume - + */ +static BOOL create_file_volume(MFT_RECORD *m, MFT_REF root_ref, VOLUME_FLAGS fl) +{ + int i, err; + u8 *sd; + + ntfs_log_verbose("Creating $Volume (mft record 3)\n"); + m = (MFT_RECORD*)(g_buf + 3 * g_vol->mft_record_size); + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_Volume, FILE_Volume), 0LL, 0LL, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$Volume", FILE_NAME_WIN32_AND_DOS); + if (!err) { + init_system_file_sd(FILE_Volume, &sd, &i); + err = add_attr_sd(m, sd, i); + } + if (!err) + err = add_attr_data(m, NULL, 0, 0, 0, NULL, 0); + if (!err) + err = add_attr_vol_name(m, g_vol->vol_name, g_vol->vol_name ? + strlen(g_vol->vol_name) : 0); + if (!err) { + if (fl & VOLUME_IS_DIRTY) + ntfs_log_quiet("Setting the volume dirty so check disk runs " + "on next reboot into Windows.\n"); + err = add_attr_vol_info(m, fl, g_vol->major_ver, g_vol->minor_ver); + } + if (err < 0) { + ntfs_log_error("Couldn't create $Volume: %s\n", strerror(-err)); + return FALSE; + } + return TRUE; +} + +/** + * create_backup_boot_sector + * + * Return 0 on success or -1 if it couldn't be created. + */ +static int create_backup_boot_sector(u8 *buff) +{ + const char *s; + ssize_t bw; + int size, e; + + ntfs_log_verbose("Creating backup boot sector.\n"); + /* + * Write the first max(512, opts.sector_size) bytes from buf to the + * last sector, but limit that to 8192 bytes of written data since that + * is how big $Boot is (and how big our buffer is).. + */ + size = 512; + if (size < opts.sector_size) + size = opts.sector_size; + if (g_vol->dev->d_ops->seek(g_vol->dev, (opts.num_sectors + 1) * + opts.sector_size - size, SEEK_SET) == (off_t)-1) { + ntfs_log_perror("Seek failed"); + goto bb_err; + } + if (size > 8192) + size = 8192; + bw = mkntfs_write(g_vol->dev, buff, size); + if (bw == size) + return 0; + e = errno; + if (bw == -1LL) + s = strerror(e); + else + s = "unknown error"; + /* At least some 2.4 kernels return EIO instead of ENOSPC. */ + if (bw != -1LL || (bw == -1LL && e != ENOSPC && e != EIO)) { + ntfs_log_critical("Couldn't write backup boot sector: %s\n", s); + return -1; + } +bb_err: + ntfs_log_error("Couldn't write backup boot sector. This is due to a " + "limitation in the\nLinux kernel. This is not a major " + "problem as Windows check disk will create the\n" + "backup boot sector when it is run on your next boot " + "into Windows.\n"); + return -1; +} + +/** + * mkntfs_create_root_structures - + */ +static BOOL mkntfs_create_root_structures(void) +{ + NTFS_BOOT_SECTOR *bs; + MFT_RECORD *m; + MFT_REF root_ref; + MFT_REF extend_ref; + int i; + int j; + int err; + u8 *sd; + FILE_ATTR_FLAGS extend_flags; + VOLUME_FLAGS volume_flags = 0; + int nr_sysfiles; + u8 *buf_log = NULL; + int buf_sds_first_size = 0; + int buf_sds_size = 0; + char *buf_sds_init = NULL; + char *buf_sds = NULL; + + ntfs_log_quiet("Creating NTFS volume structures.\n"); + nr_sysfiles = g_vol->major_ver < 3 ? 16 : 27; + /* + * Setup an empty mft record. Note, we can just give 0 as the mft + * reference as we are creating an NTFS 1.2 volume for which the mft + * reference is ignored by ntfs_mft_record_layout(). + * + * Copy the mft record onto all 16 records in the buffer and setup the + * sequence numbers of each system file to equal the mft record number + * of that file (only for $MFT is the sequence number 1 rather than 0). + */ + for (i = 0; i < nr_sysfiles; i++) { + if (ntfs_mft_record_layout(g_vol, 0, m = (MFT_RECORD *)(g_buf + + i * g_vol->mft_record_size))) { + ntfs_log_error("Failed to layout system mft records.\n"); + return FALSE; + } + if (i == 0 || i > 23) + m->sequence_number = cpu_to_le16(1); + else + m->sequence_number = cpu_to_le16(i); + } + /* + * If only one cluster contains all system files then + * fill the rest of it with empty, formatted records. + */ + if (nr_sysfiles * (s32)g_vol->mft_record_size < g_mft_size) { + for (i = nr_sysfiles; + i * (s32)g_vol->mft_record_size < g_mft_size; i++) { + m = (MFT_RECORD *)(g_buf + i * g_vol->mft_record_size); + if (ntfs_mft_record_layout(g_vol, 0, m)) { + ntfs_log_error("Failed to layout mft record.\n"); + return FALSE; + } + m->flags = cpu_to_le16(0); + m->sequence_number = cpu_to_le16(i); + } + } + /* + * Create the 16 system files, adding the system information attribute + * to each as well as marking them in use in the mft bitmap. + */ + for (i = 0; i < nr_sysfiles; i++) { + u32 file_attrs; + + m = (MFT_RECORD*)(g_buf + i * g_vol->mft_record_size); + if (i < 16 || i > 23) { + if (g_vol->major_ver >= 3 && g_vol->minor_ver >= 1) + m->mft_record_number = cpu_to_le32(i); + m->flags |= MFT_RECORD_IN_USE; + ntfs_bit_set(g_mft_bitmap, 0LL + i, 1); + } + file_attrs = FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM; + if (i == FILE_root) { + file_attrs |= FILE_ATTR_ARCHIVE; + if (opts.disable_indexing) + file_attrs |= FILE_ATTR_NOT_CONTENT_INDEXED; + if (opts.enable_compression) + file_attrs |= FILE_ATTR_COMPRESSED; + } + + if (g_vol->major_ver < 3) { + add_attr_std_info(m, file_attrs, + cpu_to_le32(0)); + } else { + /* setting specific security_id flag and */ + /* file permissions for ntfs 3.x */ + if (i == 0 || i == 1 || i == 2 || i == 6 || i == 8 || + i == 10) { + add_attr_std_info(m, file_attrs, + cpu_to_le32(0x0100)); + } else if (i == 9) { + file_attrs |= FILE_ATTR_VIEW_INDEX_PRESENT; + add_attr_std_info(m, file_attrs, + cpu_to_le32(0x0101)); + } else if (i == 11) { + add_attr_std_info(m, file_attrs, + cpu_to_le32(0x0101)); + } else if (i == 24 || i == 25 || i == 26) { + file_attrs |= FILE_ATTR_ARCHIVE; + file_attrs |= FILE_ATTR_VIEW_INDEX_PRESENT; + add_attr_std_info(m, file_attrs, + cpu_to_le32(0x0101)); + } else { + add_attr_std_info(m, file_attrs, + cpu_to_le32(0x00)); + } + } + } + /* The root directory mft reference. */ + root_ref = MK_LE_MREF(FILE_root, FILE_root); + extend_ref = MK_LE_MREF(11,11); + ntfs_log_verbose("Creating root directory (mft record 5)\n"); + m = (MFT_RECORD*)(g_buf + 5 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_DIRECTORY; + m->link_count = cpu_to_le16(le16_to_cpu(m->link_count) + 1); + err = add_attr_file_name(m, root_ref, 0LL, 0LL, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | + FILE_ATTR_I30_INDEX_PRESENT, 0, 0, ".", + FILE_NAME_WIN32_AND_DOS); + if (!err) { + if (g_vol->major_ver == 1) { + init_system_file_sd(FILE_root, &sd, &i); + err = add_attr_sd(m, sd, i); + } else if (NTFS_V3_0(g_vol->major_ver, g_vol->minor_ver)) { + init_system_file_sd(FILE_root, &sd, &i); + err = add_attr_sd(m, sd, i); + } else if (NTFS_V3_1(g_vol->major_ver, g_vol->minor_ver)) { + init_root_sd_31(&sd, &i); + err = add_attr_sd(m, sd, i); + } else { + ntfs_log_error("BUG: Unsupported NTFS version\n"); + return FALSE; + } + } + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$I30", 4, 0, AT_FILE_NAME, + COLLATION_FILE_NAME, g_vol->indx_record_size); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = upgrade_to_large_index(m, "$I30", 4, 0, &g_index_block); + if (!err) { + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_perror("Failed to allocate attribute search context"); + return FALSE; + } + /* There is exactly one file name so this is ok. */ + if (mkntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, + ctx)) { + ntfs_attr_put_search_ctx(ctx); + ntfs_log_error("BUG: $FILE_NAME attribute not found.\n"); + return FALSE; + } + a = ctx->attr; + err = insert_file_link_in_dir_index(g_index_block, root_ref, + (FILE_NAME_ATTR*)((char*)a + + le16_to_cpu(a->value_offset)), + le32_to_cpu(a->value_length)); + ntfs_attr_put_search_ctx(ctx); + } + if (err) { + ntfs_log_error("Couldn't create root directory: %s\n", + strerror(-err)); + return FALSE; + } + /* dump_mft_record(m); */ + /* Add all other attributes, on a per-file basis for clarity. */ + ntfs_log_verbose("Creating $MFT (mft record 0)\n"); + m = (MFT_RECORD*)g_buf; + err = add_attr_data_positioned(m, NULL, 0, 0, 0, g_rl_mft, g_buf, + g_mft_size); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_MFT, 1), g_mft_size, + g_mft_size, FILE_ATTR_HIDDEN | + FILE_ATTR_SYSTEM, 0, 0, "$MFT", + FILE_NAME_WIN32_AND_DOS); + if (!err && g_vol->major_ver == 1) { + init_system_file_sd(FILE_MFT, &sd, &i); + err = add_attr_sd(m, sd, i); + } + /* mft_bitmap is not modified in mkntfs; no need to sync it later. */ + if (!err) + err = add_attr_bitmap_positioned(m, NULL, 0, 0, g_rl_mft_bmp, + g_mft_bitmap, g_mft_bitmap_byte_size); + if (err < 0) { + ntfs_log_error("Couldn't create $MFT: %s\n", strerror(-err)); + return FALSE; + } + /* dump_mft_record(m); */ + + ntfs_log_verbose("Creating $MFTMirr (mft record 1)\n"); + m = (MFT_RECORD*)(g_buf + 1 * g_vol->mft_record_size); + err = add_attr_data_positioned(m, NULL, 0, 0, 0, g_rl_mftmirr, g_buf, + g_rl_mftmirr[0].length * g_vol->cluster_size); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_MFTMirr, FILE_MFTMirr), + g_rl_mftmirr[0].length * g_vol->cluster_size, + g_rl_mftmirr[0].length * g_vol->cluster_size, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$MFTMirr", FILE_NAME_WIN32_AND_DOS); + if (!err && g_vol->major_ver == 1) { + init_system_file_sd(FILE_MFTMirr, &sd, &i); + err = add_attr_sd(m, sd, i); + } + if (err < 0) { + ntfs_log_error("Couldn't create $MFTMirr: %s\n", strerror(-err)); + return FALSE; + } + /* dump_mft_record(m); */ + ntfs_log_verbose("Creating $LogFile (mft record 2)\n"); + m = (MFT_RECORD*)(g_buf + 2 * g_vol->mft_record_size); + buf_log = malloc(g_logfile_size); + if (!buf_log) { + ntfs_log_perror("Failed to allocate internal buffer"); + return FALSE; + } + memset(buf_log, -1, g_logfile_size); + err = add_attr_data_positioned(m, NULL, 0, 0, 0, g_rl_logfile, buf_log, + g_logfile_size); + free(buf_log); + buf_log = NULL; + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_LogFile, FILE_LogFile), + g_logfile_size, g_logfile_size, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$LogFile", FILE_NAME_WIN32_AND_DOS); + if (!err && g_vol->major_ver == 1) { + init_system_file_sd(FILE_LogFile, &sd, &i); + err = add_attr_sd(m, sd, i); + } + if (err < 0) { + ntfs_log_error("Couldn't create $LogFile: %s\n", strerror(-err)); + return FALSE; + } + /* dump_mft_record(m); */ + + ntfs_log_verbose("Creating $AttrDef (mft record 4)\n"); + m = (MFT_RECORD*)(g_buf + 4 * g_vol->mft_record_size); + + err = add_attr_data(m, NULL, 0, 0, 0, (u8*)g_vol->attrdef, g_vol->attrdef_len); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_AttrDef, FILE_AttrDef), + (g_vol->attrdef_len + g_vol->cluster_size - 1) & + ~(g_vol->cluster_size - 1), g_vol->attrdef_len, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$AttrDef", FILE_NAME_WIN32_AND_DOS); + if (!err) { + init_system_file_sd(FILE_AttrDef, &sd, &i); + err = add_attr_sd(m, sd, i); + } + if (err < 0) { + ntfs_log_error("Couldn't create $AttrDef: %s\n", strerror(-err)); + return FALSE; + } + /* dump_mft_record(m); */ + ntfs_log_verbose("Creating $Bitmap (mft record 6)\n"); + m = (MFT_RECORD*)(g_buf + 6 * g_vol->mft_record_size); + /* the data attribute of $Bitmap must be non-resident or otherwise */ + /* windows 2003 will regard the volume as corrupt (ERSO) */ + if (!err) + err = insert_non_resident_attr_in_mft_record(m, + AT_DATA, NULL, 0, 0, 0, + g_lcn_bitmap, g_lcn_bitmap_byte_size); + + + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_Bitmap, FILE_Bitmap), + (g_lcn_bitmap_byte_size + g_vol->cluster_size - 1) & + ~(g_vol->cluster_size - 1), g_lcn_bitmap_byte_size, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$Bitmap", FILE_NAME_WIN32_AND_DOS); + if (!err && g_vol->major_ver == 1) { + init_system_file_sd(FILE_Bitmap, &sd, &i); + err = add_attr_sd(m, sd, i); + } + if (err < 0) { + ntfs_log_error("Couldn't create $Bitmap: %s\n", strerror(-err)); + return FALSE; + } + /* dump_mft_record(m); */ + + ntfs_log_verbose("Creating $Boot (mft record 7)\n"); + m = (MFT_RECORD*)(g_buf + 7 * g_vol->mft_record_size); + bs = ntfs_calloc(1, 8192); + if (!bs) + return FALSE; + memcpy(bs, boot_array, sizeof(boot_array)); + /* + * Create the boot sector in bs. Note, that bs is already zeroed + * in the boot sector section and that it has the NTFS OEM id/magic + * already inserted, so no need to worry about these things. + */ + bs->bpb.bytes_per_sector = cpu_to_le16(opts.sector_size); + bs->bpb.sectors_per_cluster = (u8)(g_vol->cluster_size / + opts.sector_size); + bs->bpb.media_type = 0xf8; /* hard disk */ + bs->bpb.sectors_per_track = cpu_to_le16(opts.sectors_per_track); + ntfs_log_debug("sectors per track = %ld (0x%lx)\n", opts.sectors_per_track, + opts.sectors_per_track); + bs->bpb.heads = cpu_to_le16(opts.heads); + ntfs_log_debug("heads = %ld (0x%lx)\n", opts.heads, opts.heads); + bs->bpb.hidden_sectors = cpu_to_le32(opts.part_start_sect); + ntfs_log_debug("hidden sectors = %llu (0x%llx)\n", opts.part_start_sect, + opts.part_start_sect); + bs->physical_drive = 0x80; /* boot from hard disk */ + bs->extended_boot_signature = 0x80; /* everybody sets this, so we do */ + bs->number_of_sectors = cpu_to_sle64(opts.num_sectors); + bs->mft_lcn = cpu_to_sle64(g_mft_lcn); + bs->mftmirr_lcn = cpu_to_sle64(g_mftmirr_lcn); + if (g_vol->mft_record_size >= g_vol->cluster_size) { + bs->clusters_per_mft_record = g_vol->mft_record_size / + g_vol->cluster_size; + } else { + bs->clusters_per_mft_record = -(ffs(g_vol->mft_record_size) - 1); + if ((u32)(1 << -bs->clusters_per_mft_record) != + g_vol->mft_record_size) { + free(bs); + ntfs_log_error("BUG: calculated clusters_per_mft_record " + "is wrong (= 0x%x)\n", + bs->clusters_per_mft_record); + return FALSE; + } + } + ntfs_log_debug("clusters per mft record = %i (0x%x)\n", + bs->clusters_per_mft_record, + bs->clusters_per_mft_record); + if (g_vol->indx_record_size >= g_vol->cluster_size) { + bs->clusters_per_index_record = g_vol->indx_record_size / + g_vol->cluster_size; + } else { + bs->clusters_per_index_record = -g_vol->indx_record_size_bits; + if ((1 << -bs->clusters_per_index_record) != (s32)g_vol->indx_record_size) { + free(bs); + ntfs_log_error("BUG: calculated clusters_per_index_record " + "is wrong (= 0x%x)\n", + bs->clusters_per_index_record); + return FALSE; + } + } + ntfs_log_debug("clusters per index block = %i (0x%x)\n", + bs->clusters_per_index_record, + bs->clusters_per_index_record); + /* Generate a 64-bit random number for the serial number. */ + bs->volume_serial_number = cpu_to_sle64(((s64)random() << 32) | + ((s64)random() & 0xffffffff)); + /* + * Leave zero for now as NT4 leaves it zero, too. If want it later, see + * ../libntfs/bootsect.c for how to calculate it. + */ + bs->checksum = cpu_to_le32(0); + /* Make sure the bootsector is ok. */ + if (!ntfs_boot_sector_is_ntfs(bs)) { + free(bs); + ntfs_log_error("FATAL: Generated boot sector is invalid!\n"); + return FALSE; + } + err = add_attr_data_positioned(m, NULL, 0, 0, 0, g_rl_boot, (u8*)bs, 8192); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_Boot, FILE_Boot), + (8192 + g_vol->cluster_size - 1) & + ~(g_vol->cluster_size - 1), 8192, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$Boot", FILE_NAME_WIN32_AND_DOS); + if (!err) { + init_system_file_sd(FILE_Boot, &sd, &i); + err = add_attr_sd(m, sd, i); + } + if (err < 0) { + free(bs); + ntfs_log_error("Couldn't create $Boot: %s\n", strerror(-err)); + return FALSE; + } + if (create_backup_boot_sector((u8*)bs)) { + /* + * Pre-2.6 kernels couldn't access the last sector if it was + * odd and we failed to set the device block size to the sector + * size, hence we schedule chkdsk to create it. + */ + volume_flags |= VOLUME_IS_DIRTY; + } + free(bs); + if (!create_file_volume(m, root_ref, volume_flags)) + return FALSE; + ntfs_log_verbose("Creating $BadClus (mft record 8)\n"); + m = (MFT_RECORD*)(g_buf + 8 * g_vol->mft_record_size); + /* FIXME: This should be IGNORE_CASE */ + /* Create a sparse named stream of size equal to the volume size. */ + err = add_attr_data_positioned(m, "$Bad", 4, 0, 0, g_rl_bad, NULL, + g_vol->nr_clusters * g_vol->cluster_size); + if (!err) { + err = add_attr_data(m, NULL, 0, 0, 0, NULL, 0); + } + if (!err) { + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_BadClus, FILE_BadClus), + 0LL, 0LL, FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, + 0, 0, "$BadClus", FILE_NAME_WIN32_AND_DOS); + } + if (!err && g_vol->major_ver == 1) { + init_system_file_sd(FILE_BadClus, &sd, &i); + err = add_attr_sd(m, sd, i); + } + if (err < 0) { + ntfs_log_error("Couldn't create $BadClus: %s\n", strerror(-err)); + return FALSE; + } + + /* dump_mft_record(m); */ + /* create $Quota (1.2) or $Secure (3.0+) */ + if (g_vol->major_ver < 3) { + ntfs_log_verbose("Creating $Quota (mft record 9)\n"); + m = (MFT_RECORD*)(g_buf + 9 * g_vol->mft_record_size); + err = add_attr_data(m, NULL, 0, 0, 0, NULL, 0); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(9, 9), 0LL, 0LL, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, + 0, "$Quota", FILE_NAME_WIN32_AND_DOS); + if (!err) { + init_system_file_sd(FILE_Secure, &sd, &i); + err = add_attr_sd(m, sd, i); + } + if (err < 0) { + ntfs_log_error("Couldn't create $Quota: %s\n", + strerror(-err)); + return FALSE; + } + } else { + ntfs_log_verbose("Creating $Secure (mft record 9)\n"); + m = (MFT_RECORD*)(g_buf + 9 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_VIEW_INDEX; + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(9, 9), 0LL, 0LL, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | + FILE_ATTR_VIEW_INDEX_PRESENT, 0, 0, + "$Secure", FILE_NAME_WIN32_AND_DOS); + if (!err) { + if (g_vol->minor_ver == 0) { + buf_sds_first_size = 0x1E0; + buf_sds_size = 0x40000 + buf_sds_first_size; + buf_sds_init = ntfs_calloc(1, buf_sds_first_size); + if (!buf_sds_init) + return FALSE; + init_secure_30(buf_sds_init); + } else { + buf_sds_first_size = 0xFC; + buf_sds_size = 0x40000 + buf_sds_first_size; + buf_sds_init = ntfs_calloc(1, buf_sds_first_size); + if (!buf_sds_init) + return FALSE; + init_secure_31(buf_sds_init); + } + buf_sds = ntfs_calloc(1, buf_sds_size); + if (!buf_sds) { + free(buf_sds_init); + return FALSE; + } + memcpy(buf_sds, buf_sds_init, buf_sds_first_size); + memcpy(buf_sds + 0x40000, buf_sds_init, + buf_sds_first_size); + err = add_attr_data(m, "$SDS", 4, 0, 0, (u8*)buf_sds, + buf_sds_size); + free(buf_sds); + } + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$SDH", 4, 0, AT_UNUSED, + COLLATION_NTOFS_SECURITY_HASH, + g_vol->indx_record_size); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$SII", 4, 0, AT_UNUSED, + COLLATION_NTOFS_ULONG, g_vol->indx_record_size); + if (!err) + err = initialize_secure(buf_sds_init, buf_sds_first_size, m); + + free (buf_sds_init); + buf_sds_init = NULL; + if (err < 0) { + ntfs_log_error("Couldn't create $Secure: %s\n", + strerror(-err)); + return FALSE; + } + } + /* dump_mft_record(m); */ + ntfs_log_verbose("Creating $UpCase (mft record 0xa)\n"); + m = (MFT_RECORD*)(g_buf + 0xa * g_vol->mft_record_size); + err = add_attr_data(m, NULL, 0, 0, 0, (u8*)g_vol->upcase, + g_vol->upcase_len << 1); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_UpCase, FILE_UpCase), + ((g_vol->upcase_len << 1) + + g_vol->cluster_size - 1) & + ~(g_vol->cluster_size - 1), g_vol->upcase_len << 1, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$UpCase", FILE_NAME_WIN32_AND_DOS); + if (!err && g_vol->major_ver == 1) { + init_system_file_sd(FILE_UpCase, &sd, &i); + err = add_attr_sd(m, sd, i); + } + if (err < 0) { + ntfs_log_error("Couldn't create $UpCase: %s\n", strerror(-err)); + return FALSE; + } + /* dump_mft_record(m); */ + + if (g_vol->major_ver < 3) { + ntfs_log_verbose("Creating empty record, marked as in use " + "(mft record 11)\n"); + m = (MFT_RECORD*)(g_buf + 11 * g_vol->mft_record_size); + err = add_attr_data(m, NULL, 0, 0, 0, NULL, 0); + if (!err) { + init_system_file_sd(11, &sd, &j); + err = add_attr_sd(m, sd, j); + } + if (err < 0) { + ntfs_log_error("Couldn't create system file 11 (0x0b): %s\n", + strerror(-err)); + return FALSE; + } + /* dump_mft_record(m); */ + } else { + ntfs_log_verbose("Creating $Extend (mft record 11)\n"); + /* + * $Extends index must be resident. Otherwise, w2k3 will + * regard the volume as corrupt. (ERSO) + */ + m = (MFT_RECORD*)(g_buf + 11 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_DIRECTORY; + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(11, 11), 0LL, 0LL, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | + FILE_ATTR_I30_INDEX_PRESENT, 0, 0, + "$Extend", FILE_NAME_WIN32_AND_DOS); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$I30", 4, 0, AT_FILE_NAME, + COLLATION_FILE_NAME, g_vol->indx_record_size); + if (err < 0) { + ntfs_log_error("Couldn't create $Extend: %s\n", + strerror(-err)); + return FALSE; + } + } + /* NTFS 1.2 reserved system files (mft records 0xc-0xf) */ + for (i = 0xc; i < 0x10; i++) { + ntfs_log_verbose("Creating system file (mft record 0x%x)\n", i); + m = (MFT_RECORD*)(g_buf + i * g_vol->mft_record_size); + err = add_attr_data(m, NULL, 0, 0, 0, NULL, 0); + if (!err) { + init_system_file_sd(i, &sd, &j); + err = add_attr_sd(m, sd, j); + } + if (err < 0) { + ntfs_log_error("Couldn't create system file %i (0x%x): %s\n", + i, i, strerror(-err)); + return FALSE; + } + /* dump_mft_record(m); */ + } + /* create systemfiles for ntfs volumes (3.1) */ + /* starting with file 24 (ignoring file 16-23) */ + if (g_vol->major_ver >= 3) { + extend_flags = FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | + FILE_ATTR_ARCHIVE | FILE_ATTR_VIEW_INDEX_PRESENT; + ntfs_log_verbose("Creating $Quota (mft record 24)\n"); + m = (MFT_RECORD*)(g_buf + 24 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_4; + m->flags |= MFT_RECORD_IS_VIEW_INDEX; + if (!err) + err = create_hardlink_res((MFT_RECORD*)(g_buf + + 11 * g_vol->mft_record_size), extend_ref, m, + MK_LE_MREF(24, 1), 0LL, 0LL, extend_flags, + 0, 0, "$Quota", FILE_NAME_WIN32_AND_DOS); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$Q", 2, 0, AT_UNUSED, + COLLATION_NTOFS_ULONG, g_vol->indx_record_size); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$O", 2, 0, AT_UNUSED, + COLLATION_NTOFS_SID, g_vol->indx_record_size); + if (!err) + err = initialize_quota(m); + if (err < 0) { + ntfs_log_error("Couldn't create $Quota: %s\n", strerror(-err)); + return FALSE; + } + + ntfs_log_verbose("Creating $ObjId (mft record 25)\n"); + m = (MFT_RECORD*)(g_buf + 25 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_4; + m->flags |= MFT_RECORD_IS_VIEW_INDEX; + if (!err) + err = create_hardlink_res((MFT_RECORD*)(g_buf + + 11 * g_vol->mft_record_size), extend_ref, + m, MK_LE_MREF(25, 1), 0LL, 0LL, + extend_flags, 0, 0, "$ObjId", + FILE_NAME_WIN32_AND_DOS); + + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$O", 2, 0, AT_UNUSED, + COLLATION_NTOFS_ULONGS, g_vol->indx_record_size); + if (err < 0) { + ntfs_log_error("Couldn't create $ObjId: %s\n", strerror(-err)); + return FALSE; + } + + ntfs_log_verbose("Creating $Reparse (mft record 26)\n"); + m = (MFT_RECORD*)(g_buf + 26 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_4; + m->flags |= MFT_RECORD_IS_VIEW_INDEX; + if (!err) + err = create_hardlink_res((MFT_RECORD*)(g_buf + + 11 * g_vol->mft_record_size), + extend_ref, m, MK_LE_MREF(26, 1), + 0LL, 0LL, extend_flags, 0, 0, + "$Reparse", FILE_NAME_WIN32_AND_DOS); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$R", 2, 0, AT_UNUSED, + COLLATION_NTOFS_ULONGS, g_vol->indx_record_size); + if (err < 0) { + ntfs_log_error("Couldn't create $Reparse: %s\n", + strerror(-err)); + return FALSE; + } + } + return TRUE; +} + +/** + * mkntfs_redirect + */ +static int mkntfs_redirect(struct mkntfs_options *opts2) +{ + int result = 1; + ntfs_attr_search_ctx *ctx = NULL; + long long lw, pos; + ATTR_RECORD *a; + MFT_RECORD *m; + int i, err; + + if (!opts2) { + ntfs_log_error("Internal error: invalid parameters to mkntfs_options.\n"); + goto done; + } + /* Initialize the random number generator with the current time. */ + srandom(mkntfs_time().tv_sec); + /* Allocate and initialize ntfs_volume structure g_vol. */ + g_vol = ntfs_volume_alloc(); + if (!g_vol) { + ntfs_log_perror("Could not create volume"); + goto done; + } + /* Transfer some options to the volume. */ + if (opts.label) { + g_vol->vol_name = strdup(opts.label); + if (!g_vol->vol_name) { + ntfs_log_perror("Could not copy volume name"); + goto done; + } + } + if (opts.ver_major) { + g_vol->major_ver = opts.ver_major; + g_vol->minor_ver = opts.ver_minor; + } else { + /* Create NTFS 3.1 (Windows XP) volumes by default. */ + g_vol->major_ver = 3; + g_vol->minor_ver = 1; + } + if (opts.cluster_size >= 0) + g_vol->cluster_size = opts.cluster_size; + /* Length is in unicode characters. */ + g_vol->upcase_len = 65536; + g_vol->upcase = malloc(g_vol->upcase_len * sizeof(ntfschar)); + if (!g_vol->upcase) { + ntfs_log_perror("Could not create upcase structure"); + goto done; + } + init_upcase_table(g_vol->upcase, g_vol->upcase_len * sizeof(ntfschar)); + if (g_vol->major_ver < 3) { + g_vol->attrdef = ntfs_calloc(1, 36000); + if (g_vol->attrdef) { + memcpy(g_vol->attrdef, attrdef_ntfs12_array, + sizeof(attrdef_ntfs12_array)); + g_vol->attrdef_len = 36000; + } + } else { + g_vol->attrdef = malloc(sizeof(attrdef_ntfs3x_array)); + if (g_vol->attrdef) { + memcpy(g_vol->attrdef, attrdef_ntfs3x_array, + sizeof(attrdef_ntfs3x_array)); + g_vol->attrdef_len = sizeof(attrdef_ntfs3x_array); + } + } + if (!g_vol->attrdef) { + ntfs_log_perror("Could not create attrdef structure"); + goto done; + } + /* Open the partition. */ + if (!mkntfs_open_partition(g_vol)) + goto done; + /* + * Decide on the sector size, cluster size, mft record and index record + * sizes as well as the number of sectors/tracks/heads/size, etc. + */ + if (!mkntfs_override_vol_params(g_vol)) + goto done; + /* Initialize $Bitmap and $MFT/$BITMAP related stuff. */ + if (!mkntfs_initialize_bitmaps()) + goto done; + /* Initialize MFT & set g_logfile_lcn. */ + if (!mkntfs_initialize_rl_mft()) + goto done; + /* Initialize $LogFile. */ + if (!mkntfs_initialize_rl_logfile()) + goto done; + /* Initialize $Boot. */ + if (!mkntfs_initialize_rl_boot()) + goto done; + /* Allocate a buffer large enough to hold the mft. */ + g_buf = ntfs_calloc(1, g_mft_size); + if (!g_buf) + goto done; + /* Create runlist for $BadClus, $DATA named stream $Bad. */ + if (!mkntfs_initialize_rl_bad()) + goto done; + /* If not quick format, fill the device with 0s. */ + if (!opts.quick_format) { + if (!mkntfs_fill_device_with_zeroes()) + goto done; + } + /* Create NTFS volume structures. */ + if (!mkntfs_create_root_structures()) + goto done; + /* + * - Do not step onto bad blocks!!! + * - If any bad blocks were specified or found, modify $BadClus, + * allocating the bad clusters in $Bitmap. + * - C&w bootsector backup bootsector (backup in last sector of the + * partition). + * - If NTFS 3.0+, c&w $Secure file and $Extend directory with the + * corresponding special files in it, i.e. $ObjId, $Quota, $Reparse, + * and $UsnJrnl. And others? Or not all necessary? + * - RE: Populate $root with the system files (and $Extend directory if + * applicable). Possibly should move this as far to the top as + * possible and update during each subsequent c&w of each system file. + */ + ntfs_log_verbose("Syncing root directory index record.\n"); + if (!mkntfs_sync_index_record(g_index_block, (MFT_RECORD*)(g_buf + 5 * + g_vol->mft_record_size), NTFS_INDEX_I30, 4)) + goto done; + + ntfs_log_verbose("Syncing $Bitmap.\n"); + m = (MFT_RECORD*)(g_buf + 6 * g_vol->mft_record_size); + + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_perror("Could not create an attribute search context"); + goto done; + } + + if (mkntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("BUG: $DATA attribute not found.\n"); + goto done; + } + + a = ctx->attr; + if (a->non_resident) { + runlist *rl = ntfs_mapping_pairs_decompress(g_vol, a, NULL); + if (!rl) { + ntfs_log_error("ntfs_mapping_pairs_decompress() failed\n"); + goto done; + } + lw = ntfs_rlwrite(g_vol->dev, rl, g_lcn_bitmap, g_lcn_bitmap_byte_size, NULL); + err = errno; + free(rl); + if (lw != g_lcn_bitmap_byte_size) { + ntfs_log_error("ntfs_rlwrite: %s\n", lw == -1 ? + strerror(err) : "unknown error"); + goto done; + } + } else { + memcpy((char*)a + le16_to_cpu(a->value_offset), g_lcn_bitmap, le32_to_cpu(a->value_length)); + } + + /* + * No need to sync $MFT/$BITMAP as that has never been modified since + * its creation. + */ + ntfs_log_verbose("Syncing $MFT.\n"); + pos = g_mft_lcn * g_vol->cluster_size; + lw = 1; + for (i = 0; i < g_mft_size / (s32)g_vol->mft_record_size; i++) { + if (!opts.no_action) + lw = ntfs_mst_pwrite(g_vol->dev, pos, 1, g_vol->mft_record_size, g_buf + i * g_vol->mft_record_size); + if (lw != 1) { + ntfs_log_error("ntfs_mst_pwrite: %s\n", lw == -1 ? + strerror(errno) : "unknown error"); + goto done; + } + pos += g_vol->mft_record_size; + } + ntfs_log_verbose("Updating $MFTMirr.\n"); + pos = g_mftmirr_lcn * g_vol->cluster_size; + lw = 1; + for (i = 0; i < g_rl_mftmirr[0].length * g_vol->cluster_size / g_vol->mft_record_size; i++) { + m = (MFT_RECORD*)(g_buf + i * g_vol->mft_record_size); + /* + * Decrement the usn by one, so it becomes the same as the one + * in $MFT once it is mst protected. - This is as we need the + * $MFTMirr to have the exact same byte by byte content as + * $MFT, rather than just equivalent meaning content. + */ + if (ntfs_mft_usn_dec(m)) { + ntfs_log_error("ntfs_mft_usn_dec"); + goto done; + } + if (!opts.no_action) + lw = ntfs_mst_pwrite(g_vol->dev, pos, 1, g_vol->mft_record_size, g_buf + i * g_vol->mft_record_size); + if (lw != 1) { + ntfs_log_error("ntfs_mst_pwrite: %s\n", lw == -1 ? + strerror(errno) : "unknown error"); + goto done; + } + pos += g_vol->mft_record_size; + } + ntfs_log_verbose("Syncing device.\n"); + if (g_vol->dev->d_ops->sync(g_vol->dev)) { + ntfs_log_error("Syncing device. FAILED"); + goto done; + } + ntfs_log_quiet("mkntfs completed successfully. Have a nice day.\n"); + result = 0; +done: + ntfs_attr_put_search_ctx(ctx); + mkntfs_cleanup(); /* Device is unlocked and closed here */ + return result; +} + + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + int result = 1; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + utils_set_locale(); + + mkntfs_init_options(&opts); /* Set up the options */ + + if (!mkntfs_parse_options(argc, argv, &opts)) /* Read the command line options */ + goto done; + + result = mkntfs_redirect(&opts); +done: + return result; +} + diff --git a/ntfsprogs/ntfscat.8.in b/ntfsprogs/ntfscat.8.in new file mode 100644 index 00000000..2ea3ff87 --- /dev/null +++ b/ntfsprogs/ntfscat.8.in @@ -0,0 +1,137 @@ +.\" Copyright (c) 2003\-2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSCAT 8 "November 2005" "ntfsprogs @VERSION@" +.SH NAME +ntfscat \- print NTFS files and streams on the standard output +.SH SYNOPSIS +[\fIoptions\fR] \fIdevice \fR[\fIfile\fR] +.SH DESCRIPTION +.B ntfscat +will read a file or stream from an NTFS volume and display the contents +on the standard output. +.PP +The case of the filename passed to +.B ntfscat +is ignored. +.SH OPTIONS +Below is a summary of all the options that +.B ntfscat +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-a\fR, \fB\-\-attribute\fR TYPE +Display the contents of a particular attribute type. By default, the unnamed +$DATA attribute will be shown. The attribute can be specified by a number +in decimal or hexadecimal, or by name. +.TS +box; +lB lB lB +l l l. +Hex Decimal Name +0x10 16 "$STANDARD_INFORMATION" +0x20 32 "$ATTRIBUTE_LIST" +0x30 48 "$FILE_NAME" +0x40 64 "$OBJECT_ID" +0x50 80 "$SECURITY_DESCRIPTOR" +0x60 96 "$VOLUME_NAME" +0x70 112 "$VOLUME_INFORMATION" +0x80 128 "$DATA" +0x90 144 "$INDEX_ROOT" +0xA0 160 "$INDEX_ALLOCATION" +0xB0 176 "$BITMAP" +0xC0 192 "$REPARSE_POINT" +0xD0 208 "$EA_INFORMATION" +0xE0 224 "$EA" +0xF0 240 "$PROPERTY_SET" +0x100 256 "$LOGGED_UTILITY_STREAM" +.TE +.sp +.sp +.B Notes +The attribute names may be given without the leading $ symbol. +.br +If you use the $ symbol, you must quote the name to prevent the shell +interpreting the name. +.TP +\fB\-n\fR, \fB\-\-attribute\-name\fR NAME +Display this named attribute, stream. +.TP +\fB\-i\fR, \fB\-\-inode\fR NUM +Specify a file by its inode number instead of its name. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not using a mounted volume. +Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license +.BR ntfscat . +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.SH EXAMPLES +Display the contents of a file in the root of an NTFS volume. +.RS +.sp +.B ntfscat /dev/hda1 boot.ini +.sp +.RE +Display the contents of a file in a subdirectory of an NTFS volume. +.RS +.sp +.B ntfscat /dev/hda1 /winnt/system32/drivers/etc/hosts +.sp +.RE +Display the contents of the $INDEX_ROOT attribute of the root directory (inode +5). +.RS +.sp +.B ntfscat /dev/hda1 \-a INDEX_ROOT \-i 5 | hexdump \-C +.sp +.RE +.SH BUGS +There are no known problems with +.BR ntfscat . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +linux\-ntfs\-dev@lists.sourceforge.net +.hy +.SH AUTHORS +.B ntfscat +was written by Richard Russon, Anton Altaparmakov and Szabolcs Szakacsits. +.SH AVAILABILITY +.B ntfscat +is part of the +.B ntfsprogs +package and is available from: +.br +.nh +http://www.linux\-ntfs.org/content/view/19/37 +.hy +.sp +The manual pages are available online at: +.br +.nh +http://man.linux-ntfs.org/ +.hy +.SH SEE ALSO +.BR ntfsls (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfscat.c b/ntfsprogs/ntfscat.c new file mode 100644 index 00000000..08acc06b --- /dev/null +++ b/ntfsprogs/ntfscat.c @@ -0,0 +1,428 @@ +/** + * ntfscat - Part of the Linux-NTFS project. + * + * Copyright (c) 2003-2005 Richard Russon + * Copyright (c) 2003-2005 Anton Altaparmakov + * Copyright (c) 2003-2005 Szabolcs Szakacsits + * + * This utility will concatenate files and print on the standard output. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 "config.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "ntfscat.h" +/* #include "version.h" */ +#include "utils.h" + +static const char *EXEC_NAME = "ntfscat"; +static struct options opts; + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Concatenate files and print on the " + "standard output.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c) 2003-2005 Richard Russon\n"); + ntfs_log_info("Copyright (c) 2003-2005 Anton Altaparmakov\n"); + ntfs_log_info("Copyright (c) 2003-2005 Szabolcs Szakacsits\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device [file]\n\n" + " -a, --attribute TYPE Display this attribute type\n" + " -n, --attribute-name NAME Display this attribute name\n" + " -i, --inode NUM Display this inode\n\n" + " -f, --force Use less caution\n" + " -h, --help Print this help\n" + " -q, --quiet Less output\n" + " -V, --version Version information\n" + " -v, --verbose More output\n\n", + //" -r --raw Display the compressed or encrypted file", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_attribute - Read an attribute name, or number + * @value: String to be parsed + * @attr: Resulting attribute id (on success) + * + * Read a string representing an attribute. It may be a decimal, octal or + * hexadecimal number, or the attribute name in full. The leading $ sign is + * optional. + * + * Return: 1 Success, a valid attribute name or number + * 0 Error, not an attribute name or number + */ +static int parse_attribute(const char *value, ATTR_TYPES *attr) +{ + static const char *attr_name[] = { + "$STANDARD_INFORMATION", + "$ATTRIBUTE_LIST", + "$FILE_NAME", + "$OBJECT_ID", + "$SECURITY_DESCRIPTOR", + "$VOLUME_NAME", + "$VOLUME_INFORMATION", + "$DATA", + "$INDEX_ROOT", + "$INDEX_ALLOCATION", + "$BITMAP", + "$REPARSE_POINT", + "$EA_INFORMATION", + "$EA", + "$PROPERTY_SET", + "$LOGGED_UTILITY_STREAM", + NULL + }; + + int i; + long num; + + for (i = 0; attr_name[i]; i++) { + if ((strcmp(value, attr_name[i]) == 0) || + (strcmp(value, attr_name[i]+1) == 0)) { + *attr = (ATTR_TYPES) ((i+1)*16); + return 1; + } + } + + num = strtol(value, NULL, 0); + if ((num > 0) && (num < 257)) { + *attr = (ATTR_TYPES) num; + return 1; + } + + return 0; +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char **argv) +{ + static const char *sopt = "-a:fh?i:n:qVv"; + static const struct option lopt[] = { + { "attribute", required_argument, NULL, 'a' }, + { "attribute-name", required_argument, NULL, 'n' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "inode", required_argument, NULL, 'i' }, + { "quiet", no_argument, NULL, 'q' }, + { "version", no_argument, NULL, 'V' }, + { "verbose", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } + }; + + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + ATTR_TYPES attr = AT_UNUSED; + + opterr = 0; /* We'll handle the errors, thank you. */ + + opts.inode = -1; + opts.attr = -1; + opts.attr_name = NULL; + opts.attr_name_len = 0; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) { + opts.device = argv[optind-1]; + } else if (!opts.file) { + opts.file = argv[optind-1]; + } else { + ntfs_log_error("You must specify exactly one file.\n"); + err++; + } + break; + case 'a': + if (opts.attr != (ATTR_TYPES)-1) { + ntfs_log_error("You must specify exactly one attribute.\n"); + } else if (parse_attribute(optarg, &attr) > 0) { + opts.attr = attr; + break; + } else { + ntfs_log_error("Couldn't parse attribute.\n"); + } + err++; + break; + case 'f': + opts.force++; + break; + case 'h': + case '?': + if (strncmp (argv[optind-1], "--log-", 6) == 0) { + if (!ntfs_log_parse_option (argv[optind-1])) + err++; + break; + } + help++; + break; + case 'i': + if (opts.inode != -1) + ntfs_log_error("You must specify exactly one inode.\n"); + else if (utils_parse_size(optarg, &opts.inode, FALSE)) + break; + else + ntfs_log_error("Couldn't parse inode number.\n"); + err++; + break; + + case 'n': + opts.attr_name_len = ntfs_mbstoucs_libntfscompat(optarg, + &opts.attr_name, 0); + if (opts.attr_name_len < 0) { + ntfs_log_perror("Invalid attribute name '%s'", optarg); + usage(); + } + + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'V': + ver++; + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + default: + ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if (opts.device == NULL) { + ntfs_log_error("You must specify a device.\n"); + err++; + + } else if (opts.file == NULL && opts.inode == -1) { + ntfs_log_error("You must specify a file or inode " + "with the -i option.\n"); + err++; + + } else if (opts.file != NULL && opts.inode != -1) { + ntfs_log_error("You can't specify both a file and inode.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose at the " + "same time.\n"); + err++; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + return (!err && !help && !ver); +} + +/** + * index_get_size - Find the INDX block size from the index root + * @inode: Inode of the directory to be checked + * + * Find the size of a directory's INDX block from the INDEX_ROOT attribute. + * + * Return: n Success, the INDX blocks are n bytes in size + * 0 Error, not a directory + */ +static int index_get_size(ntfs_inode *inode) +{ + ATTR_RECORD *attr90; + INDEX_ROOT *iroot; + + attr90 = find_first_attribute(AT_INDEX_ROOT, inode->mrec); + if (!attr90) + return 0; // not a directory + + iroot = (INDEX_ROOT*)((u8*)attr90 + le16_to_cpu(attr90->value_offset)); + + return iroot->index_block_size; +} + +/** + * cat + */ +static int cat(ntfs_volume *vol, ntfs_inode *inode, ATTR_TYPES type, + ntfschar *name, int namelen) +{ + const int bufsize = 4096; + char *buffer; + ntfs_attr *attr; + s64 bytes_read, written; + s64 offset; + u32 block_size; + + buffer = malloc(bufsize); + if (!buffer) + return 1; + + attr = ntfs_attr_open(inode, type, name, namelen); + if (!attr) { + ntfs_log_error("Cannot find attribute type 0x%lx.\n", (long) type); + free(buffer); + return 1; + } + + if ((inode->mft_no < 2) && (attr->type == AT_DATA)) + block_size = vol->mft_record_size; + else if (attr->type == AT_INDEX_ALLOCATION) + block_size = index_get_size(inode); + else + block_size = 0; + + offset = 0; + for (;;) { + if (block_size > 0) { + // These types have fixup + bytes_read = ntfs_attr_mst_pread(attr, offset, 1, block_size, buffer); + bytes_read *= block_size; + } else { + bytes_read = ntfs_attr_pread(attr, offset, bufsize, buffer); + } + //ntfs_log_info("read %lld bytes\n", bytes_read); + if (bytes_read == -1) { + ntfs_log_perror("ERROR: Couldn't read file"); + break; + } + if (!bytes_read) + break; + + written = fwrite(buffer, 1, bytes_read, stdout); + if (written != bytes_read) { + ntfs_log_perror("ERROR: Couldn't output all data!"); + break; + } + offset += bytes_read; + } + + ntfs_attr_close(attr); + free(buffer); + return 0; +} + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + ntfs_volume *vol; + ntfs_inode *inode; + ATTR_TYPES attr; + int result = 1; + + ntfs_log_set_handler(ntfs_log_handler_stderr); + + if (!parse_options(argc, argv)) + return 1; + + utils_set_locale(); + + vol = utils_mount_volume(opts.device, MS_RDONLY, opts.force); + if (!vol) { + ntfs_log_perror("ERROR: couldn't mount volume"); + return 1; + } + + if (opts.inode != -1) + inode = ntfs_inode_open(vol, opts.inode); + else + inode = ntfs_pathname_to_inode(vol, NULL, opts.file); + + if (!inode) { + ntfs_log_perror("ERROR: Couldn't open inode"); + return 1; + } + + attr = AT_DATA; + if (opts.attr != (ATTR_TYPES)-1) + attr = opts.attr; + + result = cat(vol, inode, attr, opts.attr_name, opts.attr_name_len); + + ntfs_inode_close(inode); + ntfs_umount(vol, FALSE); + + return result; +} diff --git a/ntfsprogs/ntfscat.h b/ntfsprogs/ntfscat.h new file mode 100644 index 00000000..80ed9174 --- /dev/null +++ b/ntfsprogs/ntfscat.h @@ -0,0 +1,45 @@ +/* + * ntfscat - Part of the Linux-NTFS project. + * + * Copyright (c) 2003 Richard Russon + * Copyright (c) 2003 Anton Altaparmakov + * + * This utility will concatenate files and print on the standard output. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 _NTFSCAT_H_ +#define _NTFSCAT_H_ + +#include +#include + +struct options { + char *device; /* Device/File to work with */ + char *file; /* File to display */ + s64 inode; /* Inode to work with */ + ATTR_TYPES attr; /* Attribute type to display */ + ntfschar *attr_name; /* Attribute name to display */ + int attr_name_len; /* Attribute name length */ + int force; /* Override common sense */ + int quiet; /* Less output */ + int verbose; /* Extra output */ +}; + +#endif /* _NTFSCAT_H_ */ + + diff --git a/ntfsprogs/ntfsclone.8.in b/ntfsprogs/ntfsclone.8.in new file mode 100644 index 00000000..1a070563 --- /dev/null +++ b/ntfsprogs/ntfsclone.8.in @@ -0,0 +1,366 @@ +.\" Copyright (c) 2003\-2005 Richard Russon. +.\" Copyright (c) 2003\-2006 Szabolcs Szakacsits. +.\" Copyright (c) 2004 Per Olofsson. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSCLONE 8 "February 2006" "ntfsprogs @VERSION@" +.SH NAME +ntfsclone \- Efficiently clone, image, restore or rescue an NTFS +.SH SYNOPSIS +.B ntfsclone +[\fIOPTIONS\fR] +.I SOURCE +.br +.B ntfsclone \-\-save\-image +[\fIOPTIONS\fR] +.I SOURCE +.br +.B ntfsclone \-\-restore\-image +[\fIOPTIONS\fR] +.I SOURCE +.br +.B ntfsclone \-\-metadata +[\fIOPTIONS\fR] +.I SOURCE +.SH DESCRIPTION +.B ntfsclone +will efficiently clone (copy, save, backup, restore) or rescue an NTFS +filesystem to a sparse file, image, device (partition) or standard output. +It works at disk sector level and +copies only the used data. Unused disk space becomes zero (cloning to +sparse file), encoded with control codes (saving in special image format), +left unchanged (cloning to a disk/partition) or +filled with zeros (cloning to standard output). + +.B ntfsclone +can be useful to make backups, an exact snapshot of an NTFS filesystem +and restore it later on, or for developers to test NTFS read/write +functionality, troubleshoot/investigate users' issues using the clone +without the risk of destroying the original filesystem. + +The clone, if not using the special image format, is an exact copy of the +original NTFS filesystem from sector to sector thus it can be also mounted +just like the original NTFS filesystem. +For example if you clone to a file and the kernel has loopback device and +NTFS support then the file can be mounted as +.RS +.sp +.B mount \-t ntfs \-o loop ntfsclone.img /mnt/ntfsclone +.sp +.RE +.SS Windows Cloning +If you want to copy, move or restore a system or boot partition to another +computer, or to a different disk or partition (e.g. hda1\->hda2, hda1\->hdb1 +or to a different disk sector offset) then you will need to take extra care. + +Usually, Windows will not be able to boot, unless you copy, move or restore +NTFS to the same partition which starts at the same sector on the same type +of disk having the same BIOS legacy cylinder setting as the original +partition and disk had. + +The ntfsclone utility guarantees to make an exact copy of NTFS but it +won't deal with booting issues. This is by design: ntfsclone is a +filesystem, not system utility. Its aim is only NTFS cloning, not Windows +cloning. Hereby ntfsclone can be used as a very fast and reliable +build block for Windows clonning but itself it's not enough. You +can find useful tips following the related links on the below page +.br +.nh +http://wiki.linux-ntfs.org/doku.php?id=ntfsclone +.hy +.SS Sparse Files +A file is sparse if it has unallocated blocks (holes). The reported size of +such files are always higher than the disk space consumed by them. The +.BR du +command can tell the real disk space used by a sparse file. +The holes are always read as zeros. All major Linux filesystem like, +ext2, ext3, reiserfs, Reiser4, JFS and XFS, supports +sparse files but for example the ISO 9600 CD\-ROM filesystem doesn't. +.SS Handling Large Sparse Files +As of today Linux provides inadequate support for managing (tar, +cp, gzip, gunzip, bzip2, bunzip2, cat, etc) large sparse files. +The only main Linux filesystem +having support for efficient sparse file handling is XFS by the +XFS_IOC_GETBMAPX +.BR ioctl (2) . +However none of the common utilities supports it. +This means when you tar, cp, gzip, bzip2, etc a large sparse file +they will always read the entire file, even if you use the "sparse support" +options. + +.BR bzip2 (1) +compresses large sparse files much better than +.BR gzip (1) +but it does so +also much slower. Moreover neither of them handles large sparse +files efficiently during uncompression from disk space usage point +of view. + +At present the most efficient way, both speed and space\-wise, to +compress and uncompress large sparse files by common tools +would be using +.BR tar (1) +with the options +.B \-S +(handle sparse files "efficiently") and +.B \-j +(filter the archive through bzip2). Although +.BR tar +still reads and analyses the entire file, it doesn't pass on the +large data blocks having only zeros to filters and it also avoids +writing large amount of zeros to the disk needlessly. But since +.BR tar +can't create an archive from the standard input, you can't do this +in\-place by just reading +.BR ntfsclone +standard output. Even more sadly, using the \-S option results +serious data loss since the end of 2004 and the GNU +.BR tar +maintainers didn't release fixed versions until the present day. +.SS The Special Image Format +It's also possible, actually it's recommended, to save an NTFS filesystem +to a special image format. +Instead of representing unallocated blocks as holes, they are +encoded using control codes. Thus, the image saves space without +requiring sparse file support. The image format is ideal for streaming +filesystem images over the network and similar, and can be used as a +replacement for Ghost or Partition Image if it is combined with other +tools. The downside is that you can't mount the image directly, you +need to restore it first. + +To save an image using the special image format, use the +.B \-s +or the +.B \-\-save\-image +option. To restore an image, use the +.B \-r +or the +.B \-\-restore\-image +option. Note that you can restore images from standard input by +using '\-' as the +.I SOURCE +file. +.SS Metadata\-only Cloning +One of the features of +.BR ntfsclone +is that, it can also save only the NTFS metadata using the option +.B \-m +or +.B \-\-metadata +and the clone still will be +mountable. In this case all non\-metadata file content will be lost and +reading them back will result always zeros. + +The metadata\-only image can be compressed very +well, usually to not more than 1\-8 MB thus it's easy to transfer +for investigation, troubleshooting. + +In this mode of ntfsclone, +.B NONE +of the user's data is saved, including the resident user's data +embedded into metadata. All is filled with zeros. +Moreover all the file timestamps, deleted and unused spaces inside +the metadata are filled with zeros. Thus this mode is inappropriate +for example for forensic analyses. + +Please note, filenames are not wiped out. They might contain +sensitive information, so think twice before sending such an +image to anybody. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsclone +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.B \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.B "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-o\fR, \fB\-\-output\fR FILE +Clone NTFS to the non\-existent +.IR FILE . +If +.I FILE +is '\-' then clone to the +standard output. +.TP +\fB\-O\fR, \fB\-\-overwrite\fR FILE +Clone NTFS to +.IR FILE , +overwriting if exists. +.TP +\fB\-s\fR, \fB\-\-save\-image\fR +Save to the special image format. This is the most efficient way space and +speed\-wise if imaging is done to the standard output, e.g. for image +compression, encryption or streaming through a network. +.TP +\fB\-r\fR, \fB\-\-restore\-image\fR +Restore from the special image format specified by +.I SOURCE +argument. If the +.I SOURCE +is '\-' then the image is read from the standard input. +.TP +\fB\-\-rescue\fR +Ignore disk read errors so disks having bad sectors, e.g. dying disks, can be +rescued the most efficiently way, with minimal stress on them. Ntfsclone works +at the lowest, sector level in this mode too thus more data can be rescued. +The contents of the unreadable sectors are filled by character '?' and the +beginning of such sectors are marked by "BadSectoR\\0". +.TP +\fB\-m\fR, \fB\-\-metadata\fR +Clone +.B ONLY METADATA +(for NTFS experts). Moreover only cloning to a file is allowed. +You can't metadata\-only clone to a device, image or standard output. +.TP +\fB\-\-ignore\-fs\-check\fR +Ignore the result of the filesystem consistency check. This option is allowed +to be used only with the +.B \-\-metadata +option, for the safety of user's data. The clusters which cause the +inconsistency are saved too. +.TP +\fB\-f\fR, \fB\-\-force\fR +Forces ntfsclone to proceed if the filesystem is marked +"dirty" for consistency check. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.SH EXIT CODES +The exit code is 0 on success, non\-zero otherwise. +.SH EXAMPLES +Clone NTFS on /dev/hda1 to /dev/hdc1: +.RS +.sp +.B ntfsclone \-\-overwrite /dev/hdc1 /dev/hda1 +.sp +.RE +Save an NTFS to a file in the special image format: +.RS +.sp +.B ntfsclone \-\-save\-image \-\-output backup.img /dev/hda1 +.sp +.RE +Restore an NTFS from a special image file to its original partition: +.RS +.sp +.B ntfsclone \-\-restore\-image \-\-overwrite /dev/hda1 backup.img +.sp +.RE +Save an NTFS into a compressed image file: +.RS +.sp +.B ntfsclone \-\-save\-image \-o \- /dev/hda1 | gzip \-c > backup.img.gz +.sp +.RE +Restore an NTFS volume from a compressed image file: +.RS +.sp +.B gunzip \-c backup.img.gz | \\\\ +.br +.B ntfsclone \-\-restore\-image \-\-overwrite /dev/hda1 \- +.sp +.RE +Backup an NTFS volume to a remote host, using ssh. Please note, that +ssh may ask for a password! +.RS +.sp +.B ntfsclone \-\-save\-image \-\-output \- /dev/hda1 | \\\\ +.br +.B gzip \-c | ssh host 'cat > backup.img.gz' +.sp +.RE +Restore an NTFS volume from a remote host via ssh. Please note, that +ssh may ask for a password! +.RS +.sp +.B ssh host 'cat backup.img.gz' | gunzip \-c | \\\\ +.br +.B ntfsclone \-\-restore\-image \-\-overwrite /dev/hda1 \- +.sp +.RE +Stream an image file from a web server and restore it to a partition: +.RS +.sp +.B wget \-qO \- http://server/backup.img | \\\\ +.br +.B ntfsclone \-\-restore\-image \-\-overwrite /dev/hda1 \- +.sp +.RE +Clone an NTFS volume to a non\-existent file: +.RS +.sp +.B ntfsclone \-\-output ntfs\-clone.img /dev/hda1 +.sp +.RE +Pack NTFS metadata for NTFS experts. Please note that bzip2 runs +very long but results usually at least 10 times smaller archives +than gzip. +.RS +.sp +.B ntfsclone \-\-metadata \-\-output ntfsmeta.img /dev/hda1 +.br +.B bzip2 ntfsmeta.img +.sp +.RE +Unpacking NTFS metadata into a sparse file: +.RS +.sp +.B bunzip2 \-c ntfsmeta.img.bz2 | \\\\ +.br +.B cp \-\-sparse=always /proc/self/fd/0 ntfsmeta.img +.sp +.RE +.SH KNOWN ISSUES +There are no known problems with +.BR ntfsclone . +If you think you have found a problem then please send an email describing it +to the development team: +.nh +linux\-ntfs\-dev@lists.sourceforge.net +.hy +.sp +Sometimes it might appear ntfsclone froze if the clone is on ReiserFS +and even CTRL\-C won't stop it. This is not a bug in ntfsclone, however +it's due to ReiserFS being extremely inefficient creating large +sparse files and not handling signals during this operation. This +ReiserFS problem was improved in kernel 2.4.22. +XFS, JFS and ext3 don't have this problem. +.hy +.SH AUTHORS +.B ntfsclone +was written by Szabolcs Szakacsits with contributions from Per Olofsson +(special image format support) and Anton Altaparmakov. +.SH AVAILABILITY +.B ntfsclone +is part of the +.B ntfsprogs +package and is available at: +.br +.nh +http://www.linux\-ntfs.org/content/view/19/37 +.hy +.sp +The latest manual pages are available at: +.br +.nh +http://man.linux-ntfs.org/ +.hy +.sp +Additional up-to-date information can be found furthermore at: +.br +.nh +http://wiki.linux-ntfs.org/doku.php?id=ntfsclone +.hy +.SH SEE ALSO +.BR ntfsresize (8) +.BR ntfsprogs (8) +.BR xfs_copy (8) +.BR debugreiserfs (8) +.BR e2image (8) diff --git a/ntfsprogs/ntfsclone.c b/ntfsprogs/ntfsclone.c new file mode 100644 index 00000000..3c687361 --- /dev/null +++ b/ntfsprogs/ntfsclone.c @@ -0,0 +1,1748 @@ +/** + * ntfsclone - Part of the Linux-NTFS project. + * + * Copyright (c) 2003-2006 Szabolcs Szakacsits + * Copyright (c) 2004-2005 Anton Altaparmakov + * Special image format support copyright (c) 2004 Per Olofsson + * + * Clone NTFS data and/or metadata to a sparse file, image, device or stdout. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "config.h" + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_VFS_H +#include +#endif +#ifdef HAVE_SYS_STATVFS_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_STDARG_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +/* #include "version.h" */ + +#if defined(linux) && defined(_IO) && !defined(BLKGETSIZE) +#define BLKGETSIZE _IO(0x12,96) /* Get device size in 512-byte blocks. */ +#endif +#if defined(linux) && defined(_IOR) && !defined(BLKGETSIZE64) +#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* Get device size in bytes. */ +#endif + +static const char *EXEC_NAME = "ntfsclone"; + +static const char *bad_sectors_warning_msg = +"*************************************************************************\n" +"* WARNING: The disk has bad sector. This means physical damage on the *\n" +"* disk surface caused by deterioration, manufacturing faults or other *\n" +"* reason. The reliability of the disk may stay stable or degrade fast. *\n" +"* Use the --rescue option to efficiently save as much data as possible! *\n" +"*************************************************************************\n"; + +static const char *dirty_volume_msg = +"Volume '%s' is scheduled for a check or it was shutdown \n" +"uncleanly. Please boot Windows or use the --force option to progress.\n"; + +struct { + int verbose; + int quiet; + int debug; + int force; + int overwrite; + int std_out; + int blkdev_out; /* output file is block device */ + int metadata; /* metadata only cloning */ + int ignore_fs_check; + int rescue; + int save_image; + int restore_image; + char *output; + char *volume; + struct statfs stfs; +} opt; + +struct bitmap { + s64 size; + u8 *bm; + u8 padding[4]; /* Unused: padding to 64 bit. */ +}; + +struct progress_bar { + u64 start; + u64 stop; + int resolution; + float unit; +}; + +typedef struct { + ntfs_inode *ni; /* inode being processed */ + ntfs_attr_search_ctx *ctx; /* inode attribute being processed */ + s64 inuse; /* number of clusters in use */ +} ntfs_walk_clusters_ctx; + +typedef int (ntfs_walk_op)(ntfs_inode *ni, void *data); + +struct ntfs_walk_cluster { + ntfs_walk_op *inode_op; /* not implemented yet */ + ntfs_walk_clusters_ctx *image; +}; + + +ntfs_volume *vol = NULL; +struct bitmap lcn_bitmap; + +int fd_in; +int fd_out; +FILE *msg_out = NULL; + +int wipe = 0; +unsigned int nr_used_mft_records = 0; +unsigned int wiped_unused_mft_data = 0; +unsigned int wiped_unused_mft = 0; +unsigned int wiped_resident_data = 0; +unsigned int wiped_timestamp_data = 0; + +#define IMAGE_MAGIC "\0ntfsclone-image" +#define IMAGE_MAGIC_SIZE 16 + +struct { + char magic[IMAGE_MAGIC_SIZE]; + u8 major_ver; + u8 minor_ver; + u32 cluster_size; + s64 device_size; + s64 nr_clusters; + s64 inuse; +} __attribute__((__packed__)) image_hdr; + +#define NTFS_MBYTE (1000 * 1000) + +#define ERR_PREFIX "ERROR" +#define PERR_PREFIX ERR_PREFIX "(%d): " +#define NERR_PREFIX ERR_PREFIX ": " + +#define LAST_METADATA_INODE 11 + +#define NTFS_MAX_CLUSTER_SIZE 65536 +#define NTFS_SECTOR_SIZE 512 + +#define rounded_up_division(a, b) (((a) + (b - 1)) / (b)) + +#define read_all(f, p, n) io_all((f), (p), (n), 0) +#define write_all(f, p, n) io_all((f), (p), (n), 1) + +__attribute__((format(printf, 1, 2))) +static void Printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(msg_out, fmt, ap); + va_end(ap); + fflush(msg_out); +} + +__attribute__((format(printf, 1, 2))) +static void perr_printf(const char *fmt, ...) +{ + va_list ap; + int eo = errno; + + Printf(PERR_PREFIX, eo); + va_start(ap, fmt); + vfprintf(msg_out, fmt, ap); + va_end(ap); + Printf(": %s\n", strerror(eo)); + fflush(msg_out); +} + +__attribute__((format(printf, 1, 2))) +static void err_printf(const char *fmt, ...) +{ + va_list ap; + + Printf(NERR_PREFIX); + va_start(ap, fmt); + vfprintf(msg_out, fmt, ap); + va_end(ap); + fflush(msg_out); +} + +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static int err_exit(const char *fmt, ...) +{ + va_list ap; + + Printf(NERR_PREFIX); + va_start(ap, fmt); + vfprintf(msg_out, fmt, ap); + va_end(ap); + fflush(msg_out); + exit(1); +} + +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static int perr_exit(const char *fmt, ...) +{ + va_list ap; + int eo = errno; + + Printf(PERR_PREFIX, eo); + va_start(ap, fmt); + vfprintf(msg_out, fmt, ap); + va_end(ap); + Printf(": %s\n", strerror(eo)); + fflush(msg_out); + exit(1); +} + + +__attribute__((noreturn)) +static void usage(void) +{ + fprintf(stderr, "\nUsage: %s [OPTIONS] SOURCE\n" + " Efficiently clone NTFS to a sparse file, image, device or standard output.\n" + "\n" + " -o, --output FILE Clone NTFS to the non-existent FILE\n" + " -O, --overwrite FILE Clone NTFS to FILE, overwriting if exists\n" + " -s, --save-image Save to the special image format\n" + " -r, --restore-image Restore from the special image format\n" + " --rescue Continue after disk read errors\n" + " -m, --metadata Clone *only* metadata (for NTFS experts)\n" + " --ignore-fs-check Ignore the filesystem check result\n" + " -f, --force Force to progress (DANGEROUS)\n" + " -h, --help Display this help\n" +#ifdef DEBUG + " -d, --debug Show debug information\n" +#endif + "\n" + " If FILE is '-' then send the image to the standard output. If SOURCE is '-'\n" + " and --restore-image is used then read the image from the standard input.\n" + "\n", EXEC_NAME); + fprintf(stderr, "%s%s", ntfs_bugs, ntfs_home); + exit(1); +} + + +static void parse_options(int argc, char **argv) +{ + static const char *sopt = "-dfhmo:O:rs"; + static const struct option lopt[] = { +#ifdef DEBUG + { "debug", no_argument, NULL, 'd' }, +#endif + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "metadata", no_argument, NULL, 'm' }, + { "output", required_argument, NULL, 'o' }, + { "overwrite", required_argument, NULL, 'O' }, + { "restore-image", no_argument, NULL, 'r' }, + { "ignore-fs-check", no_argument, NULL, 'C' }, + { "rescue", no_argument, NULL, 'R' }, + { "save-image", no_argument, NULL, 's' }, + { NULL, 0, NULL, 0 } + }; + + int c; + + memset(&opt, 0, sizeof(opt)); + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (opt.volume) + usage(); + opt.volume = argv[optind-1]; + break; + case 'd': + opt.debug++; + break; + case 'f': + opt.force++; + break; + case 'h': + case '?': + usage(); + case 'm': + opt.metadata++; + break; + case 'O': + opt.overwrite++; + case 'o': + if (opt.output) + usage(); + opt.output = optarg; + break; + case 'r': + opt.restore_image++; + break; + case 'C': + opt.ignore_fs_check++; + break; + case 'R': + opt.rescue++; + break; + case 's': + opt.save_image++; + break; + default: + err_printf("Unknown option '%s'.\n", argv[optind-1]); + usage(); + } + } + + if (opt.output == NULL) { + err_printf("You must specify an output file.\n"); + usage(); + } + + if (strcmp(opt.output, "-") == 0) + opt.std_out++; + + if (opt.volume == NULL) { + err_printf("You must specify a device file.\n"); + usage(); + } + + if (opt.metadata && opt.save_image) + err_exit("Saving only metadata to an image is not " + "supported!\n"); + + if (opt.metadata && opt.restore_image) + err_exit("Restoring only metadata from an image is not " + "supported!\n"); + + if (opt.metadata && opt.std_out) + err_exit("Cloning only metadata to stdout isn't supported!\n"); + + if (opt.ignore_fs_check && !opt.metadata) + err_exit("Filesystem check can be ignored only for metadata " + "cloning!\n"); + + if (opt.save_image && opt.restore_image) + err_exit("Saving and restoring an image at the same time " + "is not supported!\n"); + + if (!opt.std_out) { + struct stat st; + + if (stat(opt.output, &st) == -1) { + if (errno != ENOENT) + perr_exit("Couldn't access '%s'", opt.output); + } else { + if (!opt.overwrite) + err_exit("Output file '%s' already exists.\n" + "Use option --overwrite if you want to" + " replace its content.\n", opt.output); + + if (S_ISBLK(st.st_mode)) { + opt.blkdev_out = 1; + if (opt.metadata) + err_exit("Cloning only metadata to a " + "block device isn't supported!\n"); + } + } + } + + msg_out = stdout; + + /* FIXME: this is a workaround for losing debug info if stdout != stderr + and for the uncontrollable verbose messages in libntfs. Ughhh. */ + if (opt.std_out) + msg_out = stderr; + else if (opt.debug) { + /* Redirect stderr to stdout, note fflush()es are essential! */ + fflush(stdout); + fflush(stderr); + if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) { + perror("Failed to redirect stderr to stdout"); + exit(1); + } + fflush(stdout); + fflush(stderr); + } else { + fflush(stderr); + if (!freopen("/dev/null", "w", stderr)) + perr_exit("Failed to redirect stderr to /dev/null"); + } +} + +static void progress_init(struct progress_bar *p, u64 start, u64 stop, int res) +{ + p->start = start; + p->stop = stop; + p->unit = 100.0 / (stop - start); + p->resolution = res; +} + + +static void progress_update(struct progress_bar *p, u64 current) +{ + float percent = p->unit * current; + + if (current != p->stop) { + if ((current - p->start) % p->resolution) + return; + Printf("%6.2f percent completed\r", percent); + } else + Printf("100.00 percent completed\n"); + fflush(msg_out); +} + +static s64 is_critical_metadata(ntfs_walk_clusters_ctx *image, runlist *rl) +{ + s64 inode = image->ni->mft_no; + + if (inode <= LAST_METADATA_INODE) { + + /* Don't save bad sectors (both $Bad and unnamed are ignored */ + if (inode == FILE_BadClus && image->ctx->attr->type == AT_DATA) + return 0; + + if (inode != FILE_LogFile) + return rl->length; + + if (image->ctx->attr->type == AT_DATA) { + + /* Save at least the first 16 KiB of FILE_LogFile */ + s64 s = (s64)16384 - rl->vcn * vol->cluster_size; + if (s > 0) { + s = rounded_up_division(s, vol->cluster_size); + if (rl->length < s) + s = rl->length; + return s; + } + return 0; + } + } + + if (image->ctx->attr->type != AT_DATA) + return rl->length; + + return 0; +} + + +static int io_all(void *fd, void *buf, int count, int do_write) +{ + int i; + struct ntfs_device *dev = fd; + + while (count > 0) { + if (do_write) + i = write(*(int *)fd, buf, count); + else if (opt.restore_image) + i = read(*(int *)fd, buf, count); + else + i = dev->d_ops->read(dev, buf, count); + if (i < 0) { + if (errno != EAGAIN && errno != EINTR) + return -1; + } else { + count -= i; + buf = i + (char *) buf; + } + } + return 0; +} + + +static void rescue_sector(void *fd, off_t pos, void *buff) +{ + const char *badsector_magic = "BadSectoR\0"; + struct ntfs_device *dev = fd; + + if (opt.restore_image) { + if (lseek(*(int *)fd, pos, SEEK_SET) == (off_t)-1) + perr_exit("lseek"); + } else { + if (vol->dev->d_ops->seek(dev, pos, SEEK_SET) == (off_t)-1) + perr_exit("seek input"); + } + + if (read_all(fd, buff, NTFS_SECTOR_SIZE) == -1) { + Printf("WARNING: Can't read sector at %llu, lost data.\n", + (unsigned long long)pos); + memset(buff, '?', NTFS_SECTOR_SIZE); + memmove(buff, badsector_magic, sizeof(badsector_magic)); + } +} + + +static void copy_cluster(int rescue, u64 rescue_lcn) +{ + char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */ + /* vol is NULL if opt.restore_image is set */ + u32 csize = image_hdr.cluster_size; + void *fd = (void *)&fd_in; + off_t rescue_pos; + + if (!opt.restore_image) { + csize = vol->cluster_size; + fd = vol->dev; + } + + rescue_pos = (off_t)(rescue_lcn * csize); + + if (read_all(fd, buff, csize) == -1) { + + if (errno != EIO) + perr_exit("read_all"); + else if (rescue){ + u32 i; + for (i = 0; i < csize; i += NTFS_SECTOR_SIZE) + rescue_sector(fd, rescue_pos + i, buff + i); + } else { + Printf("%s", bad_sectors_warning_msg); + err_exit("Disk is faulty, can't make full backup!"); + } + } + + if (opt.save_image) { + char cmd = 1; + if (write_all(&fd_out, &cmd, sizeof(cmd)) == -1) + perr_exit("write_all"); + } + + if (write_all(&fd_out, buff, csize) == -1) { + int err = errno; + perr_printf("Write failed"); + if (err == EIO && opt.stfs.f_type == 0x517b) + Printf("Apparently you tried to clone to a remote " + "Windows computer but they don't\nhave " + "efficient sparse file handling by default. " + "Please try a different method.\n"); + exit(1); + } +} + +static void lseek_to_cluster(s64 lcn) +{ + off_t pos; + + pos = (off_t)(lcn * vol->cluster_size); + + if (vol->dev->d_ops->seek(vol->dev, pos, SEEK_SET) == (off_t)-1) + perr_exit("lseek input"); + + if (opt.std_out || opt.save_image) + return; + + if (lseek(fd_out, pos, SEEK_SET) == (off_t)-1) + perr_exit("lseek output"); +} + +static void image_skip_clusters(s64 count) +{ + if (opt.save_image && count > 0) { + char buff[1 + sizeof(count)]; + + buff[0] = 0; + memcpy(buff + 1, &count, sizeof(count)); + + if (write_all(&fd_out, buff, sizeof(buff)) == -1) + perr_exit("write_all"); + } +} + +static void dump_clusters(ntfs_walk_clusters_ctx *image, runlist *rl) +{ + s64 i, len; /* number of clusters to copy */ + + if (opt.std_out || !opt.metadata) + return; + + if (!(len = is_critical_metadata(image, rl))) + return; + + lseek_to_cluster(rl->lcn); + + /* FIXME: this could give pretty suboptimal performance */ + for (i = 0; i < len; i++) + copy_cluster(opt.rescue, rl->lcn + i); +} + +static void clone_ntfs(u64 nr_clusters) +{ + u64 cl, last_cl; /* current and last used cluster */ + void *buf; + u32 csize = vol->cluster_size; + u64 p_counter = 0; + struct progress_bar progress; + + if (opt.save_image) + Printf("Saving NTFS to image ...\n"); + else + Printf("Cloning NTFS ...\n"); + + if ((buf = calloc(1, csize)) == NULL) + perr_exit("clone_ntfs"); + + progress_init(&progress, p_counter, nr_clusters, 100); + + if (opt.save_image) { + if (write_all(&fd_out, &image_hdr, sizeof(image_hdr)) == -1) + perr_exit("write_all"); + } + + for (last_cl = cl = 0; cl < (u64)vol->nr_clusters; cl++) { + + if (ntfs_bit_get(lcn_bitmap.bm, cl)) { + progress_update(&progress, ++p_counter); + lseek_to_cluster(cl); + image_skip_clusters(cl - last_cl - 1); + + copy_cluster(opt.rescue, cl); + last_cl = cl; + continue; + } + + if (opt.std_out && !opt.save_image) { + progress_update(&progress, ++p_counter); + if (write_all(&fd_out, buf, csize) == -1) + perr_exit("write_all"); + } + } + image_skip_clusters(cl - last_cl - 1); +} + +static void write_empty_clusters(s32 csize, s64 count, + struct progress_bar *progress, u64 *p_counter) +{ + s64 i; + char buff[NTFS_MAX_CLUSTER_SIZE]; + + memset(buff, 0, csize); + + for (i = 0; i < count; i++) { + if (write_all(&fd_out, buff, csize) == -1) + perr_exit("write_all"); + progress_update(progress, ++(*p_counter)); + } +} + +static void restore_image(void) +{ + s64 pos = 0, count; + s32 csize = image_hdr.cluster_size; + char cmd; + u64 p_counter = 0; + struct progress_bar progress; + + Printf("Restoring NTFS from image ...\n"); + + progress_init(&progress, p_counter, opt.std_out ? + image_hdr.nr_clusters : image_hdr.inuse, 100); + + while (pos < image_hdr.nr_clusters) { + if (read_all(&fd_in, &cmd, sizeof(cmd)) == -1) + perr_exit("read_all"); + + if (cmd == 0) { + if (read_all(&fd_in, &count, sizeof(count)) == -1) + perr_exit("read_all"); + if (opt.std_out) + write_empty_clusters(csize, count, + &progress, &p_counter); + else { + if (lseek(fd_out, count * csize, SEEK_CUR) + == (off_t)-1) + perr_exit("restore_image: lseek"); + } + pos += count; + } else if (cmd == 1) { + copy_cluster(0, 0); + pos++; + progress_update(&progress, ++p_counter); + } else + err_exit("Invalid command code in image\n"); + } +} + +static void wipe_index_entry_timestams(INDEX_ENTRY *e) +{ + static const struct timespec zero_time = { .tv_sec = 0, .tv_nsec = 0 }; + s64 timestamp = timespec2ntfs(zero_time); + + while (!(e->ie_flags & INDEX_ENTRY_END)) { + + e->key.file_name.creation_time = timestamp; + e->key.file_name.last_data_change_time = timestamp; + e->key.file_name.last_mft_change_time = timestamp; + e->key.file_name.last_access_time = timestamp; + + wiped_timestamp_data += 32; + + e = (INDEX_ENTRY *)((u8 *)e + le16_to_cpu(e->length)); + } +} + +static void wipe_index_allocation_timestamps(ntfs_inode *ni, ATTR_RECORD *attr) +{ + INDEX_ALLOCATION *indexa, *tmp_indexa; + INDEX_ENTRY *entry; + INDEX_ROOT *indexr; + u8 *bitmap, *byte; + int bit; + ntfs_attr *na; + ntfschar *name; + u32 name_len; + + indexr = ntfs_index_root_get(ni, attr); + if (!indexr) { + ntfs_log_perror("Failed to read $INDEX_ROOT attribute"); + return; + } + + if (indexr->type != AT_FILE_NAME) + goto out_indexr; + + name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); + name_len = attr->name_length; + + byte = bitmap = ntfs_attr_readall(ni, AT_BITMAP, name, name_len, NULL); + if (!byte) { + ntfs_log_perror("Failed to read $BITMAP attribute"); + goto out_indexr; + } + + na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, name, name_len); + if (!na) { + ntfs_log_perror("Failed to open $INDEX_ALLOCATION attribute"); + goto out_bitmap; + } + tmp_indexa = indexa = malloc(na->data_size); + if (!tmp_indexa) { + ntfs_log_perror("malloc failed"); + goto out_na; + } + if (ntfs_attr_pread(na, 0, na->data_size, indexa) != na->data_size) { + ntfs_log_perror("Failed to read $INDEX_ALLOCATION attribute"); + goto out_indexa; + } + + bit = 0; + while ((u8 *)tmp_indexa < (u8 *)indexa + na->data_size) { + if (*byte & (1 << bit)) { + if (ntfs_mst_post_read_fixup((NTFS_RECORD *)tmp_indexa, + indexr->index_block_size)) { + ntfs_log_perror("Damaged INDX record"); + goto out_indexa; + } + entry = (INDEX_ENTRY *)((u8 *)tmp_indexa + le32_to_cpu( + tmp_indexa->index.entries_offset) + 0x18); + + wipe_index_entry_timestams(entry); + + if (ntfs_mft_usn_dec((MFT_RECORD *)tmp_indexa)) + perr_exit("ntfs_mft_usn_dec"); + + if (ntfs_mst_pre_write_fixup((NTFS_RECORD *)tmp_indexa, + indexr->index_block_size)) { + ntfs_log_perror("INDX write fixup failed"); + goto out_indexa; + } + } + tmp_indexa = (INDEX_ALLOCATION *)((u8 *)tmp_indexa + + indexr->index_block_size); + bit++; + if (bit > 7) { + bit = 0; + byte++; + } + } + + if (ntfs_rl_pwrite(vol, na->rl, 0, 0, na->data_size, indexa) != na->data_size) + ntfs_log_perror("ntfs_rl_pwrite failed"); +out_indexa: + free(indexa); +out_na: + ntfs_attr_close(na); +out_bitmap: + free(bitmap); +out_indexr: + free(indexr); +} + +static void wipe_index_root_timestamps(ATTR_RECORD *attr, s64 timestamp) +{ + INDEX_ENTRY *entry; + INDEX_ROOT *iroot; + + iroot = (INDEX_ROOT *)((u8 *)attr + le16_to_cpu(attr->value_offset)); + entry = (INDEX_ENTRY *)((u8 *)iroot + + le32_to_cpu(iroot->index.entries_offset) + 0x10); + + while (!(entry->ie_flags & INDEX_ENTRY_END)) { + + if (iroot->type == AT_FILE_NAME) { + + entry->key.file_name.creation_time = timestamp; + entry->key.file_name.last_access_time = timestamp; + entry->key.file_name.last_data_change_time = timestamp; + entry->key.file_name.last_mft_change_time = timestamp; + + wiped_timestamp_data += 32; + + } else if (ntfs_names_are_equal(NTFS_INDEX_Q, + sizeof(NTFS_INDEX_Q) / 2 - 1, + (ntfschar *)((char *)attr + + le16_to_cpu(attr->name_offset)), + attr->name_length, 0, NULL, 0)) { + + QUOTA_CONTROL_ENTRY *quota_q; + + quota_q = (QUOTA_CONTROL_ENTRY *)((u8 *)entry + + entry->data_offset); + /* + * FIXME: no guarantee it's indeed /$Extend/$Quota:$Q. + * For now, as a minimal safeguard, we check only for + * quota version 2 ... + */ + if (le32_to_cpu(quota_q->version) == 2) { + quota_q->change_time = timestamp; + wiped_timestamp_data += 4; + } + } + + entry = (INDEX_ENTRY*)((u8*)entry + le16_to_cpu(entry->length)); + } +} + +#define WIPE_TIMESTAMPS(atype, attr, timestamp) \ +do { \ + atype *ats; \ + ats = (atype *)((char *)(attr) + (attr)->value_offset); \ + \ + ats->creation_time = (timestamp); \ + ats->last_data_change_time = (timestamp); \ + ats->last_mft_change_time= (timestamp); \ + ats->last_access_time = (timestamp); \ + \ + wiped_timestamp_data += 32; \ + \ +} while (0) + +static void wipe_timestamps(ntfs_walk_clusters_ctx *image) +{ + static const struct timespec zero_time = { .tv_sec = 0, .tv_nsec = 0 }; + ATTR_RECORD *a = image->ctx->attr; + s64 timestamp = timespec2ntfs(zero_time); + + if (a->type == AT_FILE_NAME) + WIPE_TIMESTAMPS(FILE_NAME_ATTR, a, timestamp); + + else if (a->type == AT_STANDARD_INFORMATION) + WIPE_TIMESTAMPS(STANDARD_INFORMATION, a, timestamp); + + else if (a->type == AT_INDEX_ROOT) + wipe_index_root_timestamps(a, timestamp); +} + +static void wipe_resident_data(ntfs_walk_clusters_ctx *image) +{ + ATTR_RECORD *a; + u32 i; + int n = 0; + u8 *p; + + a = image->ctx->attr; + p = (u8*)a + le16_to_cpu(a->value_offset); + + if (image->ni->mft_no <= LAST_METADATA_INODE) + return; + + if (a->type != AT_DATA) + return; + + for (i = 0; i < le32_to_cpu(a->value_length); i++) { + if (p[i]) { + p[i] = 0; + n++; + } + } + + wiped_resident_data += n; +} + +static void clone_logfile_parts(ntfs_walk_clusters_ctx *image, runlist *rl) +{ + s64 offset = 0, lcn, vcn; + + while (1) { + + vcn = offset / image->ni->vol->cluster_size; + lcn = ntfs_rl_vcn_to_lcn(rl, vcn); + if (lcn < 0) + break; + + lseek_to_cluster(lcn); + copy_cluster(opt.rescue, lcn); + + if (offset == 0) + offset = NTFS_BLOCK_SIZE >> 1; + else + offset <<= 1; + } +} + +static void walk_runs(struct ntfs_walk_cluster *walk) +{ + int i, j; + runlist *rl; + ATTR_RECORD *a; + ntfs_attr_search_ctx *ctx; + + ctx = walk->image->ctx; + a = ctx->attr; + + if (!a->non_resident) { + if (wipe) { + wipe_resident_data(walk->image); + wipe_timestamps(walk->image); + } + return; + } + + if (wipe && walk->image->ctx->attr->type == AT_INDEX_ALLOCATION) + wipe_index_allocation_timestamps(walk->image->ni, a); + + if (!(rl = ntfs_mapping_pairs_decompress(vol, a, NULL))) + perr_exit("ntfs_decompress_mapping_pairs"); + + for (i = 0; rl[i].length; i++) { + s64 lcn = rl[i].lcn; + s64 lcn_length = rl[i].length; + + if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED) + continue; + + /* FIXME: ntfs_mapping_pairs_decompress should return error */ + if (lcn < 0 || lcn_length < 0) + err_exit("Corrupt runlist in inode %lld attr %x LCN " + "%llx length %llx\n", ctx->ntfs_ino->mft_no, + (unsigned int)le32_to_cpu(a->type), lcn, + lcn_length); + + if (!wipe) + dump_clusters(walk->image, rl + i); + + for (j = 0; j < lcn_length; j++) { + u64 k = (u64)lcn + j; + if (ntfs_bit_get_and_set(lcn_bitmap.bm, k, 1)) + err_exit("Cluster %llu referenced twice!\n" + "You didn't shutdown your Windows" + "properly?\n", (unsigned long long)k); + } + + walk->image->inuse += lcn_length; + } + if (!wipe && !opt.std_out && opt.metadata && + walk->image->ni->mft_no == FILE_LogFile && + walk->image->ctx->attr->type == AT_DATA) + clone_logfile_parts(walk->image, rl); + + free(rl); +} + + +static void walk_attributes(struct ntfs_walk_cluster *walk) +{ + ntfs_attr_search_ctx *ctx; + + if (!(ctx = ntfs_attr_get_search_ctx(walk->image->ni, NULL))) + perr_exit("ntfs_get_attr_search_ctx"); + + while (!ntfs_attrs_walk(ctx)) { + if (ctx->attr->type == AT_END) + break; + + walk->image->ctx = ctx; + walk_runs(walk); + } + + ntfs_attr_put_search_ctx(ctx); +} + + + +static void compare_bitmaps(struct bitmap *a) +{ + s64 i, pos, count; + int mismatch = 0; + u8 bm[NTFS_BUF_SIZE]; + + Printf("Accounting clusters ...\n"); + + pos = 0; + while (1) { + count = ntfs_attr_pread(vol->lcnbmp_na, pos, NTFS_BUF_SIZE, bm); + if (count == -1) + perr_exit("Couldn't get $Bitmap $DATA"); + + if (count == 0) { + if (a->size > pos) + err_exit("$Bitmap size is smaller than expected" + " (%lld != %lld)\n", a->size, pos); + break; + } + + for (i = 0; i < count; i++, pos++) { + s64 cl; /* current cluster */ + + if (a->size <= pos) + goto done; + + if (a->bm[pos] == bm[i]) + continue; + + for (cl = pos * 8; cl < (pos + 1) * 8; cl++) { + char bit; + + bit = ntfs_bit_get(a->bm, cl); + if (bit == ntfs_bit_get(bm, i * 8 + cl % 8)) + continue; + + if (opt.ignore_fs_check) { + lseek_to_cluster(cl); + copy_cluster(opt.rescue, cl); + } + + if (++mismatch > 10) + continue; + + Printf("Cluster accounting failed at %lld " + "(0x%llx): %s cluster in $Bitmap\n", + (long long)cl, (unsigned long long)cl, + bit ? "missing" : "extra"); + } + } + } +done: + if (mismatch) { + Printf("Totally %d cluster accounting mismatches.\n", mismatch); + if (opt.ignore_fs_check) { + Printf("WARNING: The NTFS inconsistency was overruled " + "by the --ignore-fs-check option.\n"); + return; + } + err_exit("Filesystem check failed! Windows wasn't shutdown " + "properly or inconsistent\nfilesystem. Please run " + "chkdsk /f on Windows then reboot it TWICE.\n"); + } +} + + +static int wipe_data(char *p, int pos, int len) +{ + int wiped = 0; + + for (p += pos; --len >= 0;) { + if (p[len]) { + p[len] = 0; + wiped++; + } + } + + return wiped; +} + +static void wipe_unused_mft_data(ntfs_inode *ni) +{ + int unused; + MFT_RECORD *m = ni->mrec; + + /* FIXME: broken MFTMirr update was fixed in libntfs, check if OK now */ + if (ni->mft_no <= LAST_METADATA_INODE) + return; + + unused = le32_to_cpu(m->bytes_allocated) - le32_to_cpu(m->bytes_in_use); + wiped_unused_mft_data += wipe_data((char *)m, + le32_to_cpu(m->bytes_in_use), unused); +} + +static void wipe_unused_mft(ntfs_inode *ni) +{ + int unused; + MFT_RECORD *m = ni->mrec; + + /* FIXME: broken MFTMirr update was fixed in libntfs, check if OK now */ + if (ni->mft_no <= LAST_METADATA_INODE) + return; + + unused = le32_to_cpu(m->bytes_in_use) - sizeof(MFT_RECORD); + wiped_unused_mft += wipe_data((char *)m, sizeof(MFT_RECORD), unused); +} + +static void mft_record_write_with_same_usn(ntfs_volume *volume, ntfs_inode *ni) +{ + if (ntfs_mft_usn_dec(ni->mrec)) + perr_exit("ntfs_mft_usn_dec"); + + if (ntfs_mft_record_write(volume, ni->mft_no, ni->mrec)) + perr_exit("ntfs_mft_record_write"); +} + +static int walk_clusters(ntfs_volume *volume, struct ntfs_walk_cluster *walk) +{ + s64 inode = 0; + s64 last_mft_rec; + ntfs_inode *ni; + struct progress_bar progress; + + Printf("Scanning volume ...\n"); + + last_mft_rec = (volume->mft_na->initialized_size >> + volume->mft_record_size_bits) - 1; + progress_init(&progress, inode, last_mft_rec, 100); + + for (; inode <= last_mft_rec; inode++) { + + int err, deleted_inode; + MFT_REF mref = (MFT_REF)inode; + + progress_update(&progress, inode); + + /* FIXME: Terrible kludge for libntfs not being able to return + a deleted MFT record as inode */ + ni = (ntfs_inode*)calloc(1, sizeof(ntfs_inode)); + if (!ni) + perr_exit("walk_clusters"); + + ni->vol = volume; + + err = ntfs_file_record_read(volume, mref, &ni->mrec, NULL); + if (err == -1) { + free(ni); + continue; + } + + deleted_inode = !(ni->mrec->flags & MFT_RECORD_IN_USE); + + if (deleted_inode) { + + ni->mft_no = MREF(mref); + if (wipe) { + wipe_unused_mft(ni); + wipe_unused_mft_data(ni); + mft_record_write_with_same_usn(volume, ni); + } + } + + free(ni->mrec); + free(ni); + + if (deleted_inode) + continue; + + if ((ni = ntfs_inode_open(volume, mref)) == NULL) { + /* FIXME: continue only if it make sense, e.g. + MFT record not in use based on $MFT bitmap */ + if (errno == EIO || errno == ENOENT) + continue; + perr_exit("Reading inode %lld failed", inode); + } + + if (wipe) + nr_used_mft_records++; + + if (ni->mrec->base_mft_record) + goto out; + + walk->image->ni = ni; + walk_attributes(walk); +out: + if (wipe) { + wipe_unused_mft_data(ni); + mft_record_write_with_same_usn(volume, ni); + } + + if (ntfs_inode_close(ni)) + perr_exit("ntfs_inode_close for inode %lld", inode); + } + + return 0; +} + + +/* + * $Bitmap can overlap the end of the volume. Any bits in this region + * must be set. This region also encompasses the backup boot sector. + */ +static void bitmap_file_data_fixup(s64 cluster, struct bitmap *bm) +{ + for (; cluster < bm->size << 3; cluster++) + ntfs_bit_set(bm->bm, (u64)cluster, 1); +} + + +/* + * Allocate a block of memory with one bit for each cluster of the disk. + * All the bits are set to 0, except those representing the region beyond the + * end of the disk. + */ +static void setup_lcn_bitmap(void) +{ + /* Determine lcn bitmap byte size and allocate it. */ + lcn_bitmap.size = rounded_up_division(vol->nr_clusters, 8); + + if (!(lcn_bitmap.bm = (unsigned char *)calloc(1, lcn_bitmap.size))) + perr_exit("Failed to allocate internal buffer"); + + bitmap_file_data_fixup(vol->nr_clusters, &lcn_bitmap); +} + + +static s64 volume_size(ntfs_volume *volume, s64 nr_clusters) +{ + return nr_clusters * volume->cluster_size; +} + + +static void print_volume_size(const char *str, s64 bytes) +{ + Printf("%s: %lld bytes (%lld MB)\n", str, (long long)bytes, + (long long)rounded_up_division(bytes, NTFS_MBYTE)); +} + + +static void print_disk_usage(u32 cluster_size, s64 nr_clusters, s64 inuse) +{ + s64 total, used; + + total = nr_clusters * cluster_size; + used = inuse * cluster_size; + + Printf("Space in use : %lld MB (%.1f%%) ", + (long long)rounded_up_division(used, NTFS_MBYTE), + 100.0 * ((float)used / total)); + + Printf("\n"); +} + +static void print_image_info(void) +{ + Printf("NTFS volume version: %d.%d\n", + image_hdr.major_ver, image_hdr.minor_ver); + Printf("Cluster size : %u bytes\n", + (unsigned int)image_hdr.cluster_size); + print_volume_size("Image volume size ", + image_hdr.nr_clusters * image_hdr.cluster_size); + Printf("Image device size : %lld bytes\n", image_hdr.device_size); + print_disk_usage(image_hdr.cluster_size, + image_hdr.nr_clusters, + image_hdr.inuse); +} + +static void check_if_mounted(const char *device, unsigned long new_mntflag) +{ + unsigned long mntflag; + + if (ntfs_check_if_mounted(device, &mntflag)) + perr_exit("Failed to check '%s' mount state", device); + + if (mntflag & NTFS_MF_MOUNTED) { + if (!(mntflag & NTFS_MF_READONLY)) + err_exit("Device '%s' is mounted read-write. " + "You must 'umount' it first.\n", device); + if (!new_mntflag) + err_exit("Device '%s' is mounted. " + "You must 'umount' it first.\n", device); + } +} + +/** + * mount_volume - + * + * First perform some checks to determine if the volume is already mounted, or + * is dirty (Windows wasn't shutdown properly). If everything is OK, then mount + * the volume (load the metadata into memory). + */ +static void mount_volume(unsigned long new_mntflag) +{ + check_if_mounted(opt.volume, new_mntflag); + + if (!(vol = ntfs_mount(opt.volume, new_mntflag))) { + + int err = errno; + + perr_printf("Opening '%s' as NTFS failed", opt.volume); + if (err == EINVAL) { + Printf("Apparently device '%s' doesn't have a " + "valid NTFS. Maybe you selected\nthe whole " + "disk instead of a partition (e.g. /dev/hda, " + "not /dev/hda1)?\n", opt.volume); + } + exit(1); + } + + if (vol->flags & VOLUME_IS_DIRTY) + if (opt.force-- <= 0) + err_exit(dirty_volume_msg, opt.volume); + + if (NTFS_MAX_CLUSTER_SIZE < vol->cluster_size) + err_exit("Cluster size %u is too large!\n", + (unsigned int)vol->cluster_size); + + Printf("NTFS volume version: %d.%d\n", vol->major_ver, vol->minor_ver); + if (ntfs_version_is_supported(vol)) + perr_exit("Unknown NTFS version"); + + Printf("Cluster size : %u bytes\n", + (unsigned int)vol->cluster_size); + print_volume_size("Current volume size", + volume_size(vol, vol->nr_clusters)); +} + +struct ntfs_walk_cluster backup_clusters = { NULL, NULL }; + +static int device_offset_valid(int fd, s64 ofs) +{ + char ch; + + if (lseek(fd, ofs, SEEK_SET) >= 0 && read(fd, &ch, 1) == 1) + return 0; + return -1; +} + +static s64 device_size_get(int fd) +{ + s64 high, low; +#ifdef BLKGETSIZE64 + { u64 size; + + if (ioctl(fd, BLKGETSIZE64, &size) >= 0) { + ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu " + "(0x%llx).\n", (unsigned long long)size, + (unsigned long long)size); + return (s64)size; + } + } +#endif +#ifdef BLKGETSIZE + { unsigned long size; + + if (ioctl(fd, BLKGETSIZE, &size) >= 0) { + ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu " + "(0x%lx).\n", size, size); + return (s64)size * 512; + } + } +#endif +#ifdef FDGETPRM + { struct floppy_struct this_floppy; + + if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) { + ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu " + "(0x%lx).\n", this_floppy.size, + this_floppy.size); + return (s64)this_floppy.size * 512; + } + } +#endif + /* + * We couldn't figure it out by using a specialized ioctl, + * so do binary search to find the size of the device. + */ + low = 0LL; + for (high = 1024LL; !device_offset_valid(fd, high); high <<= 1) + low = high; + while (low < high - 1LL) { + const s64 mid = (low + high) / 2; + + if (!device_offset_valid(fd, mid)) + low = mid; + else + high = mid; + } + lseek(fd, 0LL, SEEK_SET); + return (low + 1LL); +} + +static void fsync_clone(int fd) +{ + Printf("Syncing ...\n"); + if (fsync(fd) && errno != EINVAL) + perr_exit("fsync"); +} + +static void set_filesize(s64 filesize) +{ + long fs_type = 0; /* Unknown filesystem type */ + + if (fstatfs(fd_out, &opt.stfs) == -1) + Printf("WARNING: Couldn't get filesystem type: " + "%s\n", strerror(errno)); + else + fs_type = opt.stfs.f_type; + + if (fs_type == 0x52654973) + Printf("WARNING: You're using ReiserFS, it has very poor " + "performance creating\nlarge sparse files. The next " + "operation might take a very long time!\n" + "Creating sparse output file ...\n"); + else if (fs_type == 0x517b) + Printf("WARNING: You're using SMBFS and if the remote share " + "isn't Samba but a Windows\ncomputer then the clone " + "operation will be very inefficient and may fail!\n"); + + if (ftruncate(fd_out, filesize) == -1) { + int err = errno; + perr_printf("ftruncate failed for file '%s'", opt.output); + if (fs_type) + Printf("Destination filesystem type is 0x%lx.\n", + (unsigned long)fs_type); + if (err == E2BIG) { + Printf("Your system or the destination filesystem " + "doesn't support large files.\n"); + if (fs_type == 0x517b) { + Printf("SMBFS needs minimum Linux kernel " + "version 2.4.25 and\n the 'lfs' option" + "\nfor smbmount to have large " + "file support.\n"); + } + } else if (err == EPERM) { + Printf("Apparently the destination filesystem doesn't " + "support sparse files.\nYou can overcome this " + "by using the more efficient --save-image " + "option\nof ntfsclone. Use the --restore-image " + "option to restore the image.\n"); + } + exit(1); + } +} + +static s64 open_image(void) +{ + if (strcmp(opt.volume, "-") == 0) { + if ((fd_in = fileno(stdin)) == -1) + perr_exit("fileno for stdout failed"); + } else { + if ((fd_in = open(opt.volume, O_RDONLY)) == -1) + perr_exit("failed to open image"); + } + + if (read_all(&fd_in, &image_hdr, sizeof(image_hdr)) == -1) + perr_exit("read_all"); + + if (memcmp(image_hdr.magic, IMAGE_MAGIC, IMAGE_MAGIC_SIZE) != 0) + err_exit("Input file is not an image! (invalid magic)\n"); + + return image_hdr.device_size; +} + +static s64 open_volume(void) +{ + s64 device_size; + + mount_volume(MS_RDONLY); + + device_size = ntfs_device_size_get(vol->dev, 1); + if (device_size <= 0) + err_exit("Couldn't get device size (%lld)!\n", device_size); + + print_volume_size("Current device size", device_size); + + if (device_size < vol->nr_clusters * vol->cluster_size) + err_exit("Current NTFS volume size is bigger than the device " + "size (%lld)!\nCorrupt partition table or incorrect " + "device partitioning?\n", device_size); + + return device_size; +} + +static void initialise_image_hdr(s64 device_size, s64 inuse) +{ + memcpy(image_hdr.magic, IMAGE_MAGIC, IMAGE_MAGIC_SIZE); + image_hdr.major_ver = vol->major_ver; + image_hdr.minor_ver = vol->minor_ver; + image_hdr.cluster_size = vol->cluster_size; + image_hdr.device_size = device_size; + image_hdr.nr_clusters = vol->nr_clusters; + image_hdr.inuse = inuse; +} + +static void check_output_device(s64 input_size) +{ + if (opt.blkdev_out) { + s64 dest_size = device_size_get(fd_out); + if (dest_size < input_size) + err_exit("Output device is too small (%lld) to fit the " + "NTFS image (%lld).\n", dest_size, input_size); + + check_if_mounted(opt.output, 0); + } else + set_filesize(input_size); +} + +static ntfs_attr_search_ctx *attr_get_search_ctx(ntfs_inode *ni) +{ + ntfs_attr_search_ctx *ret; + + if ((ret = ntfs_attr_get_search_ctx(ni, NULL)) == NULL) + perr_printf("ntfs_attr_get_search_ctx"); + + return ret; +} + +/** + * lookup_data_attr + * + * Find the $DATA attribute (with or without a name) for the given ntfs inode. + */ +static ntfs_attr_search_ctx *lookup_data_attr(ntfs_inode *ni, const char *aname) +{ + ntfs_attr_search_ctx *ctx; + ntfschar *ustr; + int len = 0; + + if ((ctx = attr_get_search_ctx(ni)) == NULL) + return NULL; + + if ((ustr = ntfs_str2ucs(aname, &len)) == NULL) { + perr_printf("Couldn't convert '%s' to Unicode", aname); + goto error_out; + } + + if (ntfs_attr_lookup(AT_DATA, ustr, len, 0, 0, NULL, 0, ctx)) { + perr_printf("ntfs_attr_lookup"); + goto error_out; + } + ntfs_ucsfree(ustr); + return ctx; +error_out: + ntfs_attr_put_search_ctx(ctx); + return NULL; +} + +static void ignore_bad_clusters(ntfs_walk_clusters_ctx *image) +{ + ntfs_inode *ni; + ntfs_attr_search_ctx *ctx = NULL; + runlist *rl, *rl_bad; + s64 nr_bad_clusters = 0; + + if (!(ni = ntfs_inode_open(vol, FILE_BadClus))) + perr_exit("ntfs_open_inode"); + + if ((ctx = lookup_data_attr(ni, "$Bad")) == NULL) + exit(1); + + if (!(rl_bad = ntfs_mapping_pairs_decompress(vol, ctx->attr, NULL))) + perr_exit("ntfs_mapping_pairs_decompress"); + + for (rl = rl_bad; rl->length; rl++) { + s64 lcn = rl->lcn; + + if (lcn == LCN_HOLE || lcn < 0) + continue; + + for (; lcn < rl->lcn + rl->length; lcn++, nr_bad_clusters++) { + if (ntfs_bit_get_and_set(lcn_bitmap.bm, lcn, 0)) + image->inuse--; + } + } + if (nr_bad_clusters) + Printf("WARNING: The disk has %lld or more bad sectors" + " (hardware faults).\n", nr_bad_clusters); + free(rl_bad); + + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_close(ni)) + perr_exit("ntfs_inode_close failed for $BadClus"); +} + +static void check_dest_free_space(u64 src_bytes) +{ + u64 dest_bytes; + struct statvfs stvfs; + + if (opt.metadata || opt.blkdev_out || opt.std_out) + return; + /* + * TODO: save_image needs a bit more space than src_bytes + * due to the free space encoding overhead. + */ + if (fstatvfs(fd_out, &stvfs) == -1) { + Printf("WARNING: Unknown free space on the destination: %s\n", + strerror(errno)); + return; + } + + dest_bytes = (u64)stvfs.f_frsize * stvfs.f_bfree; + if (!dest_bytes) + dest_bytes = (u64)stvfs.f_bsize * stvfs.f_bfree; + + if (dest_bytes < src_bytes) + err_exit("Destination doesn't have enough free space: " + "%llu MB < %llu MB\n", + rounded_up_division(dest_bytes, NTFS_MBYTE), + rounded_up_division(src_bytes, NTFS_MBYTE)); +} + +int main(int argc, char **argv) +{ + ntfs_walk_clusters_ctx image; + s64 device_size; /* input device size in bytes */ + s64 ntfs_size; + unsigned int wiped_total = 0; + + /* print to stderr, stdout can be an NTFS image ... */ + fprintf(stderr, "%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION); + msg_out = stderr; + + parse_options(argc, argv); + + utils_set_locale(); + + if (opt.restore_image) { + device_size = open_image(); + ntfs_size = image_hdr.nr_clusters * image_hdr.cluster_size; + } else { + device_size = open_volume(); + ntfs_size = vol->nr_clusters * vol->cluster_size; + } + ntfs_size += 512; /* add backup boot sector */ + + if (opt.std_out) { + if ((fd_out = fileno(stdout)) == -1) + perr_exit("fileno for stdout failed"); + } else { + /* device_size_get() might need to read() */ + int flags = O_RDWR; + + if (!opt.blkdev_out) { + flags |= O_CREAT | O_TRUNC; + if (!opt.overwrite) + flags |= O_EXCL; + } + + if ((fd_out = open(opt.output, flags, S_IRWXU)) == -1) + perr_exit("Opening file '%s' failed", opt.output); + + if (!opt.save_image) + check_output_device(ntfs_size); + } + + if (opt.restore_image) { + print_image_info(); + restore_image(); + fsync_clone(fd_out); + exit(0); + } + + setup_lcn_bitmap(); + memset(&image, 0, sizeof(image)); + backup_clusters.image = ℑ + + walk_clusters(vol, &backup_clusters); + compare_bitmaps(&lcn_bitmap); + print_disk_usage(vol->cluster_size, vol->nr_clusters, image.inuse); + + check_dest_free_space(vol->cluster_size * image.inuse); + + ignore_bad_clusters(&image); + + if (opt.save_image) + initialise_image_hdr(device_size, image.inuse); + + /* FIXME: save backup boot sector */ + + if (opt.std_out || !opt.metadata) { + s64 nr_clusters_to_save = image.inuse; + if (opt.std_out && !opt.save_image) + nr_clusters_to_save = vol->nr_clusters; + + clone_ntfs(nr_clusters_to_save); + fsync_clone(fd_out); + exit(0); + } + + wipe = 1; + opt.volume = opt.output; + /* 'force' again mount for dirty volumes (e.g. after resize). + FIXME: use mount flags to avoid potential side-effects in future */ + opt.force++; + mount_volume(0 /*MS_NOATIME*/); + + free(lcn_bitmap.bm); + setup_lcn_bitmap(); + memset(&image, 0, sizeof(image)); + backup_clusters.image = ℑ + + walk_clusters(vol, &backup_clusters); + + Printf("Num of MFT records = %10lld\n", + (long long)vol->mft_na->initialized_size >> + vol->mft_record_size_bits); + Printf("Num of used MFT records = %10u\n", nr_used_mft_records); + + Printf("Wiped unused MFT data = %10u\n", wiped_unused_mft_data); + Printf("Wiped deleted MFT data = %10u\n", wiped_unused_mft); + Printf("Wiped resident user data = %10u\n", wiped_resident_data); + Printf("Wiped timestamp data = %10u\n", wiped_timestamp_data); + + wiped_total += wiped_unused_mft_data; + wiped_total += wiped_unused_mft; + wiped_total += wiped_resident_data; + wiped_total += wiped_timestamp_data; + Printf("Wiped totally = %10u\n", wiped_total); + + fsync_clone(fd_out); + exit(0); +} diff --git a/ntfsprogs/ntfscluster.8.in b/ntfsprogs/ntfscluster.8.in new file mode 100644 index 00000000..0640b2a2 --- /dev/null +++ b/ntfsprogs/ntfscluster.8.in @@ -0,0 +1,129 @@ +.\" Copyright (c) 2003\-2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSCLUSTER 8 "November 2005" "ntfsprogs @VERSION@" +.SH NAME +ntfscluster \- identify files in a specified region of an NTFS volume. +.SH SYNOPSIS +.B ntfscluster +[\fIoptions\fR] \fIdevice\fR +.SH DESCRIPTION +.B ntfscluster +has three modes of operation: +.IR info , +.I sector +and +.IR cluster . +.SS Info +.PP +The default mode, +.I info +is currently not implemented. It will display general information about the +NTFS volume when it is working. +.SS Sector +.PP +The +.I sector +mode will display a list of files that have data in the specified range of +sectors. +.SS Cluster +The +.I cluster +mode will display a list of files that have data in the specified range of +clusters. When the cluster size is one sector, this will be equivalent to the +.I sector +mode of operation. +.SH OPTIONS +Below is a summary of all the options that +.B ntfscluster +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-c\fR, \fB\-\-cluster\fR RANGE +Any files whose data is in this range of clusters will be displayed. +.TP +\fB\-F\fR, \fB\-\-filename\fR NAME +Show information about this file. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not working with a mounted +volume. Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-I\fR, \fB\-\-inode\fR NUM +Show information about this inode. +.TP +\fB\-i\fR, \fB\-\-info\fR +This option is not yet implemented. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Reduce the amount of output to a minimum. Naturally, it doesn't make sense to +combine this option with +.TP +\fB\-s\fR, \fB\-\-sector\fR RANGE +Any files whose data is in this range of sectors will be displayed. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Increase the amount of output that +.B ntfscluster +prints. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license for +.BR ntfscluster . +.SH EXAMPLES +Get some information about the volume /dev/hda1. +.RS +.sp +.B ntfscluster /dev/hda1 +.sp +.RE +Look for files in the first 500 clusters of /dev/hda1. +.RS +.sp +.B ntfscluster \-c 0\-500 /dev/hda1 +.sp +.RE +.SH BUGS +The +.I info +mode isn't implemented yet. +.B ntfscluster +is quite limited, but it has no known bugs. If you find a bug please send an +email describing the problem to the development team: +.br +.nh +linux\-ntfs\-dev@lists.sourceforge.net +.hy +.SH AUTHORS +.B ntfscluster +was written by Richard Russon, with contributions from Anton Altaparmakov. +.SH AVAILABILITY +.B ntfscluster +is part of the +.B ntfsprogs +package and is available from: +.br +.nh +http://www.linux\-ntfs.org/content/view/19/37 +.hy +.sp +The manual pages are available online at: +.br +.nh +http://man.linux-ntfs.org/ +.hy +.SH SEE ALSO +.BR ntfsinfo (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfscluster.c b/ntfsprogs/ntfscluster.c new file mode 100644 index 00000000..c34c17f7 --- /dev/null +++ b/ntfsprogs/ntfscluster.c @@ -0,0 +1,563 @@ +/** + * ntfscluster - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2003 Richard Russon + * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2005-2006 Szabolcs Szakacsits + * + * This utility will locate the owner of any given sector or cluster. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 "config.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "ntfscluster.h" +#include "utils.h" +#include "cluster.h" +/* #include "version.h" */ + +static const char *EXEC_NAME = "ntfscluster"; +static struct options opts; + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Find the owner of any given sector or " + "cluster.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c) 2002-2003 Richard Russon\n"); + ntfs_log_info("Copyright (c) 2005 Anton Altaparmakov\n"); + ntfs_log_info("Copyright (c) 2005-2006 Szabolcs Szakacsits\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device\n" + " -i, --info Print information about the volume (default)\n" + "\n" + " -c, --cluster RANGE Look for objects in this range of clusters\n" + " -s, --sector RANGE Look for objects in this range of sectors\n" + " -I, --inode NUM Show information about this inode\n" + " -F, --filename NAME Show information about this file\n" + /* " -l, --last Find the last file on the volume\n" */ + "\n" + " -f, --force Use less caution\n" + " -q, --quiet Less output\n" + " -v, --verbose More output\n" + " -V, --version Version information\n" + " -h, --help Print this help\n\n", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char **argv) +{ + static const char *sopt = "-c:F:fh?I:ilqs:vV"; + static const struct option lopt[] = { + { "cluster", required_argument, NULL, 'c' }, + { "filename", required_argument, NULL, 'F' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "info", no_argument, NULL, 'i' }, + { "inode", required_argument, NULL, 'I' }, + { "last", no_argument, NULL, 'l' }, + { "quiet", no_argument, NULL, 'q' }, + { "sector", required_argument, NULL, 's' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + char *end = NULL; + + opterr = 0; /* We'll handle the errors, thank you. */ + + opts.action = act_none; + opts.range_begin = -1; + opts.range_end = -1; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) { + opts.device = argv[optind-1]; + } else { + opts.device = NULL; + err++; + } + break; + + case 'c': + if ((opts.action == act_none) && + (utils_parse_range(optarg, &opts.range_begin, &opts.range_end, FALSE))) + opts.action = act_cluster; + else + opts.action = act_error; + break; + case 'F': + if (opts.action == act_none) { + opts.action = act_file; + opts.filename = optarg; + } else { + opts.action = act_error; + } + break; + case 'f': + opts.force++; + break; + case 'h': + case '?': + if (strncmp (argv[optind-1], "--log-", 6) == 0) { + if (!ntfs_log_parse_option (argv[optind-1])) + err++; + break; + } + help++; + break; + case 'I': + if (opts.action == act_none) { + opts.action = act_inode; + opts.inode = strtol(optarg, &end, 0); + if (end && *end) + err++; + } else { + opts.action = act_error; + } + break; + case 'i': + if (opts.action == act_none) + opts.action = act_info; + else + opts.action = act_error; + break; + case 'l': + if (opts.action == act_none) + opts.action = act_last; + else + opts.action = act_error; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 's': + if ((opts.action == act_none) && + (utils_parse_range(optarg, &opts.range_begin, &opts.range_end, FALSE))) + opts.action = act_sector; + else + opts.action = act_error; + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + ver++; + break; + default: + if ((optopt == 'c') || (optopt == 's')) + ntfs_log_error("Option '%s' requires an argument.\n", argv[optind-1]); + else + ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if (opts.action == act_none) + opts.action = act_info; + if (opts.action == act_info) + opts.quiet = 0; + + if (opts.device == NULL) { + if (argc > 1) + ntfs_log_error("You must specify exactly one device.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose at the same time.\n"); + err++; + } + + if (opts.action == act_error) { + ntfs_log_error("You may only specify one action: --info, --cluster, --sector or --last.\n"); + err++; + } else if (opts.range_begin > opts.range_end) { + ntfs_log_error("The range must be in ascending order.\n"); + err++; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + return (!err && !help && !ver); +} + + +/** + * info + */ +static int info(ntfs_volume *vol) +{ + u64 a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u; + int cb, sb, cps; + u64 uc = 0, mc = 0, fc = 0; + + struct mft_search_ctx *m_ctx; + ntfs_attr_search_ctx *a_ctx; + runlist_element *rl; + ATTR_RECORD *rec; + int z; + int inuse = 0; + + m_ctx = mft_get_search_ctx(vol); + m_ctx->flags_search = FEMR_IN_USE | FEMR_METADATA | FEMR_BASE_RECORD | FEMR_NOT_BASE_RECORD; + while (mft_next_record(m_ctx) == 0) { + + if (!(m_ctx->flags_match & FEMR_IN_USE)) + continue; + + inuse++; + + a_ctx = ntfs_attr_get_search_ctx(m_ctx->inode, NULL); + + while ((rec = find_attribute(AT_UNUSED, a_ctx))) { + + if (!rec->non_resident) + continue; + + rl = ntfs_mapping_pairs_decompress(vol, rec, NULL); + + for (z = 0; rl[z].length > 0; z++) + { + if (rl[z].lcn >= 0) { + if (m_ctx->flags_match & FEMR_METADATA) + mc += rl[z].length; + else + uc += rl[z].length; + } + + } + + free(rl); + } + + ntfs_attr_put_search_ctx(a_ctx); + } + mft_put_search_ctx(m_ctx); + + cb = vol->cluster_size_bits; + sb = vol->sector_size_bits; + cps = cb - sb; + + fc = vol->nr_clusters-mc-uc; + fc <<= cb; + mc <<= cb; + uc <<= cb; + + a = vol->sector_size; + b = vol->cluster_size; + c = 1 << cps; + d = vol->nr_clusters << cb; + e = vol->nr_clusters; + f = vol->nr_clusters >> cps; + g = vol->mft_na->initialized_size >> vol->mft_record_size_bits; + h = inuse; + i = h * 100 / g; + j = fc; + k = fc >> sb; + l = fc >> cb; + m = fc * 100 / b / e; + n = uc; + o = uc >> sb; + p = uc >> cb; + q = uc * 100 / b / e; + r = mc; + s = mc >> sb; + t = mc >> cb; + u = mc * 100 / b / e; + + ntfs_log_info("bytes per sector : %llu\n", (unsigned long long)a); + ntfs_log_info("bytes per cluster : %llu\n", (unsigned long long)b); + ntfs_log_info("sectors per cluster : %llu\n", (unsigned long long)c); + ntfs_log_info("bytes per volume : %llu\n", (unsigned long long)d); + ntfs_log_info("sectors per volume : %llu\n", (unsigned long long)e); + ntfs_log_info("clusters per volume : %llu\n", (unsigned long long)f); + ntfs_log_info("initialized mft records : %llu\n", (unsigned long long)g); + ntfs_log_info("mft records in use : %llu\n", (unsigned long long)h); + ntfs_log_info("mft records percentage : %llu\n", (unsigned long long)i); + ntfs_log_info("bytes of free space : %llu\n", (unsigned long long)j); + ntfs_log_info("sectors of free space : %llu\n", (unsigned long long)k); + ntfs_log_info("clusters of free space : %llu\n", (unsigned long long)l); + ntfs_log_info("percentage free space : %llu\n", (unsigned long long)m); + ntfs_log_info("bytes of user data : %llu\n", (unsigned long long)n); + ntfs_log_info("sectors of user data : %llu\n", (unsigned long long)o); + ntfs_log_info("clusters of user data : %llu\n", (unsigned long long)p); + ntfs_log_info("percentage user data : %llu\n", (unsigned long long)q); + ntfs_log_info("bytes of metadata : %llu\n", (unsigned long long)r); + ntfs_log_info("sectors of metadata : %llu\n", (unsigned long long)s); + ntfs_log_info("clusters of metadata : %llu\n", (unsigned long long)t); + ntfs_log_info("percentage metadata : %llu\n", (unsigned long long)u); + + return 0; +} + +/** + * dump_file + */ +static int dump_file(ntfs_volume *vol, ntfs_inode *ino) +{ + char buffer[1024]; + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *rec; + int i; + runlist *runs; + + utils_inode_get_name(ino, buffer, sizeof(buffer)); + + ntfs_log_info("Dump: %s\n", buffer); + + ctx = ntfs_attr_get_search_ctx(ino, NULL); + + while ((rec = find_attribute(AT_UNUSED, ctx))) { + ntfs_log_info(" 0x%02x - ", rec->type); + if (rec->non_resident) { + ntfs_log_info("non-resident\n"); + runs = ntfs_mapping_pairs_decompress(vol, rec, NULL); + if (runs) { + ntfs_log_info(" VCN LCN Length\n"); + for (i = 0; runs[i].length > 0; i++) { + ntfs_log_info(" %8lld %8lld %8lld\n", + (long long)runs[i].vcn, + (long long)runs[i].lcn, + (long long) + runs[i].length); + } + free(runs); + } + } else { + ntfs_log_info("resident\n"); + } + } + + ntfs_attr_put_search_ctx(ctx); + return 0; +} + +/** + * print_match + */ +static int print_match(ntfs_inode *ino, ATTR_RECORD *attr, + runlist_element *run, void *data __attribute__((unused))) +{ + char *buffer; + + if (!ino || !attr || !run) + return 1; + + buffer = malloc(MAX_PATH); + if (!buffer) { + ntfs_log_error("!buffer\n"); + return 1; + } + + utils_inode_get_name(ino, buffer, MAX_PATH); + ntfs_log_info("Inode %llu %s", (unsigned long long)ino->mft_no, buffer); + + utils_attr_get_name(ino->vol, attr, buffer, MAX_PATH); + ntfs_log_info("/%s\n", buffer); + + free(buffer); + return 0; +} + +/** + * find_last + */ +static int find_last(ntfs_inode *ino, ATTR_RECORD *attr, runlist_element *run, + void *data) +{ + struct match *m; + + if (!ino || !attr || !run || !data) + return 1; + + m = data; + + if ((run->lcn + run->length) > m->lcn) { + m->inum = ino->mft_no; + m->lcn = run->lcn + run->length; + } + + return 0; +} + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + ntfs_volume *vol; + ntfs_inode *ino = NULL; + struct match m; + int result = 1; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + if (!parse_options(argc, argv)) + return 1; + + utils_set_locale(); + + vol = utils_mount_volume(opts.device, MS_RDONLY, opts.force); + if (!vol) + return 1; + + switch (opts.action) { + case act_sector: + if (opts.range_begin == opts.range_end) + ntfs_log_quiet("Searching for sector %llu\n", + (unsigned long long)opts.range_begin); + else + ntfs_log_quiet("Searching for sector range %llu-%llu\n", (unsigned long long)opts.range_begin, (unsigned long long)opts.range_end); + /* Convert to clusters */ + opts.range_begin >>= (vol->cluster_size_bits - vol->sector_size_bits); + opts.range_end >>= (vol->cluster_size_bits - vol->sector_size_bits); + result = cluster_find(vol, opts.range_begin, opts.range_end, (cluster_cb*)&print_match, NULL); + break; + case act_cluster: + if (opts.range_begin == opts.range_end) + ntfs_log_quiet("Searching for cluster %llu\n", + (unsigned long long)opts.range_begin); + else + ntfs_log_quiet("Searching for cluster range %llu-%llu\n", (unsigned long long)opts.range_begin, (unsigned long long)opts.range_end); + result = cluster_find(vol, opts.range_begin, opts.range_end, (cluster_cb*)&print_match, NULL); + break; + case act_file: + ino = ntfs_pathname_to_inode(vol, NULL, opts.filename); + if (ino) + result = dump_file(vol, ino); + break; + case act_inode: + ino = ntfs_inode_open(vol, opts.inode); + if (ino) { + result = dump_file(vol, ino); + ntfs_inode_close(ino); + } else { + ntfs_log_error("Cannot open inode %llu\n", + (unsigned long long)opts.inode); + } + break; + case act_last: + memset(&m, 0, sizeof(m)); + m.lcn = -1; + result = cluster_find(vol, 0, LONG_MAX, (cluster_cb*)&find_last, &m); + if (m.lcn >= 0) { + ino = ntfs_inode_open(vol, m.inum); + if (ino) { + result = dump_file(vol, ino); + ntfs_inode_close(ino); + } else { + ntfs_log_error("Cannot open inode %llu\n", + (unsigned long long) + opts.inode); + } + result = 0; + } else { + result = 1; + } + break; + case act_info: + default: + result = info(vol); + break; + } + + ntfs_umount(vol, FALSE); + return result; +} + + diff --git a/ntfsprogs/ntfscluster.h b/ntfsprogs/ntfscluster.h new file mode 100644 index 00000000..68e6ce50 --- /dev/null +++ b/ntfsprogs/ntfscluster.h @@ -0,0 +1,64 @@ +/* + * ntfscluster - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2003 Richard Russon + * + * This utility will locate the owner of any given sector or cluster. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 _NTFSCLUSTER_H_ +#define _NTFSCLUSTER_H_ + +#include +#include + +enum action { + act_none, + act_info, + act_cluster, + act_sector, + act_inode, + act_file, + act_last, + act_error, +}; + +struct options { + char *device; /* Device/File to work with */ + enum action action; /* What to do */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int force; /* Override common sense */ + char *filename; /* File to examine */ + u64 inode; /* Inode to examine */ + s64 range_begin; /* Look for objects in this range */ + s64 range_end; +}; + +struct match { + u64 inum; /* Inode number */ + LCN lcn; /* Last cluster in use */ + ATTR_TYPES type; /* Attribute type */ + ntfschar *name; /* Attribute name */ + int name_len; /* Length of attribute name */ + u8 padding[4]; /* Unused: padding to 64 bit. */ +}; + +#endif /* _NTFSCLUSTER_H_ */ + + diff --git a/ntfsprogs/ntfscmp.8.in b/ntfsprogs/ntfscmp.8.in new file mode 100644 index 00000000..d7a5c66d --- /dev/null +++ b/ntfsprogs/ntfscmp.8.in @@ -0,0 +1,82 @@ +.\" Copyright (c) 2005\-2006 Szabolcs Szakacsits. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSCMP 8 "April 2006" "ntfsprogs @VERSION@" +.SH NAME +ntfscmp \- compare two NTFS filesystems and tell the differences +.SH SYNOPSIS +.B ntfscmp +[\fIOPTIONS\fR] +.I DEVICE1 +.I DEVICE2 +.br +.SH DESCRIPTION +The +.B ntfscmp +program makes a comparison between two NTFS filesystems from all aspects and +reports all variances it finds. +The filesystems can be on block devices or images files. Ntfscmp can be used +for volume verification however its primary purpose was to be an efficient +development tool, used to quickly locate, identify and check the correctness +of the metadata changes made to NTFS. + +If one is interested only in the NTFS metadata changes then it could be useful +to compare the metadata images created by +using the --metadata option of +.BR ntfsclone (8) +to eliminate the usually uninteresting timestamp changes. + +The terse output of +.B ntfscmp +is intentional because the provided information is enough in each case +to determine the exact differences. This can be achieved, for instance, +if one compares the verbose outputs of +.BR ntfsinfo (8) +for each reported inodes by the +.BR diff (1) +utility. +.SH OPTIONS +Below is a summary of the options that +.B ntfscmp +accepts. +.TP +\fB\-P\fR, \fB\-\-no\-progress\-bar\fR +Don't show progress bars. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +More informational output. +.TP +\fB\-h\fR, \fB\-\-help\fR +Display help and exit. +.SH EXIT CODES +The exit code is 0 on success, non\-zero otherwise. +.SH KNOWN ISSUES +No problem is known. If you would find otherwise then please send +your report to the development team: +.nh +linux\-ntfs\-dev@lists.sourceforge.net +.hy +.SH AUTHOR +.B ntfscmp +was written by Szabolcs Szakacsits (szaka@sienet.hu). +.SH AVAILABILITY +.B ntfscmp +is part of the +.B ntfsprogs +package and is available from: +.br +.nh +http://www.linux\-ntfs.org/content/view/19/37 +.hy +.sp +The manual pages are available online at: +.br +.nh +http://man.linux-ntfs.org/ +.hy +.SH SEE ALSO +.BR ntfsinfo (8), +.BR ntfscat (8), +.BR diff (1), +.BR ntfsclone (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfscmp.c b/ntfsprogs/ntfscmp.c new file mode 100644 index 00000000..89014885 --- /dev/null +++ b/ntfsprogs/ntfscmp.c @@ -0,0 +1,872 @@ +/** + * ntfscmp - compare two NTFS volumes. + * + * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2005 Anton Altaparmakov + * + * This utility is part of the Linux-NTFS project. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +/* #include "version.h" */ + +static const char *EXEC_NAME = "ntfscmp"; + +static const char *invalid_ntfs_msg = +"Apparently device '%s' doesn't have a valid NTFS.\n" +"Maybe you selected the wrong partition? Or the whole disk instead of a\n" +"partition (e.g. /dev/hda, not /dev/hda1)?\n"; + +static const char *corrupt_volume_msg = +"Apparently you have a corrupted NTFS. Please run the filesystem checker\n" +"on Windows by invoking chkdsk /f. Don't forget the /f (force) parameter,\n" +"it's important! You probably also need to reboot Windows to take effect.\n"; + +static const char *hibernated_volume_msg = +"Apparently the NTFS partition is hibernated. Windows must be resumed and\n" +"turned off properly\n"; + + +struct { + int debug; + int show_progress; + int verbose; + char *vol1; + char *vol2; +} opt; + + +#define NTFS_PROGBAR 0x0001 +#define NTFS_PROGBAR_SUPPRESS 0x0002 + +struct progress_bar { + u64 start; + u64 stop; + int resolution; + int flags; + float unit; + u8 padding[4]; /* Unused: padding to 64 bit. */ +}; + +/* WARNING: don't modify the text, external tools grep for it */ +#define ERR_PREFIX "ERROR" +#define PERR_PREFIX ERR_PREFIX "(%d): " +#define NERR_PREFIX ERR_PREFIX ": " + +__attribute__((format(printf, 2, 3))) +static void perr_printf(int newline, const char *fmt, ...) +{ + va_list ap; + int eo = errno; + + fprintf(stdout, PERR_PREFIX, eo); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fprintf(stdout, ": %s", strerror(eo)); + if (newline) + fprintf(stdout, "\n"); + fflush(stdout); + fflush(stderr); +} + +#define perr_print(...) perr_printf(0, __VA_ARGS__) +#define perr_println(...) perr_printf(1, __VA_ARGS__) + +__attribute__((format(printf, 1, 2))) +static void err_printf(const char *fmt, ...) +{ + va_list ap; + + fprintf(stdout, NERR_PREFIX); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fflush(stdout); + fflush(stderr); +} + +/** + * err_exit + * + * Print and error message and exit the program. + */ +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static int err_exit(const char *fmt, ...) +{ + va_list ap; + + fprintf(stdout, NERR_PREFIX); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fflush(stdout); + fflush(stderr); + exit(1); +} + +/** + * perr_exit + * + * Print and error message and exit the program + */ +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static int perr_exit(const char *fmt, ...) +{ + va_list ap; + int eo = errno; + + fprintf(stdout, PERR_PREFIX, eo); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + printf(": %s\n", strerror(eo)); + fflush(stdout); + fflush(stderr); + exit(1); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +__attribute__((noreturn)) +static void usage(void) +{ + + printf("\nUsage: %s [OPTIONS] DEVICE1 DEVICE2\n" + " Compare two NTFS volumes and tell the differences.\n" + "\n" + " -P, --no-progress-bar Don't show progress bar\n" + " -v, --verbose More output\n" + " -h, --help Display this help\n" +#ifdef DEBUG + " -d, --debug Show debug information\n" +#endif + "\n", EXEC_NAME); + printf("%s%s", ntfs_bugs, ntfs_home); + exit(1); +} + + +static void parse_options(int argc, char **argv) +{ + static const char *sopt = "-dhPv"; + static const struct option lopt[] = { +#ifdef DEBUG + { "debug", no_argument, NULL, 'd' }, +#endif + { "help", no_argument, NULL, 'h' }, + { "no-progress-bar", no_argument, NULL, 'P' }, + { "verbose", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } + }; + + int c; + + memset(&opt, 0, sizeof(opt)); + opt.show_progress = 1; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opt.vol1) { + opt.vol1 = argv[optind - 1]; + } else if (!opt.vol2) { + opt.vol2 = argv[optind - 1]; + } else { + err_printf("Too many arguments!\n"); + usage(); + } + break; +#ifdef DEBUG + case 'd': + opt.debug++; + break; +#endif + case 'h': + case '?': + usage(); + case 'P': + opt.show_progress = 0; + break; + case 'v': + opt.verbose++; + break; + default: + err_printf("Unknown option '%s'.\n", argv[optind - 1]); + usage(); + break; + } + } + + if (opt.vol1 == NULL || opt.vol2 == NULL) { + err_printf("You must specify exactly 2 volumes.\n"); + usage(); + } + + /* Redirect stderr to stdout, note fflush()es are essential! */ + fflush(stdout); + fflush(stderr); + if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) { + perror("Failed to redirect stderr to stdout"); + exit(1); + } + fflush(stdout); + fflush(stderr); + +#ifdef DEBUG + if (!opt.debug) + if (!freopen("/dev/null", "w", stderr)) + perr_exit("Failed to redirect stderr to /dev/null"); +#endif +} + +static ntfs_attr_search_ctx *attr_get_search_ctx(ntfs_inode *ni) +{ + ntfs_attr_search_ctx *ret; + + if ((ret = ntfs_attr_get_search_ctx(ni, NULL)) == NULL) + perr_println("ntfs_attr_get_search_ctx"); + + return ret; +} + +static void progress_init(struct progress_bar *p, u64 start, u64 stop, int flags) +{ + p->start = start; + p->stop = stop; + p->unit = 100.0 / (stop - start); + p->resolution = 100; + p->flags = flags; +} + +static void progress_update(struct progress_bar *p, u64 current) +{ + float percent; + + if (!(p->flags & NTFS_PROGBAR)) + return; + if (p->flags & NTFS_PROGBAR_SUPPRESS) + return; + + /* WARNING: don't modify the texts, external tools grep for them */ + percent = p->unit * current; + if (current != p->stop) { + if ((current - p->start) % p->resolution) + return; + printf("%6.2f percent completed\r", percent); + } else + printf("100.00 percent completed\n"); + fflush(stdout); +} + +static u64 inumber(ntfs_inode *ni) +{ + if (ni->nr_extents >= 0) + return ni->mft_no; + + return ni->base_ni->mft_no; +} + +static int inode_close(ntfs_inode *ni) +{ + if (ni == NULL) + return 0; + + if (ntfs_inode_close(ni)) { + perr_println("ntfs_inode_close: inode %llu", inumber(ni)); + return -1; + } + return 0; +} + +static inline s64 get_nr_mft_records(ntfs_volume *vol) +{ + return vol->mft_na->initialized_size >> vol->mft_record_size_bits; +} + +#define NTFSCMP_OK 0 +#define NTFSCMP_INODE_OPEN_ERROR 1 +#define NTFSCMP_INODE_OPEN_IO_ERROR 2 +#define NTFSCMP_INODE_OPEN_ENOENT_ERROR 3 +#define NTFSCMP_EXTENSION_RECORD 4 +#define NTFSCMP_INODE_CLOSE_ERROR 5 + +const char *ntfscmp_errs[] = { + "OK", + "INODE_OPEN_ERROR", + "INODE_OPEN_IO_ERROR", + "INODE_OPEN_ENOENT_ERROR", + "EXTENSION_RECORD", + "INODE_CLOSE_ERROR", + "" +}; + + +static const char *err2string(int err) +{ + return ntfscmp_errs[err]; +} + +static const char *pret2str(void *p) +{ + if (p == NULL) + return "FAILED"; + return "OK"; +} + +static int inode_open(ntfs_volume *vol, MFT_REF mref, ntfs_inode **ni) +{ + *ni = ntfs_inode_open(vol, mref); + if (*ni == NULL) { + if (errno == EIO) + return NTFSCMP_INODE_OPEN_IO_ERROR; + if (errno == ENOENT) + return NTFSCMP_INODE_OPEN_ENOENT_ERROR; + + perr_println("Reading inode %lld failed", mref); + return NTFSCMP_INODE_OPEN_ERROR; + } + + if ((*ni)->mrec->base_mft_record) { + + if (inode_close(*ni) != 0) + return NTFSCMP_INODE_CLOSE_ERROR; + + return NTFSCMP_EXTENSION_RECORD; + } + + return NTFSCMP_OK; +} + +static ntfs_inode *base_inode(ntfs_attr_search_ctx *ctx) +{ + if (ctx->base_ntfs_ino) + return ctx->base_ntfs_ino; + + return ctx->ntfs_ino; +} + +static void print_inode(u64 inum) +{ + printf("Inode %llu ", inum); +} + +static void print_inode_ni(ntfs_inode *ni) +{ + print_inode(inumber(ni)); +} + +static void print_attribute_type(ATTR_TYPES atype) +{ + printf("attribute 0x%x", atype); +} + +static void print_attribute_name(char *name) +{ + if (name) + printf(":%s", name); +} + +#define GET_ATTR_NAME(a) \ + ((ntfschar *)(((u8 *)(a)) + ((a)->name_offset))), ((a)->name_length) + +static void free_name(char **name) +{ + if (*name) { + free(*name); + *name = NULL; + } +} + +static char *get_attr_name(u64 mft_no, + ATTR_TYPES atype, + const ntfschar *uname, + const int uname_len) +{ + char *name = NULL; + int name_len; + + if (atype == AT_END) + return NULL; + + name_len = ntfs_ucstombs(uname, uname_len, &name, 0); + if (name_len < 0) { + perr_print("ntfs_ucstombs"); + print_inode(mft_no); + print_attribute_type(atype); + puts(""); + exit(1); + + } else if (name_len > 0) + return name; + + free_name(&name); + return NULL; +} + +static char *get_attr_name_na(ntfs_attr *na) +{ + return get_attr_name(inumber(na->ni), na->type, na->name, na->name_len); +} + +static char *get_attr_name_ctx(ntfs_attr_search_ctx *ctx) +{ + u64 mft_no = inumber(ctx->ntfs_ino); + ATTR_TYPES atype = ctx->attr->type; + + return get_attr_name(mft_no, atype, GET_ATTR_NAME(ctx->attr)); +} + +static void print_attribute(ATTR_TYPES atype, char *name) +{ + print_attribute_type(atype); + print_attribute_name(name); + printf(" "); +} + +static void print_na(ntfs_attr *na) +{ + char *name = get_attr_name_na(na); + print_inode_ni(na->ni); + print_attribute(na->type, name); + free_name(&name); +} + +static void print_attribute_ctx(ntfs_attr_search_ctx *ctx) +{ + char *name = get_attr_name_ctx(ctx); + print_attribute(ctx->attr->type, name); + free_name(&name); +} + +static void print_ctx(ntfs_attr_search_ctx *ctx) +{ + char *name = get_attr_name_ctx(ctx); + print_inode_ni(base_inode(ctx)); + print_attribute(ctx->attr->type, name); + free_name(&name); +} + +static void cmp_attribute_data(ntfs_attr *na1, ntfs_attr *na2) +{ + s64 pos; + s64 count1 = 0, count2; + u8 buf1[NTFS_BUF_SIZE]; + u8 buf2[NTFS_BUF_SIZE]; + + for (pos = 0; pos <= na1->data_size; pos += count1) { + + count1 = ntfs_attr_pread(na1, pos, NTFS_BUF_SIZE, buf1); + count2 = ntfs_attr_pread(na2, pos, NTFS_BUF_SIZE, buf2); + + if (count1 != count2) { + print_na(na1); + printf("abrupt length: %lld != %lld ", + na1->data_size, na2->data_size); + printf("(count: %lld != %lld)", count1, count2); + puts(""); + return; + } + + if (count1 == -1) { + err_printf("%s read error: ", __FUNCTION__); + print_na(na1); + printf("len = %lld, pos = %lld\n", na1->data_size, pos); + exit(1); + } + + if (count1 == 0) { + + if (pos + count1 == na1->data_size) + return; /* we are ready */ + + err_printf("%s read error before EOF: ", __FUNCTION__); + print_na(na1); + printf("%lld != %lld\n", pos + count1, na1->data_size); + exit(1); + } + + if (memcmp(buf1, buf2, count1)) { + print_na(na1); + printf("content"); + if (opt.verbose) + printf(" (len = %lld)", count1); + printf(": DIFFER\n"); + return; + } + } + + err_printf("%s read overrun: ", __FUNCTION__); + print_na(na1); + err_printf("(len = %lld, pos = %lld, count = %lld)\n", + na1->data_size, pos, count1); + exit(1); +} + +static int cmp_attribute_header(ATTR_RECORD *a1, ATTR_RECORD *a2) +{ + u32 header_size = offsetof(ATTR_RECORD, resident_end); + + if (a1->non_resident != a2->non_resident) + return 1; + + if (a1->non_resident) { + /* + * FIXME: includes paddings which are not handled by ntfsinfo! + */ + header_size = a1->length; + } + + return memcmp(a1, a2, header_size); +} + +static void cmp_attribute(ntfs_attr_search_ctx *ctx1, + ntfs_attr_search_ctx *ctx2) +{ + ATTR_RECORD *a1 = ctx1->attr; + ATTR_RECORD *a2 = ctx2->attr; + ntfs_attr *na1, *na2; + + if (cmp_attribute_header(a1, a2)) { + print_ctx(ctx1); + printf("header: DIFFER\n"); + } + + na1 = ntfs_attr_open(base_inode(ctx1), a1->type, GET_ATTR_NAME(a1)); + na2 = ntfs_attr_open(base_inode(ctx2), a2->type, GET_ATTR_NAME(a2)); + + if ((!na1 && na2) || (na1 && !na2)) { + print_ctx(ctx1); + printf("open: %s != %s\n", pret2str(na1), pret2str(na2)); + goto close_attribs; + } + + if (na1 == NULL) + goto close_attribs; + + if (na1->data_size != na2->data_size) { + print_na(na1); + printf("length: %lld != %lld\n", na1->data_size, na2->data_size); + goto close_attribs; + } + + if (ntfs_inode_badclus_bad(inumber(ctx1->ntfs_ino), ctx1->attr) == 1) { + /* + * If difference exists then it's already reported at the + * attribute header since the mapping pairs must differ. + */ + return; + } + + cmp_attribute_data(na1, na2); + +close_attribs: + ntfs_attr_close(na1); + ntfs_attr_close(na2); +} + +static void vprint_attribute(ATTR_TYPES atype, char *name) +{ + if (!opt.verbose) + return; + + printf("0x%x", atype); + if (name) + printf(":%s", name); + printf(" "); +} + +static void print_attributes(ntfs_inode *ni, + ATTR_TYPES atype1, + ATTR_TYPES atype2, + char *name1, + char *name2) +{ + if (!opt.verbose) + return; + + printf("Walking inode %llu attributes: ", inumber(ni)); + vprint_attribute(atype1, name1); + vprint_attribute(atype2, name2); + printf("\n"); +} + +static int new_name(ntfs_attr_search_ctx *ctx, char *prev_name) +{ + int ret = 0; + char *name = get_attr_name_ctx(ctx); + + if (prev_name && name) { + if (strcmp(prev_name, name) != 0) + ret = 1; + } else if (prev_name || name) + ret = 1; + + free_name(&name); + return ret; + +} + +static int new_attribute(ntfs_attr_search_ctx *ctx, + ATTR_TYPES prev_atype, + char *prev_name) +{ + if (!prev_atype && !prev_name) + return 1; + + if (!ctx->attr->non_resident) + return 1; + + if (prev_atype != ctx->attr->type) + return 1; + + if (new_name(ctx, prev_name)) + return 1; + + if (opt.verbose) { + print_inode(base_inode(ctx)->mft_no); + print_attribute_ctx(ctx); + printf("record %llu lowest_vcn %lld: SKIPPED\n", + ctx->ntfs_ino->mft_no, ctx->attr->lowest_vcn); + } + + return 0; +} + +static void set_prev(char **prev_name, ATTR_TYPES *prev_atype, + char *name, ATTR_TYPES atype) +{ + free_name(prev_name); + if (name) { + *prev_name = strdup(name); + if (!*prev_name) + perr_exit("strdup error"); + } + + *prev_atype = atype; +} + +static void set_cmp_attr(ntfs_attr_search_ctx *ctx, ATTR_TYPES *atype, char **name) +{ + *atype = ctx->attr->type; + + free_name(name); + *name = get_attr_name_ctx(ctx); +} + +static int next_attr(ntfs_attr_search_ctx *ctx, ATTR_TYPES *atype, char **name, + int *err) +{ + int ret; + + ret = ntfs_attrs_walk(ctx); + *err = errno; + if (ret) { + *atype = AT_END; + free_name(name); + } else + set_cmp_attr(ctx, atype, name); + + return ret; +} + +static int cmp_attributes(ntfs_inode *ni1, ntfs_inode *ni2) +{ + int ret = -1; + int old_ret1, ret1 = 0, ret2 = 0; + int errno1 = 0, errno2 = 0; + char *prev_name = NULL, *name1 = NULL, *name2 = NULL; + ATTR_TYPES old_atype1, prev_atype = 0, atype1, atype2; + ntfs_attr_search_ctx *ctx1, *ctx2; + + if (!(ctx1 = attr_get_search_ctx(ni1))) + return -1; + if (!(ctx2 = attr_get_search_ctx(ni2))) + goto out; + + set_cmp_attr(ctx1, &atype1, &name1); + set_cmp_attr(ctx2, &atype2, &name2); + + while (1) { + + old_atype1 = atype1; + old_ret1 = ret1; + if (!ret1 && (atype1 <= atype2 || ret2)) + ret1 = next_attr(ctx1, &atype1, &name1, &errno1); + if (!ret2 && (old_atype1 >= atype2 || old_ret1)) + ret2 = next_attr(ctx2, &atype2, &name2, &errno2); + + print_attributes(ni1, atype1, atype2, name1, name2); + + if (ret1 && ret2) { + if (errno1 != errno2) { + print_inode_ni(ni1); + printf("attribute walk (errno): %d != %d\n", + errno1, errno2); + } + break; + } + + if (ret2 || atype1 < atype2) { + if (new_attribute(ctx1, prev_atype, prev_name)) { + print_ctx(ctx1); + printf("presence: EXISTS != MISSING\n"); + set_prev(&prev_name, &prev_atype, name1, atype1); + } + + } else if (ret1 || atype1 > atype2) { + if (new_attribute(ctx2, prev_atype, prev_name)) { + print_ctx(ctx2); + printf("presence: MISSING != EXISTS \n"); + set_prev(&prev_name, &prev_atype, name2, atype2); + } + + } else /* atype1 == atype2 */ { + if (new_attribute(ctx1, prev_atype, prev_name)) { + cmp_attribute(ctx1, ctx2); + set_prev(&prev_name, &prev_atype, name1, atype1); + } + } + } + + free_name(&prev_name); + ret = 0; + ntfs_attr_put_search_ctx(ctx2); +out: + ntfs_attr_put_search_ctx(ctx1); + return ret; +} + +static int cmp_inodes(ntfs_volume *vol1, ntfs_volume *vol2) +{ + u64 inode; + int ret1, ret2; + ntfs_inode *ni1, *ni2; + struct progress_bar progress; + int pb_flags = 0; /* progress bar flags */ + u64 nr_mft_records, nr_mft_records2; + + if (opt.show_progress) + pb_flags |= NTFS_PROGBAR; + + nr_mft_records = get_nr_mft_records(vol1); + nr_mft_records2 = get_nr_mft_records(vol2); + + if (nr_mft_records != nr_mft_records2) { + + printf("Number of mft records: %lld != %lld\n", + nr_mft_records, nr_mft_records2); + + if (nr_mft_records > nr_mft_records2) + nr_mft_records = nr_mft_records2; + } + + progress_init(&progress, 0, nr_mft_records - 1, pb_flags); + progress_update(&progress, 0); + + for (inode = 0; inode < nr_mft_records; inode++) { + + ret1 = inode_open(vol1, (MFT_REF)inode, &ni1); + ret2 = inode_open(vol2, (MFT_REF)inode, &ni2); + + if (ret1 != ret2) { + print_inode(inode); + printf("open: %s != %s\n", + err2string(ret1), err2string(ret2)); + goto close_inodes; + } + + if (ret1 != NTFSCMP_OK) + goto close_inodes; + + if (cmp_attributes(ni1, ni2) != 0) { + inode_close(ni1); + inode_close(ni2); + return -1; + } +close_inodes: + if (inode_close(ni1) != 0) + return -1; + if (inode_close(ni2) != 0) + return -1; + + progress_update(&progress, inode); + } + return 0; +} + +static ntfs_volume *mount_volume(const char *volume) +{ + unsigned long mntflag; + ntfs_volume *vol = NULL; + + if (ntfs_check_if_mounted(volume, &mntflag)) { + perr_println("Failed to check '%s' mount state", volume); + printf("Probably /etc/mtab is missing. It's too risky to " + "continue. You might try\nan another Linux distro.\n"); + exit(1); + } + if (mntflag & NTFS_MF_MOUNTED) { + if (!(mntflag & NTFS_MF_READONLY)) + err_exit("Device '%s' is mounted read-write. " + "You must 'umount' it first.\n", volume); + } + + vol = ntfs_mount(volume, MS_RDONLY); + if (vol == NULL) { + + int err = errno; + + perr_println("Opening '%s' as NTFS failed", volume); + if (err == EINVAL) + printf(invalid_ntfs_msg, volume); + else if (err == EIO) + puts(corrupt_volume_msg); + else if (err == EPERM) + puts(hibernated_volume_msg); + exit(1); + } + + return vol; +} + +int main(int argc, char **argv) +{ + ntfs_volume *vol1; + ntfs_volume *vol2; + + printf("%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION); + + parse_options(argc, argv); + + utils_set_locale(); + + vol1 = mount_volume(opt.vol1); + vol2 = mount_volume(opt.vol2); + + if (cmp_inodes(vol1, vol2) != 0) + exit(1); + + ntfs_umount(vol1, FALSE); + ntfs_umount(vol2, FALSE); + + exit(0); +} + diff --git a/ntfsprogs/ntfscp.8.in b/ntfsprogs/ntfscp.8.in new file mode 100644 index 00000000..d39763c5 --- /dev/null +++ b/ntfsprogs/ntfscp.8.in @@ -0,0 +1,128 @@ +.\" Copyright (c) 2004\-2005 Yura Pakhuchiy. +.\" Copyright (c) 2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSCP 8 "November 2005" "ntfsprogs @VERSION@" +.SH NAME +ntfscp \- overwrite file on an NTFS volume. +.SH SYNOPSIS +.B ntfscp +[\fIoptions\fR] \fIdevice source_file destination\fR +.SH DESCRIPTION +.B ntfscp +will overwrite file on an NTFS volume. At present +.B ntfscp +can't create new files. +.B destination +can be either file or directory. In case if +.B destination +is directory specified by name then +.B source_file +is copied into this directory, in case if +.B destination +is directory and specified by inode number then unnamed data attribute is +created for this inode and +.B source_file +is copied into it (WARNING: it's unusual to have unnamed data streams in the +directories, think twice before specifying directory by inode number). +.SH OPTIONS +Below is a summary of all the options that +.B ntfscp +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-a\fR, \fB\-\-attribute\fR NUM +Write to this attribute. +.TP +\fB\-i\fR, \fB\-\-inode\fR +Treat +.I destination +as inode number. +.TP +\fB\-N\fR, \fB\-\-attr\-name\fR NAME +Write to attribute with this name. +.TP +\fB\-n\fR, \fB\-\-no\-action\fR +Use this option to make a test run before doing the real copy operation. +Volume will be opened read\-only and no write will be done. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not working with a mounted +volume. Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license +.BR ntfscp . +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.SH DATA STREAMS +All data on NTFS is stored in streams, which can have names. A file can have +more than one data streams, but exactly one must have no name. The size of a +file is the size of its unnamed data stream. Usually when you don't specify +stream name you are access to unnamed data stream. If you want access to named +data stream you need to add ":stream_name" to the filename. For example: by +opening "some.mp3:artist" you will open stream "artist" in "some.mp3". But +windows usually prevent you from accessing to named data streams, so you need +to use some program like FAR or utils from cygwin to access named data streams. +.SH EXAMPLES +Copy new_boot.ini from /home/user as boot.ini to the root of an /dev/hda1 NTFS +volume: +.RS +.sp +.B ntfscp /dev/hda1 /home/user/new_boot.ini boot.ini +.sp +.RE +Copy myfile to C:\\some\\path\\myfile:stream (assume that /dev/hda1 letter in +windows is C): +.RS +.sp +.B ntfscp \-N stream /dev/hda1 myfile /some/path +.sp +.RE +.SH BUGS +There are no known problems with +.BR ntfscp . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +linux\-ntfs\-dev@lists.sourceforge.net +.hy +.SH AUTHORS +.B ntfscp +was written by Yura Pakhuchiy, with contributions from Anton Altaparmakov. +.SH DEDICATION +With love to Marina Sapego. +.SH AVAILABILITY +.B ntfscp +is part of the +.B ntfsprogs +package and is available from: +.br +.nh +http://www.linux\-ntfs.org/content/view/19/37 +.hy +.sp +The manual pages are available online at: +.br +.nh +http://man.linux-ntfs.org/ +.hy +.SH SEE ALSO +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfscp.c b/ntfsprogs/ntfscp.c new file mode 100644 index 00000000..d9b9589e --- /dev/null +++ b/ntfsprogs/ntfscp.c @@ -0,0 +1,480 @@ +/** + * ntfscp - Part of the Linux-NTFS project. + * + * Copyright (c) 2004-2005 Yura Pakhuchiy + * Copyright (c) 2005 Anton Altaparmakov + * + * This utility will overwrite files on NTFS volume. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 "config.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#include +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "utils.h" +/* #include "version.h" */ + +struct options { + char *device; /* Device/File to work with */ + char *src_file; /* Source file */ + char *dest_file; /* Destination file */ + char *attr_name; /* Write to attribute with this name. */ + int force; /* Override common sense */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int noaction; /* Do not write to disk */ + ATTR_TYPES attribute; /* Write to this attribute. */ + int inode; /* Treat dest_file as inode number. */ +}; + +static const char *EXEC_NAME = "ntfscp"; +static struct options opts; +volatile sig_atomic_t caught_terminate = 0; + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Overwrite files on NTFS " + "volume.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c) 2004-2005 Yura Pakhuchiy\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device src_file dest_file\n\n" + " -a, --attribute NUM Write to this attribute\n" + " -i, --inode Treat dest_file as inode number\n" + " -f, --force Use less caution\n" + " -h, --help Print this help\n" + " -N, --attr-name NAME Write to attribute with this name\n" + " -n, --no-action Do not write to disk\n" + " -q, --quiet Less output\n" + " -V, --version Version information\n" + " -v, --verbose More output\n\n", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char **argv) +{ + static const char *sopt = "-a:ifh?N:nqVv"; + static const struct option lopt[] = { + { "attribute", required_argument, NULL, 'a' }, + { "inode", no_argument, NULL, 'i' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "attr-name", required_argument, NULL, 'N' }, + { "no-action", no_argument, NULL, 'n' }, + { "quiet", no_argument, NULL, 'q' }, + { "version", no_argument, NULL, 'V' }, + { "verbose", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } + }; + + char *s; + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + s64 attr; + + opts.device = NULL; + opts.src_file = NULL; + opts.dest_file = NULL; + opts.attr_name = NULL; + opts.inode = 0; + opts.attribute = AT_DATA; + + opterr = 0; /* We'll handle the errors, thank you. */ + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) { + opts.device = argv[optind - 1]; + } else if (!opts.src_file) { + opts.src_file = argv[optind - 1]; + } else if (!opts.dest_file) { + opts.dest_file = argv[optind - 1]; + } else { + ntfs_log_error("You must specify exactly two " + "files.\n"); + err++; + } + break; + case 'a': + if (opts.attribute != AT_DATA) { + ntfs_log_error("You can specify only one " + "attribute.\n"); + err++; + break; + } + + attr = strtol(optarg, &s, 0); + if (*s) { + ntfs_log_error("Couldn't parse attribute.\n"); + err++; + } else + opts.attribute = (ATTR_TYPES)attr; + break; + case 'i': + opts.inode++; + break; + case 'f': + opts.force++; + break; + case 'h': + case '?': + if (strncmp(argv[optind - 1], "--log-", 6) == 0) { + if (!ntfs_log_parse_option(argv[optind - 1])) + err++; + break; + } + help++; + break; + case 'N': + if (opts.attr_name) { + ntfs_log_error("You can specify only one " + "attribute name.\n"); + err++; + } else + opts.attr_name = argv[optind - 1]; + break; + case 'n': + opts.noaction++; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'V': + ver++; + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + default: + ntfs_log_error("Unknown option '%s'.\n", + argv[optind - 1]); + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if (!opts.device) { + ntfs_log_error("You must specify a device.\n"); + err++; + } else if (!opts.src_file) { + ntfs_log_error("You must specify a source file.\n"); + err++; + } else if (!opts.dest_file) { + ntfs_log_error("You must specify a destination " + "file.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose " + "at the same time.\n"); + err++; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + return (!err && !help && !ver); +} + +/** + * signal_handler - Handle SIGINT and SIGTERM: abort write, sync and exit. + */ +static void signal_handler(int arg __attribute__((unused))) +{ + caught_terminate++; +} + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + FILE *in; + ntfs_volume *vol; + ntfs_inode *out; + ntfs_attr *na; + int flags = 0; + int result = 1; + s64 new_size; + u64 offset; + char *buf; + s64 br, bw; + ntfschar *attr_name; + int attr_name_len = 0; + + ntfs_log_set_handler(ntfs_log_handler_stderr); + + if (!parse_options(argc, argv)) + return 1; + + utils_set_locale(); + + /* Set SIGINT handler. */ + if (signal(SIGINT, signal_handler) == SIG_ERR) { + ntfs_log_perror("Failed to set SIGINT handler"); + return 1; + } + /* Set SIGTERM handler. */ + if (signal(SIGTERM, signal_handler) == SIG_ERR) { + ntfs_log_perror("Failed to set SIGTERM handler"); + return 1; + } + + if (opts.noaction) + flags = MS_RDONLY; + + vol = utils_mount_volume(opts.device, flags, opts.force); + if (!vol) { + ntfs_log_perror("ERROR: couldn't mount volume"); + return 1; + } + + if ((vol->flags & VOLUME_IS_DIRTY) && (!opts.force)) + goto umount; + + { + struct stat fst; + if (stat(opts.src_file, &fst) == -1) { + ntfs_log_perror("ERROR: Couldn't stat source file"); + goto umount; + } + new_size = fst.st_size; + } + ntfs_log_verbose("New file size: %lld\n", new_size); + + in = fopen(opts.src_file, "r"); + if (!in) { + ntfs_log_perror("ERROR: Couldn't open source file"); + goto umount; + } + + if (opts.inode) { + s64 inode_num; + char *s; + + inode_num = strtoll(opts.dest_file, &s, 0); + if (*s) { + ntfs_log_error("ERROR: Couldn't parse inode number.\n"); + goto close_src; + } + out = ntfs_inode_open(vol, inode_num); + } else + out = ntfs_pathname_to_inode(vol, NULL, opts.dest_file); + if (!out) { + ntfs_log_perror("ERROR: Couldn't open destination file"); + goto close_src; + } + if ((le16_to_cpu(out->mrec->flags) & MFT_RECORD_IS_DIRECTORY) && + !opts.inode){ + /* + * @out is directory and it was specified by pathname, add + * filename to path and reopen inode. + */ + char *filename, *new_dest_file; + + /* + * FIXME: There should exist more beautiful way to get filename. + * Not sure that it will work in windows, but I don't think that + * someone will use ntfscp under windows. + */ + filename = strrchr(opts.src_file, '/'); + if (filename) + filename++; + else + filename = opts.src_file; + /* Add 2 bytes for '/' and null-terminator. */ + new_dest_file = malloc(strlen(opts.dest_file) + + strlen(filename) + 2); + if (!new_dest_file) { + ntfs_log_perror("ERROR: malloc() failed"); + goto close_dst; + } + strcpy(new_dest_file, opts.dest_file); + strcat(new_dest_file, "/"); + strcat(new_dest_file, filename); + ntfs_inode_close(out); + out = ntfs_pathname_to_inode(vol, NULL, new_dest_file); + free(new_dest_file); + if (!out) { + ntfs_log_perror("ERROR: Failed to open destination " + "file"); + goto close_src; + } + } + + attr_name = ntfs_str2ucs(opts.attr_name, &attr_name_len); + if (!attr_name) { + ntfs_log_perror("ERROR: Failed to parse attribute name '%s'", + opts.attr_name); + goto close_dst; + } + + na = ntfs_attr_open(out, opts.attribute, attr_name, attr_name_len); + if (!na) { + if (errno != ENOENT) { + ntfs_log_perror("ERROR: Couldn't open attribute"); + goto close_dst; + } + /* Requested attribute isn't present, add it. */ + if (ntfs_attr_add(out, opts.attribute, attr_name, + attr_name_len, NULL, 0)) { + ntfs_log_perror("ERROR: Couldn't add attribute"); + goto close_dst; + } + na = ntfs_attr_open(out, opts.attribute, attr_name, + attr_name_len); + if (!na) { + ntfs_log_perror("ERROR: Couldn't open just added " + "attribute"); + goto close_dst; + } + } + ntfs_ucsfree(attr_name); + + ntfs_log_verbose("Old file size: %lld\n", na->data_size); + if (na->data_size != new_size) { + if (ntfs_attr_truncate(na, new_size)) { + ntfs_log_perror("ERROR: Couldn't resize attribute"); + goto close_attr; + } + } + + buf = malloc(NTFS_BUF_SIZE); + if (!buf) { + ntfs_log_perror("ERROR: malloc failed"); + goto close_attr; + } + + ntfs_log_verbose("Starting write.\n"); + offset = 0; + while (!feof(in)) { + if (caught_terminate) { + ntfs_log_error("SIGTERM or SIGINT received. " + "Aborting write.\n"); + break; + } + br = fread(buf, 1, NTFS_BUF_SIZE, in); + if (!br) { + if (!feof(in)) ntfs_log_perror("ERROR: fread failed"); + break; + } + bw = ntfs_attr_pwrite(na, offset, br, buf); + if (bw != br) { + ntfs_log_perror("ERROR: ntfs_attr_pwrite failed"); + break; + } + offset += bw; + } + ntfs_log_verbose("Syncing.\n"); + result = 0; + free(buf); +close_attr: + ntfs_attr_close(na); +close_dst: + while (ntfs_inode_close(out)) { + if (errno != EBUSY) { + ntfs_log_error("Sync failed. Run chkdsk.\n"); + break; + } + ntfs_log_error("Device busy. Will retry sync in 3 seconds.\n"); + sleep(3); + } +close_src: + fclose(in); +umount: + ntfs_umount(vol, FALSE); + ntfs_log_verbose("Done.\n"); + return result; +} diff --git a/ntfsprogs/ntfsdecrypt.c b/ntfsprogs/ntfsdecrypt.c new file mode 100644 index 00000000..8ec62271 --- /dev/null +++ b/ntfsprogs/ntfsdecrypt.c @@ -0,0 +1,1350 @@ +/** + * ntfsdecrypt - Decrypt ntfs encrypted files. Part of the Linux-NTFS project. + * + * Copyright (c) 2005 Yuval Fledel + * Copyright (c) 2005 Anton Altaparmakov + * + * This utility will decrypt files and print the decrypted data on the standard + * output. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 "config.h" + +#if !defined(HAVE_GCRYPT_H) || !defined(HAVE_GNUTLS_PKCS12_H) +#error A required header file is missing. Aborting. +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_GCRYPT_H +#include +#endif +#ifdef HAVE_GNUTLS_PKCS12_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "utils.h" +/* #include "version.h" */ + +typedef gcry_sexp_t ntfs_rsa_private_key; + +/** + * enum NTFS_CRYPTO_ALGORITHMS - List of crypto algorithms used by EFS (32 bit) + * + * To choose which one is used in Windows, create or set the REG_DWORD registry + * key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\EFS\ + * AlgorithmID to the value of your chosen crypto algorithm, e.g. to use DesX, + * set AlgorithmID to 0x6604. + * + * Note that the Windows versions I have tried so far (all are high crypto + * enabled) ignore the AlgorithmID value if it is not one of CALG_3DES, + * CALG_DESX, or CALG_AES_256, i.e. you cannot select CALG_DES at all using + * this registry key. It would be interesting to check out encryption on one + * of the "crippled" crypto Windows versions... + */ +typedef enum { + CALG_DES = const_cpu_to_le32(0x6601), + /* If not one of the below three, fall back to standard Des. */ + CALG_3DES = const_cpu_to_le32(0x6603), + CALG_DESX = const_cpu_to_le32(0x6604), + CALG_AES_256 = const_cpu_to_le32(0x6610), +} NTFS_CRYPTO_ALGORITHMS; + +/** + * struct ntfs_fek - Decrypted, in-memory file encryption key. + */ +typedef struct { + gcry_cipher_hd_t gcry_cipher_hd; + u32 alg_id; + u8 *key_data; + gcry_cipher_hd_t *des_gcry_cipher_hd_ptr; +} ntfs_fek; + +/* DESX-MS128 implementation for libgcrypt. */ +static gcry_module_t ntfs_desx_module; +static unsigned ntfs_desx_algorithm_id = -1; + +typedef struct { + u64 in_whitening, out_whitening; + gcry_cipher_hd_t gcry_cipher_hd; +} ntfs_desx_ctx; + +struct options { + char *keyfile; /* .pfx file containing the user's private key. */ + char *device; /* Device/File to work with */ + char *file; /* File to display */ + s64 inode; /* Inode to work with */ + ATTR_TYPES attr; /* Attribute type to display */ + int force; /* Override common sense */ + int quiet; /* Less output */ + int verbose; /* Extra output */ +}; + +static const char *EXEC_NAME = "ntfsdecrypt"; +static struct options opts; + +static ntfschar EFS[5] = { + const_cpu_to_le16('$'), const_cpu_to_le16('E'), const_cpu_to_le16('F'), + const_cpu_to_le16('S'), const_cpu_to_le16('\0') +}; + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Decrypt files and print on the " + "standard output.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c) 2005 Yuval Fledel\n"); + ntfs_log_info("Copyright (c) 2005 Anton Altaparmakov\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] -k name.pfx device [file]\n\n" + " -i, --inode num Display this inode\n\n" + " -k --keyfile name.pfx Use file name as the user's private key file.\n" + " -f --force Use less caution\n" + " -h --help Print this help\n" + " -q --quiet Less output\n" + " -V --version Version information\n" + " -v --verbose More output\n\n", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char **argv) +{ + static const char *sopt = "-fh?i:k:qVv"; + static const struct option lopt[] = { + {"force", no_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"inode", required_argument, NULL, 'i'}, + {"keyfile", required_argument, NULL, 'k'}, + {"quiet", no_argument, NULL, 'q'}, + {"version", no_argument, NULL, 'V'}, + {"verbose", no_argument, NULL, 'v'}, + {NULL, 0, NULL, 0} + }; + + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + + opterr = 0; /* We'll handle the errors, thank you. */ + + opts.inode = -1; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) + opts.device = argv[optind - 1]; + else if (!opts.file) + opts.file = argv[optind - 1]; + else { + ntfs_log_error("You must specify exactly one " + "file.\n"); + err++; + } + break; + case 'f': + opts.force++; + break; + case 'h': + case '?': + help++; + break; + case 'k': + if (!opts.keyfile) + opts.keyfile = argv[optind - 1]; + else { + ntfs_log_error("You must specify exactly one " + "key file.\n"); + err++; + } + break; + case 'i': + if (opts.inode != -1) + ntfs_log_error("You must specify exactly one " + "inode.\n"); + else if (utils_parse_size(optarg, &opts.inode, FALSE)) + break; + else + ntfs_log_error("Couldn't parse inode number.\n"); + err++; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'V': + ver++; + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + default: + ntfs_log_error("Unknown option '%s'.\n", + argv[optind - 1]); + err++; + break; + } + } + + if (help || ver) { + opts.quiet = 0; + ntfs_log_set_levels(NTFS_LOG_LEVEL_QUIET); + } else { + if (!opts.keyfile) { + ntfs_log_error("You must specify a key file.\n"); + err++; + } else if (opts.device == NULL) { + ntfs_log_error("You must specify a device.\n"); + err++; + } else if (opts.file == NULL && opts.inode == -1) { + ntfs_log_error("You must specify a file or inode with " + "the -i option.\n"); + err++; + } else if (opts.file != NULL && opts.inode != -1) { + ntfs_log_error("You can't specify both a file and " + "inode.\n"); + err++; + } + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose " + "at the same time.\n"); + err++; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + return (!err && !help && !ver); +} + +/** + * ntfs_pkcs12_load_pfxfile + */ +static int ntfs_pkcs12_load_pfxfile(const char *keyfile, u8 **pfx, + unsigned *pfx_size) +{ + int f, to_read, total, attempts, br; + struct stat key_stat; + + if (!keyfile || !pfx || !pfx_size) { + ntfs_log_error("You have to specify the key file, a pointer " + "to hold the key file contents, and a pointer " + "to hold the size of the key file contents.\n"); + return -1; + } + f = open(keyfile, O_RDONLY); + if (f == -1) { + ntfs_log_perror("Failed to open key file"); + return -1; + } + if (fstat(f, &key_stat) == -1) { + ntfs_log_perror("Failed to stat key file"); + goto file_out; + } + if (!S_ISREG(key_stat.st_mode)) { + ntfs_log_error("Key file is not a regular file, cannot read " + "it.\n"); + goto file_out; + } + if (!key_stat.st_size) { + ntfs_log_error("Key file has zero size.\n"); + goto file_out; + } + *pfx = malloc(key_stat.st_size + 1); + if (!*pfx) { + ntfs_log_perror("Failed to allocate buffer for key file " + "contents"); + goto file_out; + } + to_read = key_stat.st_size; + total = attempts = 0; + do { + br = read(f, *pfx + total, to_read); + if (br == -1) { + ntfs_log_perror("Failed to read from key file"); + goto free_out; + } + if (!br) + attempts++; + to_read -= br; + total += br; + } while (to_read > 0 && attempts < 3); + close(f); + /* Make sure it is zero terminated. */ + (*pfx)[key_stat.st_size] = 0; + *pfx_size = key_stat.st_size; + return 0; +free_out: + free(*pfx); +file_out: + close(f); + return -1; +} + +/** + * ntfs_crypto_init + */ +static int ntfs_crypto_init(void) +{ + int err; + + /* Initialize gcrypt library. Note: Must come before GNU TLS init. */ + if (gcry_control(GCRYCTL_DISABLE_SECMEM, 0) != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to initialize the gcrypt library.\n"); + return -1; + } + /* Initialize GNU TLS library. Note: Must come after libgcrypt init. */ + err = gnutls_global_init(); + if (err < 0) { + ntfs_log_error("Failed to initialize GNU TLS library: %s\n", + gnutls_strerror(err)); + return -1; + } + return 0; +} + +/** + * ntfs_crypto_deinit + */ +static void ntfs_crypto_deinit(void) +{ + gnutls_global_deinit(); + if (ntfs_desx_module) { + gcry_cipher_unregister(ntfs_desx_module); + ntfs_desx_module = NULL; + ntfs_desx_algorithm_id = -1; + } +} + +/** + * ntfs_rsa_private_key_import_from_gnutls + */ +static ntfs_rsa_private_key ntfs_rsa_private_key_import_from_gnutls( + gnutls_x509_privkey_t priv_key) +{ + int i, j; + size_t tmp_size; + gnutls_datum_t rd[6]; + gcry_mpi_t rm[6]; + gcry_sexp_t rsa_key; + + /* Extract the RSA parameters from the GNU TLS private key. */ + if (gnutls_x509_privkey_export_rsa_raw(priv_key, &rd[0], &rd[1], + &rd[2], &rd[3], &rd[4], &rd[5])) { + ntfs_log_error("Failed to export rsa parameters. (Is the " + "key an RSA private key?)\n"); + return NULL; + } + /* Convert each RSA parameter to mpi format. */ + for (i = 0; i < 6; i++) { + if (gcry_mpi_scan(&rm[i], GCRYMPI_FMT_USG, rd[i].data, + rd[i].size, &tmp_size) != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to convert RSA parameter %i " + "to mpi format (size %d)\n", i, + rd[i].size); + rsa_key = NULL; + break; + } + } + /* Release the no longer needed datum values. */ + for (j = 0; j < 6; j++) { + if (rd[j].data && rd[j].size) + gnutls_free(rd[j].data); + } + /* + * Build the gcrypt private key, note libgcrypt uses p and q inversed + * to what gnutls uses. + */ + if (i == 6 && gcry_sexp_build(&rsa_key, NULL, + "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", + rm[0], rm[1], rm[2], rm[4], rm[3], rm[5]) != + GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to build RSA private key s-exp.\n"); + rsa_key = NULL; + } + /* Release the no longer needed mpi values. */ + for (j = 0; j < i; j++) + gcry_mpi_release(rm[j]); + return (ntfs_rsa_private_key)rsa_key; +} + +/** + * ntfs_pkcs12_extract_rsa_key + */ +static ntfs_rsa_private_key ntfs_pkcs12_extract_rsa_key(u8 *pfx, int pfx_size, + char *password) +{ + int err, bag_index, flags; + gnutls_datum_t dpfx, dkey; + gnutls_pkcs12_t pkcs12; + gnutls_pkcs12_bag_t bag; + gnutls_x509_privkey_t pkey; + ntfs_rsa_private_key rsa_key = NULL; + + /* Create a pkcs12 structure. */ + err = gnutls_pkcs12_init(&pkcs12); + if (err) { + ntfs_log_error("Failed to initialize PKCS#12 structure: %s\n", + gnutls_strerror(err)); + return NULL; + } + /* Convert the PFX file (DER format) to native pkcs12 format. */ + dpfx.data = pfx; + dpfx.size = pfx_size; + err = gnutls_pkcs12_import(pkcs12, &dpfx, GNUTLS_X509_FMT_DER, 0); + if (err) { + ntfs_log_error("Failed to convert the PFX file from DER to " + "native PKCS#12 format: %s\n", + gnutls_strerror(err)); + goto out; + } + /* + * Verify that the password is correct and that the key file has not + * been tampered with. Note if the password has zero length and the + * verification fails, retry with password set to NULL. This is needed + * to get passwordless .pfx files generated with Windows XP SP1 (and + * probably earlier versions of Windows) to work. + */ +retry_verify: + err = gnutls_pkcs12_verify_mac(pkcs12, password); + if (err) { + if (err == GNUTLS_E_MAC_VERIFY_FAILED && + password && !strlen(password)) { + password = NULL; + goto retry_verify; + } + ntfs_log_error("Failed to verify the MAC (%s). Is the " + "password correct?\n", gnutls_strerror(err)); + goto out; + } + for (bag_index = 0; ; bag_index++) { + err = gnutls_pkcs12_bag_init(&bag); + if (err) { + ntfs_log_error("Failed to initialize PKCS#12 Bag " + "structure: %s\n", + gnutls_strerror(err)); + goto out; + } + err = gnutls_pkcs12_get_bag(pkcs12, bag_index, bag); + if (err) { + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + ntfs_log_error("Failed to obtain Bag from PKCS#12 " + "structure: %s\n", + gnutls_strerror(err)); + goto bag_out; + } +check_again: + err = gnutls_pkcs12_bag_get_count(bag); + if (err < 0) { + ntfs_log_error("Failed to obtain Bag count: %s\n", + gnutls_strerror(err)); + goto bag_out; + } + err = gnutls_pkcs12_bag_get_type(bag, 0); + if (err < 0) { + ntfs_log_error("Failed to determine Bag type: %s\n", + gnutls_strerror(err)); + goto bag_out; + } + flags = 0; + switch (err) { + case GNUTLS_BAG_PKCS8_KEY: + flags = GNUTLS_PKCS_PLAIN; + case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: + err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey); + if (err < 0) { + ntfs_log_error("Failed to obtain Bag data: " + "%s\n", gnutls_strerror(err)); + goto bag_out; + } + err = gnutls_x509_privkey_init(&pkey); + if (err) { + ntfs_log_error("Failed to initialized " + "private key structure: %s\n", + gnutls_strerror(err)); + goto bag_out; + } + /* Decrypt the private key into GNU TLS format. */ + err = gnutls_x509_privkey_import_pkcs8(pkey, &dkey, + GNUTLS_X509_FMT_DER, password, flags); + if (err) { + ntfs_log_error("Failed to convert private " + "key from DER to GNU TLS " + "format: %s\n", + gnutls_strerror(err)); + goto key_out; + } +#if 0 + /* + * Export the key again, but unencrypted, and output it + * to stderr. Note the output has an RSA header so to + * compare to openssl pkcs12 -nodes -in myfile.pfx + * output need to ignore the part of the key between + * the first "MII..." up to the second "MII...". The + * actual RSA private key begins at the second "MII..." + * and in my testing at least was identical to openssl + * output and was also identical both on big and little + * endian so gnutls should be endianness safe. + */ + char *buf = malloc(8192); + size_t bufsize = 8192; + err = gnutls_x509_privkey_export_pkcs8(pkey, + GNUTLS_X509_FMT_PEM, "", GNUTLS_PKCS_PLAIN, buf, + &bufsize); + if (err) { + ntfs_log_error("eek1\n"); + exit(1); + } + ntfs_log_error("%s\n", buf); + free(buf); +#endif + /* Convert the private key to our internal format. */ + rsa_key = ntfs_rsa_private_key_import_from_gnutls(pkey); + goto key_out; + case GNUTLS_BAG_ENCRYPTED: + err = gnutls_pkcs12_bag_decrypt(bag, password); + if (err) { + ntfs_log_error("Failed to decrypt Bag: %s\n", + gnutls_strerror(err)); + goto bag_out; + } + goto check_again; + default: + /* We do not care about other types. */ + break; + } + gnutls_pkcs12_bag_deinit(bag); + } +key_out: + gnutls_x509_privkey_deinit(pkey); +bag_out: + gnutls_pkcs12_bag_deinit(bag); +out: + gnutls_pkcs12_deinit(pkcs12); + return rsa_key; +} + +/** + * ntfs_rsa_private_key_release + */ +static void ntfs_rsa_private_key_release(ntfs_rsa_private_key rsa_key) +{ + gcry_sexp_release((gcry_sexp_t)rsa_key); +} + +/** + * ntfs_buffer_reverse - + * + * This is a utility function for reversing the order of a buffer in place. + * Users of this function should be very careful not to sweep byte order + * problems under the rug. + */ +static inline void ntfs_buffer_reverse(u8 *buf, unsigned buf_size) +{ + unsigned i; + u8 t; + + for (i = 0; i < buf_size / 2; i++) { + t = buf[i]; + buf[i] = buf[buf_size - i - 1]; + buf[buf_size - i - 1] = t; + } +} + +#ifndef HAVE_STRNLEN +/** + * strnlen - strnlen is a gnu extension so emulate it if not present + */ +static size_t strnlen(const char *s, size_t maxlen) +{ + const char *p, *end; + + /* Look for a '\0' character. */ + for (p = s, end = s + maxlen; p < end && *p; p++) + ; + return p - s; +} +#endif /* ! HAVE_STRNLEN */ + +/** + * ntfs_raw_fek_decrypt - + * + * Note: decrypting into the input buffer. + */ +static unsigned ntfs_raw_fek_decrypt(u8 *fek, u32 fek_size, + ntfs_rsa_private_key rsa_key) +{ + gcry_mpi_t fek_mpi; + gcry_sexp_t fek_sexp, fek_sexp2; + gcry_error_t err; + size_t size, padding; + + /* Reverse the raw FEK. */ + ntfs_buffer_reverse(fek, fek_size); + /* Convert the FEK to internal MPI format. */ + err = gcry_mpi_scan(&fek_mpi, GCRYMPI_FMT_USG, fek, fek_size, NULL); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to convert file encryption key to " + "internal MPI format: %s\n", + gcry_strerror(err)); + return 0; + } + /* Create an internal S-expression from the FEK. */ + err = gcry_sexp_build(&fek_sexp, NULL, + "(enc-val (flags) (rsa (a %m)))", fek_mpi); + gcry_mpi_release(fek_mpi); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to create internal S-expression of " + "the file encryption key: %s\n", + gcry_strerror(err)); + return 0; + } + /* Decrypt the FEK. */ + err = gcry_pk_decrypt(&fek_sexp2, fek_sexp, (gcry_sexp_t)rsa_key); + gcry_sexp_release(fek_sexp); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to decrypt the file encryption key: " + "%s\n", gcry_strerror(err)); + return 0; + } + /* Extract the actual FEK from the decrypted raw S-expression. */ + fek_sexp = gcry_sexp_find_token(fek_sexp2, "value", 0); + gcry_sexp_release(fek_sexp2); + if (!fek_sexp) { + ntfs_log_error("Failed to find the decrypted file encryption " + "key in the internal S-expression.\n"); + return 0; + } + /* Convert the decrypted FEK S-expression into MPI format. */ + fek_mpi = gcry_sexp_nth_mpi(fek_sexp, 1, GCRYMPI_FMT_USG); + gcry_sexp_release(fek_sexp); + if (!fek_mpi) { + ntfs_log_error("Failed to convert the decrypted file " + "encryption key S-expression to internal MPI " + "format.\n"); + return 0; + } + /* Convert the decrypted FEK from MPI format to binary data. */ + err = gcry_mpi_print(GCRYMPI_FMT_USG, fek, fek_size, &size, fek_mpi); + gcry_mpi_release(fek_mpi); + if (err != GPG_ERR_NO_ERROR || !size) { + ntfs_log_error("Failed to convert decrypted file encryption " + "key from internal MPI format to binary data: " + "%s\n", gcry_strerror(err)); + return 0; + } + /* + * Finally, remove the PKCS#1 padding and return the size of the + * decrypted FEK. + */ + padding = strnlen((char *)fek, size) + 1; + if (padding > size) { + ntfs_log_error("Failed to remove PKCS#1 padding from " + "decrypted file encryption key.\n"); + return 0; + } + size -= padding; + memmove(fek, fek + padding, size); + return size; +} + +/** + * ntfs_desx_key_expand - expand a 128-bit desx key to the needed 192-bit key + * @src: source buffer containing 128-bit key + * + * Expands the on-disk 128-bit desx key to the needed des key, the in-, and the + * out-whitening keys required to perform desx {de,en}cryption. + */ +static gcry_error_t ntfs_desx_key_expand(const u8 *src, u32 *des_key, + u64 *out_whitening, u64 *in_whitening) +{ + static const u8 *salt1 = (const u8*)"Dan Simon "; + static const u8 *salt2 = (const u8*)"Scott Field"; + static const int salt_len = 12; + gcry_md_hd_t hd1, hd2; + u32 *md; + gcry_error_t err; + + err = gcry_md_open(&hd1, GCRY_MD_MD5, 0); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to open MD5 digest.\n"); + return err; + } + /* Hash the on-disk key. */ + gcry_md_write(hd1, src, 128 / 8); + /* Copy the current hash for efficiency. */ + err = gcry_md_copy(&hd2, hd1); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to copy MD5 digest object.\n"); + goto out; + } + /* Hash with the first salt and store the result. */ + gcry_md_write(hd1, salt1, salt_len); + md = (u32*)gcry_md_read(hd1, 0); + des_key[0] = md[0] ^ md[1]; + des_key[1] = md[2] ^ md[3]; + /* Hash with the second salt and store the result. */ + gcry_md_write(hd2, salt2, salt_len); + md = (u32*)gcry_md_read(hd2, 0); + *out_whitening = *(u64*)md; + *in_whitening = *(u64*)(md + 2); + gcry_md_close(hd2); +out: + gcry_md_close(hd1); + return err; +} + +/** + * ntfs_desx_setkey - libgcrypt set_key implementation for DES-X-MS128 + * @context: pointer to a variable of type ntfs_desx_ctx + * @key: the 128 bit DES-X-MS128 key, concated with the DES handle + * @keylen: must always be 16 + * + * This is the libgcrypt set_key implementation for DES-X-MS128. + */ +static gcry_err_code_t ntfs_desx_setkey(void *context, const u8 *key, + unsigned keylen) +{ + ntfs_desx_ctx *ctx = context; + gcry_error_t err; + u8 des_key[8]; + + if (keylen != 16) { + ntfs_log_error("Key length for desx must be 16.\n"); + return GPG_ERR_INV_KEYLEN; + } + err = gcry_cipher_open(&ctx->gcry_cipher_hd, GCRY_CIPHER_DES, + GCRY_CIPHER_MODE_ECB, 0); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to open des cipher (error 0x%x).\n", + err); + return err; + } + err = ntfs_desx_key_expand(key, (u32*)des_key, &ctx->out_whitening, + &ctx->in_whitening); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to expand desx key (error 0x%x).\n", + err); + gcry_cipher_close(ctx->gcry_cipher_hd); + return err; + } + err = gcry_cipher_setkey(ctx->gcry_cipher_hd, des_key, sizeof(des_key)); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to set des key (error 0x%x).\n", err); + gcry_cipher_close(ctx->gcry_cipher_hd); + return err; + } + /* + * Take a note of the ctx->gcry_cipher_hd since we need to close it at + * ntfs_decrypt_data_key_close() time. + */ + **(gcry_cipher_hd_t***)(key + ((keylen + 7) & ~7)) = + &ctx->gcry_cipher_hd; + return GPG_ERR_NO_ERROR; +} + +/** + * ntfs_desx_decrypt + */ +static void ntfs_desx_decrypt(void *context, u8 *outbuf, const u8 *inbuf) +{ + ntfs_desx_ctx *ctx = context; + gcry_error_t err; + + err = gcry_cipher_reset(ctx->gcry_cipher_hd); + if (err != GPG_ERR_NO_ERROR) + ntfs_log_error("Failed to reset des cipher (error 0x%x).\n", + err); + *(u64*)outbuf = *(const u64*)inbuf ^ ctx->out_whitening; + err = gcry_cipher_encrypt(ctx->gcry_cipher_hd, outbuf, 8, NULL, 0); + if (err != GPG_ERR_NO_ERROR) + ntfs_log_error("Des decryption failed (error 0x%x).\n", err); + *(u64*)outbuf ^= ctx->in_whitening; +} + +static gcry_cipher_spec_t ntfs_desx_cipher = { + .name = "DES-X-MS128", + .blocksize = 8, + .keylen = 128, + .contextsize = sizeof(ntfs_desx_ctx), + .setkey = ntfs_desx_setkey, + .decrypt = ntfs_desx_decrypt, +}; + +//#define DO_CRYPTO_TESTS 1 + +#ifdef DO_CRYPTO_TESTS + +/* Do not remove this test code from this file! AIA */ +/** + * ntfs_desx_key_expand_test + */ +static BOOL ntfs_desx_key_expand_test(void) +{ + const u8 known_desx_on_disk_key[16] = { + 0xa1, 0xf9, 0xe0, 0xb2, 0x53, 0x23, 0x9e, 0x8f, + 0x0f, 0x91, 0x45, 0xd9, 0x8e, 0x20, 0xec, 0x30 + }; + const u8 known_des_key[8] = { + 0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f, + }; + const u8 known_out_whitening[8] = { + 0xed, 0xda, 0x4c, 0x47, 0x60, 0x49, 0xdb, 0x8d, + }; + const u8 known_in_whitening[8] = { + 0x75, 0xf6, 0xa0, 0x1a, 0xc0, 0xca, 0x28, 0x1e + }; + u64 test_out_whitening, test_in_whitening; + union { + u64 u64; + u32 u32[2]; + } test_des_key; + gcry_error_t err; + BOOL res; + + err = ntfs_desx_key_expand(known_desx_on_disk_key, test_des_key.u32, + &test_out_whitening, &test_in_whitening); + if (err != GPG_ERR_NO_ERROR) + res = FALSE; + else + res = test_des_key.u64 == *(u64*)known_des_key && + test_out_whitening == + *(u64*)known_out_whitening && + test_in_whitening == + *(u64*)known_in_whitening; + ntfs_log_error("Testing whether ntfs_desx_key_expand() works: %s\n", + res ? "SUCCESS" : "FAILED"); + return res; +} + +/** + * ntfs_des_test + */ +static BOOL ntfs_des_test(void) +{ + const u8 known_des_key[8] = { + 0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f + }; + const u8 known_des_encrypted_data[8] = { + 0xdc, 0xf7, 0x68, 0x2a, 0xaf, 0x48, 0x53, 0x0f + }; + const u8 known_decrypted_data[8] = { + 0xd8, 0xd9, 0x15, 0x23, 0x5b, 0x88, 0x0e, 0x09 + }; + u8 test_decrypted_data[8]; + int res; + gcry_error_t err; + gcry_cipher_hd_t gcry_cipher_hd; + + err = gcry_cipher_open(&gcry_cipher_hd, GCRY_CIPHER_DES, + GCRY_CIPHER_MODE_ECB, 0); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to open des cipher (error 0x%x).\n", + err); + return FALSE; + } + err = gcry_cipher_setkey(gcry_cipher_hd, known_des_key, + sizeof(known_des_key)); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to set des key (error 0x%x.\n", err); + gcry_cipher_close(gcry_cipher_hd); + return FALSE; + } + /* + * Apply DES decryption (ntfs actually uses encryption when decrypting). + */ + err = gcry_cipher_encrypt(gcry_cipher_hd, test_decrypted_data, + sizeof(test_decrypted_data), known_des_encrypted_data, + sizeof(known_des_encrypted_data)); + gcry_cipher_close(gcry_cipher_hd); + if (err) { + ntfs_log_error("Failed to des decrypt test data (error " + "0x%x).\n", err); + return FALSE; + } + res = !memcmp(test_decrypted_data, known_decrypted_data, + sizeof(known_decrypted_data)); + ntfs_log_error("Testing whether des decryption works: %s\n", + res ? "SUCCESS" : "FAILED"); + return res; +} + +#else /* !defined(DO_CRYPTO_TESTS) */ + +/** + * ntfs_desx_key_expand_test + */ +static inline BOOL ntfs_desx_key_expand_test(void) +{ + return TRUE; +} + +/** + * ntfs_des_test + */ +static inline BOOL ntfs_des_test(void) +{ + return TRUE; +} + +#endif /* !defined(DO_CRYPTO_TESTS) */ + +/** + * ntfs_fek_import_from_raw + */ +static ntfs_fek *ntfs_fek_import_from_raw(u8 *fek_buf, + unsigned fek_size __attribute__((unused))) +{ + ntfs_fek *fek; + u32 key_size, wanted_key_size, gcry_algo; + gcry_error_t err; + + // TODO: Sanity checking of sizes and offsets. + key_size = le32_to_cpup(fek_buf); + //ntfs_log_debug("key_size 0x%x\n", key_size); + fek = malloc(((((sizeof(*fek) + 7) & ~7) + key_size + 7) & ~7) + + sizeof(gcry_cipher_hd_t)); + if (!fek) { + errno = ENOMEM; + return NULL; + } + fek->alg_id = *(u32*)(fek_buf + 8); + //ntfs_log_debug("alg_id 0x%x\n", le32_to_cpu(fek->alg_id)); + fek->key_data = (u8*)fek + ((sizeof(*fek) + 7) & ~7); + memcpy(fek->key_data, fek_buf + 16, key_size); + fek->des_gcry_cipher_hd_ptr = NULL; + *(gcry_cipher_hd_t***)(fek->key_data + ((key_size + 7) & ~7)) = + &fek->des_gcry_cipher_hd_ptr; + switch (fek->alg_id) { + case CALG_DESX: + if (!ntfs_desx_module) { + if (!ntfs_desx_key_expand_test() || !ntfs_des_test()) { + err = EINVAL; + goto out; + } + err = gcry_cipher_register(&ntfs_desx_cipher, + &ntfs_desx_algorithm_id, + &ntfs_desx_module); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to register desx " + "cipher: %s\n", + gcry_strerror(err)); + err = EINVAL; + goto out; + } + } + wanted_key_size = 16; + gcry_algo = ntfs_desx_algorithm_id; + break; + case CALG_3DES: + wanted_key_size = 24; + gcry_algo = GCRY_CIPHER_3DES; + break; + case CALG_AES_256: + wanted_key_size = 32; + gcry_algo = GCRY_CIPHER_AES256; + break; + default: + wanted_key_size = 8; + gcry_algo = GCRY_CIPHER_DES; + if (fek->alg_id == CALG_DES) + ntfs_log_error("DES is not supported at present\n"); + else + ntfs_log_error("Unknown crypto algorithm 0x%x\n", + le32_to_cpu(fek->alg_id)); + ntfs_log_error(". Please email %s and say that you saw this " + "message. We will then try to implement " + "support for this algorithm.\n", NTFS_DEV_LIST); + err = EOPNOTSUPP; + goto out; + } + if (key_size != wanted_key_size) { + ntfs_log_error("%s key of %u bytes but needed size is %u " + "bytes, assuming corrupt key. Aborting.\n", + gcry_cipher_algo_name(gcry_algo), + (unsigned)key_size, (unsigned)wanted_key_size); + err = EIO; + goto out; + } + err = gcry_cipher_open(&fek->gcry_cipher_hd, gcry_algo, + GCRY_CIPHER_MODE_CBC, 0); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("gcry_cipher_open() failed: %s\n", + gcry_strerror(err)); + err = EINVAL; + goto out; + } + err = gcry_cipher_setkey(fek->gcry_cipher_hd, fek->key_data, key_size); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("gcry_cipher_setkey() failed: %s\n", + gcry_strerror(err)); + gcry_cipher_close(fek->gcry_cipher_hd); + err = EINVAL; + goto out; + } + return fek; +out: + free(fek); + errno = err; + return NULL; +} + +/** + * ntfs_fek_release + */ +static void ntfs_fek_release(ntfs_fek *fek) +{ + if (fek->des_gcry_cipher_hd_ptr) + gcry_cipher_close(*fek->des_gcry_cipher_hd_ptr); + gcry_cipher_close(fek->gcry_cipher_hd); + free(fek); +} + +/** + * ntfs_df_array_fek_get + */ +static ntfs_fek *ntfs_df_array_fek_get(EFS_DF_ARRAY_HEADER *df_array, + ntfs_rsa_private_key rsa_key) +{ + EFS_DF_HEADER *df_header; + EFS_DF_CREDENTIAL_HEADER *df_cred; + EFS_DF_CERT_THUMBPRINT_HEADER *df_cert; + u8 *fek_buf; + ntfs_fek *fek; + u32 df_count, fek_size; + unsigned i; + + df_header = (EFS_DF_HEADER*)(df_array + 1); + df_count = le32_to_cpu(df_array->df_count); + for (i = 0; i < df_count; i++) { + df_cred = (EFS_DF_CREDENTIAL_HEADER*)((u8*)df_header + + le32_to_cpu(df_header->cred_header_offset)); + df_cert = (EFS_DF_CERT_THUMBPRINT_HEADER*)((u8*)df_cred + + le32_to_cpu( + df_cred->cert_thumbprint_header_offset)); + fek_size = le32_to_cpu(df_header->fek_size); + fek_buf = (u8*)df_header + le32_to_cpu(df_header->fek_offset); + /* Decrypt the FEK. Note: This is done in place. */ + fek_size = ntfs_raw_fek_decrypt(fek_buf, fek_size, rsa_key); + if (fek_size) { + /* Convert the FEK to our internal format. */ + fek = ntfs_fek_import_from_raw(fek_buf, fek_size); + if (fek) + return fek; + ntfs_log_error("Failed to convert the decrypted file " + "encryption key to internal format.\n"); + } else + ntfs_log_error("Failed to decrypt the file " + "encryption key.\n"); + df_header = (EFS_DF_HEADER*)((u8*)df_header + + le32_to_cpu(df_header->df_length)); + } + return NULL; +} + +/** + * ntfs_inode_fek_get - + */ +static ntfs_fek *ntfs_inode_fek_get(ntfs_inode *inode, + ntfs_rsa_private_key rsa_key) +{ + EFS_ATTR_HEADER *efs; + EFS_DF_ARRAY_HEADER *df_array; + ntfs_fek *fek = NULL; + + /* Obtain the $EFS contents. */ + efs = ntfs_attr_readall(inode, AT_LOGGED_UTILITY_STREAM, EFS, 4, NULL); + if (!efs) { + ntfs_log_perror("Failed to read $EFS attribute"); + return NULL; + } + /* Iterate through the DDFs & DRFs until we obtain a key. */ + if (efs->offset_to_ddf_array) { + df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs + + le32_to_cpu(efs->offset_to_ddf_array)); + fek = ntfs_df_array_fek_get(df_array, rsa_key); + } + if (!fek && efs->offset_to_drf_array) { + df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs + + le32_to_cpu(efs->offset_to_drf_array)); + fek = ntfs_df_array_fek_get(df_array, rsa_key); + } + free(efs); + return fek; +} + +/** + * ntfs_fek_decrypt_sector + */ +static int ntfs_fek_decrypt_sector(ntfs_fek *fek, u8 *data, const u64 offset) +{ + gcry_error_t err; + + err = gcry_cipher_reset(fek->gcry_cipher_hd); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to reset cipher: %s\n", + gcry_strerror(err)); + return -1; + } + /* + * Note: You may wonder why we are not calling gcry_cipher_setiv() here + * instead of doing it by hand after the decryption. The answer is + * that gcry_cipher_setiv() wants an iv of length 8 bytes but we give + * it a length of 16 for AES256 so it does not like it. + */ + err = gcry_cipher_decrypt(fek->gcry_cipher_hd, data, 512, NULL, 0); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Decryption failed: %s\n", gcry_strerror(err)); + return -1; + } + /* Apply the IV. */ + if (fek->alg_id == CALG_AES_256) { + ((u64*)data)[0] ^= cpu_to_le64(0x5816657be9161312ULL + offset); + ((u64*)data)[1] ^= cpu_to_le64(0x1989adbe44918961ULL + offset); + } else { + /* All other algos (Des, 3Des, DesX) use the same IV. */ + ((u64*)data)[0] ^= cpu_to_le64(0x169119629891ad13ULL + offset); + } + return 512; +} + +/** + * ntfs_cat_decrypt + * TODO: + */ +static int ntfs_cat_decrypt(ntfs_inode *inode, ntfs_fek *fek) +{ + int bufsize = 512; + unsigned char *buffer; + ntfs_attr *attr; + s64 bytes_read, written, offset, total; + s64 old_data_size, old_initialized_size; + int i; + + buffer = malloc(bufsize); + if (!buffer) + return 1; + attr = ntfs_attr_open(inode, AT_DATA, NULL, 0); + if (!attr) { + ntfs_log_error("Cannot cat a directory.\n"); + free(buffer); + return 1; + } + total = attr->data_size; + + // hack: make sure attr will not be commited to disk if you use this. + // clear the encrypted bit, otherwise the library won't allow reading. + NAttrClearEncrypted(attr); + // extend the size, we may need to read past the end of the stream. + old_data_size = attr->data_size; + old_initialized_size = attr->initialized_size; + attr->data_size = attr->initialized_size = attr->allocated_size; + + offset = 0; + while (total > 0) { + bytes_read = ntfs_attr_pread(attr, offset, 512, buffer); + if (bytes_read == -1) { + ntfs_log_perror("ERROR: Couldn't read file"); + break; + } + if (!bytes_read) + break; + if ((i = ntfs_fek_decrypt_sector(fek, buffer, offset)) < + bytes_read) { + ntfs_log_perror("ERROR: Couldn't decrypt all data!"); + ntfs_log_error("%u/%lld/%lld/%lld\n", i, + (long long)bytes_read, (long long)offset, + (long long)total); + break; + } + if (bytes_read > total) + bytes_read = total; + written = fwrite(buffer, 1, bytes_read, stdout); + if (written != bytes_read) { + ntfs_log_perror("ERROR: Couldn't output all data!"); + break; + } + offset += bytes_read; + total -= bytes_read; + } + attr->data_size = old_data_size; + attr->initialized_size = old_initialized_size; + NAttrSetEncrypted(attr); + ntfs_attr_close(attr); + free(buffer); + return 0; +} + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + u8 *pfx_buf; + char *password; + ntfs_rsa_private_key rsa_key; + ntfs_volume *vol; + ntfs_inode *inode; + ntfs_fek *fek; + unsigned pfx_size; + int res; + + ntfs_log_set_handler(ntfs_log_handler_stderr); + + if (!parse_options(argc, argv)) + return 1; + utils_set_locale(); + + /* Initialize crypto in ntfs. */ + if (ntfs_crypto_init()) { + ntfs_log_error("Failed to initialize crypto. Aborting.\n"); + return 1; + } + /* Load the PKCS#12 (.pfx) file containing the user's private key. */ + if (ntfs_pkcs12_load_pfxfile(opts.keyfile, &pfx_buf, &pfx_size)) { + ntfs_log_error("Failed to load key file. Aborting.\n"); + ntfs_crypto_deinit(); + return 1; + } + /* Ask the user for their password. */ + password = getpass("Enter the password with which the private key was " + "encrypted: "); + if (!password) { + ntfs_log_perror("Failed to obtain user password"); + free(pfx_buf); + ntfs_crypto_deinit(); + return 1; + } + /* Obtain the user's private RSA key from the key file. */ + rsa_key = ntfs_pkcs12_extract_rsa_key(pfx_buf, pfx_size, password); + /* Destroy the password. */ + memset(password, 0, strlen(password)); + /* No longer need the pfx file contents. */ + free(pfx_buf); + if (!rsa_key) { + ntfs_log_error("Failed to extract the private RSA key. Did " + "you perhaps mistype the password?\n"); + ntfs_crypto_deinit(); + return 1; + } + /* Mount the ntfs volume. */ + vol = utils_mount_volume(opts.device, MS_RDONLY, opts.force); + if (!vol) { + ntfs_log_error("Failed to mount ntfs volume. Aborting.\n"); + ntfs_rsa_private_key_release(rsa_key); + ntfs_crypto_deinit(); + return 1; + } + /* Open the encrypted ntfs file. */ + if (opts.inode != -1) + inode = ntfs_inode_open(vol, opts.inode); + else + inode = ntfs_pathname_to_inode(vol, NULL, opts.file); + if (!inode) { + ntfs_log_error("Failed to open encrypted file. Aborting.\n"); + ntfs_umount(vol, FALSE); + ntfs_rsa_private_key_release(rsa_key); + ntfs_crypto_deinit(); + return 1; + } + /* Obtain the file encryption key of the encrypted file. */ + fek = ntfs_inode_fek_get(inode, rsa_key); + ntfs_rsa_private_key_release(rsa_key); + if (fek) { + res = ntfs_cat_decrypt(inode, fek); + ntfs_fek_release(fek); + } else { + ntfs_log_error("Failed to obtain file encryption key. " + "Aborting.\n"); + res = 1; + } + ntfs_inode_close(inode); + ntfs_umount(vol, FALSE); + ntfs_crypto_deinit(); + return res; +} diff --git a/ntfsprogs/ntfsdump_logfile.c b/ntfsprogs/ntfsdump_logfile.c new file mode 100644 index 00000000..4325bc39 --- /dev/null +++ b/ntfsprogs/ntfsdump_logfile.c @@ -0,0 +1,777 @@ +/** + * ntfsdump_logfile - Part of the Linux-NTFS project. + * + * Copyright (c) 2000-2005 Anton Altaparmakov + * + * This utility will interpret the contents of the journal ($LogFile) of an + * NTFS partition and display the results on stdout. Errors will be output to + * stderr. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS source + * in the file COPYING); if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* TODO: + * - Remove the need for clipping at 64MiB. + * - Add normal command line switchs (use getopt_long()). + * - For a volume: allow dumping only uncommitted records. + * - For a file: get an optional command line parameter for the last SN. + * - Sanity checks. + */ + +#include "config.h" + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDARG_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +/* #include "version.h" */ + +typedef struct { + BOOL is_volume; + const char *filename; + s64 data_size; + union { + struct { + ntfs_volume *vol; + ntfs_inode *ni; + ntfs_attr *na; + }; + struct { + int fd; + }; + }; +} logfile_file; + +/** + * logfile_close + */ +static int logfile_close(logfile_file *logfile) +{ + if (logfile->is_volume) { + if (logfile->na) + ntfs_attr_close(logfile->na); + if (logfile->ni && ntfs_inode_close(logfile->ni)) + ntfs_log_perror("Warning: Failed to close $LogFile " + "(inode %i)", FILE_LogFile); + if (ntfs_umount(logfile->vol, 0)) + ntfs_log_perror("Warning: Failed to umount %s", + logfile->filename); + } else { + if (close(logfile->fd)) + ntfs_log_perror("Warning: Failed to close file %s", + logfile->filename); + } + return 0; +} + +/** + * device_err_exit - put an error message, cleanup and exit. + * @vol: volume to unmount. + * @ni: Inode to free. + * @na: Attribute to close. + * + * Use when you wish to exit and collate all the cleanups together. + * if you don't have some parameter to pass, just pass NULL. + */ +__attribute__((noreturn)) +__attribute__((format(printf, 4, 5))) +static void device_err_exit(ntfs_volume *vol, ntfs_inode *ni, + ntfs_attr *na, const char *fmt, ...) +{ + va_list ap; + + if (na) + ntfs_attr_close(na); + if (ni && ntfs_inode_close(ni)) + ntfs_log_perror("Warning: Failed to close $LogFile (inode %i)", + FILE_LogFile); + if (ntfs_umount(vol, 0)) + ntfs_log_perror("Warning: Failed to umount"); + + fprintf(stderr, "ERROR: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + ntfs_log_error("Aborting...\n"); + exit(1); +} + +/** + * log_err_exit - + */ +__attribute__((noreturn)) +__attribute__((format(printf, 2, 3))) +static void log_err_exit(u8 *buf, const char *fmt, ...) +{ + va_list ap; + + free(buf); + + fprintf(stderr, "ERROR: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + ntfs_log_error("Aborting...\n"); + exit(1); +} + +/** + * usage - + */ +__attribute__((noreturn)) +static void usage(const char *exec_name) +{ + ntfs_log_error("%s v%s (libntfs-3g) - Interpret and display information " + "about the journal\n($LogFile) of an NTFS volume.\n" + "Copyright (c) 2000-2005 Anton Altaparmakov.\n" + "%s is free software, released under the GNU General " + "Public License\nand you are welcome to redistribute " + "it under certain conditions.\n%s comes with " + "ABSOLUTELY NO WARRANTY; for details read the GNU\n" + "General Public License to be found in the file " + "COPYING in the main Linux-NTFS\ndistribution " + "directory.\nUsage: %s device\n e.g. %s /dev/hda6\n" + "Alternative usage: %s -f file\n e.g. %s -f " + "MyCopyOfTheLogFile\n", exec_name, VERSION, + exec_name, exec_name, + exec_name, exec_name, exec_name, exec_name); + exit(1); +} + +/** + * logfile_open + */ +static int logfile_open(BOOL is_volume, const char *filename, + logfile_file *logfile) +{ + if (is_volume) { + ntfs_volume *vol; + ntfs_inode *ni; + ntfs_attr *na; + + vol = ntfs_mount(filename, MS_RDONLY); + if (!vol) + log_err_exit(NULL, "Failed to mount %s: %s\n", + filename, strerror(errno)); + ntfs_log_info("Mounted NTFS volume %s (NTFS v%i.%i) on device %s.\n", + vol->vol_name ? vol->vol_name : "", + vol->major_ver, vol->minor_ver, filename); + if (ntfs_version_is_supported(vol)) + device_err_exit(vol, NULL, NULL, + "Unsupported NTFS version.\n"); + ni = ntfs_inode_open(vol, FILE_LogFile); + if (!ni) + device_err_exit(vol, NULL, NULL, "Failed to " + "open $LogFile (inode %i): %s\n", + FILE_LogFile, strerror(errno)); + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) + device_err_exit(vol, ni, NULL, "Failed to open " + "$LogFile/$DATA (attribute 0x%x):" + " %s\n", (unsigned int) + le32_to_cpu(AT_DATA), strerror(errno)); + if (!na->data_size) + device_err_exit(vol, ni, na, "$LogFile has zero " + "length. Run chkdsk /f to correct " + "this.\n"); + logfile->data_size = na->data_size; + logfile->vol = vol; + logfile->ni = ni; + logfile->na = na; + } else { + struct stat sbuf; + int fd; + + if (stat(filename, &sbuf) == -1) { + if (errno == ENOENT) + log_err_exit(NULL, "The file %s does not " + "exist. Did you specify it " + "correctly?\n", filename); + log_err_exit(NULL, "Error getting information about " + "%s: %s\n", filename, strerror(errno)); + } + + fd = open(filename, O_RDONLY); + if (fd == -1) + log_err_exit(NULL, "Failed to open file %s: %s\n", + filename, strerror(errno)); + logfile->data_size = sbuf.st_size; + logfile->fd = fd; + } + + logfile->is_volume = is_volume; + logfile->filename = filename; + + return 0; +} + +/** + * logfile_read + */ +static int logfile_pread(logfile_file *logfile, int ofs, int count, u8 *buf) +{ + int br; + + if (logfile->is_volume) { + br = (int)ntfs_attr_pread(logfile->na, ofs, count, buf); + } else { + if (lseek(logfile->fd, ofs, SEEK_SET)==-1) { + ntfs_log_error("Could not seek to offset %u\n", ofs); + return 0; + } + br = read(logfile->fd, buf, count); + } + if (br != count) { + ntfs_log_error("Only %d out of %d bytes read starting at %d\n", + br, count, ofs); + } + return br; +} + +/** + * restart_header_sanity() + */ +static void restart_header_sanity(RESTART_PAGE_HEADER *rstr, u8 *buf) +{ + unsigned int usa_end_ofs, page_size; + + /* Only CHKD records are allowed to have chkdsk_lsn set. */ + if (!ntfs_is_chkd_record(rstr->magic) && + sle64_to_cpu(rstr->chkdsk_lsn)) + log_err_exit(buf, "$LogFile is corrupt: Restart page header " + "magic is not CHKD but a chkdsk LSN is " + "specified. Cannot handle this yet.\n"); + /* Both system and log page size must be >= 512 and a power of 2. */ + page_size = le32_to_cpu(rstr->log_page_size); + if (page_size < 512 || page_size & (page_size - 1)) + log_err_exit(buf, "$LogFile is corrupt: Restart page header " + "specifies invalid log page size. Cannot " + "handle this yet.\n"); + if (page_size != le32_to_cpu(rstr->system_page_size)) { + page_size = le32_to_cpu(rstr->system_page_size); + if (page_size < 512 || page_size & (page_size - 1)) + log_err_exit(buf, "$LogFile is corrupt: Restart page " + "header specifies invalid system page " + "size. Cannot handle this yet.\n"); + } + /* Abort if the version number is not 1.1. */ + if (sle16_to_cpu(rstr->major_ver != 1) || + sle16_to_cpu(rstr->minor_ver != 1)) + log_err_exit(buf, "Unknown $LogFile version %i.%i. Only know " + "how to handle version 1.1.\n", + sle16_to_cpu(rstr->major_ver), + sle16_to_cpu(rstr->minor_ver)); + /* Verify the location and size of the update sequence array. */ + usa_end_ofs = le16_to_cpu(rstr->usa_ofs) + + le16_to_cpu(rstr->usa_count) * sizeof(u16); + if (page_size / NTFS_BLOCK_SIZE + 1 != le16_to_cpu(rstr->usa_count)) + log_err_exit(buf, "Restart page header in $LogFile is " + "corrupt: Update sequence array size is " + "wrong. Cannot handle this yet.\n"); + if (le16_to_cpu(rstr->usa_ofs) < sizeof(RESTART_PAGE_HEADER)) + log_err_exit(buf, "Restart page header in $LogFile is " + "corrupt: Update sequence array overlaps " + "restart page header. Cannot handle this " + "yet.\n"); + if (usa_end_ofs > NTFS_BLOCK_SIZE - sizeof(u16)) + log_err_exit(buf, "Restart page header in $LogFile is " + "corrupt: Update sequence array overlaps or " + "is behind first protected sequence number. " + "Cannot handle this yet.\n"); + if (usa_end_ofs > le16_to_cpu(rstr->restart_area_offset)) + log_err_exit(buf, "Restart page header in $LogFile is " + "corrupt: Update sequence array overlaps or " + "is behind restart area. Cannot handle this " + "yet.\n"); + /* Finally, verify the offset of the restart area. */ + if (le16_to_cpu(rstr->restart_area_offset) & 7) + log_err_exit(buf, "Restart page header in $LogFile is " + "corrupt: Restart area offset is not aligned " + "to 8-byte boundary. Cannot handle this " + "yet.\n"); +} + +/** + * dump_restart_areas_header + */ +static void dump_restart_areas_header(RESTART_PAGE_HEADER *rstr) +{ + ntfs_log_info("\nRestart page header:\n"); + ntfs_log_info("magic = %s\n", ntfs_is_rstr_record(rstr->magic) ? "RSTR" : + "CHKD"); + ntfs_log_info("usa_ofs = %u (0x%x)\n", le16_to_cpu(rstr->usa_ofs), + le16_to_cpu(rstr->usa_ofs)); + ntfs_log_info("usa_count = %u (0x%x)\n", le16_to_cpu(rstr->usa_count), + le16_to_cpu(rstr->usa_count)); + ntfs_log_info("chkdsk_lsn = %lli (0x%llx)\n", + (long long)sle64_to_cpu(rstr->chkdsk_lsn), + (unsigned long long)sle64_to_cpu(rstr->chkdsk_lsn)); + ntfs_log_info("system_page_size = %u (0x%x)\n", + (unsigned int)le32_to_cpu(rstr->system_page_size), + (unsigned int)le32_to_cpu(rstr->system_page_size)); + ntfs_log_info("log_page_size = %u (0x%x)\n", + (unsigned int)le32_to_cpu(rstr->log_page_size), + (unsigned int)le32_to_cpu(rstr->log_page_size)); + ntfs_log_info("restart_offset = %u (0x%x)\n", + le16_to_cpu(rstr->restart_area_offset), + le16_to_cpu(rstr->restart_area_offset)); +} + +/** + * dump_restart_areas_area + */ +static void dump_restart_areas_area(RESTART_PAGE_HEADER *rstr) +{ + LOG_CLIENT_RECORD *lcr; + RESTART_AREA *ra; + int client; + + ra = (RESTART_AREA*)((u8*)rstr + + le16_to_cpu(rstr->restart_area_offset)); + ntfs_log_info("current_lsn = %lli (0x%llx)\n", + (long long)sle64_to_cpu(ra->current_lsn), + (unsigned long long)sle64_to_cpu(ra->current_lsn)); + ntfs_log_info("log_clients = %u (0x%x)\n", le16_to_cpu(ra->log_clients), + le16_to_cpu(ra->log_clients)); + ntfs_log_info("client_free_list = %i (0x%x)\n", + (s16)le16_to_cpu(ra->client_free_list), + le16_to_cpu(ra->client_free_list)); + ntfs_log_info("client_in_use_list = %i (0x%x)\n", + (s16)le16_to_cpu(ra->client_in_use_list), + le16_to_cpu(ra->client_in_use_list)); + ntfs_log_info("flags = 0x%.4x\n", le16_to_cpu(ra->flags)); + ntfs_log_info("seq_number_bits = %u (0x%x)\n", + (unsigned int)le32_to_cpu(ra->seq_number_bits), + (unsigned int)le32_to_cpu(ra->seq_number_bits)); + ntfs_log_info("restart_area_length = %u (0x%x)\n", + le16_to_cpu(ra->restart_area_length), + le16_to_cpu(ra->restart_area_length)); + ntfs_log_info("client_array_offset = %u (0x%x)\n", + le16_to_cpu(ra->client_array_offset), + le16_to_cpu(ra->client_array_offset)); + ntfs_log_info("file_size = %lli (0x%llx)\n", + (long long)sle64_to_cpu(ra->file_size), + (unsigned long long)sle64_to_cpu(ra->file_size)); + ntfs_log_info("last_lsn_data_length = %u (0x%x)\n", + (unsigned int)le32_to_cpu(ra->last_lsn_data_length), + (unsigned int)le32_to_cpu(ra->last_lsn_data_length)); + ntfs_log_info("log_record_header_length = %u (0x%x)\n", + le16_to_cpu(ra->log_record_header_length), + le16_to_cpu(ra->log_record_header_length)); + ntfs_log_info("log_page_data_offset = %u (0x%x)\n", + le16_to_cpu(ra->log_page_data_offset), + le16_to_cpu(ra->log_page_data_offset)); + ntfs_log_info("restart_log_open_count = %u (0x%x)\n", + (unsigned)le32_to_cpu(ra->restart_log_open_count), + (unsigned)le32_to_cpu(ra->restart_log_open_count)); + lcr = (LOG_CLIENT_RECORD*)((u8*)ra + + le16_to_cpu(ra->client_array_offset)); + for (client = 0; client < le16_to_cpu(ra->log_clients); client++) { + char *client_name; + + ntfs_log_info("\nLog client record number %i:\n", client + 1); + ntfs_log_info("oldest_lsn = %lli (0x%llx)\n", + (long long)sle64_to_cpu(lcr->oldest_lsn), + (unsigned long long) + sle64_to_cpu(lcr->oldest_lsn)); + ntfs_log_info("client_restart_lsn = %lli (0x%llx)\n", (long long) + sle64_to_cpu(lcr->client_restart_lsn), + (unsigned long long) + sle64_to_cpu(lcr->client_restart_lsn)); + ntfs_log_info("prev_client = %i (0x%x)\n", + (s16)le16_to_cpu(lcr->prev_client), + le16_to_cpu(lcr->prev_client)); + ntfs_log_info("next_client = %i (0x%x)\n", + (s16)le16_to_cpu(lcr->next_client), + le16_to_cpu(lcr->next_client)); + ntfs_log_info("seq_number = %u (0x%x)\n", le16_to_cpu(lcr->seq_number), + le16_to_cpu(lcr->seq_number)); + ntfs_log_info("client_name_length = %u (0x%x)\n", + (unsigned int)le32_to_cpu(lcr->client_name_length) / 2, + (unsigned int)le32_to_cpu(lcr->client_name_length) / 2); + if (le32_to_cpu(lcr->client_name_length)) { + client_name = NULL; + if (ntfs_ucstombs(lcr->client_name, + le32_to_cpu(lcr->client_name_length) / + 2, &client_name, 0) < 0) { + ntfs_log_perror("Failed to convert log client name"); + client_name = strdup(""); + } + } else + client_name = strdup(""); + ntfs_log_info("client_name = %s\n", client_name); + free(client_name); + /* + * Log client records are fixed size so we can simply use the + * C increment operator to get to the next one. + */ + lcr++; + } +} + +/** + * dump_restart_areas() + */ +static void *dump_restart_areas(RESTART_PAGE_HEADER *rstr, u8 *buf, + unsigned int page_size) +{ + int pass = 1; + +rstr_pass_loc: + if (ntfs_is_chkd_record(rstr->magic)) + log_err_exit(buf, "The %s restart page header in $LogFile has " + "been modified by chkdsk. Do not know how to " + "handle this yet. Reboot into Windows to fix " + "this.\n", (u8*)rstr == buf ? "first" : + "second"); + if (ntfs_mst_post_read_fixup((NTFS_RECORD*)rstr, page_size) || + ntfs_is_baad_record(rstr->magic)) + log_err_exit(buf, "$LogFile incomplete multi sector transfer " + "detected in restart page header. Cannot " + "handle this yet.\n"); + if (pass == 1) + ntfs_log_info("$LogFile version %i.%i.\n", + sle16_to_cpu(rstr->major_ver), + sle16_to_cpu(rstr->minor_ver)); + else /* if (pass == 2) */ { + RESTART_AREA *ra; + + /* + * rstr is now the second restart page so we declare rstr1 + * as the first restart page as this one has been verified in + * the first pass so we can use all its members safely. + */ + RESTART_PAGE_HEADER *rstr1 = (RESTART_PAGE_HEADER*)buf; + + /* Exclude the usa from the comparison. */ + ra = (RESTART_AREA*)((u8*)rstr1 + + le16_to_cpu(rstr1->restart_area_offset)); + if (!memcmp(rstr1, rstr, le16_to_cpu(rstr1->usa_ofs)) && + !memcmp((u8*)rstr1 + le16_to_cpu( + rstr1->restart_area_offset), (u8*)rstr + + le16_to_cpu(rstr->restart_area_offset), + le16_to_cpu(ra->restart_area_length))) { + puts("\nSkipping analysis of second restart page " + "because it fully matches the first " + "one."); + goto skip_rstr_pass; + } + /* + * The $LogFile versions specified in each of the two restart + * page headers must match. + */ + if (rstr1->major_ver != rstr->major_ver || + rstr1->minor_ver != rstr->minor_ver) + log_err_exit(buf, "Second restart area specifies " + "different $LogFile version to first " + "restart area. Cannot handle this " + "yet.\n"); + } + /* The restart page header is in rstr and it is mst deprotected. */ + ntfs_log_info("\n%s restart page:\n", pass == 1 ? "1st" : "2nd"); + dump_restart_areas_header(rstr); + + ntfs_log_info("\nRestart area:\n"); + dump_restart_areas_area(rstr); + +skip_rstr_pass: + if (pass == 1) { + rstr = (RESTART_PAGE_HEADER*)((u8*)rstr + page_size); + ++pass; + goto rstr_pass_loc; + } + + return rstr; +} + +/** + * dump_log_records() + */ +static void dump_log_record(LOG_RECORD *lr) +{ + unsigned int i; + ntfs_log_info("this lsn = 0x%llx\n", + (unsigned long long)le64_to_cpu(lr->this_lsn)); + ntfs_log_info("client previous lsn = 0x%llx\n", (unsigned long long) + le64_to_cpu(lr->client_previous_lsn)); + ntfs_log_info("client undo next lsn = 0x%llx\n", (unsigned long long) + le64_to_cpu(lr->client_undo_next_lsn)); + ntfs_log_info("client data length = 0x%x\n", + (unsigned int)le32_to_cpu(lr->client_data_length)); + ntfs_log_info("client_id.seq_number = 0x%x\n", + le16_to_cpu(lr->client_id.seq_number)); + ntfs_log_info("client_id.client_index = 0x%x\n", + le16_to_cpu(lr->client_id.client_index)); + ntfs_log_info("record type = 0x%x\n", + (unsigned int)le32_to_cpu(lr->record_type)); + ntfs_log_info("transaction_id = 0x%x\n", + (unsigned int)le32_to_cpu(lr->transaction_id)); + ntfs_log_info("flags = 0x%x:", lr->flags); + if (!lr->flags) + ntfs_log_info(" NONE\n"); + else { + int _b = 0; + + if (lr->flags & LOG_RECORD_MULTI_PAGE) { + ntfs_log_info(" LOG_RECORD_MULTI_PAGE"); + _b = 1; + } + if (lr->flags & ~LOG_RECORD_MULTI_PAGE) { + if (_b) + ntfs_log_info(" |"); + ntfs_log_info(" Unknown flags"); + } + ntfs_log_info("\n"); + } + ntfs_log_info("redo_operation = 0x%x\n", le16_to_cpu(lr->redo_operation)); + ntfs_log_info("undo_operation = 0x%x\n", le16_to_cpu(lr->undo_operation)); + ntfs_log_info("redo_offset = 0x%x\n", le16_to_cpu(lr->redo_offset)); + ntfs_log_info("redo_length = 0x%x\n", le16_to_cpu(lr->redo_length)); + ntfs_log_info("undo_offset = 0x%x\n", le16_to_cpu(lr->undo_offset)); + ntfs_log_info("undo_length = 0x%x\n", le16_to_cpu(lr->undo_length)); + ntfs_log_info("target_attribute = 0x%x\n", le16_to_cpu(lr->target_attribute)); + ntfs_log_info("lcns_to_follow = 0x%x\n", le16_to_cpu(lr->lcns_to_follow)); + ntfs_log_info("record_offset = 0x%x\n", le16_to_cpu(lr->record_offset)); + ntfs_log_info("attribute_offset = 0x%x\n", le16_to_cpu(lr->attribute_offset)); + ntfs_log_info("target_vcn = 0x%llx\n", + (unsigned long long)sle64_to_cpu(lr->target_vcn)); + if (le16_to_cpu(lr->lcns_to_follow) > 0) + ntfs_log_info("Array of lcns:\n"); + for (i = 0; i < le16_to_cpu(lr->lcns_to_follow); i++) + ntfs_log_info("lcn_list[%u].lcn = 0x%llx\n", i, (unsigned long long) + sle64_to_cpu(lr->lcn_list[i].lcn)); +} + +/** + * dump_log_records() + */ +static void dump_log_records(RECORD_PAGE_HEADER *rcrd, u8 *buf, + int buf_size, unsigned int page_size) +{ + LOG_RECORD *lr; + int pass = 0; + int client; + + /* Reuse pass for log area. */ +rcrd_pass_loc: + rcrd = (RECORD_PAGE_HEADER*)((u8*)rcrd + page_size); + if ((u8*)rcrd + page_size > buf + buf_size) + return; + ntfs_log_info("\nLog record page number %i", pass); + if (!ntfs_is_rcrd_record(rcrd->magic) && + !ntfs_is_chkd_record(rcrd->magic)) { + unsigned int i; + for (i = 0; i < page_size; i++) + if (((u8*)rcrd)[i] != (u8)-1) + break; + if (i < page_size) + puts(" is corrupt (magic is not RCRD or CHKD)."); + else + puts(" is empty."); + pass++; + goto rcrd_pass_loc; + } else + puts(":"); + /* Dump log record page */ + ntfs_log_info("magic = %s\n", ntfs_is_rcrd_record(rcrd->magic) ? "RCRD" : + "CHKD"); +// TODO: I am here... (AIA) + ntfs_log_info("copy.last_lsn/file_offset = 0x%llx\n", (unsigned long long) + le64_to_cpu(rcrd->copy.last_lsn)); + ntfs_log_info("flags = 0x%x\n", (unsigned int)le32_to_cpu(rcrd->flags)); + ntfs_log_info("page count = %i\n", le16_to_cpu(rcrd->page_count)); + ntfs_log_info("page position = %i\n", le16_to_cpu(rcrd->page_position)); + ntfs_log_info("header.next_record_offset = 0x%llx\n", (unsigned long long) + le64_to_cpu(rcrd->header.packed.next_record_offset)); + ntfs_log_info("header.last_end_lsn = 0x%llx\n", (unsigned long long) + le64_to_cpu(rcrd->header.packed.last_end_lsn)); + /* + * Where does the 0x40 come from? Is it just usa_offset + + * usa_client * 2 + 7 & ~7 or is it derived from somewhere? + */ + lr = (LOG_RECORD*)((u8*)rcrd + 0x40); + client = 0; + do { + ntfs_log_info("\nLog record %i:\n", client); + dump_log_record(lr); + client++; + lr = (LOG_RECORD*)((u8*)lr + 0x70); + } while (((u8*)lr + 0x70 <= (u8*)rcrd + + le64_to_cpu(rcrd->header.packed.next_record_offset))); + + pass++; + goto rcrd_pass_loc; +} + +/** + * main - + */ +int main(int argc, char **argv) +{ + RESTART_PAGE_HEADER *rstr; + RECORD_PAGE_HEADER *rcrd; + unsigned int page_size; + int buf_size, br, err; + logfile_file logfile; + u8 *buf; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + ntfs_log_info("\n"); + if (argc < 2 || argc > 3) + /* print usage and exit */ + usage(argv[0]); + /* + * If one argument, it is a device containing an NTFS volume which we + * need to mount and read the $LogFile from so we can dump its + * contents. + * + * If two arguments the first one must be "-f" and the second one is + * the path and name of the $LogFile (or copy thereof) which we need to + * read and dump the contents of. + */ + + if (argc == 2) { + logfile_open(TRUE, argv[1], &logfile); + } else /* if (argc == 3) */ { + if (strncmp(argv[1], "-f", strlen("-f"))) + usage(argv[0]); + + logfile_open(FALSE, argv[2], &logfile); + } + + buf_size = 64 * 1024 * 1024; + + if (logfile.data_size <= buf_size) + buf_size = logfile.data_size; + else + ntfs_log_error("Warning: $LogFile is too big. " + "Only analysing the first 64MiB.\n"); + + /* For simplicity we read all of $LogFile/$DATA into memory. */ + buf = malloc(buf_size); + if (!buf) { + ntfs_log_perror("Failed to allocate buffer for file data"); + logfile_close(&logfile); + exit(1); + } + + br = logfile_pread(&logfile, 0, buf_size, buf); + err = errno; + logfile_close(&logfile); + if (br != buf_size) { + log_err_exit(buf, "Failed to read $LogFile/$DATA: %s\n", + br < 0 ? strerror(err) : "Partial read."); + } + + /* + * We now have the entirety of the journal ($LogFile/$DATA or argv[2]) + * in the memory buffer buf and this has a size of buf_size. Note we + * apply a size capping at 64MiB, so if the journal is any bigger we + * only have the first 64MiB. This should not be a problem as I have + * never seen such a large $LogFile. Usually it is only a few MiB in + * size. + */ + rstr = (RESTART_PAGE_HEADER*)buf; + + /* Check for presence of restart area signature. */ + if (!ntfs_is_rstr_record(rstr->magic) && + !ntfs_is_chkd_record(rstr->magic)) { + s8 *pos = (s8*)buf; + s8 *end = pos + buf_size; + while (pos < end && *pos == -1) + pos++; + if (pos != end) + log_err_exit(buf, "$LogFile contents are corrupt " + "(magic RSTR is missing). Cannot " + "handle this yet.\n"); + /* All bytes are -1. */ + free(buf); + puts("$LogFile is not initialized."); + return 0; + } + + /* + * First, verify the restart page header for consistency. + */ + restart_header_sanity(rstr, buf); + page_size = le32_to_cpu(rstr->log_page_size); + + /* + * Second, verify the restart area itself. + */ + // TODO: Implement this. + ntfs_log_error("Warning: Sanity checking of restart area not implemented " + "yet.\n"); + /* + * Third and last, verify the array of log client records. + */ + // TODO: Implement this. + ntfs_log_error("Warning: Sanity checking of array of log client records not " + "implemented yet.\n"); + + /* + * Dump the restart headers & areas. + */ + rcrd = (RECORD_PAGE_HEADER*)dump_restart_areas(rstr, buf, page_size); + ntfs_log_info("\n\nFinished with restart pages. " + "Beginning with log pages.\n"); + + /* + * Dump the log areas. + */ + dump_log_records(rcrd, buf, buf_size, page_size); + + free(buf); + return 0; +} + diff --git a/ntfsprogs/ntfsfix.8.in b/ntfsprogs/ntfsfix.8.in new file mode 100644 index 00000000..dfe1b485 --- /dev/null +++ b/ntfsprogs/ntfsfix.8.in @@ -0,0 +1,74 @@ +.\" Copyright (c) 2005-2006 Szabolcs Szakacsits. +.\" Copyright (c) 2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSFIX 8 "January 2006" "ntfsprogs @VERSION@" +.SH NAME +ntfsfix \- fix common errors and force Windows to check NTFS +.SH SYNOPSIS +.B ntfsfix +[\fIoptions\fR] \fIdevice\fR +.SH DESCRIPTION +.B ntfsfix +is a utility that fixes some common NTFS problems. +.B ntfsfix +is +.B NOT +a Linux version of chkdsk. It only repairs some fundamental NTFS +inconsistencies, resets the NTFS journal file and schedules an NTFS consistency +check for the first boot into Windows. +.sp +You may run +.B ntfsfix +on an NTFS volume if you think it was damaged by Windows or some other way +and it can't be mounted. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsfix +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license +.SH BUGS +There are no known problems with +.BR ntfsfix . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +linux\-ntfs\-dev@lists.sourceforge.net +.hy +.SH AUTHORS +.B ntfsfix +was written by Anton Altaparmakov, with contributions from Szabolcs Szakacsits. +.SH AVAILABILITY +.B ntfsfix +is part of the +.B ntfsprogs +package and is available from: +.br +.nh +http://www.linux\-ntfs.org/content/view/19/37 +.hy +.sp +The manual pages are available online at: +.br +.nh +http://man.linux-ntfs.org/ +.hy +.SH SEE ALSO +.BR mkntfs (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsfix.c b/ntfsprogs/ntfsfix.c new file mode 100644 index 00000000..e763ba94 --- /dev/null +++ b/ntfsprogs/ntfsfix.c @@ -0,0 +1,588 @@ +/** + * ntfsfix - Part of the Linux-NTFS project. + * + * Copyright (c) 2000-2006 Anton Altaparmakov. + * Copyright (c) 2002-2006 Szabolcs Szakacsits. + * + * This utility fixes some common NTFS problems, resets the NTFS journal file + * and schedules an NTFS consistency check for the first boot into Windows. + * + * Anton Altaparmakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS source + * in the file COPYING); if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * WARNING: This program might not work on architectures which do not allow + * unaligned access. For those, the program would need to start using + * get/put_unaligned macros (#include ), but not doing it yet, + * since NTFS really mostly applies to ia32 only, which does allow unaligned + * accesses. We might not actually have a problem though, since the structs are + * defined as being packed so that might be enough for gcc to insert the + * correct code. + * + * If anyone using a non-little endian and/or an aligned access only CPU tries + * this program please let me know whether it works or not! + * + * Anton Altaparmakov + */ + +#include "config.h" + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "utils.h" +/* #include "version.h" */ + +#ifdef NO_NTFS_DEVICE_DEFAULT_IO_OPS +# error "No default device io operations! Cannot build ntfsfix. \ +You need to run ./configure without the --disable-default-device-io-ops \ +switch if you want to be able to build the NTFS utilities." +#endif + +static const char *EXEC_NAME = "ntfsfix"; +static const char *OK = "OK\n"; +static const char *FAILED = "FAILED\n"; +static BOOL vol_is_dirty = FALSE; +static BOOL journal_is_empty = FALSE; + +struct { + char *volume; +} opt; + +/** + * usage + */ +__attribute__((noreturn)) +static int usage(void) +{ + ntfs_log_info("%s v%s (libntfs-3g)\n" + "\n" + "Usage: %s [options] device\n" + " Attempt to fix an NTFS partition.\n" + "\n" + " -h, --help Display this help\n" + " -V, --version Display version information\n" + "\n" + "For example: %s /dev/hda6\n\n", + EXEC_NAME, VERSION, EXEC_NAME, + EXEC_NAME); + ntfs_log_info("%s%s", ntfs_bugs, ntfs_home); + exit(1); +} + +/** + * version + */ +__attribute__((noreturn)) +static void version(void) +{ + ntfs_log_info("%s v%s\n\n" + "Attempt to fix an NTFS partition.\n\n" + "Copyright (c) 2000-2006 Anton Altaparmakov.\n" + "Copyright (c) 2002-2006 Szabolcs Szakacsits.\n\n", + EXEC_NAME, VERSION); + ntfs_log_info("%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home); + exit(1); +} + +/** + * parse_options + */ +static void parse_options(int argc, char **argv) +{ + int c; + static const char *sopt = "-hV"; + static const struct option lopt[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + memset(&opt, 0, sizeof(opt)); + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opt.volume) + opt.volume = argv[optind - 1]; + else { + ntfs_log_info("ERROR: Too many arguments.\n"); + usage(); + } + break; + case 'h': + case '?': + usage(); + case 'V': + version(); + default: + ntfs_log_info("ERROR: Unknown option '%s'.\n", argv[optind - 1]); + usage(); + } + } + + if (opt.volume == NULL) { + ntfs_log_info("ERROR: You must specify a device.\n"); + usage(); + } +} + +/** + * OLD_ntfs_volume_set_flags + */ +static int OLD_ntfs_volume_set_flags(ntfs_volume *vol, const u16 flags) +{ + MFT_RECORD *m = NULL; + ATTR_RECORD *a; + VOLUME_INFORMATION *c; + ntfs_attr_search_ctx *ctx; + int ret = -1; /* failure */ + + if (!vol) { + errno = EINVAL; + return -1; + } + if (ntfs_file_record_read(vol, FILE_Volume, &m, NULL)) { + ntfs_log_perror("Failed to read $Volume"); + return -1; + } + /* Sanity check */ + if (!(m->flags & MFT_RECORD_IN_USE)) { + ntfs_log_error("$Volume has been deleted. Cannot handle this " + "yet. Run chkdsk to fix this.\n"); + errno = EIO; + goto err_exit; + } + /* Get a pointer to the volume information attribute. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_debug("Failed to allocate attribute search " + "context.\n"); + goto err_exit; + } + if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, + 0, ctx)) { + ntfs_log_error("Attribute $VOLUME_INFORMATION was not found in " + "$Volume!\n"); + goto err_out; + } + a = ctx->attr; + /* Sanity check. */ + if (a->non_resident) { + ntfs_log_error("Attribute $VOLUME_INFORMATION must be resident " + "(and it isn't)!\n"); + errno = EIO; + goto err_out; + } + /* Get a pointer to the value of the attribute. */ + c = (VOLUME_INFORMATION*)(le16_to_cpu(a->value_offset) + (char*)a); + /* Sanity checks. */ + if ((char*)c + le32_to_cpu(a->value_length) > + (char*)m + le32_to_cpu(m->bytes_in_use) || + le16_to_cpu(a->value_offset) + + le32_to_cpu(a->value_length) > le32_to_cpu(a->length)) { + ntfs_log_error("Attribute $VOLUME_INFORMATION in $Volume is " + "corrupt!\n"); + errno = EIO; + goto err_out; + } + /* Set the volume flags. */ + vol->flags = c->flags = cpu_to_le16(flags); + if (ntfs_mft_record_write(vol, FILE_Volume, m)) { + ntfs_log_perror("Error writing $Volume"); + goto err_out; + } + ret = 0; /* success */ +err_out: + ntfs_attr_put_search_ctx(ctx); +err_exit: + free(m); + return ret; +} + +/** + * set_dirty_flag + */ +static int set_dirty_flag(ntfs_volume *vol) +{ + u16 flags; + + if (vol_is_dirty == TRUE) + return 0; + + ntfs_log_info("Setting required flags on partition... "); + /* + * Set chkdsk flag, i.e. mark the partition dirty so chkdsk will run + * and fix it for us. + */ + flags = vol->flags | VOLUME_IS_DIRTY; + /* If NTFS volume version >= 2.0 then set mounted on NT4 flag. */ + if (vol->major_ver >= 2) + flags |= VOLUME_MOUNTED_ON_NT4; + if (OLD_ntfs_volume_set_flags(vol, flags)) { + ntfs_log_info(FAILED); + ntfs_log_error("Error setting volume flags.\n"); + return -1; + } + ntfs_log_info(OK); + vol_is_dirty = TRUE; + return 0; +} + +/** + * set_dirty_flag_mount + */ +static int set_dirty_flag_mount(ntfs_volume *vol) +{ + u16 flags; + + if (vol_is_dirty == TRUE) + return 0; + + ntfs_log_info("Setting required flags on partition... "); + /* + * Set chkdsk flag, i.e. mark the partition dirty so chkdsk will run + * and fix it for us. + */ + flags = vol->flags | VOLUME_IS_DIRTY; + /* If NTFS volume version >= 2.0 then set mounted on NT4 flag. */ + if (vol->major_ver >= 2) + flags |= VOLUME_MOUNTED_ON_NT4; + if (ntfs_volume_write_flags(vol, flags)) { + ntfs_log_info(FAILED); + ntfs_log_error("Error setting volume flags.\n"); + return -1; + } + ntfs_log_info(OK); + vol_is_dirty = TRUE; + return 0; +} + +/** + * empty_journal + */ +static int empty_journal(ntfs_volume *vol) +{ + if (journal_is_empty == TRUE) + return 0; + + ntfs_log_info("Going to empty the journal ($LogFile)... "); + if (ntfs_logfile_reset(vol)) { + ntfs_log_info(FAILED); + ntfs_log_perror("Failed to reset $LogFile"); + return -1; + } + ntfs_log_info(OK); + journal_is_empty = TRUE; + return 0; +} + +/** + * fix_mftmirr + */ +static int fix_mftmirr(ntfs_volume *vol) +{ + s64 l, br; + unsigned char *m, *m2; + int i, ret = -1; /* failure */ + BOOL done; + + ntfs_log_info("\nProcessing $MFT and $MFTMirr...\n"); + + /* Load data from $MFT and $MFTMirr and compare the contents. */ + m = (u8*)malloc(vol->mftmirr_size << vol->mft_record_size_bits); + if (!m) { + ntfs_log_perror("Failed to allocate memory"); + return -1; + } + m2 = (u8*)malloc(vol->mftmirr_size << vol->mft_record_size_bits); + if (!m2) { + ntfs_log_perror("Failed to allocate memory"); + free(m); + return -1; + } + + ntfs_log_info("Reading $MFT... "); + l = ntfs_attr_mst_pread(vol->mft_na, 0, vol->mftmirr_size, + vol->mft_record_size, m); + if (l != vol->mftmirr_size) { + ntfs_log_info(FAILED); + if (l != -1) + errno = EIO; + ntfs_log_perror("Failed to read $MFT"); + goto error_exit; + } + ntfs_log_info(OK); + + ntfs_log_info("Reading $MFTMirr... "); + l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size, + vol->mft_record_size, m2); + if (l != vol->mftmirr_size) { + ntfs_log_info(FAILED); + if (l != -1) + errno = EIO; + ntfs_log_perror("Failed to read $MFTMirr"); + goto error_exit; + } + ntfs_log_info(OK); + + /* + * FIXME: Need to actually check the $MFTMirr for being real. Otherwise + * we might corrupt the partition if someone is experimenting with + * software RAID and the $MFTMirr is not actually in the position we + * expect it to be... )-: + * FIXME: We should emit a warning it $MFTMirr is damaged and ask + * user whether to recreate it from $MFT or whether to abort. - The + * warning needs to include the danger of software RAID arrays. + * Maybe we should go as far as to detect whether we are running on a + * MD disk and if yes then bomb out right at the start of the program? + */ + + ntfs_log_info("Comparing $MFTMirr to $MFT... "); + done = FALSE; + for (i = 0; i < vol->mftmirr_size; ++i) { + MFT_RECORD *mrec, *mrec2; + const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", + "$Volume", "$AttrDef", "root directory", "$Bitmap", + "$Boot", "$BadClus", "$Secure", "$UpCase", "$Extend" }; + const char *s; + BOOL use_mirr; + + if (i < 12) + s = ESTR[i]; + else if (i < 16) + s = "system file"; + else + s = "mft record"; + + use_mirr = FALSE; + mrec = (MFT_RECORD*)(m + i * vol->mft_record_size); + if (mrec->flags & MFT_RECORD_IN_USE) { + if (ntfs_is_baad_recordp(mrec)) { + ntfs_log_info(FAILED); + ntfs_log_error("$MFT error: Incomplete multi " + "sector transfer detected in " + "%s.\nCannot handle this yet. " + ")-:\n", s); + goto error_exit; + } + if (!ntfs_is_mft_recordp(mrec)) { + ntfs_log_info(FAILED); + ntfs_log_error("$MFT error: Invalid mft " + "record for %s.\nCannot " + "handle this yet. )-:\n", s); + goto error_exit; + } + } + mrec2 = (MFT_RECORD*)(m2 + i * vol->mft_record_size); + if (mrec2->flags & MFT_RECORD_IN_USE) { + if (ntfs_is_baad_recordp(mrec2)) { + ntfs_log_info(FAILED); + ntfs_log_error("$MFTMirr error: Incomplete " + "multi sector transfer " + "detected in %s.\n", s); + goto error_exit; + } + if (!ntfs_is_mft_recordp(mrec2)) { + ntfs_log_info(FAILED); + ntfs_log_error("$MFTMirr error: Invalid mft " + "record for %s.\n", s); + goto error_exit; + } + /* $MFT is corrupt but $MFTMirr is ok, use $MFTMirr. */ + if (!(mrec->flags & MFT_RECORD_IN_USE) && + !ntfs_is_mft_recordp(mrec)) + use_mirr = TRUE; + } + if (memcmp(mrec, mrec2, ntfs_mft_record_get_data_size(mrec))) { + if (!done) { + done = TRUE; + ntfs_log_info(FAILED); + } + ntfs_log_info("Correcting differences in $MFT%s " + "record %d...", use_mirr ? "" : "Mirr", + i); + br = ntfs_mft_record_write(vol, i, + use_mirr ? mrec2 : mrec); + if (br) { + ntfs_log_info(FAILED); + ntfs_log_perror("Error correcting $MFT%s", + use_mirr ? "" : "Mirr"); + goto error_exit; + } + ntfs_log_info(OK); + } + } + if (!done) + ntfs_log_info(OK); + ntfs_log_info("Processing of $MFT and $MFTMirr completed " + "successfully.\n"); + ret = 0; +error_exit: + free(m); + free(m2); + return ret; +} + +/** + * fix_mount + */ +static int fix_mount(void) +{ + int ret = -1; /* failure */ + ntfs_volume *vol; + struct ntfs_device *dev; + + ntfs_log_info("Attempting to correct errors... "); + + dev = ntfs_device_alloc(opt.volume, 0, &ntfs_device_default_io_ops, NULL); + if (!dev) { + ntfs_log_info(FAILED); + ntfs_log_perror("Failed to allocate device"); + return -1; + } + + vol = ntfs_volume_startup(dev, 0); + if (!vol) { + ntfs_log_info(FAILED); + ntfs_log_perror("Failed to startup volume"); + ntfs_log_error("Volume is corrupt. You should run chkdsk.\n"); + ntfs_device_free(dev); + return -1; + } + + if (fix_mftmirr(vol) < 0) + goto error_exit; + + /* FIXME: Will this fail? Probably... */ + if (set_dirty_flag(vol) < 0) + goto error_exit; + + if (empty_journal(vol) < 0) + goto error_exit; + + ret = 0; +error_exit: + /* ntfs_umount() will invoke ntfs_device_free() for us. */ + if (ntfs_umount(vol, 0)) + ntfs_umount(vol, 1); + return ret; +} + +/** + * main + */ +int main(int argc, char **argv) +{ + ntfs_volume *vol; + unsigned long mnt_flags; + int ret = 1; /* failure */ + BOOL force = FALSE; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + parse_options(argc, argv); + + if (!ntfs_check_if_mounted(opt.volume, &mnt_flags)) { + if ((mnt_flags & NTFS_MF_MOUNTED) && + !(mnt_flags & NTFS_MF_READONLY) && !force) { + ntfs_log_error("Refusing to operate on read-write " + "mounted device %s.\n", opt.volume); + exit(1); + } + } else + ntfs_log_perror("Failed to determine whether %s is mounted", + opt.volume); + /* Attempt a full mount first. */ + ntfs_log_info("Mounting volume... "); + vol = ntfs_mount(opt.volume, 0); + if (vol) { + ntfs_log_info(OK); + ntfs_log_info("Processing of $MFT and $MFTMirr completed " + "successfully.\n"); + } else { + ntfs_log_info(FAILED); + if (fix_mount() < 0) + exit(1); + vol = ntfs_mount(opt.volume, 0); + if (!vol) { + ntfs_log_perror("Remount failed"); + exit(1); + } + } + + /* Check NTFS version is ok for us (in $Volume) */ + ntfs_log_info("NTFS volume version is %i.%i.\n", vol->major_ver, + vol->minor_ver); + if (ntfs_version_is_supported(vol)) { + ntfs_log_error("Error: Unknown NTFS version.\n"); + goto error_exit; + } + + if (set_dirty_flag_mount(vol) < 0) + goto error_exit; + + if (empty_journal(vol) < 0) + goto error_exit; + + if (vol->major_ver >= 3) { + /* FIXME: If on NTFS 3.0+, check for presence of the usn journal and + disable it (if present) as Win2k might be unhappy otherwise and Bad + Things(TM) could happen depending on what applications are actually + using it for. */ + } + + /* FIXME: Should we be marking the quota out of date, too? */ + + /* That's all for now! */ + ntfs_log_info("NTFS partition %s was processed successfully.\n", + vol->dev->d_name); + /* Set return code to 0. */ + ret = 0; +error_exit: + if (ntfs_umount(vol, 0)) + ntfs_umount(vol, 1); + return ret; +} + diff --git a/ntfsprogs/ntfsinfo.8.in b/ntfsprogs/ntfsinfo.8.in new file mode 100644 index 00000000..8ea5b360 --- /dev/null +++ b/ntfsprogs/ntfsinfo.8.in @@ -0,0 +1,94 @@ +.\" Copyright (c) 2002\-2004 Anton Altaparmakov. +.\" Copyright (c) 2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSINFO 8 "April 2006" "ntfsprogs @VERSION@" +.SH NAME +ntfsinfo \- dump a file's attributes +.SH SYNOPSIS +.B ntfsinfo +[\fIoptions\fR] \fIdevice\fR +.SH DESCRIPTION +.B ntfsinfo +will dump the attributes of inode +.I inode\-number +or the file +.I path\-filename +and/or information about the mft ( +.I \-m +option). +Run ntfsinfo without arguments for a full list of options. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsinfo +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-F\fR, \fB\-\-file\fR FILE +Show information about this file +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not overwriting an existing +file. Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-i\fR, \fB\-\-inode\fR NUM +Show information about this inode. +.TP +\fB\-m\fR, \fB\-\-mft\fR +Show information about the volume. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Produce less output. +.TP +\fB\-t\fR, \fB\-\-notime\fR +Do not display timestamps in the output. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Increase the amount of output that +.B ntfsinfo +prints. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license. +.SH BUGS +There are no known problems with +.BR ntfsinfo . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +linux\-ntfs\-dev@lists.sourceforge.net +.hy +.SH AUTHORS +.B ntfsinfo +was written by Matthew J. Fanto, Anton Altaparmakov, Richard Russon, Szabolcs +Szakacsits, Yuval Fledel, Yura Pakhuchiy and Cristian Klein. +.SH AVAILABILITY +.B ntfsinfo +is part of the +.B ntfsprogs +package and is available from: +.br +.nh +http://www.linux\-ntfs.org/content/view/19/37 +.hy +.sp +The manual pages are available online at: +.br +.nh +http://man.linux-ntfs.org/ +.hy +.SH SEE ALSO +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c new file mode 100644 index 00000000..b9a54187 --- /dev/null +++ b/ntfsprogs/ntfsinfo.c @@ -0,0 +1,2010 @@ +/** + * ntfsinfo - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2004 Matthew J. Fanto + * Copyright (c) 2002-2006 Anton Altaparmakov + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2003-2006 Szabolcs Szakacsits + * Copyright (c) 2004-2005 Yuval Fledel + * Copyright (c) 2004-2005 Yura Pakhuchiy + * Copyright (c) 2005 Cristian Klein + * + * This utility will dump a file's attributes. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * TODO LIST: + * - Better error checking. (focus on ntfs_dump_volume) + * - Comment things better. + * - More things at verbose mode. + * - Dump ACLs when security_id exists (NTFS 3+ only). + * - Clean ups. + * - Internationalization. + * - Add more Indexed Attr Types. + * - Make formatting look more like www.flatcap.org/ntfs/info + * + * Still not dumping certain attributes. Need to find the best + * way to output some of these attributes. + * + * Still need to do: + * $REPARSE_POINT/$SYMBOLIC_LINK + * $LOGGED_UTILITY_STREAM + */ + +#include "config.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_TIME_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +/* #include "version.h" */ + +static const char *EXEC_NAME = "ntfsinfo"; + +static struct options { + const char *device; /* Device/File to work with */ + const char *filename; /* Resolve this filename to mft number */ + s64 inode; /* Info for this inode */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int force; /* Override common sense */ + int notime; /* Don't report timestamps at all */ + int mft; /* Dump information about the volume as well */ + u8 padding[4]; /* Unused: padding to 64 bit. */ +} opts; + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + printf("\n%s v%s (libntfs-3g) - Display information about an NTFS " + "Volume.\n\n", EXEC_NAME, VERSION); + printf("Copyright (c)\n"); + printf(" 2002-2004 Matthew J. Fanto\n"); + printf(" 2002-2006 Anton Altaparmakov\n"); + printf(" 2002-2005 Richard Russon\n"); + printf(" 2003-2006 Szabolcs Szakacsits\n"); + printf(" 2003 Leonard NorrgÃ¥rd\n"); + printf(" 2004-2005 Yuval Fledel\n"); + printf(" 2004-2005 Yura Pakhuchiy\n"); + printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + printf("\nUsage: %s [options] device\n" + " -i, --inode NUM Display information about this inode\n" + " -F, --file FILE Display information about this file (absolute path)\n" + " -m, --mft Dump information about the volume\n" + " -t, --notime Don't report timestamps\n" + "\n" + " -f, --force Use less caution\n" + " -q, --quiet Less output\n" + " -v, --verbose More output\n" + " -V, --version Display version information\n" + " -h, --help Display this help\n\n", + EXEC_NAME); + printf("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char *argv[]) +{ + static const char *sopt = "-:fhi:F:mqtTvV"; + static const struct option lopt[] = { + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "inode", required_argument, NULL, 'i' }, + { "file", required_argument, NULL, 'F' }, + { "quiet", no_argument, NULL, 'q' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { "notime", no_argument, NULL, 'T' }, + { "mft", no_argument, NULL, 'm' }, + { NULL, 0, NULL, 0 } + }; + + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + + opterr = 0; /* We'll handle the errors, thank you. */ + + opts.inode = -1; + opts.filename = NULL; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + ntfs_log_trace("optind=%d; c='%c' optarg=\"%s\".\n", optind, c, + optarg); + switch (c) { + case 1: + if (!opts.device) + opts.device = optarg; + else + err++; + break; + case 'i': + if ((opts.inode != -1) || + (!utils_parse_size(optarg, &opts.inode, FALSE))) { + err++; + } + break; + case 'F': + if (opts.filename == NULL) { + /* The inode can not be resolved here, + store the filename */ + opts.filename = argv[optind-1]; + } else { + /* "-F" can't appear more than once */ + err++; + } + break; + case 'f': + opts.force++; + break; + case 'h': + help++; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 't': + opts.notime++; + break; + case 'T': + /* 'T' is deprecated, notify */ + ntfs_log_error("Option 'T' is deprecated, it was " + "replaced by 't'.\n"); + err++; + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + ver++; + break; + case 'm': + opts.mft++; + break; + case '?': + if (optopt=='?') { + help++; + continue; + } + if (ntfs_log_parse_option(argv[optind-1])) + continue; + ntfs_log_error("Unknown option '%s'.\n", + argv[optind-1]); + err++; + break; + case ':': + ntfs_log_error("Option '%s' requires an " + "argument.\n", argv[optind-1]); + err++; + break; + default: + ntfs_log_error("Unhandled option case: %d.\n", c); + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if (opts.device == NULL) { + if (argc > 1) + ntfs_log_error("You must specify exactly one " + "device.\n"); + err++; + } + + if ((opts.inode == -1) && (opts.filename == NULL) && !opts.mft) { + if (argc > 1) + ntfs_log_error("You must specify an inode to " + "learn about.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose " + "at the same time.\n"); + err++; + } + + if ((opts.inode != -1) && (opts.filename != NULL)) { + if (argc > 1) + ntfs_log_error("You may not specify --inode " + "and --file together.\n"); + err++; + } + + } + + if (ver) + version(); + if (help || err) + usage(); + + return (!err && !help && !ver); +} + + +/* *************** utility functions ******************** */ +/** + * ntfsinfo_time_to_str() - + * @sle_ntfs_clock: on disk time format in 100ns units since 1st jan 1601 + * in little-endian format + * + * Return char* in a format 'Thu Jan 1 00:00:00 1970'. + * No need to free the returned memory. + * + * Example of usage: + * char *time_str = ntfsinfo_time_to_str( + * sle64_to_cpu(standard_attr->creation_time)); + * printf("\tFile Creation Time:\t %s", time_str); + */ +static char *ntfsinfo_time_to_str(const s64 sle_ntfs_clock) +{ + struct timespec unix_clock = ntfs2timespec((ntfs_time) sle_ntfs_clock); + return ctime(&unix_clock.tv_sec); +} + +/** + * ntfs_attr_get_name() + * @attr: a valid attribute record + * + * return multi-byte string containing the attribute name if exist. the user + * is then responsible of freeing that memory. + * null if no name exists (attr->name_length==0). no memory allocated. + * null if cannot convert to multi-byte string. errno would contain the + * error id. no memory allocated in that case + */ +static char *ntfs_attr_get_name(ATTR_RECORD *attr) +{ + ntfschar *ucs_attr_name; + char *mbs_attr_name = NULL; + int mbs_attr_name_size; + + /* calculate name position */ + ucs_attr_name = (ntfschar *)((char *)attr + le16_to_cpu(attr->name_offset)); + /* convert unicode to printable format */ + mbs_attr_name_size = ntfs_ucstombs(ucs_attr_name,attr->name_length, + &mbs_attr_name,0); + if (mbs_attr_name_size>0) { + return mbs_attr_name; + } else { + return NULL; + } +} + + +/* *************** functions for dumping global info ******************** */ +/** + * ntfs_dump_volume - dump information about the volume + */ +static void ntfs_dump_volume(ntfs_volume *vol) +{ + printf("Volume Information \n"); + printf("\tName of device: %s\n", vol->dev->d_name); + printf("\tDevice state: %lu\n", vol->dev->d_state); + printf("\tVolume Name: %s\n", vol->vol_name); + printf("\tVolume State: %lu\n", vol->state); + printf("\tVolume Version: %u.%u\n", vol->major_ver, vol->minor_ver); + printf("\tSector Size: %hu\n", vol->sector_size); + printf("\tCluster Size: %u\n", (unsigned int)vol->cluster_size); + printf("\tVolume Size in Clusters: %lld\n", + (long long)vol->nr_clusters); + + printf("MFT Information \n"); + printf("\tMFT Record Size: %u\n", (unsigned int)vol->mft_record_size); + printf("\tMFT Zone Multiplier: %u\n", vol->mft_zone_multiplier); + printf("\tMFT Data Position: %lld\n", (long long)vol->mft_data_pos); + printf("\tMFT Zone Start: %lld\n", (long long)vol->mft_zone_start); + printf("\tMFT Zone End: %lld\n", (long long)vol->mft_zone_end); + printf("\tMFT Zone Position: %lld\n", (long long)vol->mft_zone_pos); + printf("\tCurrent Position in First Data Zone: %lld\n", + (long long)vol->data1_zone_pos); + printf("\tCurrent Position in Second Data Zone: %lld\n", + (long long)vol->data2_zone_pos); + printf("\tLCN of Data Attribute for FILE_MFT: %lld\n", + (long long)vol->mft_lcn); + printf("\tFILE_MFTMirr Size: %d\n", vol->mftmirr_size); + printf("\tLCN of Data Attribute for File_MFTMirr: %lld\n", + (long long)vol->mftmirr_lcn); + printf("\tSize of Attribute Definition Table: %d\n", + (int)vol->attrdef_len); + + printf("FILE_Bitmap Information \n"); + printf("\tFILE_Bitmap MFT Record Number: %llu\n", + (unsigned long long)vol->lcnbmp_ni->mft_no); + printf("\tState of FILE_Bitmap Inode: %lu\n", vol->lcnbmp_ni->state); + printf("\tLength of Attribute List: %u\n", + (unsigned int)vol->lcnbmp_ni->attr_list_size); + printf("\tAttribute List: %s\n", vol->lcnbmp_ni->attr_list); + printf("\tNumber of Attached Extent Inodes: %d\n", + (int)vol->lcnbmp_ni->nr_extents); + /* FIXME: need to add code for the union if nr_extens != 0, but + i dont know if it will ever != 0 with FILE_Bitmap */ + + printf("FILE_Bitmap Data Attribute Information\n"); + printf("\tDecompressed Runlist: not done yet\n"); + printf("\tBase Inode: %llu\n", + (unsigned long long)vol->lcnbmp_na->ni->mft_no); + printf("\tAttribute Types: not done yet\n"); + //printf("\tAttribute Name: %s\n", vol->lcnbmp_na->name); + printf("\tAttribute Name Length: %u\n", + (unsigned int)vol->lcnbmp_na->name_len); + printf("\tAttribute State: %lu\n", vol->lcnbmp_na->state); + printf("\tAttribute Allocated Size: %lld\n", + (long long)vol->lcnbmp_na->allocated_size); + printf("\tAttribute Data Size: %lld\n", + (long long)vol->lcnbmp_na->data_size); + printf("\tAttribute Initialized Size: %lld\n", + (long long)vol->lcnbmp_na->initialized_size); + printf("\tAttribute Compressed Size: %lld\n", + (long long)vol->lcnbmp_na->compressed_size); + printf("\tCompression Block Size: %u\n", + (unsigned int)vol->lcnbmp_na->compression_block_size); + printf("\tCompression Block Size Bits: %u\n", + vol->lcnbmp_na->compression_block_size_bits); + printf("\tCompression Block Clusters: %u\n", + vol->lcnbmp_na->compression_block_clusters); + + //TODO: Still need to add a few more attributes +} + +/** + * ntfs_dump_flags - Dump flags for STANDARD_INFORMATION and FILE_NAME. + * @type: dump flags for this attribute type + * @flags: flags for dumping + */ +static void ntfs_dump_flags(const char *indent, ATTR_TYPES type, u32 flags) +{ + printf("%sFile attributes:\t", indent); + if (flags & FILE_ATTR_READONLY) { + printf(" READONLY"); + flags &= ~FILE_ATTR_READONLY; + } + if (flags & FILE_ATTR_HIDDEN) { + printf(" HIDDEN"); + flags &= ~FILE_ATTR_HIDDEN; + } + if (flags & FILE_ATTR_SYSTEM) { + printf(" SYSTEM"); + flags &= ~FILE_ATTR_SYSTEM; + } + if (flags & FILE_ATTR_ARCHIVE) { + printf(" ARCHIVE"); + flags &= ~FILE_ATTR_ARCHIVE; + } + if (flags & FILE_ATTR_DEVICE) { + printf(" DEVICE"); + flags &= ~FILE_ATTR_DEVICE; + } + if (flags & FILE_ATTR_NORMAL) { + printf(" NORMAL"); + flags &= ~FILE_ATTR_NORMAL; + } + if (flags & FILE_ATTR_TEMPORARY) { + printf(" TEMPORARY"); + flags &= ~FILE_ATTR_TEMPORARY; + } + if (flags & FILE_ATTR_SPARSE_FILE) { + printf(" SPARSE_FILE"); + flags &= ~FILE_ATTR_SPARSE_FILE; + } + if (flags & FILE_ATTR_REPARSE_POINT) { + printf(" REPARSE_POINT"); + flags &= ~FILE_ATTR_REPARSE_POINT; + } + if (flags & FILE_ATTR_COMPRESSED) { + printf(" COMPRESSED"); + flags &= ~FILE_ATTR_COMPRESSED; + } + if (flags & FILE_ATTR_OFFLINE) { + printf(" OFFLINE"); + flags &= ~FILE_ATTR_OFFLINE; + } + if (flags & FILE_ATTR_NOT_CONTENT_INDEXED) { + printf(" NOT_CONTENT_INDEXED"); + flags &= ~FILE_ATTR_NOT_CONTENT_INDEXED; + } + if (flags & FILE_ATTR_ENCRYPTED) { + printf(" ENCRYPTED"); + flags &= ~FILE_ATTR_ENCRYPTED; + } + /* We know that FILE_ATTR_I30_INDEX_PRESENT only exists on $FILE_NAME, + and in case we are wrong, let it appear as UNKNOWN */ + if (type == AT_FILE_NAME) { + if (flags & FILE_ATTR_I30_INDEX_PRESENT) { + printf(" I30_INDEX"); + flags &= ~FILE_ATTR_I30_INDEX_PRESENT; + } + } + if (flags & FILE_ATTR_VIEW_INDEX_PRESENT) { + printf(" VIEW_INDEX"); + flags &= ~FILE_ATTR_VIEW_INDEX_PRESENT; + } + if (flags) + printf(" UNKNOWN: 0x%08x", (unsigned int)le32_to_cpu(flags)); + printf("\n"); +} + +/** + * ntfs_dump_namespace + */ +static void ntfs_dump_namespace(const char *indent, u8 file_name_type) +{ + const char *mbs_file_type; + + /* name space */ + switch (file_name_type) { + case FILE_NAME_POSIX: + mbs_file_type = "POSIX"; + break; + case FILE_NAME_WIN32: + mbs_file_type = "Win32"; + break; + case FILE_NAME_DOS: + mbs_file_type = "DOS"; + break; + case FILE_NAME_WIN32_AND_DOS: + mbs_file_type = "Win32 & DOS"; + break; + default: + mbs_file_type = "(unknown)"; + } + printf("%sNamespace:\t\t %s\n", indent, mbs_file_type); +} + +/* *************** functions for dumping attributes ******************** */ +/** + * ntfs_dump_standard_information + */ +static void ntfs_dump_attr_standard_information(ATTR_RECORD *attr) +{ + STANDARD_INFORMATION *standard_attr = NULL; + u32 value_length; + + standard_attr = (STANDARD_INFORMATION*)((char *)attr + + le16_to_cpu(attr->value_offset)); + + /* time conversion stuff */ + if (!opts.notime) { + char *ntfs_time_str = NULL; + + ntfs_time_str = ntfsinfo_time_to_str(standard_attr->creation_time); + printf("\tFile Creation Time:\t %s",ntfs_time_str); + + ntfs_time_str = ntfsinfo_time_to_str( + standard_attr->last_data_change_time); + printf("\tFile Altered Time:\t %s",ntfs_time_str); + + ntfs_time_str = ntfsinfo_time_to_str( + standard_attr->last_mft_change_time); + printf("\tMFT Changed Time:\t %s",ntfs_time_str); + + ntfs_time_str = ntfsinfo_time_to_str(standard_attr->last_access_time); + printf("\tLast Accessed Time:\t %s",ntfs_time_str); + } + ntfs_dump_flags("\t", attr->type, standard_attr->file_attributes); + + value_length = le32_to_cpu(attr->value_length); + if (value_length == 48) { + /* Only 12 reserved bytes here */ + } else if (value_length == 72) { + printf("\tMaximum versions:\t %u \n", (unsigned int) + le32_to_cpu(standard_attr->maximum_versions)); + printf("\tVersion number:\t\t %u \n", (unsigned int) + le32_to_cpu(standard_attr->version_number)); + printf("\tClass ID:\t\t %u \n", + (unsigned int)le32_to_cpu(standard_attr->class_id)); + printf("\tUser ID:\t\t %u \n", + (unsigned int)le32_to_cpu(standard_attr->owner_id)); + printf("\tSecurity ID:\t\t %u \n", + (unsigned int)le32_to_cpu(standard_attr->security_id)); + printf("\tQuota charged:\t\t %llu \n", (unsigned long long) + le64_to_cpu(standard_attr->quota_charged)); + printf("\tUpdate Sequence Number:\t %llu \n", + (unsigned long long) + le64_to_cpu(standard_attr->usn)); + } else { + printf("\tSize of STANDARD_INFORMATION is %u. It should be " + "either 72 or 48, something is wrong...\n", + (unsigned int)value_length); + } +} + +/** + * ntfs_dump_attr_list() + */ +static void ntfs_dump_attr_list(ATTR_RECORD *attr, ntfs_volume *vol) +{ + ATTR_LIST_ENTRY *entry; + u8 *value; + s64 l; + + if (!opts.verbose) + return; + + l = ntfs_get_attribute_value_length(attr); + if (!l) { + ntfs_log_perror("ntfs_get_attribute_value_length failed"); + return; + } + value = malloc(l); + if (!value) { + ntfs_log_perror("malloc failed"); + return; + } + l = ntfs_get_attribute_value(vol, attr, value); + if (!l) { + ntfs_log_perror("ntfs_get_attribute_value failed"); + free(value); + return; + } + printf("\tDumping attribute list:"); + entry = (ATTR_LIST_ENTRY *) value; + for (;(u8 *)entry < (u8 *) value + l; entry = (ATTR_LIST_ENTRY *) + ((u8 *) entry + le16_to_cpu(entry->length))) { + printf("\n"); + printf("\t\tAttribute type:\t0x%x\n", + (unsigned int)le32_to_cpu(entry->type)); + printf("\t\tRecord length:\t%u\n", + le16_to_cpu(entry->length)); + printf("\t\tName length:\t%u\n", entry->name_length); + printf("\t\tName offset:\t%u\n", entry->name_offset); + printf("\t\tStarting VCN:\t%lld\n", + sle64_to_cpu(entry->lowest_vcn)); + printf("\t\tMFT reference:\t%lld\n", + MREF_LE(entry->mft_reference)); + printf("\t\tInstance:\t%u\n", le16_to_cpu(entry->instance)); + printf("\t\tName:\t\t"); + if (entry->name_length) { + char *name = NULL; + int name_size; + + name_size = ntfs_ucstombs(entry->name, + entry->name_length, &name, 0); + + if (name_size > 0) { + printf("%s\n", name); + free(name); + } else + ntfs_log_perror("ntfs_ucstombs failed"); + } else + printf("unnamed\n"); + } + free(value); + printf("\tEnd of attribute list reached.\n"); +} + +/** + * ntfs_dump_filename() + */ +static void ntfs_dump_filename(const char *indent, + FILE_NAME_ATTR *file_name_attr) +{ + printf("%sParent directory:\t %lld\n", indent, + (long long)MREF_LE(file_name_attr->parent_directory)); + /* time stuff */ + if (!opts.notime) { + char *ntfs_time_str; + + ntfs_time_str = ntfsinfo_time_to_str( + file_name_attr->creation_time); + printf("%sFile Creation Time:\t %s", indent, ntfs_time_str); + + ntfs_time_str = ntfsinfo_time_to_str( + file_name_attr->last_data_change_time); + printf("%sFile Altered Time:\t %s", indent, ntfs_time_str); + + ntfs_time_str = ntfsinfo_time_to_str( + file_name_attr->last_mft_change_time); + printf("%sMFT Changed Time:\t %s", indent, ntfs_time_str); + + ntfs_time_str = ntfsinfo_time_to_str( + file_name_attr->last_access_time); + printf("%sLast Accessed Time:\t %s", indent, ntfs_time_str); + } + /* other basic stuff about the file */ + printf("%sAllocated Size:\t\t %lld\n", indent, (long long) + sle64_to_cpu(file_name_attr->allocated_size)); + printf("%sData Size:\t\t %lld\n", indent, + (long long)sle64_to_cpu(file_name_attr->data_size)); + printf("%sFilename Length:\t %d\n", indent, + (unsigned)file_name_attr->file_name_length); + ntfs_dump_flags(indent, AT_FILE_NAME, file_name_attr->file_attributes); + if (file_name_attr->file_attributes & FILE_ATTR_REPARSE_POINT && + file_name_attr->reserved) + printf("%sReparse point tag:\t 0x%x\n", indent, (unsigned) + le32_to_cpu(file_name_attr->reparse_point_tag)); + else if (file_name_attr->reparse_point_tag) { + printf("%sEA Length:\t\t %d\n", indent, (unsigned) + le16_to_cpu(file_name_attr->packed_ea_size)); + if (file_name_attr->reserved) + printf("%sReserved:\t\t %d\n", indent, (unsigned) + le16_to_cpu(file_name_attr->reserved)); + } + /* The filename. */ + ntfs_dump_namespace(indent, file_name_attr->file_name_type); + if (file_name_attr->file_name_length > 0) { + /* but first we need to convert the little endian unicode string + into a printable format */ + char *mbs_file_name = NULL; + int mbs_file_name_size; + + mbs_file_name_size = ntfs_ucstombs(file_name_attr->file_name, + file_name_attr->file_name_length,&mbs_file_name,0); + + if (mbs_file_name_size>0) { + printf("%sFilename:\t\t '%s'\n", indent, mbs_file_name); + free(mbs_file_name); + } else { + /* an error occurred, errno holds the reason - notify the user */ + ntfs_log_perror("ntfsinfo error: could not parse file name"); + } + } else { + printf("%sFile Name:\t\t unnamed?!?\n", indent); + } +} + +/** + * ntfs_dump_attr_file_name() + */ +static void ntfs_dump_attr_file_name(ATTR_RECORD *attr) +{ + ntfs_dump_filename("\t", (FILE_NAME_ATTR*)((u8*)attr + + le16_to_cpu(attr->value_offset))); +} + +/** + * ntfs_dump_object_id + * + * dump the $OBJECT_ID attribute - not present on all systems + */ +static void ntfs_dump_attr_object_id(ATTR_RECORD *attr,ntfs_volume *vol) +{ + OBJECT_ID_ATTR *obj_id_attr = NULL; + + obj_id_attr = (OBJECT_ID_ATTR *)((u8*)attr + + le16_to_cpu(attr->value_offset)); + + if (vol->major_ver >= 3.0) { + u32 value_length; + char printable_GUID[37]; + + value_length = le32_to_cpu(attr->value_length); + + /* Object ID is mandatory. */ + ntfs_guid_to_mbs(&obj_id_attr->object_id, printable_GUID); + printf("\tObject ID:\t\t %s\n", printable_GUID); + + /* Dump Birth Volume ID. */ + if ((value_length > sizeof(GUID)) && !ntfs_guid_is_zero( + &obj_id_attr->birth_volume_id)) { + ntfs_guid_to_mbs(&obj_id_attr->birth_volume_id, + printable_GUID); + printf("\tBirth Volume ID:\t\t %s\n", printable_GUID); + } else + printf("\tBirth Volume ID:\t missing\n"); + + /* Dumping Birth Object ID */ + if ((value_length > sizeof(GUID)) && !ntfs_guid_is_zero( + &obj_id_attr->birth_object_id)) { + ntfs_guid_to_mbs(&obj_id_attr->birth_object_id, + printable_GUID); + printf("\tBirth Object ID:\t\t %s\n", printable_GUID); + } else + printf("\tBirth Object ID:\t missing\n"); + + /* Dumping Domain_id - reserved for now */ + if ((value_length > sizeof(GUID)) && !ntfs_guid_is_zero( + &obj_id_attr->domain_id)) { + ntfs_guid_to_mbs(&obj_id_attr->domain_id, + printable_GUID); + printf("\tDomain ID:\t\t\t %s\n", printable_GUID); + } else + printf("\tDomain ID:\t\t missing\n"); + } else + printf("\t$OBJECT_ID not present. Only NTFS versions > 3.0\n" + "\thave $OBJECT_ID. Your version of NTFS is %d.\n", + vol->major_ver); +} + +/** + * ntfs_dump_acl + * + * given an acl, print it in a beautiful & lovely way. + */ +static void ntfs_dump_acl(const char *prefix, ACL *acl) +{ + unsigned int i; + u16 ace_count; + ACCESS_ALLOWED_ACE *ace; + + printf("%sRevision\t %u\n", prefix, acl->revision); + + /* don't recalc le16_to_cpu every iteration (minor speedup on big-endians */ + ace_count = le16_to_cpu(acl->ace_count); + + /* initialize 'ace' to the first ace (if any) */ + ace = (ACCESS_ALLOWED_ACE *)((char *)acl + 8); + + /* iterate through ACE's */ + for (i = 1; i <= ace_count; i++) { + const char *ace_type; + char *sid; + + /* set ace_type. */ + switch (ace->type) { + case ACCESS_ALLOWED_ACE_TYPE: + ace_type = "allow"; + break; + case ACCESS_DENIED_ACE_TYPE: + ace_type = "deny"; + break; + case SYSTEM_AUDIT_ACE_TYPE: + ace_type = "audit"; + break; + default: + ace_type = "unknown"; + break; + } + + printf("%sACE:\t\t type:%s flags:0x%x access:0x%x\n", prefix, + ace_type, (unsigned int)le16_to_cpu(ace->flags), + (unsigned int)le32_to_cpu(ace->mask)); + /* get a SID string */ + sid = ntfs_sid_to_mbs(&ace->sid, NULL, 0); + printf("%s\t\t SID: %s\n", prefix, sid); + free(sid); + + /* proceed to next ACE */ + ace = (ACCESS_ALLOWED_ACE *)(((char *)ace) + le32_to_cpu(ace->size)); + } +} + + +static void ntfs_dump_security_descriptor(SECURITY_DESCRIPTOR_ATTR *sec_desc, + const char *indent) +{ + char *sid; + + printf("%s\tRevision:\t\t %u\n", indent, sec_desc->revision); + + /* TODO: parse the flags */ + printf("%s\tFlags:\t\t\t 0x%0x\n", indent, sec_desc->control); + + sid = ntfs_sid_to_mbs((SID *)((char *)sec_desc + + le32_to_cpu(sec_desc->owner)), NULL, 0); + printf("%s\tOwner SID:\t\t %s\n", indent, sid); + free(sid); + + sid = ntfs_sid_to_mbs((SID *)((char *)sec_desc + + le32_to_cpu(sec_desc->group)), NULL, 0); + printf("%s\tGroup SID:\t\t %s\n", indent, sid); + free(sid); + + printf("%s\tSystem ACL:\t\t ", indent); + if (sec_desc->control & SE_SACL_PRESENT) { + if (sec_desc->control & SE_SACL_DEFAULTED) { + printf("defaulted"); + } + printf("\n"); + ntfs_dump_acl(indent ? "\t\t\t" : "\t\t", + (ACL *)((char *)sec_desc + + le32_to_cpu(sec_desc->sacl))); + } else { + printf("missing\n"); + } + + printf("%s\tDiscretionary ACL:\t ", indent); + if (sec_desc->control & SE_DACL_PRESENT) { + if (sec_desc->control & SE_SACL_DEFAULTED) { + printf("defaulted"); + } + printf("\n"); + ntfs_dump_acl(indent ? "\t\t\t" : "\t\t", + (ACL *)((char *)sec_desc + + le32_to_cpu(sec_desc->dacl))); + } else { + printf("missing\n"); + } +} + +/** + * ntfs_dump_security_descriptor() + * + * dump the security information about the file + */ +static void ntfs_dump_attr_security_descriptor(ATTR_RECORD *attr, ntfs_volume *vol) +{ + SECURITY_DESCRIPTOR_ATTR *sec_desc_attr; + + if (attr->non_resident) { + /* FIXME: We don't handle fragmented mapping pairs case. */ + runlist *rl = ntfs_mapping_pairs_decompress(vol, attr, 0); + if (rl) { + s64 data_size, bytes_read; + + data_size = sle64_to_cpu(attr->data_size); + sec_desc_attr = malloc(data_size); + if (!sec_desc_attr) { + ntfs_log_perror("malloc failed"); + free(rl); + return; + } + bytes_read = ntfs_rl_pread(vol, rl, 0, + data_size, sec_desc_attr); + if (bytes_read != data_size) { + ntfs_log_error("ntfsinfo error: could not " + "read security descriptor\n"); + free(rl); + free(sec_desc_attr); + return; + } + free(rl); + } else { + ntfs_log_error("ntfsinfo error: could not " + "decompress runlist\n"); + return; + } + } else { + sec_desc_attr = (SECURITY_DESCRIPTOR_ATTR *)((u8*)attr + + le16_to_cpu(attr->value_offset)); + } + + ntfs_dump_security_descriptor(sec_desc_attr, ""); + + if (attr->non_resident) + free(sec_desc_attr); +} + +/** + * ntfs_dump_volume_name() + * + * dump the name of the volume the inode belongs to + */ +static void ntfs_dump_attr_volume_name(ATTR_RECORD *attr) +{ + ntfschar *ucs_vol_name = NULL; + + if (attr->value_length>0) { + char *mbs_vol_name = NULL; + int mbs_vol_name_size; + /* calculate volume name position */ + ucs_vol_name = (ntfschar*)((u8*)attr + + le16_to_cpu(attr->value_offset)); + /* convert the name to current locale multibyte sequence */ + mbs_vol_name_size = ntfs_ucstombs(ucs_vol_name, + le32_to_cpu(attr->value_length)/sizeof(ntfschar), + &mbs_vol_name,0); + + if (mbs_vol_name_size>0) { + /* output the converted name. */ + printf("\tVolume Name:\t\t '%s'\n",mbs_vol_name); + free(mbs_vol_name); + } else { + /* an error occurred, errno holds the reason - notify the user */ + ntfs_log_perror("ntfsinfo error: could not parse volume name"); + } + } else { + printf("\tVolume Name:\t\t unnamed\n"); + } +} + +/** + * ntfs_dump_volume_information() + * + * dump the information for the volume the inode belongs to + * + */ +static void ntfs_dump_attr_volume_information(ATTR_RECORD *attr) +{ + VOLUME_INFORMATION *vol_information = NULL; + + vol_information = (VOLUME_INFORMATION*)((char *)attr+ + le16_to_cpu(attr->value_offset)); + + printf("\tVolume Version:\t\t %d.%d\n", vol_information->major_ver, + vol_information->minor_ver); + printf("\tFlags:\t\t\t "); + if (vol_information->flags & VOLUME_IS_DIRTY) + printf("DIRTY "); + if (vol_information->flags & VOLUME_RESIZE_LOG_FILE) + printf("RESIZE_LOG "); + if (vol_information->flags & VOLUME_UPGRADE_ON_MOUNT) + printf("UPG_ON_MOUNT "); + if (vol_information->flags & VOLUME_MOUNTED_ON_NT4) + printf("MOUNTED_NT4 "); + if (vol_information->flags & VOLUME_DELETE_USN_UNDERWAY) + printf("DEL_USN "); + if (vol_information->flags & VOLUME_REPAIR_OBJECT_ID) + printf("REPAIR_OBJID "); + if (vol_information->flags & VOLUME_CHKDSK_UNDERWAY) + printf("CHKDSK_UNDERWAY "); + if (vol_information->flags & VOLUME_MODIFIED_BY_CHKDSK) + printf("MOD_BY_CHKDSK "); + if (vol_information->flags & VOLUME_FLAGS_MASK) { + printf("\n"); + } else { + printf("none set\n"); + } + if (vol_information->flags & (0xFFFF - VOLUME_FLAGS_MASK)) + printf("\t\t\t\t Unknown Flags: 0x%04x\n", + vol_information->flags & (0xFFFF - VOLUME_FLAGS_MASK)); +} + +static ntfschar NTFS_DATA_SDS[5] = { const_cpu_to_le16('$'), + const_cpu_to_le16('S'), const_cpu_to_le16('D'), + const_cpu_to_le16('S'), const_cpu_to_le16('\0') }; + +static void ntfs_dump_sds_entry(SECURITY_DESCRIPTOR_HEADER *sds) +{ + SECURITY_DESCRIPTOR_RELATIVE *sd; + + ntfs_log_verbose("\t\tHash:\t\t\t 0x%08x\n", le32_to_cpu(sds->hash)); + ntfs_log_verbose("\t\tSecurity id:\t\t %u\n", + le32_to_cpu(sds->security_id)); + ntfs_log_verbose("\t\tOffset:\t\t\t %llu\n", le64_to_cpu(sds->offset)); + ntfs_log_verbose("\t\tLength:\t\t\t %u\n", le32_to_cpu(sds->length)); + + sd = (SECURITY_DESCRIPTOR_RELATIVE *)((char *)sds + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + + ntfs_dump_security_descriptor(sd, "\t"); +} + +static void ntfs_dump_sds(ATTR_RECORD *attr, ntfs_inode *ni) +{ + SECURITY_DESCRIPTOR_HEADER *sds, *sd; + ntfschar *name; + int name_len; + s64 data_size; + u64 inode; + + inode = ni->mft_no; + if (ni->nr_extents < 0) + inode = ni->base_ni->mft_no; + if (FILE_Secure != inode) + return; + + name_len = attr->name_length; + if (!name_len) + return; + + name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); + if (!ntfs_names_are_equal(NTFS_DATA_SDS, sizeof(NTFS_DATA_SDS) / 2 - 1, + name, name_len, 0, NULL, 0)) + return; + + sd = sds = ntfs_attr_readall(ni, AT_DATA, name, name_len, &data_size); + if (!sd) { + ntfs_log_perror("Failed to read $SDS attribute"); + return; + } + /* + * FIXME: The right way is based on the indexes, so we couldn't + * miss real entries. For now, dump until it makes sense. + */ + while (sd->length && sd->hash && + le64_to_cpu(sd->offset) < (u64)data_size && + le32_to_cpu(sd->length) < (u64)data_size && + le64_to_cpu(sd->offset) + + le32_to_cpu(sd->length) < (u64)data_size) { + ntfs_dump_sds_entry(sd); + sd = (SECURITY_DESCRIPTOR_HEADER *)((char*)sd + + ((le32_to_cpu(sd->length) + 15) & ~15)); + } + free(sds); +} + +static const char *get_attribute_type_name(u32 type) +{ + switch (type) { + case AT_UNUSED: return "$UNUSED"; + case AT_STANDARD_INFORMATION: return "$STANDARD_INFORMATION"; + case AT_ATTRIBUTE_LIST: return "$ATTRIBUTE_LIST"; + case AT_FILE_NAME: return "$FILE_NAME"; + case AT_OBJECT_ID: return "$OBJECT_ID"; + case AT_SECURITY_DESCRIPTOR: return "$SECURITY_DESCRIPTOR"; + case AT_VOLUME_NAME: return "$VOLUME_NAME"; + case AT_VOLUME_INFORMATION: return "$VOLUME_INFORMATION"; + case AT_DATA: return "$DATA"; + case AT_INDEX_ROOT: return "$INDEX_ROOT"; + case AT_INDEX_ALLOCATION: return "$INDEX_ALLOCATION"; + case AT_BITMAP: return "$BITMAP"; + case AT_REPARSE_POINT: return "$REPARSE_POINT"; + case AT_EA_INFORMATION: return "$EA_INFORMATION"; + case AT_EA: return "$EA"; + case AT_PROPERTY_SET: return "$PROPERTY_SET"; + case AT_LOGGED_UTILITY_STREAM: return "$LOGGED_UTILITY_STREAM"; + case AT_END: return "$END"; + } + + return "$UNKNOWN"; +} + +static void ntfs_dump_attribute_header(ATTR_RECORD *a, ntfs_volume *vol) +{ + printf("Dumping attribute %s (%#02x)\n", + get_attribute_type_name(a->type), a->type); + + ntfs_log_verbose("\tAttribute length:\t %u\n", le32_to_cpu(a->length)); + printf("\tResident: \t\t %s\n", a->non_resident ? "No" : "Yes"); + ntfs_log_verbose("\tName length:\t\t %u\n", a->name_length); + ntfs_log_verbose("\tName offset:\t\t %u\n", le16_to_cpu(a->name_offset)); + + /* Dump the attribute (stream) name */ + if (a->name_length) { + char *attribute_name = NULL; + + attribute_name = ntfs_attr_get_name(a); + if (attribute_name) { + printf("\tAttribute name:\t\t '%s'\n", attribute_name); + free(attribute_name); + } else + ntfs_log_perror("Error: couldn't parse attribute name"); + } + + /* TODO: parse the flags */ + printf("\tFlags:\t\t\t 0x%04hx\n",le16_to_cpu(a->flags)); + printf("\tAttribute instance:\t %u\n", le16_to_cpu(a->instance)); + + /* Resident attribute */ + if (!a->non_resident) { + printf("\tData size:\t\t %u\n", + (unsigned int)le32_to_cpu(a->value_length)); + ntfs_log_verbose("\tData offset:\t\t %u\n", + (unsigned int)le16_to_cpu(a->value_offset)); + /* TODO: parse the flags */ + printf("\tResident flags:\t\t 0x%02hhx\n", a->resident_flags); + ntfs_log_verbose("\tReservedR:\t\t %d\n", a->reservedR); + return; + } + + /* Non-resident attribute */ + ntfs_log_verbose("\tLowest VCN\t\t %lld\n", + (long long)sle64_to_cpu(a->lowest_vcn)); + ntfs_log_verbose("\tHighest VCN:\t\t %lld\n", + (long long)sle64_to_cpu(a->highest_vcn)); + ntfs_log_verbose("\tMapping pairs offset:\t %u\n", + le16_to_cpu(a->mapping_pairs_offset)); + printf("\tCompression unit:\t %u\n", a->compression_unit); + /* TODO: dump the 5 reserved bytes here in verbose mode */ + + if (!a->lowest_vcn) { + printf("\tData size:\t\t %llu\n", + (long long)le64_to_cpu(a->data_size)); + printf("\tAllocated size:\t\t %llu\n", + (long long)le64_to_cpu(a->allocated_size)); + printf("\tInitialized size:\t %llu\n", + (long long)le64_to_cpu(a->initialized_size)); + if (a->compression_unit || (a->flags & ATTR_IS_COMPRESSED)) + printf("\tCompressed size:\t %llu\n", + (long long)le64_to_cpu(a->compressed_size)); + } + + if (opts.verbose) { + runlist *rl = ntfs_mapping_pairs_decompress(vol, a, 0); + if (rl) { + runlist *rlc = rl; + printf("\tRunlist:\tVCN\t\tLCN\t\tLength\n"); + while (rlc->length) { + printf("\t\t\t%lld\t\t%lld\t\t%lld\n", + rlc->vcn, rlc->lcn, rlc->length); + rlc++; + } + free(rl); + } else + ntfs_log_error("Error: couldn't decompress runlist\n"); + } +} + +/** + * ntfs_dump_data_attr() + * + * dump some info about the data attribute if it's metadata + */ +static void ntfs_dump_attr_data(ATTR_RECORD *attr, ntfs_inode *ni) +{ + if (opts.verbose) + ntfs_dump_sds(attr, ni); +} + +typedef enum { + INDEX_ATTR_UNKNOWN, + INDEX_ATTR_DIRECTORY_I30, + INDEX_ATTR_SECURE_SII, + INDEX_ATTR_SECURE_SDH, + INDEX_ATTR_OBJID_O, + INDEX_ATTR_REPARSE_R, + INDEX_ATTR_QUOTA_O, + INDEX_ATTR_QUOTA_Q, +} INDEX_ATTR_TYPE; + +static void ntfs_dump_index_key(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type) +{ + char *sid; + char printable_GUID[37]; + + switch (type) { + case INDEX_ATTR_SECURE_SII: + ntfs_log_verbose("\t\tKey security id:\t %u\n", + le32_to_cpu(entry->key.sii.security_id)); + break; + case INDEX_ATTR_SECURE_SDH: + ntfs_log_verbose("\t\tKey hash:\t\t 0x%08x\n", + le32_to_cpu(entry->key.sdh.hash)); + ntfs_log_verbose("\t\tKey security id:\t %u\n", + le32_to_cpu(entry->key.sdh.security_id)); + break; + case INDEX_ATTR_OBJID_O: + ntfs_guid_to_mbs(&entry->key.object_id, printable_GUID); + ntfs_log_verbose("\t\tKey GUID:\t\t %s\n", printable_GUID); + break; + case INDEX_ATTR_REPARSE_R: + ntfs_log_verbose("\t\tKey reparse tag:\t 0x%08x\n", + le32_to_cpu(entry->key.reparse.reparse_tag)); + ntfs_log_verbose("\t\tKey file id:\t\t %llu\n", + le64_to_cpu(entry->key.reparse.file_id)); + break; + case INDEX_ATTR_QUOTA_O: + sid = ntfs_sid_to_mbs(&entry->key.sid, NULL, 0); + ntfs_log_verbose("\t\tKey SID:\t\t %s\n", sid); + free(sid); + break; + case INDEX_ATTR_QUOTA_Q: + ntfs_log_verbose("\t\tKey owner id:\t\t %u\n", + le32_to_cpu(entry->key.owner_id)); + break; + default: + ntfs_log_verbose("\t\tIndex attr type is UNKNOWN: \t 0x%08x\n", + le32_to_cpu(type)); + break; + } +} + +typedef union { + SII_INDEX_DATA sii; /* $SII index data in $Secure */ + SDH_INDEX_DATA sdh; /* $SDH index data in $Secure */ + QUOTA_O_INDEX_DATA quota_o; /* $O index data in $Quota */ + QUOTA_CONTROL_ENTRY quota_q; /* $Q index data in $Quota */ +} __attribute__((__packed__)) INDEX_ENTRY_DATA; + +static void ntfs_dump_index_data(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type) +{ + INDEX_ENTRY_DATA *data; + + data = (INDEX_ENTRY_DATA *)((u8 *)entry + entry->data_offset); + + switch (type) { + case INDEX_ATTR_SECURE_SII: + ntfs_log_verbose("\t\tHash:\t\t\t 0x%08x\n", + le32_to_cpu(data->sii.hash)); + ntfs_log_verbose("\t\tSecurity id:\t\t %u\n", + le32_to_cpu(data->sii.security_id)); + ntfs_log_verbose("\t\tOffset in $SDS:\t\t %llu\n", + le64_to_cpu(data->sii.offset)); + ntfs_log_verbose("\t\tLength in $SDS:\t\t %u\n", + le32_to_cpu(data->sii.length)); + break; + case INDEX_ATTR_SECURE_SDH: + ntfs_log_verbose("\t\tHash:\t\t\t 0x%08x\n", + le32_to_cpu(data->sdh.hash)); + ntfs_log_verbose("\t\tSecurity id:\t\t %u\n", + le32_to_cpu(data->sdh.security_id)); + ntfs_log_verbose("\t\tOffset in $SDS:\t\t %llu\n", + le64_to_cpu(data->sdh.offset)); + ntfs_log_verbose("\t\tLength in $SDS:\t\t %u\n", + le32_to_cpu(data->sdh.length)); + ntfs_log_verbose("\t\tUnknown (padding):\t 0x%08x\n", + le32_to_cpu(data->sdh.reserved_II)); + break; + case INDEX_ATTR_OBJID_O: { + OBJ_ID_INDEX_DATA *object_id_data; + char printable_GUID[37]; + + object_id_data = (OBJ_ID_INDEX_DATA*)((u8*)entry + + le16_to_cpu(entry->data_offset)); + ntfs_log_verbose("\t\tMFT Number:\t\t 0x%llx\n", + (unsigned long long) + MREF_LE(object_id_data->mft_reference)); + ntfs_log_verbose("\t\tMFT Sequence Number:\t 0x%x\n", + (unsigned) + MSEQNO_LE(object_id_data->mft_reference)); + ntfs_guid_to_mbs(&object_id_data->birth_volume_id, + printable_GUID); + ntfs_log_verbose("\t\tBirth volume id GUID:\t %s\n", + printable_GUID); + ntfs_guid_to_mbs(&object_id_data->birth_object_id, + printable_GUID); + ntfs_log_verbose("\t\tBirth object id GUID:\t %s\n", + printable_GUID); + ntfs_guid_to_mbs(&object_id_data->domain_id, printable_GUID); + ntfs_log_verbose("\t\tDomain id GUID:\t\t %s\n", + printable_GUID); + } + break; + case INDEX_ATTR_REPARSE_R: + /* TODO */ + break; + case INDEX_ATTR_QUOTA_O: + ntfs_log_verbose("\t\tOwner id:\t\t %u\n", + le32_to_cpu(data->quota_o.owner_id)); + ntfs_log_verbose("\t\tUnknown:\t\t %u\n", + le32_to_cpu(data->quota_o.unknown)); + break; + case INDEX_ATTR_QUOTA_Q: + ntfs_log_verbose("\t\tVersion:\t\t %u\n", + le32_to_cpu(data->quota_q.version)); + ntfs_log_verbose("\t\tQuota flags:\t\t 0x%08x\n", + le32_to_cpu(data->quota_q.flags)); + ntfs_log_verbose("\t\tBytes used:\t\t %llu\n", + le64_to_cpu(data->quota_q.bytes_used)); + ntfs_log_verbose("\t\tLast changed:\t\t %s", + ntfsinfo_time_to_str( + data->quota_q.change_time)); + ntfs_log_verbose("\t\tThreshold:\t\t %lld\n", + le64_to_cpu(data->quota_q.threshold)); + ntfs_log_verbose("\t\tLimit:\t\t\t %lld\n", + le64_to_cpu(data->quota_q.limit)); + ntfs_log_verbose("\t\tExceeded time:\t\t %lld\n", + le64_to_cpu(data->quota_q.exceeded_time)); + if (entry->data_length > 48) { + char *sid; + sid = ntfs_sid_to_mbs(&data->quota_q.sid, NULL, 0); + ntfs_log_verbose("\t\tOwner SID:\t\t %s\n", sid); + free(sid); + } + break; + default: + ntfs_log_verbose("\t\tIndex attr type is UNKNOWN: \t 0x%08x\n", + le32_to_cpu(type)); + break; + } +} + +/** + * ntfs_dump_index_entries() + * + * dump sequence of index_entries and return number of entries dumped. + */ +static int ntfs_dump_index_entries(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type) +{ + int numb_entries = 1; + while (1) { + if (!opts.verbose) { + if (entry->ie_flags & INDEX_ENTRY_END) + break; + entry = (INDEX_ENTRY *)((u8 *)entry + + le16_to_cpu(entry->length)); + numb_entries++; + continue; + } + ntfs_log_verbose("\t\tEntry length:\t\t %u\n", + le16_to_cpu(entry->length)); + ntfs_log_verbose("\t\tKey length:\t\t %u\n", + le16_to_cpu(entry->key_length)); + ntfs_log_verbose("\t\tFlags:\t\t\t 0x%02x\n", + le16_to_cpu(entry->ie_flags)); + + if (entry->ie_flags & INDEX_ENTRY_NODE) + ntfs_log_verbose("\t\tSubnode VCN:\t\t 0x%llx\n", + sle64_to_cpu(*(VCN*)((u8*)entry + + le16_to_cpu(entry->length) - sizeof(VCN)))); + if (entry->ie_flags & INDEX_ENTRY_END) + break; + + switch (type) { + case INDEX_ATTR_DIRECTORY_I30: + ntfs_log_verbose("\t\tFILE record number:\t %llu\n", + MREF_LE(entry->indexed_file)); + ntfs_dump_filename("\t\t", &entry->key.file_name); + break; + default: + ntfs_log_verbose("\t\tData offset:\t\t %u\n", + le16_to_cpu(entry->data_offset)); + ntfs_log_verbose("\t\tData length:\t\t %u\n", + le16_to_cpu(entry->data_length)); + ntfs_dump_index_key(entry, type); + ntfs_log_verbose("\t\tKey Data:\n"); + ntfs_dump_index_data(entry, type); + break; + } + entry = (INDEX_ENTRY *)((u8 *)entry + + le16_to_cpu(entry->length)); + numb_entries++; + ntfs_log_verbose("\n"); + } + ntfs_log_verbose("\tEnd of index block reached\n"); + return numb_entries; +} + +#define COMPARE_INDEX_NAMES(attr, name) \ + ntfs_names_are_equal((name), sizeof(name) / 2 - 1, \ + (ntfschar*)((char*)(attr) + le16_to_cpu((attr)->name_offset)), \ + (attr)->name_length, 0, NULL, 0) + +static INDEX_ATTR_TYPE get_index_attr_type(ntfs_inode *ni, ATTR_RECORD *attr, + INDEX_ROOT *index_root) +{ + char file_name[64]; + + if (!attr->name_length) + return INDEX_ATTR_UNKNOWN; + + if (index_root->type) { + if (index_root->type == AT_FILE_NAME) + return INDEX_ATTR_DIRECTORY_I30; + else + /* weird, this should be illegal */ + ntfs_log_error("Unknown index attribute type: 0x%0X\n", + index_root->type); + return INDEX_ATTR_UNKNOWN; + } + + if (utils_is_metadata(ni) <= 0) + return INDEX_ATTR_UNKNOWN; + if (utils_inode_get_name(ni, file_name, sizeof(file_name)) <= 0) + return INDEX_ATTR_UNKNOWN; + + if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_SDH)) + return INDEX_ATTR_SECURE_SDH; + else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_SII)) + return INDEX_ATTR_SECURE_SII; + else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_SII)) + return INDEX_ATTR_SECURE_SII; + else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_Q)) + return INDEX_ATTR_QUOTA_Q; + else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_R)) + return INDEX_ATTR_REPARSE_R; + else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_O)) { + if (!strcmp(file_name, "/$Extend/$Quota")) + return INDEX_ATTR_QUOTA_O; + else if (!strcmp(file_name, "/$Extend/$ObjId")) + return INDEX_ATTR_OBJID_O; + } + + return INDEX_ATTR_UNKNOWN; +} + +static void ntfs_dump_index_attr_type(INDEX_ATTR_TYPE type) +{ + if (type == INDEX_ATTR_DIRECTORY_I30) + printf("DIRECTORY_I30"); + else if (type == INDEX_ATTR_SECURE_SDH) + printf("SECURE_SDH"); + else if (type == INDEX_ATTR_SECURE_SII) + printf("SECURE_SII"); + else if (type == INDEX_ATTR_OBJID_O) + printf("OBJID_O"); + else if (type == INDEX_ATTR_QUOTA_O) + printf("QUOTA_O"); + else if (type == INDEX_ATTR_QUOTA_Q) + printf("QUOTA_Q"); + else if (type == INDEX_ATTR_REPARSE_R) + printf("REPARSE_R"); + else + printf("UNKNOWN"); + printf("\n"); +} + +/** + * ntfs_dump_attr_index_root() + * + * dump the index_root attribute + */ +static void ntfs_dump_attr_index_root(ATTR_RECORD *attr, ntfs_inode *ni) +{ + INDEX_ATTR_TYPE type; + INDEX_ROOT *index_root = NULL; + INDEX_ENTRY *entry; + + index_root = (INDEX_ROOT*)((u8*)attr + le16_to_cpu(attr->value_offset)); + + /* attr_type dumping */ + type = get_index_attr_type(ni, attr, index_root); + printf("\tIndexed Attr Type:\t "); + ntfs_dump_index_attr_type(type); + + /* collation rule dumping */ + printf("\tCollation Rule:\t\t %u\n", + (unsigned int)le32_to_cpu(index_root->collation_rule)); +/* COLLATION_BINARY, COLLATION_FILE_NAME, COLLATION_UNICODE_STRING, + COLLATION_NTOFS_ULONG, COLLATION_NTOFS_SID, + COLLATION_NTOFS_SECURITY_HASH, COLLATION_NTOFS_ULONGS */ + + printf("\tIndex Block Size:\t %u\n", + (unsigned int)le32_to_cpu(index_root->index_block_size)); + printf("\tClusters Per Block:\t %u\n", + index_root->clusters_per_index_block); + + /* index header starts here */ + printf("\tAllocated Size:\t\t %u\n", + (unsigned int)le32_to_cpu(index_root->index.allocated_size)); + printf("\tUsed Size:\t\t %u\n", + (unsigned int)le32_to_cpu(index_root->index.index_length)); + + /* the flags are 8bit long, no need for byte-order handling */ + printf("\tFlags:\t\t\t 0x%02x\n",index_root->index.ih_flags); + + entry = (INDEX_ENTRY *)((u8 *)index_root + + le32_to_cpu(index_root->index.entries_offset) + 0x10); + ntfs_log_verbose("\tDumping index block:\n"); + printf("\tIndex entries total:\t %d\n", + ntfs_dump_index_entries(entry, type)); +} + +static void ntfs_dump_usa_lsn(const char *indent, MFT_RECORD *mrec) +{ + printf("%sUpd. Seq. Array Offset:\t %hu\n", indent, + le16_to_cpu(mrec->usa_ofs)); + printf("%sUpd. Seq. Array Count: \t %hu\n", indent, + le16_to_cpu(mrec->usa_count)); + printf("%sUpd. Seq. Number:\t %hu\n", indent, + *(u16 *)((u8 *)mrec + le16_to_cpu(mrec->usa_ofs))); + printf("%sLogFile Seq. Number:\t 0x%llx\n", indent, + (long long int)sle64_to_cpu(mrec->lsn)); +} + +/** + * ntfs_dump_attr_index_allocation() + * + * dump context of the index_allocation attribute + */ +static void ntfs_dump_attr_index_allocation(ATTR_RECORD *attr, ntfs_inode *ni) +{ + INDEX_ALLOCATION *allocation, *tmp_alloc; + INDEX_ENTRY *entry; + INDEX_ROOT *index_root; + INDEX_ATTR_TYPE type; + int total_entries = 0; + int total_indx_blocks = 0; + u8 *bitmap, *byte; + int bit; + ntfschar *name; + u32 name_len; + s64 data_size; + + index_root = ntfs_index_root_get(ni, attr); + if (!index_root) { + ntfs_log_perror("Failed to read $INDEX_ROOT attribute"); + return; + } + + type = get_index_attr_type(ni, attr, index_root); + + name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); + name_len = attr->name_length; + + byte = bitmap = ntfs_attr_readall(ni, AT_BITMAP, name, name_len, NULL); + if (!byte) { + ntfs_log_perror("Failed to read $BITMAP attribute"); + goto out_index_root; + } + + tmp_alloc = allocation = ntfs_attr_readall(ni, AT_INDEX_ALLOCATION, + name, name_len, &data_size); + if (!tmp_alloc) { + ntfs_log_perror("Failed to read $INDEX_ALLOCATION attribute"); + goto out_bitmap; + } + + bit = 0; + while ((u8 *)tmp_alloc < (u8 *)allocation + data_size) { + if (*byte & (1 << bit)) { + if (ntfs_mst_post_read_fixup((NTFS_RECORD *) tmp_alloc, + index_root->index_block_size)) { + ntfs_log_perror("Damaged INDX record"); + goto out_allocation; + } + entry = (INDEX_ENTRY *)((u8 *)tmp_alloc + le32_to_cpu( + tmp_alloc->index.entries_offset) + 0x18); + ntfs_log_verbose("\tDumping index block (VCN 0x%llx, " + "used %u/%u, flags 0x%02x):\n", + le64_to_cpu(tmp_alloc->index_block_vcn), + (unsigned int)le32_to_cpu(tmp_alloc-> + index.index_length), (unsigned int) + le32_to_cpu(tmp_alloc->index. + allocated_size), + tmp_alloc->index.ih_flags); + if (opts.verbose) { + ntfs_dump_usa_lsn("\t\t", + (MFT_RECORD *)tmp_alloc); + printf("\n"); + } + total_entries += ntfs_dump_index_entries(entry, type); + total_indx_blocks++; + } + tmp_alloc = (INDEX_ALLOCATION *)((u8 *)tmp_alloc + + index_root->index_block_size); + bit++; + if (bit > 7) { + bit = 0; + byte++; + } + } + printf("\tIndex entries total:\t %d\n", total_entries); + printf("\tINDX blocks total:\t %d\n", total_indx_blocks); +out_allocation: + free(allocation); +out_bitmap: + free(bitmap); +out_index_root: + free(index_root); +} + +/** + * ntfs_dump_attr_bitmap() + * + * dump the bitmap attribute + */ +static void ntfs_dump_attr_bitmap(ATTR_RECORD *attr __attribute__((unused))) +{ + /* TODO */ +} + +/** + * ntfs_dump_attr_reparse_point() + * + * of ntfs 3.x dumps the reparse_point attribute + */ +static void ntfs_dump_attr_reparse_point(ATTR_RECORD *attr __attribute__((unused))) +{ + /* TODO */ +} + +/** + * ntfs_dump_attr_ea_information() + * + * dump the ea_information attribute + */ +static void ntfs_dump_attr_ea_information(ATTR_RECORD *attr) +{ + EA_INFORMATION *ea_info; + + ea_info = (EA_INFORMATION*)((u8*)attr + + le16_to_cpu(attr->value_offset)); + printf("\tPacked EA length:\t %u\n", le16_to_cpu(ea_info->ea_length)); + printf("\tNEED_EA count:\t\t %u\n", + le16_to_cpu(ea_info->need_ea_count)); + printf("\tUnpacked EA length:\t %u\n", + (unsigned)le32_to_cpu(ea_info->ea_query_length)); +} + +/** + * ntfs_dump_attr_ea() + * + * dump the ea attribute + */ +static void ntfs_dump_attr_ea(ATTR_RECORD *attr, ntfs_volume *vol) +{ + EA_ATTR *ea; + u8 *buf = NULL; + s64 data_size; + + if (attr->non_resident) { + runlist *rl; + + data_size = sle64_to_cpu(attr->data_size); + if (!opts.verbose) + return; + /* FIXME: We don't handle fragmented mapping pairs case. */ + rl = ntfs_mapping_pairs_decompress(vol, attr, 0); + if (rl) { + s64 bytes_read; + + buf = malloc(data_size); + if (!buf) { + ntfs_log_perror("malloc failed"); + free(rl); + return; + } + bytes_read = ntfs_rl_pread(vol, rl, 0, data_size, buf); + if (bytes_read != data_size) { + ntfs_log_perror("ntfs_rl_pread failed"); + free(buf); + free(rl); + return; + } + free(rl); + ea = (EA_ATTR*)buf; + } else { + ntfs_log_perror("ntfs_mapping_pairs_decompress failed"); + return; + } + } else { + data_size = le32_to_cpu(attr->value_length); + if (!opts.verbose) + return; + ea = (EA_ATTR*)((u8*)attr + le16_to_cpu(attr->value_offset)); + } + while (1) { + printf("\n\tFlags:\t\t "); + if (ea->flags) { + if (ea->flags == NEED_EA) + printf("NEED_EA\n"); + else + printf("Unknown (0x%02x)\n", ea->flags); + } else + printf("NONE\n"); + printf("\tName length:\t %d\n", ea->name_length); + printf("\tValue length:\t %d\n", + le16_to_cpu(ea->value_length)); + printf("\tName:\t\t '%s'\n", ea->name); + printf("\tValue:\t\t "); + if (ea->name_length == 11 && + !strncmp((const char*)"SETFILEBITS", + (const char*)ea->name, 11)) + printf("0%o\n", le32_to_cpu(*(le32*)(ea->value + + ea->name_length + 1))); + else + printf("'%s'\n", ea->value + ea->name_length + 1); + if (ea->next_entry_offset) + ea = (EA_ATTR*)((u8*)ea + + le32_to_cpu(ea->next_entry_offset)); + else + break; + if ((u8*)ea - buf >= data_size) + break; + } + free(buf); +} + +/** + * ntfs_dump_attr_property_set() + * + * dump the property_set attribute + */ +static void ntfs_dump_attr_property_set(ATTR_RECORD *attr __attribute__((unused))) +{ + /* TODO */ +} + +/** + * ntfs_dump_attr_logged_utility_stream() + * + * dump the property_set attribute + */ +static void ntfs_dump_attr_logged_utility_stream(ATTR_RECORD *attr __attribute__((unused))) +{ + /* TODO */ +} + +/** + * ntfs_hex_dump + */ +static void ntfs_hex_dump(void *buf,unsigned int length) +{ + unsigned int i=0; + while (i126)) { + c = '.'; + } + printf("%c",c); + } + + /* end line */ + printf("\n"); + i=j; + } +} + +/** + * ntfs_dump_attr_unknown + */ +static void ntfs_dump_attr_unknown(ATTR_RECORD *attr) +{ + printf("===== Please report this unknown attribute type to %s =====\n", + NTFS_DEV_LIST); + + if (!attr->non_resident) { + /* hex dump */ + printf("\tDumping some of the attribute data:\n"); + ntfs_hex_dump((u8*)attr + le16_to_cpu(attr->value_offset), + (le16_to_cpu(attr->value_length)>128)?128 + :le16_to_cpu(attr->value_length)); + } +} + +/** + * ntfs_dump_inode_general_info + */ +static void ntfs_dump_inode_general_info(ntfs_inode *inode) +{ + u16 inode_flags = inode->mrec->flags; + + printf("Dumping Inode #%llu\n",(long long)inode->mft_no); + + ntfs_dump_usa_lsn("", inode->mrec); + printf("MFT Record Seq. Number:\t %hu\n", + (short unsigned int)le16_to_cpu(inode->mrec->sequence_number)); + printf("Number of Hard Links:\t %hu\n", + le16_to_cpu(inode->mrec->link_count)); + printf("Attribute Offset:\t %hu\n", + le16_to_cpu(inode->mrec->attrs_offset)); + + printf("MFT Record Flags:\t "); + if (inode_flags) { + if (MFT_RECORD_IN_USE & inode_flags) { + printf("IN_USE "); + inode_flags &= ~MFT_RECORD_IN_USE; + } + if (MFT_RECORD_IS_DIRECTORY & inode_flags) { + printf("DIRECTORY "); + inode_flags &= ~MFT_RECORD_IS_DIRECTORY; + } + /* The meaning of IS_4 is illusive but not its existence. */ + if (MFT_RECORD_IS_4 & inode_flags) { + printf("IS_4 "); + inode_flags &= ~MFT_RECORD_IS_4; + } + if (MFT_RECORD_IS_VIEW_INDEX & inode_flags) { + printf("VIEW_INDEX "); + inode_flags &= ~MFT_RECORD_IS_VIEW_INDEX; + } + if (inode_flags) + printf("UNKNOWN: 0x%04hx", inode_flags); + } else { + printf("none"); + } + printf("\n"); + + printf("Bytes Used:\t\t %u bytes\n", + (unsigned int)le32_to_cpu(inode->mrec->bytes_in_use)); + printf("Bytes Allocated:\t %u bytes\n", + (unsigned int)le32_to_cpu(inode->mrec->bytes_allocated)); + + if (inode->mrec->base_mft_record) { + printf("Base MFT Record:\t %llu\n", + MREF_LE(inode->mrec->base_mft_record)); + } + printf("Next Attribute Instance: %hu\n", + le16_to_cpu(inode->mrec->next_attr_instance)); +} + +/** + * ntfs_get_file_attributes + */ +static void ntfs_dump_file_attributes(ntfs_inode *inode) +{ + ntfs_attr_search_ctx *ctx = NULL; + + /* then start enumerating attributes + see ntfs_attr_lookup documentation for detailed explanation */ + ctx = ntfs_attr_get_search_ctx(inode, NULL); + while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { + + if (ctx->attr->type == AT_END || ctx->attr->type == AT_UNUSED) { + printf("Weird: %s attribute type was found, please " + "report this.\n", + get_attribute_type_name(ctx->attr->type)); + continue; + } + + ntfs_dump_attribute_header(ctx->attr, inode->vol); + + switch (ctx->attr->type) { + case AT_STANDARD_INFORMATION: + ntfs_dump_attr_standard_information(ctx->attr); + break; + case AT_ATTRIBUTE_LIST: + ntfs_dump_attr_list(ctx->attr, inode->vol); + break; + case AT_FILE_NAME: + ntfs_dump_attr_file_name(ctx->attr); + break; + case AT_OBJECT_ID: + ntfs_dump_attr_object_id(ctx->attr, inode->vol); + break; + case AT_SECURITY_DESCRIPTOR: + ntfs_dump_attr_security_descriptor(ctx->attr, inode->vol); + break; + case AT_VOLUME_NAME: + ntfs_dump_attr_volume_name(ctx->attr); + break; + case AT_VOLUME_INFORMATION: + ntfs_dump_attr_volume_information(ctx->attr); + break; + case AT_DATA: + ntfs_dump_attr_data(ctx->attr, inode); + break; + case AT_INDEX_ROOT: + ntfs_dump_attr_index_root(ctx->attr, inode); + break; + case AT_INDEX_ALLOCATION: + ntfs_dump_attr_index_allocation(ctx->attr, inode); + break; + case AT_BITMAP: + ntfs_dump_attr_bitmap(ctx->attr); + break; + case AT_REPARSE_POINT: + ntfs_dump_attr_reparse_point(ctx->attr); + break; + case AT_EA_INFORMATION: + ntfs_dump_attr_ea_information(ctx->attr); + break; + case AT_EA: + ntfs_dump_attr_ea(ctx->attr, inode->vol); + break; + case AT_PROPERTY_SET: + ntfs_dump_attr_property_set(ctx->attr); + break; + case AT_LOGGED_UTILITY_STREAM: + ntfs_dump_attr_logged_utility_stream(ctx->attr); + break; + default: + ntfs_dump_attr_unknown(ctx->attr); + } + } + + /* if we exited the loop before we're done - notify the user */ + if (errno != ENOENT) { + ntfs_log_perror("ntfsinfo error: stopped before finished " + "enumerating attributes"); + } else { + printf("End of inode reached\n"); + } + + /* close all data-structures we used */ + ntfs_attr_put_search_ctx(ctx); + ntfs_inode_close(inode); + + /* happily exit */ +} + +/** + * main() - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char **argv) +{ + ntfs_volume *vol; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + if (!parse_options(argc, argv)) + return 1; + + utils_set_locale(); + + vol = utils_mount_volume(opts.device, MS_RDONLY, opts.force); + if (!vol) + return 1; + + /* + * if opts.mft is not 0, then we will print out information about + * the volume, such as the sector size and whatnot. + */ + if (opts.mft) + ntfs_dump_volume(vol); + + if ((opts.inode != -1) || opts.filename) { + ntfs_inode *inode; + /* obtain the inode */ + if (opts.filename) { + inode = ntfs_pathname_to_inode(vol, NULL, opts.filename); + } else { + inode = ntfs_inode_open(vol, MK_MREF(opts.inode, 0)); + } + + /* dump the inode information */ + if (inode) { + /* general info about the inode's mft record */ + ntfs_dump_inode_general_info(inode); + /* dump attributes */ + ntfs_dump_file_attributes(inode); + } else { + /* can't open inode */ + /* + * note: when the specified inode does not exist, either + * EIO or or ESPIPE is returned, we should notify better + * in those cases + */ + ntfs_log_perror("Error loading node"); + } + } + + ntfs_umount(vol, FALSE); + return 0; +} + diff --git a/ntfsprogs/ntfslabel.8.in b/ntfsprogs/ntfslabel.8.in new file mode 100644 index 00000000..adff411f --- /dev/null +++ b/ntfsprogs/ntfslabel.8.in @@ -0,0 +1,105 @@ +.\" Copyright (c) 2002\-2004 Anton Altaparmakov. +.\" Copyright (c) 2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSLABEL 8 "November 2005" "ntfsprogs @VERSION@" +.SH NAME +ntfslabel \- display/change the label on an ntfs file system +.SH SYNOPSIS +.B ntfslabel +[\fIoptions\fR] \fIdevice \fR[\fInew\-label\fR] +.SH DESCRIPTION +.B ntfslabel +will display or change the file system label on the ntfs file system located on +.IR device . +.PP +If the optional argument +.I new\-label +is not present, +.B ntfslabel +will simply display the current file system label. +.PP +If the optional argument +.I new\-label +is present, then +.B ntfslabel +will set the file system label to be +.IR new\-label . +NTFS file system labels can be at most 128 Unicode characters long; if +.I new\-label +is longer than 128 Unicode characters, +.B ntfslabel +will truncate it and print a warning message. +.PP +It is also possible to set the file system label using the +.B \-L +option of +.BR mkntfs (8) +during creation of the file system. +.SH OPTIONS +Below is a summary of all the options that +.B ntfslabel +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not working with a mounted +volume. Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-n\fR, \fB\-\-no\-action\fR +Don't actually write to disk. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Reduce the amount of output to a minimum. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Increase the amount of output that +.B ntfslabel +prints. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license for +.BR ntfslabel . +.SH BUGS +There are no known problems with +.BR ntfslabel . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +linux\-ntfs\-dev@lists.sourceforge.net +.hy +.SH AUTHORS +.B ntfslabel +was written by Matthew J. Fanto, with contributions from Anton Altaparmakov and +Richard Russon. +.SH AVAILABILITY +.B ntfslabel +is part of the +.B ntfsprogs +package and is available from: +.br +.nh +http://www.linux\-ntfs.org/content/view/19/37 +.hy +.sp +The manual pages are available online at: +.br +.nh +http://man.linux-ntfs.org/ +.hy +.SH SEE ALSO +.BR mkntfs (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfslabel.c b/ntfsprogs/ntfslabel.c new file mode 100644 index 00000000..c38662bf --- /dev/null +++ b/ntfsprogs/ntfslabel.c @@ -0,0 +1,410 @@ +/** + * ntfslabel - Part of the Linux-NTFS project. + * + * Copyright (c) 2002 Matthew J. Fanto + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2002-2003 Richard Russon + * + * This utility will display/change the label on an NTFS partition. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 "config.h" + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_LOCALE_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include +#include + +#include "utils.h" +/* #include "version.h" */ + +static const char *EXEC_NAME = "ntfslabel"; + +static struct options { + char *device; /* Device/File to work with */ + char *label; /* Set the label to this */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int force; /* Override common sense */ + int noaction; /* Do not write to disk */ +} opts; + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Display, or set, the label for an " + "NTFS Volume.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c)\n"); + ntfs_log_info(" 2002 Matthew J. Fanto\n"); + ntfs_log_info(" 2002-2005 Anton Altaparmakov\n"); + ntfs_log_info(" 2002-2003 Richard Russon\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device [label]\n" + " -n, --no-action Do not write to disk\n" + " -f, --force Use less caution\n" + " -q, --quiet Less output\n" + " -v, --verbose More output\n" + " -V, --version Display version information\n" + " -h, --help Display this help\n\n", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char *argv[]) +{ + static const char *sopt = "-fh?nqvV"; + static const struct option lopt[] = { + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "no-action", no_argument, NULL, 'n' }, + { "quiet", no_argument, NULL, 'q' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 }, + }; + + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + + opterr = 0; /* We'll handle the errors, thank you. */ + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!err && !opts.device) + opts.device = argv[optind-1]; + else if (!err && !opts.label) + opts.label = argv[optind-1]; + else + err++; + break; + case 'f': + opts.force++; + break; + case 'h': + case '?': + if (strncmp (argv[optind-1], "--log-", 6) == 0) { + if (!ntfs_log_parse_option (argv[optind-1])) + err++; + break; + } + help++; + break; + case 'n': + opts.noaction++; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + ver++; + break; + default: + ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if (opts.device == NULL) { + if (argc > 1) + ntfs_log_error("You must specify a device.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose at " + "the same time.\n"); + err++; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + return (!err && !help && !ver); +} + + +/** + * print_label - display the current label of a mounted ntfs partition. + * @dev: device to read the label from + * @mnt_flags: mount flags of the device or 0 if not mounted + * @mnt_point: mount point of the device or NULL + * + * Print the label of the device @dev. + */ +static int print_label(ntfs_volume *vol, unsigned long mnt_flags) +{ + int result = 0; + //XXX significant? + if ((mnt_flags & (NTFS_MF_MOUNTED | NTFS_MF_READONLY)) == + NTFS_MF_MOUNTED) { + ntfs_log_error("%s is mounted read-write, results may be " + "unreliable.\n", opts.device); + result = 1; + } + + ntfs_log_info("%s\n", vol->vol_name); + return result; +} + +/** + * resize_resident_attribute_value - resize a resident attribute + * @m: mft record containing attribute to resize + * @a: attribute record (inside @m) which to resize + * @new_vsize: the new attribute value size to resize the attribute to + * + * Return 0 on success and -1 with errno = ENOSPC if not enough space in the + * mft record. + */ +static int resize_resident_attribute_value(MFT_RECORD *m, ATTR_RECORD *a, + const u32 new_vsize) +{ + int new_alen, new_muse; + + /* New attribute length and mft record bytes used. */ + new_alen = (le16_to_cpu(a->value_offset) + new_vsize + 7) & ~7; + new_muse = le32_to_cpu(m->bytes_in_use) - le32_to_cpu(a->length) + + new_alen; + /* Check for sufficient space. */ + if ((u32)new_muse > le32_to_cpu(m->bytes_allocated)) { + errno = ENOSPC; + return -1; + } + /* Move attributes behind @a to their new location. */ + memmove((char*)a + new_alen, (char*)a + le32_to_cpu(a->length), + le32_to_cpu(m->bytes_in_use) - ((char*)a - (char*)m) - + le32_to_cpu(a->length)); + /* Adjust @m to reflect change in used space. */ + m->bytes_in_use = cpu_to_le32(new_muse); + /* Adjust @a to reflect new value size. */ + a->length = cpu_to_le32(new_alen); + a->value_length = cpu_to_le32(new_vsize); + return 0; +} + +/** + * change_label - change the current label on a device + * @dev: device to change the label on + * @mnt_flags: mount flags of the device or 0 if not mounted + * @mnt_point: mount point of the device or NULL + * @label: the new label + * + * Change the label on the device @dev to @label. + */ +static int change_label(ntfs_volume *vol, unsigned long mnt_flags, char *label, BOOL force) +{ + ntfs_attr_search_ctx *ctx; + ntfschar *new_label = NULL; + ATTR_RECORD *a; + int label_len; + int result = 0; + + //XXX significant? + if (mnt_flags & NTFS_MF_MOUNTED) { + /* If not the root fs or mounted read/write, refuse change. */ + if (!(mnt_flags & NTFS_MF_ISROOT) || + !(mnt_flags & NTFS_MF_READONLY)) { + if (!force) { + ntfs_log_error("Refusing to change label on " + "read-%s mounted device %s.\n", + mnt_flags & NTFS_MF_READONLY ? + "only" : "write", opts.device); + return 1; + } + } + } + ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); + if (!ctx) { + ntfs_log_perror("Failed to get attribute search context"); + goto err_out; + } + if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, + ctx)) { + if (errno != ENOENT) { + ntfs_log_perror("Lookup of $VOLUME_NAME attribute failed"); + goto err_out; + } + /* The volume name attribute does not exist. Need to add it. */ + a = NULL; + } else { + a = ctx->attr; + if (a->non_resident) { + ntfs_log_error("Error: Attribute $VOLUME_NAME must be " + "resident.\n"); + goto err_out; + } + } + label_len = ntfs_mbstoucs_libntfscompat(label, &new_label, 0); + if (label_len == -1) { + ntfs_log_perror("Unable to convert label string to Unicode"); + goto err_out; + } + label_len *= sizeof(ntfschar); + if (label_len > 0x100) { + ntfs_log_error("New label is too long. Maximum %u characters " + "allowed. Truncating excess characters.\n", + (unsigned)(0x100 / sizeof(ntfschar))); + label_len = 0x100; + new_label[label_len / sizeof(ntfschar)] = cpu_to_le16(L'\0'); + } + if (a) { + if (resize_resident_attribute_value(ctx->mrec, a, label_len)) { + ntfs_log_perror("Error resizing resident attribute"); + goto err_out; + } + } else { + /* sizeof(resident attribute record header) == 24 */ + int asize = (24 + label_len + 7) & ~7; + u32 biu = le32_to_cpu(ctx->mrec->bytes_in_use); + if (biu + asize > le32_to_cpu(ctx->mrec->bytes_allocated)) { + errno = ENOSPC; + ntfs_log_perror("Error adding resident attribute"); + goto err_out; + } + a = ctx->attr; + memmove((u8*)a + asize, a, biu - ((u8*)a - (u8*)ctx->mrec)); + ctx->mrec->bytes_in_use = cpu_to_le32(biu + asize); + a->type = AT_VOLUME_NAME; + a->length = cpu_to_le32(asize); + a->non_resident = 0; + a->name_length = 0; + a->name_offset = cpu_to_le16(24); + a->flags = cpu_to_le16(0); + a->instance = ctx->mrec->next_attr_instance; + ctx->mrec->next_attr_instance = cpu_to_le16((le16_to_cpu( + ctx->mrec->next_attr_instance) + 1) & 0xffff); + a->value_length = cpu_to_le32(label_len); + a->value_offset = a->name_offset; + a->resident_flags = 0; + a->reservedR = 0; + } + memcpy((u8*)a + le16_to_cpu(a->value_offset), new_label, label_len); + if (!opts.noaction && ntfs_inode_sync(vol->vol_ni)) { + ntfs_log_perror("Error writing MFT Record to disk"); + goto err_out; + } + result = 0; +err_out: + free(new_label); + return result; +} + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char **argv) +{ + unsigned long mnt_flags = 0; + int result = 0; + ntfs_volume *vol; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + if (!parse_options(argc, argv)) + return 1; + + utils_set_locale(); + + if (!opts.label) + opts.noaction++; + + vol = utils_mount_volume(opts.device, opts.noaction ? MS_RDONLY : 0, + opts.force); + if (!vol) + return 1; + + if (opts.label) + result = change_label(vol, mnt_flags, opts.label, opts.force); + else + result = print_label(vol, mnt_flags); + + ntfs_umount(vol, FALSE); + return result; +} + diff --git a/ntfsprogs/ntfsls.8.in b/ntfsprogs/ntfsls.8.in new file mode 100644 index 00000000..6be0f0de --- /dev/null +++ b/ntfsprogs/ntfsls.8.in @@ -0,0 +1,177 @@ +.\" Copyright (c) 2003 Anton Altaparmakov. +.\" Copyright (c) 2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSLS 8 "November 2005" "ntfsprogs @VERSION@" +.SH NAME +ntfsls \- list directory contents on an NTFS filesystem +.SH SYNOPSIS +.B ntfsls +[\fIoptions\fR] \fIdevice\fR +.sp +.B ntfsls +[ +.B \-a +| +.B \-\-all +] +[ +.B \-F +| +.B \-\-classify +] +[ +.B \-f +| +.B \-\-force +] +[ +.B \-h +| +.B \-\-help +] +[ +.B \-i +| +.B \-\-inode +] +[ +.B \-l +| +.B \-\-long +] +[ +.B \-p +| +.B \-\-path +.I PATH +] +[ +.B \-q +| +.B \-\-quiet +] +[ +.B \-s +| +.B \-\-system +] +[ +.B \-V +| +.B \-\-version +] +[ +.B \-v +| +.B \-\-verbose +] +[ +.B \-x +| +.B \-\-dos +] +.I device +.SH DESCRIPTION +.B ntfsls +is used to list information about the files specified by the +.I PATH +option (the root directory by default). +.I DEVICE +is the special file corresponding to the device (e.g +.IR /dev/hdXX ) +or an NTFS image file. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsls +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-a\fR, \fB\-\-all\fR +Display all files. If this option is not specified file names in the POSIX +namespace will not be displayed. +.TP +\fB\-F\fR, \fB\-\-classify\fR +Append indicator (one of */=@|) to entries. +.TP +\fB\-f\fR, \fB\-\-force\fR +Force execution. For example necessary to run on an NTFS partition stored in +a normal file. +.TP +\fB\-h\fR, \fB\-\-help\fR +Print the usage information of +.B ntfsls +and exit. +.TP +\fB\-i\fR, \fB\-\-inode\fR +Print inode number of each file. This is the MFT reference number in NTFS +terminology. +.TP +\fB\-l\fR, \fB\-\-long\fR +Use a long listing format. +.TP +\fB\-p\fR, \fB\-\-path\fR PATH +The directory whose contents to list or the file (including the path) about +which to display information. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-R\fR, \fB\-\-recursive\fR +Show the contents of all directories beneath the specified directory. +.TP +\fB\-s\fR, \fB\-\-system\fR +Unless this options is specified, all files beginning with a dollar sign +character will not be listed as these files are usually system files. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Print the version number of +.B ntfsls +and exit. +.TP +\fB\-x\fR, \fB\-\-dos\fR +Display short file names, i.e. files in the DOS namespace, instead of long +file names, i.e. files in the WIN32 namespace. +.SH BUGS +There are no known problems with +.BR ntfsls . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +linux\-ntfs\-dev@lists.sourceforge.net +.hy +.SH AUTHORS +This version of +.B ntfsls +was written by Lode Leroy, Anton Altaparmakov, Richard Russon, Carmelo Kintana +and Giang Nguyen. +.SH AVAILABILITY +.B ntfsls +is part of the +.B ntfsprogs +package and is available from: +.br +.nh +http://www.linux\-ntfs.org/content/view/19/37 +.hy +.sp +The manual pages are available online at: +.br +.nh +http://man.linux-ntfs.org/ +.hy +.SH SEE ALSO +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsls.c b/ntfsprogs/ntfsls.c new file mode 100644 index 00000000..4a458992 --- /dev/null +++ b/ntfsprogs/ntfsls.c @@ -0,0 +1,717 @@ +/** + * ntfsls - Part of the Linux-NTFS project. + * + * Copyright (c) 2003 Lode Leroy + * Copyright (c) 2003-2005 Anton Altaparmakov + * Copyright (c) 2003 Richard Russon + * Copyright (c) 2004 Carmelo Kintana + * Copyright (c) 2004 Giang Nguyen + * + * This utility will list a directory's files. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 "config.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_TIME_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "list.h" +/* #include "version.h" */ + +static const char *EXEC_NAME = "ntfsls"; + +/** + * To hold sub-directory information for recursive listing. + * @depth: the level of this dir relative to opts.path + */ +struct dir { + struct list_head list; + ntfs_inode *ni; + char name[MAX_PATH]; + int depth; +}; + +/** + * path_component - to store path component strings + * + * @name: string pointer + * + * NOTE: @name is not directly allocated memory. It simply points to the + * character array name in struct dir. + */ +struct path_component { + struct list_head list; + const char *name; +}; + +/* The list of sub-dirs is like a "horizontal" tree. The root of + * the tree is opts.path, but it is not part of the list because + * that's not necessary. The rules of the list are (in order of + * precedence): + * 1. directories immediately follow their parent. + * 2. siblings are next to one another. + * + * For example, if: + * 1. opts.path is / + * 2. / has 2 sub-dirs: dir1 and dir2 + * 3. dir1 has 2 sub-dirs: dir11 and dir12 + * 4. dir2 has 0 sub-dirs + * then the list will be: + * dummy head -> dir1 -> dir11 -> dir12 -> dir2 + * + * dir_list_insert_pos keeps track of where to insert a sub-dir + * into the list. + */ +static struct list_head *dir_list_insert_pos = NULL; + +/* The global depth relative to opts.path. + * ie: opts.path has depth 0, a sub-dir of opts.path has depth 1 + */ +static int depth = 0; + +static struct options { + char *device; /* Device/File to work with */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int force; /* Override common sense */ + int all; + int system; + int dos; + int lng; + int inode; + int classify; + int recursive; + const char *path; +} opts; + +typedef struct { + ntfs_volume *vol; +} ntfsls_dirent; + +static int list_dir_entry(ntfsls_dirent * dirent, const ntfschar * name, + const int name_len, const int name_type, + const s64 pos, const MFT_REF mref, + const unsigned dt_type); + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + printf("\n%s v%s (libntfs-3g) - Display information about an NTFS " + "Volume.\n\n", EXEC_NAME, VERSION); + printf("Copyright (c) 2003 Lode Leroy\n"); + printf("Copyright (c) 2003-2005 Anton Altaparmakov\n"); + printf("Copyright (c) 2003 Richard Russon\n"); + printf("Copyright (c) 2004 Carmelo Kintana\n"); + printf("Copyright (c) 2004 Giang Nguyen\n"); + printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + printf("\nUsage: %s [options] device\n" + "\n" + " -a, --all Display all files\n" + " -F, --classify Display classification\n" + " -f, --force Use less caution\n" + " -h, --help Display this help\n" + " -i, --inode Display inode numbers\n" + " -l, --long Display long info\n" + " -p, --path PATH Directory whose contents to list\n" + " -q, --quiet Less output\n" + " -R, --recursive Recursively list subdirectories\n" + " -s, --system Display system files\n" + " -V, --version Display version information\n" + " -v, --verbose More output\n" + " -x, --dos Use short (DOS 8.3) names\n" + "\n", + EXEC_NAME); + + printf("NOTE: If neither -a nor -s is specified, the program defaults to -a.\n\n"); + + printf("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char *argv[]) +{ + static const char *sopt = "-aFfh?ilp:qRsVvx"; + static const struct option lopt[] = { + { "all", no_argument, NULL, 'a' }, + { "classify", no_argument, NULL, 'F' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "inode", no_argument, NULL, 'i' }, + { "long", no_argument, NULL, 'l' }, + { "path", required_argument, NULL, 'p' }, + { "recursive", no_argument, NULL, 'R' }, + { "quiet", no_argument, NULL, 'q' }, + { "system", no_argument, NULL, 's' }, + { "version", no_argument, NULL, 'V' }, + { "verbose", no_argument, NULL, 'v' }, + { "dos", no_argument, NULL, 'x' }, + { NULL, 0, NULL, 0 }, + }; + + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + + opterr = 0; /* We'll handle the errors, thank you. */ + + memset(&opts, 0, sizeof(opts)); + opts.device = NULL; + opts.path = "/"; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: + if (!opts.device) + opts.device = optarg; + else + err++; + break; + case 'p': + opts.path = optarg; + break; + case 'f': + opts.force++; + break; + case 'h': + case '?': + if (strncmp (argv[optind-1], "--log-", 6) == 0) { + if (!ntfs_log_parse_option (argv[optind-1])) + err++; + break; + } + help++; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + ver++; + break; + case 'x': + opts.dos = 1; + break; + case 'l': + opts.lng++; + break; + case 'i': + opts.inode++; + break; + case 'F': + opts.classify++; + break; + case 'a': + opts.all++; + break; + case 's': + opts.system++; + break; + case 'R': + opts.recursive++; + break; + default: + ntfs_log_error("Unknown option '%s'.\n", argv[optind - 1]); + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + /* defaults to -a if -s is not specified */ + if (!opts.system) + opts.all++; + + if (help || ver) + opts.quiet = 0; + else { + if (opts.device == NULL) { + if (argc > 1) + ntfs_log_error("You must specify exactly one " + "device.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose at the " + "same time.\n"); + err++; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + return (!err && !help && !ver); +} + +/** + * free_dir - free one dir + * @tofree: the dir to free + * + * Close the inode and then free the dir + */ +static void free_dir(struct dir *tofree) +{ + if (tofree) { + if (tofree->ni) { + ntfs_inode_close(tofree->ni); + tofree->ni = NULL; + } + free(tofree); + } +} + +/** + * free_dirs - walk the list of dir's and free each of them + * @dir_list: the list_head of any entry in the list + * + * Iterate over @dir_list, calling free_dir on each entry + */ +static void free_dirs(struct list_head *dir_list) +{ + struct dir *tofree = NULL; + struct list_head *walker = NULL; + + if (dir_list) { + list_for_each(walker, dir_list) { + free_dir(tofree); + tofree = list_entry(walker, struct dir, list); + } + + free_dir(tofree); + } +} + +/** + * readdir_recursive - list a directory and sub-directories encountered + * @ni: ntfs inode of the directory to list + * @pos: current position in directory + * @dirent: context for filldir callback supplied by the caller + * + * For each directory, print its path relative to opts.path. List a directory, + * then list each of its sub-directories. + * + * Returns 0 on success or -1 on error. + * + * NOTE: Assumes recursive option. Currently no limit on the depths of + * recursion. + */ +static int readdir_recursive(ntfs_inode * ni, s64 * pos, ntfsls_dirent * dirent) +{ + /* list of dirs to "ls" recursively */ + static struct dir dirs = { + .list = LIST_HEAD_INIT(dirs.list), + .ni = NULL, + .name = {0}, + .depth = 0 + }; + + static struct path_component paths = { + .list = LIST_HEAD_INIT(paths.list), + .name = NULL + }; + + static struct path_component base_comp; + + struct dir *subdir = NULL; + struct dir *tofree = NULL; + struct path_component comp; + struct path_component *tempcomp = NULL; + struct list_head *dir_walker = NULL; + struct list_head *comp_walker = NULL; + s64 pos2 = 0; + int ni_depth = depth; + int result = 0; + + if (list_empty(&dirs.list)) { + base_comp.name = opts.path; + list_add(&base_comp.list, &paths.list); + dir_list_insert_pos = &dirs.list; + printf("%s:\n", opts.path); + } + + depth++; + + result = ntfs_readdir(ni, pos, dirent, (ntfs_filldir_t) list_dir_entry); + + if (result == 0) { + list_add_tail(&comp.list, &paths.list); + + /* for each of ni's sub-dirs: list in this iteration, then + free at the top of the next iteration or outside of loop */ + list_for_each(dir_walker, &dirs.list) { + if (tofree) { + free_dir(tofree); + tofree = NULL; + } + subdir = list_entry(dir_walker, struct dir, list); + + /* subdir is not a subdir of ni */ + if (subdir->depth != ni_depth + 1) + break; + + pos2 = 0; + dir_list_insert_pos = &dirs.list; + if (!subdir->ni) { + subdir->ni = + ntfs_pathname_to_inode(ni->vol, ni, + subdir->name); + + if (!subdir->ni) { + ntfs_log_error + ("ntfsls::readdir_recursive(): cannot get inode from pathname.\n"); + result = -1; + break; + } + } + puts(""); + + comp.name = subdir->name; + + /* print relative path header */ + list_for_each(comp_walker, &paths.list) { + tempcomp = + list_entry(comp_walker, + struct path_component, list); + printf("%s", tempcomp->name); + if (tempcomp != &comp + && *tempcomp->name != PATH_SEP + && (!opts.classify + || tempcomp == &base_comp)) + putchar(PATH_SEP); + } + puts(":"); + + result = readdir_recursive(subdir->ni, &pos2, dirent); + + if (result) + break; + + tofree = subdir; + list_del(dir_walker); + } + + list_del(&comp.list); + } + + if (tofree) + free_dir(tofree); + + /* if at the outer-most readdir_recursive, then clean up */ + if (ni_depth == 0) { + free_dirs(&dirs.list); + } + + depth--; + + return result; +} + +/** + * list_dir_entry + * + * FIXME: Should we print errors as we go along? (AIA) + */ +static int list_dir_entry(ntfsls_dirent * dirent, const ntfschar * name, + const int name_len, const int name_type, + const s64 pos __attribute__((unused)), + const MFT_REF mref, const unsigned dt_type) +{ + char *filename = NULL; + int result = 0; + + struct dir *dir = NULL; + + filename = calloc(1, MAX_PATH); + if (!filename) + return -1; + + if (ntfs_ucstombs(name, name_len, &filename, MAX_PATH) < 0) { + ntfs_log_error("Cannot represent filename in current locale.\n"); + goto free; + } + + result = 0; // These are successful + if ((MREF(mref) < FILE_first_user) && (!opts.system)) + goto free; + if (name_type == FILE_NAME_POSIX && !opts.all) + goto free; + if (((name_type & FILE_NAME_WIN32_AND_DOS) == FILE_NAME_WIN32) && + opts.dos) + goto free; + if (((name_type & FILE_NAME_WIN32_AND_DOS) == FILE_NAME_DOS) && + !opts.dos) + goto free; + if (dt_type == NTFS_DT_DIR && opts.classify) + sprintf(filename + strlen(filename), "/"); + + if (dt_type == NTFS_DT_DIR && opts.recursive + && strcmp(filename, ".") && strcmp(filename, "./") + && strcmp(filename, "..") && strcmp(filename, "../")) + { + dir = (struct dir *)calloc(1, sizeof(struct dir)); + + if (!dir) { + ntfs_log_error("Failed to allocate for subdir.\n"); + result = -1; + goto free; + } + + strcpy(dir->name, filename); + dir->ni = NULL; + dir->depth = depth; + } + + if (!opts.lng) { + if (!opts.inode) + printf("%s\n", filename); + else + printf("%7llu %s\n", (unsigned long long)MREF(mref), + filename); + result = 0; + } else { + s64 filesize = 0; + ntfs_inode *ni; + ntfs_attr_search_ctx *ctx = NULL; + FILE_NAME_ATTR *file_name_attr; + ATTR_RECORD *attr; + struct timespec ntfs_time; + char t_buf[26]; + + result = -1; // Everything else is bad + + ni = ntfs_inode_open(dirent->vol, mref); + if (!ni) + goto release; + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + goto release; + + if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, + 0, ctx)) + goto release; + attr = ctx->attr; + + file_name_attr = (FILE_NAME_ATTR *)((char *)attr + + le16_to_cpu(attr->value_offset)); + if (!file_name_attr) + goto release; + + ntfs_time = ntfs2timespec(file_name_attr->last_data_change_time); + strcpy(t_buf, ctime(&ntfs_time.tv_sec)); + memmove(t_buf+16, t_buf+19, 5); + t_buf[21] = '\0'; + + if (dt_type != NTFS_DT_DIR) { + if (!ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, + NULL, 0, ctx)) + filesize = ntfs_get_attribute_value_length( + ctx->attr); + } + + if (opts.inode) + printf("%7llu %8lld %s %s\n", + (unsigned long long)MREF(mref), + (long long)filesize, t_buf + 4, + filename); + else + printf("%8lld %s %s\n", (long long)filesize, t_buf + 4, + filename); + + if (dir) { + dir->ni = ni; + ni = NULL; /* so release does not close inode */ + } + + result = 0; +release: + /* Release attribute search context and close the inode. */ + if (ctx) + ntfs_attr_put_search_ctx(ctx); + if (ni) + ntfs_inode_close(ni); + } + + if (dir) { + if (result == 0) { + list_add(&dir->list, dir_list_insert_pos); + dir_list_insert_pos = &dir->list; + } else { + free(dir); + dir = NULL; + } + } + +free: + free(filename); + return result; +} + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, parsing mount options failed + * 2 Error, mount attempt failed + * 3 Error, failed to open root directory + * 4 Error, failed to open directory in search path + */ +int main(int argc, char **argv) +{ + s64 pos; + ntfs_volume *vol; + ntfs_inode *ni; + ntfsls_dirent dirent; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + if (!parse_options(argc, argv)) { + // FIXME: Print error... (AIA) + return 1; + } + + utils_set_locale(); + + vol = utils_mount_volume(opts.device, MS_RDONLY, opts.force); + if (!vol) { + // FIXME: Print error... (AIA) + return 2; + } + + ni = ntfs_pathname_to_inode(vol, NULL, opts.path); + if (!ni) { + // FIXME: Print error... (AIA) + ntfs_umount(vol, FALSE); + return 3; + } + + /* + * We now are at the final path component. If it is a file just + * list it. If it is a directory, list its contents. + */ + pos = 0; + memset(&dirent, 0, sizeof(dirent)); + dirent.vol = vol; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + if (opts.recursive) + readdir_recursive(ni, &pos, &dirent); + else + ntfs_readdir(ni, &pos, &dirent, + (ntfs_filldir_t) list_dir_entry); + // FIXME: error checking... (AIA) + } else { + ATTR_RECORD *rec; + FILE_NAME_ATTR *attr; + ntfs_attr_search_ctx *ctx; + int space = 4; + ntfschar *name = NULL; + int name_len = 0;; + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + + while ((rec = find_attribute(AT_FILE_NAME, ctx))) { + /* We know this will always be resident. */ + attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->value_offset)); + + if (attr->file_name_type < space) { + name = attr->file_name; + name_len = attr->file_name_length; + space = attr->file_name_type; + } + } + + list_dir_entry(&dirent, name, name_len, space, pos, ni->mft_no, + NTFS_DT_REG); + // FIXME: error checking... (AIA) + + ntfs_attr_put_search_ctx(ctx); + } + + /* Finished with the inode; release it. */ + ntfs_inode_close(ni); + + ntfs_umount(vol, FALSE); + return 0; +} + diff --git a/ntfsprogs/ntfsmftalloc.c b/ntfsprogs/ntfsmftalloc.c new file mode 100644 index 00000000..951097ac --- /dev/null +++ b/ntfsprogs/ntfsmftalloc.c @@ -0,0 +1,369 @@ +/** + * ntfsmftalloc - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * + * This utility will allocate and initialize an mft record. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS source + * in the file COPYING); if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_STDLIB_H +# include +#endif +#ifdef HAVE_STDIO_H +# include +#endif +#ifdef HAVE_STDARG_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_ERRNO_H +# include +#endif +#ifdef HAVE_TIME_H +#include +#endif +#ifdef HAVE_GETOPT_H +# include +#else + extern int optind; +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifndef LLONG_MAX +# define LLONG_MAX 9223372036854775807LL +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +/* #include "version.h" */ + +static const char *EXEC_NAME = "ntfsmftalloc"; + +/* Need these global so ntfsmftalloc_exit can access them. */ +static BOOL success = FALSE; + +static char *dev_name; + +static ntfs_volume *vol; +static ntfs_inode *ni = NULL; +static ntfs_inode *base_ni = NULL; +static s64 base_mft_no = -1; + +static struct { + /* -h, print usage and exit. */ + int no_action; /* -n, do not write to device, only display + what would be done. */ + int quiet; /* -q, quiet execution. */ + int verbose; /* -v, verbose execution, given twice, really + verbose execution (debug mode). */ + int force; /* -f, force allocation. */ + /* -V, print version and exit. */ +} opts; + +/** + * err_exit - error output and terminate; ignores quiet (-q) + */ +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static void err_exit(const char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "ERROR: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "Aborting...\n"); + exit(1); +} + +/** + * copyright - print copyright statements + */ +static void copyright(void) +{ + ntfs_log_info("Copyright (c) 2004-2005 Anton Altaparmakov\n" + "Allocate and initialize a base or an extent mft " + "record. If a base mft record\nis not specified, a " + "base mft record is allocated and initialized. " + "Otherwise,\nan extent mft record is allocated and " + "initialized to point to the specified\nbase mft " + "record.\n"); +} + +/** + * license - print license statement + */ +static void license(void) +{ + ntfs_log_info("%s", ntfs_gpl); +} + +/** + * usage - print a list of the parameters to the program + */ +__attribute__((noreturn)) +static void usage(void) +{ + copyright(); + ntfs_log_info("Usage: %s [options] device [base-mft-record]\n" + " -n Do not write to disk\n" + " -f Force execution despite errors\n" + " -q Quiet execution\n" + " -v Verbose execution\n" + " -vv Very verbose execution\n" + " -V Display version information\n" + " -l Display licensing information\n" + " -h Display this help\n", EXEC_NAME); + ntfs_log_info("%s%s", ntfs_bugs, ntfs_home); + exit(1); +} + +/** + * parse_options + */ +static void parse_options(int argc, char *argv[]) +{ + long long ll; + char *s; + int c; + + if (argc && *argv) + EXEC_NAME = *argv; + ntfs_log_info("%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION); + while ((c = getopt(argc, argv, "fh?nqvVl")) != EOF) { + switch (c) { + case 'f': + opts.force = 1; + break; + case 'n': + opts.no_action = 1; + break; + case 'q': + opts.quiet = 1; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + /* Version number already printed, so just exit. */ + exit(0); + case 'l': + copyright(); + license(); + exit(0); + case 'h': + case '?': + default: + usage(); + } + } + + if (opts.verbose > 1) + ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | + NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_QUIET); + + if (optind == argc) + usage(); + /* Get the device. */ + dev_name = argv[optind++]; + ntfs_log_verbose("device name = %s\n", dev_name); + if (optind != argc) { + /* Get the base mft record number. */ + ll = strtoll(argv[optind++], &s, 0); + if (*s || !ll || (ll >= LLONG_MAX && errno == ERANGE)) + err_exit("Invalid base mft record number: %s\n", + argv[optind - 1]); + base_mft_no = ll; + ntfs_log_verbose("base mft record number = 0x%llx\n", (long long)ll); + } + if (optind != argc) + usage(); +} + +/** + * dump_mft_record + */ +static void dump_mft_record(MFT_RECORD *m) +{ + ATTR_RECORD *a; + unsigned int u; + MFT_REF r; + + ntfs_log_info("-- Beginning dump of mft record. --\n"); + u = le32_to_cpu(m->magic); + ntfs_log_info("Mft record signature (magic) = %c%c%c%c\n", u & 0xff, + u >> 8 & 0xff, u >> 16 & 0xff, u >> 24 & 0xff); + u = le16_to_cpu(m->usa_ofs); + ntfs_log_info("Update sequence array offset = %u (0x%x)\n", u, u); + ntfs_log_info("Update sequence array size = %u\n", le16_to_cpu(m->usa_count)); + ntfs_log_info("$LogFile sequence number (lsn) = %llu\n", + (unsigned long long)le64_to_cpu(m->lsn)); + ntfs_log_info("Sequence number = %u\n", le16_to_cpu(m->sequence_number)); + ntfs_log_info("Reference (hard link) count = %u\n", + le16_to_cpu(m->link_count)); + u = le16_to_cpu(m->attrs_offset); + ntfs_log_info("First attribute offset = %u (0x%x)\n", u, u); + ntfs_log_info("Flags = %u: ", le16_to_cpu(m->flags)); + if (m->flags & MFT_RECORD_IN_USE) + ntfs_log_info("MFT_RECORD_IN_USE"); + else + ntfs_log_info("MFT_RECORD_NOT_IN_USE"); + if (m->flags & MFT_RECORD_IS_DIRECTORY) + ntfs_log_info(" | MFT_RECORD_IS_DIRECTORY"); + ntfs_log_info("\n"); + u = le32_to_cpu(m->bytes_in_use); + ntfs_log_info("Bytes in use = %u (0x%x)\n", u, u); + u = le32_to_cpu(m->bytes_allocated); + ntfs_log_info("Bytes allocated = %u (0x%x)\n", u, u); + r = le64_to_cpu(m->base_mft_record); + ntfs_log_info("Base mft record reference:\n\tMft record number = %llu\n\t" + "Sequence number = %u\n", + (unsigned long long)MREF(r), MSEQNO(r)); + ntfs_log_info("Next attribute instance = %u\n", + le16_to_cpu(m->next_attr_instance)); + a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); + ntfs_log_info("-- Beginning dump of attributes within mft record. --\n"); + while ((char*)a < (char*)m + le32_to_cpu(m->bytes_in_use)) { + if (a->type == AT_END) + break; + a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); + }; + ntfs_log_info("-- End of attributes. --\n"); +} + +/** + * ntfsmftalloc_exit + */ +static void ntfsmftalloc_exit(void) +{ + if (success) + return; + /* If there is a base inode, close that instead of the extent inode. */ + if (base_ni) + ni = base_ni; + /* Close the inode. */ + if (ni && ntfs_inode_close(ni)) { + ntfs_log_perror("Warning: Failed to close inode 0x%llx", + (long long)ni->mft_no); + } + /* Unmount the volume. */ + if (ntfs_umount(vol, 0) == -1) + ntfs_log_perror("Warning: Could not umount %s", dev_name); +} + +/** + * main + */ +int main(int argc, char **argv) +{ + unsigned long mnt_flags, ul; + int err; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + /* Initialize opts to zero / required values. */ + memset(&opts, 0, sizeof(opts)); + /* Parse command line options. */ + parse_options(argc, argv); + utils_set_locale(); + /* Make sure the file system is not mounted. */ + if (ntfs_check_if_mounted(dev_name, &mnt_flags)) + ntfs_log_error("Failed to determine whether %s is mounted: %s\n", + dev_name, strerror(errno)); + else if (mnt_flags & NTFS_MF_MOUNTED) { + ntfs_log_error("%s is mounted.\n", dev_name); + if (!opts.force) + err_exit("Refusing to run!\n"); + ntfs_log_error("ntfsmftalloc forced anyway. Hope /etc/mtab " + "is incorrect.\n"); + } + /* Mount the device. */ + if (opts.no_action) { + ntfs_log_quiet("Running in READ-ONLY mode!\n"); + ul = MS_RDONLY; + } else + ul = 0; + vol = ntfs_mount(dev_name, ul); + if (!vol) + err_exit("Failed to mount %s: %s\n", dev_name, strerror(errno)); + /* Register our exit function which will unlock and close the device. */ + err = atexit(&ntfsmftalloc_exit); + if (err == -1) { + ntfs_log_error("Could not set up exit() function because atexit() " + "failed: %s Aborting...\n", strerror(errno)); + ntfsmftalloc_exit(); + exit(1); + } + if (base_mft_no != -1) { + base_ni = ntfs_inode_open(vol, base_mft_no); + if (!base_ni) + err_exit("Failed to open base inode 0x%llx: %s\n", + (long long)base_mft_no, + strerror(errno)); + } + /* Open the specified inode. */ + ni = ntfs_mft_record_alloc(vol, base_ni); + if (!ni) + err_exit("Failed to allocate mft record: %s\n", + strerror(errno)); + ntfs_log_info("Allocated %s mft record 0x%llx", base_ni ? "extent" : "base", + (long long)ni->mft_no); + if (base_ni) + ntfs_log_info(" with base mft record 0x%llx", + (long long)base_mft_no); + ntfs_log_info(".\n"); + if (!opts.quiet && opts.verbose > 1) { + ntfs_log_verbose("Dumping allocated mft record 0x%llx:\n", + (long long)ni->mft_no); + dump_mft_record(ni->mrec); + } + /* Close the (base) inode. */ + if (base_ni) + ni = base_ni; + err = ntfs_inode_close(ni); + if (err) + err_exit("Failed to close inode 0x%llx: %s\n", + (long long)ni->mft_no, strerror(errno)); + /* Unmount the volume. */ + err = ntfs_umount(vol, 0); + /* Disable our ntfsmftalloc_exit() handler. */ + success = TRUE; + if (err == -1) + ntfs_log_perror("Warning: Failed to umount %s", dev_name); + else + ntfs_log_quiet("ntfsmftalloc completed successfully.\n"); + return 0; +} diff --git a/ntfsprogs/ntfsmove.c b/ntfsprogs/ntfsmove.c new file mode 100644 index 00000000..e5459d19 --- /dev/null +++ b/ntfsprogs/ntfsmove.c @@ -0,0 +1,909 @@ +/** + * ntfsmove - Part of the Linux-NTFS project. + * + * Copyright (c) 2003 Richard Russon + * Copyright (c) 2003-2005 Anton Altaparmakov + * + * This utility will move files on an NTFS volume. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 "config.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "ntfsmove.h" +/* #include "version.h" */ + +static const char *EXEC_NAME = "ntfsmove"; +static struct options opts; + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Move files and directories on an " + "NTFS volume.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c) 2003 Richard Russon\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device file\n" + "\n" + " -S --start Move to the start of the volume\n" + " -B --best Move to the best place on the volume\n" + " -E --end Move to the end of the volume\n" + " -C num --cluster num Move to this cluster offset\n" + "\n" + " -D --no-dirty Do not mark volume dirty (require chkdsk)\n" + " -n --no-action Do not write to disk\n" + " -f --force Use less caution\n" + " -h --help Print this help\n" + " -q --quiet Less output\n" + " -V --version Version information\n" + " -v --verbose More output\n\n", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char **argv) +{ + static const char *sopt = "-BC:DEfh?nqSVv"; + static const struct option lopt[] = { + { "best", no_argument, NULL, 'B' }, + { "cluster", required_argument, NULL, 'C' }, + { "end", no_argument, NULL, 'E' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "no-action", no_argument, NULL, 'n' }, + { "no-dirty", no_argument, NULL, 'D' }, + { "quiet", no_argument, NULL, 'q' }, + { "start", no_argument, NULL, 'S' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + char *end = NULL; + + opterr = 0; /* We'll handle the errors, thank you. */ + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) { + opts.device = argv[optind-1]; + } else if (!opts.file) { + opts.file = argv[optind-1]; + } else { + opts.device = NULL; + opts.file = NULL; + err++; + } + break; + case 'B': + if (opts.location == 0) + opts.location = NTFS_MOVE_LOC_BEST; + else + opts.location = -1; + break; + case 'C': + if (opts.location == 0) { + opts.location = strtoll(optarg, &end, 0); + if (end && *end) + err++; + } else { + opts.location = -1; + } + break; + case 'D': + opts.nodirty++; + break; + case 'E': + if (opts.location == 0) + opts.location = NTFS_MOVE_LOC_END; + else + opts.location = -1; + break; + case 'f': + opts.force++; + break; + case 'h': + case '?': + if (strncmp (argv[optind-1], "--log-", 6) == 0) { + if (!ntfs_log_parse_option (argv[optind-1])) + err++; + break; + } + help++; + break; + case 'n': + opts.noaction++; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'S': + if (opts.location == 0) + opts.location = NTFS_MOVE_LOC_START; + else + opts.location = -1; + break; + case 'V': + ver++; + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + default: + ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if ((opts.device == NULL) || + (opts.file == NULL)) { + if (argc > 1) + ntfs_log_error("You must specify one device and one file.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose at the " + "same time.\n"); + err++; + } + + if (opts.location == -1) { + ntfs_log_error("You may only specify one location option: " + "--start, --best, --end or --cluster\n"); + err++; + } else if (opts.location == 0) { + opts.location = NTFS_MOVE_LOC_BEST; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + return (!err && !help && !ver); +} + +#if 0 + +/** + * ntfs_debug_runlist_dump2 - Dump a runlist. + */ +static int ntfs_debug_runlist_dump2(const runlist *rl, int abbr, char *prefix) +{ + //int abbr = 3; /* abbreviate long lists */ + int len = 0; + int i; + int res = 0; + u64 total = 0; + const char *lcn_str[5] = { "HOLE", "NOTMAP", "ENOENT", "EINVAL", "XXXX" }; + + if (!rl) { + ntfs_log_info(" Run list not present.\n"); + return 0; + } + + if (!prefix) + prefix = ""; + + if (abbr) + for (len = 0; rl[len].length; len++) ; + + ntfs_log_info("%s VCN LCN len\n", prefix); + for (i = 0; rl->length; i++, rl++) { + LCN lcn = rl->lcn; + + total += rl->length; + if (abbr) + if (len > 20) { + if ((i == abbr) && (len > (abbr*2))) + ntfs_log_info("%s ... ... ...\n", prefix); + if ((i > (abbr-1)) && (i < (len - (abbr-1)))) + continue; + } + + if (rl->vcn < -1) + res = -1; + + if (lcn < (LCN)0) { + int j = -lcn - 1; + + if ((j < 0) || (j > 4)) { + j = 4; + res = -1; + } + ntfs_log_info("%s%8lld %8s %8lld\n", prefix, + rl->vcn, lcn_str[j], rl->length); + } else + ntfs_log_info("%s%8lld %8lld %8lld\n", prefix, + rl->vcn, rl->lcn, rl->length); + } + ntfs_log_info("%s --------\n", prefix); + ntfs_log_info("%s %8lld\n", prefix, total); + ntfs_log_info("\n"); + return res; +} + +#endif /* if 0 */ + +/** + * resize_nonres_attr + */ +static int resize_nonres_attr(MFT_RECORD *m, ATTR_RECORD *a, const u32 new_size) +{ + int this_attr; + int next_attr; + int tail_size; + int file_size; + int old_size; + u8 *ptr; + + old_size = a->length; + file_size = m->bytes_in_use; + this_attr = p2n(a)-p2n(m); + next_attr = this_attr + a->length; + tail_size = file_size - next_attr; + ptr = (u8*) m; + + /* + ntfs_log_info("old_size = %d\n", old_size); + ntfs_log_info("new_size = %d\n", new_size); + ntfs_log_info("file_size = %d\n", file_size); + ntfs_log_info("this_attr = %d\n", this_attr); + ntfs_log_info("next_attr = %d\n", next_attr); + ntfs_log_info("tail_size = %d\n", tail_size); + */ + + memmove(ptr + this_attr + new_size, ptr + next_attr, tail_size); + + a->length = new_size; + m->bytes_in_use += new_size - old_size; + + return 0; +} + +/** + * calc_attr_length + */ +static int calc_attr_length(ATTR_RECORD *rec, int runlength) +{ + int size; + + if (!rec) + return -1; + if (!rec->non_resident) + return -1; + + size = rec->mapping_pairs_offset + runlength + 7; + size &= 0xFFF8; + return size; +} + +#if 0 + +/** + * dump_runs + */ +static void dump_runs(u8 *buffer, int len) +{ + int i; + ntfs_log_info("RUN: \e[01;31m"); + + for (i = 0; i < len; i++) { + ntfs_log_info(" %02x", buffer[i]); + } + ntfs_log_info("\e[0m\n"); +} + +#endif /* if 0 */ + +/** + * find_unused + */ +static runlist * find_unused(ntfs_volume *vol, s64 size, u64 loc + __attribute__((unused)), int flags __attribute__((unused))) +{ + const int bufsize = 8192; + u8 *buffer; + int clus; + int i; + int curr = 0; + int count = 0; + s64 start = 0; + int bit = 0; + runlist *res = NULL; + + //ntfs_log_info("find_unused\n"); + buffer = malloc(bufsize); + if (!buffer) { + ntfs_log_info("!buffer\n"); + return NULL; + } + + //ntfs_log_info("looking for space for %lld clusters\n", size); + + clus = vol->lcnbmp_na->allocated_size / bufsize; + //ntfs_log_info("clus = %d\n", clus); + + for (i = 0; i < clus; i++) { + int bytes_read, j; + + bytes_read = ntfs_attr_pread(vol->lcnbmp_na, i*bufsize, + bufsize, buffer); + if (bytes_read != bufsize) { + ntfs_log_info("!read\n"); + return NULL; + } + for (j = 0; j < bufsize*8; j++) { + bit = !!test_bit(j & 7, buffer[j>>3]); + if (curr == bit) { + count++; + if ((!bit) && (count >= size)) { + //res = calloc(2, sizeof(*res)); + res = calloc(1, 4096); + if (res) { + res[0].vcn = 0; + res[0].lcn = start; + res[0].length = size; + res[1].lcn = LCN_ENOENT; + } + goto done; + } + } else { + //ntfs_log_info("%d * %d\n", curr, count); + curr = bit; + count = 1; + start = i*bufsize*8 + j; + } + } + } +done: + //ntfs_log_info("%d * %d\n", curr, count); + + free(buffer); + + if (res) { + for (i = 0; i < size; i++) { + if (utils_cluster_in_use(vol, res->lcn + i)) { + ntfs_log_info("ERROR cluster %lld in use\n", res->lcn + i); + } + } + } else { + ntfs_log_info("failed\n"); + } + + return res; +} + +/** + * dont_move + * + * Don't let the user move: + * ANY metadata + * Any fragmented MFT records + * The boot file 'ntldr' + */ +static int dont_move(ntfs_inode *ino) +{ + static const ntfschar ntldr[6] = { + const_cpu_to_le16('n'), const_cpu_to_le16('t'), const_cpu_to_le16('l'), + const_cpu_to_le16('d'), const_cpu_to_le16('r'), const_cpu_to_le16('\0') + }; + + ATTR_RECORD *rec; + FILE_NAME_ATTR *name; + + if (utils_is_metadata(ino)) { + ntfs_log_error("metadata\n"); + return 1; + } + + rec = find_first_attribute(AT_ATTRIBUTE_LIST, ino->mrec); + if (rec) { + ntfs_log_error("attribute list\n"); + return 1; + } + + rec = find_first_attribute(AT_FILE_NAME, ino->mrec); + if (!rec) { + ntfs_log_error("extend inode\n"); + return 1; + } + + name = (FILE_NAME_ATTR*) ((u8*)rec + rec->value_offset); + if (ntfs_names_are_equal(ntldr, 5, name->file_name, name->file_name_length, + IGNORE_CASE, ino->vol->upcase, ino->vol->upcase_len)) { + ntfs_log_error("ntldr\n"); + return 1; + } + + return 0; +} + + +/** + * bitmap_alloc + */ +static int bitmap_alloc(ntfs_volume *vol, runlist_element *rl) +{ + int res; + + if (!rl) + return -1; + + res = ntfs_bitmap_set_run(vol->lcnbmp_na, rl->lcn, rl->length); + if (res < 0) { + ntfs_log_error("bitmap alloc returns %d\n", res); + } + + return res; +} + +/** + * bitmap_free + */ +static int bitmap_free(ntfs_volume *vol, runlist_element *rl) +{ + int res; + + if (!rl) + return -1; + + res = ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, rl->length); + if (res < 0) { + ntfs_log_error("bitmap free returns %d\n", res); + } + + return res; +} + +/** + * data_copy + */ +static int data_copy(ntfs_volume *vol, runlist_element *from, runlist_element *to) +{ + int i; + u8 *buffer; + s64 res = 0; + + if (!vol || !from || !to) + return -1; + if ((from->length != to->length) || (from->lcn < 0) || (to->lcn < 0)) + return -1; + + //ntfs_log_info("data_copy: from 0x%llx to 0x%llx\n", from->lcn, to->lcn); + buffer = malloc(vol->cluster_size); + if (!buffer) { + ntfs_log_info("!buffer\n"); + return -1; + } + + for (i = 0; i < from->length; i++) { + //ntfs_log_info("read cluster at %8lld\n", from->lcn+i); + res = ntfs_pread(vol->dev, (from->lcn+i) * vol->cluster_size, + vol->cluster_size, buffer); + if (res != vol->cluster_size) { + ntfs_log_error("!read\n"); + res = -1; + break; + } + + //ntfs_log_info("write cluster to %8lld\n", to->lcn+i); + res = ntfs_pwrite(vol->dev, (to->lcn+i) * vol->cluster_size, + vol->cluster_size, buffer); + if (res != vol->cluster_size) { + ntfs_log_error("!write %lld\n", res); + res = -1; + break; + } + } + + free(buffer); + return res; +} + +/** + * move_runlist + * + * validate: + * runlists are the same size + * from in use + * to not in use + * allocate new space + * copy data + * deallocate old space + */ +static s64 move_runlist(ntfs_volume *vol, runlist_element *from, + runlist_element *to) +{ + int i; + + if (!vol || !from || !to) + return -1; + if (from->length != to->length) { + ntfs_log_error("diffsizes\n"); + return -1; + } + + if ((from->lcn < 0) || (to->lcn < 0)) { + ntfs_log_error("invalid runs\n"); + return -1; + } + + for (i = 0; i < from->length; i++) { + if (!utils_cluster_in_use(vol, from->lcn+i)) { + ntfs_log_error("from not in use\n"); + return -1; + } + } + + for (i = 0; i < to->length; i++) { + if (utils_cluster_in_use(vol, to->lcn+i)) { + ntfs_log_error("to is in use\n"); + return -1; + } + } + + if (bitmap_alloc(vol, to) < 0) { + ntfs_log_error("cannot bitmap_alloc\n"); + return -1; + } + + if (data_copy(vol, from, to) < 0) { + ntfs_log_error("cannot data_copy\n"); + return -1; + } + + if (bitmap_free(vol, from) < 0) { + ntfs_log_error("cannot bitmap_free\n"); + return -1; + } + + return 0; +} + + +/**original + * move_datarun + * > 0 Bytes moved / size to be moved + * = 0 Nothing to do + * < 0 Error + */ + +// get size of runlist +// find somewhere to put data +// backup original runlist +// move the data + +// got to get the runlist out of this function +// requires a mrec arg, not an ino (ino->mrec will do for now) +// check size of new runlist before allocating / moving +// replace one datarun with another (by hand) +static s64 move_datarun(ntfs_volume *vol, ntfs_inode *ino, ATTR_RECORD *rec, + runlist_element *run, u64 loc, int flags) +{ + runlist *from; + runlist *to; + int need_from; + int need_to; + int i; + s64 res = -1; + + // find empty space + to = find_unused(vol, run->length, loc, flags); + if (!to) { + ntfs_log_error("!to\n"); + return -1; + } + + to->vcn = run->vcn; + + // copy original runlist + from = ntfs_mapping_pairs_decompress(vol, rec, NULL); + if (!from) { + ntfs_log_info("!from\n"); + return -1; + } + + ntfs_log_info("move %lld,%lld,%lld to %lld,%lld,%lld\n", run->vcn, + run->lcn, run->length, to->vcn, to->lcn, to->length); + + need_from = ntfs_get_size_for_mapping_pairs(vol, from, 0, INT_MAX); + ntfs_log_info("orig data run = %d bytes\n", need_from); + + //ntfs_debug_runlist_dump2(from, 5, "\t"); + + for (i = 0; to[i].length > 0; i++) { + if (from[i].vcn == run->vcn) { + from[i].lcn = to->lcn; + break; + } + } + + //ntfs_debug_runlist_dump2(from, 5, "\t"); + + need_to = ntfs_get_size_for_mapping_pairs(vol, from, 0, INT_MAX); + ntfs_log_info("new data run = %d bytes\n", need_to); + + need_from = calc_attr_length(rec, need_from); + need_to = calc_attr_length(rec, need_to); + + ntfs_log_info("Before %d, after %d\n", need_from, need_to); + + if (need_from != need_to) { + if (resize_nonres_attr(ino->mrec, rec, need_to) < 0) { + ntfs_log_info("!resize\n"); + return -1; + } + } + + res = move_runlist(vol, run, to); + if (res < 0) { + ntfs_log_error("!move_runlist\n"); + return -1; + } + + // wipe orig runs + memset(((u8*)rec) +rec->mapping_pairs_offset, 0, need_to - rec->mapping_pairs_offset); + + // update data runs + ntfs_mapping_pairs_build(vol, ((u8*)rec) + rec->mapping_pairs_offset, + need_to, from, 0, NULL); + + // commit + ntfs_inode_mark_dirty(ino); + + if (ntfs_inode_sync(ino) < 0) { + ntfs_log_info("!sync\n"); + return -1; + } + + free(from); + free(to); + return res; +} + +/** + * move_attribute + * > 0 Bytes moved / size to be moved + * = 0 Nothing to do + * < 0 Error + */ +static s64 move_attribute(ntfs_volume *vol, ntfs_inode *ino, ATTR_RECORD *rec, + u64 loc, int flags) +{ + int i; + s64 res; + s64 count = 0; + runlist *runs; + + // NTFS_MOVE_LOC_BEST : assess how much space this attribute will need, + // find that space and pass the location to our children. + // Anything else we pass directly to move_datarun. + + runs = ntfs_mapping_pairs_decompress(vol, rec, NULL); + if (!runs) { + ntfs_log_error("!runs\n"); + return -1; + } + + //ntfs_debug_runlist_dump2(runs, 5, "\t"); + + //ntfs_log_info(" VCN LCN Length\n"); + for (i = 0; runs[i].length > 0; i++) { + if (runs[i].lcn == LCN_RL_NOT_MAPPED) { + continue; + } + + res = move_datarun(vol, ino, rec, runs+i, loc, flags); + //ntfs_log_info(" %8lld %8lld %8lld\n", runs[i].vcn, runs[i].lcn, runs[i].length); + if (res < 0) { + ntfs_log_error("!move_datarun\n"); + count = res; + break; + } + count += res; + } + + return count; +} + +/** + * move_file + * > 0 Bytes moved / size to be moved + * = 0 Nothing to do + * < 0 Error + */ +static s64 move_file(ntfs_volume *vol, ntfs_inode *ino, u64 loc, int flags) +{ + char *buffer; + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *rec; + s64 res; + s64 count = 0; + + buffer = malloc(MAX_PATH); + if (!buffer) { + ntfs_log_error("Out of memory\n"); + return -1; + } + + utils_inode_get_name(ino, buffer, MAX_PATH); + + if (dont_move(ino)) { + ntfs_log_error("can't move\n"); + return -1; + } + + ntfs_log_info("Moving %s\n", buffer); + + // NTFS_MOVE_LOC_BEST : assess how much space all the attributes will need, + // find that space and pass the location to our children. + // Anything else we pass directly to move_attribute. + + ctx = ntfs_attr_get_search_ctx(ino, NULL); + + while ((rec = find_attribute(AT_UNUSED, ctx))) { + utils_attr_get_name(vol, rec, buffer, MAX_PATH); + ntfs_log_info("\tAttribute 0x%02x %s is ", rec->type, buffer); + + if (rec->non_resident) { + ntfs_log_info("non-resident. Moving it.\n"); + + res = move_attribute(vol, ino, rec, loc, flags); + if (res < 0) { + count = res; + break; + } + count += res; + } else { + ntfs_log_info("resident.\n\t\tSkipping it.\n"); + } + } + + ntfs_attr_put_search_ctx(ctx); + free(buffer); + return count; +} + + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + ntfs_volume *vol; + ntfs_inode *inode; + int flags = 0; + int result = 1; + s64 count; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + if (!parse_options(argc, argv)) + return 1; + + utils_set_locale(); + + if (opts.noaction) + flags |= MS_RDONLY; + + vol = utils_mount_volume(opts.device, flags, opts.force); + if (!vol) { + ntfs_log_info("!vol\n"); + return 1; + } + + inode = ntfs_pathname_to_inode(vol, NULL, opts.file); + if (!inode) { + ntfs_log_info("!inode\n"); + return 1; + } + + count = move_file(vol, inode, opts.location, 0); + if ((count > 0) && (!opts.nodirty)) { + if (ntfs_volume_write_flags(vol, vol->flags | VOLUME_IS_DIRTY) < + 0) { + ntfs_log_error("Couldn't mark volume dirty\n"); + } + ntfs_log_info("Relocated %lld bytes\n", count); + } + if (count >= 0) + result = 0; + + if (result) + ntfs_log_info("failed\n"); + else + ntfs_log_info("success\n"); + + ntfs_inode_close(inode); + ntfs_umount(vol, FALSE); + return result; +} diff --git a/ntfsprogs/ntfsmove.h b/ntfsprogs/ntfsmove.h new file mode 100644 index 00000000..a698bc9a --- /dev/null +++ b/ntfsprogs/ntfsmove.h @@ -0,0 +1,47 @@ +/* + * ntfsmove - Part of the Linux-NTFS project. + * + * Copyright (c) 2003 Richard Russon + * + * This utility will move files on an NTFS volume. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 _NTFSMOVE_H_ +#define _NTFSMOVE_H_ + +#include + /* Move files to */ +#define NTFS_MOVE_LOC_START -1000 /* the first available space */ +#define NTFS_MOVE_LOC_BEST -1001 /* place big enough for entire file */ +#define NTFS_MOVE_LOC_END -1002 /* the last available space */ + +struct options { + char *device; /* Device/File to work with */ + char *file; /* File to display */ + s64 location; /* Where to place the file */ + int force; /* Override common sense */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int noaction; /* Do not write to disk */ + int nodirty; /* Do not mark volume dirty */ + u8 padding[4]; /* Unused: alignment to 64 bit. */ +}; + +#endif /* _NTFSMOVE_H_ */ + + diff --git a/ntfsprogs/ntfsprogs.8.in b/ntfsprogs/ntfsprogs.8.in new file mode 100644 index 00000000..1e810aaa --- /dev/null +++ b/ntfsprogs/ntfsprogs.8.in @@ -0,0 +1,72 @@ +.\" Copyright (c) 2002\-2005 Richard Russon. +.\" Copyright (c) 2002\-2003 Anton Altaparmakov. +.\" Copyright (c) 2005\-2006 Szabolcs Szakacsits. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSPROGS 8 "April 2006" "ntfsprogs @VERSION@" +.SH NAME +ntfsprogs \- tools for doing neat things with NTFS +.SH OVERVIEW +.B ntfsprogs +is a suite of NTFS utilities based around a shared library. The tools are +available for free and come with full source code. +.SH TOOLS +.PP +.BR mkntfs (8) +\- Create an NTFS filesystem. +.PP +.BR ntfscat (8) +\- Dump a file's content to the standard output. +.PP +.BR ntfsclone (8) +\- Efficiently clone, backup, restore or rescue NTFS. +.PP +.BR ntfscluster (8) +\- Locate the files which use the given sectors or clusters. +.PP +.BR ntfscmp (8) +\- Compare two NTFS filesystems and tell the differences. +.PP +.BR ntfscp (8) +\- Overwrite a file on an NTFS. +.PP +.BR ntfsfix (8) +\- Check and fix some common errors, clear the LogFile and make Windows +perform a thorough check next time it boots. +.PP +.BR ntfsinfo (8) +\- Show information about NTFS or one of the files or directories within it. +.PP +.BR ntfslabel (8) +\- Show, or set, an NTFS filesystem's volume label. +.PP +.BR ntfsls (8) +\- List information about files in a directory residing on an NTFS. +.PP +.BR ntfsresize (8) +\- Resize NTFS without losing data. +.PP +.BR ntfsundelete (8) +\- Recover deleted files from NTFS. +.SH AUTHORS +.PP +The tools were written by Anton Altaparmakov, Carmelo Kintana, Cristian Klein, +Erik Sornes, Giang Nguyen, Holger Ohmacht, Lode Leroy, Matthew J. Fanto, Per +Olofsson, Richard Russon, Szabolcs Szakacsits, Yura Pakhuchiy and Yuval Fledel. +.SH AVAILABILITY +The +.B ntfsprogs +can be downloaded from: +.br +.nh +http://www.linux\-ntfs.org/content/view/19/37 +.hy +.sp +These manual pages can be viewed online at: +.br +.nh +http://man.linux-ntfs.org/ +.hy +.SH SEE ALSO +.BR libntfs\-gnomevfs (8) + diff --git a/ntfsprogs/ntfsresize.8.in b/ntfsprogs/ntfsresize.8.in new file mode 100644 index 00000000..40d5926f --- /dev/null +++ b/ntfsprogs/ntfsresize.8.in @@ -0,0 +1,286 @@ +.\" Copyright (c) 2002\-2006 Szabolcs Szakacsits. +.\" Copyright (c) 2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSRESIZE 8 "February 2006" "ntfsprogs @VERSION@" +.SH NAME +ntfsresize \- resize an NTFS filesystem without data loss +.SH SYNOPSIS +.B ntfsresize +[\fIOPTIONS\fR] +.B \-\-info +.I DEVICE +.br +.B ntfsresize +[\fIOPTIONS\fR] +[\fB\-\-size \fISIZE\fR[\fBk\fR|\fBM\fR|\fBG\fR]] +.I DEVICE +.SH DESCRIPTION +The +.B ntfsresize +program safely resizes Windows XP, Windows Server 2003, Windows 2000, Windows +NT4 and Longhorn NTFS filesystems without data loss. All NTFS versions are +supported, used by 32\-bit and 64\-bit Windows. +.B Defragmentation is NOT required prior to resizing +because the program can relocate any data if needed, without risking data +integrity. +.PP +Ntfsresize can be used to shrink or enlarge any NTFS filesystem located +on an unmounted +.I DEVICE +(usually a disk partition). The new filesystem will have +.I SIZE +bytes. +The +.I SIZE +parameter may have one of the optional modifiers +.BR k , +.BR M , +.BR G , +which means the +.I SIZE +parameter is given in kilo\-, mega\- or gigabytes respectively. +.B Ntfsresize +conforms to the SI, ATA, IEEE standards and the disk manufacturers +by using k=10^3, M=10^6 and G=10^9. + +If both +.B \-\-info +and +.B \-\-size +are omitted then the +NTFS filesystem will be enlarged to the underlying +.I DEVICE +size. +.PP +To resize a filesystem on a partition, you must resize BOTH the filesystem +and the partition by editing the partition table on the disk. Similarly to +other command line filesystem resizers, +.B ntfsresize +doesn't manipulate the size of the partitions, hence +to do that you must use a disk partitioning tool as well, for example +.BR fdisk (8). +Alternatively you could use one of the many user friendly partitioners that +uses +.B ntfsresize +internally, like Mandriva's DiskDrake, QTParted, SUSE/Novell's YaST Partitioner, +IBM's EVMS, GParted or Debian/Ubuntu's Partman. +.PP +.B IMPORTANT! +It's a good practice making REGULAR BACKUPS of your valuable data, especially +before using ANY partitioning tools. To do so for NTFS, you could use +.BR ntfsclone (8). +Don't forget to save the partition table as well! +.SS Shrinkage +If you wish to shrink an NTFS partition, first use +.B ntfsresize +to shrink the size of the filesystem. Then you could use +.BR fdisk (8) +to shrink the size of the partition by deleting the +partition and recreating it with the smaller size. +Do not make the partition smaller than the new size of +NTFS otherwise you won't be able to boot. If you did so notwithstanding +then just recreate the partition to be as large as NTFS. +.SS Enlargement +To enlarge an NTFS filesystem, first you must enlarge the size of the +underlying partition. This can be done using +.BR fdisk (8) +by deleting the partition and recreating it with a larger size. +Make sure it will not overlap with an other existing partition. +Then you may use +.B ntfsresize +to enlarge the size of the filesystem. +.SS Partitioning +When recreating the partition by a disk partitioning tool, +make sure you create it at the same +starting sector and with the same partition type as before. +Otherwise you won't be able to access your filesystem. Use the 'u' +fdisk command to switch to the reliable sector unit from the +default cylinder one. + +Also make sure you set the bootable flag for the partition if it +existed before. Failing to do so you might not be able to boot your +computer from the disk. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsresize +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-i\fR, \fB\-\-info\fR +By using this option ntfsresize will determine the theoretically smallest +shrunken filesystem size supported. Most of the time the result is the space +already used on the filesystem. Ntfsresize will refuse shrinking to a +smaller size than what you got by this option and depending on several +factors it might be unable to shrink very close to this theoretical +size. Although the integrity of your data should be never in risk, +it's still strongly recommended to make a test run by using the +\fB\-\-no\-action\fR option before real resizing. + +Practically the smallest shrunken size generally is +at around "used space" + (20\-200 MB). Please also take into account +that Windows might need about 50\-100 MB free space left to boot safely. + +This option never causes any changes to the filesystem, the partition is +opened read\-only. +.TP +\fB\-s\fR, \fB\-\-size\fR SIZE\fR[\fBk\fR|\fBM\fR|\fBG\fR] +Resize filesystem to \fISIZE\fR[\fBk\fR|\fBM\fR|\fBG\fR] bytes. +The optional modifiers +.BR k , +.BR M , +.B G +mean the +.I SIZE +parameter is given in kilo\-, mega\- or gigabytes respectively. +Conforming to standards, k=10^3, M=10^6 and G=10^9. Use this option +with +.B \-\-no\-action +first. +.TP +\fB\-f\fR, \fB\-\-force\fR +Forces ntfsresize to proceed with the resize operation even if the filesystem +is marked for consistency check. + +Please note, ntfsresize always marks the filesystem +for consistency check before a real resize operation +and it leaves that way for extra +safety. Thus if NTFS was marked by ntfsresize then it's safe to +use this option. If you need +to resize several times without booting into Windows between each +resizing steps then you must use this option. +.TP +.B \-n, \-\-no\-action +Use this option to make a test run before doing the real resize operation. +Volume will be opened read\-only and +.B ntfsresize +displays what it would do if it were to resize the filesystem. +Continue with the real resizing only if the test run passed. +.TP +\fB\-b\fR, \fB\-\-bad\-sectors\fR +Support disks having hardware errors, bad sectors with those +.B ntfsresize +would refuse to work by default. + +Prior using this option, it's strongly recommended to make a backup by +.BR ntfsclone (8) +using the \-\-rescue option, then running 'chkdsk /f /r volume:' on Windows +from the command line. If the disk guarantee is still valid then replace it. +It's defected. Please also note, that no software can repair these type of +hardware errors. The most what they can do is to work around the permanent +defects. + +This option doesn't have any effect if the disk is flawless. +.TP +\fB\-P\fR, \fB\-\-no\-progress\-bar\fR +Don't show progress bars. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +More output. +.TP +\fB\-V\fR, \fB\-\-version\fR +Print the version number of +.B ntfsresize +and exit. +.TP +\fB\-h\fR, \fB\-\-help\fR +Display help and exit. +.SH EXIT CODES +The exit code is 0 on success, non\-zero otherwise. +.SH KNOWN ISSUES +No reliability problem is known. If you need +help please try the Ntfsresize FAQ first (see below) and if you +don't find your answer then send your question, comment or bug report to +the development team: +.br +.nh +linux\-ntfs\-dev@lists.sourceforge.net +.hy +.PP +There are a few very rarely met restrictions at present: filesystems having +unknown bad sectors, relocation +of the first MFT extent and resizing into the middle of a $MFTMirr extent +aren't supported yet. These cases are detected and +resizing is restricted to a safe size or the closest safe +size is displayed. +.PP +.B Ntfsresize +schedules an NTFS consistency check and +after the first boot into Windows you must see +.B chkdsk +running on a blue background. This is intentional and no need to worry about it. +Windows may force a quick reboot after the consistency check. +Moreover after repartitioning your disk and depending on the +hardware configuration, the Windows message +.B System Settings Change +may also appear. Just acknowledge it and reboot again. +.PP +The disk geometry handling semantic (HDIO_GETGEO ioctl) has changed +in an incompatible way in Linux 2.6 kernels and this triggered multitudinous +partition table corruptions resulting in unbootable Windows systems, even if +NTFS was consistent, if +.BR parted (8) +was involved in some way. This problem was often attributed to ntfsresize +but in fact it's completely independent of NTFS thus ntfsresize. Moreover +ntfsresize never touches the partition table at all. By changing +the 'Disk Access Mode' to LBA in the BIOS makes booting work +again, most of the time. You can find more information about this issue +in the Troubleshooting section of the below referred Ntfsresize FAQ. +.SH AUTHORS +.B ntfsresize +was written by Szabolcs Szakacsits, with contributions from Anton Altaparmakov +and Richard Russon. +.SH ACKNOWLEDGEMENT +Many thanks to Anton Altaparmakov and Richard Russon +for libntfs, the excellent documentation and comments, +to Gergely Madarasz, Dewey M. Sasser and Miguel Lastra and his colleagues +at the University of Granada for their continuous and highly valuable help, +furthermore to Erik Meade, Martin Fick, Sandro Hawke, Dave Croal, +Lorrin Nelson, Geert Hendrickx, Robert Bjorkman and Richard Burdick +for beta testing the relocation support, to Florian Eyben, Fritz Oppliger, +Richard Ebling, Sid\-Ahmed Touati, Jan Kiszka, Benjamin Redelings, Christopher +Haney, Ryan Durk, Ralf Beyer, Scott Hansen, Alan Evans for the valued +contributions and to Theodore Ts'o whose +.BR resize2fs (8) +man page originally formed the basis of this page. +.SH AVAILABILITY +.B ntfsresize +is part of the +.B ntfsprogs +package and is available from: +.br +.nh +http://www.linux\-ntfs.org/content/view/19/37 +.hy +.sp +The manual pages are available online at: +.br +.nh +http://man.linux-ntfs.org/ +.hy +.sp +.B Ntfsresize +related news, example of usage, troubleshooting, statically linked binary and +FAQ (frequently asked questions) are maintained at: +.br +.nh +http://mlf.linux.rulez.org/mlf/ezaz/ntfsresize.html +.hy +.SH SEE ALSO +.BR fdisk (8), +.BR cfdisk (8), +.BR sfdisk (8), +.BR parted (8), +.BR evms (8), +.BR ntfsclone (8), +.BR mkntfs (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsresize.c b/ntfsprogs/ntfsresize.c new file mode 100644 index 00000000..8d1e071c --- /dev/null +++ b/ntfsprogs/ntfsresize.c @@ -0,0 +1,2502 @@ +/** + * ntfsresize - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2006 Szabolcs Szakacsits + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2002-2003 Richard Russon + * + * This utility will resize an NTFS volume without data loss. + * + * WARNING FOR DEVELOPERS!!! Several external tools grep for text messages + * to control execution thus if you would like to change any message + * then PLEASE think twice before doing so then don't modify it. Thanks! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 "config.h" + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDARG_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +/* #include "version.h" */ + +static const char *EXEC_NAME = "ntfsresize"; + +static const char *resize_warning_msg = +"WARNING: Every sanity check passed and only the dangerous operations left.\n" +"Make sure that important data has been backed up! Power outage or computer\n" +"crash may result major data loss!\n"; + +static const char *resize_important_msg = +"You can go on to shrink the device for example with Linux fdisk.\n" +"IMPORTANT: When recreating the partition, make sure that you\n" +" 1) create it at the same disk sector (use sector as the unit!)\n" +" 2) create it with the same partition type (usually 7, HPFS/NTFS)\n" +" 3) do not make it smaller than the new NTFS filesystem size\n" +" 4) set the bootable flag for the partition if it existed before\n" +"Otherwise you won't be able to access NTFS or can't boot from the disk!\n" +"If you make a mistake and don't have a partition table backup then you\n" +"can recover the partition table by TestDisk or Parted's rescue mode.\n"; + +static const char *invalid_ntfs_msg = +"The device '%s' doesn't have a valid NTFS.\n" +"Maybe you selected the wrong partition? Or the whole disk instead of a\n" +"partition (e.g. /dev/hda, not /dev/hda1)? This error might also occur\n" +"if the disk was incorrectly repartitioned (see the ntfsresize FAQ).\n"; + +static const char *corrupt_volume_msg = +"NTFS is inconsistent. Run chkdsk /f on Windows then reboot it TWICE!\n" +"The usage of the /f parameter is very IMPORTANT! No modification was\n" +"and will be made to NTFS by this software until it gets repaired.\n"; + +static const char *hibernated_volume_msg = +"The NTFS partition is hibernated. Windows must be resumed and turned off\n" +"properly, so resizing could be done safely.\n"; + +static const char *unclean_journal_msg = +"The NTFS journal file is unclean. Please shutdown Windows properly before\n" +"using this software! Note, if you have run chkdsk previously then boot\n" +"Windows again which will automatically initialize the journal correctly.\n"; + +static const char *opened_volume_msg = +"This software has detected that the NTFS volume is already opened by another\n" +"software thus it refuses to progress to preserve data consistency.\n"; + +static const char *bad_sectors_warning_msg = +"****************************************************************************\n" +"* WARNING: The disk has bad sector. This means physical damage on the disk *\n" +"* surface caused by deterioration, manufacturing faults or other reason. *\n" +"* The reliability of the disk may stay stable or degrade fast. We suggest *\n" +"* making a full backup urgently by running 'ntfsclone --rescue ...' then *\n" +"* run 'chkdsk /f /r' on Windows and rebooot it TWICE! Then you can resize *\n" +"* NTFS safely by additionally using the --bad-sectors option of ntfsresize.*\n" +"****************************************************************************\n"; + +static const char *many_bad_sectors_msg = +"***************************************************************************\n" +"* WARNING: The disk has many bad sectors. This means physical damage *\n" +"* on the disk surface caused by deterioration, manufacturing faults or *\n" +"* other reason. We suggest to get a replacement disk as soon as possible. *\n" +"***************************************************************************\n"; + +struct { + int verbose; + int debug; + int ro_flag; + int force; + int info; + int show_progress; + int badsectors; + s64 bytes; + char *volume; +} opt; + +struct bitmap { + s64 size; + u8 *bm; + u8 padding[4]; /* Unused: padding to 64 bit. */ +}; + +#define NTFS_PROGBAR 0x0001 +#define NTFS_PROGBAR_SUPPRESS 0x0002 + +struct progress_bar { + u64 start; + u64 stop; + int resolution; + int flags; + float unit; + u8 padding[4]; /* Unused: padding to 64 bit. */ +}; + +struct llcn_t { + s64 lcn; /* last used LCN for a "special" file/attr type */ + s64 inode; /* inode using it */ +}; + +#define NTFSCK_PROGBAR 0x0001 + +typedef struct { + ntfs_inode *ni; /* inode being processed */ + ntfs_attr_search_ctx *ctx; /* inode attribute being processed */ + s64 inuse; /* num of clusters in use */ + int multi_ref; /* num of clusters referenced many times */ + int outsider; /* num of clusters outside the volume */ + int show_outsider; /* controls showing the above information */ + int flags; + struct bitmap lcn_bitmap; +} ntfsck_t; + +typedef struct { + ntfs_volume *vol; + ntfs_inode *ni; /* inode being processed */ + s64 new_volume_size; /* in clusters; 0 = --info w/o --size */ + MFT_REF mref; /* mft reference */ + MFT_RECORD *mrec; /* mft record */ + ntfs_attr_search_ctx *ctx; /* inode attribute being processed */ + u64 relocations; /* num of clusters to relocate */ + s64 inuse; /* num of clusters in use */ + runlist mftmir_rl; /* $MFTMirr AT_DATA's new position */ + s64 mftmir_old; /* $MFTMirr AT_DATA's old LCN */ + int dirty_inode; /* some inode data got relocated */ + int shrink; /* shrink = 1, enlarge = 0 */ + s64 badclusters; /* num of physically dead clusters */ + VCN mft_highest_vcn; /* used for relocating the $MFT */ + struct progress_bar progress; + struct bitmap lcn_bitmap; + /* Temporary statistics until all case is supported */ + struct llcn_t last_mft; + struct llcn_t last_mftmir; + struct llcn_t last_multi_mft; + struct llcn_t last_sparse; + struct llcn_t last_compressed; + struct llcn_t last_lcn; + s64 last_unsupp; /* last unsupported cluster */ +} ntfs_resize_t; + +/* FIXME: This, lcn_bitmap and pos from find_free_cluster() will make a cluster + allocation related structure, attached to ntfs_resize_t */ +s64 max_free_cluster_range = 0; + +#define NTFS_MBYTE (1000 * 1000) + +/* WARNING: don't modify the text, external tools grep for it */ +#define ERR_PREFIX "ERROR" +#define PERR_PREFIX ERR_PREFIX "(%d): " +#define NERR_PREFIX ERR_PREFIX ": " + +#define DIRTY_NONE (0) +#define DIRTY_INODE (1) +#define DIRTY_ATTRIB (2) + +#define NTFS_MAX_CLUSTER_SIZE (65536) + +static s64 rounded_up_division(s64 numer, s64 denom) +{ + return (numer + (denom - 1)) / denom; +} + +/** + * perr_printf + * + * Print an error message. + */ +__attribute__((format(printf, 1, 2))) +static void perr_printf(const char *fmt, ...) +{ + va_list ap; + int eo = errno; + + fprintf(stdout, PERR_PREFIX, eo); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fprintf(stdout, ": %s\n", strerror(eo)); + fflush(stdout); + fflush(stderr); +} + +__attribute__((format(printf, 1, 2))) +static void err_printf(const char *fmt, ...) +{ + va_list ap; + + fprintf(stdout, NERR_PREFIX); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fflush(stdout); + fflush(stderr); +} + +/** + * err_exit + * + * Print and error message and exit the program. + */ +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static int err_exit(const char *fmt, ...) +{ + va_list ap; + + fprintf(stdout, NERR_PREFIX); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fflush(stdout); + fflush(stderr); + exit(1); +} + +/** + * perr_exit + * + * Print and error message and exit the program + */ +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static int perr_exit(const char *fmt, ...) +{ + va_list ap; + int eo = errno; + + fprintf(stdout, PERR_PREFIX, eo); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + printf(": %s\n", strerror(eo)); + fflush(stdout); + fflush(stderr); + exit(1); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +__attribute__((noreturn)) +static void usage(void) +{ + + printf("\nUsage: %s [OPTIONS] DEVICE\n" + " Resize an NTFS volume non-destructively, safely move any data if needed.\n" + "\n" + " -i, --info Estimate the smallest shrunken size possible\n" + " -s, --size SIZE Resize volume to SIZE[k|M|G] bytes\n" + "\n" + " -n, --no-action Do not write to disk\n" + " -b, --bad-sectors Support disks having bad sectors\n" + " -f, --force Force to progress\n" + " -P, --no-progress-bar Don't show progress bar\n" + " -v, --verbose More output\n" + " -V, --version Display version information\n" + " -h, --help Display this help\n" +#ifdef DEBUG + " -d, --debug Show debug information\n" +#endif + "\n" + " The options -i and -s are mutually exclusive. If both options are\n" + " omitted then the NTFS volume will be enlarged to the DEVICE size.\n" + "\n", EXEC_NAME); + printf("%s%s", ntfs_bugs, ntfs_home); + printf("Ntfsresize FAQ: http://linux-ntfs.sourceforge.net/info/ntfsresize.html\n"); + exit(1); +} + +/** + * proceed_question + * + * Force the user to confirm an action before performing it. + * Copy-paste from e2fsprogs + */ +static void proceed_question(void) +{ + char buf[256]; + const char *short_yes = "yY"; + + fflush(stdout); + fflush(stderr); + printf("Are you sure you want to proceed (y/[n])? "); + buf[0] = 0; + fgets(buf, sizeof(buf), stdin); + if (strchr(short_yes, buf[0]) == 0) { + printf("OK quitting. NO CHANGES have been made to your " + "NTFS volume.\n"); + exit(1); + } +} + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + printf("\nResize an NTFS Volume, without data loss.\n\n"); + printf("Copyright (c) 2002-2006 Szabolcs Szakacsits\n"); + printf("Copyright (c) 2002-2005 Anton Altaparmakov\n"); + printf("Copyright (c) 2002-2003 Richard Russon\n"); + printf("\n%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * get_new_volume_size + * + * Convert a user-supplied string into a size. Without any suffix the number + * will be assumed to be in bytes. If the number has a suffix of k, M or G it + * will be scaled up by 1000, 1000000, or 1000000000. + */ +static s64 get_new_volume_size(char *s) +{ + s64 size; + char *suffix; + int prefix_kind = 1000; + + size = strtoll(s, &suffix, 10); + if (size <= 0 || errno == ERANGE) + err_exit("Illegal new volume size\n"); + + if (!*suffix) + return size; + + if (strlen(suffix) == 2 && suffix[1] == 'i') + prefix_kind = 1024; + else if (strlen(suffix) > 1) + usage(); + + /* We follow the SI prefixes: + http://physics.nist.gov/cuu/Units/prefixes.html + http://physics.nist.gov/cuu/Units/binary.html + Disk partitioning tools use prefixes as, + k M G + fdisk 2.11x- 2^10 2^20 10^3*2^20 + fdisk 2.11y+ 10^3 10^6 10^9 + cfdisk 10^3 10^6 10^9 + sfdisk 2^10 2^20 + parted 2^10 2^20 (may change) + fdisk (DOS) 2^10 2^20 + */ + /* FIXME: check for overflow */ + switch (*suffix) { + case 'G': + size *= prefix_kind; + case 'M': + size *= prefix_kind; + case 'k': + size *= prefix_kind; + break; + default: + usage(); + } + + return size; +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char **argv) +{ + static const char *sopt = "-bdfhinPs:vV"; + static const struct option lopt[] = { + { "bad-sectors",no_argument, NULL, 'b' }, +#ifdef DEBUG + { "debug", no_argument, NULL, 'd' }, +#endif + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "info", no_argument, NULL, 'i' }, + { "no-action", no_argument, NULL, 'n' }, + { "no-progress-bar", no_argument, NULL, 'P' }, + { "size", required_argument, NULL, 's' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + int c; + int err = 0; + int ver = 0; + int help = 0; + + memset(&opt, 0, sizeof(opt)); + opt.show_progress = 1; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!err && !opt.volume) + opt.volume = argv[optind-1]; + else + err++; + break; + case 'b': + opt.badsectors++; + break; + case 'd': + opt.debug++; + break; + case 'f': + opt.force++; + break; + case 'h': + case '?': + help++; + break; + case 'i': + opt.info++; + break; + case 'n': + opt.ro_flag = MS_RDONLY; + break; + case 'P': + opt.show_progress = 0; + break; + case 's': + if (!err && (opt.bytes == 0)) + opt.bytes = get_new_volume_size(optarg); + else + err++; + break; + case 'v': + opt.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + ver++; + break; + default: + if (optopt == 's') { + printf("Option '%s' requires an argument.\n", argv[optind-1]); + } else { + printf("Unknown option '%s'.\n", argv[optind-1]); + } + err++; + break; + } + } + + if (!help && !ver) { + if (opt.volume == NULL) { + if (argc > 1) + printf("You must specify exactly one device.\n"); + err++; + } + if (opt.info) { + opt.ro_flag = MS_RDONLY; + if (opt.bytes) { + printf(NERR_PREFIX "Options --info and --size " + "can't be used together.\n"); + usage(); + } + } + } + + /* Redirect stderr to stdout, note fflush()es are essential! */ + fflush(stdout); + fflush(stderr); + if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) + perr_exit("Failed to redirect stderr to stdout"); + fflush(stdout); + fflush(stderr); + +#ifdef DEBUG + if (!opt.debug) + if (!freopen("/dev/null", "w", stderr)) + perr_exit("Failed to redirect stderr to /dev/null"); +#endif + + if (ver) + version(); + if (help || err) + usage(); + + return (!err && !help && !ver); +} + +static void print_advise(ntfs_volume *vol, s64 supp_lcn) +{ + s64 old_b, new_b, freed_b, old_mb, new_mb, freed_mb; + + old_b = vol->nr_clusters * vol->cluster_size; + old_mb = rounded_up_division(old_b, NTFS_MBYTE); + + /* Take the next supported cluster (free or relocatable) + plus reserve a cluster for the backup boot sector */ + supp_lcn += 2; + + if (supp_lcn > vol->nr_clusters) { + err_printf("Very rare fragmentation type detected. " + "Sorry, it's not supported yet.\n" + "Try to defragment your NTFS, perhaps it helps.\n"); + exit(1); + } + + new_b = supp_lcn * vol->cluster_size; + new_mb = rounded_up_division(new_b, NTFS_MBYTE); + freed_b = (vol->nr_clusters - supp_lcn + 1) * vol->cluster_size; + freed_mb = freed_b / NTFS_MBYTE; + + /* WARNING: don't modify the text, external tools grep for it */ + printf("You might resize at %lld bytes ", (long long)new_b); + if ((new_mb * NTFS_MBYTE) < old_b) + printf("or %lld MB ", (long long)new_mb); + + printf("(freeing "); + if (freed_mb && (old_mb - new_mb)) + printf("%lld MB", (long long)(old_mb - new_mb)); + else + printf("%lld bytes", (long long)freed_b); + printf(").\n"); + + printf("Please make a test run using both the -n and -s options " + "before real resizing!\n"); +} + +static void rl_set(runlist *rl, VCN vcn, LCN lcn, s64 len) +{ + rl->vcn = vcn; + rl->lcn = lcn; + rl->length = len; +} + +static int rl_items(runlist *rl) +{ + int i = 0; + + while (rl[i++].length) + ; + + return i; +} + +static void dump_run(runlist_element *r) +{ + ntfs_log_verbose(" %8lld %8lld (0x%08llx) %lld\n", (long long)r->vcn, + (long long)r->lcn, (long long)r->lcn, + (long long)r->length); +} + +static void dump_runlist(runlist *rl) +{ + while (rl->length) + dump_run(rl++); +} + +/** + * nr_clusters_to_bitmap_byte_size + * + * Take the number of clusters in the volume and calculate the size of $Bitmap. + * The size must be always a multiple of 8 bytes. + */ +static s64 nr_clusters_to_bitmap_byte_size(s64 nr_clusters) +{ + s64 bm_bsize; + + bm_bsize = rounded_up_division(nr_clusters, 8); + bm_bsize = (bm_bsize + 7) & ~7; + + return bm_bsize; +} + +static void collect_resize_constraints(ntfs_resize_t *resize, runlist *rl) +{ + s64 inode, last_lcn; + ATTR_FLAGS flags; + ATTR_TYPES atype; + struct llcn_t *llcn = NULL; + int ret, supported = 0; + + last_lcn = rl->lcn + (rl->length - 1); + + inode = resize->ni->mft_no; + flags = resize->ctx->attr->flags; + atype = resize->ctx->attr->type; + + if ((ret = ntfs_inode_badclus_bad(inode, resize->ctx->attr)) != 0) { + if (ret == -1) + perr_exit("Bad sector list check failed"); + return; + } + + if (inode == FILE_Bitmap) { + llcn = &resize->last_lcn; + if (atype == AT_DATA && NInoAttrList(resize->ni)) + err_exit("Highly fragmented $Bitmap isn't supported yet."); + + supported = 1; + + } else if (inode == FILE_MFT) { + llcn = &resize->last_mft; + /* + * First run of $MFT AT_DATA isn't supported yet. + */ + if (atype != AT_DATA || rl->vcn) + supported = 1; + + } else if (NInoAttrList(resize->ni)) { + llcn = &resize->last_multi_mft; + + if (inode != FILE_MFTMirr) + supported = 1; + + } else if (flags & ATTR_IS_SPARSE) { + llcn = &resize->last_sparse; + supported = 1; + + } else if (flags & ATTR_IS_COMPRESSED) { + llcn = &resize->last_compressed; + supported = 1; + + } else if (inode == FILE_MFTMirr) { + llcn = &resize->last_mftmir; + supported = 1; + + /* Fragmented $MFTMirr DATA attribute isn't supported yet */ + if (atype == AT_DATA) + if (rl[1].length != 0 || rl->vcn) + supported = 0; + } else { + llcn = &resize->last_lcn; + supported = 1; + } + + if (llcn->lcn < last_lcn) { + llcn->lcn = last_lcn; + llcn->inode = inode; + } + + if (supported) + return; + + if (resize->last_unsupp < last_lcn) + resize->last_unsupp = last_lcn; +} + + +static void collect_relocation_info(ntfs_resize_t *resize, runlist *rl) +{ + s64 lcn, lcn_length, start, len, inode; + s64 new_vol_size; /* (last LCN on the volume) + 1 */ + + lcn = rl->lcn; + lcn_length = rl->length; + inode = resize->ni->mft_no; + new_vol_size = resize->new_volume_size; + + if (lcn + lcn_length <= new_vol_size) + return; + + if (inode == FILE_Bitmap && resize->ctx->attr->type == AT_DATA) + return; + + start = lcn; + len = lcn_length; + + if (lcn < new_vol_size) { + start = new_vol_size; + len = lcn_length - (new_vol_size - lcn); + + if (!opt.info && (inode == FILE_MFTMirr)) { + err_printf("$MFTMirr can't be split up yet. Please try " + "a different size.\n"); + print_advise(resize->vol, lcn + lcn_length - 1); + exit(1); + } + } + + resize->relocations += len; + + if (!opt.info || !resize->new_volume_size) + return; + + printf("Relocation needed for inode %8lld attr 0x%x LCN 0x%08llx " + "length %6lld\n", (long long)inode, + (unsigned int)le32_to_cpu(resize->ctx->attr->type), + (unsigned long long)start, (long long)len); +} + +/** + * build_lcn_usage_bitmap + * + * lcn_bitmap has one bit for each cluster on the disk. Initially, lcn_bitmap + * has no bits set. As each attribute record is read the bits in lcn_bitmap are + * checked to ensure that no other file already references that cluster. + * + * This serves as a rudimentary "chkdsk" operation. + */ +static void build_lcn_usage_bitmap(ntfs_volume *vol, ntfsck_t *fsck) +{ + s64 inode; + ATTR_RECORD *a; + runlist *rl; + int i, j; + struct bitmap *lcn_bitmap = &fsck->lcn_bitmap; + + a = fsck->ctx->attr; + inode = fsck->ni->mft_no; + + if (!a->non_resident) + return; + + if (!(rl = ntfs_mapping_pairs_decompress(vol, a, NULL))) { + int err = errno; + perr_printf("ntfs_decompress_mapping_pairs"); + if (err == EIO) + printf("%s", corrupt_volume_msg); + exit(1); + } + + + for (i = 0; rl[i].length; i++) { + s64 lcn = rl[i].lcn; + s64 lcn_length = rl[i].length; + + /* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */ + if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED) + continue; + + /* FIXME: ntfs_mapping_pairs_decompress should return error */ + if (lcn < 0 || lcn_length <= 0) + err_exit("Corrupt runlist in inode %lld attr %x LCN " + "%llx length %llx\n", inode, + (unsigned int)le32_to_cpu(a->type), lcn, + lcn_length); + + for (j = 0; j < lcn_length; j++) { + + u64 k = (u64)lcn + j; + + if (k >= (u64)vol->nr_clusters) { + + long long outsiders = lcn_length - j; + + fsck->outsider += outsiders; + + if (++fsck->show_outsider <= 10 || opt.verbose) + printf("Outside of the volume reference" + " for inode %lld at %lld:%lld\n", + inode, (long long)k, outsiders); + + break; + } + + if (ntfs_bit_get_and_set(lcn_bitmap->bm, k, 1)) { + if (++fsck->multi_ref <= 10 || opt.verbose) + printf("Cluster %lld is referenced " + "multiply times!\n", + (long long)k); + continue; + } + } + fsck->inuse += lcn_length; + } + free(rl); +} + + +static ntfs_attr_search_ctx *attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec) +{ + ntfs_attr_search_ctx *ret; + + if ((ret = ntfs_attr_get_search_ctx(ni, mrec)) == NULL) + perr_printf("ntfs_attr_get_search_ctx"); + + return ret; +} + +/** + * walk_attributes + * + * For a given MFT Record, iterate through all its attributes. Any non-resident + * data runs will be marked in lcn_bitmap. + */ +static int walk_attributes(ntfs_volume *vol, ntfsck_t *fsck) +{ + if (!(fsck->ctx = attr_get_search_ctx(fsck->ni, NULL))) + return -1; + + while (!ntfs_attrs_walk(fsck->ctx)) { + if (fsck->ctx->attr->type == AT_END) + break; + build_lcn_usage_bitmap(vol, fsck); + } + + ntfs_attr_put_search_ctx(fsck->ctx); + return 0; +} + +/** + * compare_bitmaps + * + * Compare two bitmaps. In this case, $Bitmap as read from the disk and + * lcn_bitmap which we built from the MFT Records. + */ +static void compare_bitmaps(ntfs_volume *vol, struct bitmap *a) +{ + s64 i, pos, count; + int mismatch = 0; + int backup_boot = 0; + u8 bm[NTFS_BUF_SIZE]; + + printf("Accounting clusters ...\n"); + + pos = 0; + while (1) { + count = ntfs_attr_pread(vol->lcnbmp_na, pos, NTFS_BUF_SIZE, bm); + if (count == -1) + perr_exit("Couldn't get $Bitmap $DATA"); + + if (count == 0) { + if (a->size > pos) + err_exit("$Bitmap size is smaller than expected" + " (%lld != %lld)\n", a->size, pos); + break; + } + + for (i = 0; i < count; i++, pos++) { + s64 cl; /* current cluster */ + + if (a->size <= pos) + goto done; + + if (a->bm[pos] == bm[i]) + continue; + + for (cl = pos * 8; cl < (pos + 1) * 8; cl++) { + char bit; + + bit = ntfs_bit_get(a->bm, cl); + if (bit == ntfs_bit_get(bm, i * 8 + cl % 8)) + continue; + + if (!mismatch && !bit && !backup_boot && + cl == vol->nr_clusters / 2) { + /* FIXME: call also boot sector check */ + backup_boot = 1; + printf("Found backup boot sector in " + "the middle of the volume.\n"); + continue; + } + + if (++mismatch > 10 && !opt.verbose) + continue; + + printf("Cluster accounting failed at %lld " + "(0x%llx): %s cluster in " + "$Bitmap\n", (long long)cl, + (unsigned long long)cl, + bit ? "missing" : "extra"); + } + } + } +done: + if (mismatch) { + printf("Filesystem check failed! Totally %d cluster " + "accounting mismatches.\n", mismatch); + err_printf("%s", corrupt_volume_msg); + exit(1); + } +} + +/** + * progress_init + * + * Create and scale our progress bar. + */ +static void progress_init(struct progress_bar *p, u64 start, u64 stop, int flags) +{ + p->start = start; + p->stop = stop; + p->unit = 100.0 / (stop - start); + p->resolution = 100; + p->flags = flags; +} + +/** + * progress_update + * + * Update the progress bar and tell the user. + */ +static void progress_update(struct progress_bar *p, u64 current) +{ + float percent; + + if (!(p->flags & NTFS_PROGBAR)) + return; + if (p->flags & NTFS_PROGBAR_SUPPRESS) + return; + + /* WARNING: don't modify the texts, external tools grep for them */ + percent = p->unit * current; + if (current != p->stop) { + if ((current - p->start) % p->resolution) + return; + printf("%6.2f percent completed\r", percent); + } else + printf("100.00 percent completed\n"); + fflush(stdout); +} + +static int inode_close(ntfs_inode *ni) +{ + if (ntfs_inode_close(ni)) { + perr_printf("ntfs_inode_close for inode %llu", + (unsigned long long)ni->mft_no); + return -1; + } + return 0; +} + +/** + * walk_inodes + * + * Read each record in the MFT, skipping the unused ones, and build up a bitmap + * from all the non-resident attributes. + */ +static int build_allocation_bitmap(ntfs_volume *vol, ntfsck_t *fsck) +{ + s64 nr_mft_records, inode = 0; + ntfs_inode *ni; + struct progress_bar progress; + int pb_flags = 0; /* progress bar flags */ + + /* WARNING: don't modify the text, external tools grep for it */ + printf("Checking filesystem consistency ...\n"); + + if (fsck->flags & NTFSCK_PROGBAR) + pb_flags |= NTFS_PROGBAR; + + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + + progress_init(&progress, inode, nr_mft_records - 1, pb_flags); + + for (; inode < nr_mft_records; inode++) { + progress_update(&progress, inode); + + if ((ni = ntfs_inode_open(vol, (MFT_REF)inode)) == NULL) { + /* FIXME: continue only if it make sense, e.g. + MFT record not in use based on $MFT bitmap */ + if (errno == EIO || errno == ENOENT) + continue; + perr_printf("Reading inode %lld failed", inode); + return -1; + } + + if (ni->mrec->base_mft_record) + goto close_inode; + + fsck->ni = ni; + if (walk_attributes(vol, fsck) != 0) { + inode_close(ni); + return -1; + } +close_inode: + if (inode_close(ni) != 0) + return -1; + } + return 0; +} + +static void build_resize_constraints(ntfs_resize_t *resize) +{ + s64 i; + runlist *rl; + + if (!resize->ctx->attr->non_resident) + return; + + if (!(rl = ntfs_mapping_pairs_decompress(resize->vol, + resize->ctx->attr, NULL))) + perr_exit("ntfs_decompress_mapping_pairs"); + + for (i = 0; rl[i].length; i++) { + /* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */ + if (rl[i].lcn == LCN_HOLE || rl[i].lcn == LCN_RL_NOT_MAPPED) + continue; + + collect_resize_constraints(resize, rl + i); + if (resize->shrink) + collect_relocation_info(resize, rl + i); + } + free(rl); +} + +static void resize_constraints_by_attributes(ntfs_resize_t *resize) +{ + if (!(resize->ctx = attr_get_search_ctx(resize->ni, NULL))) + exit(1); + + while (!ntfs_attrs_walk(resize->ctx)) { + if (resize->ctx->attr->type == AT_END) + break; + build_resize_constraints(resize); + } + + ntfs_attr_put_search_ctx(resize->ctx); +} + +static void set_resize_constraints(ntfs_resize_t *resize) +{ + s64 nr_mft_records, inode; + ntfs_inode *ni; + + printf("Collecting resizing constraints ...\n"); + + nr_mft_records = resize->vol->mft_na->initialized_size >> + resize->vol->mft_record_size_bits; + + for (inode = 0; inode < nr_mft_records; inode++) { + + ni = ntfs_inode_open(resize->vol, (MFT_REF)inode); + if (ni == NULL) { + if (errno == EIO || errno == ENOENT) + continue; + perr_exit("Reading inode %lld failed", inode); + } + + if (ni->mrec->base_mft_record) + goto close_inode; + + resize->ni = ni; + resize_constraints_by_attributes(resize); +close_inode: + if (inode_close(ni) != 0) + exit(1); + } +} + +static void rl_fixup(runlist **rl) +{ + runlist *tmp = *rl; + + if (tmp->lcn == LCN_RL_NOT_MAPPED) { + s64 unmapped_len = tmp->length; + + ntfs_log_verbose("Skip unmapped run at the beginning ...\n"); + + if (!tmp->length) + err_exit("Empty unmapped runlist! Please report!\n"); + (*rl)++; + for (tmp = *rl; tmp->length; tmp++) + tmp->vcn -= unmapped_len; + } + + for (tmp = *rl; tmp->length; tmp++) { + if (tmp->lcn == LCN_RL_NOT_MAPPED) { + ntfs_log_verbose("Skip unmapped run at the end ...\n"); + + if (tmp[1].length) + err_exit("Unmapped runlist in the middle! " + "Please report!\n"); + tmp->lcn = LCN_ENOENT; + tmp->length = 0; + } + } +} + +static void replace_attribute_runlist(ntfs_volume *vol, + ntfs_attr_search_ctx *ctx, + runlist *rl) +{ + int mp_size, l; + void *mp; + ATTR_RECORD *a = ctx->attr; + + rl_fixup(&rl); + + if ((mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0, INT_MAX)) == -1) + perr_exit("ntfs_get_size_for_mapping_pairs"); + + if (a->name_length) { + u16 name_offs = le16_to_cpu(a->name_offset); + u16 mp_offs = le16_to_cpu(a->mapping_pairs_offset); + + if (name_offs >= mp_offs) + err_exit("Attribute name is after mapping pairs! " + "Please report!\n"); + } + + /* CHECKME: don't trust mapping_pairs is always the last item in the + attribute, instead check for the real size/space */ + l = (int)le32_to_cpu(a->length) - le16_to_cpu(a->mapping_pairs_offset); + if (mp_size > l) { + s64 remains_size; + char *next_attr; + + ntfs_log_verbose("Enlarging attribute header ...\n"); + + mp_size = (mp_size + 7) & ~7; + + ntfs_log_verbose("Old mp size : %d\n", l); + ntfs_log_verbose("New mp size : %d\n", mp_size); + ntfs_log_verbose("Bytes in use : %u\n", (unsigned int) + le32_to_cpu(ctx->mrec->bytes_in_use)); + + next_attr = (char *)a + le16_to_cpu(a->length); + l = mp_size - l; + + ntfs_log_verbose("Bytes in use new : %u\n", l + (unsigned int) + le32_to_cpu(ctx->mrec->bytes_in_use)); + ntfs_log_verbose("Bytes allocated : %u\n", (unsigned int) + le32_to_cpu(ctx->mrec->bytes_allocated)); + + remains_size = le32_to_cpu(ctx->mrec->bytes_in_use); + remains_size -= (next_attr - (char *)ctx->mrec); + + ntfs_log_verbose("increase : %d\n", l); + ntfs_log_verbose("shift : %lld\n", + (long long)remains_size); + + if (le32_to_cpu(ctx->mrec->bytes_in_use) + l > + le32_to_cpu(ctx->mrec->bytes_allocated)) + err_exit("Extended record needed (%u > %u), not yet " + "supported!\nPlease try to free less space.\n", + (unsigned int)le32_to_cpu(ctx->mrec-> + bytes_in_use) + l, + (unsigned int)le32_to_cpu(ctx->mrec-> + bytes_allocated)); + + memmove(next_attr + l, next_attr, remains_size); + ctx->mrec->bytes_in_use = cpu_to_le32(l + + le32_to_cpu(ctx->mrec->bytes_in_use)); + a->length += l; + } + + if (!(mp = calloc(1, mp_size))) + perr_exit("Couldn't get memory"); + + if (ntfs_mapping_pairs_build(vol, mp, mp_size, rl, 0, NULL)) + perr_exit("ntfs_mapping_pairs_build"); + + memmove((u8*)a + le16_to_cpu(a->mapping_pairs_offset), mp, mp_size); + + free(mp); +} + +static void set_bitmap_range(struct bitmap *bm, s64 pos, s64 length, u8 bit) +{ + while (length--) + ntfs_bit_set(bm->bm, pos++, bit); +} + +static void set_bitmap_clusters(struct bitmap *bm, runlist *rl, u8 bit) +{ + for (; rl->length; rl++) + set_bitmap_range(bm, rl->lcn, rl->length, bit); +} + +static void release_bitmap_clusters(struct bitmap *bm, runlist *rl) +{ + max_free_cluster_range = 0; + set_bitmap_clusters(bm, rl, 0); +} + +static void set_max_free_zone(s64 length, s64 end, runlist_element *rle) +{ + if (length > rle->length) { + rle->lcn = end - length; + rle->length = length; + } +} + +static int find_free_cluster(struct bitmap *bm, + runlist_element *rle, + s64 nr_vol_clusters, + int hint) +{ + /* FIXME: get rid of this 'static' variable */ + static s64 pos = 0; + s64 i, items = rle->length; + s64 free_zone = 0; + + if (pos >= nr_vol_clusters) + pos = 0; + if (!max_free_cluster_range) + max_free_cluster_range = nr_vol_clusters; + rle->lcn = rle->length = 0; + if (hint) + pos = nr_vol_clusters / 2; + i = pos; + + do { + if (!ntfs_bit_get(bm->bm, i)) { + if (++free_zone == items) { + set_max_free_zone(free_zone, i + 1, rle); + break; + } + } else { + set_max_free_zone(free_zone, i, rle); + free_zone = 0; + } + if (++i == nr_vol_clusters) { + set_max_free_zone(free_zone, i, rle); + i = free_zone = 0; + } + if (rle->length == max_free_cluster_range) + break; + } while (i != pos); + + if (i) + set_max_free_zone(free_zone, i, rle); + + if (!rle->lcn) { + errno = ENOSPC; + return -1; + } + if (rle->length < items && rle->length < max_free_cluster_range) { + max_free_cluster_range = rle->length; + ntfs_log_verbose("Max free range: %7lld \n", + (long long)max_free_cluster_range); + } + pos = rle->lcn + items; + if (pos == nr_vol_clusters) + pos = 0; + + set_bitmap_range(bm, rle->lcn, rle->length, 1); + return 0; +} + +static runlist *alloc_cluster(struct bitmap *bm, + s64 items, + s64 nr_vol_clusters, + int hint) +{ + runlist_element rle; + runlist *rl = NULL; + int rl_size, runs = 0; + s64 vcn = 0; + + if (items <= 0) { + errno = EINVAL; + return NULL; + } + + while (items > 0) { + + if (runs) + hint = 0; + rle.length = items; + if (find_free_cluster(bm, &rle, nr_vol_clusters, hint) == -1) + return NULL; + + rl_size = (runs + 2) * sizeof(runlist_element); + if (!(rl = (runlist *)realloc(rl, rl_size))) + return NULL; + + rl_set(rl + runs, vcn, rle.lcn, rle.length); + + vcn += rle.length; + items -= rle.length; + runs++; + } + + rl_set(rl + runs, vcn, -1LL, 0LL); + + if (runs > 1) { + ntfs_log_verbose("Multi-run allocation: \n"); + dump_runlist(rl); + } + return rl; +} + +static int read_all(struct ntfs_device *dev, void *buf, int count) +{ + int i; + + while (count > 0) { + + i = count; + if (!NDevReadOnly(dev)) + i = dev->d_ops->read(dev, buf, count); + + if (i < 0) { + if (errno != EAGAIN && errno != EINTR) + return -1; + } else if (i > 0) { + count -= i; + buf = i + (char *)buf; + } else + err_exit("Unexpected end of file!\n"); + } + return 0; +} + +static int write_all(struct ntfs_device *dev, void *buf, int count) +{ + int i; + + while (count > 0) { + + i = count; + if (!NDevReadOnly(dev)) + i = dev->d_ops->write(dev, buf, count); + + if (i < 0) { + if (errno != EAGAIN && errno != EINTR) + return -1; + } else { + count -= i; + buf = i + (char *)buf; + } + } + return 0; +} + +/** + * write_mft_record + * + * Write an MFT Record back to the disk. If the read-only command line option + * was given, this function will do nothing. + */ +static int write_mft_record(ntfs_volume *v, const MFT_REF mref, MFT_RECORD *buf) +{ + if (ntfs_mft_record_write(v, mref, buf)) + perr_exit("ntfs_mft_record_write"); + +// if (v->dev->d_ops->sync(v->dev) == -1) +// perr_exit("Failed to sync device"); + + return 0; +} + +static void lseek_to_cluster(ntfs_volume *vol, s64 lcn) +{ + off_t pos; + + pos = (off_t)(lcn * vol->cluster_size); + + if (vol->dev->d_ops->seek(vol->dev, pos, SEEK_SET) == (off_t)-1) + perr_exit("seek failed to position %lld", lcn); +} + +static void copy_clusters(ntfs_resize_t *resize, s64 dest, s64 src, s64 len) +{ + s64 i; + char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */ + ntfs_volume *vol = resize->vol; + + for (i = 0; i < len; i++) { + + lseek_to_cluster(vol, src + i); + + if (read_all(vol->dev, buff, vol->cluster_size) == -1) { + perr_printf("Failed to read from the disk"); + if (errno == EIO) + printf("%s", bad_sectors_warning_msg); + exit(1); + } + + lseek_to_cluster(vol, dest + i); + + if (write_all(vol->dev, buff, vol->cluster_size) == -1) { + perr_printf("Failed to write to the disk"); + if (errno == EIO) + printf("%s", bad_sectors_warning_msg); + exit(1); + } + + resize->relocations++; + progress_update(&resize->progress, resize->relocations); + } +} + +static void relocate_clusters(ntfs_resize_t *r, runlist *dest_rl, s64 src_lcn) +{ + /* collect_shrink_constraints() ensured $MFTMir DATA is one run */ + if (r->mref == FILE_MFTMirr && r->ctx->attr->type == AT_DATA) { + if (!r->mftmir_old) { + r->mftmir_rl.lcn = dest_rl->lcn; + r->mftmir_rl.length = dest_rl->length; + r->mftmir_old = src_lcn; + } else + err_exit("Multi-run $MFTMirr. Please report!\n"); + } + + for (; dest_rl->length; src_lcn += dest_rl->length, dest_rl++) + copy_clusters(r, dest_rl->lcn, src_lcn, dest_rl->length); +} + +static void rl_split_run(runlist **rl, int run, s64 pos) +{ + runlist *rl_new, *rle_new, *rle; + int items, new_size, size_head, size_tail; + s64 len_head, len_tail; + + items = rl_items(*rl); + new_size = (items + 1) * sizeof(runlist_element); + size_head = run * sizeof(runlist_element); + size_tail = (items - run - 1) * sizeof(runlist_element); + + if (!(rl_new = (runlist *)malloc(new_size))) + perr_exit("malloc"); + + rle_new = rl_new + run; + rle = *rl + run; + + memmove(rl_new, *rl, size_head); + memmove(rle_new + 2, rle + 1, size_tail); + + len_tail = rle->length - (pos - rle->lcn); + len_head = rle->length - len_tail; + + rl_set(rle_new, rle->vcn, rle->lcn, len_head); + rl_set(rle_new + 1, rle->vcn + len_head, rle->lcn + len_head, len_tail); + + ntfs_log_verbose("Splitting run at cluster %lld:\n", (long long)pos); + dump_run(rle); dump_run(rle_new); dump_run(rle_new + 1); + + free(*rl); + *rl = rl_new; +} + +static void rl_insert_at_run(runlist **rl, int run, runlist *ins) +{ + int items, ins_items; + int new_size, size_tail; + runlist *rle; + s64 vcn; + + items = rl_items(*rl); + ins_items = rl_items(ins) - 1; + new_size = ((items - 1) + ins_items) * sizeof(runlist_element); + size_tail = (items - run - 1) * sizeof(runlist_element); + + if (!(*rl = (runlist *)realloc(*rl, new_size))) + perr_exit("realloc"); + + rle = *rl + run; + + memmove(rle + ins_items, rle + 1, size_tail); + + for (vcn = rle->vcn; ins->length; rle++, vcn += ins->length, ins++) { + rl_set(rle, vcn, ins->lcn, ins->length); +// dump_run(rle); + } + + return; + + /* FIXME: fast path if ins_items = 1 */ +// (*rl + run)->lcn = ins->lcn; +} + +static void relocate_run(ntfs_resize_t *resize, runlist **rl, int run) +{ + s64 lcn, lcn_length; + s64 new_vol_size; /* (last LCN on the volume) + 1 */ + runlist *relocate_rl; /* relocate runlist to relocate_rl */ + int hint; + + lcn = (*rl + run)->lcn; + lcn_length = (*rl + run)->length; + new_vol_size = resize->new_volume_size; + + if (lcn + lcn_length <= new_vol_size) + return; + + if (lcn < new_vol_size) { + rl_split_run(rl, run, new_vol_size); + return; + } + + hint = (resize->mref == FILE_MFTMirr) ? 1 : 0; + if (!(relocate_rl = alloc_cluster(&resize->lcn_bitmap, lcn_length, + new_vol_size, hint))) + perr_exit("Cluster allocation failed for %llu:%lld", + resize->mref, lcn_length); + + /* FIXME: check $MFTMirr DATA isn't multi-run (or support it) */ + ntfs_log_verbose("Relocate record %7llu:0x%x:%08lld:0x%08llx:0x%08llx " + "--> 0x%08llx\n", (unsigned long long)resize->mref, + (unsigned int)le32_to_cpu(resize->ctx->attr->type), + (long long)lcn_length, + (unsigned long long)(*rl + run)->vcn, + (unsigned long long)lcn, + (unsigned long long)relocate_rl->lcn); + + relocate_clusters(resize, relocate_rl, lcn); + rl_insert_at_run(rl, run, relocate_rl); + + /* We don't release old clusters in the bitmap, that area isn't + used by the allocator and will be truncated later on */ + free(relocate_rl); + + resize->dirty_inode = DIRTY_ATTRIB; +} + +static void relocate_attribute(ntfs_resize_t *resize) +{ + ATTR_RECORD *a; + runlist *rl; + int i; + + a = resize->ctx->attr; + + if (!a->non_resident) + return; + + if (!(rl = ntfs_mapping_pairs_decompress(resize->vol, a, NULL))) + perr_exit("ntfs_decompress_mapping_pairs"); + + for (i = 0; rl[i].length; i++) { + s64 lcn = rl[i].lcn; + s64 lcn_length = rl[i].length; + + if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED) + continue; + + /* FIXME: ntfs_mapping_pairs_decompress should return error */ + if (lcn < 0 || lcn_length <= 0) + err_exit("Corrupt runlist in MTF %llu attr %x LCN " + "%llx length %llx\n", resize->mref, + (unsigned int)le32_to_cpu(a->type), + lcn, lcn_length); + + relocate_run(resize, &rl, i); + } + + if (resize->dirty_inode == DIRTY_ATTRIB) { + replace_attribute_runlist(resize->vol, resize->ctx, rl); + resize->dirty_inode = DIRTY_INODE; + } + + free(rl); +} + +static int is_mftdata(ntfs_resize_t *resize) +{ + if (resize->ctx->attr->type != AT_DATA) + return 0; + + if (resize->mref == 0) + return 1; + + if ( MREF(resize->mrec->base_mft_record) == 0 && + MSEQNO(resize->mrec->base_mft_record) != 0) + return 1; + + return 0; +} + +static int handle_mftdata(ntfs_resize_t *resize, int do_mftdata) +{ + ATTR_RECORD *attr = resize->ctx->attr; + VCN highest_vcn, lowest_vcn; + + if (do_mftdata) { + + if (!is_mftdata(resize)) + return 0; + + highest_vcn = sle64_to_cpu(attr->highest_vcn); + lowest_vcn = sle64_to_cpu(attr->lowest_vcn); + + if (resize->mft_highest_vcn != highest_vcn) + return 0; + + if (lowest_vcn == 0) + resize->mft_highest_vcn = lowest_vcn; + else + resize->mft_highest_vcn = lowest_vcn - 1; + + } else if (is_mftdata(resize)) { + + highest_vcn = sle64_to_cpu(attr->highest_vcn); + + if (resize->mft_highest_vcn < highest_vcn) + resize->mft_highest_vcn = highest_vcn; + + return 0; + } + + return 1; +} + +static void relocate_attributes(ntfs_resize_t *resize, int do_mftdata) +{ + int ret; + + if (!(resize->ctx = attr_get_search_ctx(NULL, resize->mrec))) + exit(1); + + while (!ntfs_attrs_walk(resize->ctx)) { + if (resize->ctx->attr->type == AT_END) + break; + + if (handle_mftdata(resize, do_mftdata) == 0) + continue; + + ret = ntfs_inode_badclus_bad(resize->mref, resize->ctx->attr); + if (ret == -1) + perr_exit("Bad sector list check failed"); + else if (ret == 1) + continue; + + if (resize->mref == FILE_Bitmap && + resize->ctx->attr->type == AT_DATA) + continue; + + relocate_attribute(resize); + } + + ntfs_attr_put_search_ctx(resize->ctx); +} + +static void relocate_inode(ntfs_resize_t *resize, MFT_REF mref, int do_mftdata) +{ + if (ntfs_file_record_read(resize->vol, mref, &resize->mrec, NULL)) { + /* FIXME: continue only if it make sense, e.g. + MFT record not in use based on $MFT bitmap */ + if (errno == EIO || errno == ENOENT) + return; + perr_exit("ntfs_file_record_record"); + } + + if (!(resize->mrec->flags & MFT_RECORD_IN_USE)) + return; + + resize->mref = mref; + resize->dirty_inode = DIRTY_NONE; + + relocate_attributes(resize, do_mftdata); + + if (resize->dirty_inode == DIRTY_INODE) { +// if (vol->dev->d_ops->sync(vol->dev) == -1) +// perr_exit("Failed to sync device"); + if (write_mft_record(resize->vol, mref, resize->mrec)) + perr_exit("Couldn't update record %llu", mref); + } +} + +static void relocate_inodes(ntfs_resize_t *resize) +{ + s64 nr_mft_records; + MFT_REF mref; + VCN highest_vcn; + + printf("Relocating needed data ...\n"); + + progress_init(&resize->progress, 0, resize->relocations, resize->progress.flags); + resize->relocations = 0; + + resize->mrec = (MFT_RECORD *)malloc(resize->vol->mft_record_size); + if (!resize->mrec) + perr_exit("malloc failed"); + + nr_mft_records = resize->vol->mft_na->initialized_size >> + resize->vol->mft_record_size_bits; + + for (mref = 0; mref < (MFT_REF)nr_mft_records; mref++) + relocate_inode(resize, mref, 0); + + while (1) { + highest_vcn = resize->mft_highest_vcn; + mref = nr_mft_records; + do { + relocate_inode(resize, --mref, 1); + if (resize->mft_highest_vcn == 0) + goto done; + } while (mref); + + if (highest_vcn == resize->mft_highest_vcn) + err_exit("Sanity check failed! Highest_vcn = %lld. " + "Please report!\n", highest_vcn); + } +done: + free(resize->mrec); +} + +static void print_hint(ntfs_volume *vol, const char *s, struct llcn_t llcn) +{ + s64 runs_b, runs_mb; + + if (llcn.lcn == 0) + return; + + runs_b = llcn.lcn * vol->cluster_size; + runs_mb = rounded_up_division(runs_b, NTFS_MBYTE); + printf("%-19s: %9lld MB %8lld\n", s, (long long)runs_mb, + (long long)llcn.inode); +} + +/** + * advise_on_resize + * + * The metadata file $Bitmap has one bit for each cluster on disk. This has + * already been read into lcn_bitmap. By looking for the last used cluster on + * the disk, we can work out by how much we can shrink the volume. + */ +static void advise_on_resize(ntfs_resize_t *resize) +{ + ntfs_volume *vol = resize->vol; + + if (opt.verbose) { + printf("Estimating smallest shrunken size supported ...\n"); + printf("File feature Last used at By inode\n"); + print_hint(vol, "$MFT", resize->last_mft); + print_hint(vol, "Multi-Record", resize->last_multi_mft); + print_hint(vol, "$MFTMirr", resize->last_mftmir); + print_hint(vol, "Compressed", resize->last_compressed); + print_hint(vol, "Sparse", resize->last_sparse); + print_hint(vol, "Ordinary", resize->last_lcn); + } + + print_advise(vol, resize->last_unsupp); +} + + +static void rl_expand(runlist **rl, const VCN last_vcn) +{ + int len; + runlist *p = *rl; + + len = rl_items(p) - 1; + if (len <= 0) + err_exit("rl_expand: bad runlist length: %d\n", len); + + if (p[len].vcn > last_vcn) + err_exit("rl_expand: length is already more than requested " + "(%lld > %lld)\n", p[len].vcn, last_vcn); + + if (p[len - 1].lcn == LCN_HOLE) { + + p[len - 1].length += last_vcn - p[len].vcn; + p[len].vcn = last_vcn; + + } else if (p[len - 1].lcn >= 0) { + + p = realloc(*rl, (++len + 1) * sizeof(runlist_element)); + if (!p) + perr_exit("rl_expand: realloc"); + + p[len - 1].lcn = LCN_HOLE; + p[len - 1].length = last_vcn - p[len - 1].vcn; + rl_set(p + len, last_vcn, LCN_ENOENT, 0LL); + *rl = p; + + } else + err_exit("rl_expand: bad LCN: %lld\n", p[len - 1].lcn); +} + +static void rl_truncate(runlist **rl, const VCN last_vcn) +{ + int len; + VCN vcn; + + len = rl_items(*rl) - 1; + if (len <= 0) + err_exit("rl_truncate: bad runlist length: %d\n", len); + + vcn = (*rl)[len].vcn; + + if (vcn < last_vcn) + rl_expand(rl, last_vcn); + + else if (vcn > last_vcn) + if (ntfs_rl_truncate(rl, last_vcn) == -1) + perr_exit("ntfs_rl_truncate"); +} + +/** + * bitmap_file_data_fixup + * + * $Bitmap can overlap the end of the volume. Any bits in this region + * must be set. This region also encompasses the backup boot sector. + */ +static void bitmap_file_data_fixup(s64 cluster, struct bitmap *bm) +{ + for (; cluster < bm->size << 3; cluster++) + ntfs_bit_set(bm->bm, (u64)cluster, 1); +} + +/** + * truncate_badclust_bad_attr + * + * The metadata file $BadClus needs to be shrunk. + * + * FIXME: this function should go away and instead using a generalized + * "truncate_bitmap_data_attr()" + */ +static void truncate_badclust_bad_attr(ntfs_resize_t *resize) +{ + ATTR_RECORD *a; + runlist *rl_bad; + s64 nr_clusters = resize->new_volume_size; + ntfs_volume *vol = resize->vol; + + a = resize->ctx->attr; + if (!a->non_resident) + /* FIXME: handle resident attribute value */ + err_exit("Resident attribute in $BadClust isn't supported!\n"); + + if (!(rl_bad = ntfs_mapping_pairs_decompress(vol, a, NULL))) + perr_exit("ntfs_mapping_pairs_decompress"); + + rl_truncate(&rl_bad, nr_clusters); + + a->highest_vcn = cpu_to_le64(nr_clusters - 1LL); + a->allocated_size = cpu_to_le64(nr_clusters * vol->cluster_size); + a->data_size = cpu_to_le64(nr_clusters * vol->cluster_size); + + replace_attribute_runlist(vol, resize->ctx, rl_bad); + + free(rl_bad); +} + +/** + * realloc_bitmap_data_attr + * + * Reallocate the metadata file $Bitmap. It must be large enough for one bit + * per cluster of the shrunken volume. Also it must be a of 8 bytes in size. + */ +static void realloc_bitmap_data_attr(ntfs_resize_t *resize, + runlist **rl, + s64 nr_bm_clusters) +{ + s64 i; + ntfs_volume *vol = resize->vol; + ATTR_RECORD *a = resize->ctx->attr; + s64 new_size = resize->new_volume_size; + struct bitmap *bm = &resize->lcn_bitmap; + + if (!(*rl = ntfs_mapping_pairs_decompress(vol, a, NULL))) + perr_exit("ntfs_mapping_pairs_decompress"); + + release_bitmap_clusters(bm, *rl); + free(*rl); + + for (i = vol->nr_clusters; i < new_size; i++) + ntfs_bit_set(bm->bm, i, 0); + + if (!(*rl = alloc_cluster(bm, nr_bm_clusters, new_size, 0))) + perr_exit("Couldn't allocate $Bitmap clusters"); +} + +static void realloc_lcn_bitmap(ntfs_resize_t *resize, s64 bm_bsize) +{ + u8 *tmp; + + if (!(tmp = realloc(resize->lcn_bitmap.bm, bm_bsize))) + perr_exit("realloc"); + + resize->lcn_bitmap.bm = tmp; + resize->lcn_bitmap.size = bm_bsize; + bitmap_file_data_fixup(resize->new_volume_size, &resize->lcn_bitmap); +} + +/** + * truncate_bitmap_data_attr + */ +static void truncate_bitmap_data_attr(ntfs_resize_t *resize) +{ + ATTR_RECORD *a; + runlist *rl; + s64 bm_bsize, size; + s64 nr_bm_clusters; + ntfs_volume *vol = resize->vol; + + a = resize->ctx->attr; + if (!a->non_resident) + /* FIXME: handle resident attribute value */ + err_exit("Resident attribute in $Bitmap isn't supported!\n"); + + bm_bsize = nr_clusters_to_bitmap_byte_size(resize->new_volume_size); + nr_bm_clusters = rounded_up_division(bm_bsize, vol->cluster_size); + + if (resize->shrink) { + realloc_bitmap_data_attr(resize, &rl, nr_bm_clusters); + realloc_lcn_bitmap(resize, bm_bsize); + } else { + realloc_lcn_bitmap(resize, bm_bsize); + realloc_bitmap_data_attr(resize, &rl, nr_bm_clusters); + } + + a->highest_vcn = cpu_to_le64(nr_bm_clusters - 1LL); + a->allocated_size = cpu_to_le64(nr_bm_clusters * vol->cluster_size); + a->data_size = cpu_to_le64(bm_bsize); + a->initialized_size = cpu_to_le64(bm_bsize); + + replace_attribute_runlist(vol, resize->ctx, rl); + + /* + * FIXME: update allocated/data sizes and timestamps in $FILE_NAME + * attribute too, for now chkdsk will do this for us. + */ + + size = ntfs_rl_pwrite(vol, rl, 0, 0, bm_bsize, resize->lcn_bitmap.bm); + if (bm_bsize != size) { + if (size == -1) + perr_exit("Couldn't write $Bitmap"); + err_exit("Couldn't write full $Bitmap file (%lld from %lld)\n", + (long long)size, (long long)bm_bsize); + } + + free(rl); +} + +/** + * lookup_data_attr + * + * Find the $DATA attribute (with or without a name) for the given MFT reference + * (inode number). + */ +static void lookup_data_attr(ntfs_volume *vol, + MFT_REF mref, + const char *aname, + ntfs_attr_search_ctx **ctx) +{ + ntfs_inode *ni; + ntfschar *ustr; + int len = 0; + + if (!(ni = ntfs_inode_open(vol, mref))) + perr_exit("ntfs_open_inode"); + + if (!(*ctx = attr_get_search_ctx(ni, NULL))) + exit(1); + + if ((ustr = ntfs_str2ucs(aname, &len)) == NULL) { + perr_printf("Couldn't convert '%s' to Unicode", aname); + exit(1); + } + + if (ntfs_attr_lookup(AT_DATA, ustr, len, 0, 0, NULL, 0, *ctx)) + perr_exit("ntfs_lookup_attr"); + + ntfs_ucsfree(ustr); +} + +static int check_bad_sectors(ntfs_volume *vol) +{ + ntfs_attr_search_ctx *ctx; + ntfs_inode *base_ni; + runlist *rl; + s64 i, badclusters = 0; + + ntfs_log_verbose("Checking for bad sectors ...\n"); + + lookup_data_attr(vol, FILE_BadClus, "$Bad", &ctx); + + base_ni = ctx->base_ntfs_ino; + if (!base_ni) + base_ni = ctx->ntfs_ino; + + if (NInoAttrList(base_ni)) { + err_printf("Hopelessly many bad sectors has been detected!\n"); + printf("%s", many_bad_sectors_msg); + exit(1); + } + + if (!ctx->attr->non_resident) + err_exit("Resident attribute in $BadClust! Please report to " + "%s\n", NTFS_DEV_LIST); + /* + * FIXME: The below would be partial for non-base records in the + * not yet supported multi-record case. Alternatively use audited + * ntfs_attr_truncate after an umount & mount. + */ + if (!(rl = ntfs_mapping_pairs_decompress(vol, ctx->attr, NULL))) + perr_exit("Decompressing $BadClust:$Bad mapping pairs failed"); + + for (i = 0; rl[i].length; i++) { + /* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */ + if (rl[i].lcn == LCN_HOLE || rl[i].lcn == LCN_RL_NOT_MAPPED) + continue; + + badclusters += rl[i].length; + ntfs_log_verbose("Bad cluster: %#8llx - %#llx (%lld)\n", + rl[i].lcn, rl[i].lcn + rl[i].length - 1, + rl[i].length); + } + + if (badclusters) { + printf("%sThis software has detected that the disk has at least" + " %lld bad sector%s.\n", + !opt.badsectors ? NERR_PREFIX : "WARNING: ", + badclusters, badclusters - 1 ? "s" : ""); + if (!opt.badsectors) { + printf("%s", bad_sectors_warning_msg); + exit(1); + } else + printf("WARNING: Bad sectors can cause reliability " + "problems and massive data loss!!!\n"); + } + + free(rl); + ntfs_attr_put_search_ctx(ctx); + + return badclusters; +} + +/** + * truncate_badclust_file + * + * Shrink the $BadClus file to match the new volume size. + */ +static void truncate_badclust_file(ntfs_resize_t *resize) +{ + printf("Updating $BadClust file ...\n"); + + lookup_data_attr(resize->vol, FILE_BadClus, "$Bad", &resize->ctx); + /* FIXME: sanity_check_attr(ctx->attr); */ + truncate_badclust_bad_attr(resize); + + if (write_mft_record(resize->vol, resize->ctx->ntfs_ino->mft_no, + resize->ctx->mrec)) + perr_exit("Couldn't update $BadClust"); + + ntfs_attr_put_search_ctx(resize->ctx); +} + +/** + * truncate_bitmap_file + * + * Shrink the $Bitmap file to match the new volume size. + */ +static void truncate_bitmap_file(ntfs_resize_t *resize) +{ + printf("Updating $Bitmap file ...\n"); + + lookup_data_attr(resize->vol, FILE_Bitmap, NULL, &resize->ctx); + truncate_bitmap_data_attr(resize); + + if (write_mft_record(resize->vol, resize->ctx->ntfs_ino->mft_no, + resize->ctx->mrec)) + perr_exit("Couldn't update $Bitmap"); + + ntfs_attr_put_search_ctx(resize->ctx); +} + +/** + * setup_lcn_bitmap + * + * Allocate a block of memory with one bit for each cluster of the disk. + * All the bits are set to 0, except those representing the region beyond the + * end of the disk. + */ +static int setup_lcn_bitmap(struct bitmap *bm, s64 nr_clusters) +{ + /* Determine lcn bitmap byte size and allocate it. */ + bm->size = rounded_up_division(nr_clusters, 8); + + if (!(bm->bm = (unsigned char *)calloc(1, bm->size))) + return -1; + + bitmap_file_data_fixup(nr_clusters, bm); + return 0; +} + +/** + * update_bootsector + * + * FIXME: should be done using ntfs_* functions + */ +static void update_bootsector(ntfs_resize_t *r) +{ + NTFS_BOOT_SECTOR bs; + s64 bs_size = sizeof(NTFS_BOOT_SECTOR); + ntfs_volume *vol = r->vol; + + printf("Updating Boot record ...\n"); + + if (vol->dev->d_ops->seek(vol->dev, 0, SEEK_SET) == (off_t)-1) + perr_exit("lseek"); + + if (vol->dev->d_ops->read(vol->dev, &bs, bs_size) == -1) + perr_exit("read() error"); + + bs.number_of_sectors = cpu_to_sle64(r->new_volume_size * + bs.bpb.sectors_per_cluster); + + if (r->mftmir_old) { + r->progress.flags |= NTFS_PROGBAR_SUPPRESS; + copy_clusters(r, r->mftmir_rl.lcn, r->mftmir_old, + r->mftmir_rl.length); + bs.mftmirr_lcn = cpu_to_le64(r->mftmir_rl.lcn); + r->progress.flags &= ~NTFS_PROGBAR_SUPPRESS; + } + + if (vol->dev->d_ops->seek(vol->dev, 0, SEEK_SET) == (off_t)-1) + perr_exit("lseek"); + + if (!opt.ro_flag) + if (vol->dev->d_ops->write(vol->dev, &bs, bs_size) == -1) + perr_exit("write() error"); +} + +/** + * vol_size + */ +static s64 vol_size(ntfs_volume *v, s64 nr_clusters) +{ + /* add one sector_size for the backup boot sector */ + return nr_clusters * v->cluster_size + v->sector_size; +} + +/** + * print_vol_size + * + * Print the volume size in bytes and decimal megabytes. + */ +static void print_vol_size(const char *str, s64 bytes) +{ + printf("%s: %lld bytes (%lld MB)\n", str, (long long)bytes, + (long long)rounded_up_division(bytes, NTFS_MBYTE)); +} + +/** + * print_disk_usage + * + * Display the amount of disk space in use. + */ +static void print_disk_usage(ntfs_volume *vol, s64 nr_used_clusters) +{ + s64 total, used; + + total = vol->nr_clusters * vol->cluster_size; + used = nr_used_clusters * vol->cluster_size; + + /* WARNING: don't modify the text, external tools grep for it */ + printf("Space in use : %lld MB (%.1f%%)\n", + (long long)rounded_up_division(used, NTFS_MBYTE), + 100.0 * ((float)used / total)); +} + +static void print_num_of_relocations(ntfs_resize_t *resize) +{ + s64 relocations = resize->relocations * resize->vol->cluster_size; + + printf("Needed relocations : %lld (%lld MB)\n", + (long long)resize->relocations, (long long) + rounded_up_division(relocations, NTFS_MBYTE)); +} + +/** + * mount_volume + * + * First perform some checks to determine if the volume is already mounted, or + * is dirty (Windows wasn't shutdown properly). If everything is OK, then mount + * the volume (load the metadata into memory). + */ +static ntfs_volume *mount_volume(void) +{ + unsigned long mntflag; + ntfs_volume *vol = NULL; + + if (ntfs_check_if_mounted(opt.volume, &mntflag)) { + perr_printf("Failed to check '%s' mount state", opt.volume); + printf("Probably /etc/mtab is missing. It's too risky to " + "continue. You might try\nan another Linux distro.\n"); + exit(1); + } + if (mntflag & NTFS_MF_MOUNTED) { + if (!(mntflag & NTFS_MF_READONLY)) + err_exit("Device '%s' is mounted read-write. " + "You must 'umount' it first.\n", opt.volume); + if (!opt.ro_flag) + err_exit("Device '%s' is mounted. " + "You must 'umount' it first.\n", opt.volume); + } + + if (!(vol = ntfs_mount(opt.volume, opt.ro_flag /*| MS_NOATIME*/))) { + + int err = errno; + + perr_printf("Opening '%s' as NTFS failed", opt.volume); + if (err == EINVAL) + printf(invalid_ntfs_msg, opt.volume); + else if (err == EIO) + printf("%s", corrupt_volume_msg); + else if (err == EPERM) + printf("%s", hibernated_volume_msg); + else if (err == EOPNOTSUPP) + printf("%s", unclean_journal_msg); + else if (err == EBUSY) + printf("%s", opened_volume_msg); + exit(1); + } + + if (vol->flags & VOLUME_IS_DIRTY) + if (opt.force-- <= 0) + err_exit("Volume is scheduled for check.\nRun chkdsk /f" + " and please try again, or see option -f.\n"); + + if (NTFS_MAX_CLUSTER_SIZE < vol->cluster_size) + err_exit("Cluster size %u is too large!\n", + (unsigned int)vol->cluster_size); + + printf("Device name : %s\n", opt.volume); + printf("NTFS volume version: %d.%d\n", vol->major_ver, vol->minor_ver); + if (ntfs_version_is_supported(vol)) + perr_exit("Unknown NTFS version"); + + printf("Cluster size : %u bytes\n", + (unsigned int)vol->cluster_size); + print_vol_size("Current volume size", vol_size(vol, vol->nr_clusters)); + + return vol; +} + +/** + * prepare_volume_fixup + * + * Set the volume's dirty flag and wipe the filesystem journal. When Windows + * boots it will automatically run chkdsk to check for any problems. If the + * read-only command line option was given, this function will do nothing. + */ +static void prepare_volume_fixup(ntfs_volume *vol) +{ + u16 flags; + + flags = vol->flags | VOLUME_IS_DIRTY; + if (vol->major_ver >= 2) + flags |= VOLUME_MOUNTED_ON_NT4; + + printf("Schedule chkdsk for NTFS consistency check at Windows " + "boot time ...\n"); + + if (ntfs_volume_write_flags(vol, flags)) + perr_exit("Failed to set $Volume dirty"); + + if (vol->dev->d_ops->sync(vol->dev) == -1) + perr_exit("Failed to sync device"); + + printf("Resetting $LogFile ... (this might take a while)\n"); + + if (ntfs_logfile_reset(vol)) + perr_exit("Failed to reset $LogFile"); + + if (vol->dev->d_ops->sync(vol->dev) == -1) + perr_exit("Failed to sync device"); +} + + +static void set_disk_usage_constraint(ntfs_resize_t *resize) +{ + /* last lcn for a filled up volume (no empty space) */ + s64 last = resize->inuse - 1; + + if (resize->last_unsupp < last) + resize->last_unsupp = last; +} + +static void check_resize_constraints(ntfs_resize_t *resize) +{ + s64 new_size = resize->new_volume_size; + + /* FIXME: resize.shrink true also if only -i is used */ + if (!resize->shrink) + return; + + if (resize->inuse == resize->vol->nr_clusters) + err_exit("Volume is full. To shrink it, " + "delete unused files.\n"); + + if (opt.info) + return; + + /* FIXME: reserve some extra space so Windows can boot ... */ + if (new_size < resize->inuse) + err_exit("New size can't be less than the space already" + " occupied by data.\nYou either need to delete unused" + " files or see the -i option.\n"); + + if (new_size <= resize->last_unsupp) + err_exit("The fragmentation type, you have, isn't " + "supported yet. Rerun ntfsresize\nwith " + "the -i option to estimate the smallest " + "shrunken volume size supported.\n"); + + print_num_of_relocations(resize); +} + +static void check_cluster_allocation(ntfs_volume *vol, ntfsck_t *fsck) +{ + memset(fsck, 0, sizeof(ntfsck_t)); + + if (opt.show_progress) + fsck->flags |= NTFSCK_PROGBAR; + + if (setup_lcn_bitmap(&fsck->lcn_bitmap, vol->nr_clusters) != 0) + perr_exit("Failed to setup allocation bitmap"); + if (build_allocation_bitmap(vol, fsck) != 0) + exit(1); + if (fsck->outsider || fsck->multi_ref) { + err_printf("Filesystem check failed!\n"); + if (fsck->outsider) + err_printf("%d clusters are referenced outside " + "of the volume.\n", fsck->outsider); + if (fsck->multi_ref) + err_printf("%d clusters are referenced multiply" + " times.\n", fsck->multi_ref); + printf("%s", corrupt_volume_msg); + exit(1); + } + + compare_bitmaps(vol, &fsck->lcn_bitmap); +} + +int main(int argc, char **argv) +{ + ntfsck_t fsck; + ntfs_resize_t resize; + s64 new_size = 0; /* in clusters; 0 = --info w/o --size */ + s64 device_size; /* in bytes */ + ntfs_volume *vol; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + printf("%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION); + + if (!parse_options(argc, argv)) + return 1; + + utils_set_locale(); + + if ((vol = mount_volume()) == NULL) + err_exit("Couldn't open volume '%s'!\n", opt.volume); + + device_size = ntfs_device_size_get(vol->dev, vol->sector_size); + device_size *= vol->sector_size; + if (device_size <= 0) + err_exit("Couldn't get device size (%lld)!\n", device_size); + + print_vol_size("Current device size", device_size); + + if (device_size < vol->nr_clusters * vol->cluster_size) + err_exit("Current NTFS volume size is bigger than the device " + "size!\nCorrupt partition table or incorrect device " + "partitioning?\n"); + + if (!opt.bytes && !opt.info) + opt.bytes = device_size; + + /* Take the integer part: don't make the volume bigger than requested */ + new_size = opt.bytes / vol->cluster_size; + + /* Backup boot sector at the end of device isn't counted in NTFS + volume size thus we have to reserve space for it. */ + if (new_size) + --new_size; + + if (!opt.info) { + print_vol_size("New volume size ", vol_size(vol, new_size)); + if (device_size < opt.bytes) + err_exit("New size can't be bigger than the device size" + ".\nIf you want to enlarge NTFS then first " + "enlarge the device size by e.g. fdisk.\n"); + } + + if (!opt.info && (new_size == vol->nr_clusters || + (opt.bytes == device_size && + new_size == vol->nr_clusters - 1))) { + printf("Nothing to do: NTFS volume size is already OK.\n"); + exit(0); + } + + memset(&resize, 0, sizeof(resize)); + resize.vol = vol; + resize.new_volume_size = new_size; + /* This is also true if --info was used w/o --size (new_size = 0) */ + if (new_size < vol->nr_clusters) + resize.shrink = 1; + if (opt.show_progress) + resize.progress.flags |= NTFS_PROGBAR; + /* + * Checking and __reporting__ of bad sectors must be done before cluster + * allocation check because chkdsk doesn't fix $Bitmap's w/ bad sectors + * thus users would (were) quite confused why chkdsk doesn't work. + */ + resize.badclusters = check_bad_sectors(vol); + + check_cluster_allocation(vol, &fsck); + + print_disk_usage(vol, fsck.inuse); + + resize.inuse = fsck.inuse; + resize.lcn_bitmap = fsck.lcn_bitmap; + + set_resize_constraints(&resize); + set_disk_usage_constraint(&resize); + check_resize_constraints(&resize); + + if (opt.info) { + advise_on_resize(&resize); + exit(0); + } + + if (opt.force-- <= 0 && !opt.ro_flag) { + printf("%s", resize_warning_msg); + proceed_question(); + } + + /* FIXME: performance - relocate logfile here if it's needed */ + prepare_volume_fixup(vol); + + if (resize.relocations) + relocate_inodes(&resize); + + truncate_badclust_file(&resize); + truncate_bitmap_file(&resize); + update_bootsector(&resize); + + /* We don't create backup boot sector because we don't know where the + partition will be split. The scheduled chkdsk will fix it */ + + if (opt.ro_flag) { + printf("The read-only test run ended successfully.\n"); + exit(0); + } + + /* WARNING: don't modify the texts, external tools grep for them */ + printf("Syncing device ...\n"); + if (vol->dev->d_ops->sync(vol->dev) == -1) + perr_exit("fsync"); + + printf("Successfully resized NTFS on device '%s'.\n", vol->dev->d_name); + if (resize.shrink) + printf("%s", resize_important_msg); + + return 0; +} diff --git a/ntfsprogs/ntfsrm.c b/ntfsprogs/ntfsrm.c new file mode 100644 index 00000000..a651556f --- /dev/null +++ b/ntfsprogs/ntfsrm.c @@ -0,0 +1,1070 @@ +/** + * ntfsrm - Part of the Linux-NTFS project. + * + * Copyright (c) 2004-2005 Richard Russon + * + * This utility will delete files from an NTFS volume. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 "config.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_TIME_H +#include +#endif + +#include "ntfsrm.h" +#include "rich.h" +#include "utils.h" +#include "debug.h" +#include "dir.h" +#include "lcnalloc.h" +#include "mft.h" +#include "ntfstime.h" +#include "version.h" +#include "tree.h" +#include "index.h" +#include "inode.h" +#include "logging.h" + +static const char *EXEC_NAME = "ntfsrm"; +static struct options opts; +static const char *space_line = " "; + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Delete files from an NTFS volume.\n\n", + EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c) 2004 Richard Russon\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device file\n" + "\n" + " -r --recursive Delete files in subdirectories\n" + " -i --interactive Ask before deleting files\n" + //" -I num --inode num Delete the file with this inode number\n" + //" -U --unlink Unlink the file, deleting all references \n" + "\n" + " -D --no-dirty Do not mark volume dirty (require chkdsk)\n" + " -n --no-action Do not write to disk\n" + " -f --force Use less caution\n" + " -h --help Print this help\n" + " -q --quiet Less output\n" + " -V --version Version information\n" + " -v --verbose More output\n\n", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char **argv) +{ + static const char *sopt = "-Dfh?inqRrVv"; //"-Dfh?I:inqRrUVv"; + static const struct option lopt[] = { + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + //{ "inode", required_argument, NULL, 'I' }, + { "interactive", no_argument, NULL, 'i' }, + { "no-action", no_argument, NULL, 'n' }, + { "no-dirty", no_argument, NULL, 'D' }, + { "quiet", no_argument, NULL, 'q' }, + { "recursive", no_argument, NULL, 'r' }, + //{ "unlink", no_argument, NULL, 'U' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + + opterr = 0; /* We'll handle the errors, thank you. */ + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) { + opts.device = argv[optind-1]; + } else if (!opts.file) { + opts.file = argv[optind-1]; + } else { + opts.device = NULL; + opts.file = NULL; + err++; + } + break; + case 'D': + opts.nodirty++; + break; + case 'f': + opts.force++; + break; + case 'h': + case '?': + if (strncmp (argv[optind-1], "--log-", 6) == 0) { + if (!ntfs_log_parse_option (argv[optind-1])) + err++; + break; + } + help++; + break; + case 'i': + opts.interactive++; + break; + case 'n': + opts.noaction++; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'R': + case 'r': + opts.recursive++; + break; + case 'V': + ver++; + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + default: + ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if ((opts.device == NULL) || + (opts.file == NULL)) { + if (argc > 1) + ntfs_log_error("You must specify one device and one file.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose at the " + "same time.\n"); + err++; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + return (!err && !help && !ver); +} + + +/** + * ntfs_dir_print + */ +static void ntfs_dir_print(struct ntfs_dir *dir, int indent) +{ + int i; + if (!dir) + return; + + ntfs_log_info("%.*s%p ", indent, space_line, dir); + ntfs_name_print(dir->name, dir->name_len); + ntfs_log_info("\n"); + + for (i = 0; i < dir->child_count; i++) { + ntfs_dir_print(dir->children[i], indent + 4); + } + +} + +/** + * ntfs_dt_print + */ +static void ntfs_dt_print(struct ntfs_dt *dt, int indent) +{ + int i; + + if (!dt) + return; + + ntfs_log_info("%.*s%p (%d)\n", indent, space_line, dt, dt->child_count); + + for (i = 0; i < dt->child_count; i++) { + ntfs_dt_print(dt->sub_nodes[i], indent + 4); + } +} + + +/** + * utils_array_insert + */ +static int utils_array_insert(void *ptr, int asize, int before, int count) +{ + static int esize = sizeof(u8*); + u8 *src; + u8 *dst; + int len; + + if (!ptr) + return -1; + + ntfs_log_trace ("\n"); + src = (u8*) ptr + (before * esize); + dst = src + (count * esize); + len = (asize - before) * esize; + + // XXX what about realloc? + memmove(dst, src, len); + + len = count * esize; + + memset(src, 0, len); + + return 0; +} + +/** + * utils_array_remove + */ +static int utils_array_remove(void *ptr, int asize, int first, int count) +{ + static int esize = sizeof(u8*); + u8 *src; + u8 *dst; + int len; + + if (!ptr) + return -1; + + ntfs_log_trace ("\n"); + dst = (u8*) ptr + (first * esize); + src = dst + (count * esize); + len = (asize - first) * esize; + + memmove(dst, src, len); + + src = (u8*) ptr + ((asize - count) * esize); + len = count * esize; + + memset(src, 0, len); + // XXX don't want to memset, want to realloc + + return 0; +} + + +/** + * utils_pathname_to_inode2 + */ +static BOOL utils_pathname_to_inode2(ntfs_volume *vol, struct ntfs_dir *parent, const char *pathname, struct ntfs_find *found) +{ + int len; + char *p, *q; + ntfschar *unicode = NULL; + char *ascii = NULL; + struct ntfs_dir *dir = NULL; + struct ntfs_dir *child = NULL; + struct ntfs_dt *dt = NULL; + int dt_num; + BOOL result = FALSE; + + if (!vol || !pathname || !found) { + errno = EINVAL; + return FALSE; + } + + ntfs_log_trace("\n"); + memset(found, 0, sizeof(*found)); + + if (parent) { + dir = parent; + } else { + dir = (struct ntfs_dir *) vol->private_data; + if (!dir) { + ntfs_log_error("Couldn't open the inode of the root directory.\n"); + goto close; + } + } + + unicode = malloc(MAX_PATH * sizeof(ntfschar)); + ascii = strdup(pathname); // Work with a r/w copy + if (!unicode || !ascii) { + ntfs_log_error("Out of memory.\n"); + goto close; + } + + p = ascii; + while (p && *p && *p == PATH_SEP) // Remove leading /'s + p++; + while (p && *p) { + q = strchr(p, PATH_SEP); // Find the end of the first token + if (q != NULL) { + *q = '\0'; + q++; + } + + len = ntfs_mbstoucs(p, &unicode, MAX_PATH); + if (len < 0) { + ntfs_log_error("Couldn't convert name to Unicode: %s.\n", p); + goto close; + } + + //ntfs_log_info("looking for %s in dir %lld\n", p, MREF(dir->mft_num)); + //ntfs_log_info("dir: index = %p, children = %p, inode = %p, iroot = %p, ialloc = %p, count = %d\n", dir->index, dir->children, dir->inode, dir->iroot, dir->ialloc, dir->child_count); + //if (dir->parent) + if (q) { + ntfs_log_trace("q\n"); + child = ntfs_dir_find2(dir, unicode, len); + if (!child) { + ntfs_log_info("can't find %s in %s\n", p, pathname); + goto close; + } + } else { + ntfs_log_trace("!q dir->index = %p, %d\n", dir->index, dir->index->data_len); + //ntfs_log_info("file: %s\n", p); + + dt = ntfs_dt_find2(dir->index, unicode, len, &dt_num); + if (!dt) { + ntfs_log_info("can't find %s in %s (2)\n", p, pathname); + goto close; + } + ntfs_log_debug("dt = %p, data_len = %d, parent = %p\n", dt, dt->data_len, dt->parent); + + //ntfs_log_info("dt's flags = 0x%08x\n", dt->children[dt_num]->key.file_name.file_attributes); + if (dt->children[dt_num]->key.file_name.file_attributes == FILE_ATTR_I30_INDEX_PRESENT) { + //ntfs_log_info("DIR\n"); + child = ntfs_dir_create(dir->vol, dt->children[dt_num]->indexed_file); + //ntfs_log_info("child = %p (%lld)\n", child, MREF(dt->children[dt_num]->indexed_file)); + if (child) { + child->index = ntfs_dt_create(child, NULL, -1); + ntfs_dir_add(dir, child); + } + + } + + if (dt->inodes[dt_num] == NULL) { + dt->inodes[dt_num] = ntfs_inode_open(dir->vol, dt->children[dt_num]->indexed_file); + if (!dt->inodes[dt_num]) { + ntfs_log_info("Can't open inode %lld\n", MREF(dt->children[dt_num]->indexed_file)); + goto close; + } + dt->inodes[dt_num]->ref_count = 2; + dt->inodes[dt_num]->private_data = dt; + } + + //ntfs_log_info("dt = %p,%d\n", dt, dt_num); + break; + } + + dir = child; + child = NULL; + p = q; + while (p && *p && *p == PATH_SEP) + p++; + } + + found->dir = dir; + found->dt = dt; + found->dt_index = dt_num; + found->inode = dt->inodes[dt_num]; + found->mref = found->inode->mft_no; + result = TRUE; + //ntfs_log_info("dir %p, dt %p, num %d, ino %p, %lld\n", dir, dt, dt_num, dt->inodes[dt_num], MREF(found->inode->mft_no)); +close: + free(ascii); // from strdup + free(unicode); + return result; +} + + +/** + * ntfs_mft_find_free_entry + */ +static s64 ntfs_mft_find_free_entry(ntfs_volume *vol) +{ + MFT_REF i; + u64 recs; + + if (!vol) + return -1; + + ntfs_log_trace ("\n"); + recs = vol->mft_na->initialized_size >> vol->mft_record_size_bits; + //ntfs_log_info("mft contains %lld records\n", recs); + for (i = 24; i < recs; i++) { + if (utils_mftrec_in_use(vol, i) == 0) + return i; + } + return -1; +} + +/** + * ntfs_mft_set_inuse6 + */ +static int ntfs_mft_set_inuse6(ntfs_inode *inode, struct ntfs_bmp *bmp, BOOL inuse) +{ + MFT_RECORD *rec; + + if (!inode) + return -1; + + ntfs_log_trace("\n"); + if (ntfs_bmp_set_range(bmp, (VCN) MREF(inode->mft_no), 1, inuse) < 0) + return -1; + + rec = (MFT_RECORD*) inode->mrec; + + // XXX extent inodes? + + if (inuse) + rec->flags |= MFT_RECORD_IN_USE; + else + rec->flags &= ~MFT_RECORD_IN_USE; + + // XXX inc sequence number + + NInoSetDirty(inode); + + ntfs_log_info("Modified: inode %lld MFT_RECORD header\n", inode->mft_no); + return 0; +} + + +/** + * ntfs_file_remove + */ +static int ntfs_file_remove(ntfs_volume *vol, struct ntfs_dt *del, int del_num) +{ + struct ntfs_dir *find_dir = NULL; + struct ntfs_dt *top = NULL; + struct ntfs_dt *suc = NULL; + struct ntfs_dt *old = NULL; + struct ntfs_dt *par = NULL; + struct ntfs_dt *ded = NULL; + ntfschar *uname; + int name_len; + int suc_num = 0; + int par_num = -1; + INDEX_ENTRY *del_ie = NULL; + INDEX_ENTRY *suc_ie = NULL; + INDEX_ENTRY *par_ie = NULL; + INDEX_ENTRY *add_ie = NULL; + int res; + VCN vcn; + FILE_NAME_ATTR *file = NULL; + //int i; + + if (!vol || !del) { + return 1; + } + + ntfs_log_trace ("\n"); + find_dir = del->dir; + + uname = del->children[del_num]->key.file_name.file_name; + name_len = del->children[del_num]->key.file_name.file_name_length; + + top = del->dir->index; + //ntfs_dt_find_all(top); + //ntfs_dt_print(top, 0); + + del_ie = del->children[del_num]; + //utils_dump_mem(del_ie, 0, del_ie->length, DM_DEFAULTS); + //ntfs_log_info("\n"); + + /* + * If the key is not in a leaf node, then replace it with its successor. + * Continue the delete as if the successor had been deleted. + */ + + /* + for (i = 0; i < top->child_count; i++) { + par_ie = top->children[i]; + file = &par_ie->key.file_name; ntfs_log_info("\ttop node, key %d: ", i); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_info("\n"); + ntfs_log_info("\tvcn = %lld\n", ntfs_ie_get_vcn(par_ie)); + } + */ + + if (del->header->flags & INDEX_NODE) { + ntfs_log_info("Replace key with its successor:\n"); + + vcn = ntfs_ie_get_vcn(del_ie); + //ntfs_log_info("vcn = %lld\n", vcn); + + suc = ntfs_dt_find4(find_dir->index, uname, name_len, &suc_num); + //ntfs_log_info("succ = %p, index = %d\n", suc, suc_num); + //ntfs_log_info("\n"); + + suc_ie = ntfs_ie_copy(suc->children[suc_num]); + //utils_dump_mem(suc_ie, 0, suc_ie->length, DM_BLUE|DM_GREEN|DM_INDENT); + //ntfs_log_info("\n"); + + suc_ie = ntfs_ie_set_vcn(suc_ie, vcn); + //utils_dump_mem(suc_ie, 0, suc_ie->length, DM_BLUE|DM_GREEN|DM_INDENT); + //ntfs_log_info("\n"); + + file = &del_ie->key.file_name; ntfs_log_info("\trep name: "); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_info("\n"); + file = &suc_ie->key.file_name; ntfs_log_info("\tsuc name: "); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_info("\n"); + + //utils_dump_mem(del->data, 0, del->data_len, DM_BLUE|DM_GREEN|DM_INDENT); + if (ntfs_dt_isroot(del)) + res = ntfs_dt_root_replace(del, del_num, del_ie, suc_ie); + else + res = ntfs_dt_alloc_replace(del, del_num, del_ie, suc_ie); + //ntfs_log_info("\n"); + //utils_dump_mem(del->data, 0, del->data_len, DM_BLUE|DM_GREEN|DM_INDENT); + + ntfs_ie_free(suc_ie); + + if (res == FALSE) + goto done; + + del = suc; // Continue delete with the successor + del_num = suc_num; + del_ie = suc->children[suc_num]; + } + + //ntfs_dt_print(top, 0); + + /* + * Now we have the simpler case of deleting from a leaf node. + * If this step creates an empty node, we have more to do. + */ + + ntfs_log_info("\n"); + ntfs_log_info("Delete key:\n"); + + file = &del->children[del_num]->key.file_name; ntfs_log_info("\tdel name: "); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_info("\n"); + + //utils_dump_mem(del->data, 0, del->header->index_length+24, DM_BLUE|DM_GREEN|DM_INDENT); + // XXX if del->child_count == 2, we could skip this step + // no, if we combine with another node, we'll have to remember + if (ntfs_dt_isroot(del)) + ntfs_dt_root_remove(del, del_num); + else + ntfs_dt_alloc_remove(del, del_num); + //ntfs_log_info("\n"); + //utils_dump_mem(del->data, 0, del->header->index_length+24, DM_BLUE|DM_GREEN|DM_INDENT); + + if (del->child_count > 1) // XXX ntfs_dt_empty (dt), ntfs_dt_full (dt, new) + goto commit; + + /* + * Ascend the tree until we find a node that is not empty. Take the + * ancestor key and unhook it. This will free up some space in the + * index allocation. Finally add the ancestor to the node of its + * successor. + */ + + // find the key nearest the root which has no descendants + ntfs_log_info("\n"); + ntfs_log_info("Find childless parent:\n"); +#if 0 + for (par = del->parent, old = par; par; old = par, par = par->parent) { + if (par->child_count > 1) + break; + par_num = ntfs_dt_find_parent(par); + } +#endif + + ntfs_log_info("del = %p, parent = %p\n", del, del->parent); + par = del->parent; + par_num = ntfs_dt_find_parent(del); + + //utils_dump_mem(par->data, 0, par->data_len, DM_BLUE|DM_GREEN|DM_INDENT); + + ntfs_log_info("par = %p, par->parent = %p, num = %d\n", par, par->parent, par_num); + par_num = 0; // TEMP + + if (par) { + file = &par->children[par_num]->key.file_name; + ntfs_log_info("\tpar name: "); + ntfs_name_print(file->file_name, file->file_name_length); + ntfs_log_info("\n"); + } + + if (par == NULL) { + // unhook everything + goto freedts; + } + + //ntfs_dt_print(top, 0); + ntfs_log_info("\n"); + + //utils_dump_mem(par->data, 0, par->data_len, DM_BLUE|DM_GREEN|DM_INDENT); + //ntfs_log_info("\n"); + + /* + for (i = 0; i < top->child_count; i++) { + par_ie = top->children[i]; + file = &par_ie->key.file_name; ntfs_log_info("\ttop node, key %d: ", i); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_info("\n"); + ntfs_log_info("\tvcn = %lld\n", ntfs_ie_get_vcn(par_ie)); + } + */ + + // find if parent has left siblings + if (par->children[par_num]->flags & INDEX_ENTRY_END) { + ntfs_log_info("Swap the children of the parent and its left sibling\n"); + + par_ie = par->children[par_num]; + vcn = ntfs_ie_get_vcn(par_ie); + //ntfs_log_info("\toffset = %d\n", (u8*)par_ie - par->data); ntfs_log_info("\tflags = %d\n", par_ie->flags); ntfs_log_info("\tvcn = %lld\n", vcn); ntfs_log_info("\tlength = %d\n", par_ie->length); + //utils_dump_mem(par_ie, 0, par_ie->length, DM_DEFAULTS); + //ntfs_log_info("\n"); + + //ntfs_log_info("\toffset = %d\n", (u8*)par_ie - par->data); ntfs_log_info("\tflags = %d\n", par_ie->flags); ntfs_log_info("\tvcn = %lld\n", vcn); ntfs_log_info("\tlength = %d\n", par_ie->length); + //utils_dump_mem(par_ie, 0, par_ie->length, DM_DEFAULTS); + //ntfs_log_info("\n"); + + file = &par->children[par_num] ->key.file_name; ntfs_log_info("\tpar name: "); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_info("\n"); + file = &par->children[par_num-1]->key.file_name; ntfs_log_info("\tsib name: "); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_info("\n"); + + old = par->sub_nodes[par_num]; + par->sub_nodes[par_num] = par->sub_nodes[par_num-1]; + par->sub_nodes[par_num-1] = old; + + par_ie = par->children[par_num-1]; + vcn = ntfs_ie_get_vcn(par_ie); + + par_ie = par->children[par_num]; + ntfs_ie_set_vcn(par_ie, vcn); + + par_num--; + + if (ntfs_dt_isroot(par)) + ntfs_log_info("Modified: inode %lld, $INDEX_ROOT\n", par->dir->inode->mft_no); + else + ntfs_log_info("Modified: inode %lld, $INDEX_ALLOCATION vcn %lld-%lld\n", par->dir->inode->mft_no, par->vcn, par->vcn + (par->dir->index_size>>9) - 1); + } + + //ntfs_dt_print(top, 0); + + //ntfs_log_info("\n"); + //utils_dump_mem(par->data, 0, par->data_len, DM_DEFAULTS); + + // unhook and hold onto the ded dt's + ntfs_log_info("\n"); + ntfs_log_info("Remove parent\n"); + + file = &par->children[par_num]->key.file_name; ntfs_log_info("\tpar name: "); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_info("\n"); + + add_ie = ntfs_ie_copy(par->children[par_num]); + add_ie = ntfs_ie_remove_vcn(add_ie); + if (!add_ie) + goto done; + + //ntfs_log_info("\n"); + //utils_dump_mem(add_ie, 0, add_ie->length, DM_BLUE|DM_GREEN|DM_INDENT); + + ded = par->sub_nodes[par_num]; + par->sub_nodes[par_num] = NULL; + //ntfs_dt_print(ded, 8); + +#if 0 + for (i = 0; i < par->child_count; i++) { + par_ie = par->children[i]; + file = &par_ie->key.file_name; ntfs_log_info("\tdel node, key %d: ", i); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_info("\n"); + ntfs_log_info("\tvcn = %lld\n", ntfs_ie_get_vcn(par_ie)); + } +#endif + +#if 1 + //ntfs_log_info("PAR: %p,%d\n", par, par_num); + if (ntfs_dt_isroot(par)) + ntfs_dt_root_remove(par, par_num); + else + ntfs_dt_alloc_remove(par, par_num); +#endif + //ntfs_log_info("count = %d\n", par->child_count); + //utils_dump_mem(par->data, 0, par->data_len, DM_DEFAULTS); + //ntfs_log_info("0x%x\n", (u8*)par->children[0] - par->data); + +#if 0 + for (i = 0; i < par->child_count; i++) { + par_ie = par->children[i]; + file = &par_ie->key.file_name; ntfs_log_info("\tadd node, key %d: ", i); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_info("\n"); + ntfs_log_info("\tvcn = %lld\n", ntfs_ie_get_vcn(par_ie)); + } +#endif + + //ntfs_dt_print(top, 0); + ntfs_log_info("\n"); + ntfs_log_info("Add childless parent\n"); + + file = &add_ie->key.file_name; ntfs_log_info("\tadd name: "); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_info("\n"); + suc = NULL; + suc_num = -1; + suc = ntfs_dt_find4(top, file->file_name, file->file_name_length, &suc_num); + //ntfs_log_info("SUC: %p, %d\n", suc, suc_num); + + if (!suc) + goto done; + + file = &suc->children[suc_num]->key.file_name; ntfs_log_info("\tsuc name: "); ntfs_name_print(file->file_name, file->file_name_length); ntfs_log_info("\n"); + + // insert key into successor + // if any new nodes are needed, reuse the preserved nodes + if (!ntfs_dt_add2(add_ie, suc, suc_num, ded)) + goto done; + + // remove any unused nodes + + // XXX mark dts, dirs and inodes dirty + // XXX add freed dts to a list for immediate reuse (attach to dir?) + // XXX any ded dts means we may need to adjust alloc + // XXX commit will free list of spare dts + // XXX reduce size of alloc + // XXX if ded, don't write it back, just update bitmap + + ntfs_log_info("empty\n"); + goto done; + +freedts: + ntfs_log_info("\twhole dir is empty\n"); + +commit: + //ntfs_log_info("commit\n"); + +done: + return 0; +} + +/** + * ntfs_file_remove2 + */ +static int ntfs_file_remove2(ntfs_volume *vol, struct ntfs_dt *dt, int dt_num) +{ + INDEX_ENTRY *ie; + ntfs_inode *ino; + struct ntfs_bmp *bmp_mft; + struct ntfs_bmp *bmp_vol; + struct ntfs_dir *dir; + + if (!vol || !dt) + return -1; + + ntfs_log_trace ("\n"); + ie = dt->children[dt_num]; + ino = dt->inodes[dt_num]; + dir = dt->dir; + + bmp_mft = vol->private_bmp1; + bmp_vol = vol->private_bmp2; + + if (1) ntfs_mft_set_inuse6(ino, bmp_mft, FALSE); + + if (1) utils_free_non_residents2(ino, bmp_vol); + + if (1) ntfs_file_remove(vol, dt, dt_num); // remove name from index + + if (1) ntfs_dir_truncate(vol, dt->dir); + + if (1) ntfs_volume_commit(vol); + + if (0) ntfs_volume_rollback(vol); + + if (0) ntfs_log_info("last mft = %lld\n", ntfs_bmp_find_last_set(bmp_mft)); + if (0) ntfs_log_info("last vol = %lld\n", ntfs_bmp_find_last_set(bmp_vol)); + + return 0; +} + +/** + * ntfs_file_add2 + */ +static int ntfs_file_add2(ntfs_volume *vol, char *filename) +{ + MFT_REF new_num; + char *ptr = NULL; + char *dirname = NULL; + struct ntfs_find find; + INDEX_ENTRY *ie = NULL; + ntfschar *uname = NULL; + int uname_len = 0; + ntfs_inode *ino = NULL; + u8 *tmp = NULL; + u8 *buffer = NULL; + s64 now = 0; + struct ntfs_dir *dir; + struct ntfs_dt *dt; + int dt_index = 0; + int data_len = 0; + ATTR_RECORD *attr; + struct ntfs_dt *suc = NULL; + int suc_num = 0; + + ntfs_log_trace("\n"); + new_num = ntfs_mft_find_free_entry(vol); + if (new_num == (MFT_REF) -1) + return 1; + + if (rindex(filename, PATH_SEP)) { + ptr = rindex(filename, PATH_SEP); + *ptr = 0; + dirname = filename; + filename = ptr + 1; + } + + ntfs_log_info("looking for %s\n", dirname); + if (utils_pathname_to_inode2(vol, NULL, dirname, &find) == FALSE) { + ntfs_log_info("!inode\n"); + return 0; + } + + dt = find.dt; + dir = find.dir; + + uname_len = ntfs_mbstoucs(filename, &uname, 0); + if (uname_len < 0) + goto close; + + ntfs_log_info("new inode %lld\n", new_num); + ino = ntfs_inode_open3(vol, new_num); + if (!ino) { + ntfs_log_info("!ino\n"); + goto close; + } + + tmp = (u8*) ino->mrec; + now = utc2ntfs(time(NULL)); + + // Wipe all the attributes + memset(tmp + ino->mrec->attrs_offset, 0, vol->mft_record_size - ino->mrec->attrs_offset); + + // Add new end marker + *(u32*) (tmp + ino->mrec->attrs_offset) = 0xFFFFFFFF; + ino->mrec->bytes_in_use = ino->mrec->attrs_offset + 8; + + // Reset headers... + ino->mrec->lsn = 0; + ino->mrec->link_count = 1; + ino->mrec->base_mft_record = 0; + ino->mrec->next_attr_instance = 0; + ino->mrec->flags = MFT_RECORD_IN_USE; + + ntfs_mft_set_inuse6(ino, vol->private_bmp1, TRUE); + + buffer = malloc(128); + if (!buffer) + goto close; + + // Standard information + memset(buffer, 0, 128); + *(u64*)(buffer + 0x00) = now; // Time + *(u64*)(buffer + 0x08) = now; // Time + *(u64*)(buffer + 0x10) = now; // Time + *(u64*)(buffer + 0x18) = now; // Time + ino->creation_time = time(NULL); + ino->last_data_change_time = time(NULL); + ino->last_mft_change_time = time(NULL); + ino->last_access_time = time(NULL); + attr = ntfs_mft_add_attr(ino, AT_STANDARD_INFORMATION, buffer, 0x48); + + // Data + memset(buffer, 0, 128); + data_len = sprintf((char*)buffer, "Contents of file: %s\n", filename); + attr = ntfs_mft_add_attr(ino, AT_DATA, buffer, data_len); + + // File name + memset(buffer, 0, 128); + *(u64*)(buffer + 0x00) = MK_MREF(find.mref, 2); // MFT Ref of parent dir + *(u64*)(buffer + 0x08) = now; // Time + *(u64*)(buffer + 0x10) = now; // Time + *(u64*)(buffer + 0x18) = now; // Time + *(u64*)(buffer + 0x20) = now; // Time + *(u64*)(buffer + 0x28) = ATTR_SIZE(data_len); // Allocated size + *(u64*)(buffer + 0x30) = data_len; // Initialised size + *(u32*)(buffer + 0x38) = 0; // Flags + *(u32*)(buffer + 0x3C) = 0; // Not relevant + *(u8* )(buffer + 0x40) = uname_len; // Filename length + *(u8* )(buffer + 0x41) = FILE_NAME_POSIX; // Filename namespace + memcpy(buffer + 0x42, uname, uname_len * sizeof(ntfschar)); + attr = ntfs_mft_add_attr(ino, AT_FILE_NAME, buffer, ATTR_SIZE(0x42 + (uname_len * sizeof(ntfschar)))); + attr->resident_flags = RESIDENT_ATTR_IS_INDEXED; + attr->name_offset = 0x18; + + ie = ntfs_ie_create(); + ie = ntfs_ie_set_name(ie, uname, uname_len, FILE_NAME_POSIX); + if (!ie) { + ntfs_log_info("!ie\n"); + goto close; + } + + // These two NEED the sequence number in the top 8 bits + ie->key.file_name.parent_directory = MK_MREF(find.mref, 2);// MFT Ref: parent dir + ie->indexed_file = MK_MREF(new_num, ino->mrec->sequence_number); + + ie->key.file_name.creation_time = now; + ie->key.file_name.last_data_change_time = now; + ie->key.file_name.last_mft_change_time = now; + ie->key.file_name.last_access_time = now; + ie->key.file_name.allocated_size = ATTR_SIZE(data_len); + ie->key.file_name.data_size = data_len; + + dir = dt->dir->children[0]; + dt = dir->index; + + ntfs_log_debug("searching for "); ntfs_name_print(uname, uname_len); ntfs_log_debug("\n"); + // find3 doesn't map new dts. don't I _want_ to map them? + suc = ntfs_dt_find3(dt, uname, uname_len, &suc_num); + + ntfs_log_debug("dt = %p, data_len = %d, parent = %p\n", dt, dt->data_len, dt->parent); + + //dt_index = ntfs_dt_root_add(dt, ie); + dt_index = ntfs_dt_add2(ie, suc, suc_num, NULL); + if (dt_index >= 0) { + dt->inodes[dt_index] = ino; + ino->ref_count++; + } + +close: + ntfs_log_debug("working inode refcount = %d\n", ino->ref_count); + free(buffer); + ntfs_inode_close2(ino); + ntfs_ie_free(ie); + free(uname); + ntfs_inode_close2(find.inode); + return 0; +} + + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + ntfs_volume *vol = NULL; + ntfs_inode *inode = NULL; + int flags = 0; + int result = 1; + struct ntfs_find find; + + ntfs_log_set_handler (ntfs_log_handler_stdout); + ntfs_log_set_levels (NTFS_LOG_LEVEL_TRACE); + ntfs_log_set_flags (NTFS_LOG_FLAG_COLOUR); + + ntfs_log_trace ("\n"); + if (!parse_options(argc, argv)) + goto done; + + utils_set_locale(); + +#if 0 + ntfs_log_info("sizeof(ntfs_bmp) = %d\n", sizeof(struct ntfs_bmp)); + ntfs_log_info("sizeof(ntfs_dt) = %d\n", sizeof(struct ntfs_dt)); + ntfs_log_info("sizeof(ntfs_dir) = %d\n", sizeof(struct ntfs_dir)); + ntfs_log_info("\n"); +#endif + + if (opts.noaction) + flags |= MS_RDONLY; + + //ntfs_log_set_levels (NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE); + //ntfs_log_set_levels (NTFS_LOG_LEVEL_DEBUG); + vol = ntfs_volume_mount2(opts.device, flags, opts.force); + if (!vol) { + ntfs_log_info("!vol\n"); + goto done; + } + +#if 0 + if (utils_pathname_to_inode2(vol, NULL, opts.file, &find) == FALSE) { + ntfs_log_info("!inode\n"); + goto done; + } + + inode = find.inode; +#endif + + //ntfs_log_info("inode = %lld\n", inode->mft_no); + + if (0) result = ntfs_file_remove2(vol, find.dt, find.dt_index); + if (1) result = ntfs_file_add2(vol, opts.file); + +done: + if (1) ntfs_volume_commit(vol); + if (0) ntfs_volume_rollback(vol); + if (0) ntfs_inode_close2(inode); + if (1) ntfs_volume_umount2(vol, FALSE); + + //ntfs_log_clear_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE); + if (0) utils_pathname_to_inode2(NULL, NULL, NULL, NULL); + if (0) ntfs_ie_remove_name(NULL); + if (0) ntfs_dt_transfer2(NULL, NULL, 0, 0); + if (0) utils_array_remove(NULL, 0, 0, 0); + if (0) utils_array_insert(NULL, 0, 0, 0); + + ntfs_log_trace("result = %d\n", result); + return result; +} + diff --git a/ntfsprogs/ntfsrm.h b/ntfsprogs/ntfsrm.h new file mode 100644 index 00000000..c37e55b8 --- /dev/null +++ b/ntfsprogs/ntfsrm.h @@ -0,0 +1,63 @@ +/* + * ntfsrm - Part of the Linux-NTFS project. + * + * Copyright (c) 2004-2005 Richard Russon + * + * This utility will delete files from an NTFS volume. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 _NTFSRM_H_ +#define _NTFSRM_H_ + +#include "types.h" +#include "layout.h" +#include "volume.h" +#include "inode.h" + +struct ntfs_dir; +struct ntfs_dt; + +/** + * struct options + */ +struct options { + char *device; /* Device/File to work with */ + char *file; /* File to delete */ + int force; /* Override common sense */ + int interactive; /* Ask before deleting files */ + int recursive; /* Delete files in subdirectories */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int noaction; /* Do not write to disk */ + int nodirty; /* Do not mark volume dirty */ +}; + +/** + * struct ntfs_find + */ +struct ntfs_find { + ntfs_inode *inode; + struct ntfs_dir *dir; + struct ntfs_dt *dt; + int dt_index; + MFT_REF mref; +}; + + +#endif /* _NTFSRM_H_ */ + diff --git a/ntfsprogs/ntfstruncate.c b/ntfsprogs/ntfstruncate.c new file mode 100644 index 00000000..38c2db27 --- /dev/null +++ b/ntfsprogs/ntfstruncate.c @@ -0,0 +1,811 @@ +/** + * ntfstruncate - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * + * This utility will truncate a specified attribute belonging to a + * specified inode, i.e. file or directory, to a specified length. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS source + * in the file COPYING); if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_STDLIB_H +# include +#endif +#ifdef HAVE_STDIO_H +# include +#endif +#ifdef HAVE_STDARG_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_ERRNO_H +# include +#endif +#ifdef HAVE_TIME_H +#include +#endif +#ifdef HAVE_GETOPT_H +# include +#else + extern char *optarg; + extern int optind; +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifndef LLONG_MAX +# define LLONG_MAX 9223372036854775807LL +#endif + +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "attrdef.h" +/* #include "version.h" */ + +const char *EXEC_NAME = "ntfstruncate"; + +/* Need these global so ntfstruncate_exit can access them. */ +BOOL success = FALSE; + +char *dev_name; +s64 inode; +u32 attr_type; +ntfschar *attr_name = NULL; +u32 attr_name_len; +s64 new_len; + +ntfs_volume *vol; +ntfs_inode *ni; +ntfs_attr *na = NULL; + +ATTR_DEF *attr_defs; + +struct { + /* -h, print usage and exit. */ + int no_action; /* -n, do not write to device, only display + what would be done. */ + int quiet; /* -q, quiet execution. */ + int verbose; /* -v, verbose execution, given twice, really + verbose execution (debug mode). */ + int force; /* -f, force truncation. */ + /* -V, print version and exit. */ +} opts; + +/** + * err_exit - error output and terminate; ignores quiet (-q) + */ +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static void err_exit(const char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "ERROR: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "Aborting...\n"); + exit(1); +} + +/** + * copyright - print copyright statements + */ +static void copyright(void) +{ + fprintf(stderr, "Copyright (c) 2002-2005 Anton Altaparmakov\n" + "Copyright (c) 2003 Richard Russon\n" + "Truncate a specified attribute of a specified " + "inode.\n"); +} + +/** + * license - print license statement + */ +static void license(void) +{ + fprintf(stderr, "%s", ntfs_gpl); +} + +/** + * usage - print a list of the parameters to the program + */ +__attribute__((noreturn)) +static void usage(void) +{ + copyright(); + fprintf(stderr, "Usage: %s [options] device inode [attr-type " + "[attr-name]] new-length\n" + " If attr-type is not specified, 0x80 (i.e. $DATA) " + "is assumed.\n" + " If attr-name is not specified, an unnamed " + "attribute is assumed.\n" + " -n Do not write to disk\n" + " -f Force execution despite errors\n" + " -q Quiet execution\n" + " -v Verbose execution\n" + " -vv Very verbose execution\n" + " -V Display version information\n" + " -l Display licensing information\n" + " -h Display this help\n", EXEC_NAME); + fprintf(stderr, "%s%s", ntfs_bugs, ntfs_home); + exit(1); +} + +/** + * parse_options + */ +static void parse_options(int argc, char *argv[]) +{ + long long ll; + char *s, *s2; + int c; + + if (argc && *argv) + EXEC_NAME = *argv; + fprintf(stderr, "%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION); + while ((c = getopt(argc, argv, "fh?nqvVl")) != EOF) + switch (c) { + case 'f': + opts.force = 1; + break; + case 'n': + opts.no_action = 1; + break; + case 'q': + opts.quiet = 1; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + /* Version number already printed, so just exit. */ + exit(0); + case 'l': + copyright(); + license(); + exit(0); + case 'h': + case '?': + default: + usage(); + } + if (optind == argc) + usage(); + + if (opts.verbose > 1) + ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | + NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_QUIET); + + /* Get the device. */ + dev_name = argv[optind++]; + ntfs_log_verbose("device name = %s\n", dev_name); + + if (optind == argc) + usage(); + + /* Get the inode. */ + ll = strtoll(argv[optind++], &s, 0); + if (*s || !ll || (ll >= LLONG_MAX && errno == ERANGE)) + err_exit("Invalid inode number: %s\n", argv[optind - 1]); + inode = ll; + ntfs_log_verbose("inode = %lli\n", (long long)inode); + + if (optind == argc) + usage(); + + /* Get the attribute type, if specified. */ + s = argv[optind++]; + if (optind == argc) { + attr_type = AT_DATA; + attr_name = AT_UNNAMED; + attr_name_len = 0; + } else { + unsigned long ul; + + ul = strtoul(s, &s2, 0); + if (*s2 || !ul || (ul >= ULONG_MAX && errno == ERANGE)) + err_exit("Invalid attribute type %s: %s\n", s, + strerror(errno)); + attr_type = ul; + + /* Get the attribute name, if specified. */ + s = argv[optind++]; + if (optind != argc) { + /* Convert the string to little endian Unicode. */ + attr_name_len = ntfs_mbstoucs_libntfscompat(s, &attr_name, 0); + if ((int)attr_name_len < 0) + err_exit("Invalid attribute name \"%s\": %s\n", + s, strerror(errno)); + + /* Keep hold of the original string. */ + s2 = s; + + s = argv[optind++]; + if (optind != argc) + usage(); + } else { + attr_name = AT_UNNAMED; + attr_name_len = 0; + } + } + ntfs_log_verbose("attribute type = 0x%x\n", (unsigned int)attr_type); + if (attr_name == AT_UNNAMED) + ntfs_log_verbose("attribute name = \"\" (UNNAMED)\n"); + else + ntfs_log_verbose("attribute name = \"%s\" (length %u Unicode " + "characters)\n", s2, + (unsigned int)attr_name_len); + + /* Get the new length. */ + ll = strtoll(s, &s2, 0); + if (*s2 || ll < 0 || (ll >= LLONG_MAX && errno == ERANGE)) + err_exit("Invalid new length: %s\n", s); + new_len = ll; + ntfs_log_verbose("new length = %lli\n", new_len); +} + +/** + * ucstos - convert unicode-character string to ASCII + * @dest: points to buffer to receive the converted string + * @src: points to string to convert + * @maxlen: size of @dest buffer in bytes + * + * Return the number of characters written to @dest, not including the + * terminating null byte. If a unicode character was encountered which could + * not be converted -1 is returned. + */ +static int ucstos(char *dest, const ntfschar *src, int maxlen) +{ + ntfschar u; + int i; + + /* Need one byte for null terminator. */ + maxlen--; + for (i = 0; i < maxlen; i++) { + u = le16_to_cpu(src[i]); + if (!u) + break; + if (u & 0xff00) + return -1; + dest[i] = u & 0xff; + } + dest[i] = 0; + return i; +} + +/** + * dump_resident_attr_val + */ +static void dump_resident_attr_val(ATTR_TYPES type, char *val, u32 val_len) +{ + const char *don_t_know = "Don't know what to do with this attribute " + "type yet."; + const char *skip = "Skipping display of $%s attribute value.\n"; + const char *todo = "This is still work in progress."; + char *buf; + int i, j; + u32 u; + + switch (type) { + case AT_STANDARD_INFORMATION: + // TODO + printf("%s\n", todo); + return; + case AT_ATTRIBUTE_LIST: + // TODO + printf("%s\n", todo); + return; + case AT_FILE_NAME: + // TODO + printf("%s\n", todo); + return; + case AT_OBJECT_ID: + // TODO + printf("%s\n", todo); + return; + case AT_SECURITY_DESCRIPTOR: + // TODO + printf("%s\n", todo); + return; + case AT_VOLUME_NAME: + printf("Volume name length = %u\n", (unsigned int)val_len); + if (val_len) { + buf = calloc(1, val_len); + if (!buf) + err_exit("Failed to allocate internal buffer: " + "%s\n", strerror(errno)); + i = ucstos(buf, (ntfschar*)val, val_len); + if (i == -1) + printf("Volume name contains non-displayable " + "Unicode characters.\n"); + printf("Volume name = %s\n", buf); + free(buf); + } + return; + case AT_VOLUME_INFORMATION: +#define VOL_INF(x) ((VOLUME_INFORMATION *)(x)) + printf("NTFS version %i.%i\n", VOL_INF(val)->major_ver, + VOL_INF(val)->minor_ver); + i = VOL_INF(val)->flags; +#undef VOL_INF + printf("Volume flags = 0x%x: ", i); + if (!i) { + printf("NONE\n"); + return; + } + j = 0; + if (i & VOLUME_MODIFIED_BY_CHKDSK) { + j = 1; + printf("VOLUME_MODIFIED_BY_CHKDSK"); + } + if (i & VOLUME_REPAIR_OBJECT_ID) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_REPAIR_OBJECT_ID"); + } + if (i & VOLUME_DELETE_USN_UNDERWAY) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_DELETE_USN_UNDERWAY"); + } + if (i & VOLUME_MOUNTED_ON_NT4) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_MOUNTED_ON_NT4"); + } + if (i & VOLUME_UPGRADE_ON_MOUNT) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_UPGRADE_ON_MOUNT"); + } + if (i & VOLUME_RESIZE_LOG_FILE) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_RESIZE_LOG_FILE"); + } + if (i & VOLUME_IS_DIRTY) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_IS_DIRTY"); + } + printf("\n"); + return; + case AT_DATA: + printf(skip, "DATA"); + return; + case AT_INDEX_ROOT: + // TODO + printf("%s\n", todo); + return; + case AT_INDEX_ALLOCATION: + // TODO + printf("%s\n", todo); + return; + case AT_BITMAP: + printf(skip, "BITMAP"); + return; + case AT_REPARSE_POINT: + // TODO + printf("%s\n", todo); + return; + case AT_EA_INFORMATION: + // TODO + printf("%s\n", don_t_know); + return; + case AT_EA: + // TODO + printf("%s\n", don_t_know); + return; + case AT_LOGGED_UTILITY_STREAM: + // TODO + printf("%s\n", don_t_know); + return; + default: + u = le32_to_cpu(type); + printf("Cannot display unknown %s defined attribute type 0x%x" + ".\n", u >= + le32_to_cpu(AT_FIRST_USER_DEFINED_ATTRIBUTE) ? + "user" : "system", (unsigned int)u); + } +} + +/** + * dump_resident_attr + */ +static void dump_resident_attr(ATTR_RECORD *a) +{ + int i; + + i = le32_to_cpu(a->value_length); + printf("Attribute value length = %u (0x%x)\n", i, i); + i = le16_to_cpu(a->value_offset); + printf("Attribute value offset = %u (0x%x)\n", i, i); + i = a->resident_flags; + printf("Resident flags = 0x%x: ", i); + if (!i) + printf("NONE\n"); + else if (i & ~RESIDENT_ATTR_IS_INDEXED) + printf("UNKNOWN FLAG(S)\n"); + else + printf("RESIDENT_ATTR_IS_INDEXED\n"); + dump_resident_attr_val(a->type, (char*)a + le16_to_cpu(a->value_offset), + le32_to_cpu(a->value_length)); +} + +/** + * dump_mapping_pairs_array + */ +static void dump_mapping_pairs_array(char *b __attribute__((unused)), + unsigned int max_len __attribute__((unused))) +{ + // TODO + return; +} + +/** + * dump_non_resident_attr + */ +static void dump_non_resident_attr(ATTR_RECORD *a) +{ + s64 l; + int i; + + l = sle64_to_cpu(a->lowest_vcn); + printf("Lowest VCN = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + l = sle64_to_cpu(a->highest_vcn); + printf("Highest VCN = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + printf("Mapping pairs array offset = 0x%x\n", + le16_to_cpu(a->mapping_pairs_offset)); + printf("Compression unit = 0x%x: %sCOMPRESSED\n", a->compression_unit, + a->compression_unit ? "" : "NOT "); + if (sle64_to_cpu(a->lowest_vcn)) + printf("Attribute is not the first extent. The following " + "sizes are meaningless:\n"); + l = sle64_to_cpu(a->allocated_size); + printf("Allocated size = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + l = sle64_to_cpu(a->data_size); + printf("Data size = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + l = sle64_to_cpu(a->initialized_size); + printf("Initialized size = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + if (a->flags & ATTR_COMPRESSION_MASK) { + l = sle64_to_cpu(a->compressed_size); + printf("Compressed size = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + } + i = le16_to_cpu(a->mapping_pairs_offset); + dump_mapping_pairs_array((char*)a + i, le32_to_cpu(a->length) - i); +} + +/** + * dump_attr_record + */ +static void dump_attr_record(MFT_RECORD *m, ATTR_RECORD *a) +{ + unsigned int u; + char s[0x200]; + int i; + + printf("-- Beginning dump of attribute record at offset 0x%x. --\n", + (unsigned)((u8*)a - (u8*)m)); + if (a->type == AT_END) { + printf("Attribute type = 0x%x ($END)\n", + (unsigned int)le32_to_cpu(AT_END)); + u = le32_to_cpu(a->length); + printf("Length of resident part = %u (0x%x)\n", u, u); + return; + } + u = le32_to_cpu(a->type); + for (i = 0; attr_defs[i].type; i++) + if (le32_to_cpu(attr_defs[i].type) >= u) + break; + if (attr_defs[i].type) { +// printf("type = 0x%x\n", le32_to_cpu(attr_defs[i].type)); +// { char *p = (char*)attr_defs[i].name; +// printf("name = %c%c%c%c%c\n", *p, p[1], p[2], p[3], p[4]); +// } + if (ucstos(s, attr_defs[i].name, sizeof(s)) == -1) { + ntfs_log_error("Could not convert Unicode string to single " + "byte string in current locale.\n"); + strncpy(s, "Error converting Unicode string", + sizeof(s)); + } + } else + strncpy(s, "UNKNOWN_TYPE", sizeof(s)); + printf("Attribute type = 0x%x (%s)\n", u, s); + u = le32_to_cpu(a->length); + printf("Length of resident part = %u (0x%x)\n", u, u); + printf("Attribute is %sresident\n", a->non_resident ? "non-" : ""); + printf("Name length = %u unicode characters\n", a->name_length); + printf("Name offset = %u (0x%x)\n", cpu_to_le16(a->name_offset), + cpu_to_le16(a->name_offset)); + u = a->flags; + if (a->name_length) { + if (ucstos(s, (ntfschar*)((char*)a + + cpu_to_le16(a->name_offset)), + min((int)sizeof(s), + a->name_length + 1)) == -1) { + ntfs_log_error("Could not convert Unicode string to single " + "byte string in current locale.\n"); + strncpy(s, "Error converting Unicode string", + sizeof(s)); + + } + printf("Name = %s\n", s); + } + printf("Attribute flags = 0x%x: ", le16_to_cpu(u)); + if (!u) + printf("NONE"); + else { + int first = TRUE; + if (u & ATTR_COMPRESSION_MASK) { + if (u & ATTR_IS_COMPRESSED) { + printf("ATTR_IS_COMPRESSED"); + first = FALSE; + } + if ((u & ATTR_COMPRESSION_MASK) & ~ATTR_IS_COMPRESSED) { + if (!first) + printf(" | "); + else + first = FALSE; + printf("ATTR_UNKNOWN_COMPRESSION"); + } + } + if (u & ATTR_IS_ENCRYPTED) { + if (!first) + printf(" | "); + else + first = FALSE; + printf("ATTR_IS_ENCRYPTED"); + } + if (u & ATTR_IS_SPARSE) { + if (!first) + printf(" | "); + else + first = FALSE; + printf("ATTR_IS_SPARSE"); + } + } + printf("\n"); + printf("Attribute instance = %u\n", le16_to_cpu(a->instance)); + if (a->non_resident) { + dump_non_resident_attr(a); + } else { + dump_resident_attr(a); + } +} + +/** + * dump_mft_record + */ +static void dump_mft_record(MFT_RECORD *m) +{ + ATTR_RECORD *a; + unsigned int u; + MFT_REF r; + + printf("-- Beginning dump of mft record. --\n"); + u = le32_to_cpu(m->magic); + printf("Mft record signature (magic) = %c%c%c%c\n", u & 0xff, + u >> 8 & 0xff, u >> 16 & 0xff, u >> 24 & 0xff); + u = le16_to_cpu(m->usa_ofs); + printf("Update sequence array offset = %u (0x%x)\n", u, u); + printf("Update sequence array size = %u\n", le16_to_cpu(m->usa_count)); + printf("$LogFile sequence number (lsn) = %llu\n", + (unsigned long long)le64_to_cpu(m->lsn)); + printf("Sequence number = %u\n", le16_to_cpu(m->sequence_number)); + printf("Reference (hard link) count = %u\n", + le16_to_cpu(m->link_count)); + u = le16_to_cpu(m->attrs_offset); + printf("First attribute offset = %u (0x%x)\n", u, u); + printf("Flags = %u: ", le16_to_cpu(m->flags)); + if (m->flags & MFT_RECORD_IN_USE) + printf("MFT_RECORD_IN_USE"); + else + printf("MFT_RECORD_NOT_IN_USE"); + if (m->flags & MFT_RECORD_IS_DIRECTORY) + printf(" | MFT_RECORD_IS_DIRECTORY"); + printf("\n"); + u = le32_to_cpu(m->bytes_in_use); + printf("Bytes in use = %u (0x%x)\n", u, u); + u = le32_to_cpu(m->bytes_allocated); + printf("Bytes allocated = %u (0x%x)\n", u, u); + r = le64_to_cpu(m->base_mft_record); + printf("Base mft record reference:\n\tMft record number = %llu\n\t" + "Sequence number = %u\n", + (unsigned long long)MREF(r), MSEQNO(r)); + printf("Next attribute instance = %u\n", + le16_to_cpu(m->next_attr_instance)); + a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); + printf("-- Beginning dump of attributes within mft record. --\n"); + while ((char*)a < (char*)m + le32_to_cpu(m->bytes_in_use)) { + if (a->type == cpu_to_le32(attr_type)) + dump_attr_record(m, a); + if (a->type == AT_END) + break; + a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); + }; + printf("-- End of attributes. --\n"); +} + +/** + * ntfstruncate_exit + */ +static void ntfstruncate_exit(void) +{ + int err; + + if (success) + return; + /* Close the attribute. */ + if (na) + ntfs_attr_close(na); + /* Close the inode. */ + if (ni && ntfs_inode_close(ni)) { + ntfs_log_perror("Warning: Failed to close inode %lli", + (long long)inode); + } + /* Unmount the volume. */ + err = ntfs_umount(vol, 0); + if (err == -1) + ntfs_log_perror("Warning: Could not umount %s", dev_name); + /* Free the attribute name if it exists. */ + ntfs_ucsfree(attr_name); +} + +/** + * main + */ +int main(int argc, char **argv) +{ + unsigned long mnt_flags, ul; + int err; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + /* Initialize opts to zero / required values. */ + memset(&opts, 0, sizeof(opts)); + + /* + * Setup a default $AttrDef. FIXME: Should be reading this from the + * volume itself, at ntfs_mount() time. + */ + attr_defs = (ATTR_DEF*)&attrdef_ntfs12_array; + + /* Parse command line options. */ + parse_options(argc, argv); + + utils_set_locale(); + + /* Make sure the file system is not mounted. */ + if (ntfs_check_if_mounted(dev_name, &mnt_flags)) + ntfs_log_perror("Failed to determine whether %s is mounted", + dev_name); + else if (mnt_flags & NTFS_MF_MOUNTED) { + ntfs_log_error("%s is mounted.\n", dev_name); + if (!opts.force) + err_exit("Refusing to run!\n"); + fprintf(stderr, "ntfstruncate forced anyway. Hope /etc/mtab " + "is incorrect.\n"); + } + + /* Mount the device. */ + if (opts.no_action) { + ntfs_log_quiet("Running in READ-ONLY mode!\n"); + ul = MS_RDONLY; + } else + ul = 0; + vol = ntfs_mount(dev_name, ul); + if (!vol) + err_exit("Failed to mount %s: %s\n", dev_name, strerror(errno)); + + /* Register our exit function which will unlock and close the device. */ + err = atexit(&ntfstruncate_exit); + if (err == -1) { + ntfs_log_error("Could not set up exit() function because atexit() " + "failed: %s Aborting...\n", strerror(errno)); + ntfstruncate_exit(); + exit(1); + } + + /* Open the specified inode. */ + ni = ntfs_inode_open(vol, inode); + if (!ni) + err_exit("Failed to open inode %lli: %s\n", (long long)inode, + strerror(errno)); + + /* Open the specified attribute. */ + na = ntfs_attr_open(ni, attr_type, attr_name, attr_name_len); + if (!na) + err_exit("Failed to open attribute 0x%x: %s\n", + (unsigned int)attr_type, strerror(errno)); + + if (!opts.quiet && opts.verbose > 1) { + ntfs_log_verbose("Dumping mft record before calling " + "ntfs_attr_truncate():\n"); + dump_mft_record(ni->mrec); + } + + /* Truncate the attribute. */ + err = ntfs_attr_truncate(na, new_len); + if (err) + err_exit("Failed to truncate attribute 0x%x: %s\n", + (unsigned int)attr_type, strerror(errno)); + + if (!opts.quiet && opts.verbose > 1) { + ntfs_log_verbose("Dumping mft record after calling " + "ntfs_attr_truncate():\n"); + dump_mft_record(ni->mrec); + } + + /* Close the attribute. */ + ntfs_attr_close(na); + na = NULL; + + /* Close the inode. */ + err = ntfs_inode_close(ni); + if (err) + err_exit("Failed to close inode %lli: %s\n", (long long)inode, + strerror(errno)); + + /* Unmount the volume. */ + err = ntfs_umount(vol, 0); + if (err == -1) + ntfs_log_perror("Warning: Failed to umount %s", dev_name); + + /* Free the attribute name if it exists. */ + ntfs_ucsfree(attr_name); + + /* Finally, disable our ntfstruncate_exit() handler. */ + success = TRUE; + + ntfs_log_quiet("ntfstruncate completed successfully. Have a nice day.\n"); + return 0; +} + diff --git a/ntfsprogs/ntfsundelete.8.in b/ntfsprogs/ntfsundelete.8.in new file mode 100644 index 00000000..0198205a --- /dev/null +++ b/ntfsprogs/ntfsundelete.8.in @@ -0,0 +1,329 @@ +.\" Copyright (c) 2002\-2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSUNDELETE 8 "November 2005" "ntfsprogs @VERSION@" +.SH NAME +ntfsundelete \- recover a deleted file from an NTFS volume. +.SH SYNOPSIS +.B ntfsundelete +[\fIoptions\fR] \fIdevice\fR +.SH DESCRIPTION +.B ntfsundelete +has three modes of operation: +.IR scan , +.I undelete +and +.IR copy . +.SS Scan +.PP +The default mode, +.I scan +simply reads an NTFS Volume and looks for files that have been deleted. Then it +will print a list giving the inode number, name and size. +.SS Undelete +.PP +The +.I undelete +mode takes the files either matching the regular expression (option \-m) +or specified by the inode\-expressions and recovers as much of the data +as possible. It saves the result to another location. Partly for +safety, but mostly because NTFS write support isn't finished. +.SS Copy +.PP +This is a wizard's option. It will save a portion of the MFT to a file. This +probably only be useful when debugging +.I ntfsundelete +.SS Notes +.B ntfsundelete +only ever +.B reads +from the NTFS Volume. +.B ntfsundelete +will never change the volume. +.SH CAVEATS +.SS Miracles +.B ntfsundelete +cannot perform the impossible. +.PP +When a file is deleted the MFT Record is marked as not in use and the bitmap +representing the disk usage is updated. If the power isn't turned off +immediately, the free space, where the file used to live, may become +overwritten. Worse, the MFT Record may be reused for another file. If this +happens it is impossible to tell where the file was on disk. +.PP +Even if all the clusters of a file are not in use, there is no guarantee that +they haven't been overwritten by some short\-lived file. +.SS Locale +In NTFS all the filenames are stored as Unicode. They will be converted into +the current locale for display by +.BR ntfsundelete . +The utility has successfully displayed some Chinese pictogram filenames and then +correctly recovered them. +.SS Extended MFT Records +In rare circumstances, a single MFT Record will not be large enough to hold the +metadata describing a file (a file would have to be in hundreds of fragments +for this to happen). In these cases one MFT record may hold the filename, but +another will hold the information about the data. +.B ntfsundelete +will not try and piece together such records. It will simply show unnamed files +with data. +.SS Compressed and Encrypted Files +.B ntfsundelete +cannot recover compressed or encrypted files. When scanning for them, it will +display as being 0% recoverable. +.SS The Recovered File's Size and Date +To recover a file +.B ntfsundelete +has to read the file's metadata. Unfortunately, this isn't always intact. +When a file is deleted, the metadata can be left in an inconsistent state. e.g. +the file size may be zero; the dates of the file may be set to the time it was +deleted, or random. +.br +To be safe +.B ntfsundelete +will pick the largest file size it finds and write that to disk. It will also +try and set the file's date to the last modified date. This date may be the +correct last modified date, or something unexpected. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsundelete +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-b\fR, \fB\-\-byte\fR NUM +If any clusters of the file cannot be recovered, the missing parts will be +filled with this byte. The default is zeros. +.TP +\fB\-C\fR, \fB\-\-case\fR +When scanning an NTFS volume, any filename matching (using the +.B \-\-match +option) is case\-insensitive. This option makes the matching case\-sensitive. +.TP +\fB\-c\fR, \fB\-\-copy\fR RANGE +This wizard's option will write a block of MFT FILE records to a file. The +default file is +.I mft +which will be created in the current directory. This option can be combined +with the +.B \-\-output +and +.B \-\-destination +options. +.TP +\fB\-d\fR, \fB\-\-destination\fR DIR +This option controls where to put the output file of the +.B \-\-undelete +and +.B \-\-copy +options. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not overwriting an existing +file. Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-i\fR, \fB\-\-inodes\fR RANGE +Recover the files with these inode numbers. +.I RANGE +can be a single inode number, several numbers separated by commas "," or a +range separated by a dash "\-". +.TP +\fB\-m\fR, \fB\-\-match\fR PATTERN +Filter the output by only looking for matching filenames. The pattern can +include the wildcards '?', match exactly one character or '*', match zero or +more characters. By default the matching is case\-insensitive. To make the +search case sensitive, use the +.B \-\-case +option. +.TP +\fB\-O\fR, \fB\-\-optimistic\fR +Recover parts of the file even if they are currently marked as in use. +.TP +\fB\-o\fR, \fB\-\-output\fR FILE +Use this option to set name of output file that +.B \-\-undelete +or +.B \-\-copy +will create. +.TP +\fB\-P\fR, \fB\-\-parent\fR +Display the parent directory of a deleted file. +.TP +\fB\-p\fR, \fB\-\-percentage\fR NUM +Filter the output of the +.B \-\-scan +option, by only matching files with a certain amount of recoverable content. +.B Please read the caveats section for more details. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Reduce the amount of output to a minimum. Naturally, it doesn't make sense to +combine this option with +.BR \-\-scan . +.TP +\fB\-s\fR, \fB\-\-scan\fR +Search through an NTFS volume and print a list of files that could be recovered. +This is the default action of +.BR ntfsundelete . +This list can be filtered by filename, size, percentage recoverable or last +modification time, using the +.BR \-\-match , +.BR \-\-size , +.B \-\-percent +and +.B \-\-time +options, respectively. +.sp +The output of scan will be: +.sp +.nf +Inode Flags %age Date Size Filename + 6038 FN.. 93% 2002\-07\-17 26629 thesis.doc +.fi +.TS +box; +lB lB +l l. +Flag Description +F/D File/Directory +N/R (Non\-)Resident data stream +C/E Compressed/Encrypted data stream +! Missing attributes +.TE +.sp +.sp +The percentage field shows how much of the file can potentially be recovered. +.TP +\fB\-S\fR, \fB\-\-size\fR RANGE +Filter the output of the +.B \-\-scan +option, by looking for a particular range of file sizes. The range may be +specified as two numbers separated by a '\-'. The sizes may be abbreviated +using the suffixes k, m, g, t, for kilobytes, megabytes, gigabytes and terabytes +respectively. +.TP +\fB\-t\fR, \fB\-\-time\fR SINCE +Filter the output of the +.B \-\-scan +option. Only match files that have been altered since this time. The time must +be given as number using a suffix of d, w, m, y for days, weeks, months or years +ago. +.TP +\fB\-T\fR, \fB\-\-truncate\fR +If +.B ntfsundelete +is confident about the size of a deleted file, then it will restore the file to +exactly that size. The default behaviour is to round up the size to the nearest +cluster (which will be a multiple of 512 bytes). +.TP +\fB\-u\fR, \fB\-\-undelete\fR +Select +.B undelete +mode. You can specify the files to be recovered using by using +.B \-\-match +or +.B \-\-inodes +options. This option can be combined with +.BR \-\-output , +.BR \-\-destination , +and +.BR \-\-byte . +.sp +When the file is recovered it will be given its original name, unless the +.B \-\-output +option is used. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Increase the amount of output that +.B ntfsundelete +prints. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license for +.BR ntfsundelete . +.SH EXAMPLES +Look for deleted files on /dev/hda1. +.RS +.sp +.B ntfsundelete /dev/hda1 +.sp +.RE +Look for deleted documents on /dev/hda1. +.RS +.sp +.B ntfsundelete /dev/hda1 \-s \-m '*.doc' +.sp +.RE +Look for deleted files between 5000 and 6000000 bytes, with at least 90% of the +data recoverable, on /dev/hda1. +.RS +.sp +.B ntfsundelete /dev/hda1 \-S 5k\-6m \-p 90 +.sp +.RE +Look for deleted files altered in the last two days +.RS +.sp +.B ntfsundelete /dev/hda1 \-t 2d +.sp +.RE +Undelete inodes 2, 5 and 100 to 131 of device /dev/sda1 +.RS +.sp +.B ntfsundelete /dev/sda1 \-u \-i 2,5,100\-131 +.sp +.RE +Undelete inode number 3689, call the file 'work.doc' and put it in the user's +home directory. +.RS +.sp +.B ntfsundelete /dev/hda1 \-u \-i 3689 \-o work.doc \-d ~ +.sp +.RE +Save MFT Records 3689 to 3690 to a file 'debug' +.RS +.sp +.B ntfsundelete /dev/hda1 \-c 3689\-3690 \-o debug +.sp +.RE +.SH BUGS +There are some small limitations to +.BR ntfsundelete , +but currently no known bugs. If you find a bug please send an email describing +the problem to the development team: +.br +.nh +linux\-ntfs\-dev@lists.sourceforge.net +.hy +.SH AUTHORS +.B ntfsundelete +was written by Richard Russon and Holger Ohmacht, with contributions from Anton +Altaparmakov. +.SH AVAILABILITY +.B ntfsundelete +is part of the +.B ntfsprogs +package and is available from: +.br +.nh +http://www.linux\-ntfs.org/content/view/19/37 +.hy +.sp +The manual pages are available online at: +.br +.nh +http://man.linux-ntfs.org/ +.hy +.SH SEE ALSO +.BR ntfsinfo (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsundelete.c b/ntfsprogs/ntfsundelete.c new file mode 100644 index 00000000..0968f82f --- /dev/null +++ b/ntfsprogs/ntfsundelete.c @@ -0,0 +1,2165 @@ +/** + * ntfsundelete - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2004-2005 Holger Ohmacht + * Copyright (c) 2005 Anton Altaparmakov + * + * This utility will recover deleted files from an NTFS volume. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 "config.h" + +#ifdef HAVE_FEATURES_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif +#ifdef HAVE_TIME_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_STDARG_H +#include +#endif +#ifdef HAVE_UTIME_H +#include +#endif +#include + +#if !defined(REG_NOERROR) || (REG_NOERROR != 0) +#define REG_NOERROR 0 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ntfsundelete.h" +#include "utils.h" +/* #include "version.h" */ + +static const char *EXEC_NAME = "ntfsundelete"; +static const char *MFTFILE = "mft"; +static const char *UNNAMED = ""; +static const char *NONE = ""; +static const char *UNKNOWN = "unknown"; +static struct options opts; + +typedef struct +{ + u32 begin; + u32 end; +} range; + +static short with_regex; /* Flag Regular expression available */ +static short avoid_duplicate_printing; /* Flag No duplicate printing of file infos */ +static range *ranges; /* Array containing all Inode-Ranges for undelete */ +static long nr_entries; /* Number of range entries */ + +/** + * parse_inode_arg - parses the inode expression + * + * Parses the optarg after parameter -u for valid ranges + * + * Return: Number of correct inode specifications or -1 for error + */ +static int parse_inode_arg(void) +{ + int p; + u32 imax; + u32 range_begin; + u32 range_end; + u32 range_temp; + u32 inode; + char *opt_arg_ptr; + char *opt_arg_temp; + char *opt_arg_end1; + char *opt_arg_end2; + + /* Check whether optarg is available or not */ + nr_entries = 0; + if (optarg == NULL) + return (0); /* bailout if no optarg */ + + /* init variables */ + p = strlen(optarg); + imax = p; + opt_arg_ptr = optarg; + opt_arg_end1 = optarg; + opt_arg_end2 = &(optarg[p]); + + /* alloc mem for range table */ + ranges = (range *) malloc((p + 1) * sizeof(range)); + if (ranges == NULL) { + ntfs_log_error("ERROR: Couldn't alloc mem for parsing inodes!\n"); + return (-1); + } + + /* loop */ + while ((opt_arg_end1 != opt_arg_end2) && (p > 0)) { + /* Try to get inode */ + inode = strtoul(opt_arg_ptr, &opt_arg_end1, 0); + p--; + + /* invalid char at begin */ + if ((opt_arg_ptr == opt_arg_end1) || (opt_arg_ptr == opt_arg_end2)) { + ntfs_log_error("ERROR: Invalid Number: %s\n", opt_arg_ptr); + return (-1); + } + + /* RANGE - Check for range */ + if (opt_arg_end1[0] == '-') { + /* get range end */ + opt_arg_temp = opt_arg_end1; + opt_arg_end1 = & (opt_arg_temp[1]); + if (opt_arg_temp >= opt_arg_end2) { + ntfs_log_error("ERROR: Missing range end!\n"); + return (-1); + } + range_begin = inode; + + /* get count */ + range_end = strtoul(opt_arg_end1, &opt_arg_temp, 0); + if (opt_arg_temp == opt_arg_end1) { + ntfs_log_error("ERROR: Invalid Number: %s\n", opt_arg_temp); + return (-1); + } + + /* check for correct values */ + if (range_begin > range_end) { + range_temp = range_end; + range_end = range_begin; + range_begin = range_temp; + } + + /* put into struct */ + ranges[nr_entries].begin = range_begin; + ranges[nr_entries].end = range_end; + nr_entries++; + + /* Last check */ + opt_arg_ptr = & (opt_arg_temp[1]); + if (opt_arg_ptr >= opt_arg_end2) + break; + } else if (opt_arg_end1[0] == ',') { + /* SINGLE VALUE, BUT CONTINUING */ + /* put inode into range list */ + ranges[nr_entries].begin = inode; + ranges[nr_entries].end = inode; + nr_entries++; + + /* Next inode */ + opt_arg_ptr = & (opt_arg_end1[1]); + if (opt_arg_ptr >= opt_arg_end2) { + ntfs_log_error("ERROR: Missing new value at end of input!\n"); + return (-1); + } + continue; + } else { /* SINGLE VALUE, END */ + ranges[nr_entries].begin = inode; + ranges[nr_entries].end = inode; + nr_entries++; + } + } + return (nr_entries); +} + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Recover deleted files from an NTFS " + "Volume.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c) 2002-2005 Richard Russon\n" + "Copyright (c) 2004-2005 Holger Ohmacht\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device\n" + " -s, --scan Scan for files (default)\n" + " -p, --percentage NUM Minimum percentage recoverable\n" + " -m, --match PATTERN Only work on files with matching names\n" + " -C, --case Case sensitive matching\n" + " -S, --size RANGE Match files of this size\n" + " -t, --time SINCE Last referenced since this time\n" + "\n" + " -u, --undelete Undelete mode\n" + " -i, --inodes RANGE Recover these inodes\n" + //" -I, --interactive Interactive mode\n" + " -o, --output FILE Save with this filename\n" + " -O, --optimistic Undelete in-use clusters as well\n" + " -d, --destination DIR Destination directory\n" + " -b, --byte NUM Fill missing parts with this byte\n" + " -T, --truncate Truncate 100%% recoverable file to exact size.\n" + " -P, --parent Show parent directory\n" + "\n" + " -c, --copy RANGE Write a range of MFT records to a file\n" + "\n" + " -f, --force Use less caution\n" + " -q, --quiet Less output\n" + " -v, --verbose More output\n" + " -V, --version Display version information\n" + " -h, --help Display this help\n\n", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * transform - Convert a shell style pattern to a regex + * @pattern: String to be converted + * @regex: Resulting regular expression is put here + * + * This will transform patterns, such as "*.doc" to true regular expressions. + * The function will also place '^' and '$' around the expression to make it + * behave as the user would expect + * + * Before After + * . \. + * * .* + * ? . + * + * Notes: + * The returned string must be freed by the caller. + * If transform fails, @regex will not be changed. + * + * Return: 1, Success, the string was transformed + * 0, An error occurred + */ +static int transform(const char *pattern, char **regex) +{ + char *result; + int length, i, j; + + if (!pattern || !regex) + return 0; + + length = strlen(pattern); + if (length < 1) { + ntfs_log_error("Pattern to transform is empty\n"); + return 0; + } + + for (i = 0; pattern[i]; i++) { + if ((pattern[i] == '*') || (pattern[i] == '.')) + length++; + } + + result = malloc(length + 3); + if (!result) { + ntfs_log_error("Couldn't allocate memory in transform()\n"); + return 0; + } + + result[0] = '^'; + + for (i = 0, j = 1; pattern[i]; i++, j++) { + if (pattern[i] == '*') { + result[j] = '.'; + j++; + result[j] = '*'; + } else if (pattern[i] == '.') { + result[j] = '\\'; + j++; + result[j] = '.'; + } else if (pattern[i] == '?') { + result[j] = '.'; + } else { + result[j] = pattern[i]; + } + } + + result[j] = '$'; + result[j+1] = 0; + ntfs_log_debug("Pattern '%s' replaced with regex '%s'.\n", pattern, + result); + + *regex = result; + return 1; +} + +/** + * parse_time - Convert a time abbreviation to seconds + * @string: The string to be converted + * @since: The absolute time referred to + * + * Strings representing times will be converted into a time_t. The numbers will + * be regarded as seconds unless suffixed. + * + * Suffix Description + * [yY] Year + * [mM] Month + * [wW] Week + * [dD] Day + * [sS] Second + * + * Therefore, passing "1W" will return the time_t representing 1 week ago. + * + * Notes: + * Only the first character of the suffix is read. + * If parse_time fails, @since will not be changed + * + * Return: 1 Success + * 0 Error, the string was malformed + */ +static int parse_time(const char *value, time_t *since) +{ + long long result; + time_t now; + char *suffix = NULL; + + if (!value || !since) + return -1; + + ntfs_log_trace("Parsing time '%s' ago.\n", value); + + result = strtoll(value, &suffix, 10); + if (result < 0 || errno == ERANGE) { + ntfs_log_error("Invalid time '%s'.\n", value); + return 0; + } + + if (!suffix) { + ntfs_log_error("Internal error, strtoll didn't return a suffix.\n"); + return 0; + } + + if (strlen(suffix) > 1) { + ntfs_log_error("Invalid time suffix '%s'. Use Y, M, W, D or H.\n", suffix); + return 0; + } + + switch (suffix[0]) { + case 'y': case 'Y': result *= 12; + case 'm': case 'M': result *= 4; + case 'w': case 'W': result *= 7; + case 'd': case 'D': result *= 24; + case 'h': case 'H': result *= 3600; + case 0: + break; + + default: + ntfs_log_error("Invalid time suffix '%s'. Use Y, M, W, D or H.\n", suffix); + return 0; + } + + now = time(NULL); + + ntfs_log_debug("Time now = %lld, Time then = %lld.\n", (long long) now, + (long long) result); + *since = now - result; + return 1; +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char *argv[]) +{ + static const char *sopt = "-b:Cc:d:fh?i:m:o:OPp:sS:t:TuqvV"; + static const struct option lopt[] = { + { "byte", required_argument, NULL, 'b' }, + { "case", no_argument, NULL, 'C' }, + { "copy", required_argument, NULL, 'c' }, + { "destination", required_argument, NULL, 'd' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "inodes", required_argument, NULL, 'i' }, + //{ "interactive", no_argument, NULL, 'I' }, + { "match", required_argument, NULL, 'm' }, + { "optimistic", no_argument, NULL, 'O' }, + { "output", required_argument, NULL, 'o' }, + { "parent", no_argument, NULL, 'P' }, + { "percentage", required_argument, NULL, 'p' }, + { "quiet", no_argument, NULL, 'q' }, + { "scan", no_argument, NULL, 's' }, + { "size", required_argument, NULL, 'S' }, + { "time", required_argument, NULL, 't' }, + { "truncate", no_argument, NULL, 'T' }, + { "undelete", no_argument, NULL, 'u' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + int c = -1; + char *end = NULL; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + + opterr = 0; /* We'll handle the errors, thank you. */ + + opts.mode = MODE_NONE; + opts.uinode = -1; + opts.percent = -1; + opts.fillbyte = -1; + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) { + opts.device = argv[optind-1]; + } else { + opts.device = NULL; + err++; + } + break; + case 'b': + if (opts.fillbyte == (char)-1) { + end = NULL; + opts.fillbyte = strtol(optarg, &end, 0); + if (end && *end) + err++; + } else { + err++; + } + break; + case 'C': + opts.match_case++; + break; + case 'c': + if (opts.mode == MODE_NONE) { + if (!utils_parse_range(optarg, + &opts.mft_begin, &opts.mft_end, TRUE)) + err++; + opts.mode = MODE_COPY; + } else { + opts.mode = MODE_ERROR; + } + break; + case 'd': + if (!opts.dest) + opts.dest = optarg; + else + err++; + break; + case 'f': + opts.force++; + break; + case 'h': + case '?': + if (ntfs_log_parse_option (argv[optind-1])) + break; + help++; + break; + case 'i': + end = NULL; + /* parse inodes */ + if (parse_inode_arg() == -1) + err++; + if (end && *end) + err++; + break; + case 'm': + if (!opts.match) { + if (!transform(optarg, &opts.match)) { + err++; + } else { + /* set regex-flag on true ;) */ + with_regex= 1; + } + } else { + err++; + } + break; + case 'o': + if (!opts.output) { + opts.output = optarg; + } else { + err++; + } + break; + case 'O': + if (!opts.optimistic) { + opts.optimistic++; + } else { + err++; + } + break; + case 'P': + if (!opts.parent) { + opts.parent++; + } else { + err++; + } + break; + case 'p': + if (opts.percent == -1) { + end = NULL; + opts.percent = strtol(optarg, &end, 0); + if (end && ((*end != '%') && (*end != 0))) + err++; + } else { + err++; + } + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 's': + if (opts.mode == MODE_NONE) + opts.mode = MODE_SCAN; + else + opts.mode = MODE_ERROR; + break; + case 'S': + if ((opts.size_begin > 0) || (opts.size_end > 0) || + !utils_parse_range(optarg, &opts.size_begin, + &opts.size_end, TRUE)) { + err++; + } + break; + case 't': + if (opts.since == 0) { + if (!parse_time(optarg, &opts.since)) + err++; + } else { + err++; + } + break; + case 'T': + opts.truncate++; + break; + case 'u': + if (opts.mode == MODE_NONE) { + opts.mode = MODE_UNDELETE; + } else { + opts.mode = MODE_ERROR; + } + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + ver++; + break; + default: + if (((optopt == 'b') || (optopt == 'c') || + (optopt == 'd') || (optopt == 'm') || + (optopt == 'o') || (optopt == 'p') || + (optopt == 'S') || (optopt == 't') || + (optopt == 'u')) && (!optarg)) { + ntfs_log_error("Option '%s' requires an argument.\n", argv[optind-1]); + } else { + ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); + } + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if (opts.device == NULL) { + if (argc > 1) + ntfs_log_error("You must specify exactly one device.\n"); + err++; + } + + if (opts.mode == MODE_NONE) { + opts.mode = MODE_SCAN; + } + + switch (opts.mode) { + case MODE_SCAN: + if (opts.output || opts.dest || opts.truncate || + (opts.fillbyte != (char)-1)) { + ntfs_log_error("Scan can only be used with --percent, " + "--match, --ignore-case, --size and --time.\n"); + err++; + } + if (opts.match_case && !opts.match) { + ntfs_log_error("The --case option doesn't make sense without the --match option\n"); + err++; + } + break; + + case MODE_UNDELETE: + /*if ((opts.percent != -1) || (opts.size_begin > 0) || (opts.size_end > 0)) { + ntfs_log_error("Undelete can only be used with " + "--output, --destination, --byte and --truncate.\n"); + err++; + }*/ + break; + case MODE_COPY: + if ((opts.fillbyte != (char)-1) || opts.truncate || + (opts.percent != -1) || + opts.match || opts.match_case || + (opts.size_begin > 0) || + (opts.size_end > 0)) { + ntfs_log_error("Copy can only be used with --output and --destination.\n"); + err++; + } + break; + default: + ntfs_log_error("You can only select one of Scan, Undelete or Copy.\n"); + err++; + } + + if ((opts.percent < -1) || (opts.percent > 100)) { + ntfs_log_error("Percentage value must be in the range 0 - 100.\n"); + err++; + } + + if (opts.quiet) { + if (opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose at the same time.\n"); + err++; + } else if (opts.mode == MODE_SCAN) { + ntfs_log_error("You may not use --quiet when scanning a volume.\n"); + err++; + } + } + + if (opts.parent && !opts.verbose) { + ntfs_log_error("To use --parent, you must also use --verbose.\n"); + err++; + } + } + + if (opts.fillbyte == (char)-1) + opts.fillbyte = 0; + + if (ver) + version(); + if (help || err) + usage(); + + return (!err && !help && !ver); +} + +/** + * free_file - Release the resources used by a file object + * @file: The unwanted file object + * + * This will free up the memory used by a file object and iterate through the + * object's children, freeing their resources too. + * + * Return: none + */ +static void free_file(struct ufile *file) +{ + struct list_head *item, *tmp; + + if (!file) + return; + + list_for_each_safe(item, tmp, &file->name) { /* List of filenames */ + struct filename *f = list_entry(item, struct filename, list); + ntfs_log_debug("freeing filename '%s'", f->name ? f->name : + NONE); + if (f->name) + free(f->name); + if (f->parent_name) { + ntfs_log_debug(" and parent filename '%s'", + f->parent_name); + free(f->parent_name); + } + ntfs_log_debug(".\n"); + free(f); + } + + list_for_each_safe(item, tmp, &file->data) { /* List of data streams */ + struct data *d = list_entry(item, struct data, list); + ntfs_log_debug("Freeing data stream '%s'.\n", d->name ? + d->name : UNNAMED); + if (d->name) + free(d->name); + if (d->runlist) + free(d->runlist); + free(d); + } + + free(file->mft); + free(file); +} + +/** + * verify_parent - confirm a record is parent of a file + * @name: a filename of the file + * @rec: the mft record of the possible parent + * + * Check that @rec is the parent of the file represented by @name. + * If @rec is a directory, but it is created after @name, then we + * can't determine whether @rec is really @name's parent. + * + * Return: @rec's filename, either same name space as @name or lowest space. + * NULL if can't determine parenthood or on error. + */ +static FILE_NAME_ATTR* verify_parent(struct filename* name, MFT_RECORD* rec) +{ + ATTR_RECORD *attr30; + FILE_NAME_ATTR *filename_attr = NULL, *lowest_space_name = NULL; + ntfs_attr_search_ctx *ctx; + int found_same_space = 1; + + if (!name || !rec) + return NULL; + + if (!(rec->flags & MFT_RECORD_IS_DIRECTORY)) { + return NULL; + } + + ctx = ntfs_attr_get_search_ctx(NULL, rec); + if (!ctx) { + ntfs_log_error("ERROR: Couldn't create a search context.\n"); + return NULL; + } + + attr30 = find_attribute(AT_FILE_NAME, ctx); + if (!attr30) { + return NULL; + } + + filename_attr = (FILE_NAME_ATTR*)((char*)attr30 + le16_to_cpu(attr30->value_offset)); + /* if name is older than this dir -> can't determine */ + if (ntfs2timespec(filename_attr->creation_time).tv_sec > name->date_c) { + return NULL; + } + + if (filename_attr->file_name_type != name->name_space) { + found_same_space = 0; + lowest_space_name = filename_attr; + + while (!found_same_space && (attr30 = find_attribute(AT_FILE_NAME, ctx))) { + filename_attr = (FILE_NAME_ATTR*)((char*)attr30 + le16_to_cpu(attr30->value_offset)); + + if (filename_attr->file_name_type == name->name_space) { + found_same_space = 1; + } else { + if (filename_attr->file_name_type < lowest_space_name->file_name_type) { + lowest_space_name = filename_attr; + } + } + } + } + + ntfs_attr_put_search_ctx(ctx); + + return (found_same_space ? filename_attr : lowest_space_name); +} + +/** + * get_parent_name - Find the name of a file's parent. + * @name: the filename whose parent's name to find + */ +static void get_parent_name(struct filename* name, ntfs_volume* vol) +{ + ntfs_attr* mft_data; + MFT_RECORD* rec; + FILE_NAME_ATTR* filename_attr; + long long inode_num; + + if (!name || !vol) + return; + + rec = calloc(1, vol->mft_record_size); + if (!rec) { + ntfs_log_error("ERROR: Couldn't allocate memory in get_parent_name()\n"); + return; + } + + mft_data = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0); + if (!mft_data) { + ntfs_log_perror("ERROR: Couldn't open $MFT/$DATA"); + } else { + inode_num = MREF(name->parent_mref); + + if (ntfs_attr_pread(mft_data, vol->mft_record_size * inode_num, vol->mft_record_size, rec) < 1) { + ntfs_log_error("ERROR: Couldn't read MFT Record %lld.\n", inode_num); + } else if ((filename_attr = verify_parent(name, rec))) { + if (ntfs_ucstombs(filename_attr->file_name, + filename_attr->file_name_length, + &name->parent_name, 0) < 0) { + ntfs_log_debug("ERROR: Couldn't translate " + "filename to current " + "locale.\n"); + name->parent_name = NULL; + } + } + } + + if (mft_data) { + ntfs_attr_close(mft_data); + } + + if (rec) { + free(rec); + } + + return; +} + +/** + * get_filenames - Read an MFT Record's $FILENAME attributes + * @file: The file object to work with + * + * A single file may have more than one filename. This is quite common. + * Windows creates a short DOS name for each long name, e.g. LONGFI~1.XYZ, + * LongFiLeName.xyZ. + * + * The filenames that are found are put in filename objects and added to a + * linked list of filenames in the file object. For convenience, the unicode + * filename is converted into the current locale and stored in the filename + * object. + * + * One of the filenames is picked (the one with the lowest numbered namespace) + * and its locale friendly name is put in pref_name. + * + * Return: n The number of $FILENAME attributes found + * -1 Error + */ +static int get_filenames(struct ufile *file, ntfs_volume* vol) +{ + ATTR_RECORD *rec; + FILE_NAME_ATTR *attr; + ntfs_attr_search_ctx *ctx; + struct filename *name; + int count = 0; + int space = 4; + + if (!file) + return -1; + + ctx = ntfs_attr_get_search_ctx(NULL, file->mft); + if (!ctx) + return -1; + + while ((rec = find_attribute(AT_FILE_NAME, ctx))) { + /* We know this will always be resident. */ + attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->value_offset)); + + name = calloc(1, sizeof(*name)); + if (!name) { + ntfs_log_error("ERROR: Couldn't allocate memory in get_filenames().\n"); + count = -1; + break; + } + + name->uname = attr->file_name; + name->uname_len = attr->file_name_length; + name->name_space = attr->file_name_type; + name->size_alloc = sle64_to_cpu(attr->allocated_size); + name->size_data = sle64_to_cpu(attr->data_size); + name->flags = attr->file_attributes; + + name->date_c = ntfs2timespec(attr->creation_time).tv_sec; + name->date_a = ntfs2timespec(attr->last_data_change_time).tv_sec; + name->date_m = ntfs2timespec(attr->last_mft_change_time).tv_sec; + name->date_r = ntfs2timespec(attr->last_access_time).tv_sec; + + if (ntfs_ucstombs(name->uname, name->uname_len, &name->name, + 0) < 0) { + ntfs_log_debug("ERROR: Couldn't translate filename to " + "current locale.\n"); + } + + name->parent_name = NULL; + + if (opts.parent) { + name->parent_mref = attr->parent_directory; + get_parent_name(name, vol); + } + + if (name->name_space < space) { + file->pref_name = name->name; + file->pref_pname = name->parent_name; + space = name->name_space; + } + + file->max_size = max(file->max_size, name->size_alloc); + file->max_size = max(file->max_size, name->size_data); + + list_add_tail(&name->list, &file->name); + count++; + } + + ntfs_attr_put_search_ctx(ctx); + ntfs_log_debug("File has %d names.\n", count); + return count; +} + +/** + * get_data - Read an MFT Record's $DATA attributes + * @file: The file object to work with + * @vol: An ntfs volume obtained from ntfs_mount + * + * A file may have more than one data stream. All files will have an unnamed + * data stream which contains the file's data. Some Windows applications store + * extra information in a separate stream. + * + * The streams that are found are put in data objects and added to a linked + * list of data streams in the file object. + * + * Return: n The number of $FILENAME attributes found + * -1 Error + */ +static int get_data(struct ufile *file, ntfs_volume *vol) +{ + ATTR_RECORD *rec; + ntfs_attr_search_ctx *ctx; + int count = 0; + struct data *data; + + if (!file) + return -1; + + ctx = ntfs_attr_get_search_ctx(NULL, file->mft); + if (!ctx) + return -1; + + while ((rec = find_attribute(AT_DATA, ctx))) { + data = calloc(1, sizeof(*data)); + if (!data) { + ntfs_log_error("ERROR: Couldn't allocate memory in get_data().\n"); + count = -1; + break; + } + + data->resident = !rec->non_resident; + data->compressed = rec->flags & ATTR_IS_COMPRESSED; + data->encrypted = rec->flags & ATTR_IS_ENCRYPTED; + + if (rec->name_length) { + data->uname = (ntfschar *) ((char *) rec + le16_to_cpu(rec->name_offset)); + data->uname_len = rec->name_length; + + if (ntfs_ucstombs(data->uname, data->uname_len, &data->name, + 0) < 0) { + ntfs_log_error("ERROR: Cannot translate name into current locale.\n"); + } + } + + if (data->resident) { + data->size_data = le32_to_cpu(rec->value_length); + data->data = ((char*) (rec)) + le16_to_cpu(rec->value_offset); + } else { + data->size_alloc = sle64_to_cpu(rec->allocated_size); + data->size_data = sle64_to_cpu(rec->data_size); + data->size_init = sle64_to_cpu(rec->initialized_size); + data->size_vcn = sle64_to_cpu(rec->highest_vcn) + 1; + } + + data->runlist = ntfs_mapping_pairs_decompress(vol, rec, NULL); + if (!data->runlist) { + ntfs_log_debug("Couldn't decompress the data runs.\n"); + } + + file->max_size = max(file->max_size, data->size_data); + file->max_size = max(file->max_size, data->size_init); + + list_add_tail(&data->list, &file->data); + count++; + } + + ntfs_attr_put_search_ctx(ctx); + ntfs_log_debug("File has %d data streams.\n", count); + return count; +} + +/** + * read_record - Read an MFT record into memory + * @vol: An ntfs volume obtained from ntfs_mount + * @record: The record number to read + * + * Read the specified MFT record and gather as much information about it as + * possible. + * + * Return: Pointer A ufile object containing the results + * NULL Error + */ +static struct ufile * read_record(ntfs_volume *vol, long long record) +{ + ATTR_RECORD *attr10, *attr20, *attr90; + struct ufile *file; + ntfs_attr *mft; + + if (!vol) + return NULL; + + file = calloc(1, sizeof(*file)); + if (!file) { + ntfs_log_error("ERROR: Couldn't allocate memory in read_record()\n"); + return NULL; + } + + INIT_LIST_HEAD(&file->name); + INIT_LIST_HEAD(&file->data); + file->inode = record; + + file->mft = malloc(vol->mft_record_size); + if (!file->mft) { + ntfs_log_error("ERROR: Couldn't allocate memory in read_record()\n"); + free_file(file); + return NULL; + } + + mft = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0); + if (!mft) { + ntfs_log_perror("ERROR: Couldn't open $MFT/$DATA"); + free_file(file); + return NULL; + } + + if (ntfs_attr_mst_pread(mft, vol->mft_record_size * record, 1, vol->mft_record_size, file->mft) < 1) { + ntfs_log_error("ERROR: Couldn't read MFT Record %lld.\n", record); + ntfs_attr_close(mft); + free_file(file); + return NULL; + } + + ntfs_attr_close(mft); + mft = NULL; + + attr10 = find_first_attribute(AT_STANDARD_INFORMATION, file->mft); + attr20 = find_first_attribute(AT_ATTRIBUTE_LIST, file->mft); + attr90 = find_first_attribute(AT_INDEX_ROOT, file->mft); + + ntfs_log_debug("Attributes present: %s %s %s.\n", attr10?"0x10":"", + attr20?"0x20":"", attr90?"0x90":""); + + if (attr10) { + STANDARD_INFORMATION *si; + si = (STANDARD_INFORMATION *) ((char *) attr10 + le16_to_cpu(attr10->value_offset)); + file->date = ntfs2timespec(si->last_data_change_time).tv_sec; + } + + if (attr20 || !attr10) + file->attr_list = 1; + if (attr90) + file->directory = 1; + + if (get_filenames(file, vol) < 0) { + ntfs_log_error("ERROR: Couldn't get filenames.\n"); + } + if (get_data(file, vol) < 0) { + ntfs_log_error("ERROR: Couldn't get data streams.\n"); + } + + return file; +} + +/** + * calc_percentage - Calculate how much of the file is recoverable + * @file: The file object to work with + * @vol: An ntfs volume obtained from ntfs_mount + * + * Read through all the $DATA streams and determine if each cluster in each + * stream is still free disk space. This is just measuring the potential for + * recovery. The data may have still been overwritten by a another file which + * was then deleted. + * + * Files with a resident $DATA stream will have a 100% potential. + * + * N.B. If $DATA attribute spans more than one MFT record (i.e. badly + * fragmented) then only the data in this segment will be used for the + * calculation. + * + * N.B. Currently, compressed and encrypted files cannot be recovered, so they + * will return 0%. + * + * Return: n The percentage of the file that _could_ be recovered + * -1 Error + */ +static int calc_percentage(struct ufile *file, ntfs_volume *vol) +{ + runlist_element *rl = NULL; + struct list_head *pos; + struct data *data; + long long i, j; + long long start, end; + int clusters_inuse, clusters_free; + int percent = 0; + + if (!file || !vol) + return -1; + + if (file->directory) { + ntfs_log_debug("Found a directory: not recoverable.\n"); + return 0; + } + + if (list_empty(&file->data)) { + ntfs_log_verbose("File has no data streams.\n"); + return 0; + } + + list_for_each(pos, &file->data) { + data = list_entry(pos, struct data, list); + clusters_inuse = 0; + clusters_free = 0; + + if (data->encrypted) { + ntfs_log_verbose("File is encrypted, recovery is impossible.\n"); + continue; + } + + if (data->compressed) { + ntfs_log_verbose("File is compressed, recovery not yet implemented.\n"); + continue; + } + + if (data->resident) { + ntfs_log_verbose("File is resident, therefore recoverable.\n"); + percent = 100; + data->percent = 100; + continue; + } + + rl = data->runlist; + if (!rl) { + ntfs_log_verbose("File has no runlist, hence no data.\n"); + continue; + } + + if (rl[0].length <= 0) { + ntfs_log_verbose("File has an empty runlist, hence no data.\n"); + continue; + } + + if (rl[0].lcn == LCN_RL_NOT_MAPPED) { /* extended mft record */ + ntfs_log_verbose("Missing segment at beginning, %lld " + "clusters\n", (long long)rl[0].length); + clusters_inuse += rl[0].length; + rl++; + } + + for (i = 0; rl[i].length > 0; i++) { + if (rl[i].lcn == LCN_RL_NOT_MAPPED) { + ntfs_log_verbose("Missing segment at end, %lld " + "clusters\n", + (long long)rl[i].length); + clusters_inuse += rl[i].length; + continue; + } + + if (rl[i].lcn == LCN_HOLE) { + clusters_free += rl[i].length; + continue; + } + + start = rl[i].lcn; + end = rl[i].lcn + rl[i].length; + + for (j = start; j < end; j++) { + if (utils_cluster_in_use(vol, j)) + clusters_inuse++; + else + clusters_free++; + } + } + + if ((clusters_inuse + clusters_free) == 0) { + ntfs_log_error("ERROR: Unexpected error whilst " + "calculating percentage for inode %lld\n", + file->inode); + continue; + } + + data->percent = (clusters_free * 100) / + (clusters_inuse + clusters_free); + + percent = max(percent, data->percent); + } + + ntfs_log_verbose("File is %d%% recoverable\n", percent); + return percent; +} + +/** + * dump_record - Print everything we know about an MFT record + * @file: The file to work with + * + * Output the contents of the file object. This will print everything that has + * been read from the MFT record, or implied by various means. + * + * Because of the redundant nature of NTFS, there will be some duplication of + * information, though it will have been read from different sources. + * + * N.B. If the filename is missing, or couldn't be converted to the current + * locale, "" will be displayed. + * + * Return: none + */ +static void dump_record(struct ufile *file) +{ + char buffer[20]; + const char *name; + struct list_head *item; + int i; + + if (!file) + return; + + ntfs_log_quiet("MFT Record %lld\n", file->inode); + ntfs_log_quiet("Type: %s\n", (file->directory) ? "Directory" : "File"); + strftime(buffer, sizeof(buffer), "%F %R", localtime(&file->date)); + ntfs_log_quiet("Date: %s\n", buffer); + + if (file->attr_list) + ntfs_log_quiet("Metadata may span more than one MFT record\n"); + + list_for_each(item, &file->name) { + struct filename *f = list_entry(item, struct filename, list); + + if (f->name) + name = f->name; + else + name = NONE; + + ntfs_log_quiet("Filename: (%d) %s\n", f->name_space, f->name); + ntfs_log_quiet("File Flags: "); + if (f->flags & FILE_ATTR_SYSTEM) ntfs_log_quiet("System "); + if (f->flags & FILE_ATTR_DIRECTORY) ntfs_log_quiet("Directory "); + if (f->flags & FILE_ATTR_SPARSE_FILE) ntfs_log_quiet("Sparse "); + if (f->flags & FILE_ATTR_REPARSE_POINT) ntfs_log_quiet("Reparse "); + if (f->flags & FILE_ATTR_COMPRESSED) ntfs_log_quiet("Compressed "); + if (f->flags & FILE_ATTR_ENCRYPTED) ntfs_log_quiet("Encrypted "); + if (!(f->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_DIRECTORY | + FILE_ATTR_SPARSE_FILE | FILE_ATTR_REPARSE_POINT | + FILE_ATTR_COMPRESSED | FILE_ATTR_ENCRYPTED))) { + ntfs_log_quiet("%s", NONE); + } + + ntfs_log_quiet("\n"); + + if (opts.parent) { + ntfs_log_quiet("Parent: %s\n", f->parent_name ? + f->parent_name : ""); + } + + ntfs_log_quiet("Size alloc: %lld\n", f->size_alloc); + ntfs_log_quiet("Size data: %lld\n", f->size_data); + + strftime(buffer, sizeof(buffer), "%F %R", localtime(&f->date_c)); + ntfs_log_quiet("Date C: %s\n", buffer); + strftime(buffer, sizeof(buffer), "%F %R", localtime(&f->date_a)); + ntfs_log_quiet("Date A: %s\n", buffer); + strftime(buffer, sizeof(buffer), "%F %R", localtime(&f->date_m)); + ntfs_log_quiet("Date M: %s\n", buffer); + strftime(buffer, sizeof(buffer), "%F %R", localtime(&f->date_r)); + ntfs_log_quiet("Date R: %s\n", buffer); + } + + ntfs_log_quiet("Data Streams:\n"); + list_for_each(item, &file->data) { + struct data *d = list_entry(item, struct data, list); + ntfs_log_quiet("Name: %s\n", (d->name) ? d->name : UNNAMED); + ntfs_log_quiet("Flags: "); + if (d->resident) ntfs_log_quiet("Resident\n"); + if (d->compressed) ntfs_log_quiet("Compressed\n"); + if (d->encrypted) ntfs_log_quiet("Encrypted\n"); + if (!d->resident && !d->compressed && !d->encrypted) + ntfs_log_quiet("None\n"); + else + ntfs_log_quiet("\n"); + + ntfs_log_quiet("Size alloc: %lld\n", d->size_alloc); + ntfs_log_quiet("Size data: %lld\n", d->size_data); + ntfs_log_quiet("Size init: %lld\n", d->size_init); + ntfs_log_quiet("Size vcn: %lld\n", d->size_vcn); + + ntfs_log_quiet("Data runs:\n"); + if ((!d->runlist) || (d->runlist[0].length <= 0)) { + ntfs_log_quiet(" None\n"); + } else { + for (i = 0; d->runlist[i].length > 0; i++) { + ntfs_log_quiet(" %lld @ %lld\n", + (long long)d->runlist[i].length, + (long long)d->runlist[i].lcn); + } + } + + ntfs_log_quiet("Amount potentially recoverable %d%%\n", d->percent); + } + + ntfs_log_quiet("________________________________________\n\n"); +} + +/** + * list_record - Print a one line summary of the file + * @file: The file to work with + * + * Print a one line description of a file. + * + * Inode Flags %age Date Size Filename + * + * The output will contain the file's inode number (MFT Record), some flags, + * the percentage of the file that is recoverable, the last modification date, + * the size and the filename. + * + * The flags are F/D = File/Directory, N/R = Data is (Non-)Resident, + * C = Compressed, E = Encrypted, ! = Metadata may span multiple records. + * + * N.B. The file size is stored in many forms in several attributes. This + * display the largest it finds. + * + * N.B. If the filename is missing, or couldn't be converted to the current + * locale, "" will be displayed. + * + * Return: none + */ +static void list_record(struct ufile *file) +{ + char buffer[20]; + struct list_head *item; + const char *name = NULL; + long long size = 0; + int percent = 0; + + char flagd = '.', flagr = '.', flagc = '.', flagx = '.'; + + strftime(buffer, sizeof(buffer), "%F", localtime(&file->date)); + + if (file->attr_list) + flagx = '!'; + + if (file->directory) + flagd = 'D'; + else + flagd = 'F'; + + list_for_each(item, &file->data) { + struct data *d = list_entry(item, struct data, list); + + if (!d->name) { + if (d->resident) flagr = 'R'; + else flagr = 'N'; + if (d->compressed) flagc = 'C'; /* These two are mutually exclusive */ + if (d->encrypted) flagc = 'E'; + + percent = max(percent, d->percent); + } + + size = max(size, d->size_data); + size = max(size, d->size_init); + } + + if (file->pref_name) + name = file->pref_name; + else + name = NONE; + + ntfs_log_quiet("%-8lld %c%c%c%c %3d%% %s %9lld %s\n", + file->inode, flagd, flagr, flagc, flagx, + percent, buffer, size, name); + +} + +/** + * name_match - Does a file have a name matching a regex + * @re: The regular expression object + * @file: The file to be tested + * + * Iterate through the file's $FILENAME attributes and compare them against the + * regular expression, created with regcomp. + * + * Return: 1 There is a matching filename. + * 0 There is no match. + */ +static int name_match(regex_t *re, struct ufile *file) +{ + struct list_head *item; + int result; + + if (!re || !file) + return 0; + + list_for_each(item, &file->name) { + struct filename *f = list_entry(item, struct filename, list); + + if (!f->name) + continue; + result = regexec(re, f->name, 0, NULL, 0); + if (result < 0) { + ntfs_log_perror("Couldn't compare filename with regex"); + return 0; + } else if (result == REG_NOERROR) { + ntfs_log_debug("Found a matching filename.\n"); + return 1; + } + } + + ntfs_log_debug("Filename '%s' doesn't match regex.\n", file->pref_name); + return 0; +} + +/** + * write_data - Write out a block of data + * @fd: File descriptor to write to + * @buffer: Data to write + * @bufsize: Amount of data to write + * + * Write a block of data to a file descriptor. + * + * Return: -1 Error, something went wrong + * 0 Success, all the data was written + */ +static unsigned int write_data(int fd, const char *buffer, + unsigned int bufsize) +{ + ssize_t result1, result2; + + if (!buffer) { + errno = EINVAL; + return -1; + } + + result1 = write(fd, buffer, bufsize); + if ((result1 == (ssize_t) bufsize) || (result1 < 0)) + return result1; + + /* Try again with the rest of the buffer */ + buffer += result1; + bufsize -= result1; + + result2 = write(fd, buffer, bufsize); + if (result2 < 0) + return result1; + + return result1 + result2; +} + +/** + * create_pathname - Create a path/file from some components + * @dir: Directory in which to create the file (optional) + * @name: Filename to give the file (optional) + * @stream: Name of the stream (optional) + * @buffer: Store the result here + * @bufsize: Size of buffer + * + * Create a filename from various pieces. The output will be of the form: + * dir/file + * dir/file:stream + * file + * file:stream + * + * All the components are optional. If the name is missing, "unknown" will be + * used. If the directory is missing the file will be created in the current + * directory. If the stream name is present it will be appended to the + * filename, delimited by a colon. + * + * N.B. If the buffer isn't large enough the name will be truncated. + * + * Return: n Length of the allocated name + */ +static int create_pathname(const char *dir, const char *name, + const char *stream, char *buffer, int bufsize) +{ + if (!name) + name = UNKNOWN; + + if (dir) + if (stream) + snprintf(buffer, bufsize, "%s/%s:%s", dir, name, stream); + else + snprintf(buffer, bufsize, "%s/%s", dir, name); + else + if (stream) + snprintf(buffer, bufsize, "%s:%s", name, stream); + else + snprintf(buffer, bufsize, "%s", name); + + return strlen(buffer); +} + +/** + * open_file - Open a file to write to + * @pathname: Path, name and stream of the file to open + * + * Create a file and return the file descriptor. + * + * N.B. If option force is given and existing file will be overwritten. + * + * Return: -1 Error, failed to create the file + * n Success, this is the file descriptor + */ +static int open_file(const char *pathname) +{ + int flags; + + ntfs_log_verbose("Creating file: %s\n", pathname); + + if (opts.force) + flags = O_RDWR | O_CREAT | O_TRUNC; + else + flags = O_RDWR | O_CREAT | O_EXCL; + + return open(pathname, flags, S_IRUSR | S_IWUSR); +} + +/** + * set_date - Set the file's date and time + * @pathname: Path and name of the file to alter + * @date: Date and time to set + * + * Give a file a particular date and time. + * + * Return: 1 Success, set the file's date and time + * 0 Error, failed to change the file's date and time + */ +static int set_date(const char *pathname, time_t date) +{ + struct utimbuf ut; + + if (!pathname) + return 0; + + ut.actime = date; + ut.modtime = date; + if (utime(pathname, &ut)) { + ntfs_log_error("ERROR: Couldn't set the file's date and time\n"); + return 0; + } + return 1; +} + +/** + * undelete_file - Recover a deleted file from an NTFS volume + * @vol: An ntfs volume obtained from ntfs_mount + * @inode: MFT Record number to be recovered + * + * Read an MFT Record and try an recover any data associated with it. Some of + * the clusters may be in use; these will be filled with zeros or the fill byte + * supplied in the options. + * + * Each data stream will be recovered and saved to a file. The file's name will + * be the original filename and it will be written to the current directory. + * Any named data stream will be saved as filename:streamname. + * + * The output file's name and location can be altered by using the command line + * options. + * + * N.B. We cannot tell if someone has overwritten some of the data since the + * file was deleted. + * + * Return: 0 Error, something went wrong + * 1 Success, the data was recovered + */ +static int undelete_file(ntfs_volume *vol, long long inode) +{ + char pathname[256]; + char *buffer = NULL; + unsigned int bufsize; + struct ufile *file; + int i, j; + long long start, end; + runlist_element *rl; + struct list_head *item; + int fd = -1; + long long k; + int result = 0; + char *name; + long long cluster_count; /* I'll need this variable (see below). +mabs */ + + if (!vol) + return 0; + + /* try to get record */ + file = read_record(vol, inode); + if (!file || !file->mft) { + ntfs_log_error("Can't read info from mft record %lld.\n", inode); + return 0; + } + + /* if flag was not set, print file informations */ + if (avoid_duplicate_printing == 0) { + if (opts.verbose) { + dump_record(file); + } else { + list_record(file); + //ntfs_log_quiet("\n"); + } + } + + bufsize = vol->cluster_size; + buffer = malloc(bufsize); + if (!buffer) + goto free; + + /* calc_percentage() must be called before dump_record() or + * list_record(). Otherwise, when undeleting, a file will always be + * listed as 0% recoverable even if successfully undeleted. +mabs + */ + if (file->mft->flags & MFT_RECORD_IN_USE) { + ntfs_log_error("Record is in use by the mft\n"); + if (!opts.force) { + free(buffer); + free_file(file); + return 0; + } + ntfs_log_verbose("Forced to continue.\n"); + } + + if (calc_percentage(file, vol) == 0) { + ntfs_log_quiet("File has no recoverable data.\n"); + goto free; + } + + if (list_empty(&file->data)) { + ntfs_log_quiet("File has no data. There is nothing to recover.\n"); + goto free; + } + + list_for_each(item, &file->data) { + struct data *d = list_entry(item, struct data, list); + + if (opts.output) + name = opts.output; + else + name = file->pref_name; + + create_pathname(opts.dest, name, d->name, pathname, sizeof(pathname)); + if (d->resident) { + fd = open_file(pathname); + if (fd < 0) { + ntfs_log_perror("Couldn't create file"); + goto free; + } + + ntfs_log_verbose("File has resident data.\n"); + if (write_data(fd, d->data, d->size_data) < d->size_data) { + ntfs_log_perror("Write failed"); + close(fd); + goto free; + } + + if (close(fd) < 0) { + ntfs_log_perror("Close failed"); + } + fd = -1; + } else { + rl = d->runlist; + if (!rl) { + ntfs_log_verbose("File has no runlist, hence no data.\n"); + continue; + } + + if (rl[0].length <= 0) { + ntfs_log_verbose("File has an empty runlist, hence no data.\n"); + continue; + } + + fd = open_file(pathname); + if (fd < 0) { + ntfs_log_perror("Couldn't create output file"); + goto free; + } + + if (rl[0].lcn == LCN_RL_NOT_MAPPED) { /* extended mft record */ + ntfs_log_verbose("Missing segment at beginning, %lld " + "clusters.\n", + (long long)rl[0].length); + memset(buffer, opts.fillbyte, bufsize); + for (k = 0; k < rl[0].length * vol->cluster_size; k += bufsize) { + if (write_data(fd, buffer, bufsize) < bufsize) { + ntfs_log_perror("Write failed"); + close(fd); + goto free; + } + } + } + + cluster_count = 0LL; + for (i = 0; rl[i].length > 0; i++) { + + if (rl[i].lcn == LCN_RL_NOT_MAPPED) { + ntfs_log_verbose("Missing segment at end, " + "%lld clusters.\n", + (long long)rl[i].length); + memset(buffer, opts.fillbyte, bufsize); + for (k = 0; k < rl[k].length * vol->cluster_size; k += bufsize) { + if (write_data(fd, buffer, bufsize) < bufsize) { + ntfs_log_perror("Write failed"); + close(fd); + goto free; + } + cluster_count++; + } + continue; + } + + if (rl[i].lcn == LCN_HOLE) { + ntfs_log_verbose("File has a sparse section.\n"); + memset(buffer, 0, bufsize); + for (k = 0; k < rl[k].length * vol->cluster_size; k += bufsize) { + if (write_data(fd, buffer, bufsize) < bufsize) { + ntfs_log_perror("Write failed"); + close(fd); + goto free; + } + } + continue; + } + + start = rl[i].lcn; + end = rl[i].lcn + rl[i].length; + + for (j = start; j < end; j++) { + if (utils_cluster_in_use(vol, j) && !opts.optimistic) { + memset(buffer, opts.fillbyte, bufsize); + if (write_data(fd, buffer, bufsize) < bufsize) { + ntfs_log_perror("Write failed"); + close(fd); + goto free; + } + } else { + if (ntfs_cluster_read(vol, j, 1, buffer) < 1) { + ntfs_log_perror("Read failed"); + close(fd); + goto free; + } + if (write_data(fd, buffer, bufsize) < bufsize) { + ntfs_log_perror("Write failed"); + close(fd); + goto free; + } + cluster_count++; + } + } + } + ntfs_log_quiet("\n"); + + /* + * The following block of code implements the --truncate option. + * Its semantics are as follows: + * IF opts.truncate is set AND data stream currently being recovered is + * non-resident AND data stream has no holes (100% recoverability) AND + * 0 <= (data->size_alloc - data->size_data) <= vol->cluster_size AND + * cluster_count * vol->cluster_size == data->size_alloc THEN file + * currently being written is truncated to data->size_data bytes before + * it's closed. + * This multiple checks try to ensure that only files with consistent + * values of size/occupied clusters are eligible for truncation. Note + * that resident streams need not be truncated, since the original code + * already recovers their exact length. +mabs + */ + if (opts.truncate) { + if (d->percent == 100 && d->size_alloc >= d->size_data && + (d->size_alloc - d->size_data) <= (long long)vol->cluster_size && + cluster_count * (long long)vol->cluster_size == d->size_alloc) { + if (ftruncate(fd, (off_t)d->size_data)) + ntfs_log_perror("Truncation failed"); + } else ntfs_log_quiet("Truncation not performed because file has an " + "inconsistent $MFT record.\n"); + } + + if (close(fd) < 0) { + ntfs_log_perror("Close failed"); + } + fd = -1; + + } + set_date(pathname, file->date); + if (d->name) + ntfs_log_quiet("Undeleted '%s:%s' successfully.\n", file->pref_name, d->name); + else + ntfs_log_quiet("Undeleted '%s' successfully.\n", file->pref_name); + } + result = 1; +free: + if (buffer) + free(buffer); + free_file(file); + return result; +} + +/** + * scan_disk - Search an NTFS volume for files that could be undeleted + * @vol: An ntfs volume obtained from ntfs_mount + * + * Read through all the MFT entries looking for deleted files. For each one + * determine how much of the data lies in unused disk space. + * + * The list can be filtered by name, size and date, using command line options. + * + * Return: -1 Error, something went wrong + * n Success, the number of recoverable files + */ +static int scan_disk(ntfs_volume *vol) +{ + s64 nr_mft_records; + const int BUFSIZE = 8192; + char *buffer = NULL; + int results = 0; + ntfs_attr *attr; + long long size; + long long bmpsize; + int i, j, k, b; + int percent; + struct ufile *file; + regex_t re; + + if (!vol) + return -1; + + attr = ntfs_attr_open(vol->mft_ni, AT_BITMAP, AT_UNNAMED, 0); + if (!attr) { + ntfs_log_perror("ERROR: Couldn't open $MFT/$BITMAP"); + return -1; + } + bmpsize = attr->initialized_size; + + buffer = malloc(BUFSIZE); + if (!buffer) { + ntfs_log_error("ERROR: Couldn't allocate memory in scan_disk()\n"); + results = -1; + goto out; + } + + if (opts.match) { + int flags = REG_NOSUB; + + if (!opts.match_case) + flags |= REG_ICASE; + if (regcomp(&re, opts.match, flags)) { + ntfs_log_error("ERROR: Couldn't create a regex.\n"); + goto out; + } + } + + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + + ntfs_log_quiet("Inode Flags %%age Date Size Filename\n"); + ntfs_log_quiet("---------------------------------------------------------------\n"); + for (i = 0; i < bmpsize; i += BUFSIZE) { + long long read_count = min((bmpsize - i), BUFSIZE); + size = ntfs_attr_pread(attr, i, read_count, buffer); + if (size < 0) + break; + + for (j = 0; j < size; j++) { + b = buffer[j]; + for (k = 0; k < 8; k++, b>>=1) { + if (((i+j)*8+k) >= nr_mft_records) + goto done; + if (b & 1) + continue; + file = read_record(vol, (i+j)*8+k); + if (!file) { + ntfs_log_error("Couldn't read MFT Record %d.\n", (i+j)*8+k); + continue; + } + + if ((opts.since > 0) && (file->date <= opts.since)) + goto skip; + if (opts.match && !name_match(&re, file)) + goto skip; + if (opts.size_begin && (opts.size_begin > file->max_size)) + goto skip; + if (opts.size_end && (opts.size_end < file->max_size)) + goto skip; + + percent = calc_percentage(file, vol); + if ((opts.percent == -1) || (percent >= opts.percent)) { + if (opts.verbose) + dump_record(file); + else + list_record(file); + + /* Was -u specified with no inode + so undelete file by regex */ + if (opts.mode == MODE_UNDELETE) { + if (!undelete_file(vol, file->inode)) + ntfs_log_verbose("ERROR: Failed to undelete " + "inode %lli\n!", + file->inode); + ntfs_log_info("\n"); + } + } + if (((opts.percent == -1) && (percent > 0)) || + ((opts.percent > 0) && (percent >= opts.percent))) { + results++; + } +skip: + free_file(file); + } + } + } +done: + ntfs_log_quiet("\nFiles with potentially recoverable content: %d\n", + results); +out: + if (opts.match) + regfree(&re); + free(buffer); + if (attr) + ntfs_attr_close(attr); + return results; +} + +/** + * copy_mft - Write a range of MFT Records to a file + * @vol: An ntfs volume obtained from ntfs_mount + * @mft_begin: First MFT Record to save + * @mft_end: Last MFT Record to save + * + * Read a number of MFT Records and write them to a file. + * + * Return: 0 Success, all the records were written + * 1 Error, something went wrong + */ +static int copy_mft(ntfs_volume *vol, long long mft_begin, long long mft_end) +{ + s64 nr_mft_records; + char pathname[256]; + ntfs_attr *mft; + char *buffer; + const char *name; + long long i; + int result = 1; + int fd; + + if (!vol) + return 1; + + if (mft_end < mft_begin) { + ntfs_log_error("Range to copy is backwards.\n"); + return 1; + } + + buffer = malloc(vol->mft_record_size); + if (!buffer) { + ntfs_log_error("Couldn't allocate memory in copy_mft()\n"); + return 1; + } + + mft = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0); + if (!mft) { + ntfs_log_perror("Couldn't open $MFT/$DATA"); + goto free; + } + + name = opts.output; + if (!name) { + name = MFTFILE; + ntfs_log_debug("No output filename, defaulting to '%s'.\n", + name); + } + + create_pathname(opts.dest, name, NULL, pathname, sizeof(pathname)); + fd = open_file(pathname); + if (fd < 0) { + ntfs_log_perror("Couldn't open output file '%s'", name); + goto attr; + } + + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + + mft_end = min(mft_end, nr_mft_records - 1); + + ntfs_log_debug("MFT records:\n"); + ntfs_log_debug("\tTotal: %8lld\n", nr_mft_records); + ntfs_log_debug("\tBegin: %8lld\n", mft_begin); + ntfs_log_debug("\tEnd: %8lld\n", mft_end); + + for (i = mft_begin; i <= mft_end; i++) { + if (ntfs_attr_pread(mft, vol->mft_record_size * i, + vol->mft_record_size, buffer) < vol->mft_record_size) { + ntfs_log_perror("Couldn't read MFT Record %lld", i); + goto close; + } + + if (write_data(fd, buffer, vol->mft_record_size) < vol->mft_record_size) { + ntfs_log_perror("Write failed"); + goto close; + } + } + + ntfs_log_verbose("Read %lld MFT Records\n", mft_end - mft_begin + 1); + result = 0; +close: + close(fd); +attr: + ntfs_attr_close(mft); +free: + free(buffer); + return result; +} + +/** + * handle_undelete + * + * Handles the undelete + */ +static int handle_undelete(ntfs_volume *vol) +{ + int result = 1; + int i; + unsigned long long inode; + + /* Check whether (an) inode(s) was specified or at least a regex! */ + if (nr_entries == 0) { + if (with_regex == 0) { + ntfs_log_error("ERROR: NO inode(s) AND NO match-regex " + "specified!\n"); + } else { + avoid_duplicate_printing= 1; + result = !scan_disk(vol); + if (result) + ntfs_log_verbose("ERROR: Failed to scan device " + "'%s'.\n", opts.device); + } + } else { + /* Normal undelete by specifying inode(s) */ + ntfs_log_quiet("Inode Flags %%age Date Size Filename\n"); + ntfs_log_quiet("---------------------------------------------------------------\n"); + + /* loop all given inodes */ + for (i = 0; i < nr_entries; i++) { + for (inode = ranges[i].begin; inode <= ranges[i].end; inode ++) { + /* Now undelete file */ + result = !undelete_file(vol, inode); + if (result) + ntfs_log_verbose("ERROR: Failed to " + "undelete inode %lli\n!", inode); + } + } + } + return (result); +} + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + ntfs_volume *vol; + int result = 1; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + with_regex = 0; + avoid_duplicate_printing = 0; + + if (!parse_options(argc, argv)) + goto free; + + utils_set_locale(); + + vol = utils_mount_volume(opts.device, MS_RDONLY, opts.force); + if (!vol) + return 1; + + /* handling of the different modes */ + switch (opts.mode) { + /* Scanning */ + case MODE_SCAN: + result = !scan_disk(vol); + if (result) + ntfs_log_verbose("ERROR: Failed to scan device '%s'.\n", + opts.device); + break; + + /* Undelete-handling */ + case MODE_UNDELETE: + result= handle_undelete(vol); + break; + + /* Handling of copy mft */ + case MODE_COPY: + result = !copy_mft(vol, opts.mft_begin, opts.mft_end); + if (result) + ntfs_log_verbose("ERROR: Failed to read MFT blocks " + "%lld-%lld.\n", opts.mft_begin, + min((vol->mft_na->initialized_size >> + vol->mft_record_size_bits) , opts.mft_end)); + break; + default: + ; /* Cannot happen */ + } + + ntfs_umount(vol, FALSE); +free: + if (opts.match) + free(opts.match); + + return result; +} + diff --git a/ntfsprogs/ntfsundelete.h b/ntfsprogs/ntfsundelete.h new file mode 100644 index 00000000..89daf4b8 --- /dev/null +++ b/ntfsprogs/ntfsundelete.h @@ -0,0 +1,115 @@ +/* + * ntfsundelete - Part of the Linux-NTFS project. + * + * Copyright (c) 2002 Richard Russon + * + * This utility will recover deleted files from an NTFS volume. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 _NTFSUNDELETE_H_ +#define _NTFSUNDELETE_H_ + +#include +#include + +#include "list.h" + +enum optmode { + MODE_NONE = 0, + MODE_SCAN, + MODE_UNDELETE, + MODE_COPY, + MODE_ERROR +}; + +struct options { + char *device; /* Device/File to work with */ + enum optmode mode; /* Scan / Undelete / Copy */ + int percent; /* Minimum recoverability */ + int uinode; /* Undelete this inode */ + char *dest; /* Save file to this directory */ + char *output; /* With this filename */ + char *match; /* Pattern for filename matching */ + int match_case; /* Case sensitive matching */ + int truncate; /* Truncate files to exact size. */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int force; /* Override common sense */ + int optimistic; /* Undelete in-use clusters as well */ + int parent; /* Show parent directory */ + time_t since; /* Since this time */ + s64 size_begin; /* Range for file size */ + s64 size_end; + s64 mft_begin; /* Range for mft copy */ + s64 mft_end; + char fillbyte; /* Use for unrecoverable sections */ + char padding[7]; /* Unused: padding to 64 bit. */ +}; + +struct filename { + struct list_head list; /* Previous/Next links */ + ntfschar *uname; /* Filename in unicode */ + int uname_len; /* and its length */ + long long size_alloc; /* Allocated size (multiple of cluster size) */ + long long size_data; /* Actual size of data */ + FILE_ATTR_FLAGS flags; + time_t date_c; /* Time created */ + time_t date_a; /* altered */ + time_t date_m; /* mft record changed */ + time_t date_r; /* read */ + char *name; /* Filename in current locale */ + FILE_NAME_TYPE_FLAGS name_space; + long long parent_mref; + char *parent_name; + char padding[7]; /* Unused: padding to 64 bit. */ +}; + +struct data { + struct list_head list; /* Previous/Next links */ + char *name; /* Stream name in current locale */ + ntfschar *uname; /* Unicode stream name */ + int uname_len; /* and its length */ + int resident; /* Stream is resident */ + int compressed; /* Stream is compressed */ + int encrypted; /* Stream is encrypted */ + long long size_alloc; /* Allocated size (multiple of cluster size) */ + long long size_data; /* Actual size of data */ + long long size_init; /* Initialised size, may be less than data size */ + long long size_vcn; /* Highest VCN in the data runs */ + runlist_element *runlist; /* Decoded data runs */ + int percent; /* Amount potentially recoverable */ + void *data; /* If resident, a pointer to the data */ + char padding[4]; /* Unused: padding to 64 bit. */ +}; + +struct ufile { + long long inode; /* MFT record number */ + time_t date; /* Last modification date/time */ + struct list_head name; /* A list of filenames */ + struct list_head data; /* A list of data streams */ + char *pref_name; /* Preferred filename */ + char *pref_pname; /* parent filename */ + long long max_size; /* Largest size we find */ + int attr_list; /* MFT record may be one of many */ + int directory; /* MFT record represents a directory */ + MFT_RECORD *mft; /* Raw MFT record */ + char padding[4]; /* Unused: padding to 64 bit. */ +}; + +#endif /* _NTFSUNDELETE_H_ */ + diff --git a/ntfsprogs/ntfswipe.c b/ntfsprogs/ntfswipe.c new file mode 100644 index 00000000..4d2a4207 --- /dev/null +++ b/ntfsprogs/ntfswipe.c @@ -0,0 +1,1436 @@ +/** + * ntfswipe - Part of the Linux-NTFS project. + * + * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2004 Yura Pakhuchiy + * + * This utility will overwrite unused space on an NTFS volume. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 "config.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDARG_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "ntfswipe.h" +#include "utils.h" +/* #include "version.h" */ + +static const char *EXEC_NAME = "ntfswipe"; +static struct options opts; + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Overwrite the unused space on an NTFS " + "Volume.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c) 2002-2005 Richard Russon\n"); + ntfs_log_info("Copyright (c) 2004 Yura Pakhuchiy\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device\n" + " -i --info Show volume information (default)\n" + "\n" + " -d --directory Wipe directory indexes\n" + " -l --logfile Wipe the logfile (journal)\n" + " -m --mft Wipe mft space\n" + " -p --pagefile Wipe pagefile (swap space)\n" + " -t --tails Wipe file tails\n" + " -u --unused Wipe unused clusters\n" + "\n" + " -a --all Wipe all unused space\n" + "\n" + " -c num --count num Number of times to write(default = 1)\n" + " -b list --bytes list List of values to write(default = 0)\n" + "\n" + " -n --no-action Do not write to disk\n" + " -f --force Use less caution\n" + " -q --quiet Less output\n" + " -v --verbose More output\n" + " -V --version Version information\n" + " -h --help Print this help\n\n", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_list - Read a comma-separated list of numbers + * @list: The comma-separated list of numbers + * @result: Store the parsed list here (must be freed by caller) + * + * Read a comma-separated list of numbers and allocate an array of ints to store + * them in. The numbers can be in decimal, octal or hex. + * + * N.B. The caller must free the memory returned in @result. + * N.B. If the function fails, @result is not changed. + * + * Return: 0 Error, invalid string + * n Success, the count of numbers parsed + */ +static int parse_list(char *list, int **result) +{ + char *ptr; + char *end; + int i; + int count; + int *mem = NULL; + + if (!list || !result) + return 0; + + for (count = 0, ptr = list; ptr; ptr = strchr(ptr+1, ',')) + count++; + + mem = malloc((count+1) * sizeof(int)); + if (!mem) { + ntfs_log_error("Couldn't allocate memory in parse_list().\n"); + return 0; + } + + memset(mem, 0xFF, (count+1) * sizeof(int)); + + for (ptr = list, i = 0; i < count; i++) { + + end = NULL; + mem[i] = strtol(ptr, &end, 0); + + if (!end || (end == ptr) || ((*end != ',') && (*end != 0))) { + ntfs_log_error("Invalid list '%s'\n", list); + free(mem); + return 0; + } + + if ((mem[i] < 0) || (mem[i] > 255)) { + ntfs_log_error("Bytes must be in range 0-255.\n"); + free(mem); + return 0; + } + + ptr = end + 1; + } + + ntfs_log_debug("Parsing list '%s' - ", list); + for (i = 0; i <= count; i++) + ntfs_log_debug("0x%02x ", mem[i]); + ntfs_log_debug("\n"); + + *result = mem; + return count; +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char *argv[]) +{ + static const char *sopt = "-ab:c:dfh?ilmnpqtuvV"; + static struct option lopt[] = { + { "all", no_argument, NULL, 'a' }, + { "bytes", required_argument, NULL, 'b' }, + { "count", required_argument, NULL, 'c' }, + { "directory", no_argument, NULL, 'd' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "info", no_argument, NULL, 'i' }, + { "logfile", no_argument, NULL, 'l' }, + { "mft", no_argument, NULL, 'm' }, + { "no-action", no_argument, NULL, 'n' }, + //{ "no-wait", no_argument, NULL, 0 }, + { "pagefile", no_argument, NULL, 'p' }, + { "quiet", no_argument, NULL, 'q' }, + { "tails", no_argument, NULL, 't' }, + { "unused", no_argument, NULL, 'u' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + int c = -1; + char *end; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + + opterr = 0; /* We'll handle the errors, thank you. */ + + opts.count = 1; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) { + opts.device = argv[optind-1]; + } else { + opts.device = NULL; + err++; + } + break; + + case 'i': + opts.info++; /* and fall through */ + case 'a': + opts.directory++; + opts.logfile++; + opts.mft++; + opts.pagefile++; + opts.tails++; + opts.unused++; + break; + case 'b': + if (!opts.bytes) { + if (!parse_list(optarg, &opts.bytes)) + err++; + } else { + err++; + } + break; + case 'c': + if (opts.count == 1) { + end = NULL; + opts.count = strtol(optarg, &end, 0); + if (end && *end) + err++; + } else { + err++; + } + break; + case 'd': + opts.directory++; + break; + case 'f': + opts.force++; + break; + case 'h': + case '?': + if (strncmp (argv[optind-1], "--log-", 6) == 0) { + if (!ntfs_log_parse_option (argv[optind-1])) + err++; + break; + } + help++; + break; + case 'l': + opts.logfile++; + break; + case 'm': + opts.mft++; + break; + case 'n': + opts.noaction++; + break; + case 'p': + opts.pagefile++; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 't': + opts.tails++; + break; + case 'u': + opts.unused++; + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + ver++; + break; + default: + if ((optopt == 'b') || (optopt == 'c')) { + ntfs_log_error("Option '%s' requires an argument.\n", argv[optind-1]); + } else { + ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); + } + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if (opts.device == NULL) { + if (argc > 1) + ntfs_log_error("You must specify exactly one device.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose at the same time.\n"); + err++; + } + + /* + if (opts.info && (opts.unused || opts.tails || opts.mft || opts.directory)) { + ntfs_log_error("You may not use any other options with --info.\n"); + err++; + } + */ + + if ((opts.count < 1) || (opts.count > 100)) { + ntfs_log_error("The iteration count must be between 1 and 100.\n"); + err++; + } + + /* Create a default list */ + if (!opts.bytes) { + opts.bytes = malloc(2 * sizeof(int)); + if (opts.bytes) { + opts.bytes[0] = 0; + opts.bytes[1] = -1; + } else { + ntfs_log_error("Couldn't allocate memory for byte list.\n"); + err++; + } + } + + if (!opts.directory && !opts.logfile && !opts.mft && + !opts.pagefile && !opts.tails && !opts.unused) { + opts.info = 1; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + return (!err && !help && !ver); +} + +/** + * wipe_unused - Wipe unused clusters + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * + * Read $Bitmap and wipe any clusters that are marked as not in use. + * + * Return: >0 Success, the attribute was wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_unused(ntfs_volume *vol, int byte, enum action act) +{ + s64 i; + s64 total = 0; + s64 result = 0; + u8 *buffer = NULL; + + if (!vol || (byte < 0)) + return -1; + + if (act != act_info) { + buffer = malloc(vol->cluster_size); + if (!buffer) { + ntfs_log_error("malloc failed\n"); + return -1; + } + memset(buffer, byte, vol->cluster_size); + } + + for (i = 0; i < vol->nr_clusters; i++) { + if (utils_cluster_in_use(vol, i)) { + //ntfs_log_verbose("cluster %lld is in use\n", i); + continue; + } + + if (act == act_wipe) { + //ntfs_log_verbose("cluster %lld is not in use\n", i); + result = ntfs_pwrite(vol->dev, vol->cluster_size * i, vol->cluster_size, buffer); + if (result != vol->cluster_size) { + ntfs_log_error("write failed\n"); + goto free; + } + } + + total += vol->cluster_size; + } + + ntfs_log_quiet("wipe_unused 0x%02x, %lld bytes\n", byte, (long long)total); +free: + free(buffer); + return total; +} + +/** + * wipe_compressed_attribute - Wipe compressed $DATA attribute + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * @na: Opened ntfs attribute + * + * Return: >0 Success, the attribute was wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_compressed_attribute(ntfs_volume *vol, int byte, + enum action act, ntfs_attr *na) +{ + unsigned char *buf; + s64 size, offset, ret, wiped = 0; + u16 block_size; + VCN cur_vcn = 0; + runlist *rlc = na->rl; + s64 cu_mask = na->compression_block_clusters - 1; + + while (rlc->length) { + cur_vcn += rlc->length; + if ((cur_vcn & cu_mask) || + (((rlc + 1)->length) && (rlc->lcn != LCN_HOLE))) { + rlc++; + continue; + } + + if (rlc->lcn == LCN_HOLE) { + runlist *rlt; + + offset = cur_vcn - rlc->length; + if (offset == (offset & (~cu_mask))) { + rlc++; + continue; + } + offset = (offset & (~cu_mask)) + << vol->cluster_size_bits; + rlt = rlc; + while ((rlt - 1)->lcn == LCN_HOLE) rlt--; + while (1) { + ret = ntfs_rl_pread(vol, na->rl, + offset, 2, &block_size); + block_size = le16_to_cpu(block_size); + if (ret != 2) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("ntfs_rl_pread failed"); + return -1; + } + if (block_size == 0) { + offset += 2; + break; + } + block_size &= 0x0FFF; + block_size += 3; + offset += block_size; + if (offset >= (((rlt->vcn) << + vol->cluster_size_bits) - 2)) + goto next; + } + size = (rlt->vcn << vol->cluster_size_bits) - offset; + } else { + size = na->allocated_size - na->data_size; + offset = (cur_vcn << vol->cluster_size_bits) - size; + } + + if (size < 0) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("bug or damaged fs: we want " + "allocate buffer size %lld bytes", size); + return -1; + } + + if ((act == act_info) || (!size)) { + wiped += size; + rlc++; + continue; + } + + buf = malloc(size); + if (!buf) { + ntfs_log_verbose("Not enough memory\n"); + ntfs_log_error("Not enough memory to allocate " + "%lld bytes", size); + return -1; + } + memset(buf, byte, size); + + ret = ntfs_rl_pwrite(vol, na->rl, 0, offset, size, buf); + free(buf); + if (ret != size) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("ntfs_rl_pwrite failed, offset %llu, " + "size %lld, vcn %lld", offset, size, rlc->vcn); + return -1; + } + wiped += ret; +next: + rlc++; + } + + return wiped; +} + +/** + * wipe_attribute - Wipe not compressed $DATA attribute + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * @na: Opened ntfs attribute + * + * Return: >0 Success, the attribute was wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_attribute(ntfs_volume *vol, int byte, enum action act, + ntfs_attr *na) +{ + unsigned char *buf; + s64 wiped; + s64 size; + u64 offset = na->data_size; + + if (!offset) + return 0; + if (NAttrEncrypted(na)) + offset = (((offset - 1) >> 10) + 1) << 10; + size = (vol->cluster_size - offset) % vol->cluster_size; + + if (act == act_info) + return size; + + buf = malloc(size); + if (!buf) { + ntfs_log_verbose("Not enough memory\n"); + ntfs_log_error("Not enough memory to allocate %lld bytes", size); + return -1; + } + memset(buf, byte, size); + + wiped = ntfs_rl_pwrite(vol, na->rl, 0, offset, size, buf); + if (wiped == -1) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("Couldn't wipe tail"); + } + + free(buf); + return wiped; +} + +/** + * wipe_tails - Wipe the file tails + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * + * Disk space is allocated in clusters. If a file isn't an exact multiple of + * the cluster size, there is some slack space at the end. Wipe this space. + * + * Return: >0 Success, the clusters were wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_tails(ntfs_volume *vol, int byte, enum action act) +{ + s64 total = 0; + s64 nr_mft_records, inode_num; + ntfs_inode *ni; + ntfs_attr *na; + + if (!vol || (byte < 0)) + return -1; + + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + + for (inode_num = 16; inode_num < nr_mft_records; inode_num++) { + s64 wiped; + + ntfs_log_verbose("Inode %lld - ", inode_num); + ni = ntfs_inode_open(vol, inode_num); + if (!ni) { + ntfs_log_verbose("Could not open inode\n"); + continue; + } + + if (ni->mrec->base_mft_record) { + ntfs_log_verbose("Not base mft record. Skipping\n"); + goto close_inode; + } + + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_verbose("Couldn't open $DATA attribute\n"); + goto close_inode; + } + + if (!NAttrNonResident(na)) { + ntfs_log_verbose("Resident $DATA attribute. Skipping.\n"); + goto close_attr; + } + + if (ntfs_attr_map_whole_runlist(na)) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("Can't map runlist (inode %lld)\n", inode_num); + goto close_attr; + } + + if (NAttrCompressed(na)) + wiped = wipe_compressed_attribute(vol, byte, act, na); + else + wiped = wipe_attribute(vol, byte, act, na); + + if (wiped == -1) { + ntfs_log_error(" (inode %lld)\n", inode_num); + goto close_attr; + } + + if (wiped) { + ntfs_log_verbose("Wiped %llu bytes\n", wiped); + total += wiped; + } else + ntfs_log_verbose("Nothing to wipe\n"); +close_attr: + ntfs_attr_close(na); +close_inode: + ntfs_inode_close(ni); + } + ntfs_log_quiet("wipe_tails 0x%02x, %lld bytes\n", byte, total); + return total; +} + +/** + * wipe_mft - Wipe the MFT slack space + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * + * MFT Records are 1024 bytes long, but some of this space isn't used. Wipe any + * unused space at the end of the record and wipe any unused records. + * + * Return: >0 Success, the clusters were wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_mft(ntfs_volume *vol, int byte, enum action act) +{ + // by considering the individual attributes we might be able to + // wipe a few more bytes at the attr's tail. + s64 nr_mft_records, i; + s64 total = 0; + s64 result = 0; + int size = 0; + u8 *buffer = NULL; + + if (!vol || (byte < 0)) + return -1; + + buffer = malloc(vol->mft_record_size); + if (!buffer) { + ntfs_log_error("malloc failed\n"); + return -1; + } + + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + + for (i = 0; i < nr_mft_records; i++) { + if (utils_mftrec_in_use(vol, i)) { + result = ntfs_attr_mst_pread(vol->mft_na, vol->mft_record_size * i, + 1, vol->mft_record_size, buffer); + if (result != 1) { + ntfs_log_error("error attr mst read %lld\n", + (long long)i); + total = -1; // XXX just negate result? + goto free; + } + + // We know that the end marker will only take 4 bytes + size = *((u32*) (buffer + 0x18)) - 4; + + if (act == act_info) { + //ntfs_log_info("mft %d\n", size); + total += size; + continue; + } + + memset(buffer + size, byte, vol->mft_record_size - size); + + result = ntfs_attr_mst_pwrite(vol->mft_na, vol->mft_record_size * i, + 1, vol->mft_record_size, buffer); + if (result != 1) { + ntfs_log_error("error attr mst write %lld\n", + (long long)i); + total = -1; + goto free; + } + + if ((vol->mft_record_size * (i+1)) <= vol->mftmirr_na->allocated_size) + { + // We have to reduce the update sequence number, or else... + u16 offset; + u16 usa; + offset = le16_to_cpu(*(buffer + 0x04)); + usa = le16_to_cpu(*(buffer + offset)); + *((u16*) (buffer + offset)) = cpu_to_le16(usa - 1); + + result = ntfs_attr_mst_pwrite(vol->mftmirr_na, vol->mft_record_size * i, + 1, vol->mft_record_size, buffer); + if (result != 1) { + ntfs_log_error("error attr mst write %lld\n", + (long long)i); + total = -1; + goto free; + } + } + + total += vol->mft_record_size; + } else { + if (act == act_info) { + total += vol->mft_record_size; + continue; + } + + // Build the record from scratch + memset(buffer, 0, vol->mft_record_size); + + // Common values + *((u32*) (buffer + 0x00)) = magic_FILE; // Magic + *((u16*) (buffer + 0x06)) = cpu_to_le16(0x0003); // USA size + *((u16*) (buffer + 0x10)) = cpu_to_le16(0x0001); // Seq num + *((u32*) (buffer + 0x1C)) = cpu_to_le32(vol->mft_record_size); // FILE size + *((u16*) (buffer + 0x28)) = cpu_to_le16(0x0001); // Attr ID + + if (vol->major_ver == 3) { + // Only XP and 2K3 + *((u16*) (buffer + 0x04)) = cpu_to_le16(0x0030); // USA offset + *((u16*) (buffer + 0x14)) = cpu_to_le16(0x0038); // Attr offset + *((u32*) (buffer + 0x18)) = cpu_to_le32(0x00000040); // FILE usage + *((u32*) (buffer + 0x38)) = cpu_to_le32(0xFFFFFFFF); // End marker + } else { + // Only NT and 2K + *((u16*) (buffer + 0x04)) = cpu_to_le16(0x002A); // USA offset + *((u16*) (buffer + 0x14)) = cpu_to_le16(0x0030); // Attr offset + *((u32*) (buffer + 0x18)) = cpu_to_le32(0x00000038); // FILE usage + *((u32*) (buffer + 0x30)) = cpu_to_le32(0xFFFFFFFF); // End marker + } + + result = ntfs_attr_mst_pwrite(vol->mft_na, vol->mft_record_size * i, + 1, vol->mft_record_size, buffer); + if (result != 1) { + ntfs_log_error("error attr mst write %lld\n", + (long long)i); + total = -1; + goto free; + } + + total += vol->mft_record_size; + } + } + + ntfs_log_quiet("wipe_mft 0x%02x, %lld bytes\n", byte, (long long)total); +free: + free(buffer); + return total; +} + +/** + * wipe_index_allocation - Wipe $INDEX_ALLOCATION attribute + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * @naa: Opened ntfs $INDEX_ALLOCATION attribute + * @nab: Opened ntfs $BITMAP attribute + * @indx_record_size: Size of INDX record + * + * Return: >0 Success, the clusters were wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_index_allocation(ntfs_volume *vol, int byte, enum action act + __attribute__((unused)), ntfs_attr *naa, ntfs_attr *nab, + u32 indx_record_size) +{ + s64 total = 0; + s64 wiped = 0; + s64 offset = 0; + s64 obyte = 0; + u64 wipe_offset; + s64 wipe_size; + u8 obit = 0; + u8 mask; + u8 *bitmap; + u8 *buf; + + bitmap = malloc(nab->data_size); + if (!bitmap) { + ntfs_log_verbose("malloc failed\n"); + ntfs_log_error("Couldn't allocate %lld bytes", nab->data_size); + return -1; + } + + if (ntfs_attr_pread(nab, 0, nab->data_size, bitmap) + != nab->data_size) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("Couldn't read $BITMAP"); + total = -1; + goto free_bitmap; + } + + buf = malloc(indx_record_size); + if (!buf) { + ntfs_log_verbose("malloc failed\n"); + ntfs_log_error("Couldn't allocate %u bytes", + (unsigned int)indx_record_size); + total = -1; + goto free_bitmap; + } + + while (offset < naa->allocated_size) { + mask = 1 << obit; + if (bitmap[obyte] & mask) { + INDEX_ALLOCATION *indx; + + s64 ret = ntfs_rl_pread(vol, naa->rl, + offset, indx_record_size, buf); + if (ret != indx_record_size) { + ntfs_log_verbose("ntfs_rl_pread failed\n"); + ntfs_log_error("Couldn't read INDX record"); + total = -1; + goto free_buf; + } + + indx = (INDEX_ALLOCATION *) buf; + if (ntfs_mst_post_read_fixup((NTFS_RECORD *)buf, + indx_record_size)) + ntfs_log_error("damaged fs: mst_post_read_fixup failed"); + + if ((le32_to_cpu(indx->index.allocated_size) + 0x18) != + indx_record_size) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("INDX record should be %u bytes", + (unsigned int)indx_record_size); + total = -1; + goto free_buf; + } + + wipe_offset = le32_to_cpu(indx->index.index_length) + 0x18; + wipe_size = indx_record_size - wipe_offset; + memset(buf + wipe_offset, byte, wipe_size); + if (ntfs_mst_pre_write_fixup((NTFS_RECORD *)indx, + indx_record_size)) + ntfs_log_error("damaged fs: mst_pre_write_protect failed"); + if (opts.verbose > 1) + ntfs_log_verbose("+"); + } else { + wipe_size = indx_record_size; + memset(buf, byte, wipe_size); + if (opts.verbose > 1) + ntfs_log_verbose("x"); + } + + wiped = ntfs_rl_pwrite(vol, naa->rl, 0, offset, indx_record_size, buf); + if (wiped != indx_record_size) { + ntfs_log_verbose("ntfs_rl_pwrite failed\n"); + ntfs_log_error("Couldn't wipe tail of INDX record"); + total = -1; + goto free_buf; + } + total += wipe_size; + + offset += indx_record_size; + obit++; + if (obit > 7) { + obit = 0; + obyte++; + } + } + if ((opts.verbose > 1) && (wiped != -1)) + ntfs_log_verbose("\n\t"); +free_buf: + free(buf); +free_bitmap: + free(bitmap); + return total; +} + +/** + * get_indx_record_size - determine size of INDX record from $INDEX_ROOT + * @nar: Opened ntfs $INDEX_ROOT attribute + * + * Return: >0 Success, return INDX record size + * 0 Error, something went wrong + */ +static u32 get_indx_record_size(ntfs_attr *nar) +{ + u32 indx_record_size; + + if (ntfs_attr_pread(nar, 8, 4, &indx_record_size) != 4) { + ntfs_log_verbose("Couldn't determine size of INDX record\n"); + ntfs_log_error("ntfs_attr_pread failed"); + return 0; + } + + indx_record_size = le32_to_cpu(indx_record_size); + if (!indx_record_size) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("INDX record should be 0"); + } + return indx_record_size; +} + +/** + * wipe_directory - Wipe the directory indexes + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * + * Directories are kept in sorted B+ Trees. Index blocks may not be full. Wipe + * the unused space at the ends of these blocks. + * + * Return: >0 Success, the clusters were wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_directory(ntfs_volume *vol, int byte, enum action act) +{ + s64 total = 0; + s64 nr_mft_records, inode_num; + ntfs_inode *ni; + ntfs_attr *naa; + ntfs_attr *nab; + ntfs_attr *nar; + + if (!vol || (byte < 0)) + return -1; + + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + + for (inode_num = 5; inode_num < nr_mft_records; inode_num++) { + u32 indx_record_size; + s64 wiped; + + ntfs_log_verbose("Inode %lld - ", inode_num); + ni = ntfs_inode_open(vol, inode_num); + if (!ni) { + if (opts.verbose > 2) + ntfs_log_verbose("Could not open inode\n"); + else + ntfs_log_verbose("\r"); + continue; + } + + if (ni->mrec->base_mft_record) { + if (opts.verbose > 2) + ntfs_log_verbose("Not base mft record. Skipping\n"); + else + ntfs_log_verbose("\r"); + goto close_inode; + } + + naa = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); + if (!naa) { + if (opts.verbose > 2) + ntfs_log_verbose("Couldn't open $INDEX_ALLOCATION\n"); + else + ntfs_log_verbose("\r"); + goto close_inode; + } + + if (!NAttrNonResident(naa)) { + ntfs_log_verbose("Resident $INDEX_ALLOCATION\n"); + ntfs_log_error("damaged fs: Resident $INDEX_ALLOCATION " + "(inode %lld)\n", inode_num); + goto close_attr_allocation; + } + + if (ntfs_attr_map_whole_runlist(naa)) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("Can't map runlist for $INDEX_ALLOCATION " + "(inode %lld)\n", inode_num); + goto close_attr_allocation; + } + + nab = ntfs_attr_open(ni, AT_BITMAP, NTFS_INDEX_I30, 4); + if (!nab) { + ntfs_log_verbose("Couldn't open $BITMAP\n"); + ntfs_log_error("damaged fs: $INDEX_ALLOCATION is present, " + "but we can't open $BITMAP with same " + "name (inode %lld)\n", inode_num); + goto close_attr_allocation; + } + + nar = ntfs_attr_open(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4); + if (!nar) { + ntfs_log_verbose("Couldn't open $INDEX_ROOT\n"); + ntfs_log_error("damaged fs: $INDEX_ALLOCATION is present, but " + "we can't open $INDEX_ROOT with same name" + " (inode %lld)\n", inode_num); + goto close_attr_bitmap; + } + + if (NAttrNonResident(nar)) { + ntfs_log_verbose("Not resident $INDEX_ROOT\n"); + ntfs_log_error("damaged fs: Not resident $INDEX_ROOT " + "(inode %lld)\n", inode_num); + goto close_attr_root; + } + + indx_record_size = get_indx_record_size(nar); + if (!indx_record_size) { + ntfs_log_error(" (inode %lld)\n", inode_num); + goto close_attr_root; + } + + wiped = wipe_index_allocation(vol, byte, act, + naa, nab, indx_record_size); + if (wiped == -1) { + ntfs_log_error(" (inode %lld)\n", inode_num); + goto close_attr_root; + } + + if (wiped) { + ntfs_log_verbose("Wiped %llu bytes\n", wiped); + total += wiped; + } else + ntfs_log_verbose("Nothing to wipe\n"); +close_attr_root: + ntfs_attr_close(nar); +close_attr_bitmap: + ntfs_attr_close(nab); +close_attr_allocation: + ntfs_attr_close(naa); +close_inode: + ntfs_inode_close(ni); + } + + ntfs_log_quiet("wipe_directory 0x%02x, %lld bytes\n", byte, total); + return total; +} + +/** + * wipe_logfile - Wipe the logfile (journal) + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * + * The logfile journals the metadata to give the volume fault-tolerance. If the + * volume is in a consistent state, then this information can be erased. + * + * Return: >0 Success, the clusters were wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_logfile(ntfs_volume *vol, int byte, enum action act + __attribute__((unused))) +{ + const int NTFS_BUF_SIZE2 = 8192; + //FIXME(?): We might need to zero the LSN field of every single mft + //record as well. (But, first try without doing that and see what + //happens, since chkdsk might pickup the pieces and do it for us...) + ntfs_inode *ni; + ntfs_attr *na; + s64 len, pos, count; + char buf[NTFS_BUF_SIZE2]; + int eo; + + /* We can wipe logfile only with 0xff. */ + byte = 0xff; + + if (!vol || (byte < 0)) + return -1; + + //ntfs_log_quiet("wipe_logfile(not implemented) 0x%02x\n", byte); + + if ((ni = ntfs_inode_open(vol, FILE_LogFile)) == NULL) { + ntfs_log_debug("Failed to open inode FILE_LogFile.\n"); + return -1; + } + + if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) { + ntfs_log_debug("Failed to open $FILE_LogFile/$DATA.\n"); + goto error_exit; + } + + /* The $DATA attribute of the $LogFile has to be non-resident. */ + if (!NAttrNonResident(na)) { + ntfs_log_debug("$LogFile $DATA attribute is resident!?!\n"); + errno = EIO; + goto io_error_exit; + } + + /* Get length of $LogFile contents. */ + len = na->data_size; + if (!len) { + ntfs_log_debug("$LogFile has zero length, no disk write " + "needed.\n"); + return 0; + } + + /* Read $LogFile until its end. We do this as a check for correct + length thus making sure we are decompressing the mapping pairs + array correctly and hence writing below is safe as well. */ + pos = 0; + while ((count = ntfs_attr_pread(na, pos, NTFS_BUF_SIZE2, buf)) > 0) + pos += count; + + if (count == -1 || pos != len) { + ntfs_log_debug("Amount of $LogFile data read does not " + "correspond to expected length!\n"); + if (count != -1) + errno = EIO; + goto io_error_exit; + } + + /* Fill the buffer with @byte's. */ + memset(buf, byte, NTFS_BUF_SIZE2); + + /* Set the $DATA attribute. */ + pos = 0; + while ((count = len - pos) > 0) { + if (count > NTFS_BUF_SIZE2) + count = NTFS_BUF_SIZE2; + + if ((count = ntfs_attr_pwrite(na, pos, count, buf)) <= 0) { + ntfs_log_debug("Failed to set the $LogFile attribute " + "value.\n"); + if (count != -1) + errno = EIO; + goto io_error_exit; + } + + pos += count; + } + + ntfs_attr_close(na); + ntfs_inode_close(ni); + ntfs_log_quiet("wipe_logfile 0x%02x, %lld bytes\n", byte, pos); + return pos; + +io_error_exit: + eo = errno; + ntfs_attr_close(na); + errno = eo; +error_exit: + eo = errno; + ntfs_inode_close(ni); + errno = eo; + return -1; +} + +/** + * wipe_pagefile - Wipe the pagefile (swap space) + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * + * pagefile.sys is used by Windows as extra virtual memory (swap space). + * Windows recreates the file at bootup, so it can be wiped without harm. + * + * Return: >0 Success, the clusters were wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_pagefile(ntfs_volume *vol, int byte, enum action act + __attribute__((unused))) +{ + // wipe completely, chkdsk doesn't do anything, booting writes header + const int NTFS_BUF_SIZE2 = 4096; + ntfs_inode *ni; + ntfs_attr *na; + s64 len, pos, count; + char buf[NTFS_BUF_SIZE2]; + int eo; + + if (!vol || (byte < 0)) + return -1; + + //ntfs_log_quiet("wipe_pagefile(not implemented) 0x%02x\n", byte); + + ni = ntfs_pathname_to_inode(vol, NULL, "pagefile.sys"); + if (!ni) { + ntfs_log_debug("Failed to open inode of pagefile.sys.\n"); + return 0; + } + + if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) { + ntfs_log_debug("Failed to open pagefile.sys/$DATA.\n"); + goto error_exit; + } + + /* The $DATA attribute of the pagefile.sys has to be non-resident. */ + if (!NAttrNonResident(na)) { + ntfs_log_debug("pagefile.sys $DATA attribute is resident!?!\n"); + errno = EIO; + goto io_error_exit; + } + + /* Get length of pagefile.sys contents. */ + len = na->data_size; + if (!len) { + ntfs_log_debug("pagefile.sys has zero length, no disk write " + "needed.\n"); + return 0; + } + + memset(buf, byte, NTFS_BUF_SIZE2); + + /* Set the $DATA attribute. */ + pos = 0; + while ((count = len - pos) > 0) { + if (count > NTFS_BUF_SIZE2) + count = NTFS_BUF_SIZE2; + + if ((count = ntfs_attr_pwrite(na, pos, count, buf)) <= 0) { + ntfs_log_debug("Failed to set the pagefile.sys " + "attribute value.\n"); + if (count != -1) + errno = EIO; + goto io_error_exit; + } + + pos += count; + } + + ntfs_attr_close(na); + ntfs_inode_close(ni); + ntfs_log_quiet("wipe_pagefile 0x%02x, %lld bytes\n", byte, pos); + return pos; + +io_error_exit: + eo = errno; + ntfs_attr_close(na); + errno = eo; +error_exit: + eo = errno; + ntfs_inode_close(ni); + errno = eo; + return -1; +} + +/** + * print_summary - Tell the user what we are about to do + * + * List the operations about to be performed. The output will be silenced by + * the --quiet option. + * + * Return: none + */ +static void print_summary(void) +{ + int i; + + if (opts.noaction) + ntfs_log_quiet("%s is in 'no-action' mode, it will NOT write to disk." + "\n\n", EXEC_NAME); + + ntfs_log_quiet("%s is about to wipe:\n", EXEC_NAME); + if (opts.unused) + ntfs_log_quiet("\tunused disk space\n"); + if (opts.tails) + ntfs_log_quiet("\tfile tails\n"); + if (opts.mft) + ntfs_log_quiet("\tunused mft areas\n"); + if (opts.directory) + ntfs_log_quiet("\tunused directory index space\n"); + if (opts.logfile) + ntfs_log_quiet("\tthe logfile (journal)\n"); + if (opts.pagefile) + ntfs_log_quiet("\tthe pagefile (swap space)\n"); + + ntfs_log_quiet("\n%s will overwrite these areas with: ", EXEC_NAME); + if (opts.bytes) { + for (i = 0; opts.bytes[i] >= 0; i++) + ntfs_log_quiet("0x%02x ", opts.bytes[i]); + } + ntfs_log_quiet("\n"); + + if (opts.count > 1) + ntfs_log_quiet("%s will repeat these operations %d times.\n", EXEC_NAME, opts.count); + ntfs_log_quiet("\n"); +} + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + ntfs_volume *vol; + int result = 1; + int flags = 0; + int i, j; + enum action act = act_info; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + if (!parse_options(argc, argv)) + return 1; + + utils_set_locale(); + + if (!opts.info) + print_summary(); + + if (opts.info || opts.noaction) + flags = MS_RDONLY; + + vol = utils_mount_volume(opts.device, flags, opts.force); + if (!vol) + goto free; + + if ((vol->flags & VOLUME_IS_DIRTY) && (!opts.force)) + goto umount; + + if (opts.info) { + act = act_info; + opts.count = 1; + } else if (opts.noaction) { + act = act_test; + } else { + act = act_wipe; + } + + /* Even if the output it quieted, you still get 5 seconds to abort. */ + if ((act == act_wipe) && !opts.force) { + ntfs_log_quiet("\n%s will begin in 5 seconds, press CTRL-C to abort.\n", EXEC_NAME); + sleep(5); + } + + ntfs_log_info("\n"); + for (i = 0; i < opts.count; i++) { + int byte; + s64 total = 0; + s64 wiped = 0; + + for (j = 0; byte = opts.bytes[j], byte >= 0; j++) { + + if (opts.directory) { + wiped = wipe_directory(vol, byte, act); + if (wiped < 0) + goto umount; + else + total += wiped; + } + + if (opts.tails) { + wiped = wipe_tails(vol, byte, act); + if (wiped < 0) + goto umount; + else + total += wiped; + } + + if (opts.logfile) { + wiped = wipe_logfile(vol, byte, act); + if (wiped < 0) + goto umount; + else + total += wiped; + } + + if (opts.mft) { + wiped = wipe_mft(vol, byte, act); + if (wiped < 0) + goto umount; + else + total += wiped; + } + + if (opts.pagefile) { + wiped = wipe_pagefile(vol, byte, act); + if (wiped < 0) + goto umount; + else + total += wiped; + } + + if (opts.unused) { + wiped = wipe_unused(vol, byte, act); + if (wiped < 0) + goto umount; + else + total += wiped; + } + + if (act == act_info) + break; + } + + ntfs_log_info("%lld bytes were wiped\n", (long long)total); + } + result = 0; +umount: + ntfs_umount(vol, FALSE); +free: + if (opts.bytes) + free(opts.bytes); + return result; +} diff --git a/ntfsprogs/ntfswipe.h b/ntfsprogs/ntfswipe.h new file mode 100644 index 00000000..1e7c937e --- /dev/null +++ b/ntfsprogs/ntfswipe.h @@ -0,0 +1,53 @@ +/* + * ntfswipe - Part of the Linux-NTFS project. + * + * Copyright (c) 2002 Richard Russon + * + * This utility will overwrite unused space on an NTFS volume. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 _NTFSWIPE_H_ +#define _NTFSWIPE_H_ + +#include + +enum action { + act_info, + act_test, + act_wipe, +}; + +struct options { + char *device; /* Device/File to work with */ + int info; /* Show volume info */ + int force; /* Override common sense */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int noaction; /* Do not write to disk */ + int count; /* Number of iterations */ + int *bytes; /* List of overwrite characters */ + int directory; /* Wipe directory indexes */ + int logfile; /* Wipe the logfile (journal) */ + int mft; /* Wipe mft slack space */ + int pagefile; /* Wipe pagefile (swap space) */ + int tails; /* Wipe file tails */ + int unused; /* Wipe unused clusters */ +}; + +#endif /* _NTFSWIPE_H_ */ + diff --git a/ntfsprogs/sd.c b/ntfsprogs/sd.c new file mode 100644 index 00000000..fee2166b --- /dev/null +++ b/ntfsprogs/sd.c @@ -0,0 +1,1027 @@ +#include +#include +#include "sd.h" + +/** + * init_system_file_sd + * + * NTFS 1.2, 3.0, 3.1 - System files security decriptors + * ===================================================== + * + * Create the security descriptor for system file number @sys_file_no and + * return a pointer to the descriptor. + * + * $MFT, $MFTMirr, $LogFile, $AttrDef, $Bitmap, $Boot, $BadClus, and $UpCase + * are the same. + * + * $Volume, $Quota, and system files 0xb-0xf are the same. They are almost the + * same as the above, the only difference being that the two SIDs present in + * the DACL grant GENERIC_WRITE and GENERIC_READ equivalent privileges while + * the above only grant GENERIC_READ equivalent privileges. (For some reason + * the flags for GENERIC_READ/GENERIC_WRITE are not set by NT4, even though + * the permissions are equivalent, so we comply. + * + * Root directory system file (".") is different altogether. + * + * The sd is returned in *@sd_val and has length *@sd_val_len. + * + * Do NOT free *@sd_val as it is static memory. This also means that you can + * only use *@sd_val until the next call to this function. + */ +void init_system_file_sd(int sys_file_no, u8 **sd_val, int *sd_val_len) +{ + static u8 sd_array[0x68]; + SECURITY_DESCRIPTOR_RELATIVE *sd; + ACL *acl; + ACCESS_ALLOWED_ACE *aa_ace; + SID *sid; + + if (sys_file_no < 0) { + *sd_val = NULL; + *sd_val_len = 0; + return; + } + *sd_val = sd_array; + sd = (SECURITY_DESCRIPTOR_RELATIVE*)&sd_array; + sd->revision = 1; + sd->alignment = 0; + sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; + if (sys_file_no == FILE_root) { + *sd_val_len = 0x50; + sd->owner = const_cpu_to_le32(0x30); + sd->group = const_cpu_to_le32(0x40); + } else { + *sd_val_len = 0x68; + sd->owner = const_cpu_to_le32(0x48); + sd->group = const_cpu_to_le32(0x58); + } + sd->sacl = const_cpu_to_le32(0); + sd->dacl = const_cpu_to_le32(0x14); + /* + * Now at offset 0x14, as specified in the security descriptor, we have + * the DACL. + */ + acl = (ACL*)((char*)sd + le32_to_cpu(sd->dacl)); + acl->revision = 2; + acl->alignment1 = 0; + if (sys_file_no == FILE_root) { + acl->size = const_cpu_to_le16(0x1c); + acl->ace_count = const_cpu_to_le16(1); + } else { + acl->size = const_cpu_to_le16(0x34); + acl->ace_count = const_cpu_to_le16(2); + } + acl->alignment2 = const_cpu_to_le16(0); + /* + * Now at offset 0x1c, just after the DACL's ACL, we have the first + * ACE of the DACL. The type of the ACE is access allowed. + */ + aa_ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); + aa_ace->type = ACCESS_ALLOWED_ACE_TYPE; + if (sys_file_no == FILE_root) + aa_ace->flags = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE; + else + aa_ace->flags = 0; + aa_ace->size = const_cpu_to_le16(0x14); + switch (sys_file_no) { + case FILE_MFT: case FILE_MFTMirr: case FILE_LogFile: + case FILE_AttrDef: case FILE_Bitmap: case FILE_Boot: + case FILE_BadClus: case FILE_UpCase: + aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | + FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_READ_DATA; + break; + case FILE_Volume: case FILE_Secure: case 0xb ... 0xffff: + aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_WRITE | + FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | + FILE_WRITE_EA | FILE_READ_EA | FILE_APPEND_DATA | + FILE_WRITE_DATA | FILE_READ_DATA; + break; + case FILE_root: + aa_ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | + FILE_READ_ATTRIBUTES | FILE_DELETE_CHILD | + FILE_TRAVERSE | FILE_WRITE_EA | FILE_READ_EA | + FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | + FILE_LIST_DIRECTORY; + break; + } + aa_ace->sid.revision = 1; + aa_ace->sid.sub_authority_count = 1; + aa_ace->sid.identifier_authority.value[0] = 0; + aa_ace->sid.identifier_authority.value[1] = 0; + aa_ace->sid.identifier_authority.value[2] = 0; + aa_ace->sid.identifier_authority.value[3] = 0; + aa_ace->sid.identifier_authority.value[4] = 0; + if (sys_file_no == FILE_root) { + /* SECURITY_WORLD_SID_AUTHORITY (S-1-1) */ + aa_ace->sid.identifier_authority.value[5] = 1; + aa_ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_WORLD_RID); + /* This is S-1-1-0, the WORLD_SID. */ + } else { + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + aa_ace->sid.identifier_authority.value[5] = 5; + aa_ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + } + /* + * Now at offset 0x30 within security descriptor, just after the first + * ACE of the DACL. All system files, except the root directory, have + * a second ACE. + */ + if (sys_file_no != FILE_root) { + /* The second ACE of the DACL. Type is access allowed. */ + aa_ace = (ACCESS_ALLOWED_ACE*)((char*)aa_ace + + le16_to_cpu(aa_ace->size)); + aa_ace->type = ACCESS_ALLOWED_ACE_TYPE; + aa_ace->flags = 0; + aa_ace->size = const_cpu_to_le16(0x18); + switch (sys_file_no) { + case FILE_MFT: case FILE_MFTMirr: + case FILE_LogFile: case FILE_AttrDef: + case FILE_Bitmap: case FILE_Boot: + case FILE_BadClus: case FILE_UpCase: + aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | + FILE_READ_ATTRIBUTES | FILE_READ_EA | + FILE_READ_DATA; + break; + case FILE_Volume: case FILE_Secure: + case 0xb ... 0xffff : + aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | + FILE_WRITE_ATTRIBUTES | + FILE_READ_ATTRIBUTES | FILE_WRITE_EA | + FILE_READ_EA | FILE_APPEND_DATA | + FILE_WRITE_DATA | FILE_READ_DATA; + break; + } + aa_ace->sid.revision = 1; + aa_ace->sid.sub_authority_count = 2; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + aa_ace->sid.identifier_authority.value[0] = 0; + aa_ace->sid.identifier_authority.value[1] = 0; + aa_ace->sid.identifier_authority.value[2] = 0; + aa_ace->sid.identifier_authority.value[3] = 0; + aa_ace->sid.identifier_authority.value[4] = 0; + aa_ace->sid.identifier_authority.value[5] = 5; + aa_ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + aa_ace->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + /* Now at offset 0x48 into the security descriptor. */ + } + /* As specified in the security descriptor, we now have the owner SID.*/ + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 1; + sid->sub_authority_count = 2; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + /* + * Now at offset 0x40 or 0x58 (root directory and the other system + * files, respectively) into the security descriptor, as specified in + * the security descriptor, we have the group SID. + */ + sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); + sid->revision = 1; + sid->sub_authority_count = 2; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); +} + +/** + * init_root_sd_31 (ERSO) + * creates the security_descriptor for the root folder on ntfs 3.1. + * It is very long; lots of ACE's at first, then large pieces of zeroes; + * the owner user/group is near the end. On a partition created with + * w2k3 the owner user/group at the end is surrounded by 'garbage', which I + * yet do not understand. Here I have replaced the 'garbage' with + * zeros, which seems to work. Chkdsk does not add the 'garbage', nor alter + * this security descriptor in any way. + */ +void init_root_sd_31(u8 **sd_val, int *sd_val_len) +{ + SECURITY_DESCRIPTOR_RELATIVE *sd; + ACL *acl; + ACCESS_ALLOWED_ACE *ace; + SID *sid; + + static char sd_array[0x1030]; + *sd_val_len = 0x1030; + *sd_val = (u8*)&sd_array; + + //security descriptor relative + sd = (SECURITY_DESCRIPTOR_RELATIVE*)sd_array; + sd->revision = 0x01; + sd->alignment = 0x00; + sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; + sd->owner = const_cpu_to_le32(0x1014); + sd->group = const_cpu_to_le32(0x1024); + sd->sacl = const_cpu_to_le32(0x00); + sd->dacl = const_cpu_to_le32(0x14); + + //acl + acl = (ACL*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + acl->revision = 0x02; + acl->alignment1 = 0x00; + acl->size = const_cpu_to_le16(0x1000); + acl->ace_count = const_cpu_to_le16(0x07); + acl->alignment2 = const_cpu_to_le16(0x00); + + //ace1 + ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL)); + ace->type = 0x00; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + ace->size = const_cpu_to_le16(0x18); + ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | + FILE_LIST_DIRECTORY | FILE_WRITE_DATA | + FILE_ADD_SUBDIRECTORY | FILE_READ_EA | FILE_WRITE_EA | + FILE_TRAVERSE | FILE_DELETE_CHILD | + FILE_READ_ATTRIBUTES; + + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + ace->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //ace2 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = 0x00; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + ace->size = const_cpu_to_le16(0x14); + ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | + FILE_LIST_DIRECTORY | FILE_WRITE_DATA | + FILE_ADD_SUBDIRECTORY | FILE_READ_EA | FILE_WRITE_EA | + FILE_TRAVERSE | FILE_DELETE_CHILD | + FILE_READ_ATTRIBUTES; + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + + //ace3 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = 0x00; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | + INHERIT_ONLY_ACE; + ace->size = const_cpu_to_le16(0x14); + ace->mask = const_cpu_to_le32(0x10000000); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_CREATOR_SID_AUTHORITY (S-1-3) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 3; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_CREATOR_OWNER_RID); + + //ace4 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = 0x00; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + ace->size = const_cpu_to_le16(0x18); + ace->mask = const_cpu_to_le32(0x1200A9); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + ace->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_USERS); + + //ace5 + ace = (ACCESS_ALLOWED_ACE*)((char*)ace + le16_to_cpu(ace->size)); + ace->type = 0x00; + ace->flags = CONTAINER_INHERIT_ACE; + ace->size = const_cpu_to_le16(0x18); + ace->mask = const_cpu_to_le32(0x04); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + ace->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_USERS); + + //ace6 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = 0x00; + ace->flags = CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE; + ace->size = const_cpu_to_le16(0x18); + ace->mask = const_cpu_to_le32(0x02); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + ace->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_USERS); + + //ace7 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x14); + ace->mask = const_cpu_to_le32(0x1200A9); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_WORLD_SID_AUTHORITY (S-1-1) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 1; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_WORLD_RID); + + //owner sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //group sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); + sid->revision = 0x01; + sid->sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); +} + +/** + * init_secure + * + * NTFS 3.0 - System files security decriptors + * =========================================== + * Create the security descriptor entries in $SDS data stream like they + * are in a partition, newly formatted with windows 2000 + */ +void init_secure_30(char *sd_val) +{ + SECURITY_DESCRIPTOR_HEADER *sds; + SECURITY_DESCRIPTOR_RELATIVE *sd; + ACL *acl; + ACCESS_ALLOWED_ACE *ace; + SID *sid; + +/* + * security descriptor #1 + */ + //header + sds = (SECURITY_DESCRIPTOR_HEADER*)((char*)sd_val); + sds->hash = const_cpu_to_le32(0xF80312F0); + sds->security_id = const_cpu_to_le32(0x0100); + sds->offset = const_cpu_to_le64(0x00); + sds->length = const_cpu_to_le32(0x7C); + //security descriptor relative + sd = (SECURITY_DESCRIPTOR_RELATIVE*)((char*)sds + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + sd->revision = 0x01; + sd->alignment = 0x00; + sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; + sd->owner = const_cpu_to_le32(0x48); + sd->group = const_cpu_to_le32(0x58); + sd->sacl = const_cpu_to_le32(0x00); + sd->dacl = const_cpu_to_le32(0x14); + + //acl + acl = (ACL*)((char*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + acl->revision = 0x02; + acl->alignment1 = 0x00; + acl->size = const_cpu_to_le16(0x34); + acl->ace_count = const_cpu_to_le16(0x02); + acl->alignment2 = 0x00; + + //ace1 + ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x14); + ace->mask = const_cpu_to_le32(0x120089); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + + //ace2 + ace = (ACCESS_ALLOWED_ACE*)((char*)ace + le16_to_cpu(ace->size)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x18); + ace->mask = const_cpu_to_le32(0x120089); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + ace->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //owner sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //group sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + +/* + * security descriptor #2 + */ + //header + sds = (SECURITY_DESCRIPTOR_HEADER*)((char*)sd_val + 0x80); + sds->hash = const_cpu_to_le32(0xB32451); + sds->security_id = const_cpu_to_le32(0x0101); + sds->offset = const_cpu_to_le64(0x80); + sds->length = const_cpu_to_le32(0x7C); + + //security descriptor relative + sd = (SECURITY_DESCRIPTOR_RELATIVE*)((char*)sds + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + sd->revision = 0x01; + sd->alignment = 0x00; + sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; + sd->owner = const_cpu_to_le32(0x48); + sd->group = const_cpu_to_le32(0x58); + sd->sacl = const_cpu_to_le32(0x00); + sd->dacl = const_cpu_to_le32(0x14); + + //acl + acl = (ACL*)((char*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + acl->revision = 0x02; + acl->alignment1 = 0x00; + acl->size = const_cpu_to_le16(0x34); + acl->ace_count = const_cpu_to_le16(0x02); + acl->alignment2 = 0x00; + + //ace1 + ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x14); + ace->mask = const_cpu_to_le32(0x12019F); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + + //ace2 + ace = (ACCESS_ALLOWED_ACE*)((char*)ace + le16_to_cpu(ace->size)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x18); + ace->mask = const_cpu_to_le32(0x12019F); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + ace->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //owner sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //group sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + +/* + * security descriptor #3 + */ + //header + sds = (SECURITY_DESCRIPTOR_HEADER*)((char*)sd_val + 0x80 + 0x80); + sds->hash = const_cpu_to_le32(0x0A9F9562); + sds->security_id = const_cpu_to_le32(0x0102); + sds->offset = const_cpu_to_le64(0x0100); + sds->length = const_cpu_to_le32(0x60); + + //security descriptor relative + sd = (SECURITY_DESCRIPTOR_RELATIVE*)((char*)sds + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + sd->revision = 0x01; + sd->alignment = 0x00; + sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; + sd->owner = const_cpu_to_le32(0x30); + sd->group = const_cpu_to_le32(0x40); + sd->sacl = const_cpu_to_le32(0x00); + sd->dacl = const_cpu_to_le32(0x14); + + //acl + acl = (ACL*)((char*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + acl->revision = 0x02; + acl->alignment1 = 0x00; + acl->size = const_cpu_to_le16(0x1C); + acl->ace_count = const_cpu_to_le16(0x01); + acl->alignment2 = 0x00; + + //ace1 + ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x14); + ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | + FILE_LIST_DIRECTORY | FILE_WRITE_DATA | + FILE_ADD_SUBDIRECTORY | FILE_READ_EA | FILE_WRITE_EA | + FILE_TRAVERSE | FILE_DELETE_CHILD | + FILE_READ_ATTRIBUTES; + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x01; + // SECURITY_NT_SID_AUTHORITY (S-1-5) + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + + //owner sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + // SECURITY_NT_SID_AUTHORITY (S-1-5) + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + //group sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); + sid->revision = 0x01; + sid->sub_authority_count = 0x01; + // SECURITY_NT_SID_AUTHORITY (S-1-5) + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + +/* + * security descriptor #4 + */ + //header + sds = (SECURITY_DESCRIPTOR_HEADER*)((char*)sd_val + 0x80 + 0x80 + 0x60); + sds->hash = const_cpu_to_le32(0x453F0A2E); + sds->security_id = const_cpu_to_le32(0x0103); + sds->offset = const_cpu_to_le64(0x0160); + sds->length = const_cpu_to_le32(0x78); + + //security descriptor relative + sd = (SECURITY_DESCRIPTOR_RELATIVE*)((char*)sds + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + sd->revision = 0x01; + sd->alignment = 0x00; + sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; + sd->owner = const_cpu_to_le32(0x48); + sd->group = const_cpu_to_le32(0x58); + sd->sacl = const_cpu_to_le32(0x00); + sd->dacl = const_cpu_to_le32(0x14); + + //acl + acl = (ACL*)((char*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + acl->revision = 0x02; + acl->alignment1 = 0x00; + acl->size = const_cpu_to_le16(0x34); + acl->ace_count = const_cpu_to_le16(0x02); + acl->alignment2 = 0x00; + + //ace1 + ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x18); + ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | + FILE_LIST_DIRECTORY | FILE_WRITE_DATA | + FILE_ADD_SUBDIRECTORY | FILE_READ_EA | FILE_WRITE_EA | + FILE_TRAVERSE | FILE_DELETE_CHILD | + FILE_READ_ATTRIBUTES; + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x02; + // SECURITY_NT_SID_AUTHORITY (S-1-5) + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + ace->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + //ace2 + ace = (ACCESS_ALLOWED_ACE*)((char*)ace + le16_to_cpu(ace->size)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x14); + ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | + FILE_LIST_DIRECTORY | FILE_WRITE_DATA | + FILE_ADD_SUBDIRECTORY | FILE_READ_EA | FILE_WRITE_EA | + FILE_TRAVERSE | FILE_DELETE_CHILD | + FILE_READ_ATTRIBUTES; + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + + //owner sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + // SECURITY_NT_SID_AUTHORITY (S-1-5) + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //group sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); + sid->revision = 0x01; + sid->sub_authority_count = 0x01; + // SECURITY_NT_SID_AUTHORITY (S-1-5) + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + + return; +} + +/** + * init_secure_31(char **r, int size); + * + * NTFS 3.1 - System files security decriptors + * =========================================== + * Create the security descriptor entries in $SDS data stream like they + * are in a partition, newly formatted with windows 2003 + */ +void init_secure_31(char *sd_val) +{ + SECURITY_DESCRIPTOR_HEADER *sds; + SECURITY_DESCRIPTOR_RELATIVE *sd; + ACL *acl; + ACCESS_ALLOWED_ACE *ace; + SID *sid; + +/* + * security descriptor #1 + */ + //header + sds = (SECURITY_DESCRIPTOR_HEADER*)((char*)sd_val); + sds->hash = const_cpu_to_le32(0xF80312F0); + sds->security_id = const_cpu_to_le32(0x0100); + sds->offset = const_cpu_to_le64(0x00); + sds->length = const_cpu_to_le32(0x7C); + //security descriptor relative + sd = (SECURITY_DESCRIPTOR_RELATIVE*)((char*)sds + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + sd->revision = 0x01; + sd->alignment = 0x00; + sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; + sd->owner = const_cpu_to_le32(0x48); + sd->group = const_cpu_to_le32(0x58); + sd->sacl = const_cpu_to_le32(0x00); + sd->dacl = const_cpu_to_le32(0x14); + + //acl + acl = (ACL*)((char*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + acl->revision = 0x02; + acl->alignment1 = 0x00; + acl->size = const_cpu_to_le16(0x34); + acl->ace_count = const_cpu_to_le16(0x02); + acl->alignment2 = 0x00; + + //ace1 + ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x14); + ace->mask = const_cpu_to_le32(0x120089); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + //ace2 + ace = (ACCESS_ALLOWED_ACE*)((char*)ace + le16_to_cpu(ace->size)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x18); + ace->mask = const_cpu_to_le32(0x120089); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + ace->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //owner sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + //group sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); +/* + * security descriptor #2 + */ + //header + sds = (SECURITY_DESCRIPTOR_HEADER*)((char*)sd_val + 0x80); + sds->hash = const_cpu_to_le32(0xB32451); + sds->security_id = const_cpu_to_le32(0x0101); + sds->offset = const_cpu_to_le64(0x80); + sds->length = const_cpu_to_le32(0x7C); + + //security descriptor relative + sd = (SECURITY_DESCRIPTOR_RELATIVE*)((char*)sds + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + sd->revision = 0x01; + sd->alignment = 0x00; + sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; + sd->owner = const_cpu_to_le32(0x48); + sd->group = const_cpu_to_le32(0x58); + sd->sacl = const_cpu_to_le32(0x00); + sd->dacl = const_cpu_to_le32(0x14); + + //acl + acl = (ACL*)((char*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + acl->revision = 0x02; + acl->alignment1 = 0x00; + acl->size = const_cpu_to_le16(0x34); + acl->ace_count = const_cpu_to_le16(0x02); + acl->alignment2 = 0x00; + + //ace1 + ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x14); + ace->mask = const_cpu_to_le32(0x12019F); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + //ace2 + ace = (ACCESS_ALLOWED_ACE*)((char*)ace + le16_to_cpu(ace->size)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x18); + ace->mask = const_cpu_to_le32(0x12019F); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + ace->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //owner sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //group sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + return; +} diff --git a/ntfsprogs/sd.h b/ntfsprogs/sd.h new file mode 100644 index 00000000..d6443ae9 --- /dev/null +++ b/ntfsprogs/sd.h @@ -0,0 +1,12 @@ +#ifndef _NTFS_SD_H_ +#define _NTFS_SD_H_ + +#include + +void init_system_file_sd(int sys_file_no, u8 **sd_val, int *sd_val_len); +void init_root_sd_31(u8 **sd_val, int *sd_val_len); +void init_secure_30(char *sd_val); +void init_secure_31(char *sd_val); + +#endif /* _NTFS_SD_H_ */ + diff --git a/ntfsprogs/upcase.c b/ntfsprogs/upcase.c new file mode 100644 index 00000000..f83d27e6 --- /dev/null +++ b/ntfsprogs/upcase.c @@ -0,0 +1,90 @@ +/** + * upcase - Part of the Linux-NTFS project. + * + * Copyright (c) 2001 Richard Russon + * Copyright (c) 2001-2006 Anton Altaparmakov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS source + * in the file COPYING); if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_STRING_H +#include +#endif + +#include +#include +#include "upcase.h" + +/** + * init_upcase_table + */ +void init_upcase_table(ntfschar *uc, u32 uc_len) +{ + static int uc_run_table[][3] = { /* Start, End, Add */ + {0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74}, + {0x00E0, 0x00F7, -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76, 86}, + {0x00F8, 0x00FF, -32}, {0x0561, 0x0587, -48}, {0x1F76, 0x1F78, 100}, + {0x0256, 0x0258, -205}, {0x1F00, 0x1F08, 8}, {0x1F78, 0x1F7A, 128}, + {0x028A, 0x028C, -217}, {0x1F10, 0x1F16, 8}, {0x1F7A, 0x1F7C, 112}, + {0x03AC, 0x03AD, -38}, {0x1F20, 0x1F28, 8}, {0x1F7C, 0x1F7E, 126}, + {0x03AD, 0x03B0, -37}, {0x1F30, 0x1F38, 8}, {0x1FB0, 0x1FB2, 8}, + {0x03B1, 0x03C2, -32}, {0x1F40, 0x1F46, 8}, {0x1FD0, 0x1FD2, 8}, + {0x03C2, 0x03C3, -31}, {0x1F51, 0x1F52, 8}, {0x1FE0, 0x1FE2, 8}, + {0x03C3, 0x03CC, -32}, {0x1F53, 0x1F54, 8}, {0x1FE5, 0x1FE6, 7}, + {0x03CC, 0x03CD, -64}, {0x1F55, 0x1F56, 8}, {0x2170, 0x2180, -16}, + {0x03CD, 0x03CF, -63}, {0x1F57, 0x1F58, 8}, {0x24D0, 0x24EA, -26}, + {0x0430, 0x0450, -32}, {0x1F60, 0x1F68, 8}, {0xFF41, 0xFF5B, -32}, + {0} + }; + static int uc_dup_table[][2] = { /* Start, End */ + {0x0100, 0x012F}, {0x01A0, 0x01A6}, {0x03E2, 0x03EF}, {0x04CB, 0x04CC}, + {0x0132, 0x0137}, {0x01B3, 0x01B7}, {0x0460, 0x0481}, {0x04D0, 0x04EB}, + {0x0139, 0x0149}, {0x01CD, 0x01DD}, {0x0490, 0x04BF}, {0x04EE, 0x04F5}, + {0x014A, 0x0178}, {0x01DE, 0x01EF}, {0x04BF, 0x04BF}, {0x04F8, 0x04F9}, + {0x0179, 0x017E}, {0x01F4, 0x01F5}, {0x04C1, 0x04C4}, {0x1E00, 0x1E95}, + {0x018B, 0x018B}, {0x01FA, 0x0218}, {0x04C7, 0x04C8}, {0x1EA0, 0x1EF9}, + {0} + }; + static int uc_byte_table[][2] = { /* Offset, Value */ + {0x00FF, 0x0178}, {0x01AD, 0x01AC}, {0x01F3, 0x01F1}, {0x0269, 0x0196}, + {0x0183, 0x0182}, {0x01B0, 0x01AF}, {0x0253, 0x0181}, {0x026F, 0x019C}, + {0x0185, 0x0184}, {0x01B9, 0x01B8}, {0x0254, 0x0186}, {0x0272, 0x019D}, + {0x0188, 0x0187}, {0x01BD, 0x01BC}, {0x0259, 0x018F}, {0x0275, 0x019F}, + {0x018C, 0x018B}, {0x01C6, 0x01C4}, {0x025B, 0x0190}, {0x0283, 0x01A9}, + {0x0192, 0x0191}, {0x01C9, 0x01C7}, {0x0260, 0x0193}, {0x0288, 0x01AE}, + {0x0199, 0x0198}, {0x01CC, 0x01CA}, {0x0263, 0x0194}, {0x0292, 0x01B7}, + {0x01A8, 0x01A7}, {0x01DD, 0x018E}, {0x0268, 0x0197}, + {0} + }; + int i, r; + + memset(uc, 0, uc_len); + uc_len >>= 1; + /* Generate the little endian Unicode upcase table used by ntfs. */ + for (i = 0; (u32)i < uc_len; i++) + uc[i] = cpu_to_le16(i); + for (r = 0; uc_run_table[r][0]; r++) + for (i = uc_run_table[r][0]; i < uc_run_table[r][1]; i++) + uc[i] = cpu_to_le16(le16_to_cpu(uc[i]) + + uc_run_table[r][2]); + for (r = 0; uc_dup_table[r][0]; r++) + for (i = uc_dup_table[r][0]; i < uc_dup_table[r][1]; i += 2) + uc[i + 1] = cpu_to_le16(le16_to_cpu(uc[i + 1]) - 1); + for (r = 0; uc_byte_table[r][0]; r++) + uc[uc_byte_table[r][0]] = cpu_to_le16(uc_byte_table[r][1]); +} diff --git a/ntfsprogs/upcase.h b/ntfsprogs/upcase.h new file mode 100644 index 00000000..9ec567dc --- /dev/null +++ b/ntfsprogs/upcase.h @@ -0,0 +1,7 @@ +#ifndef _NTFS_UPCASE_H_ +#define _NTFS_UPCASE_H_ + +void init_upcase_table(ntfschar *uc, u32 uc_len); + +#endif /* _NTFS_UPCASE_H_ */ + diff --git a/ntfsprogs/utils.c b/ntfsprogs/utils.c new file mode 100644 index 00000000..ee7cd9d7 --- /dev/null +++ b/ntfsprogs/utils.c @@ -0,0 +1,1016 @@ +/** + * utils.c - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2003-2006 Anton Altaparmakov + * Copyright (c) 2003 Lode Leroy + * + * A set of shared functions for ntfs utilities + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDARG_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_LOCALE_H +#include +#endif +#ifdef HAVE_LIBINTL_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_CTYPE_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "utils.h" +/* #include "version.h" */ + +const char *ntfs_bugs = "Developers' email address: "NTFS_DEV_LIST"\n"; +const char *ntfs_home = "Linux NTFS homepage: http://www.linux-ntfs.org\n"; +const char *ntfs_gpl = "This program is free software, released under the GNU " + "General Public License\nand you are welcome to redistribute it under " + "certain conditions. It comes with\nABSOLUTELY NO WARRANTY; for " + "details read the GNU General Public License to be\nfound in the file " + "\"COPYING\" distributed with this program, or online at:\n" + "http://www.gnu.org/copyleft/gpl.html\n"; + +/** + * utils_set_locale + */ +int utils_set_locale(void) +{ + const char *locale; + + locale = setlocale(LC_ALL, ""); + if (!locale) { + locale = setlocale(LC_ALL, NULL); + ntfs_log_error("Failed to set locale, using default '%s'.\n", locale); + return 1; + } else { + return 0; + } +} + +#ifndef NTFS_RICH +/** + * utils_valid_device - Perform some safety checks on the device, before we start + * @name: Full pathname of the device/file to work with + * @force: Continue regardless of problems + * + * Check that the name refers to a device and that is isn't already mounted. + * These checks can be overridden by using the force option. + * + * Return: 1 Success, we can continue + * 0 Error, we cannot use this device + */ +int utils_valid_device(const char *name, int force) +{ + unsigned long mnt_flags = 0; + struct stat st; + +#ifdef __CYGWIN32__ + /* FIXME: This doesn't work for Cygwin, so just return success for now... */ + return 1; +#endif + if (!name) { + errno = EINVAL; + return 0; + } + + if (stat(name, &st) == -1) { + if (errno == ENOENT) { + ntfs_log_error("The device %s doesn't exist\n", name); + } else { + ntfs_log_perror("Error getting information about %s", name); + } + return 0; + } + + if (!S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode)) { + ntfs_log_warning("%s is not a block device, " + "nor regular file.\n", name); + if (!force) { + ntfs_log_error("Use the force option to work with other" + " file types, for your own risk!\n"); + return 0; + } + ntfs_log_warning("Forced to continue.\n"); + } + + /* Make sure the file system is not mounted. */ + if (ntfs_check_if_mounted(name, &mnt_flags)) { + ntfs_log_perror("Failed to determine whether %s is mounted", name); + if (!force) { + ntfs_log_error("Use the force option to ignore this error.\n"); + return 0; + } + ntfs_log_warning("Forced to continue.\n"); + } else if (mnt_flags & NTFS_MF_MOUNTED) { + ntfs_log_warning("The device %s, is mounted.\n", name); + if (!force) { + ntfs_log_error("Use the force option to work a mounted filesystem.\n"); + return 0; + } + ntfs_log_warning("Forced to continue.\n"); + } + + return 1; +} + +/** + * utils_mount_volume - Mount an NTFS volume + */ +ntfs_volume * utils_mount_volume(const char *device, unsigned long flags, BOOL force) +{ + ntfs_volume *vol; + + if (!device) { + errno = EINVAL; + return NULL; + } + + if (!utils_valid_device(device, force)) + return NULL; + + vol = ntfs_mount(device, flags); + if (!vol) { + int err; + + err = errno; + ntfs_log_perror("Couldn't mount device '%s'", device); + if (err == EPERM) + ntfs_log_error("Windows was hibernated. Try to mount " + "volume in windows, shut down and try " + "again.\n"); + if (err == EOPNOTSUPP) + ntfs_log_error("Windows did not shut down properly. " + "Try to mount volume in windows, " + "shut down and try again.\n"); + return NULL; + } + + if (vol->flags & VOLUME_IS_DIRTY) { + ntfs_log_warning("Volume is dirty.\n"); + if (!force) { + ntfs_log_error("Run chkdsk and try again, or use the " + "force option.\n"); + ntfs_umount(vol, FALSE); + return NULL; + } + ntfs_log_warning("Forced to continue.\n"); + } + + return vol; +} + +#endif + +/** + * utils_parse_size - Convert a string representing a size + * @value: String to be parsed + * @size: Parsed size + * @scale: Whether or not to allow a suffix to scale the value + * + * Read a string and convert it to a number. Strings may be suffixed to scale + * them. Any number without a suffix is assumed to be in bytes. + * + * Suffix Description Multiple + * [tT] Terabytes 10^12 + * [gG] Gigabytes 10^9 + * [mM] Megabytes 10^6 + * [kK] Kilobytes 10^3 + * + * Notes: + * Only the first character of the suffix is read. + * The multipliers are decimal thousands, not binary: 1000, not 1024. + * If parse_size fails, @size will not be changed + * + * Return: 1 Success + * 0 Error, the string was malformed + */ +int utils_parse_size(const char *value, s64 *size, BOOL scale) +{ + long long result; + char *suffix = NULL; + + if (!value || !size) { + errno = EINVAL; + return 0; + } + + ntfs_log_debug("Parsing size '%s'.\n", value); + + result = strtoll(value, &suffix, 0); + if (result < 0 || errno == ERANGE) { + ntfs_log_error("Invalid size '%s'.\n", value); + return 0; + } + + if (!suffix) { + ntfs_log_error("Internal error, strtoll didn't return a suffix.\n"); + return 0; + } + + if (scale) { + switch (suffix[0]) { + case 't': case 'T': result *= 1000; + case 'g': case 'G': result *= 1000; + case 'm': case 'M': result *= 1000; + case 'k': case 'K': result *= 1000; + case '-': case 0: + break; + default: + ntfs_log_error("Invalid size suffix '%s'. Use T, G, M, or K.\n", suffix); + return 0; + } + } else { + if ((suffix[0] != '-') && (suffix[0] != 0)) { + ntfs_log_error("Invalid number '%.*s'.\n", (int)(suffix - value + 1), value); + return 0; + } + } + + ntfs_log_debug("Parsed size = %lld.\n", result); + *size = result; + return 1; +} + +/** + * utils_parse_range - Convert a string representing a range of numbers + * @string: The string to be parsed + * @start: The beginning of the range will be stored here + * @finish: The end of the range will be stored here + * + * Read a string of the form n-m. If the lower end is missing, zero will be + * substituted. If the upper end is missing LONG_MAX will be used. If the + * string cannot be parsed correctly, @start and @finish will not be changed. + * + * Return: 1 Success, a valid string was found + * 0 Error, the string was not a valid range + */ +int utils_parse_range(const char *string, s64 *start, s64 *finish, BOOL scale) +{ + s64 a, b; + char *middle; + + if (!string || !start || !finish) { + errno = EINVAL; + return 0; + } + + middle = strchr(string, '-'); + if (string == middle) { + ntfs_log_debug("Range has no beginning, defaulting to 0.\n"); + a = 0; + } else { + if (!utils_parse_size(string, &a, scale)) + return 0; + } + + if (middle) { + if (middle[1] == 0) { + b = LONG_MAX; // XXX ULLONG_MAX + ntfs_log_debug("Range has no end, defaulting to %lld.\n", b); + } else { + if (!utils_parse_size(middle+1, &b, scale)) + return 0; + } + } else { + b = a; + } + + ntfs_log_debug("Range '%s' = %lld - %lld\n", string, a, b); + + *start = a; + *finish = b; + return 1; +} + +#ifndef NTFS_RICH +/** + * find_attribute - Find an attribute of the given type + * @type: An attribute type, e.g. AT_FILE_NAME + * @ctx: A search context, created using ntfs_get_attr_search_ctx + * + * Using the search context to keep track, find the first/next occurrence of a + * given attribute type. + * + * N.B. This will return a pointer into @mft. As long as the search context + * has been created without an inode, it won't overflow the buffer. + * + * Return: Pointer Success, an attribute was found + * NULL Error, no matching attributes were found + */ +ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx) +{ + if (!ctx) { + errno = EINVAL; + return NULL; + } + + if (ntfs_attr_lookup(type, NULL, 0, 0, 0, NULL, 0, ctx) != 0) { + ntfs_log_debug("find_attribute didn't find an attribute of type: 0x%02x.\n", type); + return NULL; /* None / no more of that type */ + } + + ntfs_log_debug("find_attribute found an attribute of type: 0x%02x.\n", type); + return ctx->attr; +} + +/** + * find_first_attribute - Find the first attribute of a given type + * @type: An attribute type, e.g. AT_FILE_NAME + * @mft: A buffer containing a raw MFT record + * + * Search through a raw MFT record for an attribute of a given type. + * The return value is a pointer into the MFT record that was supplied. + * + * N.B. This will return a pointer into @mft. The pointer won't stray outside + * the buffer, since we created the search context without an inode. + * + * Return: Pointer Success, an attribute was found + * NULL Error, no matching attributes were found + */ +ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *rec; + + if (!mft) { + errno = EINVAL; + return NULL; + } + + ctx = ntfs_attr_get_search_ctx(NULL, mft); + if (!ctx) { + ntfs_log_error("Couldn't create a search context.\n"); + return NULL; + } + + rec = find_attribute(type, ctx); + ntfs_attr_put_search_ctx(ctx); + if (rec) + ntfs_log_debug("find_first_attribute: found attr of type 0x%02x.\n", type); + else + ntfs_log_debug("find_first_attribute: didn't find attr of type 0x%02x.\n", type); + return rec; +} + +#endif +/** + * utils_inode_get_name + * + * using inode + * get filename + * add name to list + * get parent + * if parent is 5 (/) stop + * get inode of parent + */ +int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize) +{ + // XXX option: names = posix/win32 or dos + // flags: path, filename, or both + const int max_path = 20; + + ntfs_volume *vol; + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *rec; + FILE_NAME_ATTR *attr; + int name_space; + MFT_REF parent = FILE_root; + char *names[max_path + 1];// XXX malloc? and make max bigger? + int i, len, offset = 0; + + if (!inode || !buffer) { + errno = EINVAL; + return 0; + } + + vol = inode->vol; + + //ntfs_log_debug("sizeof(char*) = %d, sizeof(names) = %d\n", sizeof(char*), sizeof(names)); + memset(names, 0, sizeof(names)); + + for (i = 0; i < max_path; i++) { + + ctx = ntfs_attr_get_search_ctx(inode, NULL); + if (!ctx) { + ntfs_log_error("Couldn't create a search context.\n"); + return 0; + } + + //ntfs_log_debug("i = %d, inode = %p (%lld)\n", i, inode, inode->mft_no); + + name_space = 4; + while ((rec = find_attribute(AT_FILE_NAME, ctx))) { + /* We know this will always be resident. */ + attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->value_offset)); + + if (attr->file_name_type > name_space) { //XXX find the ... + continue; + } + + name_space = attr->file_name_type; + parent = le64_to_cpu(attr->parent_directory); + + if (names[i]) { + free(names[i]); + names[i] = NULL; + } + + if (ntfs_ucstombs(attr->file_name, attr->file_name_length, + &names[i], 0) < 0) { + char *temp; + ntfs_log_error("Couldn't translate filename to current locale.\n"); + temp = malloc(30); + if (!temp) + return 0; + snprintf(temp, 30, "", (unsigned + long long)inode->mft_no); + names[i] = temp; + } + + //ntfs_log_debug("names[%d] %s\n", i, names[i]); + //ntfs_log_debug("parent = %lld\n", MREF(parent)); + } + + ntfs_attr_put_search_ctx(ctx); + + if (i > 0) /* Don't close the original inode */ + ntfs_inode_close(inode); + + if (MREF(parent) == FILE_root) { /* The root directory, stop. */ + //ntfs_log_debug("inode 5\n"); + break; + } + + inode = ntfs_inode_open(vol, parent); + if (!inode) { + ntfs_log_error("Couldn't open inode %llu.\n", + (unsigned long long)MREF(parent)); + break; + } + } + + if (i >= max_path) { + /* If we get into an infinite loop, we'll end up here. */ + ntfs_log_error("The directory structure is too deep (over %d) nested directories.\n", max_path); + return 0; + } + + /* Assemble the names in the correct order. */ + for (i = max_path; i >= 0; i--) { + if (!names[i]) + continue; + + len = snprintf(buffer + offset, bufsize - offset, "%c%s", PATH_SEP, names[i]); + if (len >= (bufsize - offset)) { + ntfs_log_error("Pathname was truncated.\n"); + break; + } + + offset += len; + } + + /* Free all the allocated memory */ + for (i = 0; i < max_path; i++) + free(names[i]); + + ntfs_log_debug("Pathname: %s\n", buffer); + + return 1; +} + +/** + * utils_attr_get_name + */ +int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int bufsize) +{ + int len, namelen; + char *name; + ATTR_DEF *attrdef; + + // flags: attr, name, or both + if (!attr || !buffer) { + errno = EINVAL; + return 0; + } + + attrdef = ntfs_attr_find_in_attrdef(vol, attr->type); + if (attrdef) { + name = NULL; + namelen = ntfs_ucsnlen(attrdef->name, sizeof(attrdef->name)); + if (ntfs_ucstombs(attrdef->name, namelen, &name, 0) < 0) { + ntfs_log_error("Couldn't translate attribute type to current locale.\n"); + // ? + return 0; + } + len = snprintf(buffer, bufsize, "%s", name); + } else { + ntfs_log_error("Unknown attribute type 0x%02x\n", attr->type); + len = snprintf(buffer, bufsize, ""); + } + + if (len >= bufsize) { + ntfs_log_error("Attribute type was truncated.\n"); + return 0; + } + + if (!attr->name_length) { + return 0; + } + + buffer += len; + bufsize -= len; + + name = NULL; + namelen = attr->name_length; + if (ntfs_ucstombs((ntfschar *)((char *)attr + attr->name_offset), + namelen, &name, 0) < 0) { + ntfs_log_error("Couldn't translate attribute name to current locale.\n"); + // ? + len = snprintf(buffer, bufsize, ""); + return 0; + } + + len = snprintf(buffer, bufsize, "(%s)", name); + free(name); + + if (len >= bufsize) { + ntfs_log_error("Attribute name was truncated.\n"); + return 0; + } + + return 0; +} + +/** + * utils_cluster_in_use - Determine if a cluster is in use + * @vol: An ntfs volume obtained from ntfs_mount + * @lcn: The Logical Cluster Number to test + * + * The metadata file $Bitmap has one binary bit representing each cluster on + * disk. The bit will be set for each cluster that is in use. The function + * reads the relevant part of $Bitmap into a buffer and tests the bit. + * + * This function has a static buffer in which it caches a section of $Bitmap. + * If the lcn, being tested, lies outside the range, the buffer will be + * refreshed. + * + * Return: 1 Cluster is in use + * 0 Cluster is free space + * -1 Error occurred + */ +int utils_cluster_in_use(ntfs_volume *vol, long long lcn) +{ + static unsigned char buffer[512]; + static long long bmplcn = -sizeof(buffer) - 1; /* Which bit of $Bitmap is in the buffer */ + + int byte, bit; + ntfs_attr *attr; + + if (!vol) { + errno = EINVAL; + return -1; + } + + /* Does lcn lie in the section of $Bitmap we already have cached? */ + if ((lcn < bmplcn) || (lcn >= (bmplcn + (sizeof(buffer) << 3)))) { + ntfs_log_debug("Bit lies outside cache.\n"); + attr = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0); + if (!attr) { + ntfs_log_perror("Couldn't open $Bitmap"); + return -1; + } + + /* Mark the buffer as in use, in case the read is shorter. */ + memset(buffer, 0xFF, sizeof(buffer)); + bmplcn = lcn & (~((sizeof(buffer) << 3) - 1)); + + if (ntfs_attr_pread(attr, (bmplcn>>3), sizeof(buffer), buffer) < 0) { + ntfs_log_perror("Couldn't read $Bitmap"); + ntfs_attr_close(attr); + return -1; + } + + ntfs_log_debug("Reloaded bitmap buffer.\n"); + ntfs_attr_close(attr); + } + + bit = 1 << (lcn & 7); + byte = (lcn >> 3) & (sizeof(buffer) - 1); + ntfs_log_debug("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, in use %d\n", lcn, bmplcn, byte, bit, buffer[byte] & bit); + + return (buffer[byte] & bit); +} + +/** + * utils_mftrec_in_use - Determine if a MFT Record is in use + * @vol: An ntfs volume obtained from ntfs_mount + * @mref: MFT Reference (inode number) + * + * The metadata file $BITMAP has one binary bit representing each record in the + * MFT. The bit will be set for each record that is in use. The function + * reads the relevant part of $BITMAP into a buffer and tests the bit. + * + * This function has a static buffer in which it caches a section of $BITMAP. + * If the mref, being tested, lies outside the range, the buffer will be + * refreshed. + * + * Return: 1 MFT Record is in use + * 0 MFT Record is unused + * -1 Error occurred + */ +int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref) +{ + static u8 buffer[512]; + static s64 bmpmref = -sizeof(buffer) - 1; /* Which bit of $BITMAP is in the buffer */ + + int byte, bit; + + if (!vol) { + errno = EINVAL; + return -1; + } + + ntfs_log_trace("entering\n"); + /* Does mref lie in the section of $Bitmap we already have cached? */ + if (((s64)MREF(mref) < bmpmref) || ((s64)MREF(mref) >= (bmpmref + + (sizeof(buffer) << 3)))) { + ntfs_log_debug("Bit lies outside cache.\n"); + + /* Mark the buffer as not in use, in case the read is shorter. */ + memset(buffer, 0, sizeof(buffer)); + bmpmref = mref & (~((sizeof(buffer) << 3) - 1)); + + if (ntfs_attr_pread(vol->mftbmp_na, (bmpmref>>3), sizeof(buffer), buffer) < 0) { + ntfs_log_perror("Couldn't read $MFT/$BITMAP"); + return -1; + } + + ntfs_log_debug("Reloaded bitmap buffer.\n"); + } + + bit = 1 << (mref & 7); + byte = (mref >> 3) & (sizeof(buffer) - 1); + ntfs_log_debug("cluster = %lld, bmpmref = %lld, byte = %d, bit = %d, in use %d\n", mref, bmpmref, byte, bit, buffer[byte] & bit); + + return (buffer[byte] & bit); +} + +/** + * __metadata + */ +static int __metadata(ntfs_volume *vol, u64 num) +{ + if (num <= FILE_UpCase) + return 1; + if (!vol) + return -1; + if ((vol->major_ver == 3) && (num == FILE_Extend)) + return 1; + + return 0; +} + +/** + * utils_is_metadata - Determine if an inode represents a metadata file + * @inode: An ntfs inode to be tested + * + * A handful of files in the volume contain filesystem data - metadata. + * They can be identified by their inode number (offset in MFT/$DATA) or by + * their parent. + * + * Return: 1 inode is a metadata file + * 0 inode is not a metadata file + * -1 Error occurred + */ +int utils_is_metadata(ntfs_inode *inode) +{ + ntfs_volume *vol; + ATTR_RECORD *rec; + FILE_NAME_ATTR *attr; + MFT_RECORD *file; + u64 num; + + if (!inode) { + errno = EINVAL; + return -1; + } + + vol = inode->vol; + if (!vol) + return -1; + + num = inode->mft_no; + if (__metadata(vol, num) == 1) + return 1; + + file = inode->mrec; + if (file && (file->base_mft_record != 0)) { + num = MREF(file->base_mft_record); + if (__metadata(vol, num) == 1) + return 1; + } + file = inode->mrec; + + rec = find_first_attribute(AT_FILE_NAME, inode->mrec); + if (!rec) + return -1; + + /* We know this will always be resident. */ + attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->value_offset)); + + num = MREF(attr->parent_directory); + if ((num != FILE_root) && (__metadata(vol, num) == 1)) + return 1; + + return 0; +} + +/** + * utils_dump_mem - Display a block of memory in hex and ascii + * @buf: Buffer to be displayed + * @start: Offset into @buf to start from + * @length: Number of bytes to display + * @flags: Options to change the style of the output + * + * Display a block of memory in a tradition hex-dump manner. + * Optionally the ascii part can be turned off. + * + * The flags, described fully in utils.h, default to 0 (DM_DEFAULTS). + * Examples are: DM_INDENT (indent the output by one tab); DM_RED (colour the + * output); DM_NO_ASCII (only print the hex values). + */ +void utils_dump_mem(void *buf, int start, int length, int flags) +{ + int off, i, s, e, col; + u8 *mem = buf; + + s = start & ~15; // round down + e = (start + length + 15) & ~15; // round up + + for (off = s; off < e; off += 16) { + col = 30; + if (flags & DM_RED) + col += 1; + if (flags & DM_GREEN) + col += 2; + if (flags & DM_BLUE) + col += 4; + if (flags & DM_INDENT) + ntfs_log_debug("\t"); + if (flags & DM_BOLD) + ntfs_log_debug("\e[01m"); + if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD)) + ntfs_log_debug("\e[%dm", col); + if (off == s) + ntfs_log_debug("%6.6x ", start); + else + ntfs_log_debug("%6.6x ", off); + + for (i = 0; i < 16; i++) { + if ((i == 8) && (!(flags & DM_NO_DIVIDER))) + ntfs_log_debug(" -"); + if (((off+i) >= start) && ((off+i) < (start+length))) + ntfs_log_debug(" %02X", mem[off+i]); + else + ntfs_log_debug(" "); + } + if (!(flags & DM_NO_ASCII)) { + ntfs_log_debug(" "); + for (i = 0; i < 16; i++) { + if (((off+i) < start) || ((off+i) >= (start+length))) + ntfs_log_debug(" "); + else if (isprint(mem[off + i])) + ntfs_log_debug("%c", mem[off + i]); + else + ntfs_log_debug("."); + } + } + if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD)) + ntfs_log_debug("\e[0m"); + ntfs_log_debug("\n"); + } +} + + +/** + * mft_get_search_ctx + */ +struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol) +{ + struct mft_search_ctx *ctx; + + if (!vol) { + errno = EINVAL; + return NULL; + } + + ctx = calloc(1, sizeof *ctx); + + ctx->mft_num = -1; + ctx->vol = vol; + + return ctx; +} + +/** + * mft_put_search_ctx + */ +void mft_put_search_ctx(struct mft_search_ctx *ctx) +{ + if (!ctx) + return; + if (ctx->inode) + ntfs_inode_close(ctx->inode); + free(ctx); +} + +/** + * mft_next_record + */ +int mft_next_record(struct mft_search_ctx *ctx) +{ + s64 nr_mft_records; + ATTR_RECORD *attr10 = NULL; + ATTR_RECORD *attr20 = NULL; + ATTR_RECORD *attr80 = NULL; + ntfs_attr_search_ctx *attr_ctx; + + if (!ctx) { + errno = EINVAL; + return -1; + } + + if (ctx->inode) { + ntfs_inode_close(ctx->inode); + ctx->inode = NULL; + } + + nr_mft_records = ctx->vol->mft_na->initialized_size >> + ctx->vol->mft_record_size_bits; + + for (ctx->mft_num++; (s64)ctx->mft_num < nr_mft_records; ctx->mft_num++) { + int in_use; + + ctx->flags_match = 0; + in_use = utils_mftrec_in_use(ctx->vol, (MFT_REF) ctx->mft_num); + if (in_use == -1) { + ntfs_log_error("Error reading inode %llu. Aborting.\n", + (unsigned long long)ctx->mft_num); + return -1; + } + + if (in_use) { + ctx->flags_match |= FEMR_IN_USE; + + ctx->inode = ntfs_inode_open(ctx->vol, (MFT_REF) ctx->mft_num); + if (ctx->inode == NULL) { + ntfs_log_error("Error reading inode %llu.\n", (unsigned + long long) ctx->mft_num); + continue; + } + + attr10 = find_first_attribute(AT_STANDARD_INFORMATION, ctx->inode->mrec); + attr20 = find_first_attribute(AT_ATTRIBUTE_LIST, ctx->inode->mrec); + attr80 = find_first_attribute(AT_DATA, ctx->inode->mrec); + + if (attr10) + ctx->flags_match |= FEMR_BASE_RECORD; + else + ctx->flags_match |= FEMR_NOT_BASE_RECORD; + + if (attr20) + ctx->flags_match |= FEMR_BASE_RECORD; + + if (attr80) + ctx->flags_match |= FEMR_FILE; + + if (ctx->flags_search & FEMR_DIR) { + attr_ctx = ntfs_attr_get_search_ctx(ctx->inode, NULL); + if (attr_ctx) { + if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, 0, 0, NULL, 0, attr_ctx) == 0) + ctx->flags_match |= FEMR_DIR; + + ntfs_attr_put_search_ctx(attr_ctx); + } else { + ntfs_log_error("Couldn't create a search context.\n"); + return -1; + } + } + + switch (utils_is_metadata(ctx->inode)) { + case 1: ctx->flags_match |= FEMR_METADATA; break; + case 0: ctx->flags_match |= FEMR_NOT_METADATA; break; + default: + ctx->flags_match |= FEMR_NOT_METADATA; break; + //ntfs_log_error("Error reading inode %lld.\n", ctx->mft_num); + //return -1; + } + + } else { // !in_use + ntfs_attr *mft; + + ctx->flags_match |= FEMR_NOT_IN_USE; + + ctx->inode = calloc(1, sizeof(*ctx->inode)); + if (!ctx->inode) { + ntfs_log_error("Out of memory. Aborting.\n"); + return -1; + } + + ctx->inode->mft_no = ctx->mft_num; + ctx->inode->vol = ctx->vol; + ctx->inode->mrec = malloc(ctx->vol->mft_record_size); + if (!ctx->inode->mrec) { + free(ctx->inode); // == ntfs_inode_close + ntfs_log_error("Out of memory. Aborting.\n"); + return -1; + } + + mft = ntfs_attr_open(ctx->vol->mft_ni, AT_DATA, + AT_UNNAMED, 0); + if (!mft) { + ntfs_log_perror("Couldn't open $MFT/$DATA"); + // free / close + return -1; + } + + if (ntfs_attr_pread(mft, ctx->vol->mft_record_size * ctx->mft_num, ctx->vol->mft_record_size, ctx->inode->mrec) < ctx->vol->mft_record_size) { + ntfs_log_perror("Couldn't read MFT Record %llu", + (unsigned long long) ctx->mft_num); + // free / close + ntfs_attr_close(mft); + return -1; + } + + ntfs_attr_close(mft); + } + + if (ctx->flags_match & ctx->flags_search) { + break; + } + + if (ntfs_inode_close(ctx->inode)) { + ntfs_log_error("Error closing inode %llu.\n", + (unsigned long long)ctx->mft_num); + return -errno; + } + + ctx->inode = NULL; + } + + return (ctx->inode == NULL); +} + + diff --git a/ntfsprogs/utils.h b/ntfsprogs/utils.h new file mode 100644 index 00000000..9fbb20b8 --- /dev/null +++ b/ntfsprogs/utils.h @@ -0,0 +1,157 @@ +/* + * utils.h - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2004 Anton Altaparmakov + * + * A set of shared functions for ntfs utilities + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * 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 _NTFS_UTILS_H_ +#define _NTFS_UTILS_H_ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDARG_H +#include +#endif + +extern const char *ntfs_bugs; +extern const char *ntfs_home; +extern const char *ntfs_gpl; + +int utils_set_locale(void); +int utils_parse_size(const char *value, s64 *size, BOOL scale); +int utils_parse_range(const char *string, s64 *start, s64 *finish, BOOL scale); +int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize); +int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int bufsize); +int utils_cluster_in_use(ntfs_volume *vol, long long lcn); +int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref); +int utils_is_metadata(ntfs_inode *inode); +void utils_dump_mem(void *buf, int start, int length, int flags); + +#ifndef _NTFS_RICH_H_ +ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx); +ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft); +#endif + +#if !(defined(_NTFS_VOLUME_H) && defined(NTFS_RICH)) +int utils_valid_device(const char *name, int force); +ntfs_volume * utils_mount_volume(const char *device, unsigned long flags, BOOL force); +#endif + +/** + * defines... + * if *not in use* then the other flags are ignored? + */ +#define FEMR_IN_USE (1 << 0) +#define FEMR_NOT_IN_USE (1 << 1) +#define FEMR_FILE (1 << 2) // $DATA +#define FEMR_DIR (1 << 3) // $INDEX_ROOT, "$I30" +#define FEMR_METADATA (1 << 4) +#define FEMR_NOT_METADATA (1 << 5) +#define FEMR_BASE_RECORD (1 << 6) +#define FEMR_NOT_BASE_RECORD (1 << 7) +#define FEMR_ALL_RECORDS 0xFF + +/** + * struct mft_search_ctx + */ +struct mft_search_ctx { + int flags_search; + int flags_match; + ntfs_inode *inode; + ntfs_volume *vol; + u64 mft_num; +}; + +struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol); +void mft_put_search_ctx(struct mft_search_ctx *ctx); +int mft_next_record(struct mft_search_ctx *ctx); + +// Flags for dump mem +#define DM_DEFAULTS 0 +#define DM_NO_ASCII (1 << 0) +#define DM_NO_DIVIDER (1 << 1) +#define DM_INDENT (1 << 2) +#define DM_RED (1 << 3) +#define DM_GREEN (1 << 4) +#define DM_BLUE (1 << 5) +#define DM_BOLD (1 << 6) + +/* MAX_PATH definition was missing in ntfs-3g's headers. */ +#ifndef MAX_PATH +#define MAX_PATH 1024 +#endif + +/** + * linux-ntfs's ntfs_mbstoucs has different semantics, so we emulate it with + * ntfs-3g's. + */ +static __inline__ int ntfs_mbstoucs_libntfscompat(const char *ins, + ntfschar **outs, int outs_len) +{ + if(!outs) { + errno = EINVAL; + return -1; + } + else if(*outs != NULL) { + /* Note: libntfs's mbstoucs implementation allows the caller to + * specify a preallocated buffer while libntfs-3g's always + * allocates the output buffer. + */ + ntfschar *tmpstr = NULL; + int tmpstr_len; + + tmpstr_len = ntfs_mbstoucs(ins, &tmpstr); + if(tmpstr_len >= 0) { + if((tmpstr_len + 1) > outs_len) { + /* Doing a realloc instead of reusing tmpstr + * because it emulates libntfs's mbstoucs more + * closely. */ + ntfschar *re_outs = realloc(*outs, + sizeof(ntfschar)*(tmpstr_len + 1)); + if(!re_outs) + tmpstr_len = -1; + else + *outs = re_outs; + } + + if(tmpstr_len >= 0) { + /* The extra character is the \0 terminator. */ + memcpy(*outs, tmpstr, + sizeof(ntfschar)*(tmpstr_len + 1)); + } + + free(tmpstr); + } + + return tmpstr_len; + } + else + return ntfs_mbstoucs(ins, outs); +} + +#endif /* _NTFS_UTILS_H_ */ diff --git a/test/.cvsignore b/test/.cvsignore new file mode 100644 index 00000000..6d39d9bc --- /dev/null +++ b/test/.cvsignore @@ -0,0 +1,5 @@ +.deps +.libs +Makefile +Makefile.in +runlist diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 00000000..8ea5ea5d --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,59 @@ +if REALLYSTATIC +AM_LIBS = $(top_builddir)/libntfs/.libs/libntfs.a +AM_LFLAGS = -static +STATIC_LINK = $(CC) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +else +AM_LIBS = $(top_builddir)/libntfs/libntfs.la +AM_LFLAGS = $(all_libraries) +LIBTOOL_LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +endif + +# Workaround to make REALLYSTATIC work with automake 1.5. +LINK=$(STATIC_LINK) $(LIBTOOL_LINK) + +#TEST_VALGRIND = valgrind + +bin_PROGRAMS = runlist + +EXTRA_DIST = runlist-data + +CLEANFILES = attr[123].bin frag[123][123][123] pure-[cn][ms] zero +MAINTAINERCLEANFILES = Makefile.in + +linux_ntfsincludedir = -I$(top_srcdir)/include/ntfs + +# Set the include path. +AM_CPPFLAGS = -I$(top_srcdir)/include/ntfs $(all_includes) + +runlist_SOURCES = runlist.c +runlist_LDADD = $(AM_LIBS) +runlist_LDFLAGS = $(AM_LFLAGS) + +# Extra targets + +libs: + (cd ../libntfs && $(MAKE) libs) || exit 1; + +test: runlist testz testp testf + +testz: runlist + @$(TEST_VALGRIND) ./runlist zero > zero + @diff -qs {runlist-data/,}zero + +testp: runlist + @for i in contig noncontig; do \ + for j in single multi; do \ + L1=`echo $$i | cut -b1`; \ + L2=`echo $$j | cut -b1`; \ + FILE=pure-$$L1$$L2; \ + $(TEST_VALGRIND) ./runlist pure $$i $$j > $$FILE; \ + diff -qs {runlist-data/,}$$FILE; \ + done; \ + done + +testf: runlist + @for i in 123 132 213 231 312 321; do \ + $(TEST_VALGRIND) ./runlist frag $$i > frag$$i; \ + diff -qs {runlist-data/,}frag$$i; \ + done + diff --git a/test/runlist-data/attr1.bin b/test/runlist-data/attr1.bin new file mode 100644 index 00000000..9ad042c3 Binary files /dev/null and b/test/runlist-data/attr1.bin differ diff --git a/test/runlist-data/attr2.bin b/test/runlist-data/attr2.bin new file mode 100644 index 00000000..d46fcc53 Binary files /dev/null and b/test/runlist-data/attr2.bin differ diff --git a/test/runlist-data/attr3.bin b/test/runlist-data/attr3.bin new file mode 100644 index 00000000..04d42d0c Binary files /dev/null and b/test/runlist-data/attr3.bin differ diff --git a/test/runlist-data/frag123 b/test/runlist-data/frag123 new file mode 100644 index 00000000..5b660eb4 --- /dev/null +++ b/test/runlist-data/frag123 @@ -0,0 +1,2213 @@ +dst: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 NOTMAP 892 + 1548 ENOENT 0 + +src: + VCN LCN len + 0 NOTMAP 656 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 NOTMAP 0 + +res: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 NOTMAP 386 + 1548 ENOENT 0 + +dst: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 NOTMAP 386 + 1548 ENOENT 0 + +src: + VCN LCN len + 0 NOTMAP 1162 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 NOTMAP 0 + +res: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 ENOENT 0 + diff --git a/test/runlist-data/frag132 b/test/runlist-data/frag132 new file mode 100644 index 00000000..7056f330 --- /dev/null +++ b/test/runlist-data/frag132 @@ -0,0 +1,2061 @@ +dst: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 NOTMAP 892 + 1548 ENOENT 0 + +src: + VCN LCN len + 0 NOTMAP 1162 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 NOTMAP 0 + +res: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 NOTMAP 506 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 ENOENT 0 + +dst: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 NOTMAP 506 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 ENOENT 0 + +src: + VCN LCN len + 0 NOTMAP 656 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 NOTMAP 0 + +res: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 ENOENT 0 + diff --git a/test/runlist-data/frag213 b/test/runlist-data/frag213 new file mode 100644 index 00000000..6505a3f4 --- /dev/null +++ b/test/runlist-data/frag213 @@ -0,0 +1,2213 @@ +dst: + VCN LCN len + 0 NOTMAP 656 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 NOTMAP 0 + +src: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 NOTMAP 892 + 1548 ENOENT 0 + +res: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 NOTMAP 386 + 1548 ENOENT 0 + +dst: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 NOTMAP 386 + 1548 ENOENT 0 + +src: + VCN LCN len + 0 NOTMAP 1162 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 NOTMAP 0 + +res: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 ENOENT 0 + diff --git a/test/runlist-data/frag231 b/test/runlist-data/frag231 new file mode 100644 index 00000000..6abb9560 --- /dev/null +++ b/test/runlist-data/frag231 @@ -0,0 +1,2133 @@ +dst: + VCN LCN len + 0 NOTMAP 656 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 NOTMAP 0 + +src: + VCN LCN len + 0 NOTMAP 1162 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 656 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 NOTMAP 0 + +dst: + VCN LCN len + 0 NOTMAP 656 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 NOTMAP 0 + +src: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 NOTMAP 892 + 1548 ENOENT 0 + +res: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 ENOENT 0 + diff --git a/test/runlist-data/frag312 b/test/runlist-data/frag312 new file mode 100644 index 00000000..cc366705 --- /dev/null +++ b/test/runlist-data/frag312 @@ -0,0 +1,2061 @@ +dst: + VCN LCN len + 0 NOTMAP 1162 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 NOTMAP 0 + +src: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 NOTMAP 892 + 1548 ENOENT 0 + +res: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 NOTMAP 506 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 ENOENT 0 + +dst: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 NOTMAP 506 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 ENOENT 0 + +src: + VCN LCN len + 0 NOTMAP 656 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 NOTMAP 0 + +res: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 ENOENT 0 + diff --git a/test/runlist-data/frag321 b/test/runlist-data/frag321 new file mode 100644 index 00000000..c7ef3b26 --- /dev/null +++ b/test/runlist-data/frag321 @@ -0,0 +1,2133 @@ +dst: + VCN LCN len + 0 NOTMAP 1162 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 NOTMAP 0 + +src: + VCN LCN len + 0 NOTMAP 656 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 656 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 NOTMAP 0 + +dst: + VCN LCN len + 0 NOTMAP 656 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 NOTMAP 0 + +src: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 NOTMAP 892 + 1548 ENOENT 0 + +res: + VCN LCN len + 0 668369 202 + 202 5 3 + 205 88977 1 + 206 93277 2 + 208 700201 4 + 212 196949 2 + 214 91962 2 + 216 96039 2 + 218 510902 2 + 220 513930 2 + 222 382784 3 + 225 389236 1 + 226 200295 3 + 229 506471 1 + 230 583065 2 + 232 501093 2 + 234 159071 2 + 236 97603 3 + 239 699725 1 + 240 462560 2 + 242 388847 3 + 245 547008 1 + 246 463037 2 + 248 463213 2 + 250 588202 2 + 252 639680 2 + 254 119553 2 + 256 119557 2 + 258 123678 2 + 260 378398 2 + 262 378402 2 + 264 380803 2 + 266 393092 2 + 268 453617 2 + 270 453621 2 + 272 499548 2 + 274 499552 2 + 276 531547 2 + 278 558995 2 + 280 560544 2 + 282 560548 2 + 284 589923 2 + 286 665617 2 + 288 665621 2 + 290 665718 2 + 292 680081 2 + 294 450790 2 + 296 538154 3 + 299 440454 1 + 300 661832 2 + 302 159063 2 + 304 159067 2 + 306 692627 2 + 308 190492 2 + 310 440252 2 + 312 440256 2 + 314 539315 2 + 316 557874 2 + 318 557879 2 + 320 651343 2 + 322 669543 2 + 324 669549 2 + 326 693618 2 + 328 133878 2 + 330 143006 2 + 332 143013 2 + 334 185381 2 + 336 384428 2 + 338 384433 2 + 340 570243 2 + 342 570247 2 + 344 588378 2 + 346 588381 2 + 348 588385 2 + 350 632124 2 + 352 159043 4 + 356 185701 2 + 358 396021 4 + 362 401623 2 + 364 404847 2 + 366 404851 2 + 368 429204 2 + 370 429209 2 + 372 534835 2 + 374 534840 2 + 376 547606 2 + 378 547610 2 + 380 614127 2 + 382 614131 2 + 384 614135 2 + 386 678737 2 + 388 678741 2 + 390 691360 2 + 392 691364 2 + 394 691368 2 + 396 94182 9 + 405 398630 1 + 406 127172 3 + 409 651153 1 + 410 89356 3 + 413 119543 1 + 414 559361 5 + 419 185706 1 + 420 583086 5 + 425 680868 1 + 426 196004 4 + 430 544751 2 + 432 614137 3 + 435 583064 1 + 436 398727 2 + 438 507478 2 + 440 693671 2 + 442 190449 2 + 444 101747 3 + 447 658789 1 + 448 111669 3 + 451 401811 1 + 452 188516 3 + 455 193767 1 + 456 502571 2 + 458 517369 3 + 461 531443 1 + 462 591803 2 + 464 616659 3 + 467 669347 1 + 468 123684 2 + 470 126303 2 + 472 158876 2 + 474 187768 2 + 476 380764 4 + 480 388850 2 + 482 395980 2 + 484 401629 2 + 486 431111 4 + 490 561750 4 + 494 450831 2 + 496 650762 4 + 500 452271 2 + 502 541524 4 + 506 458990 2 + 508 505392 2 + 510 534798 2 + 512 549464 2 + 514 583689 2 + 516 106735 3 + 519 378397 1 + 520 127170 2 + 522 463158 2 + 524 501137 2 + 526 568046 2 + 528 568049 2 + 530 569317 3 + 533 571146 1 + 534 583081 2 + 536 590366 2 + 538 590369 2 + 540 632131 3 + 543 638545 1 + 544 642281 2 + 546 658810 2 + 548 658813 2 + 550 682162 3 + 553 95934 1 + 554 96446 2 + 556 96449 3 + 559 100722 1 + 560 106898 2 + 562 106901 3 + 565 112498 1 + 566 159053 2 + 568 159056 3 + 571 159084 1 + 572 189252 2 + 574 693634 2 + 576 693638 3 + 579 700985 1 + 580 396057 2 + 582 197027 2 + 584 111762 2 + 586 111767 2 + 588 185845 2 + 590 190485 2 + 592 521100 6 + 598 452201 2 + 600 452204 2 + 602 452208 2 + 604 452192 2 + 606 452236 2 + 608 452269 2 + 610 452712 2 + 612 452188 2 + 614 452715 2 + 616 452719 2 + 618 452834 2 + 620 452185 2 + 622 452849 2 + 624 452852 2 + 626 453619 2 + 628 455897 2 + 630 456008 2 + 632 456116 2 + 634 456123 2 + 636 457145 2 + 638 459068 2 + 640 459072 2 + 642 459290 2 + 644 460967 2 + 646 460975 2 + 648 460979 2 + 650 460987 2 + 652 460991 2 + 654 460999 2 + 656 461003 2 + 658 461136 2 + 660 462188 2 + 662 463150 2 + 664 463165 2 + 666 465534 2 + 668 466039 2 + 670 466304 2 + 672 466456 2 + 674 466702 2 + 676 466706 2 + 678 466718 2 + 680 466722 2 + 682 466767 2 + 684 467182 2 + 686 467455 2 + 688 467458 2 + 690 467471 2 + 692 467474 2 + 694 467610 2 + 696 469556 2 + 698 469564 2 + 700 469568 2 + 702 469576 2 + 704 469580 2 + 706 469594 2 + 708 469598 2 + 710 470581 2 + 712 470584 2 + 714 470604 2 + 716 470615 2 + 718 472161 2 + 720 472264 2 + 722 472317 2 + 724 472320 2 + 726 499209 2 + 728 499213 2 + 730 499230 2 + 732 499242 2 + 734 499550 2 + 736 500389 2 + 738 500396 2 + 740 500558 2 + 742 500565 2 + 744 500574 2 + 746 500581 2 + 748 500882 2 + 750 500932 2 + 752 500953 2 + 754 501091 2 + 756 501524 2 + 758 502235 2 + 760 502239 2 + 762 502601 2 + 764 502605 2 + 766 504799 2 + 768 504803 2 + 770 504813 2 + 772 504817 2 + 774 504830 2 + 776 504843 2 + 778 606218 4 + 782 504846 2 + 784 505846 2 + 786 506600 2 + 788 506682 2 + 790 506686 2 + 792 509203 2 + 794 509397 2 + 796 512916 2 + 798 513199 2 + 800 513732 2 + 802 513736 2 + 804 514918 2 + 806 514922 2 + 808 517616 2 + 810 517619 2 + 812 518092 2 + 814 518536 2 + 816 521342 2 + 818 521968 2 + 820 525657 2 + 822 525661 2 + 824 534520 2 + 826 534728 2 + 828 534796 2 + 830 534826 2 + 832 534945 2 + 834 536053 2 + 836 536932 2 + 838 536936 2 + 840 537064 2 + 842 538152 2 + 844 539313 2 + 846 539317 2 + 848 543513 2 + 850 543517 2 + 852 543530 2 + 854 543534 2 + 856 544573 2 + 858 546721 2 + 860 546728 2 + 862 546824 2 + 864 546951 2 + 866 546955 2 + 868 546983 2 + 870 546987 2 + 872 547001 2 + 874 547604 2 + 876 547612 2 + 878 547775 2 + 880 548897 2 + 882 549446 2 + 884 549466 2 + 886 553167 2 + 888 664000 4 + 892 553171 2 + 894 557877 2 + 896 558371 2 + 898 558378 2 + 900 558390 2 + 902 558951 2 + 904 558954 2 + 906 559319 2 + 908 559322 2 + 910 559351 2 + 912 559354 2 + 914 560495 2 + 916 560498 2 + 918 562981 2 + 920 562985 2 + 922 566029 2 + 924 566327 2 + 926 566334 2 + 928 567723 2 + 930 569315 2 + 932 569325 2 + 934 569332 2 + 936 569441 2 + 938 570191 2 + 940 570245 2 + 942 570338 2 + 944 570562 2 + 946 571144 2 + 948 571693 2 + 950 572607 2 + 952 572856 2 + 954 576073 2 + 956 577617 2 + 958 583639 2 + 960 583658 2 + 962 584802 2 + 964 584805 2 + 966 588191 2 + 968 588195 2 + 970 588293 2 + 972 588317 2 + 974 588335 2 + 976 588339 2 + 978 588936 2 + 980 588939 2 + 982 588982 2 + 984 588985 2 + 986 589053 2 + 988 589921 2 + 990 590318 2 + 992 590322 2 + 994 190644 4 + 998 590333 2 + 1000 590340 2 + 1002 599957 2 + 1004 606152 2 + 1006 606265 2 + 1008 614133 2 + 1010 616644 2 + 1012 622653 2 + 1014 622657 2 + 1016 622668 2 + 1018 632111 2 + 1020 632122 2 + 1022 635768 2 + 1024 635787 2 + 1026 638413 2 + 1028 638546 2 + 1030 640762 2 + 1032 642292 2 + 1034 642295 2 + 1036 643840 2 + 1038 643849 2 + 1040 643856 2 + 1042 644023 2 + 1044 644845 2 + 1046 645820 2 + 1048 650256 2 + 1050 651341 2 + 1052 653663 2 + 1054 658769 2 + 1056 658795 2 + 1058 658821 2 + 1060 658932 2 + 1062 660116 2 + 1064 661753 2 + 1066 663929 2 + 1068 665619 2 + 1070 665720 2 + 1072 668954 2 + 1074 668957 2 + 1076 669345 2 + 1078 669547 2 + 1080 675879 2 + 1082 675883 2 + 1084 678743 2 + 1086 680079 2 + 1088 680766 2 + 1090 681858 2 + 1092 684914 2 + 1094 499199 4 + 1098 684917 2 + 1100 505472 4 + 1104 684967 2 + 1106 684971 2 + 1108 691362 2 + 1110 691366 2 + 1112 692090 2 + 1114 692631 2 + 1116 693632 2 + 1118 693636 2 + 1120 694133 2 + 1122 698601 2 + 1124 700986 2 + 1126 700990 2 + 1128 701003 2 + 1130 88075 2 + 1132 88130 2 + 1134 111868 2 + 1136 112496 2 + 1138 119462 2 + 1140 123676 2 + 1142 123761 2 + 1144 123775 2 + 1146 123782 2 + 1148 126301 2 + 1150 127144 2 + 1152 127151 2 + 1154 129078 2 + 1156 133880 2 + 1158 133922 2 + 1160 142932 2 + 1162 143011 2 + 1164 158982 2 + 1166 158985 2 + 1168 159065 2 + 1170 159069 2 + 1172 159085 2 + 1174 178517 2 + 1176 182728 2 + 1178 584969 4 + 1182 95400 2 + 1184 96574 2 + 1186 642311 2 + 1188 142940 3 + 1191 471640 1 + 1192 463323 2 + 1194 303220 2 + 1196 320952 2 + 1198 345925 2 + 1200 284287 3 + 1203 265141 1 + 1204 692753 5 + 1209 694377 1 + 1210 696470 2 + 1212 459236 2 + 1214 691357 2 + 1216 388793 2 + 1218 391451 2 + 1220 696517 2 + 1222 133883 4 + 1226 547436 3 + 1229 380908 1 + 1230 701179 3 + 1233 571671 1 + 1234 105558 2 + 1236 105798 2 + 1238 125155 2 + 1240 231221 2 + 1242 256319 2 + 1244 391107 2 + 1246 392139 2 + 1248 429501 2 + 1250 433423 2 + 1252 342596 2 + 1254 120833 2 + 1256 230532 2 + 1258 538684 2 + 1260 523088 2 + 1262 56106 3 + 1265 525665 1 + 1266 466143 3 + 1269 432818 1 + 1270 505109 3 + 1273 505788 1 + 1274 520989 2 + 1276 376486 2 + 1278 398137 2 + 1280 424128 2 + 1282 466553 2 + 1284 536577 2 + 1286 602176 2 + 1288 50225 2 + 1290 112810 2 + 1292 540934 2 + 1294 294437 2 + 1296 576108 2 + 1298 693630 2 + 1300 696703 2 + 1302 118305 6 + 1308 190119 2 + 1310 554042 2 + 1312 21689 2 + 1314 307082 2 + 1316 693674 2 + 1318 694135 2 + 1320 694342 2 + 1322 517951 4 + 1326 303202 2 + 1328 541694 2 + 1330 378216 2 + 1332 463790 2 + 1334 565125 2 + 1336 386152 2 + 1338 462684 2 + 1340 463794 2 + 1342 463806 2 + 1344 467112 2 + 1346 472110 2 + 1348 391267 4 + 1352 569423 2 + 1354 514763 4 + 1358 499866 6 + 1364 548040 10 + 1374 469592 2 + 1376 318450 2 + 1378 32753 2 + 1380 17719 3 + 1383 29987 1 + 1384 168399 2 + 1386 174773 3 + 1389 178041 1 + 1390 320947 2 + 1392 38384 2 + 1394 42120 2 + 1396 44060 2 + 1398 47163 2 + 1400 49428 2 + 1402 54795 2 + 1404 61045 2 + 1406 65477 2 + 1408 49124 3 + 1411 38246 1 + 1412 59888 3 + 1415 39159 1 + 1416 185285 3 + 1419 185696 1 + 1420 59576 2 + 1422 63319 2 + 1424 165754 2 + 1426 375919 2 + 1428 381015 2 + 1430 382330 2 + 1432 68711 2 + 1434 470281 2 + 1436 688609 2 + 1438 699733 2 + 1440 48221 2 + 1442 386049 2 + 1444 695543 2 + 1446 317957 2 + 1448 388731 2 + 1450 470586 2 + 1452 182660 2 + 1454 185576 2 + 1456 187854 2 + 1458 168827 2 + 1460 386136 2 + 1462 235763 2 + 1464 260456 2 + 1466 238330 2 + 1468 263249 2 + 1470 34585 3 + 1473 246690 1 + 1474 389047 2 + 1476 92973 2 + 1478 290622 2 + 1480 406011 2 + 1482 499254 4 + 1486 508163 6 + 1492 247967 2 + 1494 263257 2 + 1496 294156 3 + 1499 394563 3 + 1502 30286 6 + 1508 289484 2 + 1510 394060 2 + 1512 73471 2 + 1514 285182 2 + 1516 375844 2 + 1518 336200 2 + 1520 500386 2 + 1522 308459 2 + 1524 278713 2 + 1526 289610 2 + 1528 395158 2 + 1530 324707 2 + 1532 323068 4 + 1536 276437 2 + 1538 324739 2 + 1540 278696 2 + 1542 380942 2 + 1544 394300 2 + 1546 394741 2 + 1548 ENOENT 0 + diff --git a/test/runlist-data/pure-cm b/test/runlist-data/pure-cm new file mode 100644 index 00000000..f09a7227 --- /dev/null +++ b/test/runlist-data/pure-cm @@ -0,0 +1,964 @@ +Test 1 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 0 1000 10 + 10 1010 10 + 20 1020 10 + 30 1030 10 + 40 NOTMAP 0 + +res: + VCN LCN len + 0 1000 10 + 10 1010 10 + 20 1020 10 + 30 1030 10 + 40 HOLE 60 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 2 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 40 1040 10 + 50 1050 10 + 60 1060 10 + 70 1070 10 + 80 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 40 + 40 1040 10 + 50 1050 10 + 60 1060 10 + 70 1070 10 + 80 HOLE 20 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 3 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 60 1060 10 + 70 1070 10 + 80 1080 10 + 90 1090 10 + 100 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 60 + 60 1060 10 + 70 1070 10 + 80 1080 10 + 90 1090 110 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 4 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 0 1000 25 + 25 1025 25 + 50 1050 25 + 75 1075 25 + 100 NOTMAP 0 + +res: + VCN LCN len + 0 1000 25 + 25 1025 25 + 50 1050 25 + 75 1075 125 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 5 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 200 1200 10 + 210 1210 10 + 220 1220 10 + 230 1230 10 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 110 + 210 1210 10 + 220 1220 10 + 230 1230 10 + 240 HOLE 60 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 6 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 240 1240 10 + 250 1250 10 + 260 1260 10 + 270 1270 10 + 280 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 40 + 240 1240 10 + 250 1250 10 + 260 1260 10 + 270 1270 10 + 280 HOLE 20 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 7 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 260 1260 10 + 270 1270 10 + 280 1280 10 + 290 1290 10 + 300 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 60 + 260 1260 10 + 270 1270 10 + 280 1280 10 + 290 1290 110 + 400 HOLE 100 + 500 ENOENT 0 + +Test 8 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 200 1200 25 + 225 1225 25 + 250 1250 25 + 275 1275 25 + 300 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 125 + 225 1225 25 + 250 1250 25 + 275 1275 125 + 400 HOLE 100 + 500 ENOENT 0 + +Test 9 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 400 1400 10 + 410 1410 10 + 420 1420 10 + 430 1430 10 + 440 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 110 + 410 1410 10 + 420 1420 10 + 430 1430 10 + 440 HOLE 60 + 500 ENOENT 0 + +Test 10 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 440 1440 10 + 450 1450 10 + 460 1460 10 + 470 1470 10 + 480 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 40 + 440 1440 10 + 450 1450 10 + 460 1460 10 + 470 1470 10 + 480 HOLE 20 + 500 ENOENT 0 + +Test 11 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 460 1460 10 + 470 1470 10 + 480 1480 10 + 490 1490 10 + 500 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 60 + 460 1460 10 + 470 1470 10 + 480 1480 10 + 490 1490 10 + 500 ENOENT 0 + +Test 12 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 400 1400 25 + 425 1425 25 + 450 1450 25 + 475 1475 25 + 500 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 125 + 425 1425 25 + 450 1450 25 + 475 1475 25 + 500 ENOENT 0 + +Test 13 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 ENOENT 0 + +src: + VCN LCN len + 160 1160 25 + 185 1185 25 + 210 1210 25 + 235 1235 25 + 260 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 HOLE 60 + 160 1160 25 + 185 1185 25 + 210 1210 25 + 235 1235 25 + 260 ENOENT 0 + +Test 14 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 ENOENT 0 + +src: + VCN LCN len + 100 1100 35 + 135 1135 35 + 170 1170 35 + 205 1205 35 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 1000 135 + 135 1135 35 + 170 1170 35 + 205 1205 35 + 240 ENOENT 0 + +Test 15 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 ENOENT 0 + +src: + VCN LCN len + 200 1200 10 + 210 1210 10 + 220 1220 10 + 230 1230 10 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 1200 10 + 210 1210 10 + 220 1220 10 + 230 1230 10 + 240 ENOENT 0 + +Test 16 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 ENOENT 0 + +src: + VCN LCN len + 240 1240 10 + 250 1250 10 + 260 1260 10 + 270 1270 10 + 280 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 NOTMAP 40 + 240 1240 10 + 250 1250 10 + 260 1260 10 + 270 1270 10 + 280 ENOENT 0 + +Test 17 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 ENOENT 0 + +src: + VCN LCN len + 100 1100 10 + 110 1110 10 + 120 1120 10 + 130 1130 10 + 140 NOTMAP 0 + +res: + VCN LCN len + 0 1000 110 + 110 1110 10 + 120 1120 10 + 130 1130 10 + 140 ENOENT 0 + +Test 18 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 ENOENT 0 + +src: + VCN LCN len + 140 1140 10 + 150 1150 10 + 160 1160 10 + 170 1170 10 + 180 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 NOTMAP 40 + 140 1140 10 + 150 1150 10 + 160 1160 10 + 170 1170 10 + 180 ENOENT 0 + +Test 19 ---------- +dst: + VCN LCN len + 0 ENOENT 0 + +src: + VCN LCN len + 0 1000 10 + 10 1010 10 + 20 1020 10 + 30 1030 10 + 40 NOTMAP 0 + +res: + VCN LCN len + 0 1000 10 + 10 1010 10 + 20 1020 10 + 30 1030 10 + 40 ENOENT 0 + +Test 20 ---------- +dst: + VCN LCN len + 0 ENOENT 0 + +src: + VCN LCN len + 40 1040 10 + 50 1050 10 + 60 1060 10 + 70 1070 10 + 80 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 40 + 40 1040 10 + 50 1050 10 + 60 1060 10 + 70 1070 10 + 80 ENOENT 0 + +Test 21 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 0 1000 10 + 10 1010 10 + 20 1020 10 + 30 1030 10 + 40 NOTMAP 0 + +res: + VCN LCN len + 0 1000 10 + 10 1010 10 + 20 1020 10 + 30 1030 10 + 40 NOTMAP 60 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 22 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 40 1040 10 + 50 1050 10 + 60 1060 10 + 70 1070 10 + 80 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 40 + 40 1040 10 + 50 1050 10 + 60 1060 10 + 70 1070 10 + 80 NOTMAP 20 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 23 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 60 1060 10 + 70 1070 10 + 80 1080 10 + 90 1090 10 + 100 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 60 + 60 1060 10 + 70 1070 10 + 80 1080 10 + 90 1090 110 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 24 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 0 1000 25 + 25 1025 25 + 50 1050 25 + 75 1075 25 + 100 NOTMAP 0 + +res: + VCN LCN len + 0 1000 25 + 25 1025 25 + 50 1050 25 + 75 1075 125 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 25 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 200 1200 10 + 210 1210 10 + 220 1220 10 + 230 1230 10 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 110 + 210 1210 10 + 220 1220 10 + 230 1230 10 + 240 NOTMAP 60 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 26 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 240 1240 10 + 250 1250 10 + 260 1260 10 + 270 1270 10 + 280 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 40 + 240 1240 10 + 250 1250 10 + 260 1260 10 + 270 1270 10 + 280 NOTMAP 20 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 27 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 260 1260 10 + 270 1270 10 + 280 1280 10 + 290 1290 10 + 300 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 60 + 260 1260 10 + 270 1270 10 + 280 1280 10 + 290 1290 110 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 28 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 200 1200 25 + 225 1225 25 + 250 1250 25 + 275 1275 25 + 300 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 125 + 225 1225 25 + 250 1250 25 + 275 1275 125 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 29 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 400 1400 10 + 410 1410 10 + 420 1420 10 + 430 1430 10 + 440 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 110 + 410 1410 10 + 420 1420 10 + 430 1430 10 + 440 NOTMAP 60 + 500 ENOENT 0 + +Test 30 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 440 1440 10 + 450 1450 10 + 460 1460 10 + 470 1470 10 + 480 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 40 + 440 1440 10 + 450 1450 10 + 460 1460 10 + 470 1470 10 + 480 NOTMAP 20 + 500 ENOENT 0 + +Test 31 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 460 1460 10 + 470 1470 10 + 480 1480 10 + 490 1490 10 + 500 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 60 + 460 1460 10 + 470 1470 10 + 480 1480 10 + 490 1490 10 + 500 ENOENT 0 + +Test 32 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 400 1400 25 + 425 1425 25 + 450 1450 25 + 475 1475 25 + 500 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 125 + 425 1425 25 + 450 1450 25 + 475 1475 25 + 500 ENOENT 0 + +Test 33 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 NOTMAP 100 + 200 ENOENT 0 + +src: + VCN LCN len + 160 1160 25 + 185 1185 25 + 210 1210 25 + 235 1235 25 + 260 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 NOTMAP 60 + 160 1160 25 + 185 1185 25 + 210 1210 25 + 235 1235 25 + 260 ENOENT 0 + +Test 34 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 NOTMAP 100 + 200 ENOENT 0 + +src: + VCN LCN len + 100 1100 35 + 135 1135 35 + 170 1170 35 + 205 1205 35 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 1000 135 + 135 1135 35 + 170 1170 35 + 205 1205 35 + 240 ENOENT 0 + diff --git a/test/runlist-data/pure-cs b/test/runlist-data/pure-cs new file mode 100644 index 00000000..2eb4ddbf --- /dev/null +++ b/test/runlist-data/pure-cs @@ -0,0 +1,760 @@ +Test 1 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 0 1000 40 + 40 NOTMAP 0 + +res: + VCN LCN len + 0 1000 40 + 40 HOLE 60 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 2 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 40 1040 40 + 80 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 40 + 40 1040 40 + 80 HOLE 20 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 3 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 60 1060 40 + 100 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 60 + 60 1060 140 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 4 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 0 1000 100 + 100 NOTMAP 0 + +res: + VCN LCN len + 0 1000 200 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 5 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 200 1200 40 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 140 + 240 HOLE 60 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 6 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 240 1240 40 + 280 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 40 + 240 1240 40 + 280 HOLE 20 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 7 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 260 1260 40 + 300 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 60 + 260 1260 140 + 400 HOLE 100 + 500 ENOENT 0 + +Test 8 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 200 1200 100 + 300 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 300 + 400 HOLE 100 + 500 ENOENT 0 + +Test 9 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 400 1400 40 + 440 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 140 + 440 HOLE 60 + 500 ENOENT 0 + +Test 10 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 440 1440 40 + 480 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 40 + 440 1440 40 + 480 HOLE 20 + 500 ENOENT 0 + +Test 11 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 460 1460 40 + 500 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 60 + 460 1460 40 + 500 ENOENT 0 + +Test 12 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 400 1400 100 + 500 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 200 + 500 ENOENT 0 + +Test 13 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 ENOENT 0 + +src: + VCN LCN len + 160 1160 100 + 260 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 HOLE 60 + 160 1160 100 + 260 ENOENT 0 + +Test 14 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 ENOENT 0 + +src: + VCN LCN len + 100 1100 140 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 1000 240 + 240 ENOENT 0 + +Test 15 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 ENOENT 0 + +src: + VCN LCN len + 200 1200 40 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 1200 40 + 240 ENOENT 0 + +Test 16 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 ENOENT 0 + +src: + VCN LCN len + 240 1240 40 + 280 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 NOTMAP 40 + 240 1240 40 + 280 ENOENT 0 + +Test 17 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 ENOENT 0 + +src: + VCN LCN len + 100 1100 40 + 140 NOTMAP 0 + +res: + VCN LCN len + 0 1000 140 + 140 ENOENT 0 + +Test 18 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 ENOENT 0 + +src: + VCN LCN len + 140 1140 40 + 180 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 NOTMAP 40 + 140 1140 40 + 180 ENOENT 0 + +Test 19 ---------- +dst: + VCN LCN len + 0 ENOENT 0 + +src: + VCN LCN len + 0 1000 40 + 40 NOTMAP 0 + +res: + VCN LCN len + 0 1000 40 + 40 ENOENT 0 + +Test 20 ---------- +dst: + VCN LCN len + 0 ENOENT 0 + +src: + VCN LCN len + 40 1040 40 + 80 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 40 + 40 1040 40 + 80 ENOENT 0 + +Test 21 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 0 1000 40 + 40 NOTMAP 0 + +res: + VCN LCN len + 0 1000 40 + 40 NOTMAP 60 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 22 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 40 1040 40 + 80 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 40 + 40 1040 40 + 80 NOTMAP 20 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 23 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 60 1060 40 + 100 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 60 + 60 1060 140 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 24 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 0 1000 100 + 100 NOTMAP 0 + +res: + VCN LCN len + 0 1000 200 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 25 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 200 1200 40 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 140 + 240 NOTMAP 60 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 26 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 240 1240 40 + 280 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 40 + 240 1240 40 + 280 NOTMAP 20 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 27 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 260 1260 40 + 300 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 60 + 260 1260 140 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 28 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 200 1200 100 + 300 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 300 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 29 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 400 1400 40 + 440 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 140 + 440 NOTMAP 60 + 500 ENOENT 0 + +Test 30 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 440 1440 40 + 480 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 40 + 440 1440 40 + 480 NOTMAP 20 + 500 ENOENT 0 + +Test 31 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 460 1460 40 + 500 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 60 + 460 1460 40 + 500 ENOENT 0 + +Test 32 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 400 1400 100 + 500 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 200 + 500 ENOENT 0 + +Test 33 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 NOTMAP 100 + 200 ENOENT 0 + +src: + VCN LCN len + 160 1160 100 + 260 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 NOTMAP 60 + 160 1160 100 + 260 ENOENT 0 + +Test 34 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 NOTMAP 100 + 200 ENOENT 0 + +src: + VCN LCN len + 100 1100 140 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 1000 240 + 240 ENOENT 0 + diff --git a/test/runlist-data/pure-nm b/test/runlist-data/pure-nm new file mode 100644 index 00000000..ac6324b0 --- /dev/null +++ b/test/runlist-data/pure-nm @@ -0,0 +1,983 @@ +Test 1 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 0 1999 10 + 10 2009 10 + 20 2019 10 + 30 2029 10 + 40 NOTMAP 0 + +res: + VCN LCN len + 0 1999 10 + 10 2009 10 + 20 2019 10 + 30 2029 10 + 40 HOLE 60 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 2 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 40 2039 10 + 50 2049 10 + 60 2059 10 + 70 2069 10 + 80 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 40 + 40 2039 10 + 50 2049 10 + 60 2059 10 + 70 2069 10 + 80 HOLE 20 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 3 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 60 2059 10 + 70 2069 10 + 80 2079 10 + 90 2089 10 + 100 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 60 + 60 2059 10 + 70 2069 10 + 80 2079 10 + 90 2089 10 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 4 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 0 1999 25 + 25 2024 25 + 50 2049 25 + 75 2074 25 + 100 NOTMAP 0 + +res: + VCN LCN len + 0 1999 25 + 25 2024 25 + 50 2049 25 + 75 2074 25 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 5 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 200 2199 10 + 210 2209 10 + 220 2219 10 + 230 2229 10 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 2199 10 + 210 2209 10 + 220 2219 10 + 230 2229 10 + 240 HOLE 60 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 6 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 240 2239 10 + 250 2249 10 + 260 2259 10 + 270 2269 10 + 280 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 40 + 240 2239 10 + 250 2249 10 + 260 2259 10 + 270 2269 10 + 280 HOLE 20 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 7 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 260 2259 10 + 270 2269 10 + 280 2279 10 + 290 2289 10 + 300 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 60 + 260 2259 10 + 270 2269 10 + 280 2279 10 + 290 2289 10 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 8 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 200 2199 25 + 225 2224 25 + 250 2249 25 + 275 2274 25 + 300 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 2199 25 + 225 2224 25 + 250 2249 25 + 275 2274 25 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 9 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 400 2399 10 + 410 2409 10 + 420 2419 10 + 430 2429 10 + 440 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 2399 10 + 410 2409 10 + 420 2419 10 + 430 2429 10 + 440 HOLE 60 + 500 ENOENT 0 + +Test 10 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 440 2439 10 + 450 2449 10 + 460 2459 10 + 470 2469 10 + 480 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 40 + 440 2439 10 + 450 2449 10 + 460 2459 10 + 470 2469 10 + 480 HOLE 20 + 500 ENOENT 0 + +Test 11 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 460 2459 10 + 470 2469 10 + 480 2479 10 + 490 2489 10 + 500 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 60 + 460 2459 10 + 470 2469 10 + 480 2479 10 + 490 2489 10 + 500 ENOENT 0 + +Test 12 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 400 2399 25 + 425 2424 25 + 450 2449 25 + 475 2474 25 + 500 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 2399 25 + 425 2424 25 + 450 2449 25 + 475 2474 25 + 500 ENOENT 0 + +Test 13 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 ENOENT 0 + +src: + VCN LCN len + 160 2159 25 + 185 2184 25 + 210 2209 25 + 235 2234 25 + 260 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 HOLE 60 + 160 2159 25 + 185 2184 25 + 210 2209 25 + 235 2234 25 + 260 ENOENT 0 + +Test 14 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 ENOENT 0 + +src: + VCN LCN len + 100 2099 35 + 135 2134 35 + 170 2169 35 + 205 2204 35 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 2099 35 + 135 2134 35 + 170 2169 35 + 205 2204 35 + 240 ENOENT 0 + +Test 15 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 ENOENT 0 + +src: + VCN LCN len + 200 2199 10 + 210 2209 10 + 220 2219 10 + 230 2229 10 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 2199 10 + 210 2209 10 + 220 2219 10 + 230 2229 10 + 240 ENOENT 0 + +Test 16 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 ENOENT 0 + +src: + VCN LCN len + 240 2239 10 + 250 2249 10 + 260 2259 10 + 270 2269 10 + 280 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 NOTMAP 40 + 240 2239 10 + 250 2249 10 + 260 2259 10 + 270 2269 10 + 280 ENOENT 0 + +Test 17 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 ENOENT 0 + +src: + VCN LCN len + 100 2099 10 + 110 2109 10 + 120 2119 10 + 130 2129 10 + 140 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 2099 10 + 110 2109 10 + 120 2119 10 + 130 2129 10 + 140 ENOENT 0 + +Test 18 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 ENOENT 0 + +src: + VCN LCN len + 140 2139 10 + 150 2149 10 + 160 2159 10 + 170 2169 10 + 180 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 NOTMAP 40 + 140 2139 10 + 150 2149 10 + 160 2159 10 + 170 2169 10 + 180 ENOENT 0 + +Test 19 ---------- +dst: + VCN LCN len + 0 ENOENT 0 + +src: + VCN LCN len + 0 1999 10 + 10 2009 10 + 20 2019 10 + 30 2029 10 + 40 NOTMAP 0 + +res: + VCN LCN len + 0 1999 10 + 10 2009 10 + 20 2019 10 + 30 2029 10 + 40 ENOENT 0 + +Test 20 ---------- +dst: + VCN LCN len + 0 ENOENT 0 + +src: + VCN LCN len + 40 2039 10 + 50 2049 10 + 60 2059 10 + 70 2069 10 + 80 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 40 + 40 2039 10 + 50 2049 10 + 60 2059 10 + 70 2069 10 + 80 ENOENT 0 + +Test 21 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 0 1999 10 + 10 2009 10 + 20 2019 10 + 30 2029 10 + 40 NOTMAP 0 + +res: + VCN LCN len + 0 1999 10 + 10 2009 10 + 20 2019 10 + 30 2029 10 + 40 NOTMAP 60 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 22 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 40 2039 10 + 50 2049 10 + 60 2059 10 + 70 2069 10 + 80 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 40 + 40 2039 10 + 50 2049 10 + 60 2059 10 + 70 2069 10 + 80 NOTMAP 20 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 23 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 60 2059 10 + 70 2069 10 + 80 2079 10 + 90 2089 10 + 100 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 60 + 60 2059 10 + 70 2069 10 + 80 2079 10 + 90 2089 10 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 24 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 0 1999 25 + 25 2024 25 + 50 2049 25 + 75 2074 25 + 100 NOTMAP 0 + +res: + VCN LCN len + 0 1999 25 + 25 2024 25 + 50 2049 25 + 75 2074 25 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 25 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 200 2199 10 + 210 2209 10 + 220 2219 10 + 230 2229 10 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 2199 10 + 210 2209 10 + 220 2219 10 + 230 2229 10 + 240 NOTMAP 60 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 26 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 240 2239 10 + 250 2249 10 + 260 2259 10 + 270 2269 10 + 280 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 40 + 240 2239 10 + 250 2249 10 + 260 2259 10 + 270 2269 10 + 280 NOTMAP 20 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 27 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 260 2259 10 + 270 2269 10 + 280 2279 10 + 290 2289 10 + 300 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 60 + 260 2259 10 + 270 2269 10 + 280 2279 10 + 290 2289 10 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 28 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 200 2199 25 + 225 2224 25 + 250 2249 25 + 275 2274 25 + 300 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 2199 25 + 225 2224 25 + 250 2249 25 + 275 2274 25 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 29 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 400 2399 10 + 410 2409 10 + 420 2419 10 + 430 2429 10 + 440 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 2399 10 + 410 2409 10 + 420 2419 10 + 430 2429 10 + 440 NOTMAP 60 + 500 ENOENT 0 + +Test 30 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 440 2439 10 + 450 2449 10 + 460 2459 10 + 470 2469 10 + 480 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 40 + 440 2439 10 + 450 2449 10 + 460 2459 10 + 470 2469 10 + 480 NOTMAP 20 + 500 ENOENT 0 + +Test 31 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 460 2459 10 + 470 2469 10 + 480 2479 10 + 490 2489 10 + 500 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 60 + 460 2459 10 + 470 2469 10 + 480 2479 10 + 490 2489 10 + 500 ENOENT 0 + +Test 32 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 400 2399 25 + 425 2424 25 + 450 2449 25 + 475 2474 25 + 500 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 2399 25 + 425 2424 25 + 450 2449 25 + 475 2474 25 + 500 ENOENT 0 + +Test 33 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 NOTMAP 100 + 200 ENOENT 0 + +src: + VCN LCN len + 160 2159 25 + 185 2184 25 + 210 2209 25 + 235 2234 25 + 260 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 NOTMAP 60 + 160 2159 25 + 185 2184 25 + 210 2209 25 + 235 2234 25 + 260 ENOENT 0 + +Test 34 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 NOTMAP 100 + 200 ENOENT 0 + +src: + VCN LCN len + 100 2099 35 + 135 2134 35 + 170 2169 35 + 205 2204 35 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 2099 35 + 135 2134 35 + 170 2169 35 + 205 2204 35 + 240 ENOENT 0 + diff --git a/test/runlist-data/pure-ns b/test/runlist-data/pure-ns new file mode 100644 index 00000000..3cd96283 --- /dev/null +++ b/test/runlist-data/pure-ns @@ -0,0 +1,779 @@ +Test 1 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 0 1999 40 + 40 NOTMAP 0 + +res: + VCN LCN len + 0 1999 40 + 40 HOLE 60 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 2 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 40 2039 40 + 80 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 40 + 40 2039 40 + 80 HOLE 20 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 3 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 60 2059 40 + 100 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 60 + 60 2059 40 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 4 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 0 1999 100 + 100 NOTMAP 0 + +res: + VCN LCN len + 0 1999 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 5 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 200 2199 40 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 2199 40 + 240 HOLE 60 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 6 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 240 2239 40 + 280 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 40 + 240 2239 40 + 280 HOLE 20 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 7 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 260 2259 40 + 300 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 60 + 260 2259 40 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 8 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 200 2199 100 + 300 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 2199 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +Test 9 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 400 2399 40 + 440 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 2399 40 + 440 HOLE 60 + 500 ENOENT 0 + +Test 10 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 440 2439 40 + 480 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 40 + 440 2439 40 + 480 HOLE 20 + 500 ENOENT 0 + +Test 11 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 460 2459 40 + 500 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 60 + 460 2459 40 + 500 ENOENT 0 + +Test 12 ---------- +dst: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 HOLE 100 + 500 ENOENT 0 + +src: + VCN LCN len + 400 2399 100 + 500 NOTMAP 0 + +res: + VCN LCN len + 0 HOLE 100 + 100 1100 100 + 200 HOLE 100 + 300 1300 100 + 400 2399 100 + 500 ENOENT 0 + +Test 13 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 ENOENT 0 + +src: + VCN LCN len + 160 2159 100 + 260 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 HOLE 60 + 160 2159 100 + 260 ENOENT 0 + +Test 14 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 ENOENT 0 + +src: + VCN LCN len + 100 2099 140 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 2099 140 + 240 ENOENT 0 + +Test 15 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 ENOENT 0 + +src: + VCN LCN len + 200 2199 40 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 2199 40 + 240 ENOENT 0 + +Test 16 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 ENOENT 0 + +src: + VCN LCN len + 240 2239 40 + 280 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 HOLE 100 + 200 NOTMAP 40 + 240 2239 40 + 280 ENOENT 0 + +Test 17 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 ENOENT 0 + +src: + VCN LCN len + 100 2099 40 + 140 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 2099 40 + 140 ENOENT 0 + +Test 18 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 ENOENT 0 + +src: + VCN LCN len + 140 2139 40 + 180 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 NOTMAP 40 + 140 2139 40 + 180 ENOENT 0 + +Test 19 ---------- +dst: + VCN LCN len + 0 ENOENT 0 + +src: + VCN LCN len + 0 1999 40 + 40 NOTMAP 0 + +res: + VCN LCN len + 0 1999 40 + 40 ENOENT 0 + +Test 20 ---------- +dst: + VCN LCN len + 0 ENOENT 0 + +src: + VCN LCN len + 40 2039 40 + 80 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 40 + 40 2039 40 + 80 ENOENT 0 + +Test 21 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 0 1999 40 + 40 NOTMAP 0 + +res: + VCN LCN len + 0 1999 40 + 40 NOTMAP 60 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 22 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 40 2039 40 + 80 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 40 + 40 2039 40 + 80 NOTMAP 20 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 23 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 60 2059 40 + 100 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 60 + 60 2059 40 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 24 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 0 1999 100 + 100 NOTMAP 0 + +res: + VCN LCN len + 0 1999 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 25 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 200 2199 40 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 2199 40 + 240 NOTMAP 60 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 26 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 240 2239 40 + 280 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 40 + 240 2239 40 + 280 NOTMAP 20 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 27 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 260 2259 40 + 300 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 60 + 260 2259 40 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 28 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 200 2199 100 + 300 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 2199 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +Test 29 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 400 2399 40 + 440 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 2399 40 + 440 NOTMAP 60 + 500 ENOENT 0 + +Test 30 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 440 2439 40 + 480 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 40 + 440 2439 40 + 480 NOTMAP 20 + 500 ENOENT 0 + +Test 31 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 460 2459 40 + 500 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 60 + 460 2459 40 + 500 ENOENT 0 + +Test 32 ---------- +dst: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 NOTMAP 100 + 500 ENOENT 0 + +src: + VCN LCN len + 400 2399 100 + 500 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 100 + 100 1100 100 + 200 NOTMAP 100 + 300 1300 100 + 400 2399 100 + 500 ENOENT 0 + +Test 33 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 NOTMAP 100 + 200 ENOENT 0 + +src: + VCN LCN len + 160 2159 100 + 260 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 NOTMAP 60 + 160 2159 100 + 260 ENOENT 0 + +Test 34 ---------- +dst: + VCN LCN len + 0 1000 100 + 100 NOTMAP 100 + 200 ENOENT 0 + +src: + VCN LCN len + 100 2099 140 + 240 NOTMAP 0 + +res: + VCN LCN len + 0 1000 100 + 100 2099 140 + 240 ENOENT 0 + diff --git a/test/runlist-data/zero b/test/runlist-data/zero new file mode 100644 index 00000000..63ac7200 --- /dev/null +++ b/test/runlist-data/zero @@ -0,0 +1,13 @@ +dst: + Run list not present. +src: + VCN LCN len + 10 99 5 + 15 NOTMAP 0 + +res: + VCN LCN len + 0 NOTMAP 10 + 10 99 5 + 15 NOTMAP 0 + diff --git a/test/runlist.c b/test/runlist.c new file mode 100644 index 00000000..330751d0 --- /dev/null +++ b/test/runlist.c @@ -0,0 +1,7 @@ +#include +#include "runlist.h" + +int main (int argc, char *argv[]) +{ + return test_rl_main (argc, argv); +}