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);
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);
NTFS_VOLUME_INSECURE = 22
} ntfs_volume_status;
+typedef enum {
+ NTFS_FILES_INTERIX,
+ NTFS_FILES_WSL,
+} ntfs_volume_special_files;
+
/**
* enum ntfs_volume_state_bits -
*
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;
* 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
#include "reparse.h"
#include "object_id.h"
#include "xattrs.h"
+#include "ea.h"
/*
* The little endian Unicode strings "$I30", "$SII", "$SDH", "$O"
{
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");
#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,
} 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)
err = errno;
goto err_out;
}
+ rollback_sd = 1;
}
- rollback_sd = 1;
if (S_ISDIR(type)) {
INDEX_ROOT *ir = NULL;
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;
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;
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);
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);
#include "xattrs.h"
static const char lxdev[] = "$LXDEV";
+static const char lxmod[] = "$LXMOD";
/*
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);
+}
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
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).
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 */
.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
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).
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 */
/**
* 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
{ "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 */
} ;
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
OPT_XATTRMAPPING,
OPT_EFS_RAW,
OPT_POSIX_NLINK,
+ OPT_SPECIAL_FILES,
} ;
/* Option flags */
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