Defined option "posix_nlink" to compute a Posix compliant st_nlink

When the mount option "posix_nlink" is used, the number of links
returned by stat complies with Posix : the legacy 8.3 names are not
taken into account, and the subdirectories are taken into account
for directories. This causes some overhead for recomputing the
number of links.
This commit is contained in:
Jean-Pierre André 2020-08-14 11:50:35 +02:00
parent 1bc996f52f
commit d6558f1dea
7 changed files with 104 additions and 3 deletions

View File

@ -117,6 +117,7 @@ int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
const char *value, size_t size, int flags);
int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni);
int ntfs_dir_link_cnt(ntfs_inode *ni);
#if CACHE_INODE_SIZE

View File

@ -2792,3 +2792,82 @@ int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni)
}
return (res);
}
/*
* Increment the count of subdirectories
* (excluding entries with a short name)
*/
static int nlink_increment(void *nlink_ptr,
const ntfschar *name __attribute__((unused)),
const int len __attribute__((unused)),
const int type,
const s64 pos __attribute__((unused)),
const MFT_REF mref __attribute__((unused)),
const unsigned int dt_type)
{
if ((dt_type == NTFS_DT_DIR) && (type != FILE_NAME_DOS))
(*((int*)nlink_ptr))++;
return (0);
}
/*
* Compute the number of hard links according to Posix
* For a directory count the subdirectories whose name is not
* a short one, but count "." and ".."
* Otherwise count the names, excluding the short ones.
*
* if there is an error, a null count is returned.
*/
int ntfs_dir_link_cnt(ntfs_inode *ni)
{
ntfs_attr_search_ctx *actx;
FILE_NAME_ATTR *fn;
s64 pos;
int err = 0;
int nlink = 0;
if (!ni) {
ntfs_log_error("Invalid argument.\n");
errno = EINVAL;
goto err_out;
}
if (ni->nr_extents == -1)
ni = ni->base_ni;
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
/*
* Directory : scan the directory and count
* subdirectories whose name is not DOS-only.
* The directory names are ignored, but "." and ".."
* are taken into account.
*/
pos = 0;
err = ntfs_readdir(ni, &pos, &nlink, nlink_increment);
if (err)
nlink = 0;
} else {
/*
* Non-directory : search for FILE_NAME attributes,
* and count those which are not DOS-only ones.
*/
actx = ntfs_attr_get_search_ctx(ni, NULL);
if (!actx)
goto err_out;
while (!(err = ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0,
CASE_SENSITIVE, 0, NULL, 0, actx))) {
fn = (FILE_NAME_ATTR*)((u8*)actx->attr +
le16_to_cpu(actx->attr->value_offset));
if (fn->file_name_type != FILE_NAME_DOS)
nlink++;
}
if (err && (errno != ENOENT))
nlink = 0;
ntfs_attr_put_search_ctx(actx);
}
if (!nlink)
ntfs_log_perror("Failed to compute nlink of inode %lld",
(long long)ni->mft_no);
err_out :
return (nlink);
}

View File

@ -708,6 +708,9 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx,
memset(stbuf, 0, sizeof(struct stat));
withusermapping = (scx->mapping[MAPUSERS] != (struct MAPPING*)NULL);
stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count);
if (ctx->posix_nlink
&& !(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) {
@ -770,6 +773,7 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx,
}
stbuf->st_size = ni->data_size;
stbuf->st_blocks = ni->allocated_size >> 9;
if (!ctx->posix_nlink)
stbuf->st_nlink = 1; /* Make find(1) work */
}
} else {

View File

@ -250,6 +250,13 @@ they do not appear in Windows directory displays either.
When a file is renamed or linked with a new name, the hidden flag is
adjusted to the latest name.
.TP
.B posix_nlink
Compute the count of hard links of a file or directory according to
the Posix specifications. When this option is not set, a count of 1
is set for directories, and the short name of files is accounted for.
Using the option entails some penalty as the count is not stored and
has to be computed.
.TP
.B windows_names
This option prevents files, directories and extended attributes to be
created with a name not allowed by windows, because

View File

@ -790,6 +790,9 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
}
#endif
stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count);
if (ctx->posix_nlink
&& !(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,6 +855,7 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
}
stbuf->st_size = ni->data_size;
stbuf->st_blocks = ni->allocated_size >> 9;
if (!ctx->posix_nlink)
stbuf->st_nlink = 1; /* Make find(1) work */
}
} else {

View File

@ -1,7 +1,7 @@
/**
* ntfs-3g_common.c - Common definitions for ntfs-3g and lowntfs-3g.
*
* Copyright (c) 2010-2019 Jean-Pierre Andre
* Copyright (c) 2010-2020 Jean-Pierre Andre
* Copyright (c) 2010 Erik Larsson
*
* This program/include file is free software; you can redistribute it and/or
@ -126,6 +126,7 @@ const struct DEFOPTION optionlist[] = {
{ "usermapping", OPT_USERMAPPING, FLGOPT_STRING },
{ "xattrmapping", OPT_XATTRMAPPING, FLGOPT_STRING },
{ "efs_raw", OPT_EFS_RAW, FLGOPT_BOGUS },
{ "posix_nlink", OPT_POSIX_NLINK, FLGOPT_BOGUS },
{ (const char*)NULL, 0, 0 } /* end marker */
} ;
@ -492,6 +493,9 @@ char *parse_mount_options(ntfs_fuse_context_t *ctx,
ctx->efs_raw = TRUE;
break;
#endif /* HAVE_SETXATTR */
case OPT_POSIX_NLINK :
ctx->posix_nlink = TRUE;
break;
case OPT_FSNAME : /* Filesystem name. */
/*
* We need this to be able to check whether filesystem

View File

@ -92,6 +92,7 @@ enum {
OPT_USERMAPPING,
OPT_XATTRMAPPING,
OPT_EFS_RAW,
OPT_POSIX_NLINK,
} ;
/* Option flags */
@ -153,6 +154,7 @@ typedef struct {
BOOL no_detach;
BOOL blkdev;
BOOL mounted;
BOOL posix_nlink;
#ifdef HAVE_SETXATTR /* extended attributes interface required */
BOOL efs_raw;
#ifdef XATTR_MAPPINGS