mirror of
https://git.code.sf.net/p/ntfs-3g/ntfs-3g.git
synced 2024-11-23 10:04:00 +08:00
Enabled renaming of system extended attributes
This commit is contained in:
parent
f43e972342
commit
351aec3f7a
@ -113,6 +113,13 @@ AC_ARG_ENABLE(
|
||||
[enable_posix_acls="no"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[xattr-mappings],
|
||||
[AS_HELP_STRING([--enable-xattr-mappings],[enable system extended attributes mappings])],
|
||||
,
|
||||
[enable_xattr_mappings="no"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[device-default-io-ops],
|
||||
[AS_HELP_STRING([--disable-device-default-io-ops],[install default IO ops])],
|
||||
@ -349,6 +356,7 @@ test "${enable_device_default_io_ops}" = "no" && AC_DEFINE(
|
||||
|
||||
test "${enable_mtab}" = "no" && AC_DEFINE([IGNORE_MTAB], [1], [Don't update /etc/mtab])
|
||||
test "${enable_posix_acls}" != "no" && AC_DEFINE([POSIXACLS], [1], [POSIX ACL support])
|
||||
test "${enable_xattr_mappings}" != "no" && AC_DEFINE([XATTR_MAPPINGS], [1], [system extended attributes mappings])
|
||||
|
||||
test "${enable_really_static}" = "yes" && enable_library="no"
|
||||
test "${enable_library}" = "no" && enable_ldconfig="no"
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2005 Yura Pakhuchiy
|
||||
* Copyright (c) 2006-2007 Szabolcs Szakacsits
|
||||
* Copyright (c) 2010 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
|
||||
@ -380,6 +381,12 @@ extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type,
|
||||
extern int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type,
|
||||
ntfschar *name, u32 name_len);
|
||||
extern s64 ntfs_attr_get_free_bits(ntfs_attr *na);
|
||||
extern int ntfs_attr_data_read(ntfs_inode *ni,
|
||||
ntfschar *stream_name, int stream_name_len,
|
||||
char *buf, size_t size, off_t offset);
|
||||
extern int ntfs_attr_data_write(ntfs_inode *ni,
|
||||
ntfschar *stream_name, int stream_name_len,
|
||||
char *buf, size_t size, off_t offset);
|
||||
|
||||
#endif /* defined _NTFS_ATTRIB_H */
|
||||
|
||||
|
@ -114,5 +114,8 @@ int ntfs_log_redirect(const char *function, const char *file, int line,
|
||||
#define ntfs_log_leave(FORMAT, ARGS...)do {} while (0)
|
||||
#endif /* DEBUG */
|
||||
|
||||
void ntfs_log_early_error(const char *format, ...)
|
||||
__attribute__((format(printf, 1, 2)));
|
||||
|
||||
#endif /* _LOGGING_H_ */
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* param.h - Parameter values for ntfs-3g
|
||||
*
|
||||
* Copyright (c) 2009 Jean-Pierre Andre
|
||||
* Copyright (c) 2009-2010 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
|
||||
@ -57,6 +57,13 @@ enum {
|
||||
/* only update the final extent of a runlist when appending data */
|
||||
#define PARTIAL_RUNLIST_UPDATING 1
|
||||
|
||||
/*
|
||||
* Parameters for user and xattr mappings
|
||||
*/
|
||||
|
||||
#define XATTRMAPPINGFILE ".NTFS-3G/XattrMapping" /* default mapping file */
|
||||
|
||||
|
||||
/*
|
||||
* Permission checking modes for high level and low level
|
||||
*
|
||||
|
@ -5,6 +5,7 @@
|
||||
* Copyright (c) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2005-2006 Yura Pakhuchiy
|
||||
* Copyright (c) 2005-2009 Szabolcs Szakacsits
|
||||
* Copyright (c) 2010 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
|
||||
@ -251,7 +252,9 @@ 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 */
|
||||
|
||||
#ifdef XATTR_MAPPINGS
|
||||
struct XATTRMAPPING *xattr_mapping;
|
||||
#endif /* XATTR_MAPPINGS */
|
||||
#if CACHE_INODE_SIZE
|
||||
struct CACHE_HEADER *xinode_cache;
|
||||
#endif
|
||||
|
@ -43,10 +43,25 @@ enum SYSTEMXATTRS {
|
||||
XATTR_POSIX_DEF
|
||||
} ;
|
||||
|
||||
enum SYSTEMXATTRS ntfs_xattr_system_type(const char *name);
|
||||
struct XATTRMAPPING {
|
||||
struct XATTRMAPPING *next;
|
||||
enum SYSTEMXATTRS xattr;
|
||||
char name[1]; /* variable length */
|
||||
} ;
|
||||
|
||||
#ifdef XATTR_MAPPINGS
|
||||
|
||||
struct XATTRMAPPING *ntfs_xattr_build_mapping(ntfs_volume *vol,
|
||||
const char *path);
|
||||
void ntfs_xattr_free_mapping(struct XATTRMAPPING*);
|
||||
|
||||
#endif /* XATTR_MAPPINGS */
|
||||
|
||||
enum SYSTEMXATTRS ntfs_xattr_system_type(const char *name,
|
||||
ntfs_volume *vol);
|
||||
int ntfs_xattr_listxattr(ntfs_inode *ni, ntfs_attr_search_ctx *actx,
|
||||
char *list, size_t size, BOOL prefixing);
|
||||
char *list, size_t size, BOOL prefixing);
|
||||
|
||||
int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx,
|
||||
enum SYSTEMXATTRS attr,
|
||||
ntfs_inode *ni, ntfs_inode *dir_ni,
|
||||
|
@ -4,7 +4,7 @@
|
||||
* This module is part of ntfs-3g library, but may also be
|
||||
* integrated in tools running over Linux or Windows
|
||||
*
|
||||
* Copyright (c) 2007-2009 Jean-Pierre Andre
|
||||
* Copyright (c) 2007-2010 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
|
||||
@ -3956,33 +3956,6 @@ static SID *encodesid(const char *sidstr)
|
||||
return (sid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Early logging before the logs are redirected
|
||||
*
|
||||
* (not quite satisfactory : this appears before the ntfs-g banner,
|
||||
* and with a different pid)
|
||||
*/
|
||||
|
||||
static void log_early_error(const char *format, ...)
|
||||
__attribute__((format(printf, 1, 2)));
|
||||
|
||||
static void log_early_error(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
openlog("ntfs-3g", LOG_PID, LOG_USER);
|
||||
ntfs_log_handler_syslog(NULL, NULL, 0,
|
||||
NTFS_LOG_LEVEL_ERROR, NULL,
|
||||
format, args);
|
||||
#else
|
||||
vfprintf(stderr,format,args);
|
||||
#endif
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get a single mapping item from buffer
|
||||
*
|
||||
@ -4045,7 +4018,7 @@ static struct MAPLIST *getmappingitem(FILEREADER reader, void *fileid,
|
||||
if (pu && pg)
|
||||
*pu = *pg = '\0';
|
||||
else {
|
||||
log_early_error("Bad mapping item \"%s\"\n",
|
||||
ntfs_log_early_error("Bad mapping item \"%s\"\n",
|
||||
item->maptext);
|
||||
free(item);
|
||||
item = (struct MAPLIST*)NULL;
|
||||
@ -4174,7 +4147,7 @@ struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem)
|
||||
if (pwd)
|
||||
uid = pwd->pw_uid;
|
||||
else
|
||||
log_early_error("Invalid user \"%s\"\n",
|
||||
ntfs_log_early_error("Invalid user \"%s\"\n",
|
||||
item->uidstr);
|
||||
}
|
||||
}
|
||||
@ -4254,7 +4227,7 @@ struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem)
|
||||
if (grp)
|
||||
gid = grp->gr_gid;
|
||||
else
|
||||
log_early_error("Invalid group \"%s\"\n",
|
||||
ntfs_log_early_error("Invalid group \"%s\"\n",
|
||||
item->gidstr);
|
||||
}
|
||||
}
|
||||
|
@ -6542,6 +6542,89 @@ err_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read some data from a data attribute
|
||||
*
|
||||
* Returns the amount of data read, negative if there was an error
|
||||
*/
|
||||
|
||||
int ntfs_attr_data_read(ntfs_inode *ni,
|
||||
ntfschar *stream_name, int stream_name_len,
|
||||
char *buf, size_t size, off_t offset)
|
||||
{
|
||||
ntfs_attr *na = NULL;
|
||||
int res, total = 0;
|
||||
|
||||
na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
|
||||
if (!na) {
|
||||
res = -errno;
|
||||
goto exit;
|
||||
}
|
||||
if ((size_t)offset < (size_t)na->data_size) {
|
||||
if (offset + size > (size_t)na->data_size)
|
||||
size = na->data_size - offset;
|
||||
while (size) {
|
||||
res = ntfs_attr_pread(na, offset, size, buf + total);
|
||||
if ((off_t)res < (off_t)size)
|
||||
ntfs_log_perror("ntfs_attr_pread partial read "
|
||||
"(%lld : %lld <> %d)",
|
||||
(long long)offset,
|
||||
(long long)size, res);
|
||||
if (res <= 0) {
|
||||
res = -errno;
|
||||
goto exit;
|
||||
}
|
||||
size -= res;
|
||||
offset += res;
|
||||
total += res;
|
||||
}
|
||||
}
|
||||
res = total;
|
||||
exit:
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write some data into a data attribute
|
||||
*
|
||||
* Returns the amount of data written, negative if there was an error
|
||||
*/
|
||||
|
||||
int ntfs_attr_data_write(ntfs_inode *ni,
|
||||
ntfschar *stream_name, int stream_name_len,
|
||||
char *buf, size_t size, off_t offset)
|
||||
{
|
||||
ntfs_attr *na = NULL;
|
||||
int res, total = 0;
|
||||
|
||||
na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
|
||||
if (!na) {
|
||||
res = -errno;
|
||||
goto exit;
|
||||
}
|
||||
while (size) {
|
||||
res = ntfs_attr_pwrite(na, offset, size, buf + total);
|
||||
if (res < (s64)size)
|
||||
ntfs_log_perror("ntfs_attr_pwrite partial write (%lld: "
|
||||
"%lld <> %d)", (long long)offset,
|
||||
(long long)size, res);
|
||||
if (res <= 0) {
|
||||
res = -errno;
|
||||
goto exit;
|
||||
}
|
||||
size -= res;
|
||||
offset += res;
|
||||
total += res;
|
||||
}
|
||||
res = total;
|
||||
exit:
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name,
|
||||
|
@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2005 Richard Russon
|
||||
* Copyright (c) 2005-2008 Szabolcs Szakacsits
|
||||
* Copyright (c) 2010 Jean-Pierre Andre
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
@ -388,6 +389,29 @@ out:
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Early logging before the logs are redirected
|
||||
*
|
||||
* (not quite satisfactory : this appears before the ntfs-g banner,
|
||||
* and with a different pid)
|
||||
*/
|
||||
|
||||
void ntfs_log_early_error(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
openlog("ntfs-3g", LOG_PID, LOG_USER);
|
||||
ntfs_log_handler_syslog(NULL, NULL, 0,
|
||||
NTFS_LOG_LEVEL_ERROR, NULL,
|
||||
format, args);
|
||||
#else
|
||||
vfprintf(stderr,format,args);
|
||||
#endif
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_fprintf - Basic logging handler
|
||||
* @function: Function in which the log line occurred
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2004 Anton Altaparmakov
|
||||
* Copyright (c) 2005-2006 Szabolcs Szakacsits
|
||||
* Copyright (c) 2006 Yura Pakhuchiy
|
||||
* Copyright (c) 2007-2009 Jean-Pierre Andre
|
||||
* Copyright (c) 2007-2010 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
|
||||
@ -404,91 +404,6 @@ le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len)
|
||||
return cpu_to_le32(hash);
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal read
|
||||
* copied and pasted from ntfs_fuse_read() and made independent
|
||||
* of fuse context
|
||||
*/
|
||||
|
||||
static int ntfs_local_read(ntfs_inode *ni,
|
||||
ntfschar *stream_name, int stream_name_len,
|
||||
char *buf, size_t size, off_t offset)
|
||||
{
|
||||
ntfs_attr *na = NULL;
|
||||
int res, total = 0;
|
||||
|
||||
na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
|
||||
if (!na) {
|
||||
res = -errno;
|
||||
goto exit;
|
||||
}
|
||||
if ((size_t)offset < (size_t)na->data_size) {
|
||||
if (offset + size > (size_t)na->data_size)
|
||||
size = na->data_size - offset;
|
||||
while (size) {
|
||||
res = ntfs_attr_pread(na, offset, size, buf);
|
||||
if ((off_t)res < (off_t)size)
|
||||
ntfs_log_perror("ntfs_attr_pread partial read "
|
||||
"(%lld : %lld <> %d)",
|
||||
(long long)offset,
|
||||
(long long)size, res);
|
||||
if (res <= 0) {
|
||||
res = -errno;
|
||||
goto exit;
|
||||
}
|
||||
size -= res;
|
||||
offset += res;
|
||||
total += res;
|
||||
}
|
||||
}
|
||||
res = total;
|
||||
exit:
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Internal write
|
||||
* copied and pasted from ntfs_fuse_write() and made independent
|
||||
* of fuse context
|
||||
*/
|
||||
|
||||
static int ntfs_local_write(ntfs_inode *ni,
|
||||
ntfschar *stream_name, int stream_name_len,
|
||||
char *buf, size_t size, off_t offset)
|
||||
{
|
||||
ntfs_attr *na = NULL;
|
||||
int res, total = 0;
|
||||
|
||||
na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
|
||||
if (!na) {
|
||||
res = -errno;
|
||||
goto exit;
|
||||
}
|
||||
while (size) {
|
||||
res = ntfs_attr_pwrite(na, offset, size, buf);
|
||||
if (res < (s64)size)
|
||||
ntfs_log_perror("ntfs_attr_pwrite partial write (%lld: "
|
||||
"%lld <> %d)", (long long)offset,
|
||||
(long long)size, res);
|
||||
if (res <= 0) {
|
||||
res = -errno;
|
||||
goto exit;
|
||||
}
|
||||
size -= res;
|
||||
offset += res;
|
||||
total += res;
|
||||
}
|
||||
res = total;
|
||||
exit:
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get the first entry of current index block
|
||||
* cut and pasted form ntfs_ie_get_first() in index.c
|
||||
@ -531,7 +446,7 @@ static int entersecurity_stuff(ntfs_volume *vol, off_t offs)
|
||||
if (stuff) {
|
||||
memset(stuff, 0, STUFFSZ);
|
||||
do {
|
||||
written = ntfs_local_write(vol->secure_ni,
|
||||
written = ntfs_attr_data_write(vol->secure_ni,
|
||||
STREAM_SDS, 4, stuff, STUFFSZ, offs);
|
||||
if (written == STUFFSZ) {
|
||||
total += STUFFSZ;
|
||||
@ -589,10 +504,10 @@ static int entersecurity_data(ntfs_volume *vol,
|
||||
phsds->security_id = keyid;
|
||||
phsds->offset = cpu_to_le64(offs);
|
||||
phsds->length = cpu_to_le32(fullsz - gap);
|
||||
written1 = ntfs_local_write(vol->secure_ni,
|
||||
written1 = ntfs_attr_data_write(vol->secure_ni,
|
||||
STREAM_SDS, 4, fullattr, fullsz,
|
||||
offs - gap);
|
||||
written2 = ntfs_local_write(vol->secure_ni,
|
||||
written2 = ntfs_attr_data_write(vol->secure_ni,
|
||||
STREAM_SDS, 4, fullattr, fullsz,
|
||||
offs - gap + ALIGN_SDS_BLOCK);
|
||||
if ((written1 == fullsz)
|
||||
@ -950,7 +865,7 @@ static le32 setsecurityattr(ntfs_volume *vol,
|
||||
+ sizeof(SECURITY_DESCRIPTOR_HEADER);
|
||||
oldattr = (char*)ntfs_malloc(size);
|
||||
if (oldattr) {
|
||||
rdsize = ntfs_local_read(
|
||||
rdsize = ntfs_attr_data_read(
|
||||
vol->secure_ni,
|
||||
STREAM_SDS, 4,
|
||||
oldattr, size, offs);
|
||||
@ -1772,7 +1687,7 @@ static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id)
|
||||
|
||||
securattr = (char*)ntfs_malloc(size);
|
||||
if (securattr) {
|
||||
rdsize = ntfs_local_read(
|
||||
rdsize = ntfs_attr_data_read(
|
||||
ni, STREAM_SDS, 4,
|
||||
securattr, size, offs);
|
||||
if ((rdsize != size)
|
||||
@ -4097,7 +4012,7 @@ static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribut
|
||||
|
||||
static int localread(void *fileid, char *buf, size_t size, off_t offs)
|
||||
{
|
||||
return (ntfs_local_read((ntfs_inode*)fileid,
|
||||
return (ntfs_attr_data_read((ntfs_inode*)fileid,
|
||||
AT_UNNAMED, 0, buf, size, offs));
|
||||
}
|
||||
|
||||
@ -4919,7 +4834,7 @@ int ntfs_read_sds(struct SECURITY_API *scapi,
|
||||
got = -1; /* default return */
|
||||
if (scapi && (scapi->magic == MAGIC_API)) {
|
||||
if (scapi->security.vol->secure_ni)
|
||||
got = ntfs_local_read(scapi->security.vol->secure_ni,
|
||||
got = ntfs_attr_data_read(scapi->security.vol->secure_ni,
|
||||
STREAM_SDS, 4, buf, size, offset);
|
||||
else
|
||||
errno = EOPNOTSUPP;
|
||||
|
@ -89,7 +89,7 @@ static const int nf_ns_user_prefix_len = sizeof(nf_ns_user_prefix) - 1;
|
||||
static const char nf_ns_xattr_ntfs_acl[] = "system.ntfs_acl";
|
||||
static const char nf_ns_xattr_attrib[] = "system.ntfs_attrib";
|
||||
static const char nf_ns_xattr_attrib_be[] = "system.ntfs_attrib_be";
|
||||
static const char nf_ns_xattr_efsinfo[] = "user.ntfs.efsinfo";
|
||||
static const char nf_ns_xattr_efsinfo[] = "system.ntfs_efsinfo";
|
||||
static const char nf_ns_xattr_reparse[] = "system.ntfs_reparse_data";
|
||||
static const char nf_ns_xattr_object_id[] = "system.ntfs_object_id";
|
||||
static const char nf_ns_xattr_dos_name[] = "system.ntfs_dos_name";
|
||||
@ -202,21 +202,286 @@ int cpu_to_le_acl(const struct POSIX_ACL *acl, size_t size,
|
||||
* internal data (original name in system namespace, or renamed)
|
||||
*/
|
||||
|
||||
enum SYSTEMXATTRS ntfs_xattr_system_type(const char *name)
|
||||
enum SYSTEMXATTRS ntfs_xattr_system_type(const char *name,
|
||||
ntfs_volume *vol)
|
||||
{
|
||||
struct XATTRNAME *p;
|
||||
enum SYSTEMXATTRS ret;
|
||||
#ifdef XATTR_MAPPINGS
|
||||
const struct XATTRMAPPING *q;
|
||||
#endif /* XATTR_MAPPINGS */
|
||||
|
||||
p = nf_ns_xattr_names;
|
||||
while (p->name && strcmp(p->name,name))
|
||||
p++;
|
||||
return (p->xattr);
|
||||
ret = p->xattr;
|
||||
#ifdef XATTR_MAPPINGS
|
||||
if (!p->name && vol && vol->xattr_mapping) {
|
||||
q = vol->xattr_mapping;
|
||||
while (q && strcmp(q->name,name))
|
||||
q = q->next;
|
||||
if (q)
|
||||
ret = q->xattr;
|
||||
}
|
||||
#else /* XATTR_MAPPINGS */
|
||||
if (!p->name
|
||||
&& vol
|
||||
&& vol->efs_raw
|
||||
&& !strcmp(nf_ns_alt_xattr_efsinfo,name))
|
||||
ret = XATTR_NTFS_EFSINFO;
|
||||
#endif /* XATTR_MAPPINGS */
|
||||
return (ret);
|
||||
}
|
||||
|
||||
#ifdef XATTR_MAPPINGS
|
||||
|
||||
/*
|
||||
* Basic read from a user mapping file on another volume
|
||||
*/
|
||||
|
||||
static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused)))
|
||||
{
|
||||
return (read(*(int*)fileid, buf, size));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read from a user mapping file on current NTFS partition
|
||||
*/
|
||||
|
||||
static int localread(void *fileid, char *buf, size_t size, off_t offs)
|
||||
{
|
||||
return (ntfs_attr_data_read((ntfs_inode*)fileid,
|
||||
AT_UNNAMED, 0, buf, size, offs));
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a single mapping item from buffer
|
||||
*
|
||||
* Always reads a full line, truncating long lines
|
||||
* Refills buffer when exhausted
|
||||
* Returns pointer to item, or NULL when there is no more
|
||||
* Note : errors are logged, but not returned
|
||||
// TODO partially share with acls.c
|
||||
*/
|
||||
|
||||
static struct XATTRMAPPING *getmappingitem(FILEREADER reader, void *fileid,
|
||||
off_t *poffs, char *buf, int *psrc, s64 *psize)
|
||||
{
|
||||
int src;
|
||||
int dst;
|
||||
char *pe;
|
||||
char *ps;
|
||||
char *pu;
|
||||
enum SYSTEMXATTRS xattr;
|
||||
int gotend;
|
||||
char maptext[LINESZ];
|
||||
struct XATTRMAPPING *item;
|
||||
|
||||
src = *psrc;
|
||||
dst = 0;
|
||||
do {
|
||||
gotend = 0;
|
||||
while ((src < *psize)
|
||||
&& (buf[src] != '\n')) {
|
||||
/* ignore spaces */
|
||||
if ((dst < LINESZ)
|
||||
&& (buf[src] != '\r')
|
||||
&& (buf[src] != '\t')
|
||||
&& (buf[src] != ' '))
|
||||
maptext[dst++] = buf[src];
|
||||
src++;
|
||||
}
|
||||
if (src >= *psize) {
|
||||
*poffs += *psize;
|
||||
*psize = reader(fileid, buf, (size_t)BUFSZ, *poffs);
|
||||
src = 0;
|
||||
} else {
|
||||
gotend = 1;
|
||||
src++;
|
||||
maptext[dst] = '\0';
|
||||
dst = 0;
|
||||
}
|
||||
} while (*psize && ((maptext[0] == '#') || !gotend));
|
||||
item = (struct XATTRMAPPING*)NULL;
|
||||
if (gotend) {
|
||||
/* decompose into system name and user name */
|
||||
ps = maptext;
|
||||
pu = strchr(maptext,':');
|
||||
if (pu) {
|
||||
*pu++ = 0;
|
||||
pe = strchr(pu,':');
|
||||
if (pe)
|
||||
*pe = 0;
|
||||
/* check name validity */
|
||||
if ((strlen(pu) < 6) || strncmp(pu,"user.",5))
|
||||
pu = (char*)NULL;
|
||||
xattr = ntfs_xattr_system_type(ps,
|
||||
(ntfs_volume*)NULL);
|
||||
if (xattr == XATTR_UNMAPPED)
|
||||
pu = (char*)NULL;
|
||||
}
|
||||
if (pu) {
|
||||
item = (struct XATTRMAPPING*)ntfs_malloc(
|
||||
sizeof(struct XATTRMAPPING)
|
||||
+ strlen(pu));
|
||||
if (item) {
|
||||
item->xattr = xattr;
|
||||
strcpy(item->name,pu);
|
||||
item->next = (struct XATTRMAPPING*)NULL;
|
||||
}
|
||||
} else {
|
||||
ntfs_log_early_error("Bad xattr mapping item, aborting\n");
|
||||
}
|
||||
}
|
||||
*psrc = src;
|
||||
return (item);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read xattr mapping file and split into their attribute.
|
||||
* Parameters are kept in a chained list.
|
||||
* Returns the head of list, if any
|
||||
* Errors are logged, but not returned
|
||||
*
|
||||
* If an absolute path is provided, the mapping file is assumed
|
||||
* to be located in another mounted file system, and plain read()
|
||||
* are used to get its contents.
|
||||
* If a relative path is provided, the mapping file is assumed
|
||||
* to be located on the current file system, and internal IO
|
||||
* have to be used since we are still mounting and we have not
|
||||
* entered the fuse loop yet.
|
||||
*/
|
||||
|
||||
static struct XATTRMAPPING *ntfs_read_xattr_mapping(FILEREADER reader,
|
||||
void *fileid)
|
||||
{
|
||||
char buf[BUFSZ];
|
||||
struct XATTRMAPPING *item;
|
||||
struct XATTRMAPPING *current;
|
||||
struct XATTRMAPPING *firstitem;
|
||||
struct XATTRMAPPING *lastitem;
|
||||
BOOL duplicated;
|
||||
int src;
|
||||
off_t offs;
|
||||
s64 size;
|
||||
|
||||
firstitem = (struct XATTRMAPPING*)NULL;
|
||||
lastitem = (struct XATTRMAPPING*)NULL;
|
||||
offs = 0;
|
||||
size = reader(fileid, buf, (size_t)BUFSZ, (off_t)0);
|
||||
if (size > 0) {
|
||||
src = 0;
|
||||
do {
|
||||
item = getmappingitem(reader, fileid, &offs,
|
||||
buf, &src, &size);
|
||||
if (item) {
|
||||
/* check no double mapping */
|
||||
duplicated = FALSE;
|
||||
for (current=firstitem; current; current=current->next)
|
||||
if ((current->xattr == item->xattr)
|
||||
|| !strcmp(current->name,item->name))
|
||||
duplicated = TRUE;
|
||||
if (duplicated) {
|
||||
free(item);
|
||||
ntfs_log_early_error("Conflicting xattr mapping ignored\n");
|
||||
} else {
|
||||
item->next = (struct XATTRMAPPING*)NULL;
|
||||
if (lastitem)
|
||||
lastitem->next = item;
|
||||
else
|
||||
firstitem = item;
|
||||
lastitem = item;
|
||||
}
|
||||
}
|
||||
} while (item);
|
||||
}
|
||||
return (firstitem);
|
||||
}
|
||||
|
||||
/*
|
||||
* Build the extended attribute mappings to user namespace
|
||||
*
|
||||
* Note : no error is returned. If we refused mounting when there
|
||||
* is an error it would be too difficult to fix the offending file
|
||||
*/
|
||||
|
||||
struct XATTRMAPPING *ntfs_xattr_build_mapping(ntfs_volume *vol,
|
||||
const char *xattrmap_path)
|
||||
{
|
||||
struct XATTRMAPPING *firstmapping;
|
||||
struct XATTRMAPPING *mapping;
|
||||
BOOL user_efs;
|
||||
BOOL notfound;
|
||||
ntfs_inode *ni;
|
||||
int fd;
|
||||
|
||||
firstmapping = (struct XATTRMAPPING*)NULL;
|
||||
notfound = FALSE;
|
||||
if (!xattrmap_path)
|
||||
xattrmap_path = XATTRMAPPINGFILE;
|
||||
if (xattrmap_path[0] == '/') {
|
||||
fd = open(xattrmap_path,O_RDONLY);
|
||||
if (fd > 0) {
|
||||
firstmapping = ntfs_read_xattr_mapping(basicread, (void*)&fd);
|
||||
close(fd);
|
||||
} else
|
||||
notfound = TRUE;
|
||||
} else {
|
||||
ni = ntfs_pathname_to_inode(vol, NULL, xattrmap_path);
|
||||
if (ni) {
|
||||
firstmapping = ntfs_read_xattr_mapping(localread, ni);
|
||||
ntfs_inode_close(ni);
|
||||
} else
|
||||
notfound = TRUE;
|
||||
}
|
||||
if (notfound && strcmp(xattrmap_path, XATTRMAPPINGFILE)) {
|
||||
ntfs_log_early_error("Could not open \"%s\"\n",xattrmap_path);
|
||||
}
|
||||
if (vol->efs_raw) {
|
||||
user_efs = TRUE;
|
||||
for (mapping=firstmapping; mapping; mapping=mapping->next)
|
||||
if (mapping->xattr == XATTR_NTFS_EFSINFO)
|
||||
user_efs = FALSE;
|
||||
} else
|
||||
user_efs = FALSE;
|
||||
if (user_efs) {
|
||||
mapping = (struct XATTRMAPPING*)ntfs_malloc(
|
||||
sizeof(struct XATTRMAPPING)
|
||||
+ strlen(nf_ns_alt_xattr_efsinfo));
|
||||
if (mapping) {
|
||||
mapping->next = firstmapping;
|
||||
mapping->xattr = XATTR_NTFS_EFSINFO;
|
||||
strcpy(mapping->name,nf_ns_alt_xattr_efsinfo);
|
||||
firstmapping = mapping;
|
||||
}
|
||||
}
|
||||
return (firstmapping);
|
||||
}
|
||||
|
||||
void ntfs_xattr_free_mapping(struct XATTRMAPPING *mapping)
|
||||
{
|
||||
struct XATTRMAPPING *p, *q;
|
||||
|
||||
p = mapping;
|
||||
while (p) {
|
||||
q = p->next;
|
||||
free(p);
|
||||
p = q;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* XATTR_MAPPINGS */
|
||||
|
||||
int ntfs_xattr_listxattr(ntfs_inode *ni, ntfs_attr_search_ctx *actx,
|
||||
char *list, size_t size, BOOL prefixing)
|
||||
{
|
||||
int ret = 0;
|
||||
char *to = list;
|
||||
#ifdef XATTR_MAPPINGS
|
||||
BOOL accepted;
|
||||
const struct XATTRMAPPING *item;
|
||||
#endif /* XATTR_MAPPINGS */
|
||||
|
||||
/* first list the regular user attributes (ADS) */
|
||||
while (!ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE,
|
||||
@ -268,13 +533,44 @@ int ntfs_xattr_listxattr(ntfs_inode *ni, ntfs_attr_search_ctx *actx,
|
||||
}
|
||||
free(tmp_name);
|
||||
}
|
||||
#ifdef XATTR_MAPPINGS
|
||||
/* now append the system attributes mapped to user space */
|
||||
for (item=ni->vol->xattr_mapping; item; item=item->next) {
|
||||
switch (item->xattr) {
|
||||
case XATTR_NTFS_EFSINFO :
|
||||
accepted = ni->vol->efs_raw
|
||||
&& (ni->flags & FILE_ATTR_ENCRYPTED);
|
||||
break;
|
||||
case XATTR_NTFS_REPARSE_DATA :
|
||||
accepted = (ni->flags & FILE_ATTR_REPARSE_POINT)
|
||||
!= const_cpu_to_le32(0);
|
||||
break;
|
||||
// TODO : we are supposed to only return xattrs which are set
|
||||
// this is more complex for OBJECT_ID and DOS_NAME
|
||||
default : accepted = TRUE;
|
||||
break;
|
||||
}
|
||||
if (accepted) {
|
||||
ret += strlen(item->name) + 1;
|
||||
if (size) {
|
||||
if ((size_t)ret <= size) {
|
||||
strcpy(to, item->name);
|
||||
to += strlen(item->name);
|
||||
*to++ = 0;
|
||||
} else {
|
||||
ret = -ERANGE;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
#else /* XATTR_MAPPINGS */
|
||||
/* List efs info xattr for encrypted files */
|
||||
if (ni->vol->efs_raw && (ni->flags & FILE_ATTR_ENCRYPTED)) {
|
||||
ret += sizeof(nf_ns_xattr_efsinfo);
|
||||
ret += sizeof(nf_ns_alt_xattr_efsinfo);
|
||||
if ((size_t)ret <= size) {
|
||||
memcpy(to, nf_ns_xattr_efsinfo,
|
||||
sizeof(nf_ns_xattr_efsinfo));
|
||||
to += sizeof(nf_ns_xattr_efsinfo);
|
||||
memcpy(to, nf_ns_alt_xattr_efsinfo,
|
||||
sizeof(nf_ns_alt_xattr_efsinfo));
|
||||
to += sizeof(nf_ns_alt_xattr_efsinfo);
|
||||
#endif /* XATTR_MAPPINGS */
|
||||
}
|
||||
}
|
||||
exit :
|
||||
|
@ -224,6 +224,9 @@ typedef struct {
|
||||
BOOL mounted;
|
||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
||||
BOOL efs_raw;
|
||||
#ifdef XATTR_MAPPINGS
|
||||
char *xattrmap_path;
|
||||
#endif /* XATTR_MAPPINGS */
|
||||
#endif /* HAVE_SETXATTR */
|
||||
struct fuse_chan *fc;
|
||||
BOOL inherit;
|
||||
@ -2806,7 +2809,7 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
|
||||
int namespace;
|
||||
struct SECURITY_CONTEXT security;
|
||||
|
||||
attr = ntfs_xattr_system_type(name);
|
||||
attr = ntfs_xattr_system_type(name,ctx->vol);
|
||||
if (attr != XATTR_UNMAPPED) {
|
||||
/*
|
||||
* hijack internal data and ACL retrieval, whatever
|
||||
@ -2959,7 +2962,7 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
|
||||
int namespace;
|
||||
struct SECURITY_CONTEXT security;
|
||||
|
||||
attr = ntfs_xattr_system_type(name);
|
||||
attr = ntfs_xattr_system_type(name,ctx->vol);
|
||||
if (attr != XATTR_UNMAPPED) {
|
||||
/*
|
||||
* hijack internal data and ACL setting, whatever
|
||||
@ -3167,7 +3170,7 @@ static void ntfs_fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *na
|
||||
int namespace;
|
||||
struct SECURITY_CONTEXT security;
|
||||
|
||||
attr = ntfs_xattr_system_type(name);
|
||||
attr = ntfs_xattr_system_type(name,ctx->vol);
|
||||
if (attr != XATTR_UNMAPPED) {
|
||||
switch (attr) {
|
||||
/*
|
||||
@ -3783,6 +3786,20 @@ static char *parse_mount_options(const char *orig_opts)
|
||||
goto err_exit;
|
||||
}
|
||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
||||
#ifdef XATTR_MAPPINGS
|
||||
} else if (!strcmp(opt, "xattrmapping")) {
|
||||
if (!val) {
|
||||
ntfs_log_error("'xattrmapping' option should have "
|
||||
"a value.\n");
|
||||
goto err_exit;
|
||||
}
|
||||
ctx->xattrmap_path = strdup(val);
|
||||
if (!ctx->xattrmap_path) {
|
||||
ntfs_log_error("no more memory to store "
|
||||
"'xattrmapping' option.\n");
|
||||
goto err_exit;
|
||||
}
|
||||
#endif /* XATTR_MAPPINGS */
|
||||
} else if (!strcmp(opt, "efs_raw")) {
|
||||
if (bogus_option_value(val, "efs_raw"))
|
||||
goto err_exit;
|
||||
@ -4161,6 +4178,9 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
const char *permissions_mode = (const char*)NULL;
|
||||
const char *failed_secure = (const char*)NULL;
|
||||
#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS)
|
||||
struct XATTRMAPPING *xattr_mapping = (struct XATTRMAPPING*)NULL;
|
||||
#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
|
||||
struct stat sbuf;
|
||||
unsigned long existing_mount;
|
||||
int err, fd;
|
||||
@ -4342,6 +4362,18 @@ int main(int argc, char *argv[])
|
||||
if (ctx->usermap_path)
|
||||
free (ctx->usermap_path);
|
||||
|
||||
#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS)
|
||||
xattr_mapping = ntfs_xattr_build_mapping(ctx->vol,
|
||||
ctx->xattrmap_path);
|
||||
ctx->vol->xattr_mapping = xattr_mapping;
|
||||
/*
|
||||
* Errors are logged, do not refuse mounting, it would be
|
||||
* too difficult to fix the unmountable mapping file.
|
||||
*/
|
||||
if (ctx->xattrmap_path)
|
||||
free(ctx->xattrmap_path);
|
||||
#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
|
||||
|
||||
se = mount_fuse(parsed_options);
|
||||
if (!se) {
|
||||
err = NTFS_VOLUME_FUSE_ERROR;
|
||||
@ -4372,6 +4404,9 @@ err_out:
|
||||
ntfs_mount_error(opts.device, opts.mnt_point, err);
|
||||
if (ctx->abs_mnt_point)
|
||||
free(ctx->abs_mnt_point);
|
||||
#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS)
|
||||
ntfs_xattr_free_mapping(xattr_mapping);
|
||||
#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
|
||||
err2:
|
||||
ntfs_close();
|
||||
free(ctx);
|
||||
|
@ -185,6 +185,9 @@ typedef struct {
|
||||
BOOL mounted;
|
||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
||||
BOOL efs_raw;
|
||||
#ifdef XATTR_MAPPINGS
|
||||
char *xattrmap_path;
|
||||
#endif /* XATTR_MAPPINGS */
|
||||
#endif /* HAVE_SETXATTR */
|
||||
struct fuse_chan *fc;
|
||||
BOOL inherit;
|
||||
@ -2752,7 +2755,7 @@ static int ntfs_fuse_getxattr(const char *path, const char *name,
|
||||
int namespace;
|
||||
struct SECURITY_CONTEXT security;
|
||||
|
||||
attr = ntfs_xattr_system_type(name);
|
||||
attr = ntfs_xattr_system_type(name,ctx->vol);
|
||||
if (attr != XATTR_UNMAPPED) {
|
||||
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
|
||||
/*
|
||||
@ -2884,7 +2887,7 @@ static int ntfs_fuse_setxattr(const char *path, const char *name,
|
||||
int namespace;
|
||||
struct SECURITY_CONTEXT security;
|
||||
|
||||
attr = ntfs_xattr_system_type(name);
|
||||
attr = ntfs_xattr_system_type(name,ctx->vol);
|
||||
if (attr != XATTR_UNMAPPED) {
|
||||
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
|
||||
/*
|
||||
@ -3078,7 +3081,7 @@ static int ntfs_fuse_removexattr(const char *path, const char *name)
|
||||
int namespace;
|
||||
struct SECURITY_CONTEXT security;
|
||||
|
||||
attr = ntfs_xattr_system_type(name);
|
||||
attr = ntfs_xattr_system_type(name,ctx->vol);
|
||||
if (attr != XATTR_UNMAPPED) {
|
||||
switch (attr) {
|
||||
/*
|
||||
@ -3692,6 +3695,20 @@ static char *parse_mount_options(const char *orig_opts)
|
||||
goto err_exit;
|
||||
}
|
||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
||||
#ifdef XATTR_MAPPINGS
|
||||
} else if (!strcmp(opt, "xattrmapping")) {
|
||||
if (!val) {
|
||||
ntfs_log_error("'xattrmapping' option should have "
|
||||
"a value.\n");
|
||||
goto err_exit;
|
||||
}
|
||||
ctx->xattrmap_path = strdup(val);
|
||||
if (!ctx->xattrmap_path) {
|
||||
ntfs_log_error("no more memory to store "
|
||||
"'xattrmapping' option.\n");
|
||||
goto err_exit;
|
||||
}
|
||||
#endif /* XATTR_MAPPINGS */
|
||||
} else if (!strcmp(opt, "efs_raw")) {
|
||||
if (bogus_option_value(val, "efs_raw"))
|
||||
goto err_exit;
|
||||
@ -4073,6 +4090,9 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
const char *permissions_mode = (const char*)NULL;
|
||||
const char *failed_secure = (const char*)NULL;
|
||||
#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS)
|
||||
struct XATTRMAPPING *xattr_mapping = (struct XATTRMAPPING*)NULL;
|
||||
#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
|
||||
struct stat sbuf;
|
||||
unsigned long existing_mount;
|
||||
int err, fd;
|
||||
@ -4253,6 +4273,18 @@ int main(int argc, char *argv[])
|
||||
if (ctx->usermap_path)
|
||||
free (ctx->usermap_path);
|
||||
|
||||
#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS)
|
||||
xattr_mapping = ntfs_xattr_build_mapping(ctx->vol,
|
||||
ctx->xattrmap_path);
|
||||
ctx->vol->xattr_mapping = xattr_mapping;
|
||||
/*
|
||||
* Errors are logged, do not refuse mounting, it would be
|
||||
* too difficult to fix the unmountable mapping file.
|
||||
*/
|
||||
if (ctx->xattrmap_path)
|
||||
free(ctx->xattrmap_path);
|
||||
#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
|
||||
|
||||
fh = mount_fuse(parsed_options);
|
||||
if (!fh) {
|
||||
err = NTFS_VOLUME_FUSE_ERROR;
|
||||
@ -4285,6 +4317,9 @@ err_out:
|
||||
ntfs_mount_error(opts.device, opts.mnt_point, err);
|
||||
if (ctx->abs_mnt_point)
|
||||
free(ctx->abs_mnt_point);
|
||||
#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS)
|
||||
ntfs_xattr_free_mapping(xattr_mapping);
|
||||
#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
|
||||
err2:
|
||||
ntfs_close();
|
||||
free(ctx);
|
||||
|
@ -175,6 +175,9 @@
|
||||
* Mar 2010, version 1.3.17
|
||||
* - adapted to new default user mapping
|
||||
* - fixed #ifdef'd code for selftest
|
||||
*
|
||||
* May 2010, version 1.3.18
|
||||
* - redefined early error logging
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -198,7 +201,7 @@
|
||||
* General parameters which may have to be adapted to needs
|
||||
*/
|
||||
|
||||
#define AUDT_VERSION "1.3.17"
|
||||
#define AUDT_VERSION "1.3.18"
|
||||
|
||||
#define GET_FILE_SECURITY "ntfs_get_file_security"
|
||||
#define SET_FILE_SECURITY "ntfs_set_file_security"
|
||||
@ -312,6 +315,11 @@ typedef int (*dircallback)(struct CALLBACK *context, char *ntfsname,
|
||||
int length, int type, long long pos, u64 mft_ref,
|
||||
unsigned int dt_type);
|
||||
|
||||
#ifndef HAVE_SYSLOG_H
|
||||
void ntfs_log_early_error(const char *format, ...)
|
||||
__attribute__((format(printf, 1, 2)));
|
||||
#endif
|
||||
|
||||
#if USESTUBS | defined(STSC)
|
||||
|
||||
int ntfs_get_file_security(void *scapi,
|
||||
@ -1271,6 +1279,23 @@ void printerror(FILE *file)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef HAVE_SYSLOG_H
|
||||
|
||||
/*
|
||||
* Redefine early error messages in stand-alone situations
|
||||
*/
|
||||
|
||||
void ntfs_log_early_error(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
vfprintf(stderr,format,args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Guess whether a security attribute is intended for a directory
|
||||
* based on the presence of inheritable ACE
|
||||
|
Loading…
Reference in New Issue
Block a user