From 7bcae8743fcecfa74ee7d0692c176dff1b057fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 18 Nov 2020 11:29:05 +0100 Subject: [PATCH 01/83] Allowed customization of sbin for tool directory Enabled the configure option --sbindir to define where a few ntfsprogs tools should be installed --- ntfsprogs/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ntfsprogs/Makefile.am b/ntfsprogs/Makefile.am index f4f9d1b6..08228322 100644 --- a/ntfsprogs/Makefile.am +++ b/ntfsprogs/Makefile.am @@ -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) From 4b8a66000690c1e7c8393728a60ca78379d0b838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 18 Nov 2020 11:33:49 +0100 Subject: [PATCH 02/83] Fixed maintining the allocated size when updating sparse files When filling a hole in a sparse file, the beginning of the runlist does not need to be updated. However the allocated size is within the extent holding its beginning and it needs to be updated. --- libntfs-3g/attrib.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 7d5d5374..a736a9a2 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -5,7 +5,7 @@ * Copyright (c) 2002-2005 Richard Russon * Copyright (c) 2002-2008 Szabolcs Szakacsits * Copyright (c) 2004-2007 Yura Pakhuchiy - * Copyright (c) 2007-2015 Jean-Pierre Andre + * Copyright (c) 2007-2020 Jean-Pierre Andre * Copyright (c) 2010 Erik Larsson * * This program/include file is free software; you can redistribute it and/or @@ -5850,6 +5850,9 @@ retry: & (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 From 02673bd04a2b91db74c219fcba5cabb9e7eff4fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:17 +0100 Subject: [PATCH 03/83] Enabled actions on directories in reparse plugins The plugins triggered by reparse points can now act on a directory through link(2) unlink(2) and creat(2). --- include/ntfs-3g/plugin.h | 30 ++++++++++- libntfs-3g/dir.c | 7 +-- src/lowntfs-3g.c | 106 ++++++++++++++++++++++++++++----------- src/ntfs-3g.c | 100 ++++++++++++++++++++++++++---------- 4 files changed, 181 insertions(+), 62 deletions(-) diff --git a/include/ntfs-3g/plugin.h b/include/ntfs-3g/plugin.h index a9d56a5f..88e733c4 100644 --- a/include/ntfs-3g/plugin.h +++ b/include/ntfs-3g/plugin.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; diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 5e5baf62..a517ece9 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -1508,12 +1508,7 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, errno = EINVAL; 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; diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index f78cb9c8..c9d4a062 100644 --- a/src/lowntfs-3g.c +++ b/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. @@ -261,7 +261,7 @@ 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) 2007-2021 Jean-Pierre Andre\n" "Copyright (C) 2009 Erik Larsson\n" "\n" "Usage: %s [-o option[,...]] \n" @@ -2336,26 +2336,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 +2549,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) { @@ -2712,9 +2751,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: diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 2eb197a8..70effd7b 100644 --- a/src/ntfs-3g.c +++ b/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. @@ -196,7 +196,7 @@ 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) 2007-2021 Jean-Pierre Andre\n" "Copyright (C) 2009 Erik Larsson\n" "\n" "Usage: %s [-o option[,...]] \n" @@ -2067,26 +2067,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 +2330,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 +2419,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) From b086c9ef7327910c85ae9a937537b72c39b5b0d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:17 +0100 Subject: [PATCH 04/83] Inserted the reparse tag in the bad reparse symlink The reparse tag is not quoted in the "unsupported reparse point" fake symlink shown when a reparse point cannot be processed. The tag is useful to determine which plugin, if any, is missing. --- src/lowntfs-3g.c | 76 +++++++++++++++++++++++++++++++++--------------- src/ntfs-3g.c | 60 ++++++++++++++++++++++++++++---------- 2 files changed, 97 insertions(+), 39 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index c9d4a062..d20a5dd1 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -277,7 +277,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); @@ -664,7 +666,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); @@ -722,8 +724,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 +745,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 +1035,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 +1088,41 @@ 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); - } + if (res && (errno == ELIBACC)) + 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 +1178,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, diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 70effd7b..a1788512 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -212,7 +212,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); @@ -722,7 +724,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); @@ -807,8 +809,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 +831,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 +1053,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 +1093,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 +1117,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 +1138,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. */ From c3c5c77be3b1639efbec365c5094daa5b258ad57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:17 +0100 Subject: [PATCH 05/83] Checked file type on OpenIndiana when deleting a file with a reparse point On OpenIndiana a check is needed to ensure directories are not removed by rm(1) and files not removed by rmdir(1) --- src/lowntfs-3g.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index d20a5dd1..41049162 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -2650,6 +2650,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) { @@ -2689,9 +2692,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; } From 76fe04d03ddf0e75e69c1568807818b078ae03d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:17 +0100 Subject: [PATCH 06/83] Built reparse symlinks from mount point recorded in volume When building a symlink to emulate a Windows junction or absolute symlink, use the mount point recorded in the volume attributes. This enables the plugins to emulate object as symlinks. --- libntfs-3g/reparse.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/libntfs-3g/reparse.c b/libntfs-3g/reparse.c index c1d226be..362d539a 100644 --- a/libntfs-3g/reparse.c +++ b/libntfs-3g/reparse.c @@ -604,8 +604,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 +652,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 +681,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); From 8fa3dd3f224a12a60dfd23780afe658e69dcdc4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:17 +0100 Subject: [PATCH 07/83] Defined ntfs_realloc() and ntfs_free() Currently memory allocations are done through ntfs_malloc() and ntfs_calloc(), but releases are done through free(3). Defining an ntfs_free() relay facilitates the debugging of memory leaks in plugins. --- include/ntfs-3g/misc.h | 2 ++ libntfs-3g/misc.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/ntfs-3g/misc.h b/include/ntfs-3g/misc.h index a03e964e..c5568b39 100644 --- a/include/ntfs-3g/misc.h +++ b/include/ntfs-3g/misc.h @@ -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_ */ diff --git a/libntfs-3g/misc.c b/libntfs-3g/misc.c index b2e17cbf..03fb592a 100644 --- a/libntfs-3g/misc.c +++ b/libntfs-3g/misc.c @@ -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); +} From a67746c8a80ebbf8b14da2821cff186a967bfe27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:17 +0100 Subject: [PATCH 08/83] Relocated the mount point field in volume The location of the mount point field in the volume attributes was dependent on compiling options, thus creating an uneasy dependency for plugins. So relocate the field to be independent on options. --- include/ntfs-3g/volume.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index bbb61203..67fc49a8 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -256,6 +256,7 @@ 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 */ + const char *abs_mnt_point; /* Mount point */ #ifdef XATTR_MAPPINGS struct XATTRMAPPING *xattr_mapping; #endif /* XATTR_MAPPINGS */ @@ -274,7 +275,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; From 8073ab67644161b733cc6f1101b329f72f72e8be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:17 +0100 Subject: [PATCH 09/83] Supported use of WSL special file The Windows Subsystem for Linux (WSL) of Windows 10 uses reparse points to record special files (symlinks, fifos, sockets, char or block devices). Honor such reparse points with the same meaning as WSL. --- include/ntfs-3g/ea.h | 4 +- include/ntfs-3g/layout.h | 12 ++++-- include/ntfs-3g/reparse.h | 2 + libntfs-3g/ea.c | 76 ++++++++++++++++++++++++++++++++++++- libntfs-3g/reparse.c | 79 +++++++++++++++++++++++++++++++++++++-- ntfsprogs/ntfsinfo.c | 16 ++++++++ src/lowntfs-3g.c | 60 ++++++++++++++++++++++++++++- src/ntfs-3g.c | 60 +++++++++++++++++++++++++++++ 8 files changed, 300 insertions(+), 9 deletions(-) diff --git a/include/ntfs-3g/ea.h b/include/ntfs-3g/ea.h index 1936aab4..b96e40b4 100644 --- a/include/ntfs-3g/ea.h +++ b/include/ntfs-3g/ea.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2014 Jean-Pierre Andre + * Copyright (c) 2014-2021 Jean-Pierre Andre * */ @@ -24,6 +24,8 @@ #ifndef EA_H #define EA_H +int ntfs_ea_check_wsldev(ntfs_inode *ni, dev_t *rdevp); + 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); diff --git a/include/ntfs-3g/layout.h b/include/ntfs-3g/layout.h index 6d4d589a..d2022628 100644 --- a/include/ntfs-3g/layout.h +++ b/include/ntfs-3g/layout.h @@ -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), diff --git a/include/ntfs-3g/reparse.h b/include/ntfs-3g/reparse.h index 9109762b..0813ad84 100644 --- a/include/ntfs-3g/reparse.h +++ b/include/ntfs-3g/reparse.h @@ -35,6 +35,8 @@ 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_set_ntfs_reparse_data(ntfs_inode *ni, const char *value, size_t size, int flags); int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni); diff --git a/libntfs-3g/ea.c b/libntfs-3g/ea.c index 0a7b20e9..32cccdad 100644 --- a/libntfs-3g/ea.c +++ b/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 #endif +#ifdef MAJOR_IN_MKDEV +#include +#endif +#ifdef MAJOR_IN_SYSMACROS +#include +#endif #include "types.h" #include "param.h" @@ -55,6 +61,9 @@ #include "logging.h" #include "xattrs.h" +static const char lxdev[] = "$LXDEV"; + + /* * Create a needed attribute (EA or EA_INFORMATION) * @@ -393,3 +402,68 @@ 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); +} + diff --git a/libntfs-3g/reparse.c b/libntfs-3g/reparse.c index 362d539a..d8415aa5 100644 --- a/libntfs-3g/reparse.c +++ b/libntfs-3g/reparse.c @@ -3,7 +3,7 @@ * * This module is part of ntfs-3g library * - * Copyright (c) 2008-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 #endif -#ifdef HAVE_SYS_SYSMACROS_H +#ifdef MAJOR_IN_MKDEV +#include +#endif +#ifdef MAJOR_IN_SYSMACROS #include #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; } @@ -737,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; @@ -819,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); } @@ -848,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 : ; } @@ -1279,7 +1352,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; diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c index 0a6364c7..5a22f9c3 100644 --- a/ntfsprogs/ntfsinfo.c +++ b/ntfsprogs/ntfsinfo.c @@ -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. */ diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 41049162..5cbf7f94 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -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" @@ -677,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 */ @@ -1095,7 +1139,8 @@ static void ntfs_fuse_readlink(fuse_req_t req, fuse_ino_t ino) const plugin_operations_t *ops; res = CALL_REPARSE_PLUGIN(ni, readlink, &buf); - if (res && (errno == ELIBACC)) + /* plugin missing or reparse tag failing the check */ + if (res && ((errno == ELIBACC) || (errno == EINVAL))) errno = EOPNOTSUPP; #else /* DISABLE_PLUGINS */ errno = 0; @@ -4131,10 +4176,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 */ diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index a1788512..a4c77eff 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -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" @@ -735,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 */ @@ -3832,10 +3876,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 */ From 172da099472e384fefde44176e8d028f427b6557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:17 +0100 Subject: [PATCH 10/83] Dropped rejecting having both EA and reparse data Windows traditionally rejected having both EA and reparse data assigned to a file, but Windows 10 has dropped the constraint and it uses this condition massively, so do the same. Note that pre-Windows 10 chkdsk.exe removes the EA' on reparse points, potentially damaging more recent volumes. --- libntfs-3g/ea.c | 9 ++++----- libntfs-3g/reparse.c | 11 ++++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libntfs-3g/ea.c b/libntfs-3g/ea.c index 32cccdad..f03378bc 100644 --- a/libntfs-3g/ea.c +++ b/libntfs-3g/ea.c @@ -293,12 +293,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); diff --git a/libntfs-3g/reparse.c b/libntfs-3g/reparse.c index d8415aa5..fbdc7ca1 100644 --- a/libntfs-3g/reparse.c +++ b/libntfs-3g/reparse.c @@ -1197,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, From 5d46b32b91d2641c637c015e39b7d1c6641f90c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:18 +0100 Subject: [PATCH 11/83] Enabled Recording the special files the same way as WSL Optionally record the special files (symlinks, fifos, sockets, character and block devices) using reparse points instead of using Interix representation. Doing so, the special files are interoperable with Windows Subsystem for linux (WSL). --- include/ntfs-3g/ea.h | 2 + include/ntfs-3g/reparse.h | 5 ++ include/ntfs-3g/volume.h | 6 ++ libntfs-3g/dir.c | 127 ++++++++++++++++++++++++++++++-------- libntfs-3g/ea.c | 51 +++++++++++++++ libntfs-3g/reparse.c | 86 ++++++++++++++++++++++++++ src/lowntfs-3g.c | 7 ++- src/ntfs-3g.8.in | 8 +++ src/ntfs-3g.c | 7 ++- src/ntfs-3g_common.c | 14 ++++- src/ntfs-3g_common.h | 2 + 11 files changed, 285 insertions(+), 30 deletions(-) diff --git a/include/ntfs-3g/ea.h b/include/ntfs-3g/ea.h index b96e40b4..cd61fa2f 100644 --- a/include/ntfs-3g/ea.h +++ b/include/ntfs-3g/ea.h @@ -26,6 +26,8 @@ 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); diff --git a/include/ntfs-3g/reparse.h b/include/ntfs-3g/reparse.h index 0813ad84..c617951e 100644 --- a/include/ntfs-3g/reparse.h +++ b/include/ntfs-3g/reparse.h @@ -37,6 +37,11 @@ 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); diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index 67fc49a8..30e07906 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -98,6 +98,11 @@ typedef enum { NTFS_VOLUME_INSECURE = 22 } ntfs_volume_status; +typedef enum { + NTFS_FILES_INTERIX, + NTFS_FILES_WSL, +} ntfs_volume_special_files; + /** * enum ntfs_volume_state_bits - * @@ -256,6 +261,7 @@ 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; diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index a517ece9..e85c3c52 100644 --- a/libntfs-3g/dir.c +++ b/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" @@ -1496,9 +1497,11 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, { ntfs_inode *ni; int rollback_data = 0, rollback_sd = 0; + int rollback_dir = 0; FILE_NAME_ATTR *fn = NULL; STANDARD_INFORMATION *si = NULL; int err, fn_len, si_len; + ntfs_volume_special_files special_files; ntfs_log_trace("Entering.\n"); @@ -1515,6 +1518,7 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, #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, @@ -1542,8 +1546,19 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, } else clear_nino_flag(ni, v3_Extensions); if (!S_ISREG(type) && !S_ISDIR(type)) { - si->file_attributes = FILE_ATTR_SYSTEM; - ni->flags = FILE_ATTR_SYSTEM; + switch (special_files) { + case NTFS_FILES_WSL : + if (!S_ISLNK(type)) { + si->file_attributes + = FILE_ATTRIBUTE_RECALL_ON_OPEN; + ni->flags = FILE_ATTRIBUTE_RECALL_ON_OPEN; + } + break; + default : + si->file_attributes = FILE_ATTR_SYSTEM; + ni->flags = FILE_ATTR_SYSTEM; + break; + } } ni->flags |= FILE_ATTR_ARCHIVE; if (NVolHideDotFiles(dir_ni->vol) @@ -1576,8 +1591,8 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, err = errno; goto err_out; } + rollback_sd = 1; } - rollback_sd = 1; if (S_ISDIR(type)) { INDEX_ROOT *ir = NULL; @@ -1626,34 +1641,58 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, switch (type) { case S_IFBLK: case S_IFCHR: - data_len = offsetof(INTX_FILE, device_end); - data = ntfs_malloc(data_len); - if (!data) { - err = errno; - goto err_out; + switch (special_files) { + case NTFS_FILES_WSL : + data_len = 0; + data = (INTX_FILE*)NULL; + break; + default : + data_len = offsetof(INTX_FILE, + device_end); + data = (INTX_FILE*)ntfs_malloc( + data_len); + if (!data) { + err = errno; + goto err_out; + } + data->major = cpu_to_le64(major(dev)); + data->minor = cpu_to_le64(minor(dev)); + if (type == S_IFBLK) + data->magic + = INTX_BLOCK_DEVICE; + if (type == S_IFCHR) + data->magic + = INTX_CHARACTER_DEVICE; + break; } - data->major = cpu_to_le64(major(dev)); - data->minor = cpu_to_le64(minor(dev)); - if (type == S_IFBLK) - data->magic = INTX_BLOCK_DEVICE; - if (type == S_IFCHR) - data->magic = INTX_CHARACTER_DEVICE; break; case S_IFLNK: - data_len = sizeof(INTX_FILE_TYPES) + + switch (special_files) { + case NTFS_FILES_WSL : + data_len = 0; + data = (INTX_FILE*)NULL; + break; + default : + data_len = sizeof(INTX_FILE_TYPES) + target_len * sizeof(ntfschar); - data = ntfs_malloc(data_len); - if (!data) { - err = errno; - goto err_out; - } - data->magic = INTX_SYMBOLIC_LINK; - memcpy(data->target, target, + data = (INTX_FILE*)ntfs_malloc( + data_len); + if (!data) { + err = errno; + goto err_out; + } + data->magic = INTX_SYMBOLIC_LINK; + memcpy(data->target, target, target_len * sizeof(ntfschar)); + break; + } break; case S_IFSOCK: data = NULL; - data_len = 1; + if (special_files == NTFS_FILES_WSL) + data_len = 0; + else + data_len = 1; break; default: /* FIFO or regular file. */ data = NULL; @@ -1684,9 +1723,10 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, fn->file_name_type = FILE_NAME_POSIX; if (S_ISDIR(type)) fn->file_attributes = FILE_ATTR_I30_INDEX_PRESENT; - if (!S_ISREG(type) && !S_ISDIR(type)) - fn->file_attributes = FILE_ATTR_SYSTEM; - else + if (!S_ISREG(type) && !S_ISDIR(type)) { + if (special_files == NTFS_FILES_INTERIX) + fn->file_attributes = FILE_ATTR_SYSTEM; + } else fn->file_attributes |= ni->flags & FILE_ATTR_COMPRESSED; fn->file_attributes |= FILE_ATTR_ARCHIVE; fn->file_attributes |= ni->flags & FILE_ATTR_HIDDEN; @@ -1714,10 +1754,40 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, ntfs_log_perror("Failed to add entry to the index"); goto err_out; } + rollback_dir = 1; /* Set hard links count and directory flag. */ ni->mrec->link_count = const_cpu_to_le16(1); if (S_ISDIR(type)) ni->mrec->flags |= 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); @@ -1727,6 +1797,9 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, err_out: ntfs_log_trace("Failed.\n"); + if (rollback_dir) + ntfs_index_remove(dir_ni, ni, fn, fn_len); + if (rollback_sd) ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0); diff --git a/libntfs-3g/ea.c b/libntfs-3g/ea.c index f03378bc..b86930ab 100644 --- a/libntfs-3g/ea.c +++ b/libntfs-3g/ea.c @@ -62,6 +62,7 @@ #include "xattrs.h" static const char lxdev[] = "$LXDEV"; +static const char lxmod[] = "$LXMOD"; /* @@ -466,3 +467,53 @@ int ntfs_ea_check_wsldev(ntfs_inode *ni, dev_t *rdevp) 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); +} diff --git a/libntfs-3g/reparse.c b/libntfs-3g/reparse.c index fbdc7ca1..76dbe2f1 100644 --- a/libntfs-3g/reparse.c +++ b/libntfs-3g/reparse.c @@ -1331,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 diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 5cbf7f94..eea4e6da 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -2385,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). @@ -4737,6 +4741,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 */ diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index 2d18fb4b..d4338a9c 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -316,6 +316,14 @@ data streams are mapped to xattrs and user can manipulate them using .B user_xattr Same as \fBstreams_interface=\fP\fIxattr\fP. .TP +.BI special_files= value +This option selects a mode for representing a special file to be created +(symbolic link, socket, fifo, character or block device). The mode can +be \fBinterix\fR or \fBwsl\fR, and existing files in either mode are +recognized irrespective of the selected mode. Interix is the traditional +mode, used by default, and wsl is interoperable with Windows WSL, but +it is not compatible with Windows versions earlier than Windows 10. +.TP .B efs_raw This option should only be used in backup or restore situation. It changes the apparent size of files and the behavior of read and diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index a4c77eff..6ec2020a 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2117,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). @@ -4464,6 +4468,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 */ diff --git a/src/ntfs-3g_common.c b/src/ntfs-3g_common.c index 6b24528b..7e3e93d2 100644 --- a/src/ntfs-3g_common.c +++ b/src/ntfs-3g_common.c @@ -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,7 @@ 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 }, { (const char*)NULL, 0, 0 } /* end marker */ } ; @@ -503,6 +504,17 @@ 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 diff --git a/src/ntfs-3g_common.h b/src/ntfs-3g_common.h index 8c2ecf39..4ed256a3 100644 --- a/src/ntfs-3g_common.h +++ b/src/ntfs-3g_common.h @@ -93,6 +93,7 @@ enum { OPT_XATTRMAPPING, OPT_EFS_RAW, OPT_POSIX_NLINK, + OPT_SPECIAL_FILES, } ; /* Option flags */ @@ -155,6 +156,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 From 894b7dd36e56ce7b52cfef9a105ef3eed07dc307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:18 +0100 Subject: [PATCH 12/83] Checked the locations of MFT and MFTMirr at startup On startup make sure the lcns of the MFT and the MFTMirr are not null and they are different, so that the mounting is denied gracefully if they are. --- libntfs-3g/bootsect.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libntfs-3g/bootsect.c b/libntfs-3g/bootsect.c index e9eb0b15..483473e6 100644 --- a/libntfs-3g/bootsect.c +++ b/libntfs-3g/bootsect.c @@ -154,6 +154,12 @@ BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b) } } + /* MFT and MFTMirr may not overlap the boot sector or be the same */ + if (!b->mft_lcn || !b->mftmirr_lcn || (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"); From 094f9b3f2de7718ebfad00431ed9a5ddbc97583c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:18 +0100 Subject: [PATCH 13/83] Fixed a minor endianness ajustment bug The endianness ajustment was the wrong one though it did the correct thing. --- libntfs-3g/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 6fc79d5d..006a6ecd 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -1584,7 +1584,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); From 56b8e713d56895944d1ff89b4ff2dee4c37393ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:18 +0100 Subject: [PATCH 14/83] Fixed a constant string concatenation Adjust for recent compilers requiring a space between concatenated strings. --- ntfsprogs/utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ntfsprogs/utils.c b/ntfsprogs/utils.c index 393c34a4..31522da3 100644 --- a/ntfsprogs/utils.c +++ b/ntfsprogs/utils.c @@ -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 " From da1b61ec225eaf5a78ea5ae1105defb43708827b Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Thu, 25 Feb 2021 17:13:24 +0200 Subject: [PATCH 15/83] lowntfs-3g.c: Fix compile error when building with libfuse < 2.8.0. The libfuse function 'fuse_lowlevel_notify_inval_inode' is only available starting with libfuse 2.8.0 while we advertise compatibility with FUSE 2.6.0 as the baseline. So add a preprocessor check to exclude this code from libfuse < 2.8.0. --- src/lowntfs-3g.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index eea4e6da..badf0529 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -3809,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.), @@ -4056,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.), From bcd42e5ef3e4bada89e889c7e82a04b0d3a9e5cd Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Fri, 26 Feb 2021 08:09:18 +0200 Subject: [PATCH 16/83] configure.ac: Respect 'with_fuse' user setting on non-Linux/Solaris. Previously the configure script simply disregarded the value of the 'with_fuse' flag on non-Linux/Solaris systems. This is not unreasonable since the 'internal' option doesn't work on those systems, however in some situations we want to specify the libfuse CFLAGS/LDFLAGS manually and bypass the pkg-config check with '--with-fuse=none' and this was not possible with this logic. So add a special test to bypass the automatic determination when the user has specified a 'with_fuse' value. --- configure.ac | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 88733eb5..0b483c20 100644 --- a/configure.ac +++ b/configure.ac @@ -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 From dd75ea7420b8a533d6923bb962b7efea42fa60b6 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Wed, 3 Mar 2021 16:39:45 +0200 Subject: [PATCH 17/83] configure.ac: Fix obsolete 2-argument AM_INIT_AUTOMAKE invocation. The 2-argument form has been replaced with fetching the package/version information from autoconf (AC_INIT). Instead the 1-argument form takes a list of automake options but we currently do not need to specify any. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 0b483c20..7d8324f5 100644 --- a/configure.ac +++ b/configure.ac @@ -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 From e5ee8b325c8fd3b931c2b4e3d840c53626bbf876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 5 Mar 2021 10:05:24 +0100 Subject: [PATCH 18/83] Signalled support of UTIME_OMIT to external libfuse2 The external libfuse2 optionally uses UTIME_OMIT to tell whether mtime or ctime should not be changed in a utimensat(2) call. Set the flag flag_utime_omit_ok so that it knows that ntfs-3g supports this feature. Note : this is the default with internal libfuse and with libfuse3. --- src/ntfs-3g.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 6ec2020a..3494952c 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -3965,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 From 2ede3b67eef84260d2955fb6d4429ccb2db1ab1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 5 Mar 2021 10:21:36 +0100 Subject: [PATCH 19/83] Improved checking of the location of MFT Terminate cleanly when the MFT is improperly located, by making sure it is located within the volume and that it contains its own data. --- libntfs-3g/volume.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 7a1bcf29..416ebfb7 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -355,6 +355,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. */ @@ -601,6 +606,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); From 477a4dfe5e2d7db3b56763d5aec64fac40324f17 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Fri, 5 Mar 2021 14:56:31 +0200 Subject: [PATCH 20/83] ntfscp.c: Fix improper unsigned const endianness macro for signed type. --- ntfsprogs/ntfscp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ntfsprogs/ntfscp.c b/ntfsprogs/ntfscp.c index f30be5a7..19303b6f 100644 --- a/ntfsprogs/ntfscp.c +++ b/ntfsprogs/ntfscp.c @@ -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"); From 3a535150164bb44f4d826935370a9722e407ca98 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Fri, 5 Mar 2021 14:56:32 +0200 Subject: [PATCH 21/83] Update README to mention support for Windows 8 and 10 NTFS formats. --- README | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README b/README index b829fd0b..aa457ecc 100644 --- a/README +++ b/README @@ -5,7 +5,8 @@ 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 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 and Windows 10 file +systems. The purpose of the project is to develop, quality assurance and support a trustable, featureful and high performance solution for hardware platforms From 83b66b3f39f55a97469e3c202bef3c308bdd9224 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Fri, 5 Mar 2021 14:56:34 +0200 Subject: [PATCH 22/83] Change http links in README to https for security. --- README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index aa457ecc..8cc6e917 100644 --- a/README +++ b/README @@ -22,13 +22,13 @@ and creating internally compressed files (parameter files in the directory 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/ + https://www.tuxera.com/community/ntfs-3g-advanced/ News, support answers, problem submission instructions, support and discussion forums, performance numbers and other information are available on the project web site at - http://www.tuxera.com/community/ + https://www.tuxera.com/community/ LICENSES ======== From e464ca75740116a6c77e769d4fb5da3fbb93fa84 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Fri, 5 Mar 2021 14:56:36 +0200 Subject: [PATCH 23/83] Update last two entries in README to match style of previous entries. --- README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index 8cc6e917..5c000743 100644 --- a/README +++ b/README @@ -160,6 +160,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. From 442e12fc32c3d3657dbc0233449c0d1121c79d3a Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Fri, 5 Mar 2021 14:56:38 +0200 Subject: [PATCH 24/83] Update outdated copyright notice. --- src/lowntfs-3g.c | 2 +- src/ntfs-3g.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index badf0529..efc1a036 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -263,7 +263,7 @@ static const char *usage_msg = "Copyright (C) 2005-2007 Yura Pakhuchiy\n" "Copyright (C) 2006-2009 Szabolcs Szakacsits\n" "Copyright (C) 2007-2021 Jean-Pierre Andre\n" -"Copyright (C) 2009 Erik Larsson\n" +"Copyright (C) 2009-2020 Erik Larsson\n" "\n" "Usage: %s [-o option[,...]] \n" "\n" diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 3494952c..fffe5f38 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -198,7 +198,7 @@ static const char *usage_msg = "Copyright (C) 2005-2007 Yura Pakhuchiy\n" "Copyright (C) 2006-2009 Szabolcs Szakacsits\n" "Copyright (C) 2007-2021 Jean-Pierre Andre\n" -"Copyright (C) 2009 Erik Larsson\n" +"Copyright (C) 2009-2020 Erik Larsson\n" "\n" "Usage: %s [-o option[,...]] \n" "\n" From a4a837025b6ac2b0c44c93e34e22535fe9e95b27 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Fri, 5 Mar 2021 14:56:40 +0200 Subject: [PATCH 25/83] Add the official git repository location to the README file. --- README | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README b/README index 5c000743..f072c0ea 100644 --- a/README +++ b/README @@ -30,6 +30,10 @@ web site at https://www.tuxera.com/community/ +The project's official git repository is now hosted on GitHub: + + https://github.com/tuxera/ntfs-3g + LICENSES ======== From 1258474e07e1fe587ead4003926ae71e5e55dcf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:30:17 +0300 Subject: [PATCH 26/83] Improved check of upcase table length The consistency check on the upcase size was laxist. The unicode points are limited to 1048575 (20 bits), but there is no provision to support code points beyond 65535. In the current implementation the upcase size must be even and <= 131072. --- libntfs-3g/volume.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 416ebfb7..c6356563 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -1057,19 +1057,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; @@ -1077,7 +1077,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); @@ -1086,7 +1086,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); @@ -1291,6 +1291,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: From 00a54786251611bc3241f2ec8d052f10ca514e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:31:52 +0300 Subject: [PATCH 27/83] Added check of layout of attributes Make sure the attributes fully lie within the MFT record, and make sure their variable components (name, runlist, value) fully lie within the attribute. --- libntfs-3g/attrib.c | 78 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 7 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index a736a9a2..89af840d 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -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-2020 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 @@ -2765,6 +2765,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 +2796,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_allocated) - 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) > @@ -2986,6 +2997,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; @@ -3202,12 +3215,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_allocated) - 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; @@ -3373,6 +3392,7 @@ int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, { ntfs_volume *vol; ntfs_inode *base_ni; + ATTR_RECORD *a; int ret = -1; ntfs_log_enter("Entering for attribute type 0x%x\n", le32_to_cpu(type)); @@ -3394,6 +3414,50 @@ int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, else ret = ntfs_external_attr_find(type, name, name_len, ic, lowest_vcn, val, val_len, ctx); + if (!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. + */ + a = ctx->attr; + if (a->non_resident) { + if ((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 + && ((le16_to_cpu(a->name_offset) + + a->name_length) + > le32_to_cpu(a->length)))) { + ntfs_log_error("Corrupt non resident attribute" + " 0x%x in MFT record %lld\n", + (int)le32_to_cpu(a->type), + (long long)ctx->ntfs_ino->mft_no); + errno = EIO; + ret = -1; + } + } else { + if ((le32_to_cpu(a->length) + < offsetof(ATTR_RECORD, resident_end)) + || (le16_to_cpu(a->value_offset) + + le32_to_cpu(a->value_length)) + > le32_to_cpu(a->length) + || (a->name_length + && ((le16_to_cpu(a->name_offset) + + a->name_length) + > 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)ctx->ntfs_ino->mft_no); + errno = EIO; + ret = -1; + } + } + } out: ntfs_log_leave("\n"); return ret; From 3f3b771cb08c92f34e21326462be3330973f02a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:33:04 +0300 Subject: [PATCH 28/83] Added consistency checks of index root The index root must lie within the space allocated for it. --- libntfs-3g/dir.c | 10 ++++++++++ libntfs-3g/index.c | 18 +++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index e85c3c52..f3c14d8f 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -293,6 +293,16 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, (unsigned)index_block_size); goto put_err_out; } + if (((offsetof(INDEX_ROOT,index) + + le32_to_cpu(ir->index.allocated_size)) + > le32_to_cpu(ctx->attr->value_length)) + || (le32_to_cpu(ir->index.entries_offset) + > le32_to_cpu(ir->index.index_length)) + || (le32_to_cpu(ir->index.index_length) + > le32_to_cpu(ir->index.allocated_size))) { + ntfs_log_error("Index root is corrupt.\n"); + goto put_err_out; + } index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); /* The first index entry. */ ie = (INDEX_ENTRY*)((u8*)&ir->index + diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 006a6ecd..e12cbafd 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -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 @@ -673,6 +673,7 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic INDEX_ROOT *ir; INDEX_ENTRY *ie; INDEX_BLOCK *ib = NULL; + ATTR_RECORD *a; int ret, err = 0; ntfs_log_trace("Entering\n"); @@ -712,10 +713,17 @@ 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. - */ + a = icx->actx->attr; + if (((offsetof(INDEX_ROOT,index) + + le32_to_cpu(ir->index.index_length)) + > le32_to_cpu(a->value_length)) + || (le32_to_cpu(ir->index.entries_offset) + > le32_to_cpu(ir->index.index_length))) { + ntfs_log_error("Index root is corrupt in MFT record %lld.\n", + (long long)icx->ni->mft_no); + err = errno = ERANGE; + goto err_lookup; + } ret = ntfs_ie_lookup(key, key_len, icx, &ir->index, &vcn, &ie); if (ret == STATUS_ERROR) { err = errno; From bb0168e56baa1d7e33eec49ee36866eb2f7806bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:33:41 +0300 Subject: [PATCH 29/83] Rejected updates of uninitialized MFTMirr Reject updating uninitialized MFTMirr because it leads to endless recursions as updating the MFTMirr entry of MFT implies further updating MFTMirr. This is probably specific to ntfsfix, as mounts are normally rejected when MFTMirr does not match MFT. --- libntfs-3g/mft.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index a80d1e4e..33b832a9 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -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; From 8adc9c618496d2c99b0bdc5bd7db6d59a86c513b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:35:02 +0300 Subject: [PATCH 30/83] Hardened consistency checks of MFT records Check the record is properly aligned and does not flow beyond the expected end of the record. --- libntfs-3g/mft.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 33b832a9..267f7306 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -240,7 +240,19 @@ int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, le32_to_cpu(m->bytes_allocated)); goto err_out; } - + if (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) { ntfs_log_error("Record %llu is corrupt\n", From 5bdfac970413fa1331ad938cf562bed0e343f121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:35:51 +0300 Subject: [PATCH 31/83] Added generic checks of MFT records in ntfsfix Before acting on MFT, make sure it passes the consistency check. --- ntfsprogs/ntfsfix.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ntfsprogs/ntfsfix.c b/ntfsprogs/ntfsfix.c index 6801e70e..48e8a782 100644 --- a/ntfsprogs/ntfsfix.c +++ b/ntfsprogs/ntfsfix.c @@ -823,7 +823,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 +962,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 +1020,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) From d60175230ad97a01df7862c09b392f73e9db4a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:36:58 +0300 Subject: [PATCH 32/83] Added consistency check of MFT attributes in ntfsfix Make sure the searched attribute is fully within the MFT record and is correctly aligned. --- ntfsprogs/ntfsfix.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ntfsprogs/ntfsfix.c b/ntfsprogs/ntfsfix.c index 48e8a782..da92fadc 100644 --- a/ntfsprogs/ntfsfix.c +++ b/ntfsprogs/ntfsfix.c @@ -784,7 +784,9 @@ static ATTR_RECORD *find_unnamed_attr(MFT_RECORD *mrec, ATTR_TYPES type) /* fetch the requested attribute */ offset = le16_to_cpu(mrec->attrs_offset); a = (ATTR_RECORD*)((char*)mrec + offset); - while ((offset < le32_to_cpu(mrec->bytes_in_use)) + while (((offset + le32_to_cpu(a->length)) + < le32_to_cpu(mrec->bytes_in_use)) + && !(le32_to_cpu(a->length) & 7) && (a->type != AT_END) && ((a->type != type) || a->name_length)) { offset += le32_to_cpu(a->length); From 02e199978b0af3a2cb7b5929bceaf19127ea95eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:37:45 +0300 Subject: [PATCH 33/83] Fixed initialization of a Posix ACL A posix ACL record was not fully initialized. --- libntfs-3g/acls.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libntfs-3g/acls.c b/libntfs-3g/acls.c index 9df71083..9f16fecd 100644 --- a/libntfs-3g/acls.c +++ b/libntfs-3g/acls.c @@ -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); From 218d93b37b7d1917598489d0ffc8d32bf34c7c56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:38:20 +0300 Subject: [PATCH 34/83] Cleaned the tail of compressed buffer before writing a full cluster When appending to an existing compressed file, the last block must first be decompressed. When doing so, clear the tail of buffer in order to not leak unrelated data. --- libntfs-3g/compress.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/compress.c b/libntfs-3g/compress.c index fb69a0e6..0c22bc9c 100644 --- a/libntfs-3g/compress.c +++ b/libntfs-3g/compress.c @@ -1618,7 +1618,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 +1639,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) From 37a330ea8c788db312c4fbfe03ce5f1f7d53bd7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:39:13 +0300 Subject: [PATCH 35/83] Improved the consistency checks of standard information Make sure the standard information attribute has a valid size. --- libntfs-3g/inode.c | 20 +++++++++++++------- libntfs-3g/volume.c | 7 +++++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index b25f4051..fe57efaa 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -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; @@ -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; diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index c6356563..e1c93236 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -224,10 +224,13 @@ static int __ntfs_volume_release(ntfs_volume *v) static void ntfs_attr_setup_flag(ntfs_inode *ni) { STANDARD_INFORMATION *si; + s64 lth; - 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); } } From d86c34390dcdc0be72093f9524d2c2f619b51fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:39:52 +0300 Subject: [PATCH 36/83] Added a consistency check of attribute size in readall() Make sure the attribute designated to readall() has a reasonable size. Apart from a bitmap, in a valid call, the attribute size is limited to 65536, and bitmaps are limited by the number of cluster. --- libntfs-3g/attrib.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 89af840d..6fbb9a18 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -6727,6 +6727,19 @@ 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 ((na->data_size > 65536) + && ((type != AT_BITMAP) + || ((na->data_size << 3) > ni->vol->nr_clusters))) { + 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) From a4177d625c9a5e7925dfa5422a673a12c936c3d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:42:31 +0300 Subject: [PATCH 37/83] Made sure an error code is issued for failed mounts In case of failure to mount a file system, an error code is expected to indicate the failure reason. In an unclear case the error code was not set, so force an EIO when this happens. --- src/lowntfs-3g.c | 5 ++++- src/ntfs-3g.c | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index efc1a036..cfa61626 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -4368,9 +4368,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) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index fffe5f38..1f148ba0 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -4076,9 +4076,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) From fc295da55edc624eca83b65730bde8148b77d1aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 19 May 2021 15:18:56 +0200 Subject: [PATCH 38/83] Configured for version 2021.5.19 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 7d8324f5..e2005cc5 100644 --- a/configure.ac +++ b/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],[2021.5.19],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="89" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From f3999a74c68b26d04d6d34302d8d5dcb8e3ac56d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:17 +0200 Subject: [PATCH 39/83] Fixed consistency check of MFT attributes in ntfsfix The consistency check could be defeated as a consequence of integer overflow. Reorganize it to avoid such situations. --- ntfsprogs/ntfsfix.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ntfsprogs/ntfsfix.c b/ntfsprogs/ntfsfix.c index da92fadc..b214068f 100644 --- a/ntfsprogs/ntfsfix.c +++ b/ntfsprogs/ntfsfix.c @@ -780,16 +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(a->length)) - < le32_to_cpu(mrec->bytes_in_use)) - && !(le32_to_cpu(a->length) & 7) + 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)) From 4e09b252af7ca75a278b33e5aab019f4c5c76fd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:17 +0200 Subject: [PATCH 40/83] Checked name limits while looking up for an external attribute Make sure the name of an attribute does not overflow beyond the attribute length. --- libntfs-3g/attrib.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 6fbb9a18..83a2d603 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -2835,6 +2835,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)), From e2e625065dcbf1dab82b9d1b0cb3e39f7b4f616a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:17 +0200 Subject: [PATCH 41/83] Checked the layout of attribute list entries Make sure the attribute list has at least one element, and that each of them has the minimal size and does not overflow out of the end of list. --- libntfs-3g/attrib.c | 34 ++++++++++++++++++++++++++++------ libntfs-3g/volume.c | 8 +++++--- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 83a2d603..12989615 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3049,8 +3049,21 @@ 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 ((al_entry->name_length + && ((u8*)al_entry + al_entry->name_offset + + al_entry->name_length * sizeof(ntfschar)) + > al_end)) + goto corrupt; /* * If this is an enumeration and the attribute list attribute * is the next one in the enumeration sequence, just return the @@ -3113,11 +3126,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) { @@ -3270,13 +3290,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: /* diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index e1c93236..e6ec498b 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -306,10 +306,12 @@ 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; } From 4e094563d4989ae4451fb61ea0584986f6a711cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 42/83] Checked $MFT data to be ready before fetching an extent --- libntfs-3g/attrib.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 12989615..799f2ee4 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3217,6 +3217,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) From ec7bf5437738fa56c2e259521e3a18c972ac76be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 43/83] Checked the MFTMirr lcn to be consistent with the boot sector The lcn of the initial run of the MFTMirr data must be the same as described in the boot sector. As a consequence this cannot be a hole. --- libntfs-3g/volume.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index e6ec498b..a6551c78 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -458,6 +458,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; From 84478c401236f3e4345174ea2b71f578aa45335d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 44/83] Checked attributes layout against length actually used The end of the attributes must be checked against the space in use rather than the allocated space. This contributes to the safety of subsequent attribute resizing. --- libntfs-3g/attrib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 799f2ee4..caf0f4a0 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -2801,7 +2801,7 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, * and we can safely access its minimal fields. */ offs = p2n(a) - p2n(ctx->mrec); - space = le32_to_cpu(ctx->mrec->bytes_allocated) - offs; + space = le32_to_cpu(ctx->mrec->bytes_in_use) - offs; if ((offs < 0) || (((space < (ptrdiff_t)offsetof(ATTR_RECORD, resident_end)) @@ -3256,7 +3256,7 @@ do_next_attr_loop: * and we can safely access its minimal fields. */ offs = p2n(a) - p2n(ctx->mrec); - space = le32_to_cpu(ctx->mrec->bytes_allocated) - offs; + space = le32_to_cpu(ctx->mrec->bytes_in_use) - offs; if (offs < 0) break; if ((space >= 4) && (a->type == AT_END)) From 68ead9b8fc5b1d466fca7ef4c487b8d5b8996d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 45/83] Checked expanded attribute does not overflow the space in use When resizing an attribute, make sure the trailing data is moved in the correct direction. --- libntfs-3g/attrib.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index caf0f4a0..387ccde5 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -4635,6 +4635,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); From 32e858a87a4184a05a1b54315c06ac94488770b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 46/83] Aborted mounting when cannot access standard information of MFT The standard information of the MFT must be its first attribute in the base record. If it is not accessible initially, we end up searching it in an extent before the MFT struct is ready for that. --- libntfs-3g/volume.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index a6551c78..92743735 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -221,10 +221,11 @@ 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 = (STANDARD_INFORMATION*)ntfs_attr_readall(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, <h); @@ -232,7 +233,12 @@ static void ntfs_attr_setup_flag(ntfs_inode *ni) 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); } /** @@ -317,7 +323,8 @@ static int ntfs_mft_load(ntfs_volume *vol) 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. */ From 436fe09f87ccd92cd037b8a5547340ee01b58e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 47/83] Checked consistency of index entries Make sure the data and key in indexes do not overflow from index entries --- include/ntfs-3g/index.h | 2 ++ libntfs-3g/dir.c | 27 ++++++++++++++---- libntfs-3g/index.c | 62 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 6 deletions(-) diff --git a/include/ntfs-3g/index.h b/include/ntfs-3g/index.h index 036b742f..4e3f73f7 100644 --- a/include/ntfs-3g/index.h +++ b/include/ntfs-3g/index.h @@ -139,6 +139,8 @@ 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_entry_consistent(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__; diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index f3c14d8f..b2afbc34 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -328,9 +328,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_consistent(ie, COLLATION_FILE_NAME, + dir_ni->mft_no)) { + errno = EIO; goto put_err_out; } /* @@ -467,10 +468,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_consistent(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; } /* @@ -1273,6 +1274,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_consistent(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. @@ -1429,6 +1437,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_consistent(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. diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index e12cbafd..1bd0acd5 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -469,6 +469,57 @@ static INDEX_ROOT *ntfs_ir_lookup2(ntfs_inode *ni, ntfschar *name, u32 len) return ir; } +/* + * 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_consistent(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 +572,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_consistent(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)); @@ -704,6 +761,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; @@ -790,6 +848,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; From 7f45544ed7bdbcedd344dd960a6cd4797f66b43c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 48/83] Added and grouped generic attribute checks Checked that attributes are [non-]resident when they have to be, and grouped consistency checks on each of them in a dedicated function. Consequenly request the checks where needed and remove existing index checks. --- include/ntfs-3g/attrib.h | 1 + libntfs-3g/attrib.c | 209 ++++++++++++++++++++++++++++++--------- libntfs-3g/dir.c | 17 +--- libntfs-3g/index.c | 12 --- libntfs-3g/mft.c | 49 ++++++++- libntfs-3g/volume.c | 8 ++ 6 files changed, 222 insertions(+), 74 deletions(-) diff --git a/include/ntfs-3g/attrib.h b/include/ntfs-3g/attrib.h index b3752a60..e2cf41b3 100644 --- a/include/ntfs-3g/attrib.h +++ b/include/ntfs-3g/attrib.h @@ -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_consistent(const ATTR_RECORD *a, const MFT_REF mref); #endif /* defined _NTFS_ATTRIB_H */ diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 387ccde5..66c1727d 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3356,6 +3356,170 @@ 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_consistent(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.index_length) + & 0xff000000) + || ((le32_to_cpu(a->value_length) + - le32_to_cpu(ir->index.index_length)) + < offsetof(INDEX_ROOT,index)) + || ((le32_to_cpu(a->value_length) + - le32_to_cpu(ir->index.index_length)) + < le32_to_cpu(ir->index.entries_offset)) + || (le32_to_cpu(ir->index.index_length) + > le32_to_cpu(ir->index.allocated_size))) { + ntfs_log_error("Corrupt index root" + " in MFT record %lld.\n", + (long long)inum); + errno = EIO; + ret = -1; + } + break; + case AT_STANDARD_INFORMATION : + case AT_OBJECT_ID : + 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 @@ -3430,7 +3594,6 @@ int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, { ntfs_volume *vol; ntfs_inode *base_ni; - ATTR_RECORD *a; int ret = -1; ntfs_log_enter("Entering for attribute type 0x%x\n", le32_to_cpu(type)); @@ -3452,50 +3615,6 @@ int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, else ret = ntfs_external_attr_find(type, name, name_len, ic, lowest_vcn, val, val_len, ctx); - if (!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. - */ - a = ctx->attr; - if (a->non_resident) { - if ((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 - && ((le16_to_cpu(a->name_offset) - + a->name_length) - > le32_to_cpu(a->length)))) { - ntfs_log_error("Corrupt non resident attribute" - " 0x%x in MFT record %lld\n", - (int)le32_to_cpu(a->type), - (long long)ctx->ntfs_ino->mft_no); - errno = EIO; - ret = -1; - } - } else { - if ((le32_to_cpu(a->length) - < offsetof(ATTR_RECORD, resident_end)) - || (le16_to_cpu(a->value_offset) - + le32_to_cpu(a->value_length)) - > le32_to_cpu(a->length) - || (a->name_length - && ((le16_to_cpu(a->name_offset) - + a->name_length) - > 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)ctx->ntfs_ino->mft_no); - errno = EIO; - ret = -1; - } - } - } out: ntfs_log_leave("\n"); return ret; diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index b2afbc34..bee6fbbe 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -293,16 +293,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, (unsigned)index_block_size); goto put_err_out; } - if (((offsetof(INDEX_ROOT,index) - + le32_to_cpu(ir->index.allocated_size)) - > le32_to_cpu(ctx->attr->value_length)) - || (le32_to_cpu(ir->index.entries_offset) - > le32_to_cpu(ir->index.index_length)) - || (le32_to_cpu(ir->index.index_length) - > le32_to_cpu(ir->index.allocated_size))) { - ntfs_log_error("Index root is corrupt.\n"); - 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 + @@ -1097,12 +1088,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; diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 1bd0acd5..f804efcc 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -730,7 +730,6 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic INDEX_ROOT *ir; INDEX_ENTRY *ie; INDEX_BLOCK *ib = NULL; - ATTR_RECORD *a; int ret, err = 0; ntfs_log_trace("Entering\n"); @@ -771,17 +770,6 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic } old_vcn = VCN_INDEX_ROOT_PARENT; - a = icx->actx->attr; - if (((offsetof(INDEX_ROOT,index) - + le32_to_cpu(ir->index.index_length)) - > le32_to_cpu(a->value_length)) - || (le32_to_cpu(ir->index.entries_offset) - > le32_to_cpu(ir->index.index_length))) { - ntfs_log_error("Index root is corrupt in MFT record %lld.\n", - (long long)icx->ni->mft_no); - err = errno = ERANGE; - goto err_lookup; - } ret = ntfs_ie_lookup(key, key_len, icx, &ir->index, &vcn, &ie); if (ret == STATUS_ERROR) { err = errno; diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 267f7306..cc244871 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -219,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)) @@ -240,7 +255,8 @@ int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, le32_to_cpu(m->bytes_allocated)); goto err_out; } - if (le32_to_cpu(m->bytes_in_use) > vol->mft_record_size) { + 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), @@ -259,6 +275,37 @@ int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, (unsigned long long)MREF(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_consistent(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: diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 92743735..e538fa5a 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -976,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) { @@ -985,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) { From af1bc0f5ec6db13e8d3fb289bc539e9f08e20e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 49/83] Hardened the check of locations of MFT and MFTMirr The MFT and MFTMirr may not be negative or overlap the boot sector. --- libntfs-3g/bootsect.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/bootsect.c b/libntfs-3g/bootsect.c index 483473e6..279247d9 100644 --- a/libntfs-3g/bootsect.c +++ b/libntfs-3g/bootsect.c @@ -155,7 +155,9 @@ BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b) } /* MFT and MFTMirr may not overlap the boot sector or be the same */ - if (!b->mft_lcn || !b->mftmirr_lcn || (b->mft_lcn == b->mftmirr_lcn)) { + 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; } From f30b52490f16134b70d59a0d9e37716b62e38a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 50/83] Restricted the attribute definition table size to 24 bits The standard size is 2560 bytes. It can be extended for specific purposes, but its former limit to 32 bits was unreasonable. Anyway ntfs-3g is not committed to support non-standard situations. --- libntfs-3g/volume.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index e538fa5a..d1a5f2f0 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -1248,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; } From 20d700841b73579107103dfc6a2f9f83acd4ffc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 51/83] Shown in log the inode of directory read error The unreadable directory record was poorly identified --- libntfs-3g/dir.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index bee6fbbe..0a3c87dd 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -400,8 +400,9 @@ 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; } From 5c002438f27d3dec445fa72c8089ffef7af5a51c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 52/83] Checked that indexes do not exceed the index block size Make sure the used part of an index block fits into the allocated buffer. Note : a negative size may cause overflow on 32-bit cpus. (contributed by Rakesh Pandit) --- libntfs-3g/dir.c | 3 ++- libntfs-3g/index.c | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 0a3c87dd..76c052cf 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -425,7 +425,8 @@ descend_into_child_node: goto close_err_out; } index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); - if (index_end > (u8*)ia + index_block_size) { + if (((s32)le32_to_cpu(ia->index.index_length) < 0) + || (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); diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index f804efcc..c8c03481 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -421,6 +421,14 @@ static int ntfs_ia_check(ntfs_index_context *icx, INDEX_BLOCK *ib, VCN vcn) icx->block_size); return -1; } + if (((s32)le32_to_cpu(ib->index.index_length) < 0) + || ((u8*)&ib->index + le32_to_cpu(ib->index.index_length) > + (u8*)ib + icx->block_size)) { + ntfs_log_error("Size of index buffer (%lld) of inode %llu " + "exceeds maximum size.\n", (long long)vcn, + (unsigned long long)icx->ni->mft_no); + return -1; + } return 0; } From 67f959df96136e2922100b8b585d92126438794f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 53/83] Fixed the computation of the end of index entry The end of an index entry is related to its full length, not to the length of the key. Added an error message in an overflow case. --- libntfs-3g/dir.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 76c052cf..d4c8204e 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -306,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; } /* @@ -446,7 +447,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", @@ -1248,9 +1249,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; @@ -1408,7 +1413,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); From f85ce6ff2e34464ceb68ea2d9766750b61512bbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 54/83] Skipped errors for bad free clusters before they are computed The count of free clusters may be updated while mounting before it has been initialized, which may lead to irrelevant error messages. Moreover the count is not computed at all in some ntfsprogs utilities. So set up a flags to avoid outputting irrelevant errors. --- include/ntfs-3g/volume.h | 5 +++++ libntfs-3g/lcnalloc.c | 15 +++++++++------ libntfs-3g/volume.c | 4 +++- src/lowntfs-3g.c | 3 +-- src/ntfs-3g.c | 3 +-- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index 30e07906..42800a28 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -117,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) @@ -155,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 diff --git a/libntfs-3g/lcnalloc.c b/libntfs-3g/lcnalloc.c index 486e3510..a1c1ee33 100644 --- a/libntfs-3g/lcnalloc.c +++ b/libntfs-3g/lcnalloc.c @@ -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); diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index d1a5f2f0..2cc827bd 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -1894,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); } diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index cfa61626..f28d7e0e 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -4347,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; } diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 1f148ba0..5c07f23f 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -4056,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; } From 61134117c14302408e8a1520b6712ff2a3a41d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 55/83] Fixed checking the end of attrdef data Incomplete attribute definitions have to be rejected. --- libntfs-3g/attrib.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 66c1727d..3cb696e1 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3767,8 +3767,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; From 55e7326350dd0002ee3058caa49399e2df0ce2ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 56/83] Avoided endless recursions when allocating the main bitmap Allocating clusters to the main bitmap may imply updating the bitmap itself within a cluster not yet allocated. This can turn into endless recursions and has to be rejected. Currently the bitmap is assumed to be fully allocated. --- libntfs-3g/attrib.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 3cb696e1..1c748544 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -1256,6 +1256,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; From 2bf50778043dfebd8c1cb45859446069d99c6d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 57/83] Checked consistency of index blocks Improved existing consistency checks of index blocks and grouped them into a specific function. --- include/ntfs-3g/index.h | 2 + libntfs-3g/dir.c | 52 ++--------------- libntfs-3g/index.c | 125 +++++++++++++++++++++++++--------------- 3 files changed, 87 insertions(+), 92 deletions(-) diff --git a/include/ntfs-3g/index.h b/include/ntfs-3g/index.h index 4e3f73f7..22dcbb80 100644 --- a/include/ntfs-3g/index.h +++ b/include/ntfs-3g/index.h @@ -139,6 +139,8 @@ 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_consistent(const INDEX_ENTRY *ie, COLLATION_RULES collation_rule, u64 inum); extern int ntfs_index_lookup(const void *key, const int key_len, diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index d4c8204e..142c6826 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -407,33 +407,12 @@ descend_into_child_node: 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 (((s32)le32_to_cpu(ia->index.index_length) < 0) - || (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 + @@ -1372,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)); diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index c8c03481..943450e2 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -388,50 +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; - } - if (((s32)le32_to_cpu(ib->index.index_length) < 0) - || ((u8*)&ib->index + le32_to_cpu(ib->index.index_length) > - (u8*)ib + icx->block_size)) { - ntfs_log_error("Size of index buffer (%lld) of inode %llu " - "exceeds maximum size.\n", (long long)vcn, - (unsigned long long)icx->ni->mft_no); - return -1; - } - return 0; -} - static INDEX_ROOT *ntfs_ir_lookup(ntfs_inode *ni, ntfschar *name, u32 name_len, ntfs_attr_search_ctx **ctx) { @@ -477,6 +433,82 @@ 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 * @@ -671,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; } From e70d10d8488d3dea835d0bd85e16e567af23cd82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 58/83] Added a check of the minimal length of some attributes The minimal lengths of STANDARD_ATTRIBUTE and OBJECT_ID were not checked and could lead to out-of-buffer access. --- libntfs-3g/attrib.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 1c748544..628fa819 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3492,7 +3492,28 @@ int ntfs_attr_consistent(const ATTR_RECORD *a, const MFT_REF mref) } 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) { From b95b4ba1a54e589475bee4c88d33f020b4d0d562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 59/83] Rejected negative data length in readall() The negative data length of an attribute is an indication of a probable corruption and must be rejected. --- libntfs-3g/attrib.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 628fa819..f7a2ef18 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -6930,9 +6930,10 @@ void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, * index bitmaps may need more, but still limited by * the number of clusters. */ - if ((na->data_size > 65536) + if (((u64)na->data_size > 65536) && ((type != AT_BITMAP) - || ((na->data_size << 3) > ni->vol->nr_clusters))) { + || ((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; From 0911ef206dc8fea68e18282a1ee5f4489cf0ca1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 60/83] Rejected negative data length in an attribute list The negative data length of an attribute list is an indication of a probable corruption and must be rejected. --- libntfs-3g/inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index fe57efaa..8819ff1d 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -231,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; } From 4462f82580a691619e98683efb4141ec00119687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 61/83] Reset the resident attribute offset when appending from none When there is no resident attribute value, its offset is unsafe, so better to recompute it when appending data. --- libntfs-3g/attrib.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index f7a2ef18..d2d83ac6 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -4828,6 +4828,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) From 81725f6a54863145e6e715d0c978ae6edaeb9ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 62/83] Made sure the requested compression block size is supported Compressed files can only be opened if NTFS version < 3.0, and the only supported compression block size is 16 clusters long. --- libntfs-3g/attrib.c | 22 ++++++++++++++++++++++ libntfs-3g/compress.c | 18 ++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index d2d83ac6..f4f31cf6 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -489,6 +489,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 +508,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, diff --git a/libntfs-3g/compress.c b/libntfs-3g/compress.c index 0c22bc9c..390b2d97 100644 --- a/libntfs-3g/compress.c +++ b/libntfs-3g/compress.c @@ -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) { @@ -1702,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 */ @@ -1883,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; From bb4456d3397c5eac559c35b2310d0bb02cd15525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 63/83] Redesigned the INDEX_ROOT consistency checks By ordering the values from smallest to biggest, there is less chance to be caught by an arithmetic overflow. --- libntfs-3g/attrib.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index f4f31cf6..b831c2bb 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3496,16 +3496,15 @@ int ntfs_attr_consistent(const ATTR_RECORD *a, const MFT_REF mref) 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) - & 0xff000000) - || ((le32_to_cpu(a->value_length) - - le32_to_cpu(ir->index.index_length)) - < offsetof(INDEX_ROOT,index)) - || ((le32_to_cpu(a->value_length) - - le32_to_cpu(ir->index.index_length)) < le32_to_cpu(ir->index.entries_offset)) - || (le32_to_cpu(ir->index.index_length) - > le32_to_cpu(ir->index.allocated_size))) { + || (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); From 45141516d7f9512e1a340ff466b35a9d66da4df3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 64/83] Renamed ntfs_attr_consistent() as ntfs_attr_inconsistent() The original name was error prone while checking the condition. --- include/ntfs-3g/attrib.h | 2 +- libntfs-3g/attrib.c | 2 +- libntfs-3g/mft.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/ntfs-3g/attrib.h b/include/ntfs-3g/attrib.h index e2cf41b3..f2a180c9 100644 --- a/include/ntfs-3g/attrib.h +++ b/include/ntfs-3g/attrib.h @@ -398,7 +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_consistent(const ATTR_RECORD *a, const MFT_REF mref); +extern int ntfs_attr_inconsistent(const ATTR_RECORD *a, const MFT_REF mref); #endif /* defined _NTFS_ATTRIB_H */ diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index b831c2bb..420fb493 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3402,7 +3402,7 @@ not_found: * -1 with errno = EIO otherwise */ -int ntfs_attr_consistent(const ATTR_RECORD *a, const MFT_REF mref) +int ntfs_attr_inconsistent(const ATTR_RECORD *a, const MFT_REF mref) { const FILE_NAME_ATTR *fn; const INDEX_ROOT *ir; diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index cc244871..d0a601ff 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -286,7 +286,7 @@ int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, && (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_consistent(a, mref)) { + if (!ntfs_attr_inconsistent(a, mref)) { previous_type = a->type; offset += le32_to_cpu(a->length); space -= le32_to_cpu(a->length); From a337c4c1eb75638026bfe4b385a3335801f5b001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 65/83] Renamed ntfs_index_entry_consistent() as ntfs_index_entry_inconsistent() The original name was error prone when checking the condition. --- include/ntfs-3g/index.h | 2 +- libntfs-3g/dir.c | 8 ++++---- libntfs-3g/index.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/ntfs-3g/index.h b/include/ntfs-3g/index.h index 22dcbb80..d001863a 100644 --- a/include/ntfs-3g/index.h +++ b/include/ntfs-3g/index.h @@ -141,7 +141,7 @@ 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_consistent(const INDEX_ENTRY *ie, +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__; diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 142c6826..5cab5c54 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -321,7 +321,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, break; /* The file name must not overflow from the entry */ - if (ntfs_index_entry_consistent(ie, COLLATION_FILE_NAME, + if (ntfs_index_entry_inconsistent(ie, COLLATION_FILE_NAME, dir_ni->mft_no)) { errno = EIO; goto put_err_out; @@ -442,7 +442,7 @@ descend_into_child_node: break; /* The file name must not overflow from the entry */ - if (ntfs_index_entry_consistent(ie, COLLATION_FILE_NAME, + if (ntfs_index_entry_inconsistent(ie, COLLATION_FILE_NAME, dir_ni->mft_no)) { errno = EIO; goto close_err_out; @@ -1247,7 +1247,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, continue; /* The file name must not overflow from the entry */ - if (ntfs_index_entry_consistent(ie, COLLATION_FILE_NAME, + if (ntfs_index_entry_inconsistent(ie, COLLATION_FILE_NAME, dir_ni->mft_no)) { errno = EIO; goto dir_err_out; @@ -1389,7 +1389,7 @@ find_next_index_buffer: continue; /* The file name must not overflow from the entry */ - if (ntfs_index_entry_consistent(ie, COLLATION_FILE_NAME, + if (ntfs_index_entry_inconsistent(ie, COLLATION_FILE_NAME, dir_ni->mft_no)) { errno = EIO; goto dir_err_out; diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 943450e2..e48d6aaf 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -522,7 +522,7 @@ int ntfs_index_block_inconsistent(const INDEX_BLOCK *ib, u32 block_size, * -1 otherwise (with errno unchanged) */ -int ntfs_index_entry_consistent(const INDEX_ENTRY *ie, +int ntfs_index_entry_inconsistent(const INDEX_ENTRY *ie, COLLATION_RULES collation_rule, u64 inum) { int ret; @@ -614,7 +614,7 @@ static int ntfs_ie_lookup(const void *key, const int key_len, return STATUS_ERROR; } /* Make sure key and data do not overflow from entry */ - if (ntfs_index_entry_consistent(ie, icx->ir->collation_rule, + if (ntfs_index_entry_inconsistent(ie, icx->ir->collation_rule, icx->ni->mft_no)) { errno = EIO; return STATUS_ERROR; From 1261e6b60a33a5984c5662313505998625318d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 26 Jul 2021 08:49:45 +0200 Subject: [PATCH 66/83] Fixed the detection of the end of attribute list The recent detection of a truncated attribute list entry overlooked the normal detection of the end of list. Moreover the check for name overflow is to be done later and not needed at this stage. --- libntfs-3g/attrib.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 420fb493..e8d6fafb 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3092,10 +3092,11 @@ static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name, al_entry = (ATTR_LIST_ENTRY*)((char*)ctx->al_entry + le16_to_cpu(ctx->al_entry->length)); - if ((al_entry->name_length - && ((u8*)al_entry + al_entry->name_offset - + al_entry->name_length * sizeof(ntfschar)) - > al_end)) + 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 From 21b49600eac039034723588e2a7c1e4af3bb53a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sat, 14 Aug 2021 08:44:08 +0200 Subject: [PATCH 67/83] Configured for version 2021.8.14 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index e2005cc5..081fa432 100644 --- a/configure.ac +++ b/configure.ac @@ -24,7 +24,7 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[2021.5.19],[ntfs-3g-devel@lists.sf.net]) +AC_INIT([ntfs-3g],[2021.8.14],[ntfs-3g-devel@lists.sf.net]) LIBNTFS_3G_VERSION="89" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) From a213e6352bb03f18db2dac115f310dfffbb7c224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sun, 22 Aug 2021 17:01:39 +0200 Subject: [PATCH 68/83] Defined Github as the host for documentation Documentation and support is now on github.com/tuxera --- ChangeLog | 7 ++----- NEWS | 6 ++---- README | 42 +++++++++++++++++------------------------- 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/ChangeLog b/ChangeLog index a49d76a1..68c1565e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 diff --git a/NEWS b/NEWS index 3a7effde..1040fd8b 100644 --- a/NEWS +++ b/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/ diff --git a/README b/README index f072c0ea..9962af40 100644 --- a/README +++ b/README @@ -3,10 +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, Windows 7, Windows 8 and Windows 10 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 @@ -19,25 +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 - - https://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 - - https://www.tuxera.com/community/ - -The project's official git repository is now hosted on GitHub: +forums, and other information are available on the project web site at https://github.com/tuxera/ntfs-3g +The project has been funded, supported and maintained since 2008 by Tuxera: + + https://tuxera.com + + 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 @@ -46,6 +43,7 @@ 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 ================== @@ -78,12 +76,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 ===== @@ -111,23 +103,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: From 31ac7e4f0fc07beeca455eba6a8f02abd7e4d76f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sun, 22 Aug 2021 17:05:00 +0200 Subject: [PATCH 69/83] Configured for version 2021.8.22 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 081fa432..029b334f 100644 --- a/configure.ac +++ b/configure.ac @@ -24,7 +24,7 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[2021.8.14],[ntfs-3g-devel@lists.sf.net]) +AC_INIT([ntfs-3g],[2021.8.22],[ntfs-3g-devel@lists.sf.net]) LIBNTFS_3G_VERSION="89" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) From 88c4a19c5ae63f548b6b425eb17db6a784c6ab89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 25 Aug 2021 08:50:03 +0200 Subject: [PATCH 70/83] Updated the README Added a recommendation to use ntfs-3g packaged by a distribution, and inserted a link to the Wiki. --- README | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README b/README index 9962af40..aaa63414 100644 --- a/README +++ b/README @@ -24,7 +24,7 @@ available in Windows 10 can also be read through a plugin. News, support answers, problem submission instructions, support and discussion forums, and other information are available on the project web site at - https://github.com/tuxera/ntfs-3g + https://github.com/tuxera/ntfs-3g/wiki The project has been funded, supported and maintained since 2008 by Tuxera: @@ -47,8 +47,13 @@ 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 From 84739d9e4da5346d1c347620c1d1489fbdaa5436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 30 Aug 2021 09:20:48 +0200 Subject: [PATCH 71/83] Updated the ntfs-3g manual Reordered the options alphabetically and updated the urls. --- src/ntfs-3g.8.in | 382 ++++++++++++++++++++++++----------------------- 1 file changed, 194 insertions(+), 188 deletions(-) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index d4338a9c..30b41103 100644 --- a/src/ntfs-3g.8.in +++ b/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,92 +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 -.BI special_files= value -This option selects a mode for representing a special file to be created -(symbolic link, socket, fifo, character or block device). The mode can -be \fBinterix\fR or \fBwsl\fR, and existing files in either mode are -recognized irrespective of the selected mode. Interix is the traditional -mode, used by default, and wsl is interoperable with Windows WSL, but -it is not compatible with Windows versions earlier than Windows 10. -.TP -.B efs_raw -This option should only be used in backup or restore situation. -It changes the apparent size of files and the behavior of read and -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 @@ -379,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 @@ -454,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 @@ -472,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 From 399ba862c9aa926e0a3955f2970ad7438f37770f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 30 Aug 2021 09:27:33 +0200 Subject: [PATCH 72/83] Updated the urls present in ntfs-3g code The urls now point at Github --- libntfs-3g/volume.c | 4 ++-- src/lowntfs-3g.c | 6 +++--- src/ntfs-3g.c | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 2cc827bd..a6d467b3 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -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 diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index f28d7e0e..9330500c 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -300,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 @@ -4398,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) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 5c07f23f..d8227e71 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -235,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 @@ -4106,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) From 92b9fbc6fe5aaa940a401b8f5ff2ed8cac6d5cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 30 Aug 2021 10:42:14 +0200 Subject: [PATCH 73/83] Updated the urls present in the ntfsprogs manuals The urls now point at Github --- ntfsprogs/mkntfs.8.in | 2 +- ntfsprogs/ntfscat.8.in | 2 +- ntfsprogs/ntfsclone.8.in | 2 +- ntfsprogs/ntfscluster.8.in | 2 +- ntfsprogs/ntfscmp.8.in | 2 +- ntfsprogs/ntfscp.8.in | 2 +- ntfsprogs/ntfsdecrypt.8.in | 2 +- ntfsprogs/ntfsfallocate.8.in | 2 +- ntfsprogs/ntfsfix.8.in | 2 +- ntfsprogs/ntfsinfo.8.in | 2 +- ntfsprogs/ntfslabel.8.in | 2 +- ntfsprogs/ntfsls.8.in | 2 +- ntfsprogs/ntfsprogs.8.in | 2 +- ntfsprogs/ntfsrecover.8.in | 2 +- ntfsprogs/ntfsresize.8.in | 2 +- ntfsprogs/ntfssecaudit.8.in | 2 +- ntfsprogs/ntfstruncate.8.in | 2 +- ntfsprogs/ntfsundelete.8.in | 2 +- ntfsprogs/ntfsusermap.8.in | 4 +--- ntfsprogs/ntfswipe.8.in | 2 +- src/ntfs-3g.probe.8.in | 12 ++++++++---- 21 files changed, 28 insertions(+), 26 deletions(-) diff --git a/ntfsprogs/mkntfs.8.in b/ntfsprogs/mkntfs.8.in index 13a17af2..b4f61b66 100644 --- a/ntfsprogs/mkntfs.8.in +++ b/ntfsprogs/mkntfs.8.in @@ -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), diff --git a/ntfsprogs/ntfscat.8.in b/ntfsprogs/ntfscat.8.in index 478d9b4b..d594b9d7 100644 --- a/ntfsprogs/ntfscat.8.in +++ b/ntfsprogs/ntfscat.8.in @@ -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. diff --git a/ntfsprogs/ntfsclone.8.in b/ntfsprogs/ntfsclone.8.in index a1585c3c..aa04dde8 100644 --- a/ntfsprogs/ntfsclone.8.in +++ b/ntfsprogs/ntfsclone.8.in @@ -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) diff --git a/ntfsprogs/ntfscluster.8.in b/ntfsprogs/ntfscluster.8.in index b0052df1..458a1cc3 100644 --- a/ntfsprogs/ntfscluster.8.in +++ b/ntfsprogs/ntfscluster.8.in @@ -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), diff --git a/ntfsprogs/ntfscmp.8.in b/ntfsprogs/ntfscmp.8.in index 647cf544..7196b437 100644 --- a/ntfsprogs/ntfscmp.8.in +++ b/ntfsprogs/ntfscmp.8.in @@ -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), diff --git a/ntfsprogs/ntfscp.8.in b/ntfsprogs/ntfscp.8.in index 67973331..ba1777a4 100644 --- a/ntfsprogs/ntfscp.8.in +++ b/ntfsprogs/ntfscp.8.in @@ -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) diff --git a/ntfsprogs/ntfsdecrypt.8.in b/ntfsprogs/ntfsdecrypt.8.in index fd156b07..0ac7f33c 100644 --- a/ntfsprogs/ntfsdecrypt.8.in +++ b/ntfsprogs/ntfsdecrypt.8.in @@ -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, diff --git a/ntfsprogs/ntfsfallocate.8.in b/ntfsprogs/ntfsfallocate.8.in index 04cc9ea9..35dc224f 100644 --- a/ntfsprogs/ntfsfallocate.8.in +++ b/ntfsprogs/ntfsfallocate.8.in @@ -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), diff --git a/ntfsprogs/ntfsfix.8.in b/ntfsprogs/ntfsfix.8.in index 51010001..a9657c3a 100644 --- a/ntfsprogs/ntfsfix.8.in +++ b/ntfsprogs/ntfsfix.8.in @@ -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), diff --git a/ntfsprogs/ntfsinfo.8.in b/ntfsprogs/ntfsinfo.8.in index e0141c79..03efc654 100644 --- a/ntfsprogs/ntfsinfo.8.in +++ b/ntfsprogs/ntfsinfo.8.in @@ -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) diff --git a/ntfsprogs/ntfslabel.8.in b/ntfsprogs/ntfslabel.8.in index dcd4d00e..a9493660 100644 --- a/ntfsprogs/ntfslabel.8.in +++ b/ntfsprogs/ntfslabel.8.in @@ -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), diff --git a/ntfsprogs/ntfsls.8.in b/ntfsprogs/ntfsls.8.in index f528a3bb..874bc044 100644 --- a/ntfsprogs/ntfsls.8.in +++ b/ntfsprogs/ntfsls.8.in @@ -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) diff --git a/ntfsprogs/ntfsprogs.8.in b/ntfsprogs/ntfsprogs.8.in index 82abf861..c21b839e 100644 --- a/ntfsprogs/ntfsprogs.8.in +++ b/ntfsprogs/ntfsprogs.8.in @@ -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) diff --git a/ntfsprogs/ntfsrecover.8.in b/ntfsprogs/ntfsrecover.8.in index 6c0eb69b..e4eca85d 100644 --- a/ntfsprogs/ntfsrecover.8.in +++ b/ntfsprogs/ntfsrecover.8.in @@ -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), diff --git a/ntfsprogs/ntfsresize.8.in b/ntfsprogs/ntfsresize.8.in index 4c21ed2c..dee51239 100644 --- a/ntfsprogs/ntfsresize.8.in +++ b/ntfsprogs/ntfsresize.8.in @@ -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), diff --git a/ntfsprogs/ntfssecaudit.8.in b/ntfsprogs/ntfssecaudit.8.in index b715f4d9..955d6c02 100644 --- a/ntfsprogs/ntfssecaudit.8.in +++ b/ntfsprogs/ntfssecaudit.8.in @@ -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. diff --git a/ntfsprogs/ntfstruncate.8.in b/ntfsprogs/ntfstruncate.8.in index e793f649..e69d4b98 100644 --- a/ntfsprogs/ntfstruncate.8.in +++ b/ntfsprogs/ntfstruncate.8.in @@ -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), diff --git a/ntfsprogs/ntfsundelete.8.in b/ntfsprogs/ntfsundelete.8.in index f80a4d75..d95c4a07 100644 --- a/ntfsprogs/ntfsundelete.8.in +++ b/ntfsprogs/ntfsundelete.8.in @@ -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), diff --git a/ntfsprogs/ntfsusermap.8.in b/ntfsprogs/ntfsusermap.8.in index 9c60899e..a38e43dd 100644 --- a/ntfsprogs/ntfsusermap.8.in +++ b/ntfsprogs/ntfsusermap.8.in @@ -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. diff --git a/ntfsprogs/ntfswipe.8.in b/ntfsprogs/ntfswipe.8.in index b21e8c9e..adf8fb24 100644 --- a/ntfsprogs/ntfswipe.8.in +++ b/ntfsprogs/ntfswipe.8.in @@ -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), diff --git a/src/ntfs-3g.probe.8.in b/src/ntfs-3g.probe.8.in index 62ce57e5..9b57493a 100644 --- a/src/ntfs-3g.probe.8.in +++ b/src/ntfs-3g.probe.8.in @@ -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. From a8818cf779d3a32f2f52337c6f258c16719625a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 21 Sep 2021 10:53:16 +0200 Subject: [PATCH 74/83] Used a default usn when the former one cannot be retrieved When creating a new MFT record, the former seq_no and usn are retrieved to avoid the new one to be mistaken for the former one. This may not be possible when the record is used for the first time or after some bad error. In such situation use default values. --- libntfs-3g/mft.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index d0a601ff..5052d1ec 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -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 @@ -1529,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); From 838b6e35b43062353998853eab50cd0675201ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 21 Sep 2021 10:54:50 +0200 Subject: [PATCH 75/83] Made sure there is no null character in an attribute name When copying an attribute name which contains a null, it is truncated and this may lead to accessing non-allocated bytes when relying on the expected name length. Such names must therefore be rejected. --- libntfs-3g/attrib.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index e8d6fafb..00bfca84 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -414,7 +414,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; From 60717a846deaaea47e50ce58872869f7bd1103b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 21 Sep 2021 10:56:06 +0200 Subject: [PATCH 76/83] Avoided allocating and reading an attribute beyond its full size Before reading a full attribute value for internal use, its expected length has been checked to be < 0x40000. However the allocated size in the runlist may be much bigger as a consequence of a bug or malice. To prevent malloc'ing excessive size, restrict the size of the last run to read to the needed length. --- libntfs-3g/attrib.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 00bfca84..51c8536f 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -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; From 6efc1305c1951c1d72181f449f2fab68fa25fae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 20 Oct 2021 09:53:28 +0200 Subject: [PATCH 77/83] Made sure the client log data does not overflow from restart page Strengthen the consistency check of the length of restart pages, and check that log client records are within such a restart page. --- libntfs-3g/logfile.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/libntfs-3g/logfile.c b/libntfs-3g/logfile.c index adc0557f..9c3155e7 100644 --- a/libntfs-3g/logfile.c +++ b/libntfs-3g/logfile.c @@ -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; From 5ce8941bf47291cd6ffe7cdb1797253f1cc3a86f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 5 Nov 2021 08:41:20 +0100 Subject: [PATCH 78/83] Made sure there is no null character in an attribute name (bis) When copying an attribute name which contains a null, it is truncated and this may lead to accessing non-allocated bytes when relying on the expected name length. Such (illegal) names must therefore be rejected. --- libntfs-3g/attrib.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 51c8536f..efb91943 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -452,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; From 96412e28e5c7ac2d15f1cff8c825330bbb60976e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 10 May 2022 10:30:24 +0200 Subject: [PATCH 79/83] Fixed possible out-of-buffer condition in ntfsck A bad usa_count could lead to an out-of-buffer condition. Just avoid the issue and report the error, still not fix it. --- ntfsprogs/ntfsck.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ntfsprogs/ntfsck.c b/ntfsprogs/ntfsck.c index d49f3f96..8c126411 100644 --- a/ntfsprogs/ntfsck.c +++ b/ntfsprogs/ntfsck.c @@ -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 Date: Tue, 10 May 2022 10:40:17 +0200 Subject: [PATCH 80/83] Fixed operation on little endian data Forcing an even usa_of, in a recent security patch, must be made on cpu endian data. --- libntfs-3g/mft.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 5052d1ec..aefbb5f1 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -1537,7 +1537,7 @@ found_free_rec: */ seq_no = m->sequence_number; if (le16_to_cpu(m->usa_ofs) <= (NTFS_BLOCK_SIZE - 2)) - usn = *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs & -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)) { From 7f81935f32e58e8fec22bc46683b1b067469405f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 10 May 2022 10:44:34 +0200 Subject: [PATCH 81/83] Returned an error code when the --help or --version options are used Accepting --help or --version options may leave the ntfs-3g process in an unclean state, so reject them while processing options. Also reject them in libfuse-lite. --- libfuse-lite/mount.c | 3 +-- src/ntfs-3g_common.c | 6 ++++++ src/ntfs-3g_common.h | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/libfuse-lite/mount.c b/libfuse-lite/mount.c index 64adee7d..6ae29d8c 100644 --- a/libfuse-lite/mount.c +++ b/libfuse-lite/mount.c @@ -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__ diff --git a/src/ntfs-3g_common.c b/src/ntfs-3g_common.c index 7e3e93d2..29021dfc 100644 --- a/src/ntfs-3g_common.c +++ b/src/ntfs-3g_common.c @@ -128,6 +128,10 @@ const struct DEFOPTION optionlist[] = { { "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 */ } ; @@ -521,6 +525,8 @@ char *parse_mount_options(ntfs_fuse_context_t *ctx, * 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); diff --git a/src/ntfs-3g_common.h b/src/ntfs-3g_common.h index 4ed256a3..8ead5107 100644 --- a/src/ntfs-3g_common.h +++ b/src/ntfs-3g_common.h @@ -94,6 +94,8 @@ enum { OPT_EFS_RAW, OPT_POSIX_NLINK, OPT_SPECIAL_FILES, + OPT_HELP, + OPT_VERSION, } ; /* Option flags */ From fb28eef6f1c26170566187c1ab7dc913a13ea43c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 10 May 2022 10:48:18 +0200 Subject: [PATCH 82/83] Hardened the checking of directory offset requested by a readdir When asked for the next directory entries, make sure the chunk offset is within valid values, otherwise return no more entries in chunk. --- libfuse-lite/fuse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c index 6f9242b7..3d653e63 100644 --- a/libfuse-lite/fuse.c +++ b/libfuse-lite/fuse.c @@ -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 From 875a1d4e90874457151466870fe2c70a2bedfd98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 17 May 2022 08:34:26 +0200 Subject: [PATCH 83/83] Configured for version 2022.5.17 This is a security release. The soname is unchanged as no API has changed. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 029b334f..9aa25bd5 100644 --- a/configure.ac +++ b/configure.ac @@ -24,7 +24,7 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[2021.8.22],[ntfs-3g-devel@lists.sf.net]) +AC_INIT([ntfs-3g],[2022.5.17],[ntfs-3g-devel@lists.sf.net]) LIBNTFS_3G_VERSION="89" AC_CONFIG_SRCDIR([src/ntfs-3g.c])