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:
@@ -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
6
NEWS
@@ -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
52
README
@@ -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.
|
||||
|
||||
12
configure.ac
12
configure.ac
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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__;
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
241
libntfs-3g/dir.c
241
libntfs-3g/dir.c
@@ -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);
|
||||
|
||||
|
||||
136
libntfs-3g/ea.c
136
libntfs-3g/ea.c
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, <h);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 "
|
||||
|
||||
299
src/lowntfs-3g.c
299
src/lowntfs-3g.c
@@ -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 */
|
||||
|
||||
374
src/ntfs-3g.8.in
374
src/ntfs-3g.8.in
@@ -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
|
||||
|
||||
246
src/ntfs-3g.c
246
src/ntfs-3g.c
@@ -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 */
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user