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