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).
This commit is contained in:
Jean-Pierre André 2021-01-26 10:06:18 +01:00
parent 172da09947
commit 5d46b32b91
11 changed files with 285 additions and 30 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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