Allowed names with trailing dot or space on conditions

Windows places filenames with a trailing dot or space in the Win32
namespace and allows setting DOS names on such files.  This is true even
though on Windows such filenames can only be created and accessed using
WinNT-style paths and will confuse most Windows software.  Regardless,
because libntfs-3g did not allow setting DOS names on such files, in
some cases it was impossible to correctly restore, using libntfs-3g, a
directory structure that was created under Windows.

Update ntfs_set_ntfs_dos_name() to permit operating on a file that has a
long name with a trailing dot or space.  But continue to forbid creating
such names on a filesystem FUSE-mounted with the windows_name option.
Additionally, continue to forbid a trailing a dot or space in DOS names;
this matches the Windows behavior.

(contributed by Eric Biggers)
This commit is contained in:
Jean-Pierre André 2017-02-11 10:54:51 +01:00
parent 1178a7a801
commit 17b56ccfa2
6 changed files with 39 additions and 21 deletions

View File

@ -1068,12 +1068,17 @@ typedef enum {
FILE_NAME_WIN32 = 0x01,
/* The standard WinNT/2k NTFS long filenames. Case insensitive.
All Unicode chars except: '\0', '"', '*', '/', ':', '<',
'>', '?', '\' and '|'. Further, names cannot end with a '.'
or a space. */
'>', '?', '\' and '|'. Trailing dots and spaces are allowed,
even though on Windows a filename with such a suffix can only
be created and accessed using a WinNT-style path, i.e.
\\?\-prefixed. (If a regular path is used, Windows will
strip the trailing dots and spaces, which makes such
filenames incompatible with most Windows software.) */
FILE_NAME_DOS = 0x02,
/* The standard DOS filenames (8.3 format). Uppercase only.
All 8-bit characters greater space, except: '"', '*', '+',
',', '/', ':', ';', '<', '=', '>', '?' and '\'. */
',', '/', ':', ';', '<', '=', '>', '?' and '\'. Trailing
dots and spaces are forbidden. */
FILE_NAME_WIN32_AND_DOS = 0x03,
/* 3 means that both the Win32 and the DOS filenames are
identical and hence have been saved in this single filename

View File

@ -68,9 +68,9 @@ extern ntfschar *ntfs_str2ucs(const char *s, int *len);
extern void ntfs_ucsfree(ntfschar *ucs);
extern BOOL ntfs_forbidden_chars(const ntfschar *name, int len);
extern BOOL ntfs_forbidden_chars(const ntfschar *name, int len, BOOL strict);
extern BOOL ntfs_forbidden_names(ntfs_volume *vol,
const ntfschar *name, int len);
const ntfschar *name, int len, BOOL strict);
extern BOOL ntfs_collapsible_chars(ntfs_volume *vol,
const ntfschar *shortname, int shortlen,
const ntfschar *longname, int longlen);

View File

@ -2654,9 +2654,12 @@ int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
shortlen = ntfs_mbstoucs(newname, &shortname);
if (shortlen > MAX_DOS_NAME_LENGTH)
shortlen = MAX_DOS_NAME_LENGTH;
/* make sure the short name has valid chars */
/* Make sure the short name has valid chars.
* Note: the short name cannot end with dot or space, but the
* corresponding long name can. */
if ((shortlen < 0)
|| ntfs_forbidden_names(ni->vol,shortname,shortlen)) {
|| ntfs_forbidden_names(ni->vol,shortname,shortlen,TRUE)) {
ntfs_inode_close_in_dir(ni,dir_ni);
ntfs_inode_close(dir_ni);
res = -errno;
@ -2667,7 +2670,8 @@ int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
if (longlen > 0) {
oldlen = get_dos_name(ni, dnum, oldname);
if ((oldlen >= 0)
&& !ntfs_forbidden_names(ni->vol, longname, longlen)) {
&& !ntfs_forbidden_names(ni->vol, longname, longlen,
FALSE)) {
if (oldlen > 0) {
if (flags & XATTR_CREATE) {
res = -1;

View File

@ -1477,10 +1477,14 @@ void ntfs_ucsfree(ntfschar *ucs)
* Check whether a name contains no chars forbidden
* for DOS or Win32 use
*
* If @strict is TRUE, then trailing dots and spaces are forbidden.
* These names are technically allowed in the Win32 namespace, but
* they can be problematic. See comment for FILE_NAME_WIN32.
*
* If there is a bad char, errno is set to EINVAL
*/
BOOL ntfs_forbidden_chars(const ntfschar *name, int len)
BOOL ntfs_forbidden_chars(const ntfschar *name, int len, BOOL strict)
{
BOOL forbidden;
int ch;
@ -1493,9 +1497,9 @@ BOOL ntfs_forbidden_chars(const ntfschar *name, int len)
| (1L << ('>' - 0x20))
| (1L << ('?' - 0x20));
forbidden = (len == 0)
|| (name[len-1] == const_cpu_to_le16(' '))
|| (name[len-1] == const_cpu_to_le16('.'));
forbidden = (len == 0) ||
(strict && (name[len-1] == const_cpu_to_le16(' ') ||
name[len-1] == const_cpu_to_le16('.')));
for (i=0; i<len; i++) {
ch = le16_to_cpu(name[i]);
if ((ch < 0x20)
@ -1517,10 +1521,15 @@ BOOL ntfs_forbidden_chars(const ntfschar *name, int len)
* The reserved names are CON, PRN, AUX, NUL, COM1..COM9, LPT1..LPT9
* with no suffix or any suffix.
*
* If @strict is TRUE, then trailing dots and spaces are forbidden.
* These names are technically allowed in the Win32 namespace, but
* they can be problematic. See comment for FILE_NAME_WIN32.
*
* If the name is forbidden, errno is set to EINVAL
*/
BOOL ntfs_forbidden_names(ntfs_volume *vol, const ntfschar *name, int len)
BOOL ntfs_forbidden_names(ntfs_volume *vol, const ntfschar *name, int len,
BOOL strict)
{
BOOL forbidden;
int h;
@ -1538,7 +1547,7 @@ BOOL ntfs_forbidden_names(ntfs_volume *vol, const ntfschar *name, int len)
static const ntfschar lpt[] = { const_cpu_to_le16('l'),
const_cpu_to_le16('p'), const_cpu_to_le16('t') };
forbidden = ntfs_forbidden_chars(name, len);
forbidden = ntfs_forbidden_chars(name, len, strict);
if (!forbidden && (len >= 3)) {
/*
* Rough hash check to tell whether the first couple of chars

View File

@ -2169,7 +2169,7 @@ static int ntfs_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name,
uname_len = ntfs_mbstoucs(name, &uname);
if ((uname_len < 0)
|| (ctx->windows_names
&& ntfs_forbidden_names(ctx->vol,uname,uname_len))) {
&& ntfs_forbidden_names(ctx->vol,uname,uname_len,TRUE))) {
res = -errno;
goto exit;
}
@ -2390,7 +2390,7 @@ static int ntfs_fuse_newlink(fuse_req_t req __attribute__((unused)),
uname_len = ntfs_mbstoucs(newname, &uname);
if ((uname_len < 0)
|| (ctx->windows_names
&& ntfs_forbidden_names(ctx->vol,uname,uname_len))) {
&& ntfs_forbidden_names(ctx->vol,uname,uname_len,TRUE))) {
res = -errno;
goto exit;
}
@ -3571,7 +3571,7 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
lename_len = fix_xattr_prefix(name, namespace, &lename);
if ((lename_len == -1)
|| (ctx->windows_names
&& ntfs_forbidden_chars(lename,lename_len))) {
&& ntfs_forbidden_chars(lename,lename_len,TRUE))) {
res = -errno;
goto exit;
}

View File

@ -1954,7 +1954,7 @@ static int ntfs_fuse_create(const char *org_path, mode_t typemode, dev_t dev,
uname_len = ntfs_mbstoucs(name, &uname);
if ((uname_len < 0)
|| (ctx->windows_names
&& ntfs_forbidden_names(ctx->vol,uname,uname_len))) {
&& ntfs_forbidden_names(ctx->vol,uname,uname_len,TRUE))) {
res = -errno;
goto exit;
}
@ -2162,7 +2162,7 @@ static int ntfs_fuse_mknod_common(const char *org_path, mode_t mode, dev_t dev,
&& (!S_ISREG(mode)
|| (ctx->windows_names
&& ntfs_forbidden_names(ctx->vol,stream_name,
stream_name_len)))) {
stream_name_len, TRUE)))) {
res = -EINVAL;
goto exit;
}
@ -2231,7 +2231,7 @@ static int ntfs_fuse_link(const char *old_path, const char *new_path)
uname_len = ntfs_mbstoucs(name, &uname);
if ((uname_len < 0)
|| (ctx->windows_names
&& ntfs_forbidden_names(ctx->vol,uname,uname_len))) {
&& ntfs_forbidden_names(ctx->vol,uname,uname_len,TRUE))) {
res = -errno;
goto exit;
}
@ -3379,7 +3379,7 @@ static int ntfs_fuse_setxattr(const char *path, const char *name,
lename_len = fix_xattr_prefix(name, namespace, &lename);
if ((lename_len == -1)
|| (ctx->windows_names
&& ntfs_forbidden_chars(lename,lename_len))) {
&& ntfs_forbidden_chars(lename,lename_len,TRUE))) {
res = -errno;
goto exit;
}