commit
						ba63b7daca
					
				|  | @ -0,0 +1,38 @@ | |||
| The following people have contributed directly or indirectly  | ||||
| to the ntfs-3g project. | ||||
| 
 | ||||
| Please let ntfs-3g-devel@lists.sf.net know if you believe | ||||
| someone is missing, or if you prefer not to be listed. | ||||
| 
 | ||||
| Dominique L Bouix | ||||
| Gergely Erdelyi | ||||
| Anton Altaparmakov  | ||||
| Peter Boross | ||||
| Don Bright | ||||
| Mario Emmenlauer  | ||||
| Yuval Fledel  | ||||
| Kano from Kanotix | ||||
| Roland Kletzing | ||||
| Maarten Lankhorst | ||||
| Gergely Madarasz | ||||
| Patrick McLean | ||||
| Florent Mertens | ||||
| Yura Pakhuchiy  | ||||
| Miklos Szeredi | ||||
| Bartosz Taudul | ||||
| Zhanglinbao | ||||
| Wade Fitzpatrick | ||||
| Carsten Einig | ||||
| Adam Cecile | ||||
| Bruno Damour | ||||
| Ales Fruman | ||||
| Curt McDowell | ||||
| Thomas Franken | ||||
| Jonatan Lambert | ||||
| Klaus Knopper | ||||
| Zhanglinbao | ||||
| Ismail Donmez | ||||
| Laszlo Dvornik | ||||
| Pallaghy Ajtony | ||||
| Szabolcs Szakacsits  | ||||
| 
 | ||||
|  | @ -0,0 +1,17 @@ | |||
| 
 | ||||
| SUBDIRS = include libntfs-3g src | ||||
| 
 | ||||
| EXTRA_DIST = AUTHORS CREDITS COPYING INSTALL NEWS README autogen.sh   | ||||
| AUTOMAKE_OPTIONS = gnu | ||||
| 
 | ||||
| MAINTAINERCLEANFILES = configure Makefile.in aclocal.m4 compile depcomp \
 | ||||
| 	install-sh ltmain.sh missing config.guess config.sub config.h.in INSTALL | ||||
| 
 | ||||
| libtool: $(LIBTOOL_DEPS) | ||||
| 	$(SHELL) ./config.status --recheck | ||||
| 
 | ||||
| strip: | ||||
| 	(cd src && $(MAKE) strip) || exit 1; | ||||
| 
 | ||||
| libs: | ||||
| 	(cd libntfs-3g && $(MAKE) libs) || exit 1; | ||||
|  | @ -0,0 +1,63 @@ | |||
| 
 | ||||
| INTRODUCTION | ||||
| ============ | ||||
| 
 | ||||
| The ntfs-3g driver is an open source, freely available read/write NTFS | ||||
| driver, which provides safe and fast handling of the Windows XP, Windows  | ||||
| Server 2003 and Windows 2000 filesystems. Almost the full POSIX filesystem  | ||||
| functionality is supported, the major exceptions are changing the file  | ||||
| ownerships and the access rights. | ||||
| 
 | ||||
| The purpose of the project is to develop, continuously quality test and | ||||
| support a trustable, featureful and high performance solution for hardware | ||||
| platforms and operating systems whose users need to reliably interoperate | ||||
| with NTFS. Besides this practical goal, the project also aims to explore | ||||
| the limits of the hybrid, kernel/user space filesystem driver approach, | ||||
| performance, reliability and feature richness per invested effort wise. | ||||
| 
 | ||||
| The driver currently is in BETA status, which means that we weren't | ||||
| reported and haven't found any data corruption or loss during ordinary | ||||
| driver use and in our extensive quality testing before release of the | ||||
| latest version of ntfs-3g, however we are aware of some usability issues | ||||
| and driver restrictions which are all documented and planned to be resolved | ||||
| in the future. | ||||
| 
 | ||||
| You can find news, technical answers, problem submission instructions,  | ||||
| performance evaluation and other informations on the project web site:  | ||||
| 
 | ||||
| 			http://www.ntfs-3g.org | ||||
| 
 | ||||
| 
 | ||||
| QUICK INSTALLATION | ||||
| ================== | ||||
| 	 | ||||
| Make sure you have the basic Linux development tools and the full FUSE  | ||||
| package (http://fuse.sourceforge.net) is already installed correctly on  | ||||
| the computer. Then type:   | ||||
| 
 | ||||
| 	./configure | ||||
| 	make | ||||
| 	make install      # or 'sudo make install' if you aren't root. | ||||
| 
 | ||||
| 
 | ||||
| USAGE | ||||
| ===== | ||||
| 
 | ||||
| If there was no error during installation then the NTFS volume can be | ||||
| read-write mounted for everybody the following way (unmount the volume if  | ||||
| it was already mounted, and replace /dev/hda1 and /mnt/windows, if needed): | ||||
| 
 | ||||
|     ntfs-3g /dev/hda1 /mnt/windows | ||||
| 
 | ||||
| You may also need to set the 'locale' option to make all files with national  | ||||
| characters visible. Replace the below hu_HU.utf8 with the appropriate setting.  | ||||
| 
 | ||||
|     ntfs-3g /dev/hda1 /mnt/windows -o locale=hu_HU.utf8 | ||||
| 
 | ||||
| Please see the ntfs-3g manual page for more options and examples. | ||||
| 
 | ||||
| You can also make NTFS to be mounted during boot by putting the below  | ||||
| line at the end of the /etc/fstab file: | ||||
| 
 | ||||
|     /dev/hda1 /mnt/windows ntfs-3g defaults 0 0 | ||||
| 
 | ||||
|  | @ -0,0 +1,22 @@ | |||
| #!/bin/bash | ||||
| # Run this to generate configure, Makefile.in's, etc | ||||
| 
 | ||||
| (autoreconf --version) < /dev/null > /dev/null 2>&1 || { | ||||
|   (autoconf --version) < /dev/null > /dev/null 2>&1 || { | ||||
|     echo | ||||
|     echo "**Error**: You must have the GNU Build System (autoconf, automake, " | ||||
|     echo "libtool, etc) to update the ntfs-3g build system.  Download the " | ||||
|     echo "appropriate packages for your distribution, or get the source " | ||||
|     echo "tar balls from ftp://ftp.gnu.org/pub/gnu/." | ||||
|     exit 1 | ||||
|   } | ||||
|   echo | ||||
|   echo "**Error**: Your version of autoconf is too old (you need at least 2.57)" | ||||
|   echo "to update the ntfs-3g build system.  Download the appropriate " | ||||
|   echo "updated package for your distribution, or get the source tar ball " | ||||
|   echo "from ftp://ftp.gnu.org/pub/gnu/." | ||||
|   exit 1 | ||||
| } | ||||
| 
 | ||||
| echo Running autoreconf --verbose --install | ||||
| autoreconf --force --verbose --install  | ||||
|  | @ -0,0 +1,169 @@ | |||
| # | ||||
| # configure.ac - Source file to generate "./configure" to prepare package for | ||||
| #		 compilation. | ||||
| # | ||||
| # Copyright (c) 2000-2006 Anton Altaparmakov | ||||
| # Copyright (c) 2003 Jan Kratochvil | ||||
| # Copyright (c) 2005-2006 Szabolcs Szakacsits | ||||
| # | ||||
| # 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 NTFS-3G | ||||
| # distribution in the file COPYING); if not, write to the Free Software | ||||
| # Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
| # | ||||
| 
 | ||||
| AC_PREREQ(2.59) | ||||
| AC_INIT([ntfs-3g],[0.20061031-BETA],[ntfs-3g-devel@lists.sf.net]) | ||||
| 
 | ||||
| AC_CANONICAL_HOST([]) | ||||
| AC_CANONICAL_TARGET([]) | ||||
| AC_CONFIG_SRCDIR([config.h.in]) | ||||
| AC_CONFIG_HEADERS([config.h]) | ||||
| AM_INIT_AUTOMAKE | ||||
| AM_MAINTAINER_MODE | ||||
| AM_ENABLE_SHARED | ||||
| AM_ENABLE_STATIC | ||||
| 
 | ||||
| AC_PREFIX_DEFAULT(/usr/local) | ||||
| if test "x$prefix" = "xNONE"; then | ||||
| 	prefix=$ac_default_prefix | ||||
| 	ac_configure_args="$ac_configure_args --prefix $prefix" | ||||
| fi | ||||
| 
 | ||||
| # Command-line options. | ||||
| AC_ARG_ENABLE(debug, | ||||
| 	AS_HELP_STRING(--enable-debug,enable additional debugging code and | ||||
| 		       output), , | ||||
| 	enable_debug=no | ||||
| ) | ||||
| 
 | ||||
| AC_ARG_ENABLE(really-static, | ||||
| 	AS_HELP_STRING(--enable-really-static,create static binaries | ||||
| 		       for the utilities), , | ||||
| 	enable_really_static=no | ||||
| ) | ||||
| AM_CONDITIONAL(REALLYSTATIC, test "$enable_really_static" = yes) | ||||
| 
 | ||||
| AC_ARG_ENABLE(warnings, | ||||
| 	AS_HELP_STRING(--enable-warnings,enable additional compiler warnings), , | ||||
| 	enable_warnings=no | ||||
| ) | ||||
| 
 | ||||
| # Use GNU extensions if available. | ||||
| AC_GNU_SOURCE | ||||
| 
 | ||||
| # Checks for programs. | ||||
| AC_PROG_CC | ||||
| AC_PROG_GCC_TRADITIONAL | ||||
| AC_PROG_INSTALL | ||||
| AC_PATH_PROG(RM, rm, rm) | ||||
| AC_PROG_LN_S | ||||
| AC_PROG_MAKE_SET | ||||
| AC_PROG_LIBTOOL | ||||
| 
 | ||||
| # Libraries often install their metadata .pc files in directories not searched | ||||
| # by pkg-config. Let's workaround this.  | ||||
| export PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:/usr/lib/pkgconfig:/opt/gnome/lib/pkgconfig:/usr/share/pkgconfig:/usr/local/lib/pkgconfig:$prefix/lib/pkgconfig:/opt/gnome/share/pkgconfig:/usr/local/share/pkgconfig | ||||
| 
 | ||||
| # Enable large file support. | ||||
| AC_SYS_LARGEFILE | ||||
| 
 | ||||
| case "$target_os" in | ||||
| linux*) | ||||
| 	PKG_CHECK_MODULES(FUSE_MODULE, fuse >= 2.5.0, [ compile_fuse_module=true ], | ||||
| 	[ | ||||
| 		AC_MSG_ERROR([ntfs-3g requires FUSE >= 2.5.0. Please see http://fuse.sf.net/ or install __all__ the precompiled fuse packages.]) | ||||
| 	]);; | ||||
| *) | ||||
| 		AC_MSG_ERROR([ntfs-3g can be built only under Linux.]) | ||||
| 	;; | ||||
| esac | ||||
| 
 | ||||
| # 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) | ||||
| 
 | ||||
| # Add our compiler switches not discarding 'CFLAGS' as they may have been | ||||
| # passed to us by rpmbuild(8). | ||||
| # We add -Wall to enable some compiler warnings. | ||||
| CFLAGS="$CFLAGS -Wall" | ||||
| 
 | ||||
| # Add lots of extra warnings if --enable-warnings was specified. | ||||
| if test "$enable_warnings" = "yes"; then | ||||
| 	CFLAGS="$CFLAGS -W -Wall -Waggregate-return -Wbad-function-cast -Wcast-align -Wcast-qual -Wdisabled-optimization -Wdiv-by-zero -Wfloat-equal -Winline -Wmissing-declarations -Wmissing-format-attribute -Wmissing-noreturn -Wmissing-prototypes -Wmultichar -Wnested-externs -Wpointer-arith -Wredundant-decls -Wshadow -Wsign-compare -Wstrict-prototypes -Wundef -Wwrite-strings" | ||||
| fi | ||||
| 
 | ||||
| # Add debugging switches if --enable-debug was specified. | ||||
| if test "$enable_debug" = "yes"; then | ||||
| 	CFLAGS="$CFLAGS -ggdb3 -DDEBUG" | ||||
| fi | ||||
| 
 | ||||
| AC_SUBST(CFLAGS) | ||||
| AC_SUBST(CPPFLAGS) | ||||
| AC_SUBST(LDFLAGS) | ||||
| AC_SUBST(LIBS) | ||||
| 
 | ||||
| AC_SUBST(LIBNTFS_3G_CFLAGS) | ||||
| 
 | ||||
| AC_SUBST(AUTODIRS) | ||||
| 
 | ||||
| # Checks for libraries. | ||||
| 
 | ||||
| # Checks for header files. | ||||
| AC_HEADER_STDC | ||||
| AC_CHECK_HEADERS([ctype.h fcntl.h libgen.h libintl.h limits.h locale.h \ | ||||
| 	mntent.h stddef.h stdint.h stdlib.h stdio.h stdarg.h string.h \ | ||||
| 	strings.h errno.h time.h unistd.h utime.h wchar.h getopt.h features.h \ | ||||
| 	endian.h byteswap.h sys/byteorder.h sys/endian.h sys/param.h \ | ||||
| 	sys/ioctl.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 gcrypt.h windows.h gnutls/pkcs12.h syslog.h]) | ||||
| 
 | ||||
| # Checks for typedefs, structures, and compiler characteristics. | ||||
| AC_HEADER_STDBOOL | ||||
| AC_C_BIGENDIAN(, | ||||
| 	[AC_DEFINE([WORDS_LITTLEENDIAN], 1, | ||||
| 	[Define to 1 if your processor stores words with the least significant | ||||
| 	byte first (like Intel and VAX, unlike Motorola and SPARC).])] | ||||
| 	,) | ||||
| AC_C_CONST | ||||
| AC_C_INLINE | ||||
| AC_TYPE_OFF_T | ||||
| AC_TYPE_SIZE_T | ||||
| AC_STRUCT_ST_BLOCKS | ||||
| AC_CHECK_MEMBERS([struct stat.st_rdev]) | ||||
| 
 | ||||
| # Checks for library functions. | ||||
| AC_FUNC_GETMNTENT | ||||
| AC_FUNC_MBRTOWC | ||||
| AC_FUNC_MEMCMP | ||||
| AC_FUNC_STAT | ||||
| AC_FUNC_STRFTIME | ||||
| AC_FUNC_UTIME_NULL | ||||
| AC_FUNC_VPRINTF | ||||
| AC_CHECK_FUNCS([atexit basename dup2 fdatasync getopt_long hasmntopt mbsinit \ | ||||
| 	memmove memset realpath regcomp setlocale setxattr strcasecmp strchr \ | ||||
| 	strdup strerror strnlen strtol strtoul sysconf utime]) | ||||
| 
 | ||||
| # Makefiles to be created by configure. | ||||
| AC_CONFIG_FILES([ | ||||
| 	Makefile | ||||
| 	include/Makefile | ||||
| 	include/ntfs-3g/Makefile | ||||
| 	libntfs-3g/Makefile | ||||
| 	src/Makefile | ||||
| 	src/ntfs-3g.8 | ||||
| ]) | ||||
| AC_OUTPUT | ||||
|  | @ -0,0 +1,3 @@ | |||
| SUBDIRS = ntfs-3g | ||||
| 
 | ||||
| MAINTAINERCLEANFILES = Makefile.in | ||||
|  | @ -0,0 +1,34 @@ | |||
| 
 | ||||
| linux_ntfsincludedir = $(includedir)/ntfs-3g | ||||
| linux_ntfsinclude_HEADERS = \
 | ||||
| 	attrib.h		\
 | ||||
| 	attrlist.h		\
 | ||||
| 	bitmap.h		\
 | ||||
| 	bootsect.h		\
 | ||||
| 	collate.h		\
 | ||||
| 	compat.h		\
 | ||||
| 	compress.h		\
 | ||||
| 	debug.h			\
 | ||||
| 	device.h		\
 | ||||
| 	device_io.h		\
 | ||||
| 	dir.h			\
 | ||||
| 	endians.h		\
 | ||||
| 	index.h			\
 | ||||
| 	inode.h			\
 | ||||
| 	layout.h		\
 | ||||
| 	lcnalloc.h		\
 | ||||
| 	list.h			\
 | ||||
| 	logfile.h		\
 | ||||
| 	logging.h		\
 | ||||
| 	mft.h			\
 | ||||
| 	mst.h			\
 | ||||
| 	ntfstime.h		\
 | ||||
| 	runlist.h		\
 | ||||
| 	security.h		\
 | ||||
| 	support.h		\
 | ||||
| 	types.h			\
 | ||||
| 	unistr.h		\
 | ||||
| 	version.h		\
 | ||||
| 	volume.h | ||||
| 
 | ||||
| MAINTAINERCLEANFILES = Makefile.in | ||||
|  | @ -0,0 +1,359 @@ | |||
| /*
 | ||||
|  * attrib.h - Exports for attribute handling. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2000-2004 Anton Altaparmakov | ||||
|  * Copyright (c) 2004-2005 Yura Pakhuchiy | ||||
|  * Copyright (c) 2006 Szabolcs Szakacsits | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_ATTRIB_H | ||||
| #define _NTFS_ATTRIB_H | ||||
| 
 | ||||
| /* Forward declarations */ | ||||
| typedef struct _ntfs_attr ntfs_attr; | ||||
| typedef struct _ntfs_attr_search_ctx ntfs_attr_search_ctx; | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "inode.h" | ||||
| #include "unistr.h" | ||||
| #include "runlist.h" | ||||
| #include "volume.h" | ||||
| #include "debug.h" | ||||
| #include "logging.h" | ||||
| 
 | ||||
| extern ntfschar AT_UNNAMED[]; | ||||
| 
 | ||||
| /**
 | ||||
|  * enum ntfs_lcn_special_values - special return values for ntfs_*_vcn_to_lcn() | ||||
|  * | ||||
|  * Special return values for ntfs_rl_vcn_to_lcn() and ntfs_attr_vcn_to_lcn(). | ||||
|  * | ||||
|  * TODO: Describe them. | ||||
|  */ | ||||
| typedef enum { | ||||
| 	LCN_HOLE		= -1,	/* Keep this as highest value or die! */ | ||||
| 	LCN_RL_NOT_MAPPED	= -2, | ||||
| 	LCN_ENOENT		= -3, | ||||
| 	LCN_EINVAL		= -4, | ||||
| 	LCN_EIO			= -5, | ||||
| } ntfs_lcn_special_values; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct ntfs_attr_search_ctx - search context used in attribute search functions | ||||
|  * @mrec:	buffer containing mft record to search | ||||
|  * @attr:	attribute record in @mrec where to begin/continue search | ||||
|  * @is_first:	if true lookup_attr() begins search with @attr, else after @attr | ||||
|  * | ||||
|  * Structure must be initialized to zero before the first call to one of the | ||||
|  * attribute search functions. Initialize @mrec to point to the mft record to | ||||
|  * search, and @attr to point to the first attribute within @mrec (not necessary | ||||
|  * if calling the _first() functions), and set @is_first to TRUE (not necessary | ||||
|  * if calling the _first() functions). | ||||
|  * | ||||
|  * If @is_first is TRUE, the search begins with @attr. If @is_first is FALSE, | ||||
|  * the search begins after @attr. This is so that, after the first call to one | ||||
|  * of the search attribute functions, we can call the function again, without | ||||
|  * any modification of the search context, to automagically get the next | ||||
|  * matching attribute. | ||||
|  */ | ||||
| struct _ntfs_attr_search_ctx { | ||||
| 	MFT_RECORD *mrec; | ||||
| 	ATTR_RECORD *attr; | ||||
| 	BOOL is_first; | ||||
| 	ntfs_inode *ntfs_ino; | ||||
| 	ATTR_LIST_ENTRY *al_entry; | ||||
| 	ntfs_inode *base_ntfs_ino; | ||||
| 	MFT_RECORD *base_mrec; | ||||
| 	ATTR_RECORD *base_attr; | ||||
| }; | ||||
| 
 | ||||
| extern void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx); | ||||
| extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, | ||||
| 		MFT_RECORD *mrec); | ||||
| extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx); | ||||
| 
 | ||||
| extern int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, | ||||
| 		const u32 name_len, const IGNORE_CASE_BOOL ic, | ||||
| 		const VCN lowest_vcn, const u8 *val, const u32 val_len, | ||||
| 		ntfs_attr_search_ctx *ctx); | ||||
| 
 | ||||
| extern ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, | ||||
| 		const ATTR_TYPES type); | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_attrs_walk - syntactic sugar for walking all attributes in an inode | ||||
|  * @ctx:	initialised attribute search context | ||||
|  * | ||||
|  * Syntactic sugar for walking attributes in an inode. | ||||
|  * | ||||
|  * Return 0 on success and -1 on error with errno set to the error code from | ||||
|  * ntfs_attr_lookup(). | ||||
|  * | ||||
|  * Example: When you want to enumerate all attributes in an open ntfs inode | ||||
|  *	    @ni, you can simply do: | ||||
|  * | ||||
|  *	int err; | ||||
|  *	ntfs_attr_search_ctx *ctx = ntfs_attr_get_search_ctx(ni, NULL); | ||||
|  *	if (!ctx) | ||||
|  *		// Error code is in errno. Handle this case.
 | ||||
|  *	while (!(err = ntfs_attrs_walk(ctx))) { | ||||
|  *		ATTR_RECORD *attr = ctx->attr; | ||||
|  *		// attr now contains the next attribute. Do whatever you want
 | ||||
|  *		// with it and then just continue with the while loop.
 | ||||
|  *	} | ||||
|  *	if (err && errno != ENOENT) | ||||
|  *		// Ooops. An error occurred! You should handle this case.
 | ||||
|  *	// Now finished with all attributes in the inode.
 | ||||
|  */ | ||||
| static __inline__ int ntfs_attrs_walk(ntfs_attr_search_ctx *ctx) | ||||
| { | ||||
| 	return ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0, | ||||
| 			NULL, 0, ctx); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * struct ntfs_attr - ntfs in memory non-resident attribute structure | ||||
|  * @rl:			if not NULL, the decompressed runlist | ||||
|  * @ni:			base ntfs inode to which this attribute belongs | ||||
|  * @type:		attribute type | ||||
|  * @name:		Unicode name of the attribute | ||||
|  * @name_len:		length of @name in Unicode characters | ||||
|  * @state:		NTFS attribute specific flags describing this attribute | ||||
|  * @allocated_size:	copy from the attribute record | ||||
|  * @data_size:		copy from the attribute record | ||||
|  * @initialized_size:	copy from the attribute record | ||||
|  * @compressed_size:	copy from the attribute record | ||||
|  * @compression_block_size:		size of a compression block (cb) | ||||
|  * @compression_block_size_bits:	log2 of the size of a cb | ||||
|  * @compression_block_clusters:		number of clusters per cb | ||||
|  * | ||||
|  * This structure exists purely to provide a mechanism of caching the runlist | ||||
|  * of an attribute. If you want to operate on a particular attribute extent, | ||||
|  * you should not be using this structure at all. If you want to work with a | ||||
|  * resident attribute, you should not be using this structure at all. As a | ||||
|  * fail-safe check make sure to test NAttrNonResident() and if it is false, you | ||||
|  * know you shouldn't be using this structure. | ||||
|  * | ||||
|  * If you want to work on a resident attribute or on a specific attribute | ||||
|  * extent, you should use ntfs_lookup_attr() to retrieve the attribute (extent) | ||||
|  * record, edit that, and then write back the mft record (or set the | ||||
|  * corresponding ntfs inode dirty for delayed write back). | ||||
|  * | ||||
|  * @rl is the decompressed runlist of the attribute described by this | ||||
|  * structure. Obviously this only makes sense if the attribute is not resident, | ||||
|  * i.e. NAttrNonResident() is true. If the runlist hasn't been decompressed yet | ||||
|  * @rl is NULL, so be prepared to cope with @rl == NULL. | ||||
|  * | ||||
|  * @ni is the base ntfs inode of the attribute described by this structure. | ||||
|  * | ||||
|  * @type is the attribute type (see layout.h for the definition of ATTR_TYPES), | ||||
|  * @name and @name_len are the little endian Unicode name and the name length | ||||
|  * in Unicode characters of the attribute, respectively. | ||||
|  * | ||||
|  * @state contains NTFS attribute specific flags describing this attribute | ||||
|  * structure. See ntfs_attr_state_bits above. | ||||
|  */ | ||||
| struct _ntfs_attr { | ||||
| 	runlist_element *rl; | ||||
| 	ntfs_inode *ni; | ||||
| 	ATTR_TYPES type; | ||||
| 	ntfschar *name; | ||||
| 	u32 name_len; | ||||
| 	unsigned long state; | ||||
| 	s64 allocated_size; | ||||
| 	s64 data_size; | ||||
| 	s64 initialized_size; | ||||
| 	s64 compressed_size; | ||||
| 	u32 compression_block_size; | ||||
| 	u8 compression_block_size_bits; | ||||
| 	u8 compression_block_clusters; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * enum ntfs_attr_state_bits - bits for the state field in the ntfs_attr structure | ||||
|  */ | ||||
| typedef enum { | ||||
| 	NA_Initialized,		/* 1: structure is initialized. */ | ||||
| 	NA_NonResident,		/* 1: Attribute is not resident. */ | ||||
| } ntfs_attr_state_bits; | ||||
| 
 | ||||
| #define  test_nattr_flag(na, flag)	 test_bit(NA_##flag, (na)->state) | ||||
| #define   set_nattr_flag(na, flag)	  set_bit(NA_##flag, (na)->state) | ||||
| #define clear_nattr_flag(na, flag)	clear_bit(NA_##flag, (na)->state) | ||||
| 
 | ||||
| #define NAttrInitialized(na)		 test_nattr_flag(na, Initialized) | ||||
| #define NAttrSetInitialized(na)		  set_nattr_flag(na, Initialized) | ||||
| #define NAttrClearInitialized(na)	clear_nattr_flag(na, Initialized) | ||||
| 
 | ||||
| #define NAttrNonResident(na)		 test_nattr_flag(na, NonResident) | ||||
| #define NAttrSetNonResident(na)		  set_nattr_flag(na, NonResident) | ||||
| #define NAttrClearNonResident(na)	clear_nattr_flag(na, NonResident) | ||||
| 
 | ||||
| #define GenNAttrIno(func_name,flag)				\ | ||||
| static inline int NAttr##func_name(ntfs_attr *na)		\ | ||||
| {								\ | ||||
| 	if (na->type == AT_DATA && na->name == AT_UNNAMED)	\ | ||||
| 		return (na->ni->flags & FILE_ATTR_##flag);	\ | ||||
| 	return 0;						\ | ||||
| }								\ | ||||
| static inline void NAttrSet##func_name(ntfs_attr *na)		\ | ||||
| {								\ | ||||
| 	if (na->type == AT_DATA && na->name == AT_UNNAMED)	\ | ||||
| 		na->ni->flags |= FILE_ATTR_##flag;		\ | ||||
| 	else							\ | ||||
| 		ntfs_log_trace("BUG! Should be called only for "\ | ||||
| 			"unnamed data attribute.\n");		\ | ||||
| }								\ | ||||
| static inline void NAttrClear##func_name(ntfs_attr *na)		\ | ||||
| {								\ | ||||
| 	if (na->type == AT_DATA && na->name == AT_UNNAMED)	\ | ||||
| 		na->ni->flags &= ~FILE_ATTR_##flag;		\ | ||||
| } | ||||
| 
 | ||||
| GenNAttrIno(Compressed, COMPRESSED) | ||||
| GenNAttrIno(Encrypted, ENCRYPTED) | ||||
| GenNAttrIno(Sparse, SPARSE_FILE) | ||||
| 
 | ||||
| /**
 | ||||
|  * union attr_val - Union of all known attribute values | ||||
|  * | ||||
|  * For convenience. Used in the attr structure. | ||||
|  */ | ||||
| typedef union { | ||||
| 	u8 _default;	/* Unnamed u8 to serve as default when just using
 | ||||
| 			   a_val without specifying any of the below. */ | ||||
| 	STANDARD_INFORMATION std_inf; | ||||
| 	ATTR_LIST_ENTRY al_entry; | ||||
| 	FILE_NAME_ATTR filename; | ||||
| 	OBJECT_ID_ATTR obj_id; | ||||
| 	SECURITY_DESCRIPTOR_ATTR sec_desc; | ||||
| 	VOLUME_NAME vol_name; | ||||
| 	VOLUME_INFORMATION vol_inf; | ||||
| 	DATA_ATTR data; | ||||
| 	INDEX_ROOT index_root; | ||||
| 	INDEX_BLOCK index_blk; | ||||
| 	BITMAP_ATTR bmp; | ||||
| 	REPARSE_POINT reparse; | ||||
| 	EA_INFORMATION ea_inf; | ||||
| 	EA_ATTR ea; | ||||
| 	PROPERTY_SET property_set; | ||||
| 	LOGGED_UTILITY_STREAM logged_util_stream; | ||||
| 	EFS_ATTR_HEADER efs; | ||||
| } attr_val; | ||||
| 
 | ||||
| extern void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident, | ||||
| 		const BOOL compressed, const BOOL encrypted, const BOOL sparse, | ||||
| 		const s64 allocated_size, const s64 data_size, | ||||
| 		const s64 initialized_size, const s64 compressed_size, | ||||
| 		const u8 compression_unit); | ||||
| 
 | ||||
| extern ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, | ||||
| 		ntfschar *name, u32 name_len); | ||||
| extern void ntfs_attr_close(ntfs_attr *na); | ||||
| 
 | ||||
| extern s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, | ||||
| 		void *b); | ||||
| extern s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, | ||||
| 		const void *b); | ||||
| 
 | ||||
| extern void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, | ||||
| 			       ntfschar *name, u32 name_len, s64 *data_size); | ||||
| 
 | ||||
| extern s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, | ||||
| 		const s64 bk_cnt, const u32 bk_size, void *dst); | ||||
| extern s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, | ||||
| 		s64 bk_cnt, const u32 bk_size, void *src); | ||||
| 
 | ||||
| extern int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn); | ||||
| extern int ntfs_attr_map_whole_runlist(ntfs_attr *na); | ||||
| 
 | ||||
| extern LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn); | ||||
| extern runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn); | ||||
| 
 | ||||
| extern int ntfs_attr_size_bounds_check(const ntfs_volume *vol, | ||||
| 		const ATTR_TYPES type, const s64 size); | ||||
| extern int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, | ||||
| 		const ATTR_TYPES type); | ||||
| extern int ntfs_attr_can_be_resident(const ntfs_volume *vol, | ||||
| 		const ATTR_TYPES type); | ||||
| 
 | ||||
| extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size); | ||||
| 
 | ||||
| extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, | ||||
| 		ntfschar *name, u8 name_len, u8 *val, u32 size, | ||||
| 		ATTR_FLAGS flags); | ||||
| extern int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, | ||||
| 		ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size, | ||||
| 		ATTR_FLAGS flags); | ||||
| extern int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx); | ||||
| 
 | ||||
| extern int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, | ||||
| 		ntfschar *name, u8 name_len, u8 *val, s64 size); | ||||
| extern int ntfs_attr_rm(ntfs_attr *na); | ||||
| 
 | ||||
| extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size); | ||||
| 
 | ||||
| extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, | ||||
| 		const u32 new_size); | ||||
| 
 | ||||
| extern int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni); | ||||
| extern int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra); | ||||
| 
 | ||||
| extern int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn); | ||||
| 
 | ||||
| extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize); | ||||
| 
 | ||||
| // FIXME / TODO: Above here the file is cleaned up. (AIA)
 | ||||
| /**
 | ||||
|  * get_attribute_value_length - return the length of the value of an attribute | ||||
|  * @a:	pointer to a buffer containing the attribute record | ||||
|  * | ||||
|  * Return the byte size of the attribute value of the attribute @a (as it | ||||
|  * would be after eventual decompression and filling in of holes if sparse). | ||||
|  * If we return 0, check errno. If errno is 0 the actual length was 0, | ||||
|  * otherwise errno describes the error. | ||||
|  * | ||||
|  * FIXME: Describe possible errnos. | ||||
|  */ | ||||
| s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a); | ||||
| 
 | ||||
| /**
 | ||||
|  * get_attribute_value - return the attribute value of an attribute | ||||
|  * @vol:	volume on which the attribute is present | ||||
|  * @a:		attribute to get the value of | ||||
|  * @b:		destination buffer for the attribute value | ||||
|  * | ||||
|  * Make a copy of the attribute value of the attribute @a into the destination | ||||
|  * buffer @b. Note, that the size of @b has to be at least equal to the value | ||||
|  * returned by get_attribute_value_length(@a). | ||||
|  * | ||||
|  * Return number of bytes copied. If this is zero check errno. If errno is 0 | ||||
|  * then nothing was read due to a zero-length attribute value, otherwise | ||||
|  * errno describes the error. | ||||
|  */ | ||||
| s64 ntfs_get_attribute_value(const ntfs_volume *vol, const ATTR_RECORD *a, | ||||
| 		u8 *b); | ||||
| 
 | ||||
| void ntfs_attr_name_free(char **name); | ||||
| char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len); | ||||
| 
 | ||||
| int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, | ||||
| 		    u32 name_len); | ||||
| 
 | ||||
| #endif /* defined _NTFS_ATTRIB_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,51 @@ | |||
| /*
 | ||||
|  * attrlist.h - Exports for attribute list attribute handling.   | ||||
|  * 		Originated from Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2004 Anton Altaparmakov | ||||
|  * Copyright (c) 2004 Yura Pakhuchiy | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_ATTRLIST_H | ||||
| #define _NTFS_ATTRLIST_H | ||||
| 
 | ||||
| #include "attrib.h" | ||||
| 
 | ||||
| extern int ntfs_attrlist_need(ntfs_inode *ni); | ||||
| 
 | ||||
| extern int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr); | ||||
| extern int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx); | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_attrlist_mark_dirty - set the attribute list dirty | ||||
|  * @ni:		ntfs inode which base inode contain dirty attribute list | ||||
|  * | ||||
|  * Set the attribute list dirty so it is written out later (at the latest at | ||||
|  * ntfs_inode_close() time). | ||||
|  * | ||||
|  * This function cannot fail. | ||||
|  */ | ||||
| static __inline__ void ntfs_attrlist_mark_dirty(ntfs_inode *ni) | ||||
| { | ||||
| 	if (ni->nr_extents == -1) | ||||
| 		NInoAttrListSetDirty(ni->base_ni); | ||||
| 	else | ||||
| 		NInoAttrListSetDirty(ni); | ||||
| } | ||||
| 
 | ||||
| #endif /* defined _NTFS_ATTRLIST_H */ | ||||
|  | @ -0,0 +1,74 @@ | |||
| /*
 | ||||
|  * bitmap.h - Exports for bitmap handling. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2000-2004 Anton Altaparmakov | ||||
|  * 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 NTFS-3G | ||||
|  * 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_BITMAP_H | ||||
| #define _NTFS_BITMAP_H | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "attrib.h" | ||||
| 
 | ||||
| /*
 | ||||
|  * NOTES: | ||||
|  * | ||||
|  * - Operations are 8-bit only to ensure the functions work both on little | ||||
|  *   and big endian machines! So don't make them 32-bit ops! | ||||
|  * - bitmap starts at bit = 0 and ends at bit = bitmap size - 1. | ||||
|  * - _Caller_ has to make sure that the bit to operate on is less than the | ||||
|  *   size of the bitmap. | ||||
|  */ | ||||
| 
 | ||||
| extern void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value); | ||||
| extern char ntfs_bit_get(const u8 *bitmap, const u64 bit); | ||||
| extern char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value); | ||||
| extern int  ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count); | ||||
| extern int  ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count); | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_bitmap_set_bit - set a bit in a bitmap | ||||
|  * @na:		attribute containing the bitmap | ||||
|  * @bit:	bit to set | ||||
|  * | ||||
|  * Set the @bit in the bitmap described by the attribute @na. | ||||
|  * | ||||
|  * On success return 0 and on error return -1 with errno set to the error code. | ||||
|  */ | ||||
| static __inline__ int ntfs_bitmap_set_bit(ntfs_attr *na, s64 bit) | ||||
| { | ||||
| 	return ntfs_bitmap_set_run(na, bit, 1); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_bitmap_clear_bit - clear a bit in a bitmap | ||||
|  * @na:		attribute containing the bitmap | ||||
|  * @bit:	bit to clear | ||||
|  * | ||||
|  * Clear @bit in the bitmap described by the attribute @na. | ||||
|  * | ||||
|  * On success return 0 and on error return -1 with errno set to the error code. | ||||
|  */ | ||||
| static __inline__ int ntfs_bitmap_clear_bit(ntfs_attr *na, s64 bit) | ||||
| { | ||||
| 	return ntfs_bitmap_clear_run(na, bit, 1); | ||||
| } | ||||
| 
 | ||||
| #endif /* defined _NTFS_BITMAP_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,47 @@ | |||
| /*
 | ||||
|  * bootsect.h - Exports for bootsector record handling. Originated from the Linux-NTFS | ||||
|  *		project. | ||||
|  * | ||||
|  * Copyright (c) 2000-2002 Anton Altaparmakov | ||||
|  * | ||||
|  * This program/include file is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as published | ||||
|  * by the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program/include file is distributed in the hope that it will be | ||||
|  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty | ||||
|  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program (in the main directory of the NTFS-3G | ||||
|  * 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_BOOTSECT_H | ||||
| #define _NTFS_BOOTSECT_H | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "volume.h" | ||||
| #include "layout.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * is_boot_sector_ntfs - check a boot sector for describing an ntfs volume | ||||
|  * @b:		buffer containing the boot sector | ||||
|  * @silent:	if 1 don't display progress information | ||||
|  * | ||||
|  * This function checks the boot sector in @b for describing a valid ntfs | ||||
|  * volume. Return TRUE if @b is a valid NTFS boot sector or FALSE otherwise. | ||||
|  * If silent is FALSE, progress output will be output to stdout. If silent is | ||||
|  * TRUE no output to stdout will occur. Errors/warnings to stderr will occur | ||||
|  * disregarding the value of silent (but only if configure was run with | ||||
|  * --enable-debug). | ||||
|  */ | ||||
| extern BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b, BOOL silent); | ||||
| extern int ntfs_boot_sector_parse(ntfs_volume *vol, | ||||
| 		const NTFS_BOOT_SECTOR *bs); | ||||
| 
 | ||||
| #endif /* defined _NTFS_BOOTSECT_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,37 @@ | |||
| /*
 | ||||
|  * collate.h - Defines for NTFS collation handling.  Originated from the Linux-NTFS | ||||
|  *             project. | ||||
|  * | ||||
|  * Copyright (c) 2004 Anton Altaparmakov | ||||
|  * Copyright (c) 2005 Yura Pakhuchiy | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_COLLATE_H | ||||
| #define _NTFS_COLLATE_H | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "volume.h" | ||||
| 
 | ||||
| #define NTFS_COLLATION_ERROR -2 | ||||
| 
 | ||||
| extern BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr); | ||||
| extern int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr, | ||||
| 			const void *data1, const int data1_len, | ||||
| 			const void *data2, const int data2_len); | ||||
| 
 | ||||
| #endif /* _NTFS_COLLATE_H */ | ||||
|  | @ -0,0 +1,54 @@ | |||
| /*
 | ||||
|  * compat.h - Tweaks for Windows compatibility. | ||||
|  * | ||||
|  * Copyright (c) 2002 Richard Russon | ||||
|  * Copyright (c) 2002-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 NTFS-3G | ||||
|  * 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_COMPAT_H | ||||
| #define _NTFS_COMPAT_H | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| 
 | ||||
| #ifdef WINDOWS | ||||
| 
 | ||||
| #ifndef HAVE_FFS | ||||
| #define HAVE_FFS | ||||
| extern int ffs(int i); | ||||
| #endif /* HAVE_FFS */ | ||||
| 
 | ||||
| #define HAVE_STDIO_H		/* mimic config.h */ | ||||
| #define HAVE_STDARG_H | ||||
| 
 | ||||
| #define atoll			_atoi64 | ||||
| #define fdatasync		commit | ||||
| #define __inline__		inline | ||||
| #define __attribute__(X)	/*nothing*/ | ||||
| 
 | ||||
| #else /* !defined WINDOWS */ | ||||
| 
 | ||||
| #ifndef O_BINARY | ||||
| #define O_BINARY		0		/* unix is binary by default */ | ||||
| #endif | ||||
| 
 | ||||
| #endif /* defined WINDOWS */ | ||||
| 
 | ||||
| #endif /* defined _NTFS_COMPAT_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,33 @@ | |||
| /*
 | ||||
|  * compress.h - Exports for compressed attribute handling.  | ||||
|  * 		Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 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 NTFS-3G | ||||
|  * 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_COMPRESS_H | ||||
| #define _NTFS_COMPRESS_H | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "attrib.h" | ||||
| 
 | ||||
| extern s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, | ||||
| 		void *b); | ||||
| 
 | ||||
| #endif /* defined _NTFS_COMPRESS_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,47 @@ | |||
| /*
 | ||||
|  * debug.h - Debugging output functions. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2002-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 NTFS-3G | ||||
|  * 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_DEBUG_H | ||||
| #define _NTFS_DEBUG_H | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| 
 | ||||
| #include "logging.h" | ||||
| 
 | ||||
| struct _runlist_element; | ||||
| 
 | ||||
| #ifdef DEBUG | ||||
| extern void ntfs_debug_runlist_dump(const struct _runlist_element *rl); | ||||
| #else | ||||
| static __inline__ void ntfs_debug_runlist_dump(const struct _runlist_element *rl __attribute__((unused))) {} | ||||
| #endif | ||||
| 
 | ||||
| #define NTFS_BUG(msg)							\ | ||||
| {									\ | ||||
| 	int ___i;							\ | ||||
| 	ntfs_log_critical("Bug in %s(): %s\n", __FUNCTION__, msg);	\ | ||||
| 	ntfs_log_debug("Forcing segmentation fault!");			\ | ||||
| 	___i = ((int*)NULL)[1];						\ | ||||
| } | ||||
| 
 | ||||
| #endif /* defined _NTFS_DEBUG_H */ | ||||
|  | @ -0,0 +1,128 @@ | |||
| /*
 | ||||
|  * device.h - Exports for low level device io. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2000-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 NTFS-3G | ||||
|  * 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_DEVICE_H | ||||
| #define _NTFS_DEVICE_H | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| 
 | ||||
| #include "device_io.h" | ||||
| #include "types.h" | ||||
| #include "support.h" | ||||
| #include "volume.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * enum ntfs_device_state_bits - | ||||
|  * | ||||
|  * Defined bits for the state field in the ntfs_device structure. | ||||
|  */ | ||||
| typedef enum { | ||||
| 	ND_Open,	/* 1: Device is open. */ | ||||
| 	ND_ReadOnly,	/* 1: Device is read-only. */ | ||||
| 	ND_Dirty,	/* 1: Device is dirty, needs sync. */ | ||||
| 	ND_Block,	/* 1: Device is a block device. */ | ||||
| } ntfs_device_state_bits; | ||||
| 
 | ||||
| #define  test_ndev_flag(nd, flag)	   test_bit(ND_##flag, (nd)->d_state) | ||||
| #define   set_ndev_flag(nd, flag)	    set_bit(ND_##flag, (nd)->d_state) | ||||
| #define clear_ndev_flag(nd, flag)	  clear_bit(ND_##flag, (nd)->d_state) | ||||
| 
 | ||||
| #define NDevOpen(nd)		 test_ndev_flag(nd, Open) | ||||
| #define NDevSetOpen(nd)		  set_ndev_flag(nd, Open) | ||||
| #define NDevClearOpen(nd)	clear_ndev_flag(nd, Open) | ||||
| 
 | ||||
| #define NDevReadOnly(nd)	 test_ndev_flag(nd, ReadOnly) | ||||
| #define NDevSetReadOnly(nd)	  set_ndev_flag(nd, ReadOnly) | ||||
| #define NDevClearReadOnly(nd)	clear_ndev_flag(nd, ReadOnly) | ||||
| 
 | ||||
| #define NDevDirty(nd)		 test_ndev_flag(nd, Dirty) | ||||
| #define NDevSetDirty(nd)	  set_ndev_flag(nd, Dirty) | ||||
| #define NDevClearDirty(nd)	clear_ndev_flag(nd, Dirty) | ||||
| 
 | ||||
| #define NDevBlock(nd)		 test_ndev_flag(nd, Block) | ||||
| #define NDevSetBlock(nd)	  set_ndev_flag(nd, Block) | ||||
| #define NDevClearBlock(nd)	clear_ndev_flag(nd, Block) | ||||
| 
 | ||||
| /**
 | ||||
|  * struct ntfs_device - | ||||
|  * | ||||
|  * The ntfs device structure defining all operations needed to access the low | ||||
|  * level device underlying the ntfs volume. | ||||
|  */ | ||||
| struct ntfs_device { | ||||
| 	struct ntfs_device_operations *d_ops;	/* Device operations. */ | ||||
| 	unsigned long d_state;			/* State of the device. */ | ||||
| 	char *d_name;				/* Name of device. */ | ||||
| 	void *d_private;			/* Private data used by the
 | ||||
| 						   device operations. */ | ||||
| }; | ||||
| 
 | ||||
| struct stat; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct ntfs_device_operations - | ||||
|  * | ||||
|  * The ntfs device operations defining all operations that can be performed on | ||||
|  * the low level device described by an ntfs device structure. | ||||
|  */ | ||||
| struct ntfs_device_operations { | ||||
| 	int (*open)(struct ntfs_device *dev, int flags); | ||||
| 	int (*close)(struct ntfs_device *dev); | ||||
| 	s64 (*seek)(struct ntfs_device *dev, s64 offset, int whence); | ||||
| 	s64 (*read)(struct ntfs_device *dev, void *buf, s64 count); | ||||
| 	s64 (*write)(struct ntfs_device *dev, const void *buf, s64 count); | ||||
| 	s64 (*pread)(struct ntfs_device *dev, void *buf, s64 count, s64 offset); | ||||
| 	s64 (*pwrite)(struct ntfs_device *dev, const void *buf, s64 count, | ||||
| 			s64 offset); | ||||
| 	int (*sync)(struct ntfs_device *dev); | ||||
| 	int (*stat)(struct ntfs_device *dev, struct stat *buf); | ||||
| 	int (*ioctl)(struct ntfs_device *dev, int request, void *argp); | ||||
| }; | ||||
| 
 | ||||
| extern struct ntfs_device *ntfs_device_alloc(const char *name, const long state, | ||||
| 		struct ntfs_device_operations *dops, void *priv_data); | ||||
| extern int ntfs_device_free(struct ntfs_device *dev); | ||||
| 
 | ||||
| extern s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, | ||||
| 		void *b); | ||||
| extern s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, | ||||
| 		const void *b); | ||||
| 
 | ||||
| extern s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count, | ||||
| 		const u32 bksize, void *b); | ||||
| extern s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, | ||||
| 		const u32 bksize, void *b); | ||||
| 
 | ||||
| extern s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, | ||||
| 		const s64 count, void *b); | ||||
| extern s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn, | ||||
| 		const s64 count, const void *b); | ||||
| 
 | ||||
| extern s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size); | ||||
| extern s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev); | ||||
| extern int ntfs_device_heads_get(struct ntfs_device *dev); | ||||
| extern int ntfs_device_sectors_per_track_get(struct ntfs_device *dev); | ||||
| extern int ntfs_device_sector_size_get(struct ntfs_device *dev); | ||||
| extern int ntfs_device_block_size_set(struct ntfs_device *dev, int block_size); | ||||
| 
 | ||||
| #endif /* defined _NTFS_DEVICE_H */ | ||||
|  | @ -0,0 +1,77 @@ | |||
| /*
 | ||||
|  * device_io.h - Exports for default device io. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2000-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 NTFS-3G | ||||
|  * 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_DEVICE_IO_H | ||||
| #define _NTFS_DEVICE_IO_H | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| 
 | ||||
| #ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS | ||||
| 
 | ||||
| #ifndef __CYGWIN32__ | ||||
| 
 | ||||
| /* Not on Cygwin; use standard Unix style low level device operations. */ | ||||
| #define ntfs_device_default_io_ops ntfs_device_unix_io_ops | ||||
| 
 | ||||
| #else /* __CYGWIN32__ */ | ||||
| 
 | ||||
| #ifndef HDIO_GETGEO | ||||
| #	define HDIO_GETGEO	0x301 | ||||
| /**
 | ||||
|  * struct hd_geometry - | ||||
|  */ | ||||
| struct hd_geometry { | ||||
| 	unsigned char heads; | ||||
| 	unsigned char sectors; | ||||
| 	unsigned short cylinders; | ||||
| 	unsigned long start; | ||||
| }; | ||||
| #endif | ||||
| #ifndef BLKGETSIZE | ||||
| #	define BLKGETSIZE	0x1260 | ||||
| #endif | ||||
| #ifndef BLKSSZGET | ||||
| #	define BLKSSZGET	0x1268 | ||||
| #endif | ||||
| #ifndef BLKGETSIZE64 | ||||
| #	define BLKGETSIZE64	0x80041272 | ||||
| #endif | ||||
| #ifndef BLKBSZSET | ||||
| #	define BLKBSZSET	0x40041271 | ||||
| #endif | ||||
| 
 | ||||
| /* On Cygwin; use Win32 low level device operations. */ | ||||
| #define ntfs_device_default_io_ops ntfs_device_win32_io_ops | ||||
| 
 | ||||
| #endif /* __CYGWIN32__ */ | ||||
| 
 | ||||
| 
 | ||||
| /* Forward declaration. */ | ||||
| struct ntfs_device_operations; | ||||
| 
 | ||||
| extern struct ntfs_device_operations ntfs_device_default_io_ops; | ||||
| 
 | ||||
| #endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */ | ||||
| 
 | ||||
| #endif /* defined _NTFS_DEVICE_IO_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,111 @@ | |||
| /*
 | ||||
|  * dir.h - Exports for directory handling. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2002 Anton Altaparmakov | ||||
|  * Copyright (c) 2005-2006 Yura Pakhuchiy | ||||
|  * 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 NTFS-3G | ||||
|  * 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_DIR_H | ||||
| #define _NTFS_DIR_H | ||||
| 
 | ||||
| #include "types.h" | ||||
| 
 | ||||
| #define PATH_SEP '/' | ||||
| 
 | ||||
| #ifndef MAX_PATH | ||||
| #define MAX_PATH 1024 | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * We do not have these under DJGPP, so define our version that do not conflict | ||||
|  * with other S_IFs defined under DJGPP. | ||||
|  */ | ||||
| #ifdef DJGPP | ||||
| #ifndef S_IFLNK | ||||
| #define S_IFLNK  0120000 | ||||
| #endif | ||||
| #ifndef S_ISLNK | ||||
| #define S_ISLNK(m)  (((m) & S_IFMT) == S_IFLNK) | ||||
| #endif | ||||
| #ifndef S_IFSOCK | ||||
| #define S_IFSOCK 0140000 | ||||
| #endif | ||||
| #ifndef S_ISSOCK | ||||
| #define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * The little endian Unicode strings $I30, $SII, $SDH, $O, $Q, $R | ||||
|  * as a global constant. | ||||
|  */ | ||||
| extern ntfschar NTFS_INDEX_I30[5]; | ||||
| extern ntfschar NTFS_INDEX_SII[5]; | ||||
| extern ntfschar NTFS_INDEX_SDH[5]; | ||||
| extern ntfschar NTFS_INDEX_O[3]; | ||||
| extern ntfschar NTFS_INDEX_Q[3]; | ||||
| extern ntfschar NTFS_INDEX_R[3]; | ||||
| 
 | ||||
| extern u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, | ||||
| 		const ntfschar *uname, const int uname_len); | ||||
| 
 | ||||
| extern ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, | ||||
| 		const char *pathname); | ||||
| 
 | ||||
| extern ntfs_inode *ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, | ||||
| 		dev_t type); | ||||
| extern ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, | ||||
| 		ntfschar *name, u8 name_len, dev_t type, dev_t dev); | ||||
| extern ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, | ||||
| 		ntfschar *name, u8 name_len, ntfschar *target, u8 target_len); | ||||
| extern int ntfs_check_empty_dir(ntfs_inode *ni); | ||||
| extern int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, | ||||
| 		u8 name_len); | ||||
| 
 | ||||
| extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, | ||||
| 		u8 name_len); | ||||
| 
 | ||||
| /*
 | ||||
|  * File types (adapted from include <linux/fs.h>) | ||||
|  */ | ||||
| #define NTFS_DT_UNKNOWN		0 | ||||
| #define NTFS_DT_FIFO		1 | ||||
| #define NTFS_DT_CHR		2 | ||||
| #define NTFS_DT_DIR		4 | ||||
| #define NTFS_DT_BLK		6 | ||||
| #define NTFS_DT_REG		8 | ||||
| #define NTFS_DT_LNK		10 | ||||
| #define NTFS_DT_SOCK		12 | ||||
| #define NTFS_DT_WHT		14 | ||||
| 
 | ||||
| /*
 | ||||
|  * This is the "ntfs_filldir" function type, used by ntfs_readdir() to let | ||||
|  * the caller specify what kind of dirent layout it wants to have. | ||||
|  * This allows the caller to read directories into their application or | ||||
|  * to have different dirent layouts depending on the binary type. | ||||
|  */ | ||||
| typedef int (*ntfs_filldir_t)(void *dirent, const ntfschar *name, | ||||
| 		const int name_len, const int name_type, const s64 pos, | ||||
| 		const MFT_REF mref, const unsigned dt_type); | ||||
| 
 | ||||
| extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, | ||||
| 		void *dirent, ntfs_filldir_t filldir); | ||||
| 
 | ||||
| #endif /* defined _NTFS_DIR_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,203 @@ | |||
| /*
 | ||||
|  * endians.h - Definitions related to handling of byte ordering.  | ||||
|  *             Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2000-2005 Anton Altaparmakov | ||||
|  * | ||||
|  * This program/include file is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as published | ||||
|  * by the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program/include file is distributed in the hope that it will be | ||||
|  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty | ||||
|  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program (in the main directory of the NTFS-3G | ||||
|  * 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_ENDIANS_H | ||||
| #define _NTFS_ENDIANS_H | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * Notes: | ||||
|  *	We define the conversion functions including typecasts since the | ||||
|  * defaults don't necessarily perform appropriate typecasts. | ||||
|  *	Also, using our own functions means that we can change them if it | ||||
|  * turns out that we do need to use the unaligned access macros on | ||||
|  * architectures requiring aligned memory accesses... | ||||
|  */ | ||||
| 
 | ||||
| #ifdef HAVE_ENDIAN_H | ||||
| #include <endian.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYS_ENDIAN_H | ||||
| #include <sys/endian.h> | ||||
| #endif | ||||
| #ifdef HAVE_MACHINE_ENDIAN_H | ||||
| #include <machine/endian.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYS_BYTEORDER_H | ||||
| #include <sys/byteorder.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYS_PARAM_H | ||||
| #include <sys/param.h> | ||||
| #endif | ||||
| 
 | ||||
| #ifndef __BYTE_ORDER | ||||
| #	if defined(_BYTE_ORDER) | ||||
| #		define __BYTE_ORDER _BYTE_ORDER | ||||
| #		define __LITTLE_ENDIAN _LITTLE_ENDIAN | ||||
| #		define __BIG_ENDIAN _BIG_ENDIAN | ||||
| #	elif defined(BYTE_ORDER) | ||||
| #		define __BYTE_ORDER BYTE_ORDER | ||||
| #		define __LITTLE_ENDIAN LITTLE_ENDIAN | ||||
| #		define __BIG_ENDIAN BIG_ENDIAN | ||||
| #	elif defined(__BYTE_ORDER__) | ||||
| #		define __BYTE_ORDER __BYTE_ORDER__ | ||||
| #		define __LITTLE_ENDIAN __LITTLE_ENDIAN__ | ||||
| #		define __BIG_ENDIAN __BIG_ENDIAN__ | ||||
| #	elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) || \ | ||||
| 			defined(WORDS_LITTLEENDIAN) | ||||
| #		define __BYTE_ORDER 1 | ||||
| #		define __LITTLE_ENDIAN 1 | ||||
| #		define __BIG_ENDIAN 0 | ||||
| #	elif (!defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)) || \ | ||||
| 			defined(WORDS_BIGENDIAN) | ||||
| #		define __BYTE_ORDER 0 | ||||
| #		define __LITTLE_ENDIAN 1 | ||||
| #		define __BIG_ENDIAN 0 | ||||
| #	else | ||||
| #		error "__BYTE_ORDER is not defined." | ||||
| #	endif | ||||
| #endif | ||||
| 
 | ||||
| #define __ntfs_bswap_constant_16(x)		\ | ||||
| 	  (u16)((((u16)(x) & 0xff00) >> 8) |	\ | ||||
| 		(((u16)(x) & 0x00ff) << 8)) | ||||
| 
 | ||||
| #define __ntfs_bswap_constant_32(x)			\ | ||||
| 	  (u32)((((u32)(x) & 0xff000000u) >> 24) |	\ | ||||
| 		(((u32)(x) & 0x00ff0000u) >>  8) |	\ | ||||
| 		(((u32)(x) & 0x0000ff00u) <<  8) |	\ | ||||
| 		(((u32)(x) & 0x000000ffu) << 24)) | ||||
| 
 | ||||
| #define __ntfs_bswap_constant_64(x)				\ | ||||
| 	  (u64)((((u64)(x) & 0xff00000000000000ull) >> 56) |	\ | ||||
| 		(((u64)(x) & 0x00ff000000000000ull) >> 40) |	\ | ||||
| 		(((u64)(x) & 0x0000ff0000000000ull) >> 24) |	\ | ||||
| 		(((u64)(x) & 0x000000ff00000000ull) >>  8) |	\ | ||||
| 		(((u64)(x) & 0x00000000ff000000ull) <<  8) |	\ | ||||
| 		(((u64)(x) & 0x0000000000ff0000ull) << 24) |	\ | ||||
| 		(((u64)(x) & 0x000000000000ff00ull) << 40) |	\ | ||||
| 		(((u64)(x) & 0x00000000000000ffull) << 56)) | ||||
| 
 | ||||
| #ifdef HAVE_BYTESWAP_H | ||||
| #	include <byteswap.h> | ||||
| #else | ||||
| #	define bswap_16(x) __ntfs_bswap_constant_16(x) | ||||
| #	define bswap_32(x) __ntfs_bswap_constant_32(x) | ||||
| #	define bswap_64(x) __ntfs_bswap_constant_64(x) | ||||
| #endif | ||||
| 
 | ||||
| #if defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN) | ||||
| 
 | ||||
| #define __le16_to_cpu(x) (x) | ||||
| #define __le32_to_cpu(x) (x) | ||||
| #define __le64_to_cpu(x) (x) | ||||
| 
 | ||||
| #define __cpu_to_le16(x) (x) | ||||
| #define __cpu_to_le32(x) (x) | ||||
| #define __cpu_to_le64(x) (x) | ||||
| 
 | ||||
| #define __constant_le16_to_cpu(x) (x) | ||||
| #define __constant_le32_to_cpu(x) (x) | ||||
| #define __constant_le64_to_cpu(x) (x) | ||||
| 
 | ||||
| #define __constant_cpu_to_le16(x) (x) | ||||
| #define __constant_cpu_to_le32(x) (x) | ||||
| #define __constant_cpu_to_le64(x) (x) | ||||
| 
 | ||||
| #elif defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN) | ||||
| 
 | ||||
| #define __le16_to_cpu(x) bswap_16(x) | ||||
| #define __le32_to_cpu(x) bswap_32(x) | ||||
| #define __le64_to_cpu(x) bswap_64(x) | ||||
| 
 | ||||
| #define __cpu_to_le16(x) bswap_16(x) | ||||
| #define __cpu_to_le32(x) bswap_32(x) | ||||
| #define __cpu_to_le64(x) bswap_64(x) | ||||
| 
 | ||||
| #define __constant_le16_to_cpu(x) __ntfs_bswap_constant_16((u16)(x)) | ||||
| #define __constant_le32_to_cpu(x) __ntfs_bswap_constant_32((u32)(x)) | ||||
| #define __constant_le64_to_cpu(x) __ntfs_bswap_constant_64((u64)(x)) | ||||
| 
 | ||||
| #define __constant_cpu_to_le16(x) __ntfs_bswap_constant_16((u16)(x)) | ||||
| #define __constant_cpu_to_le32(x) __ntfs_bswap_constant_32((u32)(x)) | ||||
| #define __constant_cpu_to_le64(x) __ntfs_bswap_constant_64((u64)(x)) | ||||
| 
 | ||||
| #else | ||||
| 
 | ||||
| #error "You must define __BYTE_ORDER to be __LITTLE_ENDIAN or __BIG_ENDIAN." | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| /* Unsigned from LE to CPU conversion. */ | ||||
| 
 | ||||
| #define le16_to_cpu(x)		(u16)__le16_to_cpu((u16)(x)) | ||||
| #define le32_to_cpu(x)		(u32)__le32_to_cpu((u32)(x)) | ||||
| #define le64_to_cpu(x)		(u64)__le64_to_cpu((u64)(x)) | ||||
| 
 | ||||
| #define le16_to_cpup(x)		(u16)__le16_to_cpu(*(const u16*)(x)) | ||||
| #define le32_to_cpup(x)		(u32)__le32_to_cpu(*(const u32*)(x)) | ||||
| #define le64_to_cpup(x)		(u64)__le64_to_cpu(*(const u64*)(x)) | ||||
| 
 | ||||
| /* Signed from LE to CPU conversion. */ | ||||
| 
 | ||||
| #define sle16_to_cpu(x)		(s16)__le16_to_cpu((s16)(x)) | ||||
| #define sle32_to_cpu(x)		(s32)__le32_to_cpu((s32)(x)) | ||||
| #define sle64_to_cpu(x)		(s64)__le64_to_cpu((s64)(x)) | ||||
| 
 | ||||
| #define sle16_to_cpup(x)	(s16)__le16_to_cpu(*(s16*)(x)) | ||||
| #define sle32_to_cpup(x)	(s32)__le32_to_cpu(*(s32*)(x)) | ||||
| #define sle64_to_cpup(x)	(s64)__le64_to_cpu(*(s64*)(x)) | ||||
| 
 | ||||
| /* Unsigned from CPU to LE conversion. */ | ||||
| 
 | ||||
| #define cpu_to_le16(x)		(u16)__cpu_to_le16((u16)(x)) | ||||
| #define cpu_to_le32(x)		(u32)__cpu_to_le32((u32)(x)) | ||||
| #define cpu_to_le64(x)		(u64)__cpu_to_le64((u64)(x)) | ||||
| 
 | ||||
| #define cpu_to_le16p(x)		(u16)__cpu_to_le16(*(u16*)(x)) | ||||
| #define cpu_to_le32p(x)		(u32)__cpu_to_le32(*(u32*)(x)) | ||||
| #define cpu_to_le64p(x)		(u64)__cpu_to_le64(*(u64*)(x)) | ||||
| 
 | ||||
| /* Signed from CPU to LE conversion. */ | ||||
| 
 | ||||
| #define cpu_to_sle16(x)		(s16)__cpu_to_le16((s16)(x)) | ||||
| #define cpu_to_sle32(x)		(s32)__cpu_to_le32((s32)(x)) | ||||
| #define cpu_to_sle64(x)		(s64)__cpu_to_le64((s64)(x)) | ||||
| 
 | ||||
| #define cpu_to_sle16p(x)	(s16)__cpu_to_le16(*(s16*)(x)) | ||||
| #define cpu_to_sle32p(x)	(s32)__cpu_to_le32(*(s32*)(x)) | ||||
| #define cpu_to_sle64p(x)	(s64)__cpu_to_le64(*(s64*)(x)) | ||||
| 
 | ||||
| /* Constant endianness conversion defines. */ | ||||
| 
 | ||||
| #define const_le16_to_cpu(x)	__constant_le16_to_cpu(x) | ||||
| #define const_le32_to_cpu(x)	__constant_le32_to_cpu(x) | ||||
| #define const_le64_to_cpu(x)	__constant_le64_to_cpu(x) | ||||
| 
 | ||||
| #define const_cpu_to_le16(x)	__constant_cpu_to_le16(x) | ||||
| #define const_cpu_to_le32(x)	__constant_cpu_to_le32(x) | ||||
| #define const_cpu_to_le64(x)	__constant_cpu_to_le64(x) | ||||
| 
 | ||||
| #endif /* defined _NTFS_ENDIANS_H */ | ||||
|  | @ -0,0 +1,131 @@ | |||
| /*
 | ||||
|  * index.h - Defines for NTFS index handling.  Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2004 Anton Altaparmakov | ||||
|  * Copyright (c) 2004-2005 Richard Russon | ||||
|  * Copyright (c) 2005 Yura Pakhuchiy | ||||
|  * Copyright (c) 2006 Szabolcs Szakacsits | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_INDEX_H | ||||
| #define _NTFS_INDEX_H | ||||
| 
 | ||||
| #include "attrib.h" | ||||
| #include "types.h" | ||||
| #include "layout.h" | ||||
| #include "inode.h" | ||||
| #include "mft.h" | ||||
| 
 | ||||
| #define  VCN_INDEX_ROOT_PARENT  ((VCN)-2) | ||||
| 
 | ||||
| #define  MAX_PARENT_VCN		32 | ||||
| 
 | ||||
| /**
 | ||||
|  * struct ntfs_index_context - | ||||
|  * @ni:			inode containing the @entry described by this context | ||||
|  * @name:		name of the index described by this context | ||||
|  * @name_len:		length of the index name | ||||
|  * @entry:		index entry (points into @ir or @ia) | ||||
|  * @data:		index entry data (points into @entry) | ||||
|  * @data_len:		length in bytes of @data | ||||
|  * @is_in_root:		TRUE if @entry is in @ir or FALSE if it is in @ia | ||||
|  * @ir:			index root if @is_in_root or NULL otherwise | ||||
|  * @actx:		attribute search context if in root or NULL otherwise | ||||
|  * @ia:			index block if @is_in_root is FALSE or NULL otherwise | ||||
|  * @ia_na:		opened INDEX_ALLOCATION attribute | ||||
|  * @ib_vcn:		VCN from which @ia where read from | ||||
|  * @parent_pos:		parent entries' positions in the index block | ||||
|  * @parent_vcn:		entry's parent node or VCN_INDEX_ROOT_PARENT | ||||
|  * @new_vcn:            new VCN if we need to create a new index block | ||||
|  * @median:		move to the parent if splitting index blocks | ||||
|  * @ib_dirty:		TRUE if index block was changed | ||||
|  * @block_size:		index block size | ||||
|  * @vcn_size_bits:	VCN size bits for this index block | ||||
|  * | ||||
|  * @ni is the inode this context belongs to. | ||||
|  * | ||||
|  * @entry is the index entry described by this context.  @data and @data_len | ||||
|  * are the index entry data and its length in bytes, respectively.  @data | ||||
|  * simply points into @entry.  This is probably what the user is interested in. | ||||
|  * | ||||
|  * If @is_in_root is TRUE, @entry is in the index root attribute @ir described | ||||
|  * by the attribute search context @actx and inode @ni.  @ia, @ib_vcn and | ||||
|  * @ib_dirty are undefined in this case. | ||||
|  * | ||||
|  * If @is_in_root is FALSE, @entry is in the index allocation attribute and @ia | ||||
|  * and @ib_vcn point to the index allocation block and VCN where it's placed, | ||||
|  * respectively. @ir and @actx are NULL in this case. @ia_na is opened | ||||
|  * INDEX_ALLOCATION attribute. @ib_dirty is TRUE if index block was changed and | ||||
|  * FALSE otherwise. | ||||
|  * | ||||
|  * To obtain a context call ntfs_index_ctx_get(). | ||||
|  * | ||||
|  * When finished with the @entry and its @data, call ntfs_index_ctx_put() to | ||||
|  * free the context and other associated resources. | ||||
|  * | ||||
|  * If the index entry was modified, call ntfs_index_entry_mark_dirty() before | ||||
|  * the call to ntfs_index_ctx_put() to ensure that the changes are written | ||||
|  * to disk. | ||||
|  */ | ||||
| typedef struct { | ||||
| 	ntfs_inode *ni; | ||||
| 	ntfschar *name; | ||||
| 	u32 name_len; | ||||
| 	INDEX_ENTRY *entry; | ||||
| 	void *data; | ||||
| 	u16 data_len; | ||||
| 	COLLATION_RULES cr; | ||||
| 	BOOL is_in_root; | ||||
| 	INDEX_ROOT *ir; | ||||
| 	ntfs_attr_search_ctx *actx; | ||||
| 	INDEX_BLOCK *ib; | ||||
| 	ntfs_attr *ia_na; | ||||
| 	int parent_pos[MAX_PARENT_VCN];  /* parent entries' positions */ | ||||
| 	VCN ib_vcn; | ||||
| 	VCN parent_vcn[MAX_PARENT_VCN]; /* entry's parent nodes */ | ||||
| 	int max_depth;	     /* number of the parent nodes 	*/ | ||||
| 	int pindex;	     /* maximum it's the number of the parent nodes  */ | ||||
| 	BOOL ib_dirty; | ||||
| 	u32 block_size; | ||||
| 	u8 vcn_size_bits; | ||||
| } ntfs_index_context; | ||||
| 
 | ||||
| extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni, | ||||
| 						ntfschar *name, u32 name_len); | ||||
| extern void ntfs_index_ctx_put(ntfs_index_context *ictx); | ||||
| extern void ntfs_index_ctx_reinit(ntfs_index_context *ictx); | ||||
| 
 | ||||
| extern int ntfs_index_lookup(const void *key, const int key_len, | ||||
| 		ntfs_index_context *ictx); | ||||
| 
 | ||||
| extern int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, | ||||
| 		MFT_REF mref); | ||||
| extern int ntfs_index_rm(ntfs_index_context *ictx); | ||||
| 
 | ||||
| extern INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr); | ||||
| 
 | ||||
| extern VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie); | ||||
| 
 | ||||
| extern void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx); | ||||
| 
 | ||||
| extern char *ntfs_ie_filename_get(INDEX_ENTRY *ie); | ||||
| extern void ntfs_ie_filename_dump(INDEX_ENTRY *ie); | ||||
| extern void ntfs_ih_filename_dump(INDEX_HEADER *ih); | ||||
| 
 | ||||
| #endif /* _NTFS_INDEX_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,176 @@ | |||
| /*
 | ||||
|  * inode.h - Defines for NTFS inode handling. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2001,2002 Anton Altaparmakov | ||||
|  * Copyright (c) 2004-2005 Yura Pakhuchiy | ||||
|  * Copyright (c) 2004-2005 Richard Russon | ||||
|  * Copyright (c) 2006 Szabolcs Szakacsits | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_INODE_H | ||||
| #define _NTFS_INODE_H | ||||
| 
 | ||||
| /* Forward declaration */ | ||||
| typedef struct _ntfs_inode ntfs_inode; | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "layout.h" | ||||
| #include "support.h" | ||||
| #include "volume.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * enum ntfs_inode_state_bits - | ||||
|  * | ||||
|  * Defined bits for the state field in the ntfs_inode structure. | ||||
|  * (f) = files only, (d) = directories only | ||||
|  */ | ||||
| typedef enum { | ||||
| 	NI_Dirty,		/* 1: Mft record needs to be written to disk. */ | ||||
| 
 | ||||
| 	/* The NI_AttrList* tests only make sense for base inodes. */ | ||||
| 	NI_AttrList,		/* 1: Mft record contains an attribute list. */ | ||||
| 	NI_AttrListDirty,	/* 1: Attribute list needs to be written to the
 | ||||
| 				      mft record and then to disk. */ | ||||
| 	NI_FileNameDirty,	/* 1: FILE_NAME attributes need to be updated
 | ||||
| 				      in the index. */ | ||||
| } ntfs_inode_state_bits; | ||||
| 
 | ||||
| #define  test_nino_flag(ni, flag)	   test_bit(NI_##flag, (ni)->state) | ||||
| #define   set_nino_flag(ni, flag)	    set_bit(NI_##flag, (ni)->state) | ||||
| #define clear_nino_flag(ni, flag)	  clear_bit(NI_##flag, (ni)->state) | ||||
| 
 | ||||
| #define test_and_set_nino_flag(ni, flag)	\ | ||||
| 				   test_and_set_bit(NI_##flag, (ni)->state) | ||||
| #define test_and_clear_nino_flag(ni, flag)	\ | ||||
| 				 test_and_clear_bit(NI_##flag, (ni)->state) | ||||
| 
 | ||||
| #define NInoDirty(ni)				  test_nino_flag(ni, Dirty) | ||||
| #define NInoSetDirty(ni)			   set_nino_flag(ni, Dirty) | ||||
| #define NInoClearDirty(ni)			 clear_nino_flag(ni, Dirty) | ||||
| #define NInoTestAndSetDirty(ni)		  test_and_set_nino_flag(ni, Dirty) | ||||
| #define NInoTestAndClearDirty(ni)	test_and_clear_nino_flag(ni, Dirty) | ||||
| 
 | ||||
| #define NInoAttrList(ni)			  test_nino_flag(ni, AttrList) | ||||
| #define NInoSetAttrList(ni)			   set_nino_flag(ni, AttrList) | ||||
| #define NInoClearAttrList(ni)			 clear_nino_flag(ni, AttrList) | ||||
| 
 | ||||
| 
 | ||||
| #define  test_nino_al_flag(ni, flag)	 test_nino_flag(ni, AttrList##flag) | ||||
| #define   set_nino_al_flag(ni, flag)	  set_nino_flag(ni, AttrList##flag) | ||||
| #define clear_nino_al_flag(ni, flag)	clear_nino_flag(ni, AttrList##flag) | ||||
| 
 | ||||
| #define test_and_set_nino_al_flag(ni, flag)	\ | ||||
| 				 test_and_set_nino_flag(ni, AttrList##flag) | ||||
| #define test_and_clear_nino_al_flag(ni, flag)	\ | ||||
| 			       test_and_clear_nino_flag(ni, AttrList##flag) | ||||
| 
 | ||||
| #define NInoAttrListDirty(ni)			    test_nino_al_flag(ni, Dirty) | ||||
| #define NInoAttrListSetDirty(ni)		     set_nino_al_flag(ni, Dirty) | ||||
| #define NInoAttrListClearDirty(ni)		   clear_nino_al_flag(ni, Dirty) | ||||
| #define NInoAttrListTestAndSetDirty(ni)	    test_and_set_nino_al_flag(ni, Dirty) | ||||
| #define NInoAttrListTestAndClearDirty(ni) test_and_clear_nino_al_flag(ni, Dirty) | ||||
| 
 | ||||
| #define NInoFileNameDirty(ni)			\ | ||||
| 					  test_nino_flag(ni, FileNameDirty) | ||||
| #define NInoFileNameSetDirty(ni)		\ | ||||
| 					   set_nino_flag(ni, FileNameDirty) | ||||
| #define NInoFileNameClearDirty(ni)		\ | ||||
| 					 clear_nino_flag(ni, FileNameDirty) | ||||
| #define NInoFileNameTestAndSetDirty(ni)		\ | ||||
| 				  test_and_set_nino_flag(ni, FileNameDirty) | ||||
| #define NInoFileNameTestAndClearDirty(ni)	\ | ||||
| 				test_and_clear_nino_flag(ni, FileNameDirty) | ||||
| 
 | ||||
| /**
 | ||||
|  * struct _ntfs_inode - The NTFS in-memory inode structure. | ||||
|  * | ||||
|  * It is just used as an extension to the fields already provided in the VFS | ||||
|  * inode. | ||||
|  */ | ||||
| struct _ntfs_inode { | ||||
| 	u64 mft_no;		/* Inode / mft record number. */ | ||||
| 	MFT_RECORD *mrec;	/* The actual mft record of the inode. */ | ||||
| 	ntfs_volume *vol;	/* Pointer to the ntfs volume of this inode. */ | ||||
| 	unsigned long state;	/* NTFS specific flags describing this inode.
 | ||||
| 				   See ntfs_inode_state_bits above. */ | ||||
| 	FILE_ATTR_FLAGS flags;	/* Flags describing the file.
 | ||||
| 				   (Copy from STANDARD_INFORMATION) */ | ||||
| 	/*
 | ||||
| 	 * Attribute list support (for use by the attribute lookup functions). | ||||
| 	 * Setup during ntfs_open_inode() for all inodes with attribute lists. | ||||
| 	 * Only valid if NI_AttrList is set in state. | ||||
| 	 */ | ||||
| 	u32 attr_list_size;	/* Length of attribute list value in bytes. */ | ||||
| 	u8 *attr_list;		/* Attribute list value itself. */ | ||||
| 	/* Below fields are always valid. */ | ||||
| 	s32 nr_extents;		/* For a base mft record, the number of
 | ||||
| 				   attached extent inodes (0 if none), for | ||||
| 				   extent records this is -1. */ | ||||
| 	union {		/* This union is only used if nr_extents != 0. */ | ||||
| 		ntfs_inode **extent_nis;/* For nr_extents > 0, array of the
 | ||||
| 					   ntfs inodes of the extent mft | ||||
| 					   records belonging to this base | ||||
| 					   inode which have been loaded. */ | ||||
| 		ntfs_inode *base_ni;	/* For nr_extents == -1, the ntfs
 | ||||
| 					   inode of the base mft record. */ | ||||
| 	}; | ||||
| 
 | ||||
| 	/* Temp: for directory handling */ | ||||
| 	void *private_data;	/* ntfs_dt containing this inode */ | ||||
| 	int ref_count; | ||||
| 
 | ||||
| 	/* Below fields are valid only for base inode. */ | ||||
| 	s64 data_size;		/* Data size stored in the filename index. */ | ||||
| 	s64 allocated_size;	/* Allocated size stored in the filename
 | ||||
| 				   index. (NOTE: Equal to allocated size of | ||||
| 				   the unnamed data attribute for normal or | ||||
| 				   encrypted files and to compressed size | ||||
| 				   of the unnamed data attribute for sparse or | ||||
| 				   compressed files.) */ | ||||
| 
 | ||||
| 	time_t creation_time; | ||||
| 	time_t last_data_change_time; | ||||
| 	time_t last_mft_change_time; | ||||
| 	time_t last_access_time; | ||||
| }; | ||||
| 
 | ||||
| extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol); | ||||
| 
 | ||||
| extern ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref); | ||||
| 
 | ||||
| extern int ntfs_inode_close(ntfs_inode *ni); | ||||
| 
 | ||||
| extern ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, | ||||
| 		const MFT_REF mref); | ||||
| 
 | ||||
| extern int ntfs_inode_attach_all_extents(ntfs_inode *ni); | ||||
| 
 | ||||
| extern void ntfs_inode_mark_dirty(ntfs_inode *ni); | ||||
| 
 | ||||
| extern void ntfs_inode_update_atime(ntfs_inode *ni); | ||||
| extern void ntfs_inode_update_time(ntfs_inode *ni); | ||||
| 
 | ||||
| extern int ntfs_inode_sync(ntfs_inode *ni); | ||||
| 
 | ||||
| extern int ntfs_inode_add_attrlist(ntfs_inode *ni); | ||||
| 
 | ||||
| extern int ntfs_inode_free_space(ntfs_inode *ni, int size); | ||||
| 
 | ||||
| extern int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *a); | ||||
| 
 | ||||
| #endif /* defined _NTFS_INODE_H */ | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,50 @@ | |||
| /*
 | ||||
|  * lcnalloc.h - Exports for cluster (de)allocation. Originated from the Linux-NTFS | ||||
|  *		project. | ||||
|  * | ||||
|  * Copyright (c) 2002 Anton Altaparmakov | ||||
|  * Copyright (c) 2004 Yura Pakhuchiy | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_LCNALLOC_H | ||||
| #define _NTFS_LCNALLOC_H | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "runlist.h" | ||||
| #include "volume.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * enum NTFS_CLUSTER_ALLOCATION_ZONES - | ||||
|  */ | ||||
| typedef enum { | ||||
| 	FIRST_ZONE	= 0,	/* For sanity checking. */ | ||||
| 	MFT_ZONE	= 0,	/* Allocate from $MFT zone. */ | ||||
| 	DATA_ZONE	= 1,	/* Allocate from $DATA zone. */ | ||||
| 	LAST_ZONE	= 1,	/* For sanity checking. */ | ||||
| } NTFS_CLUSTER_ALLOCATION_ZONES; | ||||
| 
 | ||||
| extern runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, | ||||
| 		LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone); | ||||
| 
 | ||||
| extern int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl); | ||||
| 
 | ||||
| extern int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, | ||||
| 		s64 count); | ||||
| 
 | ||||
| #endif /* defined _NTFS_LCNALLOC_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,192 @@ | |||
| /*
 | ||||
|  * list.h - Linked list implementation. Originated from 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 NTFS-3G | ||||
|  * 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 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 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 */ | ||||
| 
 | ||||
|  | @ -0,0 +1,394 @@ | |||
| /*
 | ||||
|  * logfile.h - Exports for $LogFile handling.  Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2000-2005 Anton Altaparmakov | ||||
|  * | ||||
|  * This program/include file is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as published | ||||
|  * by the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program/include file is distributed in the hope that it will be | ||||
|  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty | ||||
|  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program (in the main directory of the NTFS-3G | ||||
|  * 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_LOGFILE_H | ||||
| #define _NTFS_LOGFILE_H | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "endians.h" | ||||
| #include "layout.h" | ||||
| 
 | ||||
| /*
 | ||||
|  * Journal ($LogFile) organization: | ||||
|  * | ||||
|  * Two restart areas present in the first two pages (restart pages, one restart | ||||
|  * area in each page).  When the volume is dismounted they should be identical, | ||||
|  * except for the update sequence array which usually has a different update | ||||
|  * sequence number. | ||||
|  * | ||||
|  * These are followed by log records organized in pages headed by a log record | ||||
|  * header going up to log file size.  Not all pages contain log records when a | ||||
|  * volume is first formatted, but as the volume ages, all records will be used. | ||||
|  * When the log file fills up, the records at the beginning are purged (by | ||||
|  * modifying the oldest_lsn to a higher value presumably) and writing begins | ||||
|  * at the beginning of the file.  Effectively, the log file is viewed as a | ||||
|  * circular entity. | ||||
|  * | ||||
|  * NOTE: Windows NT, 2000, and XP all use log file version 1.1 but they accept | ||||
|  * versions <= 1.x, including 0.-1.  (Yes, that is a minus one in there!)  We | ||||
|  * probably only want to support 1.1 as this seems to be the current version | ||||
|  * and we don't know how that differs from the older versions.  The only | ||||
|  * exception is if the journal is clean as marked by the two restart pages | ||||
|  * then it doesn't matter whether we are on an earlier version.  We can just | ||||
|  * reinitialize the logfile and start again with version 1.1. | ||||
|  */ | ||||
| 
 | ||||
| /* Some $LogFile related constants. */ | ||||
| #define MaxLogFileSize		0x100000000ULL | ||||
| #define DefaultLogPageSize	4096 | ||||
| #define MinLogRecordPages	48 | ||||
| 
 | ||||
| /**
 | ||||
|  * struct RESTART_PAGE_HEADER - Log file restart page header. | ||||
|  * | ||||
|  * Begins the restart area. | ||||
|  */ | ||||
| typedef struct { | ||||
| /*Ofs*/ | ||||
| /*  0	NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ | ||||
| /*  0*/	NTFS_RECORD_TYPES magic;/* The magic is "RSTR". */ | ||||
| /*  4*/	le16 usa_ofs;		/* See NTFS_RECORD definition in layout.h.
 | ||||
| 				   When creating, set this to be immediately | ||||
| 				   after this header structure (without any | ||||
| 				   alignment). */ | ||||
| /*  6*/	le16 usa_count;		/* See NTFS_RECORD definition in layout.h. */ | ||||
| 
 | ||||
| /*  8*/	leLSN chkdsk_lsn;	/* The last log file sequence number found by
 | ||||
| 				   chkdsk.  Only used when the magic is changed | ||||
| 				   to "CHKD".  Otherwise this is zero. */ | ||||
| /* 16*/	le32 system_page_size;	/* Byte size of system pages when the log file
 | ||||
| 				   was created, has to be >= 512 and a power of | ||||
| 				   2.  Use this to calculate the required size | ||||
| 				   of the usa (usa_count) and add it to usa_ofs. | ||||
| 				   Then verify that the result is less than the | ||||
| 				   value of the restart_area_offset. */ | ||||
| /* 20*/	le32 log_page_size;	/* Byte size of log file pages, has to be >=
 | ||||
| 				   512 and a power of 2.  The default is 4096 | ||||
| 				   and is used when the system page size is | ||||
| 				   between 4096 and 8192.  Otherwise this is | ||||
| 				   set to the system page size instead. */ | ||||
| /* 24*/	le16 restart_area_offset;/* Byte offset from the start of this header to
 | ||||
| 				   the RESTART_AREA.  Value has to be aligned | ||||
| 				   to 8-byte boundary.  When creating, set this | ||||
| 				   to be after the usa. */ | ||||
| /* 26*/	sle16 minor_ver;	/* Log file minor version.  Only check if major
 | ||||
| 				   version is 1. */ | ||||
| /* 28*/	sle16 major_ver;	/* Log file major version.  We only support
 | ||||
| 				   version 1.1. */ | ||||
| /* sizeof() = 30 (0x1e) bytes */ | ||||
| } __attribute__((__packed__)) RESTART_PAGE_HEADER; | ||||
| 
 | ||||
| /*
 | ||||
|  * Constant for the log client indices meaning that there are no client records | ||||
|  * in this particular client array.  Also inside the client records themselves, | ||||
|  * this means that there are no client records preceding or following this one. | ||||
|  */ | ||||
| #define LOGFILE_NO_CLIENT	const_cpu_to_le16(0xffff) | ||||
| #define LOGFILE_NO_CLIENT_CPU	0xffff | ||||
| 
 | ||||
| /*
 | ||||
|  * These are the so far known RESTART_AREA_* flags (16-bit) which contain | ||||
|  * information about the log file in which they are present. | ||||
|  */ | ||||
| enum { | ||||
| 	RESTART_VOLUME_IS_CLEAN	= const_cpu_to_le16(0x0002), | ||||
| 	RESTART_SPACE_FILLER	= 0xffff, /* gcc: Force enum bit width to 16. */ | ||||
| } __attribute__((__packed__)); | ||||
| 
 | ||||
| typedef le16 RESTART_AREA_FLAGS; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct RESTART_AREA - Log file restart area record. | ||||
|  * | ||||
|  * The offset of this record is found by adding the offset of the | ||||
|  * RESTART_PAGE_HEADER to the restart_area_offset value found in it. | ||||
|  * See notes at restart_area_offset above. | ||||
|  */ | ||||
| typedef struct { | ||||
| /*Ofs*/ | ||||
| /*  0*/	leLSN current_lsn;	/* The current, i.e. last LSN inside the log
 | ||||
| 				   when the restart area was last written. | ||||
| 				   This happens often but what is the interval? | ||||
| 				   Is it just fixed time or is it every time a | ||||
| 				   check point is written or something else? | ||||
| 				   On create set to 0. */ | ||||
| /*  8*/	le16 log_clients;	/* Number of log client records in the array of
 | ||||
| 				   log client records which follows this | ||||
| 				   restart area.  Must be 1.  */ | ||||
| /* 10*/	le16 client_free_list;	/* The index of the first free log client record
 | ||||
| 				   in the array of log client records. | ||||
| 				   LOGFILE_NO_CLIENT means that there are no | ||||
| 				   free log client records in the array. | ||||
| 				   If != LOGFILE_NO_CLIENT, check that | ||||
| 				   log_clients > client_free_list.  On Win2k | ||||
| 				   and presumably earlier, on a clean volume | ||||
| 				   this is != LOGFILE_NO_CLIENT, and it should | ||||
| 				   be 0, i.e. the first (and only) client | ||||
| 				   record is free and thus the logfile is | ||||
| 				   closed and hence clean.  A dirty volume | ||||
| 				   would have left the logfile open and hence | ||||
| 				   this would be LOGFILE_NO_CLIENT.  On WinXP | ||||
| 				   and presumably later, the logfile is always | ||||
| 				   open, even on clean shutdown so this should | ||||
| 				   always be LOGFILE_NO_CLIENT. */ | ||||
| /* 12*/	le16 client_in_use_list;/* The index of the first in-use log client
 | ||||
| 				   record in the array of log client records. | ||||
| 				   LOGFILE_NO_CLIENT means that there are no | ||||
| 				   in-use log client records in the array.  If | ||||
| 				   != LOGFILE_NO_CLIENT check that log_clients | ||||
| 				   > client_in_use_list.  On Win2k and | ||||
| 				   presumably earlier, on a clean volume this | ||||
| 				   is LOGFILE_NO_CLIENT, i.e. there are no | ||||
| 				   client records in use and thus the logfile | ||||
| 				   is closed and hence clean.  A dirty volume | ||||
| 				   would have left the logfile open and hence | ||||
| 				   this would be != LOGFILE_NO_CLIENT, and it | ||||
| 				   should be 0, i.e. the first (and only) | ||||
| 				   client record is in use.  On WinXP and | ||||
| 				   presumably later, the logfile is always | ||||
| 				   open, even on clean shutdown so this should | ||||
| 				   always be 0. */ | ||||
| /* 14*/	RESTART_AREA_FLAGS flags;/* Flags modifying LFS behaviour.  On Win2k
 | ||||
| 				   and presumably earlier this is always 0.  On | ||||
| 				   WinXP and presumably later, if the logfile | ||||
| 				   was shutdown cleanly, the second bit, | ||||
| 				   RESTART_VOLUME_IS_CLEAN, is set.  This bit | ||||
| 				   is cleared when the volume is mounted by | ||||
| 				   WinXP and set when the volume is dismounted, | ||||
| 				   thus if the logfile is dirty, this bit is | ||||
| 				   clear.  Thus we don't need to check the | ||||
| 				   Windows version to determine if the logfile | ||||
| 				   is clean.  Instead if the logfile is closed, | ||||
| 				   we know it must be clean.  If it is open and | ||||
| 				   this bit is set, we also know it must be | ||||
| 				   clean.  If on the other hand the logfile is | ||||
| 				   open and this bit is clear, we can be almost | ||||
| 				   certain that the logfile is dirty. */ | ||||
| /* 16*/	le32 seq_number_bits;	/* How many bits to use for the sequence
 | ||||
| 				   number.  This is calculated as 67 - the | ||||
| 				   number of bits required to store the logfile | ||||
| 				   size in bytes and this can be used in with | ||||
| 				   the specified file_size as a consistency | ||||
| 				   check. */ | ||||
| /* 20*/	le16 restart_area_length;/* Length of the restart area including the
 | ||||
| 				   client array.  Following checks required if | ||||
| 				   version matches.  Otherwise, skip them. | ||||
| 				   restart_area_offset + restart_area_length | ||||
| 				   has to be <= system_page_size.  Also, | ||||
| 				   restart_area_length has to be >= | ||||
| 				   client_array_offset + (log_clients * | ||||
| 				   sizeof(log client record)). */ | ||||
| /* 22*/	le16 client_array_offset;/* Offset from the start of this record to
 | ||||
| 				   the first log client record if versions are | ||||
| 				   matched.  When creating, set this to be | ||||
| 				   after this restart area structure, aligned | ||||
| 				   to 8-bytes boundary.  If the versions do not | ||||
| 				   match, this is ignored and the offset is | ||||
| 				   assumed to be (sizeof(RESTART_AREA) + 7) & | ||||
| 				   ~7, i.e. rounded up to first 8-byte | ||||
| 				   boundary.  Either way, client_array_offset | ||||
| 				   has to be aligned to an 8-byte boundary. | ||||
| 				   Also, restart_area_offset + | ||||
| 				   client_array_offset has to be <= 510. | ||||
| 				   Finally, client_array_offset + (log_clients | ||||
| 				   * sizeof(log client record)) has to be <= | ||||
| 				   system_page_size.  On Win2k and presumably | ||||
| 				   earlier, this is 0x30, i.e. immediately | ||||
| 				   following this record.  On WinXP and | ||||
| 				   presumably later, this is 0x40, i.e. there | ||||
| 				   are 16 extra bytes between this record and | ||||
| 				   the client array.  This probably means that | ||||
| 				   the RESTART_AREA record is actually bigger | ||||
| 				   in WinXP and later. */ | ||||
| /* 24*/	sle64 file_size;	/* Usable byte size of the log file.  If the
 | ||||
| 				   restart_area_offset + the offset of the | ||||
| 				   file_size are > 510 then corruption has | ||||
| 				   occurred.  This is the very first check when | ||||
| 				   starting with the restart_area as if it | ||||
| 				   fails it means that some of the above values | ||||
| 				   will be corrupted by the multi sector | ||||
| 				   transfer protection.  The file_size has to | ||||
| 				   be rounded down to be a multiple of the | ||||
| 				   log_page_size in the RESTART_PAGE_HEADER and | ||||
| 				   then it has to be at least big enough to | ||||
| 				   store the two restart pages and 48 (0x30) | ||||
| 				   log record pages. */ | ||||
| /* 32*/	le32 last_lsn_data_length;/* Length of data of last LSN, not including
 | ||||
| 				   the log record header.  On create set to | ||||
| 				   0. */ | ||||
| /* 36*/	le16 log_record_header_length;/* Byte size of the log record header.
 | ||||
| 				   If the version matches then check that the | ||||
| 				   value of log_record_header_length is a | ||||
| 				   multiple of 8, i.e. | ||||
| 				   (log_record_header_length + 7) & ~7 == | ||||
| 				   log_record_header_length.  When creating set | ||||
| 				   it to sizeof(LOG_RECORD_HEADER), aligned to | ||||
| 				   8 bytes. */ | ||||
| /* 38*/	le16 log_page_data_offset;/* Offset to the start of data in a log record
 | ||||
| 				   page.  Must be a multiple of 8.  On create | ||||
| 				   set it to immediately after the update | ||||
| 				   sequence array of the log record page. */ | ||||
| /* 40*/	le32 restart_log_open_count;/* A counter that gets incremented every
 | ||||
| 				   time the logfile is restarted which happens | ||||
| 				   at mount time when the logfile is opened. | ||||
| 				   When creating set to a random value.  Win2k | ||||
| 				   sets it to the low 32 bits of the current | ||||
| 				   system time in NTFS format (see time.h). */ | ||||
| /* 44*/	le32 reserved;		/* Reserved/alignment to 8-byte boundary. */ | ||||
| /* sizeof() = 48 (0x30) bytes */ | ||||
| } __attribute__((__packed__)) RESTART_AREA; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct LOG_CLIENT_RECORD - Log client record. | ||||
|  * | ||||
|  * The offset of this record is found by adding the offset of the | ||||
|  * RESTART_AREA to the client_array_offset value found in it. | ||||
|  */ | ||||
| typedef struct { | ||||
| /*Ofs*/ | ||||
| /*  0*/	leLSN oldest_lsn;	/* Oldest LSN needed by this client.  On create
 | ||||
| 				   set to 0. */ | ||||
| /*  8*/	leLSN client_restart_lsn;/* LSN at which this client needs to restart
 | ||||
| 				   the volume, i.e. the current position within | ||||
| 				   the log file.  At present, if clean this | ||||
| 				   should = current_lsn in restart area but it | ||||
| 				   probably also = current_lsn when dirty most | ||||
| 				   of the time.  At create set to 0. */ | ||||
| /* 16*/	le16 prev_client;	/* The offset to the previous log client record
 | ||||
| 				   in the array of log client records. | ||||
| 				   LOGFILE_NO_CLIENT means there is no previous | ||||
| 				   client record, i.e. this is the first one. | ||||
| 				   This is always LOGFILE_NO_CLIENT. */ | ||||
| /* 18*/	le16 next_client;	/* The offset to the next log client record in
 | ||||
| 				   the array of log client records. | ||||
| 				   LOGFILE_NO_CLIENT means there are no next | ||||
| 				   client records, i.e. this is the last one. | ||||
| 				   This is always LOGFILE_NO_CLIENT. */ | ||||
| /* 20*/	le16 seq_number;	/* On Win2k and presumably earlier, this is set
 | ||||
| 				   to zero every time the logfile is restarted | ||||
| 				   and it is incremented when the logfile is | ||||
| 				   closed at dismount time.  Thus it is 0 when | ||||
| 				   dirty and 1 when clean.  On WinXP and | ||||
| 				   presumably later, this is always 0. */ | ||||
| /* 22*/	u8 reserved[6];		/* Reserved/alignment. */ | ||||
| /* 28*/	le32 client_name_length;/* Length of client name in bytes.  Should
 | ||||
| 				   always be 8. */ | ||||
| /* 32*/	ntfschar client_name[64];/* Name of the client in Unicode.  Should
 | ||||
| 				   always be "NTFS" with the remaining bytes | ||||
| 				   set to 0. */ | ||||
| /* sizeof() = 160 (0xa0) bytes */ | ||||
| } __attribute__((__packed__)) LOG_CLIENT_RECORD; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct RECORD_PAGE_HEADER - Log page record page header. | ||||
|  * | ||||
|  * Each log page begins with this header and is followed by several LOG_RECORD | ||||
|  * structures, starting at offset 0x40 (the size of this structure and the | ||||
|  * following update sequence array and then aligned to 8 byte boundary, but is | ||||
|  * this specified anywhere?). | ||||
|  */ | ||||
| typedef struct { | ||||
| /*  0	NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ | ||||
| 	NTFS_RECORD_TYPES magic;/* Usually the magic is "RCRD". */ | ||||
| 	u16 usa_ofs;		/* See NTFS_RECORD definition in layout.h.
 | ||||
| 				   When creating, set this to be immediately | ||||
| 				   after this header structure (without any | ||||
| 				   alignment). */ | ||||
| 	u16 usa_count;		/* See NTFS_RECORD definition in layout.h. */ | ||||
| 
 | ||||
| 	union { | ||||
| 		LSN last_lsn; | ||||
| 		s64 file_offset; | ||||
| 	} __attribute__((__packed__)) copy; | ||||
| 	u32 flags; | ||||
| 	u16 page_count; | ||||
| 	u16 page_position; | ||||
| 	union { | ||||
| 		struct { | ||||
| 			u16 next_record_offset; | ||||
| 			u8 reserved[6]; | ||||
| 			LSN last_end_lsn; | ||||
| 		} __attribute__((__packed__)) packed; | ||||
| 	} __attribute__((__packed__)) header; | ||||
| } __attribute__((__packed__)) RECORD_PAGE_HEADER; | ||||
| 
 | ||||
| /**
 | ||||
|  * enum LOG_RECORD_FLAGS - Possible 16-bit flags for log records. | ||||
|  * | ||||
|  * (Or is it log record pages?) | ||||
|  */ | ||||
| typedef enum { | ||||
| 	LOG_RECORD_MULTI_PAGE = const_cpu_to_le16(0x0001),	/* ??? */ | ||||
| 	LOG_RECORD_SIZE_PLACE_HOLDER = 0xffff, | ||||
| 		/* This has nothing to do with the log record. It is only so
 | ||||
| 		   gcc knows to make the flags 16-bit. */ | ||||
| } __attribute__((__packed__)) LOG_RECORD_FLAGS; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct LOG_CLIENT_ID - The log client id structure identifying a log client. | ||||
|  */ | ||||
| typedef struct { | ||||
| 	u16 seq_number; | ||||
| 	u16 client_index; | ||||
| } __attribute__((__packed__)) LOG_CLIENT_ID; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct LOG_RECORD - Log record header. | ||||
|  * | ||||
|  * Each log record seems to have a constant size of 0x70 bytes. | ||||
|  */ | ||||
| typedef struct { | ||||
| 	LSN this_lsn; | ||||
| 	LSN client_previous_lsn; | ||||
| 	LSN client_undo_next_lsn; | ||||
| 	u32 client_data_length; | ||||
| 	LOG_CLIENT_ID client_id; | ||||
| 	u32 record_type; | ||||
| 	u32 transaction_id; | ||||
| 	u16 flags; | ||||
| 	u16 reserved_or_alignment[3]; | ||||
| /* Now are at ofs 0x30 into struct. */ | ||||
| 	u16 redo_operation; | ||||
| 	u16 undo_operation; | ||||
| 	u16 redo_offset; | ||||
| 	u16 redo_length; | ||||
| 	u16 undo_offset; | ||||
| 	u16 undo_length; | ||||
| 	u16 target_attribute; | ||||
| 	u16 lcns_to_follow;		   /* Number of lcn_list entries
 | ||||
| 					      following this entry. */ | ||||
| /* Now at ofs 0x40. */ | ||||
| 	u16 record_offset; | ||||
| 	u16 attribute_offset; | ||||
| 	u32 alignment_or_reserved; | ||||
| 	VCN target_vcn; | ||||
| /* Now at ofs 0x50. */ | ||||
| 	struct {			   /* Only present if lcns_to_follow
 | ||||
| 					      is not 0. */ | ||||
| 		LCN lcn; | ||||
| 	} __attribute__((__packed__)) lcn_list[0]; | ||||
| } __attribute__((__packed__)) LOG_RECORD; | ||||
| 
 | ||||
| extern BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp); | ||||
| extern BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp); | ||||
| extern int ntfs_empty_logfile(ntfs_attr *na); | ||||
| 
 | ||||
| #endif /* defined _NTFS_LOGFILE_H */ | ||||
|  | @ -0,0 +1,111 @@ | |||
| /*
 | ||||
|  * logging.h - Centralised logging. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 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 NTFS-3G | ||||
|  * 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 _LOGGING_H_ | ||||
| #define _LOGGING_H_ | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| 
 | ||||
| #ifdef HAVE_STDARG_H | ||||
| #include <stdarg.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "types.h" | ||||
| 
 | ||||
| /* Function prototype for the logging handlers */ | ||||
| typedef int (ntfs_log_handler)(const char *function, const char *file, int line, | ||||
| 	u32 level, void *data, const char *format, va_list args); | ||||
| 
 | ||||
| /* Set the logging handler from one of the functions, below. */ | ||||
| void ntfs_log_set_handler(ntfs_log_handler *handler); | ||||
| 
 | ||||
| /* Logging handlers */ | ||||
| ntfs_log_handler ntfs_log_handler_syslog  __attribute__((format(printf, 6, 0))); | ||||
| ntfs_log_handler ntfs_log_handler_fprintf __attribute__((format(printf, 6, 0))); | ||||
| ntfs_log_handler ntfs_log_handler_null    __attribute__((format(printf, 6, 0))); | ||||
| ntfs_log_handler ntfs_log_handler_stdout  __attribute__((format(printf, 6, 0))); | ||||
| ntfs_log_handler ntfs_log_handler_outerr  __attribute__((format(printf, 6, 0))); | ||||
| ntfs_log_handler ntfs_log_handler_stderr  __attribute__((format(printf, 6, 0))); | ||||
| 
 | ||||
| /* Enable/disable certain log levels */ | ||||
| u32 ntfs_log_set_levels(u32 levels); | ||||
| u32 ntfs_log_clear_levels(u32 levels); | ||||
| u32 ntfs_log_get_levels(void); | ||||
| 
 | ||||
| /* Enable/disable certain log flags */ | ||||
| u32 ntfs_log_set_flags(u32 flags); | ||||
| u32 ntfs_log_clear_flags(u32 flags); | ||||
| u32 ntfs_log_get_flags(void); | ||||
| 
 | ||||
| /* Turn command-line options into logging flags */ | ||||
| BOOL ntfs_log_parse_option(const char *option); | ||||
| 
 | ||||
| int ntfs_log_redirect(const char *function, const char *file, int line, | ||||
| 	u32 level, void *data, const char *format, ...) | ||||
| 	__attribute__((format(printf, 6, 7))); | ||||
| 
 | ||||
| /* Logging levels - Determine what gets logged */ | ||||
| #define NTFS_LOG_LEVEL_DEBUG	(1 <<  0) /* x = 42 */ | ||||
| #define NTFS_LOG_LEVEL_TRACE	(1 <<  1) /* Entering function x() */ | ||||
| #define NTFS_LOG_LEVEL_QUIET	(1 <<  2) /* Quietable output */ | ||||
| #define NTFS_LOG_LEVEL_INFO	(1 <<  3) /* Volume needs defragmenting */ | ||||
| #define NTFS_LOG_LEVEL_VERBOSE	(1 <<  4) /* Forced to continue */ | ||||
| #define NTFS_LOG_LEVEL_PROGRESS	(1 <<  5) /* 54% complete */ | ||||
| #define NTFS_LOG_LEVEL_WARNING	(1 <<  6) /* You should backup before starting */ | ||||
| #define NTFS_LOG_LEVEL_ERROR	(1 <<  7) /* Operation failed, no damage done */ | ||||
| #define NTFS_LOG_LEVEL_PERROR	(1 <<  8) /* Message : standard error description */ | ||||
| #define NTFS_LOG_LEVEL_CRITICAL	(1 <<  9) /* Operation failed,damage may have occurred */ | ||||
| 
 | ||||
| /* Logging style flags - Manage the style of the output */ | ||||
| #define NTFS_LOG_FLAG_PREFIX	(1 << 0) /* Prefix messages with "ERROR: ", etc */ | ||||
| #define NTFS_LOG_FLAG_FILENAME	(1 << 1) /* Show the file origin of the message */ | ||||
| #define NTFS_LOG_FLAG_LINE	(1 << 2) /* Show the line number of the message */ | ||||
| #define NTFS_LOG_FLAG_FUNCTION	(1 << 3) /* Show the function name containing the message */ | ||||
| #define NTFS_LOG_FLAG_ONLYNAME	(1 << 4) /* Only display the filename, not the pathname */ | ||||
| #define NTFS_LOG_FLAG_COLOUR	(1 << 5) /* Colour highlight some messages */ | ||||
| 
 | ||||
| /* Macros to simplify logging.  One for each level defined above.
 | ||||
|  * Note, ntfs_log_debug/trace have effect only if DEBUG is defined. | ||||
|  */ | ||||
| #define ntfs_log_critical(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_CRITICAL,NULL,FORMAT,##ARGS) | ||||
| #define ntfs_log_error(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ERROR,NULL,FORMAT,##ARGS) | ||||
| #define ntfs_log_info(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_INFO,NULL,FORMAT,##ARGS) | ||||
| #define ntfs_log_perror(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PERROR,NULL,FORMAT,##ARGS) | ||||
| #define ntfs_log_progress(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PROGRESS,NULL,FORMAT,##ARGS) | ||||
| #define ntfs_log_quiet(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_QUIET,NULL,FORMAT,##ARGS) | ||||
| #define ntfs_log_verbose(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_VERBOSE,NULL,FORMAT,##ARGS) | ||||
| #define ntfs_log_warning(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_WARNING,NULL,FORMAT,##ARGS) | ||||
| 
 | ||||
| /* By default debug and trace messages are compiled into the program,
 | ||||
|  * but not displayed. | ||||
|  */ | ||||
| #ifdef DEBUG | ||||
| #define ntfs_log_debug(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_DEBUG,NULL,FORMAT,##ARGS) | ||||
| #define ntfs_log_trace(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_TRACE,NULL,FORMAT,##ARGS) | ||||
| #else | ||||
| #define ntfs_log_debug(FORMAT, ARGS...)do {} while (0) | ||||
| #define ntfs_log_trace(FORMAT, ARGS...)do {} while (0) | ||||
| #endif /* DEBUG */ | ||||
| 
 | ||||
| #endif /* _LOGGING_H_ */ | ||||
| 
 | ||||
|  | @ -0,0 +1,118 @@ | |||
| /*
 | ||||
|  * mft.h - Exports for MFT record handling. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2000-2002 Anton Altaparmakov | ||||
|  * Copyright (c) 2004-2005 Richard Russon | ||||
|  * Copyright (c) 2006 Szabolcs Szakacsits | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_MFT_H | ||||
| #define _NTFS_MFT_H | ||||
| 
 | ||||
| #include "volume.h" | ||||
| #include "inode.h" | ||||
| #include "layout.h" | ||||
| 
 | ||||
| extern int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, | ||||
| 		const s64 count, MFT_RECORD *b); | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_mft_record_read - read a record from the mft | ||||
|  * @vol:	volume to read from | ||||
|  * @mref:	mft record number to read | ||||
|  * @b:		output data buffer | ||||
|  * | ||||
|  * Read the mft record specified by @mref from volume @vol into buffer @b. | ||||
|  * Return 0 on success or -1 on error, with errno set to the error code. | ||||
|  * | ||||
|  * The read mft record is mst deprotected and is hence ready to use. The caller | ||||
|  * should check the record with is_baad_record() in case mst deprotection | ||||
|  * failed. | ||||
|  * | ||||
|  * NOTE: @b has to be at least of size vol->mft_record_size. | ||||
|  */ | ||||
| static __inline__ int ntfs_mft_record_read(const ntfs_volume *vol, | ||||
| 		const MFT_REF mref, MFT_RECORD *b) | ||||
| { | ||||
| 	return ntfs_mft_records_read(vol, mref, 1, b); | ||||
| } | ||||
| 
 | ||||
| extern int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, | ||||
| 		MFT_RECORD **mrec, ATTR_RECORD **attr); | ||||
| 
 | ||||
| extern int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, | ||||
| 		const s64 count, MFT_RECORD *b); | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_mft_record_write - write an mft record to disk | ||||
|  * @vol:	volume to write to | ||||
|  * @mref:	mft record number to write | ||||
|  * @b:		data buffer containing the mft record to write | ||||
|  * | ||||
|  * Write the mft record specified by @mref from buffer @b to volume @vol. | ||||
|  * Return 0 on success or -1 on error, with errno set to the error code. | ||||
|  * | ||||
|  * Before the mft record is written, it is mst protected. After the write, it | ||||
|  * is deprotected again, thus resulting in an increase in the update sequence | ||||
|  * number inside the buffer @b. | ||||
|  * | ||||
|  * NOTE: @b has to be at least of size vol->mft_record_size. | ||||
|  */ | ||||
| static __inline__ int ntfs_mft_record_write(const ntfs_volume *vol, | ||||
| 		const MFT_REF mref, MFT_RECORD *b) | ||||
| { | ||||
| 	return ntfs_mft_records_write(vol, mref, 1, b); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_mft_record_get_data_size - return number of bytes used in mft record @b | ||||
|  * @m:		mft record to get the data size of | ||||
|  * | ||||
|  * Takes the mft record @m and returns the number of bytes used in the record | ||||
|  * or 0 on error (i.e. @m is not a valid mft record).  Zero is not a valid size | ||||
|  * for an mft record as it at least has to have the MFT_RECORD itself and a | ||||
|  * zero length attribute of type AT_END, thus making the minimum size 56 bytes. | ||||
|  * | ||||
|  * Aside:  The size is independent of NTFS versions 1.x/3.x because the 8-byte | ||||
|  * alignment of the first attribute mask the difference in MFT_RECORD size | ||||
|  * between NTFS 1.x and 3.x.  Also, you would expect every mft record to | ||||
|  * contain an update sequence array as well but that could in theory be | ||||
|  * non-existent (don't know if Windows' NTFS driver/chkdsk wouldn't view this | ||||
|  * as corruption in itself though). | ||||
|  */ | ||||
| static __inline__ u32 ntfs_mft_record_get_data_size(const MFT_RECORD *m) | ||||
| { | ||||
| 	if (!m || !ntfs_is_mft_record(m->magic)) | ||||
| 		return 0; | ||||
| 	/* Get the number of used bytes and return it. */ | ||||
| 	return le32_to_cpu(m->bytes_in_use); | ||||
| } | ||||
| 
 | ||||
| extern int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, | ||||
| 		MFT_RECORD *mrec); | ||||
| 
 | ||||
| extern int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref); | ||||
| 
 | ||||
| extern ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni); | ||||
| 
 | ||||
| extern int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni); | ||||
| 
 | ||||
| extern int ntfs_mft_usn_dec(MFT_RECORD *mrec); | ||||
| 
 | ||||
| #endif /* defined _NTFS_MFT_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,8 @@ | |||
| #ifndef _NTFS_MISC_H_ | ||||
| #define _NTFS_MISC_H_ | ||||
| 
 | ||||
| void *ntfs_calloc(size_t size); | ||||
| void *ntfs_malloc(size_t size); | ||||
| 
 | ||||
| #endif /* _NTFS_MISC_H_ */ | ||||
| 
 | ||||
|  | @ -0,0 +1,34 @@ | |||
| /*
 | ||||
|  * mst.h - Exports for multi sector transfer fixup functions. | ||||
|  *         Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2000-2002 Anton Altaparmakov | ||||
|  * | ||||
|  * This program/include file is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as published | ||||
|  * by the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program/include file is distributed in the hope that it will be | ||||
|  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty | ||||
|  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program (in the main directory of the NTFS-3G | ||||
|  * 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_MST_H | ||||
| #define _NTFS_MST_H | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "layout.h" | ||||
| 
 | ||||
| extern int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size); | ||||
| extern int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size); | ||||
| extern void ntfs_mst_post_write_fixup(NTFS_RECORD *b); | ||||
| 
 | ||||
| #endif /* defined _NTFS_MST_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,69 @@ | |||
| /*
 | ||||
|  * ntfstime.h - NTFS time related functions.  Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2005 Anton Altaparmakov | ||||
|  * Copyright (c) 2005 Yura Pakhuchiy | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_NTFSTIME_H | ||||
| #define _NTFS_NTFSTIME_H | ||||
| 
 | ||||
| #ifdef HAVE_TIME_H | ||||
| #include <time.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "types.h" | ||||
| 
 | ||||
| #define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000) | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs2utc - Convert an NTFS time to Unix time | ||||
|  * @ntfs_time:  An NTFS time in 100ns units since 1601 | ||||
|  * | ||||
|  * NTFS stores times as the number of 100ns intervals since January 1st 1601 at | ||||
|  * 00:00 UTC.  This system will not suffer from Y2K problems until ~57000AD. | ||||
|  * | ||||
|  * Return:  n  A Unix time (number of seconds since 1970) | ||||
|  */ | ||||
| static __inline__ time_t ntfs2utc(s64 ntfs_time) | ||||
| { | ||||
| 	return (sle64_to_cpu(ntfs_time) - (NTFS_TIME_OFFSET)) / 10000000; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * utc2ntfs - Convert Linux time to NTFS time | ||||
|  * @utc_time:  Linux time to convert to NTFS | ||||
|  * | ||||
|  * Convert the Linux time @utc_time to its corresponding NTFS time. | ||||
|  * | ||||
|  * Linux stores time in a long at present and measures it as the number of | ||||
|  * 1-second intervals since 1st January 1970, 00:00:00 UTC. | ||||
|  * | ||||
|  * NTFS uses Microsoft's standard time format which is stored in a s64 and is | ||||
|  * measured as the number of 100 nano-second intervals since 1st January 1601, | ||||
|  * 00:00:00 UTC. | ||||
|  * | ||||
|  * Return:  n  An NTFS time (100ns units since Jan 1601) | ||||
|  */ | ||||
| static __inline__ s64 utc2ntfs(time_t utc_time) | ||||
| { | ||||
| 	/* Convert to 100ns intervals and then add the NTFS time offset. */ | ||||
| 	return cpu_to_sle64((s64)utc_time * 10000000 + NTFS_TIME_OFFSET); | ||||
| } | ||||
| 
 | ||||
| #endif /* _NTFS_NTFSTIME_H */ | ||||
|  | @ -0,0 +1,87 @@ | |||
| /*
 | ||||
|  * runlist.h - Exports for runlist handling. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2002 Anton Altaparmakov | ||||
|  * Copyright (c) 2002 Richard Russon | ||||
|  * | ||||
|  * This program/include file is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as published | ||||
|  * by the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program/include file is distributed in the hope that it will be | ||||
|  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty | ||||
|  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program (in the main directory of the NTFS-3G | ||||
|  * 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_RUNLIST_H | ||||
| #define _NTFS_RUNLIST_H | ||||
| 
 | ||||
| #include "types.h" | ||||
| 
 | ||||
| /* Forward declarations */ | ||||
| typedef struct _runlist_element runlist_element; | ||||
| typedef runlist_element runlist; | ||||
| 
 | ||||
| #include "attrib.h" | ||||
| #include "volume.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * struct _runlist_element - in memory vcn to lcn mapping array element. | ||||
|  * @vcn:	starting vcn of the current array element | ||||
|  * @lcn:	starting lcn of the current array element | ||||
|  * @length:	length in clusters of the current array element | ||||
|  * | ||||
|  * The last vcn (in fact the last vcn + 1) is reached when length == 0. | ||||
|  * | ||||
|  * When lcn == -1 this means that the count vcns starting at vcn are not | ||||
|  * physically allocated (i.e. this is a hole / data is sparse). | ||||
|  */ | ||||
| struct _runlist_element {/* In memory vcn to lcn mapping structure element. */ | ||||
| 	VCN vcn;	/* vcn = Starting virtual cluster number. */ | ||||
| 	LCN lcn;	/* lcn = Starting logical cluster number. */ | ||||
| 	s64 length;	/* Run length in clusters. */ | ||||
| }; | ||||
| 
 | ||||
| extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn); | ||||
| 
 | ||||
| extern s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl, | ||||
| 		const s64 pos, s64 count, void *b); | ||||
| extern s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl, | ||||
| 		const s64 pos, s64 count, void *b); | ||||
| 
 | ||||
| extern runlist_element *ntfs_runlists_merge(runlist_element *drl, | ||||
| 		runlist_element *srl); | ||||
| 
 | ||||
| extern runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, | ||||
| 		const ATTR_RECORD *attr, runlist_element *old_rl); | ||||
| 
 | ||||
| extern int ntfs_get_nr_significant_bytes(const s64 n); | ||||
| 
 | ||||
| extern int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, | ||||
| 		const runlist_element *rl, const VCN start_vcn); | ||||
| 
 | ||||
| extern int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max, | ||||
| 		const s64 n); | ||||
| 
 | ||||
| extern int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst, | ||||
| 		const int dst_len, const runlist_element *rl, | ||||
| 		const VCN start_vcn, VCN *const stop_vcn); | ||||
| 
 | ||||
| extern int ntfs_rl_truncate(runlist **arl, const VCN start_vcn); | ||||
| 
 | ||||
| extern int ntfs_rl_sparse(runlist *rl); | ||||
| extern s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl); | ||||
| 
 | ||||
| #ifdef NTFS_TEST | ||||
| int test_rl_main(int argc, char *argv[]); | ||||
| #endif | ||||
| 
 | ||||
| #endif /* defined _NTFS_RUNLIST_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,58 @@ | |||
| /*
 | ||||
|  * security.h - Exports for handling security/ACLs in NTFS.   | ||||
|  *              Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2004      Anton Altaparmakov | ||||
|  * Copyright (c) 2005-2006 Szabolcs Szakacsits | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_SECURITY_H | ||||
| #define _NTFS_SECURITY_H | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "layout.h" | ||||
| #include "inode.h" | ||||
| 
 | ||||
| extern const GUID *const zero_guid; | ||||
| 
 | ||||
| extern BOOL ntfs_guid_is_zero(const GUID *guid); | ||||
| extern char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str); | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_sid_is_valid - determine if a SID is valid | ||||
|  * @sid:	SID for which to determine if it is valid | ||||
|  * | ||||
|  * Determine if the SID pointed to by @sid is valid. | ||||
|  * | ||||
|  * Return TRUE if it is valid and FALSE otherwise. | ||||
|  */ | ||||
| static __inline__ BOOL ntfs_sid_is_valid(const SID *sid) | ||||
| { | ||||
| 	if (!sid || sid->revision != SID_REVISION || | ||||
| 			sid->sub_authority_count > SID_MAX_SUB_AUTHORITIES) | ||||
| 		return FALSE; | ||||
| 	return TRUE; | ||||
| } | ||||
| 
 | ||||
| extern int ntfs_sid_to_mbs_size(const SID *sid); | ||||
| extern char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, | ||||
| 		size_t sid_str_size); | ||||
| extern void ntfs_generate_guid(GUID *guid); | ||||
| extern int ntfs_sd_add_everyone(ntfs_inode *ni); | ||||
| 
 | ||||
| #endif /* defined _NTFS_SECURITY_H */ | ||||
|  | @ -0,0 +1,85 @@ | |||
| /*
 | ||||
|  * support.h - Useful definitions and macros. Originated from 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 NTFS-3G | ||||
|  * 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_SUPPORT_H | ||||
| #define _NTFS_SUPPORT_H | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| 
 | ||||
| #ifdef HAVE_STDDEF_H | ||||
| #include <stddef.h> | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * Our mailing list. Use this define to prevent typos in email address. | ||||
|  */ | ||||
| #define NTFS_DEV_LIST	"ntfs-3g-devel@lists.sf.net" | ||||
| 
 | ||||
| /*
 | ||||
|  * Generic macro to convert pointers to values for comparison purposes. | ||||
|  */ | ||||
| #ifndef p2n | ||||
| #define p2n(p)		((ptrdiff_t)((ptrdiff_t*)(p))) | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * The classic min and max macros. | ||||
|  */ | ||||
| #ifndef min | ||||
| #define min(a,b)	((a) <= (b) ? (a) : (b)) | ||||
| #endif | ||||
| 
 | ||||
| #ifndef max | ||||
| #define max(a,b)	((a) >= (b) ? (a) : (b)) | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * Useful macro for determining the offset of a struct member. | ||||
|  */ | ||||
| #ifndef offsetof | ||||
| #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * Simple bit operation macros. NOTE: These are NOT atomic. | ||||
|  */ | ||||
| #define test_bit(bit, var)	      ((var) & (1 << (bit))) | ||||
| #define set_bit(bit, var)	      (var) |= 1 << (bit) | ||||
| #define clear_bit(bit, var)	      (var) &= ~(1 << (bit)) | ||||
| 
 | ||||
| #define test_and_set_bit(bit, var)			\ | ||||
| ({							\ | ||||
| 	const BOOL old_state = test_bit(bit, var);	\ | ||||
| 	set_bit(bit, var);				\ | ||||
| 	old_state;					\ | ||||
| }) | ||||
| 
 | ||||
| #define test_and_clear_bit(bit, var)			\ | ||||
| ({							\ | ||||
| 	const BOOL old_state = test_bit(bit, var);	\ | ||||
| 	clear_bit(bit, var);				\ | ||||
| 	old_state;					\ | ||||
| }) | ||||
| 
 | ||||
| #endif /* defined _NTFS_SUPPORT_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,124 @@ | |||
| /*
 | ||||
|  * types.h - Misc type definitions not related to on-disk structure.   | ||||
|  *           Originated from 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 NTFS-3G | ||||
|  * 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_TYPES_H | ||||
| #define _NTFS_TYPES_H | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| 
 | ||||
| #if HAVE_STDINT_H || !HAVE_CONFIG_H | ||||
| #include <stdint.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYS_TYPES_H | ||||
| #include <sys/types.h> | ||||
| #endif | ||||
| 
 | ||||
| typedef uint8_t  u8;			/* Unsigned types of an exact size */ | ||||
| typedef uint16_t u16; | ||||
| typedef uint32_t u32; | ||||
| typedef uint64_t u64; | ||||
| 
 | ||||
| typedef int8_t  s8;			/* Signed types of an exact size */ | ||||
| typedef int16_t s16; | ||||
| typedef int32_t s32; | ||||
| typedef int64_t s64; | ||||
| 
 | ||||
| typedef u16 le16; | ||||
| typedef u32 le32; | ||||
| typedef u64 le64; | ||||
| 
 | ||||
| /*
 | ||||
|  * Declare sle{16,32,64} to be unsigned because we do not want sign extension | ||||
|  * on BE architectures. | ||||
|  */ | ||||
| typedef u16 sle16; | ||||
| typedef u32 sle32; | ||||
| typedef u64 sle64; | ||||
| 
 | ||||
| typedef u16 ntfschar;			/* 2-byte Unicode character type. */ | ||||
| #define UCHAR_T_SIZE_BITS 1 | ||||
| 
 | ||||
| /*
 | ||||
|  * Clusters are signed 64-bit values on NTFS volumes.  We define two types, LCN | ||||
|  * and VCN, to allow for type checking and better code readability. | ||||
|  */ | ||||
| typedef s64 VCN; | ||||
| typedef sle64 leVCN; | ||||
| typedef s64 LCN; | ||||
| typedef sle64 leLCN; | ||||
| 
 | ||||
| /*
 | ||||
|  * The NTFS journal $LogFile uses log sequence numbers which are signed 64-bit | ||||
|  * values.  We define our own type LSN, to allow for type checking and better | ||||
|  * code readability. | ||||
|  */ | ||||
| typedef s64 LSN; | ||||
| typedef sle64 leLSN; | ||||
| 
 | ||||
| /*
 | ||||
|  * Cygwin has a collision between our BOOL and <windef.h>'s | ||||
|  * As long as this file will be included after <windows.h> were fine. | ||||
|  */ | ||||
| #ifndef _WINDEF_H | ||||
| /**
 | ||||
|  * enum BOOL - These are just to make the code more readable... | ||||
|  */ | ||||
| typedef enum { | ||||
| #ifndef FALSE | ||||
| 	FALSE = 0, | ||||
| #endif | ||||
| #ifndef NO | ||||
| 	NO = 0, | ||||
| #endif | ||||
| #ifndef ZERO | ||||
| 	ZERO = 0, | ||||
| #endif | ||||
| #ifndef TRUE | ||||
| 	TRUE = 1, | ||||
| #endif | ||||
| #ifndef YES | ||||
| 	YES = 1, | ||||
| #endif | ||||
| #ifndef ONE | ||||
| 	ONE = 1, | ||||
| #endif | ||||
| } BOOL; | ||||
| #endif /* defined _WINDEF_H */ | ||||
| 
 | ||||
| /**
 | ||||
|  * enum IGNORE_CASE_BOOL - | ||||
|  */ | ||||
| typedef enum { | ||||
| 	CASE_SENSITIVE = 0, | ||||
| 	IGNORE_CASE = 1, | ||||
| } IGNORE_CASE_BOOL; | ||||
| 
 | ||||
| #define STATUS_OK				(0) | ||||
| #define STATUS_ERROR				(-1) | ||||
| #define STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT	(-2) | ||||
| #define STATUS_KEEP_SEARCHING			(-3) | ||||
| #define STATUS_NOT_FOUND			(-4) | ||||
| 
 | ||||
| #endif /* defined _NTFS_TYPES_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,69 @@ | |||
| /*
 | ||||
|  * unistr.h - Exports for Unicode string handling. Originated from 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 NTFS-3G | ||||
|  * 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_UNISTR_H | ||||
| #define _NTFS_UNISTR_H | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "layout.h" | ||||
| 
 | ||||
| extern BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len, | ||||
| 		const ntfschar *s2, size_t s2_len, const IGNORE_CASE_BOOL ic, | ||||
| 		const ntfschar *upcase, const u32 upcase_size); | ||||
| 
 | ||||
| extern int ntfs_names_collate(const ntfschar *name1, const u32 name1_len, | ||||
| 		const ntfschar *name2, const u32 name2_len, | ||||
| 		const int err_val, const IGNORE_CASE_BOOL ic, | ||||
| 		const ntfschar *upcase, const u32 upcase_len); | ||||
| 
 | ||||
| extern int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n); | ||||
| 
 | ||||
| extern int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, | ||||
| 		const ntfschar *upcase, const u32 upcase_size); | ||||
| 
 | ||||
| extern u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen); | ||||
| 
 | ||||
| extern ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen); | ||||
| 
 | ||||
| extern void ntfs_name_upcase(ntfschar *name, u32 name_len, | ||||
| 		const ntfschar *upcase, const u32 upcase_len); | ||||
| 
 | ||||
| extern void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr, | ||||
| 		const ntfschar *upcase, const u32 upcase_len); | ||||
| 
 | ||||
| extern int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1, | ||||
| 		const FILE_NAME_ATTR *file_name_attr2, | ||||
| 		const int err_val, const IGNORE_CASE_BOOL ic, | ||||
| 		const ntfschar *upcase, const u32 upcase_len); | ||||
| 
 | ||||
| extern int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, | ||||
| 		int outs_len); | ||||
| extern int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len); | ||||
| 
 | ||||
| extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len); | ||||
| 
 | ||||
| extern ntfschar *ntfs_str2ucs(const char *s, int *len); | ||||
| 
 | ||||
| extern void ntfs_ucsfree(ntfschar *ucs); | ||||
| 
 | ||||
| #endif /* defined _NTFS_UNISTR_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,29 @@ | |||
| /*
 | ||||
|  * version.h - Info about the NTFS library.  Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2005 Anton Altaparmakov | ||||
|  * Copyright (c) 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 NTFS-3G | ||||
|  * 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_VERSION_H_ | ||||
| #define _NTFS_VERSION_H_ | ||||
| 
 | ||||
| extern const char *ntfs_libntfs_version(void); | ||||
| 
 | ||||
| #endif /* _NTFS_VERSION_H_ */ | ||||
| 
 | ||||
|  | @ -0,0 +1,233 @@ | |||
| /*
 | ||||
|  * volume.h - Exports for NTFS volume handling. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2000-2004 Anton Altaparmakov | ||||
|  * Copyright (c) 2004-2005 Richard Russon | ||||
|  * Copyright (c) 2005-2006 Yura Pakhuchiy | ||||
|  * Copyright (c) 2005-2006 Szabolcs Szakacsits | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_VOLUME_H | ||||
| #define _NTFS_VOLUME_H | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| 
 | ||||
| #ifdef HAVE_STDIO_H | ||||
| #include <stdio.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYS_PARAM_H | ||||
| #include <sys/param.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYS_MOUNT_H | ||||
| #include <sys/mount.h> | ||||
| #endif | ||||
| #ifdef HAVE_MNTENT_H | ||||
| #include <mntent.h> | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY and MS_NOATIME, | ||||
|  * so we define them ourselves. | ||||
|  */ | ||||
| #ifndef MS_RDONLY | ||||
| #define MS_RDONLY 1 | ||||
| #endif | ||||
| /*
 | ||||
|  * Solaris defines MS_RDONLY but not MS_NOATIME thus we need to carefully | ||||
|  * define MS_NOATIME. | ||||
|  */ | ||||
| #ifndef MS_NOATIME | ||||
| #if (MS_RDONLY != 1) | ||||
| #	define MS_NOATIME 1 | ||||
| #else | ||||
| #	define MS_NOATIME 2 | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| /* Forward declaration */ | ||||
| typedef struct _ntfs_volume ntfs_volume; | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "support.h" | ||||
| #include "device.h" | ||||
| #include "inode.h" | ||||
| #include "attrib.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * enum ntfs_mount_flags - | ||||
|  * | ||||
|  * Flags returned by the ntfs_check_if_mounted() function. | ||||
|  */ | ||||
| typedef enum { | ||||
| 	NTFS_MF_MOUNTED		= 1,	/* Device is mounted. */ | ||||
| 	NTFS_MF_ISROOT		= 2,	/* Device is mounted as system root. */ | ||||
| 	NTFS_MF_READONLY	= 4,	/* Device is mounted read-only. */ | ||||
| } ntfs_mount_flags; | ||||
| 
 | ||||
| extern int ntfs_check_if_mounted(const char *file, unsigned long *mnt_flags); | ||||
| 
 | ||||
| /**
 | ||||
|  * enum ntfs_volume_state_bits - | ||||
|  * | ||||
|  * Defined bits for the state field in the ntfs_volume structure. | ||||
|  */ | ||||
| typedef enum { | ||||
| 	NV_ReadOnly,		/* 1: Volume is read-only. */ | ||||
| 	NV_CaseSensitive,	/* 1: Volume is mounted case-sensitive. */ | ||||
| 	NV_LogFileEmpty,	/* 1: $logFile journal is empty. */ | ||||
| 	NV_NoATime,		/* 1: Do not update access time. */ | ||||
| } ntfs_volume_state_bits; | ||||
| 
 | ||||
| #define  test_nvol_flag(nv, flag)	 test_bit(NV_##flag, (nv)->state) | ||||
| #define   set_nvol_flag(nv, flag)	  set_bit(NV_##flag, (nv)->state) | ||||
| #define clear_nvol_flag(nv, flag)	clear_bit(NV_##flag, (nv)->state) | ||||
| 
 | ||||
| #define NVolReadOnly(nv)		 test_nvol_flag(nv, ReadOnly) | ||||
| #define NVolSetReadOnly(nv)		  set_nvol_flag(nv, ReadOnly) | ||||
| #define NVolClearReadOnly(nv)		clear_nvol_flag(nv, ReadOnly) | ||||
| 
 | ||||
| #define NVolCaseSensitive(nv)		 test_nvol_flag(nv, CaseSensitive) | ||||
| #define NVolSetCaseSensitive(nv)	  set_nvol_flag(nv, CaseSensitive) | ||||
| #define NVolClearCaseSensitive(nv)	clear_nvol_flag(nv, CaseSensitive) | ||||
| 
 | ||||
| #define NVolLogFileEmpty(nv)		 test_nvol_flag(nv, LogFileEmpty) | ||||
| #define NVolSetLogFileEmpty(nv)		  set_nvol_flag(nv, LogFileEmpty) | ||||
| #define NVolClearLogFileEmpty(nv)	clear_nvol_flag(nv, LogFileEmpty) | ||||
| 
 | ||||
| #define NVolNoATime(nv)			 test_nvol_flag(nv, NoATime) | ||||
| #define NVolSetNoATime(nv)		  set_nvol_flag(nv, NoATime) | ||||
| #define NVolClearNoATime(nv)		clear_nvol_flag(nv, NoATime) | ||||
| 
 | ||||
| /*
 | ||||
|  * NTFS version 1.1 and 1.2 are used by Windows NT4. | ||||
|  * NTFS version 2.x is used by Windows 2000 Beta | ||||
|  * NTFS version 3.0 is used by Windows 2000. | ||||
|  * NTFS version 3.1 is used by Windows XP, 2003 and Vista. | ||||
|  */ | ||||
| 
 | ||||
| #define NTFS_V1_1(major, minor) ((major) == 1 && (minor) == 1) | ||||
| #define NTFS_V1_2(major, minor) ((major) == 1 && (minor) == 2) | ||||
| #define NTFS_V2_X(major, minor) ((major) == 2) | ||||
| #define NTFS_V3_0(major, minor) ((major) == 3 && (minor) == 0) | ||||
| #define NTFS_V3_1(major, minor) ((major) == 3 && (minor) == 1) | ||||
| 
 | ||||
| #define NTFS_BUF_SIZE 8192 | ||||
| 
 | ||||
| /**
 | ||||
|  * struct _ntfs_volume - structure describing an open volume in memory. | ||||
|  */ | ||||
| struct _ntfs_volume { | ||||
| 	union { | ||||
| 		struct ntfs_device *dev;	/* NTFS device associated with
 | ||||
| 						   the volume. */ | ||||
| 		void *sb;	/* For kernel porting compatibility. */ | ||||
| 	}; | ||||
| 	char *vol_name;		/* Name of the volume. */ | ||||
| 	unsigned long state;	/* NTFS specific flags describing this volume.
 | ||||
| 				   See ntfs_volume_state_bits above. */ | ||||
| 
 | ||||
| 	ntfs_inode *vol_ni;	/* ntfs_inode structure for FILE_Volume. */ | ||||
| 	u8 major_ver;		/* Ntfs major version of volume. */ | ||||
| 	u8 minor_ver;		/* Ntfs minor version of volume. */ | ||||
| 	u16 flags;		/* Bit array of VOLUME_* flags. */ | ||||
| 
 | ||||
| 	u16 sector_size;	/* Byte size of a sector. */ | ||||
| 	u8 sector_size_bits;	/* Log(2) of the byte size of a sector. */ | ||||
| 	u32 cluster_size;	/* Byte size of a cluster. */ | ||||
| 	u32 mft_record_size;	/* Byte size of a mft record. */ | ||||
| 	u32 indx_record_size;	/* Byte size of a INDX record. */ | ||||
| 	u8 cluster_size_bits;	/* Log(2) of the byte size of a cluster. */ | ||||
| 	u8 mft_record_size_bits;/* Log(2) of the byte size of a mft record. */ | ||||
| 	u8 indx_record_size_bits;/* Log(2) of the byte size of a INDX record. */ | ||||
| 
 | ||||
| 	/* Variables used by the cluster and mft allocators. */ | ||||
| 	u8 mft_zone_multiplier;	/* Initial mft zone multiplier. */ | ||||
| 	s64 mft_data_pos;	/* Mft record number at which to allocate the
 | ||||
| 				   next mft record. */ | ||||
| 	LCN mft_zone_start;	/* First cluster of the mft zone. */ | ||||
| 	LCN mft_zone_end;	/* First cluster beyond the mft zone. */ | ||||
| 	LCN mft_zone_pos;	/* Current position in the mft zone. */ | ||||
| 	LCN data1_zone_pos;	/* Current position in the first data zone. */ | ||||
| 	LCN data2_zone_pos;	/* Current position in the second data zone. */ | ||||
| 
 | ||||
| 	s64 nr_clusters;	/* Volume size in clusters, hence also the
 | ||||
| 				   number of bits in lcn_bitmap. */ | ||||
| 	ntfs_inode *lcnbmp_ni;	/* ntfs_inode structure for FILE_Bitmap. */ | ||||
| 	ntfs_attr *lcnbmp_na;	/* ntfs_attr structure for the data attribute
 | ||||
| 				   of FILE_Bitmap. Each bit represents a | ||||
| 				   cluster on the volume, bit 0 representing | ||||
| 				   lcn 0 and so on. A set bit means that the | ||||
| 				   cluster and vice versa. */ | ||||
| 
 | ||||
| 	LCN mft_lcn;		/* Logical cluster number of the data attribute
 | ||||
| 				   for FILE_MFT. */ | ||||
| 	ntfs_inode *mft_ni;	/* ntfs_inode structure for FILE_MFT. */ | ||||
| 	ntfs_attr *mft_na;	/* ntfs_attr structure for the data attribute
 | ||||
| 				   of FILE_MFT. */ | ||||
| 	ntfs_attr *mftbmp_na;	/* ntfs_attr structure for the bitmap attribute
 | ||||
| 				   of FILE_MFT. Each bit represents an mft | ||||
| 				   record in the $DATA attribute, bit 0 | ||||
| 				   representing mft record 0 and so on. A set | ||||
| 				   bit means that the mft record is in use and | ||||
| 				   vice versa. */ | ||||
| 
 | ||||
| 	int mftmirr_size;	/* Size of the FILE_MFTMirr in mft records. */ | ||||
| 	LCN mftmirr_lcn;	/* Logical cluster number of the data attribute
 | ||||
| 				   for FILE_MFTMirr. */ | ||||
| 	ntfs_inode *mftmirr_ni;	/* ntfs_inode structure for FILE_MFTMirr. */ | ||||
| 	ntfs_attr *mftmirr_na;	/* ntfs_attr structure for the data attribute
 | ||||
| 				   of FILE_MFTMirr. */ | ||||
| 
 | ||||
| 	ntfschar *upcase;	/* Upper case equivalents of all 65536 2-byte
 | ||||
| 				   Unicode characters. Obtained from | ||||
| 				   FILE_UpCase. */ | ||||
| 	u32 upcase_len;		/* Length in Unicode characters of the upcase
 | ||||
| 				   table. */ | ||||
| 
 | ||||
| 	ATTR_DEF *attrdef;	/* Attribute definitions. Obtained from
 | ||||
| 				   FILE_AttrDef. */ | ||||
| 	s32 attrdef_len;	/* Size of the attribute definition table in
 | ||||
| 				   bytes. */ | ||||
| 
 | ||||
| 	/* Temp: for directory handling */ | ||||
| 	void *private_data;	/* ntfs_dir for . */ | ||||
| 	void *private_bmp1;	/* ntfs_bmp for $MFT/$BITMAP */ | ||||
| 	void *private_bmp2;	/* ntfs_bmp for $Bitmap */ | ||||
| }; | ||||
| 
 | ||||
| extern ntfs_volume *ntfs_volume_alloc(void); | ||||
| 
 | ||||
| extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, | ||||
| 		unsigned long flags); | ||||
| 
 | ||||
| extern ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, | ||||
| 		unsigned long flags); | ||||
| extern int ntfs_device_umount(ntfs_volume *vol, const BOOL force); | ||||
| 
 | ||||
| extern ntfs_volume *ntfs_mount(const char *name, unsigned long flags); | ||||
| extern int ntfs_umount(ntfs_volume *vol, const BOOL force); | ||||
| 
 | ||||
| extern int ntfs_version_is_supported(ntfs_volume *vol); | ||||
| extern int ntfs_logfile_reset(ntfs_volume *vol); | ||||
| 
 | ||||
| extern int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags); | ||||
| 
 | ||||
| #endif /* defined _NTFS_VOLUME_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,64 @@ | |||
| #
 | ||||
| # 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
 | ||||
| #
 | ||||
| 
 | ||||
| linux_ntfsincludedir = -I$(top_srcdir)/include/ntfs-3g | ||||
| 
 | ||||
| lib_LTLIBRARIES = libntfs-3g.la | ||||
| libntfs_3g_la_CFLAGS = $(LIBNTFS_3G_CFLAGS)  | ||||
| libntfs_3g_la_SOURCES =	\
 | ||||
| 	attrib.c	\
 | ||||
| 	attrlist.c	\
 | ||||
| 	bitmap.c	\
 | ||||
| 	bootsect.c	\
 | ||||
| 	collate.c	\
 | ||||
| 	compat.c	\
 | ||||
| 	compress.c	\
 | ||||
| 	debug.c		\
 | ||||
| 	device.c	\
 | ||||
| 	device_io.c	\
 | ||||
| 	dir.c		\
 | ||||
| 	index.c		\
 | ||||
| 	inode.c		\
 | ||||
| 	lcnalloc.c	\
 | ||||
| 	logfile.c	\
 | ||||
| 	logging.c	\
 | ||||
| 	mft.c		\
 | ||||
| 	misc.c		\
 | ||||
| 	mst.c		\
 | ||||
| 	runlist.c	\
 | ||||
| 	security.c	\
 | ||||
| 	unistr.c	\
 | ||||
| 	version.c	\
 | ||||
| 	volume.c | ||||
| 
 | ||||
| AM_CPPFLAGS = $(linux_ntfsincludedir) $(all_includes) | ||||
| 
 | ||||
| EXTRA_DIST = unix_io.c | ||||
| 
 | ||||
| MAINTAINERCLEANFILES = Makefile.in | ||||
| 
 | ||||
| libs:	$(lib_LTLIBRARIES) | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,315 @@ | |||
| /**
 | ||||
|  * attrlist.c - Attribute list attribute handling code.  Originated from the Linux-NTFS | ||||
|  *		project. | ||||
|  * | ||||
|  * Copyright (c) 2004-2005 Anton Altaparmakov | ||||
|  * Copyright (c) 2004-2005 Yura Pakhuchiy | ||||
|  * Copyright (c)      2006 Szabolcs Szakacsits | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_STRING_H | ||||
| #include <string.h> | ||||
| #endif | ||||
| #ifdef HAVE_STDLIB_H | ||||
| #include <stdlib.h> | ||||
| #endif | ||||
| #ifdef HAVE_ERRNO_H | ||||
| #include <errno.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "layout.h" | ||||
| #include "attrib.h" | ||||
| #include "attrlist.h" | ||||
| #include "debug.h" | ||||
| #include "unistr.h" | ||||
| #include "logging.h" | ||||
| #include "misc.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_attrlist_need - check whether inode need attribute list | ||||
|  * @ni:		opened ntfs inode for which perform check | ||||
|  * | ||||
|  * Check whether all are attributes belong to one MFT record, in that case | ||||
|  * attribute list is not needed. | ||||
|  * | ||||
|  * Return 1 if inode need attribute list, 0 if not, -1 on error with errno set | ||||
|  * to the error code. If function succeed errno set to 0. The following error | ||||
|  * codes are defined: | ||||
|  *	EINVAL	- Invalid arguments passed to function or attribute haven't got | ||||
|  *		  attribute list. | ||||
|  */ | ||||
| int ntfs_attrlist_need(ntfs_inode *ni) | ||||
| { | ||||
| 	ATTR_LIST_ENTRY *ale; | ||||
| 
 | ||||
| 	if (!ni) { | ||||
| 		ntfs_log_trace("Invalid arguments.\n"); | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); | ||||
| 
 | ||||
| 	if (!NInoAttrList(ni)) { | ||||
| 		ntfs_log_trace("Inode haven't got attribute list.\n"); | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!ni->attr_list) { | ||||
| 		ntfs_log_trace("Corrupt in-memory struct.\n"); | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	errno = 0; | ||||
| 	ale = (ATTR_LIST_ENTRY *)ni->attr_list; | ||||
| 	while ((u8*)ale < ni->attr_list + ni->attr_list_size) { | ||||
| 		if (MREF_LE(ale->mft_reference) != ni->mft_no) | ||||
| 			return 1; | ||||
| 		ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length)); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_attrlist_entry_add - add an attribute list attribute entry | ||||
|  * @ni:		opened ntfs inode, which contains that attribute | ||||
|  * @attr:	attribute record to add to attribute list | ||||
|  * | ||||
|  * Return 0 on success and -1 on error with errno set to the error code. The | ||||
|  * following error codes are defined: | ||||
|  *	EINVAL	- Invalid arguments passed to function. | ||||
|  *	ENOMEM	- Not enough memory to allocate necessary buffers. | ||||
|  *	EIO	- I/O error occurred or damaged filesystem. | ||||
|  *	EEXIST	- Such attribute already present in attribute list. | ||||
|  */ | ||||
| int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) | ||||
| { | ||||
| 	ATTR_LIST_ENTRY *ale; | ||||
| 	MFT_REF mref; | ||||
| 	ntfs_attr *na = NULL; | ||||
| 	ntfs_attr_search_ctx *ctx; | ||||
| 	u8 *new_al; | ||||
| 	int entry_len, entry_offset, err; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", | ||||
| 			(long long) ni->mft_no, | ||||
| 			(unsigned) le32_to_cpu(attr->type)); | ||||
| 
 | ||||
| 	if (!ni || !attr) { | ||||
| 		ntfs_log_trace("Invalid arguments.\n"); | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); | ||||
| 
 | ||||
| 	if (ni->nr_extents == -1) | ||||
| 		ni = ni->base_ni; | ||||
| 
 | ||||
| 	if (!NInoAttrList(ni)) { | ||||
| 		ntfs_log_trace("Attribute list isn't present.\n"); | ||||
| 		errno = ENOENT; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Determine size and allocate memory for new attribute list. */ | ||||
| 	entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * | ||||
| 			attr->name_length + 7) & ~7; | ||||
| 	new_al = ntfs_calloc(ni->attr_list_size + entry_len); | ||||
| 	if (!new_al) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	/* Find place for the new entry. */ | ||||
| 	ctx = ntfs_attr_get_search_ctx(ni, NULL); | ||||
| 	if (!ctx) { | ||||
| 		err = errno; | ||||
| 		ntfs_log_trace("Failed to obtain attribute search context.\n"); | ||||
| 		goto err_out; | ||||
| 	} | ||||
| 	if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*) | ||||
| 			((u8*)attr + le16_to_cpu(attr->name_offset)) : | ||||
| 			AT_UNNAMED, attr->name_length, CASE_SENSITIVE, | ||||
| 			(attr->non_resident) ? le64_to_cpu(attr->lowest_vcn) : | ||||
| 			0, (attr->non_resident) ? NULL : ((u8*)attr + | ||||
| 			le16_to_cpu(attr->value_offset)), (attr->non_resident) ? | ||||
| 			0 : le32_to_cpu(attr->value_length), ctx)) { | ||||
| 		/* Found some extent, check it to be before new extent. */ | ||||
| 		if (ctx->al_entry->lowest_vcn == attr->lowest_vcn) { | ||||
| 			err = EEXIST; | ||||
| 			ntfs_log_trace("Such attribute already present in the " | ||||
| 					"attribute list.\n"); | ||||
| 			ntfs_attr_put_search_ctx(ctx); | ||||
| 			goto err_out; | ||||
| 		} | ||||
| 		/* Add new entry after this extent. */ | ||||
| 		ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry + | ||||
| 				le16_to_cpu(ctx->al_entry->length)); | ||||
| 	} else { | ||||
| 		/* Check for real errors. */ | ||||
| 		if (errno != ENOENT) { | ||||
| 			err = errno; | ||||
| 			ntfs_log_trace("Attribute lookup failed.\n"); | ||||
| 			ntfs_attr_put_search_ctx(ctx); | ||||
| 			goto err_out; | ||||
| 		} | ||||
| 		/* No previous extents found. */ | ||||
| 		ale = ctx->al_entry; | ||||
| 	} | ||||
| 	/* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */ | ||||
| 	ntfs_attr_put_search_ctx(ctx); | ||||
| 
 | ||||
| 	/* Determine new entry offset. */ | ||||
| 	entry_offset = ((u8 *)ale - ni->attr_list); | ||||
| 	/* Set pointer to new entry. */ | ||||
| 	ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset); | ||||
| 	/* Zero it to fix valgrind warning. */ | ||||
| 	memset(ale, 0, entry_len); | ||||
| 	/* Form new entry. */ | ||||
| 	ale->type = attr->type; | ||||
| 	ale->length = cpu_to_le16(entry_len); | ||||
| 	ale->name_length = attr->name_length; | ||||
| 	ale->name_offset = offsetof(ATTR_LIST_ENTRY, name); | ||||
| 	if (attr->non_resident) | ||||
| 		ale->lowest_vcn = attr->lowest_vcn; | ||||
| 	else | ||||
| 		ale->lowest_vcn = 0; | ||||
| 	ale->mft_reference = mref; | ||||
| 	ale->instance = attr->instance; | ||||
| 	memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset), | ||||
| 			attr->name_length * sizeof(ntfschar)); | ||||
| 
 | ||||
| 	/* Resize $ATTRIBUTE_LIST to new length. */ | ||||
| 	na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); | ||||
| 	if (!na) { | ||||
| 		err = errno; | ||||
| 		ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); | ||||
| 		goto err_out; | ||||
| 	} | ||||
| 	if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) { | ||||
| 		err = errno; | ||||
| 		ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); | ||||
| 		goto err_out; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Copy entries from old attribute list to new. */ | ||||
| 	memcpy(new_al, ni->attr_list, entry_offset); | ||||
| 	memcpy(new_al + entry_offset + entry_len, ni->attr_list + | ||||
| 			entry_offset, ni->attr_list_size - entry_offset); | ||||
| 
 | ||||
| 	/* Set new runlist. */ | ||||
| 	free(ni->attr_list); | ||||
| 	ni->attr_list = new_al; | ||||
| 	ni->attr_list_size = ni->attr_list_size + entry_len; | ||||
| 	NInoAttrListSetDirty(ni); | ||||
| 	/* Done! */ | ||||
| 	ntfs_attr_close(na); | ||||
| 	return 0; | ||||
| err_out: | ||||
| 	if (na) | ||||
| 		ntfs_attr_close(na); | ||||
| 	free(new_al); | ||||
| 	errno = err; | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_attrlist_entry_rm - remove an attribute list attribute entry | ||||
|  * @ctx:	attribute search context describing the attribute list entry | ||||
|  * | ||||
|  * Remove the attribute list entry @ctx->al_entry from the attribute list. | ||||
|  * | ||||
|  * Return 0 on success and -1 on error with errno set to the error code. | ||||
|  */ | ||||
| int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx) | ||||
| { | ||||
| 	u8 *new_al; | ||||
| 	int new_al_len; | ||||
| 	ntfs_inode *base_ni; | ||||
| 	ntfs_attr *na; | ||||
| 	ATTR_LIST_ENTRY *ale; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) { | ||||
| 		ntfs_log_trace("Invalid arguments.\n"); | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ctx->base_ntfs_ino) | ||||
| 		base_ni = ctx->base_ntfs_ino; | ||||
| 	else | ||||
| 		base_ni = ctx->ntfs_ino; | ||||
| 	ale = ctx->al_entry; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n", | ||||
| 			(long long) ctx->ntfs_ino->mft_no, | ||||
| 			(unsigned) le32_to_cpu(ctx->al_entry->type), | ||||
| 			(long long) le64_to_cpu(ctx->al_entry->lowest_vcn)); | ||||
| 
 | ||||
| 	if (!NInoAttrList(base_ni)) { | ||||
| 		ntfs_log_trace("Attribute list isn't present.\n"); | ||||
| 		errno = ENOENT; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Allocate memory for new attribute list. */ | ||||
| 	new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length); | ||||
| 	new_al = ntfs_calloc(new_al_len); | ||||
| 	if (!new_al) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	/* Reisze $ATTRIBUTE_LIST to new length. */ | ||||
| 	na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); | ||||
| 	if (!na) { | ||||
| 		err = errno; | ||||
| 		ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); | ||||
| 		goto err_out; | ||||
| 	} | ||||
| 	if (ntfs_attr_truncate(na, new_al_len)) { | ||||
| 		err = errno; | ||||
| 		ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); | ||||
| 		goto err_out; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Copy entries from old attribute list to new. */ | ||||
| 	memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list); | ||||
| 	memcpy(new_al + ((u8*)ale - base_ni->attr_list), (u8*)ale + le16_to_cpu( | ||||
| 		ale->length), new_al_len - ((u8*)ale - base_ni->attr_list)); | ||||
| 
 | ||||
| 	/* Set new runlist. */ | ||||
| 	free(base_ni->attr_list); | ||||
| 	base_ni->attr_list = new_al; | ||||
| 	base_ni->attr_list_size = new_al_len; | ||||
| 	NInoAttrListSetDirty(base_ni); | ||||
| 	/* Done! */ | ||||
| 	ntfs_attr_close(na); | ||||
| 	return 0; | ||||
| err_out: | ||||
| 	if (na) | ||||
| 		ntfs_attr_close(na); | ||||
| 	free(new_al); | ||||
| 	errno = err; | ||||
| 	return -1; | ||||
| } | ||||
|  | @ -0,0 +1,289 @@ | |||
| /**
 | ||||
|  * bitmap.c - Bitmap handling code. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2002-2006 Anton Altaparmakov | ||||
|  * Copyright (c) 2004-2005 Richard Russon | ||||
|  * Copyright (c) 2004-2006 Szabolcs Szakacsits | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_STDLIB_H | ||||
| #include <stdlib.h> | ||||
| #endif | ||||
| #ifdef HAVE_STDIO_H | ||||
| #include <stdio.h> | ||||
| #endif | ||||
| #ifdef HAVE_STRING_H | ||||
| #include <string.h> | ||||
| #endif | ||||
| #ifdef HAVE_ERRNO_H | ||||
| #include <errno.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "attrib.h" | ||||
| #include "bitmap.h" | ||||
| #include "debug.h" | ||||
| #include "logging.h" | ||||
| #include "misc.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_bit_set - set a bit in a field of bits | ||||
|  * @bitmap:	field of bits | ||||
|  * @bit:	bit to set | ||||
|  * @new_value:	value to set bit to (0 or 1) | ||||
|  * | ||||
|  * Set the bit @bit in the @bitmap to @new_value. Ignore all errors. | ||||
|  */ | ||||
| void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value) | ||||
| { | ||||
| 	if (!bitmap || new_value > 1) | ||||
| 		return; | ||||
| 	if (!new_value) | ||||
| 		bitmap[bit >> 3] &= ~(1 << (bit & 7)); | ||||
| 	else | ||||
| 		bitmap[bit >> 3] |= (1 << (bit & 7)); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_bit_get - get value of a bit in a field of bits | ||||
|  * @bitmap:	field of bits | ||||
|  * @bit:	bit to get | ||||
|  * | ||||
|  * Get and return the value of the bit @bit in @bitmap (0 or 1). | ||||
|  * Return -1 on error. | ||||
|  */ | ||||
| char ntfs_bit_get(const u8 *bitmap, const u64 bit) | ||||
| { | ||||
| 	if (!bitmap) | ||||
| 		return -1; | ||||
| 	return (bitmap[bit >> 3] >> (bit & 7)) & 1; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_bit_get_and_set - get value of a bit in a field of bits and set it | ||||
|  * @bitmap:	field of bits | ||||
|  * @bit:	bit to get/set | ||||
|  * @new_value:	value to set bit to (0 or 1) | ||||
|  * | ||||
|  * Return the value of the bit @bit and set it to @new_value (0 or 1). | ||||
|  * Return -1 on error. | ||||
|  */ | ||||
| char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value) | ||||
| { | ||||
| 	register u8 old_bit, shift; | ||||
| 
 | ||||
| 	if (!bitmap || new_value > 1) | ||||
| 		return -1; | ||||
| 	shift = bit & 7; | ||||
| 	old_bit = (bitmap[bit >> 3] >> shift) & 1; | ||||
| 	if (new_value != old_bit) | ||||
| 		bitmap[bit >> 3] ^= 1 << shift; | ||||
| 	return old_bit; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value | ||||
|  * @na:		attribute containing the bitmap | ||||
|  * @start_bit:	first bit to set | ||||
|  * @count:	number of bits to set | ||||
|  * @value:	value to set the bits to (i.e. 0 or 1) | ||||
|  * | ||||
|  * Set @count bits starting at bit @start_bit in the bitmap described by the | ||||
|  * attribute @na to @value, where @value is either 0 or 1. | ||||
|  * | ||||
|  * On success return 0 and on error return -1 with errno set to the error code. | ||||
|  */ | ||||
| static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, | ||||
| 				       s64 count, int value) | ||||
| { | ||||
| 	s64 bufsize, br; | ||||
| 	u8 *buf, *lastbyte_buf; | ||||
| 	int bit, firstbyte, lastbyte, lastbyte_pos, tmp, err; | ||||
| 
 | ||||
| 	if (!na || start_bit < 0 || count < 0) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	bit = start_bit & 7; | ||||
| 	if (bit) | ||||
| 		firstbyte = 1; | ||||
| 	else | ||||
| 		firstbyte = 0; | ||||
| 
 | ||||
| 	/* Calculate the required buffer size in bytes, capping it at 8kiB. */ | ||||
| 	bufsize = ((count - (bit ? 8 - bit : 0) + 7) >> 3) + firstbyte; | ||||
| 	if (bufsize > 8192) | ||||
| 		bufsize = 8192; | ||||
| 
 | ||||
| 	buf = ntfs_malloc(bufsize); | ||||
| 	if (!buf) | ||||
| 		return -1; | ||||
| 	 | ||||
| 	/* Depending on @value, zero or set all bits in the allocated buffer. */ | ||||
| 	memset(buf, value ? 0xff : 0, bufsize); | ||||
| 
 | ||||
| 	/* If there is a first partial byte... */ | ||||
| 	if (bit) { | ||||
| 		/* read it in... */ | ||||
| 		br = ntfs_attr_pread(na, start_bit >> 3, 1, buf); | ||||
| 		if (br != 1) { | ||||
| 			free(buf); | ||||
| 			errno = EIO; | ||||
| 			return -1; | ||||
| 		} | ||||
| 		/* and set or clear the appropriate bits in it. */ | ||||
| 		while ((bit & 7) && count--) { | ||||
| 			if (value) | ||||
| 				*buf |= 1 << bit++; | ||||
| 			else | ||||
| 				*buf &= ~(1 << bit++); | ||||
| 		} | ||||
| 		/* Update @start_bit to the new position. */ | ||||
| 		start_bit = (start_bit + 7) & ~7; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Loop until @count reaches zero. */ | ||||
| 	lastbyte = 0; | ||||
| 	lastbyte_buf = NULL; | ||||
| 	bit = count & 7; | ||||
| 	do { | ||||
| 		/* If there is a last partial byte... */ | ||||
| 		if (count > 0 && bit) { | ||||
| 			lastbyte_pos = ((count + 7) >> 3) + firstbyte; | ||||
| 			if (!lastbyte_pos) { | ||||
| 				// FIXME: Eeek! BUG!
 | ||||
| 				ntfs_log_trace("Eeek! lastbyte is zero. Leaving " | ||||
| 						"inconsistent metadata.\n"); | ||||
| 				err = EIO; | ||||
| 				goto free_err_out; | ||||
| 			} | ||||
| 			/* and it is in the currently loaded bitmap window... */ | ||||
| 			if (lastbyte_pos <= bufsize) { | ||||
| 				lastbyte_buf = buf + lastbyte_pos - 1; | ||||
| 
 | ||||
| 				/* read the byte in... */ | ||||
| 				br = ntfs_attr_pread(na, (start_bit + count) >> | ||||
| 						3, 1, lastbyte_buf); | ||||
| 				if (br != 1) { | ||||
| 					// FIXME: Eeek! We need rollback! (AIA)
 | ||||
| 					ntfs_log_trace("Eeek! Read of last byte " | ||||
| 							"failed. Leaving " | ||||
| 							"inconsistent metadata.\n"); | ||||
| 					err = EIO; | ||||
| 					goto free_err_out; | ||||
| 				} | ||||
| 				/* and set/clear the appropriate bits in it. */ | ||||
| 				while (bit && count--) { | ||||
| 					if (value) | ||||
| 						*lastbyte_buf |= 1 << --bit; | ||||
| 					else | ||||
| 						*lastbyte_buf &= ~(1 << --bit); | ||||
| 				} | ||||
| 				/* We don't want to come back here... */ | ||||
| 				bit = 0; | ||||
| 				/* We have a last byte that we have handled. */ | ||||
| 				lastbyte = 1; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		/* Write the prepared buffer to disk. */ | ||||
| 		tmp = (start_bit >> 3) - firstbyte; | ||||
| 		br = ntfs_attr_pwrite(na, tmp, bufsize, buf); | ||||
| 		if (br != bufsize) { | ||||
| 			// FIXME: Eeek! We need rollback! (AIA)
 | ||||
| 			ntfs_log_trace("Eeek! Failed to write buffer to bitmap. " | ||||
| 					"Leaving inconsistent metadata.\n"); | ||||
| 			err = EIO; | ||||
| 			goto free_err_out; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Update counters. */ | ||||
| 		tmp = (bufsize - firstbyte - lastbyte) << 3; | ||||
| 		if (firstbyte) { | ||||
| 			firstbyte = 0; | ||||
| 			/*
 | ||||
| 			 * Re-set the partial first byte so a subsequent write | ||||
| 			 * of the buffer does not have stale, incorrect bits. | ||||
| 			 */ | ||||
| 			*buf = value ? 0xff : 0; | ||||
| 		} | ||||
| 		start_bit += tmp; | ||||
| 		count -= tmp; | ||||
| 		if (bufsize > (tmp = (count + 7) >> 3)) | ||||
| 			bufsize = tmp; | ||||
| 
 | ||||
| 		if (lastbyte && count != 0) { | ||||
| 			// FIXME: Eeek! BUG!
 | ||||
| 			ntfs_log_trace("Eeek! Last buffer but count is not zero (= " | ||||
| 					"%lli). Leaving inconsistent metadata.\n", | ||||
| 					(long long)count); | ||||
| 			err = EIO; | ||||
| 			goto free_err_out; | ||||
| 		} | ||||
| 	} while (count > 0); | ||||
| 
 | ||||
| 	/* Done! */ | ||||
| 	free(buf); | ||||
| 	return 0; | ||||
| 
 | ||||
| free_err_out: | ||||
| 	free(buf); | ||||
| 	errno = err; | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_bitmap_set_run - set a run of bits in a bitmap | ||||
|  * @na:		attribute containing the bitmap | ||||
|  * @start_bit:	first bit to set | ||||
|  * @count:	number of bits to set | ||||
|  * | ||||
|  * Set @count bits starting at bit @start_bit in the bitmap described by the | ||||
|  * attribute @na. | ||||
|  * | ||||
|  * On success return 0 and on error return -1 with errno set to the error code. | ||||
|  */ | ||||
| int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count) | ||||
| { | ||||
| 	return ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_bitmap_clear_run - clear a run of bits in a bitmap | ||||
|  * @na:		attribute containing the bitmap | ||||
|  * @start_bit:	first bit to clear | ||||
|  * @count:	number of bits to clear | ||||
|  * | ||||
|  * Clear @count bits starting at bit @start_bit in the bitmap described by the | ||||
|  * attribute @na. | ||||
|  * | ||||
|  * On success return 0 and on error return -1 with errno set to the error code. | ||||
|  */ | ||||
| int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count) | ||||
| { | ||||
| 	ntfs_log_trace("Dealloc from bit 0x%llx, count 0x%llx.\n", | ||||
| 		       (long long)start_bit, (long long)count); | ||||
| 
 | ||||
| 	return ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0); | ||||
| } | ||||
| 
 | ||||
|  | @ -0,0 +1,273 @@ | |||
| /**
 | ||||
|  * bootsect.c - Boot sector handling code. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2000-2006 Anton Altaparmakov | ||||
|  * Copyright (c) 2003-2004 Szabolcs Szakacsits | ||||
|  * Copyright (c)      2005 Yura Pakhuchiy | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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 <stdio.h> | ||||
| #endif | ||||
| #ifdef HAVE_STDLIB_H | ||||
| #include <stdlib.h> | ||||
| #endif | ||||
| #ifdef HAVE_STRING_H | ||||
| #include <string.h> | ||||
| #endif | ||||
| #ifdef HAVE_ERRNO_H | ||||
| #include <errno.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "compat.h" | ||||
| #include "bootsect.h" | ||||
| #include "debug.h" | ||||
| #include "logging.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_boot_sector_is_ntfs - check if buffer contains a valid ntfs boot sector | ||||
|  * @b:		buffer containing putative boot sector to analyze | ||||
|  * @silent:	if zero, output progress messages to stderr | ||||
|  * | ||||
|  * Check if the buffer @b contains a valid ntfs boot sector. The buffer @b | ||||
|  * must be at least 512 bytes in size. | ||||
|  * | ||||
|  * If @silent is zero, output progress messages to stderr. Otherwise, do not | ||||
|  * output any messages (except when configured with --enable-debug in which | ||||
|  * case warning/debug messages may be displayed). | ||||
|  * | ||||
|  * Return TRUE if @b contains a valid ntfs boot sector and FALSE if not. | ||||
|  */ | ||||
| BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b, const BOOL silent __attribute__((unused))) | ||||
| { | ||||
| 	u32 i; | ||||
| 
 | ||||
| 	ntfs_log_debug("\nBeginning bootsector check...\n"); | ||||
| 
 | ||||
| 	/* Calculate the checksum. Note, this is just a simple addition of
 | ||||
| 	   all u32 values in the bootsector starting at the beginning and | ||||
| 	   finishing at the offset of the checksum itself (i.e. not including | ||||
| 	   the checksum...). */ | ||||
| 	if ((void*)b < (void*)&b->checksum) { | ||||
| 		u32 *u = (u32 *)b; | ||||
| 		u32 *bi = (u32 *)(&b->checksum); | ||||
| 
 | ||||
| 		ntfs_log_debug("Calculating bootsector checksum... "); | ||||
| 
 | ||||
| 		for (i = 0; u < bi; ++u) | ||||
| 			i += le32_to_cpup(u); | ||||
| 
 | ||||
| 		if (le32_to_cpu(b->checksum) && le32_to_cpu(b->checksum) != i) | ||||
| 			goto not_ntfs; | ||||
| 		ntfs_log_debug("OK\n"); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Check OEMidentifier is "NTFS    " */ | ||||
| 	ntfs_log_debug("Checking OEMid... "); | ||||
| 	if (b->oem_id != cpu_to_le64(0x202020205346544eULL)) /* "NTFS    " */ | ||||
| 		goto not_ntfs; | ||||
| 	ntfs_log_debug("OK\n"); | ||||
| 
 | ||||
| 	/* Check bytes per sector value is between 256 and 4096. */ | ||||
| 	ntfs_log_debug("Checking bytes per sector... "); | ||||
| 	if (le16_to_cpu(b->bpb.bytes_per_sector) <  0x100 || | ||||
| 	    le16_to_cpu(b->bpb.bytes_per_sector) > 0x1000) | ||||
| 		goto not_ntfs; | ||||
| 	ntfs_log_debug("OK\n"); | ||||
| 
 | ||||
| 	/* Check sectors per cluster value is valid. */ | ||||
| 	ntfs_log_debug("Checking sectors per cluster... "); | ||||
| 	switch (b->bpb.sectors_per_cluster) { | ||||
| 	case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: | ||||
| 		break; | ||||
| 	default: | ||||
| 		goto not_ntfs; | ||||
| 	} | ||||
| 	ntfs_log_debug("OK\n"); | ||||
| 
 | ||||
| 	/* Check the cluster size is not above 65536 bytes. */ | ||||
| 	ntfs_log_debug("Checking cluster size... "); | ||||
| 	if ((u32)le16_to_cpu(b->bpb.bytes_per_sector) * | ||||
| 	    b->bpb.sectors_per_cluster > 0x10000) | ||||
| 		goto not_ntfs; | ||||
| 	ntfs_log_debug("OK\n"); | ||||
| 
 | ||||
| 	/* Check reserved/unused fields are really zero. */ | ||||
| 	ntfs_log_debug("Checking reserved fields are zero... "); | ||||
| 	if (le16_to_cpu(b->bpb.reserved_sectors) || | ||||
| 	    le16_to_cpu(b->bpb.root_entries) || | ||||
| 	    le16_to_cpu(b->bpb.sectors) || | ||||
| 	    le16_to_cpu(b->bpb.sectors_per_fat) || | ||||
| 	    le32_to_cpu(b->bpb.large_sectors) || | ||||
| 	    b->bpb.fats) | ||||
| 		goto not_ntfs; | ||||
| 	ntfs_log_debug("OK\n"); | ||||
| 
 | ||||
| 	/* Check clusters per file mft record value is valid. */ | ||||
| 	ntfs_log_debug("Checking clusters per mft record... "); | ||||
| 	if ((u8)b->clusters_per_mft_record < 0xe1 || | ||||
| 	    (u8)b->clusters_per_mft_record > 0xf7) { | ||||
| 		switch (b->clusters_per_mft_record) { | ||||
| 		case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: | ||||
| 			break; | ||||
| 		default: | ||||
| 			goto not_ntfs; | ||||
| 		} | ||||
| 	} | ||||
| 	ntfs_log_debug("OK\n"); | ||||
| 
 | ||||
| 	/* Check clusters per index block value is valid. */ | ||||
| 	ntfs_log_debug("Checking clusters per index block... "); | ||||
| 	if ((u8)b->clusters_per_index_record < 0xe1 || | ||||
| 	    (u8)b->clusters_per_index_record > 0xf7) { | ||||
| 		switch (b->clusters_per_index_record) { | ||||
| 		case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: | ||||
| 			break; | ||||
| 		default: | ||||
| 			goto not_ntfs; | ||||
| 		} | ||||
| 	} | ||||
| 	ntfs_log_debug("OK\n"); | ||||
| 
 | ||||
| 	if (b->end_of_sector_marker != cpu_to_le16(0xaa55)) | ||||
| 		ntfs_log_debug("Warning: Bootsector has invalid end of sector marker.\n"); | ||||
| 
 | ||||
| 	ntfs_log_debug("Bootsector check completed successfully.\n"); | ||||
| 
 | ||||
| 	return TRUE; | ||||
| not_ntfs: | ||||
| 	ntfs_log_debug("FAILED\n"); | ||||
| 	ntfs_log_debug("Bootsector check failed.  Aborting...\n"); | ||||
| 	return FALSE; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_boot_sector_parse - setup an ntfs volume from an ntfs boot sector | ||||
|  * @vol:	ntfs_volume to setup | ||||
|  * @bs:		buffer containing ntfs boot sector to parse | ||||
|  * | ||||
|  * Parse the ntfs bootsector @bs and setup the ntfs volume @vol with the | ||||
|  * obtained values. | ||||
|  * | ||||
|  * Return 0 on success or -1 on error with errno set to the error code EINVAL. | ||||
|  */ | ||||
| int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) | ||||
| { | ||||
| 	u8 sectors_per_cluster; | ||||
| 	s8 c; | ||||
| 
 | ||||
| 	/* We return -1 with errno = EINVAL on error. */ | ||||
| 	errno = EINVAL; | ||||
| 
 | ||||
| 	vol->sector_size = le16_to_cpu(bs->bpb.bytes_per_sector); | ||||
| 	vol->sector_size_bits = ffs(vol->sector_size) - 1; | ||||
| 	ntfs_log_debug("SectorSize = 0x%x\n", vol->sector_size); | ||||
| 	ntfs_log_debug("SectorSizeBits = %u\n", vol->sector_size_bits); | ||||
| 	/*
 | ||||
| 	 * The bounds checks on mft_lcn and mft_mirr_lcn (i.e. them being | ||||
| 	 * below or equal the number_of_clusters) really belong in the | ||||
| 	 * ntfs_boot_sector_is_ntfs but in this way we can just do this once. | ||||
| 	 */ | ||||
| 	sectors_per_cluster = bs->bpb.sectors_per_cluster; | ||||
| 	ntfs_log_debug("NumberOfSectors = %lli\n", sle64_to_cpu(bs->number_of_sectors)); | ||||
| 	ntfs_log_debug("SectorsPerCluster = 0x%x\n", sectors_per_cluster); | ||||
| 	if (sectors_per_cluster & (sectors_per_cluster - 1)) { | ||||
| 		ntfs_log_debug("Error: %s is not a valid NTFS partition! " | ||||
| 				"sectors_per_cluster is not a power of 2.\n", | ||||
| 				vol->dev->d_name); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	vol->nr_clusters = sle64_to_cpu(bs->number_of_sectors) >> | ||||
| 			(ffs(sectors_per_cluster) - 1); | ||||
| 
 | ||||
| 	vol->mft_lcn = sle64_to_cpu(bs->mft_lcn); | ||||
| 	vol->mftmirr_lcn = sle64_to_cpu(bs->mftmirr_lcn); | ||||
| 	ntfs_log_debug("MFT LCN = 0x%llx\n", vol->mft_lcn); | ||||
| 	ntfs_log_debug("MFTMirr LCN = 0x%llx\n", vol->mftmirr_lcn); | ||||
| 	if (vol->mft_lcn > vol->nr_clusters || | ||||
| 			vol->mftmirr_lcn > vol->nr_clusters) { | ||||
| 		ntfs_log_debug("Error: %s is not a valid NTFS partition!\n", | ||||
| 				vol->dev->d_name); | ||||
| 		ntfs_log_debug("($Mft LCN or $MftMirr LCN is greater than the " | ||||
| 				"number of clusters!)\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	vol->cluster_size = sectors_per_cluster * vol->sector_size; | ||||
| 	if (vol->cluster_size & (vol->cluster_size - 1)) { | ||||
| 		ntfs_log_debug("Error: %s is not a valid NTFS partition! " | ||||
| 				"cluster_size is not a power of 2.\n", | ||||
| 				vol->dev->d_name); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	vol->cluster_size_bits = ffs(vol->cluster_size) - 1; | ||||
| 	/*
 | ||||
| 	 * Need to get the clusters per mft record and handle it if it is | ||||
| 	 * negative. Then calculate the mft_record_size. A value of 0x80 is | ||||
| 	 * illegal, thus signed char is actually ok! | ||||
| 	 */ | ||||
| 	c = bs->clusters_per_mft_record; | ||||
| 	ntfs_log_debug("ClusterSize = 0x%x\n", (unsigned)vol->cluster_size); | ||||
| 	ntfs_log_debug("ClusterSizeBits = %u\n", vol->cluster_size_bits); | ||||
| 	ntfs_log_debug("ClustersPerMftRecord = 0x%x\n", c); | ||||
| 	/*
 | ||||
| 	 * When clusters_per_mft_record is negative, it means that it is to | ||||
| 	 * be taken to be the negative base 2 logarithm of the mft_record_size | ||||
| 	 * min bytes. Then: | ||||
| 	 *	 mft_record_size = 2^(-clusters_per_mft_record) bytes. | ||||
| 	 */ | ||||
| 	if (c < 0) | ||||
| 		vol->mft_record_size = 1 << -c; | ||||
| 	else | ||||
| 		vol->mft_record_size = c << vol->cluster_size_bits; | ||||
| 	if (vol->mft_record_size & (vol->mft_record_size - 1)) { | ||||
| 		ntfs_log_debug("Error: %s is not a valid NTFS partition! " | ||||
| 				"mft_record_size is not a power of 2.\n", | ||||
| 				vol->dev->d_name); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1; | ||||
| 	ntfs_log_debug("MftRecordSize = 0x%x\n", (unsigned)vol->mft_record_size); | ||||
| 	ntfs_log_debug("MftRecordSizeBits = %u\n", vol->mft_record_size_bits); | ||||
| 	/* Same as above for INDX record. */ | ||||
| 	c = bs->clusters_per_index_record; | ||||
| 	ntfs_log_debug("ClustersPerINDXRecord = 0x%x\n", c); | ||||
| 	if (c < 0) | ||||
| 		vol->indx_record_size = 1 << -c; | ||||
| 	else | ||||
| 		vol->indx_record_size = c << vol->cluster_size_bits; | ||||
| 	vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1; | ||||
| 	ntfs_log_debug("INDXRecordSize = 0x%x\n", (unsigned)vol->indx_record_size); | ||||
| 	ntfs_log_debug("INDXRecordSizeBits = %u\n", vol->indx_record_size_bits); | ||||
| 	/*
 | ||||
| 	 * Work out the size of the MFT mirror in number of mft records. If the | ||||
| 	 * cluster size is less than or equal to the size taken by four mft | ||||
| 	 * records, the mft mirror stores the first four mft records. If the | ||||
| 	 * cluster size is bigger than the size taken by four mft records, the | ||||
| 	 * mft mirror contains as many mft records as will fit into one | ||||
| 	 * cluster. | ||||
| 	 */ | ||||
| 	if (vol->cluster_size <= 4 * vol->mft_record_size) | ||||
| 		vol->mftmirr_size = 4; | ||||
| 	else | ||||
| 		vol->mftmirr_size = vol->cluster_size / vol->mft_record_size; | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -0,0 +1,221 @@ | |||
| /**
 | ||||
|  * collate.c - NTFS collation handling.  Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2004 Anton Altaparmakov | ||||
|  * Copyright (c) 2005 Yura Pakhuchiy | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_STRING_H | ||||
| #include <string.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "collate.h" | ||||
| #include "debug.h" | ||||
| #include "unistr.h" | ||||
| #include "logging.h" | ||||
| 
 | ||||
| BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * FIXME:  At the moment we only support COLLATION_BINARY, | ||||
| 	 * COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME so we return false | ||||
| 	 * for everything else. | ||||
| 	 */ | ||||
| 	if (cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG && | ||||
| 			cr != COLLATION_FILE_NAME) | ||||
| 		return FALSE; | ||||
| 	i = le32_to_cpu(cr); | ||||
| 	if (((i >= 0) && (i <= 0x02)) || | ||||
| 			((i >= 0x10) && (i <= 0x13))) | ||||
| 		return TRUE; | ||||
| 	 | ||||
| 	return FALSE; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_collate_binary - Which of two binary objects should be listed first | ||||
|  * @vol: unused | ||||
|  * @data1: | ||||
|  * @data1_len: | ||||
|  * @data2: | ||||
|  * @data2_len: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| static int ntfs_collate_binary(ntfs_volume *vol __attribute__((unused)), | ||||
| 		const void *data1, const int data1_len, | ||||
| 		const void *data2, const int data2_len) | ||||
| { | ||||
| 	int rc; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering.\n"); | ||||
| 	rc = memcmp(data1, data2, min(data1_len, data2_len)); | ||||
| 	if (!rc && (data1_len != data2_len)) { | ||||
| 		if (data1_len < data2_len) | ||||
| 			rc = -1; | ||||
| 		else | ||||
| 			rc = 1; | ||||
| 	} | ||||
| 	ntfs_log_trace("Done, returning %i.\n", rc); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_collate_ntofs_ulong - Which of two long ints should be listed first | ||||
|  * @vol: unused | ||||
|  * @data1: | ||||
|  * @data1_len: | ||||
|  * @data2: | ||||
|  * @data2_len: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| static int ntfs_collate_ntofs_ulong(ntfs_volume *vol __attribute__((unused)), | ||||
| 		const void *data1, const int data1_len, | ||||
| 		const void *data2, const int data2_len) | ||||
| { | ||||
| 	int rc; | ||||
| 	u32 d1, d2; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering.\n"); | ||||
| 	if (data1_len != data2_len || data1_len != 4) { | ||||
| 		ntfs_log_error("data1_len or/and data2_len not equal to 4.\n"); | ||||
| 		return NTFS_COLLATION_ERROR; | ||||
| 	} | ||||
| 	d1 = le32_to_cpup(data1); | ||||
| 	d2 = le32_to_cpup(data2); | ||||
| 	if (d1 < d2) | ||||
| 		rc = -1; | ||||
| 	else { | ||||
| 		if (d1 == d2) | ||||
| 			rc = 0; | ||||
| 		else | ||||
| 			rc = 1; | ||||
| 	} | ||||
| 	ntfs_log_trace("Done, returning %i.\n", rc); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_collate_file_name - Which of two filenames should be listed first | ||||
|  * @vol: | ||||
|  * @data1: | ||||
|  * @data1_len: unused | ||||
|  * @data2: | ||||
|  * @data2_len: unused | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| static int ntfs_collate_file_name(ntfs_volume *vol, | ||||
| 		const void *data1, const int data1_len __attribute__((unused)), | ||||
| 		const void *data2, const int data2_len __attribute__((unused))) | ||||
| { | ||||
| 	int rc; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering.\n"); | ||||
| 	rc = ntfs_file_values_compare(data1, data2, NTFS_COLLATION_ERROR, | ||||
| 			IGNORE_CASE, vol->upcase, vol->upcase_len); | ||||
| 	if (!rc) | ||||
| 		rc = ntfs_file_values_compare(data1, data2, | ||||
| 				NTFS_COLLATION_ERROR, CASE_SENSITIVE, | ||||
| 				vol->upcase, vol->upcase_len); | ||||
| 	ntfs_log_trace("Done, returning %i.\n", rc); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| typedef int (*ntfs_collate_func_t)(ntfs_volume *, const void *, const int, | ||||
| 		const void *, const int); | ||||
| 
 | ||||
| static ntfs_collate_func_t ntfs_do_collate0x0[3] = { | ||||
| 	ntfs_collate_binary, | ||||
| 	ntfs_collate_file_name, | ||||
| 	NULL/*ntfs_collate_unicode_string*/, | ||||
| }; | ||||
| 
 | ||||
| static ntfs_collate_func_t ntfs_do_collate0x1[4] = { | ||||
| 	ntfs_collate_ntofs_ulong, | ||||
| 	NULL/*ntfs_collate_ntofs_sid*/, | ||||
| 	NULL/*ntfs_collate_ntofs_security_hash*/, | ||||
| 	NULL/*ntfs_collate_ntofs_ulongs*/, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_collate - collate two data items using a specified collation rule | ||||
|  * @vol:	ntfs volume to which the data items belong | ||||
|  * @cr:		collation rule to use when comparing the items | ||||
|  * @data1:	first data item to collate | ||||
|  * @data1_len:	length in bytes of @data1 | ||||
|  * @data2:	second data item to collate | ||||
|  * @data2_len:	length in bytes of @data2 | ||||
|  * | ||||
|  * Collate the two data items @data1 and @data2 using the collation rule @cr | ||||
|  * and return -1, 0, or 1 if @data1 is found, respectively, to collate before, | ||||
|  * to match, or to collate after @data2. | ||||
|  * | ||||
|  * For speed we use the collation rule @cr as an index into two tables of | ||||
|  * function pointers to call the appropriate collation function. | ||||
|  * | ||||
|  * Return NTFS_COLLATION_ERROR if error occurred. | ||||
|  */ | ||||
| int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr, | ||||
| 		const void *data1, const int data1_len, | ||||
| 		const void *data2, const int data2_len) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering.\n"); | ||||
| 	if (!vol || !data1 || !data2 || data1_len < 0 || data2_len < 0) { | ||||
| 		ntfs_log_error("Invalid arguments passed.\n"); | ||||
| 		return NTFS_COLLATION_ERROR; | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * FIXME:  At the moment we only support COLLATION_BINARY, | ||||
| 	 * COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME so we return error | ||||
| 	 * for everything else. | ||||
| 	 */ | ||||
| 	if (cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG && | ||||
| 			cr != COLLATION_FILE_NAME) | ||||
| 		goto err; | ||||
| 	i = le32_to_cpu(cr); | ||||
| 	if (i < 0) | ||||
| 		goto err; | ||||
| 	if (i <= 0x02) | ||||
| 		return ntfs_do_collate0x0[i](vol, data1, data1_len, | ||||
| 				data2, data2_len); | ||||
| 	if (i < 0x10) | ||||
| 		goto err; | ||||
| 	i -= 0x10; | ||||
| 	if (i <= 3) | ||||
| 		return ntfs_do_collate0x1[i](vol, data1, data1_len, | ||||
| 				data2, data2_len); | ||||
| err: | ||||
| 	ntfs_log_debug("Unknown collation rule.\n"); | ||||
| 	return NTFS_COLLATION_ERROR; | ||||
| } | ||||
|  | @ -0,0 +1,73 @@ | |||
| /**
 | ||||
|  * compat.c - Tweaks for Windows compatibility | ||||
|  * | ||||
|  * Copyright (c) 2002 Richard Russon | ||||
|  * Copyright (c) 2002-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 NTFS-3G | ||||
|  * 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 WINDOWS | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| 
 | ||||
| #include "compat.h" | ||||
| 
 | ||||
| /* TODO: Add check for FFS in the configure script... (AIA) */ | ||||
| 
 | ||||
| #ifndef HAVE_FFS | ||||
| /**
 | ||||
|  * ffs - Find the first set bit in an int | ||||
|  * @x: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| int ffs(int x) | ||||
| { | ||||
| 	int r = 1; | ||||
| 
 | ||||
| 	if (!x) | ||||
| 		return 0; | ||||
| 	if (!(x & 0xffff)) { | ||||
| 		x >>= 16; | ||||
| 		r += 16; | ||||
| 	} | ||||
| 	if (!(x & 0xff)) { | ||||
| 		x >>= 8; | ||||
| 		r += 8; | ||||
| 	} | ||||
| 	if (!(x & 0xf)) { | ||||
| 		x >>= 4; | ||||
| 		r += 4; | ||||
| 	} | ||||
| 	if (!(x & 3)) { | ||||
| 		x >>= 2; | ||||
| 		r += 2; | ||||
| 	} | ||||
| 	if (!(x & 1)) { | ||||
| 		x >>= 1; | ||||
| 		r += 1; | ||||
| 	} | ||||
| 	return r; | ||||
| } | ||||
| #endif /* HAVE_FFS */ | ||||
| 
 | ||||
| #endif /* WINDOWS */ | ||||
| 
 | ||||
|  | @ -0,0 +1,552 @@ | |||
| /**
 | ||||
|  * compress.c - Compressed attribute handling code.  Originated from the Linux-NTFS | ||||
|  *		project. | ||||
|  * | ||||
|  * Copyright (c) 2004-2005 Anton Altaparmakov | ||||
|  * Copyright (c) 2004-2006 Szabolcs Szakacsits | ||||
|  * Copyright (c)      2005 Yura Pakhuchiy | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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 <stdio.h> | ||||
| #endif | ||||
| #ifdef HAVE_STRING_H | ||||
| #include <string.h> | ||||
| #endif | ||||
| #ifdef HAVE_STDLIB_H | ||||
| #include <stdlib.h> | ||||
| #endif | ||||
| #ifdef HAVE_ERRNO_H | ||||
| #include <errno.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "attrib.h" | ||||
| #include "debug.h" | ||||
| #include "volume.h" | ||||
| #include "types.h" | ||||
| #include "layout.h" | ||||
| #include "runlist.h" | ||||
| #include "compress.h" | ||||
| #include "logging.h" | ||||
| #include "misc.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * enum ntfs_compression_constants - constants used in the compression code | ||||
|  */ | ||||
| typedef enum { | ||||
| 	/* Token types and access mask. */ | ||||
| 	NTFS_SYMBOL_TOKEN	=	0, | ||||
| 	NTFS_PHRASE_TOKEN	=	1, | ||||
| 	NTFS_TOKEN_MASK		=	1, | ||||
| 
 | ||||
| 	/* Compression sub-block constants. */ | ||||
| 	NTFS_SB_SIZE_MASK	=	0x0fff, | ||||
| 	NTFS_SB_SIZE		=	0x1000, | ||||
| 	NTFS_SB_IS_COMPRESSED	=	0x8000, | ||||
| } ntfs_compression_constants; | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_decompress - decompress a compression block into an array of pages | ||||
|  * @dest:	buffer to which to write the decompressed data | ||||
|  * @dest_size:	size of buffer @dest in bytes | ||||
|  * @cb_start:	compression block to decompress | ||||
|  * @cb_size:	size of compression block @cb_start in bytes | ||||
|  * | ||||
|  * This decompresses the compression block @cb_start into the destination | ||||
|  * buffer @dest. | ||||
|  * | ||||
|  * @cb_start is a pointer to the compression block which needs decompressing | ||||
|  * and @cb_size is the size of @cb_start in bytes (8-64kiB). | ||||
|  * | ||||
|  * Return 0 if success or -EOVERFLOW on error in the compressed stream. | ||||
|  */ | ||||
| static int ntfs_decompress(u8 *dest, const u32 dest_size, | ||||
| 		u8 *const cb_start, const u32 cb_size) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * Pointers into the compressed data, i.e. the compression block (cb), | ||||
| 	 * and the therein contained sub-blocks (sb). | ||||
| 	 */ | ||||
| 	u8 *cb_end = cb_start + cb_size; /* End of cb. */ | ||||
| 	u8 *cb = cb_start;	/* Current position in cb. */ | ||||
| 	u8 *cb_sb_start = cb;	/* Beginning of the current sb in the cb. */ | ||||
| 	u8 *cb_sb_end;		/* End of current sb / beginning of next sb. */ | ||||
| 	/* Variables for uncompressed data / destination. */ | ||||
| 	u8 *dest_end = dest + dest_size;	/* End of dest buffer. */ | ||||
| 	u8 *dest_sb_start;	/* Start of current sub-block in dest. */ | ||||
| 	u8 *dest_sb_end;	/* End of current sb in dest. */ | ||||
| 	/* Variables for tag and token parsing. */ | ||||
| 	u8 tag;			/* Current tag. */ | ||||
| 	int token;		/* Loop counter for the eight tokens in tag. */ | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering, cb_size = 0x%x.\n", (unsigned)cb_size); | ||||
| do_next_sb: | ||||
| 	ntfs_log_debug("Beginning sub-block at offset = 0x%x in the cb.\n", | ||||
| 			cb - cb_start); | ||||
| 	/*
 | ||||
| 	 * Have we reached the end of the compression block or the end of the | ||||
| 	 * decompressed data?  The latter can happen for example if the current | ||||
| 	 * position in the compression block is one byte before its end so the | ||||
| 	 * first two checks do not detect it. | ||||
| 	 */ | ||||
| 	if (cb == cb_end || !le16_to_cpup((u16*)cb) || dest == dest_end) { | ||||
| 		ntfs_log_debug("Completed. Returning success (0).\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	/* Setup offset for the current sub-block destination. */ | ||||
| 	dest_sb_start = dest; | ||||
| 	dest_sb_end = dest + NTFS_SB_SIZE; | ||||
| 	/* Check that we are still within allowed boundaries. */ | ||||
| 	if (dest_sb_end > dest_end) | ||||
| 		goto return_overflow; | ||||
| 	/* Does the minimum size of a compressed sb overflow valid range? */ | ||||
| 	if (cb + 6 > cb_end) | ||||
| 		goto return_overflow; | ||||
| 	/* Setup the current sub-block source pointers and validate range. */ | ||||
| 	cb_sb_start = cb; | ||||
| 	cb_sb_end = cb_sb_start + (le16_to_cpup((u16*)cb) & NTFS_SB_SIZE_MASK) | ||||
| 			+ 3; | ||||
| 	if (cb_sb_end > cb_end) | ||||
| 		goto return_overflow; | ||||
| 	/* Now, we are ready to process the current sub-block (sb). */ | ||||
| 	if (!(le16_to_cpup((u16*)cb) & NTFS_SB_IS_COMPRESSED)) { | ||||
| 		ntfs_log_debug("Found uncompressed sub-block.\n"); | ||||
| 		/* This sb is not compressed, just copy it into destination. */ | ||||
| 		/* Advance source position to first data byte. */ | ||||
| 		cb += 2; | ||||
| 		/* An uncompressed sb must be full size. */ | ||||
| 		if (cb_sb_end - cb != NTFS_SB_SIZE) | ||||
| 			goto return_overflow; | ||||
| 		/* Copy the block and advance the source position. */ | ||||
| 		memcpy(dest, cb, NTFS_SB_SIZE); | ||||
| 		cb += NTFS_SB_SIZE; | ||||
| 		/* Advance destination position to next sub-block. */ | ||||
| 		dest += NTFS_SB_SIZE; | ||||
| 		goto do_next_sb; | ||||
| 	} | ||||
| 	ntfs_log_debug("Found compressed sub-block.\n"); | ||||
| 	/* This sb is compressed, decompress it into destination. */ | ||||
| 	/* Forward to the first tag in the sub-block. */ | ||||
| 	cb += 2; | ||||
| do_next_tag: | ||||
| 	if (cb == cb_sb_end) { | ||||
| 		/* Check if the decompressed sub-block was not full-length. */ | ||||
| 		if (dest < dest_sb_end) { | ||||
| 			int nr_bytes = dest_sb_end - dest; | ||||
| 
 | ||||
| 			ntfs_log_debug("Filling incomplete sub-block with zeroes.\n"); | ||||
| 			/* Zero remainder and update destination position. */ | ||||
| 			memset(dest, 0, nr_bytes); | ||||
| 			dest += nr_bytes; | ||||
| 		} | ||||
| 		/* We have finished the current sub-block. */ | ||||
| 		goto do_next_sb; | ||||
| 	} | ||||
| 	/* Check we are still in range. */ | ||||
| 	if (cb > cb_sb_end || dest > dest_sb_end) | ||||
| 		goto return_overflow; | ||||
| 	/* Get the next tag and advance to first token. */ | ||||
| 	tag = *cb++; | ||||
| 	/* Parse the eight tokens described by the tag. */ | ||||
| 	for (token = 0; token < 8; token++, tag >>= 1) { | ||||
| 		u16 lg, pt, length, max_non_overlap; | ||||
| 		register u16 i; | ||||
| 		u8 *dest_back_addr; | ||||
| 
 | ||||
| 		/* Check if we are done / still in range. */ | ||||
| 		if (cb >= cb_sb_end || dest > dest_sb_end) | ||||
| 			break; | ||||
| 		/* Determine token type and parse appropriately.*/ | ||||
| 		if ((tag & NTFS_TOKEN_MASK) == NTFS_SYMBOL_TOKEN) { | ||||
| 			/*
 | ||||
| 			 * We have a symbol token, copy the symbol across, and | ||||
| 			 * advance the source and destination positions. | ||||
| 			 */ | ||||
| 			*dest++ = *cb++; | ||||
| 			/* Continue with the next token. */ | ||||
| 			continue; | ||||
| 		} | ||||
| 		/*
 | ||||
| 		 * We have a phrase token. Make sure it is not the first tag in | ||||
| 		 * the sb as this is illegal and would confuse the code below. | ||||
| 		 */ | ||||
| 		if (dest == dest_sb_start) | ||||
| 			goto return_overflow; | ||||
| 		/*
 | ||||
| 		 * Determine the number of bytes to go back (p) and the number | ||||
| 		 * of bytes to copy (l). We use an optimized algorithm in which | ||||
| 		 * we first calculate log2(current destination position in sb), | ||||
| 		 * which allows determination of l and p in O(1) rather than | ||||
| 		 * O(n). We just need an arch-optimized log2() function now. | ||||
| 		 */ | ||||
| 		lg = 0; | ||||
| 		for (i = dest - dest_sb_start - 1; i >= 0x10; i >>= 1) | ||||
| 			lg++; | ||||
| 		/* Get the phrase token into i. */ | ||||
| 		pt = le16_to_cpup((u16*)cb); | ||||
| 		/*
 | ||||
| 		 * Calculate starting position of the byte sequence in | ||||
| 		 * the destination using the fact that p = (pt >> (12 - lg)) + 1 | ||||
| 		 * and make sure we don't go too far back. | ||||
| 		 */ | ||||
| 		dest_back_addr = dest - (pt >> (12 - lg)) - 1; | ||||
| 		if (dest_back_addr < dest_sb_start) | ||||
| 			goto return_overflow; | ||||
| 		/* Now calculate the length of the byte sequence. */ | ||||
| 		length = (pt & (0xfff >> lg)) + 3; | ||||
| 		/* Verify destination is in range. */ | ||||
| 		if (dest + length > dest_sb_end) | ||||
| 			goto return_overflow; | ||||
| 		/* The number of non-overlapping bytes. */ | ||||
| 		max_non_overlap = dest - dest_back_addr; | ||||
| 		if (length <= max_non_overlap) { | ||||
| 			/* The byte sequence doesn't overlap, just copy it. */ | ||||
| 			memcpy(dest, dest_back_addr, length); | ||||
| 			/* Advance destination pointer. */ | ||||
| 			dest += length; | ||||
| 		} else { | ||||
| 			/*
 | ||||
| 			 * The byte sequence does overlap, copy non-overlapping | ||||
| 			 * part and then do a slow byte by byte copy for the | ||||
| 			 * overlapping part. Also, advance the destination | ||||
| 			 * pointer. | ||||
| 			 */ | ||||
| 			memcpy(dest, dest_back_addr, max_non_overlap); | ||||
| 			dest += max_non_overlap; | ||||
| 			dest_back_addr += max_non_overlap; | ||||
| 			length -= max_non_overlap; | ||||
| 			while (length--) | ||||
| 				*dest++ = *dest_back_addr++; | ||||
| 		} | ||||
| 		/* Advance source position and continue with the next token. */ | ||||
| 		cb += 2; | ||||
| 	} | ||||
| 	/* No tokens left in the current tag. Continue with the next tag. */ | ||||
| 	goto do_next_tag; | ||||
| return_overflow: | ||||
| 	errno = EOVERFLOW; | ||||
| 	ntfs_log_perror("Failed to decompress file"); | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_is_cb_compressed - internal function, do not use | ||||
|  * | ||||
|  * This is a very specialised function determining if a cb is compressed or | ||||
|  * uncompressed.  It is assumed that checking for a sparse cb has already been | ||||
|  * performed and that the cb is not sparse.  It makes all sorts of other | ||||
|  * assumptions as well and hence it is not useful anywhere other than where it | ||||
|  * is used at the moment.  Please, do not make this function available for use | ||||
|  * outside of compress.c as it is bound to confuse people and not do what they | ||||
|  * want. | ||||
|  * | ||||
|  * Return TRUE on errors so that the error will be detected later on in the | ||||
|  * code.  Might be a bit confusing to debug but there really should never be | ||||
|  * errors coming from here. | ||||
|  */ | ||||
| static BOOL ntfs_is_cb_compressed(ntfs_attr *na, runlist_element *rl,  | ||||
| 				  VCN cb_start_vcn, int cb_clusters) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * The simplest case: the run starting at @cb_start_vcn contains | ||||
| 	 * @cb_clusters clusters which are all not sparse, thus the cb is not | ||||
| 	 * compressed. | ||||
| 	 */ | ||||
| restart: | ||||
| 	cb_clusters -= rl->length - (cb_start_vcn - rl->vcn); | ||||
| 	while (cb_clusters > 0) { | ||||
| 		/* Go to the next run. */ | ||||
| 		rl++; | ||||
| 		/* Map the next runlist fragment if it is not mapped. */ | ||||
| 		if (rl->lcn < LCN_HOLE || !rl->length) { | ||||
| 			cb_start_vcn = rl->vcn; | ||||
| 			rl = ntfs_attr_find_vcn(na, rl->vcn); | ||||
| 			if (!rl || rl->lcn < LCN_HOLE || !rl->length) | ||||
| 				return TRUE; | ||||
| 			/*
 | ||||
| 			 * If the runs were merged need to deal with the | ||||
| 			 * resulting partial run so simply restart. | ||||
| 			 */ | ||||
| 			if (rl->vcn < cb_start_vcn) | ||||
| 				goto restart; | ||||
| 		} | ||||
| 		/* If the current run is sparse, the cb is compressed. */ | ||||
| 		if (rl->lcn == LCN_HOLE) | ||||
| 			return TRUE; | ||||
| 		/* If the whole cb is not sparse, it is not compressed. */ | ||||
| 		if (rl->length >= cb_clusters) | ||||
| 			return FALSE; | ||||
| 		cb_clusters -= rl->length; | ||||
| 	}; | ||||
| 	/* All cb_clusters were not sparse thus the cb is not compressed. */ | ||||
| 	return FALSE; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_compressed_attr_pread - read from a compressed attribute | ||||
|  * @na:		ntfs attribute to read from | ||||
|  * @pos:	byte position in the attribute to begin reading from | ||||
|  * @count:	number of bytes to read | ||||
|  * @b:		output data buffer | ||||
|  * | ||||
|  * NOTE:  You probably want to be using attrib.c::ntfs_attr_pread() instead. | ||||
|  * | ||||
|  * This function will read @count bytes starting at offset @pos from the | ||||
|  * compressed ntfs attribute @na into the data buffer @b. | ||||
|  * | ||||
|  * On success, return the number of successfully read bytes.  If this number | ||||
|  * is lower than @count this means that the read reached end of file or that | ||||
|  * an error was encountered during the read so that the read is partial. | ||||
|  * 0 means end of file or nothing was read (also return 0 when @count is 0). | ||||
|  * | ||||
|  * On error and nothing has been read, return -1 with errno set appropriately | ||||
|  * to the return code of ntfs_pread(), or to EINVAL in case of invalid | ||||
|  * arguments. | ||||
|  */ | ||||
| s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b) | ||||
| { | ||||
| 	s64 br, to_read, ofs, total, total2; | ||||
| 	u64 cb_size_mask; | ||||
| 	VCN start_vcn, vcn, end_vcn; | ||||
| 	ntfs_volume *vol; | ||||
| 	runlist_element *rl; | ||||
| 	u8 *dest, *cb, *cb_pos, *cb_end; | ||||
| 	u32 cb_size; | ||||
| 	int err; | ||||
| 	unsigned int nr_cbs, cb_clusters; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count 0x%llx.\n", | ||||
| 			(unsigned long long)na->ni->mft_no, na->type, | ||||
| 			(long long)pos, (long long)count); | ||||
| 	if (!na || !NAttrCompressed(na) || !na->ni || !na->ni->vol || !b || | ||||
| 			pos < 0 || count < 0) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * Encrypted attributes are not supported.  We return access denied, | ||||
| 	 * which is what Windows NT4 does, too. | ||||
| 	 */ | ||||
| 	if (NAttrEncrypted(na)) { | ||||
| 		errno = EACCES; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (!count) | ||||
| 		return 0; | ||||
| 	/* Truncate reads beyond end of attribute. */ | ||||
| 	if (pos + count > na->data_size) { | ||||
| 		if (pos >= na->data_size) { | ||||
| 			return 0; | ||||
| 		} | ||||
| 		count = na->data_size - pos; | ||||
| 	} | ||||
| 	/* If it is a resident attribute, simply use ntfs_attr_pread(). */ | ||||
| 	if (!NAttrNonResident(na)) | ||||
| 		return ntfs_attr_pread(na, pos, count, b); | ||||
| 	total = total2 = 0; | ||||
| 	/* Zero out reads beyond initialized size. */ | ||||
| 	if (pos + count > na->initialized_size) { | ||||
| 		if (pos >= na->initialized_size) { | ||||
| 			memset(b, 0, count); | ||||
| 			return count; | ||||
| 		} | ||||
| 		total2 = pos + count - na->initialized_size; | ||||
| 		count -= total2; | ||||
| 		memset((u8*)b + count, 0, total2); | ||||
| 	} | ||||
| 	vol = na->ni->vol; | ||||
| 	cb_size = na->compression_block_size; | ||||
| 	cb_size_mask = cb_size - 1UL; | ||||
| 	cb_clusters = na->compression_block_clusters; | ||||
| 	 | ||||
| 	/* Need a temporary buffer for each loaded compression block. */ | ||||
| 	cb = ntfs_malloc(cb_size); | ||||
| 	if (!cb) | ||||
| 		return -1; | ||||
| 	 | ||||
| 	/* Need a temporary buffer for each uncompressed block. */ | ||||
| 	dest = ntfs_malloc(cb_size); | ||||
| 	if (!dest) { | ||||
| 		free(cb); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * The first vcn in the first compression block (cb) which we need to | ||||
| 	 * decompress. | ||||
| 	 */ | ||||
| 	start_vcn = (pos & ~cb_size_mask) >> vol->cluster_size_bits; | ||||
| 	/* Offset in the uncompressed cb at which to start reading data. */ | ||||
| 	ofs = pos & cb_size_mask; | ||||
| 	/*
 | ||||
| 	 * The first vcn in the cb after the last cb which we need to | ||||
| 	 * decompress. | ||||
| 	 */ | ||||
| 	end_vcn = ((pos + count + cb_size - 1) & ~cb_size_mask) >> | ||||
| 			vol->cluster_size_bits; | ||||
| 	/* Number of compression blocks (cbs) in the wanted vcn range. */ | ||||
| 	nr_cbs = (end_vcn - start_vcn) << vol->cluster_size_bits >> | ||||
| 			na->compression_block_size_bits; | ||||
| 	cb_end = cb + cb_size; | ||||
| do_next_cb: | ||||
| 	nr_cbs--; | ||||
| 	cb_pos = cb; | ||||
| 	vcn = start_vcn; | ||||
| 	start_vcn += cb_clusters; | ||||
| 
 | ||||
| 	/* Check whether the compression block is sparse. */ | ||||
| 	rl = ntfs_attr_find_vcn(na, vcn); | ||||
| 	if (!rl || rl->lcn < LCN_HOLE) { | ||||
| 		free(cb); | ||||
| 		free(dest); | ||||
| 		if (total) | ||||
| 			return total; | ||||
| 		/* FIXME: Do we want EIO or the error code? (AIA) */ | ||||
| 		errno = EIO; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (rl->lcn == LCN_HOLE) { | ||||
| 		/* Sparse cb, zero out destination range overlapping the cb. */ | ||||
| 		ntfs_log_debug("Found sparse compression block.\n"); | ||||
| 		to_read = min(count, cb_size - ofs); | ||||
| 		memset(b, 0, to_read); | ||||
| 		ofs = 0; | ||||
| 		total += to_read; | ||||
| 		count -= to_read; | ||||
| 		b = (u8*)b + to_read; | ||||
| 	} else if (!ntfs_is_cb_compressed(na, rl, vcn, cb_clusters)) { | ||||
| 		s64 tdata_size, tinitialized_size; | ||||
| 		/*
 | ||||
| 		 * Uncompressed cb, read it straight into the destination range | ||||
| 		 * overlapping the cb. | ||||
| 		 */ | ||||
| 		ntfs_log_debug("Found uncompressed compression block.\n"); | ||||
| 		/*
 | ||||
| 		 * Read the uncompressed data into the destination buffer. | ||||
| 		 * NOTE: We cheat a little bit here by marking the attribute as | ||||
| 		 * not compressed in the ntfs_attr structure so that we can | ||||
| 		 * read the data by simply using ntfs_attr_pread().  (-8 | ||||
| 		 * NOTE: we have to modify data_size and initialized_size | ||||
| 		 * temporarily as well... | ||||
| 		 */ | ||||
| 		to_read = min(count, cb_size - ofs); | ||||
| 		ofs += vcn << vol->cluster_size_bits; | ||||
| 		NAttrClearCompressed(na); | ||||
| 		tdata_size = na->data_size; | ||||
| 		tinitialized_size = na->initialized_size; | ||||
| 		na->data_size = na->initialized_size = na->allocated_size; | ||||
| 		do { | ||||
| 			br = ntfs_attr_pread(na, ofs, to_read, b); | ||||
| 			if (br < 0) { | ||||
| 				err = errno; | ||||
| 				na->data_size = tdata_size; | ||||
| 				na->initialized_size = tinitialized_size; | ||||
| 				NAttrSetCompressed(na); | ||||
| 				free(cb); | ||||
| 				free(dest); | ||||
| 				if (total) | ||||
| 					return total; | ||||
| 				errno = err; | ||||
| 				return br; | ||||
| 			} | ||||
| 			total += br; | ||||
| 			count -= br; | ||||
| 			b = (u8*)b + br; | ||||
| 			to_read -= br; | ||||
| 			ofs += br; | ||||
| 		} while (to_read > 0); | ||||
| 		na->data_size = tdata_size; | ||||
| 		na->initialized_size = tinitialized_size; | ||||
| 		NAttrSetCompressed(na); | ||||
| 		ofs = 0; | ||||
| 	} else { | ||||
| 		s64 tdata_size, tinitialized_size; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Compressed cb, decompress it into the temporary buffer, then | ||||
| 		 * copy the data to the destination range overlapping the cb. | ||||
| 		 */ | ||||
| 		ntfs_log_debug("Found compressed compression block.\n"); | ||||
| 		/*
 | ||||
| 		 * Read the compressed data into the temporary buffer. | ||||
| 		 * NOTE: We cheat a little bit here by marking the attribute as | ||||
| 		 * not compressed in the ntfs_attr structure so that we can | ||||
| 		 * read the raw, compressed data by simply using | ||||
| 		 * ntfs_attr_pread().  (-8 | ||||
| 		 * NOTE: We have to modify data_size and initialized_size | ||||
| 		 * temporarily as well... | ||||
| 		 */ | ||||
| 		to_read = cb_size; | ||||
| 		NAttrClearCompressed(na); | ||||
| 		tdata_size = na->data_size; | ||||
| 		tinitialized_size = na->initialized_size; | ||||
| 		na->data_size = na->initialized_size = na->allocated_size; | ||||
| 		do { | ||||
| 			br = ntfs_attr_pread(na, | ||||
| 					(vcn << vol->cluster_size_bits) + | ||||
| 					(cb_pos - cb), to_read, cb_pos); | ||||
| 			if (br < 0) { | ||||
| 				err = errno; | ||||
| 				na->data_size = tdata_size; | ||||
| 				na->initialized_size = tinitialized_size; | ||||
| 				NAttrSetCompressed(na); | ||||
| 				free(cb); | ||||
| 				free(dest); | ||||
| 				if (total) | ||||
| 					return total; | ||||
| 				errno = err; | ||||
| 				return br; | ||||
| 			} | ||||
| 			cb_pos += br; | ||||
| 			to_read -= br; | ||||
| 		} while (to_read > 0); | ||||
| 		na->data_size = tdata_size; | ||||
| 		na->initialized_size = tinitialized_size; | ||||
| 		NAttrSetCompressed(na); | ||||
| 		/* Just a precaution. */ | ||||
| 		if (cb_pos + 2 <= cb_end) | ||||
| 			*(u16*)cb_pos = 0; | ||||
| 		ntfs_log_debug("Successfully read the compression block.\n"); | ||||
| 		if (ntfs_decompress(dest, cb_size, cb, cb_size) < 0) { | ||||
| 			err = errno; | ||||
| 			free(cb); | ||||
| 			free(dest); | ||||
| 			if (total) | ||||
| 				return total; | ||||
| 			errno = err; | ||||
| 			return -1; | ||||
| 		} | ||||
| 		to_read = min(count, cb_size - ofs); | ||||
| 		memcpy(b, dest + ofs, to_read); | ||||
| 		total += to_read; | ||||
| 		count -= to_read; | ||||
| 		b = (u8*)b + to_read; | ||||
| 		ofs = 0; | ||||
| 	} | ||||
| 	/* Do we have more work to do? */ | ||||
| 	if (nr_cbs) | ||||
| 		goto do_next_cb; | ||||
| 	/* We no longer need the buffers. */ | ||||
| 	free(cb); | ||||
| 	free(dest); | ||||
| 	/* Return number of bytes read. */ | ||||
| 	return total + total2; | ||||
| } | ||||
|  | @ -0,0 +1,73 @@ | |||
| /**
 | ||||
|  * debug.c - Debugging output functions. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2002-2004 Anton Altaparmakov | ||||
|  * Copyright (c) 2004-2006 Szabolcs Szakacsits | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_ERRNO_H | ||||
| #include <errno.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "runlist.h" | ||||
| #include "debug.h" | ||||
| #include "logging.h" | ||||
| 
 | ||||
| #ifdef DEBUG | ||||
| /**
 | ||||
|  * ntfs_debug_runlist_dump - Dump a runlist. | ||||
|  * @rl: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| void ntfs_debug_runlist_dump(const runlist_element *rl) | ||||
| { | ||||
| 	int i = 0; | ||||
| 	const char *lcn_str[5] = { "LCN_HOLE         ", "LCN_RL_NOT_MAPPED", | ||||
| 				   "LCN_ENOENT       ", "LCN_EINVAL       ", | ||||
| 				   "LCN_unknown      " }; | ||||
| 
 | ||||
| 	ntfs_log_debug("NTFS-fs DEBUG: Dumping runlist (values in hex):\n"); | ||||
| 	if (!rl) { | ||||
| 		ntfs_log_debug("Run list not present.\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 	ntfs_log_debug("VCN              LCN               Run length\n"); | ||||
| 	do { | ||||
| 		LCN lcn = (rl + i)->lcn; | ||||
| 
 | ||||
| 		if (lcn < (LCN)0) { | ||||
| 			int idx = -lcn - 1; | ||||
| 
 | ||||
| 			if (idx > -LCN_EINVAL - 1) | ||||
| 				idx = 4; | ||||
| 			ntfs_log_debug("%-16llx %s %-16llx%s\n", rl[i].vcn, lcn_str[idx], rl[i].length, rl[i].length ? "" : " (runlist end)"); | ||||
| 		} else | ||||
| 			ntfs_log_debug("%-16llx %-16llx  %-16llx%s\n", rl[i].vcn, rl[i].lcn, rl[i].length, rl[i].length ? "" : " (runlist end)"); | ||||
| 	} while (rl[i++].length); | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
|  | @ -0,0 +1,734 @@ | |||
| /**
 | ||||
|  * device.c - Low level device io functions. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2004-2006 Anton Altaparmakov | ||||
|  * Copyright (c) 2004-2006 Szabolcs Szakacsits | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_UNISTD_H | ||||
| #include <unistd.h> | ||||
| #endif | ||||
| #ifdef HAVE_STDLIB_H | ||||
| #include <stdlib.h> | ||||
| #endif | ||||
| #ifdef HAVE_STRING_H | ||||
| #include <string.h> | ||||
| #endif | ||||
| #ifdef HAVE_ERRNO_H | ||||
| #include <errno.h> | ||||
| #endif | ||||
| #ifdef HAVE_STDIO_H | ||||
| #include <stdio.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYS_TYPES_H | ||||
| #include <sys/types.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYS_STAT_H | ||||
| #include <sys/stat.h> | ||||
| #endif | ||||
| #ifdef HAVE_FCNTL_H | ||||
| #include <fcntl.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYS_IOCTL_H | ||||
| #include <sys/ioctl.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYS_PARAM_H | ||||
| #include <sys/param.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYS_MOUNT_H | ||||
| #include <sys/mount.h> | ||||
| #endif | ||||
| #ifdef HAVE_LINUX_FD_H | ||||
| #include <linux/fd.h> | ||||
| #endif | ||||
| #ifdef HAVE_LINUX_HDREG_H | ||||
| #include <linux/hdreg.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "mst.h" | ||||
| #include "debug.h" | ||||
| #include "device.h" | ||||
| #include "logging.h" | ||||
| #include "misc.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 | ||||
| #if defined(linux) && !defined(HDIO_GETGEO) | ||||
| #define HDIO_GETGEO	0x0301	/* Get device geometry. */ | ||||
| #endif | ||||
| #if defined(linux) && defined(_IO) && !defined(BLKSSZGET) | ||||
| #	define BLKSSZGET _IO(0x12,104) /* Get device sector size in bytes. */ | ||||
| #endif | ||||
| #if defined(linux) && defined(_IO) && !defined(BLKBSZSET) | ||||
| #	define BLKBSZSET _IOW(0x12,113,size_t) /* Set device block size in bytes. */ | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_alloc - allocate an ntfs device structure and pre-initialize it | ||||
|  * @name:	name of the device (must be present) | ||||
|  * @state:	initial device state (usually zero) | ||||
|  * @dops:	ntfs device operations to use with the device (must be present) | ||||
|  * @priv_data:	pointer to private data (optional) | ||||
|  * | ||||
|  * Allocate an ntfs device structure and pre-initialize it with the user- | ||||
|  * specified device operations @dops, device state @state, device name @name, | ||||
|  * and optional private data @priv_data. | ||||
|  * | ||||
|  * Note, @name is copied and can hence be freed after this functions returns. | ||||
|  * | ||||
|  * On success return a pointer to the allocated ntfs device structure and on | ||||
|  * error return NULL with errno set to the error code returned by ntfs_malloc(). | ||||
|  */ | ||||
| struct ntfs_device *ntfs_device_alloc(const char *name, const long state, | ||||
| 		struct ntfs_device_operations *dops, void *priv_data) | ||||
| { | ||||
| 	struct ntfs_device *dev; | ||||
| 
 | ||||
| 	if (!name) { | ||||
| 		errno = EINVAL; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	dev = ntfs_malloc(sizeof(struct ntfs_device)); | ||||
| 	if (dev) { | ||||
| 		if (!(dev->d_name = strdup(name))) { | ||||
| 			int eo = errno; | ||||
| 			free(dev); | ||||
| 			errno = eo; | ||||
| 			return NULL; | ||||
| 		} | ||||
| 		dev->d_ops = dops; | ||||
| 		dev->d_state = state; | ||||
| 		dev->d_private = priv_data; | ||||
| 	} | ||||
| 	return dev; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_free - free an ntfs device structure | ||||
|  * @dev:	ntfs device structure to free | ||||
|  * | ||||
|  * Free the ntfs device structure @dev. | ||||
|  * | ||||
|  * Return 0 on success or -1 on error with errno set to the error code. The | ||||
|  * following error codes are defined: | ||||
|  *	EINVAL		Invalid pointer @dev. | ||||
|  *	EBUSY		Device is still open. Close it before freeing it! | ||||
|  */ | ||||
| int ntfs_device_free(struct ntfs_device *dev) | ||||
| { | ||||
| 	if (!dev) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (NDevOpen(dev)) { | ||||
| 		errno = EBUSY; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	free(dev->d_name); | ||||
| 	free(dev); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_pread - positioned read from disk | ||||
|  * @dev:	device to read from | ||||
|  * @pos:	position in device to read from | ||||
|  * @count:	number of bytes to read | ||||
|  * @b:		output data buffer | ||||
|  * | ||||
|  * This function will read @count bytes from device @dev at position @pos into | ||||
|  * the data buffer @b. | ||||
|  * | ||||
|  * On success, return the number of successfully read bytes. If this number is | ||||
|  * lower than @count this means that we have either reached end of file or | ||||
|  * encountered an error during the read so that the read is partial. 0 means | ||||
|  * end of file or nothing to read (@count is 0). | ||||
|  * | ||||
|  * On error and nothing has been read, return -1 with errno set appropriately | ||||
|  * to the return code of either seek, read, or set to EINVAL in case of | ||||
|  * invalid arguments. | ||||
|  */ | ||||
| s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, void *b) | ||||
| { | ||||
| 	s64 br, total; | ||||
| 	struct ntfs_device_operations *dops; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering for pos 0x%llx, count 0x%llx.\n", pos, count); | ||||
| 	if (!b || count < 0 || pos < 0) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (!count) | ||||
| 		return 0; | ||||
| 	dops = dev->d_ops; | ||||
| 	/* Locate to position. */ | ||||
| 	if (dops->seek(dev, pos, SEEK_SET) == (off_t)-1) { | ||||
| 		ntfs_log_perror("ntfs_pread: device seek to 0x%llx returned error", | ||||
| 				pos); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	/* Read the data. */ | ||||
| 	for (total = 0; count; count -= br, total += br) { | ||||
| 		br = dops->read(dev, (char*)b + total, count); | ||||
| 		/* If everything ok, continue. */ | ||||
| 		if (br > 0) | ||||
| 			continue; | ||||
| 		/* If EOF or error return number of bytes read. */ | ||||
| 		if (!br || total) | ||||
| 			return total; | ||||
| 		/* Nothing read and error, return error status. */ | ||||
| 		return br; | ||||
| 	} | ||||
| 	/* Finally, return the number of bytes read. */ | ||||
| 	return total; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_pwrite - positioned write to disk | ||||
|  * @dev:	device to write to | ||||
|  * @pos:	position in file descriptor to write to | ||||
|  * @count:	number of bytes to write | ||||
|  * @b:		data buffer to write to disk | ||||
|  * | ||||
|  * This function will write @count bytes from data buffer @b to the device @dev | ||||
|  * at position @pos. | ||||
|  * | ||||
|  * On success, return the number of successfully written bytes. If this number | ||||
|  * is lower than @count this means that the write has been interrupted in | ||||
|  * flight or that an error was encountered during the write so that the write | ||||
|  * is partial. 0 means nothing was written (also return 0 when @count is 0). | ||||
|  * | ||||
|  * On error and nothing has been written, return -1 with errno set | ||||
|  * appropriately to the return code of either seek, write, or set | ||||
|  * to EINVAL in case of invalid arguments. | ||||
|  */ | ||||
| s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, | ||||
| 		const void *b) | ||||
| { | ||||
| 	s64 written, total, ret = -1; | ||||
| 	struct ntfs_device_operations *dops; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering for pos 0x%llx, count 0x%llx.\n", pos, count); | ||||
| 	if (!b || count < 0 || pos < 0) { | ||||
| 		errno = EINVAL; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (!count) | ||||
| 		return 0; | ||||
| 	if (NDevReadOnly(dev)) { | ||||
| 		errno = EROFS; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	dops = dev->d_ops; | ||||
| 	/* Locate to position. */ | ||||
| 	if (dops->seek(dev, pos, SEEK_SET) == (off_t)-1) { | ||||
| 		ntfs_log_perror("ntfs_pwrite: seek to 0x%llx returned error", | ||||
| 				pos); | ||||
| 		goto out; | ||||
| 	} | ||||
| 	NDevSetDirty(dev); | ||||
| 	for (total = 0; count; count -= written, total += written) { | ||||
| 		written = dops->write(dev, (const char*)b + total, count); | ||||
| 		/* If everything ok, continue. */ | ||||
| 		if (written > 0) | ||||
| 			continue; | ||||
| 		/*
 | ||||
| 		 * If nothing written or error return number of bytes written. | ||||
| 		 */ | ||||
| 		if (!written || total) | ||||
| 			break; | ||||
| 		/* Nothing written and error, return error status. */ | ||||
| 		total = written; | ||||
| 		break; | ||||
| 	} | ||||
| 	ret = total; | ||||
| out:	 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_mst_pread - multi sector transfer (mst) positioned read | ||||
|  * @dev:	device to read from | ||||
|  * @pos:	position in file descriptor to read from | ||||
|  * @count:	number of blocks to read | ||||
|  * @bksize:	size of each block that needs mst deprotecting | ||||
|  * @b:		output data buffer | ||||
|  * | ||||
|  * Multi sector transfer (mst) positioned read. This function will read @count | ||||
|  * blocks of size @bksize bytes each from device @dev at position @pos into the | ||||
|  * the data buffer @b. | ||||
|  * | ||||
|  * On success, return the number of successfully read blocks. If this number is | ||||
|  * lower than @count this means that we have reached end of file, that the read | ||||
|  * was interrupted, or that an error was encountered during the read so that | ||||
|  * the read is partial. 0 means end of file or nothing was read (also return 0 | ||||
|  * when @count or @bksize are 0). | ||||
|  * | ||||
|  * On error and nothing was read, return -1 with errno set appropriately to the | ||||
|  * return code of either seek, read, or set to EINVAL in case of invalid | ||||
|  * arguments. | ||||
|  * | ||||
|  * NOTE: If an incomplete multi sector transfer has been detected the magic | ||||
|  * will have been changed to magic_BAAD but no error will be returned. Thus it | ||||
|  * is possible that we return count blocks as being read but that any number | ||||
|  * (between zero and count!) of these blocks is actually subject to a multi | ||||
|  * sector transfer error. This should be detected by the caller by checking for | ||||
|  * the magic being "BAAD". | ||||
|  */ | ||||
| s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count, | ||||
| 		const u32 bksize, void *b) | ||||
| { | ||||
| 	s64 br, i; | ||||
| 
 | ||||
| 	if (bksize & (bksize - 1) || bksize % NTFS_BLOCK_SIZE) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	/* Do the read. */ | ||||
| 	br = ntfs_pread(dev, pos, count * bksize, b); | ||||
| 	if (br < 0) | ||||
| 		return br; | ||||
| 	/*
 | ||||
| 	 * Apply fixups to successfully read data, disregarding any errors | ||||
| 	 * returned from the MST fixup function. This is because we want to | ||||
| 	 * fixup everything possible and we rely on the fact that the "BAAD" | ||||
| 	 * magic will be detected later on. | ||||
| 	 */ | ||||
| 	count = br / bksize; | ||||
| 	for (i = 0; i < count; ++i) | ||||
| 		ntfs_mst_post_read_fixup((NTFS_RECORD*) | ||||
| 				((u8*)b + i * bksize), bksize); | ||||
| 	/* Finally, return the number of complete blocks read. */ | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_mst_pwrite - multi sector transfer (mst) positioned write | ||||
|  * @dev:	device to write to | ||||
|  * @pos:	position in file descriptor to write to | ||||
|  * @count:	number of blocks to write | ||||
|  * @bksize:	size of each block that needs mst protecting | ||||
|  * @b:		data buffer to write to disk | ||||
|  * | ||||
|  * Multi sector transfer (mst) positioned write. This function will write | ||||
|  * @count blocks of size @bksize bytes each from data buffer @b to the device | ||||
|  * @dev at position @pos. | ||||
|  * | ||||
|  * On success, return the number of successfully written blocks. If this number | ||||
|  * is lower than @count this means that the write has been interrupted or that | ||||
|  * an error was encountered during the write so that the write is partial. 0 | ||||
|  * means nothing was written (also return 0 when @count or @bksize are 0). | ||||
|  * | ||||
|  * On error and nothing has been written, return -1 with errno set | ||||
|  * appropriately to the return code of either seek, write, or set | ||||
|  * to EINVAL in case of invalid arguments. | ||||
|  * | ||||
|  * NOTE: We mst protect the data, write it, then mst deprotect it using a quick | ||||
|  * deprotect algorithm (no checking). This saves us from making a copy before | ||||
|  * the write and at the same time causes the usn to be incremented in the | ||||
|  * buffer. This conceptually fits in better with the idea that cached data is | ||||
|  * always deprotected and protection is performed when the data is actually | ||||
|  * going to hit the disk and the cache is immediately deprotected again | ||||
|  * simulating an mst read on the written data. This way cache coherency is | ||||
|  * achieved. | ||||
|  */ | ||||
| s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, | ||||
| 		const u32 bksize, void *b) | ||||
| { | ||||
| 	s64 written, i; | ||||
| 
 | ||||
| 	if (count < 0 || bksize % NTFS_BLOCK_SIZE) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (!count) | ||||
| 		return 0; | ||||
| 	/* Prepare data for writing. */ | ||||
| 	for (i = 0; i < count; ++i) { | ||||
| 		int err; | ||||
| 
 | ||||
| 		err = ntfs_mst_pre_write_fixup((NTFS_RECORD*) | ||||
| 				((u8*)b + i * bksize), bksize); | ||||
| 		if (err < 0) { | ||||
| 			/* Abort write at this position. */ | ||||
| 			if (!i) | ||||
| 				return err; | ||||
| 			count = i; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	/* Write the prepared data. */ | ||||
| 	written = ntfs_pwrite(dev, pos, count * bksize, b); | ||||
| 	/* Quickly deprotect the data again. */ | ||||
| 	for (i = 0; i < count; ++i) | ||||
| 		ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)b + i * bksize)); | ||||
| 	if (written <= 0) | ||||
| 		return written; | ||||
| 	/* Finally, return the number of complete blocks written. */ | ||||
| 	return written / bksize; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_cluster_read - read ntfs clusters | ||||
|  * @vol:	volume to read from | ||||
|  * @lcn:	starting logical cluster number | ||||
|  * @count:	number of clusters to read | ||||
|  * @b:		output data buffer | ||||
|  * | ||||
|  * Read @count ntfs clusters starting at logical cluster number @lcn from | ||||
|  * volume @vol into buffer @b. Return number of clusters read or -1 on error, | ||||
|  * with errno set to the error code. | ||||
|  */ | ||||
| s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, const s64 count, | ||||
| 		void *b) | ||||
| { | ||||
| 	s64 br; | ||||
| 
 | ||||
| 	if (!vol || lcn < 0 || count < 0) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (vol->nr_clusters < lcn + count) { | ||||
| 		errno = ESPIPE; | ||||
| 		ntfs_log_perror("Trying to read outside of volume " | ||||
| 				"(%lld < %lld)", vol->nr_clusters, lcn + count); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	br = ntfs_pread(vol->dev, lcn << vol->cluster_size_bits, | ||||
| 			count << vol->cluster_size_bits, b); | ||||
| 	if (br < 0) { | ||||
| 		ntfs_log_perror("Error reading cluster(s)"); | ||||
| 		return br; | ||||
| 	} | ||||
| 	return br >> vol->cluster_size_bits; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_cluster_write - write ntfs clusters | ||||
|  * @vol:	volume to write to | ||||
|  * @lcn:	starting logical cluster number | ||||
|  * @count:	number of clusters to write | ||||
|  * @b:		data buffer to write to disk | ||||
|  * | ||||
|  * Write @count ntfs clusters starting at logical cluster number @lcn from | ||||
|  * buffer @b to volume @vol. Return the number of clusters written or -1 on | ||||
|  * error, with errno set to the error code. | ||||
|  */ | ||||
| s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn, | ||||
| 		const s64 count, const void *b) | ||||
| { | ||||
| 	s64 bw; | ||||
| 
 | ||||
| 	if (!vol || lcn < 0 || count < 0) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (vol->nr_clusters < lcn + count) { | ||||
| 		errno = ESPIPE; | ||||
| 		ntfs_log_perror("Trying to write outside of volume " | ||||
| 				"(%lld < %lld)", vol->nr_clusters, lcn + count); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (!NVolReadOnly(vol)) | ||||
| 		bw = ntfs_pwrite(vol->dev, lcn << vol->cluster_size_bits, | ||||
| 				count << vol->cluster_size_bits, b); | ||||
| 	else | ||||
| 		bw = count << vol->cluster_size_bits; | ||||
| 	if (bw < 0) { | ||||
| 		ntfs_log_perror("Error writing cluster(s)"); | ||||
| 		return bw; | ||||
| 	} | ||||
| 	return bw >> vol->cluster_size_bits; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_offset_valid - test if a device offset is valid | ||||
|  * @dev:	open device | ||||
|  * @ofs:	offset to test for validity | ||||
|  * | ||||
|  * Test if the offset @ofs is an existing location on the device described | ||||
|  * by the open device structure @dev. | ||||
|  * | ||||
|  * Return 0 if it is valid and -1 if it is not valid. | ||||
|  */ | ||||
| static int ntfs_device_offset_valid(struct ntfs_device *dev, s64 ofs) | ||||
| { | ||||
| 	char ch; | ||||
| 
 | ||||
| 	if (dev->d_ops->seek(dev, ofs, SEEK_SET) >= 0 && | ||||
| 			dev->d_ops->read(dev, &ch, 1) == 1) | ||||
| 		return 0; | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_size_get - return the size of a device in blocks | ||||
|  * @dev:	open device | ||||
|  * @block_size:	block size in bytes in which to return the result | ||||
|  * | ||||
|  * Return the number of @block_size sized blocks in the device described by the | ||||
|  * open device @dev. | ||||
|  * | ||||
|  * Adapted from e2fsutils-1.19, Copyright (C) 1995 Theodore Ts'o. | ||||
|  * | ||||
|  * On error return -1 with errno set to the error code. | ||||
|  */ | ||||
| s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size) | ||||
| { | ||||
| 	s64 high, low; | ||||
| 
 | ||||
| 	if (!dev || block_size <= 0 || (block_size - 1) & block_size) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| #ifdef BLKGETSIZE64 | ||||
| 	{	u64 size; | ||||
| 
 | ||||
| 		if (dev->d_ops->ioctl(dev, BLKGETSIZE64, &size) >= 0) { | ||||
| 			ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu (0x%llx)\n", | ||||
| 					(unsigned long long)size, | ||||
| 					(unsigned long long)size); | ||||
| 			return (s64)size / block_size; | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
| #ifdef BLKGETSIZE | ||||
| 	{	unsigned long size; | ||||
| 
 | ||||
| 		if (dev->d_ops->ioctl(dev, BLKGETSIZE, &size) >= 0) { | ||||
| 			ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu (0x%lx)\n", | ||||
| 					size, size); | ||||
| 			return (s64)size * 512 / block_size; | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
| #ifdef FDGETPRM | ||||
| 	{       struct floppy_struct this_floppy; | ||||
| 
 | ||||
| 		if (dev->d_ops->ioctl(dev, FDGETPRM, &this_floppy) >= 0) { | ||||
| 			ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu (0x%lx)\n", | ||||
| 					(unsigned long)this_floppy.size, | ||||
| 					(unsigned long)this_floppy.size); | ||||
| 			return (s64)this_floppy.size * 512 / block_size; | ||||
| 		} | ||||
| 	} | ||||
| #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; !ntfs_device_offset_valid(dev, high); high <<= 1) | ||||
| 		low = high; | ||||
| 	while (low < high - 1LL) { | ||||
| 		const s64 mid = (low + high) / 2; | ||||
| 
 | ||||
| 		if (!ntfs_device_offset_valid(dev, mid)) | ||||
| 			low = mid; | ||||
| 		else | ||||
| 			high = mid; | ||||
| 	} | ||||
| 	dev->d_ops->seek(dev, 0LL, SEEK_SET); | ||||
| 	return (low + 1LL) / block_size; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_partition_start_sector_get - get starting sector of a partition | ||||
|  * @dev:	open device | ||||
|  * | ||||
|  * On success, return the starting sector of the partition @dev in the parent | ||||
|  * block device of @dev.  On error return -1 with errno set to the error code. | ||||
|  * | ||||
|  * The following error codes are defined: | ||||
|  *	EINVAL		Input parameter error | ||||
|  *	EOPNOTSUPP	System does not support HDIO_GETGEO ioctl | ||||
|  *	ENOTTY		@dev is a file or a device not supporting HDIO_GETGEO | ||||
|  */ | ||||
| s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev) | ||||
| { | ||||
| 	if (!dev) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| #ifdef HDIO_GETGEO | ||||
| 	{	struct hd_geometry geo; | ||||
| 
 | ||||
| 		if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { | ||||
| 			ntfs_log_debug("HDIO_GETGEO start_sect = %lu (0x%lx)\n", | ||||
| 					geo.start, geo.start); | ||||
| 			return geo.start; | ||||
| 		} | ||||
| 	} | ||||
| #else | ||||
| 	errno = EOPNOTSUPP; | ||||
| #endif | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_heads_get - get number of heads of device | ||||
|  * @dev:		open device | ||||
|  * | ||||
|  * On success, return the number of heads on the device @dev.  On error return | ||||
|  * -1 with errno set to the error code. | ||||
|  * | ||||
|  * The following error codes are defined: | ||||
|  *	EINVAL		Input parameter error | ||||
|  *	EOPNOTSUPP	System does not support HDIO_GETGEO ioctl | ||||
|  *	ENOTTY		@dev is a file or a device not supporting HDIO_GETGEO | ||||
|  */ | ||||
| int ntfs_device_heads_get(struct ntfs_device *dev) | ||||
| { | ||||
| 	if (!dev) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| #ifdef HDIO_GETGEO | ||||
| 	{	struct hd_geometry geo; | ||||
| 
 | ||||
| 		if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { | ||||
| 			ntfs_log_debug("HDIO_GETGEO heads = %u (0x%x)\n", | ||||
| 					(unsigned)geo.heads, | ||||
| 					(unsigned)geo.heads); | ||||
| 			return geo.heads; | ||||
| 		} | ||||
| 	} | ||||
| #else | ||||
| 	errno = EOPNOTSUPP; | ||||
| #endif | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_sectors_per_track_get - get number of sectors per track of device | ||||
|  * @dev:		open device | ||||
|  * | ||||
|  * On success, return the number of sectors per track on the device @dev.  On | ||||
|  * error return -1 with errno set to the error code. | ||||
|  * | ||||
|  * The following error codes are defined: | ||||
|  *	EINVAL		Input parameter error | ||||
|  *	EOPNOTSUPP	System does not support HDIO_GETGEO ioctl | ||||
|  *	ENOTTY		@dev is a file or a device not supporting HDIO_GETGEO | ||||
|  */ | ||||
| int ntfs_device_sectors_per_track_get(struct ntfs_device *dev) | ||||
| { | ||||
| 	if (!dev) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| #ifdef HDIO_GETGEO | ||||
| 	{	struct hd_geometry geo; | ||||
| 
 | ||||
| 		if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { | ||||
| 			ntfs_log_debug("HDIO_GETGEO sectors_per_track = %u (0x%x)\n", | ||||
| 					(unsigned)geo.sectors, | ||||
| 					(unsigned)geo.sectors); | ||||
| 			return geo.sectors; | ||||
| 		} | ||||
| 	} | ||||
| #else | ||||
| 	errno = EOPNOTSUPP; | ||||
| #endif | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_sector_size_get - get sector size of a device | ||||
|  * @dev:	open device | ||||
|  * | ||||
|  * On success, return the sector size in bytes of the device @dev. | ||||
|  * On error return -1 with errno set to the error code. | ||||
|  * | ||||
|  * The following error codes are defined: | ||||
|  *	EINVAL		Input parameter error | ||||
|  *	EOPNOTSUPP	System does not support BLKSSZGET ioctl | ||||
|  *	ENOTTY		@dev is a file or a device not supporting BLKSSZGET | ||||
|  */ | ||||
| int ntfs_device_sector_size_get(struct ntfs_device *dev) | ||||
| { | ||||
| 	if (!dev) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| #ifdef BLKSSZGET | ||||
| 	{ | ||||
| 		int sect_size = 0; | ||||
| 
 | ||||
| 		if (!dev->d_ops->ioctl(dev, BLKSSZGET, §_size)) { | ||||
| 			ntfs_log_debug("BLKSSZGET sector size = %d bytes\n", | ||||
| 					sect_size); | ||||
| 			return sect_size; | ||||
| 		} | ||||
| 	} | ||||
| #else | ||||
| 	errno = EOPNOTSUPP; | ||||
| #endif | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_block_size_set - set block size of a device | ||||
|  * @dev:	open device | ||||
|  * @block_size: block size to set @dev to | ||||
|  * | ||||
|  * On success, return 0. | ||||
|  * On error return -1 with errno set to the error code. | ||||
|  * | ||||
|  * The following error codes are defined: | ||||
|  *	EINVAL		Input parameter error | ||||
|  *	EOPNOTSUPP	System does not support BLKBSZSET ioctl | ||||
|  *	ENOTTY		@dev is a file or a device not supporting BLKBSZSET | ||||
|  */ | ||||
| int ntfs_device_block_size_set(struct ntfs_device *dev, | ||||
| 		int block_size __attribute__((unused))) | ||||
| { | ||||
| 	if (!dev) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| #ifdef BLKBSZSET | ||||
| 	{ | ||||
| 		size_t s_block_size = block_size; | ||||
| 		if (!dev->d_ops->ioctl(dev, BLKBSZSET, &s_block_size)) { | ||||
| 			ntfs_log_debug("Used BLKBSZSET to set block size to " | ||||
| 					"%d bytes.\n", block_size); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		/* If not a block device, pretend it was successful. */ | ||||
| 		if (!NDevBlock(dev)) | ||||
| 			return 0; | ||||
| 	} | ||||
| #else | ||||
| 	/* If not a block device, pretend it was successful. */ | ||||
| 	if (!NDevBlock(dev)) | ||||
| 		return 0; | ||||
| 	errno = EOPNOTSUPP; | ||||
| #endif | ||||
| 	return -1; | ||||
| } | ||||
|  | @ -0,0 +1,38 @@ | |||
| /*
 | ||||
|  * device_io.c - Default device io operations. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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" | ||||
| 
 | ||||
| #ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS | ||||
| 
 | ||||
| #ifndef __CYGWIN32__ | ||||
| 
 | ||||
| /* Not on Cygwin; use standard Unix style low level device operations. */ | ||||
| #include "unix_io.c" | ||||
| 
 | ||||
| #else /* __CYGWIN32__ */ | ||||
| 
 | ||||
| /* On Cygwin; use Win32 low level device operations. */ | ||||
| #include "win32_io.c" | ||||
| 
 | ||||
| #endif /* __CYGWIN32__ */ | ||||
| 
 | ||||
| #endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */ | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,858 @@ | |||
| /**
 | ||||
|  * lcnalloc.c - Cluster (de)allocation code. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2002-2004 Anton Altaparmakov | ||||
|  * Copyright (c) 2004-2006 Szabolcs Szakacsits | ||||
|  * Copyright (c) 2004 Yura Pakhuchiy | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_STDLIB_H | ||||
| #include <stdlib.h> | ||||
| #endif | ||||
| #ifdef HAVE_STDIO_H | ||||
| #include <stdio.h> | ||||
| #endif | ||||
| #ifdef HAVE_ERRNO_H | ||||
| #include <errno.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "attrib.h" | ||||
| #include "bitmap.h" | ||||
| #include "debug.h" | ||||
| #include "runlist.h" | ||||
| #include "volume.h" | ||||
| #include "lcnalloc.h" | ||||
| #include "logging.h" | ||||
| #include "misc.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_cluster_alloc - allocate clusters on an ntfs volume | ||||
|  * @vol:	mounted ntfs volume on which to allocate the clusters | ||||
|  * @start_vcn:	vcn to use for the first allocated cluster | ||||
|  * @count:	number of clusters to allocate | ||||
|  * @start_lcn:	starting lcn at which to allocate the clusters (or -1 if none) | ||||
|  * @zone:	zone from which to allocate the clusters | ||||
|  * | ||||
|  * Allocate @count clusters preferably starting at cluster @start_lcn or at the | ||||
|  * current allocator position if @start_lcn is -1, on the mounted ntfs volume | ||||
|  * @vol. @zone is either DATA_ZONE for allocation of normal clusters and | ||||
|  * MFT_ZONE for allocation of clusters for the master file table, i.e. the | ||||
|  * $MFT/$DATA attribute. | ||||
|  * | ||||
|  * On success return a runlist describing the allocated cluster(s). | ||||
|  * | ||||
|  * On error return NULL with errno set to the error code. | ||||
|  * | ||||
|  * Notes on the allocation algorithm | ||||
|  * ================================= | ||||
|  * | ||||
|  * There are two data zones. First is the area between the end of the mft zone | ||||
|  * and the end of the volume, and second is the area between the start of the | ||||
|  * volume and the start of the mft zone. On unmodified/standard NTFS 1.x | ||||
|  * volumes, the second data zone doesn't exist due to the mft zone being | ||||
|  * expanded to cover the start of the volume in order to reserve space for the | ||||
|  * mft bitmap attribute. | ||||
|  * | ||||
|  * This is not the prettiest function but the complexity stems from the need of | ||||
|  * implementing the mft vs data zoned approach and from the fact that we have | ||||
|  * access to the lcn bitmap in portions of up to 8192 bytes at a time, so we | ||||
|  * need to cope with crossing over boundaries of two buffers. Further, the fact | ||||
|  * that the allocator allows for caller supplied hints as to the location of | ||||
|  * where allocation should begin and the fact that the allocator keeps track of | ||||
|  * where in the data zones the next natural allocation should occur, contribute | ||||
|  * to the complexity of the function. But it should all be worthwhile, because | ||||
|  * this allocator should: 1) be a full implementation of the MFT zone approach | ||||
|  * used by Windows, 2) cause reduction in fragmentation as much as possible, | ||||
|  * and 3) be speedy in allocations (the code is not optimized for speed, but | ||||
|  * the algorithm is, so further speed improvements are probably possible). | ||||
|  * | ||||
|  * FIXME: We should be monitoring cluster allocation and increment the MFT zone | ||||
|  * size dynamically but this is something for the future. We will just cause | ||||
|  * heavier fragmentation by not doing it and I am not even sure Windows would | ||||
|  * grow the MFT zone dynamically, so it might even be correct not to do this. | ||||
|  * The overhead in doing dynamic MFT zone expansion would be very large and | ||||
|  * unlikely worth the effort. (AIA) | ||||
|  * | ||||
|  * TODO: I have added in double the required zone position pointer wrap around | ||||
|  * logic which can be optimized to having only one of the two logic sets. | ||||
|  * However, having the double logic will work fine, but if we have only one of | ||||
|  * the sets and we get it wrong somewhere, then we get into trouble, so | ||||
|  * removing the duplicate logic requires _very_ careful consideration of _all_ | ||||
|  * possible code paths. So at least for now, I am leaving the double logic - | ||||
|  * better safe than sorry... (AIA) | ||||
|  */ | ||||
| runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, | ||||
| 		LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone) | ||||
| { | ||||
| 	LCN zone_start, zone_end, bmp_pos, bmp_initial_pos, last_read_pos, lcn; | ||||
| 	LCN prev_lcn = 0, prev_run_len = 0, mft_zone_size; | ||||
| 	s64 clusters, br; | ||||
| 	runlist *rl = NULL, *trl; | ||||
| 	u8 *buf, *byte; | ||||
| 	int err = 0, rlpos, rlsize, buf_size; | ||||
| 	u8 pass, done_zones, search_zone, need_writeback, bit; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering with count = 0x%llx, start_lcn = 0x%llx, zone = " | ||||
| 			"%s_ZONE.\n", (long long)count, (long long)start_lcn, | ||||
| 			zone == MFT_ZONE ? "MFT" : "DATA"); | ||||
| 	if (!vol || count < 0 || start_lcn < -1 || !vol->lcnbmp_na || | ||||
| 			(s8)zone < FIRST_ZONE || zone > LAST_ZONE) { | ||||
| 		ntfs_log_trace("Invalid arguments!\n"); | ||||
| 		errno = EINVAL; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Return empty runlist if @count == 0 */ | ||||
| 	if (!count) { | ||||
| 		rl = ntfs_malloc(0x1000); | ||||
| 		if (!rl) | ||||
| 			return NULL; | ||||
| 		rl[0].vcn = start_vcn; | ||||
| 		rl[0].lcn = LCN_RL_NOT_MAPPED; | ||||
| 		rl[0].length = 0; | ||||
| 		return rl; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Allocate memory. */ | ||||
| 	buf = ntfs_malloc(8192); | ||||
| 	if (!buf) | ||||
| 		return NULL; | ||||
| 	/*
 | ||||
| 	 * If no specific @start_lcn was requested, use the current data zone | ||||
| 	 * position, otherwise use the requested @start_lcn but make sure it | ||||
| 	 * lies outside the mft zone. Also set done_zones to 0 (no zones done) | ||||
| 	 * and pass depending on whether we are starting inside a zone (1) or | ||||
| 	 * at the beginning of a zone (2). If requesting from the MFT_ZONE, | ||||
| 	 * we either start at the current position within the mft zone or at | ||||
| 	 * the specified position. If the latter is out of bounds then we start | ||||
| 	 * at the beginning of the MFT_ZONE. | ||||
| 	 */ | ||||
| 	done_zones = 0; | ||||
| 	pass = 1; | ||||
| 	/*
 | ||||
| 	 * zone_start and zone_end are the current search range. search_zone | ||||
| 	 * is 1 for mft zone, 2 for data zone 1 (end of mft zone till end of | ||||
| 	 * volume) and 4 for data zone 2 (start of volume till start of mft | ||||
| 	 * zone). | ||||
| 	 */ | ||||
| 	zone_start = start_lcn; | ||||
| 	if (zone_start < 0) { | ||||
| 		if (zone == DATA_ZONE) | ||||
| 			zone_start = vol->data1_zone_pos; | ||||
| 		else | ||||
| 			zone_start = vol->mft_zone_pos; | ||||
| 		if (!zone_start) { | ||||
| 			/*
 | ||||
| 			 * Zone starts at beginning of volume which means a | ||||
| 			 * single pass is sufficient. | ||||
| 			 */ | ||||
| 			pass = 2; | ||||
| 		} | ||||
| 	} else if (zone == DATA_ZONE && zone_start >= vol->mft_zone_start && | ||||
| 			zone_start < vol->mft_zone_end) { | ||||
| 		zone_start = vol->mft_zone_end; | ||||
| 		/*
 | ||||
| 		 * Starting at beginning of data1_zone which means a single | ||||
| 		 * pass in this zone is sufficient. | ||||
| 		 */ | ||||
| 		pass = 2; | ||||
| 	} else if (zone == MFT_ZONE && (zone_start < vol->mft_zone_start || | ||||
| 			zone_start >= vol->mft_zone_end)) { | ||||
| 		zone_start = vol->mft_lcn; | ||||
| 		if (!vol->mft_zone_end) | ||||
| 			zone_start = 0; | ||||
| 		/*
 | ||||
| 		 * Starting at beginning of volume which means a single pass | ||||
| 		 * is sufficient. | ||||
| 		 */ | ||||
| 		pass = 2; | ||||
| 	} | ||||
| 	if (zone == MFT_ZONE) { | ||||
| 		zone_end = vol->mft_zone_end; | ||||
| 		search_zone = 1; | ||||
| 	} else /* if (zone == DATA_ZONE) */ { | ||||
| 		/* Skip searching the mft zone. */ | ||||
| 		done_zones |= 1; | ||||
| 		if (zone_start >= vol->mft_zone_end) { | ||||
| 			zone_end = vol->nr_clusters; | ||||
| 			search_zone = 2; | ||||
| 		} else { | ||||
| 			zone_end = vol->mft_zone_start; | ||||
| 			search_zone = 4; | ||||
| 		} | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * bmp_pos is the current bit position inside the bitmap. We use | ||||
| 	 * bmp_initial_pos to determine whether or not to do a zone switch. | ||||
| 	 */ | ||||
| 	bmp_pos = bmp_initial_pos = zone_start; | ||||
| 
 | ||||
| 	/* Loop until all clusters are allocated, i.e. clusters == 0. */ | ||||
| 	clusters = count; | ||||
| 	rlpos = rlsize = 0; | ||||
| 	while (1) { | ||||
| 		ntfs_log_trace("Start of outer while loop: done_zones = 0x%x, " | ||||
| 				"search_zone = %i, pass = %i, zone_start = " | ||||
| 				"0x%llx, zone_end = 0x%llx, bmp_initial_pos = " | ||||
| 				"0x%llx, bmp_pos = 0x%llx, rlpos = %i, rlsize = " | ||||
| 				"%i.\n", done_zones, search_zone, pass, | ||||
| 				(long long)zone_start, (long long)zone_end, | ||||
| 				(long long)bmp_initial_pos, (long long)bmp_pos, | ||||
| 				rlpos, rlsize); | ||||
| 		/* Loop until we run out of free clusters. */ | ||||
| 		last_read_pos = bmp_pos >> 3; | ||||
| 		ntfs_log_trace("last_read_pos = 0x%llx.\n", (long long)last_read_pos); | ||||
| 		br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos, 8192, buf); | ||||
| 		if (br <= 0) { | ||||
| 			if (!br) { | ||||
| 				/* Reached end of attribute. */ | ||||
| 				ntfs_log_trace("End of attribute reached. Skipping " | ||||
| 						"to zone_pass_done.\n"); | ||||
| 				goto zone_pass_done; | ||||
| 			} | ||||
| 			err = errno; | ||||
| 			ntfs_log_trace("ntfs_attr_pread() failed. Aborting.\n"); | ||||
| 			goto err_ret; | ||||
| 		} | ||||
| 		/*
 | ||||
| 		 * We might have read less than 8192 bytes if we are close to | ||||
| 		 * the end of the attribute. | ||||
| 		 */ | ||||
| 		buf_size = (int)br << 3; | ||||
| 		lcn = bmp_pos & 7; | ||||
| 		bmp_pos &= ~7; | ||||
| 		need_writeback = 0; | ||||
| 		ntfs_log_trace("Before inner while loop: buf_size = %i, lcn = " | ||||
| 				"0x%llx, bmp_pos = 0x%llx, need_writeback = %i.\n", | ||||
| 				buf_size, (long long)lcn, (long long)bmp_pos, | ||||
| 				need_writeback); | ||||
| 		while (lcn < buf_size && lcn + bmp_pos < zone_end) { | ||||
| 			byte = buf + (lcn >> 3); | ||||
| 			ntfs_log_trace("In inner while loop: buf_size = %i, lcn = " | ||||
| 					"0x%llx, bmp_pos = 0x%llx, " | ||||
| 					"need_writeback = %i, byte ofs = 0x%x, " | ||||
| 					"*byte = 0x%x.\n", buf_size, | ||||
| 					(long long)lcn, (long long)bmp_pos, | ||||
| 					need_writeback, (unsigned int)(lcn >> 3), | ||||
| 					(unsigned int)*byte); | ||||
| 			/* Skip full bytes. */ | ||||
| 			if (*byte == 0xff) { | ||||
| 				lcn = (lcn + 8) & ~7; | ||||
| 				ntfs_log_trace("continuing while loop 1.\n"); | ||||
| 				continue; | ||||
| 			} | ||||
| 			bit = 1 << (lcn & 7); | ||||
| 			ntfs_log_trace("bit = %i.\n", bit); | ||||
| 			/* If the bit is already set, go onto the next one. */ | ||||
| 			if (*byte & bit) { | ||||
| 				lcn++; | ||||
| 				ntfs_log_trace("continuing while loop 2.\n"); | ||||
| 				continue; | ||||
| 			} | ||||
| 			/* Reallocate memory if necessary. */ | ||||
| 			if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) { | ||||
| 				ntfs_log_trace("Reallocating space.\n"); | ||||
| 				if (!rl) | ||||
| 					ntfs_log_trace("First free bit is at LCN = " | ||||
| 						"0x%llx.\n", (long long)(lcn + bmp_pos)); | ||||
| 				rlsize += 4096; | ||||
| 				trl = (runlist*)realloc(rl, rlsize); | ||||
| 				if (!trl) { | ||||
| 					err = ENOMEM; | ||||
| 					ntfs_log_trace("Failed to allocate memory, " | ||||
| 							"going to wb_err_ret.\n"); | ||||
| 					goto wb_err_ret; | ||||
| 				} | ||||
| 				rl = trl; | ||||
| 				ntfs_log_trace("Reallocated memory, rlsize = " | ||||
| 						"0x%x.\n", rlsize); | ||||
| 			} | ||||
| 			/* Allocate the bitmap bit. */ | ||||
| 			*byte |= bit; | ||||
| 			/* We need to write this bitmap buffer back to disk! */ | ||||
| 			need_writeback = 1; | ||||
| 			ntfs_log_trace("*byte = 0x%x, need_writeback is set.\n", | ||||
| 					(unsigned int)*byte); | ||||
| 			/*
 | ||||
| 			 * Coalesce with previous run if adjacent LCNs. | ||||
| 			 * Otherwise, append a new run. | ||||
| 			 */ | ||||
| 			ntfs_log_trace("Adding run (lcn 0x%llx, len 0x%llx), " | ||||
| 					"prev_lcn = 0x%llx, lcn = 0x%llx, " | ||||
| 					"bmp_pos = 0x%llx, prev_run_len = " | ||||
| 					"0x%llx, rlpos = %i.\n", | ||||
| 					(long long)(lcn + bmp_pos), 1LL, | ||||
| 					(long long)prev_lcn, (long long)lcn, | ||||
| 					(long long)bmp_pos, | ||||
| 					(long long)prev_run_len, rlpos); | ||||
| 			if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) { | ||||
| 				ntfs_log_trace("Coalescing to run (lcn 0x%llx, len " | ||||
| 						"0x%llx).\n", | ||||
| 						(long long)rl[rlpos - 1].lcn, | ||||
| 						(long long) rl[rlpos - 1].length); | ||||
| 				rl[rlpos - 1].length = ++prev_run_len; | ||||
| 				ntfs_log_trace("Run now (lcn 0x%llx, len 0x%llx), " | ||||
| 						"prev_run_len = 0x%llx.\n", | ||||
| 						(long long)rl[rlpos - 1].lcn, | ||||
| 						(long long)rl[rlpos - 1].length, | ||||
| 						(long long)prev_run_len); | ||||
| 			} else { | ||||
| 				if (rlpos) { | ||||
| 					ntfs_log_trace("Adding new run, (previous " | ||||
| 						"run lcn 0x%llx, len 0x%llx).\n", | ||||
| 						(long long) rl[rlpos - 1].lcn, | ||||
| 						(long long) rl[rlpos - 1].length); | ||||
| 					rl[rlpos].vcn = rl[rlpos - 1].vcn + | ||||
| 							prev_run_len; | ||||
| 				} else { | ||||
| 					ntfs_log_trace("Adding new run, is first run.\n"); | ||||
| 					rl[rlpos].vcn = start_vcn; | ||||
| 				} | ||||
| 				rl[rlpos].lcn = prev_lcn = lcn + bmp_pos; | ||||
| 				rl[rlpos].length = prev_run_len = 1; | ||||
| 				rlpos++; | ||||
| 			} | ||||
| 			/* Done? */ | ||||
| 			if (!--clusters) { | ||||
| 				LCN tc; | ||||
| 				/*
 | ||||
| 				 * Update the current zone position. Positions | ||||
| 				 * of already scanned zones have been updated | ||||
| 				 * during the respective zone switches. | ||||
| 				 */ | ||||
| 				tc = lcn + bmp_pos + 1; | ||||
| 				ntfs_log_trace("Done. Updating current zone " | ||||
| 					"position, tc = 0x%llx, search_zone = %i.\n", | ||||
| 					(long long)tc, search_zone); | ||||
| 				switch (search_zone) { | ||||
| 				case 1: | ||||
| 					ntfs_log_trace("Before checks, vol->mft_zone_pos = 0x%llx.\n", | ||||
| 							(long long) vol->mft_zone_pos); | ||||
| 					if (tc >= vol->mft_zone_end) { | ||||
| 						vol->mft_zone_pos = | ||||
| 								vol->mft_lcn; | ||||
| 						if (!vol->mft_zone_end) | ||||
| 							vol->mft_zone_pos = 0; | ||||
| 					} else if ((bmp_initial_pos >= | ||||
| 							vol->mft_zone_pos || | ||||
| 							tc > vol->mft_zone_pos) | ||||
| 							&& tc >= vol->mft_lcn) | ||||
| 						vol->mft_zone_pos = tc; | ||||
| 					ntfs_log_trace("After checks, vol->mft_zone_pos = 0x%llx.\n", | ||||
| 							(long long) vol->mft_zone_pos); | ||||
| 					break; | ||||
| 				case 2: | ||||
| 					ntfs_log_trace("Before checks, vol->data1_zone_pos = 0x%llx.\n", | ||||
| 							(long long) vol->data1_zone_pos); | ||||
| 					if (tc >= vol->nr_clusters) | ||||
| 						vol->data1_zone_pos = | ||||
| 							     vol->mft_zone_end; | ||||
| 					else if ((bmp_initial_pos >= | ||||
| 						    vol->data1_zone_pos || | ||||
| 						    tc > vol->data1_zone_pos) | ||||
| 						    && tc >= vol->mft_zone_end) | ||||
| 						vol->data1_zone_pos = tc; | ||||
| 					ntfs_log_trace("After checks, vol->data1_zone_pos = 0x%llx.\n", | ||||
| 							(long long) vol->data1_zone_pos); | ||||
| 					break; | ||||
| 				case 4: | ||||
| 					ntfs_log_trace("Before checks, vol->data2_zone_pos = 0x%llx.\n", | ||||
| 							(long long) vol->data2_zone_pos); | ||||
| 					if (tc >= vol->mft_zone_start) | ||||
| 						vol->data2_zone_pos = 0; | ||||
| 					else if (bmp_initial_pos >= | ||||
| 						      vol->data2_zone_pos || | ||||
| 						      tc > vol->data2_zone_pos) | ||||
| 						vol->data2_zone_pos = tc; | ||||
| 					ntfs_log_trace("After checks, vol->data2_zone_pos = 0x%llx.\n", | ||||
| 							(long long) vol->data2_zone_pos); | ||||
| 					break; | ||||
| 				default: | ||||
| 					free(rl); | ||||
| 					free(buf); | ||||
| 					NTFS_BUG("switch (search_zone) 1"); | ||||
| 					return NULL; | ||||
| 				} | ||||
| 				ntfs_log_trace("Going to done_ret.\n"); | ||||
| 				goto done_ret; | ||||
| 			} | ||||
| 			lcn++; | ||||
| 		} | ||||
| 		bmp_pos += buf_size; | ||||
| 		ntfs_log_trace("After inner while loop: buf_size = 0x%x, lcn = " | ||||
| 				"0x%llx, bmp_pos = 0x%llx, need_writeback = %i.\n", | ||||
| 				buf_size, (long long)lcn, | ||||
| 				(long long)bmp_pos, need_writeback); | ||||
| 		if (need_writeback) { | ||||
| 			s64 bw; | ||||
| 			ntfs_log_trace("Writing back.\n"); | ||||
| 			need_writeback = 0; | ||||
| 			bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos, | ||||
| 					br, buf); | ||||
| 			if (bw != br) { | ||||
| 				if (bw == -1) | ||||
| 					err = errno; | ||||
| 				else | ||||
| 					err = EIO; | ||||
| 				ntfs_log_trace("Bitmap writeback failed in read next " | ||||
| 					"buffer code path with error code %i.\n", err); | ||||
| 				goto err_ret; | ||||
| 			} | ||||
| 		} | ||||
| 		if (bmp_pos < zone_end) { | ||||
| 			ntfs_log_trace("Continuing outer while loop, bmp_pos = " | ||||
| 					"0x%llx, zone_end = 0x%llx.\n", | ||||
| 					(long long)bmp_pos, | ||||
| 					(long long)zone_end); | ||||
| 			continue; | ||||
| 		} | ||||
| zone_pass_done:	/* Finished with the current zone pass. */ | ||||
| 		ntfs_log_trace("At zone_pass_done, pass = %i.\n", pass); | ||||
| 		if (pass == 1) { | ||||
| 			/*
 | ||||
| 			 * Now do pass 2, scanning the first part of the zone | ||||
| 			 * we omitted in pass 1. | ||||
| 			 */ | ||||
| 			pass = 2; | ||||
| 			zone_end = zone_start; | ||||
| 			switch (search_zone) { | ||||
| 			case 1: /* mft_zone */ | ||||
| 				zone_start = vol->mft_zone_start; | ||||
| 				break; | ||||
| 			case 2: /* data1_zone */ | ||||
| 				zone_start = vol->mft_zone_end; | ||||
| 				break; | ||||
| 			case 4: /* data2_zone */ | ||||
| 				zone_start = 0; | ||||
| 				break; | ||||
| 			default: | ||||
| 				NTFS_BUG("switch (search_zone) 2"); | ||||
| 			} | ||||
| 			/* Sanity check. */ | ||||
| 			if (zone_end < zone_start) | ||||
| 				zone_end = zone_start; | ||||
| 			bmp_pos = zone_start; | ||||
| 			ntfs_log_trace("Continuing outer while loop, pass = 2, " | ||||
| 					"zone_start = 0x%llx, zone_end = " | ||||
| 					"0x%llx, bmp_pos = 0x%llx.\n", | ||||
| 					zone_start, zone_end, bmp_pos); | ||||
| 			continue; | ||||
| 		} /* pass == 2 */ | ||||
| done_zones_check: | ||||
| 		ntfs_log_trace("At done_zones_check, search_zone = %i, done_zones " | ||||
| 				"before = 0x%x, done_zones after = 0x%x.\n", | ||||
| 				search_zone, done_zones, done_zones | search_zone); | ||||
| 		done_zones |= search_zone; | ||||
| 		if (done_zones < 7) { | ||||
| 			ntfs_log_trace("Switching zone.\n"); | ||||
| 			/* Now switch to the next zone we haven't done yet. */ | ||||
| 			pass = 1; | ||||
| 			switch (search_zone) { | ||||
| 			case 1: | ||||
| 				ntfs_log_trace("Switching from mft zone to data1 " | ||||
| 						"zone.\n"); | ||||
| 				/* Update mft zone position. */ | ||||
| 				if (rlpos) { | ||||
| 					LCN tc; | ||||
| 					ntfs_log_trace("Before checks, vol->mft_zone_pos = 0x%llx.\n", | ||||
| 							(long long) vol->mft_zone_pos); | ||||
| 					tc = rl[rlpos - 1].lcn + | ||||
| 							rl[rlpos - 1].length; | ||||
| 					if (tc >= vol->mft_zone_end) { | ||||
| 						vol->mft_zone_pos = | ||||
| 								vol->mft_lcn; | ||||
| 						if (!vol->mft_zone_end) | ||||
| 							vol->mft_zone_pos = 0; | ||||
| 					} else if ((bmp_initial_pos >= | ||||
| 							vol->mft_zone_pos || | ||||
| 							tc > vol->mft_zone_pos) | ||||
| 							&& tc >= vol->mft_lcn) | ||||
| 						vol->mft_zone_pos = tc; | ||||
| 					ntfs_log_trace("After checks, vol->mft_zone_pos = 0x%llx.\n", | ||||
| 							(long long) vol->mft_zone_pos); | ||||
| 				} | ||||
| 				/* Switch from mft zone to data1 zone. */ | ||||
| switch_to_data1_zone:		search_zone = 2; | ||||
| 				zone_start = bmp_initial_pos = | ||||
| 						vol->data1_zone_pos; | ||||
| 				zone_end = vol->nr_clusters; | ||||
| 				if (zone_start == vol->mft_zone_end) | ||||
| 					pass = 2; | ||||
| 				if (zone_start >= zone_end) { | ||||
| 					vol->data1_zone_pos = zone_start = | ||||
| 							vol->mft_zone_end; | ||||
| 					pass = 2; | ||||
| 				} | ||||
| 				break; | ||||
| 			case 2: | ||||
| 				ntfs_log_trace("Switching from data1 zone to data2 " | ||||
| 						"zone.\n"); | ||||
| 				/* Update data1 zone position. */ | ||||
| 				if (rlpos) { | ||||
| 					LCN tc; | ||||
| 					ntfs_log_trace("Before checks, vol->data1_zone_pos = 0x%llx.\n", | ||||
| 							(long long) vol->data1_zone_pos); | ||||
| 					tc = rl[rlpos - 1].lcn + | ||||
| 							rl[rlpos - 1].length; | ||||
| 					if (tc >= vol->nr_clusters) | ||||
| 						vol->data1_zone_pos = | ||||
| 							     vol->mft_zone_end; | ||||
| 					else if ((bmp_initial_pos >= | ||||
| 						    vol->data1_zone_pos || | ||||
| 						    tc > vol->data1_zone_pos) | ||||
| 						    && tc >= vol->mft_zone_end) | ||||
| 						vol->data1_zone_pos = tc; | ||||
| 					ntfs_log_trace("After checks, vol->data1_zone_pos = 0x%llx.\n", | ||||
| 							(long long) vol->data1_zone_pos); | ||||
| 				} | ||||
| 				/* Switch from data1 zone to data2 zone. */ | ||||
| 				search_zone = 4; | ||||
| 				zone_start = bmp_initial_pos = | ||||
| 						vol->data2_zone_pos; | ||||
| 				zone_end = vol->mft_zone_start; | ||||
| 				if (!zone_start) | ||||
| 					pass = 2; | ||||
| 				if (zone_start >= zone_end) { | ||||
| 					vol->data2_zone_pos = zone_start = | ||||
| 							bmp_initial_pos = 0; | ||||
| 					pass = 2; | ||||
| 				} | ||||
| 				break; | ||||
| 			case 4: | ||||
| 				ntfs_log_debug("Switching from data2 zone to data1 " | ||||
| 						"zone.\n"); | ||||
| 				/* Update data2 zone position. */ | ||||
| 				if (rlpos) { | ||||
| 					LCN tc; | ||||
| 					ntfs_log_trace("Before checks, vol->data2_zone_pos = 0x%llx.\n", | ||||
| 							(long long) vol->data2_zone_pos); | ||||
| 					tc = rl[rlpos - 1].lcn + | ||||
| 							rl[rlpos - 1].length; | ||||
| 					if (tc >= vol->mft_zone_start) | ||||
| 						vol->data2_zone_pos = 0; | ||||
| 					else if (bmp_initial_pos >= | ||||
| 						      vol->data2_zone_pos || | ||||
| 						      tc > vol->data2_zone_pos) | ||||
| 						vol->data2_zone_pos = tc; | ||||
| 					ntfs_log_trace("After checks, vol->data2_zone_pos = 0x%llx.\n", | ||||
| 							(long long) vol->data2_zone_pos); | ||||
| 				} | ||||
| 				/* Switch from data2 zone to data1 zone. */ | ||||
| 				goto switch_to_data1_zone; /* See above. */ | ||||
| 			default: | ||||
| 				NTFS_BUG("switch (search_zone) 3"); | ||||
| 			} | ||||
| 			ntfs_log_trace("After zone switch, search_zone = %i, pass = " | ||||
| 					"%i, bmp_initial_pos = 0x%llx, " | ||||
| 					"zone_start = 0x%llx, zone_end = " | ||||
| 					"0x%llx.\n", search_zone, pass, | ||||
| 					(long long)bmp_initial_pos, | ||||
| 					(long long)zone_start, | ||||
| 					(long long)zone_end); | ||||
| 			bmp_pos = zone_start; | ||||
| 			if (zone_start == zone_end) { | ||||
| 				ntfs_log_trace("Empty zone, going to " | ||||
| 						"done_zones_check.\n"); | ||||
| 				/* Empty zone. Don't bother searching it. */ | ||||
| 				goto done_zones_check; | ||||
| 			} | ||||
| 			ntfs_log_trace("Continuing outer while loop.\n"); | ||||
| 			continue; | ||||
| 		} /* done_zones == 7 */ | ||||
| 		ntfs_log_trace("All zones are finished.\n"); | ||||
| 		/*
 | ||||
| 		 * All zones are finished! If DATA_ZONE, shrink mft zone. If | ||||
| 		 * MFT_ZONE, we have really run out of space. | ||||
| 		 */ | ||||
| 		mft_zone_size = vol->mft_zone_end - vol->mft_zone_start; | ||||
| 		ntfs_log_trace("vol->mft_zone_start = 0x%llx, vol->mft_zone_end = " | ||||
| 				"0x%llx, mft_zone_size = 0x%llx.\n", | ||||
| 				(long long)vol->mft_zone_start, | ||||
| 				(long long)vol->mft_zone_end, | ||||
| 				(long long)mft_zone_size); | ||||
| 		if (zone == MFT_ZONE || mft_zone_size <= 0) { | ||||
| 			ntfs_log_trace("No free clusters left, going to err_ret.\n"); | ||||
| 			/* Really no more space left on device. */ | ||||
| 			err = ENOSPC; | ||||
| 			goto err_ret; | ||||
| 		} /* zone == DATA_ZONE && mft_zone_size > 0 */ | ||||
| 		ntfs_log_trace("Shrinking mft zone.\n"); | ||||
| 		zone_end = vol->mft_zone_end; | ||||
| 		mft_zone_size >>= 1; | ||||
| 		if (mft_zone_size > 0) | ||||
| 			vol->mft_zone_end = vol->mft_zone_start + mft_zone_size; | ||||
| 		else /* mft zone and data2 zone no longer exist. */ | ||||
| 			vol->data2_zone_pos = vol->mft_zone_start = | ||||
| 					vol->mft_zone_end = 0; | ||||
| 		if (vol->mft_zone_pos >= vol->mft_zone_end) { | ||||
| 			vol->mft_zone_pos = vol->mft_lcn; | ||||
| 			if (!vol->mft_zone_end) | ||||
| 				vol->mft_zone_pos = 0; | ||||
| 		} | ||||
| 		bmp_pos = zone_start = bmp_initial_pos = | ||||
| 				vol->data1_zone_pos = vol->mft_zone_end; | ||||
| 		search_zone = 2; | ||||
| 		pass = 2; | ||||
| 		done_zones &= ~2; | ||||
| 		ntfs_log_trace("After shrinking mft zone, mft_zone_size = 0x%llx, " | ||||
| 				"vol->mft_zone_start = 0x%llx, " | ||||
| 				"vol->mft_zone_end = 0x%llx, vol->mft_zone_pos " | ||||
| 				"= 0x%llx, search_zone = 2, pass = 2, " | ||||
| 				"dones_zones = 0x%x, zone_start = 0x%llx, " | ||||
| 				"zone_end = 0x%llx, vol->data1_zone_pos = " | ||||
| 				"0x%llx, continuing outer while loop.\n", | ||||
| 				(long long)mft_zone_size, | ||||
| 				(long long)vol->mft_zone_start, | ||||
| 				(long long)vol->mft_zone_end, | ||||
| 				(long long)vol->mft_zone_pos, | ||||
| 				done_zones, | ||||
| 				(long long)zone_start, | ||||
| 				(long long)zone_end, | ||||
| 				(long long)vol->data1_zone_pos); | ||||
| 	} | ||||
| 	ntfs_log_debug("After outer while loop.\n"); | ||||
| done_ret: | ||||
| 	ntfs_log_debug("At done_ret.\n"); | ||||
| 	/* Add runlist terminator element. */ | ||||
| 	rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; | ||||
| 	rl[rlpos].lcn = LCN_RL_NOT_MAPPED; | ||||
| 	rl[rlpos].length = 0; | ||||
| 	if (need_writeback) { | ||||
| 		s64 bw; | ||||
| 		ntfs_log_trace("Writing back.\n"); | ||||
| 		need_writeback = 0; | ||||
| 		bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos, br, buf); | ||||
| 		if (bw != br) { | ||||
| 			if (bw < 0) | ||||
| 				err = errno; | ||||
| 			else | ||||
| 				err = EIO; | ||||
| 			ntfs_log_trace("Bitmap writeback failed in done code path " | ||||
| 					"with error code %i.\n", err); | ||||
| 			goto err_ret; | ||||
| 		} | ||||
| 	} | ||||
| done_err_ret: | ||||
| 	ntfs_log_debug("At done_err_ret (follows done_ret).\n"); | ||||
| 	free(buf); | ||||
| 	/* Done! */ | ||||
| 	if (!err) | ||||
| 		return rl; | ||||
| 	ntfs_log_trace("Failed to allocate clusters. Returning with error code " | ||||
| 			"%i.\n", err); | ||||
| 	errno = err; | ||||
| 	return NULL; | ||||
| wb_err_ret: | ||||
| 	ntfs_log_trace("At wb_err_ret.\n"); | ||||
| 	if (need_writeback) { | ||||
| 		s64 bw; | ||||
| 		ntfs_log_trace("Writing back.\n"); | ||||
| 		need_writeback = 0; | ||||
| 		bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos, br, buf); | ||||
| 		if (bw != br) { | ||||
| 			if (bw < 0) | ||||
| 				err = errno; | ||||
| 			else | ||||
| 				err = EIO; | ||||
| 			ntfs_log_trace("Bitmap writeback failed in error code path " | ||||
| 					"with error code %i.\n", err); | ||||
| 		} | ||||
| 	} | ||||
| err_ret: | ||||
| 	ntfs_log_trace("At err_ret.\n"); | ||||
| 	if (rl) { | ||||
| 		if (err == ENOSPC) { | ||||
| 			ntfs_log_trace("err = ENOSPC, first free lcn = 0x%llx, could " | ||||
| 					"allocate up to = 0x%llx clusters.\n", | ||||
| 					(long long)rl[0].lcn, | ||||
| 					(long long)count - clusters); | ||||
| 		} | ||||
| 		/* Add runlist terminator element. */ | ||||
| 		rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; | ||||
| 		rl[rlpos].lcn = LCN_RL_NOT_MAPPED; | ||||
| 		rl[rlpos].length = 0; | ||||
| 		/* Deallocate all allocated clusters. */ | ||||
| 		ntfs_log_trace("Deallocating allocated clusters.\n"); | ||||
| 		ntfs_cluster_free_from_rl(vol, rl); | ||||
| 		/* Free the runlist. */ | ||||
| 		free(rl); | ||||
| 		rl = NULL; | ||||
| 	} else { | ||||
| 		if (err == ENOSPC) { | ||||
| 			ntfs_log_trace("No space left at all, err = ENOSPC, first " | ||||
| 					"free lcn = 0x%llx.\n", | ||||
| 					(long long)vol->data1_zone_pos); | ||||
| 		} | ||||
| 	} | ||||
| 	ntfs_log_trace("rl = NULL, going to done_err_ret.\n"); | ||||
| 	goto done_err_ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_cluster_free_from_rl - free clusters from runlist | ||||
|  * @vol:	mounted ntfs volume on which to free the clusters | ||||
|  * @rl:		runlist from which deallocate clusters | ||||
|  * | ||||
|  * On success return 0 and on error return -1 with errno set to the error code. | ||||
|  */ | ||||
| int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl) | ||||
| { | ||||
| 	ntfs_log_trace("Entering.\n"); | ||||
| 
 | ||||
| 	for (; rl->length; rl++) { | ||||
| 
 | ||||
| 		ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n", | ||||
| 			       (long long)rl->lcn, (long long)rl->length); | ||||
| 
 | ||||
| 		if (rl->lcn >= 0 && ntfs_bitmap_clear_run(vol->lcnbmp_na, | ||||
| 				rl->lcn, rl->length)) { | ||||
| 			int eo = errno; | ||||
| 			ntfs_log_trace("Eeek! Deallocation of clusters failed.\n"); | ||||
| 			errno = eo; | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_cluster_free - free clusters on an ntfs volume | ||||
|  * @vol:	mounted ntfs volume on which to free the clusters | ||||
|  * @na:		attribute whose runlist describes the clusters to free | ||||
|  * @start_vcn:	vcn in @rl at which to start freeing clusters | ||||
|  * @count:	number of clusters to free or -1 for all clusters | ||||
|  * | ||||
|  * Free @count clusters starting at the cluster @start_vcn in the runlist | ||||
|  * described by the attribute @na from the mounted ntfs volume @vol. | ||||
|  * | ||||
|  * If @count is -1, all clusters from @start_vcn to the end of the runlist | ||||
|  * are deallocated. | ||||
|  * | ||||
|  * On success return the number of deallocated clusters (not counting sparse | ||||
|  * clusters) and on error return -1 with errno set to the error code. | ||||
|  */ | ||||
| int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) | ||||
| { | ||||
| 	runlist *rl; | ||||
| 	s64 nr_freed, delta, to_free; | ||||
| 
 | ||||
| 	if (!vol || !vol->lcnbmp_na || !na || start_vcn < 0 || | ||||
| 			(count < 0 && count != -1)) { | ||||
| 		ntfs_log_trace("Invalid arguments!\n"); | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, " | ||||
| 		       "vcn 0x%llx.\n", (unsigned long long)na->ni->mft_no, | ||||
| 		       na->type, (long long)count, (long long)start_vcn); | ||||
| 
 | ||||
| 	rl = ntfs_attr_find_vcn(na, start_vcn); | ||||
| 	if (!rl) { | ||||
| 		if (errno == ENOENT) | ||||
| 			return 0; | ||||
| 		else | ||||
| 			return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { | ||||
| 		errno = EIO; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Find the starting cluster inside the run that needs freeing. */ | ||||
| 	delta = start_vcn - rl->vcn; | ||||
| 
 | ||||
| 	/* The number of clusters in this run that need freeing. */ | ||||
| 	to_free = rl->length - delta; | ||||
| 	if (count >= 0 && to_free > count) | ||||
| 		to_free = count; | ||||
| 
 | ||||
| 	if (rl->lcn != LCN_HOLE) { | ||||
| 		/* Do the actual freeing of the clusters in this run. */ | ||||
| 		if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn + delta, | ||||
| 				to_free)) | ||||
| 			return -1; | ||||
| 		/* We have freed @to_free real clusters. */ | ||||
| 		nr_freed = to_free; | ||||
| 	} else { | ||||
| 		/* No real clusters were freed. */ | ||||
| 		nr_freed = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Go to the next run and adjust the number of clusters left to free. */ | ||||
| 	++rl; | ||||
| 	if (count >= 0) | ||||
| 		count -= to_free; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Loop over the remaining runs, using @count as a capping value, and | ||||
| 	 * free them. | ||||
| 	 */ | ||||
| 	for (; rl->length && count != 0; ++rl) { | ||||
| 		// FIXME: Need to try ntfs_attr_map_runlist() for attribute
 | ||||
| 		//	  list support! (AIA)
 | ||||
| 		if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { | ||||
| 			// FIXME: Eeek! We need rollback! (AIA)
 | ||||
| 			ntfs_log_trace("Eeek! invalid lcn (= %lli).  Should attempt " | ||||
| 					"to map runlist!  Leaving inconsistent " | ||||
| 					"metadata!\n", (long long)rl->lcn); | ||||
| 			errno = EIO; | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| 		/* The number of clusters in this run that need freeing. */ | ||||
| 		to_free = rl->length; | ||||
| 		if (count >= 0 && to_free > count) | ||||
| 			to_free = count; | ||||
| 
 | ||||
| 		if (rl->lcn != LCN_HOLE) { | ||||
| 			/* Do the actual freeing of the clusters in the run. */ | ||||
| 			if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, | ||||
| 					to_free)) { | ||||
| 				int eo = errno; | ||||
| 
 | ||||
| 				// FIXME: Eeek! We need rollback! (AIA)
 | ||||
| 				ntfs_log_trace("Eeek!  bitmap clear run failed.  " | ||||
| 						"Leaving inconsistent metadata!\n"); | ||||
| 				errno = eo; | ||||
| 				return -1; | ||||
| 			} | ||||
| 			/* We have freed @to_free real clusters. */ | ||||
| 			nr_freed += to_free; | ||||
| 		} | ||||
| 
 | ||||
| 		if (count >= 0) | ||||
| 			count -= to_free; | ||||
| 	} | ||||
| 
 | ||||
| 	if (count != -1 && count != 0) { | ||||
| 		// FIXME: Eeek! BUG()
 | ||||
| 		ntfs_log_trace("Eeek!  count still not zero (= %lli).  Leaving " | ||||
| 				"inconsistent metadata!\n", (long long)count); | ||||
| 		errno = EIO; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Done. Return the number of actual clusters that were freed. */ | ||||
| 	return nr_freed; | ||||
| } | ||||
|  | @ -0,0 +1,762 @@ | |||
| /**
 | ||||
|  * logfile.c - NTFS journal handling. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2002-2005 Anton Altaparmakov | ||||
|  * Copyright (c) 2005 Yura Pakhuchiy | ||||
|  * Copyright (c) 2005-2006 Szabolcs Szakacsits | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_STDLIB_H | ||||
| #include <stdlib.h> | ||||
| #endif | ||||
| #ifdef HAVE_STRING_H | ||||
| #include <string.h> | ||||
| #endif | ||||
| #ifdef HAVE_ERRNO_H | ||||
| #include <errno.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "attrib.h" | ||||
| #include "debug.h" | ||||
| #include "logfile.h" | ||||
| #include "volume.h" | ||||
| #include "mst.h" | ||||
| #include "logging.h" | ||||
| #include "misc.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_check_restart_page_header - check the page header for consistency | ||||
|  * @rp:		restart page header to check | ||||
|  * @pos:	position in logfile at which the restart page header resides | ||||
|  * | ||||
|  * Check the restart page header @rp for consistency and return TRUE if it is | ||||
|  * consistent and FALSE otherwise. | ||||
|  * | ||||
|  * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not | ||||
|  * require the full restart page. | ||||
|  */ | ||||
| static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos) | ||||
| { | ||||
| 	u32 logfile_system_page_size, logfile_log_page_size; | ||||
| 	u16 ra_ofs, usa_count, usa_ofs, usa_end = 0; | ||||
| 	BOOL have_usa = TRUE; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering.\n"); | ||||
| 	/*
 | ||||
| 	 * If the system or log page sizes are smaller than the ntfs block size | ||||
| 	 * or either is not a power of 2 we cannot handle this log file. | ||||
| 	 */ | ||||
| 	logfile_system_page_size = le32_to_cpu(rp->system_page_size); | ||||
| 	logfile_log_page_size = le32_to_cpu(rp->log_page_size); | ||||
| 	if (logfile_system_page_size < NTFS_BLOCK_SIZE || | ||||
| 			logfile_log_page_size < NTFS_BLOCK_SIZE || | ||||
| 			logfile_system_page_size & | ||||
| 			(logfile_system_page_size - 1) || | ||||
| 			logfile_log_page_size & (logfile_log_page_size - 1)) { | ||||
| 		ntfs_log_error("$LogFile uses unsupported page size.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * We must be either at !pos (1st restart page) or at pos = system page | ||||
| 	 * size (2nd restart page). | ||||
| 	 */ | ||||
| 	if (pos && pos != logfile_system_page_size) { | ||||
| 		ntfs_log_error("Found restart area in incorrect " | ||||
| 				"position in $LogFile.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	/* We only know how to handle version 1.1. */ | ||||
| 	if (sle16_to_cpu(rp->major_ver) != 1 || | ||||
| 			sle16_to_cpu(rp->minor_ver) != 1) { | ||||
| 		ntfs_log_error("$LogFile version %i.%i is not " | ||||
| 				"supported.  (This driver supports version " | ||||
| 				"1.1 only.)\n", (int)sle16_to_cpu(rp->major_ver), | ||||
| 				(int)sle16_to_cpu(rp->minor_ver)); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * If chkdsk has been run the restart page may not be protected by an | ||||
| 	 * update sequence array. | ||||
| 	 */ | ||||
| 	if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) { | ||||
| 		have_usa = FALSE; | ||||
| 		goto skip_usa_checks; | ||||
| 	} | ||||
| 	/* Verify the size of the update sequence array. */ | ||||
| 	usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS); | ||||
| 	if (usa_count != le16_to_cpu(rp->usa_count)) { | ||||
| 		ntfs_log_error("$LogFile restart page specifies " | ||||
| 				"inconsistent update sequence array count.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	/* Verify the position of the update sequence array. */ | ||||
| 	usa_ofs = le16_to_cpu(rp->usa_ofs); | ||||
| 	usa_end = usa_ofs + usa_count * sizeof(u16); | ||||
| 	if (usa_ofs < sizeof(RESTART_PAGE_HEADER) || | ||||
| 			usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) { | ||||
| 		ntfs_log_error("$LogFile restart page specifies " | ||||
| 				"inconsistent update sequence array offset.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| skip_usa_checks: | ||||
| 	/*
 | ||||
| 	 * Verify the position of the restart area.  It must be: | ||||
| 	 *	- aligned to 8-byte boundary, | ||||
| 	 *	- after the update sequence array, and | ||||
| 	 *	- within the system page size. | ||||
| 	 */ | ||||
| 	ra_ofs = le16_to_cpu(rp->restart_area_offset); | ||||
| 	if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end : | ||||
| 			ra_ofs < sizeof(RESTART_PAGE_HEADER)) || | ||||
| 			ra_ofs > logfile_system_page_size) { | ||||
| 		ntfs_log_error("$LogFile restart page specifies " | ||||
| 				"inconsistent restart area offset.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn | ||||
| 	 * set. | ||||
| 	 */ | ||||
| 	if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) { | ||||
| 		ntfs_log_error("$LogFile restart page is not modified " | ||||
| 				"by chkdsk but a chkdsk LSN is specified.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	ntfs_log_trace("Done.\n"); | ||||
| 	return TRUE; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_check_restart_area - check the restart area for consistency | ||||
|  * @rp:		restart page whose restart area to check | ||||
|  * | ||||
|  * Check the restart area of the restart page @rp for consistency and return | ||||
|  * TRUE if it is consistent and FALSE otherwise. | ||||
|  * | ||||
|  * This function assumes that the restart page header has already been | ||||
|  * consistency checked. | ||||
|  * | ||||
|  * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not | ||||
|  * require the full restart page. | ||||
|  */ | ||||
| static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp) | ||||
| { | ||||
| 	u64 file_size; | ||||
| 	RESTART_AREA *ra; | ||||
| 	u16 ra_ofs, ra_len, ca_ofs; | ||||
| 	u8 fs_bits; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering.\n"); | ||||
| 	ra_ofs = le16_to_cpu(rp->restart_area_offset); | ||||
| 	ra = (RESTART_AREA*)((u8*)rp + ra_ofs); | ||||
| 	/*
 | ||||
| 	 * Everything before ra->file_size must be before the first word | ||||
| 	 * protected by an update sequence number.  This ensures that it is | ||||
| 	 * safe to access ra->client_array_offset. | ||||
| 	 */ | ||||
| 	if (ra_ofs + offsetof(RESTART_AREA, file_size) > | ||||
| 			NTFS_BLOCK_SIZE - sizeof(u16)) { | ||||
| 		ntfs_log_error("$LogFile restart area specifies " | ||||
| 				"inconsistent file offset.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * Now that we can access ra->client_array_offset, make sure everything | ||||
| 	 * up to the log client array is before the first word protected by an | ||||
| 	 * update sequence number.  This ensures we can access all of the | ||||
| 	 * restart area elements safely.  Also, the client array offset must be | ||||
| 	 * aligned to an 8-byte boundary. | ||||
| 	 */ | ||||
| 	ca_ofs = le16_to_cpu(ra->client_array_offset); | ||||
| 	if (((ca_ofs + 7) & ~7) != ca_ofs || | ||||
| 			ra_ofs + ca_ofs > (u16)(NTFS_BLOCK_SIZE - | ||||
| 			sizeof(u16))) { | ||||
| 		ntfs_log_error("$LogFile restart area specifies " | ||||
| 				"inconsistent client array offset.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * The restart area must end within the system page size both when | ||||
| 	 * calculated manually and as specified by ra->restart_area_length. | ||||
| 	 * Also, the calculated length must not exceed the specified length. | ||||
| 	 */ | ||||
| 	ra_len = ca_ofs + le16_to_cpu(ra->log_clients) * | ||||
| 			sizeof(LOG_CLIENT_RECORD); | ||||
| 	if ((u32)(ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) || | ||||
| 			(u32)(ra_ofs + le16_to_cpu(ra->restart_area_length)) > | ||||
| 			le32_to_cpu(rp->system_page_size) || | ||||
| 			ra_len > le16_to_cpu(ra->restart_area_length)) { | ||||
| 		ntfs_log_error("$LogFile restart area is out of bounds " | ||||
| 				"of the system page size specified by the " | ||||
| 				"restart page header and/or the specified " | ||||
| 				"restart area length is inconsistent.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * The ra->client_free_list and ra->client_in_use_list must be either | ||||
| 	 * LOGFILE_NO_CLIENT or less than ra->log_clients or they are | ||||
| 	 * overflowing the client array. | ||||
| 	 */ | ||||
| 	if ((ra->client_free_list != LOGFILE_NO_CLIENT && | ||||
| 			le16_to_cpu(ra->client_free_list) >= | ||||
| 			le16_to_cpu(ra->log_clients)) || | ||||
| 			(ra->client_in_use_list != LOGFILE_NO_CLIENT && | ||||
| 			le16_to_cpu(ra->client_in_use_list) >= | ||||
| 			le16_to_cpu(ra->log_clients))) { | ||||
| 		ntfs_log_error("$LogFile restart area specifies " | ||||
| 				"overflowing client free and/or in use lists.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * Check ra->seq_number_bits against ra->file_size for consistency. | ||||
| 	 * We cannot just use ffs() because the file size is not a power of 2. | ||||
| 	 */ | ||||
| 	file_size = (u64)sle64_to_cpu(ra->file_size); | ||||
| 	fs_bits = 0; | ||||
| 	while (file_size) { | ||||
| 		file_size >>= 1; | ||||
| 		fs_bits++; | ||||
| 	} | ||||
| 	if (le32_to_cpu(ra->seq_number_bits) != (u32)(67 - fs_bits)) { | ||||
| 		ntfs_log_error("$LogFile restart area specifies " | ||||
| 				"inconsistent sequence number bits.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	/* The log record header length must be a multiple of 8. */ | ||||
| 	if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) != | ||||
| 			le16_to_cpu(ra->log_record_header_length)) { | ||||
| 		ntfs_log_error("$LogFile restart area specifies " | ||||
| 				"inconsistent log record header length.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	/* Ditto for the log page data offset. */ | ||||
| 	if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) != | ||||
| 			le16_to_cpu(ra->log_page_data_offset)) { | ||||
| 		ntfs_log_error("$LogFile restart area specifies " | ||||
| 				"inconsistent log page data offset.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	ntfs_log_trace("Done.\n"); | ||||
| 	return TRUE; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_check_log_client_array - check the log client array for consistency | ||||
|  * @rp:		restart page whose log client array to check | ||||
|  * | ||||
|  * Check the log client array of the restart page @rp for consistency and | ||||
|  * return TRUE if it is consistent and FALSE otherwise. | ||||
|  * | ||||
|  * This function assumes that the restart page header and the restart area have | ||||
|  * already been consistency checked. | ||||
|  * | ||||
|  * Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this | ||||
|  * function needs @rp->system_page_size bytes in @rp, i.e. it requires the full | ||||
|  * restart page and the page must be multi sector transfer deprotected. | ||||
|  */ | ||||
| static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp) | ||||
| { | ||||
| 	RESTART_AREA *ra; | ||||
| 	LOG_CLIENT_RECORD *ca, *cr; | ||||
| 	u16 nr_clients, idx; | ||||
| 	BOOL in_free_list, idx_is_first; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering.\n"); | ||||
| 	ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); | ||||
| 	ca = (LOG_CLIENT_RECORD*)((u8*)ra + | ||||
| 			le16_to_cpu(ra->client_array_offset)); | ||||
| 	/*
 | ||||
| 	 * Check the ra->client_free_list first and then check the | ||||
| 	 * ra->client_in_use_list.  Check each of the log client records in | ||||
| 	 * each of the lists and check that the array does not overflow the | ||||
| 	 * ra->log_clients value.  Also keep track of the number of records | ||||
| 	 * visited as there cannot be more than ra->log_clients records and | ||||
| 	 * that way we detect eventual loops in within a list. | ||||
| 	 */ | ||||
| 	nr_clients = le16_to_cpu(ra->log_clients); | ||||
| 	idx = le16_to_cpu(ra->client_free_list); | ||||
| 	in_free_list = TRUE; | ||||
| check_list: | ||||
| 	for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--, | ||||
| 			idx = le16_to_cpu(cr->next_client)) { | ||||
| 		if (!nr_clients || idx >= le16_to_cpu(ra->log_clients)) | ||||
| 			goto err_out; | ||||
| 		/* Set @cr to the current log client record. */ | ||||
| 		cr = ca + idx; | ||||
| 		/* The first log client record must not have a prev_client. */ | ||||
| 		if (idx_is_first) { | ||||
| 			if (cr->prev_client != LOGFILE_NO_CLIENT) | ||||
| 				goto err_out; | ||||
| 			idx_is_first = FALSE; | ||||
| 		} | ||||
| 	} | ||||
| 	/* Switch to and check the in use list if we just did the free list. */ | ||||
| 	if (in_free_list) { | ||||
| 		in_free_list = FALSE; | ||||
| 		idx = le16_to_cpu(ra->client_in_use_list); | ||||
| 		goto check_list; | ||||
| 	} | ||||
| 	ntfs_log_trace("Done.\n"); | ||||
| 	return TRUE; | ||||
| err_out: | ||||
| 	ntfs_log_error("$LogFile log client array is corrupt.\n"); | ||||
| 	return FALSE; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_check_and_load_restart_page - check the restart page for consistency | ||||
|  * @log_na:	opened ntfs attribute for journal $LogFile | ||||
|  * @rp:		restart page to check | ||||
|  * @pos:	position in @log_na at which the restart page resides | ||||
|  * @wrp:       [OUT] copy of the multi sector transfer deprotected restart page | ||||
|  * @lsn:       [OUT] set to the current logfile lsn on success | ||||
|  * | ||||
|  * Check the restart page @rp for consistency and return 0 if it is consistent | ||||
|  * and errno otherwise.  The restart page may have been modified by chkdsk in | ||||
|  * which case its magic is CHKD instead of RSTR. | ||||
|  * | ||||
|  * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not | ||||
|  * require the full restart page. | ||||
|  * | ||||
|  * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a | ||||
|  * copy of the complete multi sector transfer deprotected page.  On failure, | ||||
|  * *@wrp is undefined. | ||||
|  * | ||||
|  * Similarly, if @lsn is not NULL, on success *@lsn will be set to the current | ||||
|  * logfile lsn according to this restart page.  On failure, *@lsn is undefined. | ||||
|  * | ||||
|  * The following error codes are defined: | ||||
|  *     EINVAL - The restart page is inconsistent. | ||||
|  *     ENOMEM - Not enough memory to load the restart page. | ||||
|  *     EIO    - Failed to reading from $LogFile. | ||||
|  */ | ||||
| static int ntfs_check_and_load_restart_page(ntfs_attr *log_na, | ||||
| 		RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp, | ||||
| 		LSN *lsn) | ||||
| { | ||||
| 	RESTART_AREA *ra; | ||||
| 	RESTART_PAGE_HEADER *trp; | ||||
| 	int err; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering.\n"); | ||||
| 	/* Check the restart page header for consistency. */ | ||||
| 	if (!ntfs_check_restart_page_header(rp, pos)) { | ||||
| 		/* Error output already done inside the function. */ | ||||
| 		return EINVAL; | ||||
| 	} | ||||
| 	/* Check the restart area for consistency. */ | ||||
| 	if (!ntfs_check_restart_area(rp)) { | ||||
| 		/* Error output already done inside the function. */ | ||||
| 		return EINVAL; | ||||
| 	} | ||||
| 	ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); | ||||
| 	/*
 | ||||
| 	 * Allocate a buffer to store the whole restart page so we can multi | ||||
| 	 * sector transfer deprotect it. | ||||
| 	 */ | ||||
| 	trp = ntfs_malloc(le32_to_cpu(rp->system_page_size)); | ||||
| 	if (!trp) | ||||
| 		return errno; | ||||
| 	/*
 | ||||
| 	 * Read the whole of the restart page into the buffer.  If it fits | ||||
| 	 * completely inside @rp, just copy it from there.  Otherwise read it | ||||
| 	 * from disk. | ||||
| 	 */ | ||||
| 	if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE) | ||||
| 		memcpy(trp, rp, le32_to_cpu(rp->system_page_size)); | ||||
| 	else if (ntfs_attr_pread(log_na, pos, | ||||
| 			le32_to_cpu(rp->system_page_size), trp) != | ||||
| 			le32_to_cpu(rp->system_page_size)) { | ||||
| 		err = errno; | ||||
| 		ntfs_log_error("Failed to read whole restart page into the " | ||||
| 				"buffer.\n"); | ||||
| 		if (err != ENOMEM) | ||||
| 			err = EIO; | ||||
| 		goto err_out; | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * Perform the multi sector transfer deprotection on the buffer if the | ||||
| 	 * restart page is protected. | ||||
| 	 */ | ||||
| 	if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count)) | ||||
| 			&& ntfs_mst_post_read_fixup((NTFS_RECORD*)trp, | ||||
| 			le32_to_cpu(rp->system_page_size))) { | ||||
| 		/*
 | ||||
| 		 * A multi sector tranfer error was detected.  We only need to | ||||
| 		 * abort if the restart page contents exceed the multi sector | ||||
| 		 * transfer fixup of the first sector. | ||||
| 		 */ | ||||
| 		if (le16_to_cpu(rp->restart_area_offset) + | ||||
| 				le16_to_cpu(ra->restart_area_length) > | ||||
| 				NTFS_BLOCK_SIZE - (int)sizeof(u16)) { | ||||
| 			ntfs_log_error("Multi sector transfer error " | ||||
| 				   "detected in $LogFile restart page.\n"); | ||||
| 			err = EINVAL; | ||||
| 			goto err_out; | ||||
| 		} | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * If the restart page is modified by chkdsk or there are no active | ||||
| 	 * logfile clients, the logfile is consistent.  Otherwise, need to | ||||
| 	 * check the log client records for consistency, too. | ||||
| 	 */ | ||||
| 	err = 0; | ||||
| 	if (ntfs_is_rstr_record(rp->magic) && | ||||
| 			ra->client_in_use_list != LOGFILE_NO_CLIENT) { | ||||
| 		if (!ntfs_check_log_client_array(trp)) { | ||||
| 			err = EINVAL; | ||||
| 			goto err_out; | ||||
| 		} | ||||
| 	} | ||||
| 	if (lsn) { | ||||
| 		if (ntfs_is_rstr_record(rp->magic)) | ||||
| 			*lsn = sle64_to_cpu(ra->current_lsn); | ||||
| 		else /* if (ntfs_is_chkd_record(rp->magic)) */ | ||||
| 			*lsn = sle64_to_cpu(rp->chkdsk_lsn); | ||||
| 	} | ||||
| 	ntfs_log_trace("Done.\n"); | ||||
| 	if (wrp) | ||||
| 		*wrp = trp; | ||||
| 	else { | ||||
| err_out: | ||||
| 		free(trp); | ||||
| 	} | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_check_logfile - check in the journal if the volume is consistent | ||||
|  * @log_na:	ntfs attribute of loaded journal $LogFile to check | ||||
|  * @rp:         [OUT] on success this is a copy of the current restart page | ||||
|  * | ||||
|  * Check the $LogFile journal for consistency and return TRUE if it is | ||||
|  * consistent and FALSE if not.  On success, the current restart page is | ||||
|  * returned in *@rp.  Caller must call ntfs_free(*@rp) when finished with it. | ||||
|  * | ||||
|  * At present we only check the two restart pages and ignore the log record | ||||
|  * pages. | ||||
|  * | ||||
|  * Note that the MstProtected flag is not set on the $LogFile inode and hence | ||||
|  * when reading pages they are not deprotected.  This is because we do not know | ||||
|  * if the $LogFile was created on a system with a different page size to ours | ||||
|  * yet and mst deprotection would fail if our page size is smaller. | ||||
|  */ | ||||
| BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp) | ||||
| { | ||||
| 	s64 size, pos; | ||||
| 	LSN rstr1_lsn, rstr2_lsn; | ||||
| 	ntfs_volume *vol = log_na->ni->vol; | ||||
| 	u8 *kaddr = NULL; | ||||
| 	RESTART_PAGE_HEADER *rstr1_ph = NULL; | ||||
| 	RESTART_PAGE_HEADER *rstr2_ph = NULL; | ||||
| 	int log_page_size, log_page_mask, err; | ||||
| 	BOOL logfile_is_empty = TRUE; | ||||
| 	u8 log_page_bits; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering.\n"); | ||||
| 	/* An empty $LogFile must have been clean before it got emptied. */ | ||||
| 	if (NVolLogFileEmpty(vol)) | ||||
| 		goto is_empty; | ||||
| 	size = log_na->data_size; | ||||
| 	/* Make sure the file doesn't exceed the maximum allowed size. */ | ||||
| 	if (size > (s64)MaxLogFileSize) | ||||
| 		size = MaxLogFileSize; | ||||
| 	log_page_size = DefaultLogPageSize; | ||||
| 	log_page_mask = log_page_size - 1; | ||||
| 	/*
 | ||||
| 	 * Use generic_ffs() instead of ffs() to enable the compiler to | ||||
| 	 * optimize log_page_size and log_page_bits into constants. | ||||
| 	 */ | ||||
| 	log_page_bits = ffs(log_page_size) - 1; | ||||
| 	size &= ~(log_page_size - 1); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Ensure the log file is big enough to store at least the two restart | ||||
| 	 * pages and the minimum number of log record pages. | ||||
| 	 */ | ||||
| 	if (size < log_page_size * 2 || (size - log_page_size * 2) >> | ||||
| 			log_page_bits < MinLogRecordPages) { | ||||
| 		ntfs_log_error("$LogFile is too small.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	/* Allocate memory for restart page. */ | ||||
| 	kaddr = ntfs_malloc(NTFS_BLOCK_SIZE); | ||||
| 	if (!kaddr) | ||||
| 		return FALSE; | ||||
| 	/*
 | ||||
| 	 * Read through the file looking for a restart page.  Since the restart | ||||
| 	 * page header is at the beginning of a page we only need to search at | ||||
| 	 * what could be the beginning of a page (for each page size) rather | ||||
| 	 * than scanning the whole file byte by byte.  If all potential places | ||||
| 	 * contain empty and uninitialized records, the log file can be assumed | ||||
| 	 * to be empty. | ||||
| 	 */ | ||||
| 	for (pos = 0; pos < size; pos <<= 1) { | ||||
| 		/*
 | ||||
| 		 * Read first NTFS_BLOCK_SIZE bytes of potential restart page. | ||||
| 		 */ | ||||
| 		if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) != | ||||
| 				NTFS_BLOCK_SIZE) { | ||||
| 			ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE " | ||||
| 					"bytes of potential restart page.\n"); | ||||
| 			goto err_out; | ||||
| 		} | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * A non-empty block means the logfile is not empty while an | ||||
| 		 * empty block after a non-empty block has been encountered | ||||
| 		 * means we are done. | ||||
| 		 */ | ||||
| 		if (!ntfs_is_empty_recordp((le32*)kaddr)) | ||||
| 			logfile_is_empty = FALSE; | ||||
| 		else if (!logfile_is_empty) | ||||
| 			break; | ||||
| 		/*
 | ||||
| 		 * A log record page means there cannot be a restart page after | ||||
| 		 * this so no need to continue searching. | ||||
| 		 */ | ||||
| 		if (ntfs_is_rcrd_recordp((le32*)kaddr)) | ||||
| 			break; | ||||
| 		/* If not a (modified by chkdsk) restart page, continue. */ | ||||
| 		if (!ntfs_is_rstr_recordp((le32*)kaddr) && | ||||
| 				!ntfs_is_chkd_recordp((le32*)kaddr)) { | ||||
| 			if (!pos) | ||||
| 				pos = NTFS_BLOCK_SIZE >> 1; | ||||
| 			continue; | ||||
| 		} | ||||
| 		/*
 | ||||
| 		 * Check the (modified by chkdsk) restart page for consistency | ||||
| 		 * and get a copy of the complete multi sector transfer | ||||
| 		 * deprotected restart page. | ||||
| 		 */ | ||||
| 		err = ntfs_check_and_load_restart_page(log_na, | ||||
| 				(RESTART_PAGE_HEADER*)kaddr, pos, | ||||
| 				!rstr1_ph ? &rstr1_ph : &rstr2_ph, | ||||
| 				!rstr1_ph ? &rstr1_lsn : &rstr2_lsn); | ||||
| 		if (!err) { | ||||
| 			/*
 | ||||
| 			 * If we have now found the first (modified by chkdsk) | ||||
| 			 * restart page, continue looking for the second one. | ||||
| 			 */ | ||||
| 			if (!pos) { | ||||
| 				pos = NTFS_BLOCK_SIZE >> 1; | ||||
| 				continue; | ||||
| 			} | ||||
| 			/*
 | ||||
| 			 * We have now found the second (modified by chkdsk) | ||||
| 			 * restart page, so we can stop looking. | ||||
| 			 */ | ||||
| 			break; | ||||
| 		} | ||||
| 		/*
 | ||||
| 		 * Error output already done inside the function.  Note, we do | ||||
| 		 * not abort if the restart page was invalid as we might still | ||||
| 		 * find a valid one further in the file. | ||||
| 		 */ | ||||
| 		if (err != EINVAL) | ||||
| 		      goto err_out; | ||||
| 		/* Continue looking. */ | ||||
| 		if (!pos) | ||||
| 			pos = NTFS_BLOCK_SIZE >> 1; | ||||
| 	} | ||||
| 	if (kaddr) { | ||||
| 		free(kaddr); | ||||
| 		kaddr = NULL; | ||||
| 	} | ||||
| 	if (logfile_is_empty) { | ||||
| 		NVolSetLogFileEmpty(vol); | ||||
| is_empty: | ||||
| 		ntfs_log_trace("Done.  ($LogFile is empty.)\n"); | ||||
| 		return TRUE; | ||||
| 	} | ||||
| 	if (!rstr1_ph) { | ||||
| 		if (rstr2_ph) | ||||
| 			ntfs_log_error("BUG: rstr2_ph isn't NULL!\n"); | ||||
| 		ntfs_log_error("Did not find any restart pages in " | ||||
| 			   "$LogFile and it was not empty.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	/* If both restart pages were found, use the more recent one. */ | ||||
| 	if (rstr2_ph) { | ||||
| 		/*
 | ||||
| 		 * If the second restart area is more recent, switch to it. | ||||
| 		 * Otherwise just throw it away. | ||||
| 		 */ | ||||
| 		if (rstr2_lsn > rstr1_lsn) { | ||||
| 			ntfs_log_debug("Using second restart page as it is more " | ||||
| 					"recent.\n"); | ||||
| 			free(rstr1_ph); | ||||
| 			rstr1_ph = rstr2_ph; | ||||
| 			/* rstr1_lsn = rstr2_lsn; */ | ||||
| 		} else { | ||||
| 			ntfs_log_debug("Using first restart page as it is more " | ||||
| 					"recent.\n"); | ||||
| 			free(rstr2_ph); | ||||
| 		} | ||||
| 		rstr2_ph = NULL; | ||||
| 	} | ||||
| 	/* All consistency checks passed. */ | ||||
| 	if (rp) | ||||
| 		*rp = rstr1_ph; | ||||
| 	else | ||||
| 		free(rstr1_ph); | ||||
| 	ntfs_log_trace("Done.\n"); | ||||
| 	return TRUE; | ||||
| err_out: | ||||
| 	free(kaddr); | ||||
| 	free(rstr1_ph); | ||||
| 	free(rstr2_ph); | ||||
| 	return FALSE; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_is_logfile_clean - check in the journal if the volume is clean | ||||
|  * @log_na:	ntfs attribute of loaded journal $LogFile to check | ||||
|  * @rp:         copy of the current restart page | ||||
|  * | ||||
|  * Analyze the $LogFile journal and return TRUE if it indicates the volume was | ||||
|  * shutdown cleanly and FALSE if not. | ||||
|  * | ||||
|  * At present we only look at the two restart pages and ignore the log record | ||||
|  * pages.  This is a little bit crude in that there will be a very small number | ||||
|  * of cases where we think that a volume is dirty when in fact it is clean. | ||||
|  * This should only affect volumes that have not been shutdown cleanly but did | ||||
|  * not have any pending, non-check-pointed i/o, i.e. they were completely idle | ||||
|  * at least for the five seconds preceding the unclean shutdown. | ||||
|  * | ||||
|  * This function assumes that the $LogFile journal has already been consistency | ||||
|  * checked by a call to ntfs_check_logfile() and in particular if the $LogFile | ||||
|  * is empty this function requires that NVolLogFileEmpty() is true otherwise an | ||||
|  * empty volume will be reported as dirty. | ||||
|  */ | ||||
| BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp) | ||||
| { | ||||
| 	RESTART_AREA *ra; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering.\n"); | ||||
| 	/* An empty $LogFile must have been clean before it got emptied. */ | ||||
| 	if (NVolLogFileEmpty(log_na->ni->vol)) { | ||||
| 		ntfs_log_trace("Done.  ($LogFile is empty.)\n"); | ||||
| 		return TRUE; | ||||
| 	} | ||||
| 	if (!rp) { | ||||
| 		ntfs_log_error("Restart page header is NULL.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	if (!ntfs_is_rstr_record(rp->magic) && | ||||
| 			!ntfs_is_chkd_record(rp->magic)) { | ||||
| 		ntfs_log_error("Restart page buffer is invalid.  This is " | ||||
| 			   "probably a bug in that the $LogFile should " | ||||
| 			   "have been consistency checked before calling " | ||||
| 			   "this function.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 
 | ||||
| 	ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); | ||||
| 	/*
 | ||||
| 	 * If the $LogFile has active clients, i.e. it is open, and we do not | ||||
| 	 * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags, | ||||
| 	 * we assume there was an unclean shutdown. | ||||
| 	 */ | ||||
| 	if (ra->client_in_use_list != LOGFILE_NO_CLIENT && | ||||
| 			!(ra->flags & RESTART_VOLUME_IS_CLEAN)) { | ||||
| 		ntfs_log_debug("Done.  $LogFile indicates a dirty shutdown.\n"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	/* $LogFile indicates a clean shutdown. */ | ||||
| 	ntfs_log_trace("Done.  $LogFile indicates a clean shutdown.\n"); | ||||
| 	return TRUE; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_empty_logfile - empty the contents of the $LogFile journal | ||||
|  * @na:		ntfs attribute of journal $LogFile to empty | ||||
|  * | ||||
|  * Empty the contents of the $LogFile journal @na and return 0 on success and | ||||
|  * -1 on error. | ||||
|  * | ||||
|  * This function assumes that the $LogFile journal has already been consistency | ||||
|  * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean() | ||||
|  * has been used to ensure that the $LogFile is clean. | ||||
|  */ | ||||
| int ntfs_empty_logfile(ntfs_attr *na) | ||||
| { | ||||
| 	s64 len, pos, count; | ||||
| 	char buf[NTFS_BUF_SIZE]; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering.\n"); | ||||
| 	if (NVolLogFileEmpty(na->ni->vol)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* The $DATA attribute of the $LogFile has to be non-resident. */ | ||||
| 	if (!NAttrNonResident(na)) { | ||||
| 		errno = EIO; | ||||
| 		ntfs_log_perror("$LogFile $DATA attribute is resident!?!\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* 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_SIZE, buf)) > 0) | ||||
| 		pos += count; | ||||
| 
 | ||||
| 	if (count == -1 || pos != len) { | ||||
| 		ntfs_log_error("Amount of $LogFile data read does not " | ||||
| 				"correspond to expected length!\n"); | ||||
| 		if (count != -1) | ||||
| 			errno = EIO; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Fill the buffer with 0xff's. */ | ||||
| 	memset(buf, -1, NTFS_BUF_SIZE); | ||||
| 
 | ||||
| 	/* Set the $DATA attribute. */ | ||||
| 	pos = 0; | ||||
| 	while ((count = len - pos) > 0) { | ||||
| 		if (count > NTFS_BUF_SIZE) | ||||
| 			count = NTFS_BUF_SIZE; | ||||
| 
 | ||||
| 		if ((count = ntfs_attr_pwrite(na, pos, count, buf)) <= 0) { | ||||
| 			ntfs_log_perror("Failed to set the $LogFile attribute " | ||||
| 					"value.\n"); | ||||
| 			if (count != -1) | ||||
| 				errno = EIO; | ||||
| 			return -1; | ||||
| 		} | ||||
| 		pos += count; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Set the flag so we do not have to do it again on remount. */ | ||||
| 	NVolSetLogFileEmpty(na->ni->vol); | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -0,0 +1,634 @@ | |||
| /**
 | ||||
|  * logging.c - Centralised logging.  Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2005 Richard Russon | ||||
|  * Copyright (c) 2005-2006 Szabolcs Szakacsits | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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 <stdio.h> | ||||
| #endif | ||||
| #ifdef HAVE_ERRNO_H | ||||
| #include <errno.h> | ||||
| #endif | ||||
| #ifdef HAVE_STDARG_H | ||||
| #include <stdarg.h> | ||||
| #endif | ||||
| #ifdef HAVE_STRING_H | ||||
| #include <string.h> | ||||
| #endif | ||||
| #ifdef HAVE_STDLIB_H | ||||
| #include <stdlib.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYSLOG_H | ||||
| #include <syslog.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "logging.h" | ||||
| #include "misc.h" | ||||
| 
 | ||||
| #ifndef PATH_SEP | ||||
| #define PATH_SEP '/' | ||||
| #endif | ||||
| 
 | ||||
| /* Colour prefixes and a suffix */ | ||||
| static const char *col_green  = "\e[32m"; | ||||
| static const char *col_cyan   = "\e[36m"; | ||||
| static const char *col_yellow = "\e[01;33m"; | ||||
| static const char *col_red    = "\e[01;31m"; | ||||
| static const char *col_redinv = "\e[01;07;31m"; | ||||
| static const char *col_end    = "\e[0m"; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct ntfs_logging - Control info for the logging system | ||||
|  * @levels:	Bitfield of logging levels | ||||
|  * @flags:	Flags which affect the output style | ||||
|  * @handler:	Function to perform the actual logging | ||||
|  */ | ||||
| struct ntfs_logging { | ||||
| 	u32 levels; | ||||
| 	u32 flags; | ||||
| 	ntfs_log_handler *handler; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log | ||||
|  * This struct controls all the logging within the library and tools. | ||||
|  */ | ||||
| static struct ntfs_logging ntfs_log = { | ||||
| #ifdef DEBUG | ||||
| 	NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | | ||||
| #endif | ||||
| 	NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_WARNING | | ||||
| 	NTFS_LOG_LEVEL_ERROR | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL | | ||||
| 	NTFS_LOG_LEVEL_PROGRESS, | ||||
| 	NTFS_LOG_FLAG_ONLYNAME, | ||||
| #ifdef DEBUG | ||||
| 	ntfs_log_handler_outerr | ||||
| #else | ||||
| 	ntfs_log_handler_null | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_get_levels - Get a list of the current logging levels | ||||
|  * | ||||
|  * Find out which logging levels are enabled. | ||||
|  * | ||||
|  * Returns:  Log levels in a 32-bit field | ||||
|  */ | ||||
| u32 ntfs_log_get_levels(void) | ||||
| { | ||||
| 	return ntfs_log.levels; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_set_levels - Enable extra logging levels | ||||
|  * @levels:	32-bit field of log levels to set | ||||
|  * | ||||
|  * Enable one or more logging levels. | ||||
|  * The logging levels are named: NTFS_LOG_LEVEL_*. | ||||
|  * | ||||
|  * Returns:  Log levels that were enabled before the call | ||||
|  */ | ||||
| u32 ntfs_log_set_levels(u32 levels) | ||||
| { | ||||
| 	u32 old; | ||||
| 	old = ntfs_log.levels; | ||||
| 	ntfs_log.levels |= levels; | ||||
| 	return old; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_clear_levels - Disable some logging levels | ||||
|  * @levels:	32-bit field of log levels to clear | ||||
|  * | ||||
|  * Disable one or more logging levels. | ||||
|  * The logging levels are named: NTFS_LOG_LEVEL_*. | ||||
|  * | ||||
|  * Returns:  Log levels that were enabled before the call | ||||
|  */ | ||||
| u32 ntfs_log_clear_levels(u32 levels) | ||||
| { | ||||
| 	u32 old; | ||||
| 	old = ntfs_log.levels; | ||||
| 	ntfs_log.levels &= (~levels); | ||||
| 	return old; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_get_flags - Get a list of logging style flags | ||||
|  * | ||||
|  * Find out which logging flags are enabled. | ||||
|  * | ||||
|  * Returns:  Logging flags in a 32-bit field | ||||
|  */ | ||||
| u32 ntfs_log_get_flags(void) | ||||
| { | ||||
| 	return ntfs_log.flags; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_set_flags - Enable extra logging style flags | ||||
|  * @flags:	32-bit field of logging flags to set | ||||
|  * | ||||
|  * Enable one or more logging flags. | ||||
|  * The log flags are named: NTFS_LOG_LEVEL_*. | ||||
|  * | ||||
|  * Returns:  Logging flags that were enabled before the call | ||||
|  */ | ||||
| u32 ntfs_log_set_flags(u32 flags) | ||||
| { | ||||
| 	u32 old; | ||||
| 	old = ntfs_log.flags; | ||||
| 	ntfs_log.flags |= flags; | ||||
| 	return old; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_clear_flags - Disable some logging styles | ||||
|  * @flags:	32-bit field of logging flags to clear | ||||
|  * | ||||
|  * Disable one or more logging flags. | ||||
|  * The log flags are named: NTFS_LOG_LEVEL_*. | ||||
|  * | ||||
|  * Returns:  Logging flags that were enabled before the call | ||||
|  */ | ||||
| u32 ntfs_log_clear_flags(u32 flags) | ||||
| { | ||||
| 	u32 old; | ||||
| 	old = ntfs_log.flags; | ||||
| 	ntfs_log.flags &= (~flags); | ||||
| 	return old; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_get_stream - Default output streams for logging levels | ||||
|  * @level:	Log level | ||||
|  * | ||||
|  * By default, urgent messages are sent to "stderr". | ||||
|  * Other messages are sent to "stdout". | ||||
|  * | ||||
|  * Returns:  "string"  Prefix to be used | ||||
|  */ | ||||
| static FILE * ntfs_log_get_stream(u32 level) | ||||
| { | ||||
| 	FILE *stream; | ||||
| 
 | ||||
| 	switch (level) { | ||||
| 		case NTFS_LOG_LEVEL_INFO: | ||||
| 		case NTFS_LOG_LEVEL_QUIET: | ||||
| 		case NTFS_LOG_LEVEL_PROGRESS: | ||||
| 		case NTFS_LOG_LEVEL_VERBOSE: | ||||
| 			stream = stdout; | ||||
| 			break; | ||||
| 
 | ||||
| 		case NTFS_LOG_LEVEL_DEBUG: | ||||
| 		case NTFS_LOG_LEVEL_TRACE: | ||||
| 		case NTFS_LOG_LEVEL_WARNING: | ||||
| 		case NTFS_LOG_LEVEL_ERROR: | ||||
| 		case NTFS_LOG_LEVEL_CRITICAL: | ||||
| 		case NTFS_LOG_LEVEL_PERROR: | ||||
| 		default: | ||||
| 			stream = stderr; | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	return stream; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_get_prefix - Default prefixes for logging levels | ||||
|  * @level:	Log level to be prefixed | ||||
|  * | ||||
|  * Prefixing the logging output can make it easier to parse. | ||||
|  * | ||||
|  * Returns:  "string"  Prefix to be used | ||||
|  */ | ||||
| static const char * ntfs_log_get_prefix(u32 level) | ||||
| { | ||||
| 	const char *prefix; | ||||
| 
 | ||||
| 	switch (level) { | ||||
| 		case NTFS_LOG_LEVEL_DEBUG: | ||||
| 			prefix = "DEBUG: "; | ||||
| 			break; | ||||
| 		case NTFS_LOG_LEVEL_TRACE: | ||||
| 			prefix = "TRACE: "; | ||||
| 			break; | ||||
| 		case NTFS_LOG_LEVEL_QUIET: | ||||
| 			prefix = "QUIET: "; | ||||
| 			break; | ||||
| 		case NTFS_LOG_LEVEL_INFO: | ||||
| 			prefix = "INFO: "; | ||||
| 			break; | ||||
| 		case NTFS_LOG_LEVEL_VERBOSE: | ||||
| 			prefix = "VERBOSE: "; | ||||
| 			break; | ||||
| 		case NTFS_LOG_LEVEL_PROGRESS: | ||||
| 			prefix = "PROGRESS: "; | ||||
| 			break; | ||||
| 		case NTFS_LOG_LEVEL_WARNING: | ||||
| 			prefix = "WARNING: "; | ||||
| 			break; | ||||
| 		case NTFS_LOG_LEVEL_ERROR: | ||||
| 			prefix = "ERROR: "; | ||||
| 			break; | ||||
| 		case NTFS_LOG_LEVEL_PERROR: | ||||
| 			prefix = "ERROR: "; | ||||
| 			break; | ||||
| 		case NTFS_LOG_LEVEL_CRITICAL: | ||||
| 			prefix = "CRITICAL: "; | ||||
| 			break; | ||||
| 		default: | ||||
| 			prefix = ""; | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	return prefix; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_set_handler - Provide an alternate logging handler | ||||
|  * @handler:	function to perform the logging | ||||
|  * | ||||
|  * This alternate handler will be called for all future logging requests. | ||||
|  * If no @handler is specified, logging will revert to the default handler. | ||||
|  */ | ||||
| void ntfs_log_set_handler(ntfs_log_handler *handler) | ||||
| { | ||||
| 	if (handler) { | ||||
| 		ntfs_log.handler = handler; | ||||
| #ifdef HAVE_SYSLOG_H | ||||
| 		if (handler == ntfs_log_handler_syslog) | ||||
| 			openlog("libntfs", LOG_PID, LOG_USER); | ||||
| #endif | ||||
| 	} else | ||||
| 		ntfs_log.handler = ntfs_log_handler_null; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_redirect - Pass on the request to the real handler | ||||
|  * @function:	Function in which the log line occurred | ||||
|  * @file:	File in which the log line occurred | ||||
|  * @line:	Line number on which the log line occurred | ||||
|  * @level:	Level at which the line is logged | ||||
|  * @data:	User specified data, possibly specific to a handler | ||||
|  * @format:	printf-style formatting string | ||||
|  * @...:	Arguments to be formatted | ||||
|  * | ||||
|  * This is just a redirector function.  The arguments are simply passed to the | ||||
|  * main logging handler (as defined in the global logging struct @ntfs_log). | ||||
|  * | ||||
|  * Returns:  -1  Error occurred | ||||
|  *            0  Message wasn't logged | ||||
|  *          num  Number of output characters | ||||
|  */ | ||||
| int ntfs_log_redirect(const char *function, const char *file, | ||||
| 	int line, u32 level, void *data, const char *format, ...) | ||||
| { | ||||
| 	int olderr = errno; | ||||
| 	int ret; | ||||
| 	va_list args; | ||||
| 
 | ||||
| 	if (!(ntfs_log.levels & level))		/* Don't log this message */ | ||||
| 		return 0; | ||||
| 
 | ||||
| 	va_start(args, format); | ||||
| 	errno = olderr; | ||||
| 	ret = ntfs_log.handler(function, file, line, level, data, format, args); | ||||
| 	va_end(args); | ||||
| 
 | ||||
| 	errno = olderr; | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_handler_syslog - syslog logging handler | ||||
|  * @function:	Function in which the log line occurred | ||||
|  * @file:	File in which the log line occurred | ||||
|  * @line:	Line number on which the log line occurred | ||||
|  * @level:	Level at which the line is logged | ||||
|  * @data:	User specified data, possibly specific to a handler | ||||
|  * @format:	printf-style formatting string | ||||
|  * @args:	Arguments to be formatted | ||||
|  * | ||||
|  * A simple syslog logging handler.  Ignores colors. | ||||
|  * | ||||
|  * Returns:  -1  Error occurred | ||||
|  *            0  Message wasn't logged | ||||
|  *          num  Number of output characters | ||||
|  */ | ||||
| 
 | ||||
| #ifdef HAVE_SYSLOG_H | ||||
| int ntfs_log_handler_syslog(const char *function  __attribute__((unused)), | ||||
| 	const char *file, __attribute__((unused)) int line, u32 level, | ||||
| 	void *data __attribute__((unused)), const char *format, va_list args) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	int olderr = errno; | ||||
| 
 | ||||
| 	if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && | ||||
| 	    (strchr(file, PATH_SEP)))		/* Abbreviate the filename */ | ||||
| 		file = strrchr(file, PATH_SEP) + 1; | ||||
| #if 0	/* FIXME: Implement this all. */
 | ||||
| 	if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX)	/* Prefix the output */ | ||||
| 		ret += fprintf(stream, "%s", ntfs_log_get_prefix(level)); | ||||
| 
 | ||||
| 	if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME)	/* Source filename */ | ||||
| 		ret += fprintf(stream, "%s ", file); | ||||
| 
 | ||||
| 	if (ntfs_log.flags & NTFS_LOG_FLAG_LINE)	/* Source line number */ | ||||
| 		ret += fprintf(stream, "(%d) ", line); | ||||
| 
 | ||||
| 	if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */ | ||||
| 	    (level & NTFS_LOG_LEVEL_TRACE)) | ||||
| 		ret += fprintf(stream, "%s(): ", function); | ||||
| 
 | ||||
| 	ret += vfprintf(stream, format, args); | ||||
| 
 | ||||
| 	if (level & NTFS_LOG_LEVEL_PERROR) { | ||||
| 		if (reason) | ||||
| 			ret += fprintf(stream, ": %s\n", reason); | ||||
| 		else | ||||
| 			ret += fprintf(stream, ": %s\n", strerror(olderr)); | ||||
| 	} | ||||
| #endif | ||||
| 	vsyslog(LOG_NOTICE, format, args); | ||||
| 	ret = 1; /* FIXME: caclulate how many bytes had been written. */ | ||||
| 
 | ||||
| 	errno = olderr; | ||||
| 	return ret; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_handler_fprintf - Basic logging handler | ||||
|  * @function:	Function in which the log line occurred | ||||
|  * @file:	File in which the log line occurred | ||||
|  * @line:	Line number on which the log line occurred | ||||
|  * @level:	Level at which the line is logged | ||||
|  * @data:	User specified data, possibly specific to a handler | ||||
|  * @format:	printf-style formatting string | ||||
|  * @args:	Arguments to be formatted | ||||
|  * | ||||
|  * A simple logging handler.  This is where the log line is finally displayed. | ||||
|  * It is more likely that you will want to set the handler to either | ||||
|  * ntfs_log_handler_outerr or ntfs_log_handler_stderr. | ||||
|  * | ||||
|  * Note: For this handler, @data is a pointer to a FILE output stream. | ||||
|  *       If @data is NULL, nothing will be displayed. | ||||
|  * | ||||
|  * Returns:  -1  Error occurred | ||||
|  *            0  Message wasn't logged | ||||
|  *          num  Number of output characters | ||||
|  */ | ||||
| int ntfs_log_handler_fprintf(const char *function, const char *file, | ||||
| 	int line, u32 level, void *data, const char *format, va_list args) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	int olderr = errno; | ||||
| 	FILE *stream; | ||||
| 	const char *col_prefix = NULL; | ||||
| 	const char *col_suffix = NULL; | ||||
| 
 | ||||
| 	if (!data)		/* Interpret data as a FILE stream. */ | ||||
| 		return 0;	/* If it's NULL, we can't do anything. */ | ||||
| 	stream = (FILE*)data; | ||||
| 
 | ||||
| 	if (ntfs_log.flags & NTFS_LOG_FLAG_COLOUR) { | ||||
| 		/* Pick a colour determined by the log level */ | ||||
| 		switch (level) { | ||||
| 			case NTFS_LOG_LEVEL_DEBUG: | ||||
| 				col_prefix = col_green; | ||||
| 				col_suffix = col_end; | ||||
| 				break; | ||||
| 			case NTFS_LOG_LEVEL_TRACE: | ||||
| 				col_prefix = col_cyan; | ||||
| 				col_suffix = col_end; | ||||
| 				break; | ||||
| 			case NTFS_LOG_LEVEL_WARNING: | ||||
| 				col_prefix = col_yellow; | ||||
| 				col_suffix = col_end; | ||||
| 				break; | ||||
| 			case NTFS_LOG_LEVEL_ERROR: | ||||
| 			case NTFS_LOG_LEVEL_PERROR: | ||||
| 				col_prefix = col_red; | ||||
| 				col_suffix = col_end; | ||||
| 				break; | ||||
| 			case NTFS_LOG_LEVEL_CRITICAL: | ||||
| 				col_prefix = col_redinv; | ||||
| 				col_suffix = col_end; | ||||
| 				break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (col_prefix) | ||||
| 		ret += fprintf(stream, col_prefix); | ||||
| 
 | ||||
| 	if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && | ||||
| 	    (strchr(file, PATH_SEP)))		/* Abbreviate the filename */ | ||||
| 		file = strrchr(file, PATH_SEP) + 1; | ||||
| 
 | ||||
| 	if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX)	/* Prefix the output */ | ||||
| 		ret += fprintf(stream, "%s", ntfs_log_get_prefix(level)); | ||||
| 
 | ||||
| 	if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME)	/* Source filename */ | ||||
| 		ret += fprintf(stream, "%s ", file); | ||||
| 
 | ||||
| 	if (ntfs_log.flags & NTFS_LOG_FLAG_LINE)	/* Source line number */ | ||||
| 		ret += fprintf(stream, "(%d) ", line); | ||||
| 
 | ||||
| 	if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */ | ||||
| 	    (level & NTFS_LOG_LEVEL_TRACE)) | ||||
| 		ret += fprintf(stream, "%s(): ", function); | ||||
| 
 | ||||
| 	ret += vfprintf(stream, format, args); | ||||
| 
 | ||||
| 	if (level & NTFS_LOG_LEVEL_PERROR) | ||||
| 		ret += fprintf(stream, ": %s\n", strerror(olderr)); | ||||
| 
 | ||||
| 	if (col_suffix) | ||||
| 		ret += fprintf(stream, col_suffix); | ||||
| 
 | ||||
| 
 | ||||
| 	fflush(stream); | ||||
| 	errno = olderr; | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_handler_null - Null logging handler (no output) | ||||
|  * @function:	Function in which the log line occurred | ||||
|  * @file:	File in which the log line occurred | ||||
|  * @line:	Line number on which the log line occurred | ||||
|  * @level:	Level at which the line is logged | ||||
|  * @data:	User specified data, possibly specific to a handler | ||||
|  * @format:	printf-style formatting string | ||||
|  * @args:	Arguments to be formatted | ||||
|  * | ||||
|  * This handler produces no output.  It provides a way to temporarily disable | ||||
|  * logging, without having to change the levels and flags. | ||||
|  * | ||||
|  * Returns:  0  Message wasn't logged | ||||
|  */ | ||||
| int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)), | ||||
| 	int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)), | ||||
| 	const char *format __attribute__((unused)), va_list args __attribute__((unused))) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_handler_stdout - All logs go to stdout | ||||
|  * @function:	Function in which the log line occurred | ||||
|  * @file:	File in which the log line occurred | ||||
|  * @line:	Line number on which the log line occurred | ||||
|  * @level:	Level at which the line is logged | ||||
|  * @data:	User specified data, possibly specific to a handler | ||||
|  * @format:	printf-style formatting string | ||||
|  * @args:	Arguments to be formatted | ||||
|  * | ||||
|  * Display a log message to stdout. | ||||
|  * | ||||
|  * Note: For this handler, @data is a pointer to a FILE output stream. | ||||
|  *       If @data is NULL, then stdout will be used. | ||||
|  * | ||||
|  * Note: This function calls ntfs_log_handler_fprintf to do the main work. | ||||
|  * | ||||
|  * Returns:  -1  Error occurred | ||||
|  *            0  Message wasn't logged | ||||
|  *          num  Number of output characters | ||||
|  */ | ||||
| int ntfs_log_handler_stdout(const char *function, const char *file, | ||||
| 	int line, u32 level, void *data, const char *format, va_list args) | ||||
| { | ||||
| 	if (!data) | ||||
| 		data = stdout; | ||||
| 
 | ||||
| 	return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level | ||||
|  * @function:	Function in which the log line occurred | ||||
|  * @file:	File in which the log line occurred | ||||
|  * @line:	Line number on which the log line occurred | ||||
|  * @level:	Level at which the line is logged | ||||
|  * @data:	User specified data, possibly specific to a handler | ||||
|  * @format:	printf-style formatting string | ||||
|  * @args:	Arguments to be formatted | ||||
|  * | ||||
|  * Display a log message.  The output stream will be determined by the log | ||||
|  * level. | ||||
|  * | ||||
|  * Note: For this handler, @data is a pointer to a FILE output stream. | ||||
|  *       If @data is NULL, the function ntfs_log_get_stream will be called | ||||
|  * | ||||
|  * Note: This function calls ntfs_log_handler_fprintf to do the main work. | ||||
|  * | ||||
|  * Returns:  -1  Error occurred | ||||
|  *            0  Message wasn't logged | ||||
|  *          num  Number of output characters | ||||
|  */ | ||||
| int ntfs_log_handler_outerr(const char *function, const char *file, | ||||
| 	int line, u32 level, void *data, const char *format, va_list args) | ||||
| { | ||||
| 	if (!data) | ||||
| 		data = ntfs_log_get_stream(level); | ||||
| 
 | ||||
| 	return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_handler_stderr - All logs go to stderr | ||||
|  * @function:	Function in which the log line occurred | ||||
|  * @file:	File in which the log line occurred | ||||
|  * @line:	Line number on which the log line occurred | ||||
|  * @level:	Level at which the line is logged | ||||
|  * @data:	User specified data, possibly specific to a handler | ||||
|  * @format:	printf-style formatting string | ||||
|  * @args:	Arguments to be formatted | ||||
|  * | ||||
|  * Display a log message to stderr. | ||||
|  * | ||||
|  * Note: For this handler, @data is a pointer to a FILE output stream. | ||||
|  *       If @data is NULL, then stdout will be used. | ||||
|  * | ||||
|  * Note: This function calls ntfs_log_handler_fprintf to do the main work. | ||||
|  * | ||||
|  * Returns:  -1  Error occurred | ||||
|  *            0  Message wasn't logged | ||||
|  *          num  Number of output characters | ||||
|  */ | ||||
| int ntfs_log_handler_stderr(const char *function, const char *file, | ||||
| 	int line, u32 level, void *data, const char *format, va_list args) | ||||
| { | ||||
| 	if (!data) | ||||
| 		data = stderr; | ||||
| 
 | ||||
| 	return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_log_parse_option - Act upon command line options | ||||
|  * @option:	Option flag | ||||
|  * | ||||
|  * Delegate some of the work of parsing the command line.  All the options begin | ||||
|  * with "--log-".  Options cause log levels to be enabled in @ntfs_log (the | ||||
|  * global logging structure). | ||||
|  * | ||||
|  * Note: The "colour" option changes the logging handler. | ||||
|  * | ||||
|  * Returns:  TRUE  Option understood | ||||
|  *          FALSE  Invalid log option | ||||
|  */ | ||||
| BOOL ntfs_log_parse_option(const char *option) | ||||
| { | ||||
| 	if (strcmp(option, "--log-debug") == 0) { | ||||
| 		ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG); | ||||
| 		return TRUE; | ||||
| 	} else if (strcmp(option, "--log-verbose") == 0) { | ||||
| 		ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); | ||||
| 		return TRUE; | ||||
| 	} else if (strcmp(option, "--log-quiet") == 0) { | ||||
| 		ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); | ||||
| 		return TRUE; | ||||
| 	} else if (strcmp(option, "--log-trace") == 0) { | ||||
| 		ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); | ||||
| 		return TRUE; | ||||
| 	} else if ((strcmp(option, "--log-colour") == 0) || | ||||
| 		   (strcmp(option, "--log-color") == 0)) { | ||||
| 		ntfs_log_set_flags(NTFS_LOG_FLAG_COLOUR); | ||||
| 		return TRUE; | ||||
| 	} | ||||
| 
 | ||||
| 	ntfs_log_debug("Unknown logging option '%s'\n", option); | ||||
| 	return FALSE; | ||||
| } | ||||
| 
 | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,36 @@ | |||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| 
 | ||||
| #ifdef HAVE_STDLIB_H | ||||
| #include <stdlib.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "misc.h" | ||||
| #include "logging.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_calloc | ||||
|  *  | ||||
|  * Return a pointer to the allocated memory or NULL if the request fails. | ||||
|  */ | ||||
| void *ntfs_calloc(size_t size) | ||||
| { | ||||
| 	void *p; | ||||
| 	 | ||||
| 	p = calloc(1, size); | ||||
| 	if (!p) | ||||
| 		ntfs_log_perror("Failed to calloc %lld bytes", (long long)size); | ||||
| 	return p; | ||||
| } | ||||
| 
 | ||||
| void *ntfs_malloc(size_t size) | ||||
| { | ||||
| 	void *p; | ||||
| 	 | ||||
| 	p = malloc(size); | ||||
| 	if (!p) | ||||
| 		ntfs_log_perror("Failed to malloc %lld bytes", (long long)size); | ||||
| 	return p; | ||||
| } | ||||
| 
 | ||||
|  | @ -0,0 +1,222 @@ | |||
| /**
 | ||||
|  * mst.c - Multi sector fixup handling code. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2000-2004 Anton Altaparmakov | ||||
|  * Copyright (c)      2006 Szabolcs Szakacsits | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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_ERRNO_H | ||||
| #include <errno.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "mst.h" | ||||
| #include "logging.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data | ||||
|  * @b:		pointer to the data to deprotect | ||||
|  * @size:	size in bytes of @b | ||||
|  * | ||||
|  * Perform the necessary post read multi sector transfer fixups and detect the | ||||
|  * presence of incomplete multi sector transfers. - In that case, overwrite the | ||||
|  * magic of the ntfs record header being processed with "BAAD" (in memory only!) | ||||
|  * and abort processing. | ||||
|  * | ||||
|  * Return 0 on success and -1 on error, with errno set to the error code. The | ||||
|  * following error codes are defined: | ||||
|  *	EINVAL	Invalid arguments or invalid NTFS record in buffer @b. | ||||
|  *	EIO	Multi sector transfer error was detected. Magic of the NTFS | ||||
|  *		record in @b will have been set to "BAAD". | ||||
|  */ | ||||
| int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) | ||||
| { | ||||
| 	u16 usa_ofs, usa_count, usn; | ||||
| 	u16 *usa_pos, *data_pos; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering\n"); | ||||
| 
 | ||||
| 	/* Setup the variables. */ | ||||
| 	usa_ofs = le16_to_cpu(b->usa_ofs); | ||||
| 	/* Decrement usa_count to get number of fixups. */ | ||||
| 	usa_count = le16_to_cpu(b->usa_count) - 1; | ||||
| 	/* Size and alignment checks. */ | ||||
| 	if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || | ||||
| 			(u32)(usa_ofs + (usa_count * 2)) > size || | ||||
| 			(size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	/* Position of usn in update sequence array. */ | ||||
| 	usa_pos = (u16*)b + usa_ofs/sizeof(u16); | ||||
| 	/*
 | ||||
| 	 * The update sequence number which has to be equal to each of the | ||||
| 	 * u16 values before they are fixed up. Note no need to care for | ||||
| 	 * endianness since we are comparing and moving data for on disk | ||||
| 	 * structures which means the data is consistent. - If it is | ||||
| 	 * consistency the wrong endianness it doesn't make any difference. | ||||
| 	 */ | ||||
| 	usn = *usa_pos; | ||||
| 	/*
 | ||||
| 	 * Position in protected data of first u16 that needs fixing up. | ||||
| 	 */ | ||||
| 	data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; | ||||
| 	/*
 | ||||
| 	 * Check for incomplete multi sector transfer(s). | ||||
| 	 */ | ||||
| 	while (usa_count--) { | ||||
| 		if (*data_pos != usn) { | ||||
| 			/*
 | ||||
| 			 * Incomplete multi sector transfer detected! )-: | ||||
| 			 * Set the magic to "BAAD" and return failure. | ||||
| 			 * Note that magic_BAAD is already converted to le32. | ||||
| 			 */ | ||||
| 			b->magic = magic_BAAD; | ||||
| 			errno = EIO; | ||||
| 			return -1; | ||||
| 		} | ||||
| 		data_pos += NTFS_BLOCK_SIZE/sizeof(u16); | ||||
| 	} | ||||
| 	/* Re-setup the variables. */ | ||||
| 	usa_count = le16_to_cpu(b->usa_count) - 1; | ||||
| 	data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; | ||||
| 	/* Fixup all sectors. */ | ||||
| 	while (usa_count--) { | ||||
| 		/*
 | ||||
| 		 * Increment position in usa and restore original data from | ||||
| 		 * the usa into the data buffer. | ||||
| 		 */ | ||||
| 		*data_pos = *(++usa_pos); | ||||
| 		/* Increment position in data as well. */ | ||||
| 		data_pos += NTFS_BLOCK_SIZE/sizeof(u16); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_mst_pre_write_fixup - apply multi sector transfer protection | ||||
|  * @b:		pointer to the data to protect | ||||
|  * @size:	size in bytes of @b | ||||
|  * | ||||
|  * Perform the necessary pre write multi sector transfer fixup on the data | ||||
|  * pointer to by @b of @size. | ||||
|  * | ||||
|  * Return 0 if fixups applied successfully or -1 if no fixups were performed | ||||
|  * due to errors. In that case errno i set to the error code (EINVAL). | ||||
|  * | ||||
|  * NOTE: We consider the absence / invalidity of an update sequence array to | ||||
|  * mean error. This means that you have to create a valid update sequence | ||||
|  * array header in the ntfs record before calling this function, otherwise it | ||||
|  * will fail (the header needs to contain the position of the update sequence | ||||
|  * array together with the number of elements in the array). You also need to | ||||
|  * initialise the update sequence number before calling this function | ||||
|  * otherwise a random word will be used (whatever was in the record at that | ||||
|  * position at that time). | ||||
|  */ | ||||
| int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) | ||||
| { | ||||
| 	u16 usa_ofs, usa_count, usn; | ||||
| 	u16 *usa_pos, *data_pos; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering\n"); | ||||
| 
 | ||||
| 	/* Sanity check + only fixup if it makes sense. */ | ||||
| 	if (!b || ntfs_is_baad_record(b->magic) || | ||||
| 			ntfs_is_hole_record(b->magic)) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	/* Setup the variables. */ | ||||
| 	usa_ofs = le16_to_cpu(b->usa_ofs); | ||||
| 	/* Decrement usa_count to get number of fixups. */ | ||||
| 	usa_count = le16_to_cpu(b->usa_count) - 1; | ||||
| 	/* Size and alignment checks. */ | ||||
| 	if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || | ||||
| 			(u32)(usa_ofs + (usa_count * 2)) > size || | ||||
| 			(size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	/* Position of usn in update sequence array. */ | ||||
| 	usa_pos = (u16*)((u8*)b + usa_ofs); | ||||
| 	/*
 | ||||
| 	 * Cyclically increment the update sequence number | ||||
| 	 * (skipping 0 and -1, i.e. 0xffff). | ||||
| 	 */ | ||||
| 	usn = le16_to_cpup(usa_pos) + 1; | ||||
| 	if (usn == 0xffff || !usn) | ||||
| 		usn = 1; | ||||
| 	usn = cpu_to_le16(usn); | ||||
| 	*usa_pos = usn; | ||||
| 	/* Position in data of first u16 that needs fixing up. */ | ||||
| 	data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; | ||||
| 	/* Fixup all sectors. */ | ||||
| 	while (usa_count--) { | ||||
| 		/*
 | ||||
| 		 * Increment the position in the usa and save the | ||||
| 		 * original data from the data buffer into the usa. | ||||
| 		 */ | ||||
| 		*(++usa_pos) = *data_pos; | ||||
| 		/* Apply fixup to data. */ | ||||
| 		*data_pos = usn; | ||||
| 		/* Increment position in data as well. */ | ||||
| 		data_pos += NTFS_BLOCK_SIZE/sizeof(u16); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_mst_post_write_fixup - deprotect multi sector transfer protected data | ||||
|  * @b:		pointer to the data to deprotect | ||||
|  * | ||||
|  * Perform the necessary post write multi sector transfer fixup, not checking | ||||
|  * for any errors, because we assume we have just used | ||||
|  * ntfs_mst_pre_write_fixup(), thus the data will be fine or we would never | ||||
|  * have gotten here. | ||||
|  */ | ||||
| void ntfs_mst_post_write_fixup(NTFS_RECORD *b) | ||||
| { | ||||
| 	u16 *usa_pos, *data_pos; | ||||
| 
 | ||||
| 	u16 usa_ofs = le16_to_cpu(b->usa_ofs); | ||||
| 	u16 usa_count = le16_to_cpu(b->usa_count) - 1; | ||||
| 
 | ||||
| 	ntfs_log_trace("Entering\n"); | ||||
| 
 | ||||
| 	/* Position of usn in update sequence array. */ | ||||
| 	usa_pos = (u16*)b + usa_ofs/sizeof(u16); | ||||
| 
 | ||||
| 	/* Position in protected data of first u16 that needs fixing up. */ | ||||
| 	data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; | ||||
| 
 | ||||
| 	/* Fixup all sectors. */ | ||||
| 	while (usa_count--) { | ||||
| 		/*
 | ||||
| 		 * Increment position in usa and restore original data from | ||||
| 		 * the usa into the data buffer. | ||||
| 		 */ | ||||
| 		*data_pos = *(++usa_pos); | ||||
| 
 | ||||
| 		/* Increment position in data as well. */ | ||||
| 		data_pos += NTFS_BLOCK_SIZE/sizeof(u16); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,340 @@ | |||
| /**
 | ||||
|  * security.c - Handling security/ACLs in NTFS.  Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2004 Anton Altaparmakov | ||||
|  * Copyright (c) 2005-2006 Szabolcs Szakacsits | ||||
|  * Copyright (c) 2006 Yura Pakhuchiy | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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 <stdio.h> | ||||
| #endif | ||||
| #ifdef HAVE_STDLIB_H | ||||
| #include <stdlib.h> | ||||
| #endif | ||||
| #ifdef HAVE_STRING_H | ||||
| #include <string.h> | ||||
| #endif | ||||
| #ifdef HAVE_ERRNO_H | ||||
| #include <errno.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "layout.h" | ||||
| #include "attrib.h" | ||||
| #include "security.h" | ||||
| #include "misc.h" | ||||
| 
 | ||||
| /*
 | ||||
|  * The zero GUID. | ||||
|  */ | ||||
| static const GUID __zero_guid = { const_cpu_to_le32(0), const_cpu_to_le16(0), | ||||
| 		const_cpu_to_le16(0), { 0, 0, 0, 0, 0, 0, 0, 0 } }; | ||||
| const GUID *const zero_guid = &__zero_guid; | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_guid_is_zero - check if a GUID is zero | ||||
|  * @guid:	[IN] guid to check | ||||
|  * | ||||
|  * Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID | ||||
|  * and FALSE otherwise. | ||||
|  */ | ||||
| BOOL ntfs_guid_is_zero(const GUID *guid) | ||||
| { | ||||
| 	return (memcmp(guid, zero_guid, sizeof(*zero_guid))); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_guid_to_mbs - convert a GUID to a multi byte string | ||||
|  * @guid:	[IN]  guid to convert | ||||
|  * @guid_str:	[OUT] string in which to return the GUID (optional) | ||||
|  * | ||||
|  * Convert the GUID pointed to by @guid to a multi byte string of the form | ||||
|  * "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX".  Therefore, @guid_str (if not NULL) | ||||
|  * needs to be able to store at least 37 bytes. | ||||
|  * | ||||
|  * If @guid_str is not NULL it will contain the converted GUID on return.  If | ||||
|  * it is NULL a string will be allocated and this will be returned.  The caller | ||||
|  * is responsible for free()ing the string in that case. | ||||
|  * | ||||
|  * On success return the converted string and on failure return NULL with errno | ||||
|  * set to the error code. | ||||
|  */ | ||||
| char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str) | ||||
| { | ||||
| 	char *_guid_str; | ||||
| 	int res; | ||||
| 
 | ||||
| 	if (!guid) { | ||||
| 		errno = EINVAL; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	_guid_str = guid_str; | ||||
| 	if (!_guid_str) { | ||||
| 		_guid_str = ntfs_malloc(37); | ||||
| 		if (!_guid_str) | ||||
| 			return _guid_str; | ||||
| 	} | ||||
| 	res = snprintf(_guid_str, 37, | ||||
| 			"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", | ||||
| 			(unsigned int)le32_to_cpu(guid->data1), | ||||
| 			le16_to_cpu(guid->data2), le16_to_cpu(guid->data3), | ||||
| 			guid->data4[0], guid->data4[1], | ||||
| 			guid->data4[2], guid->data4[3], guid->data4[4], | ||||
| 			guid->data4[5], guid->data4[6], guid->data4[7]); | ||||
| 	if (res == 36) | ||||
| 		return _guid_str; | ||||
| 	if (!guid_str) | ||||
| 		free(_guid_str); | ||||
| 	errno = EINVAL; | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_sid_to_mbs_size - determine maximum size for the string of a SID | ||||
|  * @sid:	[IN]  SID for which to determine the maximum string size | ||||
|  * | ||||
|  * Determine the maximum multi byte string size in bytes which is needed to | ||||
|  * store the standard textual representation of the SID pointed to by @sid. | ||||
|  * See ntfs_sid_to_mbs(), below. | ||||
|  * | ||||
|  * On success return the maximum number of bytes needed to store the multi byte | ||||
|  * string and on failure return -1 with errno set to the error code. | ||||
|  */ | ||||
| int ntfs_sid_to_mbs_size(const SID *sid) | ||||
| { | ||||
| 	int size, i; | ||||
| 
 | ||||
| 	if (!ntfs_sid_is_valid(sid)) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	/* Start with "S-". */ | ||||
| 	size = 2; | ||||
| 	/*
 | ||||
| 	 * Add the SID_REVISION.  Hopefully the compiler will optimize this | ||||
| 	 * away as SID_REVISION is a constant. | ||||
| 	 */ | ||||
| 	for (i = SID_REVISION; i > 0; i /= 10) | ||||
| 		size++; | ||||
| 	/* Add the "-". */ | ||||
| 	size++; | ||||
| 	/*
 | ||||
| 	 * Add the identifier authority.  If it needs to be in decimal, the | ||||
| 	 * maximum is 2^32-1 = 4294967295 = 10 characters.  If it needs to be | ||||
| 	 * in hexadecimal, then maximum is 0x665544332211 = 14 characters. | ||||
| 	 */ | ||||
| 	if (!sid->identifier_authority.high_part) | ||||
| 		size += 10; | ||||
| 	else | ||||
| 		size += 14; | ||||
| 	/*
 | ||||
| 	 * Finally, add the sub authorities.  For each we have a "-" followed | ||||
| 	 * by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters. | ||||
| 	 */ | ||||
| 	size += (1 + 10) * sid->sub_authority_count; | ||||
| 	/* We need the zero byte at the end, too. */ | ||||
| 	size++; | ||||
| 	return size * sizeof(char); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_sid_to_mbs - convert a SID to a multi byte string | ||||
|  * @sid:		[IN]  SID to convert | ||||
|  * @sid_str:		[OUT] string in which to return the SID (optional) | ||||
|  * @sid_str_size:	[IN]  size in bytes of @sid_str | ||||
|  * | ||||
|  * Convert the SID pointed to by @sid to its standard textual representation. | ||||
|  * @sid_str (if not NULL) needs to be able to store at least | ||||
|  * ntfs_sid_to_mbs_size() bytes.  @sid_str_size is the size in bytes of | ||||
|  * @sid_str if @sid_str is not NULL. | ||||
|  * | ||||
|  * The standard textual representation of the SID is of the form: | ||||
|  *	S-R-I-S-S... | ||||
|  * Where: | ||||
|  *    - The first "S" is the literal character 'S' identifying the following | ||||
|  *	digits as a SID. | ||||
|  *    - R is the revision level of the SID expressed as a sequence of digits | ||||
|  *	in decimal. | ||||
|  *    - I is the 48-bit identifier_authority, expressed as digits in decimal, | ||||
|  *	if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32. | ||||
|  *    - S... is one or more sub_authority values, expressed as digits in | ||||
|  *	decimal. | ||||
|  * | ||||
|  * If @sid_str is not NULL it will contain the converted SUID on return.  If it | ||||
|  * is NULL a string will be allocated and this will be returned.  The caller is | ||||
|  * responsible for free()ing the string in that case. | ||||
|  * | ||||
|  * On success return the converted string and on failure return NULL with errno | ||||
|  * set to the error code. | ||||
|  */ | ||||
| char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size) | ||||
| { | ||||
| 	u64 u; | ||||
| 	char *s; | ||||
| 	int i, j, cnt; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will | ||||
| 	 * check @sid, too.  8 is the minimum SID string size. | ||||
| 	 */ | ||||
| 	if (sid_str && (sid_str_size < 8 || !ntfs_sid_is_valid(sid))) { | ||||
| 		errno = EINVAL; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	/* Allocate string if not provided. */ | ||||
| 	if (!sid_str) { | ||||
| 		cnt = ntfs_sid_to_mbs_size(sid); | ||||
| 		if (cnt < 0) | ||||
| 			return NULL; | ||||
| 		s = ntfs_malloc(cnt); | ||||
| 		if (!s) | ||||
| 			return s; | ||||
| 		sid_str = s; | ||||
| 		/* So we know we allocated it. */ | ||||
| 		sid_str_size = 0; | ||||
| 	} else { | ||||
| 		s = sid_str; | ||||
| 		cnt = sid_str_size; | ||||
| 	} | ||||
| 	/* Start with "S-R-". */ | ||||
| 	i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision); | ||||
| 	if (i < 0 || i >= cnt) | ||||
| 		goto err_out; | ||||
| 	s += i; | ||||
| 	cnt -= i; | ||||
| 	/* Add the identifier authority. */ | ||||
| 	for (u = i = 0, j = 40; i < 6; i++, j -= 8) | ||||
| 		u += (u64)sid->identifier_authority.value[i] << j; | ||||
| 	if (!sid->identifier_authority.high_part) | ||||
| 		i = snprintf(s, cnt, "%lu", (unsigned long)u); | ||||
| 	else | ||||
| 		i = snprintf(s, cnt, "0x%llx", (unsigned long long)u); | ||||
| 	if (i < 0 || i >= cnt) | ||||
| 		goto err_out; | ||||
| 	s += i; | ||||
| 	cnt -= i; | ||||
| 	/* Finally, add the sub authorities. */ | ||||
| 	for (j = 0; j < sid->sub_authority_count; j++) { | ||||
| 		i = snprintf(s, cnt, "-%u", (unsigned int) | ||||
| 				le32_to_cpu(sid->sub_authority[j])); | ||||
| 		if (i < 0 || i >= cnt) | ||||
| 			goto err_out; | ||||
| 		s += i; | ||||
| 		cnt -= i; | ||||
| 	} | ||||
| 	return sid_str; | ||||
| err_out: | ||||
| 	if (i >= cnt) | ||||
| 		i = EMSGSIZE; | ||||
| 	else | ||||
| 		i = errno; | ||||
| 	if (!sid_str_size) | ||||
| 		free(sid_str); | ||||
| 	errno = i; | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_generate_guid - generatates a random current guid. | ||||
|  * @guid:	[OUT]   pointer to a GUID struct to hold the generated guid. | ||||
|  * | ||||
|  * perhaps not a very good random number generator though... | ||||
|  */ | ||||
| void ntfs_generate_guid(GUID *guid) | ||||
| { | ||||
| 	unsigned int i; | ||||
| 	u8 *p = (u8 *)guid; | ||||
| 
 | ||||
| 	for (i = 0; i < sizeof(GUID); i++) { | ||||
| 		p[i] = (u8)(random() & 0xFF); | ||||
| 		if (i == 7) | ||||
| 			p[7] = (p[7] & 0x0F) | 0x40; | ||||
| 		if (i == 8) | ||||
| 			p[8] = (p[8] & 0x3F) | 0x80; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int ntfs_sd_add_everyone(ntfs_inode *ni) | ||||
| { | ||||
| 	SECURITY_DESCRIPTOR_ATTR *sd; | ||||
| 	ACL *acl; | ||||
| 	ACCESS_ALLOWED_ACE *ace; | ||||
| 	SID *sid; | ||||
| 	int ret, sd_len; | ||||
| 	 | ||||
| 	/* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */ | ||||
| 	/*
 | ||||
| 	 * Calculate security descriptor length. We have 2 sub-authorities in | ||||
| 	 * owner and group SIDs, but structure SID contain only one, so add | ||||
| 	 * 4 bytes to every SID. | ||||
| 	 */ | ||||
| 	sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) + | ||||
| 		sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE);  | ||||
| 	sd = ntfs_calloc(sd_len); | ||||
| 	if (!sd) | ||||
| 		return -1; | ||||
| 	 | ||||
| 	sd->revision = 1; | ||||
| 	sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE; | ||||
| 	 | ||||
| 	sid = (SID *)((u8 *)sd + sizeof(SECURITY_DESCRIPTOR_ATTR)); | ||||
| 	sid->revision = 1; | ||||
| 	sid->sub_authority_count = 2; | ||||
| 	sid->sub_authority[0] = cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); | ||||
| 	sid->sub_authority[1] = cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); | ||||
| 	sid->identifier_authority.value[5] = 5; | ||||
| 	sd->owner = cpu_to_le32((u8 *)sid - (u8 *)sd); | ||||
| 	 | ||||
| 	sid = (SID *)((u8 *)sid + sizeof(SID) + 4);  | ||||
| 	sid->revision = 1; | ||||
| 	sid->sub_authority_count = 2; | ||||
| 	sid->sub_authority[0] = cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); | ||||
| 	sid->sub_authority[1] = cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); | ||||
| 	sid->identifier_authority.value[5] = 5; | ||||
| 	sd->group = cpu_to_le32((u8 *)sid - (u8 *)sd); | ||||
| 	 | ||||
| 	acl = (ACL *)((u8 *)sid + sizeof(SID) + 4); | ||||
| 	acl->revision = 2; | ||||
| 	acl->size = cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE)); | ||||
| 	acl->ace_count = cpu_to_le16(1); | ||||
| 	sd->dacl = cpu_to_le32((u8 *)acl - (u8 *)sd); | ||||
| 	 | ||||
| 	ace = (ACCESS_ALLOWED_ACE *)((u8 *)acl + sizeof(ACL)); | ||||
| 	ace->type = ACCESS_ALLOWED_ACE_TYPE; | ||||
| 	ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; | ||||
| 	ace->size = cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE)); | ||||
| 	ace->mask = cpu_to_le32(0x1f01ff); /* FIXME */ | ||||
| 	ace->sid.revision = 1; | ||||
| 	ace->sid.sub_authority_count = 1; | ||||
| 	ace->sid.sub_authority[0] = 0; | ||||
| 	ace->sid.identifier_authority.value[5] = 1; | ||||
| 	 | ||||
| 	ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8 *)sd, | ||||
| 			    sd_len); | ||||
| 	if (ret) | ||||
| 		ntfs_log_perror("Failed to add SECURITY_DESCRIPTOR\n"); | ||||
| 	 | ||||
| 	free(sd); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
|  | @ -0,0 +1,757 @@ | |||
| /**
 | ||||
|  * unistr.c - Unicode string handling. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2000-2004 Anton Altaparmakov | ||||
|  * Copyright (c) 2002-2006 Szabolcs Szakacsits | ||||
|  * | ||||
|  * 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 NTFS-3G | ||||
|  * 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 <stdio.h> | ||||
| #endif | ||||
| #ifdef HAVE_STDLIB_H | ||||
| #include <stdlib.h> | ||||
| #endif | ||||
| #ifdef HAVE_WCHAR_H | ||||
| #include <wchar.h> | ||||
| #endif | ||||
| #ifdef HAVE_STRING_H | ||||
| #include <string.h> | ||||
| #endif | ||||
| #ifdef HAVE_ERRNO_H | ||||
| #include <errno.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "attrib.h" | ||||
| #include "types.h" | ||||
| #include "unistr.h" | ||||
| #include "debug.h" | ||||
| #include "logging.h" | ||||
| #include "misc.h" | ||||
| 
 | ||||
| /*
 | ||||
|  * IMPORTANT | ||||
|  * ========= | ||||
|  * | ||||
|  * All these routines assume that the Unicode characters are in little endian | ||||
|  * encoding inside the strings!!! | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
|  * This is used by the name collation functions to quickly determine what | ||||
|  * characters are (in)valid. | ||||
|  */ | ||||
| #if 0 | ||||
| static const u8 legal_ansi_char_array[0x40] = { | ||||
| 	0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, | ||||
| 	0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, | ||||
| 
 | ||||
| 	0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, | ||||
| 	0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, | ||||
| 
 | ||||
| 	0x17, 0x07, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, | ||||
| 	0x17, 0x17, 0x18, 0x16, 0x16, 0x17, 0x07, 0x00, | ||||
| 
 | ||||
| 	0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, | ||||
| 	0x17, 0x17, 0x04, 0x16, 0x18, 0x16, 0x18, 0x18, | ||||
| }; | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_names_are_equal - compare two Unicode names for equality | ||||
|  * @s1:			name to compare to @s2 | ||||
|  * @s1_len:		length in Unicode characters of @s1 | ||||
|  * @s2:			name to compare to @s1 | ||||
|  * @s2_len:		length in Unicode characters of @s2 | ||||
|  * @ic:			ignore case bool | ||||
|  * @upcase:		upcase table (only if @ic == IGNORE_CASE) | ||||
|  * @upcase_size:	length in Unicode characters of @upcase (if present) | ||||
|  * | ||||
|  * Compare the names @s1 and @s2 and return TRUE (1) if the names are | ||||
|  * identical, or FALSE (0) if they are not identical. If @ic is IGNORE_CASE, | ||||
|  * the @upcase table is used to perform a case insensitive comparison. | ||||
|  */ | ||||
| BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len, | ||||
| 		const ntfschar *s2, size_t s2_len, | ||||
| 		const IGNORE_CASE_BOOL ic, | ||||
| 		const ntfschar *upcase, const u32 upcase_size) | ||||
| { | ||||
| 	if (s1_len != s2_len) | ||||
| 		return FALSE; | ||||
| 	if (!s1_len) | ||||
| 		return TRUE; | ||||
| 	if (ic == CASE_SENSITIVE) | ||||
| 		return ntfs_ucsncmp(s1, s2, s1_len) ? FALSE: TRUE; | ||||
| 	return ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size) ? FALSE: | ||||
| 								       TRUE; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_names_collate - collate two Unicode names | ||||
|  * @name1:	first Unicode name to compare | ||||
|  * @name1_len:	length of first Unicode name to compare | ||||
|  * @name2:	second Unicode name to compare | ||||
|  * @name2_len:	length of second Unicode name to compare | ||||
|  * @err_val:	if @name1 contains an invalid character return this value | ||||
|  * @ic:		either CASE_SENSITIVE or IGNORE_CASE | ||||
|  * @upcase:	upcase table (ignored if @ic is CASE_SENSITIVE) | ||||
|  * @upcase_len:	upcase table size (ignored if @ic is CASE_SENSITIVE) | ||||
|  * | ||||
|  * ntfs_names_collate() collates two Unicode names and returns: | ||||
|  * | ||||
|  *  -1 if the first name collates before the second one, | ||||
|  *   0 if the names match, | ||||
|  *   1 if the second name collates before the first one, or | ||||
|  * @err_val if an invalid character is found in @name1 during the comparison. | ||||
|  * | ||||
|  * The following characters are considered invalid: '"', '*', '<', '>' and '?'. | ||||
|  */ | ||||
| int ntfs_names_collate(const ntfschar *name1, const u32 name1_len, | ||||
| 		const ntfschar *name2, const u32 name2_len, | ||||
| 		const int err_val __attribute__((unused)), | ||||
| 		const IGNORE_CASE_BOOL ic, const ntfschar *upcase, | ||||
| 		const u32 upcase_len) | ||||
| { | ||||
| 	u32 cnt; | ||||
| 	ntfschar c1, c2; | ||||
| 
 | ||||
| #ifdef DEBUG | ||||
| 	if (!name1 || !name2 || (ic && (!upcase || !upcase_len))) { | ||||
| 		ntfs_log_debug("ntfs_names_collate received NULL pointer!\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
| #endif | ||||
| 	for (cnt = 0; cnt < min(name1_len, name2_len); ++cnt) { | ||||
| 		c1 = le16_to_cpu(*name1); | ||||
| 		name1++; | ||||
| 		c2 = le16_to_cpu(*name2); | ||||
| 		name2++; | ||||
| 		if (ic) { | ||||
| 			if (c1 < upcase_len) | ||||
| 				c1 = le16_to_cpu(upcase[c1]); | ||||
| 			if (c2 < upcase_len) | ||||
| 				c2 = le16_to_cpu(upcase[c2]); | ||||
| 		} | ||||
| #if 0 | ||||
| 		if (c1 < 64 && legal_ansi_char_array[c1] & 8) | ||||
| 			return err_val; | ||||
| #endif | ||||
| 		if (c1 < c2) | ||||
| 			return -1; | ||||
| 		if (c1 > c2) | ||||
| 			return 1; | ||||
| 	} | ||||
| 	if (name1_len < name2_len) | ||||
| 		return -1; | ||||
| 	if (name1_len == name2_len) | ||||
| 		return 0; | ||||
| 	/* name1_len > name2_len */ | ||||
| #if 0 | ||||
| 	c1 = le16_to_cpu(*name1); | ||||
| 	if (c1 < 64 && legal_ansi_char_array[c1] & 8) | ||||
| 		return err_val; | ||||
| #endif | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_ucsncmp - compare two little endian Unicode strings | ||||
|  * @s1:		first string | ||||
|  * @s2:		second string | ||||
|  * @n:		maximum unicode characters to compare | ||||
|  * | ||||
|  * Compare the first @n characters of the Unicode strings @s1 and @s2, | ||||
|  * The strings in little endian format and appropriate le16_to_cpu() | ||||
|  * conversion is performed on non-little endian machines. | ||||
|  * | ||||
|  * The function returns an integer less than, equal to, or greater than zero | ||||
|  * if @s1 (or the first @n Unicode characters thereof) is found, respectively, | ||||
|  * to be less than, to match, or be greater than @s2. | ||||
|  */ | ||||
| int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n) | ||||
| { | ||||
| 	ntfschar c1, c2; | ||||
| 	size_t i; | ||||
| 
 | ||||
| #ifdef DEBUG | ||||
| 	if (!s1 || !s2) { | ||||
| 		ntfs_log_debug("ntfs_wcsncmp() received NULL pointer!\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
| #endif | ||||
| 	for (i = 0; i < n; ++i) { | ||||
| 		c1 = le16_to_cpu(s1[i]); | ||||
| 		c2 = le16_to_cpu(s2[i]); | ||||
| 		if (c1 < c2) | ||||
| 			return -1; | ||||
| 		if (c1 > c2) | ||||
| 			return 1; | ||||
| 		if (!c1) | ||||
| 			break; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_ucsncasecmp - compare two little endian Unicode strings, ignoring case | ||||
|  * @s1:			first string | ||||
|  * @s2:			second string | ||||
|  * @n:			maximum unicode characters to compare | ||||
|  * @upcase:		upcase table | ||||
|  * @upcase_size:	upcase table size in Unicode characters | ||||
|  * | ||||
|  * Compare the first @n characters of the Unicode strings @s1 and @s2, | ||||
|  * ignoring case. The strings in little endian format and appropriate | ||||
|  * le16_to_cpu() conversion is performed on non-little endian machines. | ||||
|  * | ||||
|  * Each character is uppercased using the @upcase table before the comparison. | ||||
|  * | ||||
|  * The function returns an integer less than, equal to, or greater than zero | ||||
|  * if @s1 (or the first @n Unicode characters thereof) is found, respectively, | ||||
|  * to be less than, to match, or be greater than @s2. | ||||
|  */ | ||||
| int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, | ||||
| 		const ntfschar *upcase, const u32 upcase_size) | ||||
| { | ||||
| 	ntfschar c1, c2; | ||||
| 	size_t i; | ||||
| 
 | ||||
| #ifdef DEBUG | ||||
| 	if (!s1 || !s2 || !upcase) { | ||||
| 		ntfs_log_debug("ntfs_wcsncasecmp() received NULL pointer!\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
| #endif | ||||
| 	for (i = 0; i < n; ++i) { | ||||
| 		if ((c1 = le16_to_cpu(s1[i])) < upcase_size) | ||||
| 			c1 = le16_to_cpu(upcase[c1]); | ||||
| 		if ((c2 = le16_to_cpu(s2[i])) < upcase_size) | ||||
| 			c2 = le16_to_cpu(upcase[c2]); | ||||
| 		if (c1 < c2) | ||||
| 			return -1; | ||||
| 		if (c1 > c2) | ||||
| 			return 1; | ||||
| 		if (!c1) | ||||
| 			break; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_ucsnlen - determine the length of a little endian Unicode string | ||||
|  * @s:		pointer to Unicode string | ||||
|  * @maxlen:	maximum length of string @s | ||||
|  * | ||||
|  * Return the number of Unicode characters in the little endian Unicode | ||||
|  * string @s up to a maximum of maxlen Unicode characters, not including | ||||
|  * the terminating (ntfschar)'\0'. If there is no (ntfschar)'\0' between @s | ||||
|  * and @s + @maxlen, @maxlen is returned. | ||||
|  * | ||||
|  * This function never looks beyond @s + @maxlen. | ||||
|  */ | ||||
| u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen) | ||||
| { | ||||
| 	u32 i; | ||||
| 
 | ||||
| 	for (i = 0; i < maxlen; i++) { | ||||
| 		if (!le16_to_cpu(s[i])) | ||||
| 			break; | ||||
| 	} | ||||
| 	return i; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_ucsndup - duplicate little endian Unicode string | ||||
|  * @s:		pointer to Unicode string | ||||
|  * @maxlen:	maximum length of string @s | ||||
|  * | ||||
|  * Return a pointer to a new little endian Unicode string which is a duplicate | ||||
|  * of the string s.  Memory for the new string is obtained with ntfs_malloc(3), | ||||
|  * and can be freed with free(3). | ||||
|  * | ||||
|  * A maximum of @maxlen Unicode characters are copied and a terminating | ||||
|  * (ntfschar)'\0' little endian Unicode character is added. | ||||
|  * | ||||
|  * This function never looks beyond @s + @maxlen. | ||||
|  * | ||||
|  * Return a pointer to the new little endian Unicode string on success and NULL | ||||
|  * on failure with errno set to the error code. | ||||
|  */ | ||||
| ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen) | ||||
| { | ||||
| 	ntfschar *dst; | ||||
| 	u32 len; | ||||
| 
 | ||||
| 	len = ntfs_ucsnlen(s, maxlen); | ||||
| 	dst = ntfs_malloc((len + 1) * sizeof(ntfschar)); | ||||
| 	if (dst) { | ||||
| 		memcpy(dst, s, len * sizeof(ntfschar)); | ||||
| 		dst[len] = cpu_to_le16(L'\0'); | ||||
| 	} | ||||
| 	return dst; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_name_upcase - Map an Unicode name to its uppercase equivalent | ||||
|  * @name: | ||||
|  * @name_len: | ||||
|  * @upcase: | ||||
|  * @upcase_len: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| void ntfs_name_upcase(ntfschar *name, u32 name_len, const ntfschar *upcase, | ||||
| 		const u32 upcase_len) | ||||
| { | ||||
| 	u32 i; | ||||
| 	ntfschar u; | ||||
| 
 | ||||
| 	for (i = 0; i < name_len; i++) | ||||
| 		if ((u = le16_to_cpu(name[i])) < upcase_len) | ||||
| 			name[i] = upcase[u]; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_file_value_upcase - Convert a filename to upper case | ||||
|  * @file_name_attr: | ||||
|  * @upcase: | ||||
|  * @upcase_len: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr, | ||||
| 		const ntfschar *upcase, const u32 upcase_len) | ||||
| { | ||||
| 	ntfs_name_upcase((ntfschar*)&file_name_attr->file_name, | ||||
| 			file_name_attr->file_name_length, upcase, upcase_len); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_file_values_compare - Which of two filenames should be listed first | ||||
|  * @file_name_attr1: | ||||
|  * @file_name_attr2: | ||||
|  * @err_val: | ||||
|  * @ic: | ||||
|  * @upcase: | ||||
|  * @upcase_len: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1, | ||||
| 		const FILE_NAME_ATTR *file_name_attr2, | ||||
| 		const int err_val, const IGNORE_CASE_BOOL ic, | ||||
| 		const ntfschar *upcase, const u32 upcase_len) | ||||
| { | ||||
| 	return ntfs_names_collate((ntfschar*)&file_name_attr1->file_name, | ||||
| 			file_name_attr1->file_name_length, | ||||
| 			(ntfschar*)&file_name_attr2->file_name, | ||||
| 			file_name_attr2->file_name_length, | ||||
| 			err_val, ic, upcase, upcase_len); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_ucstombs - convert a little endian Unicode string to a multibyte string | ||||
|  * @ins:	input Unicode string buffer | ||||
|  * @ins_len:	length of input string in Unicode characters | ||||
|  * @outs:	on return contains the (allocated) output multibyte string | ||||
|  * @outs_len:	length of output buffer in bytes | ||||
|  * | ||||
|  * Convert the input little endian, 2-byte Unicode string @ins, of length | ||||
|  * @ins_len into the multibyte string format dictated by the current locale. | ||||
|  * | ||||
|  * If *@outs is NULL, the function allocates the string and the caller is | ||||
|  * responsible for calling free(*@outs); when finished with it. | ||||
|  * | ||||
|  * On success the function returns the number of bytes written to the output | ||||
|  * string *@outs (>= 0), not counting the terminating NULL byte. If the output | ||||
|  * string buffer was allocated, *@outs is set to it. | ||||
|  * | ||||
|  * On error, -1 is returned, and errno is set to the error code. The following | ||||
|  * error codes can be expected: | ||||
|  *	EINVAL		Invalid arguments (e.g. @ins or @outs is NULL). | ||||
|  *	EILSEQ		The input string cannot be represented as a multibyte | ||||
|  *			sequence according to the current locale. | ||||
|  *	ENAMETOOLONG	Destination buffer is too small for input string. | ||||
|  *	ENOMEM		Not enough memory to allocate destination buffer. | ||||
|  */ | ||||
| int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, | ||||
| 		int outs_len) | ||||
| { | ||||
| 	char *mbs; | ||||
| 	wchar_t wc; | ||||
| 	int i, o, mbs_len; | ||||
| 	int cnt = 0; | ||||
| #ifdef HAVE_MBSINIT | ||||
| 	mbstate_t mbstate; | ||||
| #endif | ||||
| 
 | ||||
| 	if (!ins || !outs) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	mbs = *outs; | ||||
| 	mbs_len = outs_len; | ||||
| 	if (mbs && !mbs_len) { | ||||
| 		errno = ENAMETOOLONG; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (!mbs) { | ||||
| 		mbs_len = (ins_len + 1) * MB_CUR_MAX; | ||||
| 		mbs = ntfs_malloc(mbs_len); | ||||
| 		if (!mbs) | ||||
| 			return -1; | ||||
| 	} | ||||
| #ifdef HAVE_MBSINIT | ||||
| 	memset(&mbstate, 0, sizeof(mbstate)); | ||||
| #else | ||||
| 	wctomb(NULL, 0); | ||||
| #endif | ||||
| 	for (i = o = 0; i < ins_len; i++) { | ||||
| 		/* Reallocate memory if necessary or abort. */ | ||||
| 		if ((int)(o + MB_CUR_MAX) > mbs_len) { | ||||
| 			char *tc; | ||||
| 			if (mbs == *outs) { | ||||
| 				errno = ENAMETOOLONG; | ||||
| 				return -1; | ||||
| 			} | ||||
| 			tc = ntfs_malloc((mbs_len + 64) & ~63); | ||||
| 			if (!tc) | ||||
| 				goto err_out; | ||||
| 			memcpy(tc, mbs, mbs_len); | ||||
| 			mbs_len = (mbs_len + 64) & ~63; | ||||
| 			free(mbs); | ||||
| 			mbs = tc; | ||||
| 		} | ||||
| 		/* Convert the LE Unicode character to a CPU wide character. */ | ||||
| 		wc = (wchar_t)le16_to_cpu(ins[i]); | ||||
| 		if (!wc) | ||||
| 			break; | ||||
| 		/* Convert the CPU endian wide character to multibyte. */ | ||||
| #ifdef HAVE_MBSINIT | ||||
| 		cnt = wcrtomb(mbs + o, wc, &mbstate); | ||||
| #else | ||||
| 		cnt = wctomb(mbs + o, wc); | ||||
| #endif | ||||
| 		if (cnt == -1) | ||||
| 			goto err_out; | ||||
| 		if (cnt <= 0) { | ||||
| 			ntfs_log_debug("Eeek. cnt <= 0, cnt = %i\n", cnt); | ||||
| 			errno = EINVAL; | ||||
| 			goto err_out; | ||||
| 		} | ||||
| 		o += cnt; | ||||
| 	} | ||||
| #ifdef HAVE_MBSINIT | ||||
| 	/* Make sure we are back in the initial state. */ | ||||
| 	if (!mbsinit(&mbstate)) { | ||||
| 		ntfs_log_debug("Eeek. mbstate not in initial state!\n"); | ||||
| 		errno = EILSEQ; | ||||
| 		goto err_out; | ||||
| 	} | ||||
| #endif | ||||
| 	/* Now write the NULL character. */ | ||||
| 	mbs[o] = '\0'; | ||||
| 	if (*outs != mbs) | ||||
| 		*outs = mbs; | ||||
| 	return o; | ||||
| err_out: | ||||
| 	if (mbs != *outs) { | ||||
| 		int eo = errno; | ||||
| 		free(mbs); | ||||
| 		errno = eo; | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_mbstoucs - convert a multibyte string to a little endian Unicode string | ||||
|  * @ins:	input multibyte string buffer | ||||
|  * @outs:	on return contains the (allocated) output Unicode string | ||||
|  * @outs_len:	length of output buffer in Unicode characters | ||||
|  * | ||||
|  * Convert the input multibyte string @ins, from the current locale into the | ||||
|  * corresponding little endian, 2-byte Unicode string. | ||||
|  * | ||||
|  * If *@outs is NULL, the function allocates the string and the caller is | ||||
|  * responsible for calling free(*@outs); when finished with it. | ||||
|  * | ||||
|  * On success the function returns the number of Unicode characters written to | ||||
|  * the output string *@outs (>= 0), not counting the terminating Unicode NULL | ||||
|  * character. If the output string buffer was allocated, *@outs is set to it. | ||||
|  * | ||||
|  * On error, -1 is returned, and errno is set to the error code. The following | ||||
|  * error codes can be expected: | ||||
|  *	EINVAL		Invalid arguments (e.g. @ins or @outs is NULL). | ||||
|  *	EILSEQ		The input string cannot be represented as a Unicode | ||||
|  *			string according to the current locale. | ||||
|  *	ENAMETOOLONG	Destination buffer is too small for input string. | ||||
|  *	ENOMEM		Not enough memory to allocate destination buffer. | ||||
|  */ | ||||
| int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len) | ||||
| { | ||||
| 	ntfschar *ucs; | ||||
| 	const char *s; | ||||
| 	wchar_t wc; | ||||
| 	int i, o, cnt, ins_len, ucs_len, ins_size; | ||||
| #ifdef HAVE_MBSINIT | ||||
| 	mbstate_t mbstate; | ||||
| #endif | ||||
| 
 | ||||
| 	if (!ins || !outs) { | ||||
| 		errno = EINVAL; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	ucs = *outs; | ||||
| 	ucs_len = outs_len; | ||||
| 	if (ucs && !ucs_len) { | ||||
| 		errno = ENAMETOOLONG; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	/* Determine the size of the multi-byte string in bytes. */ | ||||
| 	ins_size = strlen(ins); | ||||
| 	/* Determine the length of the multi-byte string. */ | ||||
| 	s = ins; | ||||
| #if defined(HAVE_MBSINIT) | ||||
| 	memset(&mbstate, 0, sizeof(mbstate)); | ||||
| 	ins_len = mbsrtowcs(NULL, (const char **)&s, 0, &mbstate); | ||||
| #ifdef __CYGWIN32__ | ||||
| 	if (!ins_len && *ins) { | ||||
| 		/* Older Cygwin had broken mbsrtowcs() implementation. */ | ||||
| 		ins_len = strlen(ins); | ||||
| 	} | ||||
| #endif | ||||
| #elif !defined(DJGPP) | ||||
| 	ins_len = mbstowcs(NULL, s, 0); | ||||
| #else | ||||
| 	/* Eeek!!! DJGPP has broken mbstowcs() implementation!!! */ | ||||
| 	ins_len = strlen(ins); | ||||
| #endif | ||||
| 	if (ins_len == -1) | ||||
| 		return ins_len; | ||||
| #ifdef HAVE_MBSINIT | ||||
| 	if ((s != ins) || !mbsinit(&mbstate)) { | ||||
| #else | ||||
| 	if (s != ins) { | ||||
| #endif | ||||
| 		errno = EILSEQ; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	/* Add the NULL terminator. */ | ||||
| 	ins_len++; | ||||
| 	if (!ucs) { | ||||
| 		ucs_len = ins_len; | ||||
| 		ucs = ntfs_malloc(ucs_len * sizeof(ntfschar)); | ||||
| 		if (!ucs) | ||||
| 			return -1; | ||||
| 	} | ||||
| #ifdef HAVE_MBSINIT | ||||
| 	memset(&mbstate, 0, sizeof(mbstate)); | ||||
| #else | ||||
| 	mbtowc(NULL, NULL, 0); | ||||
| #endif | ||||
| 	for (i = o = cnt = 0; i < ins_size; i += cnt, o++) { | ||||
| 		/* Reallocate memory if necessary or abort. */ | ||||
| 		if (o >= ucs_len) { | ||||
| 			ntfschar *tc; | ||||
| 			if (ucs == *outs) { | ||||
| 				errno = ENAMETOOLONG; | ||||
| 				return -1; | ||||
| 			} | ||||
| 			/*
 | ||||
| 			 * We will never get here but hey, it's only a bit of | ||||
| 			 * extra code... | ||||
| 			 */ | ||||
| 			ucs_len = (ucs_len * sizeof(ntfschar) + 64) & ~63; | ||||
| 			tc = (ntfschar*)realloc(ucs, ucs_len); | ||||
| 			if (!tc) | ||||
| 				goto err_out; | ||||
| 			ucs = tc; | ||||
| 			ucs_len /= sizeof(ntfschar); | ||||
| 		} | ||||
| 		/* Convert the multibyte character to a wide character. */ | ||||
| #ifdef HAVE_MBSINIT | ||||
| 		cnt = mbrtowc(&wc, ins + i, ins_size - i, &mbstate); | ||||
| #else | ||||
| 		cnt = mbtowc(&wc, ins + i, ins_size - i); | ||||
| #endif | ||||
| 		if (!cnt) | ||||
| 			break; | ||||
| 		if (cnt == -1) | ||||
| 			goto err_out; | ||||
| 		if (cnt < -1) { | ||||
| 			ntfs_log_trace("Eeek. cnt = %i\n", cnt); | ||||
| 			errno = EINVAL; | ||||
| 			goto err_out; | ||||
| 		} | ||||
| 		/* Make sure we are not overflowing the NTFS Unicode set. */ | ||||
| 		if ((unsigned long)wc >= (unsigned long)(1 << | ||||
| 				(8 * sizeof(ntfschar)))) { | ||||
| 			errno = EILSEQ; | ||||
| 			goto err_out; | ||||
| 		} | ||||
| 		/* Convert the CPU wide character to a LE Unicode character. */ | ||||
| 		ucs[o] = cpu_to_le16(wc); | ||||
| 	} | ||||
| #ifdef HAVE_MBSINIT | ||||
| 	/* Make sure we are back in the initial state. */ | ||||
| 	if (!mbsinit(&mbstate)) { | ||||
| 		ntfs_log_trace("Eeek. mbstate not in initial state!\n"); | ||||
| 		errno = EILSEQ; | ||||
| 		goto err_out; | ||||
| 	} | ||||
| #endif | ||||
| 	/* Now write the NULL character. */ | ||||
| 	ucs[o] = cpu_to_le16(L'\0'); | ||||
| 	if (*outs != ucs) | ||||
| 		*outs = ucs; | ||||
| 	return o; | ||||
| err_out: | ||||
| 	if (ucs != *outs) { | ||||
| 		int eo = errno; | ||||
| 		free(ucs); | ||||
| 		errno = eo; | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_upcase_table_build - build the default upcase table for NTFS | ||||
|  * @uc:		destination buffer where to store the built table | ||||
|  * @uc_len:	size of destination buffer in bytes | ||||
|  * | ||||
|  * ntfs_upcase_table_build() builds the default upcase table for NTFS and | ||||
|  * stores it in the caller supplied buffer @uc of size @uc_len. | ||||
|  * | ||||
|  * Note, @uc_len must be at least 128kiB in size or bad things will happen! | ||||
|  */ | ||||
| void ntfs_upcase_table_build(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((char*)uc, 0, uc_len); | ||||
| 	uc_len >>= 1; | ||||
| 	if (uc_len > 65536) | ||||
| 		uc_len = 65536; | ||||
| 	for (i = 0; (u32)i < uc_len; i++) | ||||
| 		uc[i] = 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] += 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]--; | ||||
| 	for (r = 0; uc_byte_table[r][0]; r++) | ||||
| 		uc[uc_byte_table[r][0]] = uc_byte_table[r][1]; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_str2ucs - convert a string to a valid NTFS file name | ||||
|  * @s:		input string | ||||
|  * @len:	length of output buffer in Unicode characters | ||||
|  * | ||||
|  * Convert the input @s string into the corresponding little endian, | ||||
|  * 2-byte Unicode string. The length of the converted string is less  | ||||
|  * or equal to the maximum length allowed by the NTFS format (255). | ||||
|  * | ||||
|  * If @s is NULL then return AT_UNNAMED. | ||||
|  * | ||||
|  * On success the function returns the Unicode string in an allocated  | ||||
|  * buffer and the caller is responsible to free it when it's not needed | ||||
|  * anymore. | ||||
|  * | ||||
|  * On error NULL is returned and errno is set to the error code. | ||||
|  */ | ||||
| ntfschar *ntfs_str2ucs(const char *s, int *len) | ||||
| { | ||||
| 	ntfschar *ucs = NULL; | ||||
| 
 | ||||
| 	if (s && ((*len = ntfs_mbstoucs(s, &ucs, 0)) == -1)) { | ||||
| 		ntfs_log_perror("Couldn't convert '%s' to Unicode", s); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (*len > NTFS_MAX_NAME_LEN) { | ||||
| 		free(ucs); | ||||
| 		errno = ENAMETOOLONG; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (!ucs || !*len) { | ||||
| 		ucs  = AT_UNNAMED; | ||||
| 		*len = 0; | ||||
| 	} | ||||
| 	return ucs; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_ucsfree - free memory allocated by ntfs_str2ucs() | ||||
|  * @ucs		input string to be freed | ||||
|  * | ||||
|  * Free memory at @ucs and which was allocated by ntfs_str2ucs. | ||||
|  * | ||||
|  * Return value: none. | ||||
|  */ | ||||
| void ntfs_ucsfree(ntfschar *ucs) | ||||
| { | ||||
| 	if (ucs && (ucs != AT_UNNAMED)) | ||||
| 		free(ucs); | ||||
| } | ||||
| 
 | ||||
|  | @ -0,0 +1,327 @@ | |||
| /**
 | ||||
|  * unix_io.c - Unix style disk io functions. Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2000-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 NTFS-3G | ||||
|  * 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_UNISTD_H | ||||
| #include <unistd.h> | ||||
| #endif | ||||
| #ifdef HAVE_STDLIB_H | ||||
| #include <stdlib.h> | ||||
| #endif | ||||
| #ifdef HAVE_STRING_H | ||||
| #include <string.h> | ||||
| #endif | ||||
| #ifdef HAVE_ERRNO_H | ||||
| #include <errno.h> | ||||
| #endif | ||||
| #ifdef HAVE_STDIO_H | ||||
| #include <stdio.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYS_TYPES_H | ||||
| #include <sys/types.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYS_STAT_H | ||||
| #include <sys/stat.h> | ||||
| #endif | ||||
| #ifdef HAVE_FCNTL_H | ||||
| #include <fcntl.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYS_IOCTL_H | ||||
| #include <sys/ioctl.h> | ||||
| #endif | ||||
| #ifdef HAVE_LINUX_FD_H | ||||
| #include <linux/fd.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "types.h" | ||||
| #include "mst.h" | ||||
| #include "debug.h" | ||||
| #include "device.h" | ||||
| #include "logging.h" | ||||
| #include "misc.h" | ||||
| 
 | ||||
| #define DEV_FD(dev)	(*(int *)dev->d_private) | ||||
| 
 | ||||
| /* Define to nothing if not present on this system. */ | ||||
| #ifndef O_EXCL | ||||
| #	define O_EXCL 0 | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_unix_io_open - Open a device and lock it exclusively | ||||
|  * @dev: | ||||
|  * @flags: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| static int ntfs_device_unix_io_open(struct ntfs_device *dev, int flags) | ||||
| { | ||||
| 	struct flock flk; | ||||
| 	struct stat sbuf; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (NDevOpen(dev)) { | ||||
| 		errno = EBUSY; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	dev->d_private = ntfs_malloc(sizeof(int)); | ||||
| 	if (!dev->d_private) | ||||
| 		return -1; | ||||
| 	/*
 | ||||
| 	 * Open the device/file obtaining the file descriptor for exclusive | ||||
| 	 * access (but only if mounting r/w). | ||||
| 	 */  | ||||
| 	if ((flags & O_RDWR) == O_RDWR) | ||||
| 		flags |= O_EXCL; | ||||
| 	*(int*)dev->d_private = open(dev->d_name, flags); | ||||
| 	if (*(int*)dev->d_private == -1) { | ||||
| 		err = errno; | ||||
| 		goto err_out; | ||||
| 	} | ||||
| 	/* Setup our read-only flag. */ | ||||
| 	if ((flags & O_RDWR) != O_RDWR) | ||||
| 		NDevSetReadOnly(dev); | ||||
| 	/* Acquire exclusive (mandatory) lock on the whole device. */ | ||||
| 	memset(&flk, 0, sizeof(flk)); | ||||
| 	if (NDevReadOnly(dev)) | ||||
| 		flk.l_type = F_RDLCK; | ||||
| 	else | ||||
| 		flk.l_type = F_WRLCK; | ||||
| 	flk.l_whence = SEEK_SET; | ||||
| 	flk.l_start = flk.l_len = 0LL; | ||||
| 	if (fcntl(DEV_FD(dev), F_SETLK, &flk)) { | ||||
| 		err = errno; | ||||
| 		ntfs_log_debug("ntfs_device_unix_io_open: Could not lock %s for %s\n", | ||||
| 				dev->d_name, NDevReadOnly(dev) ? "reading" : "writing"); | ||||
| 		if (close(DEV_FD(dev))) | ||||
| 			ntfs_log_perror("ntfs_device_unix_io_open: Warning: Could not " | ||||
| 					"close %s", dev->d_name); | ||||
| 		goto err_out; | ||||
| 	} | ||||
| 	/* Determine if device is a block device or not, ignoring errors. */ | ||||
| 	if (!fstat(DEV_FD(dev), &sbuf) && S_ISBLK(sbuf.st_mode)) | ||||
| 		NDevSetBlock(dev); | ||||
| 	/* Set our open flag. */ | ||||
| 	NDevSetOpen(dev); | ||||
| 	return 0; | ||||
| err_out: | ||||
| 	free(dev->d_private); | ||||
| 	dev->d_private = NULL; | ||||
| 	errno = err; | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_unix_io_close - Close the device, releasing the lock | ||||
|  * @dev: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| static int ntfs_device_unix_io_close(struct ntfs_device *dev) | ||||
| { | ||||
| 	struct flock flk; | ||||
| 
 | ||||
| 	if (!NDevOpen(dev)) { | ||||
| 		errno = EBADF; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (NDevDirty(dev)) | ||||
| 		fsync(DEV_FD(dev)); | ||||
| 	/* Release exclusive (mandatory) lock on the whole device. */ | ||||
| 	memset(&flk, 0, sizeof(flk)); | ||||
| 	flk.l_type = F_UNLCK; | ||||
| 	flk.l_whence = SEEK_SET; | ||||
| 	flk.l_start = flk.l_len = 0LL; | ||||
| 	if (fcntl(DEV_FD(dev), F_SETLK, &flk)) | ||||
| 		ntfs_log_perror("ntfs_device_unix_io_close: Warning: Could not " | ||||
| 				"unlock %s", dev->d_name); | ||||
| 	/* Close the file descriptor and clear our open flag. */ | ||||
| 	if (close(DEV_FD(dev))) | ||||
| 		return -1; | ||||
| 	NDevClearOpen(dev); | ||||
| 	free(dev->d_private); | ||||
| 	dev->d_private = NULL; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_unix_io_seek - Seek to a place on the device | ||||
|  * @dev: | ||||
|  * @offset: | ||||
|  * @whence: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| static s64 ntfs_device_unix_io_seek(struct ntfs_device *dev, s64 offset, | ||||
| 		int whence) | ||||
| { | ||||
| 	return lseek(DEV_FD(dev), offset, whence); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_unix_io_read - Read from the device, from the current location | ||||
|  * @dev: | ||||
|  * @buf: | ||||
|  * @count: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| static s64 ntfs_device_unix_io_read(struct ntfs_device *dev, void *buf, | ||||
| 		s64 count) | ||||
| { | ||||
| 	return read(DEV_FD(dev), buf, count); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_unix_io_write - Write to the device, at the current location | ||||
|  * @dev: | ||||
|  * @buf: | ||||
|  * @count: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| static s64 ntfs_device_unix_io_write(struct ntfs_device *dev, const void *buf, | ||||
| 		s64 count) | ||||
| { | ||||
| 	if (NDevReadOnly(dev)) { | ||||
| 		errno = EROFS; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	NDevSetDirty(dev); | ||||
| 	return write(DEV_FD(dev), buf, count); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_unix_io_pread - Perform a positioned read from the device | ||||
|  * @dev: | ||||
|  * @buf: | ||||
|  * @count: | ||||
|  * @offset: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| static s64 ntfs_device_unix_io_pread(struct ntfs_device *dev, void *buf, | ||||
| 		s64 count, s64 offset) | ||||
| { | ||||
| 	return ntfs_pread(dev, offset, count, buf); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_unix_io_pwrite - Perform a positioned write to the device | ||||
|  * @dev: | ||||
|  * @buf: | ||||
|  * @count: | ||||
|  * @offset: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| static s64 ntfs_device_unix_io_pwrite(struct ntfs_device *dev, const void *buf, | ||||
| 		s64 count, s64 offset) | ||||
| { | ||||
| 	if (NDevReadOnly(dev)) { | ||||
| 		errno = EROFS; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	NDevSetDirty(dev); | ||||
| 	return ntfs_pwrite(dev, offset, count, buf); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_unix_io_sync - Flush any buffered changes to the device | ||||
|  * @dev: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| static int ntfs_device_unix_io_sync(struct ntfs_device *dev) | ||||
| { | ||||
| 	if (!NDevReadOnly(dev) && NDevDirty(dev)) { | ||||
| 		int res = fsync(DEV_FD(dev)); | ||||
| 		if (!res) | ||||
| 			NDevClearDirty(dev); | ||||
| 		return res; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_unix_io_stat - Get information about the device | ||||
|  * @dev: | ||||
|  * @buf: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| static int ntfs_device_unix_io_stat(struct ntfs_device *dev, struct stat *buf) | ||||
| { | ||||
| 	return fstat(DEV_FD(dev), buf); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_device_unix_io_ioctl - Perform an ioctl on the device | ||||
|  * @dev: | ||||
|  * @request: | ||||
|  * @argp: | ||||
|  * | ||||
|  * Description... | ||||
|  * | ||||
|  * Returns: | ||||
|  */ | ||||
| static int ntfs_device_unix_io_ioctl(struct ntfs_device *dev, int request, | ||||
| 		void *argp) | ||||
| { | ||||
| 	return ioctl(DEV_FD(dev), request, argp); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Device operations for working with unix style devices and files. | ||||
|  */ | ||||
| struct ntfs_device_operations ntfs_device_unix_io_ops = { | ||||
| 	.open		= ntfs_device_unix_io_open, | ||||
| 	.close		= ntfs_device_unix_io_close, | ||||
| 	.seek		= ntfs_device_unix_io_seek, | ||||
| 	.read		= ntfs_device_unix_io_read, | ||||
| 	.write		= ntfs_device_unix_io_write, | ||||
| 	.pread		= ntfs_device_unix_io_pread, | ||||
| 	.pwrite		= ntfs_device_unix_io_pwrite, | ||||
| 	.sync		= ntfs_device_unix_io_sync, | ||||
| 	.stat		= ntfs_device_unix_io_stat, | ||||
| 	.ioctl		= ntfs_device_unix_io_ioctl, | ||||
| }; | ||||
|  | @ -0,0 +1,40 @@ | |||
| /**
 | ||||
|  * version.c - Info about the NTFS library.  Originated from the Linux-NTFS project. | ||||
|  * | ||||
|  * Copyright (c) 2005 Anton Altaparmakov | ||||
|  * Copyright (c) 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 NTFS-3G | ||||
|  * 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 | ||||
| 
 | ||||
| #include "version.h" | ||||
| 
 | ||||
| /* FIXME: merge libntfs into the user space NTFS driver */ | ||||
| static const char *libntfs_version_string = "22:0:0"; | ||||
| 
 | ||||
| /**
 | ||||
|  * ntfs_libntfs_version - query version number of the ntfs library libntfs | ||||
|  * | ||||
|  * Returns pointer to a text string representing the version of libntfs. | ||||
|  */ | ||||
| const char *ntfs_libntfs_version(void) | ||||
| { | ||||
| 	return libntfs_version_string; | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,50 @@ | |||
| if REALLYSTATIC | ||||
| AM_LIBS		= $(top_builddir)/libntfs-3g/.libs/libntfs-3g.a | ||||
| AM_LFLAGS	= -static | ||||
| STATIC_LINK     = $(CC) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ | ||||
| else | ||||
| AM_LIBS		= $(top_builddir)/libntfs-3g/libntfs-3g.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) | ||||
| 
 | ||||
| man_MANS		= ntfs-3g.8  | ||||
| 
 | ||||
| MAINTAINERCLEANFILES	= Makefile.in | ||||
| 
 | ||||
| linux_ntfsincludedir	= -I$(top_srcdir)/include/ntfs | ||||
| 
 | ||||
| bin_PROGRAMS		= ntfs-3g | ||||
| 
 | ||||
| # Set the include path.
 | ||||
| AM_CPPFLAGS		= -I$(top_srcdir)/include/ntfs-3g $(all_includes) | ||||
| 
 | ||||
| ntfs_3g_SOURCES	= ntfs-3g.c utils.c utils.h | ||||
| ntfs_3g_LDADD	= $(AM_LIBS) $(FUSE_MODULE_LIBS) | ||||
| ntfs_3g_LDFLAGS	= $(AM_LFLAGS) | ||||
| ntfs_3g_CFLAGS	= $(FUSE_MODULE_CFLAGS) -DFUSE_USE_VERSION=25 | ||||
| 
 | ||||
| # Extra targets
 | ||||
| 
 | ||||
| strip:	$(bin_PROGRAMS) | ||||
| 	$(STRIP) $^ | ||||
| 
 | ||||
| libs: | ||||
| 	(cd ../libntfs-3g && $(MAKE) libs) || exit 1; | ||||
| 
 | ||||
| install-exec-hook: | ||||
| 	$(INSTALL) -d $(DESTDIR)/sbin | ||||
| 	$(LN_S) -f $(bindir)/ntfs-3g $(DESTDIR)/sbin/mount.ntfs-3g | ||||
| 	ldconfig | ||||
| 
 | ||||
| install-data-hook: | ||||
| 	$(INSTALL) -d $(DESTDIR)$(man8dir) | ||||
| 	$(LN_S) -f ntfs-3g.8 $(DESTDIR)$(man8dir)/mount.ntfs-3g.8 | ||||
| 
 | ||||
| uninstall-local: | ||||
| 	$(RM) -f $(DESTDIR)/sbin/mount.ntfs-3g | ||||
| 	$(RM) -f $(DESTDIR)$(man8dir)/mount.ntfs-3g.8 | ||||
| 
 | ||||
|  | @ -0,0 +1,198 @@ | |||
| .\" Copyright (c) 2005-2006 Yura Pakhuchiy. | ||||
| .\" Copyright (c) 2005 Richard Russon. | ||||
| .\" Copyright (c) 2006 Szabolcs Szakacsits. | ||||
| .\" This file may be copied under the terms of the GNU Public License. | ||||
| .\" | ||||
| .TH NTFS-3G 8 "October 2006" "ntfs-3g @VERSION@" | ||||
| .SH NAME | ||||
| ntfs-3g \- Third Generation NTFS Driver | ||||
| .SH SYNOPSIS | ||||
| .B ntfs-3g | ||||
| .I device mount_point | ||||
| [\fB\-o options\fR] | ||||
| .SH DESCRIPTION | ||||
| \fBntfs-3g\fR is an NTFS driver, which can | ||||
| create, remove, rename files, directories, hard links, and | ||||
| streams; it can read and write files, including  | ||||
| streams and sparse files; it can handle special files like  | ||||
| symbolic links, devices, and FIFOs; moreover it can also read | ||||
| compressed files. | ||||
| .SH OPTIONS | ||||
| Below is a summary of the options that \fBntfs-3g\fR accepts. | ||||
| .TP | ||||
| .B uid=, gid=, umask= | ||||
| Provide default owner, group, and access mode mask. | ||||
| These options work as documented in mount(8).  By | ||||
| default, the files and directories are owned by the user who  | ||||
| mounted the volume but everybody has full read, write and | ||||
| executable access, moreover browse permission to any directory.  | ||||
| If you want to use the currently limited permission | ||||
| handling then use these options together with the  | ||||
| .B default_permissions, | ||||
| .B fmask | ||||
| and | ||||
| .B dmask | ||||
| options. The usage of the | ||||
| .B default_permissions | ||||
| option is a must in such cases. | ||||
| .TP | ||||
| .B default_permissions | ||||
| By default FUSE doesn't check file access permissions, the | ||||
| filesystem is free to implement its access policy or leave it to | ||||
| the underlying file access mechanism (e.g. in case of network | ||||
| filesystems).  This option enables permission checking, restricting | ||||
| accesses based on file modes.  This option is usually useful | ||||
| together with the  | ||||
| .B allow_other | ||||
| mount option. | ||||
| .TP | ||||
| .B fmask=, dmask= | ||||
| Instead of specifying umask which applies both to | ||||
| files and directories, fmask applies only to files and | ||||
| mask only to directories. | ||||
| .TP | ||||
| .B ro | ||||
| Mount filesystem read\-only. | ||||
| .TP | ||||
| .B locale= | ||||
| You can set locale with this option which is often required to make  | ||||
| visible files with national charaters. It's useful if locale  | ||||
| environment variables are not set before partitions had been mounted  | ||||
| from /etc/fstab. | ||||
| .TP | ||||
| .B force | ||||
| Force mount even if the volume is scheduled for consistency check. | ||||
| Use this option with caution and preferably with the | ||||
| .B ro | ||||
| option. | ||||
| .TP | ||||
| .B show_sys_files | ||||
| Show the system files in directory listings.  | ||||
| Otherwise the default behaviour is to hide the system files. | ||||
| Please note that even when this option is specified, "$MFT" | ||||
| may not be visible due to a glibc bug. | ||||
| Furthermore, irrespectively of show_sys_files, all | ||||
| files are accessible by name, for example you can always do | ||||
| "ls \-l '$UpCase'". | ||||
| .TP | ||||
| .B allow_other | ||||
| This option overrides the security measure restricting file access | ||||
| to the user mounting the filesystem.  This option is by default only | ||||
| allowed to root, but this restriction can be removed with a | ||||
| configuration option described in the previous section. | ||||
| .TP | ||||
| .B large_read | ||||
| Issue large read requests.  This can improve performance for some | ||||
| filesystems, but can also degrade performance.  This option is mostly | ||||
| useful on 2.4.X kernels, as on 2.6 kernels requests size is | ||||
| automatically determined for optimum performance. | ||||
| .TP | ||||
| .B max_read= | ||||
| With this option the maximum size of read operations can be set. | ||||
| The default is infinite.  Note that the size of read requests is | ||||
| limited anyway to 32 pages (which is 128kbyte on i386). | ||||
| .TP | ||||
| .B silent | ||||
| Do nothing on chmod and chown operations, but do not return error.  | ||||
| This option is on by default. | ||||
| .TP | ||||
| .B no_def_opts | ||||
| By default ntfs-3g acts as "silent,allow_other" was passed to it, | ||||
| this option cancel this behaviour. | ||||
| .TP | ||||
| .B streams_interface= | ||||
| This option controls how the user can access Alternate Data Streams (ADS) | ||||
| or in other words, named data streams. It can be set | ||||
| to, one of \fBnone\fR, \fBwindows\fR or \fBxattr\fR. If the option is set to | ||||
| \fBnone\fR, the user will have no access to the named data streams.  If it's set | ||||
| to \fBwindows\fR, then the user can access them just like in Windows (eg. cat | ||||
| file:stream). If it's set to \fBxattr\fR, then the named data streams are | ||||
| mapped to xattrs and user can manipulate them using \fB{get,set}fattr\fR | ||||
| utilities. The default is \fBnone\fR. | ||||
| .TP | ||||
| .B debug | ||||
| Makes ntfs-3g to not detach from terminal and print a lot of debug output from | ||||
| libntfs-3g and FUSE. | ||||
| .TP | ||||
| .B no_detach | ||||
| Same as above but with less debug output. | ||||
| .SH ALTERNATE DATA STREAMS (ADS) | ||||
| All data on NTFS is stored in streams.  Every file has exactly one unnamed | ||||
| data stream and can have many named data streams.  The size of a file is the | ||||
| size of its unnamed data stream.  By default, \fBntfs-3g\fR will only read | ||||
| the unnamed data stream. | ||||
| .PP | ||||
| By using the options "streams_interface=windows", you will be able to read | ||||
| any named data streams, simply by specifying the stream's name after a colon. | ||||
| For example: | ||||
| .RS | ||||
| .sp | ||||
| cat some.mp3:artist | ||||
| .sp | ||||
| .RE | ||||
| Named data streams act like normals files, so you can read from them, write to | ||||
| them and even delete them (using rm).  You can list all the named data streams | ||||
| a file has by getting the "ntfs.streams.list" extended attribute. | ||||
| .SH EXAMPLES | ||||
| Mount /dev/hda1 to /mnt/windows: | ||||
| .RS | ||||
| .sp | ||||
| .B ntfs-3g /dev/hda1 /mnt/windows | ||||
| .sp | ||||
| .RE | ||||
| Read\-only mount /dev/hda5 to /home/user/mnt and make user with uid 1000  | ||||
| to be the owner of all files: | ||||
| .RS | ||||
| .sp | ||||
| .B ntfs-3g /dev/hda5 /home/user/mnt \-o ro,uid=1000 | ||||
| .sp | ||||
| .RE | ||||
| /etc/fstab entry for the above: | ||||
| .RS | ||||
| .sp | ||||
| .B /dev/hda5 /home/user/mnt ntfs\-3g ro,uid=1000 0 0 | ||||
| .sp | ||||
| .RE | ||||
| Unmount /mnt/windows: | ||||
| .RS | ||||
| .sp | ||||
| .B umount /mnt/windows | ||||
| .sp | ||||
| .RE | ||||
| You can also unmount /mnt/windows with fusermount: | ||||
| .RS | ||||
| .sp | ||||
| .B fusermount \-u /mnt/windows | ||||
| .sp | ||||
| .RE | ||||
| .SH KNOWN ISSUES | ||||
| Please see  | ||||
| .RS | ||||
| .sp | ||||
| http://www.ntfs-3g.org/support.html | ||||
| .sp | ||||
| .RE | ||||
| for all known issues. | ||||
| If you would find a new one in the latest release of | ||||
| this software then please send an email describing it | ||||
| according to the above page. You can also contact the  | ||||
| development team on the ntfs\-3g\-devel@lists.sf.net email  | ||||
| address anytime. | ||||
| .SH AUTHORS | ||||
| .B ntfs-3g  | ||||
| was based on and a major improvement to ntfsmount and libntfs which were | ||||
| written by Yura Pakhuchiy and the Linux-NTFS team. The improvements were  | ||||
| made, the ntfs-3g project was initiated and currently led by long time  | ||||
| Linux-NTFS team developer Szabolcs Szakacsits (szaka@sienet.hu) to revive | ||||
| the stalled open source development and project management. | ||||
| 
 | ||||
| .SH THANKS | ||||
| Several people made heroic efforts, often over five or more | ||||
| years which resulted the ntfs-3g driver. Most importantly they are  | ||||
| Anton Altaparmakov, Richard Russon, Szabolcs Szakacsits, Yura Pakhuchiy, | ||||
| Yuval Fedel, and the author of the groundbreaking FUSE filesystem development  | ||||
| framework, Miklos Szeredi. | ||||
| .SH SEE ALSO | ||||
| .BR ntfsprogs (8), | ||||
| .BR attr (5), | ||||
| .BR getfattr (1) | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,966 @@ | |||
| /**
 | ||||
|  * utils.c - Originated from 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 NTFS-3G | ||||
|  * 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 <stdio.h> | ||||
| #endif | ||||
| #ifdef HAVE_STDARG_H | ||||
| #include <stdarg.h> | ||||
| #endif | ||||
| #ifdef HAVE_ERRNO_H | ||||
| #include <errno.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYS_TYPES_H | ||||
| #include <sys/types.h> | ||||
| #endif | ||||
| #ifdef HAVE_SYS_STAT_H | ||||
| #include <sys/stat.h> | ||||
| #endif | ||||
| #ifdef HAVE_UNISTD_H | ||||
| #include <unistd.h> | ||||
| #endif | ||||
| #ifdef HAVE_STRING_H | ||||
| #include <string.h> | ||||
| #endif | ||||
| #ifdef HAVE_LOCALE_H | ||||
| #include <locale.h> | ||||
| #endif | ||||
| #ifdef HAVE_LIBINTL_H | ||||
| #include <libintl.h> | ||||
| #endif | ||||
| #ifdef HAVE_STDLIB_H | ||||
| #include <stdlib.h> | ||||
| #endif | ||||
| #ifdef HAVE_LIMITS_H | ||||
| #include <limits.h> | ||||
| #endif | ||||
| #ifdef HAVE_CTYPE_H | ||||
| #include <ctype.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "utils.h" | ||||
| #include "types.h" | ||||
| #include "volume.h" | ||||
| #include "debug.h" | ||||
| #include "dir.h" | ||||
| #include "version.h" | ||||
| #include "logging.h" | ||||
| #include "misc.h" | ||||
| 
 | ||||
| const char *ntfs_home =  | ||||
|    "Ntfs-3g news, support and information:  http://www.ntfs-3g.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"; | ||||
| 
 | ||||
| static const char *invalid_ntfs_msg = | ||||
| "The device '%s' doesn't have a valid NTFS.\n" | ||||
| "Maybe you selected the wrong device? Or the whole disk instead of a\n" | ||||
| "partition (e.g. /dev/hda, not /dev/hda1)? Or the other way around?\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" | ||||
| "made to NTFS by this software.\n"; | ||||
| 
 | ||||
| static const char *hibernated_volume_msg = | ||||
| "The NTFS partition is hibernated. Please resume Windows and turned it \n" | ||||
| "off properly, so mounting could be done safely.\n"; | ||||
| 
 | ||||
| static const char *unclean_journal_msg = | ||||
| "Mount is denied because the NTFS journal file is unclean. Choices are:\n" | ||||
| " A) Shutdown Windows properly.\n" | ||||
| " B) Click the 'Safely Remove Hardware' icon in the Windows taskbar\n" | ||||
| "    notification area before disconnecting the device.\n" | ||||
| " C) Use 'Eject' from Windows Explorer to safely remove the device.\n" | ||||
| " D) If you ran chkdsk previously then boot Windows again which will\n" | ||||
| "    automatically initialize the journal.\n" | ||||
| " E) Run 'ntfsfix' on Linux which will reset the NTFS journal.\n" | ||||
| " F) Mount the volume read-only by using the 'ro' mount option.\n"; | ||||
| 
 | ||||
| static const char *opened_volume_msg = | ||||
| "Mount is denied because the NTFS volume is already exclusively opened.\n" | ||||
| "The volume may be already mounted, or another software may use it which\n" | ||||
| "could be identified for example by the help of the 'fuser' command.\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("Couldn't set local environment, using default '%s'.\n", locale); | ||||
| 		return 1; | ||||
| 	} else { | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| ntfs_volume *utils_mount_volume(const char *volume, unsigned long flags, | ||||
| 				BOOL force) | ||||
| { | ||||
| 	ntfs_volume *vol; | ||||
| 
 | ||||
| 	vol = ntfs_mount(volume, flags); | ||||
| 	if (!vol) { | ||||
| 		 | ||||
| 		ntfs_log_perror("Failed to mount '%s'", volume); | ||||
| 		 | ||||
| 		if (errno == EINVAL) | ||||
| 			ntfs_log_error(invalid_ntfs_msg, volume); | ||||
| 		else if (errno == EIO) | ||||
| 			ntfs_log_error("%s", corrupt_volume_msg); | ||||
| 		else if (errno == EPERM) | ||||
| 			ntfs_log_error("%s", hibernated_volume_msg); | ||||
| 		else if (errno == EOPNOTSUPP) | ||||
| 			ntfs_log_error("%s", unclean_journal_msg); | ||||
| 		else if (errno == EBUSY) | ||||
| 			ntfs_log_error("%s", opened_volume_msg); | ||||
| 		 | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (vol->flags & VOLUME_IS_DIRTY) { | ||||
| 		if (!force) { | ||||
| 			ntfs_log_error("Volume is scheduled for check.\nPlease " | ||||
| 				       "boot into Windows TWICE, or use the " | ||||
| 				       "'force' mount option.\n"); | ||||
| 			ntfs_umount(vol, FALSE); | ||||
| 			 | ||||
| 			return NULL; | ||||
| 		} else | ||||
| 			ntfs_log_error("WARNING: Dirty volume mount was forced " | ||||
| 				       "by the 'force' mount option.\n"); | ||||
| 	} | ||||
| 	 | ||||
| 	return vol; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * 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; | ||||
| 	} | ||||
| 
 | ||||
| 	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; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	*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; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * 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; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * 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 ntfs_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 = ntfs_malloc(30); | ||||
| 				if (!temp) | ||||
| 					return 0; | ||||
| 				snprintf(temp, 30, "<MFT%llu>", (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"); | ||||
| 			// <UNKNOWN>?
 | ||||
| 			return 0; | ||||
| 		} | ||||
| 		len = snprintf(buffer, bufsize, "%s", name); | ||||
| 	} else { | ||||
| 		ntfs_log_error("Unknown attribute type 0x%02x\n", attr->type); | ||||
| 		len = snprintf(buffer, bufsize, "<UNKNOWN>"); | ||||
| 	} | ||||
| 
 | ||||
| 	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"); | ||||
| 		// <UNKNOWN>?
 | ||||
| 		len = snprintf(buffer, bufsize, "<UNKNOWN>"); | ||||
| 		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   = ntfs_malloc(ctx->vol->mft_record_size); | ||||
| 			if (!ctx->inode->mrec) { | ||||
| 				free(ctx->inode); // == ntfs_inode_close
 | ||||
| 				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); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -0,0 +1,99 @@ | |||
| /*
 | ||||
|  * utils.h - Originated from 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 NTFS-3G | ||||
|  * 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 "types.h" | ||||
| #include "layout.h" | ||||
| #include "volume.h" | ||||
| 
 | ||||
| #ifdef HAVE_ERRNO_H | ||||
| #include <errno.h> | ||||
| #endif | ||||
| #ifdef HAVE_STDARG_H | ||||
| #include <stdarg.h> | ||||
| #endif | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
| 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_valid_device(const char *name, int force); | ||||
| ntfs_volume * utils_mount_volume(const char *device, unsigned long flags, BOOL force); | ||||
| 
 | ||||
| /**
 | ||||
|  * 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) | ||||
| 
 | ||||
| #endif /* _NTFS_UTILS_H_ */ | ||||
		Loading…
	
		Reference in New Issue