Merge branch 'edge' into edge.strict_endians

Conflicts:
	include/ntfs-3g/layout.h
	libntfs-3g/acls.c
	libntfs-3g/attrib.c
	libntfs-3g/bootsect.c
	libntfs-3g/dir.c
	libntfs-3g/mft.c
	libntfs-3g/reparse.c
	ntfsprogs/ntfsinfo.c
	ntfsprogs/ntfsresize.c
	ntfsprogs/playlog.c
	src/lowntfs-3g.c
	src/ntfs-3g_common.c
edge.strict_endians
Erik Larsson 2021-03-05 14:48:11 +02:00
commit a00610ed15
63 changed files with 2467 additions and 494 deletions

View File

@ -33,7 +33,7 @@ AC_CANONICAL_HOST
AC_CANONICAL_TARGET
# Automake
AM_INIT_AUTOMAKE([${PACKAGE_NAME}], [${PACKAGE_VERSION}])
AM_INIT_AUTOMAKE([])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
AM_MAINTAINER_MODE
@ -226,7 +226,9 @@ case "${target}" in
;;
esac
if test "x${enable_ntfs_3g}" = "xyes"; then
if test "x${enable_ntfs_3g}" != "xyes"; then
with_fuse="none"
elif test "x${with_fuse}" == "x"; then
AC_MSG_CHECKING([fuse compatibility])
case "${target_os}" in
linux*|solaris*)
@ -248,8 +250,6 @@ if test "x${enable_ntfs_3g}" = "xyes"; then
;;
esac
AC_MSG_RESULT([${with_fuse}])
else
with_fuse="none"
fi
case "${target_os}" in

View File

@ -432,6 +432,9 @@ struct fuse_operations {
* non-NULL cases, the area is of _IOC_SIZE(cmd) bytes.
*
* Introduced in version 2.8
*
* Note : the unsigned long request submitted by the application
* is truncated to 32 bits, and forwarded as a signed int.
*/
int (*ioctl) (const char *, int cmd, void *arg,
struct fuse_file_info *, unsigned int flags, void *data);

View File

@ -814,6 +814,9 @@ struct fuse_lowlevel_ops {
*
* Introduced in version 2.8
*
* Note : the unsigned long request submitted by the application
* is truncated to 32 bits, and forwarded as a signed int.
*
* Valid replies:
* fuse_reply_ioctl_retry
* fuse_reply_ioctl

View File

@ -1,9 +1,10 @@
/*
* compat.h - Tweaks for Windows compatibility.
* compat.h - Tweaks for compatibility with non-Linux systems.
*
* Copyright (c) 2002 Richard Russon
* Copyright (c) 2002-2004 Anton Altaparmakov
* Copyright (c) 2008-2009 Szabolcs Szakacsits
* Copyright (c) 2019 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
@ -41,6 +42,20 @@
#define ELIBBAD ENOEXEC
#endif
#ifndef ELIBACC
#define ELIBACC ENOENT
#endif
/* xattr APIs in macOS differs from Linux ones in that they expect the special
* error code ENOATTR to be returned when an attribute cannot be found. So
* define NTFS_NOXATTR_ERRNO to the appropriate "no xattr found" errno value for
* the platform. */
#if defined(__APPLE__) || defined(__DARWIN__)
#define NTFS_NOXATTR_ERRNO ENOATTR
#else
#define NTFS_NOXATTR_ERRNO ENODATA
#endif
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif

View File

@ -111,7 +111,8 @@ struct ntfs_device_operations {
s64 offset);
int (*sync)(struct ntfs_device *dev);
int (*stat)(struct ntfs_device *dev, struct stat *buf);
int (*ioctl)(struct ntfs_device *dev, int request, void *argp);
int (*ioctl)(struct ntfs_device *dev, unsigned long request,
void *argp);
};
extern struct ntfs_device *ntfs_device_alloc(const char *name, const long state,

View File

@ -94,6 +94,7 @@ extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name,
#define NTFS_DT_LNK 10
#define NTFS_DT_SOCK 12
#define NTFS_DT_WHT 14
#define NTFS_DT_REPARSE 32
/*
* This is the "ntfs_filldir" function type, used by ntfs_readdir() to let
@ -116,6 +117,7 @@ int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
const char *value, size_t size, int flags);
int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni);
int ntfs_dir_link_cnt(ntfs_inode *ni);
#if CACHE_INODE_SIZE

View File

@ -1,6 +1,6 @@
/*
*
* Copyright (c) 2014 Jean-Pierre Andre
* Copyright (c) 2014-2021 Jean-Pierre Andre
*
*/
@ -24,6 +24,10 @@
#ifndef EA_H
#define EA_H
int ntfs_ea_check_wsldev(ntfs_inode *ni, dev_t *rdevp);
int ntfs_ea_set_wsl_not_symlink(ntfs_inode *ni, mode_t mode, dev_t dev);
int ntfs_get_ntfs_ea(ntfs_inode *ni, char *value, size_t size);
int ntfs_set_ntfs_ea(ntfs_inode *ni, const char *value, size_t size, int flags);

View File

@ -24,7 +24,12 @@
#ifndef IOCTL_H
#define IOCTL_H
int ntfs_ioctl(ntfs_inode *ni, int cmd, void *arg,
/*
* Using an "unsigned long cmd" internally, like in <sys/ioctl.h> for Linux
* Note however that fuse truncates the arg to 32 bits, and that
* some commands (e.g. FITRIM) do not fit in a signed 32 bit field.
*/
int ntfs_ioctl(ntfs_inode *ni, unsigned long cmd, void *arg,
unsigned int flags, void *data);
#endif /* IOCTL_H */

View File

@ -916,8 +916,10 @@ typedef enum {
FILE_ATTR_OFFLINE = const_cpu_to_le32(0x00001000),
FILE_ATTR_NOT_CONTENT_INDEXED = const_cpu_to_le32(0x00002000),
FILE_ATTR_ENCRYPTED = const_cpu_to_le32(0x00004000),
/* Supposed to mean no data locally, possibly repurposed */
FILE_ATTRIBUTE_RECALL_ON_OPEN = const_cpu_to_le32(0x00040000),
FILE_ATTR_VALID_FLAGS = const_cpu_to_le32(0x00007fb7),
FILE_ATTR_VALID_FLAGS = const_cpu_to_le32(0x00047fb7),
/* FILE_ATTR_VALID_FLAGS masks out the old DOS VolId and the
FILE_ATTR_DEVICE and preserves everything else. This mask
is used to obtain all flags that are valid for reading. */
@ -1503,6 +1505,26 @@ typedef enum {
/* This one is for WinNT&2k. */
ACCESS_MAX_MS_ACE_TYPE = 8,
/* Windows XP and later */
ACCESS_ALLOWED_CALLBACK_ACE_TYPE = 9,
ACCESS_DENIED_CALLBACK_ACE_TYPE = 10,
ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE = 11,
ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE = 12,
SYSTEM_AUDIT_CALLBACK_ACE_TYPE = 13,
SYSTEM_ALARM_CALLBACK_ACE_TYPE = 14, /* reserved */
SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE = 15,
SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE = 16, /* reserved */
/* Windows Vista and later */
SYSTEM_MANDATORY_LABEL_ACE_TYPE = 17,
/* Windows 8 and later */
SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE = 18,
SYSTEM_SCOPED_POLICY_ID_ACE_TYPE = 19,
/* Windows 10 and later */
SYSTEM_PROCESS_TRUST_LABEL_ACE_TYPE = 20,
} __attribute__((__packed__)) ACE_TYPES;
/**
@ -2523,15 +2545,20 @@ typedef struct {
*
* 1. The least significant 16 bits (i.e. bits 0 to 15) specify the type of
* the reparse point.
* 2. The 13 bits after this (i.e. bits 16 to 28) are reserved for future use.
* 3. The most significant three bits are flags describing the reparse point.
* 2. The 12 bits after this (i.e. bits 16 to 27) are reserved for future use.
* 3. The most significant four bits are flags describing the reparse point.
* They are defined as follows:
* bit 28: Directory bit. If set, the directory is not a surrogate
* and can be used the usual way.
* bit 29: Name surrogate bit. If set, the filename is an alias for
* another object in the system.
* bit 30: High-latency bit. If set, accessing the first byte of data will
* be slow. (E.g. the data is stored on a tape drive.)
* bit 31: Microsoft bit. If set, the tag is owned by Microsoft. User
* defined tags have to use zero here.
* 4. Moreover, on Windows 10 :
* Some flags may be used in bits 12 to 15 to further describe the
* reparse point.
*/
#if ENABLE_STRICT_ENDIANNESS_CHECKING
typedef le32 PREDEFINED_REPARSE_TAGS;
@ -2540,6 +2567,7 @@ static const PREDEFINED_REPARSE_TAGS
#else
typedef enum {
#endif
IO_REPARSE_TAG_DIRECTORY = const_cpu_to_le32(0x10000000),
IO_REPARSE_TAG_IS_ALIAS = const_cpu_to_le32(0x20000000),
IO_REPARSE_TAG_IS_HIGH_LATENCY = const_cpu_to_le32(0x40000000),
IO_REPARSE_TAG_IS_MICROSOFT = const_cpu_to_le32(0x80000000),
@ -2559,9 +2587,20 @@ typedef enum {
IO_REPARSE_TAG_SIS = const_cpu_to_le32(0x80000007),
IO_REPARSE_TAG_SYMLINK = const_cpu_to_le32(0xA000000C),
IO_REPARSE_TAG_WIM = const_cpu_to_le32(0x80000008),
IO_REPARSE_TAG_DFM = const_cpu_to_le32(0x80000016),
IO_REPARSE_TAG_WOF = const_cpu_to_le32(0x80000017),
IO_REPARSE_TAG_WCI = const_cpu_to_le32(0x80000018),
IO_REPARSE_TAG_CLOUD = const_cpu_to_le32(0x9000001A),
IO_REPARSE_TAG_APPEXECLINK = const_cpu_to_le32(0x8000001B),
IO_REPARSE_TAG_GVFS = const_cpu_to_le32(0x9000001C),
IO_REPARSE_TAG_LX_SYMLINK = const_cpu_to_le32(0xA000001D),
IO_REPARSE_TAG_AF_UNIX = const_cpu_to_le32(0x80000023),
IO_REPARSE_TAG_LX_FIFO = const_cpu_to_le32(0x80000024),
IO_REPARSE_TAG_LX_CHR = const_cpu_to_le32(0x80000025),
IO_REPARSE_TAG_LX_BLK = const_cpu_to_le32(0x80000026),
IO_REPARSE_TAG_VALID_VALUES = const_cpu_to_le32(0xf000ffff),
IO_REPARSE_PLUGIN_SELECT = const_cpu_to_le32(0xffff0fff),
#if !ENABLE_STRICT_ENDIANNESS_CHECKING
} PREDEFINED_REPARSE_TAGS;
#else

View File

@ -25,6 +25,8 @@
void *ntfs_calloc(size_t size);
void *ntfs_malloc(size_t size);
void *ntfs_realloc(void *ptr, size_t size);
void ntfs_free(void *ptr);
#endif /* _NTFS_MISC_H_ */

View File

@ -39,6 +39,13 @@ enum {
DEFSECBASE = 10000
};
/*
* Parameters for formatting
*/
/* Up to Windows 10, the cluster size was limited to 64K */
#define NTFS_MAX_CLUSTER_SIZE 2097152 /* Windows 10 Creators allows 2MB */
/*
* Parameters for compression
*/
@ -112,6 +119,9 @@ enum {
* having access control in the file system leads to fewer requests
* to the file system and fewer context switches.
*
* Irrespective of the selected mode, cacheing is always used
* in read-only mounts
*
* Possible values for high level :
* 1 : no cache, kernel control (recommended)
* 4 : no cache, file system control
@ -120,7 +130,7 @@ enum {
*
* Possible values for low level :
* 2 : no cache, kernel control
* 3 : use kernel/fuse cache, kernel control (recommended)
* 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
@ -131,6 +141,7 @@ enum {
* of 6 is added in the mount report.
*/
#define TIMEOUT_RO 600 /* Attribute time out for read-only mounts */
#if defined(__sun) && defined(__SVR4)
/*
* Access control by kernel is not implemented on OpenIndiana,
@ -143,9 +154,10 @@ enum {
* 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.
* Also ACL checks by recent kernels do not prove satisfactory.
*/
#define HPERMSCONFIG 1
#define LPERMSCONFIG 3 /* Use 9 when ACLs are supported by fuse kernel */
#define LPERMSCONFIG 3
#endif /* defined(__sun) && defined(__SVR4) */
#endif /* defined _NTFS_PARAM_H */

View File

@ -1,7 +1,7 @@
/*
* plugin.h : define interface for plugin development
*
* Copyright (c) 2015 Jean-Pierre Andre
* Copyright (c) 2015-2021 Jean-Pierre Andre
*
*/
@ -30,8 +30,9 @@
#ifndef _NTFS_PLUGIN_H
#define _NTFS_PLUGIN_H
#include "inode.h"
#include "layout.h"
#include "inode.h"
#include "dir.h"
struct fuse_file_info;
struct stat;
@ -71,10 +72,10 @@ typedef struct plugin_operations {
struct fuse_file_info *fi);
/*
* Release an open file
* Release an open file or directory
* 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.
* specific to the open file or directory
* The returned value is zero for success or a negative errno
* value for failure.
*/
@ -110,7 +111,7 @@ typedef struct plugin_operations {
* 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.
* with the locale mount option.
* The returned value is zero for success or a negative errno
* value for failure.
*/
@ -126,6 +127,58 @@ typedef struct plugin_operations {
*/
int (*truncate)(ntfs_inode *ni, const REPARSE_POINT *reparse,
off_t size);
/*
* Open a directory
* 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 readdir(). When used
* this field must be non-null and freed in release().
* The returned value is zero for success and a negative errno
* value for failure.
*/
int (*opendir)(ntfs_inode *ni, const REPARSE_POINT *reparse,
struct fuse_file_info *fi);
/*
* Get entries from a directory
*
* Use the filldir() function with fillctx argument to return
* the directory entries.
* Names "." and ".." are expected to be returned.
* The returned value is zero for success and a negative errno
* value for failure.
*/
int (*readdir)(ntfs_inode *ni, const REPARSE_POINT *reparse,
s64 *pos, void *fillctx, ntfs_filldir_t filldir,
struct fuse_file_info *fi);
/*
* Create a new file of any type
*
* The returned value is a pointer to the inode created, or
* NULL if failed, with errno telling why.
*/
ntfs_inode *(*create)(ntfs_inode *dir_ni, const REPARSE_POINT *reparse,
le32 securid, ntfschar *name, int name_len,
mode_t type);
/*
* Link a new name to a file or directory
* Linking a directory is needed for renaming a directory
* 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 (*link)(ntfs_inode *dir_ni, const REPARSE_POINT *reparse,
ntfs_inode *ni, ntfschar *name, int name_len);
/*
* Unlink a name from a directory
* The argument pathname may be NULL
* The returned value is zero for success or a negative errno
* value for failure.
*/
int (*unlink)(ntfs_inode *dir_ni, const REPARSE_POINT *reparse,
const char *pathname,
ntfs_inode *ni, ntfschar *name, int name_len);
} plugin_operations_t;

View File

@ -30,8 +30,18 @@ BOOL ntfs_possible_symlink(ntfs_inode *ni);
int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size);
char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction,
int count, const char *mnt_point, BOOL isdir);
REPARSE_POINT *ntfs_get_reparse_point(ntfs_inode *ni);
int ntfs_reparse_check_wsl(ntfs_inode *ni, const REPARSE_POINT *reparse);
int ntfs_reparse_set_wsl_symlink(ntfs_inode *ni,
const ntfschar *target, int target_len);
int ntfs_reparse_set_wsl_not_symlink(ntfs_inode *ni, mode_t mode);
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);

View File

@ -98,6 +98,11 @@ typedef enum {
NTFS_VOLUME_INSECURE = 22
} ntfs_volume_status;
typedef enum {
NTFS_FILES_INTERIX,
NTFS_FILES_WSL,
} ntfs_volume_special_files;
/**
* enum ntfs_volume_state_bits -
*
@ -256,6 +261,8 @@ struct _ntfs_volume {
s64 free_mft_records; /* Same for free mft records (see above) */
BOOL efs_raw; /* volume is mounted for raw access to
efs-encrypted files */
ntfs_volume_special_files special_files; /* Implementation of special files */
const char *abs_mnt_point; /* Mount point */
#ifdef XATTR_MAPPINGS
struct XATTRMAPPING *xattr_mapping;
#endif /* XATTR_MAPPINGS */
@ -274,7 +281,6 @@ struct _ntfs_volume {
#if CACHE_LEGACY_SIZE
struct CACHE_HEADER *legacy_cache;
#endif
};
extern const char *ntfs_home;

View File

@ -7,7 +7,10 @@ endif
libfuse_lite_la_CFLAGS= \
$(AM_CFLAGS) \
$(LIBFUSE_LITE_CFLAGS) \
$(LIBFUSE_LITE_CFLAGS)
libfuse_lite_la_CPPFLAGS= \
$(AM_CPPFLAGS) \
-I$(top_srcdir)/include/fuse-lite
libfuse_lite_la_LIBADD = $(LIBFUSE_LITE_LIBS)

View File

@ -134,7 +134,6 @@ struct fuse_dh {
struct fuse *fuse;
fuse_req_t req;
char *contents;
int allocated;
unsigned len;
unsigned size;
unsigned needlen;
@ -464,7 +463,7 @@ static char *add_name(char *buf, char *s, const char *name)
return NULL;
}
#endif /* __SOLARIS__ */
strncpy(s, name, len);
memcpy(s, name, len);
s--;
*s = '/';
@ -2782,8 +2781,10 @@ static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
fuse_finish_interrupt(f, req, &d);
free(path);
fuse_reply_ioctl(req, err, out_buf, out_bufsz);
if (err >= 0) { /* not an error */
fuse_reply_ioctl(req, err, out_buf, out_bufsz);
goto out;
}
err:
reply_err(req, err);
out:

View File

@ -247,7 +247,7 @@ char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf,
dirent->off = off;
dirent->namelen = namelen;
dirent->type = (stbuf->st_mode & 0170000) >> 12;
strncpy(dirent->name, name, namelen);
memcpy(dirent->name, name, namelen);
if (padlen)
memset(buf + entlen, 0, padlen);

View File

@ -9,8 +9,8 @@ else
noinst_LTLIBRARIES = libntfs-3g.la
endif
libntfs_3g_la_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g
libntfs_3g_la_CPPFLAGS= $(AM_CPPFLAGS) $(LIBNTFS_CPPFLAGS)
libntfs_3g_la_CFLAGS = $(AM_CFLAGS)
libntfs_3g_la_CPPFLAGS= $(AM_CPPFLAGS) $(LIBNTFS_CPPFLAGS) -I$(top_srcdir)/include/ntfs-3g
libntfs_3g_la_LIBADD = $(LIBNTFS_LIBS)
libntfs_3g_la_LDFLAGS = -version-info $(LIBNTFS_3G_VERSION) -no-undefined

View File

@ -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-2016 Jean-Pierre Andre
* Copyright (c) 2007-2017 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
@ -560,16 +560,34 @@ static BOOL valid_acl(const ACL *pacl, unsigned int end)
pace = (const ACCESS_ALLOWED_ACE*)
&((const char*)pacl)[offace];
acesz = le16_to_cpu(pace->size);
if (((offace + acesz) > end)
|| !ntfs_valid_sid(&pace->sid))
ok = FALSE;
else {
/* Win10 may insert garbage in the last ACE */
switch (pace->type) {
case ACCESS_ALLOWED_ACE_TYPE :
case ACCESS_DENIED_ACE_TYPE :
wantsz = ntfs_sid_size(&pace->sid) + 8;
if (((nace < (acecnt - 1))
&& (wantsz != acesz))
if (((offace + acesz) > end)
|| !ntfs_valid_sid(&pace->sid)
|| (wantsz != acesz))
ok = FALSE;
break;
case SYSTEM_AUDIT_ACE_TYPE :
case ACCESS_ALLOWED_CALLBACK_ACE_TYPE :
case ACCESS_DENIED_CALLBACK_ACE_TYPE :
case SYSTEM_AUDIT_CALLBACK_ACE_TYPE :
case SYSTEM_MANDATORY_LABEL_ACE_TYPE :
case SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE :
case SYSTEM_SCOPED_POLICY_ID_ACE_TYPE :
/* Extra data after the SID */
wantsz = ntfs_sid_size(&pace->sid) + 8;
if (((offace + acesz) > end)
|| !ntfs_valid_sid(&pace->sid)
|| (wantsz > acesz))
ok = FALSE;
break;
default :
/* SID at a different location */
if ((offace + acesz) > end)
ok = FALSE;
break;
}
offace += acesz;
}
@ -683,6 +701,7 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl,
int acesz;
int usidsz;
int gsidsz;
BOOL acceptable;
const ACCESS_ALLOWED_ACE *poldace;
ACCESS_ALLOWED_ACE *pnewace;
ACCESS_ALLOWED_ACE *pauthace;
@ -707,6 +726,20 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl,
poldace = (const ACCESS_ALLOWED_ACE*)((const char*)oldacl + src);
acesz = le16_to_cpu(poldace->size);
src += acesz;
/*
* Currently only ACE for file or directory access are
* processed. More information needed about what to do
* for other types (whose SID may be at a different location)
*/
switch (poldace->type) {
case ACCESS_ALLOWED_ACE_TYPE :
case ACCESS_DENIED_ACE_TYPE :
acceptable = TRUE;
break;
default :
acceptable = FALSE;
break;
}
/*
* Extract inheritance for access, including inheritance for
* access from an ACE with is both applied and inheritable.
@ -721,6 +754,7 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl,
* "information."
*/
if ((poldace->flags & selection)
&& acceptable
&& (!fordir
|| (poldace->flags & NO_PROPAGATE_INHERIT_ACE)
|| !le32_andz(poldace->mask, le32_or(GENERIC_ALL, le32_or(GENERIC_READ,
@ -810,9 +844,10 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl,
* Inheritance for access, specific to
* creator-owner (and creator-group)
*/
if (fordir || le16_cmpz(inherited)
|| (poldace->flags
& (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))) {
if ((fordir || le16_cmpz(inherited)
|| (poldace->flags
& (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE)))
&& acceptable) {
pnewace = (ACCESS_ALLOWED_ACE*)
((char*)newacl + dst);
memcpy(pnewace,poldace,acesz);
@ -869,7 +904,8 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl,
&& !(poldace->flags & (CONTAINER_INHERIT_ACE
| NO_PROPAGATE_INHERIT_ACE)))
pnewace->flags |= INHERIT_ONLY_ACE;
if ((poldace->flags & CONTAINER_INHERIT_ACE)
if (acceptable
&& (poldace->flags & CONTAINER_INHERIT_ACE)
&& !(poldace->flags & NO_PROPAGATE_INHERIT_ACE)
&& !ntfs_same_sid(&poldace->sid, ownersid)
&& !ntfs_same_sid(&poldace->sid, groupsid)) {
@ -3867,7 +3903,9 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix(
}
}
}
if (!ignore) {
if (((pace->type == ACCESS_ALLOWED_ACE_TYPE)
|| (pace->type == ACCESS_DENIED_ACE_TYPE))
&& !ignore) {
pxace->perms = 0;
/* specific decoding for vtx/uid/gid */
if (pxace->tag == POSIX_ACL_SPECIAL) {

View File

@ -5,7 +5,7 @@
* Copyright (c) 2002-2005 Richard Russon
* Copyright (c) 2002-2008 Szabolcs Szakacsits
* Copyright (c) 2004-2007 Yura Pakhuchiy
* Copyright (c) 2007-2015 Jean-Pierre Andre
* Copyright (c) 2007-2020 Jean-Pierre Andre
* Copyright (c) 2010 Erik Larsson
*
* This program/include file is free software; you can redistribute it and/or
@ -5850,6 +5850,9 @@ retry:
le16_or(ATTR_IS_COMPRESSED, ATTR_IS_SPARSE));
if (!le16_cmpz(spcomp))
a->compressed_size = cpu_to_sle64(na->compressed_size);
/* Updating sizes taints the extent holding the attr */
if (ctx->ntfs_ino)
NInoSetDirty(ctx->ntfs_ino);
if (le32_eq(na->type, AT_DATA) && (na->name == AT_UNNAMED)) {
na->ni->allocated_size
= (!le16_cmpz(spcomp)
@ -6902,7 +6905,9 @@ s64 ntfs_attr_get_free_bits(ntfs_attr *na)
}
switch (br % 4) {
case 3: nr_free += lut[*(buf + br - 3)];
/* FALLTHRU */
case 2: nr_free += lut[*(buf + br - 2)];
/* FALLTHRU */
case 1: nr_free += lut[*(buf + br - 1)];
}
}

View File

@ -38,6 +38,7 @@
#include <errno.h>
#endif
#include "param.h"
#include "compat.h"
#include "bootsect.h"
#include "debug.h"
@ -61,6 +62,7 @@ BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b)
{
u32 i;
BOOL ret = FALSE;
u16 sectors_per_cluster;
ntfs_log_debug("Beginning bootsector check.\n");
@ -83,15 +85,27 @@ BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b)
case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
break;
default:
ntfs_log_error("Unexpected sectors per cluster value (%d).\n",
b->bpb.sectors_per_cluster);
goto not_ntfs;
if ((b->bpb.sectors_per_cluster < 240)
|| (b->bpb.sectors_per_cluster > 253)) {
if (b->bpb.sectors_per_cluster > 128)
ntfs_log_error("Unexpected sectors"
" per cluster value (code 0x%x)\n",
b->bpb.sectors_per_cluster);
else
ntfs_log_error("Unexpected sectors"
" per cluster value (%d).\n",
b->bpb.sectors_per_cluster);
goto not_ntfs;
}
}
ntfs_log_debug("Checking cluster size.\n");
i = (u32)le16_to_cpu(b->bpb.bytes_per_sector) *
b->bpb.sectors_per_cluster;
if (i > 65536) {
if (b->bpb.sectors_per_cluster > 128)
sectors_per_cluster = 1 << (256 - b->bpb.sectors_per_cluster);
else
sectors_per_cluster = b->bpb.sectors_per_cluster;
i = (u32)le16_to_cpu(b->bpb.bytes_per_sector) * sectors_per_cluster;
if (i > NTFS_MAX_CLUSTER_SIZE) {
ntfs_log_error("Unexpected cluster size (%d).\n", i);
goto not_ntfs;
}
@ -140,6 +154,12 @@ BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b)
}
}
/* MFT and MFTMirr may not overlap the boot sector or be the same */
if (sle64_cmpz(b->mft_lcn) || sle64_cmpz(b->mftmirr_lcn) || sle64_eq(b->mft_lcn, b->mftmirr_lcn)) {
ntfs_log_error("Invalid location of MFT or MFTMirr.\n");
goto not_ntfs;
}
if (!le16_eq(b->end_of_sector_marker, const_cpu_to_le16(0xaa55)))
ntfs_log_debug("Warning: Bootsector has invalid end of sector "
"marker.\n");
@ -171,7 +191,7 @@ static const char *last_sector_error =
int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs)
{
s64 sectors;
u8 sectors_per_cluster;
u16 sectors_per_cluster;
s8 c;
/* We return -1 with errno = EINVAL on error. */
@ -186,7 +206,10 @@ int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs)
* below or equal the number_of_clusters) really belong in the
* ntfs_boot_sector_is_ntfs but in this way we can just do this once.
*/
sectors_per_cluster = bs->bpb.sectors_per_cluster;
if (bs->bpb.sectors_per_cluster > 128)
sectors_per_cluster = 1 << (256 - bs->bpb.sectors_per_cluster);
else
sectors_per_cluster = bs->bpb.sectors_per_cluster;
ntfs_log_debug("SectorsPerCluster = 0x%x\n", sectors_per_cluster);
if (sectors_per_cluster & (sectors_per_cluster - 1)) {
ntfs_log_error("sectors_per_cluster (%d) is not a power of 2."

View File

@ -491,6 +491,8 @@ do_next_sb:
* first two checks do not detect it.
*/
if (cb == cb_end || !le16_to_cpup((le16*)cb) || dest == dest_end) {
if (dest_end > dest)
memset(dest, 0, dest_end - dest);
ntfs_log_debug("Completed. Returning success (0).\n");
return 0;
}

View File

@ -5,7 +5,7 @@
* Copyright (c) 2004-2005 Richard Russon
* Copyright (c) 2004-2008 Szabolcs Szakacsits
* Copyright (c) 2005-2007 Yura Pakhuchiy
* Copyright (c) 2008-2014 Jean-Pierre Andre
* Copyright (c) 2008-2021 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
@ -68,6 +68,7 @@
#include "reparse.h"
#include "object_id.h"
#include "xattrs.h"
#include "ea.h"
/*
* The little endian Unicode strings "$I30", "$SII", "$SDH", "$O"
@ -934,9 +935,9 @@ static u32 ntfs_dir_entry_type(ntfs_inode *dir_ni, MFT_REF mref,
dt_type = NTFS_DT_UNKNOWN;
ni = ntfs_inode_open(dir_ni->vol, mref);
if (ni) {
if (!le32_andz(attributes, FILE_ATTR_REPARSE_POINT)
&& ntfs_possible_symlink(ni))
dt_type = NTFS_DT_LNK;
if (!le32_andz(attributes, FILE_ATTR_REPARSE_POINT))
dt_type = (ntfs_possible_symlink(ni)
? NTFS_DT_LNK : NTFS_DT_REPARSE);
else
if (!le32_andz(attributes, FILE_ATTR_SYSTEM)
&& le32_andz(attributes, FILE_ATTR_I30_INDEX_PRESENT))
@ -1496,9 +1497,11 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
{
ntfs_inode *ni;
int rollback_data = 0, rollback_sd = 0;
int rollback_dir = 0;
FILE_NAME_ATTR *fn = NULL;
STANDARD_INFORMATION *si = NULL;
int err, fn_len, si_len;
ntfs_volume_special_files special_files;
ntfs_log_trace("Entering.\n");
@ -1508,18 +1511,14 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
errno = EINVAL;
return NULL;
}
if (!le32_andz(dir_ni->flags, FILE_ATTR_REPARSE_POINT)) {
errno = EOPNOTSUPP;
return NULL;
}
ni = ntfs_mft_record_alloc(dir_ni->vol, NULL);
if (!ni)
return NULL;
#if CACHE_NIDATA_SIZE
ntfs_inode_invalidate(dir_ni->vol, ni->mft_no);
#endif
special_files = dir_ni->vol->special_files;
/*
* Create STANDARD_INFORMATION attribute.
* JPA Depending on available inherited security descriptor,
@ -1547,8 +1546,19 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
} else
clear_nino_flag(ni, v3_Extensions);
if (!S_ISREG(type) && !S_ISDIR(type)) {
si->file_attributes = FILE_ATTR_SYSTEM;
ni->flags = FILE_ATTR_SYSTEM;
switch (special_files) {
case NTFS_FILES_WSL :
if (!S_ISLNK(type)) {
si->file_attributes
= FILE_ATTRIBUTE_RECALL_ON_OPEN;
ni->flags = FILE_ATTRIBUTE_RECALL_ON_OPEN;
}
break;
default :
si->file_attributes = FILE_ATTR_SYSTEM;
ni->flags = FILE_ATTR_SYSTEM;
break;
}
}
ni->flags = le32_or(ni->flags, FILE_ATTR_ARCHIVE);
if (NVolHideDotFiles(dir_ni->vol)
@ -1581,8 +1591,8 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
err = errno;
goto err_out;
}
rollback_sd = 1;
}
rollback_sd = 1;
if (S_ISDIR(type)) {
INDEX_ROOT *ir = NULL;
@ -1631,34 +1641,58 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
switch (type) {
case S_IFBLK:
case S_IFCHR:
data_len = offsetof(INTX_FILE, device_end);
data = ntfs_malloc(data_len);
if (!data) {
err = errno;
goto err_out;
switch (special_files) {
case NTFS_FILES_WSL :
data_len = 0;
data = (INTX_FILE*)NULL;
break;
default :
data_len = offsetof(INTX_FILE,
device_end);
data = (INTX_FILE*)ntfs_malloc(
data_len);
if (!data) {
err = errno;
goto err_out;
}
data->major = cpu_to_le64(major(dev));
data->minor = cpu_to_le64(minor(dev));
if (type == S_IFBLK)
data->magic
= INTX_BLOCK_DEVICE;
if (type == S_IFCHR)
data->magic
= INTX_CHARACTER_DEVICE;
break;
}
data->major = cpu_to_le64(major(dev));
data->minor = cpu_to_le64(minor(dev));
if (type == S_IFBLK)
data->magic = INTX_BLOCK_DEVICE;
if (type == S_IFCHR)
data->magic = INTX_CHARACTER_DEVICE;
break;
case S_IFLNK:
data_len = sizeof(INTX_FILE_TYPES) +
switch (special_files) {
case NTFS_FILES_WSL :
data_len = 0;
data = (INTX_FILE*)NULL;
break;
default :
data_len = sizeof(INTX_FILE_TYPES) +
target_len * sizeof(ntfschar);
data = ntfs_malloc(data_len);
if (!data) {
err = errno;
goto err_out;
}
data->magic = INTX_SYMBOLIC_LINK;
memcpy(data->target, target,
data = (INTX_FILE*)ntfs_malloc(
data_len);
if (!data) {
err = errno;
goto err_out;
}
data->magic = INTX_SYMBOLIC_LINK;
memcpy(data->target, target,
target_len * sizeof(ntfschar));
break;
}
break;
case S_IFSOCK:
data = NULL;
data_len = 1;
if (special_files == NTFS_FILES_WSL)
data_len = 0;
else
data_len = 1;
break;
default: /* FIFO or regular file. */
data = NULL;
@ -1689,9 +1723,10 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
fn->file_name_type = FILE_NAME_POSIX;
if (S_ISDIR(type))
fn->file_attributes = FILE_ATTR_I30_INDEX_PRESENT;
if (!S_ISREG(type) && !S_ISDIR(type))
fn->file_attributes = FILE_ATTR_SYSTEM;
else
if (!S_ISREG(type) && !S_ISDIR(type)) {
if (special_files == NTFS_FILES_INTERIX)
fn->file_attributes = FILE_ATTR_SYSTEM;
} else
fn->file_attributes = le32_or(fn->file_attributes, le32_and(ni->flags, FILE_ATTR_COMPRESSED));
fn->file_attributes = le32_or(fn->file_attributes, FILE_ATTR_ARCHIVE);
fn->file_attributes = le32_or(fn->file_attributes, le32_and(ni->flags, FILE_ATTR_HIDDEN));
@ -1719,10 +1754,40 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
ntfs_log_perror("Failed to add entry to the index");
goto err_out;
}
rollback_dir = 1;
/* Set hard links count and directory flag. */
ni->mrec->link_count = const_cpu_to_le16(1);
if (S_ISDIR(type))
ni->mrec->flags = le16_or(ni->mrec->flags, MFT_RECORD_IS_DIRECTORY);
/* Add reparse data */
if (special_files == NTFS_FILES_WSL) {
switch (type) {
case S_IFLNK :
err = ntfs_reparse_set_wsl_symlink(ni, target,
target_len);
break;
case S_IFIFO :
case S_IFSOCK :
case S_IFCHR :
case S_IFBLK :
err = ntfs_reparse_set_wsl_not_symlink(ni,
type);
if (!err) {
err = ntfs_ea_set_wsl_not_symlink(ni,
type, dev);
if (err)
ntfs_remove_ntfs_reparse_data(ni);
}
break;
default :
err = 0;
break;
}
if (err) {
err = errno;
goto err_out;
}
}
ntfs_inode_mark_dirty(ni);
/* Done! */
free(fn);
@ -1732,6 +1797,9 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
err_out:
ntfs_log_trace("Failed.\n");
if (rollback_dir)
ntfs_index_remove(dir_ni, ni, fn, fn_len);
if (rollback_sd)
ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0);
@ -2792,3 +2860,82 @@ int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni)
}
return (res);
}
/*
* Increment the count of subdirectories
* (excluding entries with a short name)
*/
static int nlink_increment(void *nlink_ptr,
const ntfschar *name __attribute__((unused)),
const int len __attribute__((unused)),
const int type,
const s64 pos __attribute__((unused)),
const MFT_REF mref __attribute__((unused)),
const unsigned int dt_type)
{
if ((dt_type == NTFS_DT_DIR) && (type != FILE_NAME_DOS))
(*((int*)nlink_ptr))++;
return (0);
}
/*
* Compute the number of hard links according to Posix
* For a directory count the subdirectories whose name is not
* a short one, but count "." and ".."
* Otherwise count the names, excluding the short ones.
*
* if there is an error, a null count is returned.
*/
int ntfs_dir_link_cnt(ntfs_inode *ni)
{
ntfs_attr_search_ctx *actx;
FILE_NAME_ATTR *fn;
s64 pos;
int err = 0;
int nlink = 0;
if (!ni) {
ntfs_log_error("Invalid argument.\n");
errno = EINVAL;
goto err_out;
}
if (ni->nr_extents == -1)
ni = ni->base_ni;
if (!le16_andz(ni->mrec->flags, MFT_RECORD_IS_DIRECTORY)) {
/*
* Directory : scan the directory and count
* subdirectories whose name is not DOS-only.
* The directory names are ignored, but "." and ".."
* are taken into account.
*/
pos = 0;
err = ntfs_readdir(ni, &pos, &nlink, nlink_increment);
if (err)
nlink = 0;
} else {
/*
* Non-directory : search for FILE_NAME attributes,
* and count those which are not DOS-only ones.
*/
actx = ntfs_attr_get_search_ctx(ni, NULL);
if (!actx)
goto err_out;
while (!(err = ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0,
CASE_SENSITIVE, 0, NULL, 0, actx))) {
fn = (FILE_NAME_ATTR*)((u8*)actx->attr +
le16_to_cpu(actx->attr->value_offset));
if (fn->file_name_type != FILE_NAME_DOS)
nlink++;
}
if (err && (errno != ENOENT))
nlink = 0;
ntfs_attr_put_search_ctx(actx);
}
if (!nlink)
ntfs_log_perror("Failed to compute nlink of inode %lld",
(long long)ni->mft_no);
err_out :
return (nlink);
}

View File

@ -3,7 +3,7 @@
*
* This module is part of ntfs-3g library
*
* Copyright (c) 2014 Jean-Pierre Andre
* Copyright (c) 2014-2021 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
@ -43,6 +43,12 @@
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#endif
#ifdef MAJOR_IN_SYSMACROS
#include <sys/sysmacros.h>
#endif
#include "types.h"
#include "param.h"
@ -55,6 +61,10 @@
#include "logging.h"
#include "xattrs.h"
static const char lxdev[] = "$LXDEV";
static const char lxmod[] = "$LXMOD";
/*
* Create a needed attribute (EA or EA_INFORMATION)
*
@ -284,12 +294,11 @@ int ntfs_set_ntfs_ea(ntfs_inode *ni, const char *value, size_t size, int flags)
}
}
/*
* EA and REPARSE_POINT exclude each other
* see http://msdn.microsoft.com/en-us/library/windows/desktop/aa364404(v=vs.85).aspx
* Also return EINVAL if REPARSE_POINT is present.
* EA and REPARSE_POINT compatibility not checked any more,
* required by Windows 10, but having both may lead to
* problems with earlier versions.
*/
if (ok
&& !ntfs_attr_exist(ni, AT_REPARSE_POINT, AT_UNNAMED,0)) {
if (ok) {
ea_info.ea_length = cpu_to_le16(ea_packed);
ea_info.need_ea_count = cpu_to_le16(ea_count);
ea_info.ea_query_length = cpu_to_le32(nextoffs);
@ -393,3 +402,118 @@ int ntfs_remove_ntfs_ea(ntfs_inode *ni)
}
return (res ? -1 : 0);
}
/*
* Check for the presence of an EA "$LXDEV" (used by WSL)
* and return its value as a device address
*
* Returns zero if successful
* -1 if failed, with errno set
*/
int ntfs_ea_check_wsldev(ntfs_inode *ni, dev_t *rdevp)
{
const EA_ATTR *p_ea;
int bufsize;
char *buf;
int lth;
int res;
int offset;
int next;
BOOL found;
struct {
le32 major;
le32 minor;
} device;
res = -EOPNOTSUPP;
bufsize = 256; /* expected to be enough */
buf = (char*)malloc(bufsize);
if (buf) {
lth = ntfs_get_ntfs_ea(ni, buf, bufsize);
/* retry if short buf */
if (lth > bufsize) {
free(buf);
bufsize = lth;
buf = (char*)malloc(bufsize);
if (buf)
lth = ntfs_get_ntfs_ea(ni, buf, bufsize);
}
}
if (buf && (lth > 0) && (lth <= bufsize)) {
offset = 0;
found = FALSE;
do {
p_ea = (const EA_ATTR*)&buf[offset];
next = le32_to_cpu(p_ea->next_entry_offset);
found = ((next > (int)(sizeof(lxdev) + sizeof(device)))
&& (p_ea->name_length == (sizeof(lxdev) - 1))
&& le16_eq(p_ea->value_length,
const_cpu_to_le16(sizeof(device)))
&& !memcmp(p_ea->name, lxdev, sizeof(lxdev)));
if (!found)
offset += next;
} while (!found && (next > 0) && (offset < lth));
if (found) {
/* beware of alignment */
memcpy(&device, &p_ea->name[p_ea->name_length + 1],
sizeof(device));
*rdevp = makedev(le32_to_cpu(device.major),
le32_to_cpu(device.minor));
res = 0;
}
}
free(buf);
return (res);
}
int ntfs_ea_set_wsl_not_symlink(ntfs_inode *ni, mode_t type, dev_t dev)
{
le32 mode;
struct {
le32 major;
le32 minor;
} device;
struct EA_WSL {
struct EA_LXMOD { /* always inserted */
EA_ATTR base;
char name[sizeof(lxmod)];
char value[sizeof(mode)];
char stuff[3 & -(sizeof(lxmod) + sizeof(mode))];
} mod;
struct EA_LXDEV { /* char or block devices only */
EA_ATTR base;
char name[sizeof(lxdev)];
char value[sizeof(device)];
char stuff[3 & -(sizeof(lxdev) + sizeof(device))];
} dev;
} attr;
int len;
int res;
memset(&attr, 0, sizeof(attr));
mode = cpu_to_le32((u32)(type | 0644));
attr.mod.base.next_entry_offset
= const_cpu_to_le32(sizeof(attr.mod));
attr.mod.base.flags = 0;
attr.mod.base.name_length = sizeof(lxmod) - 1;
attr.mod.base.value_length = const_cpu_to_le16(sizeof(mode));
memcpy(attr.mod.name, lxmod, sizeof(lxmod));
memcpy(attr.mod.value, &mode, sizeof(mode));
len = sizeof(attr.mod);
if (S_ISCHR(type) || S_ISBLK(type)) {
device.major = cpu_to_le32(major(dev));
device.minor = cpu_to_le32(minor(dev));
attr.dev.base.next_entry_offset
= const_cpu_to_le32(sizeof(attr.dev));
attr.dev.base.flags = 0;
attr.dev.base.name_length = sizeof(lxdev) - 1;
attr.dev.base.value_length = const_cpu_to_le16(sizeof(device));
memcpy(attr.dev.name, lxdev, sizeof(lxdev));
memcpy(attr.dev.value, &device, sizeof(device));
len += sizeof(attr.dev);
}
res = ntfs_set_ntfs_ea(ni, (char*)&attr, len, 0);
return (res);
}

View File

@ -5,7 +5,7 @@
* Copyright (c) 2004-2005 Richard Russon
* Copyright (c) 2005-2006 Yura Pakhuchiy
* Copyright (c) 2005-2008 Szabolcs Szakacsits
* Copyright (c) 2007 Jean-Pierre Andre
* Copyright (c) 2007-2020 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
@ -1563,19 +1563,32 @@ static int ntfs_ih_takeout(ntfs_index_context *icx, INDEX_HEADER *ih,
INDEX_ENTRY *ie, INDEX_BLOCK *ib)
{
INDEX_ENTRY *ie_roam;
int freed_space;
BOOL full;
int ret = STATUS_ERROR;
ntfs_log_trace("Entering\n");
full = le32_eq(ih->index_length, ih->allocated_size);
ie_roam = ntfs_ie_dup_novcn(ie);
if (!ie_roam)
return STATUS_ERROR;
ntfs_ie_delete(ih, ie);
if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT)
if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) {
/*
* Recover the space which may have been freed
* while deleting an entry from root index
*/
freed_space = le32_to_cpu(ih->allocated_size)
- le32_to_cpu(ih->index_length);
if (full && (freed_space > 0) && !(freed_space & 7)) {
ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length));
/* do nothing if truncation fails */
}
ntfs_inode_mark_dirty(icx->actx->ntfs_ino);
else
} else
if (ntfs_ib_write(icx, ib))
goto out;

View File

@ -837,7 +837,7 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni)
if (!err)
err = errno;
ntfs_log_perror("Failed to open inode %lld with index",
(long long)le64_to_cpu(fn->parent_directory));
(long long)MREF_LE(fn->parent_directory));
continue;
}
ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4);
@ -1518,14 +1518,16 @@ int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size,
ntfs_attr_search_ctx *ctx;
STANDARD_INFORMATION *std_info;
FILE_NAME_ATTR *fn;
const u64 *times;
u64 times[4];
ntfs_time now;
int cnt;
int ret;
ret = -1;
if ((size >= 8) && !(flags & XATTR_CREATE)) {
times = (const u64*)value;
/* Copy, to avoid alignment issue encountered on ARM */
memcpy(times, value,
(size < sizeof(times) ? size : sizeof(times)));
now = ntfs_current_time();
/* update the standard information attribute */
ctx = ntfs_attr_get_search_ctx(ni, NULL);

View File

@ -3,7 +3,7 @@
*
* This module is part of ntfs-3g library
*
* Copyright (c) 2014-2015 Jean-Pierre Andre
* Copyright (c) 2014-2019 Jean-Pierre Andre
* Copyright (c) 2014 Red Hat, Inc.
*
* This program/include file is free software; you can redistribute it and/or
@ -141,7 +141,8 @@ static int fstrim_limits(ntfs_volume *vol,
u64 *discard_max_bytes)
{
struct stat statbuf;
char path1[80], path2[80];
char path1[40]; /* holds "/sys/dev/block/%d:%d" */
char path2[40 + sizeof(path1)]; /* less than 40 bytes more than path1 */
int ret;
/* Stat the backing device. Caller has ensured it is a block device. */
@ -225,6 +226,24 @@ not_found:
return 0;
}
static inline LCN align_up(ntfs_volume *vol, LCN lcn, u64 granularity)
{
u64 aligned;
aligned = (lcn << vol->cluster_size_bits) + granularity - 1;
aligned -= aligned % granularity;
return (aligned >> vol->cluster_size_bits);
}
static inline u64 align_down(ntfs_volume *vol, u64 count, u64 granularity)
{
u64 aligned;
aligned = count << vol->cluster_size_bits;
aligned -= aligned % granularity;
return (aligned >> vol->cluster_size_bits);
}
#define FSTRIM_BUFSIZ 4096
/* Trim the filesystem.
@ -255,11 +274,11 @@ static int fstrim(ntfs_volume *vol, void *data, u64 *trimmed)
* XXX We could fix these limitations in future.
*/
if (start != 0 || len != (uint64_t)-1) {
ntfs_log_debug("fstrim: setting start or length is not supported\n");
ntfs_log_error("fstrim: setting start or length is not supported\n");
return -EINVAL;
}
if (minlen > vol->cluster_size) {
ntfs_log_debug("fstrim: minlen > cluster size is not supported\n");
ntfs_log_error("fstrim: minlen > cluster size is not supported\n");
return -EINVAL;
}
@ -269,7 +288,7 @@ static int fstrim(ntfs_volume *vol, void *data, u64 *trimmed)
* different.
*/
if (!NDevBlock(vol->dev)) {
ntfs_log_debug("fstrim: not supported for non-block-device\n");
ntfs_log_error("fstrim: not supported for non-block-device\n");
return -EOPNOTSUPP;
}
@ -278,15 +297,12 @@ static int fstrim(ntfs_volume *vol, void *data, u64 *trimmed)
if (ret)
return ret;
if (discard_alignment != 0) {
ntfs_log_debug("fstrim: backing device is not aligned for discards\n");
return -EOPNOTSUPP;
}
if (discard_granularity > vol->cluster_size) {
ntfs_log_debug("fstrim: discard granularity of backing device is larger than cluster size\n");
ntfs_log_error("fstrim: backing device is not aligned for discards\n");
return -EOPNOTSUPP;
}
if (discard_max_bytes == 0) {
ntfs_log_debug("fstrim: backing device does not support discard (discard_max_bytes == 0)\n");
ntfs_log_error("fstrim: backing device does not support discard (discard_max_bytes == 0)\n");
return -EOPNOTSUPP;
}
@ -323,11 +339,14 @@ static int fstrim(ntfs_volume *vol, void *data, u64 *trimmed)
}
/* Trim the clusters in large as possible blocks, but
* not larger than discard_max_bytes.
* not larger than discard_max_bytes, and compatible
* with the supported trim granularity.
*/
for (start_lcn = start_buf; start_lcn < end_buf; ++start_lcn) {
if (!ntfs_bit_get(buf, start_lcn-start_buf)) {
LCN end_lcn;
LCN aligned_lcn;
u64 aligned_count;
/* Cluster 'start_lcn' is not in use,
* find end of this run.
@ -338,14 +357,25 @@ static int fstrim(ntfs_volume *vol, void *data, u64 *trimmed)
< discard_max_bytes &&
!ntfs_bit_get(buf, end_lcn-start_buf))
end_lcn++;
aligned_lcn = align_up(vol, start_lcn,
discard_granularity);
if (aligned_lcn >= end_lcn)
aligned_count = 0;
else {
aligned_count =
align_down(vol,
end_lcn - aligned_lcn,
discard_granularity);
}
if (aligned_count) {
ret = fstrim_clusters(vol,
aligned_lcn, aligned_count);
if (ret)
goto free_out;
ret = fstrim_clusters(vol,
start_lcn, end_lcn-start_lcn);
if (ret)
goto free_out;
*trimmed += (end_lcn - start_lcn)
*trimmed += aligned_count
<< vol->cluster_size_bits;
}
start_lcn = end_lcn-1;
}
}
@ -359,7 +389,8 @@ free_out:
#endif /* FITRIM && BLKDISCARD */
int ntfs_ioctl(ntfs_inode *ni, int cmd, void *arg __attribute__((unused)),
int ntfs_ioctl(ntfs_inode *ni, unsigned long cmd,
void *arg __attribute__((unused)),
unsigned int flags __attribute__((unused)), void *data)
{
int ret = 0;

View File

@ -5,7 +5,7 @@
* Copyright (c) 2004-2005 Richard Russon
* Copyright (c) 2004-2008 Szabolcs Szakacsits
* Copyright (c) 2005 Yura Pakhuchiy
* Copyright (c) 2014-2015 Jean-Pierre Andre
* Copyright (c) 2014-2018 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
@ -1389,16 +1389,27 @@ ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol, BOOL mft_data)
*/
if (ext_ni) {
/*
* Make sure record 15 is a base extent and has
* no extents.
* Also make sure it has no name : a base inode with
* no extents and no name cannot be in use.
* Otherwise apply standard procedure.
* Make sure record 15 is a base extent and it has
* no name. A base inode with no name cannot be in use.
* The test based on base_mft_record fails for
* extents of MFT, so we need a special check.
* If already used, apply standard procedure.
*/
if (le64_cmpz(ext_ni->mrec->base_mft_record)
&& !ext_ni->nr_extents)
&& le16_cmpz(ext_ni->mrec->link_count))
forced_mft_data = TRUE;
ntfs_inode_close(ext_ni);
/* Double-check, in case it is used for MFT */
if (forced_mft_data && base_ni->nr_extents) {
int i;
for (i=0; i<base_ni->nr_extents; i++) {
if (base_ni->extent_nis[i]
&& (base_ni->extent_nis[i]->mft_no
== FILE_mft_data))
forced_mft_data = FALSE;
}
}
}
}
if (forced_mft_data)

View File

@ -59,3 +59,19 @@ void *ntfs_malloc(size_t size)
ntfs_log_perror("Failed to malloc %lld bytes", (long long)size);
return p;
}
void *ntfs_realloc(void *ptr, size_t size)
{
void *p;
p = realloc(ptr, size);
if (!p)
ntfs_log_perror("Failed to realloc %lld bytes",
(long long)size);
return p;
}
void ntfs_free(void *p)
{
free(p);
}

View File

@ -3,7 +3,7 @@
*
* This module is part of ntfs-3g library
*
* Copyright (c) 2009 Jean-Pierre Andre
* Copyright (c) 2009-2019 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
@ -63,9 +63,13 @@
* significant byte first, and the six fields be compared individually
* for ordering. RFC 4122 does not define the internal representation.
*
* Windows apparently stores the first three fields in little endian
* order, and the last two fields in big endian order.
*
* Here we always copy disk images with no endianness change,
* and, for indexing, GUIDs are compared as if they were a sequence
* of four unsigned 32 bit integers.
* of four little-endian unsigned 32 bit integers (as Windows
* does it that way.)
*
* --------------------- begin from RFC 4122 ----------------------
* Consider each field of the UUID to be an unsigned integer as shown
@ -329,7 +333,7 @@ static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo,
na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
if (na) {
memset(&old_attr, 0, sizeof(OBJECT_ID_ATTR));
/* remove the existing index entry */
oldsize = remove_object_id_index(na,xo,&old_attr);
if (oldsize < 0)
@ -349,10 +353,12 @@ static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo,
res = -1;
}
}
/* write index part if provided */
/* overwrite index data with new value */
memcpy(&old_attr, value,
(size < sizeof(OBJECT_ID_ATTR)
? size : sizeof(OBJECT_ID_ATTR)));
if (!res
&& ((size < sizeof(OBJECT_ID_ATTR))
|| set_object_id_index(ni,xo,value))) {
&& set_object_id_index(ni,xo,&old_attr)) {
/*
* If cannot index, try to remove the object
* id and log the error. There will be an
@ -500,9 +506,11 @@ int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size)
/*
* Set the object id from an extended attribute
*
* If the size is 64, the attribute and index are set.
* else if the size is not less than 16 only the attribute is set.
* The object id index is set accordingly.
* The first 16 bytes are the new object id, they can be followed
* by the birth volume id, the birth object id and the domain id.
* If they are not present, their previous value is kept.
* Only the object id is stored into the attribute, all the fields
* are stored into the index.
*
* Returns 0, or -1 if there is a problem
*/
@ -519,10 +527,12 @@ int ntfs_set_ntfs_object_id(ntfs_inode *ni,
if (ni && value && (size >= sizeof(GUID))) {
xo = open_object_id_index(ni->vol);
if (xo) {
/* make sure the GUID was not used somewhere */
/* make sure the GUID was not used elsewhere */
memcpy(&key.object_id, value, sizeof(GUID));
if (ntfs_index_lookup(&key,
sizeof(OBJECT_ID_INDEX_KEY), xo)) {
if ((ntfs_index_lookup(&key,
sizeof(OBJECT_ID_INDEX_KEY), xo))
|| (MREF_LE(((struct OBJECT_ID_INDEX*)xo->entry)
->data.file_id) == ni->mft_no)) {
ntfs_index_ctx_reinit(xo);
res = add_object_id(ni, flags);
if (!res) {

View File

@ -45,8 +45,8 @@ canonicalize_dm_name(const char *ptname, char *canonical)
{
FILE *f;
size_t sz;
char path[MAPPERNAMELTH + 24];
char name[MAPPERNAMELTH + 16];
char path[sizeof(name) + 16];
char *res = NULL;
snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname);

View File

@ -3,7 +3,7 @@
*
* This module is part of ntfs-3g library
*
* Copyright (c) 2008-2016 Jean-Pierre Andre
* Copyright (c) 2008-2021 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,7 +37,10 @@
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_SYS_SYSMACROS_H
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#endif
#ifdef MAJOR_IN_SYSMACROS
#include <sys/sysmacros.h>
#endif
@ -56,6 +59,7 @@
#include "misc.h"
#include "reparse.h"
#include "xattrs.h"
#include "ea.h"
struct MOUNT_POINT_REPARSE_DATA { /* reparse data for junctions */
le16 subst_name_offset;
@ -74,6 +78,11 @@ struct SYMLINK_REPARSE_DATA { /* reparse data for symlinks */
char path_buffer[0]; /* above data assume this is char array */
} ;
struct WSL_LINK_REPARSE_DATA {
le32 type;
char link[0];
} ;
struct REPARSE_INDEX { /* index entry in $Extend/$Reparse */
INDEX_ENTRY_HEADER header;
REPARSE_INDEX_KEY key;
@ -415,6 +424,35 @@ static int ntfs_drive_letter(ntfs_volume *vol, ntfschar letter)
return (ret);
}
/*
* Check whether reparse data describes a valid wsl special file
* which is either a socket, a fifo, or a character or block device
*
* Return zero if valid, otherwise returns a negative error code
*/
int ntfs_reparse_check_wsl(ntfs_inode *ni, const REPARSE_POINT *reparse)
{
int res;
res = -EOPNOTSUPP;
/* switch (reparse->reparse_tag) { */
if (le32_eq(reparse->reparse_tag, IO_REPARSE_TAG_AF_UNIX) ||
le32_eq(reparse->reparse_tag, IO_REPARSE_TAG_LX_FIFO) ||
le32_eq(reparse->reparse_tag, IO_REPARSE_TAG_LX_CHR) ||
le32_eq(reparse->reparse_tag, IO_REPARSE_TAG_LX_BLK)) {
if (le16_cmpz(reparse->reparse_data_length)
&& !le32_andz(ni->flags, FILE_ATTRIBUTE_RECALL_ON_OPEN))
res = 0;
}
else {
}
/* } */
if (res)
errno = EOPNOTSUPP;
return (res);
}
/*
* Do some sanity checks on reparse data
*
@ -435,6 +473,7 @@ static BOOL valid_reparse_data(ntfs_inode *ni,
unsigned int lth;
const struct MOUNT_POINT_REPARSE_DATA *mount_point_data;
const struct SYMLINK_REPARSE_DATA *symlink_data;
const struct WSL_LINK_REPARSE_DATA *wsl_reparse_data;
ok = ni && reparse_attr
&& (size >= sizeof(REPARSE_POINT))
@ -477,7 +516,24 @@ static BOOL valid_reparse_data(ntfs_inode *ni,
+ offs + lth)) > size)
ok = FALSE;
break;
} else {
} else if (le32_eq(reparse_attr->reparse_tag, IO_REPARSE_TAG_LX_SYMLINK)) {
wsl_reparse_data = (const struct WSL_LINK_REPARSE_DATA*)
reparse_attr->reparse_data;
if ((le16_to_cpu(reparse_attr->reparse_data_length)
<= sizeof(wsl_reparse_data->type))
|| (!le32_eq(wsl_reparse_data->type, const_cpu_to_le32(2))))
ok = FALSE;
break;
} else if (le32_eq(reparse_attr->reparse_tag, IO_REPARSE_TAG_AF_UNIX) ||
le32_eq(reparse_attr->reparse_tag, IO_REPARSE_TAG_LX_FIFO) ||
le32_eq(reparse_attr->reparse_tag, IO_REPARSE_TAG_LX_CHR) ||
le32_eq(reparse_attr->reparse_tag, IO_REPARSE_TAG_LX_BLK)) {
if (!le16_cmpz(reparse_attr->reparse_data_length)
|| le32_andz(ni->flags, FILE_ATTRIBUTE_RECALL_ON_OPEN))
ok = FALSE;
break;
}
else {
break;
} } while(0);
}
@ -604,8 +660,9 @@ static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction,
* or NULL if there were some problem, as described by errno
*/
static char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction,
int count, const char *mnt_point, BOOL isdir)
char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction, int count,
const char *mnt_point __attribute__((unused)),
BOOL isdir)
{
char *target;
char *fulltarget;
@ -651,10 +708,11 @@ static char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction,
target = search_absolute(vol, &junction[3],
count - 3, isdir);
if (target) {
fulltarget = (char*)ntfs_malloc(strlen(mnt_point)
fulltarget = (char*)ntfs_malloc(
strlen(vol->abs_mnt_point)
+ strlen(target) + 2);
if (fulltarget) {
strcpy(fulltarget,mnt_point);
strcpy(fulltarget,vol->abs_mnt_point);
strcat(fulltarget,"/");
strcat(fulltarget,target);
}
@ -679,10 +737,11 @@ static char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction,
&& (target[0] >= 'a')
&& (target[0] <= 'z'))
target[0] += 'A' - 'a';
fulltarget = (char*)ntfs_malloc(strlen(mnt_point)
fulltarget = (char*)ntfs_malloc(
strlen(vol->abs_mnt_point)
+ sizeof(mappingdir) + strlen(target) + 1);
if (fulltarget) {
strcpy(fulltarget,mnt_point);
strcpy(fulltarget,vol->abs_mnt_point);
strcat(fulltarget,"/");
strcat(fulltarget,mappingdir);
strcat(fulltarget,target);
@ -734,6 +793,7 @@ char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point)
REPARSE_POINT *reparse_attr;
struct MOUNT_POINT_REPARSE_DATA *mount_point_data;
struct SYMLINK_REPARSE_DATA *symlink_data;
struct WSL_LINK_REPARSE_DATA *wsl_link_data;
enum { FULL_TARGET, ABS_TARGET, REL_TARGET } kind;
ntfschar *p;
BOOL bad;
@ -815,7 +875,22 @@ char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point)
}
break;
}
} else {
}
else if (le32_eq(reparse_attr->reparse_tag, IO_REPARSE_TAG_LX_SYMLINK)) {
wsl_link_data = (struct WSL_LINK_REPARSE_DATA*)
reparse_attr->reparse_data;
if (le32_eq(wsl_link_data->type, const_cpu_to_le32(2))) {
lth = le16_to_cpu(
reparse_attr->reparse_data_length)
- sizeof(wsl_link_data->type);
target = (char*)ntfs_malloc(lth + 1);
if (target) {
memcpy(target, wsl_link_data->link,
lth);
target[lth] = 0;
bad = FALSE;
}
}
}
free(reparse_attr);
}
@ -844,7 +919,8 @@ BOOL ntfs_possible_symlink(ntfs_inode *ni)
if (reparse_attr && attr_size) {
/* switch (reparse_attr->reparse_tag) { */
if (le32_eq(reparse_attr->reparse_tag, IO_REPARSE_TAG_MOUNT_POINT) ||
le32_eq(reparse_attr->reparse_tag, IO_REPARSE_TAG_SYMLINK)) {
le32_eq(reparse_attr->reparse_tag, IO_REPARSE_TAG_SYMLINK) ||
le32_eq(reparse_attr->reparse_tag, IO_REPARSE_TAG_LX_SYMLINK)) {
possible = TRUE;
} else {
}
@ -1121,11 +1197,12 @@ int ntfs_set_ntfs_reparse_data(ntfs_inode *ni,
ntfs_index_context *xr;
res = 0;
/* reparse data is not compatible with EA */
if (ni
&& !ntfs_attr_exist(ni, AT_EA_INFORMATION, AT_UNNAMED, 0)
&& !ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0)
&& valid_reparse_data(ni, (const REPARSE_POINT*)value, size)) {
/*
* reparse data compatibily with EA is not checked
* any more, it is required by Windows 10, but may
* lead to problems with earlier versions.
*/
if (ni && valid_reparse_data(ni, (const REPARSE_POINT*)value, size)) {
xr = open_reparse_index(ni->vol);
if (xr) {
if (!ntfs_attr_exist(ni,AT_REPARSE_POINT,
@ -1254,6 +1331,92 @@ int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni)
return (res ? -1 : 0);
}
/*
* Set reparse data for a WSL type symlink
*/
int ntfs_reparse_set_wsl_symlink(ntfs_inode *ni,
const ntfschar *target, int target_len)
{
int res;
int len;
int reparse_len;
char *utarget;
REPARSE_POINT *reparse;
struct WSL_LINK_REPARSE_DATA *data;
res = -1;
utarget = (char*)NULL;
len = ntfs_ucstombs(target, target_len, &utarget, 0);
if (len > 0) {
reparse_len = sizeof(REPARSE_POINT) + sizeof(data->type) + len;
reparse = (REPARSE_POINT*)malloc(reparse_len);
if (reparse) {
data = (struct WSL_LINK_REPARSE_DATA*)
reparse->reparse_data;
reparse->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK;
reparse->reparse_data_length
= cpu_to_le16(sizeof(data->type) + len);
reparse->reserved = const_cpu_to_le16(0);
data->type = const_cpu_to_le32(2);
memcpy(data->link, utarget, len);
res = ntfs_set_ntfs_reparse_data(ni,
(char*)reparse, reparse_len, 0);
free(reparse);
}
}
free(utarget);
return (res);
}
/*
* Set reparse data for a WSL special file other than a symlink
* (socket, fifo, character or block device)
*/
int ntfs_reparse_set_wsl_not_symlink(ntfs_inode *ni, mode_t mode)
{
int res;
int len;
int reparse_len;
le32 reparse_tag;
REPARSE_POINT *reparse;
res = -1;
len = 0;
switch (mode) {
case S_IFSOCK :
reparse_tag = IO_REPARSE_TAG_AF_UNIX;
break;
case S_IFIFO :
reparse_tag = IO_REPARSE_TAG_LX_FIFO;
break;
case S_IFCHR :
reparse_tag = IO_REPARSE_TAG_LX_CHR;
break;
case S_IFBLK :
reparse_tag = IO_REPARSE_TAG_LX_BLK;
break;
default :
len = -1;
errno = EOPNOTSUPP;
break;
}
if (len >= 0) {
reparse_len = sizeof(REPARSE_POINT) + len;
reparse = (REPARSE_POINT*)malloc(reparse_len);
if (reparse) {
reparse->reparse_tag = reparse_tag;
reparse->reparse_data_length = cpu_to_le16(len);
reparse->reserved = const_cpu_to_le16(0);
res = ntfs_set_ntfs_reparse_data(ni,
(char*)reparse, reparse_len, 0);
free(reparse);
}
}
return (res);
}
/*
* Get the reparse data into a buffer
@ -1276,7 +1439,7 @@ REPARSE_POINT *ntfs_get_reparse_point(ntfs_inode *ni)
&& !valid_reparse_data(ni, reparse_attr, attr_size)) {
free(reparse_attr);
reparse_attr = (REPARSE_POINT*)NULL;
errno = ENOENT;
errno = EINVAL;
}
} else
errno = EINVAL;

View File

@ -3048,13 +3048,13 @@ BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni)
free(oldattr);
}
}
allowed = FALSE;
if (gotowner) {
/* TODO : use CAP_FOWNER process capability */
if (!processuid || (processuid == uid))
allowed = TRUE;
else
errno = EPERM;
if (gotowner
&& (!processuid || (processuid == uid)))
allowed = TRUE;
else {
allowed = FALSE;
errno = EPERM;
}
}
return (allowed);

View File

@ -53,6 +53,9 @@
#ifdef HAVE_LINUX_FD_H
#include <linux/fd.h>
#endif
#ifdef HAVE_LINUX_FS_H
#include <linux/fs.h>
#endif
#include "types.h"
#include "mst.h"
@ -140,8 +143,26 @@ static int ntfs_device_unix_io_open(struct ntfs_device *dev, int flags)
*(int*)dev->d_private = open(dev->d_name, flags);
if (*(int*)dev->d_private == -1) {
err = errno;
/* if permission error and rw, retry read-only */
if ((err == EACCES) && ((flags & O_RDWR) == O_RDWR))
err = EROFS;
goto err_out;
}
#ifdef HAVE_LINUX_FS_H
/* Check whether the device was forced read-only */
if (NDevBlock(dev) && ((flags & O_RDWR) == O_RDWR)) {
int r;
int state;
r = ioctl(DEV_FD(dev), BLKROGET, &state);
if (!r && state) {
err = EROFS;
if (close(DEV_FD(dev)))
err = errno;
goto err_out;
}
}
#endif
if ((flags & O_RDWR) != O_RDWR)
NDevSetReadOnly(dev);
@ -348,8 +369,8 @@ static int ntfs_device_unix_io_stat(struct ntfs_device *dev, struct stat *buf)
*
* Returns:
*/
static int ntfs_device_unix_io_ioctl(struct ntfs_device *dev, int request,
void *argp)
static int ntfs_device_unix_io_ioctl(struct ntfs_device *dev,
unsigned long request, void *argp)
{
return ioctl(DEV_FD(dev), request, argp);
}

View File

@ -529,7 +529,7 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev,
dev->d_name);
goto error_exit;
} else {
ntfs_log_info("Can only open '%s' as read-only\n",
ntfs_log_info("Error opening '%s' read-write\n",
dev->d_name);
NVolSetReadOnly(vol);
}
@ -959,7 +959,8 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
vol->mftmirr_size = l;
}
ntfs_log_debug("Comparing $MFTMirr to $MFT...\n");
for (i = 0; i < vol->mftmirr_size; ++i) {
/* Windows 10 does not update the full $MFTMirr any more */
for (i = 0; (i < vol->mftmirr_size) && (i < FILE_first_user); ++i) {
MFT_RECORD *mrec, *mrec2;
const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile",
"$Volume", "$AttrDef", "root directory", "$Bitmap",

View File

@ -1847,14 +1847,14 @@ static __inline__ int ntfs_win32_blksszget(struct ntfs_device *dev,int *argp)
return -1;
}
static int ntfs_device_win32_ioctl(struct ntfs_device *dev, int request,
void *argp)
static int ntfs_device_win32_ioctl(struct ntfs_device *dev,
unsigned long request, void *argp)
{
#if defined(BLKGETSIZE) | defined(BLKGETSIZE64)
win32_fd *fd = (win32_fd *)dev->d_private;
#endif
ntfs_log_trace("win32_ioctl(%d) called.\n", request);
ntfs_log_trace("win32_ioctl(0x%lx) called.\n", request);
switch (request) {
#if defined(BLKGETSIZE)
case BLKGETSIZE:
@ -1897,7 +1897,7 @@ static int ntfs_device_win32_ioctl(struct ntfs_device *dev, int request,
return 0;
#endif
default:
ntfs_log_debug("unimplemented ioctl %d.\n", request);
ntfs_log_debug("unimplemented ioctl 0x%lx.\n", request);
errno = EOPNOTSUPP;
return -1;
}

View File

@ -470,6 +470,15 @@ void ntfs_xattr_free_mapping(struct XATTRMAPPING *mapping)
#endif /* XATTR_MAPPINGS */
/*
* Get an NTFS attribute into an extended attribute
*
* Returns the non-negative size of attribute if successful,
* or negative, with errno set, when fails
* Note : the size is returned even if no buffer is provided
* for returning the attribute, or if it is zero-sized.
*/
int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx,
enum SYSTEMXATTRS attr,
ntfs_inode *ni, ntfs_inode *dir_ni,
@ -483,12 +492,6 @@ int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx,
#endif
#endif
/*
* the returned value is the needed
* size. If it is too small, no copy
* is done, and the caller has to
* issue a new call with correct size.
*/
switch (attr) {
case XATTR_NTFS_ACL :
res = ntfs_get_ntfs_acl(scx, ni, value, size);
@ -596,6 +599,13 @@ int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx,
return (res);
}
/*
* Set an NTFS attribute from an extended attribute
*
* Returns 0 if successful,
* non-zero, with errno set, when fails
*/
int ntfs_xattr_system_setxattr(struct SECURITY_CONTEXT *scx,
enum SYSTEMXATTRS attr,
ntfs_inode *ni, ntfs_inode *dir_ni,
@ -669,8 +679,10 @@ int ntfs_xattr_system_setxattr(struct SECURITY_CONTEXT *scx,
case XATTR_NTFS_EFSINFO :
if (ni->vol->efs_raw)
res = ntfs_set_efs_info(ni, value, size, flags);
else
else {
errno = EPERM;
res = -EPERM;
}
break;
case XATTR_NTFS_REPARSE_DATA :
res = ntfs_set_ntfs_reparse_data(ni, value, size, flags);
@ -683,8 +695,10 @@ int ntfs_xattr_system_setxattr(struct SECURITY_CONTEXT *scx,
/* warning : this closes both inodes */
res = ntfs_set_ntfs_dos_name(ni, dir_ni, value,
size, flags);
else
else {
errno = EINVAL;
res = -errno;
}
break;
case XATTR_NTFS_TIMES:
res = ntfs_inode_set_times(ni, value, size, flags);

View File

@ -165,8 +165,8 @@ extras: libs $(EXTRA_PROGRAMS)
if ENABLE_MOUNT_HELPER
install-exec-hook:
$(INSTALL) -d $(DESTDIR)/sbin
$(LN_S) -f $(sbindir)/mkntfs $(DESTDIR)/sbin/mkfs.ntfs
$(INSTALL) -d $(DESTDIR)/$(sbindir)
$(LN_S) -f $(sbindir)/mkntfs $(DESTDIR)$(sbindir)/mkfs.ntfs
install-data-hook:
$(INSTALL) -d $(DESTDIR)$(man8dir)

View File

@ -132,7 +132,7 @@ actual writing to the device.
.TP
\fB\-c\fR, \fB\-\-cluster\-size\fR BYTES
Specify the size of clusters in bytes. Valid cluster size values are powers of
two, with at least 256, and at most 65536 bytes per cluster. If omitted,
two, with at least 256, and at most 2097152 bytes (2MB) per cluster. If omitted,
.B mkntfs
uses 4096 bytes as the default cluster size.
.sp

View File

@ -6,7 +6,7 @@
* Copyright (c) 2002-2006 Szabolcs Szakacsits
* Copyright (c) 2005 Erik Sornes
* Copyright (c) 2007 Yura Pakhuchiy
* Copyright (c) 2010-2014 Jean-Pierre Andre
* Copyright (c) 2010-2018 Jean-Pierre Andre
*
* This utility will create an NTFS 1.2 or 3.1 volume on a user
* specified (block) device.
@ -119,6 +119,7 @@
# endif
#endif
#include "param.h"
#include "security.h"
#include "types.h"
#include "attrib.h"
@ -287,7 +288,7 @@ static void mkntfs_version(void)
ntfs_log_info("Copyright (c) 2002-2006 Szabolcs Szakacsits\n");
ntfs_log_info("Copyright (c) 2005 Erik Sornes\n");
ntfs_log_info("Copyright (c) 2007 Yura Pakhuchiy\n");
ntfs_log_info("Copyright (c) 2010-2014 Jean-Pierre Andre\n");
ntfs_log_info("Copyright (c) 2010-2018 Jean-Pierre Andre\n");
ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
}
@ -669,7 +670,7 @@ static int mkntfs_parse_options(int argc, char *argv[], struct mkntfs_options *o
break;
case 'L':
if (!opts2->label) {
opts2->label = argv[optind-1];
opts2->label = optarg;
} else {
ntfs_log_error("You may only specify the label "
"once.\n");
@ -3719,11 +3720,11 @@ static BOOL mkntfs_override_vol_params(ntfs_volume *vol)
/*
* For huge volumes, grow the cluster size until the number of
* clusters fits into 32 bits or the cluster size exceeds the
* maximum limit of 64kiB.
* maximum limit of NTFS_MAX_CLUSTER_SIZE.
*/
while (volume_size >> (ffs(vol->cluster_size) - 1 + 32)) {
vol->cluster_size <<= 1;
if (vol->cluster_size > 65535) {
if (vol->cluster_size >= NTFS_MAX_CLUSTER_SIZE) {
ntfs_log_error("Device is too large to hold an "
"NTFS volume (maximum size is "
"256TiB).\n");
@ -3744,15 +3745,18 @@ static BOOL mkntfs_override_vol_params(ntfs_volume *vol)
"to, or larger than, the sector size.\n");
return FALSE;
}
if (vol->cluster_size > 128 * (u32)opts.sector_size) {
/* Before Windows 10 Creators, the limit was 128 */
if (vol->cluster_size > 4096 * (u32)opts.sector_size) {
ntfs_log_error("The cluster size is invalid. It cannot be "
"more that 128 times the size of the sector "
"more that 4096 times the size of the sector "
"size.\n");
return FALSE;
}
if (vol->cluster_size > 65536) {
if (vol->cluster_size > NTFS_MAX_CLUSTER_SIZE) {
ntfs_log_error("The cluster size is invalid. The maximum "
"cluster size is 65536 bytes (64kiB).\n");
"cluster size is %lu bytes (%lukiB).\n",
(unsigned long)NTFS_MAX_CLUSTER_SIZE,
(unsigned long)(NTFS_MAX_CLUSTER_SIZE >> 10));
return FALSE;
}
vol->cluster_size_bits = ffs(vol->cluster_size) - 1;
@ -4387,6 +4391,7 @@ static BOOL mkntfs_create_root_structures(void)
u8 *sd;
FILE_ATTR_FLAGS extend_flags;
VOLUME_FLAGS volume_flags = const_cpu_to_le16(0);
int sectors_per_cluster;
int nr_sysfiles;
int buf_sds_first_size;
char *buf_sds;
@ -4639,8 +4644,11 @@ static BOOL mkntfs_create_root_structures(void)
* already inserted, so no need to worry about these things.
*/
bs->bpb.bytes_per_sector = cpu_to_le16(opts.sector_size);
bs->bpb.sectors_per_cluster = (u8)(g_vol->cluster_size /
opts.sector_size);
sectors_per_cluster = g_vol->cluster_size / opts.sector_size;
if (sectors_per_cluster > 128)
bs->bpb.sectors_per_cluster = 257 - ffs(sectors_per_cluster);
else
bs->bpb.sectors_per_cluster = sectors_per_cluster;
bs->bpb.media_type = 0xf8; /* hard disk */
bs->bpb.sectors_per_track = cpu_to_le16(opts.sectors_per_track);
ntfs_log_debug("sectors per track = %ld (0x%lx)\n",

View File

@ -236,6 +236,7 @@ static int parse_options(int argc, char **argv)
optarg);
usage();
}
break;
case 'q':
opts.quiet++;

View File

@ -3,7 +3,7 @@
*
* Copyright (c) 2003-2006 Szabolcs Szakacsits
* Copyright (c) 2004-2006 Anton Altaparmakov
* Copyright (c) 2010-2016 Jean-Pierre Andre
* Copyright (c) 2010-2018 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.
@ -71,6 +71,7 @@
*/
#define NTFS_DO_NOT_CHECK_ENDIANS
#include "param.h"
#include "debug.h"
#include "types.h"
#include "support.h"
@ -270,7 +271,6 @@ static int compare_bitmaps(struct bitmap *a, BOOL copy);
#define LAST_METADATA_INODE 11
#define NTFS_MAX_CLUSTER_SIZE 65536
#define NTFS_SECTOR_SIZE 512
#define rounded_up_division(a, b) (((a) + (b - 1)) / (b))
@ -393,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-2016 Jean-Pierre Andre\n\n");
"Copyright (c) 2010-2018 Jean-Pierre Andre\n\n");
fprintf(stderr, "%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home);
exit(0);
}
@ -465,6 +465,7 @@ static void parse_options(int argc, char **argv)
break;
case 'O':
opt.overwrite++;
/* FALLTHRU */
case 'o':
if (opt.output)
usage(1);
@ -756,7 +757,7 @@ static void read_rescue(void *fd, char *buff, u32 csize, u32 bytes_per_sector,
static void copy_cluster(int rescue, u64 rescue_lcn, u64 lcn)
{
char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */
char *buff;
/* vol is NULL if opt.restore_image is set */
s32 csize = le32_to_cpu(image_hdr.cluster_size);
BOOL backup_bootsector;
@ -773,6 +774,9 @@ static void copy_cluster(int rescue, u64 rescue_lcn, u64 lcn)
}
rescue_pos = (off_t)(rescue_lcn * csize);
buff = (char*)ntfs_malloc(csize);
if (!buff)
err_exit("Not enough memory");
/* possible partial cluster holding the backup boot sector */
backup_bootsector = (lcn + 1)*csize >= full_device_size;
@ -858,6 +862,7 @@ static void copy_cluster(int rescue, u64 rescue_lcn, u64 lcn)
perr_printf("Write failed");
#endif
}
free(buff);
}
static s64 lseek_out(int fd, s64 pos, int mode)
@ -995,7 +1000,11 @@ static void write_empty_clusters(s32 csize, s64 count,
struct progress_bar *progress, u64 *p_counter)
{
s64 i;
char buff[NTFS_MAX_CLUSTER_SIZE];
char *buff;
buff = (char*)ntfs_malloc(csize);
if (!buff)
err_exit("Not enough memory");
memset(buff, 0, csize);
@ -1004,6 +1013,7 @@ static void write_empty_clusters(s32 csize, s64 count,
perr_exit("write_all");
progress_update(progress, ++(*p_counter));
}
free(buff);
}
static void restore_image(void)
@ -1492,11 +1502,12 @@ static void write_set(char *buff, u32 csize, s64 *current_lcn,
static void copy_wipe_mft(ntfs_walk_clusters_ctx *image, runlist *rl)
{
char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */
char *buff;
void *fd;
s64 mft_no;
u32 mft_record_size;
u32 csize;
u32 buff_size;
u32 bytes_per_sector;
u32 records_per_set;
u32 clusters_per_set;
@ -1514,14 +1525,21 @@ static void copy_wipe_mft(ntfs_walk_clusters_ctx *image, runlist *rl)
/*
* Depending on the sizes, there may be several records
* per cluster, or several clusters per record.
* Anyway, records are read and rescued by full clusters.
*/
if (csize >= mft_record_size) {
records_per_set = csize/mft_record_size;
clusters_per_set = 1;
buff_size = csize;
} else {
clusters_per_set = mft_record_size/csize;
records_per_set = 1;
buff_size = mft_record_size;
}
buff = (char*)ntfs_malloc(buff_size);
if (!buff)
err_exit("Not enough memory");
mft_no = 0;
ri = rj = 0;
wi = wj = 0;
@ -1554,6 +1572,7 @@ static void copy_wipe_mft(ntfs_walk_clusters_ctx *image, runlist *rl)
}
}
image->current_lcn = current_lcn;
free(buff);
}
/*
@ -1566,10 +1585,11 @@ static void copy_wipe_mft(ntfs_walk_clusters_ctx *image, runlist *rl)
static void copy_wipe_i30(ntfs_walk_clusters_ctx *image, runlist *rl)
{
char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */
char *buff;
void *fd;
u32 indx_record_size;
u32 csize;
u32 buff_size;
u32 bytes_per_sector;
u32 records_per_set;
u32 clusters_per_set;
@ -1586,15 +1606,22 @@ static void copy_wipe_i30(ntfs_walk_clusters_ctx *image, runlist *rl)
/*
* Depending on the sizes, there may be several records
* per cluster, or several clusters per record.
* Anyway, records are read and rescued by full clusters.
*/
indx_record_size = image->ni->vol->indx_record_size;
if (csize >= indx_record_size) {
records_per_set = csize/indx_record_size;
clusters_per_set = 1;
buff_size = csize;
} else {
clusters_per_set = indx_record_size/csize;
records_per_set = 1;
buff_size = indx_record_size;
}
buff = (char*)ntfs_malloc(buff_size);
if (!buff)
err_exit("Not enough memory");
ri = rj = 0;
wi = wj = 0;
if (rl[ri].length)
@ -1627,6 +1654,7 @@ static void copy_wipe_i30(ntfs_walk_clusters_ctx *image, runlist *rl)
}
}
image->current_lcn = current_lcn;
free(buff);
}
static void dump_clusters(ntfs_walk_clusters_ctx *image, runlist *rl)
@ -1711,10 +1739,17 @@ static void walk_runs(struct ntfs_walk_cluster *walk)
for (j = 0; j < lcn_length; j++) {
u64 k = (u64)lcn + j;
if (ntfs_bit_get_and_set(lcn_bitmap.bm, k, 1))
err_exit("Cluster %llu referenced twice!\n"
"You didn't shutdown your Windows "
"properly?\n", (unsigned long long)k);
if (ntfs_bit_get_and_set(lcn_bitmap.bm, k, 1)) {
if (opt.ignore_fs_check)
Printf("Cluster %llu is referenced"
" twice!\n",
(unsigned long long)k);
else
err_exit("Cluster %llu referenced"
" twice!\nYou didn't shutdown"
" your Windows properly?\n",
(unsigned long long)k);
}
}
if (!opt.metadata_image)

View File

@ -69,7 +69,7 @@ This option is not yet implemented.
.TP
\fB\-q\fR, \fB\-\-quiet\fR
Reduce the amount of output to a minimum. Naturally, it doesn't make sense to
combine this option with
combine this option with \fB\-\-verbose\fR
.TP
\fB\-s\fR, \fB\-\-sector\fR RANGE
Any files whose data is in this range of sectors will be displayed.

View File

@ -59,6 +59,10 @@ Show a list of options with a brief description of each one.
\fB\-q\fR, \fB\-\-quiet\fR
Suppress some debug/warning/error messages.
.TP
\fB\-t\fR, \fB\-\-timestamp\fR
Copy the modification time of source_file to destination. This is
not compatible with \fB\-\-attr\-name\fR and \fB\-\-attribute\fR.
.TP
\fB\-V\fR, \fB\-\-version\fR
Show the version number, copyright and license
.BR ntfscp .

View File

@ -4,7 +4,7 @@
* Copyright (c) 2004-2007 Yura Pakhuchiy
* Copyright (c) 2005 Anton Altaparmakov
* Copyright (c) 2006 Hil Liao
* Copyright (c) 2014 Jean-Pierre Andre
* Copyright (c) 2014-2019 Jean-Pierre Andre
*
* This utility will copy file to an NTFS volume.
*
@ -58,6 +58,7 @@
#include "debug.h"
/* #include "version.h" */
#include "logging.h"
#include "ntfstime.h"
#include "misc.h"
struct options {
@ -69,6 +70,7 @@ struct options {
int quiet; /* Less output */
int verbose; /* Extra output */
int minfragments; /* Do minimal fragmentation */
int timestamp; /* Copy the modification time */
int noaction; /* Do not write to disk */
ATTR_TYPES attribute; /* Write to this attribute. */
int inode; /* Treat dest_file as inode number. */
@ -129,6 +131,7 @@ static void usage(void)
" -N, --attr-name NAME Write to attribute with this name\n"
" -n, --no-action Do not write to disk\n"
" -q, --quiet Less output\n"
" -t, --timestamp Copy the modification time\n"
" -V, --version Version information\n"
" -v, --verbose More output\n\n",
EXEC_NAME);
@ -146,7 +149,7 @@ static void usage(void)
*/
static int parse_options(int argc, char **argv)
{
static const char *sopt = "-a:ifh?mN:no:qVv";
static const char *sopt = "-a:ifh?mN:no:qtVv";
static const struct option lopt[] = {
{ "attribute", required_argument, NULL, 'a' },
{ "inode", no_argument, NULL, 'i' },
@ -156,6 +159,7 @@ static int parse_options(int argc, char **argv)
{ "attr-name", required_argument, NULL, 'N' },
{ "no-action", no_argument, NULL, 'n' },
{ "quiet", no_argument, NULL, 'q' },
{ "timestamp", no_argument, NULL, 't' },
{ "version", no_argument, NULL, 'V' },
{ "verbose", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0 }
@ -175,6 +179,7 @@ static int parse_options(int argc, char **argv)
opts.attr_name = NULL;
opts.inode = 0;
opts.attribute = AT_DATA;
opts.timestamp = 0;
opterr = 0; /* We'll handle the errors, thank you. */
@ -235,6 +240,9 @@ static int parse_options(int argc, char **argv)
opts.quiet++;
ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
break;
case 't':
opts.timestamp++;
break;
case 'V':
ver++;
break;
@ -284,6 +292,12 @@ static int parse_options(int argc, char **argv)
"at the same time.\n");
err++;
}
if (opts.timestamp
&& (opts.attr_name || !le32_eq(opts.attribute, AT_DATA))) {
ntfs_log_error("Setting --timestamp is only possible"
" with unname data attribute.\n");
err++;
}
}
if (ver)
@ -822,6 +836,7 @@ static ntfs_inode *ntfs_new_file(ntfs_inode *dir_ni,
int main(int argc, char *argv[])
{
FILE *in;
struct stat st;
ntfs_volume *vol;
ntfs_inode *out;
ntfs_attr *na;
@ -1136,6 +1151,16 @@ int main(int argc, char *argv[])
free(buf);
close_attr:
ntfs_attr_close(na);
if (opts.timestamp) {
if (!fstat(fileno(in),&st)) {
s64 change_time = st.st_mtime*10000000LL
+ NTFS_TIME_OFFSET;
out->last_data_change_time = cpu_to_le64(change_time);
ntfs_inode_update_times(out, 0);
} else {
ntfs_log_error("Failed to get the time stamp.\n");
}
}
close_dst:
while (ntfs_inode_close(out) && !opts.noaction) {
if (errno != EBUSY) {

View File

@ -567,6 +567,7 @@ check_again:
switch (err) {
case GNUTLS_BAG_PKCS8_KEY:
flags = GNUTLS_PKCS_PLAIN;
/* FALLTHRU */
case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey);
if (err < 0) {
@ -1491,12 +1492,15 @@ static int ntfs_feed_encrypt(ntfs_inode *inode, ntfs_fek *fek)
default :
*b++ = val;
val >>= 8;
/* FALLTHRU */
case 3 :
*b++ = val;
val >>= 8;
/* FALLTHRU */
case 2 :
*b++ = val;
val >>= 8;
/* FALLTHRU */
case 1 :
*b++ = val;
val >>= 8;

View File

@ -1,7 +1,7 @@
/**
* ntfsfallocate
*
* Copyright (c) 2013-2014 Jean-Pierre Andre
* Copyright (c) 2013-2020 Jean-Pierre Andre
*
* This utility will allocate clusters to a specified attribute belonging
* to a specified file or directory, to a specified length.
@ -214,10 +214,15 @@ static s64 option_value(const char *arg)
count = 0;
switch (*s++) {
case 'E' : count++;
/* FALLTHRU */
case 'P' : count++;
/* FALLTHRU */
case 'T' : count++;
/* FALLTHRU */
case 'G' : count++;
/* FALLTHRU */
case 'M' : count++;
/* FALLTHRU */
case 'K' : count++;
switch (*s++) {
case 'i' :
@ -770,10 +775,14 @@ static int ntfs_fallocate(ntfs_inode *ni, s64 alloc_offs, s64 alloc_len)
/* Get and save the initial allocations */
allocated_size = na->allocated_size;
data_size = ni->data_size;
err = ntfs_attr_map_whole_runlist(na);
if (na->rl)
err = ntfs_attr_map_whole_runlist(na);
if (!err) {
oldrl = ntfs_save_rl(na->rl);
if (oldrl) {
if (na->rl)
oldrl = ntfs_save_rl(na->rl);
else
oldrl = (runlist_element*)NULL;
if (!na->rl || oldrl) {
err = ntfs_full_allocation(na, ctx,
alloc_offs, alloc_len);
if (err) {

View File

@ -506,6 +506,11 @@ static int fix_mftmirr(ntfs_volume *vol)
ntfs_log_info("Comparing $MFTMirr to $MFT... ");
done = FALSE;
/*
* Since 2017, Windows 10 does not mirror to full $MFTMirr when
* using big clusters, and some records may be found different.
* Nevertheless chkdsk.exe mirrors it fully, so we do similarly.
*/
for (i = 0; i < vol->mftmirr_size; ++i) {
MFT_RECORD *mrec, *mrec2;
const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile",

View File

@ -8,7 +8,7 @@
* Copyright (c) 2004-2005 Yuval Fledel
* Copyright (c) 2004-2007 Yura Pakhuchiy
* Copyright (c) 2005 Cristian Klein
* Copyright (c) 2011-2015 Jean-Pierre Andre
* Copyright (c) 2011-2020 Jean-Pierre Andre
*
* This utility will dump a file's attributes.
*
@ -119,7 +119,7 @@ static void version(void)
printf(" 2003 Leonard Norrgård\n");
printf(" 2004-2005 Yuval Fledel\n");
printf(" 2004-2007 Yura Pakhuchiy\n");
printf(" 2011-2014 Jean-Pierre Andre\n");
printf(" 2011-2018 Jean-Pierre Andre\n");
printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
}
@ -331,7 +331,7 @@ static char *ntfsinfo_time_to_str(const sle64 sle_ntfs_clock)
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" } ;
static const char *wdays[]
= { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } ;
static char str[30];
static char str[50];
long long stamp;
u32 days;
u32 seconds;
@ -371,7 +371,7 @@ static char *ntfsinfo_time_to_str(const sle64 sle_ntfs_clock)
mon = days/31 + 1;
days -= 31*(mon - 1) - 1;
}
sprintf(str,"%3s %3s %2u %02u:%02u:%02u %4u UTC\n",
snprintf(str, sizeof(str), "%3s %3s %2u %02u:%02u:%02u %4u UTC\n",
wdays[wday],
months[mon-1],(unsigned int)days,
(unsigned int)(seconds/3600),
@ -411,20 +411,49 @@ static char *ntfs_attr_get_name_mbs(ATTR_RECORD *attr)
static const char *reparse_type_name(le32 tag)
{
const char *name;
le32 seltag;
seltag = le32_and(tag, IO_REPARSE_PLUGIN_SELECT);
do {
if (le32_eq(tag, IO_REPARSE_TAG_MOUNT_POINT)) {
if (le32_eq(seltag, IO_REPARSE_TAG_MOUNT_POINT)) {
name = " (mount point)";
break;
} else if (le32_eq(tag, IO_REPARSE_TAG_SYMLINK)) {
} else if (le32_eq(seltag, IO_REPARSE_TAG_SYMLINK)) {
name = " (symlink)";
break;
} else if (le32_eq(tag, IO_REPARSE_TAG_WOF)) {
} else if (le32_eq(seltag, IO_REPARSE_TAG_WOF)) {
name = " (Wof compressed)";
break;
} else if (le32_eq(tag, IO_REPARSE_TAG_DEDUP)) {
} else if (le32_eq(seltag, IO_REPARSE_TAG_DEDUP)) {
name = " (deduplicated)";
break;
} else if (le32_eq(seltag, IO_REPARSE_TAG_WCI)) {
name = " (Windows container)";
break;
} else if (le32_eq(seltag, IO_REPARSE_TAG_CLOUD)) {
name = " (Cloud)";
break;
} else if (le32_eq(seltag, IO_REPARSE_TAG_NFS)) {
name = " (NFS symlink)";
break;
} else if (le32_eq(seltag, IO_REPARSE_TAG_LX_SYMLINK)) {
name = " (Linux symlink)";
break;
} else if (le32_eq(seltag, IO_REPARSE_TAG_LX_FIFO)) {
name = " (Linux fifo)";
break;
} else if (le32_eq(seltag, IO_REPARSE_TAG_LX_CHR)) {
name = " (Linux character device)";
break;
} else if (le32_eq(seltag, IO_REPARSE_TAG_LX_BLK)) {
name = " (Linux block device)";
break;
} else if (le32_eq(seltag, IO_REPARSE_TAG_AF_UNIX)) {
name = " (Unix socket)";
break;
} else if (le32_eq(seltag, IO_REPARSE_TAG_APPEXECLINK)) {
name = " (Exec link)";
break;
} else {
name = "";
break;
@ -605,6 +634,10 @@ static void ntfs_dump_flags(const char *indent, ATTR_TYPES type, le32 flags)
printf(" VIEW_INDEX");
flags = le32_and(flags, le32_not(FILE_ATTR_VIEW_INDEX_PRESENT));
}
if (!le32_andz(flags, FILE_ATTRIBUTE_RECALL_ON_OPEN)) {
printf(" RECALL_ON_OPEN");
flags = le32_and(flags, le32_not(FILE_ATTRIBUTE_RECALL_ON_OPEN));
}
if (!le32_cmpz(flags))
printf(" UNKNOWN: 0x%08x", (unsigned int)le32_to_cpu(flags));
/* Print all the flags in hex. */

View File

@ -31,6 +31,9 @@ available for free and come with full source code.
.BR ntfscp (8)
\- Copy a file to an NTFS volume.
.PP
.BR ntfsfallocate (8)
\- Preallocate space to a file on an NTFS volume
.PP
.BR ntfsfix (8)
\- Check and fix some common errors, clear the LogFile and make Windows
perform a thorough check next time it boots.
@ -47,6 +50,9 @@ perform a thorough check next time it boots.
.BR ntfsresize (8)
\- Resize NTFS without losing data.
.PP
.BR ntfsrecover (8)
\- Recover updates committed by Windows on an NTFS volume.
.PP
.BR ntfstruncate (8)
\- Truncate a file on an NTFS volume.
.PP

View File

@ -1,7 +1,7 @@
/*
* Process log data from an NTFS partition
*
* Copyright (c) 2012-2016 Jean-Pierre Andre
* Copyright (c) 2012-2017 Jean-Pierre Andre
*
* This program examines the Windows log file of an ntfs partition
* and plays the committed transactions in order to restore the
@ -43,6 +43,7 @@
*/
#define BASEBLKS 4 /* number of special blocks (always shown) */
#define BASEBLKS2 34 /* number of special blocks when version >= 2.0 */
#define RSTBLKS 2 /* number of restart blocks */
#define BUFFERCNT 64 /* number of block buffers - a power of 2 */
#define NTFSBLKLTH 512 /* usa block size */
@ -122,6 +123,7 @@ u32 clustersz = 0;
int clusterbits;
u32 blocksz;
int blockbits;
int log_major;
u16 bytespersect;
u64 mftlcn;
u32 mftrecsz;
@ -136,6 +138,7 @@ u64 committed_lsn;
u64 synced_lsn;
u64 latest_lsn;
u64 restart_lsn;
u64 offset_mask; /* block number in an lsn */
unsigned long firstblk; /* first block to dump (option -r) */
unsigned long lastblk; /* last block to dump (option -r) */
u64 firstlcn; /* first block to dump (option -c) */
@ -164,6 +167,7 @@ unsigned int playedactions; // change the name
unsigned int redocount;
unsigned int undocount;
struct BUFFER *buffer_table[BASEBLKS + BUFFERCNT];
unsigned int redirect[BASEBLKS2];
static const le16 SDS[4] = {
const_cpu_to_le16('$'), const_cpu_to_le16('S'),
@ -319,18 +323,22 @@ static const struct BUFFER *read_buffer(CONTEXT *ctx, unsigned int num)
{
struct BUFFER *buffer;
BOOL got;
int k;
unsigned int rnum;
/*
* The first four blocks are stored apart, to make
* sure pages 2 and 3 and the page which is logically
* before them can be accessed at the same time.
* (Only two blocks are stored apart if version >= 2.0)
* Also, block 0 is smaller because it has to be read
* before the block size is known.
* Note : the last block is supposed to have an odd
* number, and cannot be overwritten by block 4 which
* follows logically.
* number, and cannot be overwritten by block 4 (or 34
* if version >= 2.0) which follows logically.
*/
if (num < BASEBLKS)
if ((num < RSTBLKS)
|| ((log_major < 2) && (num < BASEBLKS)))
buffer = buffer_table[num + BUFFERCNT];
else
buffer = buffer_table[num & (BUFFERCNT - 1)];
@ -342,20 +350,27 @@ static const struct BUFFER *read_buffer(CONTEXT *ctx, unsigned int num)
buffer = (struct BUFFER*)
malloc(sizeof(struct BUFFER) + blocksz);
buffer->size = blocksz;
buffer->num = num + 1; /* forced to being read */
buffer->rnum = num + 1; /* forced to being read */
buffer->safe = FALSE;
if (num < BASEBLKS)
buffer_table[num + BUFFERCNT] = buffer;
else
buffer_table[num & (BUFFERCNT - 1)] = buffer;
}
if (buffer && (buffer->num != num)) {
rnum = num;
if (log_major >= 2) {
for (k=RSTBLKS; k<BASEBLKS2; k++)
if (redirect[k] == num)
rnum = k;
}
if (buffer && (buffer->rnum != rnum)) {
buffer->num = num;
buffer->rnum = rnum;
if (ctx->vol)
got = (ntfs_attr_pread(log_na,(u64)num << blockbits,
got = (ntfs_attr_pread(log_na,(u64)rnum << blockbits,
blocksz, buffer->block.data) == blocksz);
else
got = !fseek(ctx->file, loclogblk(ctx, num), 0)
got = !fseek(ctx->file, loclogblk(ctx, rnum), 0)
&& (fread(buffer->block.data, blocksz,
1, ctx->file) == 1);
if (got) {
@ -365,7 +380,7 @@ static const struct BUFFER *read_buffer(CONTEXT *ctx, unsigned int num)
buffer->safe = !replaceusa(buffer, blocksz);
} else {
buffer->safe = FALSE;
fprintf(stderr,"** Could not read block %d\n", num);
fprintf(stderr,"** Could not read block %d\n", rnum);
}
}
return (buffer && buffer->safe ? buffer : (const struct BUFFER*)NULL);
@ -1096,22 +1111,30 @@ static const struct BUFFER *findprevious(CONTEXT *ctx, const struct BUFFER *buf)
skipped = 0;
do {
prevmiddle = FALSE;
if (prevblk > BASEBLKS)
if (prevblk > (log_major < 2 ? BASEBLKS : BASEBLKS2))
prevblk--;
else
if (prevblk == BASEBLKS)
if (prevblk == (log_major < 2 ? BASEBLKS : BASEBLKS2))
prevblk = (logfilesz >> blockbits) - 1;
else {
rph = &buf->block.record;
prevblk = (sle64_to_cpu(rph->copy.file_offset)
if (log_major < 2)
prevblk = (sle64_to_cpu(
rph->copy.file_offset)
>> blockbits) - 1;
else
prevblk = (sle64_to_cpu(
rph->copy.last_lsn)
& offset_mask)
>> (blockbits - 3);
/*
* If an initial block leads to block 4, it
* can mean the last block or no previous
* block at all. Using the last block is safer,
* its lsn will indicate whether it is stale.
*/
if (prevblk < BASEBLKS)
if (prevblk
< (log_major < 2 ? BASEBLKS : BASEBLKS2))
prevblk = (logfilesz >> blockbits) - 1;
}
/* No previous block if the log only consists of block 2 or 3 */
@ -2706,7 +2729,7 @@ static void showrest(const RESTART_PAGE_HEADER *rest)
(long)le32_to_cpu(rest->system_page_size));
printf("log_page_size %08lx\n",
(long)le32_to_cpu(rest->log_page_size));
printf("restart_area_offset %04x\n",
printf("restart_area_offset %04x\n",
(int)le16_to_cpu(rest->restart_area_offset));
printf("minor_vers %d\n",
(int)sle16_to_cpu(rest->minor_ver));
@ -2876,6 +2899,8 @@ static BOOL dorest(CONTEXT *ctx, unsigned long blk,
}
}
restart_lsn = synced_lsn;
offset_mask = ((u64)1 << (64 - le32_to_cpu(restart.seq_number_bits)))
- (1 << (blockbits - 3));
return (dirty);
}
@ -2895,9 +2920,13 @@ static const struct BUFFER *read_restart(CONTEXT *ctx)
{
const struct BUFFER *buf;
BOOL bad;
int blk;
int major, minor;
bad = FALSE;
for (blk=0; blk<BASEBLKS2; blk++)
redirect[blk] = 0;
log_major = 0; /* needed for reading into a buffer */
if (ctx->vol) {
RESTART_PAGE_HEADER *rph;
@ -2961,6 +2990,7 @@ static const struct BUFFER *read_restart(CONTEXT *ctx)
major, minor);
bad = TRUE;
}
log_major = major;
if (bad) {
buf = (const struct BUFFER*)NULL;
}
@ -3343,7 +3373,8 @@ static TRISTATE backoverlap(CONTEXT *ctx, int blk,
mblk = blk + 1;
while (total < size) {
if (mblk >= (logfilesz >> blockbits))
mblk = BASEBLKS;
mblk = (log_major < 2 ? BASEBLKS
: BASEBLKS2);
more = size - total;
if (more > nextspace)
more = nextspace;
@ -3427,9 +3458,15 @@ static TRISTATE backward_rcrd(CONTEXT *ctx, u32 blk, int skipped,
if (optv) {
if (optv >= 2)
hexdump(data,blocksz);
printf("* RCRD in block %ld 0x%lx (addr 0x%llx)\n",
(long)blk,(long)blk,
(long long)loclogblk(ctx, blk));
if (buf->rnum != blk)
printf("* RCRD for block %ld 0x%lx"
" in block %ld (addr 0x%llx)\n",
(long)blk,(long)blk,(long)buf->rnum,
(long long)loclogblk(ctx, blk));
else
printf("* RCRD in block %ld 0x%lx (addr 0x%llx)\n",
(long)blk,(long)blk,
(long long)loclogblk(ctx, blk));
} else {
if (optt)
printf("block %ld\n",(long)blk);
@ -3551,9 +3588,15 @@ static int walkback(CONTEXT *ctx, const struct BUFFER *buf, u32 blk,
u32 stopblk;
TRISTATE state;
if (optv)
printf("\n* block %d at 0x%llx\n",(int)blk,
if (optv) {
if ((log_major >= 2) && (buf->rnum != blk))
printf("\n* block %d for block %d at 0x%llx\n",
(int)buf->rnum,(int)blk,
(long long)loclogblk(ctx, buf->rnum));
else
printf("\n* block %d at 0x%llx\n",(int)blk,
(long long)loclogblk(ctx, blk));
}
ctx->firstaction = (struct ACTION_RECORD*)NULL;
ctx->lastaction = (struct ACTION_RECORD*)NULL;
nextbuf = (const struct BUFFER*)NULL;
@ -3576,7 +3619,9 @@ static int walkback(CONTEXT *ctx, const struct BUFFER *buf, u32 blk,
skipped = blk - prevblk - 1;
else
skipped = blk - prevblk - 1
+ (logfilesz >> blockbits) - BASEBLKS;
+ (logfilesz >> blockbits)
- (log_major < 2 ? BASEBLKS
: BASEBLKS2);
magic = prevbuf->block.record.magic;
/* switch (magic) { */
if (le32_eq(magic, magic_RCRD)) {
@ -3599,9 +3644,18 @@ static int walkback(CONTEXT *ctx, const struct BUFFER *buf, u32 blk,
(long long)loclogblk(ctx, blk),
(long)prevblk);
else
printf("\n* block %ld at 0x%llx\n",
(long)blk,
(long long)loclogblk(ctx, blk));
if ((log_major >= 2)
&& (buf->rnum != blk))
printf("\n* block %ld for block %ld at 0x%llx\n",
(long)buf->rnum,
(long)blk,
(long long)loclogblk(
ctx,buf->rnum));
else
printf("\n* block %ld at 0x%llx\n",
(long)blk,
(long long)loclogblk(
ctx, blk));
}
state = backward_rcrd(ctx, blk, skipped,
buf, prevbuf, nextbuf);
@ -3632,6 +3686,155 @@ static int walkback(CONTEXT *ctx, const struct BUFFER *buf, u32 blk,
return (state == T_ERR ? 1 : 0);
}
/*
* Find the latest log block
*
* Usually, the latest block is either block 2 or 3 which act as
* temporary block before being copied to target location.
* However under some unknown condition the block are written
* immediately to target location, and we have to scan for the
* latest one.
* Currently this is not checked for logfile version 2.x which
* use a different layout of temporary blocks.
*/
static const struct BUFFER *find_latest_block(CONTEXT *ctx, u32 baseblk,
const struct BUFFER *basebuf)
{
le64 offset;
leLSN prevlsn;
leLSN curlsn;
u32 curblk;
u32 prevblk;
const struct BUFFER *prevbuf;
const struct BUFFER *curbuf;
offset = basebuf->block.record.copy.file_offset;
curbuf = (const struct BUFFER*)NULL;
curlsn = const_cpu_to_le64(0);
prevblk = 0;
curblk = baseblk;
do {
if (curblk < BASEBLKS) {
prevbuf = basebuf;
prevlsn = basebuf->block.record.last_end_lsn;
prevblk = baseblk;
curblk = le64_to_cpu(offset) >> blockbits;
} else {
if (optv)
printf("block %d is more recent than block %d\n",
(int)curblk, (int)prevblk);
prevbuf = curbuf;
prevlsn = curlsn;
prevblk = curblk;
curblk++;
if (curblk >= (logfilesz >> blockbits))
curblk = (log_major < 2 ? BASEBLKS : BASEBLKS2);
}
curbuf = read_buffer(ctx, curblk);
if (curbuf && (curbuf->block.record.magic == magic_RCRD)) {
curlsn = curbuf->block.record.copy.last_lsn;
}
} while (curbuf
&& (curbuf->block.record.magic == magic_RCRD)
&& (le64_to_cpu(curlsn) > le64_to_cpu(prevlsn)));
if (optv)
printf("Block %d is the latest one\n",(int)prevblk);
return (prevbuf);
}
/*
* Determine the sequencing of blocks (when version >= 2.0)
*
* Blocks 2..17 and 18..33 are temporary blocks being filled until
* they are copied to their target locations, so there are three
* possible location for recent blocks.
*
* Returns the latest target block number
*/
static int block_sequence(CONTEXT *ctx)
{
const struct BUFFER *buf;
int blk;
int k;
int target_blk;
int latest_blk;
s64 final_lsn;
s64 last_lsn;
s64 last_lsn12;
s64 last_lsn1, last_lsn2;
final_lsn = 0;
for (blk=RSTBLKS; 2*blk<(RSTBLKS+BASEBLKS2); blk++) {
/* First temporary block */
last_lsn1 = 0;
buf = read_buffer(ctx, blk);
if (buf && (buf->block.record.magic == magic_RCRD)) {
last_lsn1 = le64_to_cpu(
buf->block.record.copy.last_lsn);
if (!final_lsn
|| ((s64)(last_lsn1 - final_lsn) > 0))
final_lsn = last_lsn1;
}
/* Second temporary block */
buf = read_buffer(ctx, blk + (BASEBLKS2 - RSTBLKS)/2);
last_lsn2 = 0;
if (buf && (buf->block.record.magic == magic_RCRD)) {
last_lsn2 = le64_to_cpu(
buf->block.record.copy.last_lsn);
if (!final_lsn
|| ((s64)(last_lsn2 - final_lsn) > 0))
final_lsn = last_lsn2;
}
/* the latest last_lsn defines the target block */
last_lsn12 = 0;
latest_blk = 0;
if (last_lsn1 || last_lsn2) {
if (!last_lsn2
|| ((s64)(last_lsn1 - last_lsn2) > 0)) {
last_lsn12 = last_lsn1;
latest_blk = blk;
}
if (!last_lsn1
|| ((s64)(last_lsn1 - last_lsn2) <= 0)) {
last_lsn12 = last_lsn2;
latest_blk = blk + (BASEBLKS2 - RSTBLKS)/2;
}
}
last_lsn = 0;
target_blk = 0;
if (last_lsn12) {
target_blk = (last_lsn12 & offset_mask)
>> (blockbits - 3);
buf = read_buffer(ctx, target_blk);
if (buf && (buf->block.record.magic == magic_RCRD)) {
last_lsn = le64_to_cpu(
buf->block.record.copy.last_lsn);
if (!final_lsn
|| ((s64)(last_lsn - final_lsn) > 0))
final_lsn = last_lsn;
}
}
/* redirect to the latest block */
if (latest_blk
&& (!last_lsn || ((s64)(last_lsn - last_lsn12) < 0)))
redirect[latest_blk] = target_blk;
}
if (optv) {
printf("\n Blocks redirected :\n");
for (k=RSTBLKS; k<BASEBLKS2; k++)
if (redirect[k])
printf("* block %d to block %d\n",
(int)redirect[k],(int)k);
}
latest_lsn = final_lsn;
blk = (final_lsn & offset_mask) >> (blockbits - 3);
if (optv > 1)
printf("final lsn %llx in blk %d\n",(long long)final_lsn,blk);
return (blk);
}
static int walk(CONTEXT *ctx)
{
const struct BUFFER *buf;
@ -3644,6 +3847,7 @@ static int walk(CONTEXT *ctx)
u32 blk;
u32 nextblk;
u32 prevblk;
u32 finalblk;
int err;
u16 blkheadsz;
u16 pos;
@ -3657,6 +3861,7 @@ static int walk(CONTEXT *ctx)
}
done = FALSE;
dirty = TRUE;
finalblk = 0;
err = 0;
blk = 0;
pos = 0;
@ -3675,7 +3880,8 @@ static int walk(CONTEXT *ctx)
while (!done) {
/* next block is needed to process the current one */
if ((nextblk >= (logfilesz >> blockbits)) && (optr || optf))
nextbuf = read_buffer(ctx, BASEBLKS);
nextbuf = read_buffer(ctx,
(log_major < 2 ? BASEBLKS : BASEBLKS2));
else
nextbuf = read_buffer(ctx,nextblk);
if (nextbuf) {
@ -3741,17 +3947,30 @@ static int walk(CONTEXT *ctx)
}
blk = nextblk;
nextblk++;
if (!optr && (log_major >= 2) && (nextblk == RSTBLKS)) {
finalblk = block_sequence(ctx);
if (!finalblk) {
done = TRUE;
err = 1;
}
}
if (optr) { /* Only selected range */
if ((nextblk == BASEBLKS) && (nextblk < firstblk))
u32 endblk;
endblk = (log_major < 2 ? BASEBLKS : RSTBLKS);
if ((nextblk == endblk) && (nextblk < firstblk))
nextblk = firstblk;
if ((blk >= BASEBLKS) && (blk > lastblk))
if ((blk >= endblk) && (blk > lastblk))
done = TRUE;
} else
if (optf) { /* Full log, forward */
if (blk*blocksz >= logfilesz)
done = TRUE;
} else
if (optb || optp || optu || opts) {
if (optb || optp || optu || opts
|| (log_major >= 2)) {
/* Restart blocks only (2 blocks) */
if (blk >= RSTBLKS)
done = TRUE;
@ -3782,16 +4001,18 @@ static int walk(CONTEXT *ctx)
}
if (optv && opts && !dirty)
printf("* Volume is clean, nothing to do\n");
if (optb || optp || optu
|| (opts && dirty)) {
if (log_major >= 2)
blk = finalblk;
if (!err
&& (optb || optp || optu || (opts && dirty))) {
playedactions = 0;
ctx->firstaction = (struct ACTION_RECORD*)NULL;
ctx->lastaction = (struct ACTION_RECORD*)NULL;
buf = nextbuf;
nextbuf = read_buffer(ctx, blk+1);
startbuf = best_start(buf,nextbuf);
if (startbuf) {
if (startbuf == nextbuf) {
if (log_major < 2) {
buf = nextbuf;
nextbuf = read_buffer(ctx, blk+1);
startbuf = best_start(buf,nextbuf);
if (startbuf && (startbuf == nextbuf)) {
/* nextbuf is better, show blk */
if (optv && buf) {
printf("* Ignored block %d at 0x%llx\n",
@ -3818,6 +4039,17 @@ static int walk(CONTEXT *ctx)
&nextbuf->block.record);
}
}
if (startbuf && opts) {
buf = startbuf = find_latest_block(ctx,
blk, startbuf);
latest_lsn = le64_to_cpu(
buf->block.record.last_end_lsn);
}
} else {
buf = startbuf = read_buffer(ctx, blk);
nextbuf = (const struct BUFFER*)NULL;
}
if (startbuf) {
/* The latest buf may be more recent than restart */
rph = &buf->block.record;
if ((s64)(sle64_to_cpu(rph->last_end_lsn)
@ -3874,7 +4106,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-2016 Jean-Pierre Andre\n");
printf("Copyright (c) 2012-2017 Jean-Pierre Andre\n");
printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
}

View File

@ -74,10 +74,12 @@ enum ACTIONS {
struct BUFFER {
unsigned int num;
unsigned int rnum;
unsigned int size;
unsigned int headsz;
BOOL safe;
union {
u64 alignment;
RESTART_PAGE_HEADER restart;
RECORD_PAGE_HEADER record;
char data[1];

View File

@ -243,10 +243,9 @@ Display help and exit.
.SH EXIT CODES
The exit code is 0 on success, non\-zero otherwise.
.SH KNOWN ISSUES
No reliability problem is known. If you need
help please try the Ntfsresize FAQ first (see below) and if you
don't find your answer then send your question, comment or bug report to
the development team:
No reliability problem is known.
If you find a bug please send an email describing the problem to the
development team at:
.br
.nh
ntfs\-3g\-devel@lists.sf.net
@ -308,14 +307,6 @@ package and is available from:
.nh
http://www.tuxera.com/community/
.hy
.sp
.B Ntfsresize
related news, example of usage, troubleshooting, statically linked binary and
FAQ (frequently asked questions) are maintained at:
.br
.nh
http://mlf.linux.rulez.org/mlf/ezaz/ntfsresize.html
.hy
.SH SEE ALSO
.BR fdisk (8),
.BR cfdisk (8),

View File

@ -5,7 +5,7 @@
* Copyright (c) 2002-2005 Anton Altaparmakov
* Copyright (c) 2002-2003 Richard Russon
* Copyright (c) 2007 Yura Pakhuchiy
* Copyright (c) 2011-2016 Jean-Pierre Andre
* Copyright (c) 2011-2018 Jean-Pierre Andre
*
* This utility will resize an NTFS volume without data loss.
*
@ -59,6 +59,7 @@
#include <fcntl.h>
#endif
#include "param.h"
#include "debug.h"
#include "types.h"
#include "support.h"
@ -137,6 +138,8 @@ static const char *many_bad_sectors_msg =
"* other reason. We suggest to get a replacement disk as soon as possible. *\n"
"***************************************************************************\n";
enum mirror_source { MIRR_OLD, MIRR_NEWMFT, MIRR_MFT };
static struct {
int verbose;
int debug;
@ -226,6 +229,7 @@ typedef struct {
struct llcn_t last_compressed;
struct llcn_t last_lcn;
s64 last_unsupp; /* last unsupported cluster */
enum mirror_source mirr_from;
} ntfs_resize_t;
/* FIXME: This, lcn_bitmap and pos from find_free_cluster() will make a cluster
@ -243,8 +247,6 @@ static s64 max_free_cluster_range = 0;
#define DIRTY_INODE (1)
#define DIRTY_ATTRIB (2)
#define NTFS_MAX_CLUSTER_SIZE (65536)
static s64 rounded_up_division(s64 numer, s64 denom)
{
return (numer + (denom - 1)) / denom;
@ -404,7 +406,7 @@ static void version(void)
printf("Copyright (c) 2002-2005 Anton Altaparmakov\n");
printf("Copyright (c) 2002-2003 Richard Russon\n");
printf("Copyright (c) 2007 Yura Pakhuchiy\n");
printf("Copyright (c) 2011-2016 Jean-Pierre Andre\n");
printf("Copyright (c) 2011-2018 Jean-Pierre Andre\n");
printf("\n%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home);
}
@ -451,8 +453,10 @@ static s64 get_new_volume_size(char *s)
switch (*suffix) {
case 'G':
size *= prefix_kind;
/* FALLTHRU */
case 'M':
size *= prefix_kind;
/* FALLTHRU */
case 'k':
size *= prefix_kind;
break;
@ -1459,10 +1463,13 @@ static int record_mft_in_bitmap(ntfs_resize_t *resize)
static void delayed_updates(ntfs_resize_t *resize)
{
struct DELAYED *delayed;
struct DELAYED *delayed_mft_data;
int nr_extents;
if (ntfs_volume_get_free_space(resize->vol))
err_exit("Failed to determine free space\n");
delayed_mft_data = (struct DELAYED*)NULL;
if (resize->delayed_runlists && reload_mft(resize))
err_exit("Failed to reload the MFT for delayed updates\n");
@ -1476,19 +1483,55 @@ static void delayed_updates(ntfs_resize_t *resize)
* So we update MFT data first, and we record the MFT
* extents again in the MFT bitmap if they were recorded
* in the old location.
*
* However, if we are operating in "no action" mode, the
* MFT records to update are not written to their new location
* and the MFT data runlist has to be updated last in order
* to have the entries read from their old location.
* In this situation the MFT bitmap is never written to
* disk, so the same extents are reallocated repeatedly,
* which is not what would be done in a real resizing.
*/
if (opt.ro_flag
&& resize->delayed_runlists
&& (resize->delayed_runlists->mref == FILE_MFT)
&& le32_eq(resize->delayed_runlists->type, AT_DATA)) {
/* Update the MFT data runlist later */
delayed_mft_data = resize->delayed_runlists;
resize->delayed_runlists = resize->delayed_runlists->next;
}
while (resize->delayed_runlists) {
delayed = resize->delayed_runlists;
expand_attribute_runlist(resize->vol, delayed);
if ((delayed->mref == FILE_MFT) && le32_eq(delayed->type, AT_BITMAP))
record_mft_in_bitmap(resize);
if (delayed->mref == FILE_MFT) {
if (le32_eq(delayed->type, AT_BITMAP))
record_mft_in_bitmap(resize);
if (le32_eq(delayed->type, AT_DATA))
resize->mirr_from = MIRR_MFT;
}
resize->delayed_runlists = resize->delayed_runlists->next;
if (delayed->attr_name)
free(delayed->attr_name);
free(delayed->head_rl);
free(delayed);
}
if (opt.ro_flag && delayed_mft_data) {
/* in "no action" mode, check updating the MFT runlist now */
expand_attribute_runlist(resize->vol, delayed_mft_data);
resize->mirr_from = MIRR_MFT;
if (delayed_mft_data->attr_name)
free(delayed_mft_data->attr_name);
free(delayed_mft_data->head_rl);
free(delayed_mft_data);
}
/* Beware of MFT fragmentation when the target size is too small */
nr_extents = resize->vol->mft_ni->nr_extents;
if (nr_extents > 2) {
printf("WARNING: The MFT is now severely fragmented"
" (%d extents)\n", nr_extents);
}
}
/*
@ -1849,9 +1892,13 @@ static void lseek_to_cluster(ntfs_volume *vol, s64 lcn)
static void copy_clusters(ntfs_resize_t *resize, s64 dest, s64 src, s64 len)
{
s64 i;
char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */
char *buff;
ntfs_volume *vol = resize->vol;
buff = (char*)ntfs_malloc(vol->cluster_size);
if (!buff)
perr_exit("ntfs_malloc");
for (i = 0; i < len; i++) {
lseek_to_cluster(vol, src + i);
@ -1875,6 +1922,7 @@ static void copy_clusters(ntfs_resize_t *resize, s64 dest, s64 src, s64 len)
resize->relocations++;
progress_update(&resize->progress, resize->relocations);
}
free(buff);
}
static void relocate_clusters(ntfs_resize_t *r, runlist *dest_rl, s64 src_lcn)
@ -2262,6 +2310,7 @@ static void relocate_inodes(ntfs_resize_t *resize)
err_exit("Could not allocate 16 records in"
" the first MFT chunk\n");
}
resize->mirr_from = MIRR_NEWMFT;
}
for (mref = 0; mref < (MFT_REF)nr_mft_records; mref++)
@ -2716,19 +2765,34 @@ static void update_bootsector(ntfs_resize_t *r)
if (vol->dev->d_ops->read(vol->dev, bs, bs_size) == -1)
perr_exit("read() error");
bs->number_of_sectors = cpu_to_sle64(r->new_volume_size *
bs->bpb.sectors_per_cluster);
if (bs->bpb.sectors_per_cluster > 128)
bs->number_of_sectors = cpu_to_sle64(r->new_volume_size
<< (256 - bs->bpb.sectors_per_cluster));
else
bs->number_of_sectors = cpu_to_sle64(r->new_volume_size *
bs->bpb.sectors_per_cluster);
if (r->mftmir_old) {
if (r->mftmir_old || (r->mirr_from == MIRR_MFT)) {
r->progress.flags |= NTFS_PROGBAR_SUPPRESS;
/* Be sure the MFTMirr holds the updated MFT runlist */
if (r->new_mft_start)
switch (r->mirr_from) {
case MIRR_MFT :
/* The late updates of MFT have not been synced */
ntfs_inode_sync(vol->mft_ni);
copy_clusters(r, r->mftmir_rl.lcn,
vol->mft_na->rl->lcn, r->mftmir_rl.length);
break;
case MIRR_NEWMFT :
copy_clusters(r, r->mftmir_rl.lcn,
r->new_mft_start->lcn, r->mftmir_rl.length);
else
break;
default :
copy_clusters(r, r->mftmir_rl.lcn, r->mftmir_old,
r->mftmir_rl.length);
bs->mftmirr_lcn = cpu_to_sle64(r->mftmir_rl.lcn);
break;
}
if (r->mftmir_old)
bs->mftmirr_lcn = cpu_to_sle64(r->mftmir_rl.lcn);
r->progress.flags &= ~NTFS_PROGBAR_SUPPRESS;
}
/* Set the start of the relocated MFT */
@ -3905,6 +3969,7 @@ static int update_runlist(expand_t *expand, s64 inum,
ctx.mrec = mrec;
resize.mref = inum;
resize.delayed_runlists = expand->delayed_runlists;
resize.mirr_from = MIRR_OLD;
must_delay = 1;
replace_later(&resize,rl,head_rl);
expand->delayed_runlists = resize.delayed_runlists;
@ -4578,6 +4643,7 @@ int main(int argc, char **argv)
resize.inuse = fsck.inuse;
resize.lcn_bitmap = fsck.lcn_bitmap;
resize.mirr_from = MIRR_OLD;
set_resize_constraints(&resize);
set_disk_usage_constraint(&resize);

View File

@ -5,7 +5,7 @@
* Copyright (c) 2004-2005 Holger Ohmacht
* Copyright (c) 2005 Anton Altaparmakov
* Copyright (c) 2007 Yura Pakhuchiy
* Copyright (c) 2013-2014 Jean-Pierre Andre
* Copyright (c) 2013-2018 Jean-Pierre Andre
*
* This utility will recover deleted files from an NTFS volume.
*
@ -392,7 +392,7 @@ static void version(void)
"Copyright (c) 2004-2005 Holger Ohmacht\n"
"Copyright (c) 2005 Anton Altaparmakov\n"
"Copyright (c) 2007 Yura Pakhuchiy\n"
"Copyright (c) 2013-2014 Jean-Pierre Andre\n");
"Copyright (c) 2013-2018 Jean-Pierre Andre\n");
ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
}
@ -567,10 +567,15 @@ static int parse_time(const char *value, time_t *since)
switch (suffix[0]) {
case 'y': case 'Y': result *= 12;
/* FALLTHRU */
case 'm': case 'M': result *= 4;
/* FALLTHRU */
case 'w': case 'W': result *= 7;
/* FALLTHRU */
case 'd': case 'D': result *= 24;
/* FALLTHRU */
case 'h': case 'H': result *= 3600;
/* FALLTHRU */
case 0:
break;
@ -1835,19 +1840,49 @@ static unsigned int write_data(int fd, const char *buffer,
static int create_pathname(const char *dir, const char *name,
const char *stream, char *buffer, int bufsize)
{
struct stat st;
int s;
int len;
int suffix;
if (!name)
name = UNKNOWN;
if (dir)
if (dir) {
#ifdef HAVE_WINDOWS_H
if (stream)
snprintf(buffer, bufsize, "%s/%s:%s", dir, name, stream);
snprintf(buffer, bufsize, "%s\\%s:%s", dir, name,
stream);
else
snprintf(buffer, bufsize, "%s\\%s", dir, name);
#else
if (stream)
snprintf(buffer, bufsize, "%s/%s:%s", dir, name,
stream);
else
snprintf(buffer, bufsize, "%s/%s", dir, name);
else
#endif
} else
if (stream)
snprintf(buffer, bufsize, "%s:%s", name, stream);
else
snprintf(buffer, bufsize, "%s", name);
len = strlen(buffer);
suffix = 0;
#ifdef HAVE_WINDOWS_H
s = stat(buffer, &st);
#else
s = lstat(buffer, &st);
#endif
while (!s && (suffix < 999)) {
suffix++;
snprintf(&buffer[len], bufsize - len, ".%d", suffix);
#ifdef HAVE_WINDOWS_H
s = stat(buffer, &st);
#else
s = lstat(buffer, &st);
#endif
}
return strlen(buffer);
}
@ -2012,7 +2047,8 @@ static int undelete_file(ntfs_volume *vol, long long inode)
if (d->resident) {
fd = open_file(pathname);
if (fd < 0) {
ntfs_log_perror("Couldn't create file");
ntfs_log_perror("Couldn't create file %s",
pathname);
goto free;
}
@ -2041,7 +2077,8 @@ static int undelete_file(ntfs_volume *vol, long long inode)
fd = open_file(pathname);
if (fd < 0) {
ntfs_log_perror("Couldn't create output file");
ntfs_log_perror("Couldn't create file %s",
pathname);
goto free;
}
@ -2151,9 +2188,11 @@ static int undelete_file(ntfs_volume *vol, long long inode)
}
set_date(pathname, file->date);
if (d->name)
ntfs_log_quiet("Undeleted '%s:%s' successfully.\n", file->pref_name, d->name);
ntfs_log_quiet("Undeleted '%s:%s' successfully to %s.\n",
file->pref_name, d->name, pathname);
else
ntfs_log_quiet("Undeleted '%s' successfully.\n", file->pref_name);
ntfs_log_quiet("Undeleted '%s' successfully to %s.\n",
file->pref_name, pathname);
}
result = 1;
free:
@ -2348,7 +2387,7 @@ static int copy_mft(ntfs_volume *vol, long long mft_begin, long long mft_end)
create_pathname(opts.dest, name, NULL, pathname, sizeof(pathname));
fd = open_file(pathname);
if (fd < 0) {
ntfs_log_perror("Couldn't open output file '%s'", name);
ntfs_log_perror("Couldn't create output file '%s'", name);
goto attr;
}
@ -2376,6 +2415,7 @@ static int copy_mft(ntfs_volume *vol, long long mft_begin, long long mft_end)
}
ntfs_log_verbose("Read %lld MFT Records\n", mft_end - mft_begin + 1);
ntfs_log_quiet("MFT extracted to file %s\n", pathname);
result = 0;
close:
close(fd);

View File

@ -314,6 +314,7 @@ static int parse_options(int argc, char *argv[])
case 'i':
opts.info++; /* and fall through */
/* FALLTHRU */
case 'a':
opts.directory++;
opts.logfile++;

View File

@ -1,7 +1,7 @@
/*
* Redo or undo a list of logged actions
*
* Copyright (c) 2014-2016 Jean-Pierre Andre
* Copyright (c) 2014-2017 Jean-Pierre Andre
*
*/
@ -229,7 +229,7 @@ static int sanity_indx_list(const char *buffer, u32 k, u32 end)
err = 0;
done = FALSE;
while ((k <= end) && !done) {
while ((k <= end) && !done && !err) {
lth = getle16(buffer,k+8);
if (optv > 1)
/* Usual indexes can be determined from size */
@ -270,9 +270,20 @@ static int sanity_indx_list(const char *buffer, u32 k, u32 end)
(long long)getle64(buffer,k),
(int)lth,
(int)getle16(buffer,k+12),(int)k);
if ((lth < 80) || (lth & 7)) {
printf("** Invalid index record"
" length %d\n",lth);
err = 1;
}
}
done = !le16_andz(feedle16(buffer,k+12), INDEX_ENTRY_END) || !lth;
k += lth;
if (lth & 7) {
if (optv <= 1) /* Do not repeat the warning */
printf("** Invalid index record length %d\n",
lth);
err = 1;
} else
k += lth;
}
if (k != end) {
printf("** Bad index record length %ld (computed %ld)\n",
@ -795,7 +806,9 @@ static int adjust_high_vcn(ntfs_volume *vol, ATTR_RECORD *attr)
rl = ntfs_mapping_pairs_decompress(vol, attr, (runlist_element*)NULL);
if (rl) {
xrl = rl;
while (xrl->length)
if (xrl->length)
xrl++;
while ((xrl->length) && (xrl->lcn != LCN_RL_NOT_MAPPED))
xrl++;
high_vcn = xrl->vcn - 1;
attr->highest_vcn = cpu_to_sle64(high_vcn);
@ -2117,7 +2130,7 @@ static int redo_delete_file(ntfs_volume *vol,
record = (MFT_RECORD*)buffer;
if ((target + length) <= mftrecsz) {
/* write a void mft entry (needed ?) */
changed = memcmp(buffer + target, data, length)
changed = (length && memcmp(buffer + target, data, length))
|| !le16_andz(record->flags, MFT_RECORD_IN_USE);
err = 0;
if (changed) {
@ -2157,7 +2170,6 @@ static int redo_delete_index(ntfs_volume *vol,
data = ((const char*)&action->record)
+ get_undo_offset(&action->record);
length = le16_to_cpu(action->record.undo_length);
// TODO merge with undo_add_index ?
target = le16_to_cpu(action->record.record_offset)
+ le16_to_cpu(action->record.attribute_offset);
if (optv > 1) {
@ -2176,7 +2188,9 @@ static int redo_delete_index(ntfs_volume *vol,
&& !(length & 7)
&& ((target + length) <= xsize)) {
/* This has to be an idempotent action */
found = !memcmp(buffer + target, data, length);
found = (action->record.undo_operation
== const_cpu_to_le16(CompensationlogRecord))
|| !memcmp(buffer + target, data, length);
err = 0;
if (found) {
/* Remove the entry */
@ -2191,7 +2205,7 @@ static int redo_delete_index(ntfs_volume *vol,
}
if (optv > 1) {
printf("-> INDX record %s\n",
(found ? "unchanged" : "removed"));
(found ? "removed" : "unchanged"));
}
}
return (err);
@ -2238,7 +2252,9 @@ static int redo_delete_root_index(ntfs_volume *vol,
&& !(length & 7)
&& ((target + length) <= mftrecsz)) {
/* This has to be an idempotent action */
found = !memcmp(buffer + target, data, length);
found = (action->record.undo_operation
== const_cpu_to_le16(CompensationlogRecord))
|| !memcmp(buffer + target, data, length);
err = 0;
/* Only delete if present */
if (found) {
@ -2578,7 +2594,9 @@ static int redo_update_resident(ntfs_volume *vol,
dump(&buffer[target], length);
}
if ((target + length) <= mftrecsz) {
changed = memcmp(buffer + target, data, length);
changed = (action->record.undo_operation
== const_cpu_to_le16(CompensationlogRecord))
|| memcmp(buffer + target, data, length);
err = 0;
if (changed) {
memcpy(buffer + target, data, length);
@ -2627,8 +2645,13 @@ static int redo_update_root_index(ntfs_volume *vol,
+ le16_to_cpu(action->record.attribute_offset)
+ offsetof(INDEX_ENTRY, key.file_name.file_name_length)
- length;
err = change_resident_expect(vol, action, buffer, data, expected,
target, length, AT_INDEX_ROOT);
if (action->record.undo_operation
== const_cpu_to_le16(CompensationlogRecord))
err = change_resident(vol, action, buffer, data,
target, length);
else
err = change_resident_expect(vol, action, buffer, data,
expected, target, length, AT_INDEX_ROOT);
return (err);
}
@ -2649,12 +2672,14 @@ static int redo_update_root_vcn(ntfs_volume *vol,
expected = ((const char*)&action->record)
+ get_undo_offset(&action->record);
length = le16_to_cpu(action->record.redo_length);
// length must be 8
target = le16_to_cpu(action->record.record_offset)
+ le16_to_cpu(action->record.attribute_offset)
+ 16; // explanation needed (right justified ?)
err = change_resident_expect(vol, action, buffer, data, expected,
target, length, AT_INDEX_ROOT);
if (length == 8) {
target = le16_to_cpu(action->record.record_offset)
+ le16_to_cpu(action->record.attribute_offset);
/* target is right-justified to end of attribute */
target += getle16(buffer, target + 8) - length;
err = change_resident_expect(vol, action, buffer, data,
expected, target, length, AT_INDEX_ROOT);
}
return (err);
}
@ -2738,11 +2763,13 @@ static int redo_update_vcn(ntfs_volume *vol,
data = ((const char*)&action->record)
+ get_redo_offset(&action->record);
length = le16_to_cpu(action->record.redo_length);
/* target is left-justified to creation time */
target = le16_to_cpu(action->record.record_offset)
+ le16_to_cpu(action->record.attribute_offset)
+ 16; // to better describe
err = update_index(vol, action, buffer, data, target, length);
if (length == 8) {
target = le16_to_cpu(action->record.record_offset)
+ le16_to_cpu(action->record.attribute_offset);
/* target is right-justified to end of attribute */
target += getle16(buffer, target + 8) - length;
err = update_index(vol, action, buffer, data, target, length);
}
return (err);
}
@ -3568,11 +3595,13 @@ static int undo_update_vcn(ntfs_volume *vol, const struct ACTION_RECORD *action,
data = ((const char*)&action->record)
+ get_undo_offset(&action->record);
length = le16_to_cpu(action->record.undo_length);
/* target is left-justified to creation time */
target = le16_to_cpu(action->record.record_offset)
+ le16_to_cpu(action->record.attribute_offset)
+ 16; // to better describe
err = update_index(vol, action, buffer, data, target, length);
if (length == 8) {
target = le16_to_cpu(action->record.record_offset)
+ le16_to_cpu(action->record.attribute_offset);
/* target is right-justified to end of attribute */
target += getle16(buffer, target + 8) - length;
err = update_index(vol, action, buffer, data, target, length);
}
return (err);
}
@ -3775,12 +3804,14 @@ static int undo_update_root_vcn(ntfs_volume *vol,
expected = ((const char*)&action->record)
+ get_redo_offset(&action->record);
length = le16_to_cpu(action->record.undo_length);
/* the fixup is right-justified to the name length */
target = le16_to_cpu(action->record.record_offset)
+ le16_to_cpu(action->record.attribute_offset)
+ 16; // explanation needed
err = change_resident_expect(vol, action, buffer, data, expected,
target, length, AT_INDEX_ROOT);
if (length == 8) {
target = le16_to_cpu(action->record.record_offset)
+ le16_to_cpu(action->record.attribute_offset);
/* target is right-justified to end of attribute */
target += getle16(buffer, target + 8) - length;
err = change_resident_expect(vol, action, buffer, data,
expected, target, length, AT_INDEX_ROOT);
}
return (err);
}
@ -4085,7 +4116,9 @@ static int distribute_redos(ntfs_volume *vol,
break;
case ClearBitsInNonResidentBitMap :
if (le16_eq(action->record.undo_operation,
const_cpu_to_le16(SetBitsInNonResidentBitMap)))
const_cpu_to_le16(SetBitsInNonResidentBitMap))
|| le16_eq(action->record.undo_operation,
const_cpu_to_le16(CompensationlogRecord)))
err = redo_force_bits(vol, action, buffer);
break;
case CompensationlogRecord :
@ -4095,27 +4128,37 @@ static int distribute_redos(ntfs_volume *vol,
break;
case CreateAttribute :
if (le16_eq(action->record.undo_operation,
const_cpu_to_le16(DeleteAttribute)))
const_cpu_to_le16(DeleteAttribute))
|| le16_eq(action->record.undo_operation,
const_cpu_to_le16(CompensationlogRecord)))
err = redo_create_attribute(vol, action, buffer);
break;
case DeallocateFileRecordSegment :
if (le16_eq(action->record.undo_operation,
const_cpu_to_le16(InitializeFileRecordSegment)))
const_cpu_to_le16(InitializeFileRecordSegment))
|| le16_eq(action->record.undo_operation,
const_cpu_to_le16(CompensationlogRecord)))
err = redo_delete_file(vol, action, buffer);
break;
case DeleteAttribute :
if (le16_eq(action->record.undo_operation,
const_cpu_to_le16(CreateAttribute)))
const_cpu_to_le16(CreateAttribute))
|| le16_eq(action->record.undo_operation,
const_cpu_to_le16(CompensationlogRecord)))
err = redo_delete_attribute(vol, action, buffer);
break;
case DeleteIndexEntryAllocation :
if (le16_eq(action->record.undo_operation,
const_cpu_to_le16(AddIndexEntryAllocation)))
const_cpu_to_le16(AddIndexEntryAllocation))
|| le16_eq(action->record.undo_operation,
const_cpu_to_le16(CompensationlogRecord)))
err = redo_delete_index(vol, action, buffer);
break;
case DeleteIndexEntryRoot :
if (le16_eq(action->record.undo_operation,
const_cpu_to_le16(AddIndexEntryRoot)))
const_cpu_to_le16(AddIndexEntryRoot))
|| le16_eq(action->record.undo_operation,
const_cpu_to_le16(CompensationlogRecord)))
err = redo_delete_root_index(vol, action, buffer);
break;
case InitializeFileRecordSegment :
@ -4135,7 +4178,9 @@ static int distribute_redos(ntfs_volume *vol,
break;
case SetIndexEntryVcnAllocation :
if (le16_eq(action->record.undo_operation,
const_cpu_to_le16(SetIndexEntryVcnAllocation)))
const_cpu_to_le16(SetIndexEntryVcnAllocation))
|| le16_eq(action->record.undo_operation,
const_cpu_to_le16(CompensationlogRecord)))
err = redo_update_vcn(vol, action, buffer);
break;
case SetIndexEntryVcnRoot :
@ -4145,17 +4190,23 @@ static int distribute_redos(ntfs_volume *vol,
break;
case SetNewAttributeSizes :
if (le16_eq(action->record.undo_operation,
const_cpu_to_le16(SetNewAttributeSizes)))
const_cpu_to_le16(SetNewAttributeSizes))
|| le16_eq(action->record.undo_operation,
const_cpu_to_le16(CompensationlogRecord)))
err = redo_sizes(vol, action, buffer);
break;
case UpdateFileNameAllocation :
if (le16_eq(action->record.undo_operation,
const_cpu_to_le16(UpdateFileNameAllocation)))
const_cpu_to_le16(UpdateFileNameAllocation))
|| le16_eq(action->record.undo_operation,
const_cpu_to_le16(CompensationlogRecord)))
err = redo_update_index(vol, action, buffer);
break;
case UpdateFileNameRoot :
if (le16_eq(action->record.undo_operation,
const_cpu_to_le16(UpdateFileNameRoot)))
const_cpu_to_le16(UpdateFileNameRoot))
|| le16_eq(action->record.undo_operation,
const_cpu_to_le16(CompensationlogRecord)))
err = redo_update_root_index(vol, action, buffer);
break;
case UpdateMappingPairs :
@ -4178,7 +4229,9 @@ static int distribute_redos(ntfs_volume *vol,
break;
case UpdateResidentValue :
if (le16_eq(action->record.undo_operation,
const_cpu_to_le16(UpdateResidentValue)))
const_cpu_to_le16(UpdateResidentValue))
|| le16_eq(action->record.undo_operation,
const_cpu_to_le16(CompensationlogRecord)))
err = redo_update_resident(vol, action, buffer);
break;
case Win10Action37 :
@ -4193,7 +4246,9 @@ static int distribute_redos(ntfs_volume *vol,
break;
case WriteEndOfIndexBuffer :
if (le16_eq(action->record.undo_operation,
const_cpu_to_le16(WriteEndOfIndexBuffer)))
const_cpu_to_le16(WriteEndOfIndexBuffer))
|| le16_eq(action->record.undo_operation,
const_cpu_to_le16(CompensationlogRecord)))
err = redo_write_index(vol, action, buffer);
break;
case AttributeNamesDump :

View File

@ -75,7 +75,7 @@
#include "logging.h"
#include "misc.h"
const char *ntfs_bugs = "Developers' email address: "NTFS_DEV_LIST"\n";
const char *ntfs_bugs = "Developers' email address: " NTFS_DEV_LIST "\n";
const char *ntfs_gpl = "This program is free software, released under the GNU "
"General Public License\nand you are welcome to redistribute it under "
"certain conditions. It comes with\nABSOLUTELY NO WARRANTY; for "
@ -364,9 +364,13 @@ int utils_parse_size(const char *value, s64 *size, BOOL scale)
if (scale) {
switch (suffix[0]) {
case 't': case 'T': result *= 1000;
/* FALLTHRU */
case 'g': case 'G': result *= 1000;
/* FALLTHRU */
case 'm': case 'M': result *= 1000;
/* FALLTHRU */
case 'k': case 'K': result *= 1000;
/* FALLTHRU */
case '-': case 0:
break;
default:

View File

@ -4,7 +4,7 @@
* Copyright (c) 2005-2007 Yura Pakhuchiy
* Copyright (c) 2005 Yuval Fledel
* Copyright (c) 2006-2009 Szabolcs Szakacsits
* Copyright (c) 2007-2017 Jean-Pierre Andre
* Copyright (c) 2007-2021 Jean-Pierre Andre
* Copyright (c) 2009 Erik Larsson
*
* This file is originated from the Linux-NTFS project.
@ -86,10 +86,6 @@
#include <sys/param.h>
#endif /* defined(__APPLE__) || defined(__DARWIN__), ... */
#ifdef HAVE_LINUX_FS_H
#include <linux/fs.h>
#endif
#ifndef FUSE_CAP_POSIX_ACL /* until defined in <fuse/fuse_common.h> */
#define FUSE_CAP_POSIX_ACL (1 << 18)
#endif /* FUSE_CAP_POSIX_ACL */
@ -106,6 +102,7 @@
#include "ntfstime.h"
#include "security.h"
#include "reparse.h"
#include "ea.h"
#include "object_id.h"
#include "efs.h"
#include "logging.h"
@ -133,12 +130,12 @@
#endif
#if !CACHEING
#define ATTR_TIMEOUT 0.0
#define ENTRY_TIMEOUT 0.0
#define ATTR_TIMEOUT (ctx->ro ? TIMEOUT_RO : 0.0)
#define ENTRY_TIMEOUT (ctx->ro ? TIMEOUT_RO : 0.0)
#else
#if defined(__sun) && defined (__SVR4)
#define ATTR_TIMEOUT 10.0
#define ENTRY_TIMEOUT 10.0
#define ATTR_TIMEOUT (ctx->ro ? TIMEOUT_RO : 10.0)
#define ENTRY_TIMEOUT (ctx->ro ? TIMEOUT_RO : 10.0)
#else /* defined(__sun) && defined (__SVR4) */
/*
* FUSE cacheing is only usable with basic permissions
@ -148,11 +145,13 @@
#warning "Fuse cacheing is only usable with basic permissions checked by kernel"
#endif
#if KERNELACLS
#define ATTR_TIMEOUT 10.0
#define ENTRY_TIMEOUT 10.0
#define ATTR_TIMEOUT (ctx->ro ? TIMEOUT_RO : 10.0)
#define ENTRY_TIMEOUT (ctx->ro ? TIMEOUT_RO : 10.0)
#else /* KERNELACLS */
#define ATTR_TIMEOUT (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT) ? 10.0 : 0.0)
#define ENTRY_TIMEOUT (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT) ? 10.0 : 0.0)
#define ATTR_TIMEOUT (ctx->ro ? TIMEOUT_RO : \
(ctx->vol->secure_flags & (1 << SECURITY_DEFAULT) ? 10.0 : 0.0))
#define ENTRY_TIMEOUT (ctx->ro ? TIMEOUT_RO : \
(ctx->vol->secure_flags & (1 << SECURITY_DEFAULT) ? 10.0 : 0.0))
#endif /* KERNELACLS */
#endif /* defined(__sun) && defined (__SVR4) */
#endif /* !CACHEING */
@ -203,6 +202,9 @@ typedef struct fill_item {
typedef struct fill_context {
struct fill_item *first;
struct fill_item *last;
#ifndef DISABLE_PLUGINS
u64 fh;
#endif /* DISABLE_PLUGINS */
off_t off;
fuse_req_t req;
fuse_ino_t ino;
@ -260,7 +262,7 @@ static const char *usage_msg =
"\n"
"Copyright (C) 2005-2007 Yura Pakhuchiy\n"
"Copyright (C) 2006-2009 Szabolcs Szakacsits\n"
"Copyright (C) 2007-2017 Jean-Pierre Andre\n"
"Copyright (C) 2007-2021 Jean-Pierre Andre\n"
"Copyright (C) 2009 Erik Larsson\n"
"\n"
"Usage: %s [-o option[,...]] <device|image_file> <mount_point>\n"
@ -269,11 +271,16 @@ static const char *usage_msg =
" umask=, fmask=, dmask=, streams_interface=.\n"
" Please see the details in the manual (type: man ntfs-3g).\n"
"\n"
"Example: ntfs-3g /dev/sda1 /mnt/windows\n"
"Example: lowntfs-3g /dev/sda1 /mnt/windows\n"
"\n"
#ifdef PLUGIN_DIR
"Plugin path: " PLUGIN_DIR "\n\n"
#endif /* PLUGIN_DIR */
"%s";
static const char ntfs_bad_reparse[] = "unsupported reparse point";
static const char ntfs_bad_reparse[] = "unsupported reparse tag 0x%08lx";
/* exact length of target text, without the terminator */
#define ntfs_bad_reparse_lth (sizeof(ntfs_bad_reparse) + 2)
#ifdef FUSE_INTERNAL
int drop_privs(void);
@ -660,7 +667,7 @@ static int junction_getstat(ntfs_inode *ni,
if (target)
stbuf->st_size = strlen(target);
else
stbuf->st_size = sizeof(ntfs_bad_reparse) - 1;
stbuf->st_size = ntfs_bad_reparse_lth;
stbuf->st_blocks = (ni->allocated_size + 511) >> 9;
stbuf->st_mode = S_IFLNK;
free(target);
@ -671,6 +678,49 @@ static int junction_getstat(ntfs_inode *ni,
return (res);
}
static int wsl_getstat(ntfs_inode *ni, const REPARSE_POINT *reparse,
struct stat *stbuf)
{
dev_t rdev;
int res;
res = ntfs_reparse_check_wsl(ni, reparse);
if (!res) {
/* switch (reparse->reparse_tag) { */
if (le32_eq(reparse->reparse_tag, IO_REPARSE_TAG_AF_UNIX)) {
stbuf->st_mode = S_IFSOCK;
}
else if (le32_eq(reparse->reparse_tag, IO_REPARSE_TAG_LX_FIFO)) {
stbuf->st_mode = S_IFIFO;
}
else if (le32_eq(reparse->reparse_tag, IO_REPARSE_TAG_LX_CHR)) {
stbuf->st_mode = S_IFCHR;
res = ntfs_ea_check_wsldev(ni, &rdev);
stbuf->st_rdev = rdev;
}
else if (le32_eq(reparse->reparse_tag, IO_REPARSE_TAG_LX_BLK)) {
stbuf->st_mode = S_IFBLK;
res = ntfs_ea_check_wsldev(ni, &rdev);
stbuf->st_rdev = rdev;
}
else {
stbuf->st_size = ntfs_bad_reparse_lth;
stbuf->st_mode = S_IFLNK;
}
/* } */
}
/*
* If the reparse point is not a valid wsl special file
* we display as a symlink
*/
if (res) {
stbuf->st_size = ntfs_bad_reparse_lth;
stbuf->st_mode = S_IFLNK;
res = 0;
}
return (res);
}
/*
* Apply permission masks to st_mode returned by reparse handler
*/
@ -704,6 +754,9 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx,
memset(stbuf, 0, sizeof(struct stat));
withusermapping = (scx->mapping[MAPUSERS] != (struct MAPPING*)NULL);
stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count);
if (ctx->posix_nlink
&& le32_andz(ni->flags, FILE_ATTR_REPARSE_POINT))
stbuf->st_nlink = ntfs_dir_link_cnt(ni);
if (!le16_andz(ni->mrec->flags, MFT_RECORD_IS_DIRECTORY)
|| !le32_andz(ni->flags, FILE_ATTR_REPARSE_POINT)) {
if (!le32_andz(ni->flags, FILE_ATTR_REPARSE_POINT)) {
@ -715,8 +768,7 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx,
if (!res) {
apply_umask(stbuf);
} else {
stbuf->st_size =
sizeof(ntfs_bad_reparse) - 1;
stbuf->st_size = ntfs_bad_reparse_lth;
stbuf->st_blocks =
(ni->allocated_size + 511) >> 9;
stbuf->st_mode = S_IFLNK;
@ -737,8 +789,7 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx,
if (target)
stbuf->st_size = strlen(target);
else
stbuf->st_size =
sizeof(ntfs_bad_reparse) - 1;
stbuf->st_size = ntfs_bad_reparse_lth;
stbuf->st_blocks =
(ni->allocated_size + 511) >> 9;
stbuf->st_nlink =
@ -766,7 +817,8 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx,
}
stbuf->st_size = ni->data_size;
stbuf->st_blocks = ni->allocated_size >> 9;
stbuf->st_nlink = 1; /* Make find(1) work */
if (!ctx->posix_nlink)
stbuf->st_nlink = 1; /* Make find(1) work */
}
} else {
/* Regular or Interix (INTX) file. */
@ -1027,19 +1079,33 @@ static void ntfs_fuse_lookup(fuse_req_t req, fuse_ino_t parent,
*/
static int junction_readlink(ntfs_inode *ni,
const REPARSE_POINT *reparse __attribute__((unused)),
char **pbuf)
const REPARSE_POINT *reparse, char **pbuf)
{
int res;
le32 tag;
int lth;
errno = 0;
res = 0;
*pbuf = ntfs_make_symlink(ni, ctx->abs_mnt_point);
if (!*pbuf) {
if (errno == EOPNOTSUPP) {
*pbuf = strdup(ntfs_bad_reparse);
if (!*pbuf)
res = -errno;
*pbuf = (char*)ntfs_malloc(ntfs_bad_reparse_lth + 1);
if (*pbuf) {
if (reparse)
tag = reparse->reparse_tag;
else
tag = const_cpu_to_le32(0);
lth = snprintf(*pbuf, ntfs_bad_reparse_lth + 1,
ntfs_bad_reparse,
(long)le32_to_cpu(tag));
if (lth != ntfs_bad_reparse_lth) {
free(*pbuf);
*pbuf = (char*)NULL;
res = -errno;
}
} else
res = -ENOMEM;
} else
res = -errno;
}
@ -1066,27 +1132,42 @@ static void ntfs_fuse_readlink(fuse_req_t req, fuse_ino_t ino)
* Reparse point : analyze as a junction point
*/
if (!le32_andz(ni->flags, FILE_ATTR_REPARSE_POINT)) {
REPARSE_POINT *reparse;
le32 tag;
int lth;
#ifndef DISABLE_PLUGINS
const plugin_operations_t *ops;
REPARSE_POINT *reparse;
res = CALL_REPARSE_PLUGIN(ni, readlink, &buf);
if (res) {
buf = strdup(ntfs_bad_reparse);
if (!buf)
res = -errno;
}
/* plugin missing or reparse tag failing the check */
if (res && ((errno == ELIBACC) || (errno == EINVAL)))
errno = EOPNOTSUPP;
#else /* DISABLE_PLUGINS */
errno = 0;
res = 0;
buf = ntfs_make_symlink(ni, ctx->abs_mnt_point);
if (!buf) {
if (errno == EOPNOTSUPP)
buf = strdup(ntfs_bad_reparse);
if (!buf)
res = -errno;
}
#endif /* DISABLE_PLUGINS */
if (!buf && (errno == EOPNOTSUPP)) {
buf = (char*)malloc(ntfs_bad_reparse_lth + 1);
if (buf) {
reparse = ntfs_get_reparse_point(ni);
if (reparse) {
tag = reparse->reparse_tag;
free(reparse);
} else
tag = const_cpu_to_le32(0);
lth = snprintf(buf, ntfs_bad_reparse_lth + 1,
ntfs_bad_reparse,
(long)le32_to_cpu(tag));
res = 0;
if (lth != ntfs_bad_reparse_lth) {
free(buf);
buf = (char*)NULL;
}
}
}
if (!buf)
res = -errno;
goto exit;
}
/* Sanity checks. */
@ -1142,8 +1223,7 @@ exit:
fuse_reply_err(req, -res);
else
fuse_reply_readlink(req, buf);
if (buf != ntfs_bad_reparse)
free(buf);
free(buf);
}
static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx,
@ -1169,6 +1249,9 @@ static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx,
/* never return inodes 0 and 1 */
if (MREF(mref) > 1) {
struct stat st = { .st_ino = MREF(mref) };
#ifndef DISABLE_PLUGINS
ntfs_inode *ni;
#endif /* DISABLE_PLUGINS */
switch (dt_type) {
case NTFS_DT_DIR :
@ -1189,6 +1272,26 @@ static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx,
case NTFS_DT_CHR :
st.st_mode = S_IFCHR;
break;
case NTFS_DT_REPARSE :
st.st_mode = S_IFLNK | 0777; /* default */
#ifndef DISABLE_PLUGINS
/* get emulated type from plugin if available */
ni = ntfs_inode_open(ctx->vol, mref);
if (ni && !le32_andz(ni->flags, FILE_ATTR_REPARSE_POINT)) {
const plugin_operations_t *ops;
REPARSE_POINT *reparse;
int res;
res = CALL_REPARSE_PLUGIN(ni, getattr, &st);
if (!res)
apply_umask(&st);
else
st.st_mode = S_IFLNK;
}
if (ni)
ntfs_inode_close(ni);
#endif /* DISABLE_PLUGINS */
break;
default : /* unexpected types shown as plain files */
case NTFS_DT_REG :
st.st_mode = S_IFREG | (0777 & ~ctx->fmask);
@ -1292,6 +1395,17 @@ static void ntfs_fuse_opendir(fuse_req_t req, fuse_ino_t ino,
if (!ntfs_allowed_access(&security,ni,accesstype))
res = -EACCES;
}
if (!le32_andz(ni->flags, FILE_ATTR_REPARSE_POINT)) {
#ifndef DISABLE_PLUGINS
const plugin_operations_t *ops;
REPARSE_POINT *reparse;
fi->fh = 0;
res = CALL_REPARSE_PLUGIN(ni, opendir, fi);
#else /* DISABLE_PLUGINS */
res = -EOPNOTSUPP;
#endif /* DISABLE_PLUGINS */
}
if (ntfs_inode_close(ni))
set_fuse_error(&res);
if (!res) {
@ -1305,6 +1419,9 @@ static void ntfs_fuse_opendir(fuse_req_t req, fuse_ino_t ino,
fill->filled = FALSE;
fill->ino = ino;
fill->off = 0;
#ifndef DISABLE_PLUGINS
fill->fh = fi->fh;
#endif /* DISABLE_PLUGINS */
}
fi->fh = (long)fill;
}
@ -1321,9 +1438,15 @@ static void ntfs_fuse_releasedir(fuse_req_t req,
fuse_ino_t ino __attribute__((unused)),
struct fuse_file_info *fi)
{
#ifndef DISABLE_PLUGINS
struct fuse_file_info ufi;
ntfs_inode *ni;
#endif /* DISABLE_PLUGINS */
ntfs_fuse_fill_context_t *fill;
ntfs_fuse_fill_item_t *current;
int res;
res = 0;
fill = (ntfs_fuse_fill_context_t*)(long)fi->fh;
if (fill && (fill->ino == ino)) {
/* make sure to clear results */
@ -1333,16 +1456,38 @@ static void ntfs_fuse_releasedir(fuse_req_t req,
free(fill->first);
fill->first = current;
}
#ifndef DISABLE_PLUGINS
if (fill->fh) {
const plugin_operations_t *ops;
REPARSE_POINT *reparse;
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (ni) {
if (!le32_andz(ni->flags, FILE_ATTR_REPARSE_POINT)) {
memcpy(&ufi, fi, sizeof(ufi));
ufi.fh = fill->fh;
res = CALL_REPARSE_PLUGIN(ni, release,
&ufi);
}
if (ntfs_inode_close(ni) && !res)
res = -errno;
} else
res = -errno;
}
#endif /* DISABLE_PLUGINS */
fill->ino = 0;
free(fill);
}
fuse_reply_err(req, 0);
fuse_reply_err(req, -res);
}
static void ntfs_fuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off __attribute__((unused)),
struct fuse_file_info *fi __attribute__((unused)))
{
#ifndef DISABLE_PLUGINS
struct fuse_file_info ufi;
#endif /* DISABLE_PLUGINS */
ntfs_fuse_fill_item_t *first;
ntfs_fuse_fill_item_t *current;
ntfs_fuse_fill_context_t *fill;
@ -1379,10 +1524,27 @@ static void ntfs_fuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
if (!ni)
err = -errno;
else {
if (ntfs_readdir(ni, &pos, fill,
(ntfs_filldir_t)
if (!le32_andz(ni->flags,
FILE_ATTR_REPARSE_POINT)) {
#ifndef DISABLE_PLUGINS
const plugin_operations_t *ops;
REPARSE_POINT *reparse;
memcpy(&ufi, fi, sizeof(ufi));
ufi.fh = fill->fh;
err = CALL_REPARSE_PLUGIN(ni,
readdir, &pos, fill,
(ntfs_filldir_t)
ntfs_fuse_filler, &ufi);
#else /* DISABLE_PLUGINS */
err = -EOPNOTSUPP;
#endif /* DISABLE_PLUGINS */
} else {
if (ntfs_readdir(ni, &pos, fill,
(ntfs_filldir_t)
ntfs_fuse_filler))
err = -errno;
err = -errno;
}
fill->filled = TRUE;
ntfs_fuse_update_times(ni,
NTFS_UPDATE_ATIME);
@ -1927,15 +2089,41 @@ static int ntfs_fuse_utimens(struct SECURITY_CONTEXT *scx, fuse_ino_t ino,
if (to_set & FUSE_SET_ATTR_ATIME_NOW)
mask |= NTFS_UPDATE_ATIME;
else
if (to_set & FUSE_SET_ATTR_ATIME)
if (to_set & FUSE_SET_ATTR_ATIME) {
#ifdef HAVE_STRUCT_STAT_ST_ATIMESPEC
ni->last_access_time
= timespec2ntfs(stin->st_atimespec);
#elif defined(HAVE_STRUCT_STAT_ST_ATIM)
ni->last_access_time
= timespec2ntfs(stin->st_atim);
#else
ni->last_access_time.tv_sec
= stin->st_atime;
#ifdef HAVE_STRUCT_STAT_ST_ATIMENSEC
ni->last_access_time.tv_nsec
= stin->st_atimensec;
#endif
#endif
}
if (to_set & FUSE_SET_ATTR_MTIME_NOW)
mask |= NTFS_UPDATE_MTIME;
else
if (to_set & FUSE_SET_ATTR_MTIME)
if (to_set & FUSE_SET_ATTR_MTIME) {
#ifdef HAVE_STRUCT_STAT_ST_ATIMESPEC
ni->last_data_change_time
= timespec2ntfs(stin->st_mtimespec);
#elif defined(HAVE_STRUCT_STAT_ST_ATIM)
ni->last_data_change_time
= timespec2ntfs(stin->st_mtim);
#else
ni->last_data_change_time.tv_sec
= stin->st_mtime;
#ifdef HAVE_STRUCT_STAT_ST_ATIMENSEC
ni->last_data_change_time.tv_nsec
= stin->st_mtimensec;
#endif
#endif
}
ntfs_inode_update_times(ni, mask);
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
} else
@ -2197,7 +2385,11 @@ static int ntfs_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name,
perm = (typemode & ~ctx->dmask & 0777)
| (dsetgid & S_ISGID);
else
perm = typemode & ~ctx->fmask & 0777;
if ((ctx->special_files == NTFS_FILES_WSL)
&& S_ISLNK(type))
perm = typemode | 0777;
else
perm = typemode & ~ctx->fmask & 0777;
/*
* Try to get a security id available for
* file creation (from inheritance or argument).
@ -2221,26 +2413,50 @@ static int ntfs_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name,
perm & ~security.umask, S_ISDIR(type));
#endif
/* Create object specified in @type. */
switch (type) {
case S_IFCHR:
case S_IFBLK:
ni = ntfs_create_device(dir_ni, securid,
uname, uname_len, type, dev);
break;
case S_IFLNK:
utarget_len = ntfs_mbstoucs(target, &utarget);
if (utarget_len < 0) {
res = -errno;
goto exit;
}
ni = ntfs_create_symlink(dir_ni, securid,
uname, uname_len,
utarget, utarget_len);
break;
default:
ni = ntfs_create(dir_ni, securid, uname,
uname_len, type);
break;
if (!le32_andz(dir_ni->flags, FILE_ATTR_REPARSE_POINT)) {
#ifndef DISABLE_PLUGINS
const plugin_operations_t *ops;
REPARSE_POINT *reparse;
reparse = (REPARSE_POINT*)NULL;
ops = select_reparse_plugin(ctx, dir_ni, &reparse);
if (ops && ops->create) {
ni = (*ops->create)(dir_ni, reparse,
securid, uname, uname_len, type);
} else {
ni = (ntfs_inode*)NULL;
errno = EOPNOTSUPP;
}
free(reparse);
#else /* DISABLE_PLUGINS */
ni = (ntfs_inode*)NULL;
errno = EOPNOTSUPP;
#endif /* DISABLE_PLUGINS */
} else {
switch (type) {
case S_IFCHR:
case S_IFBLK:
ni = ntfs_create_device(dir_ni, securid,
uname, uname_len,
type, dev);
break;
case S_IFLNK:
utarget_len = ntfs_mbstoucs(target,
&utarget);
if (utarget_len < 0) {
res = -errno;
goto exit;
}
ni = ntfs_create_symlink(dir_ni,
securid,
uname, uname_len,
utarget, utarget_len);
break;
default:
ni = ntfs_create(dir_ni, securid, uname,
uname_len, type);
break;
}
}
if (ni) {
/*
@ -2410,10 +2626,25 @@ static int ntfs_fuse_newlink(fuse_req_t req __attribute__((unused)),
#else
ntfs_fuse_fill_security_context(req, &security);
#endif
{
if (ntfs_link(ni, dir_ni, uname, uname_len)) {
res = -errno;
{
if (!le32_andz(dir_ni->flags, FILE_ATTR_REPARSE_POINT)) {
#ifndef DISABLE_PLUGINS
const plugin_operations_t *ops;
REPARSE_POINT *reparse;
res = CALL_REPARSE_PLUGIN(dir_ni, link,
ni, uname, uname_len);
if (res < 0)
goto exit;
#else /* DISABLE_PLUGINS */
res = -EOPNOTSUPP;
goto exit;
#endif /* DISABLE_PLUGINS */
} else {
if (ntfs_link(ni, dir_ni, uname, uname_len)) {
res = -errno;
goto exit;
}
}
ntfs_inode_update_mbsname(dir_ni, newname, ni->mft_no);
if (e) {
@ -2468,6 +2699,9 @@ static int ntfs_fuse_rm(fuse_req_t req, fuse_ino_t parent, const char *name,
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
struct SECURITY_CONTEXT security;
#endif
#if defined(__sun) && defined (__SVR4)
int isdir;
#endif /* defined(__sun) && defined (__SVR4) */
/* Deny removing from $Extend */
if (parent == FILE_Extend) {
@ -2507,9 +2741,32 @@ static int ntfs_fuse_rm(fuse_req_t req, fuse_ino_t parent, const char *name,
#if defined(__sun) && defined (__SVR4)
/* on Solaris : deny unlinking directories */
if (rm_type
== (!le16_andz(ni->mrec->flags, MFT_RECORD_IS_DIRECTORY) ? RM_LINK : RM_DIR)) {
errno = EPERM;
isdir = !le16_andz(ni->mrec->flags, MFT_RECORD_IS_DIRECTORY);
#ifndef DISABLE_PLUGINS
/* get emulated type from plugin if available */
if (!le32_andz((ni->flags, FILE_ATTR_REPARSE_POINT) {
struct stat st;
const plugin_operations_t *ops;
REPARSE_POINT *reparse;
/* Avoid double opening of parent directory */
res = ntfs_inode_close(dir_ni);
if (res)
goto exit;
dir_ni = (ntfs_inode*)NULL;
res = CALL_REPARSE_PLUGIN(ni, getattr, &st);
if (res)
goto exit;
isdir = S_ISDIR(st.st_mode);
dir_ni = ntfs_inode_open(ctx->vol, INODE(parent));
if (!dir_ni) {
res = -errno;
goto exit;
}
}
#endif /* DISABLE_PLUGINS */
if (rm_type == (isdir ? RM_LINK : RM_DIR)) {
errno = (isdir ? EISDIR : ENOTDIR);
res = -errno;
goto exit;
}
@ -2597,9 +2854,20 @@ static int ntfs_fuse_rm(fuse_req_t req, fuse_ino_t parent, const char *name,
goto exit;
}
}
if (ntfs_delete(ctx->vol, (char*)NULL, ni, dir_ni,
uname, uname_len))
res = -errno;
if (!le32_andz(dir_ni->flags, FILE_ATTR_REPARSE_POINT)) {
#ifndef DISABLE_PLUGINS
const plugin_operations_t *ops;
REPARSE_POINT *reparse;
res = CALL_REPARSE_PLUGIN(dir_ni, unlink, (char*)NULL,
ni, uname, uname_len);
#else /* DISABLE_PLUGINS */
res = -EOPNOTSUPP;
#endif /* DISABLE_PLUGINS */
} else
if (ntfs_delete(ctx->vol, (char*)NULL, ni, dir_ni,
uname, uname_len))
res = -errno;
/* ntfs_delete() always closes ni and dir_ni */
ni = dir_ni = NULL;
exit:
@ -2949,7 +3217,14 @@ static void ntfs_fuse_ioctl(fuse_req_t req __attribute__((unused)),
}
memcpy(buf, data, in_bufsz);
}
ret = ntfs_ioctl(ni, cmd, arg, flags, buf);
/*
* Linux defines the request argument of ioctl() to be an
* unsigned long, which fuse 2.x forwards as a signed int
* into which the request sometimes does not fit.
* So we must expand the value and make sure it is not
* sign-extended.
*/
ret = ntfs_ioctl(ni, (unsigned int)cmd, arg, flags, buf);
if (ntfs_inode_close (ni))
set_fuse_error(&ret);
}
@ -3064,7 +3339,7 @@ static ntfs_inode *ntfs_check_access_xattr(fuse_req_t req,
|| !(ctx->secure_flags & (1 << SECURITY_ACL))
|| (setting && ctx->inherit))
&& foracl) {
if (ctx->silent)
if (ctx->silent && !ctx->security.mapping[MAPUSERS])
errno = 0;
else
errno = EOPNOTSUPP;
@ -3251,9 +3526,18 @@ out :
free(list);
}
#if defined(__APPLE__) || defined(__DARWIN__)
static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
size_t size, uint32_t position)
#else
static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
size_t size)
#endif
{
#if !(defined(__APPLE__) || defined(__DARWIN__))
static const unsigned int position = 0U;
#endif
ntfs_inode *ni;
ntfs_inode *dir_ni;
ntfs_attr *na = NULL;
@ -3266,6 +3550,16 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
int namespace;
struct SECURITY_CONTEXT security;
#if defined(__APPLE__) || defined(__DARWIN__)
/* If the attribute is not a resource fork attribute and the position
* parameter is non-zero, we return with EINVAL as requesting position
* is not permitted for non-resource fork attributes. */
if (position && strcmp(name, XATTR_RESOURCEFORK_NAME)) {
fuse_reply_err(req, EINVAL);
return;
}
#endif
attr = ntfs_xattr_system_type(name,ctx->vol);
if (attr != XATTR_UNMAPPED) {
/*
@ -3343,7 +3637,7 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
/* trusted only readable by root */
if ((namespace == XATTRNS_TRUSTED)
&& security.uid) {
res = -ENODATA;
res = -NTFS_NOXATTR_ERRNO;
goto out;
}
#endif
@ -3354,7 +3648,7 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
}
/* Return with no result for symlinks, fifo, etc. */
if (!user_xattrs_allowed(ctx, ni)) {
res = -ENODATA;
res = -NTFS_NOXATTR_ERRNO;
goto exit;
}
/* otherwise file must be readable */
@ -3371,7 +3665,7 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
}
na = ntfs_attr_open(ni, AT_DATA, lename, lename_len);
if (!na) {
res = -ENODATA;
res = -NTFS_NOXATTR_ERRNO;
goto exit;
}
rsize = na->data_size;
@ -3380,11 +3674,13 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
&& !le16_andz(na->data_flags, ATTR_IS_ENCRYPTED)
&& NAttrNonResident(na))
rsize = ((na->data_size + 511) & ~511) + 2;
rsize -= position;
if (size) {
if (size >= (size_t)rsize) {
value = (char*)ntfs_malloc(rsize);
if (value)
res = ntfs_attr_pread(na, 0, rsize, value);
res = ntfs_attr_pread(na, position, rsize,
value);
if (!value || (res != rsize))
res = -errno;
} else
@ -3409,9 +3705,21 @@ out :
free(value);
}
#if defined(__APPLE__) || defined(__DARWIN__)
static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
const char *value, size_t size, int flags,
uint32_t position)
#else
static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
const char *value, size_t size, int flags)
#endif
{
#if !(defined(__APPLE__) || defined(__DARWIN__))
static const unsigned int position = 0U;
#else
BOOL is_resource_fork;
#endif
ntfs_inode *ni;
ntfs_inode *dir_ni;
ntfs_attr *na = NULL;
@ -3423,6 +3731,17 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
int namespace;
struct SECURITY_CONTEXT security;
#if defined(__APPLE__) || defined(__DARWIN__)
/* If the attribute is not a resource fork attribute and the position
* parameter is non-zero, we return with EINVAL as requesting position
* is not permitted for non-resource fork attributes. */
is_resource_fork = strcmp(name, XATTR_RESOURCEFORK_NAME) ? FALSE : TRUE;
if (position && !is_resource_fork) {
fuse_reply_err(req, EINVAL);
return;
}
#endif
attr = ntfs_xattr_system_type(name,ctx->vol);
if (attr != XATTR_UNMAPPED) {
/*
@ -3490,7 +3809,7 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
} else
res = -errno;
#endif
#if CACHEING && !defined(FUSE_INTERNAL)
#if CACHEING && !defined(FUSE_INTERNAL) && FUSE_VERSION >= 28
/*
* Most of system xattr settings cause changes to some
* file attribute (st_mode, st_nlink, st_mtime, etc.),
@ -3582,7 +3901,7 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
}
if (!na) {
if (flags == XATTR_REPLACE) {
res = -ENODATA;
res = -NTFS_NOXATTR_ERRNO;
goto exit;
}
if (ntfs_attr_add(ni, AT_DATA, lename, lename_len, NULL, 0)) {
@ -3598,6 +3917,11 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
res = -errno;
goto exit;
}
#if defined(__APPLE__) || defined(__DARWIN__)
} else if (is_resource_fork) {
/* In macOS, the resource fork is a special case. It doesn't
* ever shrink (it would have to be removed and re-added). */
#endif
} else {
/* currently compressed streams can only be wiped out */
if (ntfs_attr_truncate(na, (s64)0 /* size */)) {
@ -3609,8 +3933,8 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
res = 0;
if (size) {
do {
part = ntfs_attr_pwrite(na, total, size - total,
&value[total]);
part = ntfs_attr_pwrite(na, position + total,
size - total, &value[total]);
if (part > 0)
total += part;
} while ((part > 0) && (total < size));
@ -3732,7 +4056,7 @@ static void ntfs_fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *na
} else
res = -errno;
#endif
#if CACHEING && !defined(FUSE_INTERNAL)
#if CACHEING && !defined(FUSE_INTERNAL) && FUSE_VERSION >= 28
/*
* Some allowed system xattr removals cause changes to
* some file attribute (st_mode, st_nlink, etc.),
@ -3820,7 +4144,7 @@ static void ntfs_fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *na
}
if (ntfs_attr_remove(ni, AT_DATA, lename, lename_len)) {
if (errno == ENOENT)
errno = ENODATA;
errno = NTFS_NOXATTR_ERRNO;
res = -errno;
}
if (!res) {
@ -3856,10 +4180,23 @@ static void register_internal_reparse_plugins(void)
.getattr = junction_getstat,
.readlink = junction_readlink,
} ;
static const plugin_operations_t wsl_ops = {
.getattr = wsl_getstat,
} ;
register_reparse_plugin(ctx, IO_REPARSE_TAG_MOUNT_POINT,
&ops, (void*)NULL);
register_reparse_plugin(ctx, IO_REPARSE_TAG_SYMLINK,
&ops, (void*)NULL);
register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_SYMLINK,
&ops, (void*)NULL);
register_reparse_plugin(ctx, IO_REPARSE_TAG_AF_UNIX,
&wsl_ops, (void*)NULL);
register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_FIFO,
&wsl_ops, (void*)NULL);
register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_CHR,
&wsl_ops, (void*)NULL);
register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_BLK,
&wsl_ops, (void*)NULL);
}
#endif /* DISABLE_PLUGINS */
@ -4323,7 +4660,8 @@ int main(int argc, char *argv[])
else {
ctx->abs_mnt_point = (char*)ntfs_malloc(PATH_MAX);
if (ctx->abs_mnt_point) {
if (getcwd(ctx->abs_mnt_point,
if ((strlen(opts.mnt_point) < PATH_MAX)
&& getcwd(ctx->abs_mnt_point,
PATH_MAX - strlen(opts.mnt_point) - 1)) {
strcat(ctx->abs_mnt_point, "/");
strcat(ctx->abs_mnt_point, opts.mnt_point);
@ -4331,6 +4669,9 @@ int main(int argc, char *argv[])
/* Solaris also wants the absolute mount point */
opts.mnt_point = ctx->abs_mnt_point;
#endif /* defined(__sun) && defined (__SVR4) */
} else {
free(ctx->abs_mnt_point);
ctx->abs_mnt_point = (char*)NULL;
}
}
}
@ -4387,14 +4728,20 @@ int main(int argc, char *argv[])
/* Force read-only mount if the device was found read-only */
if (!ctx->ro && NVolReadOnly(ctx->vol)) {
ctx->rw = FALSE;
ctx->ro = TRUE;
if (ntfs_strinsert(&parsed_options, ",ro"))
goto err_out;
}
ntfs_log_info("Could not mount read-write, trying read-only\n");
} else
if (ctx->rw && ntfs_strinsert(&parsed_options, ",rw"))
goto err_out;
/* We must do this after ntfs_open() to be able to set the blksize */
if (ctx->blkdev && set_fuseblk_options(&parsed_options))
goto err_out;
ctx->vol->abs_mnt_point = ctx->abs_mnt_point;
ctx->vol->special_files = ctx->special_files;
ctx->security.vol = ctx->vol;
ctx->vol->secure_flags = ctx->secure_flags;
#ifdef HAVE_SETXATTR /* extended attributes interface required */

View File

@ -52,6 +52,8 @@ hibernation and fast restarting :
powercfg /h off
.sp
.RE
If either Windows is hibernated or its fast restart is enabled, partitions
on internal disks are forced to be mounted in read-only mode.
.SS Access Handling and Security
By default, files and directories are owned by the effective
user and group of the mounting process, and everybody has
@ -248,6 +250,13 @@ they do not appear in Windows directory displays either.
When a file is renamed or linked with a new name, the hidden flag is
adjusted to the latest name.
.TP
.B posix_nlink
Compute the count of hard links of a file or directory according to
the Posix specifications. When this option is not set, a count of 1
is set for directories, and the short name of files is accounted for.
Using the option entails some penalty as the count is not stored and
has to be computed.
.TP
.B windows_names
This option prevents files, directories and extended attributes to be
created with a name not allowed by windows, because
@ -307,6 +316,14 @@ data streams are mapped to xattrs and user can manipulate them using
.B user_xattr
Same as \fBstreams_interface=\fP\fIxattr\fP.
.TP
.BI special_files= value
This option selects a mode for representing a special file to be created
(symbolic link, socket, fifo, character or block device). The mode can
be \fBinterix\fR or \fBwsl\fR, and existing files in either mode are
recognized irrespective of the selected mode. Interix is the traditional
mode, used by default, and wsl is interoperable with Windows WSL, but
it is not compatible with Windows versions earlier than Windows 10.
.TP
.B efs_raw
This option should only be used in backup or restore situation.
It changes the apparent size of files and the behavior of read and

View File

@ -4,7 +4,7 @@
* Copyright (c) 2005-2007 Yura Pakhuchiy
* Copyright (c) 2005 Yuval Fledel
* Copyright (c) 2006-2009 Szabolcs Szakacsits
* Copyright (c) 2007-2017 Jean-Pierre Andre
* Copyright (c) 2007-2021 Jean-Pierre Andre
* Copyright (c) 2009 Erik Larsson
*
* This file is originated from the Linux-NTFS project.
@ -100,6 +100,7 @@
#include "ntfstime.h"
#include "security.h"
#include "reparse.h"
#include "ea.h"
#include "object_id.h"
#include "efs.h"
#include "logging.h"
@ -196,7 +197,7 @@ static const char *usage_msg =
"\n"
"Copyright (C) 2005-2007 Yura Pakhuchiy\n"
"Copyright (C) 2006-2009 Szabolcs Szakacsits\n"
"Copyright (C) 2007-2017 Jean-Pierre Andre\n"
"Copyright (C) 2007-2021 Jean-Pierre Andre\n"
"Copyright (C) 2009 Erik Larsson\n"
"\n"
"Usage: %s [-o option[,...]] <device|image_file> <mount_point>\n"
@ -207,9 +208,14 @@ static const char *usage_msg =
"\n"
"Example: ntfs-3g /dev/sda1 /mnt/windows\n"
"\n"
#ifdef PLUGIN_DIR
"Plugin path: " PLUGIN_DIR "\n\n"
#endif /* PLUGIN_DIR */
"%s";
static const char ntfs_bad_reparse[] = "unsupported reparse point";
static const char ntfs_bad_reparse[] = "unsupported reparse tag 0x%08lx";
/* exact length of target text, without the terminator */
#define ntfs_bad_reparse_lth (sizeof(ntfs_bad_reparse) + 2)
#ifdef FUSE_INTERNAL
int drop_privs(void);
@ -719,7 +725,7 @@ static int junction_getattr(ntfs_inode *ni,
if (target)
stbuf->st_size = strlen(target);
else
stbuf->st_size = sizeof(ntfs_bad_reparse) - 1;
stbuf->st_size = ntfs_bad_reparse_lth;
stbuf->st_blocks = (ni->allocated_size + 511) >> 9;
stbuf->st_mode = S_IFLNK;
free(target);
@ -730,6 +736,49 @@ static int junction_getattr(ntfs_inode *ni,
return (res);
}
static int wsl_getattr(ntfs_inode *ni, const REPARSE_POINT *reparse,
struct stat *stbuf)
{
dev_t rdev;
int res;
res = ntfs_reparse_check_wsl(ni, reparse);
if (!res) {
/* switch (reparse->reparse_tag) { */
if (le32_eq(reparse->reparse_tag, IO_REPARSE_TAG_AF_UNIX)) {
stbuf->st_mode = S_IFSOCK;
}
else if (le32_eq(reparse->reparse_tag, IO_REPARSE_TAG_LX_FIFO)) {
stbuf->st_mode = S_IFIFO;
}
else if (le32_eq(reparse->reparse_tag, IO_REPARSE_TAG_LX_CHR)) {
stbuf->st_mode = S_IFCHR;
res = ntfs_ea_check_wsldev(ni, &rdev);
stbuf->st_rdev = rdev;
}
else if (le32_eq(reparse->reparse_tag, IO_REPARSE_TAG_LX_BLK)) {
stbuf->st_mode = S_IFBLK;
res = ntfs_ea_check_wsldev(ni, &rdev);
stbuf->st_rdev = rdev;
}
else {
stbuf->st_size = ntfs_bad_reparse_lth;
stbuf->st_mode = S_IFLNK;
}
/* } */
}
/*
* If the reparse point is not a valid wsl special file
* we display as a symlink
*/
if (res) {
stbuf->st_size = ntfs_bad_reparse_lth;
stbuf->st_mode = S_IFLNK;
res = 0;
}
return (res);
}
/*
* Apply permission masks to st_mode returned by a reparse handler
*/
@ -787,6 +836,9 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
}
#endif
stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count);
if (ctx->posix_nlink
&& le32_andz(ni->flags, FILE_ATTR_REPARSE_POINT))
stbuf->st_nlink = ntfs_dir_link_cnt(ni);
if ((!le16_andz(ni->mrec->flags, MFT_RECORD_IS_DIRECTORY)
|| !le32_andz(ni->flags, FILE_ATTR_REPARSE_POINT))
@ -801,8 +853,7 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
apply_umask(stbuf);
goto ok;
} else {
stbuf->st_size =
sizeof(ntfs_bad_reparse) - 1;
stbuf->st_size = ntfs_bad_reparse_lth;
stbuf->st_blocks =
(ni->allocated_size + 511) >> 9;
stbuf->st_mode = S_IFLNK;
@ -824,7 +875,7 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
if (target)
stbuf->st_size = strlen(target);
else
stbuf->st_size = sizeof(ntfs_bad_reparse) - 1;
stbuf->st_size = ntfs_bad_reparse_lth;
stbuf->st_blocks = (ni->allocated_size + 511) >> 9;
stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count);
stbuf->st_mode = S_IFLNK;
@ -849,7 +900,8 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
}
stbuf->st_size = ni->data_size;
stbuf->st_blocks = ni->allocated_size >> 9;
stbuf->st_nlink = 1; /* Make find(1) work */
if (!ctx->posix_nlink)
stbuf->st_nlink = 1; /* Make find(1) work */
}
} else {
/* Regular or Interix (INTX) file. */
@ -1045,15 +1097,30 @@ static int junction_readlink(ntfs_inode *ni,
char **pbuf)
{
int res;
le32 tag;
int lth;
errno = 0;
res = 0;
*pbuf = ntfs_make_symlink(ni, ctx->abs_mnt_point);
if (!*pbuf) {
if (errno == EOPNOTSUPP) {
*pbuf = strdup(ntfs_bad_reparse);
if (!*pbuf)
res = -errno;
*pbuf = (char*)ntfs_malloc(ntfs_bad_reparse_lth + 1);
if (*pbuf) {
if (reparse)
tag = reparse->reparse_tag;
else
tag = const_cpu_to_le32(0);
lth = snprintf(*pbuf, ntfs_bad_reparse_lth + 1,
ntfs_bad_reparse,
(long)le32_to_cpu(tag));
if (lth != ntfs_bad_reparse_lth) {
free(*pbuf);
*pbuf = (char*)NULL;
res = -errno;
}
} else
res = -ENOMEM;
} else
res = -errno;
}
@ -1070,6 +1137,9 @@ static int ntfs_fuse_readlink(const char *org_path, char *buf, size_t buf_size)
ntfs_attr *na = NULL;
INTX_FILE *intx_file = NULL;
int stream_name_len, res = 0;
REPARSE_POINT *reparse;
le32 tag;
int lth;
/* Get inode. */
stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
@ -1091,16 +1161,16 @@ static int ntfs_fuse_readlink(const char *org_path, char *buf, size_t buf_size)
#ifndef DISABLE_PLUGINS
char *gotlink;
const plugin_operations_t *ops;
REPARSE_POINT *reparse;
gotlink = (char*)NULL;
res = CALL_REPARSE_PLUGIN(ni, readlink, &gotlink);
if (gotlink) {
strncpy(buf, gotlink, buf_size);
free(gotlink);
} else {
strncpy(buf, ntfs_bad_reparse, buf_size);
res = 0;
} else {
errno = EOPNOTSUPP;
res = -EOPNOTSUPP;
}
#else /* DISABLE_PLUGINS */
char *target;
@ -1112,11 +1182,22 @@ static int ntfs_fuse_readlink(const char *org_path, char *buf, size_t buf_size)
strncpy(buf,target,buf_size);
free(target);
} else
if (errno == EOPNOTSUPP)
strcpy(buf,ntfs_bad_reparse);
else
res = -errno;
res = -errno;
#endif /* DISABLE_PLUGINS */
if (res == -EOPNOTSUPP) {
reparse = ntfs_get_reparse_point(ni);
if (reparse) {
tag = reparse->reparse_tag;
free(reparse);
} else
tag = const_cpu_to_le32(0);
lth = snprintf(buf, ntfs_bad_reparse_lth + 1,
ntfs_bad_reparse,
(long)le32_to_cpu(tag));
res = 0;
if (lth != ntfs_bad_reparse_lth)
res = -errno;
}
goto exit;
}
/* Sanity checks. */
@ -1199,6 +1280,9 @@ static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx,
return 0;
} else {
struct stat st = { .st_ino = MREF(mref) };
#ifndef DISABLE_PLUGINS
ntfs_inode *ni;
#endif /* DISABLE_PLUGINS */
switch (dt_type) {
case NTFS_DT_DIR :
@ -1219,6 +1303,26 @@ static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx,
case NTFS_DT_CHR :
st.st_mode = S_IFCHR;
break;
case NTFS_DT_REPARSE :
st.st_mode = S_IFLNK | 0777; /* default */
#ifndef DISABLE_PLUGINS
/* get emulated type from plugin if available */
ni = ntfs_inode_open(ctx->vol, mref);
if (ni && !le32_andz(ni->flags, FILE_ATTR_REPARSE_POINT)) {
const plugin_operations_t *ops;
REPARSE_POINT *reparse;
int res;
res = CALL_REPARSE_PLUGIN(ni, getattr, &st);
if (!res)
apply_umask(&st);
else
st.st_mode = S_IFLNK;
}
if (ni)
ntfs_inode_close(ni);
#endif /* DISABLE_PLUGINS */
break;
default : /* unexpected types shown as plain files */
case NTFS_DT_REG :
st.st_mode = S_IFREG | (0777 & ~ctx->fmask);
@ -1300,6 +1404,17 @@ static int ntfs_fuse_opendir(const char *path,
ni,accesstype))
res = -EACCES;
}
if (ni->flags & FILE_ATTR_REPARSE_POINT) {
#ifndef DISABLE_PLUGINS
const plugin_operations_t *ops;
REPARSE_POINT *reparse;
fi->fh = 0;
res = CALL_REPARSE_PLUGIN(ni, opendir, fi);
#else /* DISABLE_PLUGINS */
res = -EOPNOTSUPP;
#endif /* DISABLE_PLUGINS */
}
if (ntfs_inode_close(ni))
set_fuse_error(&res);
} else
@ -1323,9 +1438,22 @@ static int ntfs_fuse_readdir(const char *path, void *buf,
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
return -errno;
if (ntfs_readdir(ni, &pos, &fill_ctx,
(ntfs_filldir_t)ntfs_fuse_filler))
err = -errno;
if (!le32_andz(ni->flags, FILE_ATTR_REPARSE_POINT)) {
#ifndef DISABLE_PLUGINS
const plugin_operations_t *ops;
REPARSE_POINT *reparse;
err = CALL_REPARSE_PLUGIN(ni, readdir, &pos, &fill_ctx,
(ntfs_filldir_t)ntfs_fuse_filler, fi);
#else /* DISABLE_PLUGINS */
err = -EOPNOTSUPP;
#endif /* DISABLE_PLUGINS */
} else {
if (ntfs_readdir(ni, &pos, &fill_ctx,
(ntfs_filldir_t)ntfs_fuse_filler))
err = -errno;
}
ntfs_fuse_update_times(ni, NTFS_UPDATE_ATIME);
if (ntfs_inode_close(ni))
set_fuse_error(&err);
@ -1972,7 +2100,7 @@ static int ntfs_fuse_create(const char *org_path, mode_t typemode, dev_t dev,
if (!dir_ni || (dir_ni->mft_no == FILE_Extend)) {
free(path);
res = -errno;
if (dir_ni->mft_no == FILE_Extend)
if (dir_ni)
res = -EPERM;
goto exit;
}
@ -1989,7 +2117,11 @@ static int ntfs_fuse_create(const char *org_path, mode_t typemode, dev_t dev,
perm = (typemode & ~ctx->dmask & 0777)
| (dsetgid & S_ISGID);
else
perm = typemode & ~ctx->fmask & 0777;
if ((ctx->special_files == NTFS_FILES_WSL)
&& S_ISLNK(type))
perm = typemode | 0777;
else
perm = typemode & ~ctx->fmask & 0777;
/*
* Try to get a security id available for
* file creation (from inheritance or argument).
@ -2013,26 +2145,47 @@ static int ntfs_fuse_create(const char *org_path, mode_t typemode, dev_t dev,
perm & ~security.umask, S_ISDIR(type));
#endif
/* Create object specified in @type. */
switch (type) {
case S_IFCHR:
case S_IFBLK:
ni = ntfs_create_device(dir_ni, securid,
if (!le32_andz(dir_ni->flags, FILE_ATTR_REPARSE_POINT)) {
#ifndef DISABLE_PLUGINS
const plugin_operations_t *ops;
REPARSE_POINT *reparse;
reparse = (REPARSE_POINT*)NULL;
ops = select_reparse_plugin(ctx, dir_ni, &reparse);
if (ops && ops->create) {
ni = (*ops->create)(dir_ni, reparse,
securid, uname, uname_len, type);
} else {
ni = (ntfs_inode*)NULL;
errno = EOPNOTSUPP;
}
free(reparse);
#else /* DISABLE_PLUGINS */
errno = EOPNOTSUPP;
#endif /* DISABLE_PLUGINS */
} else {
switch (type) {
case S_IFCHR:
case S_IFBLK:
ni = ntfs_create_device(dir_ni, securid,
uname, uname_len, type, dev);
break;
case S_IFLNK:
utarget_len = ntfs_mbstoucs(target, &utarget);
if (utarget_len < 0) {
res = -errno;
goto exit;
}
ni = ntfs_create_symlink(dir_ni, securid,
uname, uname_len,
break;
case S_IFLNK:
utarget_len = ntfs_mbstoucs(target,
&utarget);
if (utarget_len < 0) {
res = -errno;
goto exit;
}
ni = ntfs_create_symlink(dir_ni,
securid, uname, uname_len,
utarget, utarget_len);
break;
default:
ni = ntfs_create(dir_ni, securid, uname,
uname_len, type);
break;
break;
default:
ni = ntfs_create(dir_ni, securid,
uname, uname_len, type);
break;
}
}
if (ni) {
/*
@ -2255,10 +2408,24 @@ static int ntfs_fuse_link(const char *old_path, const char *new_path)
else
#endif
{
if (ntfs_link(ni, dir_ni, uname, uname_len)) {
res = -errno;
goto exit;
}
if (!le32_andz(dir_ni->flags, FILE_ATTR_REPARSE_POINT)) {
#ifndef DISABLE_PLUGINS
const plugin_operations_t *ops;
REPARSE_POINT *reparse;
res = CALL_REPARSE_PLUGIN(dir_ni, link,
ni, uname, uname_len);
#else /* DISABLE_PLUGINS */
errno = EOPNOTSUPP;
res = -errno;
#endif /* DISABLE_PLUGINS */
if (res)
goto exit;
} else
if (ntfs_link(ni, dir_ni, uname, uname_len)) {
res = -errno;
goto exit;
}
set_archive(ni);
ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
@ -2319,7 +2486,7 @@ static int ntfs_fuse_rm(const char *org_path)
/* deny unlinking metadata files from $Extend */
if (!dir_ni || (dir_ni->mft_no == FILE_Extend)) {
res = -errno;
if (dir_ni->mft_no == FILE_Extend)
if (dir_ni)
res = -EPERM;
goto exit;
}
@ -2330,9 +2497,20 @@ static int ntfs_fuse_rm(const char *org_path)
|| ntfs_allowed_dir_access(&security, org_path, dir_ni, ni,
S_IEXEC + S_IWRITE + S_ISVTX)) {
#endif
if (ntfs_delete(ctx->vol, org_path, ni, dir_ni,
uname, uname_len))
res = -errno;
if (!le32_andz(dir_ni->flags, FILE_ATTR_REPARSE_POINT)) {
#ifndef DISABLE_PLUGINS
const plugin_operations_t *ops;
REPARSE_POINT *reparse;
res = CALL_REPARSE_PLUGIN(dir_ni, unlink,
org_path, ni, uname, uname_len);
#else /* DISABLE_PLUGINS */
res = -EOPNOTSUPP;
#endif /* DISABLE_PLUGINS */
} else
if (ntfs_delete(ctx->vol, org_path, ni, dir_ni,
uname, uname_len))
res = -errno;
/* ntfs_delete() always closes ni and dir_ni */
ni = dir_ni = NULL;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
@ -2753,7 +2931,13 @@ static int ntfs_fuse_ioctl(const char *path,
if (!ni)
return -errno;
ret = ntfs_ioctl(ni, cmd, arg, flags, data);
/*
* Linux defines the request argument of ioctl() to be an
* unsigned long, which fuse 2.x forwards as a signed int into
* which the request sometimes does not fit.
* So we must expand the value and make sure it is not sign-extended.
*/
ret = ntfs_ioctl(ni, (unsigned int)cmd, arg, flags, data);
if (ntfs_inode_close (ni))
set_fuse_error(&ret);
@ -2855,7 +3039,7 @@ static ntfs_inode *ntfs_check_access_xattr(struct SECURITY_CONTEXT *security,
|| !(ctx->secure_flags & (1 << SECURITY_ACL))
|| (setting && ctx->inherit))
&& foracl) {
if (ctx->silent)
if (ctx->silent && !ctx->security.mapping[MAPUSERS])
errno = 0;
else
errno = EOPNOTSUPP;
@ -3091,9 +3275,18 @@ exit:
return ret;
}
#if defined(__APPLE__) || defined(__DARWIN__)
static int ntfs_fuse_getxattr(const char *path, const char *name,
char *value, size_t size, uint32_t position)
#else
static int ntfs_fuse_getxattr(const char *path, const char *name,
char *value, size_t size)
#endif
{
#if !(defined(__APPLE__) || defined(__DARWIN__))
static const unsigned int position = 0U;
#endif
ntfs_inode *ni;
ntfs_inode *dir_ni;
ntfs_attr *na = NULL;
@ -3104,6 +3297,15 @@ static int ntfs_fuse_getxattr(const char *path, const char *name,
int namespace;
struct SECURITY_CONTEXT security;
#if defined(__APPLE__) || defined(__DARWIN__)
/* If the attribute is not a resource fork attribute and the position
* parameter is non-zero, we return with EINVAL as requesting position
* is not permitted for non-resource fork attributes. */
if (position && strcmp(name, XATTR_RESOURCEFORK_NAME)) {
return -EINVAL;
}
#endif
attr = ntfs_xattr_system_type(name,ctx->vol);
if (attr != XATTR_UNMAPPED) {
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
@ -3176,14 +3378,14 @@ static int ntfs_fuse_getxattr(const char *path, const char *name,
/* trusted only readable by root */
if ((namespace == XATTRNS_TRUSTED)
&& security.uid)
return -ENODATA;
return -NTFS_NOXATTR_ERRNO;
#endif
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
return -errno;
/* Return with no result for symlinks, fifo, etc. */
if (!user_xattrs_allowed(ctx, ni)) {
res = -ENODATA;
res = -NTFS_NOXATTR_ERRNO;
goto exit;
}
/* otherwise file must be readable */
@ -3200,7 +3402,7 @@ static int ntfs_fuse_getxattr(const char *path, const char *name,
}
na = ntfs_attr_open(ni, AT_DATA, lename, lename_len);
if (!na) {
res = -ENODATA;
res = -NTFS_NOXATTR_ERRNO;
goto exit;
}
rsize = na->data_size;
@ -3209,9 +3411,10 @@ static int ntfs_fuse_getxattr(const char *path, const char *name,
&& !le16_andz(na->data_flags, ATTR_IS_ENCRYPTED)
&& NAttrNonResident(na))
rsize = ((na->data_size + 511) & ~511) + 2;
rsize -= position;
if (size) {
if (size >= (size_t)rsize) {
res = ntfs_attr_pread(na, 0, rsize, value);
res = ntfs_attr_pread(na, position, rsize, value);
if (res != rsize)
res = -errno;
} else
@ -3227,9 +3430,21 @@ exit:
return res;
}
#if defined(__APPLE__) || defined(__DARWIN__)
static int ntfs_fuse_setxattr(const char *path, const char *name,
const char *value, size_t size, int flags,
uint32_t position)
#else
static int ntfs_fuse_setxattr(const char *path, const char *name,
const char *value, size_t size, int flags)
#endif
{
#if !(defined(__APPLE__) || defined(__DARWIN__))
static const unsigned int position = 0U;
#else
BOOL is_resource_fork;
#endif
ntfs_inode *ni;
ntfs_inode *dir_ni;
ntfs_attr *na = NULL;
@ -3241,6 +3456,16 @@ static int ntfs_fuse_setxattr(const char *path, const char *name,
int namespace;
struct SECURITY_CONTEXT security;
#if defined(__APPLE__) || defined(__DARWIN__)
/* If the attribute is not a resource fork attribute and the position
* parameter is non-zero, we return with EINVAL as requesting position
* is not permitted for non-resource fork attributes. */
is_resource_fork = strcmp(name, XATTR_RESOURCEFORK_NAME) ? FALSE : TRUE;
if (position && !is_resource_fork) {
return -EINVAL;
}
#endif
attr = ntfs_xattr_system_type(name,ctx->vol);
if (attr != XATTR_UNMAPPED) {
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
@ -3390,7 +3615,7 @@ static int ntfs_fuse_setxattr(const char *path, const char *name,
}
if (!na) {
if (flags == XATTR_REPLACE) {
res = -ENODATA;
res = -NTFS_NOXATTR_ERRNO;
goto exit;
}
if (ntfs_attr_add(ni, AT_DATA, lename, lename_len, NULL, 0)) {
@ -3406,6 +3631,11 @@ static int ntfs_fuse_setxattr(const char *path, const char *name,
res = -errno;
goto exit;
}
#if defined(__APPLE__) || defined(__DARWIN__)
} else if (is_resource_fork) {
/* In macOS, the resource fork is a special case. It doesn't
* ever shrink (it would have to be removed and re-added). */
#endif
} else {
/* currently compressed streams can only be wiped out */
if (ntfs_attr_truncate(na, (s64)0 /* size */)) {
@ -3417,8 +3647,8 @@ static int ntfs_fuse_setxattr(const char *path, const char *name,
res = 0;
if (size) {
do {
part = ntfs_attr_pwrite(na, total, size - total,
&value[total]);
part = ntfs_attr_pwrite(na, position + total,
size - total, &value[total]);
if (part > 0)
total += part;
} while ((part > 0) && (total < size));
@ -3620,7 +3850,7 @@ static int ntfs_fuse_removexattr(const char *path, const char *name)
}
if (ntfs_attr_remove(ni, AT_DATA, lename, lename_len)) {
if (errno == ENOENT)
errno = ENODATA;
errno = NTFS_NOXATTR_ERRNO;
res = -errno;
}
if (!res) {
@ -3650,10 +3880,26 @@ static void register_internal_reparse_plugins(void)
.getattr = junction_getattr,
.readlink = junction_readlink,
} ;
static const plugin_operations_t wsl_ops = {
.getattr = wsl_getattr,
} ;
register_reparse_plugin(ctx, IO_REPARSE_TAG_MOUNT_POINT,
&ops, (void*)NULL);
register_reparse_plugin(ctx, IO_REPARSE_TAG_SYMLINK,
&ops, (void*)NULL);
register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_SYMLINK,
&ops, (void*)NULL);
register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_SYMLINK,
&ops, (void*)NULL);
register_reparse_plugin(ctx, IO_REPARSE_TAG_AF_UNIX,
&wsl_ops, (void*)NULL);
register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_FIFO,
&wsl_ops, (void*)NULL);
register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_CHR,
&wsl_ops, (void*)NULL);
register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_BLK,
&wsl_ops, (void*)NULL);
}
#endif /* DISABLE_PLUGINS */
@ -3732,6 +3978,7 @@ static struct fuse_operations ntfs_3g_ops = {
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
.access = ntfs_fuse_access,
.opendir = ntfs_fuse_opendir,
.releasedir = ntfs_fuse_release,
#endif
#ifdef HAVE_SETXATTR
.getxattr = ntfs_fuse_getxattr,
@ -4000,13 +4247,28 @@ static struct fuse *mount_fuse(char *parsed_options)
if (fuse_opt_add_arg(&args, "") == -1)
goto err;
if (ctx->ro) {
char buf[128];
int len;
len = snprintf(buf, sizeof(buf), "-ouse_ino,kernel_cache"
",attr_timeout=%d,entry_timeout=%d",
(int)TIMEOUT_RO, (int)TIMEOUT_RO);
if ((len < 0)
|| (len >= (int)sizeof(buf))
|| (fuse_opt_add_arg(&args, buf) == -1))
goto err;
} else {
#if !CACHEING
if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache,attr_timeout=0") == -1)
goto err;
if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache"
",attr_timeout=0") == -1)
goto err;
#else
if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache,attr_timeout=1") == -1)
goto err;
if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache"
",attr_timeout=1") == -1)
goto err;
#endif
}
if (ctx->debug)
if (fuse_opt_add_arg(&args, "-odebug") == -1)
goto err;
@ -4123,7 +4385,8 @@ int main(int argc, char *argv[])
else {
ctx->abs_mnt_point = (char*)ntfs_malloc(PATH_MAX);
if (ctx->abs_mnt_point) {
if (getcwd(ctx->abs_mnt_point,
if ((strlen(opts.mnt_point) < PATH_MAX)
&& getcwd(ctx->abs_mnt_point,
PATH_MAX - strlen(opts.mnt_point) - 1)) {
strcat(ctx->abs_mnt_point, "/");
strcat(ctx->abs_mnt_point, opts.mnt_point);
@ -4131,6 +4394,9 @@ int main(int argc, char *argv[])
/* Solaris also wants the absolute mount point */
opts.mnt_point = ctx->abs_mnt_point;
#endif /* defined(__sun) && defined (__SVR4) */
} else {
free(ctx->abs_mnt_point);
ctx->abs_mnt_point = (char*)NULL;
}
}
}
@ -4187,16 +4453,22 @@ int main(int argc, char *argv[])
/* Force read-only mount if the device was found read-only */
if (!ctx->ro && NVolReadOnly(ctx->vol)) {
ctx->rw = FALSE;
ctx->ro = TRUE;
if (ntfs_strinsert(&parsed_options, ",ro"))
goto err_out;
}
ntfs_log_info("Could not mount read-write, trying read-only\n");
} else
if (ctx->rw && ntfs_strinsert(&parsed_options, ",rw"))
goto err_out;
/* We must do this after ntfs_open() to be able to set the blksize */
if (ctx->blkdev && set_fuseblk_options(&parsed_options))
goto err_out;
ctx->vol->abs_mnt_point = ctx->abs_mnt_point;
ctx->security.vol = ctx->vol;
ctx->vol->secure_flags = ctx->secure_flags;
ctx->vol->special_files = ctx->special_files;
#ifdef HAVE_SETXATTR /* extended attributes interface required */
ctx->vol->efs_raw = ctx->efs_raw;
#endif /* HAVE_SETXATTR */

View File

@ -1,7 +1,7 @@
/**
* ntfs-3g_common.c - Common definitions for ntfs-3g and lowntfs-3g.
*
* Copyright (c) 2010-2016 Jean-Pierre Andre
* Copyright (c) 2010-2021 Jean-Pierre Andre
* Copyright (c) 2010 Erik Larsson
*
* This program/include file is free software; you can redistribute it and/or
@ -85,6 +85,7 @@ const struct DEFOPTION optionlist[] = {
{ "atime", OPT_ATIME, FLGOPT_BOGUS },
{ "relatime", OPT_RELATIME, FLGOPT_BOGUS },
{ "delay_mtime", OPT_DMTIME, FLGOPT_DECIMAL | FLGOPT_OPTIONAL },
{ "rw", OPT_RW, FLGOPT_BOGUS },
{ "fake_rw", OPT_FAKE_RW, FLGOPT_BOGUS },
{ "fsname", OPT_FSNAME, FLGOPT_NOSUPPORT },
{ "no_def_opts", OPT_NO_DEF_OPTS, FLGOPT_BOGUS },
@ -125,6 +126,8 @@ const struct DEFOPTION optionlist[] = {
{ "usermapping", OPT_USERMAPPING, FLGOPT_STRING },
{ "xattrmapping", OPT_XATTRMAPPING, FLGOPT_STRING },
{ "efs_raw", OPT_EFS_RAW, FLGOPT_BOGUS },
{ "posix_nlink", OPT_POSIX_NLINK, FLGOPT_BOGUS },
{ "special_files", OPT_SPECIAL_FILES, FLGOPT_STRING },
{ (const char*)NULL, 0, 0 } /* end marker */
} ;
@ -291,6 +294,9 @@ char *parse_mount_options(ntfs_fuse_context_t *ctx,
case OPT_FAKE_RW :
ctx->ro = TRUE;
break;
case OPT_RW :
ctx->rw = TRUE;
break;
case OPT_NOATIME :
ctx->atime = ATIME_DISABLED;
break;
@ -422,7 +428,14 @@ char *parse_mount_options(ntfs_fuse_context_t *ctx,
}
break;
case OPT_USER_XATTR :
#if defined(__APPLE__) || defined(__DARWIN__)
/* macOS builds use non-namespaced extended
* attributes by default since it matches the
* standard behaviour of macOS filesystems. */
ctx->streams = NF_STREAMS_INTERFACE_OPENXATTR;
#else
ctx->streams = NF_STREAMS_INTERFACE_XATTR;
#endif
break;
case OPT_NOAUTO :
/* Don't pass noauto option to fuse. */
@ -488,6 +501,20 @@ char *parse_mount_options(ntfs_fuse_context_t *ctx,
ctx->efs_raw = TRUE;
break;
#endif /* HAVE_SETXATTR */
case OPT_POSIX_NLINK :
ctx->posix_nlink = TRUE;
break;
case OPT_SPECIAL_FILES :
if (!strcmp(val, "interix"))
ctx->special_files = NTFS_FILES_INTERIX;
else if (!strcmp(val, "wsl"))
ctx->special_files = NTFS_FILES_WSL;
else {
ntfs_log_error("Invalid special_files"
" mode.\n");
goto err_exit;
}
break;
case OPT_FSNAME : /* Filesystem name. */
/*
* We need this to be able to check whether filesystem
@ -542,6 +569,7 @@ char *parse_mount_options(ntfs_fuse_context_t *ctx,
if (ctx->ro) {
ctx->secure_flags &= ~(1 << SECURITY_ADDSECURIDS);
ctx->hiberfile = FALSE;
ctx->rw = FALSE;
}
exit:
free(options);
@ -796,7 +824,7 @@ const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx,
const struct plugin_operations *ops;
void *handle;
REPARSE_POINT *reparse;
le32 tag;
le32 tag, seltag;
plugin_list_t *plugin;
plugin_init_t pinit;
@ -804,7 +832,8 @@ const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx,
reparse = ntfs_get_reparse_point(ni);
if (reparse) {
tag = reparse->reparse_tag;
for (plugin=ctx->plugins; plugin && !le32_eq(plugin->tag, tag);
seltag = le32_and(tag, IO_REPARSE_PLUGIN_SELECT);
for (plugin=ctx->plugins; plugin && !le32_eq(plugin->tag, seltag);
plugin = plugin->next) { }
if (plugin) {
ops = plugin->ops;
@ -814,12 +843,12 @@ const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx,
snprintf(name,sizeof(name), PLUGIN_DIR
"/ntfs-plugin-%08lx.so",
(long)le32_to_cpu(tag));
(long)le32_to_cpu(seltag));
#else
char name[64];
snprintf(name,sizeof(name), "ntfs-plugin-%08lx.so",
(long)le32_to_cpu(tag));
(long)le32_to_cpu(seltag));
#endif
handle = dlopen(name, RTLD_LAZY);
if (handle) {
@ -828,13 +857,14 @@ const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx,
/* pinit() should set errno if it fails */
ops = (*pinit)(tag);
if (ops && register_reparse_plugin(ctx,
tag, ops, handle))
seltag, ops, handle))
ops = (struct plugin_operations*)NULL;
} else
errno = ELIBBAD;
if (!ops)
dlclose(handle);
} else {
errno = ELIBACC;
if (!(ctx->errors_logged & ERR_PLUGIN)) {
ntfs_log_perror(
"Could not load plugin %s",

View File

@ -51,6 +51,7 @@ enum {
OPT_ATIME,
OPT_RELATIME,
OPT_DMTIME,
OPT_RW,
OPT_FAKE_RW,
OPT_FSNAME,
OPT_NO_DEF_OPTS,
@ -91,6 +92,8 @@ enum {
OPT_USERMAPPING,
OPT_XATTRMAPPING,
OPT_EFS_RAW,
OPT_POSIX_NLINK,
OPT_SPECIAL_FILES,
} ;
/* Option flags */
@ -135,6 +138,7 @@ typedef struct {
ntfs_atime_t atime;
s64 dmtime;
BOOL ro;
BOOL rw;
BOOL show_sys_files;
BOOL hide_hid_files;
BOOL hide_dot_files;
@ -151,6 +155,8 @@ typedef struct {
BOOL no_detach;
BOOL blkdev;
BOOL mounted;
BOOL posix_nlink;
ntfs_volume_special_files special_files;
#ifdef HAVE_SETXATTR /* extended attributes interface required */
BOOL efs_raw;
#ifdef XATTR_MAPPINGS