Merge tag '2022.5.17' into lineage-19.1

* tag '2022.5.17':
  Configured for version 2022.5.17
  Hardened the checking of directory offset requested by a readdir
  Returned an error code when the --help or --version options are used
  Fixed operation on little endian data
  Fixed possible out-of-buffer condition in ntfsck
  Made sure there is no null character in an attribute name (bis)
  Made sure the client log data does not overflow from restart page
  Avoided allocating and reading an attribute beyond its full size
  Made sure there is no null character in an attribute name
  Used a default usn when the former one cannot be retrieved
  Updated the urls present in the ntfsprogs manuals
  Updated the urls present in ntfs-3g code
  Updated the ntfs-3g manual
  Updated the README
  Configured for version 2021.8.22
  Defined Github as the host for documentation
  Configured for version 2021.8.14
  Fixed the detection of the end of attribute list
  Renamed ntfs_index_entry_consistent() as ntfs_index_entry_inconsistent()
  Renamed ntfs_attr_consistent() as ntfs_attr_inconsistent()
  Redesigned the INDEX_ROOT consistency checks
  Made sure the requested compression block size is supported
  Reset the resident attribute offset when appending from none
  Rejected negative data length in an attribute list
  Rejected negative data length in readall()
  Added a check of the minimal length of some attributes
  Checked consistency of index blocks
  Avoided endless recursions when allocating the main bitmap
  Fixed checking the end of attrdef data
  Skipped errors for bad free clusters before they are computed
  Fixed the computation of the end of index entry
  Checked that indexes do not exceed the index block size
  Shown in log the inode of directory read error
  Restricted the attribute definition table size to 24 bits
  Hardened the check of locations of MFT and MFTMirr
  Added and grouped generic attribute checks
  Checked consistency of index entries
  Aborted mounting when cannot access standard information of MFT
  Checked expanded attribute does not overflow the space in use
  Checked attributes layout against length actually used
  Checked the MFTMirr lcn to be consistent with the boot sector
  Checked $MFT data to be ready before fetching an extent
  Checked the layout of attribute list entries
  Checked name limits while looking up for an external attribute
  Fixed consistency check of MFT attributes in ntfsfix
  Configured for version 2021.5.19
  Made sure an error code is issued for failed mounts
  Added a consistency check of attribute size in readall()
  Improved the consistency checks of standard information
  Cleaned the tail of compressed buffer before writing a full cluster
  Fixed initialization of a Posix ACL
  Added consistency check of MFT attributes in ntfsfix
  Added generic checks of MFT records in ntfsfix
  Hardened consistency checks of MFT records
  Rejected updates of uninitialized MFTMirr
  Added consistency checks of index root
  Added check of layout of attributes
  Improved check of upcase table length
  Add the official git repository location to the README file.
  Update outdated copyright notice.
  Update last two entries in README to match style of previous entries.
  Change http links in README to https for security.
  Update README to mention support for Windows 8 and 10 NTFS formats.
  ntfscp.c: Fix improper unsigned const endianness macro for signed type.
  Improved checking of the location of MFT
  Signalled support of UTIME_OMIT to external libfuse2
  configure.ac: Fix obsolete 2-argument AM_INIT_AUTOMAKE invocation.
  configure.ac: Respect 'with_fuse' user setting on non-Linux/Solaris.
  lowntfs-3g.c: Fix compile error when building with libfuse < 2.8.0.
  Fixed a constant string concatenation
  Fixed a minor endianness ajustment bug
  Checked the locations of MFT and MFTMirr at startup
  Enabled Recording the special files the same way as WSL
  Dropped rejecting having both EA and reparse data
  Supported use of WSL special file
  Relocated the mount point field in volume
  Defined ntfs_realloc() and ntfs_free()
  Built reparse symlinks from mount point recorded in volume
  Checked file type on OpenIndiana when deleting a file with a reparse point
  Inserted the reparse tag in the bad reparse symlink
  Enabled actions on directories in reparse plugins
  Fixed maintining the allocated size when updating sparse files
  Allowed customization of sbin for tool directory

Change-Id: Iea8f3f111e16c7465badf4863d06a3fc1a1bff1f
This commit is contained in:
Michael Bestas
2022-06-27 22:41:07 +03:00
60 changed files with 2010 additions and 607 deletions

View File

@@ -1,6 +1,3 @@
ChangeLog can be found at :
Detailed ChangeLog can be found at
http://www.tuxera.com/community/release-history/
The changes and history may also be found on the source repository :
http://sourceforge.net/p/ntfs-3g/ntfs-3g/ci/edge/tree/
https://github.com/tuxera/ntfs-3g/wiki

6
NEWS
View File

@@ -1,5 +1,3 @@
Project information can be found at :
Project news are at http://tuxera.com/community/ntfs-3g-download/
Release notes are maintained at http://tuxera.com/community/release-history/
https://github.com/tuxera/ntfs-3g/

52
README
View File

@@ -3,9 +3,10 @@ INTRODUCTION
============
The NTFS-3G driver is an open source, freely available read/write NTFS driver
for Linux, FreeBSD, Mac OS X, NetBSD, OpenSolaris, QNX and Haiku. It provides
for Linux, FreeBSD, macOS, NetBSD, OpenIndiana, QNX and Haiku. It provides
safe and fast handling of the Windows XP, Windows Server 2003, Windows 2000,
Windows Vista, Windows Server 2008 and Windows 7 file systems.
Windows Vista, Windows Server 2008, Windows 7, Windows 8, Windows Server 2012,
Windows Server 2016, Windows 10 and Windows Server 2019 NTFS file systems.
The purpose of the project is to develop, quality assurance and support a
trustable, featureful and high performance solution for hardware platforms
@@ -18,21 +19,22 @@ Besides the common file system features, NTFS-3G has support for file
ownership and permissions, POSIX ACLs, junction points, extended attributes
and creating internally compressed files (parameter files in the directory
.NTFS-3G may be required to enable them). The new compressed file formats
available in Windows 10 can also be read through a plugin. For using
advanced features, please get the instructions from
http://www.tuxera.com/community/ntfs-3g-advanced/
available in Windows 10 can also be read through a plugin.
News, support answers, problem submission instructions, support and discussion
forums, performance numbers and other information are available on the project
web site at
forums, and other information are available on the project web site at
https://github.com/tuxera/ntfs-3g/wiki
The project has been funded, supported and maintained since 2008 by Tuxera:
https://tuxera.com
http://www.tuxera.com/community/
LICENSES
========
All the NTFS related components : the file system drivers, the ntfsprogs
All the NTFS related components: the file system drivers, the ntfsprogs
utilities and the shared library libntfs-3g are distributed under the terms
of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
@@ -41,11 +43,17 @@ version. See the included file COPYING.
The fuse-lite library is distributed under the terms of the GNU LGPLv2.
See the included file COPYING.LIB.
QUICK INSTALLATION
==================
Linux: Make sure you have the basic development tools and the kernel includes
the FUSE kernel module. Then unpack the source tarball and type:
Most distributions have an up-to-date NTFS-3G package ready for use, and
the recommended way is to install it.
If you need some specific customization, you can compile and install from
the released source code. Make sure you have the basic development tools
and the kernel includes the FUSE kernel module. Then unpack the source
tarball and type:
./configure
make
@@ -73,12 +81,6 @@ There are also a few make targets for building parts :
make drivers : only build drivers and libraries, without ntfsprogs
make ntfsprogs : only build ntfsprogs and libntfs-3g, without drivers
Non-Linux: Please see
http://www.tuxera.com/community/ntfs-3g-download/
for known OS specific installation and source packages, but generally
the same procedures apply.
USAGE
=====
@@ -106,23 +108,23 @@ TESTING WITHOUT INSTALLING
Newer versions of ntfs-3g can be tested without installing anything and
without disturbing an existing installation. Just configure and make as
shown previously. This will create the scripts ntfs-3g and lowntfs-3g
in the src directory, which you may activate for testing :
in the src directory, which you may activate for testing:
./configure
make
then, as root :
then, as root:
src/ntfs-3g [-o mount-options] /dev/sda1 /mnt/windows
And, to end the test, unmount the usual way :
And, to end the test, unmount the usual way:
umount /dev/sda1
NTFS UTILITIES
==============
The ntfsprogs includes utilities for doing all required tasks to NTFS
partitions. In general, just run a utility without any command line
The ntfsprogs directory includes utilities for doing all required tasks to
NTFS partitions. In general, just run a utility without any command line
options to display the version number and usage syntax.
The following utilities are so far implemented:
@@ -159,6 +161,6 @@ ntfscat - Concatenate files and print their contents on the standard output.
ntfscp - Overwrite files on an NTFS partition.
ntfssecaudit : audit the security metadata.
ntfssecaudit - Audit the security metadata.
ntfsusermap : assistance for building a user mapping file.
ntfsusermap - Assistance for building a user mapping file.

View File

@@ -24,8 +24,8 @@
# Autoconf
AC_PREREQ(2.59)
AC_INIT([ntfs-3g],[2017.3.23],[ntfs-3g-devel@lists.sf.net])
LIBNTFS_3G_VERSION="88"
AC_INIT([ntfs-3g],[2022.5.17],[ntfs-3g-devel@lists.sf.net])
LIBNTFS_3G_VERSION="89"
AC_CONFIG_SRCDIR([src/ntfs-3g.c])
# Environment
@@ -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

@@ -398,6 +398,7 @@ extern int ntfs_attr_data_write(ntfs_inode *ni,
const char *buf, size_t size, off_t offset);
extern int ntfs_attr_shrink_size(ntfs_inode *ni, ntfschar *stream_name,
int stream_name_len, off_t offset);
extern int ntfs_attr_inconsistent(const ATTR_RECORD *a, const MFT_REF mref);
#endif /* defined _NTFS_ATTRIB_H */

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

@@ -139,6 +139,10 @@ extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni,
extern void ntfs_index_ctx_put(ntfs_index_context *ictx);
extern void ntfs_index_ctx_reinit(ntfs_index_context *ictx);
extern int ntfs_index_block_inconsistent(const INDEX_BLOCK *ib, u32 block_size,
u64 inum, VCN vcn);
extern int ntfs_index_entry_inconsistent(const INDEX_ENTRY *ie,
COLLATION_RULES collation_rule, u64 inum);
extern int ntfs_index_lookup(const void *key, const int key_len,
ntfs_index_context *ictx) __attribute_warn_unused_result__;

View File

@@ -850,8 +850,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. */
@@ -2444,8 +2446,12 @@ typedef enum {
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_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),

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

@@ -1,7 +1,7 @@
/*
* plugin.h : define interface for plugin development
*
* Copyright (c) 2015 Jean-Pierre Andre
* Copyright (c) 2015-2021 Jean-Pierre Andre
*
*/
@@ -151,6 +151,34 @@ typedef struct plugin_operations {
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

@@ -35,6 +35,13 @@ char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction,
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 -
*
@@ -112,6 +117,7 @@ typedef enum {
NV_HideDotFiles, /* 1: Set hidden flag on dot files */
NV_Compression, /* 1: allow compression */
NV_NoFixupWarn, /* 1: Do not log fixup errors */
NV_FreeSpaceKnown, /* 1: The free space is now known */
} ntfs_volume_state_bits;
#define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state)
@@ -150,6 +156,10 @@ typedef enum {
#define NVolSetNoFixupWarn(nv) set_nvol_flag(nv, NoFixupWarn)
#define NVolClearNoFixupWarn(nv) clear_nvol_flag(nv, NoFixupWarn)
#define NVolFreeSpaceKnown(nv) test_nvol_flag(nv, FreeSpaceKnown)
#define NVolSetFreeSpaceKnown(nv) set_nvol_flag(nv, FreeSpaceKnown)
#define NVolClearFreeSpaceKnown(nv) clear_nvol_flag(nv, FreeSpaceKnown)
/*
* NTFS version 1.1 and 1.2 are used by Windows NT4.
* NTFS version 2.x is used by Windows 2000 Beta
@@ -256,6 +266,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 +286,6 @@ struct _ntfs_volume {
#if CACHE_LEGACY_SIZE
struct CACHE_HEADER *legacy_cache;
#endif
const char *abs_mnt_point; /* Mount point */
};
extern const char *ntfs_home;

View File

@@ -2223,7 +2223,7 @@ static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
}
}
if (dh->filled) {
if (off < dh->len) {
if ((off >= 0) && (off < dh->len)) {
if (off + size > dh->len)
size = dh->len - off;
} else

View File

@@ -670,11 +670,10 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
goto out;
}
res = 0;
res = -1;
if (mo.ishelp)
goto out;
res = -1;
if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1)
goto out;
#ifndef __SOLARIS__

View File

@@ -1330,6 +1330,10 @@ struct POSIX_SECURITY *ntfs_build_basic_posix(
pydesc->acccnt = 3;
pydesc->defcnt = 0;
pydesc->firstdef = 6;
pydesc->filler = 0;
pydesc->acl.version = POSIX_VERSION;
pydesc->acl.flags = 0;
pydesc->acl.filler = 0;
} else
errno = ENOMEM;
return (pydesc);

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-2021 Jean-Pierre Andre
* Copyright (c) 2010 Erik Larsson
*
* This program/include file is free software; you can redistribute it and/or
@@ -216,6 +216,7 @@ s64 ntfs_get_attribute_value(const ntfs_volume *vol,
if (total + (rl[i].length << vol->cluster_size_bits) >=
sle64_to_cpu(a->data_size)) {
unsigned char *intbuf = NULL;
s64 intlth;
/*
* We have reached the last run so we were going to
* overflow when executing the ntfs_pread() which is
@@ -229,8 +230,18 @@ s64 ntfs_get_attribute_value(const ntfs_volume *vol,
* We have reached the end of data size so we were
* going to overflow in the same fashion.
* Temporary fix: same as above.
*
* For safety, limit the amount to read to the
* needed size, knowing that the whole attribute
* size has been checked to be <= 0x40000.
*/
intbuf = ntfs_malloc(rl[i].length << vol->cluster_size_bits);
intlth = (sle64_to_cpu(a->data_size) - total
+ vol->cluster_size - 1)
>> vol->cluster_size_bits;
if (rl[i].length < intlth)
intlth = rl[i].length;
intbuf = (u8*)ntfs_malloc(intlth
<< vol->cluster_size_bits);
if (!intbuf) {
free(rl);
return 0;
@@ -246,14 +257,15 @@ s64 ntfs_get_attribute_value(const ntfs_volume *vol,
* - Yes we can, in sparse files! But not necessarily
* size of 16, just run length.
*/
r = ntfs_pread(vol->dev, rl[i].lcn <<
vol->cluster_size_bits, rl[i].length <<
vol->cluster_size_bits, intbuf);
if (r != rl[i].length << vol->cluster_size_bits) {
r = ntfs_pread(vol->dev,
rl[i].lcn << vol->cluster_size_bits,
intlth << vol->cluster_size_bits,
intbuf);
if (r != intlth << vol->cluster_size_bits) {
#define ESTR "Error reading attribute value"
if (r == -1)
ntfs_log_perror(ESTR);
else if (r < rl[i].length <<
else if (r < intlth <<
vol->cluster_size_bits) {
ntfs_log_debug(ESTR ": Ran out of input data.\n");
errno = EIO;
@@ -414,7 +426,15 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
na = ntfs_calloc(sizeof(ntfs_attr));
if (!na)
goto out;
if (!name_len)
name = (ntfschar*)NULL;
if (name && name != AT_UNNAMED && name != NTFS_INDEX_I30) {
/* A null char leads to a short name and unallocated bytes */
if (ntfs_ucsnlen(name, name_len) != name_len) {
ntfs_log_error("Null character in attribute name"
" of inode %lld\n",(long long)ni->mft_no);
goto err_out;
}
name = ntfs_ucsndup(name, name_len);
if (!name)
goto err_out;
@@ -432,8 +452,19 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
if (!name) {
if (a->name_length) {
name = ntfs_ucsndup((ntfschar*)((u8*)a + le16_to_cpu(
a->name_offset)), a->name_length);
ntfschar *attr_name;
attr_name = (ntfschar*)((u8*)a
+ le16_to_cpu(a->name_offset));
/* A null character leads to illegal memory access */
if (ntfs_ucsnlen(attr_name, a->name_length)
!= a->name_length) {
ntfs_log_error("Null character in attribute"
" name in inode %lld\n",
(long long)ni->mft_no);
goto put_err_out;
}
name = ntfs_ucsndup(attr_name, a->name_length);
if (!name)
goto put_err_out;
newname = name;
@@ -489,6 +520,17 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
}
if (a->non_resident) {
if (((a->flags & ATTR_COMPRESSION_MASK)
|| a->compression_unit)
&& (ni->vol->major_ver < 3)) {
errno = EIO;
ntfs_log_perror("Compressed inode %lld not allowed"
" on NTFS %d.%d",
(unsigned long long)ni->mft_no,
ni->vol->major_ver,
ni->vol->major_ver);
goto put_err_out;
}
if ((a->flags & ATTR_COMPRESSION_MASK)
&& !a->compression_unit) {
errno = EIO;
@@ -497,6 +539,17 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
(unsigned long long)ni->mft_no, le32_to_cpu(type));
goto put_err_out;
}
if ((a->flags & ATTR_COMPRESSION_MASK)
&& (a->compression_unit
!= STANDARD_COMPRESSION_UNIT)) {
errno = EIO;
ntfs_log_perror("Compressed inode %lld attr 0x%lx has "
"an unsupported compression unit %d",
(unsigned long long)ni->mft_no,
(long)le32_to_cpu(type),
(int)a->compression_unit);
goto put_err_out;
}
ntfs_attr_init(na, TRUE, a->flags,
a->flags & ATTR_IS_ENCRYPTED,
a->flags & ATTR_IS_SPARSE,
@@ -1256,6 +1309,17 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs,
LCN lcn_seek_from = -1;
VCN cur_vcn, from_vcn;
if (na->ni->mft_no == FILE_Bitmap) {
/*
* Filling a hole in the main bitmap implies allocating
* clusters, which is likely to imply updating the
* bitmap in a cluster being allocated.
* Not supported now, could lead to endless recursions.
*/
ntfs_log_error("Corrupt $BitMap not fully allocated\n");
errno = EIO;
goto err_out;
}
to_write = min(count, ((*rl)->length << vol->cluster_size_bits) - *ofs);
cur_vcn = (*rl)->vcn;
@@ -2765,6 +2829,8 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name,
ATTR_RECORD *a;
ntfs_volume *vol;
ntfschar *upcase;
ptrdiff_t offs;
ptrdiff_t space;
u32 upcase_len;
ntfs_log_trace("attribute type 0x%x.\n", le32_to_cpu(type));
@@ -2794,8 +2860,17 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name,
a = (ATTR_RECORD*)((char*)ctx->attr +
le32_to_cpu(ctx->attr->length));
for (;; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) {
if (p2n(a) < p2n(ctx->mrec) || (char*)a > (char*)ctx->mrec +
le32_to_cpu(ctx->mrec->bytes_allocated))
/*
* Make sure the attribute fully lies within the MFT record
* and we can safely access its minimal fields.
*/
offs = p2n(a) - p2n(ctx->mrec);
space = le32_to_cpu(ctx->mrec->bytes_in_use) - offs;
if ((offs < 0)
|| (((space < (ptrdiff_t)offsetof(ATTR_RECORD,
resident_end))
|| (space < (ptrdiff_t)le32_to_cpu(a->length)))
&& ((space < 4) || (a->type != AT_END))))
break;
ctx->attr = a;
if (((type != AT_UNUSED) && (le32_to_cpu(a->type) >
@@ -2824,6 +2899,16 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name,
}
} else {
register int rc;
if (a->name_length
&& ((le16_to_cpu(a->name_offset)
+ a->name_length * sizeof(ntfschar))
> le32_to_cpu(a->length))) {
ntfs_log_error("Corrupt attribute name"
" in MFT record %lld\n",
(long long)ctx->ntfs_ino->mft_no);
break;
}
if (name && ((rc = ntfs_names_full_collate(name,
name_len, (ntfschar*)((char*)a +
le16_to_cpu(a->name_offset)),
@@ -2986,6 +3071,8 @@ static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name,
u8 *al_start, *al_end;
ATTR_RECORD *a;
ntfschar *al_name;
ptrdiff_t offs;
ptrdiff_t space;
u32 al_name_len;
BOOL is_first_search = FALSE;
@@ -3026,8 +3113,22 @@ static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name,
le32_to_cpu(AT_ATTRIBUTE_LIST))
goto find_attr_list_attr;
} else {
/* Check for small entry */
if (((p2n(al_end) - p2n(ctx->al_entry))
< (long)offsetof(ATTR_LIST_ENTRY, name))
|| (le16_to_cpu(ctx->al_entry->length) & 7)
|| (le16_to_cpu(ctx->al_entry->length)
< offsetof(ATTR_LIST_ENTRY, name)))
goto corrupt;
al_entry = (ATTR_LIST_ENTRY*)((char*)ctx->al_entry +
le16_to_cpu(ctx->al_entry->length));
if ((u8*)al_entry == al_end)
goto not_found;
/* Preliminary check for small entry */
if ((p2n(al_end) - p2n(al_entry))
< (long)offsetof(ATTR_LIST_ENTRY, name))
goto corrupt;
/*
* If this is an enumeration and the attribute list attribute
* is the next one in the enumeration sequence, just return the
@@ -3090,11 +3191,18 @@ find_attr_list_attr:
/* Catch the end of the attribute list. */
if ((u8*)al_entry == al_end)
goto not_found;
if (!al_entry->length)
break;
if ((u8*)al_entry + 6 > al_end || (u8*)al_entry +
le16_to_cpu(al_entry->length) > al_end)
break;
if ((((u8*)al_entry + offsetof(ATTR_LIST_ENTRY, name)) > al_end)
|| ((u8*)al_entry + le16_to_cpu(al_entry->length) > al_end)
|| (le16_to_cpu(al_entry->length) & 7)
|| (le16_to_cpu(al_entry->length)
< offsetof(ATTR_LIST_ENTRY, name_length))
|| (al_entry->name_length
&& ((u8*)al_entry + al_entry->name_offset
+ al_entry->name_length * sizeof(ntfschar))
> al_end))
break; /* corrupt */
next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry +
le16_to_cpu(al_entry->length));
if (type != AT_UNUSED) {
@@ -3174,6 +3282,12 @@ is_enumeration:
ctx->mrec = ctx->base_mrec;
} else {
/* We want an extent record. */
if (!vol->mft_na) {
ntfs_log_perror("$MFT not ready for "
"opening an extent to inode %lld\n",
(long long)base_ni->mft_no);
break;
}
ni = ntfs_extent_inode_open(base_ni,
al_entry->mft_reference);
if (!ni)
@@ -3202,12 +3316,18 @@ is_enumeration:
* with the same meanings as above.
*/
do_next_attr_loop:
if ((char*)a < (char*)ctx->mrec || (char*)a > (char*)ctx->mrec +
le32_to_cpu(ctx->mrec->bytes_allocated))
/*
* Make sure the attribute fully lies within the MFT record
* and we can safely access its minimal fields.
*/
offs = p2n(a) - p2n(ctx->mrec);
space = le32_to_cpu(ctx->mrec->bytes_in_use) - offs;
if (offs < 0)
break;
if (a->type == AT_END)
if ((space >= 4) && (a->type == AT_END))
continue;
if (!a->length)
if ((space < (ptrdiff_t)offsetof(ATTR_RECORD, resident_end))
|| (space < (ptrdiff_t)le32_to_cpu(a->length)))
break;
if (al_entry->instance != a->instance)
goto do_next_attr;
@@ -3241,13 +3361,15 @@ do_next_attr:
a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length));
goto do_next_attr_loop;
}
corrupt :
if (ni != base_ni) {
ctx->ntfs_ino = base_ni;
ctx->mrec = ctx->base_mrec;
ctx->attr = ctx->base_attr;
}
errno = EIO;
ntfs_log_perror("Inode is corrupt (%lld)", (long long)base_ni->mft_no);
ntfs_log_error("Corrupt attribute list entry in MFT record %lld\n",
(long long)base_ni->mft_no);
return -1;
not_found:
/*
@@ -3299,6 +3421,190 @@ not_found:
}
}
/*
* Check the consistency of an attribute
*
* Do the general consistency checks of the selected attribute :
* - the required fields can be accessed
* - the variable fields do not overflow
* - the attribute is [non-]resident if it must be
* - miscelleaneous checks
*
* Returns 0 if the checks pass
* -1 with errno = EIO otherwise
*/
int ntfs_attr_inconsistent(const ATTR_RECORD *a, const MFT_REF mref)
{
const FILE_NAME_ATTR *fn;
const INDEX_ROOT *ir;
u64 inum;
int ret;
/*
* The attribute was found to fully lie within the MFT
* record, now make sure its relevant parts (name, runlist,
* value) also lie within. The first step is to make sure
* the attribute has the minimum length so that accesses to
* the lengths and offsets of these parts are safe.
*/
ret = 0;
inum = MREF(mref);
if (a->non_resident) {
if ((a->non_resident != 1)
|| (le32_to_cpu(a->length)
< offsetof(ATTR_RECORD, non_resident_end))
|| (le16_to_cpu(a->mapping_pairs_offset)
>= le32_to_cpu(a->length))
|| (a->name_length
&& (((u32)le16_to_cpu(a->name_offset)
+ a->name_length * sizeof(ntfschar))
> le32_to_cpu(a->length)))
|| (le64_to_cpu(a->highest_vcn)
< le64_to_cpu(a->lowest_vcn))) {
ntfs_log_error("Corrupt non resident attribute"
" 0x%x in MFT record %lld\n",
(int)le32_to_cpu(a->type),
(long long)inum);
errno = EIO;
ret = -1;
}
} else {
if ((le32_to_cpu(a->length)
< offsetof(ATTR_RECORD, resident_end))
|| (le32_to_cpu(a->value_length) & 0xff000000)
|| (a->value_length
&& ((le16_to_cpu(a->value_offset)
+ le32_to_cpu(a->value_length))
> le32_to_cpu(a->length)))
|| (a->name_length
&& (((u32)le16_to_cpu(a->name_offset)
+ a->name_length * sizeof(ntfschar))
> le32_to_cpu(a->length)))) {
ntfs_log_error("Corrupt resident attribute"
" 0x%x in MFT record %lld\n",
(int)le32_to_cpu(a->type),
(long long)inum);
errno = EIO;
ret = -1;
}
}
if (!ret) {
/*
* Checking whether an attribute must be [non-]resident
* is hard-coded for well-known ones. This should be
* done through ntfs_attr_can_be_non_resident(), based on
* $AttrDef, but this would give an easy way to bypass
* the checks.
* Attributes which are not well-known are not checked.
*
* Note : at this stage we know that a->length and
* a->value_length cannot look like being negative.
*/
switch(a->type) {
case AT_FILE_NAME :
/* Check file names are resident and do not overflow */
fn = (const FILE_NAME_ATTR*)((const u8*)a
+ le16_to_cpu(a->value_offset));
if (a->non_resident
|| (le32_to_cpu(a->value_length)
< offsetof(FILE_NAME_ATTR, file_name))
|| !fn->file_name_length
|| ((fn->file_name_length * sizeof(ntfschar)
+ offsetof(FILE_NAME_ATTR, file_name))
> le32_to_cpu(a->value_length))) {
ntfs_log_error("Corrupt file name"
" attribute in MFT record %lld.\n",
(long long)inum);
errno = EIO;
ret = -1;
}
break;
case AT_INDEX_ROOT :
/* Check root index is resident and does not overflow */
ir = (const INDEX_ROOT*)((const u8*)a +
le16_to_cpu(a->value_offset));
/* index.allocated_size may overflow while resizing */
if (a->non_resident
|| (le32_to_cpu(a->value_length)
< offsetof(INDEX_ROOT, index.reserved))
|| (le32_to_cpu(ir->index.entries_offset)
< sizeof(INDEX_HEADER))
|| (le32_to_cpu(ir->index.index_length)
< le32_to_cpu(ir->index.entries_offset))
|| (le32_to_cpu(ir->index.allocated_size)
< le32_to_cpu(ir->index.index_length))
|| (le32_to_cpu(a->value_length)
< (le32_to_cpu(ir->index.allocated_size)
+ offsetof(INDEX_ROOT, reserved)))) {
ntfs_log_error("Corrupt index root"
" in MFT record %lld.\n",
(long long)inum);
errno = EIO;
ret = -1;
}
break;
case AT_STANDARD_INFORMATION :
if (a->non_resident
|| (le32_to_cpu(a->value_length)
< offsetof(STANDARD_INFORMATION,
v1_end))) {
ntfs_log_error("Corrupt standard information"
" in MFT record %lld\n",
(long long)inum);
errno = EIO;
ret = -1;
}
break;
case AT_OBJECT_ID :
if (a->non_resident
|| (le32_to_cpu(a->value_length)
< sizeof(GUID))) {
ntfs_log_error("Corrupt object id"
" in MFT record %lld\n",
(long long)inum);
errno = EIO;
ret = -1;
}
break;
case AT_VOLUME_NAME :
case AT_EA_INFORMATION :
if (a->non_resident) {
ntfs_log_error("Attribute 0x%x in MFT record"
" %lld should be resident.\n",
(int)le32_to_cpu(a->type),
(long long)inum);
errno = EIO;
ret = -1;
}
break;
case AT_VOLUME_INFORMATION :
if (a->non_resident
|| (le32_to_cpu(a->value_length)
< sizeof(VOLUME_INFORMATION))) {
ntfs_log_error("Corrupt volume information"
" in MFT record %lld\n",
(long long)inum);
errno = EIO;
ret = -1;
}
break;
case AT_INDEX_ALLOCATION :
if (!a->non_resident) {
ntfs_log_error("Corrupt index allocation"
" in MFT record %lld",
(long long)inum);
errno = EIO;
ret = -1;
}
break;
default :
break;
}
}
return (ret);
}
/**
* ntfs_attr_lookup - find an attribute in an ntfs inode
* @type: attribute type to find
@@ -3546,8 +3852,9 @@ ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol,
ntfs_log_perror("%s: type=%d", __FUNCTION__, le32_to_cpu(type));
return NULL;
}
for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef <
vol->attrdef_len && ad->type; ++ad) {
for (ad = vol->attrdef; ((ptrdiff_t)((u8*)ad - (u8*)vol->attrdef
+ sizeof(ATTR_DEF)) <= vol->attrdef_len)
&& ad->type; ++ad) {
/* We haven't found it yet, carry on searching. */
if (le32_to_cpu(ad->type) < le32_to_cpu(type))
continue;
@@ -4533,6 +4840,13 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size)
}
/* Move attributes following @a to their new location. */
if (((u8 *)m + old_size) < ((u8 *)a + attr_size)) {
ntfs_log_error("Attribute 0x%x overflows"
" from MFT record\n",
(int)le32_to_cpu(a->type));
errno = EIO;
return (-1);
}
memmove((u8 *)a + new_size, (u8 *)a + attr_size,
old_size - ((u8 *)a - (u8 *)m) - attr_size);
@@ -4567,6 +4881,13 @@ int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
ntfs_log_trace("Entering for new size %u.\n", (unsigned)new_size);
if (!a->value_length) {
/* Offset is unsafe when no value */
int offset = ((offsetof(ATTR_RECORD, resident_end)
+ a->name_length*sizeof(ntfschar) - 1) | 7) + 1;
a->value_offset = cpu_to_le16(offset);
}
/* Resize the resident part of the attribute record. */
if ((ret = ntfs_attr_record_resize(m, a, (le16_to_cpu(a->value_offset) +
new_size + 7) & ~7)) < 0)
@@ -5850,6 +6171,9 @@ retry:
& (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE);
if (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 ((na->type == AT_DATA) && (na->name == AT_UNNAMED)) {
na->ni->allocated_size
= (spcomp
@@ -6660,6 +6984,20 @@ void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type,
ntfs_log_perror("ntfs_attr_open failed, inode %lld attr 0x%lx",
(long long)ni->mft_no,(long)le32_to_cpu(type));
goto err_exit;
}
/*
* Consistency check : restrict to 65536 bytes.
* index bitmaps may need more, but still limited by
* the number of clusters.
*/
if (((u64)na->data_size > 65536)
&& ((type != AT_BITMAP)
|| ((u64)na->data_size >
(u64)((ni->vol->nr_clusters + 7) >> 3)))) {
ntfs_log_error("Corrupt attribute 0x%lx in inode %lld\n",
(long)le32_to_cpu(type),(long long)ni->mft_no);
errno = EOVERFLOW;
goto out;
}
data = ntfs_malloc(na->data_size);
if (!data)

View File

@@ -154,6 +154,14 @@ BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b)
}
}
/* MFT and MFTMirr may not overlap the boot sector or be the same */
if (((s64)sle64_to_cpu(b->mft_lcn) <= 0)
|| ((s64)sle64_to_cpu(b->mftmirr_lcn) <= 0)
|| (b->mft_lcn == b->mftmirr_lcn)) {
ntfs_log_error("Invalid location of MFT or MFTMirr.\n");
goto not_ntfs;
}
if (b->end_of_sector_marker != const_cpu_to_le16(0xaa55))
ntfs_log_debug("Warning: Bootsector has invalid end of sector "
"marker.\n");

View File

@@ -752,6 +752,12 @@ s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b)
/* If it is a resident attribute, simply use ntfs_attr_pread(). */
if (!NAttrNonResident(na))
return ntfs_attr_pread(na, pos, count, b);
if (na->compression_block_size < NTFS_SB_SIZE) {
ntfs_log_error("Unsupported compression block size %ld\n",
(long)na->compression_block_size);
errno = EOVERFLOW;
return (-1);
}
total = total2 = 0;
/* Zero out reads beyond initialized size. */
if (pos + count > na->initialized_size) {
@@ -1618,7 +1624,7 @@ static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl,
*/
static s32 ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs,
const char *outbuf, s32 count, BOOL compress,
char *outbuf, s32 count, BOOL compress,
BOOL appending, VCN *update_from)
{
s32 rounded;
@@ -1639,6 +1645,8 @@ static s32 ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs,
if (!compress) {
clsz = 1 << na->ni->vol->cluster_size_bits;
rounded = ((count - 1) | (clsz - 1)) + 1;
if (rounded > count)
memset(&outbuf[count], 0, rounded - count);
written = write_clusters(na->ni->vol, rl,
offs, rounded, outbuf);
if (written != rounded)
@@ -1700,6 +1708,12 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos,
errno = EIO;
return (-1);
}
if (na->compression_block_size < NTFS_SB_SIZE) {
ntfs_log_error("Unsupported compression block size %ld\n",
(long)na->compression_block_size);
errno = EOVERFLOW;
return (-1);
}
if (wrl->vcn < *update_from)
*update_from = wrl->vcn;
written = -1; /* default return */
@@ -1881,6 +1895,12 @@ int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs,
errno = EIO;
return (-1);
}
if (na->compression_block_size < NTFS_SB_SIZE) {
ntfs_log_error("Unsupported compression block size %ld\n",
(long)na->compression_block_size);
errno = EOVERFLOW;
return (-1);
}
if (wrl->vcn < *update_from)
*update_from = wrl->vcn;
vol = na->ni->vol;

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-2020 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"
@@ -292,6 +293,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
(unsigned)index_block_size);
goto put_err_out;
}
/* Consistency check of ir done while fetching attribute */
index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length);
/* The first index entry. */
ie = (INDEX_ENTRY*)((u8*)&ir->index +
@@ -304,10 +306,11 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
/* Bounds checks. */
if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie +
sizeof(INDEX_ENTRY_HEADER) > index_end ||
(u8*)ie + le16_to_cpu(ie->key_length) >
(u8*)ie + le16_to_cpu(ie->length) >
index_end) {
ntfs_log_error("Index entry out of bounds in inode %lld"
"\n", (unsigned long long)dir_ni->mft_no);
ntfs_log_error("Index root entry out of bounds in"
" inode %lld\n",
(unsigned long long)dir_ni->mft_no);
goto put_err_out;
}
/*
@@ -317,9 +320,10 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
if (ie->ie_flags & INDEX_ENTRY_END)
break;
if (!le16_to_cpu(ie->length)) {
ntfs_log_error("Zero length index entry in inode %lld"
"\n", (unsigned long long)dir_ni->mft_no);
/* The file name must not overflow from the entry */
if (ntfs_index_entry_inconsistent(ie, COLLATION_FILE_NAME,
dir_ni->mft_no)) {
errno = EIO;
goto put_err_out;
}
/*
@@ -397,37 +401,18 @@ descend_into_child_node:
if (br != 1) {
if (br != -1)
errno = EIO;
ntfs_log_perror("Failed to read vcn 0x%llx",
(unsigned long long)vcn);
ntfs_log_perror("Failed to read vcn 0x%llx from inode %lld",
(unsigned long long)vcn,
(unsigned long long)ia_na->ni->mft_no);
goto close_err_out;
}
if (sle64_to_cpu(ia->index_block_vcn) != vcn) {
ntfs_log_error("Actual VCN (0x%llx) of index buffer is different "
"from expected VCN (0x%llx).\n",
(long long)sle64_to_cpu(ia->index_block_vcn),
(long long)vcn);
errno = EIO;
goto close_err_out;
}
if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) {
ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode 0x%llx "
"has a size (%u) differing from the directory "
"specified size (%u).\n", (long long)vcn,
(unsigned long long)dir_ni->mft_no,
(unsigned) le32_to_cpu(ia->index.allocated_size) + 0x18,
(unsigned)index_block_size);
if (ntfs_index_block_inconsistent((INDEX_BLOCK*)ia, index_block_size,
ia_na->ni->mft_no, vcn)) {
errno = EIO;
goto close_err_out;
}
index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
if (index_end > (u8*)ia + index_block_size) {
ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode "
"0x%llx exceeds maximum size.\n",
(long long)vcn, (unsigned long long)dir_ni->mft_no);
errno = EIO;
goto close_err_out;
}
/* The first index entry. */
ie = (INDEX_ENTRY*)((u8*)&ia->index +
@@ -441,7 +426,7 @@ descend_into_child_node:
/* Bounds check. */
if ((u8*)ie < (u8*)ia || (u8*)ie +
sizeof(INDEX_ENTRY_HEADER) > index_end ||
(u8*)ie + le16_to_cpu(ie->key_length) >
(u8*)ie + le16_to_cpu(ie->length) >
index_end) {
ntfs_log_error("Index entry out of bounds in directory "
"inode %lld.\n",
@@ -456,10 +441,10 @@ descend_into_child_node:
if (ie->ie_flags & INDEX_ENTRY_END)
break;
if (!le16_to_cpu(ie->length)) {
/* The file name must not overflow from the entry */
if (ntfs_index_entry_inconsistent(ie, COLLATION_FILE_NAME,
dir_ni->mft_no)) {
errno = EIO;
ntfs_log_error("Zero length index entry in inode %lld"
"\n", (unsigned long long)dir_ni->mft_no);
goto close_err_out;
}
/*
@@ -1085,12 +1070,6 @@ static MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni)
}
fn = (FILE_NAME_ATTR*)((u8*)ctx->attr +
le16_to_cpu(ctx->attr->value_offset));
if ((u8*)fn + le32_to_cpu(ctx->attr->value_length) >
(u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) {
ntfs_log_error("Corrupt file name attribute in inode %lld.\n",
(unsigned long long)ni->mft_no);
goto io_err_out;
}
mref = le64_to_cpu(fn->parent_directory);
ntfs_attr_put_search_ctx(ctx);
return mref;
@@ -1249,9 +1228,13 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
/* Bounds checks. */
if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie +
sizeof(INDEX_ENTRY_HEADER) > index_end ||
(u8*)ie + le16_to_cpu(ie->key_length) >
index_end)
(u8*)ie + le16_to_cpu(ie->length) >
index_end) {
ntfs_log_error("Index root entry out of bounds in"
" inode %lld\n",
(unsigned long long)dir_ni->mft_no);
goto dir_err_out;
}
/* The last entry cannot contain a name. */
if (ie->ie_flags & INDEX_ENTRY_END)
break;
@@ -1262,6 +1245,13 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
/* Skip index root entry if continuing previous readdir. */
if (ir_pos > (u8*)ie - (u8*)ir)
continue;
/* The file name must not overflow from the entry */
if (ntfs_index_entry_inconsistent(ie, COLLATION_FILE_NAME,
dir_ni->mft_no)) {
errno = EIO;
goto dir_err_out;
}
/*
* Submit the directory entry to ntfs_filldir(), which will
* invoke the filldir() callback as appropriate.
@@ -1361,33 +1351,12 @@ find_next_index_buffer:
}
ia_start = ia_pos & ~(s64)(index_block_size - 1);
if (sle64_to_cpu(ia->index_block_vcn) != ia_start >>
index_vcn_size_bits) {
ntfs_log_error("Actual VCN (0x%llx) of index buffer is different "
"from expected VCN (0x%llx) in inode 0x%llx.\n",
(long long)sle64_to_cpu(ia->index_block_vcn),
(long long)ia_start >> index_vcn_size_bits,
(unsigned long long)dir_ni->mft_no);
goto dir_err_out;
}
if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) {
ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode %lld "
"has a size (%u) differing from the directory "
"specified size (%u).\n", (long long)ia_start >>
index_vcn_size_bits,
(unsigned long long)dir_ni->mft_no,
(unsigned) le32_to_cpu(ia->index.allocated_size)
+ 0x18, (unsigned)index_block_size);
if (ntfs_index_block_inconsistent((INDEX_BLOCK*)ia, index_block_size,
ia_na->ni->mft_no, ia_start >> index_vcn_size_bits)) {
goto dir_err_out;
}
index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
if (index_end > (u8*)ia + index_block_size) {
ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode "
"%lld exceeds maximum size.\n",
(long long)ia_start >> index_vcn_size_bits,
(unsigned long long)dir_ni->mft_no);
goto dir_err_out;
}
/* The first index entry. */
ie = (INDEX_ENTRY*)((u8*)&ia->index +
le32_to_cpu(ia->index.entries_offset));
@@ -1402,7 +1371,7 @@ find_next_index_buffer:
/* Bounds checks. */
if ((u8*)ie < (u8*)ia || (u8*)ie +
sizeof(INDEX_ENTRY_HEADER) > index_end ||
(u8*)ie + le16_to_cpu(ie->key_length) >
(u8*)ie + le16_to_cpu(ie->length) >
index_end) {
ntfs_log_error("Index entry out of bounds in directory inode "
"%lld.\n", (unsigned long long)dir_ni->mft_no);
@@ -1418,6 +1387,13 @@ find_next_index_buffer:
/* Skip index entry if continuing previous readdir. */
if (ia_pos - ia_start > (u8*)ie - (u8*)ia)
continue;
/* The file name must not overflow from the entry */
if (ntfs_index_entry_inconsistent(ie, COLLATION_FILE_NAME,
dir_ni->mft_no)) {
errno = EIO;
goto dir_err_out;
}
/*
* Submit the directory entry to ntfs_filldir(), which will
* invoke the filldir() callback as appropriate.
@@ -1496,9 +1472,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");
@@ -1509,17 +1487,13 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
return NULL;
}
if (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 +1521,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 |= FILE_ATTR_ARCHIVE;
if (NVolHideDotFiles(dir_ni->vol)
@@ -1581,8 +1566,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 +1616,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 +1698,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 |= ni->flags & FILE_ATTR_COMPRESSED;
fn->file_attributes |= FILE_ATTR_ARCHIVE;
fn->file_attributes |= ni->flags & FILE_ATTR_HIDDEN;
@@ -1719,10 +1729,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 |= 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 +1772,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);

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))
&& (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-2020 Jean-Pierre Andre
* Copyright (c) 2007-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
@@ -388,42 +388,6 @@ static INDEX_ENTRY *ntfs_ie_dup_novcn(INDEX_ENTRY *ie)
return dup;
}
static int ntfs_ia_check(ntfs_index_context *icx, INDEX_BLOCK *ib, VCN vcn)
{
u32 ib_size = (unsigned)le32_to_cpu(ib->index.allocated_size) + 0x18;
ntfs_log_trace("Entering\n");
if (!ntfs_is_indx_record(ib->magic)) {
ntfs_log_error("Corrupt index block signature: vcn %lld inode "
"%llu\n", (long long)vcn,
(unsigned long long)icx->ni->mft_no);
return -1;
}
if (sle64_to_cpu(ib->index_block_vcn) != vcn) {
ntfs_log_error("Corrupt index block: VCN (%lld) is different "
"from expected VCN (%lld) in inode %llu\n",
(long long)sle64_to_cpu(ib->index_block_vcn),
(long long)vcn,
(unsigned long long)icx->ni->mft_no);
return -1;
}
if (ib_size != icx->block_size) {
ntfs_log_error("Corrupt index block : VCN (%lld) of inode %llu "
"has a size (%u) differing from the index "
"specified size (%u)\n", (long long)vcn,
(unsigned long long)icx->ni->mft_no, ib_size,
icx->block_size);
return -1;
}
return 0;
}
static INDEX_ROOT *ntfs_ir_lookup(ntfs_inode *ni, ntfschar *name,
u32 name_len, ntfs_attr_search_ctx **ctx)
{
@@ -469,6 +433,133 @@ static INDEX_ROOT *ntfs_ir_lookup2(ntfs_inode *ni, ntfschar *name, u32 len)
return ir;
}
/*
* Check the consistency of an index block
*
* Make sure the index block does not overflow from the index record.
* The size of block is assumed to have been checked to be what is
* defined in the index root.
*
* Returns 0 if no error was found
* -1 otherwise (with errno unchanged)
*
* |<--->| offsetof(INDEX_BLOCK, index)
* | |<--->| sizeof(INDEX_HEADER)
* | | |
* | | | seq index entries unused
* |=====|=====|=====|===========================|==============|
* | | | | |
* | |<--------->| entries_offset | |
* | |<---------------- index_length ------->| |
* | |<--------------------- allocated_size --------------->|
* |<--------------------------- block_size ------------------->|
*
* size(INDEX_HEADER) <= ent_offset < ind_length <= alloc_size < bk_size
*/
int ntfs_index_block_inconsistent(const INDEX_BLOCK *ib, u32 block_size,
u64 inum, VCN vcn)
{
u32 ib_size = (unsigned)le32_to_cpu(ib->index.allocated_size)
+ offsetof(INDEX_BLOCK, index);
if (!ntfs_is_indx_record(ib->magic)) {
ntfs_log_error("Corrupt index block signature: vcn %lld inode "
"%llu\n", (long long)vcn,
(unsigned long long)inum);
return -1;
}
if (sle64_to_cpu(ib->index_block_vcn) != vcn) {
ntfs_log_error("Corrupt index block: VCN (%lld) is different "
"from expected VCN (%lld) in inode %llu\n",
(long long)sle64_to_cpu(ib->index_block_vcn),
(long long)vcn,
(unsigned long long)inum);
return -1;
}
if (ib_size != block_size) {
ntfs_log_error("Corrupt index block : VCN (%lld) of inode %llu "
"has a size (%u) differing from the index "
"specified size (%u)\n", (long long)vcn,
(unsigned long long)inum, ib_size,
(unsigned int)block_size);
return -1;
}
if (le32_to_cpu(ib->index.entries_offset) < sizeof(INDEX_HEADER)) {
ntfs_log_error("Invalid index entry offset in inode %lld\n",
(unsigned long long)inum);
return -1;
}
if (le32_to_cpu(ib->index.index_length)
<= le32_to_cpu(ib->index.entries_offset)) {
ntfs_log_error("No space for index entries in inode %lld\n",
(unsigned long long)inum);
return -1;
}
if (le32_to_cpu(ib->index.allocated_size)
< le32_to_cpu(ib->index.index_length)) {
ntfs_log_error("Index entries overflow in inode %lld\n",
(unsigned long long)inum);
return -1;
}
return (0);
}
/*
* Check the consistency of an index entry
*
* Make sure data and key do not overflow from entry.
* As a side effect, an entry with zero length is rejected.
* This entry must be a full one (no INDEX_ENTRY_END flag), and its
* length must have been checked beforehand to not overflow from the
* index record.
*
* Returns 0 if no error was found
* -1 otherwise (with errno unchanged)
*/
int ntfs_index_entry_inconsistent(const INDEX_ENTRY *ie,
COLLATION_RULES collation_rule, u64 inum)
{
int ret;
ret = 0;
if (ie->key_length
&& ((le16_to_cpu(ie->key_length) + offsetof(INDEX_ENTRY, key))
> le16_to_cpu(ie->length))) {
ntfs_log_error("Overflow from index entry in inode %lld\n",
(long long)inum);
ret = -1;
} else
if (collation_rule == COLLATION_FILE_NAME) {
if ((offsetof(INDEX_ENTRY, key.file_name.file_name)
+ ie->key.file_name.file_name_length
* sizeof(ntfschar))
> le16_to_cpu(ie->length)) {
ntfs_log_error("File name overflow from index"
" entry in inode %lld\n",
(long long)inum);
ret = -1;
}
} else {
if (ie->data_length
&& ((le16_to_cpu(ie->data_offset)
+ le16_to_cpu(ie->data_length))
> le16_to_cpu(ie->length))) {
ntfs_log_error("Data overflow from index"
" entry in inode %lld\n",
(long long)inum);
ret = -1;
}
}
return (ret);
}
/**
* Find a key in the index block.
*
@@ -521,6 +612,12 @@ static int ntfs_ie_lookup(const void *key, const int key_len,
ntfs_log_error("Collation function not defined\n");
errno = EOPNOTSUPP;
return STATUS_ERROR;
}
/* Make sure key and data do not overflow from entry */
if (ntfs_index_entry_inconsistent(ie, icx->ir->collation_rule,
icx->ni->mft_no)) {
errno = EIO;
return STATUS_ERROR;
}
rc = icx->collate(icx->ni->vol, key, key_len,
&ie->key, le16_to_cpu(ie->key_length));
@@ -606,8 +703,11 @@ static int ntfs_ib_read(ntfs_index_context *icx, VCN vcn, INDEX_BLOCK *dst)
return -1;
}
if (ntfs_ia_check(icx, dst, vcn))
if (ntfs_index_block_inconsistent((INDEX_BLOCK*)dst, icx->block_size,
icx->ia_na->ni->mft_no, vcn)) {
errno = EIO;
return -1;
}
return 0;
}
@@ -703,6 +803,7 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic
else
icx->vcn_size_bits = NTFS_BLOCK_SIZE_BITS;
/* get the appropriate collation function */
icx->ir = ir;
icx->collate = ntfs_get_collate_function(ir->collation_rule);
if (!icx->collate) {
err = errno = EOPNOTSUPP;
@@ -712,10 +813,6 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic
}
old_vcn = VCN_INDEX_ROOT_PARENT;
/*
* FIXME: check for both ir and ib that the first index entry is
* within the index block.
*/
ret = ntfs_ie_lookup(key, key_len, icx, &ir->index, &vcn, &ie);
if (ret == STATUS_ERROR) {
err = errno;
@@ -782,6 +879,10 @@ descend_into_child_node:
err_out:
icx->bad_index = TRUE; /* Force icx->* to be freed */
err_lookup:
if (icx->actx) {
ntfs_attr_put_search_ctx(icx->actx);
icx->actx = NULL;
}
free(ib);
if (!err)
err = EIO;
@@ -1584,7 +1685,7 @@ static int ntfs_ih_takeout(ntfs_index_context *icx, INDEX_HEADER *ih,
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, cpu_to_le32(ih->index_length));
ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length));
/* do nothing if truncation fails */
}
ntfs_inode_mark_dirty(icx->actx->ntfs_ino);

View File

@@ -189,6 +189,13 @@ static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref)
" %lld", (long long)MREF(mref));
goto put_err_out;
}
lthle = ctx->attr->value_length;
if (le32_to_cpu(lthle) < offsetof(STANDARD_INFORMATION, owner_id)) {
ntfs_log_error("Corrupt STANDARD_INFORMATION in base"
" record %lld\n",
(long long)MREF(mref));
goto put_err_out;
}
std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr +
le16_to_cpu(ctx->attr->value_offset));
ni->flags = std_info->file_attributes;
@@ -196,10 +203,9 @@ static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref)
ni->last_data_change_time = std_info->last_data_change_time;
ni->last_mft_change_time = std_info->last_mft_change_time;
ni->last_access_time = std_info->last_access_time;
/* JPA insert v3 extensions if present */
/* length may be seen as 72 (v1.x) or 96 (v3.x) */
lthle = ctx->attr->length;
if (le32_to_cpu(lthle) > sizeof(STANDARD_INFORMATION)) {
/* Insert v3 extensions if present */
/* length may be seen as 48 (v1.x) or 72 (v3.x) */
if (le32_to_cpu(lthle) >= offsetof(STANDARD_INFORMATION, v3_end)) {
set_nino_flag(ni, v3_Extensions);
ni->owner_id = std_info->owner_id;
ni->security_id = std_info->security_id;
@@ -225,9 +231,9 @@ static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref)
l = ntfs_get_attribute_value_length(ctx->attr);
if (!l)
goto put_err_out;
if (l > 0x40000) {
if ((u64)l > 0x40000) {
errno = EIO;
ntfs_log_perror("Too large attrlist attribute (%lld), inode "
ntfs_log_perror("Too large attrlist attribute (%llu), inode "
"%lld", (long long)l, (long long)MREF(mref));
goto put_err_out;
}
@@ -760,13 +766,13 @@ static int ntfs_inode_sync_standard_information(ntfs_inode *ni)
/* JPA update v3.x extensions, ensuring consistency */
lthle = ctx->attr->length;
lthle = ctx->attr->value_length;
lth = le32_to_cpu(lthle);
if (test_nino_flag(ni, v3_Extensions)
&& (lth <= sizeof(STANDARD_INFORMATION)))
&& (lth < offsetof(STANDARD_INFORMATION, v3_end)))
ntfs_log_error("bad sync of standard information\n");
if (lth > sizeof(STANDARD_INFORMATION)) {
if (lth >= offsetof(STANDARD_INFORMATION, v3_end)) {
std_info->owner_id = ni->owner_id;
std_info->security_id = ni->security_id;
std_info->quota_charged = ni->quota_charged;

View File

@@ -368,12 +368,14 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
/* Allocate the bitmap bit. */
*byte |= bit;
writeback = 1;
if (vol->free_clusters <= 0)
ntfs_log_error("Non-positive free clusters "
"(%lld)!\n",
if (NVolFreeSpaceKnown(vol)) {
if (vol->free_clusters <= 0)
ntfs_log_error("Non-positive free"
" clusters (%lld)!\n",
(long long)vol->free_clusters);
else
vol->free_clusters--;
else
vol->free_clusters--;
}
/*
* Coalesce with previous run if adjacent LCNs.
@@ -602,7 +604,8 @@ int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl)
ret = 0;
out:
vol->free_clusters += nr_freed;
if (vol->free_clusters > vol->nr_clusters)
if (NVolFreeSpaceKnown(vol)
&& (vol->free_clusters > vol->nr_clusters))
ntfs_log_error("Too many free clusters (%lld > %lld)!",
(long long)vol->free_clusters,
(long long)vol->nr_clusters);

View File

@@ -287,9 +287,19 @@ static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp)
LOG_CLIENT_RECORD *ca, *cr;
u16 nr_clients, idx;
BOOL in_free_list, idx_is_first;
u32 offset_clients;
ntfs_log_trace("Entering.\n");
/* The restart area must be fully within page */
if ((le16_to_cpu(rp->restart_area_offset) + sizeof(RESTART_AREA))
> le32_to_cpu(rp->system_page_size))
goto err_out;
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
offset_clients = le16_to_cpu(rp->restart_area_offset)
+ le16_to_cpu(ra->client_array_offset);
/* The clients' records must begin within page */
if (offset_clients >= le32_to_cpu(rp->system_page_size))
goto err_out;
ca = (LOG_CLIENT_RECORD*)((u8*)ra +
le16_to_cpu(ra->client_array_offset));
/*
@@ -308,6 +318,10 @@ check_list:
idx = le16_to_cpu(cr->next_client)) {
if (!nr_clients || idx >= le16_to_cpu(ra->log_clients))
goto err_out;
/* The client record must be fully within page */
if ((offset_clients + (idx + 1)*sizeof(LOG_CLIENT_RECORD))
> le32_to_cpu(rp->system_page_size))
goto err_out;
/* Set @cr to the current log client record. */
cr = ca + idx;
/* The first log client record must not have a prev_client. */
@@ -380,7 +394,14 @@ static int ntfs_check_and_load_restart_page(ntfs_attr *log_na,
/*
* Allocate a buffer to store the whole restart page so we can multi
* sector transfer deprotect it.
* For safety, make sure this is consistent with the usa_count
* and shorter than the full log size
*/
if ((le32_to_cpu(rp->system_page_size)
> (u32)(le16_to_cpu(rp->usa_count) - 1)*NTFS_BLOCK_SIZE)
|| (le32_to_cpu(rp->system_page_size)
> le64_to_cpu(log_na->data_size)))
return (EINVAL);
trp = ntfs_malloc(le32_to_cpu(rp->system_page_size));
if (!trp)
return errno;

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-2018 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
@@ -172,6 +172,15 @@ int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref,
cnt = vol->mftmirr_size - m;
if (cnt > count)
cnt = count;
if ((m + cnt) > vol->mftmirr_na->initialized_size >>
vol->mft_record_size_bits) {
errno = ESPIPE;
ntfs_log_perror("Trying to write non-allocated mftmirr"
" records (%lld > %lld)", (long long)m + cnt,
(long long)vol->mftmirr_na->initialized_size >>
vol->mft_record_size_bits);
return -1;
}
bmirr = ntfs_malloc(cnt * vol->mft_record_size);
if (!bmirr)
return -1;
@@ -210,11 +219,26 @@ int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref,
return -1;
}
/*
* Check the consistency of an MFT record
*
* Make sure its general fields are safe, then examine all its
* attributes and apply generic checks to them.
* The attribute checks are skipped when a record is being read in
* order to collect its sequence number for creating a new record.
*
* Returns 0 if the checks are successful
* -1 with errno = EIO otherwise
*/
int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref,
MFT_RECORD *m)
{
ATTR_RECORD *a;
ATTR_TYPES previous_type;
int ret = -1;
u32 offset;
s32 space;
if (!ntfs_is_file_record(m->magic)) {
if (!NVolNoFixupWarn(vol))
@@ -231,6 +255,19 @@ int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref,
le32_to_cpu(m->bytes_allocated));
goto err_out;
}
if (!NVolNoFixupWarn(vol)
&& (le32_to_cpu(m->bytes_in_use) > vol->mft_record_size)) {
ntfs_log_error("Record %llu has corrupt in-use size "
"(%u > %u)\n", (unsigned long long)MREF(mref),
(int)le32_to_cpu(m->bytes_in_use),
(int)vol->mft_record_size);
goto err_out;
}
if (le16_to_cpu(m->attrs_offset) & 7) {
ntfs_log_error("Attributes badly aligned in record %llu\n",
(unsigned long long)MREF(mref));
goto err_out;
}
a = (ATTR_RECORD *)((char *)m + le16_to_cpu(m->attrs_offset));
if (p2n(a) < p2n(m) || (char *)a > (char *)m + vol->mft_record_size) {
@@ -239,6 +276,37 @@ int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref,
goto err_out;
}
if (!NVolNoFixupWarn(vol)) {
offset = le16_to_cpu(m->attrs_offset);
space = le32_to_cpu(m->bytes_in_use) - offset;
a = (ATTR_RECORD*)((char*)m + offset);
previous_type = AT_STANDARD_INFORMATION;
while ((space >= (s32)offsetof(ATTR_RECORD, resident_end))
&& (a->type != AT_END)
&& (le32_to_cpu(a->type) >= le32_to_cpu(previous_type))) {
if ((le32_to_cpu(a->length) <= (u32)space)
&& !(le32_to_cpu(a->length) & 7)) {
if (!ntfs_attr_inconsistent(a, mref)) {
previous_type = a->type;
offset += le32_to_cpu(a->length);
space -= le32_to_cpu(a->length);
a = (ATTR_RECORD*)((char*)m + offset);
} else
goto err_out;
} else {
ntfs_log_error("Corrupted MFT record %llu\n",
(unsigned long long)MREF(mref));
goto err_out;
}
}
/* We are supposed to reach an AT_END */
if ((space < 4) || (a->type != AT_END)) {
ntfs_log_error("Bad end of MFT record %llu\n",
(unsigned long long)MREF(mref));
goto err_out;
}
}
ret = 0;
err_out:
if (ret)
@@ -1461,8 +1529,17 @@ found_free_rec:
goto undo_mftbmp_alloc;
}
/*
* Retrieve the former seq_no and usn so that the new record
* cannot be mistaken for the former one.
* However the original record may just be garbage, so
* use some sensible value when they cannot be retrieved.
*/
seq_no = m->sequence_number;
usn = *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs));
if (le16_to_cpu(m->usa_ofs) <= (NTFS_BLOCK_SIZE - 2))
usn = *(le16*)((u8*)m + (le16_to_cpu(m->usa_ofs) & -2));
else
usn = const_cpu_to_le16(1);
if (ntfs_mft_record_layout(vol, bit, m)) {
ntfs_log_error("Failed to re-format mft record.\n");
free(m);

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) 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) {
case IO_REPARSE_TAG_AF_UNIX :
case IO_REPARSE_TAG_LX_FIFO :
case IO_REPARSE_TAG_LX_CHR :
case IO_REPARSE_TAG_LX_BLK :
if (!reparse->reparse_data_length
&& (ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN))
res = 0;
break;
default :
break;
}
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,6 +516,22 @@ static BOOL valid_reparse_data(ntfs_inode *ni,
+ offs + lth)) > size)
ok = FALSE;
break;
case 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))
|| (wsl_reparse_data->type != const_cpu_to_le32(2)))
ok = FALSE;
break;
case IO_REPARSE_TAG_AF_UNIX :
case IO_REPARSE_TAG_LX_FIFO :
case IO_REPARSE_TAG_LX_CHR :
case IO_REPARSE_TAG_LX_BLK :
if (reparse_attr->reparse_data_length
|| !(ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN))
ok = FALSE;
break;
default :
break;
}
@@ -604,8 +659,9 @@ static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction,
* or NULL if there were some problem, as described by errno
*/
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 +707,11 @@ 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 +736,11 @@ 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 +792,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;
@@ -816,6 +875,22 @@ char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point)
break;
}
break;
case IO_REPARSE_TAG_LX_SYMLINK :
wsl_link_data = (struct WSL_LINK_REPARSE_DATA*)
reparse_attr->reparse_data;
if (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;
}
}
break;
}
free(reparse_attr);
}
@@ -845,6 +920,7 @@ BOOL ntfs_possible_symlink(ntfs_inode *ni)
switch (reparse_attr->reparse_tag) {
case IO_REPARSE_TAG_MOUNT_POINT :
case IO_REPARSE_TAG_SYMLINK :
case IO_REPARSE_TAG_LX_SYMLINK :
possible = TRUE;
default : ;
}
@@ -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

@@ -77,7 +77,7 @@
#include "security.h"
const char *ntfs_home =
"News, support and information: http://tuxera.com\n";
"News, support and information: https://github.com/tuxera/ntfs-3g/\n";
static const char *invalid_ntfs_msg =
"The device '%s' doesn't seem to have a valid NTFS.\n"
@@ -121,7 +121,7 @@ static const char *fakeraid_msg =
static const char *access_denied_msg =
"Please check '%s' and the ntfs-3g binary permissions,\n"
"and the mounting user ID. More explanation is provided at\n"
"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n";
"https://github.com/tuxera/ntfs-3g/wiki/NTFS-3G-FAQ\n";
/**
* ntfs_volume_alloc - Create an NTFS volume object and initialise it
@@ -221,15 +221,24 @@ static int __ntfs_volume_release(ntfs_volume *v)
return errno ? -1 : 0;
}
static void ntfs_attr_setup_flag(ntfs_inode *ni)
static int ntfs_attr_setup_flag(ntfs_inode *ni)
{
STANDARD_INFORMATION *si;
s64 lth;
int r;
si = ntfs_attr_readall(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, NULL);
si = (STANDARD_INFORMATION*)ntfs_attr_readall(ni,
AT_STANDARD_INFORMATION, AT_UNNAMED, 0, &lth);
if (si) {
ni->flags = si->file_attributes;
if ((u64)lth >= offsetof(STANDARD_INFORMATION, owner_id))
ni->flags = si->file_attributes;
free(si);
r = 0;
} else {
ntfs_log_error("Failed to get standard information of $MFT\n");
r = -1;
}
return (r);
}
/**
@@ -303,16 +312,19 @@ static int ntfs_mft_load(ntfs_volume *vol)
ntfs_log_error("Failed to get value of $MFT/$ATTR_LIST.\n");
goto io_error_exit;
}
if (l != vol->mft_ni->attr_list_size) {
if ((l != vol->mft_ni->attr_list_size)
|| (l < (s64)offsetof(ATTR_LIST_ENTRY, name))) {
ntfs_log_error("Partial read of $MFT/$ATTR_LIST (%lld != "
"%u).\n", (long long)l,
vol->mft_ni->attr_list_size);
"%u or < %d).\n", (long long)l,
vol->mft_ni->attr_list_size,
(int)offsetof(ATTR_LIST_ENTRY, name));
goto io_error_exit;
}
mft_has_no_attr_list:
ntfs_attr_setup_flag(vol->mft_ni);
if (ntfs_attr_setup_flag(vol->mft_ni))
goto error_exit;
/* We now have a fully setup ntfs inode for $MFT in vol->mft_ni. */
@@ -355,6 +367,11 @@ mft_has_no_attr_list:
ntfs_log_perror("ntfs_mapping_pairs_decompress() failed");
goto error_exit;
}
/* Make sure $DATA is the MFT itself */
if (nrl->lcn != vol->mft_lcn) {
ntfs_log_perror("The MFT is not self-contained");
goto error_exit;
}
vol->mft_na->rl = nrl;
/* Get the lowest vcn for the next extent. */
@@ -448,6 +465,12 @@ static int ntfs_mftmirr_load(ntfs_volume *vol)
ntfs_log_perror("Failed to map runlist of $MFTMirr/$DATA");
goto error_exit;
}
if (vol->mftmirr_na->rl->lcn != vol->mftmirr_lcn) {
ntfs_log_error("Bad $MFTMirr lcn 0x%llx, want 0x%llx\n",
(long long)vol->mftmirr_na->rl->lcn,
(long long)vol->mftmirr_lcn);
goto error_exit;
}
return 0;
@@ -601,6 +624,10 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev,
vol->mft_zone_end = vol->mft_lcn + mft_zone_size;
while (vol->mft_zone_end >= vol->nr_clusters) {
mft_zone_size >>= 1;
if (!mft_zone_size) {
errno = EINVAL;
goto error_exit;
}
vol->mft_zone_end = vol->mft_lcn + mft_zone_size;
}
ntfs_log_debug("mft_zone_end = 0x%llx\n", (long long)vol->mft_zone_end);
@@ -949,6 +976,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
}
goto error_exit;
}
for (i = 0; (i < l) && (i < FILE_first_user); ++i)
if (ntfs_mft_record_check(vol, FILE_MFT + i,
(MFT_RECORD*)(m + i*vol->mft_record_size)))
goto error_exit;
l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size,
vol->mft_record_size, m2);
if (l != vol->mftmirr_size) {
@@ -958,6 +989,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
}
vol->mftmirr_size = l;
}
for (i = 0; (i < l) && (i < FILE_first_user); ++i)
if (ntfs_mft_record_check(vol, FILE_MFT + i,
(MFT_RECORD*)(m2 + i*vol->mft_record_size)))
goto error_exit;
ntfs_log_debug("Comparing $MFTMirr to $MFT...\n");
/* Windows 10 does not update the full $MFTMirr any more */
for (i = 0; (i < vol->mftmirr_size) && (i < FILE_first_user); ++i) {
@@ -1048,19 +1083,19 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
if (!na) {
ntfs_log_perror("Failed to open ntfs attribute");
ntfs_inode_close(ni);
goto error_exit;
}
/*
* Note: Normally, the upcase table has a length equal to 65536
* 2-byte Unicode characters but allow for different cases, so no
* checks done. Just check we don't overflow 32-bits worth of Unicode
* characters.
* 2-byte Unicode characters. Anyway we currently can only process
* such characters.
*/
if (na->data_size & ~0x1ffffffffULL) {
ntfs_log_error("Error: Upcase table is too big (max 32-bit "
"allowed).\n");
if ((na->data_size - 2) & ~0x1fffeULL) {
ntfs_log_error("Error: Upcase table is invalid (want size even "
"<= 131072).\n");
errno = EINVAL;
goto error_exit;
goto bad_upcase;
}
if (vol->upcase_len != na->data_size >> 1) {
vol->upcase_len = na->data_size >> 1;
@@ -1068,7 +1103,7 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
free(vol->upcase);
vol->upcase = ntfs_malloc(na->data_size);
if (!vol->upcase)
goto error_exit;
goto bad_upcase;
}
/* Read in the $DATA attribute value into the buffer. */
l = ntfs_attr_pread(na, 0, na->data_size, vol->upcase);
@@ -1077,7 +1112,7 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
"(%lld != %lld).\n", (long long)l,
(long long)na->data_size);
errno = EIO;
goto error_exit;
goto bad_upcase;
}
/* Done with the $UpCase mft record. */
ntfs_attr_close(na);
@@ -1213,10 +1248,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
ntfs_log_perror("Failed to open ntfs attribute");
goto error_exit;
}
/* Check we don't overflow 32-bits. */
if (na->data_size > 0xffffffffLL) {
/* Check we don't overflow 24-bits. */
if ((u64)na->data_size > 0xffffffLL) {
ntfs_log_error("Attribute definition table is too big (max "
"32-bit allowed).\n");
"24-bit allowed).\n");
errno = EINVAL;
goto error_exit;
}
@@ -1282,6 +1317,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
}
return vol;
bad_upcase :
ntfs_attr_close(na);
ntfs_inode_close(ni);
goto error_exit;
io_error_exit:
errno = EIO;
error_exit:
@@ -1855,8 +1894,10 @@ int ntfs_volume_get_free_space(ntfs_volume *vol)
if (vol->free_mft_records < 0)
ntfs_log_perror("Failed to calculate free MFT records");
else
else {
NVolSetFreeSpaceKnown(vol);
ret = 0;
}
}
return (ret);
}

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

@@ -285,7 +285,7 @@ is part of the
package and is available from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
.BR badblocks (8),

View File

@@ -126,7 +126,7 @@ is part of the
package and is available from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
Read \fBlibntfs\fR(8) for details how to access encrypted files.

View File

@@ -616,7 +616,8 @@ static BOOL check_file_record(u8 *buffer, u16 buflen)
// Remove update seq & check it.
usa = *(u16*)(buffer+usa_ofs); // The value that should be at the end of every sector.
assert_u32_equal(usa_count-1, buflen/NTFS_BLOCK_SIZE, "USA length");
if (assert_u32_equal(usa_count-1, buflen/NTFS_BLOCK_SIZE, "USA length"))
return (1);
for (i=1;i<usa_count;i++) {
u16 *fixup = (u16*)(buffer+NTFS_BLOCK_SIZE*i-2); // the value at the end of the sector.
u16 saved_val = *(u16*)(buffer+usa_ofs+2*i); // the actual data value that was saved in the us array.

View File

@@ -387,7 +387,7 @@ is part of the
package and is available at:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
.BR ntfsresize (8)

View File

@@ -117,7 +117,7 @@ is part of the
package and is available from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
.BR ntfsinfo (8),

View File

@@ -67,7 +67,7 @@ is part of the
package and is available from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
.BR ntfsinfo (8),

View File

@@ -113,7 +113,7 @@ is part of the
package and is available from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g
.hy
.SH SEE ALSO
.BR ntfsprogs (8)

View File

@@ -1155,7 +1155,7 @@ close_attr:
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);
out->last_data_change_time = cpu_to_sle64(change_time);
ntfs_inode_update_times(out, 0);
} else {
ntfs_log_error("Failed to get the time stamp.\n");

View File

@@ -120,7 +120,7 @@ is part of the
package and is available from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
Read \fBntfs-3g\fR(8) for details on option efs_raw,

View File

@@ -128,7 +128,7 @@ is part of the
package and is available from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
.BR ntfs-3g (8),

View File

@@ -75,7 +75,7 @@ is part of the
package and is available from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
.BR mkntfs (8),

View File

@@ -780,14 +780,19 @@ static ATTR_RECORD *find_unnamed_attr(MFT_RECORD *mrec, ATTR_TYPES type)
{
ATTR_RECORD *a;
u32 offset;
s32 space;
/* fetch the requested attribute */
offset = le16_to_cpu(mrec->attrs_offset);
space = le32_to_cpu(mrec->bytes_in_use) - offset;
a = (ATTR_RECORD*)((char*)mrec + offset);
while ((offset < le32_to_cpu(mrec->bytes_in_use))
while ((space >= (s32)offsetof(ATTR_RECORD, resident_end))
&& (a->type != AT_END)
&& (le32_to_cpu(a->length) <= (u32)space)
&& !(le32_to_cpu(a->length) & 7)
&& ((a->type != type) || a->name_length)) {
offset += le32_to_cpu(a->length);
space -= le32_to_cpu(a->length);
a = (ATTR_RECORD*)((char*)mrec + offset);
}
if ((offset >= le32_to_cpu(mrec->bytes_in_use))
@@ -823,7 +828,8 @@ static BOOL short_mft_selfloc_condition(struct MFT_SELF_LOCATED *selfloc)
vol->mft_record_size, mft0)
== vol->mft_record_size)
&& !ntfs_mst_post_read_fixup((NTFS_RECORD*)mft0,
vol->mft_record_size)) {
vol->mft_record_size)
&& !ntfs_mft_record_check(vol, 0, mft0)) {
a = find_unnamed_attr(mft0,AT_DATA);
if (a
&& a->non_resident
@@ -961,7 +967,9 @@ static BOOL self_mapped_selfloc_condition(struct MFT_SELF_LOCATED *selfloc)
if ((ntfs_pread(vol->dev, offs, vol->mft_record_size,
mft1) == vol->mft_record_size)
&& !ntfs_mst_post_read_fixup((NTFS_RECORD*)mft1,
vol->mft_record_size)) {
vol->mft_record_size)
&& !ntfs_mft_record_check(vol, inum, mft1)) {
lowest_vcn = (SELFLOC_LIMIT*vol->mft_record_size)
>> vol->cluster_size_bits;
a = find_unnamed_attr(mft1,AT_DATA);
@@ -1017,7 +1025,8 @@ static BOOL spare_record_selfloc_condition(struct MFT_SELF_LOCATED *selfloc)
if ((ntfs_pread(vol->dev, offs, vol->mft_record_size,
mft2) == vol->mft_record_size)
&& !ntfs_mst_post_read_fixup((NTFS_RECORD*)mft2,
vol->mft_record_size)) {
vol->mft_record_size)
&& !ntfs_mft_record_check(vol, inum, mft2)) {
if (!mft2->base_mft_record
&& (mft2->flags & MFT_RECORD_IN_USE)
&& !find_unnamed_attr(mft2,AT_ATTRIBUTE_LIST)

View File

@@ -83,7 +83,7 @@ is part of the
package and is available from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
.BR ntfsprogs (8)

View File

@@ -439,6 +439,18 @@ static const char *reparse_type_name(le32 tag)
case IO_REPARSE_TAG_LX_SYMLINK :
name = " (Linux symlink)";
break;
case IO_REPARSE_TAG_LX_FIFO :
name = " (Linux fifo)";
break;
case IO_REPARSE_TAG_LX_CHR :
name = " (Linux character device)";
break;
case IO_REPARSE_TAG_LX_BLK :
name = " (Linux block device)";
break;
case IO_REPARSE_TAG_AF_UNIX :
name = " (Unix socket)";
break;
case IO_REPARSE_TAG_APPEXECLINK :
name = " (Exec link)";
break;
@@ -622,6 +634,10 @@ static void ntfs_dump_flags(const char *indent, ATTR_TYPES type, le32 flags)
printf(" VIEW_INDEX");
flags &= ~FILE_ATTR_VIEW_INDEX_PRESENT;
}
if (flags & FILE_ATTRIBUTE_RECALL_ON_OPEN) {
printf(" RECALL_ON_OPEN");
flags &= ~FILE_ATTRIBUTE_RECALL_ON_OPEN;
}
if (flags)
printf(" UNKNOWN: 0x%08x", (unsigned int)le32_to_cpu(flags));
/* Print all the flags in hex. */

View File

@@ -112,7 +112,7 @@ is part of the
package and is available from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
.BR mkntfs (8),

View File

@@ -166,7 +166,7 @@ is part of the
package and is available from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
.BR ntfsprogs (8)

View File

@@ -74,7 +74,7 @@ are part of the
package which can be downloaded from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
.BR ntfs\-3g (8)

View File

@@ -164,7 +164,7 @@ is part of the
package and is available from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
.BR ntfs-3g (8),

View File

@@ -305,7 +305,7 @@ is part of the
package and is available from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
.BR fdisk (8),

View File

@@ -160,7 +160,7 @@ of 1 when an error was detected.
Please see
.RS
.sp
http://www.tuxera.com/community/ntfs-3g-faq/
https://github.com/tuxera/ntfs-3g/wiki/NTFS-3G-FAQ/
.sp
.RE
for common questions and known issues.

View File

@@ -116,7 +116,7 @@ is part of the
package and is available from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
.BR ntfs-3g (8),

View File

@@ -317,7 +317,7 @@ is part of the
package and is available from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
.BR ntfsinfo (8),

View File

@@ -62,8 +62,6 @@ Map the users defined on the Windows system present on /dev/sda1 :
.sp
.RE
.PP
A detailed example, with screen displays is available on
http://jp-andre.pagesperso-orange.fr/ntfsusermap.html
.SH EXIT CODES
.B ntfsusermap
exits with a value of 0 when no error was detected, and with a value
@@ -72,7 +70,7 @@ of 1 when an error was detected.
Please see
.RS
.sp
http://www.tuxera.com/community/ntfs-3g-faq/
https://github.com/tuxera/ntfs-3g/wiki/NTFS-3G-FAQ/
.sp
.RE
for common questions and known issues.

View File

@@ -126,7 +126,7 @@ is part of the
package and is available from:
.br
.nh
http://www.tuxera.com/community/
https://github.com/tuxera/ntfs-3g/wiki/
.hy
.SH SEE ALSO
.BR ntfs-3g (8),

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 "

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-2020 Jean-Pierre Andre
* Copyright (c) 2007-2021 Jean-Pierre Andre
* Copyright (c) 2009 Erik Larsson
*
* This file is originated from the Linux-NTFS project.
@@ -102,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"
@@ -261,8 +262,8 @@ static const char *usage_msg =
"\n"
"Copyright (C) 2005-2007 Yura Pakhuchiy\n"
"Copyright (C) 2006-2009 Szabolcs Szakacsits\n"
"Copyright (C) 2007-2020 Jean-Pierre Andre\n"
"Copyright (C) 2009 Erik Larsson\n"
"Copyright (C) 2007-2021 Jean-Pierre Andre\n"
"Copyright (C) 2009-2020 Erik Larsson\n"
"\n"
"Usage: %s [-o option[,...]] <device|image_file> <mount_point>\n"
"\n"
@@ -277,7 +278,9 @@ static const char *usage_msg =
#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);
@@ -297,13 +300,13 @@ static const char *setuid_msg =
"external FUSE library. Either remove the setuid/setgid bit from the binary\n"
"or rebuild NTFS-3G with integrated FUSE support and make it setuid root.\n"
"Please see more information at\n"
"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n";
"https://github.com/tuxera/ntfs-3g/wiki/NTFS-3G-FAQ\n";
static const char *unpriv_fuseblk_msg =
"Unprivileged user can not mount NTFS block devices using the external FUSE\n"
"library. Either mount the volume as root, or rebuild NTFS-3G with integrated\n"
"FUSE support and make it setuid root. Please see more information at\n"
"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n";
"https://github.com/tuxera/ntfs-3g/wiki/NTFS-3G-FAQ\n";
#endif
@@ -664,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);
@@ -675,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) {
case IO_REPARSE_TAG_AF_UNIX :
stbuf->st_mode = S_IFSOCK;
break;
case IO_REPARSE_TAG_LX_FIFO :
stbuf->st_mode = S_IFIFO;
break;
case IO_REPARSE_TAG_LX_CHR :
stbuf->st_mode = S_IFCHR;
res = ntfs_ea_check_wsldev(ni, &rdev);
stbuf->st_rdev = rdev;
break;
case IO_REPARSE_TAG_LX_BLK :
stbuf->st_mode = S_IFBLK;
res = ntfs_ea_check_wsldev(ni, &rdev);
stbuf->st_rdev = rdev;
break;
default :
stbuf->st_size = ntfs_bad_reparse_lth;
stbuf->st_mode = S_IFLNK;
break;
}
}
/*
* 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
*/
@@ -722,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;
@@ -744,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 =
@@ -1035,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;
}
@@ -1074,26 +1132,42 @@ static void ntfs_fuse_readlink(fuse_req_t req, fuse_ino_t ino)
* Reparse point : analyze as a junction point
*/
if (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) {
buf = strdup(ntfs_bad_reparse);
res = (buf ? 0 : -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. */
@@ -1149,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,
@@ -2312,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).
@@ -2336,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 (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) {
/*
@@ -2525,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 (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) {
@@ -2583,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) {
@@ -2622,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
== (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ? RM_LINK : RM_DIR)) {
errno = EPERM;
isdir = ni->mrec->flags & MFT_RECORD_IS_DIRECTORY;
#ifndef DISABLE_PLUGINS
/* get emulated type from plugin if available */
if (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;
}
@@ -2712,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 (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:
@@ -3656,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.),
@@ -3903,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.),
@@ -4027,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 */
@@ -4181,8 +4347,7 @@ static int ntfs_open(const char *device)
if (ctx->ignore_case && ntfs_set_ignore_case(vol))
goto err_out;
vol->free_clusters = ntfs_attr_get_free_bits(vol->lcnbmp_na);
if (vol->free_clusters < 0) {
if (ntfs_volume_get_free_space(ctx->vol)) {
ntfs_log_perror("Failed to read NTFS $Bitmap");
goto err_out;
}
@@ -4202,9 +4367,12 @@ static int ntfs_open(const char *device)
}
errno = 0;
goto out;
err_out:
if (!errno) /* Make sure to return an error */
errno = EIO;
out :
return ntfs_volume_error(errno);
}
static void usage(void)
@@ -4230,7 +4398,7 @@ static const char *fuse26_kmod_msg =
" message to disappear then you should upgrade to at least kernel\n"
" version 2.6.20, or request help from your distribution to fix\n"
" the kernel problem. The below web page has more information:\n"
" http://tuxera.com/community/ntfs-3g-faq/#fuse26\n"
" https://github.com/tuxera/ntfs-3g/wiki/NTFS-3G-FAQ\n"
"\n";
static void mknod_dev_fuse(const char *dev)
@@ -4575,6 +4743,7 @@ int main(int argc, char *argv[])
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

@@ -4,7 +4,7 @@
.\" Copyright (c) 2009-2014 Jean-Pierre Andre
.\" This file may be copied under the terms of the GNU Public License.
.\"
.TH NTFS-3G 8 "Mar 2014" "ntfs-3g @VERSION@"
.TH NTFS-3G 8 "Aug 2021" "ntfs-3g @VERSION@"
.SH NAME
ntfs-3g \- Third Generation Read/Write NTFS Driver
.SH SYNOPSIS
@@ -35,7 +35,7 @@ It comes in two variants \fBntfs-3g\fR and \fBlowntfs-3g\fR with
a few differences mentioned below in relevant options descriptions.
.PP
The \fIvolume\fR to be mounted can be either a block device or
an image file.
an image file, either by using the mount command or starting the drive.
.SS Windows hibernation and fast restarting
On computers which can be dual-booted into Windows or Linux, Windows has
to be fully shut down before booting into Linux, otherwise the NTFS file
@@ -70,7 +70,7 @@ and
.B dmask
options.
.PP
Doing so, Windows users have full access to the files created by
Doing so, all Windows users have full access to the files created by
.B ntfs-3g.
.PP
But, by setting the \fBpermissions\fR option, you can benefit from the full
@@ -96,9 +96,9 @@ data stream and can have many named data streams. The size of a file is the
size of its unnamed data stream. By default, \fBntfs-3g\fR will only read
the unnamed data stream.
.PP
By using the options "streams_interface=windows", with the ntfs-3g driver
By using the option \fBstreams_interface=windows\fP, with the ntfs-3g driver
(not possible with lowntfs-3g), you will be able to read any named data
streams, simply by specifying the stream's name after a colon.
streams, simply by specifying the stream name after a colon.
For example:
.RS
.sp
@@ -107,45 +107,10 @@ cat some.mp3:artist
.RE
Named data streams act like normal files, so you can read from them, write to
them and even delete them (using rm). You can list all the named data streams
a file has by getting the "ntfs.streams.list" extended attribute.
a file has by getting the \fBntfs.streams.list\fP extended attribute.
.SH OPTIONS
Below is a summary of the options that \fBntfs-3g\fR accepts.
.TP
\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP
Set the owner and the group of files and directories. The values are numerical.
The defaults are the uid and gid of the current process.
.TP
.BI umask= value
Set the bitmask of the file and directory permissions that are not
present. The value is given in octal. The default value is 0 which
means full access to everybody.
.TP
.BI fmask= value
Set the bitmask of the file permissions that are not present.
The value is given in octal. The default value is 0 which
means full access to everybody.
.TP
.BI dmask= value
Set the bitmask of the directory permissions that are not
present. The value is given in octal. The default value is 0 which
means full access to everybody.
.TP
.BI usermapping= file-name
Use file \fIfile-name\fP as the user mapping file instead of the default
\fB.NTFS-3G/UserMapping\fP. If \fIfile-name\fP defines a full path, the
file must be located on a partition previously mounted. If it defines a
relative path, it is interpreted relative to the root of NTFS partition
being mounted.
.P
.RS
When a user mapping file is defined, the options \fBuid=\fP, \fBgid=\fP,
\fBumask=\fP, \fBfmask=\fP, \fBdmask=\fP and \fBsilent\fP are ignored.
.RE
.TP
.B permissions
Set standard permissions on created files and use standard access control.
This option is set by default when a user mapping file is present.
.TP
.B acl
Enable setting Posix ACLs on created files and use them for access control.
This option is only available on specific builds. It is set by default
@@ -153,46 +118,11 @@ when a user mapping file is present and the
.B permissions
mount option is not set.
.TP
.B inherit
When creating a new file, set its initial protections
according to inheritance rules defined in parent directory. These rules
deviate from Posix specifications, but yield a better Windows
compatibility. The \fBpermissions\fR option or a valid user mapping file
is required for this option to be effective.
.TP
.B ro
Mount filesystem read\-only. Useful if Windows is hibernated or the
NTFS journal file is unclean.
.TP
.BI locale= value
This option can be useful when wanting a language specific locale environment.
It is however discouraged as it leads to files with untranslatable chars
to not be visible.
.TP
.B force
This option is obsolete. It has been superseded by the \fBrecover\fR and
\fBnorecover\fR options.
.TP
.B recover
Recover and try to mount a partition which was not unmounted properly by
Windows. The Windows logfile is cleared, which may cause inconsistencies.
Currently this is the default option.
.TP
.B norecover
Do not try to mount a partition which was not unmounted properly by Windows.
.TP
.B ignore_case \fP(only with lowntfs-3g)
Ignore character case when accessing a file (\fBFOO\fR, \fBFoo\fR, \fBfoo\fR,
etc. designate the same file). All files are displayed with lower case in
directory listings.
.TP
.B remove_hiberfile
When the NTFS volume is hibernated, a read-write mount is denied and
a read-only mount is forced. One needs either to resume Windows and
shutdown it properly, or use this option which will remove the Windows
hibernation file. Please note, this means that the saved Windows
session will be completely lost. Use this option under your own
responsibility.
.B allow_other
This option overrides the security measure restricting file access
to the user mounting the filesystem. This option is only
allowed to root, but this restriction can be overridden by
the \fBuser_allow_other\fP option in the /etc/fuse.conf file.
.TP
.B atime, noatime, relatime
The
@@ -201,7 +131,7 @@ option updates inode access time for each access.
The
.B noatime
option disables inode access time updates which can speed up
option disables inode access time updates, which can speed up
file operations and prevent sleeping (notebook) disks spinning
up too often thus saving energy and disk lifetime.
@@ -217,6 +147,23 @@ this option doesn't break applications that need to know
if a file has been read since the last time it was modified.
This is the default behaviour.
.TP
.B big_writes
This option prevents fuse from splitting write buffers into 4K chunks,
enabling big write buffers to be transferred from the application in a
single step (up to some system limit, generally 128K bytes).
.TP
.B compression
This option enables creating new transparently compressed files in
directories marked for compression. A directory is marked for compression by
setting the bit 11 (value 0x00000800) in its Windows attribute. In such a
directory, new files are created compressed and new subdirectories are
themselves marked for compression. The option and the flag have no effect
on existing files. Currently this is the default option.
.TP
.B debug
Makes ntfs-3g (or lowntfs-3g) to print a lot of debug output from libntfs-3g
and FUSE.
.TP
.B delay_mtime[= value]
Only update the file modification time and the file change time of a file
when it is closed or when the indicated delay since the previous update has
@@ -225,22 +172,26 @@ This is mainly useful for big files which are kept open for a long
time and written to without changing their size, such as databases or file
system images mounted as loop.
.TP
.B show_sys_files
Show the metafiles in directory listings. Otherwise the default behaviour is
to hide the metafiles, which are special files used to store the NTFS
structure. Please note that even when this option is specified, "$MFT" may
not be visible due to a glibc bug. Furthermore, irrespectively of
show_sys_files, all files are accessible by name, for example you can always
do
"ls \-l '$UpCase'".
.BI dmask= value
Set the bitmask of the directory permissions that are not
present. The value is given in octal. The default value is 0 which
means full access to everybody.
.TP
.B hide_hid_files
Hide the hidden files and directories in directory listings, the hidden files
and directories being the ones whose NTFS attribute have the hidden flag set.
The hidden files will not be selected when using wildcards in commands,
but all files and directories remain accessible by full name, for example you
can always display the Windows trash bin directory by :
"ls \-ld '$RECYCLE.BIN'".
.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
write operation so that encrypted files can be saved and restored
without being decrypted. The \fBuser.ntfs.efsinfo\fP extended attribute
has also to be saved and restored for the file to be decrypted.
.TP
.BI fmask= value
Set the bitmask of the file permissions that are not present.
The value is given in octal. The default value is 0 which
means full access to everybody.
.TP
.B force
This option is obsolete. It has been superseded by the \fBrecover\fR and
\fBnorecover\fR options.
.TP
.B hide_dot_files
Set the hidden flag in the NTFS attribute for created files and directories
@@ -250,13 +201,141 @@ 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 hide_hid_files
Hide the hidden files and directories in directory listings, the hidden files
and directories being the ones whose NTFS attribute have the hidden flag set.
The hidden files will not be selected when using wildcards in commands,
but all files and directories remain accessible by full name, for example you
can always display the Windows trash bin directory by :
"ls \-ld '$RECYCLE.BIN'".
.TP
.B ignore_case \fP(only with lowntfs-3g)
Ignore character case when accessing a file (\fBFOO\fR, \fBFoo\fR, \fBfoo\fR,
etc. designate the same file). All files are displayed with lower case in
directory listings.
.TP
.B inherit
When creating a new file, set its initial protections
according to inheritance rules defined in parent directory. These rules
deviate from Posix specifications, but yield a better Windows
compatibility. The \fBpermissions\fR (or **acl**) option or a valid user
mapping file is required for this option to be effective.
.TP
.BI locale= value
This option can be useful when wanting a language specific locale environment.
It is however discouraged as it leads to files with untranslatable characters
to not be visible.
.TP
.BI max_read= value
With this option the maximum size of read operations can be set.
The default is infinite. Note that the size of read requests is
limited anyway by the system (usually to 128kbyte).
.TP
.B no_def_opts
By default ntfs-3g acts as if \fBsilent\fP (ignore permission errors when
permissions are not enabled),
\fBallow_other\fP (allow any user to access files) and \fBnonempty\fP
(allow mounting on non-empty directories) were set, and \fBno_def_opts\fP
cancels these default options.
.TP
.B no_detach
Makes ntfs-3g to not detach from terminal and print some debug output.
.TP
.B nocompression
This option disables creating new transparently compressed files in directories
marked for compression. Existing compressed files can still be read and
updated.
.TP
.B norecover
Do not try to mount a partition which was not unmounted properly by Windows.
.TP
.B permissions
Set standard permissions on created files and use standard access control.
This option is set by default when a user mapping file is present.
.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
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 recover
Recover and try to mount a partition which was not unmounted properly by
Windows. The Windows logfile is cleared, which may cause inconsistencies.
Currently this is the default option.
.TP
.B remove_hiberfile
When the NTFS volume is hibernated, a read-write mount is denied and
a read-only mount is forced. One needs either to resume Windows and
shutdown it properly, or use this option which will remove the Windows
hibernation file. Please note, this means that the saved Windows
session will be completely lost. Use this option under your own
responsibility.
.TP
.B ro
Mount the filesystem read\-only. Useful if Windows is hibernated or the
NTFS journal file is unclean.
.TP
.B show_sys_files
Show the metafiles in directory listings. Otherwise the default behaviour is
to hide the metafiles, which are special files used to store the NTFS
structure. Please note that even when this option is specified, "$MFT" may
not be visible due to a glibc bug. Furthermore, irrespectively of
\fBshow_sys_files\fP, all files are accessible by name, for example you can
always do
"ls \-l '$UpCase'".
.TP
.B silent
Do nothing, without returning any error, on chmod and chown operations
and on permission checking errors,
when the \fBpermissions\fR option is not set and no user mapping file
is defined. This option is on by default, and when set off (through option
\fBno_def_opts\fR) ownership and permissions parameters have to be set.
.TP
.BI special_files= mode
This option selects a mode for representing a special file to be created
(symbolic link, socket, fifo, character or block device). The \fImode\fP 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.
Neither mode are interoperable with Windows.
.TP
.BI streams_interface= mode
This option controls how the user can access Alternate Data Streams (ADS) or in
other words, named data streams. The \fImode\fP can be set to one of \fBnone\fR,
\fBwindows\fR or \fBxattr\fR. If the option is set to \fBnone\fR, the user
will have no access to the named data streams. If it is set to \fBwindows\fR
(not possible with lowntfs-3g), then the user can access them just like in
Windows (eg. cat file:stream). If it's set to \fBxattr\fR, then the named
data streams are mapped to extended attributes and a user can manipulate them
using \fB{get,set}fattr\fR utilities. The default is \fBxattr\fR.
.TP
\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP
Set the owner and the group of files and directories. The values are numerical.
The defaults are the uid and gid of the current process.
.TP
.BI umask= value
Set the bitmask of the file and directory permissions that are not
present. The value is given in octal. The default value is 0 which
means full access to everybody.
.TP
.BI usermapping= file-name
Use file \fIfile-name\fP as the user mapping file instead of the default
\fB.NTFS-3G/UserMapping\fP. If \fIfile-name\fP defines a full path, the
file must be located on a partition previously mounted. If it defines a
relative path, it is interpreted relative to the root of NTFS partition
being mounted.
.P
.RS
When a user mapping file is defined, the options \fBuid=\fP, \fBgid=\fP,
\fBumask=\fP, \fBfmask=\fP, \fBdmask=\fP and \fBsilent\fP are ignored.
.RE
.TP
.B user_xattr
Same as \fBstreams_interface=\fP\fIxattr\fP.
.TP
.B windows_names
This option prevents files, directories and extended attributes to be
created with a name not allowed by windows, because
@@ -277,84 +356,15 @@ with no suffix or followed by a dot.
.sp
Existing such files can still be read (and renamed).
.RE
.TP
.B allow_other
This option overrides the security measure restricting file access
to the user mounting the filesystem. This option is only
allowed to root, but this restriction can be overridden by
the 'user_allow_other' option in the /etc/fuse.conf file.
.TP
.BI max_read= value
With this option the maximum size of read operations can be set.
The default is infinite. Note that the size of read requests is
limited anyway to 32 pages (which is 128kbyte on i386).
.TP
.B silent
Do nothing, without returning any error, on chmod and chown operations
and on permission checking errors,
when the \fBpermissions\fR option is not set and no user mapping file
is defined. This option is on by default, and when set off (through option
\fBno_def_opts\fR) ownership and permissions parameters have to be set.
.TP
.B no_def_opts
By default ntfs-3g acts as if "silent" (ignore permission errors when
permissions are not enabled),
"allow_other" (allow any user to access files) and "nonempty"
(allow mounting on non-empty directories) were set, and "no_def_opts"
cancels these default options.
.TP
.BI streams_interface= value
This option controls how the user can access Alternate Data Streams (ADS) or
in other words, named data streams. It can be set to, one of \fBnone\fR,
\fBwindows\fR or \fBxattr\fR. If the option is set to \fBnone\fR, the user
will have no access to the named data streams. If it is set to \fBwindows\fR
(not possible with lowntfs-3g), then the user can access them just like in
Windows (eg. cat file:stream). If it's set to \fBxattr\fR, then the named
data streams are mapped to xattrs and user can manipulate them using
\fB{get,set}fattr\fR utilities. The default is \fBxattr\fR.
.TP
.B user_xattr
Same as \fBstreams_interface=\fP\fIxattr\fP.
.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
write operation so that encrypted files can be saved and restored
without being decrypted. The \fBuser.ntfs.efsinfo\fP extended attribute
has also to be saved and restored for the file to be decrypted.
.TP
.B compression
This option enables creating new transparently compressed files in
directories marked for compression. A directory is marked for compression by
setting the bit 11 (value 0x00000800) in its Windows attribute. In such a
directory, new files are created compressed and new subdirectories are
themselves marked for compression. The option and the flag have no effect
on existing files. Currently this is the default option.
.TP
.B nocompression
This option disables creating new transparently compressed files in directories
marked for compression. Existing compressed files can still be read and
updated.
.TP
.B big_writes
This option prevents fuse from splitting write buffers into 4K chunks,
enabling big write buffers to be transferred from the application in a
single step (up to some system limit, generally 128K bytes).
.TP
.B debug
Makes ntfs-3g to print a lot of debug output from libntfs-3g and FUSE.
.TP
.B no_detach
Makes ntfs-3g to not detach from terminal and print some debug output.
.SH USER MAPPING
NTFS uses specific ids to record the ownership of files instead of
the \fBuid\fP and \fBgid\fP used by Linux. As a consequence a mapping
between the ids has to be defined for ownerships to be recorded into
NTFS and recognized.
the \fBuid\fP (user id) and \fBgid\fP (group id) used by Linux. As a
consequence a mapping between the ids has to be defined for ownerships
to be recorded into NTFS files and recognized.
.P
By default, this mapping is fetched from the file \fB.NTFS-3G/UserMapping\fP
located in the NTFS partition. The option \fBusermapping=\fP may be used
to define another location. When the option permissions is set and
to define another location. When the option **permissions** is set and
no mapping file is found, a default mapping is used.
.P
Each line in the user mapping file defines a mapping. It is organized
@@ -371,15 +381,15 @@ both cases, files created on Linux will appear to Windows as owned by a
foreign user, and files created on Windows will appear to Linux as owned by
root. Just copy the example below and replace the 9 and 10-digit numbers by
any number not greater than 4294967295. The resulting behavior is the same as
the one with the option permission set with no ownership option and no user
mapping file available.
the one with the option \fBpermission\fP set with no ownership option and no
user mapping file available.
.RS
.sp
.B ::S-1-5-21-3141592653-589793238-462643383-10000
.sp
.RE
If a strong interoperation with Windows is needed, the mapping has to be
defined for each user and group known in both system, and the \fBSID\fPs used
defined for each user and group known to both system, and the \fBSID\fPs used
by Windows has to be collected. This will lead to a user mapping file like :
.RS
.sp
@@ -446,15 +456,18 @@ manual page.
Please see
.RS
.sp
http://www.tuxera.com/support/
https://github.com/tuxera/ntfs-3g/wiki/NTFS-3G-FAQ
.sp
.RE
for common questions and known issues.
If you would find a new one in the latest release of
the software then please send an email describing it
in detail. You can contact the
development team on the ntfs\-3g\-devel@lists.sf.net
address.
the software then please post an ntfs-3g issue describing it in detail
so that the development team can be aware of the issue and take care of it:
.RS
.sp
https://github.com/tuxera/ntfs-3g/issues
.sp
.RE
.SH AUTHORS
.B ntfs-3g
was based on and a major improvement to ntfsmount and libntfs which were
@@ -464,7 +477,8 @@ Linux-NTFS team developer Szabolcs Szakacsits (szaka@tuxera.com).
.SH THANKS
Several people made heroic efforts, often over five or more
years which resulted the ntfs-3g driver. Most importantly they are
Anton Altaparmakov, Jean-Pierre André, Richard Russon, Szabolcs Szakacsits,
Anton Altaparmakov, Jean-Pierre André, Erik Larsson, Richard Russon,
Szabolcs Szakacsits,
Yura Pakhuchiy, Yuval Fledel, and the author of the groundbreaking FUSE
filesystem development framework, Miklos Szeredi.
.SH SEE ALSO

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-2020 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,8 +197,8 @@ static const char *usage_msg =
"\n"
"Copyright (C) 2005-2007 Yura Pakhuchiy\n"
"Copyright (C) 2006-2009 Szabolcs Szakacsits\n"
"Copyright (C) 2007-2020 Jean-Pierre Andre\n"
"Copyright (C) 2009 Erik Larsson\n"
"Copyright (C) 2007-2021 Jean-Pierre Andre\n"
"Copyright (C) 2009-2020 Erik Larsson\n"
"\n"
"Usage: %s [-o option[,...]] <device|image_file> <mount_point>\n"
"\n"
@@ -212,7 +213,9 @@ static const char *usage_msg =
#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);
@@ -232,13 +235,13 @@ static const char *setuid_msg =
"external FUSE library. Either remove the setuid/setgid bit from the binary\n"
"or rebuild NTFS-3G with integrated FUSE support and make it setuid root.\n"
"Please see more information at\n"
"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n";
"https://github.com/tuxera/ntfs-3g/wiki/NTFS-3G-FAQ\n";
static const char *unpriv_fuseblk_msg =
"Unprivileged user can not mount NTFS block devices using the external FUSE\n"
"library. Either mount the volume as root, or rebuild NTFS-3G with integrated\n"
"FUSE support and make it setuid root. Please see more information at\n"
"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n";
"https://github.com/tuxera/ntfs-3g/wiki/NTFS-3G-FAQ\n";
#endif
@@ -722,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);
@@ -733,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) {
case IO_REPARSE_TAG_AF_UNIX :
stbuf->st_mode = S_IFSOCK;
break;
case IO_REPARSE_TAG_LX_FIFO :
stbuf->st_mode = S_IFIFO;
break;
case IO_REPARSE_TAG_LX_CHR :
stbuf->st_mode = S_IFCHR;
res = ntfs_ea_check_wsldev(ni, &rdev);
stbuf->st_rdev = rdev;
break;
case IO_REPARSE_TAG_LX_BLK :
stbuf->st_mode = S_IFBLK;
res = ntfs_ea_check_wsldev(ni, &rdev);
stbuf->st_rdev = rdev;
break;
default :
stbuf->st_size = ntfs_bad_reparse_lth;
stbuf->st_mode = S_IFLNK;
break;
}
}
/*
* 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
*/
@@ -807,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;
@@ -830,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;
@@ -1052,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;
}
@@ -1077,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);
@@ -1098,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;
@@ -1119,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. */
@@ -2043,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).
@@ -2067,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 (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) {
/*
@@ -2309,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 (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);
@@ -2384,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 (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)
@@ -3756,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 */
@@ -3825,6 +3965,9 @@ static struct fuse_operations ntfs_3g_ops = {
.rmdir = ntfs_fuse_rmdir,
#ifdef HAVE_UTIMENSAT
.utimens = ntfs_fuse_utimens,
#if defined(linux) & !defined(FUSE_INTERNAL) & (FUSE_VERSION < 30)
.flag_utime_omit_ok = 1,
#endif /* defined(linux) & !defined(FUSE_INTERNAL) */
#else
.utime = ntfs_fuse_utime,
#endif
@@ -3913,8 +4056,7 @@ static int ntfs_open(const char *device)
!ctx->hide_hid_files, ctx->hide_dot_files))
goto err_out;
ctx->vol->free_clusters = ntfs_attr_get_free_bits(ctx->vol->lcnbmp_na);
if (ctx->vol->free_clusters < 0) {
if (ntfs_volume_get_free_space(ctx->vol)) {
ntfs_log_perror("Failed to read NTFS $Bitmap");
goto err_out;
}
@@ -3933,9 +4075,12 @@ static int ntfs_open(const char *device)
}
errno = 0;
goto out;
err_out:
if (!errno)
errno = EIO;
out :
return ntfs_volume_error(errno);
}
static void usage(void)
@@ -3961,7 +4106,7 @@ static const char *fuse26_kmod_msg =
" message to disappear then you should upgrade to at least kernel\n"
" version 2.6.20, or request help from your distribution to fix\n"
" the kernel problem. The below web page has more information:\n"
" http://tuxera.com/community/ntfs-3g-faq/#fuse26\n"
" https://github.com/tuxera/ntfs-3g/wiki/NTFS-3G-FAQ\n"
"\n";
static void mknod_dev_fuse(const char *dev)
@@ -4328,6 +4473,7 @@ int main(int argc, char *argv[])
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

@@ -63,14 +63,18 @@ Unclassified FUSE error.
Please see
.RS
.sp
http://tuxera.com/community/ntfs-3g-faq/
https://github.com/tuxera/ntfs-3g/wiki/NTFS-3G-FAQ
.sp
.RE
for common questions and known issues.
If you think you have found an undocumented problem in the latest release of
the software then please send an email describing it in detail.
You can contact the development team on the ntfs\-3g\-devel@lists.sf.net
address.
the software then please post an ntfs-3g issue describing it in detail
so that the development team can be aware of the issue and take care of it:
.RS
.sp
https://github.com/tuxera/ntfs-3g/issues
.sp
.RE
.SH AUTHORS
.B ntfs-3g.probe
was written by Szabolcs Szakacsits.

View File

@@ -1,7 +1,7 @@
/**
* ntfs-3g_common.c - Common definitions for ntfs-3g and lowntfs-3g.
*
* Copyright (c) 2010-2020 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
@@ -127,6 +127,11 @@ const struct DEFOPTION optionlist[] = {
{ "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 },
{ "--help", OPT_HELP, FLGOPT_BOGUS },
{ "-h", OPT_HELP, FLGOPT_BOGUS },
{ "--version", OPT_VERSION, FLGOPT_BOGUS },
{ "-V", OPT_VERSION, FLGOPT_BOGUS },
{ (const char*)NULL, 0, 0 } /* end marker */
} ;
@@ -503,12 +508,25 @@ char *parse_mount_options(ntfs_fuse_context_t *ctx,
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
* mounted or not.
* (falling through to default)
*/
case OPT_HELP : /* Could lead to unclean condition */
case OPT_VERSION : /* Could lead to unclean condition */
default :
ntfs_log_error("'%s' is an unsupported option.\n",
poptl->name);

View File

@@ -93,6 +93,9 @@ enum {
OPT_XATTRMAPPING,
OPT_EFS_RAW,
OPT_POSIX_NLINK,
OPT_SPECIAL_FILES,
OPT_HELP,
OPT_VERSION,
} ;
/* Option flags */
@@ -155,6 +158,7 @@ typedef struct {
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