diff --git a/README b/README index 0f6a7db2..b829fd0b 100644 --- a/README +++ b/README @@ -16,8 +16,10 @@ reliability and feature richness per invested effort wise. Besides the common file system features, NTFS-3G has support for file ownership and permissions, POSIX ACLs, junction points, extended attributes -and creating compressed files. Parameter files in the directory .NTFS-3G may -be required to enable them, please get the instructions from +and creating internally compressed files (parameter files in the directory +.NTFS-3G may be required to enable them). The new compressed file formats +available in Windows 10 can also be read through a plugin. For using +advanced features, please get the instructions from http://www.tuxera.com/community/ntfs-3g-advanced/ @@ -59,6 +61,8 @@ typing : Below are a few specific options to ./configure : --disable-ntfsprogs : do not build the ntfsprogs tools, + --enable-extras : build more ntfsprogs tools, + --disable-plugins : disable support for plugins --enable-posix-acls : enable support for Posix ACLs --enable-xattr-mappings : enable system extended attributes mappings --with-fuse=external : use external fuse (overriding Linux default) @@ -145,9 +149,16 @@ partition. See man 8 ntfscluster for details. ntfsinfo - Show some information about an NTFS partition or one of the files or directories within it. See man 8 ntfsinfo for details. +ntfsrecover - Recover updates committed by Windows but interrupted before +being synced. + ntfsls - List information about files in a directory residing on an NTFS partition. See man 8 ntfsls for details. ntfscat - Concatenate files and print their contents on the standard output. ntfscp - Overwrite files on an NTFS partition. + +ntfssecaudit : audit the security metadata. + +ntfsusermap : assistance for building a user mapping file. diff --git a/configure.ac b/configure.ac index d31a9cda..4bc098b4 100644 --- a/configure.ac +++ b/configure.ac @@ -24,8 +24,8 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[2016.2.22],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="87" +AC_INIT([ntfs-3g],[2017.2.15],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="88" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment @@ -121,6 +121,14 @@ AC_ARG_ENABLE( [enable_xattr_mappings="no"] ) +AC_ARG_ENABLE( + [plugins], + [AS_HELP_STRING([--disable-plugins], [Disable external reparse point + plugins for the ntfs-3g FUSE driver])], + [if test x${enableval} = "xyes"; then disable_plugins="no"; fi], + [disable_plugins="no"] +) + AC_ARG_ENABLE( [device-default-io-ops], [AS_HELP_STRING([--disable-device-default-io-ops],[install default IO ops])], @@ -465,12 +473,13 @@ fi # Checks for header files. AC_HEADER_STDC +AC_HEADER_MAJOR 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 \ regex.h endian.h byteswap.h sys/byteorder.h sys/disk.h sys/endian.h \ - sys/param.h sys/ioctl.h sys/mkdev.h sys/mount.h sys/stat.h sys/types.h \ - sys/vfs.h sys/statvfs.h sys/sysmacros.h linux/major.h linux/fd.h \ + sys/param.h sys/ioctl.h sys/mount.h sys/stat.h sys/types.h \ + sys/vfs.h sys/statvfs.h linux/major.h linux/fd.h \ linux/fs.h inttypes.h linux/hdreg.h \ machine/endian.h windows.h syslog.h pwd.h malloc.h]) @@ -534,6 +543,16 @@ AC_CHECK_FUNCS([ \ ]) AC_SYS_LARGEFILE +# The dlopen API might be in libc or in libdl. Check libc first, then +# fall back to libdl. +LIBDL="" +if test "x${disable_plugins}" = "xno"; then + AC_CHECK_LIB(c, dlopen, , + [AC_CHECK_LIB(dl, dlopen, [LIBDL="-ldl"], + [AC_MSG_ERROR(["Unable to find libdl (required for external plugin support)"])])]) +fi +AC_SUBST([LIBDL]) + if test "$GCC" = "yes" ; then # We add -Wall to enable some compiler warnings. CFLAGS="${CFLAGS} -Wall" @@ -566,6 +585,7 @@ test "${enable_device_default_io_ops}" = "no" && AC_DEFINE( test "${enable_mtab}" = "no" && AC_DEFINE([IGNORE_MTAB], [1], [Don't update /etc/mtab]) test "${enable_posix_acls}" != "no" && AC_DEFINE([POSIXACLS], [1], [POSIX ACL support]) test "${enable_xattr_mappings}" != "no" && AC_DEFINE([XATTR_MAPPINGS], [1], [system extended attributes mappings]) +test "${disable_plugins}" != "no" && AC_DEFINE([DISABLE_PLUGINS], [1], [Define to 1 for disabling reparse plugins]) test "${enable_really_static}" = "yes" && enable_library="no" test "${enable_library}" = "no" && enable_ldconfig="no" @@ -617,6 +637,7 @@ AM_CONDITIONAL([ENABLE_NTFS_3G], [test "${enable_ntfs_3g}" = "yes"]) AM_CONDITIONAL([ENABLE_NTFSPROGS], [test "${enable_ntfsprogs}" = "yes"]) AM_CONDITIONAL([ENABLE_EXTRAS], [test "${enable_extras}" = "yes"]) AM_CONDITIONAL([ENABLE_QUARANTINED], [test "${enable_quarantined}" = "yes"]) +AM_CONDITIONAL([DISABLE_PLUGINS], [test "${disable_plugins}" != "no"]) # workaround for ', '?', '\' and '|'. Further, names cannot end with a '.' - or a space. */ + '>', '?', '\' and '|'. Trailing dots and spaces are allowed, + even though on Windows a filename with such a suffix can only + be created and accessed using a WinNT-style path, i.e. + \\?\-prefixed. (If a regular path is used, Windows will + strip the trailing dots and spaces, which makes such + filenames incompatible with most Windows software.) */ FILE_NAME_DOS = 0x02, /* The standard DOS filenames (8.3 format). Uppercase only. All 8-bit characters greater space, except: '"', '*', '+', - ',', '/', ':', ';', '<', '=', '>', '?' and '\'. */ + ',', '/', ':', ';', '<', '=', '>', '?' and '\'. Trailing + dots and spaces are forbidden. */ FILE_NAME_WIN32_AND_DOS = 0x03, /* 3 means that both the Win32 and the DOS filenames are identical and hence have been saved in this single filename diff --git a/include/ntfs-3g/logfile.h b/include/ntfs-3g/logfile.h index ad66bd87..11359c5a 100644 --- a/include/ntfs-3g/logfile.h +++ b/include/ntfs-3g/logfile.h @@ -2,6 +2,7 @@ * logfile.h - Exports for $LogFile handling. Originated from the Linux-NTFS project. * * Copyright (c) 2000-2005 Anton Altaparmakov + * Copyright (c) 2016 Jean-Pierre Andre * * 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 @@ -93,7 +94,8 @@ typedef struct { version is 1. */ /* 28*/ sle16 major_ver; /* Log file major version. We only support version 1.1. */ -/* sizeof() = 30 (0x1e) bytes */ +/* 30*/ le16 usn; +/* sizeof() = 32 (0x20) bytes */ } __attribute__((__packed__)) RESTART_PAGE_HEADER; /* @@ -101,8 +103,8 @@ typedef struct { * 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 +#define LOGFILE_NO_CLIENT const_cpu_to_le16(LOGFILE_NO_CLIENT_CPU) /* * These are the so far known RESTART_AREA_* flags (16-bit) which contain @@ -329,18 +331,16 @@ typedef struct { le32 flags; le16 page_count; le16 page_position; - union { - struct { - le16 next_record_offset; - u8 reserved[6]; - leLSN last_end_lsn; - } __attribute__((__packed__)) packed; - } __attribute__((__packed__)) header; + le16 next_record_offset; + le16 reserved[3]; + leLSN last_end_lsn; } __attribute__((__packed__)) RECORD_PAGE_HEADER; /** * enum LOG_RECORD_FLAGS - Possible 16-bit flags for log records. * + * Some flags describe what kind of update is being logged. + * * (Or is it log record pages?) */ #if ENABLE_STRICT_ENDIANNESS_CHECKING @@ -351,6 +351,9 @@ static const LOG_RECORD_FLAGS typedef enum { #endif LOG_RECORD_MULTI_PAGE = const_cpu_to_le16(0x0001), /* ??? */ + /* The flags below were introduced in Windows 10 */ + LOG_RECORD_DELETING = const_cpu_to_le16(0x0002), + LOG_RECORD_ADDING = const_cpu_to_le16(0x0004), #if !ENABLE_STRICT_ENDIANNESS_CHECKING LOG_RECORD_SIZE_PLACE_HOLDER = 0xffff, /* This has nothing to do with the log record. It is only so @@ -368,6 +371,52 @@ typedef struct { le16 client_index; } __attribute__((__packed__)) LOG_CLIENT_ID; +/* + * LOG_RECORD_TYPE : types of log records + */ + +#if ENABLE_STRICT_ENDIANNESS_CHECKING +typedef le32 LOG_RECORD_TYPE; + +static const LOG_RECORD_TYPE +#else +enum { +#endif + LOG_STANDARD = const_cpu_to_le32(1), + LOG_CHECKPOINT = const_cpu_to_le32(2), +#if !ENABLE_STRICT_ENDIANNESS_CHECKING + LOG_RECORD_TYPE_PLACE_HOLDER = 0xffffffffU +} ; +typedef le32 LOG_RECORD_TYPE; +#else + __LOG_RECORD_TYPE_end; +#endif + +/* + * ATTRIBUTE_FLAGS : flags describing the kind of NTFS record + * is being updated. + * These flags were introduced in Vista, only two flags are known? + */ + +#if ENABLE_STRICT_ENDIANNESS_CHECKING +typedef le16 ATTRIBUTE_FLAGS; + +static const ATTRIBUTE_FLAGS +#else +enum { +#endif + ACTS_ON_MFT = const_cpu_to_le16(2), + ACTS_ON_INDX = const_cpu_to_le16(8), +#if !ENABLE_STRICT_ENDIANNESS_CHECKING + ATTRIBUTE_FLAGS_PLACE_HOLDER = 0xffff, +} ; +typedef le16 ATTRIBUTE_FLAGS; +#else + __ATTRIBUTE_FLAGS_end; +#endif + +#define LOG_RECORD_HEAD_SZ 0x30 /* size of header of struct LOG_RECORD */ + /** * struct LOG_RECORD - Log record header. * @@ -379,32 +428,79 @@ typedef struct { leLSN client_undo_next_lsn; le32 client_data_length; LOG_CLIENT_ID client_id; - le32 record_type; + LOG_RECORD_TYPE record_type; le32 transaction_id; - le16 flags; + LOG_RECORD_FLAGS log_record_flags; le16 reserved_or_alignment[3]; /* Now are at ofs 0x30 into struct. */ le16 redo_operation; le16 undo_operation; le16 redo_offset; le16 redo_length; - le16 undo_offset; - le16 undo_length; - le16 target_attribute; - le16 lcns_to_follow; /* Number of lcn_list entries + union { + struct { + le16 undo_offset; + le16 undo_length; + le16 target_attribute; + le16 lcns_to_follow; /* Number of lcn_list entries following this entry. */ /* Now at ofs 0x40. */ - le16 record_offset; - le16 attribute_offset; - le32 alignment_or_reserved; - leVCN target_vcn; + le16 record_offset; + le16 attribute_offset; + le16 cluster_index; + ATTRIBUTE_FLAGS attribute_flags; + leVCN target_vcn; /* Now at ofs 0x50. */ - struct { /* Only present if lcns_to_follow - is not 0. */ - leLCN lcn; - } __attribute__((__packed__)) lcn_list[0]; + leLCN lcn_list[0]; /* Only present if lcns_to_follow + is not 0. */ + } __attribute__((__packed__)); + struct { + leLSN transaction_lsn; + leLSN attributes_lsn; + leLSN names_lsn; + leLSN dirty_pages_lsn; + le64 unknown_list[0]; + } __attribute__((__packed__)); + } __attribute__((__packed__)); } __attribute__((__packed__)) LOG_RECORD; +/** + * struct BITMAP_ACTION - Bitmap change being logged + */ + +struct BITMAP_ACTION { + le32 firstbit; + le32 count; +} ; + +/** + * struct ATTR - Attribute record. + * + * The format of an attribute record has changed from Windows 10. + * The old format was 44 bytes long, despite having 8 bytes fields, + * and this leads to alignment problems in arrays. + * This problem does not occur in the new format, which is shorter. + * The format being used can generally be determined from size. + */ +typedef struct { /* Format up to Win10 (44 bytes) */ + le64 unknown1; + le64 unknown2; + le64 inode; + leLSN lsn; + le32 unknown3; + le32 type; + le32 unknown4; +} __attribute__((__packed__)) ATTR_OLD; + +typedef struct { /* Format since Win10 (40 bytes) */ + le64 unknown1; + le64 unknown2; + le32 type; + le32 unknown3; + le64 inode; + leLSN lsn; +} __attribute__((__packed__)) ATTR_NEW; + 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); diff --git a/include/ntfs-3g/param.h b/include/ntfs-3g/param.h index 3210fab9..23a728cc 100644 --- a/include/ntfs-3g/param.h +++ b/include/ntfs-3g/param.h @@ -107,6 +107,11 @@ enum { * performances, but bad on security with internal fuse or external * fuse older than 2.8 * + * On Linux, cacheing is discouraged for the high level interface + * in order to get proper support of hard links. As a consequence, + * having access control in the file system leads to fewer requests + * to the file system and fewer context switches. + * * Possible values for high level : * 1 : no cache, kernel control (recommended) * 4 : no cache, file system control @@ -115,12 +120,13 @@ enum { * * Possible values for low level : * 2 : no cache, kernel control - * 3 : use kernel/fuse cache, kernel control (external fuse >= 2.8) - * 5 : no cache, file system control (recommended on Linux) + * 3 : use kernel/fuse cache, kernel control (recommended) + * 5 : no cache, file system control * 6 : kernel/fuse cache, file system control (OpenIndiana only) * 8 : no cache, kernel control for ACLs + * 9 : kernel/fuse cache, kernel control for ACLs (target) * - * Use of options 7 and 8 requires a patch to fuse + * Use of options 7, 8 and 9 requires a fuse module upgrade * When Posix ACLs are selected in the configure options, a value * of 6 is added in the mount report. */ @@ -132,13 +138,14 @@ enum { */ #define HPERMSCONFIG 6 #define LPERMSCONFIG 6 -#else +#else /* defined(__sun) && defined(__SVR4) */ +/* + * Cacheing by kernel is buggy on Linux when access control is done + * by the file system, and also when using hard-linked files on + * the fuse high level interface. + */ #define HPERMSCONFIG 1 -#if defined(FUSE_INTERNAL) || !defined(FUSE_VERSION) || (FUSE_VERSION < 28) -#define LPERMSCONFIG 5 -#else -#define LPERMSCONFIG 3 -#endif +#define LPERMSCONFIG 3 /* Use 9 when ACLs are supported by fuse kernel */ #endif /* defined(__sun) && defined(__SVR4) */ #endif /* defined _NTFS_PARAM_H */ diff --git a/include/ntfs-3g/plugin.h b/include/ntfs-3g/plugin.h new file mode 100644 index 00000000..d32e149d --- /dev/null +++ b/include/ntfs-3g/plugin.h @@ -0,0 +1,141 @@ +/* + * plugin.h : define interface for plugin development + * + * Copyright (c) 2015 Jean-Pierre Andre + * + */ + +/* + * 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 + */ + +/* + * This file defines the interface to ntfs-3g plugins which + * add support for processing some type of reparse points. + */ + +#ifndef _NTFS_PLUGIN_H +#define _NTFS_PLUGIN_H + +#include "inode.h" +#include "layout.h" + +struct fuse_file_info; +struct stat; + + /* + * The plugin operations currently defined. + * These functions should return a non-negative value when they + * succeed, or a negative errno value when they fail. + * They must not close or free their arguments. + * The file system must be left in a consistent state after + * each individual call. + * If an operation is not defined, an EOPNOTSUPP error is + * returned to caller. + */ +typedef struct plugin_operations { + /* + * Set the attributes st_size, st_blocks and st_mode + * into a struct stat. The returned st_mode must at least + * define the file type. Depending on the permissions options + * used for mounting, the umask will be applied to the returned + * permissions, or the permissions will be changed according + * to the ACL set on the file. + */ + int (*getattr)(ntfs_inode *ni, const REPARSE_POINT *reparse, + struct stat *stbuf); + + /* + * Open a file for reading or writing + * The field fi->flags indicates the kind of opening. + * The field fi->fh may be used to store some information which + * will be available to subsequent reads and writes. When used + * this field must be non-null. + * The returned value is zero for success and a negative errno + * value for failure. + */ + int (*open)(ntfs_inode *ni, const REPARSE_POINT *reparse, + struct fuse_file_info *fi); + + /* + * Release an open file + * This is only called if fi->fh has been set to a non-null + * value while opening. It may be used to free some context + * specific to the open file. + * The returned value is zero for success or a negative errno + * value for failure. + */ + int (*release)(ntfs_inode *ni, const REPARSE_POINT *reparse, + struct fuse_file_info *fi); + + /* + * Read from an open file + * The returned value is the count of bytes which were read + * or a negative errno value for failure. + * If the returned value is positive, the access time stamp + * will be updated after the call. + */ + int (*read)(ntfs_inode *ni, const REPARSE_POINT *reparse, + char *buf, size_t size, + off_t offset, struct fuse_file_info *fi); + + /* + * Write to an open file + * The file system must be left consistent after each write call, + * the file itself must be at least deletable if the application + * writing to it is killed for some reason. + * The returned value is the count of bytes which were written + * or a negative errno value for failure. + * If the returned value is positive, the modified time stamp + * will be updated after the call. + */ + int (*write)(ntfs_inode *ni, const REPARSE_POINT *reparse, + const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi); + + /* + * Get a symbolic link + * The symbolic link must be returned in an allocated buffer, + * encoded in a zero terminated multibyte string compatible + * which the locale mount option. + * The returned value is zero for success or a negative errno + * value for failure. + */ + int (*readlink)(ntfs_inode *ni, const REPARSE_POINT *reparse, + char **pbuf); + + /* + * Truncate a file (shorten or append zeroes) + * The returned value is zero for success or a negative errno + * value for failure. + * If the returned value is zero, the modified time stamp + * will be updated after the call. + */ + int (*truncate)(ntfs_inode *ni, const REPARSE_POINT *reparse, + off_t size); +} plugin_operations_t; + + +/* + * Plugin initialization routine + * Returns the entry table if successful, otherwise returns NULL + * and sets errno (e.g. to EINVAL if the tag is not supported by + * the plugin.) + */ +typedef const struct plugin_operations *(*plugin_init_t)(le32 tag); +const struct plugin_operations *init(le32 tag); + +#endif /* _NTFS_PLUGIN_H */ diff --git a/include/ntfs-3g/reparse.h b/include/ntfs-3g/reparse.h index 35f4aa45..76af9150 100644 --- a/include/ntfs-3g/reparse.h +++ b/include/ntfs-3g/reparse.h @@ -24,12 +24,14 @@ #ifndef REPARSE_H #define REPARSE_H -char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point, - int *pattr_size); +char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point); + BOOL ntfs_possible_symlink(ntfs_inode *ni); int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size); +REPARSE_POINT *ntfs_get_reparse_point(ntfs_inode *ni); + int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, const char *value, size_t size, int flags); int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni); diff --git a/include/ntfs-3g/security.h b/include/ntfs-3g/security.h index d28bc89b..c76ce9f2 100644 --- a/include/ntfs-3g/security.h +++ b/include/ntfs-3g/security.h @@ -173,7 +173,7 @@ struct POSIX_SECURITY { int defcnt; int firstdef; u16 tagsset; - s32 alignment[0]; + u16 filler; struct POSIX_ACL acl; } ; @@ -256,7 +256,9 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, ntfs_inode *dir_ni, BOOL fordir); int ntfs_open_secure(ntfs_volume *vol); -void ntfs_close_secure(struct SECURITY_CONTEXT *scx); +int ntfs_close_secure(ntfs_volume *vol); + +void ntfs_destroy_security_context(struct SECURITY_CONTEXT *scx); #if POSIXACLS diff --git a/include/ntfs-3g/unistr.h b/include/ntfs-3g/unistr.h index b6d428e3..28048ed7 100644 --- a/include/ntfs-3g/unistr.h +++ b/include/ntfs-3g/unistr.h @@ -68,9 +68,9 @@ extern ntfschar *ntfs_str2ucs(const char *s, int *len); extern void ntfs_ucsfree(ntfschar *ucs); -extern BOOL ntfs_forbidden_chars(const ntfschar *name, int len); +extern BOOL ntfs_forbidden_chars(const ntfschar *name, int len, BOOL strict); extern BOOL ntfs_forbidden_names(ntfs_volume *vol, - const ntfschar *name, int len); + const ntfschar *name, int len, BOOL strict); extern BOOL ntfs_collapsible_chars(ntfs_volume *vol, const ntfschar *shortname, int shortlen, const ntfschar *longname, int longlen); diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index 2720864c..cee91e21 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -60,6 +60,7 @@ typedef struct _ntfs_volume ntfs_volume; enum { NTFS_MNT_NONE = 0x00000000, NTFS_MNT_RDONLY = 0x00000001, + NTFS_MNT_MAY_RDONLY = 0x02000000, /* Allow fallback to ro */ NTFS_MNT_FORENSIC = 0x04000000, /* No modification during * mount. */ NTFS_MNT_EXCLUSIVE = 0x08000000, diff --git a/include/ntfs-3g/xattrs.h b/include/ntfs-3g/xattrs.h index d4e43a3e..c5faeac5 100644 --- a/include/ntfs-3g/xattrs.h +++ b/include/ntfs-3g/xattrs.h @@ -19,8 +19,27 @@ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _NTFS_XATTR_H_ -#define _NTFS_XATTR_H_ +#ifndef _NTFS_XATTRS_H_ +#define _NTFS_XATTRS_H_ + +/* + * Flags that modify setxattr() semantics. These flags are also used by a + * number of libntfs-3g functions, such as ntfs_set_ntfs_acl(), which were + * originally tied to extended attributes support but now can be used by + * applications even if the platform does not support extended attributes. + * + * Careful: applications including this header should define HAVE_SETXATTR or + * HAVE_SYS_XATTR_H if the platform supports extended attributes. Otherwise the + * defined flags values may be incorrect (they will be correct for Linux but not + * necessarily for other platforms). + */ +#if defined(HAVE_SETXATTR) || defined(HAVE_SYS_XATTR_H) +#include +#else +#include "compat.h" /* may be needed for ENODATA definition */ +#define XATTR_CREATE 1 +#define XATTR_REPLACE 2 +#endif /* * Identification of data mapped to the system name space @@ -61,6 +80,8 @@ void ntfs_xattr_free_mapping(struct XATTRMAPPING*); enum SYSTEMXATTRS ntfs_xattr_system_type(const char *name, ntfs_volume *vol); +struct SECURITY_CONTEXT; + int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx, enum SYSTEMXATTRS attr, ntfs_inode *ni, ntfs_inode *dir_ni, @@ -73,4 +94,4 @@ int ntfs_xattr_system_removexattr(struct SECURITY_CONTEXT *scx, enum SYSTEMXATTRS attr, ntfs_inode *ni, ntfs_inode *dir_ni); -#endif /* _NTFS_XATTR_H_ */ +#endif /* _NTFS_XATTRS_H_ */ diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c index 4c6c7133..903a05f7 100644 --- a/libfuse-lite/fuse.c +++ b/libfuse-lite/fuse.c @@ -1281,8 +1281,15 @@ static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e, { if (!err) { struct fuse *f = req_fuse(req); +#ifdef __SOLARIS__ + /* Skip forget for negative result */ + if ((fuse_reply_entry(req, e) == -ENOENT) + && (e->ino != 0)) + forget_node(f, e->ino, 1); +#else /* __SOLARIS__ */ if (fuse_reply_entry(req, e) == -ENOENT) forget_node(f, e->ino, 1); +#endif } else reply_err(req, err); } @@ -2081,9 +2088,7 @@ static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino, } } else { reply_err(req, err); -#ifndef __SOLARIS__ pthread_mutex_destroy(&dh->lock); -#endif /* ! __SOLARIS__ */ free(dh); } free(path); @@ -2901,7 +2906,7 @@ static void fuse_lib_help(void) " -o direct_io use direct I/O\n" " -o kernel_cache cache files in kernel\n" #ifdef __SOLARIS__ -" -o [no]auto_cache enable caching based on modification times\n" +" -o [no]auto_cache enable caching based on modification times (off)\n" #endif /* __SOLARIS__ */ " -o umask=M set file permissions (octal)\n" " -o uid=N set file owner\n" diff --git a/libfuse-lite/fuse_lowlevel.c b/libfuse-lite/fuse_lowlevel.c index 496800e7..fb93b074 100644 --- a/libfuse-lite/fuse_lowlevel.c +++ b/libfuse-lite/fuse_lowlevel.c @@ -22,6 +22,16 @@ #include #include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef MAJOR_IN_MKDEV +#include +#endif +#ifdef MAJOR_IN_SYSMACROS +#include +#endif + #define PARAM(inarg) (((const char *)(inarg)) + sizeof(*(inarg))) #define OFFSET_MAX 0x7fffffffffffffffLL @@ -561,10 +571,13 @@ static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) if (req->f->op.mknod) { #if defined(__SOLARIS__) && defined(_LP64) - /* Must unpack the device, as arg->rdev is limited to 32 bits */ + /* + * Must unpack the device, as arg->rdev is limited to 32 bits, + * and must have the same format in 32-bit and 64-bit builds. + */ req->f->op.mknod(req, nodeid, name, arg->mode, - makedev((arg->rdev >> 18) & 0x3ffff, - arg->rdev & 0x3fff)); + makedev((arg->rdev >> 18) & 0x3fff, + arg->rdev & 0x3ffff)); #else req->f->op.mknod(req, nodeid, name, arg->mode, arg->rdev); #endif @@ -1090,6 +1103,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) #ifdef POSIXACLS if (arg->flags & FUSE_DONT_MASK) f->conn.capable |= FUSE_CAP_DONT_MASK; + if (arg->flags & FUSE_POSIX_ACL) + f->conn.capable |= FUSE_CAP_POSIX_ACL; #endif if (arg->flags & FUSE_BIG_WRITES) f->conn.capable |= FUSE_CAP_BIG_WRITES; @@ -1130,6 +1145,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) #ifdef POSIXACLS if (f->conn.want & FUSE_CAP_DONT_MASK) outarg.flags |= FUSE_DONT_MASK; + if (f->conn.want & FUSE_CAP_POSIX_ACL) + outarg.flags |= FUSE_POSIX_ACL; #endif } else { /* Never use a version more recent than supported by the kernel */ @@ -1144,6 +1161,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) #ifdef POSIXACLS if (f->conn.want & FUSE_CAP_DONT_MASK) outarg.flags |= FUSE_DONT_MASK; + if (f->conn.want & FUSE_CAP_POSIX_ACL) + outarg.flags |= FUSE_POSIX_ACL; #endif } } diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index 4e724dbc..680fee1c 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -19,6 +19,7 @@ #include #include #include +#include #ifdef __SOLARIS__ #include diff --git a/libfuse-lite/mount.c b/libfuse-lite/mount.c index 70454f4e..64adee7d 100644 --- a/libfuse-lite/mount.c +++ b/libfuse-lite/mount.c @@ -350,7 +350,7 @@ static int receive_fd(int fd) } cmsg = CMSG_FIRSTHDR(&msg); - if (!cmsg->cmsg_type == SCM_RIGHTS) { + if (cmsg->cmsg_type != SCM_RIGHTS) { fprintf(stderr, "got control message of unknown type %d\n", cmsg->cmsg_type); return -1; @@ -380,10 +380,13 @@ void fuse_kern_unmount(const char *mountpoint, int fd) then the filesystem is already unmounted */ if (res == 1 && (pfd.revents & POLLERR)) return; + /* + * Need to close file descriptor, otherwise synchronous umount + * would recurse into filesystem, and deadlock. + */ + close(fd); } #ifndef __SOLARIS__ - close(fd); - fusermount(1, 0, 1, "", mountpoint); #else /* __SOLARIS__ */ if (geteuid() == 0) { diff --git a/libfuse-lite/mount_util.c b/libfuse-lite/mount_util.c index 8ea5e088..8b317224 100644 --- a/libfuse-lite/mount_util.c +++ b/libfuse-lite/mount_util.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #ifdef __SOLARIS__ diff --git a/libntfs-3g/acls.c b/libntfs-3g/acls.c index 96c49554..3a20221e 100644 --- a/libntfs-3g/acls.c +++ b/libntfs-3g/acls.c @@ -4,7 +4,7 @@ * This module is part of ntfs-3g library, but may also be * integrated in tools running over Linux or Windows * - * Copyright (c) 2007-2015 Jean-Pierre Andre + * Copyright (c) 2007-2016 Jean-Pierre Andre * * 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 @@ -22,10 +22,6 @@ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifdef HAVE_CONFIG_H - /* - * integration into ntfs-3g - */ #include "config.h" #ifdef HAVE_STDIO_H @@ -58,55 +54,6 @@ #include "security.h" #include "acls.h" #include "misc.h" -#else - - /* - * integration into secaudit, check whether Win32, - * may have to be adapted to compiler or something else - */ - -#ifndef WIN32 -#if defined(__WIN32) | defined(__WIN32__) | defined(WNSC) -#define WIN32 1 -#endif -#endif - -#include -#include -#include -#include -#include -#include -#include - - /* - * integration into secaudit/Win32 - */ -#ifdef WIN32 -#include -#include -#define __LITTLE_ENDIAN 1234 -#define __BYTE_ORDER __LITTLE_ENDIAN -#else - /* - * integration into secaudit/STSC - */ -#ifdef STSC -#include -#undef __BYTE_ORDER -#define __BYTE_ORDER __BIG_ENDIAN -#else - /* - * integration into secaudit/Linux - */ -#include -#include -#include -#include -#endif /* STSC */ -#endif /* WIN32 */ -#include "secaudit.h" -#endif /* HAVE_CONFIG_H */ /* * A few useful constants @@ -599,6 +546,7 @@ static BOOL valid_acl(const ACL *pacl, unsigned int end) unsigned int acecnt; unsigned int acesz; unsigned int nace; + unsigned int wantsz; BOOL ok; ok = TRUE; @@ -613,9 +561,16 @@ static BOOL valid_acl(const ACL *pacl, unsigned int end) &((const char*)pacl)[offace]; acesz = le16_to_cpu(pace->size); if (((offace + acesz) > end) - || !ntfs_valid_sid(&pace->sid) - || ((ntfs_sid_size(&pace->sid) + 8) != (int)acesz)) + || !ntfs_valid_sid(&pace->sid)) ok = FALSE; + else { + /* Win10 may insert garbage in the last ACE */ + wantsz = ntfs_sid_size(&pace->sid) + 8; + if (((nace < (acecnt - 1)) + && (wantsz != acesz)) + || (wantsz > acesz)) + ok = FALSE; + } offace += acesz; } } @@ -1638,7 +1593,7 @@ static BOOL build_user_denials(ACL *pacl, grants = OWNER_RIGHTS; } else { if (pxace->id) { - sid = NTFS_FIND_USID(mapping[MAPUSERS], + sid = ntfs_find_usid(mapping[MAPUSERS], pxace->id, (SID*)&defsid); grants = WORLD_RIGHTS; } else { @@ -1794,7 +1749,7 @@ static BOOL build_user_grants(ACL *pacl, grants = OWNER_RIGHTS; } else { if (pxace->id) { - sid = NTFS_FIND_USID(mapping[MAPUSERS], + sid = ntfs_find_usid(mapping[MAPUSERS], pxace->id, (SID*)&defsid); if (sid) sidsz = ntfs_sid_size(sid); @@ -1880,7 +1835,7 @@ static BOOL build_group_denials_grant(ACL *pacl, sid = gsid; else if (pxace->id) - sid = NTFS_FIND_GSID(mapping[MAPGROUPS], + sid = ntfs_find_gsid(mapping[MAPGROUPS], pxace->id, (SID*)&defsid); else { sid = adminsid; @@ -2185,7 +2140,7 @@ static int buildacls_posix(struct MAPPING* const mapping[], case POSIX_ACL_USER : pset->designates++; if (pxace->id) { - sid = NTFS_FIND_USID(mapping[MAPUSERS], + sid = ntfs_find_usid(mapping[MAPUSERS], pxace->id, (SID*)&defsid); if (sid && ntfs_same_sid(sid,usid)) pset->selfuserperms |= pxace->perms; @@ -2196,7 +2151,7 @@ static int buildacls_posix(struct MAPPING* const mapping[], case POSIX_ACL_GROUP : pset->designates++; if (pxace->id) { - sid = NTFS_FIND_GSID(mapping[MAPUSERS], + sid = ntfs_find_gsid(mapping[MAPUSERS], pxace->id, (SID*)&defsid); if (sid && ntfs_same_sid(sid,gsid)) pset->selfgrpperms |= pxace->perms; @@ -2790,10 +2745,10 @@ char *ntfs_build_descr_posix(struct MAPPING* const mapping[], for (k=0; kacccnt; k++) { if ((pxdesc->acl.ace[k].tag == POSIX_ACL_USER) || (pxdesc->acl.ace[k].tag == POSIX_ACL_GROUP)) - newattrsz += 3*40; /* fixme : maximum size */ + newattrsz += 3*MAX_SID_SIZE; } /* account for default ACE's */ - newattrsz += 2*40*pxdesc->defcnt; /* fixme : maximum size */ + newattrsz += 2*MAX_SID_SIZE*pxdesc->defcnt; newattr = (char*)ntfs_malloc(newattrsz); if (newattr) { /* build the main header part */ @@ -3647,7 +3602,7 @@ static uid_t find_tenant(struct MAPPING *const mapping[], pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; if ((pace->type == ACCESS_ALLOWED_ACE_TYPE) && (pace->mask & DIR_WRITE)) { - xid = NTFS_FIND_USER(mapping[MAPUSERS], &pace->sid); + xid = ntfs_find_user(mapping[MAPUSERS], &pace->sid); if (xid) tid = xid; } offace += le16_to_cpu(pace->size); @@ -3782,13 +3737,13 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix( } else { if (ntfs_same_sid(&pace->sid,usid)) groupowns = TRUE; - gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + gid = ntfs_find_group(mapping[MAPGROUPS],&pace->sid); if (gid) { pxace->tag = POSIX_ACL_GROUP; pxace->id = gid; pctx->prevgid = gid; } else { - uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + uid = ntfs_find_user(mapping[MAPUSERS],&pace->sid); if (uid) { pxace->tag = POSIX_ACL_USER; pxace->id = uid; @@ -3811,7 +3766,7 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix( if (pace->type == ACCESS_ALLOWED_ACE_TYPE) pctx->gotowner = TRUE; if (pctx->gotownermask && !pctx->gotowner) { - uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + uid = ntfs_find_user(mapping[MAPUSERS],&pace->sid); pxace->id = uid; pxace->tag = POSIX_ACL_USER; } else @@ -3843,7 +3798,7 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix( pctx->groupmasks++; } else { if (pctx->gotgroup || (pctx->groupmasks > 1)) { - gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + gid = ntfs_find_group(mapping[MAPGROUPS],&pace->sid); if (gid) { pxace->id = gid; pxace->tag = POSIX_ACL_GROUP; @@ -3877,7 +3832,7 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix( pxace->id = -1; pxace->tag = POSIX_ACL_SPECIAL; } else { - uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + uid = ntfs_find_user(mapping[MAPUSERS],&pace->sid); if (uid) { if ((pace->type == ACCESS_DENIED_ACE_TYPE) && (pace->mask & WRITE_OWNER) @@ -3890,7 +3845,7 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix( } pctx->prevuid = uid; } else { - gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + gid = ntfs_find_group(mapping[MAPGROUPS],&pace->sid); if (gid) { if ((pace->type == ACCESS_DENIED_ACE_TYPE) && (pace->mask & WRITE_OWNER) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index bf497f72..89e6f0fa 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -2215,7 +2215,7 @@ retry: } else written = ntfs_pwrite(vol->dev, wpos, to_write, b); - } + } } else written = to_write; /* If everything ok, update progress counters and continue. */ @@ -5142,6 +5142,10 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, */ if (le32_eq(na->type, AT_STANDARD_INFORMATION) || le32_eq(na->type, AT_ATTRIBUTE_LIST)) { ntfs_attr_put_search_ctx(ctx); + if (!NInoAttrList(na->ni) && ntfs_inode_add_attrlist(na->ni)) { + ntfs_log_perror("Could not add attribute list"); + return -1; + } if (ntfs_inode_free_space(na->ni, offsetof(ATTR_RECORD, non_resident_end) + 8)) { ntfs_log_perror("Could not free space in MFT record"); diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 34dabc16..6fd36005 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -40,7 +40,13 @@ #include #endif -#ifdef HAVE_SYS_SYSMACROS_H +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef MAJOR_IN_MKDEV +#include +#endif +#ifdef MAJOR_IN_SYSMACROS #include #endif @@ -61,10 +67,7 @@ #include "security.h" #include "reparse.h" #include "object_id.h" - -#ifdef HAVE_SETXATTR -#include -#endif +#include "xattrs.h" /* * The little endian Unicode strings "$I30", "$SII", "$SDH", "$O" @@ -875,7 +878,7 @@ typedef enum { * and most metadata files have such similar patters. */ -static u32 ntfs_interix_types(ntfs_inode *ni) +u32 ntfs_interix_types(ntfs_inode *ni) { ntfs_attr *na; u32 dt_type; @@ -884,8 +887,14 @@ static u32 ntfs_interix_types(ntfs_inode *ni) dt_type = NTFS_DT_UNKNOWN; na = ntfs_attr_open(ni, AT_DATA, NULL, 0); if (na) { - /* Unrecognized patterns (eg HID + SYST) are plain files */ - dt_type = NTFS_DT_REG; + /* + * Unrecognized patterns (eg HID + SYST for metadata) + * are plain files or directories + */ + if (!le16_andz(ni->mrec->flags, MFT_RECORD_IS_DIRECTORY)) + dt_type = NTFS_DT_DIR; + else + dt_type = NTFS_DT_REG; if (na->data_size <= 1) { if (le32_andz(ni->flags, FILE_ATTR_HIDDEN)) dt_type = (na->data_size ? @@ -1892,20 +1901,23 @@ int ntfs_delete(ntfs_volume *vol, const char *pathname, if (!actx) goto err_out; search: - while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, - 0, NULL, 0, actx)) { + while (!(err = ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, actx))) { + #ifdef DEBUG char *s; + #endif IGNORE_CASE_BOOL case_sensitive = IGNORE_CASE; - errno = 0; fn = (FILE_NAME_ATTR*)((u8*)actx->attr + le16_to_cpu(actx->attr->value_offset)); + #ifdef DEBUG s = ntfs_attr_name_get(fn->file_name, fn->file_name_length); ntfs_log_trace("name: '%s' type: %d dos: %d win32: %d " "case: %d\n", s, fn->file_name_type, looking_for_dos_name, looking_for_win32_name, case_sensitive_match); ntfs_attr_name_free(&s); + #endif if (looking_for_dos_name) { if (fn->file_name_type == FILE_NAME_DOS) break; @@ -1946,7 +1958,7 @@ search: break; } } - if (errno) { + if (err) { /* * If case sensitive search failed, then try once again * ignoring case. @@ -2268,8 +2280,6 @@ ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni) return (dir_ni); } -#ifdef HAVE_SETXATTR - #define MAX_DOS_NAME_LENGTH 12 /* @@ -2418,11 +2428,11 @@ int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, */ ntfs_name_upcase(dosname, doslen, ni->vol->upcase, ni->vol->upcase_len); - if (ntfs_ucstombs(dosname, doslen, &outname, size) < 0) { + outsize = ntfs_ucstombs(dosname, doslen, &outname, 0); + if (outsize < 0) { ntfs_log_error("Cannot represent dosname in current locale.\n"); outsize = -errno; } else { - outsize = strlen(outname); if (value && (outsize <= (int)size)) memcpy(value, outname, outsize); else @@ -2644,9 +2654,12 @@ int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, shortlen = ntfs_mbstoucs(newname, &shortname); if (shortlen > MAX_DOS_NAME_LENGTH) shortlen = MAX_DOS_NAME_LENGTH; - /* make sure the short name has valid chars */ + + /* Make sure the short name has valid chars. + * Note: the short name cannot end with dot or space, but the + * corresponding long name can. */ if ((shortlen < 0) - || ntfs_forbidden_names(ni->vol,shortname,shortlen)) { + || ntfs_forbidden_names(ni->vol,shortname,shortlen,TRUE)) { ntfs_inode_close_in_dir(ni,dir_ni); ntfs_inode_close(dir_ni); res = -errno; @@ -2657,7 +2670,8 @@ int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, if (longlen > 0) { oldlen = get_dos_name(ni, dnum, oldname); if ((oldlen >= 0) - && !ntfs_forbidden_names(ni->vol, longname, longlen)) { + && !ntfs_forbidden_names(ni->vol, longname, longlen, + FALSE)) { if (oldlen > 0) { if (flags & XATTR_CREATE) { res = -1; @@ -2778,5 +2792,3 @@ int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni) } return (res); } - -#endif diff --git a/libntfs-3g/ea.c b/libntfs-3g/ea.c index d07e111b..0a7b20e9 100644 --- a/libntfs-3g/ea.c +++ b/libntfs-3g/ea.c @@ -25,8 +25,6 @@ #include "config.h" #endif -#ifdef HAVE_SETXATTR /* extended attributes support required */ - #ifdef HAVE_STDIO_H #include #endif @@ -46,10 +44,6 @@ #include #endif -#ifdef HAVE_SETXATTR -#include -#endif - #include "types.h" #include "param.h" #include "layout.h" @@ -59,6 +53,7 @@ #include "ea.h" #include "misc.h" #include "logging.h" +#include "xattrs.h" /* * Create a needed attribute (EA or EA_INFORMATION) @@ -398,5 +393,3 @@ int ntfs_remove_ntfs_ea(ntfs_inode *ni) } return (res ? -1 : 0); } - -#endif /* HAVE_SETXATTR */ diff --git a/libntfs-3g/efs.c b/libntfs-3g/efs.c index 7a0c0dbc..eb137d3d 100644 --- a/libntfs-3g/efs.c +++ b/libntfs-3g/efs.c @@ -39,10 +39,6 @@ #include #endif -#ifdef HAVE_SETXATTR -#include -#endif - #ifdef HAVE_SYS_SYSMACROS_H #include #endif @@ -57,8 +53,7 @@ #include "logging.h" #include "misc.h" #include "efs.h" - -#ifdef HAVE_SETXATTR /* extended attributes interface required */ +#include "xattrs.h" static ntfschar logged_utility_stream_name[] = { const_cpu_to_le16('$'), @@ -433,5 +428,3 @@ err_out: ntfs_attr_put_search_ctx(ctx); return (-1); } - -#endif /* HAVE_SETXATTR */ diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index f31aed6b..a5020187 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -143,7 +143,7 @@ static void ntfs_index_ctx_free(ntfs_index_context *icx) { ntfs_log_trace("Entering\n"); - if (!icx->entry) + if (!icx->bad_index && !icx->entry) return; if (icx->actx) @@ -719,7 +719,7 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic ret = ntfs_ie_lookup(key, key_len, icx, &ir->index, &vcn, &ie); if (ret == STATUS_ERROR) { err = errno; - goto err_out; + goto err_lookup; } icx->ir = ir; @@ -780,6 +780,8 @@ descend_into_child_node: goto descend_into_child_node; err_out: + icx->bad_index = TRUE; /* Force icx->* to be freed */ +err_lookup: free(ib); if (!err) err = EIO; diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index e1fe1c20..6562a2be 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -36,9 +36,6 @@ #ifdef HAVE_ERRNO_H #include #endif -#ifdef HAVE_SETXATTR -#include -#endif #include "param.h" #include "compat.h" @@ -57,6 +54,7 @@ #include "ntfstime.h" #include "logging.h" #include "misc.h" +#include "xattrs.h" ntfs_inode *ntfs_inode_base(ntfs_inode *ni) { @@ -1446,8 +1444,6 @@ int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *attr) return ret; } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ - /* * Get high precision NTFS times * @@ -1604,5 +1600,3 @@ int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size, errno = EEXIST; return (ret); } - -#endif /* HAVE_SETXATTR */ diff --git a/libntfs-3g/ioctl.c b/libntfs-3g/ioctl.c index c350164b..2eef5a51 100644 --- a/libntfs-3g/ioctl.c +++ b/libntfs-3g/ioctl.c @@ -49,14 +49,15 @@ #include #endif #include - -#ifdef HAVE_SETXATTR -#include -#endif - #ifdef HAVE_SYS_TYPES_H #include #endif +#ifdef MAJOR_IN_MKDEV +#include +#endif +#ifdef MAJOR_IN_SYSMACROS +#include +#endif #ifdef HAVE_SYS_STAT_H #include diff --git a/libntfs-3g/logfile.c b/libntfs-3g/logfile.c index f6dba974..f19d499e 100644 --- a/libntfs-3g/logfile.c +++ b/libntfs-3g/logfile.c @@ -119,7 +119,7 @@ static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos) /* 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) || + if (usa_ofs < offsetof(RESTART_PAGE_HEADER, usn) || usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) { ntfs_log_error("$LogFile restart page specifies " "inconsistent update sequence array offset.\n"); @@ -134,7 +134,7 @@ skip_usa_checks: */ 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 < offsetof(RESTART_PAGE_HEADER, usn)) || ra_ofs > logfile_system_page_size) { ntfs_log_error("$LogFile restart page specifies " "inconsistent restart area offset.\n"); diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 0e50641c..0076621b 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -1629,6 +1629,7 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) int err; u32 usa_ofs; le16 seq_no, usn; + BOOL oldwarn; if (base_ni) ntfs_log_enter("Entering (allocating an extent mft record for " @@ -1742,10 +1743,22 @@ found_free_rec: if (!m) goto undo_mftbmp_alloc; + /* + * As this is allocating a new record, do not expect it to have + * been initialized previously, so do not warn over bad fixups + * (hence avoid warn flooding when an NTFS partition has been wiped). + */ + oldwarn = !NVolNoFixupWarn(vol); + NVolSetNoFixupWarn(vol); if (ntfs_mft_record_read(vol, bit, m)) { + if (oldwarn) + NVolClearNoFixupWarn(vol); free(m); goto undo_mftbmp_alloc; } + if (oldwarn) + NVolClearNoFixupWarn(vol); + /* Sanity check that the mft record is really not in use. */ if (ntfs_is_file_record(m->magic) && !le16_andz(m->flags, MFT_RECORD_IN_USE)) { ntfs_log_error("Inode %lld is used but it wasn't marked in " diff --git a/libntfs-3g/mst.c b/libntfs-3g/mst.c index 9dff7738..af17edbc 100644 --- a/libntfs-3g/mst.c +++ b/libntfs-3g/mst.c @@ -31,6 +31,21 @@ #include "mst.h" #include "logging.h" +/* + * Basic validation of a NTFS multi-sector record. The record size must be a + * multiple of the logical sector size; and the update sequence array must be + * properly aligned, of the expected length, and must end before the last le16 + * in the first logical sector. + */ +static BOOL +is_valid_record(u32 size, u16 usa_ofs, u16 usa_count) +{ + return size % NTFS_BLOCK_SIZE == 0 && + usa_ofs % 2 == 0 && + usa_count == 1 + (size / NTFS_BLOCK_SIZE) && + usa_ofs + ((u32)usa_count * 2) <= NTFS_BLOCK_SIZE - 2; +} + /** * ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data * @b: pointer to the data to deprotect @@ -57,12 +72,9 @@ int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, /* 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) { + usa_count = le16_to_cpu(b->usa_count); + + if (!is_valid_record(size, usa_ofs, usa_count)) { errno = EINVAL; if (warn) { ntfs_log_perror("%s: magic: 0x%08lx size: %ld " @@ -91,7 +103,7 @@ int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, /* * Check for incomplete multi sector transfer(s). */ - while (usa_count--) { + while (--usa_count) { if (*data_pos != usn) { /* * Incomplete multi sector transfer detected! )-: @@ -109,10 +121,10 @@ int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, data_pos += NTFS_BLOCK_SIZE/sizeof(u16); } /* Re-setup the variables. */ - usa_count = le16_to_cpu(b->usa_count) - 1; + usa_count = le16_to_cpu(b->usa_count); data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; /* Fixup all sectors. */ - while (usa_count--) { + while (--usa_count) { /* * Increment position in usa and restore original data from * the usa into the data buffer. @@ -171,12 +183,9 @@ int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) } /* 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) { + usa_count = le16_to_cpu(b->usa_count); + + if (!is_valid_record(size, usa_ofs, usa_count)) { errno = EINVAL; ntfs_log_perror("%s", __FUNCTION__); return -1; @@ -195,7 +204,7 @@ int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) /* Position in data of first le16 that needs fixing up. */ data_pos = (le16*)b + NTFS_BLOCK_SIZE/sizeof(le16) - 1; /* Fixup all sectors. */ - while (usa_count--) { + while (--usa_count) { /* * Increment the position in the usa and save the * original data from the data buffer into the usa. @@ -223,7 +232,7 @@ 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; + u16 usa_count = le16_to_cpu(b->usa_count); ntfs_log_trace("Entering\n"); @@ -234,7 +243,7 @@ void ntfs_mst_post_write_fixup(NTFS_RECORD *b) data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; /* Fixup all sectors. */ - while (usa_count--) { + while (--usa_count) { /* * Increment position in usa and restore original data from * the usa into the data buffer. diff --git a/libntfs-3g/object_id.c b/libntfs-3g/object_id.c index 299357e5..bd72d026 100644 --- a/libntfs-3g/object_id.c +++ b/libntfs-3g/object_id.c @@ -37,11 +37,6 @@ #ifdef HAVE_SYS_STAT_H #include #endif - -#ifdef HAVE_SETXATTR -#include -#endif - #ifdef HAVE_SYS_SYSMACROS_H #include #endif @@ -59,6 +54,7 @@ #include "object_id.h" #include "logging.h" #include "misc.h" +#include "xattrs.h" /* * Endianness considerations @@ -130,7 +126,6 @@ struct OBJECT_ID_INDEX { /* index entry in $Extend/$ObjId */ static ntfschar objid_index_name[] = { const_cpu_to_le16('$'), const_cpu_to_le16('O') }; -#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* * Set the index for a new object id @@ -176,8 +171,6 @@ static int set_object_id_index(ntfs_inode *ni, ntfs_index_context *xo, return (ntfs_ie_add(xo,(INDEX_ENTRY*)&indx)); } -#endif /* HAVE_SETXATTR */ - /* * Open the $Extend/$ObjId file and its index * @@ -213,7 +206,6 @@ static ntfs_index_context *open_object_id_index(ntfs_volume *vol) return (xo); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* * Merge object_id data stored in the index into @@ -263,7 +255,6 @@ static int merge_index_data(ntfs_inode *ni, return (res); } -#endif /* HAVE_SETXATTR */ /* * Remove an object id index entry if attribute present @@ -311,7 +302,6 @@ static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo, return (ret); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* * Update the object id and index @@ -417,7 +407,6 @@ static int add_object_id(ntfs_inode *ni, int flags) return (res); } -#endif /* HAVE_SETXATTR */ /* * Delete an object_id index entry @@ -456,7 +445,6 @@ int ntfs_delete_object_id_index(ntfs_inode *ni) return (res); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* * Get the ntfs object id into an extended attribute @@ -636,5 +624,3 @@ int ntfs_remove_ntfs_object_id(ntfs_inode *ni) } return (res ? -1 : 0); } - -#endif /* HAVE_SETXATTR */ diff --git a/libntfs-3g/reparse.c b/libntfs-3g/reparse.c index db8e233b..177a61e8 100644 --- a/libntfs-3g/reparse.c +++ b/libntfs-3g/reparse.c @@ -3,7 +3,7 @@ * * This module is part of ntfs-3g library * - * Copyright (c) 2008-2014 Jean-Pierre Andre + * Copyright (c) 2008-2016 Jean-Pierre Andre * * 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 @@ -37,11 +37,6 @@ #ifdef HAVE_SYS_STAT_H #include #endif - -#ifdef HAVE_SETXATTR -#include -#endif - #ifdef HAVE_SYS_SYSMACROS_H #include #endif @@ -60,6 +55,7 @@ #include "logging.h" #include "misc.h" #include "reparse.h" +#include "xattrs.h" struct MOUNT_POINT_REPARSE_DATA { /* reparse data for junctions */ le16 subst_name_offset; @@ -448,8 +444,13 @@ static BOOL valid_reparse_data(ntfs_inode *ni, + (!le32_andz(reparse_attr->reparse_tag, IO_REPARSE_TAG_IS_MICROSOFT) ? 0 : sizeof(GUID))) == size); if (ok) { - /* switch (reparse_attr->reparse_tag) { */ + do { if (le32_eq(reparse_attr->reparse_tag, IO_REPARSE_TAG_MOUNT_POINT)) { + if (size < sizeof(REPARSE_POINT) + + sizeof(struct MOUNT_POINT_REPARSE_DATA)) { + ok = FALSE; + break; + } mount_point_data = (const struct MOUNT_POINT_REPARSE_DATA*) reparse_attr->reparse_data; offs = le16_to_cpu(mount_point_data->subst_name_offset); @@ -460,8 +461,13 @@ static BOOL valid_reparse_data(ntfs_inode *ni, + sizeof(struct MOUNT_POINT_REPARSE_DATA) + offs + lth)) > size)) ok = FALSE; - } - else if (le32_eq(reparse_attr->reparse_tag, IO_REPARSE_TAG_SYMLINK)) { + break; + } else if (le32_eq(reparse_attr->reparse_tag, IO_REPARSE_TAG_SYMLINK)) { + if (size < sizeof(REPARSE_POINT) + + sizeof(struct SYMLINK_REPARSE_DATA)) { + ok = FALSE; + break; + } symlink_data = (const struct SYMLINK_REPARSE_DATA*) reparse_attr->reparse_data; offs = le16_to_cpu(symlink_data->subst_name_offset); @@ -470,10 +476,10 @@ static BOOL valid_reparse_data(ntfs_inode *ni, + sizeof(struct SYMLINK_REPARSE_DATA) + offs + lth)) > size) ok = FALSE; - } - else { - /* break; */ - } + break; + } else { + break; + } } while(0); } if (!ok) errno = EINVAL; @@ -718,8 +724,7 @@ static char *ntfs_get_rellink(ntfs_inode *ni, ntfschar *junction, int count) * symbolic link or directory junction */ -char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point, - int *pattr_size) +char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point) { s64 attr_size = 0; char *target; @@ -814,7 +819,6 @@ char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point, } free(reparse_attr); } - *pattr_size = attr_size; if (bad) errno = EOPNOTSUPP; return (target); @@ -849,7 +853,6 @@ BOOL ntfs_possible_symlink(ntfs_inode *ni) return (possible); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* * Set the index for new reparse data @@ -888,7 +891,6 @@ static int set_reparse_index(ntfs_inode *ni, ntfs_index_context *xr, return (ntfs_ie_add(xr,(INDEX_ENTRY*)&indx)); } -#endif /* HAVE_SETXATTR */ /* * Remove a reparse data index entry if attribute present @@ -965,7 +967,6 @@ static ntfs_index_context *open_reparse_index(ntfs_volume *vol) return (xr); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* * Update the reparse data and index @@ -1031,7 +1032,6 @@ static int update_reparse_data(ntfs_inode *ni, ntfs_index_context *xr, return (res); } -#endif /* HAVE_SETXATTR */ /* * Delete a reparse index entry @@ -1070,7 +1070,6 @@ int ntfs_delete_reparse_index(ntfs_inode *ni) return (res); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* * Get the ntfs reparse data into an extended attribute @@ -1255,4 +1254,31 @@ int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni) return (res ? -1 : 0); } -#endif /* HAVE_SETXATTR */ + +/* + * Get the reparse data into a buffer + * + * Returns the buffer if the reparse data exists and is valid + * NULL otherwise (with errno set according to the cause). + * When a buffer is returned, it has to be freed by caller. + */ + +REPARSE_POINT *ntfs_get_reparse_point(ntfs_inode *ni) +{ + s64 attr_size = 0; + REPARSE_POINT *reparse_attr; + + reparse_attr = (REPARSE_POINT*)NULL; + if (ni) { + reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, + AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); + if (reparse_attr + && !valid_reparse_data(ni, reparse_attr, attr_size)) { + free(reparse_attr); + reparse_attr = (REPARSE_POINT*)NULL; + errno = ENOENT; + } + } else + errno = EINVAL; + return (reparse_attr); +} diff --git a/libntfs-3g/security.c b/libntfs-3g/security.c index 63c54e37..e7d4eb70 100644 --- a/libntfs-3g/security.c +++ b/libntfs-3g/security.c @@ -41,9 +41,6 @@ #ifdef HAVE_FCNTL_H #include #endif -#ifdef HAVE_SETXATTR -#include -#endif #ifdef HAVE_SYS_STAT_H #include #endif @@ -64,6 +61,7 @@ #include "acls.h" #include "cache.h" #include "misc.h" +#include "xattrs.h" /* * JPA NTFS constants or structs @@ -525,8 +523,7 @@ static int entersecurity_data(ntfs_volume *vol, */ res = ntfs_attr_shrink_size(vol->secure_ni,STREAM_SDS, 4, offs - gap + ALIGN_SDS_BLOCK + fullsz); - } - else + } else errno = ENOSPC; free(fullattr); } else @@ -3063,7 +3060,6 @@ BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni) return (allowed); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ #if POSIXACLS @@ -3242,7 +3238,6 @@ int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, return (res ? -1 : 0); } -#endif /* HAVE_SETXATTR */ /* * Set new permissions to a file @@ -4383,7 +4378,6 @@ int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path, return (!scx->mapping[MAPUSERS] || link_group_members(scx)); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* * Get the ntfs attribute into an extended attribute @@ -4469,58 +4463,83 @@ int ntfs_set_ntfs_attrib(ntfs_inode *ni, return (res ? -1 : 0); } -#endif /* HAVE_SETXATTR */ /* - * Open $Secure once for all - * returns zero if it succeeds - * non-zero if it fails. This is not an error (on NTFS v1.x) + * Open the volume's security descriptor index ($Secure) + * + * returns 0 if it succeeds + * -1 with errno set if it fails and the volume is NTFS v3.0+ */ - - int ntfs_open_secure(ntfs_volume *vol) { ntfs_inode *ni; - int res; + ntfs_index_context *sii; + ntfs_index_context *sdh; - res = -1; - vol->secure_ni = (ntfs_inode*)NULL; - vol->secure_xsii = (ntfs_index_context*)NULL; - vol->secure_xsdh = (ntfs_index_context*)NULL; - if (vol->major_ver >= 3) { - /* make sure this is a genuine $Secure inode 9 */ - ni = ntfs_pathname_to_inode(vol, NULL, "$Secure"); - if (ni && (ni->mft_no == 9)) { - vol->secure_reentry = 0; - vol->secure_xsii = ntfs_index_ctx_get(ni, - sii_stream, 4); - vol->secure_xsdh = ntfs_index_ctx_get(ni, - sdh_stream, 4); - if (ni && vol->secure_xsii && vol->secure_xsdh) { - vol->secure_ni = ni; - res = 0; - } - } + if (vol->secure_ni) /* Already open? */ + return 0; + + ni = ntfs_pathname_to_inode(vol, NULL, "$Secure"); + if (!ni) + goto err; + + if (ni->mft_no != FILE_Secure) { + ntfs_log_error("$Secure does not have expected inode number!"); + errno = EINVAL; + goto err_close_ni; } - return (res); + + /* Allocate the needed index contexts. */ + sii = ntfs_index_ctx_get(ni, sii_stream, 4); + if (!sii) + goto err_close_ni; + + sdh = ntfs_index_ctx_get(ni, sdh_stream, 4); + if (!sdh) + goto err_close_sii; + + vol->secure_xsdh = sdh; + vol->secure_xsii = sii; + vol->secure_ni = ni; + return 0; + +err_close_sii: + ntfs_index_ctx_put(sii); +err_close_ni: + ntfs_inode_close(ni); +err: + /* Failing on NTFS pre-v3.0 is expected. */ + if (vol->major_ver < 3) + return 0; + ntfs_log_perror("Failed to open $Secure"); + return -1; } /* - * Final cleaning + * Close the volume's security descriptor index ($Secure) + * + * returns 0 if it succeeds + * -1 with errno set if it fails + */ +int ntfs_close_secure(ntfs_volume *vol) +{ + int res = 0; + + if (vol->secure_ni) { + ntfs_index_ctx_put(vol->secure_xsdh); + ntfs_index_ctx_put(vol->secure_xsii); + res = ntfs_inode_close(vol->secure_ni); + vol->secure_ni = NULL; + } + return res; +} + +/* + * Destroy a security context * Allocated memory is freed to facilitate the detection of memory leaks */ - -void ntfs_close_secure(struct SECURITY_CONTEXT *scx) +void ntfs_destroy_security_context(struct SECURITY_CONTEXT *scx) { - ntfs_volume *vol; - - vol = scx->vol; - if (vol->secure_ni) { - ntfs_index_ctx_put(vol->secure_xsii); - ntfs_index_ctx_put(vol->secure_xsdh); - ntfs_inode_close(vol->secure_ni); - - } ntfs_free_mapping(scx->mapping); free_caches(scx); } @@ -5339,7 +5358,6 @@ struct SECURITY_API *ntfs_initialize_file_security(const char *device, scx->vol->secure_flags = 0; /* accept no mapping and no $Secure */ ntfs_build_mapping(scx,(const char*)NULL,TRUE); - ntfs_open_secure(vol); } else { if (scapi) free(scapi); @@ -5371,7 +5389,7 @@ BOOL ntfs_leave_file_security(struct SECURITY_API *scapi) ok = FALSE; if (scapi && (scapi->magic == MAGIC_API) && scapi->security.vol) { vol = scapi->security.vol; - ntfs_close_secure(&scapi->security); + ntfs_destroy_security_context(&scapi->security); free(scapi); if (!ntfs_umount(vol, 0)) ok = TRUE; diff --git a/libntfs-3g/unistr.c b/libntfs-3g/unistr.c index 80e57f6e..fbedd891 100644 --- a/libntfs-3g/unistr.c +++ b/libntfs-3g/unistr.c @@ -59,7 +59,11 @@ #include "logging.h" #include "misc.h" -#define NOREVBOM 0 /* JPA rejecting U+FFFE and U+FFFF, open to debate */ +#ifndef ALLOW_BROKEN_UNICODE +/* Erik allowing broken UTF-16 surrogate pairs and U+FFFE and U+FFFF by default, + * open to debate. */ +#define ALLOW_BROKEN_UNICODE 1 +#endif /* !defined(ALLOW_BROKEN_UNICODE) */ /* * IMPORTANT @@ -139,14 +143,24 @@ BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len, * @name1_len: length of first Unicode name to compare * @name2: second Unicode name to compare * @name2_len: length of second Unicode name to compare - * @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) + * @ic: either CASE_SENSITIVE or IGNORE_CASE (see below) + * @upcase: upcase table + * @upcase_len: upcase table size * + * If @ic is CASE_SENSITIVE, then the names are compared primarily ignoring + * case, but if the names are equal ignoring case, then they are compared + * case-sensitively. As an example, "abc" would collate before "BCD" (since + * "abc" and "BCD" differ ignoring case and 'A' < 'B') but after "ABC" (since + * "ABC" and "abc" are equal ignoring case and 'A' < 'a'). This matches the + * collation order of filenames as indexed in NTFS directories. + * + * If @ic is IGNORE_CASE, then the names are only compared case-insensitively + * and are considered to match if and only if they are equal ignoring case. + * + * 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 - * + * 0 if the names match, or + * 1 if the second name collates before the first one */ int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, const ntfschar *name2, const u32 name2_len, @@ -158,7 +172,7 @@ int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, u16 u1, u2; #ifdef DEBUG - if (!name1 || !name2 || (ic && (!upcase || !upcase_len))) { + if (!name1 || !name2 || !upcase || !upcase_len) { ntfs_log_debug("ntfs_names_collate received NULL pointer!\n"); exit(1); } @@ -201,9 +215,9 @@ int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, return 1; } else { do { - u1 = c1 = le16_to_cpu(*name1); + u1 = le16_to_cpu(*name1); name1++; - u2 = c2 = le16_to_cpu(*name2); + u2 = le16_to_cpu(*name2); name2++; if (u1 < upcase_len) u1 = le16_to_cpu(upcase[u1]); @@ -444,10 +458,15 @@ void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr, */ /* - * Return the amount of 8-bit elements in UTF-8 needed (without the terminating - * null) to store a given UTF-16LE string. + * Return the number of bytes in UTF-8 needed (without the terminating null) to + * store the given UTF-16LE string. * - * Return -1 with errno set if string has invalid byte sequence or too long. + * On error, -1 is returned, and errno is set to the error code. The following + * error codes can be expected: + * EILSEQ The input string is not valid UTF-16LE (only possible + * if compiled without ALLOW_BROKEN_UNICODE). + * ENAMETOOLONG The length of the UTF-8 string in bytes (without the + * terminating null) would exceed @outs_len. */ static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_len) { @@ -456,14 +475,28 @@ static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_l BOOL surrog; surrog = FALSE; - for (i = 0; i < ins_len && !le16_cmpz(ins[i]); i++) { + for (i = 0; i < ins_len && !le16_cmpz(ins[i]) && count <= outs_len; i++) { unsigned short c = le16_to_cpu(ins[i]); if (surrog) { if ((c >= 0xdc00) && (c < 0xe000)) { surrog = FALSE; count += 4; - } else + } else { +#if ALLOW_BROKEN_UNICODE + /* The first UTF-16 unit of a surrogate pair has + * a value between 0xd800 and 0xdc00. It can be + * encoded as an individual UTF-8 sequence if we + * cannot combine it with the next UTF-16 unit + * unit as a surrogate pair. */ + surrog = FALSE; + count += 3; + + --i; + continue; +#else goto fail; +#endif /* ALLOW_BROKEN_UNICODE */ + } } else if (c < 0x80) count++; @@ -473,21 +506,30 @@ static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_l count += 3; else if (c < 0xdc00) surrog = TRUE; -#if NOREVBOM - else if ((c >= 0xe000) && (c < 0xfffe)) -#else +#if ALLOW_BROKEN_UNICODE + else if (c < 0xe000) + count += 3; else if (c >= 0xe000) -#endif +#else + else if ((c >= 0xe000) && (c < 0xfffe)) +#endif /* ALLOW_BROKEN_UNICODE */ count += 3; else goto fail; - if (count > outs_len) { - errno = ENAMETOOLONG; - goto out; - } } - if (surrog) + + if (surrog && count <= outs_len) { +#if ALLOW_BROKEN_UNICODE + count += 3; /* ending with a single surrogate */ +#else goto fail; +#endif /* ALLOW_BROKEN_UNICODE */ + } + + if (count > outs_len) { + errno = ENAMETOOLONG; + goto out; + } ret = count; out: @@ -502,7 +544,7 @@ fail: * @ins: input utf16 string buffer * @ins_len: length of input string in utf16 characters * @outs: on return contains the (allocated) output multibyte string - * @outs_len: length of output buffer in bytes + * @outs_len: length of output buffer in bytes (ignored if *@outs is NULL) * * Return -1 with errno set if string has invalid byte sequence or too long. */ @@ -521,10 +563,16 @@ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, int halfpair; halfpair = 0; - if (!*outs) + if (!*outs) { + /* If no output buffer was provided, we will allocate one and + * limit its length to PATH_MAX. Note: we follow the standard + * convention of PATH_MAX including the terminating null. */ outs_len = PATH_MAX; + } - size = utf16_to_utf8_size(ins, ins_len, outs_len); + /* The size *with* the terminating null is limited to @outs_len, + * so the size *without* the terminating null is limited to one less. */ + size = utf16_to_utf8_size(ins, ins_len, outs_len - 1); if (size < 0) goto out; @@ -548,8 +596,24 @@ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, *t++ = 0x80 + ((c >> 6) & 15) + ((halfpair & 3) << 4); *t++ = 0x80 + (c & 63); halfpair = 0; - } else + } else { +#if ALLOW_BROKEN_UNICODE + /* The first UTF-16 unit of a surrogate pair has + * a value between 0xd800 and 0xdc00. It can be + * encoded as an individual UTF-8 sequence if we + * cannot combine it with the next UTF-16 unit + * unit as a surrogate pair. */ + *t++ = 0xe0 | (halfpair >> 12); + *t++ = 0x80 | ((halfpair >> 6) & 0x3f); + *t++ = 0x80 | (halfpair & 0x3f); + halfpair = 0; + + --i; + continue; +#else goto fail; +#endif /* ALLOW_BROKEN_UNICODE */ + } } else if (c < 0x80) { *t++ = c; } else { @@ -562,6 +626,13 @@ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, *t++ = 0x80 | (c & 0x3f); } else if (c < 0xdc00) halfpair = c; +#if ALLOW_BROKEN_UNICODE + else if (c < 0xe000) { + *t++ = 0xe0 | (c >> 12); + *t++ = 0x80 | ((c >> 6) & 0x3f); + *t++ = 0x80 | (c & 0x3f); + } +#endif /* ALLOW_BROKEN_UNICODE */ else if (c >= 0xe000) { *t++ = 0xe0 | (c >> 12); *t++ = 0x80 | ((c >> 6) & 0x3f); @@ -570,6 +641,13 @@ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, goto fail; } } +#if ALLOW_BROKEN_UNICODE + if (halfpair) { /* ending with a single surrogate */ + *t++ = 0xe0 | (halfpair >> 12); + *t++ = 0x80 | ((halfpair >> 6) & 0x3f); + *t++ = 0x80 | (halfpair & 0x3f); + } +#endif /* ALLOW_BROKEN_UNICODE */ *t = '\0'; #if defined(__APPLE__) || defined(__DARWIN__) @@ -691,15 +769,16 @@ static int utf8_to_unicode(u32 *wc, const char *s) | ((u32)(s[1] & 0x3F) << 6) | ((u32)(s[2] & 0x3F)); /* Check valid ranges */ -#if NOREVBOM +#if ALLOW_BROKEN_UNICODE if (((*wc >= 0x800) && (*wc <= 0xD7FF)) - || ((*wc >= 0xe000) && (*wc <= 0xFFFD))) + || ((*wc >= 0xD800) && (*wc <= 0xDFFF)) + || ((*wc >= 0xe000) && (*wc <= 0xFFFF))) return 3; #else if (((*wc >= 0x800) && (*wc <= 0xD7FF)) - || ((*wc >= 0xe000) && (*wc <= 0xFFFF))) + || ((*wc >= 0xe000) && (*wc <= 0xFFFD))) return 3; -#endif +#endif /* ALLOW_BROKEN_UNICODE */ } goto fail; /* four-byte */ @@ -804,7 +883,7 @@ fail: * @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 + * @outs_len: length of output buffer in bytes (ignored if *@outs is NULL) * * Convert the input little endian, 2-byte Unicode string @ins, of length * @ins_len into the multibyte string format dictated by the current locale. @@ -1236,7 +1315,8 @@ void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len) { 0x3c2, 0x3c2, 0x0, 2, 6, 1 }, { 0x3d7, 0x3d7, -0x8, 2, 6, 1 }, { 0x515, 0x523, -0x1, 2, 6, 1 }, - { 0x1d79, 0x1d79, 0x8a04, 2, 6, 1 }, + /* below, -0x75fc stands for 0x8a04 and truncation */ + { 0x1d79, 0x1d79, -0x75fc, 2, 6, 1 }, { 0x1efb, 0x1eff, -0x1, 2, 6, 1 }, { 0x1fc3, 0x1ff3, 0x9, 48, 6, 1 }, { 0x1fcc, 0x1ffc, 0x0, 48, 6, 1 }, @@ -1397,10 +1477,14 @@ void ntfs_ucsfree(ntfschar *ucs) * Check whether a name contains no chars forbidden * for DOS or Win32 use * + * If @strict is TRUE, then trailing dots and spaces are forbidden. + * These names are technically allowed in the Win32 namespace, but + * they can be problematic. See comment for FILE_NAME_WIN32. + * * If there is a bad char, errno is set to EINVAL */ -BOOL ntfs_forbidden_chars(const ntfschar *name, int len) +BOOL ntfs_forbidden_chars(const ntfschar *name, int len, BOOL strict) { BOOL forbidden; int ch; @@ -1413,9 +1497,9 @@ BOOL ntfs_forbidden_chars(const ntfschar *name, int len) | (1L << ('>' - 0x20)) | (1L << ('?' - 0x20)); - forbidden = (len == 0) - || le16_eq(name[len-1], const_cpu_to_le16(' ')) - || le16_eq(name[len-1], const_cpu_to_le16('.')); + forbidden = (len == 0) || + (strict && (le16_eq(name[len-1], const_cpu_to_le16(' ')) || + le16_eq(name[len-1], const_cpu_to_le16('.')))); for (i=0; i= 3)) { /* * Rough hash check to tell whether the first couple of chars diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 09001ce7..2c7e4fc4 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -74,6 +74,7 @@ #include "cache.h" #include "realpath.h" #include "misc.h" +#include "security.h" const char *ntfs_home = "News, support and information: http://tuxera.com\n"; @@ -97,6 +98,11 @@ static const char *hibernated_volume_msg = "Windows fully (no hibernation or fast restarting), or mount the volume\n" "read-only with the 'ro' mount option.\n"; +static const char *fallback_readonly_msg = +"Falling back to read-only mount because the NTFS partition is in an\n" +"unsafe state. Please resume and shutdown Windows fully (no hibernation\n" +"or fast restarting.)\n"; + static const char *unclean_journal_msg = "Write access is denied because the disk wasn't safely powered\n" "off and the 'norecover' mount option was specified.\n"; @@ -167,6 +173,9 @@ static int __ntfs_volume_release(ntfs_volume *v) { int err = 0; + if (ntfs_close_secure(v)) + ntfs_error_set(&err); + if (ntfs_inode_free(&v->vol_ni)) ntfs_error_set(&err); /* @@ -914,7 +923,9 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) int i, j, eo; unsigned int k; u32 u; + BOOL need_fallback_ro; + need_fallback_ro = FALSE; vol = ntfs_volume_startup(dev, flags); if (!vol) return NULL; @@ -1227,26 +1238,46 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) ntfs_log_perror("Failed to close $AttrDef"); goto error_exit; } + + /* Open $Secure. */ + if (ntfs_open_secure(vol)) + goto error_exit; + /* * Check for dirty logfile and hibernated Windows. * We care only about read-write mounts. */ if (!(flags & (NTFS_MNT_RDONLY | NTFS_MNT_FORENSIC))) { if (!(flags & NTFS_MNT_IGNORE_HIBERFILE) && - ntfs_volume_check_hiberfile(vol, 1) < 0) - goto error_exit; + ntfs_volume_check_hiberfile(vol, 1) < 0) { + if (flags & NTFS_MNT_MAY_RDONLY) + need_fallback_ro = TRUE; + else + goto error_exit; + } if (ntfs_volume_check_logfile(vol) < 0) { /* Always reject cached metadata for now */ - if (!(flags & NTFS_MNT_RECOVER) || (errno == EPERM)) - goto error_exit; - ntfs_log_info("The file system wasn't safely " - "closed on Windows. Fixing.\n"); - if (ntfs_logfile_reset(vol)) - goto error_exit; + if (!(flags & NTFS_MNT_RECOVER) || (errno == EPERM)) { + if (flags & NTFS_MNT_MAY_RDONLY) + need_fallback_ro = TRUE; + else + goto error_exit; + } else { + ntfs_log_info("The file system wasn't safely " + "closed on Windows. Fixing.\n"); + if (ntfs_logfile_reset(vol)) + goto error_exit; + } } /* make $TXF_DATA resident if present on the root directory */ - if (fix_txf_data(vol)) - goto error_exit; + if (!(flags & NTFS_MNT_RDONLY) && !need_fallback_ro) { + if (fix_txf_data(vol)) + goto error_exit; + } + } + if (need_fallback_ro) { + NVolSetReadOnly(vol); + ntfs_log_error("%s", fallback_readonly_msg); } return vol; diff --git a/libntfs-3g/win32_io.c b/libntfs-3g/win32_io.c index c4c09d7d..e5cfb640 100644 --- a/libntfs-3g/win32_io.c +++ b/libntfs-3g/win32_io.c @@ -150,6 +150,7 @@ enum { /* see http://msdn.microsoft.com/en-us/library/cc704588(v=prot.10).aspx * STATUS_FILE_NOT_FOUND = 0xC0000028, STATUS_OBJECT_NAME_INVALID = 0xC0000033, STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034, + STATUS_SHARING_VIOLATION = 0xC0000043, STATUS_INVALID_PARAMETER_1 = 0xC00000EF, STATUS_IO_DEVICE_ERROR = 0xC0000185, STATUS_GUARD_PAGE_VIOLATION = 0x80000001 @@ -354,6 +355,8 @@ static int ntfs_ntstatus_to_errno(NTSTATUS status) case STATUS_IO_DEVICE_ERROR : case STATUS_END_OF_FILE : return (EIO); + case STATUS_SHARING_VIOLATION : + return (EBUSY); default: /* generic message */ return ENOMSG; diff --git a/libntfs-3g/xattrs.c b/libntfs-3g/xattrs.c index 6da81467..2b7e7091 100644 --- a/libntfs-3g/xattrs.c +++ b/libntfs-3g/xattrs.c @@ -23,8 +23,6 @@ #include "config.h" #endif -#ifdef HAVE_SETXATTR /* extended attributes support required */ - #ifdef HAVE_STDIO_H #include #endif @@ -83,10 +81,6 @@ struct LE_POSIX_ACL { #endif #endif -static const char xattr_ntfs_3g[] = "ntfs-3g."; -static const char nf_ns_user_prefix[] = "user."; -static const int nf_ns_user_prefix_len = sizeof(nf_ns_user_prefix) - 1; - static const char nf_ns_xattr_ntfs_acl[] = "system.ntfs_acl"; static const char nf_ns_xattr_attrib[] = "system.ntfs_attrib"; static const char nf_ns_xattr_attrib_be[] = "system.ntfs_attrib_be"; @@ -798,5 +792,3 @@ int ntfs_xattr_system_removexattr(struct SECURITY_CONTEXT *scx, } return (res); } - -#endif /* HAVE_SETXATTR */ diff --git a/ntfsprogs/Makefile.am b/ntfsprogs/Makefile.am index 0bbc70c5..f4f9d1b6 100644 --- a/ntfsprogs/Makefile.am +++ b/ntfsprogs/Makefile.am @@ -17,7 +17,8 @@ if ENABLE_NTFSPROGS bin_PROGRAMS = ntfsfix ntfsinfo ntfscluster ntfsls ntfscat ntfscmp sbin_PROGRAMS = mkntfs ntfslabel ntfsundelete ntfsresize ntfsclone \ ntfscp -EXTRA_PROGRAM_NAMES = ntfswipe ntfstruncate ntfsrecover +EXTRA_PROGRAM_NAMES = ntfswipe ntfstruncate ntfsrecover \ + ntfsusermap ntfssecaudit QUARANTINED_PROGRAM_NAMES = ntfsdump_logfile ntfsmftalloc ntfsmove ntfsck \ ntfsfallocate @@ -26,7 +27,8 @@ man_MANS = mkntfs.8 ntfsfix.8 ntfslabel.8 ntfsinfo.8 \ ntfsundelete.8 ntfsresize.8 ntfsprogs.8 ntfsls.8 \ ntfsclone.8 ntfscluster.8 ntfscat.8 ntfscp.8 \ ntfscmp.8 ntfswipe.8 ntfstruncate.8 \ - ntfsdecrypt.8 ntfsfallocate.8 ntfsrecover.8 + ntfsdecrypt.8 ntfsfallocate.8 ntfsrecover.8 \ + ntfsusermap.8 ntfssecaudit.8 EXTRA_MANS = CLEANFILES = $(EXTRA_PROGRAMS) @@ -106,6 +108,14 @@ ntfsrecover_SOURCES = playlog.c ntfsrecover.c utils.c utils.h ntfsrecover.h ntfsrecover_LDADD = $(AM_LIBS) $(NTFSRECOVER_LIBS) ntfsrecover_LDFLAGS = $(AM_LFLAGS) +ntfsusermap_SOURCES = ntfsusermap.c utils.c utils.h +ntfsusermap_LDADD = $(AM_LIBS) $(NTFSRECOVER_LIBS) +ntfsusermap_LDFLAGS = $(AM_LFLAGS) + +ntfssecaudit_SOURCES = ntfssecaudit.c utils.c utils.h +ntfssecaudit_LDADD = $(AM_LIBS) $(NTFSRECOVER_LIBS) +ntfssecaudit_LDFLAGS = $(AM_LFLAGS) + # We don't distribute these ntfstruncate_SOURCES = attrdef.c ntfstruncate.c utils.c utils.h diff --git a/ntfsprogs/ntfsclone.8.in b/ntfsprogs/ntfsclone.8.in index d156634c..a1585c3c 100644 --- a/ntfsprogs/ntfsclone.8.in +++ b/ntfsprogs/ntfsclone.8.in @@ -185,12 +185,14 @@ Clone NTFS to the non\-existent If .I FILE is '\-' then clone to the -standard output. +standard output. This option cannot be used for creating a partition, +use \fB\-\-overwrite\fR for an existing partition. .TP \fB\-O\fR, \fB\-\-overwrite\fR FILE Clone NTFS to .IR FILE , -overwriting if exists. +which can be an existing partition or a regular file which will be +overwritten if it exists. .TP \fB\-s\fR, \fB\-\-save\-image\fR Save to the special image format. This is the most efficient way space and @@ -235,8 +237,11 @@ inconsistency are saved too. Do not wipe the timestamps, to be used only with the .B \-\-metadata option. - - +.TP +\fB\-\-full\-logfile\fR +Include the Windows log file in the copy. This is only useful for +extracting metadata, saving or cloning a file system which was not +properly unmounted from Windows. .TP \fB\-\-new\-serial\fR, or .TP diff --git a/ntfsprogs/ntfsclone.c b/ntfsprogs/ntfsclone.c index 6deeffdb..76c7e65c 100644 --- a/ntfsprogs/ntfsclone.c +++ b/ntfsprogs/ntfsclone.c @@ -3,7 +3,7 @@ * * Copyright (c) 2003-2006 Szabolcs Szakacsits * Copyright (c) 2004-2006 Anton Altaparmakov - * Copyright (c) 2010-2015 Jean-Pierre Andre + * Copyright (c) 2010-2016 Jean-Pierre Andre * Special image format support copyright (c) 2004 Per Olofsson * * Clone NTFS data and/or metadata to a sparse file, image, device or stdout. @@ -160,6 +160,7 @@ static struct { int new_serial; int metadata_image; int preserve_timestamps; + int full_logfile; int restore_image; char *output; char *volume; @@ -368,11 +369,12 @@ static void usage(int ret) " -t, --preserve-timestamps Do not clear the timestamps\n" " -q, --quiet Do not display any progress bars\n" " -f, --force Force to progress (DANGEROUS)\n" + " --full-logfile Include the full logfile in metadata output\n" " -h, --help Display this help\n" #ifdef DEBUG " -d, --debug Show debug information\n" #endif - " -V, --version Display version information\n" + " -V, --version Display version information\n" "\n" " If FILE is '-' then send the image to the standard output. If SOURCE is '-'\n" " and --restore-image is used then read the image from the standard input.\n" @@ -391,7 +393,7 @@ static void version(void) "Efficiently clone, image, restore or rescue an NTFS Volume.\n\n" "Copyright (c) 2003-2006 Szabolcs Szakacsits\n" "Copyright (c) 2004-2006 Anton Altaparmakov\n" - "Copyright (c) 2010-2015 Jean-Pierre Andre\n\n"); + "Copyright (c) 2010-2016 Jean-Pierre Andre\n\n"); fprintf(stderr, "%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home); exit(0); } @@ -415,6 +417,7 @@ static void parse_options(int argc, char **argv) { "rescue", no_argument, NULL, 'R' }, { "new-serial", no_argument, NULL, 'I' }, { "new-half-serial", no_argument, NULL, 'i' }, + { "full-logfile", no_argument, NULL, 'l' }, { "save-image", no_argument, NULL, 's' }, { "preserve-timestamps", no_argument, NULL, 't' }, { "version", no_argument, NULL, 'V' }, @@ -451,6 +454,9 @@ static void parse_options(int argc, char **argv) case 'I': /* not proposed as a short option */ opt.new_serial |= 2; break; + case 'l': + opt.full_logfile++; + break; case 'm': opt.metadata++; break; @@ -633,7 +639,7 @@ static s64 is_critical_metadata(ntfs_walk_clusters_ctx *image, runlist *rl) if (inode == FILE_BadClus && le32_eq(image->ctx->attr->type, AT_DATA)) return 0; - if (inode != FILE_LogFile) + if ((inode != FILE_LogFile) || opt.full_logfile) return rl->length; if (le32_eq(image->ctx->attr->type, AT_DATA)) { @@ -2150,11 +2156,20 @@ static void mount_volume(unsigned long new_mntflag) * Normally avoided in order to get the original log file * data, but needed when remounting the metadata of a * volume improperly unmounted from Windows. + * If the full log file was requested, it must be kept + * as is, so we just remount read-only. */ if (!(new_mntflag & (NTFS_MNT_RDONLY | NTFS_MNT_RECOVER))) { - Printf("Trying to recover...\n"); - vol = ntfs_mount(opt.volume, + if (opt.full_logfile) { + Printf("Retrying read-only to ignore" + " the log file...\n"); + vol = ntfs_mount(opt.volume, + new_mntflag | NTFS_MNT_RDONLY); + } else { + Printf("Trying to recover...\n"); + vol = ntfs_mount(opt.volume, new_mntflag | NTFS_MNT_RECOVER); + } Printf("... %s\n",(vol ? "Successful" : "Failed")); } if (!vol) diff --git a/ntfsprogs/ntfscluster.c b/ntfsprogs/ntfscluster.c index 6a7e7289..1f171634 100644 --- a/ntfsprogs/ntfscluster.c +++ b/ntfsprogs/ntfscluster.c @@ -486,6 +486,9 @@ int main(int argc, char *argv[]) struct match m; int res; int result = 1; +#ifdef HAVE_WINDOWS_H + char *unix_name; +#endif ntfs_log_set_handler(ntfs_log_handler_outerr); @@ -521,7 +524,17 @@ int main(int argc, char *argv[]) result = cluster_find(vol, opts.range_begin, opts.range_end, (cluster_cb*)&print_match, NULL); break; case act_file: +#ifdef HAVE_WINDOWS_H + unix_name = ntfs_utils_unix_path(opts.filename); + ino = 0; + if (unix_name) { + ino = ntfs_pathname_to_inode(vol, NULL, + unix_name); + free(unix_name); + } +#else ino = ntfs_pathname_to_inode(vol, NULL, opts.filename); +#endif if (ino) result = dump_file(vol, ino); break; diff --git a/ntfsprogs/ntfscmp.c b/ntfsprogs/ntfscmp.c index 2844e22a..7463699d 100644 --- a/ntfsprogs/ntfscmp.c +++ b/ntfsprogs/ntfscmp.c @@ -547,7 +547,7 @@ static void cmp_index_allocation(ntfs_attr *na1, ntfs_attr *na2) /* * FIXME: ia can be the same even if the bitmap sizes are different. */ - if (cia1.bm_size != cia1.bm_size) + if (cia1.bm_size != cia2.bm_size) goto out; if (cmp_buffer(cia1.bitmap, cia2.bitmap, cia1.bm_size, na1)) diff --git a/ntfsprogs/ntfsdump_logfile.c b/ntfsprogs/ntfsdump_logfile.c index 0dce95cd..2c04e26c 100644 --- a/ntfsprogs/ntfsdump_logfile.c +++ b/ntfsprogs/ntfsdump_logfile.c @@ -319,7 +319,7 @@ static void restart_header_sanity(RESTART_PAGE_HEADER *rstr, u8 *buf) log_err_exit(buf, "Restart page header in $LogFile is " "corrupt: Update sequence array size is " "wrong. Cannot handle this yet.\n"); - if (le16_to_cpu(rstr->usa_ofs) < sizeof(RESTART_PAGE_HEADER)) + if (le16_to_cpu(rstr->usa_ofs) < offsetof(RESTART_PAGE_HEADER, usn)) log_err_exit(buf, "Restart page header in $LogFile is " "corrupt: Update sequence array overlaps " "restart page header. Cannot handle this " @@ -557,17 +557,17 @@ static void dump_log_record(LOG_RECORD *lr) (unsigned int)le32_to_cpu(lr->record_type)); ntfs_log_info("transaction_id = 0x%x\n", (unsigned int)le32_to_cpu(lr->transaction_id)); - ntfs_log_info("flags = 0x%x:", le16_to_cpu(lr->flags)); - if (le16_cmpz(lr->flags)) + ntfs_log_info("flags = 0x%x:", le16_to_cpu(lr->log_record_flags)); + if (le16_cmpz(lr->log_record_flags)) ntfs_log_info(" NONE\n"); else { int _b = 0; - if (!le16_andz(lr->flags, LOG_RECORD_MULTI_PAGE)) { + if (!le16_andz(lr->log_record_flags, LOG_RECORD_MULTI_PAGE)) { ntfs_log_info(" LOG_RECORD_MULTI_PAGE"); _b = 1; } - if (!le16_andz(lr->flags, le16_not(LOG_RECORD_MULTI_PAGE))) { + if (!le16_andz(lr->log_record_flags, le16_not(LOG_RECORD_MULTI_PAGE))) { if (_b) ntfs_log_info(" |"); ntfs_log_info(" Unknown flags"); @@ -589,8 +589,8 @@ static void dump_log_record(LOG_RECORD *lr) if (le16_to_cpu(lr->lcns_to_follow) > 0) ntfs_log_info("Array of lcns:\n"); for (i = 0; i < le16_to_cpu(lr->lcns_to_follow); i++) - ntfs_log_info("lcn_list[%u].lcn = 0x%llx\n", i, (unsigned long long) - sle64_to_cpu(lr->lcn_list[i].lcn)); + ntfs_log_info("lcn_list[%u].lcn = 0x%llx\n", i, + (unsigned long long)sle64_to_cpu(lr->lcn_list[i])); } /** @@ -633,9 +633,9 @@ rcrd_pass_loc: ntfs_log_info("page count = %i\n", le16_to_cpu(rcrd->page_count)); ntfs_log_info("page position = %i\n", le16_to_cpu(rcrd->page_position)); ntfs_log_info("header.next_record_offset = 0x%llx\n", (unsigned long long) - le16_to_cpu(rcrd->header.packed.next_record_offset)); + le16_to_cpu(rcrd->next_record_offset)); ntfs_log_info("header.last_end_lsn = 0x%llx\n", (unsigned long long) - sle64_to_cpu(rcrd->header.packed.last_end_lsn)); + sle64_to_cpu(rcrd->last_end_lsn)); /* * Where does the 0x40 come from? Is it just usa_offset + * usa_client * 2 + 7 & ~7 or is it derived from somewhere? @@ -648,7 +648,7 @@ rcrd_pass_loc: client++; lr = (LOG_RECORD*)((u8*)lr + 0x70); } while (((u8*)lr + 0x70 <= (u8*)rcrd + - le16_to_cpu(rcrd->header.packed.next_record_offset))); + le16_to_cpu(rcrd->next_record_offset))); pass++; goto rcrd_pass_loc; diff --git a/ntfsprogs/ntfsfix.c b/ntfsprogs/ntfsfix.c index 61db0668..2bd6a3e5 100644 --- a/ntfsprogs/ntfsfix.c +++ b/ntfsprogs/ntfsfix.c @@ -341,6 +341,39 @@ static int empty_journal(ntfs_volume *vol) return 0; } +/* + * Clear the sparse flag of an attribute + */ + +static int clear_sparse(ntfs_attr *na, const char *name) +{ + ntfs_attr_search_ctx *ctx; + int res; + + res = -1; + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (ctx) { + if (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + na->data_flags = le16_and(na->data_flags, le16_not(ATTR_IS_SPARSE)); + ctx->attr->data_size = cpu_to_sle64(na->data_size); + ctx->attr->initialized_size + = cpu_to_sle64(na->initialized_size); + ctx->attr->flags = cpu_to_le16(na->data_flags); + ctx->attr->compression_unit = 0; + ntfs_inode_mark_dirty(ctx->ntfs_ino); + NInoFileNameSetDirty(na->ni); + res = 0; + } else + ntfs_log_perror("Could not locate attribute for %s", + name); + ntfs_attr_put_search_ctx(ctx); + } else + ntfs_log_perror("Could not get a search context for %s", + name); + return (res); +} + /** * Clear the bad cluster marks (option) */ @@ -372,15 +405,14 @@ static int clear_badclus(ntfs_volume *vol) * (which requires setting the data size according * to allocation), then reallocate a sparse stream * to full size of volume and reset the data size. + * Note : the sparse flags should not be set. */ na->data_size = na->allocated_size; na->initialized_size = na->allocated_size; if (!ntfs_attr_truncate(na,0) && !ntfs_attr_truncate(na,vol->nr_clusters << vol->cluster_size_bits)) { - na->data_size = 0; na->initialized_size = 0; - ni->flags = le32_or(ni->flags, FILE_ATTR_SPARSE_FILE); NInoFileNameSetDirty(ni); ok = TRUE; } else { @@ -390,6 +422,14 @@ static int clear_badclus(ntfs_volume *vol) ntfs_log_info("No bad clusters..."); ok = TRUE; } + /* + * The sparse flags are not set after an initial + * formatting, so do the same. + */ + if (ok) { + ni->flags = le32_and(ni->flags, le32_not(FILE_ATTR_SPARSE_FILE)); + ok = !clear_sparse(na, "$BadClus::$Bad"); + } ntfs_attr_close(na); } else { ntfs_log_perror("Failed to open $BadClus::$Bad"); diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c index f9335b5a..236866a2 100644 --- a/ntfsprogs/ntfsinfo.c +++ b/ntfsprogs/ntfsinfo.c @@ -412,16 +412,23 @@ static const char *reparse_type_name(le32 tag) { const char *name; - if (le32_eq(tag, IO_REPARSE_TAG_MOUNT_POINT)) + do { + if (le32_eq(tag, IO_REPARSE_TAG_MOUNT_POINT)) { name = " (mount point)"; - else - if (le32_eq(tag, IO_REPARSE_TAG_SYMLINK)) - name = " (symlink)"; - else - if (le32_eq(tag, IO_REPARSE_TAG_WOF)) - name = " (Wof compressed)"; - else - name = ""; + break; + } else if (le32_eq(tag, IO_REPARSE_TAG_SYMLINK)) { + name = " (symlink)"; + break; + } else if (le32_eq(tag, IO_REPARSE_TAG_WOF)) { + name = " (Wof compressed)"; + break; + } else if (le32_eq(tag, IO_REPARSE_TAG_DEDUP)) { + name = " (deduplicated)"; + break; + } else { + name = ""; + break; + } } while(0); return (name); } diff --git a/ntfsprogs/ntfslabel.c b/ntfsprogs/ntfslabel.c index 1a1cfc67..ae40a326 100644 --- a/ntfsprogs/ntfslabel.c +++ b/ntfsprogs/ntfslabel.c @@ -299,8 +299,9 @@ static int set_new_serial(ntfs_volume *vol) serial_number = cpu_to_le64(sn); } if (!change_serial(vol, 0, serial_number, bs, oldbs)) { - number_of_sectors = sle64_to_cpu(bs->number_of_sectors); - if (!change_serial(vol, number_of_sectors, + number_of_sectors = ntfs_device_size_get(vol->dev, + vol->sector_size); + if (!change_serial(vol, number_of_sectors - 1, serial_number, bs, oldbs)) { ntfs_log_info("New serial number : %016llx\n", (long long)le64_to_cpu( diff --git a/ntfsprogs/ntfsrecover.8.in b/ntfsprogs/ntfsrecover.8.in index 1fe2e449..6c0eb69b 100644 --- a/ntfsprogs/ntfsrecover.8.in +++ b/ntfsprogs/ntfsrecover.8.in @@ -86,6 +86,13 @@ generated during several sessions. \fB\-h\fR, \fB\-\-help\fR Show some help information. .TP +\fB\-k\fR, \fB\-\-kill\-fast\-restart\fR +When Windows has been interrupted with fast restart mode activated, +part of pending changes are kept in the Windows cache and only the same +Windows version can recover them. This option can be used to apply the +changes recorded in the log file and drop the ones in the Windows cache. +This is dangerous and may cause loss of data. +.TP \fB\-n\fR, \fB\-\-no-action\fR Do not apply any modification, useful when using the options -p, -s or -u. .TP diff --git a/ntfsprogs/ntfsrecover.c b/ntfsprogs/ntfsrecover.c index c69c027a..bab6c66a 100644 --- a/ntfsprogs/ntfsrecover.c +++ b/ntfsprogs/ntfsrecover.c @@ -1,7 +1,7 @@ /* * Process log data from an NTFS partition * - * Copyright (c) 2012-2015 Jean-Pierre Andre + * Copyright (c) 2012-2016 Jean-Pierre Andre * * This program examines the Windows log file of an ntfs partition * and plays the committed transactions in order to restore the @@ -101,6 +101,7 @@ #include "volume.h" #include "unistr.h" #include "mst.h" +#include "logfile.h" #include "ntfsrecover.h" #include "utils.h" #include "misc.h" @@ -114,9 +115,9 @@ typedef struct { typedef enum { T_OK, T_ERR, T_DONE } TRISTATE; -struct RESTART_PAGE_HEADER log_header; -struct RESTART_AREA restart; -struct RESTART_CLIENT client; +RESTART_PAGE_HEADER log_header; +RESTART_AREA restart; +LOG_CLIENT_RECORD client; u32 clustersz = 0; int clusterbits; u32 blocksz; @@ -145,6 +146,7 @@ BOOL optd; /* device argument present*/ BOOL opth; /* show help */ BOOL opti; /* show invalid (stale) records */ BOOL optf; /* show full log */ +BOOL optk; /* kill fast restart */ BOOL optn; /* do not apply modifications */ BOOL optp; /* count of transaction sets to play */ BOOL optr; /* show a range of blocks */ @@ -206,7 +208,7 @@ static s64 loclogblk(CONTEXT *ctx, unsigned int blk) static int replaceusa(struct BUFFER *buffer, unsigned int lth) { char *buf; - struct RECORD_PAGE_HEADER *record; + RECORD_PAGE_HEADER *record; unsigned int j; BOOL err; unsigned int used; @@ -218,8 +220,8 @@ static int replaceusa(struct BUFFER *buffer, unsigned int lth) /* Do not check beyond used sectors */ record = &buffer->block.record; used = blocksz; - xusa = le16_to_cpu(record->head.usa_ofs); - nusa = le16_to_cpu(record->head.usa_count); + xusa = le16_to_cpu(record->usa_ofs); + nusa = le16_to_cpu(record->usa_count); if (xusa && nusa && ((xusa + 1) < lth) && ((nusa - 1)*NTFSBLKLTH == lth)) { @@ -358,7 +360,7 @@ static const struct BUFFER *read_buffer(CONTEXT *ctx, unsigned int num) 1, ctx->file) == 1); if (got) { char *data = buffer->block.data; - buffer->headsz = sizeof(struct RECORD_PAGE_HEADER) + buffer->headsz = sizeof(RECORD_PAGE_HEADER) + ((2*getle16(data,6) - 1) | 7) + 1; buffer->safe = !replaceusa(buffer, blocksz); } else { @@ -794,7 +796,7 @@ static BOOL acts_on_mft(int op) return (onmft); } -u32 get_undo_offset(const struct LOG_RECORD *logr) +u32 get_undo_offset(const LOG_RECORD *logr) { u32 offset; @@ -805,7 +807,7 @@ u32 get_undo_offset(const struct LOG_RECORD *logr) return (offset); } -u32 get_redo_offset(const struct LOG_RECORD *logr) +u32 get_redo_offset(const LOG_RECORD *logr) { u32 offset; @@ -816,7 +818,7 @@ u32 get_redo_offset(const struct LOG_RECORD *logr) return (offset); } -u32 get_extra_offset(const struct LOG_RECORD *logr) +u32 get_extra_offset(const LOG_RECORD *logr) { u32 uoffset; u32 roffset; @@ -828,12 +830,12 @@ u32 get_extra_offset(const struct LOG_RECORD *logr) return ((((uoffset > roffset ? uoffset : roffset) - 1) | 7) + 1); } -static BOOL likelyop(const struct LOG_RECORD *logr) +static BOOL likelyop(const LOG_RECORD *logr) { BOOL likely; - switch (le32_to_cpu(logr->record_type)) { - case LOG_STANDARD : /* standard record */ + do { + if (le32_eq(logr->record_type, LOG_STANDARD)) { /* standard record */ /* Operations in range 0..LastAction-1, can be both null */ likely = ((unsigned int)le16_to_cpu(logr->redo_operation) < LastAction) @@ -880,7 +882,7 @@ static BOOL likelyop(const struct LOG_RECORD *logr) } } break; - case LOG_CHECKPOINT : /* check-point */ + } else if (le32_eq(logr->record_type, LOG_CHECKPOINT)) { /* check-point */ /* * undo and redo operations are null * or CompensationlogRecord with no data @@ -897,10 +899,10 @@ static BOOL likelyop(const struct LOG_RECORD *logr) && ((le32_to_cpu(logr->client_data_length) == 0x68) || (le32_to_cpu(logr->client_data_length) == 0x70)); break; - default : + } else { likely = FALSE; break; - } + } } while(0); return (likely); } @@ -914,7 +916,7 @@ static BOOL likelyop(const struct LOG_RECORD *logr) static u16 searchlikely(const struct BUFFER *buf) { - const struct LOG_RECORD *logr; + const LOG_RECORD *logr; const char *data; u16 k; @@ -922,11 +924,11 @@ static u16 searchlikely(const struct BUFFER *buf) printf("** Error : searchlikely() used for syncing\n"); data = buf->block.data; k = buf->headsz; - logr = (const struct LOG_RECORD*)&data[k]; + logr = (const LOG_RECORD*)&data[k]; if (!likelyop(logr)) { do { k += 8; - logr = (const struct LOG_RECORD*)&data[k]; + logr = (const LOG_RECORD*)&data[k]; } while ((k <= (blocksz - LOG_RECORD_HEAD_SZ)) && !likelyop(logr)); if (k > (blocksz - LOG_RECORD_HEAD_SZ)) @@ -954,9 +956,9 @@ static u16 searchlikely(const struct BUFFER *buf) static u16 firstrecord(int skipped, const struct BUFFER *buf, const struct BUFFER *prevbuf) { - const struct RECORD_PAGE_HEADER *rph; - const struct RECORD_PAGE_HEADER *prevrph; - const struct LOG_RECORD *logr; + const RECORD_PAGE_HEADER *rph; + const RECORD_PAGE_HEADER *prevrph; + const LOG_RECORD *logr; const char *data; const char *prevdata; u16 k; @@ -978,7 +980,7 @@ static u16 firstrecord(int skipped, const struct BUFFER *buf, k = 0; /* Minimal size is apparently 48 : offset of redo_operation */ if (k && ((blocksz - k) >= LOG_RECORD_HEAD_SZ)) { - logr = (const struct LOG_RECORD*)&prevdata[k]; + logr = (const LOG_RECORD*)&prevdata[k]; if (le32_cmpz(logr->client_data_length)) { /* * Sometimes the end of record is free space. @@ -1051,7 +1053,7 @@ static u16 firstrecord(int skipped, const struct BUFFER *buf, * try to find a starting record. */ if (k && prevbuf && (prevbuf->num > buf->num)) { - logr = (const struct LOG_RECORD*)&data[k]; + logr = (const LOG_RECORD*)&data[k]; /* Accept reaching the end with no record beginning */ if ((k != le16_to_cpu(rph->next_record_offset)) && !likelyop(logr)) { @@ -1081,7 +1083,7 @@ static const struct BUFFER *findprevious(CONTEXT *ctx, const struct BUFFER *buf) { const struct BUFFER *prevbuf; const struct BUFFER *savebuf; - const struct RECORD_PAGE_HEADER *rph; + const RECORD_PAGE_HEADER *rph; int skipped; int prevblk; BOOL prevmiddle; @@ -1101,7 +1103,7 @@ static const struct BUFFER *findprevious(CONTEXT *ctx, const struct BUFFER *buf) prevblk = (logfilesz >> blockbits) - 1; else { rph = &buf->block.record; - prevblk = (le32_to_cpu(rph->copy.file_offset) + prevblk = (sle64_to_cpu(rph->copy.file_offset) >> blockbits) - 1; /* * If an initial block leads to block 4, it @@ -1148,18 +1150,18 @@ static const struct BUFFER *findprevious(CONTEXT *ctx, const struct BUFFER *buf) void copy_attribute(struct ATTR *pa, const char *buf, int length) { - const struct ATTR_NEW *panew; - struct ATTR_OLD old_aligned; + const ATTR_NEW *panew; + ATTR_OLD old_aligned; if (pa) { switch (length) { - case sizeof(struct ATTR_NEW) : - panew = (const struct ATTR_NEW*)buf; + case sizeof(ATTR_NEW) : + panew = (const ATTR_NEW*)buf; pa->type = panew->type; pa->lsn = sle64_to_cpu(panew->lsn); pa->inode = MREF(le64_to_cpu(panew->inode)); break; - case sizeof(struct ATTR_OLD) : + case sizeof(ATTR_OLD) : /* Badly aligned, first realign */ memcpy(&old_aligned,buf,sizeof(old_aligned)); pa->type = old_aligned.type; @@ -1176,7 +1178,7 @@ void copy_attribute(struct ATTR *pa, const char *buf, int length) static int refresh_attributes(const struct ACTION_RECORD *firstaction) { const struct ACTION_RECORD *action; - const struct LOG_RECORD *logr; + const LOG_RECORD *logr; struct ATTR *pa; const char *buf; u32 extra; @@ -1267,7 +1269,7 @@ static int refresh_attributes(const struct ACTION_RECORD *firstaction) * Display a fixup */ -static void fixup(CONTEXT *ctx, const struct LOG_RECORD *logr, const char *buf, +static void fixup(CONTEXT *ctx, const LOG_RECORD *logr, const char *buf, BOOL redo) { struct ATTR *pa; @@ -1320,7 +1322,7 @@ static void fixup(CONTEXT *ctx, const struct LOG_RECORD *logr, const char *buf, } printf(" new base MFT record, attr 0x%x (%s)\n",attr,attrname(attr)); printf(" inode %lld\n", - (((long long)le32_to_cpu(logr->target_vcn) + (((long long)le64_to_cpu(logr->target_vcn) << clusterbits) + (le16_to_cpu(logr->cluster_index) << 9)) >> mftrecbits); @@ -1369,7 +1371,7 @@ static void fixup(CONTEXT *ctx, const struct LOG_RECORD *logr, const char *buf, printf(" free base MFT record, attr 0x%x (%s)\n", attr,attrname(attr)); printf(" inode %lld\n", - (((long long)le32_to_cpu(logr->target_vcn) << clusterbits) + (((long long)le64_to_cpu(logr->target_vcn) << clusterbits) + (le16_to_cpu(logr->cluster_index) << 9)) >> mftrecbits); break; case CreateAttribute : /* 5 */ @@ -1758,8 +1760,8 @@ static void fixup(CONTEXT *ctx, const struct LOG_RECORD *logr, const char *buf, * Changed from Win10, formerly we got step = 44. * The record layout has also changed */ - if ((step != sizeof(struct ATTR_OLD)) - && (step != sizeof(struct ATTR_NEW))) { + if ((step != sizeof(ATTR_OLD)) + && (step != sizeof(ATTR_NEW))) { printf(" ** Unexpected step %d\n",step); } more = 0; @@ -1832,7 +1834,7 @@ static void fixup(CONTEXT *ctx, const struct LOG_RECORD *logr, const char *buf, } } -static void detaillogr(CONTEXT *ctx, const struct LOG_RECORD *logr) +static void detaillogr(CONTEXT *ctx, const LOG_RECORD *logr) { u64 lcn; u64 baselcn; @@ -1845,8 +1847,8 @@ static void detaillogr(CONTEXT *ctx, const struct LOG_RECORD *logr) unsigned int listsize; BOOL onmft; - switch (le32_to_cpu(logr->record_type)) { - case 1 : + do { + if (le32_eq(logr->record_type, LOG_STANDARD)) { onmft = !le16_cmpz(logr->cluster_index) || acts_on_mft(le16_to_cpu(logr->redo_operation)) || acts_on_mft(le16_to_cpu(logr->undo_operation)); @@ -1877,20 +1879,18 @@ static void detaillogr(CONTEXT *ctx, const struct LOG_RECORD *logr) printf("attribute_flags %04x\n", (int)le16_to_cpu(logr->attribute_flags)); if (mftrecbits && onmft) - printf("target_vcn %08lx (inode %lld)\n", - (long)le32_to_cpu(logr->target_vcn), - (((long long)le32_to_cpu(logr->target_vcn) + printf("target_vcn %016llx (inode %lld)\n", + (long long)le64_to_cpu(logr->target_vcn), + (((long long)le64_to_cpu(logr->target_vcn) << clusterbits) + (le16_to_cpu(logr->cluster_index) << 9)) >> mftrecbits); else - printf("target_vcn %08lx\n", - (long)le32_to_cpu(logr->target_vcn)); - printf("reserved3 %08lx\n", - (long)le32_to_cpu(logr->reserved3)); + printf("target_vcn %016llx\n", + (long long)le64_to_cpu(logr->target_vcn)); /* Compute a base for the current run of mft */ baselcn = le64_to_cpu(logr->lcn_list[0]) - - le32_to_cpu(logr->target_vcn); + - le64_to_cpu(logr->target_vcn); for (i=0; ilcns_to_follow) && (ilcn_list[i]); @@ -2068,7 +2068,7 @@ static void detaillogr(CONTEXT *ctx, const struct LOG_RECORD *logr) printf("* undo data overflows from record\n"); } break; - case 2 : + } else if (le32_eq(logr->record_type, LOG_CHECKPOINT)) { printf("---> checkpoint record\n"); printf("redo_operation %04x %s\n", (int)le16_to_cpu(logr->redo_operation), @@ -2090,14 +2090,14 @@ static void detaillogr(CONTEXT *ctx, const struct LOG_RECORD *logr) (long long)sle64_to_cpu(logr->dirty_pages_lsn)); listsize = le32_to_cpu(logr->client_data_length) + LOG_RECORD_HEAD_SZ - - offsetof(struct LOG_RECORD, unknown_list); + - offsetof(LOG_RECORD, unknown_list); if (listsize > 8*SHOWLISTS) listsize = 8*SHOWLISTS; for (i=0; 8*iunknown_list[i])); break; - default : + } else { printf("** Unknown action type\n"); if (le32_to_cpu(logr->client_data_length) < blocksz) { printf("client_data for record type %ld\n", @@ -2107,31 +2107,31 @@ static void detaillogr(CONTEXT *ctx, const struct LOG_RECORD *logr) } else printf("** Bad client data\n"); break; - } + } } while(0); } -BOOL within_lcn_range(const struct LOG_RECORD *logr) +BOOL within_lcn_range(const LOG_RECORD *logr) { u64 lcn; unsigned int i; BOOL within; within = FALSE; - switch (le32_to_cpu(logr->record_type)) { - case 1 : + do { + if (le32_eq(logr->record_type, LOG_STANDARD)) { for (i=0; ilcns_to_follow); i++) { lcn = MREF(le64_to_cpu(logr->lcn_list[i])); if ((lcn >= firstlcn) && (lcn <= lastlcn)) within = TRUE; } break; - default : + } else { break; - } + } } while(0); return (within); } -static void showlogr(CONTEXT *ctx, int k, const struct LOG_RECORD *logr) +static void showlogr(CONTEXT *ctx, int k, const LOG_RECORD *logr) { s32 diff; @@ -2158,15 +2158,15 @@ static void showlogr(CONTEXT *ctx, int k, const struct LOG_RECORD *logr) printf("log_record_flags %04x\n", (int)le16_to_cpu(logr->log_record_flags)); printf("reserved1 %04x %04x %04x\n", - (int)le16_to_cpu(logr->reserved1[0]), - (int)le16_to_cpu(logr->reserved1[1]), - (int)le16_to_cpu(logr->reserved1[2])); + (int)le16_to_cpu(logr->reserved_or_alignment[0]), + (int)le16_to_cpu(logr->reserved_or_alignment[1]), + (int)le16_to_cpu(logr->reserved_or_alignment[2])); detaillogr(ctx, logr); } if (optt) { const char *state; - if (le32_eq(logr->record_type, const_cpu_to_le32(2))) + if (le32_eq(logr->record_type, LOG_CHECKPOINT)) state = "--checkpoint--"; else state = commitment(sle64_to_cpu(logr->this_lsn)); @@ -2206,7 +2206,7 @@ static void showlogr(CONTEXT *ctx, int k, const struct LOG_RECORD *logr) static void mark_transactions(struct ACTION_RECORD *lastaction) { struct ACTION_RECORD *action; - const struct LOG_RECORD *logr; + const LOG_RECORD *logr; le32 id; int actives; BOOL more; @@ -2263,7 +2263,7 @@ static void mark_transactions(struct ACTION_RECORD *lastaction) * Enqueue an action and play the queued actions on end of set */ -static TRISTATE enqueue_action(CONTEXT *ctx, const struct LOG_RECORD *logr, +static TRISTATE enqueue_action(CONTEXT *ctx, const LOG_RECORD *logr, int size, int num) { struct ACTION_RECORD *action; @@ -2290,7 +2290,7 @@ static TRISTATE enqueue_action(CONTEXT *ctx, const struct LOG_RECORD *logr, err = 0; state = T_OK; if ((optp || optu) - && le32_eq(logr->record_type, const_cpu_to_le32(2))) { + && le32_eq(logr->record_type, LOG_CHECKPOINT)) { /* if chkp process queue, and increment count */ playedactions++; if (playedactions <= playcount) { @@ -2359,20 +2359,20 @@ static TRISTATE enqueue_action(CONTEXT *ctx, const struct LOG_RECORD *logr, } -static void showheadrcrd(u32 blk, const struct RECORD_PAGE_HEADER *rph) +static void showheadrcrd(u32 blk, const RECORD_PAGE_HEADER *rph) { s32 diff; if (optv) { printf("magic %08lx\n", - (long)le32_to_cpu(rph->head.magic)); + (long)le32_to_cpu(rph->magic)); printf("usa_ofs %04x\n", - (int)le16_to_cpu(rph->head.usa_ofs)); + (int)le16_to_cpu(rph->usa_ofs)); printf("usa_count %04x\n", - (int)le16_to_cpu(rph->head.usa_count)); + (int)le16_to_cpu(rph->usa_count)); if (blk < 4) - printf("file_offset %08lx\n", - (long)le32_to_cpu(rph->copy.file_offset)); + printf("file_offset %016llx\n", + (long long)sle64_to_cpu(rph->copy.file_offset)); else { diff = sle64_to_cpu(rph->copy.last_lsn) - synced_lsn; printf("last_lsn %016llx" @@ -2389,15 +2389,15 @@ static void showheadrcrd(u32 blk, const struct RECORD_PAGE_HEADER *rph) printf("next_record_offset %04x\n", (int)le16_to_cpu(rph->next_record_offset)); printf("reserved4 %04x %04x %04x\n", - (int)le16_to_cpu(rph->reserved4[0]), - (int)le16_to_cpu(rph->reserved4[1]), - (int)le16_to_cpu(rph->reserved4[2])); + (int)le16_to_cpu(rph->reserved[0]), + (int)le16_to_cpu(rph->reserved[1]), + (int)le16_to_cpu(rph->reserved[2])); diff = sle64_to_cpu(rph->last_end_lsn) - synced_lsn; printf("last_end_lsn %016llx (synced%s%ld)\n", (long long)sle64_to_cpu(rph->last_end_lsn), (diff < 0 ? "" : "+"),(long)diff); printf("usn %04x\n", - (int)getle16(rph,le16_to_cpu(rph->head.usa_ofs))); + (int)getle16(rph,le16_to_cpu(rph->usa_ofs))); printf("\n"); } else { if (optt) { @@ -2430,7 +2430,7 @@ static void showheadrcrd(u32 blk, const struct RECORD_PAGE_HEADER *rph) static u16 overlapshow(CONTEXT *ctx, u16 k, u32 blk, const struct BUFFER *buf, const struct BUFFER *nextbuf) { - const struct LOG_RECORD *logr; + const LOG_RECORD *logr; const char *data; const char *nextdata; char *fullrec; @@ -2441,7 +2441,7 @@ static u16 overlapshow(CONTEXT *ctx, u16 k, u32 blk, const struct BUFFER *buf, u16 blkheadsz; data = buf->block.data; - logr = (const struct LOG_RECORD*)&data[k]; + logr = (const LOG_RECORD*)&data[k]; size = le32_to_cpu(logr->client_data_length) + LOG_RECORD_HEAD_SZ; blkheadsz = buf->headsz; if (nextbuf && (blk >= BASEBLKS)) { @@ -2457,7 +2457,7 @@ static u16 overlapshow(CONTEXT *ctx, u16 k, u32 blk, const struct BUFFER *buf, memcpy(&fullrec[space], nextdata + blkheadsz, size - space); - likely = likelyop((struct LOG_RECORD*)fullrec); + likely = likelyop((LOG_RECORD*)fullrec); actionnum++; if (optv) { printf("\nOverlapping record %u at 0x%x" @@ -2470,7 +2470,7 @@ static u16 overlapshow(CONTEXT *ctx, u16 k, u32 blk, const struct BUFFER *buf, } if (likely) showlogr(ctx, k, - (struct LOG_RECORD*)fullrec); + (LOG_RECORD*)fullrec); else printf("** Skipping unlikely" " overlapping record\n"); @@ -2523,7 +2523,7 @@ static u16 overlapshow(CONTEXT *ctx, u16 k, u32 blk, const struct BUFFER *buf, size - pos); else likely = FALSE; - if (!likelyop((struct LOG_RECORD*)fullrec)) + if (!likelyop((LOG_RECORD*)fullrec)) likely = FALSE; actionnum++; if (optv) { @@ -2537,7 +2537,7 @@ static u16 overlapshow(CONTEXT *ctx, u16 k, u32 blk, const struct BUFFER *buf, } if (likely) showlogr(ctx, k, - (struct LOG_RECORD*)fullrec); + (LOG_RECORD*)fullrec); else printf("** Skipping unlikely" " overlapping record\n"); @@ -2589,15 +2589,15 @@ static u16 overlapshow(CONTEXT *ctx, u16 k, u32 blk, const struct BUFFER *buf, static u16 forward_rcrd(CONTEXT *ctx, u32 blk, u16 pos, const struct BUFFER *buf, const struct BUFFER *nextbuf) { - const struct RECORD_PAGE_HEADER *rph; - const struct LOG_RECORD *logr; + const RECORD_PAGE_HEADER *rph; + const LOG_RECORD *logr; const char *data; u16 k; u16 endoff; BOOL stop; rph = &buf->block.record; - if (rph && le32_eq(rph->head.magic, magic_RCRD)) { + if (rph && le32_eq(rph->magic, magic_RCRD)) { data = buf->block.data; showheadrcrd(blk, rph); k = buf->headsz; @@ -2605,19 +2605,19 @@ static u16 forward_rcrd(CONTEXT *ctx, u32 blk, u16 pos, k = ((pos - 1) | 7) + 1; } // TODO check bad start > blocksz - 48 - logr = (const struct LOG_RECORD*)&data[k]; + logr = (const LOG_RECORD*)&data[k]; stop = FALSE; if (!likelyop(logr)) { if (optv) printf("* Bad start 0x%x for block %d\n", (int)pos,(int)blk); k = searchlikely(buf); - if ((k + sizeof(struct LOG_RECORD)) > blocksz) { + if ((k + sizeof(LOG_RECORD)) > blocksz) { printf("No likely full record in block %lu\n", (unsigned long)blk); /* there can be a partial one */ k = le16_to_cpu(rph->next_record_offset); - if ((k < (u16)sizeof(struct RECORD_PAGE_HEADER)) + if ((k < (u16)sizeof(RECORD_PAGE_HEADER)) || ((blocksz - k) < LOG_RECORD_HEAD_SZ)) stop = TRUE; } else { @@ -2629,7 +2629,7 @@ static u16 forward_rcrd(CONTEXT *ctx, u32 blk, u16 pos, while (!stop) { s32 size; - logr = (const struct LOG_RECORD*)&data[k]; + logr = (const LOG_RECORD*)&data[k]; size = le32_to_cpu(logr->client_data_length) + LOG_RECORD_HEAD_SZ; if ((size < MINRECSIZE) @@ -2674,7 +2674,7 @@ static u16 forward_rcrd(CONTEXT *ctx, u32 blk, u16 pos, } } else { printf("** Not a RCRD record, MAGIC 0x%08lx\n", - (long)le32_to_cpu(rph->head.magic)); + (long)le32_to_cpu(rph->magic)); k = 0; } return (k); @@ -2684,30 +2684,30 @@ static u16 forward_rcrd(CONTEXT *ctx, u32 blk, u16 pos, * Display a restart page */ -static void showrest(const struct RESTART_PAGE_HEADER *rest) +static void showrest(const RESTART_PAGE_HEADER *rest) { - const struct RESTART_AREA *resa; - const struct RESTART_CLIENT *rcli; + const RESTART_AREA *resa; + const LOG_CLIENT_RECORD *rcli; const char *data; data = (const char*)rest; - if (le32_eq(rest->head.magic, magic_RSTR) - || le32_eq(rest->head.magic, magic_CHKD)) { + if (le32_eq(rest->magic, magic_RSTR) + || le32_eq(rest->magic, magic_CHKD)) { if (optv) { printf("magic %08lx\n", - (long)le32_to_cpu(rest->head.magic)); + (long)le32_to_cpu(rest->magic)); printf("usa_ofs %04x\n", - (int)le16_to_cpu(rest->head.usa_ofs)); + (int)le16_to_cpu(rest->usa_ofs)); printf("usa_count %04x\n", - (int)le16_to_cpu(rest->head.usa_count)); + (int)le16_to_cpu(rest->usa_count)); printf("chkdsk_lsn %016llx\n", (long long)sle64_to_cpu(rest->chkdsk_lsn)); printf("system_page_size %08lx\n", (long)le32_to_cpu(rest->system_page_size)); printf("log_page_size %08lx\n", (long)le32_to_cpu(rest->log_page_size)); - printf("restart_offset %04x\n", - (int)le16_to_cpu(rest->restart_offset)); + printf("restart_area_offset %04x\n", + (int)le16_to_cpu(rest->restart_area_offset)); printf("minor_vers %d\n", (int)le16_to_cpu(rest->minor_ver)); printf("major_vers %d\n", @@ -2720,8 +2720,8 @@ static void showrest(const struct RESTART_PAGE_HEADER *rest) printf(" chkdsk %016llx\n", (long long)sle64_to_cpu(rest->chkdsk_lsn)); } - resa = (const struct RESTART_AREA*) - &data[le16_to_cpu(rest->restart_offset)]; + resa = (const RESTART_AREA*) + &data[le16_to_cpu(rest->restart_area_offset)]; if (optv) { printf("current_lsn %016llx\n", (long long)sle64_to_cpu(resa->current_lsn)); @@ -2744,7 +2744,7 @@ static void showrest(const struct RESTART_PAGE_HEADER *rest) printf("last_lsn_data_len %08lx\n", (long)le32_to_cpu(resa->last_lsn_data_length)); printf("record_length %04x\n", - (int)le16_to_cpu(resa->record_length)); + (int)le16_to_cpu(resa->log_record_header_length)); printf("log_page_data_offs %04x\n", (int)le16_to_cpu(resa->log_page_data_offset)); printf("restart_log_open_count %08lx\n", @@ -2756,8 +2756,8 @@ static void showrest(const struct RESTART_PAGE_HEADER *rest) (long long)sle64_to_cpu(resa->current_lsn)); } - rcli = (const struct RESTART_CLIENT*) - &data[le16_to_cpu(rest->restart_offset) + rcli = (const LOG_CLIENT_RECORD*) + &data[le16_to_cpu(rest->restart_area_offset) + le16_to_cpu(resa->client_array_offset)]; if (optv) { printf("oldest_lsn %016llx\n", @@ -2787,14 +2787,14 @@ static void showrest(const struct RESTART_PAGE_HEADER *rest) } } else printf("Not a RSTR or CHKD record, MAGIC 0x%08lx\n", - (long)le32_to_cpu(rest->head.magic)); + (long)le32_to_cpu(rest->magic)); } static BOOL dorest(CONTEXT *ctx, unsigned long blk, - const struct RESTART_PAGE_HEADER *rph, BOOL initial) + const RESTART_PAGE_HEADER *rph, BOOL initial) { - const struct RESTART_AREA *resa; - const struct RESTART_CLIENT *rcli; + const RESTART_AREA *resa; + const LOG_CLIENT_RECORD *rcli; const char *data; s64 diff; int offs; @@ -2803,9 +2803,9 @@ static BOOL dorest(CONTEXT *ctx, unsigned long blk, BOOL dirty; data = (const char*)rph; - offs = le16_to_cpu(rph->restart_offset); - resa = (const struct RESTART_AREA*)&data[offs]; - rcli = (const struct RESTART_CLIENT*)&data[offs + offs = le16_to_cpu(rph->restart_area_offset); + resa = (const RESTART_AREA*)&data[offs]; + rcli = (const LOG_CLIENT_RECORD*)&data[offs + le16_to_cpu(resa->client_array_offset)]; if (initial) { /* Information from block initially found best */ @@ -2813,13 +2813,13 @@ static BOOL dorest(CONTEXT *ctx, unsigned long blk, committed_lsn = sle64_to_cpu(rcli->client_restart_lsn); synced_lsn = sle64_to_cpu(rcli->oldest_lsn); memcpy(&log_header, rph, - sizeof(struct RESTART_PAGE_HEADER)); - offs = le16_to_cpu(log_header.restart_offset); + sizeof(RESTART_PAGE_HEADER)); + offs = le16_to_cpu(log_header.restart_area_offset); memcpy(&restart, &data[offs], - sizeof(struct RESTART_AREA)); + sizeof(RESTART_AREA)); offs += le16_to_cpu(restart.client_array_offset); memcpy(&client, &data[offs], - sizeof(struct RESTART_CLIENT)); + sizeof(LOG_CLIENT_RECORD)); dirty = le16_andz(resa->flags, RESTART_VOLUME_IS_CLEAN); if (optv || optt) printf("* Using initial restart page," @@ -2859,13 +2859,13 @@ static BOOL dorest(CONTEXT *ctx, unsigned long blk, synced_lsn = sle64_to_cpu(rcli->oldest_lsn); latest_lsn = sle64_to_cpu(resa->current_lsn); memcpy(&log_header, rph, - sizeof(struct RESTART_PAGE_HEADER)); - offs = le16_to_cpu(log_header.restart_offset); + sizeof(RESTART_PAGE_HEADER)); + offs = le16_to_cpu(log_header.restart_area_offset); memcpy(&restart, &data[offs], - sizeof(struct RESTART_AREA)); + sizeof(RESTART_AREA)); offs += le16_to_cpu(restart.client_array_offset); memcpy(&client, &data[offs], - sizeof(struct RESTART_CLIENT)); + sizeof(LOG_CLIENT_RECORD)); dirty = le16_andz(resa->flags, RESTART_VOLUME_IS_CLEAN); if (optv || optt) printf("* Using %s restart page," @@ -2895,12 +2895,13 @@ static const struct BUFFER *read_restart(CONTEXT *ctx) { const struct BUFFER *buf; BOOL bad; + int major, minor; bad = FALSE; if (ctx->vol) { - struct RESTART_PAGE_HEADER *rph; + RESTART_PAGE_HEADER *rph; - rph = (struct RESTART_PAGE_HEADER*)NULL; + rph = (RESTART_PAGE_HEADER*)NULL; /* Full mode : use the restart page selected by the library */ if (ntfs_check_logfile(log_na, &rph)) { /* rph is left unchanged for a wiped out log file */ @@ -2925,7 +2926,7 @@ static const struct BUFFER *read_restart(CONTEXT *ctx) if (buf) { NTFS_RECORD_TYPES magic; - magic = buf->block.restart.head.magic; + magic = buf->block.restart.magic; /* switch (magic) { */ if (le32_eq(magic, magic_RSTR)) { } @@ -2944,13 +2945,22 @@ static const struct BUFFER *read_restart(CONTEXT *ctx) } if (!bad && !ctx->vol) dorest(ctx, 0, &buf->block.restart, TRUE); - if (!le16_eq(buf->block.restart.major_ver, const_cpu_to_le16(1)) - || !le16_eq(buf->block.restart.minor_ver, const_cpu_to_le16(1))) { - printf("** Unsupported $LogFile version %d.%d\n", - le16_to_cpu(buf->block.restart.major_ver), - le16_to_cpu(buf->block.restart.minor_ver)); - bad = TRUE; - } + major = le16_to_cpu(buf->block.restart.major_ver); + minor = le16_to_cpu(buf->block.restart.minor_ver); + if ((major == 2) && (minor == 0)) { + if (!optk) { + printf("** Fast restart mode detected," + " data could be lost\n"); + printf(" Use option --kill-fast-restart" + " to bypass\n"); + bad = TRUE; + } + } else + if ((major != 1) || (minor != 1)) { + printf("** Unsupported $LogFile version %d.%d\n", + major, minor); + bad = TRUE; + } if (bad) { buf = (const struct BUFFER*)NULL; } @@ -2975,14 +2985,17 @@ static int reset_logfile(CONTEXT *ctx __attribute__((unused))) restart.client_in_use_list = LOGFILE_NO_CLIENT; restart.flags = le16_or(restart.flags, RESTART_VOLUME_IS_CLEAN); client.oldest_lsn = cpu_to_sle64(restart_lsn); + /* Set $LogFile version to 1.1 so that volume can be mounted */ + log_header.major_ver = const_cpu_to_le16(1); + log_header.minor_ver = const_cpu_to_le16(1); memcpy(buffer, &log_header, - sizeof(struct RESTART_PAGE_HEADER)); - off = le16_to_cpu(log_header.restart_offset); + sizeof(RESTART_PAGE_HEADER)); + off = le16_to_cpu(log_header.restart_area_offset); memcpy(&buffer[off], &restart, - sizeof(struct RESTART_AREA)); + sizeof(RESTART_AREA)); off += le16_to_cpu(restart.client_array_offset); memcpy(&buffer[off], &client, - sizeof(struct RESTART_CLIENT)); + sizeof(LOG_CLIENT_RECORD)); if (!ntfs_mst_pre_write_fixup((NTFS_RECORD*)buffer, blocksz) && (ntfs_attr_pwrite(log_na, 0, blocksz, buffer) == blocksz) @@ -3002,8 +3015,8 @@ static const struct BUFFER *best_start(const struct BUFFER *buf, const struct BUFFER *altbuf) { const struct BUFFER *best; - const struct RECORD_PAGE_HEADER *head; - const struct RECORD_PAGE_HEADER *althead; + const RECORD_PAGE_HEADER *head; + const RECORD_PAGE_HEADER *althead; s64 diff; if (!buf || !altbuf) @@ -3019,7 +3032,7 @@ static const struct BUFFER *best_start(const struct BUFFER *buf, else best = buf; } - if (best && !le32_eq(best->block.record.head.magic, magic_RCRD)) + if (best && !le32_eq(best->block.record.magic, magic_RCRD)) best = (const struct BUFFER*)NULL; return (best); } @@ -3109,18 +3122,37 @@ static int locatelogfile(CONTEXT *ctx) static BOOL getlogfiledata(CONTEXT *ctx, const char *boot) { - const struct RESTART_PAGE_HEADER *rph; - const struct RESTART_AREA *rest; + const RESTART_PAGE_HEADER *rph; + const RESTART_AREA *rest; BOOL ok; u32 off; s64 size; + u32 system_page_size; + u32 log_page_size; ok = FALSE; fseek(ctx->file,0L,2); size = ftell(ctx->file); - rph = (const struct RESTART_PAGE_HEADER*)boot; - off = le16_to_cpu(rph->restart_offset); - rest = (const struct RESTART_AREA*)&boot[off]; + rph = (const RESTART_PAGE_HEADER*)boot; + off = le16_to_cpu(rph->restart_area_offset); + /* + * 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. + */ + system_page_size = le32_to_cpu(rph->system_page_size); + log_page_size = le32_to_cpu(rph->log_page_size); + if (system_page_size < NTFS_BLOCK_SIZE || + log_page_size < NTFS_BLOCK_SIZE || + system_page_size & (system_page_size - 1) || + log_page_size & (log_page_size - 1)) { + printf("** Unsupported page size.\n"); + goto out; + } + if (off & 7 || off > system_page_size) { + printf("** Inconsistent restart area offset.\n"); + goto out; + } + rest = (const RESTART_AREA*)&boot[off]; /* estimate cluster size from log file size (unreliable) */ switch (le32_to_cpu(rest->seq_number_bits)) { @@ -3143,6 +3175,7 @@ static BOOL getlogfiledata(CONTEXT *ctx, const char *boot) mftrecsz = 0; mftrecbits = 0; ok = TRUE; +out: return (ok); } @@ -3155,11 +3188,11 @@ static BOOL getlogfiledata(CONTEXT *ctx, const char *boot) static BOOL getvolumedata(CONTEXT *ctx, char *boot) { - const struct RESTART_AREA *rest; + const RESTART_AREA *rest; BOOL ok; ok = FALSE; - rest = (const struct RESTART_AREA*)NULL; + rest = (const RESTART_AREA*)NULL; if (ctx->vol) { getboot(boot); mftlcn = ctx->vol->mft_lcn; @@ -3173,8 +3206,9 @@ static BOOL getvolumedata(CONTEXT *ctx, char *boot) if (ctx->file && (!memcmp(boot,"RSTR",4) || !memcmp(boot,"CHKD",4))) { printf("* Assuming a log file copy\n"); - getlogfiledata(ctx, boot); - ok = TRUE; + ok = getlogfiledata(ctx, boot); + if (!ok) + goto out; } else fprintf(stderr,"** Not an NTFS image or log file\n"); } @@ -3188,6 +3222,7 @@ static BOOL getvolumedata(CONTEXT *ctx, char *boot) if (le16_to_cpu(rest->client_in_use_list) > 1) printf("** multiple clients not implemented\n"); } +out: return (ok); } @@ -3224,15 +3259,19 @@ static BOOL open_volume(CONTEXT *ctx, const char *device_name) /* This appears to be a log file */ ctx->vol = (ntfs_volume*)NULL; ok = getvolumedata(ctx, boot.buf); - } - if (!ok) + if (!ok) { + fclose(ctx->file); + goto out; + } + } else { fclose(ctx->file); + } } if (!ok) { /* Not a log file, assume an ntfs device, mount it */ ctx->file = (FILE*)NULL; ctx->vol = ntfs_mount(device_name, - ((optp || optu || opts) && !optn + ((optk || optp || optu || opts) && !optn ? NTFS_MNT_FORENSIC : NTFS_MNT_RDONLY)); if (ctx->vol) { ok = getvolumedata(ctx, boot.buf); @@ -3240,6 +3279,7 @@ static BOOL open_volume(CONTEXT *ctx, const char *device_name) ntfs_umount(ctx->vol, TRUE); } } +out: return (ok); } @@ -3267,7 +3307,7 @@ static u16 dorcrd(CONTEXT *ctx, u32 blk, u16 pos, const struct BUFFER *buf, static TRISTATE backoverlap(CONTEXT *ctx, int blk, const char *data, const char *nextdata, int k) { - const struct LOG_RECORD *logr; + const LOG_RECORD *logr; char *fullrec; s32 size; int space; @@ -3275,11 +3315,11 @@ static TRISTATE backoverlap(CONTEXT *ctx, int blk, TRISTATE state; u16 blkheadsz; - logr = (const struct LOG_RECORD*)&data[k]; + logr = (const LOG_RECORD*)&data[k]; state = T_ERR; size = le32_to_cpu(logr->client_data_length) + LOG_RECORD_HEAD_SZ; space = blocksz - k; - blkheadsz = sizeof(struct RECORD_PAGE_HEADER) + blkheadsz = sizeof(RECORD_PAGE_HEADER) + ((2*getle16(data,6) - 1) | 7) + 1; nextspace = blocksz - blkheadsz; if ((space >= LOG_RECORD_HEAD_SZ) @@ -3318,7 +3358,7 @@ static TRISTATE backoverlap(CONTEXT *ctx, int blk, } } - state = (likelyop((struct LOG_RECORD*)fullrec) ? T_OK : T_ERR); + state = (likelyop((LOG_RECORD*)fullrec) ? T_OK : T_ERR); actionnum++; if (optv) { printf("\nOverlapping backward action %d at 0x%x" @@ -3330,10 +3370,10 @@ static TRISTATE backoverlap(CONTEXT *ctx, int blk, (long)blk,(int)space,(state == T_OK)); } if (state == T_OK) { - showlogr(ctx, k, (struct LOG_RECORD*)fullrec); + showlogr(ctx, k, (LOG_RECORD*)fullrec); if (optp || optu || opts) state = enqueue_action(ctx, - (struct LOG_RECORD*)fullrec, + (LOG_RECORD*)fullrec, size, actionnum); } else { /* Try to go on unless playing actions */ @@ -3362,10 +3402,10 @@ static TRISTATE backward_rcrd(CONTEXT *ctx, u32 blk, int skipped, const struct BUFFER *buf, const struct BUFFER *prevbuf, const struct BUFFER *nextbuf) { - u16 poslist[75]; /* 4096/sizeof(struct LOG_RECORD) */ - const struct RECORD_PAGE_HEADER *rph; - const struct RECORD_PAGE_HEADER *prevrph; - const struct LOG_RECORD *logr; + u16 poslist[75]; /* 4096/sizeof(LOG_RECORD) */ + const RECORD_PAGE_HEADER *rph; + const RECORD_PAGE_HEADER *prevrph; + const LOG_RECORD *logr; const char *data; const char *nextdata; BOOL stop; @@ -3378,12 +3418,12 @@ static TRISTATE backward_rcrd(CONTEXT *ctx, u32 blk, int skipped, state = T_ERR; rph = &buf->block.record; - prevrph = (struct RECORD_PAGE_HEADER*)NULL; + prevrph = (RECORD_PAGE_HEADER*)NULL; if (prevbuf) prevrph = &prevbuf->block.record; data = buf->block.data; - if (rph && le32_eq(rph->head.magic, magic_RCRD) - && (!prevrph || le32_eq(prevrph->head.magic, magic_RCRD))) { + if (rph && le32_eq(rph->magic, magic_RCRD) + && (!prevrph || le32_eq(prevrph->magic, magic_RCRD))) { if (optv) { if (optv >= 2) hexdump(data,blocksz); @@ -3399,12 +3439,12 @@ static TRISTATE backward_rcrd(CONTEXT *ctx, u32 blk, int skipped, k = buf->headsz; else k = firstrecord(skipped, buf, prevbuf); - logr = (const struct LOG_RECORD*)&data[k]; + logr = (const LOG_RECORD*)&data[k]; cnt = 0; /* check whether there is at least one beginning of record */ endoff = le16_to_cpu(rph->next_record_offset); if (k && ((k < endoff) || !endoff)) { - logr = (const struct LOG_RECORD*)&data[k]; + logr = (const LOG_RECORD*)&data[k]; if (likelyop(logr)) { stop = FALSE; state = T_OK; @@ -3430,7 +3470,7 @@ static TRISTATE backward_rcrd(CONTEXT *ctx, u32 blk, int skipped, } } while (!stop) { - logr = (const struct LOG_RECORD*)&data[k]; + logr = (const LOG_RECORD*)&data[k]; size = le32_to_cpu(logr->client_data_length) + LOG_RECORD_HEAD_SZ; if ((size < MINRECSIZE) @@ -3472,7 +3512,7 @@ static TRISTATE backward_rcrd(CONTEXT *ctx, u32 blk, int skipped, } for (j=cnt-1; (j>=0) && (state==T_OK); j--) { k = poslist[j]; - logr = (const struct LOG_RECORD*)&data[k]; + logr = (const LOG_RECORD*)&data[k]; size = le32_to_cpu(logr->client_data_length) + LOG_RECORD_HEAD_SZ; actionnum++; @@ -3537,7 +3577,7 @@ static int walkback(CONTEXT *ctx, const struct BUFFER *buf, u32 blk, else skipped = blk - prevblk - 1 + (logfilesz >> blockbits) - BASEBLKS; - magic = prevbuf->block.record.head.magic; + magic = prevbuf->block.record.magic; /* switch (magic) { */ if (le32_eq(magic, magic_RCRD)) { } @@ -3599,7 +3639,7 @@ static int walk(CONTEXT *ctx) const struct BUFFER *prevbuf; const struct BUFFER *startbuf; const NTFS_RECORD *record; - const struct RECORD_PAGE_HEADER *rph; + const RECORD_PAGE_HEADER *rph; NTFS_RECORD_TYPES magic; u32 blk; u32 nextblk; @@ -3652,7 +3692,7 @@ static int walk(CONTEXT *ctx) err = 1; /* break; */ } - magic = buf->block.record.head.magic; + magic = buf->block.record.magic; /* switch (magic) { */ if (le32_eq(magic, magic_CHKD) || le32_eq(magic, magic_RSTR)) { @@ -3790,7 +3830,7 @@ static int walk(CONTEXT *ctx) prevbuf = findprevious(ctx, buf); if (prevbuf) { prevblk = prevbuf->num; - magic = prevbuf->block.record.head.magic; + magic = prevbuf->block.record.magic; /* switch (magic) { */ if (le32_eq(magic, magic_RCRD)) { } @@ -3834,7 +3874,7 @@ static void version(void) { printf("\n%s v%s (libntfs-3g) - Recover updates committed by Windows" " on an NTFS Volume.\n\n", "ntfsrecover", VERSION); - printf("Copyright (c) 2012-2015 Jean-Pierre Andre\n"); + printf("Copyright (c) 2012-2016 Jean-Pierre Andre\n"); printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); } @@ -3850,6 +3890,7 @@ static void usage(void) fprintf(stderr," -i : show invalid (stale) records\n"); fprintf(stderr," -f : show the full log forward\n"); fprintf(stderr," -h : show this help information\n"); + fprintf(stderr," -k : kill fast restart data\n"); fprintf(stderr," -n : do not apply any modification\n"); fprintf(stderr," -p : undo the latest count transaction sets and play one\n"); fprintf(stderr," -r : show a range of log blocks forward\n"); @@ -3858,7 +3899,6 @@ static void usage(void) fprintf(stderr," -u : undo the latest count transaction sets\n"); fprintf(stderr," -v : show more information (-vv yet more)\n"); fprintf(stderr," -V : show version and exit\n"); - fprintf(stderr," Copyright (c) 2012-2015 Jean-Pierre Andre\n"); } /* @@ -3872,12 +3912,13 @@ static BOOL getoptions(int argc, char *argv[]) u32 xval; char *endptr; BOOL err; - static const char *sopt = "-bc:hifnp:r:stu:vVx:"; + static const char *sopt = "-bc:hifknp:r:stu:vVx:"; static const struct option lopt[] = { { "backward", no_argument, NULL, 'b' }, { "clusters", required_argument, NULL, 'c' }, { "forward", no_argument, NULL, 'f' }, { "help", no_argument, NULL, 'h' }, + { "kill-fast-restart", no_argument, NULL, 'k' }, { "no-action", no_argument, NULL, 'n' }, { "play", required_argument, NULL, 'p' }, { "range", required_argument, NULL, 'r' }, @@ -3897,6 +3938,7 @@ static BOOL getoptions(int argc, char *argv[]) optf = FALSE; opth = FALSE; opti = FALSE; + optk = FALSE; optn = FALSE; optp = FALSE; optr = FALSE; @@ -3939,6 +3981,9 @@ static BOOL getoptions(int argc, char *argv[]) case 'h': opth = TRUE; break; + case 'k': + optk = TRUE; + break; case 'n': optn = TRUE; break; @@ -4052,40 +4097,40 @@ static BOOL checkstructs(void) BOOL ok; ok = TRUE; - if (sizeof(struct RECORD_PAGE_HEADER) != 40) { + if (sizeof(RECORD_PAGE_HEADER) != 40) { fprintf(stderr, - "* error : bad sizeof(struct RECORD_PAGE_HEADER) %d\n", - (int)sizeof(struct RECORD_PAGE_HEADER)); + "* error : bad sizeof(RECORD_PAGE_HEADER) %d\n", + (int)sizeof(RECORD_PAGE_HEADER)); ok = FALSE; } - if (sizeof(struct LOG_RECORD) != 88) { + if (sizeof(LOG_RECORD) != 88) { fprintf(stderr, - "* error : bad sizeof(struct LOG_RECORD) %d\n", - (int)sizeof(struct LOG_RECORD)); + "* error : bad sizeof(LOG_RECORD) %d\n", + (int)sizeof(LOG_RECORD)); ok = FALSE; } - if (sizeof(struct RESTART_PAGE_HEADER) != 32) { + if (sizeof(RESTART_PAGE_HEADER) != 32) { fprintf(stderr, - "* error : bad sizeof(struct RESTART_PAGE_HEADER) %d\n", - (int)sizeof(struct RESTART_PAGE_HEADER)); + "* error : bad sizeof(RESTART_PAGE_HEADER) %d\n", + (int)sizeof(RESTART_PAGE_HEADER)); ok = FALSE; } - if (sizeof(struct RESTART_AREA) != 44) { + if (sizeof(RESTART_AREA) != 48) { fprintf(stderr, - "* error : bad sizeof(struct RESTART_AREA) %d\n", - (int)sizeof(struct RESTART_AREA)); + "* error : bad sizeof(RESTART_AREA) %d\n", + (int)sizeof(RESTART_AREA)); ok = FALSE; } - if (sizeof(struct ATTR_OLD) != 44) { + if (sizeof(ATTR_OLD) != 44) { fprintf(stderr, - "* error : bad sizeof(struct ATTR_OLD) %d\n", - (int)sizeof(struct ATTR_OLD)); + "* error : bad sizeof(ATTR_OLD) %d\n", + (int)sizeof(ATTR_OLD)); ok = FALSE; } - if (sizeof(struct ATTR_NEW) != 40) { + if (sizeof(ATTR_NEW) != 40) { fprintf(stderr, - "* error : bad sizeof(struct ATTR_NEW) %d\n", - (int)sizeof(struct ATTR_NEW)); + "* error : bad sizeof(ATTR_NEW) %d\n", + (int)sizeof(ATTR_NEW)); ok = FALSE; } if (LastAction != 38) { diff --git a/ntfsprogs/ntfsrecover.h b/ntfsprogs/ntfsrecover.h index 5da42c66..abc72a0f 100644 --- a/ntfsprogs/ntfsrecover.h +++ b/ntfsprogs/ntfsrecover.h @@ -2,7 +2,7 @@ * Declarations for processing log data * * Copyright (c) 2000-2005 Anton Altaparmakov - * Copyright (c) 2014-2015 Jean-Pierre Andre + * Copyright (c) 2014-2016 Jean-Pierre Andre */ /* @@ -22,16 +22,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* - * TODO - * This file partially duplicates logfile.h (with modifications). - * The generic declarations are to be moved to logfile.h, thus - * implying adapting (at least) libntfs-3g/logfile.c and - * ntfsprogs/ntfsdump_logfile.c, and the declarations specific to - * ntfsrecover should be kept in this file. - * (removing ntfsdump_logfile.c might also be considered). - */ - #define getle16(p,x) le16_to_cpu(*(const le16*)((const char*)(p) + (x))) #define getle32(p,x) le32_to_cpu(*(const le32*)((const char*)(p) + (x))) #define getle64(p,x) le64_to_cpu(*(const le64*)((const char*)(p) + (x))) @@ -40,17 +30,6 @@ #define feedle32(p,x) (*(const le32*)((const char*)(p) + (x))) #define feedle64(p,x) (*(const le64*)((const char*)(p) + (x))) -enum LOG_RECORD_TYPE { - LOG_STANDARD = 1, - LOG_CHECKPOINT = 2 -} ; - - /* These flags were introduced in Vista in field attribute_flags */ -enum ATTRIBUTE_FLAGS { - ACTS_ON_MFT = 2, - ACTS_ON_INDX = 8 -} ; - enum ACTIONS { Noop, /* 0 */ CompensationlogRecord, /* 1 */ @@ -93,153 +72,14 @@ enum ACTIONS { LastAction /* 38 */ } ; - /* Flags for field log_record_flags, their meaning is unclear */ -enum RECORD_FLAGS { - RECORD_UNKNOWN = 1, - /* The flags below were introduced in Windows 10 */ - RECORD_DELETING = 2, - RECORD_ADDING = 4 -} ; -typedef le16 LOG_RECORD_FLAGS; - -#define LOGFILE_NO_CLIENT const_cpu_to_le16(0xffff) -#define RESTART_VOLUME_IS_CLEAN const_cpu_to_le16(0x0002) - -/* ntfsdoc p 39 (47), not in layout.h */ - -typedef struct RESTART_PAGE_HEADER { /* size 32 */ - NTFS_RECORD head; - leLSN chkdsk_lsn; - le32 system_page_size; - le32 log_page_size; - le16 restart_offset; - le16 minor_ver; - le16 major_ver; - le16 usn; -} __attribute__((__packed__)) RESTART_PAGE_HEADER; - -/* ntfsdoc p 40 (48), not in layout.h */ - -struct RESTART_AREA { /* size 44 */ - leLSN current_lsn; - le16 log_clients; - le16 client_free_list; - le16 client_in_use_list; - le16 flags; - le32 seq_number_bits; - le16 restart_area_length; - le16 client_array_offset; - le64 file_size; - le32 last_lsn_data_length; - le16 record_length; - le16 log_page_data_offset; - le32 restart_log_open_count; -} __attribute__((__packed__)) ; - -typedef struct RESTART_CLIENT { /* size 160 */ -/*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*/ le16 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; - -/* ntfsdoc p 41 (49), not in layout.h */ - -struct RECORD_PAGE_HEADER { /* size 40 */ - NTFS_RECORD head; /* the magic is "RCRD" */ - union { - leLSN last_lsn; - le32 file_offset; - } __attribute__((__packed__)) copy; - le32 flags; - le16 page_count; - le16 page_position; - le16 next_record_offset; - le16 reserved4[3]; - leLSN last_end_lsn; -} __attribute__((__packed__)) ; - -/* ntfsdoc p 42 (50), not in layout.h */ - -#define LOG_RECORD_HEAD_SZ 0x30 /* size of header of struct LOG_RECORD */ - -typedef struct LOG_RECORD { /* size 80 */ - leLSN this_lsn; - leLSN client_previous_lsn; - leLSN client_undo_next_lsn; - le32 client_data_length; - struct { - le16 seq_number; - le16 client_index; - } __attribute__((__packed__)) client_id; - le32 record_type; - le32 transaction_id; - LOG_RECORD_FLAGS log_record_flags; - le16 reserved1[3]; - le16 redo_operation; - le16 undo_operation; - le16 redo_offset; - le16 redo_length; - union { - struct { - le16 undo_offset; - le16 undo_length; - le16 target_attribute; - le16 lcns_to_follow; - le16 record_offset; - le16 attribute_offset; - le16 cluster_index; - le16 attribute_flags; - le32 target_vcn; - le32 reserved3; - le64 lcn_list[0]; - } __attribute__((__packed__)); - struct { - leLSN transaction_lsn; - leLSN attributes_lsn; - leLSN names_lsn; - leLSN dirty_pages_lsn; - le64 unknown_list[0]; - } __attribute__((__packed__)); - } __attribute__((__packed__)); -} __attribute__((__packed__)) LOG_RECORD; - struct BUFFER { unsigned int num; unsigned int size; unsigned int headsz; BOOL safe; union { - struct RESTART_PAGE_HEADER restart; - struct RECORD_PAGE_HEADER record; + RESTART_PAGE_HEADER restart; + RECORD_PAGE_HEADER record; char data[1]; } block; /* variable length, keep at the end */ } ; @@ -249,7 +89,7 @@ struct ACTION_RECORD { struct ACTION_RECORD *prev; int num; unsigned int flags; - struct LOG_RECORD record; /* variable length, keep at the end */ + LOG_RECORD record; /* variable length, keep at the end */ } ; enum { /* Flag values for ACTION_RECORD */ @@ -265,31 +105,6 @@ struct ATTR { le16 name[1]; } ; -struct BITMAP_ACTION { - le32 firstbit; - le32 count; -} ; - -/* Danger in arrays : contains le64's though size is not a multiple of 8 */ -typedef struct ATTR_OLD { /* Format up to Win10 (44 bytes) */ - le64 unknown1; - le64 unknown2; - le64 inode; - leLSN lsn; - le32 unknown3; - le32 type; - le32 unknown4; -} __attribute__((__packed__)) ATTR_OLD; - -typedef struct ATTR_NEW { /* Format since Win10 (40 bytes) */ - le64 unknown1; - le64 unknown2; - le32 type; - le32 unknown3; - le64 inode; - leLSN lsn; -} __attribute__((__packed__)) ATTR_NEW; - extern u32 clustersz; extern int clusterbits; extern u32 blocksz; @@ -315,23 +130,22 @@ extern u64 synced_lsn; extern u64 latest_lsn; extern u64 restart_lsn; -extern struct RESTART_AREA restart; -extern struct RESTART_CLIENT client; +extern RESTART_AREA restart; +extern LOG_CLIENT_RECORD client; const char *actionname(int op); const char *mftattrname(ATTR_TYPES attr); void showname(const char *prefix, const char *name, int cnt); int fixnamelen(const char *name, int len); -BOOL within_lcn_range(const struct LOG_RECORD *logr); +BOOL within_lcn_range(const LOG_RECORD *logr); struct ATTR *getattrentry(unsigned int key, unsigned int lth); void copy_attribute(struct ATTR *pa, const char *buf, int length); -u32 get_undo_offset(const struct LOG_RECORD *logr); -u32 get_redo_offset(const struct LOG_RECORD *logr); -u32 get_extra_offset(const struct LOG_RECORD *logr); +u32 get_undo_offset(const LOG_RECORD *logr); +u32 get_redo_offset(const LOG_RECORD *logr); +u32 get_extra_offset(const LOG_RECORD *logr); BOOL exception(int num); struct STORE; -BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp); extern int play_undos(ntfs_volume *vol, const struct ACTION_RECORD *firstundo); extern int play_redos(ntfs_volume *vol, const struct ACTION_RECORD *firstredo); extern void show_redos(void); diff --git a/ntfsprogs/ntfsresize.8.in b/ntfsprogs/ntfsresize.8.in index 177eb500..2ee53460 100644 --- a/ntfsprogs/ntfsresize.8.in +++ b/ntfsprogs/ntfsresize.8.in @@ -88,7 +88,7 @@ To enlarge an NTFS filesystem, first you must enlarge the size of the underlying partition. This can be done using .BR fdisk (8) by deleting the partition and recreating it with a larger size. -Make sure it will not overlap with an other existing partition. +Make sure it will not overlap with another existing partition. You may enlarge upwards (first sector unchanged) or downwards (last sector unchanged), but you may not enlarge at both ends in a single step. If you merge two NTFS partitions, only one of them can be expanded to the diff --git a/ntfsprogs/ntfsresize.c b/ntfsprogs/ntfsresize.c index bd350599..8d1d5f96 100644 --- a/ntfsprogs/ntfsresize.c +++ b/ntfsprogs/ntfsresize.c @@ -2377,6 +2377,7 @@ static void truncate_badclust_bad_attr(ntfs_resize_t *resize) { ntfs_inode *base_ni; ntfs_attr *na; + ntfs_attr_search_ctx *ctx; s64 nr_clusters = resize->new_volume_size; ntfs_volume *vol = resize->vol; @@ -2390,7 +2391,17 @@ static void truncate_badclust_bad_attr(ntfs_resize_t *resize) err_printf("Could not adjust the bad sector list\n"); exit(1); } - na->ni->flags = le32_or(na->ni->flags, FILE_ATTR_SPARSE_FILE); + /* Clear the sparse flags, even if there are bad clusters */ + na->ni->flags = + le32_and(na->ni->flags, le32_not(FILE_ATTR_SPARSE_FILE)); + na->data_flags = le16_and(na->data_flags, le16_not(ATTR_IS_SPARSE)); + ctx = resize->ctx; + ctx->attr->data_size = cpu_to_sle64(na->data_size); + ctx->attr->initialized_size = cpu_to_sle64(na->initialized_size); + ctx->attr->flags = cpu_to_le16(na->data_flags); + ctx->attr->compression_unit = 0; + ntfs_inode_mark_dirty(ctx->ntfs_ino); + NInoFileNameSetDirty(na->ni); NInoFileNameSetDirty(na->ni); ntfs_attr_close(na); diff --git a/src/ntfs-3g.secaudit.8.in b/ntfsprogs/ntfssecaudit.8.in similarity index 93% rename from src/ntfs-3g.secaudit.8.in rename to ntfsprogs/ntfssecaudit.8.in index 669828e5..b715f4d9 100644 --- a/src/ntfs-3g.secaudit.8.in +++ b/ntfsprogs/ntfssecaudit.8.in @@ -1,11 +1,11 @@ -.\" Copyright (c) 2007-2009 Jean-Pierre André. +.\" Copyright (c) 2007-2016 Jean-Pierre André. .\" This file may be copied under the terms of the GNU Public License. .\" -.TH NTFS-3G.SECAUDIT 8 "February 2010" "ntfs-3g.secaudit 1.4.1" +.TH NTFSSECAUDIT 8 "February 2010" "ntfssecaudit 1.5.0" .SH NAME -ntfs-3g.secaudit \- NTFS Security Data Auditing +ntfssecaudit \- NTFS Security Data Auditing .SH SYNOPSIS -.B ntfs-3g.secaudit +.B ntfssecaudit \fB[\fIoptions\fP\fB]\fR .I args .PP @@ -39,7 +39,7 @@ and args define the parameters and the set of files acted upon. .PP Typing secaudit with no args will display a summary of available options. .SH DESCRIPTION -\fBntfs-3g.secaudit\fR +\fBntfssecaudit\fR displays the ownership and permissions of a set of files on an NTFS file system, and checks their consistency. It can be started in terminal mode only (no graphical user interface is available.) @@ -53,7 +53,7 @@ of information. It is therefore advisable to redirect the output to a file or pipe it to a text editor for examination. .SH OPTIONS Below are the valid combinations of options and arguments that -\fBntfs-3g.secaudit\fR accepts. All the indicated arguments are +\fBntfssecaudit\fR accepts. All the indicated arguments are mandatory and must be unique (if wildcards are used, they must resolve to a single name.) .TP @@ -135,25 +135,25 @@ Example : "\fBu::7,g::5,o:0,u:510:rwx,g:500:5,d:u:510:7\fP" Audit the global security data on /dev/sda1 .RS .sp -.B ntfs-3g.secaudit -ar /dev/sda1 +.B ntfssecaudit -ar /dev/sda1 .sp .RE Display the ownership and permissions parameters for files in directory /audio/music on device /dev/sda5, excluding sub-directories : .RS .sp -.B ntfs-3g.secaudit /dev/sda5 /audio/music +.B ntfssecaudit /dev/sda5 /audio/music .sp .RE Set all files in directory /audio/music on device /dev/sda5 as writeable by owner and read-only for everybody : .RS .sp -.B ntfs-3g.secaudit -r /dev/sda5 644 /audio/music +.B ntfssecaudit -r /dev/sda5 644 /audio/music .sp .RE .SH EXIT CODES -.B ntfs-3g.secaudit +.B ntfssecaudit exits with a value of 0 when no error was detected, and with a value of 1 when an error was detected. .SH KNOWN ISSUES @@ -170,7 +170,7 @@ in detail. You can contact the development team on the ntfs\-3g\-devel@lists.sf.net address. .SH AUTHORS -.B ntfs-3g.secaudit +.B ntfssecaudit has been developed by Jean-Pierre André. .SH THANKS Several people made heroic efforts, often over five or more diff --git a/src/secaudit.c b/ntfsprogs/ntfssecaudit.c similarity index 64% rename from src/secaudit.c rename to ntfsprogs/ntfssecaudit.c index 29236c6d..c1af9b82 100644 --- a/src/secaudit.c +++ b/ntfsprogs/ntfssecaudit.c @@ -1,8 +1,8 @@ /* * Display and audit security attributes in an NTFS volume * - * Copyright (c) 2007-2015 Jean-Pierre Andre - * + * Copyright (c) 2007-2016 Jean-Pierre Andre + * * Options : * -a auditing security data * -b backing up NTFS ACLs @@ -16,43 +16,47 @@ * -t run internal tests (with no access to storage) * * On Linux (being root, with volume not mounted) : - * secaudit -h [file] + * ntfssecaudit -h [file] * display the security descriptors found in file - * secaudit -a[rv] volume + * ntfssecaudit -a[rv] volume * audit the volume - * secaudit [-v] volume file + * ntfssecaudit [-v] volume file * display the security parameters of file - * secaudit -r[v] volume directory + * ntfssecaudit -r[v] volume directory * display the security parameters of files in directory - * secaudit -b[v] volume [directory] + * ntfssecaudit -b[v] volume [directory] * backup the security parameters of files in directory - * secaudit -s[ve] volume [backupfile] + * ntfssecaudit -s[ve] volume [backupfile] * set the security parameters as indicated in backup * with -e set extra parameters (Windows attrib) - * secaudit volume perms file + * ntfssecaudit volume perms file * set the security parameters of file to perms (mode or acl) - * secaudit -r[v] volume perms directory + * ntfssecaudit -r[v] volume perms directory * set the security parameters of files in directory to perms - * special case, does not require being root : - * secaudit [-v] mounted-file + * special cases, do not require being root : + * ntfssecaudit [-v] mounted-file * display the security parameters of mounted file + * ntfssecaudit -u[v] mounted-file + * display a user mapping proposal * * * On Windows (the volume being part of file name) - * secaudit -h [file] + * ntfssecaudit -h [file] * display the security descriptors found in file - * secaudit [-v] file + * ntfssecaudit -a[rv] volume + * audit the volume + * ntfssecaudit [-v] file * display the security parameters of file - * secaudit -r[v] directory + * ntfssecaudit -r[v] directory * display the security parameters of files in directory - * secaudit -b[v] directory + * ntfssecaudit -b[v] directory * backup the security parameters of files in directory - * secaudit -s[v] [backupfile] + * ntfssecaudit -s[v] volume [backupfile] * set the security parameters as indicated in backup * with -e set extra parameters (Windows attrib) - * secaudit perms file + * ntfssecaudit perms file * set the security parameters of file to perms (mode or acl) - * secaudit -r[v] perms directory + * ntfssecaudit -r[v] perms directory * set the security parameters of files in directory to perms */ @@ -221,6 +225,9 @@ * * May 2015, version 1.4.6 * - made to load shared library based on generic name + * + * Mar 2016, Version 1.5.0 + * - reorganized to rely on libntfs-3g even on Windows */ /* @@ -244,312 +251,202 @@ * General parameters which may have to be adapted to needs */ -#define AUDT_VERSION "1.4.6" +#define AUDT_VERSION "1.5.0" -#define GET_FILE_SECURITY "ntfs_get_file_security" -#define SET_FILE_SECURITY "ntfs_set_file_security" -#define GET_FILE_ATTRIBUTES "ntfs_get_file_attributes" -#define SET_FILE_ATTRIBUTES "ntfs_set_file_attributes" -#define READ_DIRECTORY "ntfs_read_directory" -#define READ_SDS "ntfs_read_sds" -#define READ_SII "ntfs_read_sii" -#define READ_SDH "ntfs_read_sdh" -#define GET_USER "ntfs_get_user" -#define GET_GROUP "ntfs_get_group" -#define GET_USID "ntfs_get_usid" -#define GET_GSID "ntfs_get_gsid" -#define INIT_FILE_SECURITY "ntfs_initialize_file_security" -#define LEAVE_FILE_SECURITY "ntfs_leave_file_security" +#define SELFTESTS 0 +#define NOREVBOM 0 /* still unclear what this should be */ /* * External declarations */ -#include -#include -#include -#include -#include -#include -#include -#include - - /* - * integration into secaudit, check whether Win32, - * may have to be adapted to compiler or something else - */ - -#ifndef WIN32 -#if defined(__WIN32) | defined(__WIN32__) | defined(WNSC) -#define WIN32 1 -#endif -#endif - - /* - * integration into secaudit/Win32 - */ -#ifdef WIN32 -#include -#define __LITTLE_ENDIAN 1234 -#define __BYTE_ORDER __LITTLE_ENDIAN -#else - /* - * integration into secaudit/STSC - */ -#ifdef STSC -#include -#undef __BYTE_ORDER -#define __BYTE_ORDER __BIG_ENDIAN -#else - /* - * integration into secaudit/Linux - */ - -#include -#ifdef HAVE_ENDIAN_H -#include -#endif -#ifdef HAVE_MACHINE_ENDIAN_H -#include -#endif -#include -#include -#endif /* STSC */ -#endif /* WIN32 */ -#include "secaudit.h" - -#ifndef WIN32 - -#ifndef STSC - -#if !defined(HAVE_CONFIG_H) && POSIXACLS && !defined(__SVR4) - /* require if not integrated into ntfs-3g package */ -#define HAVE_SETXATTR 1 -#endif - -#ifdef HAVE_CONFIG_H -#ifdef _FILE_OFFSET_BITS -#undef _FILE_OFFSET_BITS /* work around "_FILE_OFFSET_BITS" possibly already defined */ -#endif - /* according to config.h if integrated into ntfs-3g package */ #include "config.h" -#ifdef const /* work around "const" possibly redefined in config.h */ -#undef const -#endif -#ifndef POSIXACLS -#define POSIXACLS 0 -#endif -#endif /* HAVE_CONFIG_H */ +#ifdef HAVE_STDIO_H +#include +#endif /* HAVE_STDIO_H */ +#ifdef HAVE_STDLIB_H +#include +#endif /* HAVE_STDLIB_H */ +#ifdef HAVE_STRING_H +#include +#endif /* HAVE_STRING_H */ +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_TIME_H +#include +#endif /* HAVE_TIME_H */ +#ifdef HAVE_GETOPT_H +#include +#endif /* HAVE_GETOPT_H */ +#ifdef HAVE_ERRNO_H +#include +#endif /* HAVE_ERRNO_H */ +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ +#ifdef HAVE_SYS_TYPES_H +#include +#endif /* HAVE_SYS_TYPES_H */ +#ifdef HAVE_SYS_STAT_H +#include +#endif /* HAVE_SYS_STAT_H */ #ifdef HAVE_SETXATTR #include -#else +#else /* HAVE_SETXATTR */ #warning "The extended attribute package is not available" #endif /* HAVE_SETXATTR */ -#endif /* STSC */ - -#define NTFS_MNT_NONE 0 /* no flag for mounting the device */ -#define NTFS_MNT_RDONLY 1 /* flag for mounting the device read-only */ +#include "types.h" +#include "endians.h" +#include "support.h" +#include "layout.h" +#include "param.h" +#include "ntfstime.h" +#include "device_io.h" +#include "device.h" +#include "logging.h" +#include "runlist.h" +#include "mft.h" +#include "inode.h" +#include "attrib.h" +#include "bitmap.h" +#include "index.h" +#include "volume.h" +#include "unistr.h" +#include "mst.h" +#include "security.h" +#include "acls.h" +#include "realpath.h" +#include "utils.h" +#include "misc.h" struct CALLBACK; -typedef int (*dircallback)(struct CALLBACK *context, char *ntfsname, - int length, int type, long long pos, u64 mft_ref, - unsigned int dt_type); +typedef int (*dircallback)(void *context, const ntfschar *ntfsname, + const int length, const int type, const s64 pos, + const MFT_REF mft_ref, const unsigned int dt_type); + +#if POSIXACLS + +static BOOL same_posix(struct POSIX_SECURITY *pxdesc1, + struct POSIX_SECURITY *pxdesc2); + +#endif /* POSIXACLS */ #ifndef HAVE_SYSLOG_H void ntfs_log_early_error(const char *format, ...) __attribute__((format(printf, 1, 2))); -#endif - -#if USESTUBS | defined(STSC) - -int ntfs_get_file_security(void *scapi, - const char *path, DWORD selection, - char *buf, DWORD buflen, LPDWORD psize); -BOOL ntfs_set_file_security(void *scapi, - const char *path, DWORD selection, const char *attr); -int ntfs_get_file_attributes(void *scapi, - const char *path); -BOOL ntfs_set_file_attributes(void *scapi, - const char *path, DWORD attrib); -BOOL ntfs_read_directory(void *scapi, - const char *path, dircallback callback, void *context); -int ntfs_read_sds(void *scapi, - char *buf, DWORD buflen, DWORD offset); -void *ntfs_read_sii(void *scapi, void *entry); -void *ntfs_read_sdh(void *scapi, void *entry); - -int ntfs_get_usid(void *scapi, uid_t uid, char *buf); -int ntfs_get_gsid(void *scapi, gid_t gid, char *buf); -int ntfs_get_user(void *scapi, const char *usid); -int ntfs_get_group(void *scapi, const char *gsid); - -void *ntfs_initialize_file_security(const char *device, unsigned long flags); -BOOL ntfs_leave_file_security(void *scapi); - -#else - -typedef int (*type_get_file_security)(void *scapi, - const char *path, DWORD selection, - char *buf, DWORD buflen, LPDWORD psize); -typedef BOOL (*type_set_file_security)(void *scapi, - const char *path, DWORD selection, const char *attr); -typedef int (*type_get_file_attributes)(void *scapi, - const char *path); -typedef BOOL (*type_set_file_attributes)(void *scapi, - const char *path, DWORD attrib); -typedef BOOL (*type_read_directory)(void *scapi, - const char *path, dircallback callback, void *context); -typedef int (*type_read_sds)(void *scapi, - char *buf, DWORD buflen, DWORD offset); -typedef void *(*type_read_sii)(void *scapi, void *entry); -typedef void *(*type_read_sdh)(void *scapi, void *entry); - -typedef int (*type_get_usid)(void *scapi, uid_t uid, char *buf); -typedef int (*type_get_gsid)(void *scapi, gid_t gid, char *buf); -typedef int (*type_get_user)(void *scapi, const char *usid); -typedef int (*type_get_group)(void *scapi, const char *gsid); - -typedef void *(*type_initialize_file_security)(const char *device, - unsigned long flags); -typedef BOOL (*type_leave_file_security)(void *scapi); - -type_get_file_security ntfs_get_file_security; -type_set_file_security ntfs_set_file_security; -type_get_file_attributes ntfs_get_file_attributes; -type_set_file_attributes ntfs_set_file_attributes; -type_read_directory ntfs_read_directory; -type_read_sds ntfs_read_sds; -type_read_sii ntfs_read_sii; -type_read_sdh ntfs_read_sdh; - -type_get_usid ntfs_get_usid; -type_get_gsid ntfs_get_gsid; -type_get_user ntfs_get_user; -type_get_group ntfs_get_group; - -type_initialize_file_security ntfs_initialize_file_security; -type_leave_file_security ntfs_leave_file_security; - - -#endif /* USESTUBS | defined(STSC) */ -#endif /* WIN32 */ +#endif /* HAVE_SYSLOG_H */ #define ACCOUNTSIZE 256 /* maximum size of an account name */ +#define MAXFILENAME 4096 +#define MAXATTRSZ 65536 /* Max sec attr size (16448 met for WinXP) */ +#define MAXLINE 80 /* maximum processed size of a line */ +#define BUFSZ 1024 /* buffer size to read mapping file */ +#define LINESZ 120 /* maximum useful size of a mapping line */ + +typedef enum { RECSHOW, RECSET, RECSETPOSIX } RECURSE; +typedef enum { MAPNONE, MAPEXTERN, MAPLOCAL, MAPDUMMY } MAPTYPE; +typedef enum { CMD_AUDIT, CMD_BACKUP, CMD_HEX, CMD_HELP, CMD_SET, + CMD_TEST, CMD_USERMAP, CMD_VERSION, CMD_NONE } CMDS; + + +#define MAXSECURID 262144 +#define SECBLKSZ 8 +#define MAPDIR ".NTFS-3G" +#define MAPFILE "UserMapping" + +#ifdef HAVE_WINDOWS_H +#define DIRSEP "\\" +#else +#define DIRSEP "/" +#endif + + /* standard owner (and administrator) rights */ + +#define OWNER_RIGHTS le32_or(DELETE, le32_or(READ_CONTROL, le32_or(WRITE_DAC, le32_or(WRITE_OWNER, \ + le32_or(SYNCHRONIZE, \ + le32_or(FILE_READ_ATTRIBUTES, le32_or(FILE_WRITE_ATTRIBUTES, \ + le32_or(FILE_READ_EA, FILE_WRITE_EA)))))))) + + /* standard world rights */ + +#define WORLD_RIGHTS le32_or(READ_CONTROL, le32_or(FILE_READ_ATTRIBUTES, le32_or(FILE_READ_EA, \ + SYNCHRONIZE))) + + /* inheritance flags for files and directories */ + +#define FILE_INHERITANCE NO_PROPAGATE_INHERIT_ACE +#define DIR_INHERITANCE (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE) /* - * Prototypes for local functions + * To identify NTFS ACL meaning Posix ACL granted to root + * we use rights always granted to anybody, so they have no impact + * either on Windows or on Linux. */ -BOOL open_security_api(void); -BOOL close_security_api(void); -#ifndef WIN32 -BOOL open_volume(const char*, unsigned long flags); -BOOL close_volume(const char*); -#endif -unsigned int get2l(const char*, int); -unsigned long get4l(const char*, int); -u64 get6l(const char*, int); -u64 get6h(const char*, int); -u64 get8l(const char*, int); -void set2l(char*, unsigned int); -void set4l(char*, unsigned long); -void hexdump(const char*, int, int); -u32 hash(const le32*, int); -unsigned int utf8size(const char*, int); -unsigned int makeutf8(char*, const char*, int); -unsigned int utf16size(const char*); -unsigned int makeutf16(char*, const char*); -unsigned int utf16len(const char*); -void printname(FILE*, const char*); -void printerror(FILE*); -BOOL guess_dir(const char*); -void showsid(const char*, int, const char*, int); -void showusid(const char*, int); -void showgsid(const char*, int); -void showownership(const char*); -void showheader(const char*, int); -void showace(const char*, int, int, int); -void showacl(const char*, int, int, int); -void showdacl(const char*, int, int); -void showsacl(const char*, int, int); -void showall(const char*, int); -void showposix(const struct POSIX_SECURITY*); -int linux_permissions(const char*, BOOL); -uid_t linux_owner(const char*); -gid_t linux_group(const char*); -int basicread(void*, char*, size_t, off_t); -int dummyread(void*, char*, size_t, off_t); -int local_build_mapping(struct MAPPING *[], const char*); -void newblock(s32); -void freeblocks(void); -u32 getmsbhex(const char*); -u32 getlsbhex(const char*); -BOOL ishexdump(const char*, int, int); -void showhex(FILE*); -void showfull(const char*, BOOL); -BOOL applyattr(const char*, const char*, BOOL, int, s32); -BOOL restore(FILE*); -BOOL dorestore(const char*, FILE*); -u32 merge_rights(const struct POSIX_SECURITY*, BOOL); -void tryposix(struct POSIX_SECURITY*); -void check_samples(void); -void basictest(int, BOOL, const SID*, const SID*); -void posixtest(int, BOOL, const SID*, const SID*); -void selftests(void); -unsigned int getfull(char*, const char*); -BOOL updatefull(const char *name, DWORD flags, char *attr); -BOOL setfull(const char*, int, BOOL); -BOOL singleshow(const char*); -BOOL proposal(const char*, const char*); -BOOL showmounted(const char*); -BOOL processmounted(const char*); -BOOL recurseshow(const char*); -BOOL singleset(const char*, int); -BOOL recurseset(const char*, int); -#ifdef WIN32 -BOOL backup(const char*); -BOOL listfiles(const char*); -#if POSIXACLS -BOOL iterate(RECURSE, const char*, const struct POSIX_SECURITY*); -#else -BOOL iterate(RECURSE, const char*, mode_t); -#endif -#else -BOOL backup(const char*, const char*); -BOOL listfiles(const char*, const char*); -BOOL mapproposal(const char*, const char*); -#endif -#if POSIXACLS -BOOL setfull_posix(const char *, const struct POSIX_SECURITY*, BOOL); -struct POSIX_SECURITY *linux_permissions_posix(const char*, BOOL); -BOOL recurseset_posix(const char*, const struct POSIX_SECURITY*); -BOOL singleset_posix(const char*, const struct POSIX_SECURITY*); -struct POSIX_SECURITY *encode_posix_acl(const char*); -#endif -static void *stdmalloc(size_t); -static void stdfree(void*); +#define ROOT_OWNER_UNMARK SYNCHRONIZE /* ACL granted to root as owner */ +#define ROOT_GROUP_UNMARK FILE_READ_EA /* ACL granted to root as group */ -BOOL valid_sds(const char*, unsigned int, unsigned int, - unsigned int, u32, BOOL); -BOOL valid_sii(const char*, u32); -BOOL valid_sdh(const char*, u32, u32); -int consist_sds(const char*, unsigned int, unsigned int, BOOL); -int consist_sii(const char*); -int consist_sdh(const char*); -int audit_sds(BOOL); -int audit_sii(void); -int audit_sdh(void); -void audit_summary(void); -BOOL audit(const char*); -int getoptions(int, char*[]); +#define INSDS1 1 +#define INSDS2 2 +#define INSII 4 +#define INSDH 8 -#ifndef WIN32 +struct SII { /* this is an image of an $SII index entry */ + le16 offs; + le16 size; + le32 fill1; + le16 indexsz; + le16 indexksz; + le16 flags; + le16 fill2; + le32 keysecurid; + + /* did not find official description for the following */ + le32 hash; + le32 securid; + le32 dataoffsl; /* documented as badly aligned */ + le32 dataoffsh; + le32 datasize; +} ; + +struct SDH { /* this is an image of an $SDH index entry */ + le16 offs; + le16 size; + le32 fill1; + le16 indexsz; + le16 indexksz; + le16 flags; + le16 fill2; + le32 keyhash; + le32 keysecurid; + + /* did not find official description for the following */ + le32 hash; + le32 securid; + le32 dataoffsl; + le32 dataoffsh; + le32 datasize; + le32 fill3; + } ; + +#ifdef HAVE_WINDOWS_H +/* + * Including leads to numerous conflicts with layout.h + * so define a few needed Windows calls unrelated to ntfs-3g + */ +BOOL WINAPI LookupAccountSidA(const char*, void*, char*, u32*, + char*, u32*, s32*); +u32 WINAPI GetFileAttributesA(const char*); +#endif /* HAVE_WINDOWS_H */ + +#define INVALID_FILE_ATTRIBUTES (-1)/* error from ntfs_get_file_attributes() */ /* - * Structures for collecting directory contents (Linux only) + * Structures for collecting directory contents */ struct LINK { @@ -562,18 +459,27 @@ struct CALLBACK { const char *dir; } ; -int callback(struct CALLBACK *context, char *ntfsname, - int length, int type, long long pos, u64 mft_ref, - unsigned int dt_type); -#endif +static int callback(void *context, const ntfschar *ntfsname, + const int length, const int type, const s64 pos, + const MFT_REF mft_ref, const unsigned int dt_type); + +struct SECURITY_DATA { + u64 offset; + char *attr; + u32 hash; + u32 length; + unsigned int filecount:16; + unsigned int mode:12; + unsigned int flags:4; +} ; /* * Global constants */ -#define BANNER "secaudit " AUDT_VERSION " : NTFS security data auditing" +#define BANNER "ntfssecaudit " AUDT_VERSION " : NTFS security data auditing" -#if SELFTESTS & !USESTUBS +#ifdef SELFTESTS /* * Dummy mapping file (self tests only) @@ -591,44 +497,18 @@ char dummymapping[] = "500::" DUMMYAUTH "1000\n" "::" DUMMYAUTH "10000\n"; /* - * SID for world (S-1-1-0) - */ - -static const char worldsidbytes[] = { - 1, /* revision */ - 1, /* auth count */ - 0, 0, 0, 0, 0, 1, /* base */ - 0, 0, 0, 0 /* 1st level */ -} ; -static const SID *worldsid = (const SID*)worldsidbytes; - -/* * SID for authenticated user (S-1-5-11) */ - + static const char authsidbytes[] = { - 1, /* revision */ + 1, /* revision */ 1, /* auth count */ 0, 0, 0, 0, 0, 5, /* base */ 11, 0, 0, 0 /* 1st level */ }; - + static const SID *authsid = (const SID*)authsidbytes; -/* - * SID for administrator (S-1-5-32-544) - */ - -static const char adminsidbytes[] = { - 1, /* revision */ - 2, /* auth count */ - 0, 0, 0, 0, 0, 5, /* base */ - 32, 0, 0, 0, /* 1st level */ - 32, 2, 0, 0 /* 2nd level */ -}; - -static const SID *adminsid = (const SID*)adminsidbytes; - /* * SID for local users (S-1-5-32-545) */ @@ -643,180 +523,101 @@ static const char localsidbytes[] = { static const SID *localsid = (const SID*)localsidbytes; -/* +/* * SID for system (S-1-5-18) */ - + static const char systemsidbytes[] = { - 1, /* revision */ + 1, /* revision */ 1, /* auth count */ 0, 0, 0, 0, 0, 5, /* base */ 18, 0, 0, 0 /* 1st level */ }; - + static const SID *systemsid = (const SID*)systemsidbytes; -#endif +#endif /* SELFTESTS */ /* * Global variables */ -BOOL opt_a; /* audit security data */ -BOOL opt_b; /* backup NTFS ACLs */ BOOL opt_e; /* restore extra (currently windows attribs) */ -BOOL opt_h; /* display an hexadecimal descriptor in a file */ BOOL opt_r; /* recursively apply to subdirectories */ -BOOL opt_s; /* restore NTFS ACLs */ BOOL opt_u; /* user mapping proposal */ -#if SELFTESTS & !USESTUBS -BOOL opt_t; /* run self-tests */ -#endif int opt_v; /* verbose or very verbose*/ +CMDS cmd; /* command to process */ struct SECURITY_DATA *securdata[(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ)]; -#if FORCEMASK -BOOL opt_m; /* force a mask - dangerous */ -u32 forcemsk /* mask to force */ -#endif unsigned int errors; /* number of severe errors */ unsigned int warnings; /* number of non-severe errors */ -struct CHKALLOC *firstalloc; struct SECURITY_CONTEXT context; MAPTYPE mappingtype; - -#ifdef STSC -#define static -#endif - -#ifndef WIN32 - -void *ntfs_handle; -void *ntfs_context = (void*)NULL; +struct SECURITY_API *ntfs_context = (struct SECURITY_API*)NULL; /* - * Open and close the security API (platform dependent) + * Open and close the security API (obsolete) */ -BOOL open_security_api(void) +static BOOL open_security_api(void) { -#if USESTUBS | defined(STSC) return (TRUE); -#else - char *error; - BOOL err; - const char *libfile; - - err = TRUE; - libfile = getenv(ENVNTFS3G); - if (!libfile) - libfile = (sizeof(char*) == 8 ? LIBFILE64 : LIBFILE); -#ifdef __SVR4 - /* do not override library functions by caller ones */ - ntfs_handle = dlopen(libfile,RTLD_LAZY | RTLD_GROUP); -#else - ntfs_handle = dlopen(libfile,RTLD_LAZY); -#endif - if (ntfs_handle) { - ntfs_initialize_file_security = (type_initialize_file_security) - dlsym(ntfs_handle,INIT_FILE_SECURITY); - error = dlerror(); - if (error) - fprintf(stderr," %s\n",error); - else { - ntfs_leave_file_security = (type_leave_file_security) - dlsym(ntfs_handle,LEAVE_FILE_SECURITY); - ntfs_get_file_security = (type_get_file_security) - dlsym(ntfs_handle,GET_FILE_SECURITY); - ntfs_set_file_security = (type_set_file_security) - dlsym(ntfs_handle,SET_FILE_SECURITY); - ntfs_get_file_attributes = (type_get_file_attributes) - dlsym(ntfs_handle,GET_FILE_ATTRIBUTES); - ntfs_set_file_attributes = (type_set_file_attributes) - dlsym(ntfs_handle,SET_FILE_ATTRIBUTES); - ntfs_read_directory = (type_read_directory) - dlsym(ntfs_handle,READ_DIRECTORY); - ntfs_read_sds = (type_read_sds) - dlsym(ntfs_handle,READ_SDS); - ntfs_read_sii = (type_read_sii) - dlsym(ntfs_handle,READ_SII); - ntfs_read_sdh = (type_read_sdh) - dlsym(ntfs_handle,READ_SDH); - ntfs_get_user = (type_get_user) - dlsym(ntfs_handle,GET_USER); - ntfs_get_group = (type_get_group) - dlsym(ntfs_handle,GET_GROUP); - ntfs_get_usid = (type_get_usid) - dlsym(ntfs_handle,GET_USID); - ntfs_get_gsid = (type_get_gsid) - dlsym(ntfs_handle,GET_GSID); - err = !ntfs_initialize_file_security - || !ntfs_leave_file_security - || !ntfs_get_file_security - || !ntfs_set_file_security - || !ntfs_get_file_attributes - || !ntfs_set_file_attributes - || !ntfs_read_directory - || !ntfs_read_sds - || !ntfs_read_sii - || !ntfs_read_sdh - || !ntfs_get_user - || !ntfs_get_group - || !ntfs_get_usid - || !ntfs_get_gsid; - - if (error) - fprintf(stderr,"ntfs-3g API not available\n"); - } - } else { - fprintf(stderr,"Could not open ntfs-3g library\n"); - fprintf(stderr,"\nPlease set environment variable \"" ENVNTFS3G "\"\n"); - fprintf(stderr,"to appropriate path and retry\n"); - } - return (!err); -#endif /* USESTUBS | defined(STSC) */ } -BOOL close_security_api(void) +static BOOL close_security_api(void) { -#if USESTUBS | defined(STSC) return (0); -#else - return (!dlclose(ntfs_handle)); -#endif /* USESTUBS | defined(STSC) */ } /* - * Open and close a volume (platform dependent) + * Open and close a volume * Assumes a single volume is opened */ -BOOL open_volume(const char *volume, unsigned long flags) +static BOOL open_volume(const char *volume, unsigned long flags) { BOOL ok; ok = FALSE; if (!ntfs_context) { - ntfs_context = ntfs_initialize_file_security(volume,flags); + ntfs_context = ntfs_initialize_file_security(volume, flags); if (ntfs_context) { if (*(u32*)ntfs_context != MAGIC_API) { - fprintf(stderr,"Versions of ntfs-3g and secaudit" + fprintf(stderr,"Versions of ntfs-3g and ntfssecaudit" " are not compatible\n"); } else { - fprintf(stderr,"\"%s\" opened\n",volume); + fprintf(stderr,"\"%s\" opened %s\n",volume, + (flags & NTFS_MNT_RDONLY + ? "read-only" : "read-write")); mappingtype = MAPEXTERN; ok = TRUE; } } else { fprintf(stderr,"Could not open \"%s\"\n",volume); +#ifdef HAVE_WINDOWS_H + switch (errno) { + case EACCES : + fprintf(stderr,"You need Administrator rights to open \"%s\"\n", + volume); + break; + case EBUSY : + fprintf(stderr,"Looks like \"%s\" is mounted,\n",volume); + fprintf(stderr,"close all applications using it\n"); + break; + default : + fprintf(stderr,"Close all applications using %s\n", volume); + fprintf(stderr,"to make sure it is not mounted\n"); + } +#else fprintf(stderr,"Make sure \"%s\" is not mounted\n",volume); +#endif } } else fprintf(stderr,"A volume is already open\n"); return (ok); } -BOOL close_volume(const char *volume) +static BOOL close_volume(const char *volume) { BOOL r; @@ -825,17 +626,84 @@ BOOL close_volume(const char *volume) fprintf(stderr,"\"%s\" closed\n",volume); else fprintf(stderr,"Could not close \"%s\"\n",volume); - ntfs_context = (void*)NULL; + ntfs_context = (struct SECURITY_API*)NULL; return (r); } -#endif /* WIN32 */ +#ifdef HAVE_WINDOWS_H + +/* + * Make a path suitable for feeding to libntfs-3g + * + * Use '/' as a directory separator and remove /../ and /./ + */ + +static int cleanpath(char *path) +{ + int err; + char *p; + char *s, *d; + + err = 0; + for (p=path; *p; p++) + if (*p == '\\') + *p = '/'; + do { + s = (char*)NULL; + p = strstr(path, "/./"); + if (p) { + s = p + 3; + d = p + 1; + } else { + p = strstr(path, "/../"); + if (p) { + d = p; + while ((d != path) && (*--d != '/')) + d--; + if ((d != p) && (*d == '/')) { + s = p + 3; + } else + err = 1; + } + } + if (s) { + while (*s) + *d++ = *s++; + *d = 0; + } + } while (s && !err); + return (err); +} + +/* + * Build a path with Unix-type separators + * + * The path from the ntfs root is required for libntfs-3g calls + */ + +static char *unixname(const char *name) +{ + char *uname; + + uname = (char*)malloc(strlen(name) + 1); + if (uname) { + strcpy(uname, name); + if (cleanpath(uname)) { + fprintf(stderr,"Bad path %s\n",name); + free(uname); + uname = (char*)NULL; + } + } + return (uname); +} + +#endif /* HAVE_WINDOWS_H */ /* * Extract small or big endian data from an array of bytes */ -unsigned int get2l(const char *attr, int p) +static unsigned int get2l(const char *attr, int p) { int i; unsigned int v; @@ -846,7 +714,7 @@ unsigned int get2l(const char *attr, int p) return (v); } -unsigned long get4l(const char *attr, int p) +static unsigned long get4l(const char *attr, int p) { int i; unsigned long v; @@ -857,18 +725,7 @@ unsigned long get4l(const char *attr, int p) return (v); } -u64 get6l(const char *attr, int p) -{ - int i; - u64 v; - - v = 0; - for (i=0; i<6; i++) - v += ((long long)(attr[p+i] & 255)) << (8*i); - return (v); -} - -u64 get6h(const char *attr, int p) +static u64 get6h(const char *attr, int p) { int i; u64 v; @@ -879,7 +736,7 @@ u64 get6h(const char *attr, int p) return (v); } -u64 get8l(const char *attr, int p) +static u64 get8l(const char *attr, int p) { int i; u64 v; @@ -894,7 +751,7 @@ u64 get8l(const char *attr, int p) * Set small or big endian data into an array of bytes */ -void set2l(char *p, unsigned int v) +static void set2l(char *p, unsigned int v) { int i; @@ -902,7 +759,7 @@ void set2l(char *p, unsigned int v) p[i] = ((v >> 8*i) & 255); } -void set4l(char *p, unsigned long v) +static void set4l(char *p, unsigned long v) { int i; @@ -915,7 +772,7 @@ void set4l(char *p, unsigned long v) * hexadecimal dump of an array of bytes */ -void hexdump(const char *attr, int size, int level) +static void hexdump(const char *attr, int size, int level) { int i,j; @@ -929,7 +786,7 @@ void hexdump(const char *attr, int size, int level) } } -u32 hash(const le32 *buf, int size /* bytes */) +static u32 hash(const le32 *buf, int size /* bytes */) { u32 h; int i; @@ -946,50 +803,45 @@ u32 hash(const le32 *buf, int size /* bytes */) * Returns 0 for invalid input */ -unsigned int utf8size(const char *utf16, int length) +static unsigned int utf8size(const ntfschar *utf16, int length) { int i; - unsigned int size; - enum { BASE, SURR, ERR } state; + int count = 0; + BOOL surrog; + BOOL fail; - size = 0; - state = BASE; - for (i=0; i<2*length; i+=2) { - switch (state) { - case BASE : - if (utf16[i+1] & 0xf8) { - if ((utf16[i+1] & 0xf8) == 0xd8) - state = (utf16[i+1] & 4 ? ERR : SURR); - else + surrog = FALSE; + fail = FALSE; + for (i = 0; i < length && !le16_cmpz(utf16[i]) && !fail; i++) { + unsigned short c = le16_to_cpu(utf16[i]); + if (surrog) { + if ((c >= 0xdc00) && (c < 0xe000)) { + surrog = FALSE; + count += 4; + } else + fail = TRUE; + } else + if (c < 0x80) + count++; + else if (c < 0x800) + count += 2; + else if (c < 0xd800) + count += 3; + else if (c < 0xdc00) + surrog = TRUE; #if NOREVBOM - if (((utf16[i+1] & 0xff) == 0xff) - && ((utf16[i] & 0xfe) == 0xfe)) - state = ERR; - else - size += 3; + else if ((c >= 0xe000) && (c < 0xfffe)) #else - size += 3; + else if (c >= 0xe000) #endif - } else - if ((utf16[i] & 0x80) || utf16[i+1]) - size += 2; - else - size++; - break; - case SURR : - if ((utf16[i+1] & 0xfc) == 0xdc) { - state = BASE; - size += 4; - } else - state = ERR; - break; - case ERR : - break; - } + count += 3; + else + fail = TRUE; } - if (state != BASE) - size = 0; - return (size); + if (surrog) + fail = TRUE; + + return (fail ? 0 : count); } /* @@ -999,368 +851,57 @@ unsigned int utf8size(const char *utf16, int length) * Returns size or zero for invalid input */ -unsigned int makeutf8(char *utf8, const char *utf16, int length) +static unsigned int makeutf8(char *utf8, const ntfschar *utf16, int length) { - int i; - unsigned int size; - unsigned int rem; - enum { BASE, SURR, ERR } state; + int size; - size = 0; - rem = 0; - state = BASE; - for (i=0; i<2*length; i+=2) { - switch (state) { - case BASE : - if (utf16[i+1] & 0xf8) { - if ((utf16[i+1] & 0xf8) == 0xd8) { - if (utf16[i+1] & 4) - state = ERR; - else { - utf8[size++] = 0xf0 + (utf16[i+1] & 7) - + ((utf16[i] & 0xc0) == 0xc0); - utf8[size++] = 0x80 + (((utf16[i] + 64) >> 2) & 63); - rem = utf16[i] & 3; - state = SURR; - } - } else { -#if NOREVBOM - if (((utf16[i+1] & 0xff) == 0xff) - && ((utf16[i] & 0xfe) == 0xfe)) - state = ERR; - else { - utf8[size++] = 0xe0 + ((utf16[i+1] >> 4) & 15); - utf8[size++] = 0x80 - + ((utf16[i+1] & 15) << 2) - + ((utf16[i] >> 6) & 3); - utf8[size++] = 0x80 + (utf16[i] & 63); - } -#else - utf8[size++] = 0xe0 + ((utf16[i+1] >> 4) & 15); - utf8[size++] = 0x80 - + ((utf16[i+1] & 15) << 2) - + ((utf16[i] >> 6) & 3); - utf8[size++] = 0x80 + (utf16[i] & 63); -#endif - } - } else - if ((utf16[i] & 0x80) || utf16[i+1]) { - utf8[size++] = 0xc0 - + ((utf16[i+1] & 15) << 2) - + ((utf16[i] >> 6) & 3); - utf8[size++] = 0x80 + (utf16[i] & 63); - } else - utf8[size++] = utf16[i]; - break; - case SURR : - if ((utf16[i+1] & 0xfc) == 0xdc) { - utf8[size++] = 0x80 + (rem << 4) - + ((utf16[i+1] & 3) << 2) - + ((utf16[i] >> 6) & 3); - utf8[size++] = 0x80 + (utf16[i] & 63); - state = BASE; - } else - state = ERR; - break; - case ERR : - break; - } - } - utf8[size] = 0; - if (state != BASE) - state = ERR; - return (state == ERR ? 0 : size); + size = ntfs_ucstombs(utf16, length, &utf8, MAXFILENAME); + return (size < 0 ? 0 : size); } -#ifdef WIN32 - -/* - * Evaluate the size of UTF-16LE conversion of a UTF-8 text - * (basic conversions only) - * trailing '\0' not accounted for - */ - -unsigned int utf16size(const char *utf8) -{ - unsigned int size; - const char *p; - int c; - unsigned int code; - enum { BASE, TWO, THREE, THREE2, THREE3, FOUR, ERR } state; - - p = utf8; - size = 0; - state = BASE; - while (*p) { - c = *p++ & 255; - switch (state) { - case BASE : - if (!(c & 0x80)) - size++; - else - if (c < 0xc2) - state = ERR; - else - if (c < 0xe0) - state = TWO; - else - if (c < 0xf0) { - if (c == 0xe0) - state = THREE2; - else - if (c == 0xed) - state = THREE3; - else - state = THREE; - } else - if (c < 0xf8) { - state = FOUR; - code = c & 7; - } else - state = ERR; - break; - case TWO : - if ((c & 0xc0) != 0x80) - state = ERR; - else { - size++; - state = BASE; - } - break; - case THREE : - if ((c & 0xc0) != 0x80) - state = ERR; - else - state = TWO; - break; - case THREE2 : - if ((c & 0xe0) != 0xa0) - state = ERR; - else - state = TWO; - break; - case THREE3 : - if ((c & 0xe0) != 0x80) - state = ERR; - else - state = TWO; - break; - case FOUR : - if ((((code << 6) + (c & 63)) > 0x10f) - || (((code << 6) + (c & 63)) < 0x10)) - state = ERR; - else { - size++; - state = THREE; - } - break; - case ERR : - break; - } - } - if (state != BASE) size = 0; - return (size); -} - -/* - * Convert a UTF8 text to UTF-16LE - * (basic conversions only) - * Note : mbstowcs() not used because on Linux it fails for characters - * not present in current locale - */ - -unsigned int makeutf16(char *target, const char *utf8) -{ - unsigned int size; - unsigned int code; - const char *p; - int c; - enum { BASE, TWO, THREE, THREE2, THREE3, FOUR, FOUR2, FOUR3, ERR } state; - - p = utf8; - size = 0; - c = 0; - state = BASE; - while (*p) { - c = *p++ & 255; - switch (state) { - case BASE : - if (!(c & 0x80)) { - target[2*size] = c; - target[2*size + 1] = 0; - size++; - } else { - if (c < 0xc2) - state = ERR; - else - if (c < 0xe0) { - code = c & 31; - state = TWO; - } else - if (c < 0xf0) { - code = c & 15; - if (c == 0xe0) - state = THREE2; - else - if (c == 0xed) - state = THREE3; - else - state = THREE; - } else - if (c < 0xf8) { - code = c & 7; - state = FOUR; - } else - state = ERR; - } - break; - case TWO : -#if NOREVBOM - if (((c & 0xc0) != 0x80) - || ((code == 0x3ff) && (c >= 0xbe))) -#else - if ((c & 0xc0) != 0x80) -#endif - state = ERR; - else { - target[2*size] = ((code & 3) << 6) + (c & 63); - target[2*size + 1] = ((code >> 2) & 255); - size++; - state = BASE; - } - break; - case THREE : - if ((c & 0xc0) != 0x80) - state = ERR; - else { - code = ((code & 15) << 6) + (c & 63); - state = TWO; - } - break; - case THREE2 : - if ((c & 0xe0) != 0xa0) - state = ERR; - else { - code = ((code & 15) << 6) + (c & 63); - state = TWO; - } - break; - case THREE3 : - if ((c & 0xe0) != 0x80) - state = ERR; - else { - code = ((code & 15) << 6) + (c & 63); - state = TWO; - } - break; - case FOUR : - if ((c & 0xc0) != 0x80) - state = ERR; - else { - code = (code << 6) + (c & 63); - state = FOUR2; - } - break; - case FOUR2 : - if ((c & 0xc0) != 0x80) - state = ERR; - else { - code = (code << 6) + (c & 63); - state = FOUR3; - } - break; - case FOUR3 : - if ((code > 0x43ff) - || (code < 0x400) - || ((c & 0xc0) != 0x80)) - state = ERR; - else { - target[2*size] = ((code - 0x400) >> 4) & 255; - target[2*size+1] = 0xd8 + (((code - 0x400) >> 12) & 3); - target[2*size+2] = ((code & 3) << 6) + (c & 63); - target[2*size+3] = 0xdc + ((code >> 2) & 3); - size += 2; - state = BASE; - } - break; - case ERR : - break; - } - } - if (state != BASE) - size = 0; - target[2*size] = 0; - target[2*size + 1] = 0; - return (size); -} - -unsigned int utf16len(const char *str) -{ - unsigned int len; - - len = 0; - while (str[2*len] || str[2*len+1]) len++; - return (len); -} - -#endif - /* * Print a file name * on Windows it prints UTF-16LE names as UTF-8 */ -void printname(FILE *file, const char *name) +static void printname(FILE *file, const char *name) { -#ifdef WIN32 - char utf8name[MAXFILENAME]; +#ifdef HAVE_WINDOWS_H + char *wname; + char *p; - makeutf8(utf8name,name,utf16len(name)); - fprintf(file,"%s",utf8name); -#else + wname = (char*)malloc(strlen(name) + 1); + if (wname) { + strcpy(wname, name); + for (p=wname; *p; p++) + if (*p == '/') + *p = '\\'; + fprintf(file,"%s", wname); + free(wname); + } +#else /* HAVE_WINDOWS_H */ fprintf(file,"%s",name); -#endif +#endif /* HAVE_WINDOWS_H */ } /* * Print the last error code */ -void printerror(FILE *file) +static void printerror(FILE *file) { -#ifdef WIN32 - int err; - const char *txt; - - err = GetLastError(); - switch (err) { - case 5 : - txt = "Access to security descriptor was denied"; + if (errno) + fprintf(file,"Error code %d : %s\n",errno,strerror(errno)); + switch (errno) { + case EACCES : + fprintf(file,"You probably need Administrator rights\n"); break; - case 1307 : - txt = "This SID may not be assigned as the owner of this object"; - break; - case 1308 : - txt = "This SID may not be assigned as the group of this object"; - break; - case 1314 : - txt = "You do not have the privilege to change this SID"; + case EBUSY : + fprintf(file,"You probably try to write to a mounted device\n"); break; default : - txt = (const char*)NULL; break; } - if (txt) - fprintf(file,"Error %d : %s\n",err,txt); - else - fprintf(file,"Error %d\n",err); -#else -#ifdef STSC - if (errno) fprintf(file,"Error code %d\n",errno); -#else - if (errno) fprintf(file,"Error code %d : %s\n",errno,strerror(errno)); -#endif -#endif } #ifndef HAVE_SYSLOG_H @@ -1369,7 +910,7 @@ void printerror(FILE *file) * Redefine early error messages in stand-alone situations */ -void ntfs_log_early_error(const char *format, ...) +static void ntfs_log_early_error(const char *format, ...) { va_list args; @@ -1378,7 +919,7 @@ void ntfs_log_early_error(const char *format, ...) va_end(args); } -#endif +#endif /* HAVE_SYSLOG_H */ /* * Guess whether a security attribute is intended for a directory @@ -1386,7 +927,7 @@ void ntfs_log_early_error(const char *format, ...) * (not 100% reliable) */ -BOOL guess_dir(const char *attr) +static BOOL guess_dir(const char *attr) { int off; int isdir; @@ -1413,7 +954,7 @@ BOOL guess_dir(const char *attr) * See http://msdn2.microsoft.com/en-us/library/aa379649.aspx */ -void showsid(const char *attr, int off, const char *prefix, int level) +static void showsid(const char *attr, int off, const char *prefix, int level) { int cnt; int i; @@ -1424,16 +965,18 @@ void showsid(const char *attr, int off, const char *prefix, int level) unsigned long last; char marker; - if (opt_b) + if (cmd == CMD_BACKUP) marker = '#'; else marker = ' '; cnt = attr[off+1] & 255; auth = get6h(attr,off+2); - first = get4l(attr,off+8); known = FALSE; + /* SID names taken from https://support.microsoft.com/en-us/kb/243330 */ if ((attr[off] == 1) /* revision */ - && (auth < 100)) + && cnt + && (auth < 100)) { + first = get4l(attr,off+8); switch (cnt) { case 0 : /* no level (error) */ break; @@ -1487,23 +1030,23 @@ void showsid(const char *attr, int off, const char *prefix, int level) break; case 7 : known = TRUE; - printf("%*cAnonymous logon SID\n",-level,marker); + printf("%*cAnonymous SID\n",-level,marker); break; case 11 : known = TRUE; - printf("%*cAuthenticated user SID\n",-level,marker); + printf("%*cAuthenticated Users SID\n",-level,marker); break; case 13 : known = TRUE; - printf("%*cLocal service SID\n",-level,marker); + printf("%*cTerminal Server Users SID\n",-level,marker); break; case 14 : known = TRUE; - printf("%*cNetwork service SID\n",-level,marker); + printf("%*cRemote Interactive Logon SID\n",-level,marker); break; case 18 : known = TRUE; - printf("%*cNT System SID\n",-level,marker); + printf("%*cLocal System SID\n",-level,marker); break; } break; @@ -1517,13 +1060,13 @@ void showsid(const char *attr, int off, const char *prefix, int level) known = TRUE; switch (second) { case 544 : - printf("%*cLocal admins SID\n",-level,marker); + printf("%*cAdministrators SID\n",-level,marker); break; case 545 : - printf("%*cLocal users SID\n",-level,marker); + printf("%*cUsers SID\n",-level,marker); break; case 546 : - printf("%*cLocal guests SID\n",-level,marker); + printf("%*cGuests SID\n",-level,marker); break; default : printf("%*cSome domain SID\n",-level,marker); @@ -1532,6 +1075,7 @@ void showsid(const char *attr, int off, const char *prefix, int level) } break; } + break; default : /* three levels or more */ second = get4l(attr,off+12); last = get4l(attr,off+4+4*cnt); @@ -1540,49 +1084,50 @@ void showsid(const char *attr, int off, const char *prefix, int level) if (first == 21) { known = TRUE; switch (last) { - case 500 : - printf("%*cSystem admin SID\n",-level,marker); - break; - case 501 : - printf("%*cGuest SID\n",-level,marker); - break; - case 512 : - printf("%*cLocal admins SID\n",-level,marker); - break; - case 513 : - printf("%*cLocal users SID\n",-level,marker); - break; - case 514 : - printf("%*cLocal guests SID\n",-level,marker); - break; - default : - printf("%*cLocal user-%lu SID\n",-level,marker,last); - break; + case 500 : + printf("%*cAdministrator SID\n",-level,marker); + break; + case 501 : + printf("%*cGuest SID\n",-level,marker); + break; + case 512 : + printf("%*cDomain Admins SID\n",-level,marker); + break; + case 513 : + printf("%*cDomain Users SID\n",-level,marker); + break; + case 514 : + printf("%*cDomain Guests SID\n",-level,marker); + break; + default : + printf("%*cLocal user-%lu SID\n",-level,marker,last); + break; } } break; } } + } if (!known) printf("%*cUnknown SID\n",-level,marker); - printf("%*c%shex S-%d-",-level,marker,prefix,attr[off] & 255); - printf("%llx",auth); + printf("%*c%shex S-%x-",-level,marker,prefix,attr[off] & 255); + printf("%llx",(long long)auth); for (i=0; iowner)]; usid = ntfs_acl_owner((const char*)attr); -#if SELFTESTS & !USESTUBS - if (!opt_t && !ntfs_same_sid(usid,osid)) - printf("== Linux owner is different from Windows owner\n"); -#else +#ifdef SELFTESTS + if ((cmd != CMD_TEST) && !ntfs_same_sid(usid,osid)) + printf("== Linux owner is different from Windows owner\n"); +#else /* SELFTESTS */ if (!ntfs_same_sid(usid,osid)) - printf("== Linux owner is different from Windows owner\n"); -#endif -#else + printf("== Linux owner is different from Windows owner\n"); +#endif /* SELFTESTS */ +#else /* OWNERFROMACL */ usid = (const SID*)&attr[le32_to_cpu(phead->owner)]; -#endif - posix_desc = ntfs_build_permissions_posix(context.mapping, - (const char*)attr, usid, gsid, isdir); +#endif /* OWNERFROMACL */ + if (mappingtype == MAPEXTERN) + posix_desc = ntfs_build_permissions_posix( + ntfs_context->security.mapping, + (const char*)attr, usid, gsid, isdir); + else + posix_desc = ntfs_build_permissions_posix(context.mapping, + (const char*)attr, usid, gsid, isdir); if (!posix_desc) { printf("** Failed to build a Posix descriptor\n"); errors++; @@ -2161,12 +1661,12 @@ struct POSIX_SECURITY *linux_permissions_posix(const char *attr, BOOL isdir) #endif /* POSIXACLS */ -int linux_permissions(const char *attr, BOOL isdir) +static int linux_permissions(const char *attr, BOOL isdir) { const SECURITY_DESCRIPTOR_RELATIVE *phead; #if OWNERFROMACL const SID *osid; -#endif +#endif /* OWNERFROMACL */ const SID *usid; const SID *gsid; int perm; @@ -2176,16 +1676,16 @@ int linux_permissions(const char *attr, BOOL isdir) #if OWNERFROMACL osid = (const SID*)&attr[le32_to_cpu(phead->owner)]; usid = ntfs_acl_owner((const char*)attr); -#if SELFTESTS & !USESTUBS - if (!opt_t && !ntfs_same_sid(usid,osid)) - printf("== Linux owner is different from Windows owner\n"); -#else +#ifdef SELFTESTS + if ((cmd != CMD_TEST) && !ntfs_same_sid(usid,osid)) + printf("== Linux owner is different from Windows owner\n"); +#else /* SELFTESTS */ if (!ntfs_same_sid(usid,osid)) - printf("== Linux owner is different from Windows owner\n"); -#endif -#else + printf("== Linux owner is different from Windows owner\n"); +#endif /* SELFTESTS */ +#else /* OWNERFROMACL */ usid = (const SID*)&attr[le32_to_cpu(phead->owner)]; -#endif +#endif /* OWNERFROMACL */ perm = ntfs_build_permissions((const char*)attr, usid, gsid, isdir); if (perm < 0) { printf("** Failed to build permissions\n"); @@ -2194,31 +1694,31 @@ int linux_permissions(const char *attr, BOOL isdir) return (perm); } -uid_t linux_owner(const char *attr) +static uid_t linux_owner(const char *attr) { const SID *usid; uid_t uid; #if OWNERFROMACL usid = ntfs_acl_owner((const char*)attr); -#else +#else /* OWNERFROMACL */ const SECURITY_DESCRIPTOR_RELATIVE *phead; phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; usid = (const SID*)&attr[le32_to_cpu(phead->owner)]; -#endif -#if defined(WIN32) | defined(STSC) +#endif /* OWNERFROMACL */ +#ifdef HAVE_WINDOWS_H uid = ntfs_find_user(context.mapping[MAPUSERS],usid); -#else +#else /* defined(HAVE_WINDOWS_H) */ if (mappingtype == MAPEXTERN) uid = relay_find_user(context.mapping[MAPUSERS],usid); else uid = ntfs_find_user(context.mapping[MAPUSERS],usid); -#endif +#endif /* defined(HAVE_WINDOWS_H) */ return (uid); } -gid_t linux_group(const char *attr) +static gid_t linux_group(const char *attr) { const SECURITY_DESCRIPTOR_RELATIVE *phead; const SID *gsid; @@ -2226,18 +1726,18 @@ gid_t linux_group(const char *attr) phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; gsid = (const SID*)&attr[le32_to_cpu(phead->group)]; -#if defined(WIN32) | defined(STSC) +#ifdef HAVE_WINDOWS_H gid = ntfs_find_group(context.mapping[MAPGROUPS],gsid); -#else +#else /* defined(HAVE_WINDOWS_H) */ if (mappingtype == MAPEXTERN) gid = relay_find_group(context.mapping[MAPGROUPS],gsid); else gid = ntfs_find_group(context.mapping[MAPGROUPS],gsid); -#endif +#endif /* defined(HAVE_WINDOWS_H) */ return (gid); } -void newblock(s32 key) +static void newblock(s32 key) { struct SECURITY_DATA *psecurdata; int i; @@ -2256,7 +1756,7 @@ void newblock(s32 key) } } -void freeblocks(void) +static void freeblocks(void) { int i,j; struct SECURITY_DATA *psecurdata; @@ -2276,19 +1776,19 @@ void freeblocks(void) * Basic read from a user mapping file (Win32) */ -int basicread(void *fileid, char *buf, size_t size, +static int basicread(void *fileid, char *buf, size_t size, off_t pos __attribute__((unused))) { return (read(*(int*)fileid, buf, size)); } -#if SELFTESTS & !USESTUBS +#ifdef SELFTESTS /* * Read a dummy mapping file for tests */ -int dummyread(void *fileid __attribute__((unused)), +static int dummyread(void *fileid __attribute__((unused)), char *buf, size_t size, off_t pos) { size_t sz; @@ -2305,7 +1805,7 @@ int dummyread(void *fileid __attribute__((unused)), return (sz); } -#endif /* POSIXACLS & SELFTESTS & !USESTUBS */ +#endif /* SELFTESTS */ /* * Apply default single user mapping @@ -2323,24 +1823,12 @@ static int do_default_mapping(struct MAPPING *mapping[], res = -1; sidsz = ntfs_sid_size(usid); -#if USESTUBS - sid = (SID*)stdmalloc(sidsz); /* will be freed within the library */ -#else sid = (SID*)ntfs_malloc(sidsz); -#endif if (sid) { memcpy(sid,usid,sidsz); -#if USESTUBS - usermapping = (struct MAPPING*)stdmalloc(sizeof(struct MAPPING)); -#else usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING)); -#endif if (usermapping) { -#if USESTUBS - groupmapping = (struct MAPPING*)stdmalloc(sizeof(struct MAPPING)); -#else groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING)); -#endif if (groupmapping) { usermapping->sid = sid; usermapping->xid = 0; @@ -2369,15 +1857,15 @@ static int do_default_mapping(struct MAPPING *mapping[], * (failure should not be interpreted as an error) */ -int local_build_mapping(struct MAPPING *mapping[], const char *usermap_path) +static int local_build_mapping(struct MAPPING *mapping[], const char *usermap_path) { -#ifdef WIN32 +#ifdef HAVE_WINDOWS_H char mapfile[sizeof(MAPDIR) + sizeof(MAPFILE) + 6]; - char currpath[261]; -#else + char currpath[MAXFILENAME]; +#else /* HAVE_WINDOWS_H */ char *mapfile; char *p; -#endif +#endif /* HAVE_WINDOWS_H */ int fd; struct MAPLIST *item; struct MAPLIST *firstitem = (struct MAPLIST*)NULL; @@ -2405,28 +1893,32 @@ int local_build_mapping(struct MAPPING *mapping[], const char *usermap_path) mapping[MAPGROUPS] = (struct MAPPING*)NULL; if (usermap_path) { -#ifdef WIN32 +#ifdef HAVE_WINDOWS_H /* TODO : check whether the device can store acls */ strcpy(mapfile,"x:\\" MAPDIR "\\" MAPFILE); if (((const le16*)usermap_path)[1] == ':') mapfile[0] = usermap_path[0]; else { - GetModuleFileName(NULL, currpath, 261); + getcwd(currpath,MAXFILENAME); mapfile[0] = currpath[0]; } fd = open(mapfile,O_RDONLY); -#else +#else /* HAVE_WINDOWS_H */ fd = 0; mapfile = (char*)malloc(MAXFILENAME); if (mapfile) { /* build a full path to locate the mapping file */ +/* if ((usermap_path[0] != '/') && getcwd(mapfile,MAXFILENAME)) { strcat(mapfile,"/"); strcat(mapfile,usermap_path); } else strcpy(mapfile,usermap_path); - p = strrchr(mapfile,'/'); +*/ + p = ntfs_realpath(usermap_path, mapfile); + if (p) + p = strrchr(mapfile,'/'); if (p) do { strcpy(p,"/" MAPDIR "/" MAPFILE); @@ -2446,15 +1938,15 @@ int local_build_mapping(struct MAPPING *mapping[], const char *usermap_path) errors++; } } -#endif +#endif /* HAVE_WINDOWS_H */ if (fd > 0) { firstitem = ntfs_read_mapping(basicread, (void*)&fd); close(fd); } } else { -#if SELFTESTS & !USESTUBS +#ifdef SELFTESTS firstitem = ntfs_read_mapping(dummyread, (void*)NULL); -#endif +#endif /* SELFTESTS */ } if (firstitem) { @@ -2469,11 +1961,7 @@ int local_build_mapping(struct MAPPING *mapping[], const char *usermap_path) /* and rely on internal representation */ while (firstitem) { item = firstitem->next; -#if USESTUBS - stdfree(firstitem); /* allocated within library */ -#else free(firstitem); -#endif firstitem = item; } } else { @@ -2488,7 +1976,7 @@ int local_build_mapping(struct MAPPING *mapping[], const char *usermap_path) * Get an hexadecimal number (source with MSB first) */ -u32 getmsbhex(const char *text) +static u32 getmsbhex(const char *text) { u32 v; int b; @@ -2517,7 +2005,7 @@ u32 getmsbhex(const char *text) * An odd number of digits might yield a strange result */ -u32 getlsbhex(const char *text) +static u32 getlsbhex(const char *text) { u32 v; int b; @@ -2548,7 +2036,7 @@ u32 getlsbhex(const char *text) * Check whether a line looks like an hex dump */ -BOOL ishexdump(const char *line, int first, int lth) +static BOOL ishexdump(const char *line, int first, int lth) { BOOL ok; int i; @@ -2578,13 +2066,13 @@ BOOL ishexdump(const char *line, int first, int lth) * This is typically to convert a verbose output to a very verbose one */ -void showhex(FILE *fd) +static void showhex(FILE *fd) { static char attr[MAXATTRSZ]; char line[MAXLINE+1]; #if POSIXACLS struct POSIX_SECURITY *pxdesc; -#endif +#endif /* POSIXACLS */ int lth; int first; unsigned int pos; @@ -2595,6 +2083,7 @@ void showhex(FILE *fd) unsigned int off; int i; le32 *pattr; + BOOL acceptable; BOOL isdump; BOOL done; @@ -2619,13 +2108,26 @@ void showhex(FILE *fd) isdump = ishexdump(line, first, lth); if (isdump) off = getmsbhex(&line[first]); /* line is not an hexadecimal dump */ - /* display what we have in store */ - if ((!isdump || !off) && pos && ntfs_valid_descr((char*)attr,pos)) { + /* display what we have in store if acceptable */ + acceptable = ((!isdump || !off) + && (pos >= 20)) + && (pos > get4l(attr,4)) + && (pos > get4l(attr,8)) + && (pos > get4l(attr,12)) + && (pos > get4l(attr,16)) + && (pos >= ntfs_attr_size(attr)); + if (acceptable) { printf(" Computed hash : 0x%08lx\n", (unsigned long)hash((le32*)attr, ntfs_attr_size(attr))); isdir = guess_dir(attr); - printf(" Estimated type : %s\n",(isdir ? "directory" : "file")); + printf(" Estimated type : %s\n", + (isdir ? "directory" : "file")); + if (!ntfs_valid_descr((char*)attr,pos)) { + printf("** Bad descriptor," + " trying to display anyway\n"); + errors++; + } showheader(attr,4); showusid(attr,4); showgsid(attr,4); @@ -2646,7 +2148,7 @@ void showhex(FILE *fd) free(pxdesc); } } -#endif +#endif /* POSIXACLS */ pos = 0; } if (isdump && !off) @@ -2677,7 +2179,7 @@ void showhex(FILE *fd) } while (!done); } -BOOL applyattr(const char *fullname, const char *attr, +static BOOL applyattr(const char *fullname, const char *attr, BOOL withattr, int attrib, s32 key) { struct SECURITY_DATA *psecurdata; @@ -2687,10 +2189,6 @@ BOOL applyattr(const char *fullname, const char *attr, BOOL bad; BOOL badattrib; BOOL err; -#ifdef WIN32 - HANDLE htoken; - TOKEN_PRIVILEGES tkp; -#endif err = FALSE; psecurdata = (struct SECURITY_DATA*)NULL; @@ -2708,11 +2206,7 @@ BOOL applyattr(const char *fullname, const char *attr, /* If we have a usable attrib value. Try applying */ badattrib = FALSE; if (opt_e && (attrib != INVALID_FILE_ATTRIBUTES)) { -#ifdef WIN32 - badattrib = !SetFileAttributesW((LPCWSTR)fullname, attrib); -#else badattrib = !ntfs_set_file_attributes(ntfs_context, fullname, attrib); -#endif if (badattrib) { printf("** Could not set Windows attrib of "); printname(stdout,fullname); @@ -2738,58 +2232,15 @@ BOOL applyattr(const char *fullname, const char *attr, */ if (psecurdata) curattr = psecurdata->attr; - + if (curattr) { -#ifdef WIN32 - selection = OWNER_SECURITY_INFORMATION - | GROUP_SECURITY_INFORMATION - | DACL_SECURITY_INFORMATION; - if (OpenProcessToken(GetCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &htoken)) { - if (LookupPrivilegeValue(NULL, SE_SECURITY_NAME, - &tkp.Privileges[0].Luid)) { - tkp.PrivilegeCount = 1; - tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - if (AdjustTokenPrivileges(htoken, FALSE, &tkp, 0, NULL, 0)) { - selection |= SACL_SECURITY_INFORMATION; - } - } - } - /* const missing from stupid prototype */ - bad = !SetFileSecurityW((LPCWSTR)fullname, - selection, (PSECURITY_DESCRIPTOR)(LONG_PTR)curattr); - if (bad) - switch (GetLastError()) { - case 1307 : - case 1314 : - printf("** Could not set owner or SACL of "); - printname(stdout,fullname); - printf(", retrying with no owner or SACL setting\n"); - warnings++; - /* const missing from stupid prototype */ - bad = !SetFileSecurityW((LPCWSTR)fullname, - selection & ~OWNER_SECURITY_INFORMATION - & ~SACL_SECURITY_INFORMATION, - (PSECURITY_DESCRIPTOR) - (LONG_PTR)curattr); - break; - default : - break; - } - /* Release privileges once we are done*/ - if (selection ^ SACL_SECURITY_INFORMATION) { - tkp.Privileges[0].Attributes = 0; - AdjustTokenPrivileges(htoken, FALSE, &tkp, 0, NULL, 0); - } -#else selection = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION; bad = !ntfs_set_file_security(ntfs_context,fullname, selection, (const char*)curattr); -#endif if (bad) { printf("** Could not set the ACL of "); printname(stdout,fullname); @@ -2819,7 +2270,7 @@ BOOL applyattr(const char *fullname, const char *attr, * Restore security descriptors from a file */ -BOOL restore(FILE *fd) +static BOOL restore(FILE *fd) { static char attr[MAXATTRSZ]; char line[MAXFILENAME+25]; @@ -2871,7 +2322,7 @@ BOOL restore(FILE *fd) isdump = ishexdump(line, first, lth); if (isdump) off = getmsbhex(&line[first]); /* line is not an hexadecimal dump */ - /* apply what we have in store */ + /* apply what we have in store, only if valid */ if ((!isdump || !off) && pos && ntfs_valid_descr((char*)attr,pos)) { withattr = TRUE; if (opt_v >= 2) { @@ -2948,8 +2399,8 @@ BOOL restore(FILE *fd) if (fullname[0]) { phead = (SECURITY_DESCRIPTOR_RELATIVE*)attr; /* set the request for auto-inheritance */ - if (phead->control & SE_DACL_AUTO_INHERITED) - phead->control |= SE_DACL_AUTO_INHERIT_REQ; + if (!le16_andz(phead->control, SE_DACL_AUTO_INHERITED)) + phead->control = le16_or(phead->control, SE_DACL_AUTO_INHERIT_REQ); if (!applyattr(fullname,attr,withattr, attrib,key)) errors++; @@ -2962,17 +2413,13 @@ BOOL restore(FILE *fd) oldhash = 0; attrib = INVALID_FILE_ATTRIBUTES; if (!done) { -#ifdef WIN32 - if (!strncmp(line,"File ",5)) - makeutf16(fullname,&line[5]); - else - makeutf16(fullname,&line[10]); -#else if (!strncmp(line,"File ",5)) strcpy(fullname,&line[5]); else strcpy(fullname,&line[10]); -#endif +#ifdef HAVE_WINDOWS_H + cleanpath(fullname); +#endif /* HAVE_WINDOWS_H */ } } } while (!done); @@ -2980,13 +2427,7 @@ BOOL restore(FILE *fd) return (FALSE); } -/* - * Open the security API in rw mode for an ACL restoration - */ - -#ifdef WIN32 -#else -BOOL dorestore(const char *volume, FILE *fd) +static BOOL dorestore(const char *volume, FILE *fd) { BOOL err; @@ -3013,9 +2454,8 @@ BOOL dorestore(const char *volume, FILE *fd) } return (err); } -#endif /* WIN32 */ -#if POSIXACLS & SELFTESTS & !USESTUBS +#if POSIXACLS /* * Merge Posix ACL rights into an u32 (self test only) @@ -3025,7 +2465,7 @@ BOOL dorestore(const char *volume, FILE *fd) * * Only two users (U1, U2) and two groups (G1, G2) taken into account */ -u32 merge_rights(const struct POSIX_SECURITY *pxdesc, BOOL def) +static u32 merge_rights(const struct POSIX_SECURITY *pxdesc, BOOL def) { const struct POSIX_ACE *pxace; int i; @@ -3077,21 +2517,55 @@ u32 merge_rights(const struct POSIX_SECURITY *pxdesc, BOOL def) return (rights); } -void tryposix(struct POSIX_SECURITY *pxdesc) +static BOOL same_posix(struct POSIX_SECURITY *pxdesc1, + struct POSIX_SECURITY *pxdesc2) +{ + BOOL same; + int i; + + same = pxdesc1 + && pxdesc2 + && (pxdesc1->mode == pxdesc2->mode) + && (pxdesc1->acccnt == pxdesc2->acccnt) + && (pxdesc1->defcnt == pxdesc2->defcnt) + && (pxdesc1->firstdef == pxdesc2->firstdef) + && (pxdesc1->tagsset == pxdesc2->tagsset) + && (pxdesc1->acl.version == pxdesc2->acl.version) + && (pxdesc1->acl.flags == pxdesc2->acl.flags); + i = 0; + while (same && (i < pxdesc1->acccnt)) { + same = (pxdesc1->acl.ace[i].tag == pxdesc2->acl.ace[i].tag) + && (pxdesc1->acl.ace[i].perms == pxdesc2->acl.ace[i].perms) + && (pxdesc1->acl.ace[i].id == pxdesc2->acl.ace[i].id); + i++; + } + i = pxdesc1->firstdef; + while (same && (i < pxdesc1->firstdef + pxdesc1->defcnt)) { + same = (pxdesc1->acl.ace[i].tag == pxdesc2->acl.ace[i].tag) + && (pxdesc1->acl.ace[i].perms == pxdesc2->acl.ace[i].perms) + && (pxdesc1->acl.ace[i].id == pxdesc2->acl.ace[i].id); + i++; + } + return (same); +} + +#endif /* POSIXACLS */ + +#if POSIXACLS & SELFTESTS + +static void tryposix(struct POSIX_SECURITY *pxdesc) { le32 owner_sid[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */ { - const_cpu_to_le32(0x501), const_cpu_to_le32(0x05000000), - const_cpu_to_le32(21), const_cpu_to_le32(DEFSECAUTH1), - const_cpu_to_le32(DEFSECAUTH2), const_cpu_to_le32(DEFSECAUTH3), - const_cpu_to_le32(1016) + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2), + cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016) } ; le32 group_sid[] = /* S-1-5-21-3141592653-589793238-462843383-513 */ { - const_cpu_to_le32(0x501), const_cpu_to_le32(0x05000000), - const_cpu_to_le32(21), const_cpu_to_le32(DEFSECAUTH1), - const_cpu_to_le32(DEFSECAUTH2), const_cpu_to_le32(DEFSECAUTH3), - const_cpu_to_le32(513) + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2), + cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513) } ; char *attr; @@ -3137,41 +2611,9 @@ void tryposix(struct POSIX_SECURITY *pxdesc) } } -static BOOL same_posix(struct POSIX_SECURITY *pxdesc1, - struct POSIX_SECURITY *pxdesc2) -{ - BOOL same; - int i; +#endif /* POSIXACLS & SELFTESTS */ - same = pxdesc1 - && pxdesc2 - && (pxdesc1->mode == pxdesc2->mode) - && (pxdesc1->acccnt == pxdesc2->acccnt) - && (pxdesc1->defcnt == pxdesc2->defcnt) - && (pxdesc1->firstdef == pxdesc2->firstdef) - && (pxdesc1->tagsset == pxdesc2->tagsset) - && (pxdesc1->acl.version == pxdesc2->acl.version) - && (pxdesc1->acl.flags == pxdesc2->acl.flags); - i = 0; - while (same && (i < pxdesc1->acccnt)) { - same = (pxdesc1->acl.ace[i].tag == pxdesc2->acl.ace[i].tag) - && (pxdesc1->acl.ace[i].perms == pxdesc2->acl.ace[i].perms) - && (pxdesc1->acl.ace[i].id == pxdesc2->acl.ace[i].id); - i++; - } - i = pxdesc1->firstdef; - while (same && (i < pxdesc1->firstdef + pxdesc1->defcnt)) { - same = (pxdesc1->acl.ace[i].tag == pxdesc2->acl.ace[i].tag) - && (pxdesc1->acl.ace[i].perms == pxdesc2->acl.ace[i].perms) - && (pxdesc1->acl.ace[i].id == pxdesc2->acl.ace[i].id); - i++; - } - return (same); -} - -#endif /* POSIXACLS & SELFTESTS & !USESTUBS */ - -#if SELFTESTS & !USESTUBS +#ifdef SELFTESTS /* * Build a dummy security descriptor @@ -3180,7 +2622,7 @@ static BOOL same_posix(struct POSIX_SECURITY *pxdesc1, static char *build_dummy_descr(BOOL isdir __attribute__((unused)), const SID *usid, const SID *gsid, - int cnt, + int cnt, /* seq of int allow, SID *sid, int flags, u32 mask */ ...) { @@ -3228,8 +2670,8 @@ static char *build_dummy_descr(BOOL isdir __attribute__((unused)), * The flag SE_DACL_PROTECTED prevents the ACL * to be changed in an inheritance after creation */ - pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED - | SE_SELF_RELATIVE; + pnhead->control = le16_or(SE_DACL_PRESENT, le16_or(SE_DACL_PROTECTED, + SE_SELF_RELATIVE)); /* * Windows prefers ACL first, do the same to * get the same hash value and avoid duplication @@ -3239,9 +2681,9 @@ static char *build_dummy_descr(BOOL isdir __attribute__((unused)), pacl = (ACL*)&attr[pos]; pacl->revision = ACL_REVISION; pacl->alignment1 = 0; - pacl->size = const_cpu_to_le16(0); /* fixed later */ + pacl->size = cpu_to_le16(0); /* fixed later */ pacl->ace_count = cpu_to_le16(cnt); - pacl->alignment2 = const_cpu_to_le16(0); + pacl->alignment2 = cpu_to_le16(0); /* enter the ACEs */ @@ -3268,8 +2710,8 @@ static char *build_dummy_descr(BOOL isdir __attribute__((unused)), /* append usid and gsid if defined */ /* positions of ACL, USID and GSID into header */ - pnhead->owner = const_cpu_to_le32(0); - pnhead->group = const_cpu_to_le32(0); + pnhead->owner = cpu_to_le32(0); + pnhead->group = cpu_to_le32(0); if (usid) { memcpy(&attr[pos], usid, usidsz); pnhead->owner = cpu_to_le32(pos); @@ -3279,14 +2721,13 @@ static char *build_dummy_descr(BOOL isdir __attribute__((unused)), pnhead->group = cpu_to_le32(pos + usidsz); } /* positions of DACL and SACL into header */ - pnhead->sacl = const_cpu_to_le32(0); + pnhead->sacl = cpu_to_le32(0); if (cnt) { pacl->size = cpu_to_le16(aclsz); pnhead->dacl = - const_cpu_to_le32(sizeof( - SECURITY_DESCRIPTOR_RELATIVE)); + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)); } else - pnhead->dacl = const_cpu_to_le32(0); + pnhead->dacl = cpu_to_le32(0); if (!ntfs_valid_descr(attr,pos+usidsz+gsidsz)) { printf("** Bad sample descriptor\n"); free(attr); @@ -3302,7 +2743,7 @@ static char *build_dummy_descr(BOOL isdir __attribute__((unused)), * Check a few samples with special conditions */ -void check_samples() +static void check_samples(void) { char *descr = (char*)NULL; BOOL isdir = FALSE; @@ -3318,41 +2759,36 @@ void check_samples() struct POSIX_SECURITY *pxdesc; struct POSIX_SECURITY *pxsample; const char *txsample; -#endif +#endif /* POSIXACLS */ le32 owner1[] = /* S-1-5-21-1833069642-4243175381-1340018762-1003 */ { - const_cpu_to_le32(0x501), const_cpu_to_le32(0x05000000), - const_cpu_to_le32(21), const_cpu_to_le32(1833069642), - const_cpu_to_le32(4243175381), const_cpu_to_le32(1340018762), - const_cpu_to_le32(1003) + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(1833069642), cpu_to_le32(4243175381U), + cpu_to_le32(1340018762), cpu_to_le32(1003) } ; le32 group1[] = /* S-1-5-21-1833069642-4243175381-1340018762-513 */ { - const_cpu_to_le32(0x501), const_cpu_to_le32(0x05000000), - const_cpu_to_le32(21), const_cpu_to_le32(1833069642), - const_cpu_to_le32(4243175381), const_cpu_to_le32(1340018762), - const_cpu_to_le32(513) + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(1833069642), cpu_to_le32(4243175381U), + cpu_to_le32(1340018762), cpu_to_le32(513) } ; le32 group2[] = /* S-1-5-21-1607551490-981732888-1819828000-513 */ { - const_cpu_to_le32(0x501), const_cpu_to_le32(0x05000000), - const_cpu_to_le32(21), const_cpu_to_le32(1607551490), - const_cpu_to_le32(981732888), const_cpu_to_le32(1819828000), - const_cpu_to_le32(513) + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(1607551490), cpu_to_le32(981732888), + cpu_to_le32(1819828000), cpu_to_le32(513) } ; le32 owner3[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */ { - const_cpu_to_le32(0x501), const_cpu_to_le32(0x05000000), - const_cpu_to_le32(21), const_cpu_to_le32(DEFSECAUTH1), - const_cpu_to_le32(DEFSECAUTH2), const_cpu_to_le32(DEFSECAUTH3), - const_cpu_to_le32(1016) + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2), + cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016) } ; le32 group3[] = /* S-1-5-21-3141592653-589793238-462843383-513 */ { - const_cpu_to_le32(0x501), const_cpu_to_le32(0x05000000), - const_cpu_to_le32(21), const_cpu_to_le32(DEFSECAUTH1), - const_cpu_to_le32(DEFSECAUTH2), const_cpu_to_le32(DEFSECAUTH3), - const_cpu_to_le32(513) + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2), + cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513) } ; #if POSIXACLS @@ -3361,7 +2797,7 @@ void check_samples() struct POSIX_ACE ace[4]; } sampletry1 = { - { 0645, 4, 0, 4, 0x35, + { 0645, 4, 0, 4, 0x35, 0, { POSIX_VERSION, 0, 0 } }, { @@ -3377,7 +2813,7 @@ void check_samples() struct POSIX_ACE ace[6]; } sampletry3 = { - { 0100, 6, 0, 6, 0x3f, + { 0100, 6, 0, 6, 0x3f, 0, { POSIX_VERSION, 0, 0 } }, { @@ -3395,7 +2831,7 @@ void check_samples() struct POSIX_ACE ace[8]; } sampletry4 = { - { 0140, 8, 0, 8, 0x3f, + { 0140, 8, 0, 8, 0x3f, 0, { POSIX_VERSION, 0, 0 } }, { @@ -3415,7 +2851,7 @@ void check_samples() struct POSIX_ACE ace[6]; } sampletry5 = { - { 0454, 6, 0, 6, 0x3f, + { 0454, 6, 0, 6, 0x3f, 0, { POSIX_VERSION, 0, 0 } }, { @@ -3433,7 +2869,7 @@ void check_samples() struct POSIX_ACE ace[8]; } sampletry6 = { - { 0332, 8, 0, 8, 0x3f, + { 0332, 8, 0, 8, 0x3f, 0, { POSIX_VERSION, 0, 0 } }, { @@ -3453,7 +2889,7 @@ void check_samples() struct POSIX_ACE ace[4]; } sampletry8 = { - { 0677, 4, 0, 4, 0x35, + { 0677, 4, 0, 4, 0x35, 0, { POSIX_VERSION, 0, 0 } }, { @@ -3536,10 +2972,12 @@ void check_samples() else pxdesc = (struct POSIX_SECURITY*)NULL; if (!descr || !pxdesc || !same_posix(pxsample,pxdesc)) { - printf("** Error in %s\n",txsample); + printf("** Error in %s (errno %d)\n",txsample,errno); showposix(pxsample); - showall(descr,0); - showposix(pxdesc); + if (descr) + showall(descr,0); + if (pxdesc) + showposix(pxdesc); errors++; } free(descr); @@ -3562,7 +3000,7 @@ void check_samples() (const SID*)owner1, (const SID*)group1, 1, (int)TRUE, worldsid, (int)0x3, (u32)0x1f01ff); - expectacc = expect = 0777; + expect = expectacc = 0777; expectdef = 0; break; case 2 : /* swsetup */ @@ -3586,12 +3024,12 @@ void check_samples() descr = build_dummy_descr(isdir, (const SID*)owner3, (const SID*)group3, 4, - (int)TRUE, (const SID*)owner3, 0, - le32_to_cpu(FILE_READ_DATA | OWNER_RIGHTS), + (int)TRUE, (const SID*)owner3, 0, + le32_to_cpu(le32_or(FILE_READ_DATA, OWNER_RIGHTS)), (int)TRUE, (const SID*)group3, 0, le32_to_cpu(FILE_WRITE_DATA), (int)TRUE, (const SID*)group2, 0, - le32_to_cpu(FILE_WRITE_DATA | FILE_READ_DATA), + le32_to_cpu(le32_or(FILE_WRITE_DATA, FILE_READ_DATA)), (int)TRUE, (const SID*)worldsid, 0, le32_to_cpu(FILE_EXECUTE)); expect = 0731; @@ -3623,8 +3061,7 @@ void check_samples() (int)TRUE, adminsid, (int)0xb, (u32)0x1f01ff, (int)TRUE, owner3, (int)0x3, (u32)0x1200a9); expectacc = 0500070700; - expectdef = 0700; - expect = 0700; + expectdef = expect = 0700; break; case 7 : /* WinXP/JP */ isdir = TRUE; @@ -3710,11 +3147,10 @@ void check_samples() (long)defrights,(long)expectdef); showall(descr,0); showposix(pxdesc); -exit(1); } free(pxdesc); } -#endif +#endif /* POSIXACLS */ } free(descr); } @@ -3727,7 +3163,7 @@ exit(1); * back exactly as set */ -void basictest(int kind, BOOL isdir, const SID *owner, const SID *group) +static void basictest(int kind, BOOL isdir, const SID *owner, const SID *group) { char *attr; mode_t perm; @@ -3761,7 +3197,7 @@ void basictest(int kind, BOOL isdir, const SID *owner, const SID *group) u32 pxcount; u32 pxacecount; u32 pxglobhash; -#endif +#endif /* POSIXACLS */ count = 0; acecount = 0; @@ -3770,7 +3206,7 @@ void basictest(int kind, BOOL isdir, const SID *owner, const SID *group) pxcount = 0; pxacecount = 0; pxglobhash = 0; -#endif +#endif /* POSIXACLS */ for (perm=0; (perm<=07777) && (errors < 10); perm++) { err = ERRNO; /* file owned by plain user and group */ @@ -3820,7 +3256,7 @@ void basictest(int kind, BOOL isdir, const SID *owner, const SID *group) free(attr); } free(pxdesc); -#else +#else /* POSIXACLS */ gotback = linux_permissions(attr, isdir); if (gotback != perm) err = ERRAM; @@ -3868,7 +3304,7 @@ void basictest(int kind, BOOL isdir, const SID *owner, const SID *group) break; case ERRAP : /* continued */ -#else +#else /* POSIXACLS */ case ERRAM : case ERRAP : #endif /* POSIXACLS */ @@ -3930,7 +3366,7 @@ void basictest(int kind, BOOL isdir, const SID *owner, const SID *group) * back exactly as set */ -void posixtest(int kind, BOOL isdir, +static void posixtest(int kind, BOOL isdir, const SID *owner, const SID *group) { struct POSIX_SECURITY *pxdesc; @@ -3956,23 +3392,8 @@ void posixtest(int kind, BOOL isdir, enum { ERRNO, ERRMA, ERRPA, /* error converting mode or Posix ACL to NTFS */ ERRAM, ERRAP, /* error converting NTFS to mode or Posix ACL */ - } err; + } ; u32 expectcnt[] = { -#ifdef STSC - 32400, 34992, - 25920, 28512, - 25920, 28512, - 25920, 28512, - 26460, 29052, - 0, 0, - 0, 0, - 0, 0, - 24516, 27108, - 20736, 23328, - 20736, 23328, - 20736, 23328, - 21060, 23652, -#else 252720, 273456, 199584, 220320, 199584, 220320, @@ -3986,7 +3407,6 @@ void posixtest(int kind, BOOL isdir, 165888, 186624, 165888, 186624, 168480, 189216, -#endif 0, 0, 0, 0, 0, 0, @@ -3997,21 +3417,6 @@ void posixtest(int kind, BOOL isdir, 14640, 0 } ; u32 expecthash[] = { -#ifdef STSC - 0xf9f82115, 0x40666647, - 0xde826d30, 0xa181b660, - 0x952d4500, 0x8ac49450, - 0xf80acee0, 0xbd9ec6c0, - 0xfe09b868, 0xde24e84d, - 0, 0, - 0, 0, - 0, 0, - 0x2381438d, 0x3ab42dc6, - 0x7cccf6f8, 0x108ad430, - 0x5e448840, 0x83ab6c40, - 0x9b037100, 0x8f7c3b40, - 0x04a359dc, 0xa4619609, -#else 0x1808a6cd, 0xd82f7c60, 0x5ad29e85, 0x518c7620, 0x188ce270, 0x7e44e590, @@ -4025,7 +3430,6 @@ void posixtest(int kind, BOOL isdir, 0xf9685700, 0x44d16700, 0x587ebe90, 0xf7c51480, 0x2cb1b518, 0x52408df6, -#endif 0, 0, 0, 0, 0, 0, @@ -4080,13 +3484,8 @@ void posixtest(int kind, BOOL isdir, mindes = 3; maxdes = (kind & 32 ? mindes : 6); -#ifdef STSC - minmsk = (kind & 32 ? 0 : 3); - maxmsk = (kind & 32 ? 7 : 3); -#else minmsk = 0; maxmsk = 7; -#endif for (mask=minmsk; mask<=maxmsk; mask++) for (ownobj=1; ownobj<7; ownobj++) for (grpobj=1; grpobj<7; grpobj++) @@ -4110,7 +3509,6 @@ void posixtest(int kind, BOOL isdir, pxdesc->acl.ace[5].perms = wrld; } - err = ERRNO; gotback = (struct POSIX_SECURITY*)NULL; pxattr = ntfs_build_descr_posix(context.mapping, pxdesc,isdir,owner,group); @@ -4136,7 +3534,6 @@ void posixtest(int kind, BOOL isdir, printf("gotback ACL\n"); showposix(gotback); errors++; -exit(1); } } else { printf("Got back an invalid Posix ACL\n"); @@ -4184,32 +3581,24 @@ exit(1); #endif /* POSIXACLS */ -void selftests(void) +static void selftests(void) { le32 owner_sid[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */ { - const_cpu_to_le32(0x501), const_cpu_to_le32(0x05000000), - const_cpu_to_le32(21), const_cpu_to_le32(DEFSECAUTH1), - const_cpu_to_le32(DEFSECAUTH2), const_cpu_to_le32(DEFSECAUTH3), - const_cpu_to_le32(1016) + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2), + cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016) } ; le32 group_sid[] = /* S-1-5-21-3141592653-589793238-462843383-513 */ { - const_cpu_to_le32(0x501), const_cpu_to_le32(0x05000000), - const_cpu_to_le32(21), const_cpu_to_le32(DEFSECAUTH1), - const_cpu_to_le32(DEFSECAUTH2), const_cpu_to_le32(DEFSECAUTH3), - const_cpu_to_le32(513) + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2), + cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513) } ; #if POSIXACLS -#ifdef STSC - unsigned char kindlist[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 16, 17, 18, 20, 22, 24, - 32, 33, 36, 40 } ; -#else unsigned char kindlist[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 20, 22, 24, 19, 21, 23, 25, 32, 33, 36, 40 } ; -#endif unsigned int k; #endif /* POSIXACLS */ int kind; @@ -4219,11 +3608,10 @@ void selftests(void) #if POSIXACLS local_build_mapping(context.mapping, (const char*)NULL); -#endif +#endif /* POSIXACLS */ /* first check samples */ mappingtype = MAPDUMMY; check_samples(); -if (errors) exit(1); /* * kind is oring of : * 1 : directory @@ -4254,202 +3642,17 @@ if (errors) exit(1); posixtest(kind, isdir, owner, group); } ntfs_free_mapping(context.mapping); -#endif +#endif /* POSIXACLS */ if (errors >= 10) printf("** too many errors, test aborted\n"); } -#endif /* SELFTESTS & !USESTUBS */ - -#ifdef WIN32 +#endif /* SELFTESTS */ /* - * Get the security descriptor of a file (Windows version) + * Get the security descriptor of a file */ -unsigned int getfull(char *attr, const char *fullname) -{ - static char part[MAXATTRSZ]; - BIGSID ownsid; - int xowner; - int ownersz; - u16 ownerfl; - ULONG attrsz; - ULONG partsz; - BOOL overflow; - HANDLE htoken; - TOKEN_PRIVILEGES tkp; - BOOL saclsuccess; - - attrsz = 0; - partsz = 0; - overflow = FALSE; - if (GetFileSecurityW((LPCWSTR)fullname,OWNER_SECURITY_INFORMATION, - (char*)part,MAXATTRSZ,&partsz)) { - xowner = get4l(part,4); - if (xowner) { - ownerfl = get2l(part,2); - ownersz = ntfs_sid_size((SID*)&part[xowner]); - if (ownersz <= (int)sizeof(BIGSID)) - memcpy(ownsid,&part[xowner],ownersz); - else - overflow = TRUE; - } else { - ownerfl = 0; - ownersz = 0; - } - /* - * SACL : just feed in or clean - * This requires the SE_SECURITY_NAME privilege - */ - saclsuccess = FALSE; - if (OpenProcessToken(GetCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &htoken)) { - if (LookupPrivilegeValue(NULL, SE_SECURITY_NAME, - &tkp.Privileges[0].Luid)) { - tkp.PrivilegeCount = 1; - tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - if (AdjustTokenPrivileges(htoken, FALSE, &tkp, 0, NULL, 0)) { - if (GetFileSecurityW((LPCWSTR)fullname, - SACL_SECURITY_INFORMATION, - (char*)attr,MAXATTRSZ,&attrsz)) { - saclsuccess = TRUE; - } - tkp.Privileges[0].Attributes = 0; - AdjustTokenPrivileges(htoken, FALSE, &tkp, 0, NULL, 0); - } - } - } - if (!saclsuccess) { - attrsz = 20; - set4l(attr,0); - attr[0] = SECURITY_DESCRIPTOR_REVISION; - set4l(&attr[12],0); - if (opt_v >= 2) - printf(" No SACL\n"); - } - /* - * append DACL and merge its flags - */ - partsz = 0; - set4l(&attr[16],0); - if (GetFileSecurityW((LPCWSTR)fullname,DACL_SECURITY_INFORMATION, - (char*)part,MAXATTRSZ,&partsz)) { - if ((attrsz + partsz - 20) <= MAXATTRSZ) { - memcpy(&attr[attrsz],&part[20],partsz-20); - set4l(&attr[16],(partsz > 20 ? attrsz : 0)); - set2l(&attr[2],get2l(attr,2) | (get2l(part,2) - & const_le16_to_cpu(SE_DACL_PROTECTED - | SE_DACL_AUTO_INHERITED - | SE_DACL_PRESENT))); - attrsz += partsz - 20; - } else - overflow = TRUE; - } else - if (partsz > MAXATTRSZ) - overflow = TRUE; - else { - if (opt_b) - printf("# No discretionary access control list\n"); - else - printf(" No discretionary access control list\n"); - warnings++; - } - - /* - * append owner and merge its flag - */ - if (xowner && !overflow) { - memcpy(&attr[attrsz],ownsid,ownersz); - set4l(&attr[4],attrsz); - set2l(&attr[2],get2l(attr,2) - | (ownerfl & const_le16_to_cpu(SE_OWNER_DEFAULTED))); - attrsz += ownersz; - } else - set4l(&attr[4],0); - /* - * append group - */ - partsz = 0; - set4l(&attr[8],0); - if (GetFileSecurityW((LPCWSTR)fullname,GROUP_SECURITY_INFORMATION, - (char*)part,MAXATTRSZ,&partsz)) { - if ((attrsz + partsz - 20) <= MAXATTRSZ) { - memcpy(&attr[attrsz],&part[20],partsz-20); - set4l(&attr[8],(partsz > 20 ? attrsz : 0)); - set2l(&attr[2],get2l(attr,2) | (get2l(part,2) - & const_le16_to_cpu(SE_GROUP_DEFAULTED))); - attrsz += partsz - 20; - } else - overflow = TRUE; - } else - if (partsz > MAXATTRSZ) - overflow = TRUE; - else { - printf("** No group SID\n"); - warnings++; - } - set2l(&attr[2],get2l(attr,2) - | const_le16_to_cpu(SE_SELF_RELATIVE)); - if (overflow) { - printf("** Descriptor was too long (> %d)\n",MAXATTRSZ); - warnings++; - attrsz = 0; - } else - if (!ntfs_valid_descr((char*)attr,attrsz)) { - printf("** Descriptor for "); - printname(stdout,fullname); - printf(" is not valid\n"); - errors++; - attrsz = 0; - } - - } else { - printf("** Could not get owner of "); - printname(stdout,fullname); - printf(", partsz %d\n",partsz); - printerror(stdout); - warnings++; - attrsz = 0; - } - return (attrsz); -} - -/* - * Update a security descriptor (Windows version) - */ - -BOOL updatefull(const char *name, DWORD flags, char *attr) -{ - BOOL bad; - - bad = !SetFileSecurityW((LPCWSTR)name, flags, attr); - if (bad - && (flags & OWNER_SECURITY_INFORMATION) - && (GetLastError() == 1307)) { - printf("** Could not set owner of "); - printname(stdout,name); - printf(", retrying with no owner setting\n"); - warnings++; - bad = !SetFileSecurityW((LPCWSTR)name, - flags & ~OWNER_SECURITY_INFORMATION, (char*)attr); - } - if (bad) { - printf("** Could not change attributes of "); - printname(stdout,name); - printf("\n"); - printerror(stdout); - errors++; - } - return (!bad); -} - -#else - -/* - * Get the security descriptor of a file (Linux version) - */ - -unsigned int getfull(char *attr, const char *fullname) +static unsigned int getfull(char *attr, const char *fullname) { static char part[MAXATTRSZ]; BIGSID ownsid; @@ -4503,9 +3706,9 @@ unsigned int getfull(char *attr, const char *fullname) memcpy(&attr[attrsz],&part[20],partsz-20); set4l(&attr[16],(partsz > 20 ? attrsz : 0)); set2l(&attr[2],get2l(attr,2) | (get2l(part,2) - & const_le16_to_cpu(SE_DACL_PROTECTED - | SE_DACL_AUTO_INHERITED - | SE_DACL_PRESENT))); + & const_le16_to_cpu(le16_or(SE_DACL_PROTECTED, + le16_or(SE_DACL_AUTO_INHERITED, + SE_DACL_PRESENT))))); attrsz += partsz - 20; } else overflow = TRUE; @@ -4513,7 +3716,7 @@ unsigned int getfull(char *attr, const char *fullname) if (partsz > MAXATTRSZ) overflow = TRUE; else { - if (opt_b) + if (cmd == CMD_BACKUP) printf("# No discretionary access control list\n"); else printf(" No discretionary access control list\n"); @@ -4568,38 +3771,37 @@ unsigned int getfull(char *attr, const char *fullname) } else { printf("** Could not get owner of %s\n",fullname); warnings++; - attrsz = 0; + attrsz = 0; } return (attrsz); } /* - * Update a security descriptor (Linux version) + * Update a security descriptor */ -BOOL updatefull(const char *name, DWORD flags, char *attr) +static BOOL updatefull(const char *name, u32 flags, char *attr) { - BOOL ok; + BOOL err; - ok = !ntfs_set_file_security(ntfs_context, name, flags, attr); - if (ok) { +// Why was the error not seen before ? + err = !ntfs_set_file_security(ntfs_context, name, flags, attr); + if (err) { printf("** Could not change attributes of %s\n",name); printerror(stdout); errors++; } - return (ok); + return (!err); } -#endif - #if POSIXACLS /* * Set all the parameters associated to a file */ -BOOL setfull_posix(const char *fullname, const struct POSIX_SECURITY *pxdesc, +static BOOL setfull_posix(const char *fullname, const struct POSIX_SECURITY *pxdesc, BOOL isdir) { static char attr[MAXATTRSZ]; @@ -4614,7 +3816,7 @@ BOOL setfull_posix(const char *fullname, const struct POSIX_SECURITY *pxdesc, const SID *gsid; #if OWNERFROMACL const SID *osid; -#endif +#endif /* OWNERFROMACL */ printf("%s ",(isdir ? "Directory" : "File")); printname(stdout,fullname); @@ -4658,12 +3860,18 @@ BOOL setfull_posix(const char *fullname, const struct POSIX_SECURITY *pxdesc, osid = (const SID*)&attr[le32_to_cpu(phead->owner)]; usid = ntfs_acl_owner((const char*)attr); if (!ntfs_same_sid(usid,osid)) - printf("== Windows owner might change\n"); -#else + printf("== Windows owner might change\n"); +#else /* OWNERFROMACL */ usid = (const SID*)&attr[le32_to_cpu(phead->owner)]; -#endif - newattr = ntfs_build_descr_posix(context.mapping, - newpxdesc,isdir,usid,gsid); +#endif /* OWNERFROMACL */ + if (mappingtype == MAPEXTERN) + newattr = ntfs_build_descr_posix( + ntfs_context->security.mapping, + newpxdesc,isdir,usid,gsid); + else + newattr = ntfs_build_descr_posix( + context.mapping, + newpxdesc,isdir,usid,gsid); free(newpxdesc); } else newattr = (char*)NULL; @@ -4683,23 +3891,11 @@ BOOL setfull_posix(const char *fullname, const struct POSIX_SECURITY *pxdesc, showsacl(newattr,isdir,0); } -#ifdef WIN32 - /* - * avoid getting a set owner error on Windows - * owner should not be changed anyway - */ if (!updatefull(fullname, DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, newattr)) -#else - if (!updatefull(fullname, - DACL_SECURITY_INFORMATION - | GROUP_SECURITY_INFORMATION - | OWNER_SECURITY_INFORMATION, - newattr)) -#endif err = TRUE; /* { @@ -4718,9 +3914,9 @@ free(interp); return (!err); } -#endif +#else /* POSIXACLS */ -BOOL setfull(const char *fullname, int mode, BOOL isdir) +static BOOL setfull(const char *fullname, int mode, BOOL isdir) { static char attr[MAXATTRSZ]; const SECURITY_DESCRIPTOR_RELATIVE *phead; @@ -4732,7 +3928,7 @@ BOOL setfull(const char *fullname, int mode, BOOL isdir) const SID *gsid; #if OWNERFROMACL const SID *osid; -#endif +#endif /* OWNERFROMACL */ printf("%s ",(isdir ? "Directory" : "File")); printname(stdout,fullname); @@ -4746,10 +3942,10 @@ BOOL setfull(const char *fullname, int mode, BOOL isdir) osid = (const SID*)&attr[le32_to_cpu(phead->owner)]; usid = ntfs_acl_owner((const char*)attr); if (!ntfs_same_sid(usid,osid)) - printf("== Windows owner might change\n"); -#else + printf("== Windows owner might change\n"); +#else /* OWNERFROMACL */ usid = (const SID*)&attr[le32_to_cpu(phead->owner)]; -#endif +#endif /* OWNERFROMACL */ newattr = ntfs_build_descr(mode,isdir,usid,gsid); if (newattr) { newattrsz = ntfs_attr_size(newattr); @@ -4767,33 +3963,23 @@ BOOL setfull(const char *fullname, int mode, BOOL isdir) showsacl(newattr,isdir,0); } -#ifdef WIN32 - /* - * avoid getting a set owner error on Windows - * owner should not be changed anyway - */ if (!updatefull(fullname, DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, newattr)) -#else - if (!updatefull(fullname, - DACL_SECURITY_INFORMATION - | GROUP_SECURITY_INFORMATION - | OWNER_SECURITY_INFORMATION, - newattr)) -#endif err = TRUE; free(newattr); } - + } else err = TRUE; return (err); } -BOOL proposal(const char *name, const char *attr) +#endif /* POSIXACLS */ + +static BOOL proposal(const char *name, const char *attr) { char fullname[MAXFILENAME]; int uoff, goff; @@ -4802,20 +3988,20 @@ BOOL proposal(const char *name, const char *attr) int ucnt, gcnt; int uid, gid; BOOL err; -#ifdef WIN32 +#ifdef HAVE_WINDOWS_H char driveletter; -#else +#else /* HAVE_WINDOWS_H */ struct stat st; char *p,*q; -#endif +#endif /* HAVE_WINDOWS_H */ err = FALSE; -#ifdef WIN32 +#ifdef HAVE_WINDOWS_H uid = gid = 0; -#else +#else /* HAVE_WINDOWS_H */ uid = getuid(); gid = getgid(); -#endif +#endif /* HAVE_WINDOWS_H */ uoff = get4l(attr,4); uauth = get6h(attr,uoff+2); ucnt = attr[uoff+1] & 255; @@ -4832,7 +4018,7 @@ BOOL proposal(const char *name, const char *attr) printf("%d::",uid); else printf("user::"); - printf("S-%d-%llu",attr[uoff] & 255,uauth); + printf("S-%d-%llu",attr[uoff] & 255,(long long)uauth); for (i=0; i