Commit 1d07a9df authored by Daniel Pinto's avatar Daniel Pinto Committed by Konstantin Komarov

fs/ntfs3: Add windows_names mount option

When enabled, the windows_names mount option prevents the creation
of files or directories with names not allowed by Windows. Use
the same option name as NTFS-3G for compatibility.
Signed-off-by: default avatarDaniel Pinto <danielpinto52@gmail.com>
Signed-off-by: default avatarKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
parent 75b5e472
...@@ -3011,6 +3011,7 @@ int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni, ...@@ -3011,6 +3011,7 @@ int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
struct NTFS_DE *de) struct NTFS_DE *de)
{ {
int err; int err;
struct ntfs_sb_info *sbi = ni->mi.sbi;
struct ATTRIB *attr; struct ATTRIB *attr;
struct ATTR_LIST_ENTRY *le; struct ATTR_LIST_ENTRY *le;
struct mft_inode *mi; struct mft_inode *mi;
...@@ -3018,6 +3019,10 @@ int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni, ...@@ -3018,6 +3019,10 @@ int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
struct ATTR_FILE_NAME *de_name = (struct ATTR_FILE_NAME *)(de + 1); struct ATTR_FILE_NAME *de_name = (struct ATTR_FILE_NAME *)(de + 1);
u16 de_key_size = le16_to_cpu(de->key_size); u16 de_key_size = le16_to_cpu(de->key_size);
if (sbi->options->windows_names &&
!valid_windows_name(sbi, (struct le_str *)&de_name->name_len))
return -EINVAL;
mi_get_ref(&ni->mi, &de->ref); mi_get_ref(&ni->mi, &de->ref);
mi_get_ref(&dir_ni->mi, &de_name->home); mi_get_ref(&dir_ni->mi, &de_name->home);
...@@ -3036,7 +3041,7 @@ int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni, ...@@ -3036,7 +3041,7 @@ int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), de_name, de_key_size); memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), de_name, de_key_size);
/* Insert new name into directory. */ /* Insert new name into directory. */
err = indx_insert_entry(&dir_ni->dir, dir_ni, de, ni->mi.sbi, NULL, 0); err = indx_insert_entry(&dir_ni->dir, dir_ni, de, sbi, NULL, 0);
if (err) if (err)
ni_remove_attr_le(ni, attr, mi, le); ni_remove_attr_le(ni, attr, mi, le);
......
...@@ -98,6 +98,30 @@ const __le16 WOF_NAME[17] = { ...@@ -98,6 +98,30 @@ const __le16 WOF_NAME[17] = {
}; };
#endif #endif
static const __le16 CON_NAME[3] = {
cpu_to_le16('C'), cpu_to_le16('O'), cpu_to_le16('N'),
};
static const __le16 NUL_NAME[3] = {
cpu_to_le16('N'), cpu_to_le16('U'), cpu_to_le16('L'),
};
static const __le16 AUX_NAME[3] = {
cpu_to_le16('A'), cpu_to_le16('U'), cpu_to_le16('X'),
};
static const __le16 PRN_NAME[3] = {
cpu_to_le16('P'), cpu_to_le16('R'), cpu_to_le16('N'),
};
static const __le16 COM_NAME[3] = {
cpu_to_le16('C'), cpu_to_le16('O'), cpu_to_le16('M'),
};
static const __le16 LPT_NAME[3] = {
cpu_to_le16('L'), cpu_to_le16('P'), cpu_to_le16('T'),
};
// clang-format on // clang-format on
/* /*
...@@ -2504,3 +2528,83 @@ int run_deallocate(struct ntfs_sb_info *sbi, struct runs_tree *run, bool trim) ...@@ -2504,3 +2528,83 @@ int run_deallocate(struct ntfs_sb_info *sbi, struct runs_tree *run, bool trim)
return 0; return 0;
} }
static inline bool name_has_forbidden_chars(const struct le_str *fname)
{
int i, ch;
/* check for forbidden chars */
for (i = 0; i < fname->len; ++i) {
ch = le16_to_cpu(fname->name[i]);
/* control chars */
if (ch < 0x20)
return true;
switch (ch) {
/* disallowed by Windows */
case '\\':
case '/':
case ':':
case '*':
case '?':
case '<':
case '>':
case '|':
case '\"':
return true;
default:
/* allowed char */
break;
}
}
/* file names cannot end with space or . */
if (fname->len > 0) {
ch = le16_to_cpu(fname->name[fname->len - 1]);
if (ch == ' ' || ch == '.')
return true;
}
return false;
}
static inline bool is_reserved_name(struct ntfs_sb_info *sbi,
const struct le_str *fname)
{
int port_digit;
const __le16 *name = fname->name;
int len = fname->len;
u16 *upcase = sbi->upcase;
/* check for 3 chars reserved names (device names) */
/* name by itself or with any extension is forbidden */
if (len == 3 || (len > 3 && le16_to_cpu(name[3]) == '.'))
if (!ntfs_cmp_names(name, 3, CON_NAME, 3, upcase, false) ||
!ntfs_cmp_names(name, 3, NUL_NAME, 3, upcase, false) ||
!ntfs_cmp_names(name, 3, AUX_NAME, 3, upcase, false) ||
!ntfs_cmp_names(name, 3, PRN_NAME, 3, upcase, false))
return true;
/* check for 4 chars reserved names (port name followed by 1..9) */
/* name by itself or with any extension is forbidden */
if (len == 4 || (len > 4 && le16_to_cpu(name[4]) == '.')) {
port_digit = le16_to_cpu(name[3]);
if (port_digit >= '1' && port_digit <= '9')
if (!ntfs_cmp_names(name, 3, COM_NAME, 3, upcase, false) ||
!ntfs_cmp_names(name, 3, LPT_NAME, 3, upcase, false))
return true;
}
return false;
}
/*
* valid_windows_name - Check if a file name is valid in Windows.
*/
bool valid_windows_name(struct ntfs_sb_info *sbi, const struct le_str *fname)
{
return !name_has_forbidden_chars(fname) &&
!is_reserved_name(sbi, fname);
}
...@@ -1368,6 +1368,13 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, ...@@ -1368,6 +1368,13 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
mi_get_ref(&ni->mi, &new_de->ref); mi_get_ref(&ni->mi, &new_de->ref);
fname = (struct ATTR_FILE_NAME *)(new_de + 1); fname = (struct ATTR_FILE_NAME *)(new_de + 1);
if (sbi->options->windows_names &&
!valid_windows_name(sbi, (struct le_str *)&fname->name_len)) {
err = -EINVAL;
goto out4;
}
mi_get_ref(&dir_ni->mi, &fname->home); mi_get_ref(&dir_ni->mi, &fname->home);
fname->dup.cr_time = fname->dup.m_time = fname->dup.c_time = fname->dup.cr_time = fname->dup.m_time = fname->dup.c_time =
fname->dup.a_time = std5->cr_time; fname->dup.a_time = std5->cr_time;
......
...@@ -98,6 +98,7 @@ struct ntfs_mount_options { ...@@ -98,6 +98,7 @@ struct ntfs_mount_options {
unsigned showmeta : 1; /* Show meta files. */ unsigned showmeta : 1; /* Show meta files. */
unsigned nohidden : 1; /* Do not show hidden files. */ unsigned nohidden : 1; /* Do not show hidden files. */
unsigned hide_dot_files : 1; /* Set hidden flag on dot files. */ unsigned hide_dot_files : 1; /* Set hidden flag on dot files. */
unsigned windows_names : 1; /* Disallow names forbidden by Windows. */
unsigned force : 1; /* RW mount dirty volume. */ unsigned force : 1; /* RW mount dirty volume. */
unsigned noacsrules : 1; /* Exclude acs rules. */ unsigned noacsrules : 1; /* Exclude acs rules. */
unsigned prealloc : 1; /* Preallocate space when file is growing. */ unsigned prealloc : 1; /* Preallocate space when file is growing. */
...@@ -645,6 +646,7 @@ int ntfs_remove_reparse(struct ntfs_sb_info *sbi, __le32 rtag, ...@@ -645,6 +646,7 @@ int ntfs_remove_reparse(struct ntfs_sb_info *sbi, __le32 rtag,
const struct MFT_REF *ref); const struct MFT_REF *ref);
void mark_as_free_ex(struct ntfs_sb_info *sbi, CLST lcn, CLST len, bool trim); void mark_as_free_ex(struct ntfs_sb_info *sbi, CLST lcn, CLST len, bool trim);
int run_deallocate(struct ntfs_sb_info *sbi, struct runs_tree *run, bool trim); int run_deallocate(struct ntfs_sb_info *sbi, struct runs_tree *run, bool trim);
bool valid_windows_name(struct ntfs_sb_info *sbi, const struct le_str *name);
/* Globals from index.c */ /* Globals from index.c */
int indx_used_bit(struct ntfs_index *indx, struct ntfs_inode *ni, size_t *bit); int indx_used_bit(struct ntfs_index *indx, struct ntfs_inode *ni, size_t *bit);
......
...@@ -248,6 +248,7 @@ enum Opt { ...@@ -248,6 +248,7 @@ enum Opt {
Opt_sparse, Opt_sparse,
Opt_nohidden, Opt_nohidden,
Opt_hide_dot_files, Opt_hide_dot_files,
Opt_windows_names,
Opt_showmeta, Opt_showmeta,
Opt_acl, Opt_acl,
Opt_iocharset, Opt_iocharset,
...@@ -269,6 +270,7 @@ static const struct fs_parameter_spec ntfs_fs_parameters[] = { ...@@ -269,6 +270,7 @@ static const struct fs_parameter_spec ntfs_fs_parameters[] = {
fsparam_flag_no("sparse", Opt_sparse), fsparam_flag_no("sparse", Opt_sparse),
fsparam_flag_no("hidden", Opt_nohidden), fsparam_flag_no("hidden", Opt_nohidden),
fsparam_flag_no("hidedotfiles", Opt_hide_dot_files), fsparam_flag_no("hidedotfiles", Opt_hide_dot_files),
fsparam_flag_no("windows_names", Opt_windows_names),
fsparam_flag_no("acl", Opt_acl), fsparam_flag_no("acl", Opt_acl),
fsparam_flag_no("showmeta", Opt_showmeta), fsparam_flag_no("showmeta", Opt_showmeta),
fsparam_flag_no("prealloc", Opt_prealloc), fsparam_flag_no("prealloc", Opt_prealloc),
...@@ -361,6 +363,9 @@ static int ntfs_fs_parse_param(struct fs_context *fc, ...@@ -361,6 +363,9 @@ static int ntfs_fs_parse_param(struct fs_context *fc,
case Opt_hide_dot_files: case Opt_hide_dot_files:
opts->hide_dot_files = result.negated ? 1 : 0; opts->hide_dot_files = result.negated ? 1 : 0;
break; break;
case Opt_windows_names:
opts->windows_names = result.negated ? 0 : 1;
break;
case Opt_acl: case Opt_acl:
if (!result.negated) if (!result.negated)
#ifdef CONFIG_NTFS3_FS_POSIX_ACL #ifdef CONFIG_NTFS3_FS_POSIX_ACL
...@@ -561,6 +566,8 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root) ...@@ -561,6 +566,8 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root)
seq_puts(m, ",showmeta"); seq_puts(m, ",showmeta");
if (opts->nohidden) if (opts->nohidden)
seq_puts(m, ",nohidden"); seq_puts(m, ",nohidden");
if (opts->windows_names)
seq_puts(m, ",windows_names");
if (opts->force) if (opts->force)
seq_puts(m, ",force"); seq_puts(m, ",force");
if (opts->noacsrules) if (opts->noacsrules)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment