Hi,
Attached is an updated patch for computing a Posix compliant
count of links, including for directories.
The option has been renamed posix_nlink.
Please test and report.
Jean-Pierre
Jean-Pierre André wrote on 7/27/20 10:49 AM:
Hi,
Attached is a proposed patch for computing the hard links counts
whenever it is required, instead of relying on the value stored on disk.
This obviously causes some extra delay when listing a crowded
directory such as \Windows\System32.
This feature is activated by setting the mount option "hard_links".
Please test and report.
Note : this does not apply to directories. Counting the number
of subdirectories is much more time consuming.
Jean-Pierre
Alexander Shchadilov wrote on 7/26/20 10:17 PM:
Hello,
It seems that a counter in an MFT entry that is often referred to as a
"hard link counter" (offset 0x12 - 0x13) is increased by one when
Windows adds an 8.3 alias for a file name.
Description of short names in Windows docs:
"When you create a long file name, Windows may also create a short 8.3
form of the name, called the 8.3 alias or short name, and store it on
disk also."
ntfs-3g should differentiate hard links and short names, even if they
are stored on disk in a similar way, and calculate an accurate number
of hard links. I can not provide an established definition of a hard
link from some Linux-related standard, but the following command in
Windows 10 will not show a 8.3 name in its output:
fsutil hardlink list <filename>
>From fsutil documentation:
A hard link is a directory entry for a file. Every file can be
considered to have at least one hard link.
On NTFS volumes, each file can have multiple hard links, so a single
file can appear in many directories (or even in the same directory
with different names).
Considering that this definition is provided by Microsoft, the company
that developed NTFS, and 8.3 names do not make a file "appear in the
same directory with different names" when viewed by File Explorer in
Windows, I think it is safe to tell that a short alias should be
excluded from a number of hard links.
This issue was discovered in the discussion of QDirStat bug (issue #88
on Github).
Best wishes,
Alexander Shchadilov
--- include/ntfs-3g/dir.h.ref 2020-07-27 10:00:34.320440500 +0200
+++ include/ntfs-3g/dir.h 2020-07-27 21:37:21.063436500 +0200
@@ -117,6 +117,7 @@
int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
const char *value, size_t size, int flags);
int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni);
+int ntfs_dir_link_cnt(ntfs_inode *ni);
#if CACHE_INODE_SIZE
--- src/ntfs-3g_common.h.ref 2020-07-27 10:00:33.812887100 +0200
+++ src/ntfs-3g_common.h 2020-07-27 21:37:21.064760500 +0200
@@ -92,6 +92,7 @@
OPT_USERMAPPING,
OPT_XATTRMAPPING,
OPT_EFS_RAW,
+ OPT_POSIX_NLINK,
} ;
/* Option flags */
@@ -153,6 +154,7 @@
BOOL no_detach;
BOOL blkdev;
BOOL mounted;
+ BOOL posix_nlink;
#ifdef HAVE_SETXATTR /* extended attributes interface required */
BOOL efs_raw;
#ifdef XATTR_MAPPINGS
--- src/ntfs-3g_common.c.ref 2020-07-27 10:00:33.922037800 +0200
+++ src/ntfs-3g_common.c 2020-07-27 21:37:21.066213100 +0200
@@ -126,6 +126,7 @@
{ "usermapping", OPT_USERMAPPING, FLGOPT_STRING },
{ "xattrmapping", OPT_XATTRMAPPING, FLGOPT_STRING },
{ "efs_raw", OPT_EFS_RAW, FLGOPT_BOGUS },
+ { "posix_nlink", OPT_POSIX_NLINK, FLGOPT_BOGUS },
{ (const char*)NULL, 0, 0 } /* end marker */
} ;
@@ -492,6 +493,9 @@
ctx->efs_raw = TRUE;
break;
#endif /* HAVE_SETXATTR */
+ case OPT_POSIX_NLINK :
+ ctx->posix_nlink = TRUE;
+ break;
case OPT_FSNAME : /* Filesystem name. */
/*
* We need this to be able to check whether filesystem
--- src/ntfs-3g.c.ref 2020-07-27 10:00:34.447994200 +0200
+++ src/ntfs-3g.c 2020-07-27 21:37:21.068605200 +0200
@@ -790,6 +790,9 @@
}
#endif
stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count);
+ if (ctx->posix_nlink
+ && !(ni->flags & FILE_ATTR_REPARSE_POINT))
+ stbuf->st_nlink = ntfs_dir_link_cnt(ni);
if (((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
|| (ni->flags & FILE_ATTR_REPARSE_POINT))
@@ -852,7 +855,8 @@
}
stbuf->st_size = ni->data_size;
stbuf->st_blocks = ni->allocated_size >> 9;
- stbuf->st_nlink = 1; /* Make find(1) work */
+ if (!ctx->posix_nlink)
+ stbuf->st_nlink = 1; /* Make find(1) work */
}
} else {
/* Regular or Interix (INTX) file. */
--- src/lowntfs-3g.c.ref 2020-07-27 10:00:34.460471300 +0200
+++ src/lowntfs-3g.c 2020-07-27 21:37:21.071106700 +0200
@@ -645,6 +645,9 @@
memset(stbuf, 0, sizeof(struct stat));
withusermapping = (scx->mapping[MAPUSERS] != (struct MAPPING*)NULL);
stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count);
+ if (ctx->posix_nlink
+ && !(ni->flags & FILE_ATTR_REPARSE_POINT))
+ stbuf->st_nlink = ntfs_dir_link_cnt(ni);
if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
|| (ni->flags & FILE_ATTR_REPARSE_POINT)) {
if (ni->flags & FILE_ATTR_REPARSE_POINT) {
@@ -707,7 +710,8 @@
}
stbuf->st_size = ni->data_size;
stbuf->st_blocks = ni->allocated_size >> 9;
- stbuf->st_nlink = 1; /* Make find(1) work */
+ if (!ctx->posix_nlink)
+ stbuf->st_nlink = 1; /* Make find(1) work */
}
} else {
/* Regular or Interix (INTX) file. */
--- libntfs-3g/dir.c.ref 2020-07-27 10:00:34.342375000 +0200
+++ libntfs-3g/dir.c 2020-07-27 22:05:14.252807900 +0200
@@ -2792,3 +2792,82 @@
}
return (res);
}
+
+/*
+ * Increment the count of subdirectories
+ * (excluding entries with a short name)
+ */
+
+static int nlink_increment(void *nlink_ptr,
+ const ntfschar *name __attribute__((unused)),
+ const int len __attribute__((unused)),
+ const int type,
+ const s64 pos __attribute__((unused)),
+ const MFT_REF mref __attribute__((unused)),
+ const unsigned int dt_type)
+{
+ if ((dt_type == NTFS_DT_DIR) && (type != FILE_NAME_DOS))
+ (*((int*)nlink_ptr))++;
+ return (0);
+}
+
+/*
+ * Compute the number of hard links according to Posix
+ * For a directory count the subdirectories whose name is not
+ * a short one, but count "." and ".."
+ * Otherwise count the names, excluding the short ones.
+ *
+ * if there is an error, a null count is returned.
+ */
+
+int ntfs_dir_link_cnt(ntfs_inode *ni)
+{
+ ntfs_attr_search_ctx *actx;
+ FILE_NAME_ATTR *fn;
+ s64 pos;
+ int err = 0;
+ int nlink = 0;
+
+ if (!ni) {
+ ntfs_log_error("Invalid argument.\n");
+ errno = EINVAL;
+ goto err_out;
+ }
+ if (ni->nr_extents == -1)
+ ni = ni->base_ni;
+ if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
+ /*
+ * Directory : scan the directory and count
+ * subdirectories whose name is not DOS-only.
+ * The directory names are ignored, but "." and ".."
+ * are taken into account.
+ */
+ pos = 0;
+ err = ntfs_readdir(ni, &pos, &nlink, nlink_increment);
+ if (err)
+ nlink = 0;
+ } else {
+ /*
+ * Non-directory : search for FILE_NAME attributes,
+ * and count those which are not DOS-only ones.
+ */
+ actx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!actx)
+ goto err_out;
+ while (!(err = ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0,
+ CASE_SENSITIVE, 0, NULL, 0, actx))) {
+ fn = (FILE_NAME_ATTR*)((u8*)actx->attr +
+ le16_to_cpu(actx->attr->value_offset));
+ if (fn->file_name_type != FILE_NAME_DOS)
+ nlink++;
+ }
+ if (err && (errno != ENOENT))
+ nlink = 0;
+ ntfs_attr_put_search_ctx(actx);
+ }
+ if (!nlink)
+ ntfs_log_perror("Failed to compute nlink of inode %lld",
+ (long long)le64_to_cpu(ni->mft_no));
+err_out :
+ return (nlink);
+}
--- src/ntfs-3g.8.in.ref 2020-07-27 21:32:20.915133200 +0200
+++ src/ntfs-3g.8.in 2020-07-27 21:37:21.074543300 +0200
@@ -250,6 +250,13 @@
When a file is renamed or linked with a new name, the hidden flag is
adjusted to the latest name.
.TP
+.B posix_nlink
+Compute the count of hard links of a file or directory according to
+the Posix specifications. When this option is not set, a count of 1
+is set for directories, and the short name of files is accounted for.
+Using the option entails some penalty as the count is not stored and
+has to be computed.
+.TP
.B windows_names
This option prevents files, directories and extended attributes to be
created with a name not allowed by windows, because
_______________________________________________
ntfs-3g-devel mailing list
ntfs-3g-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/ntfs-3g-devel