Cygwin: mkdir and rmdir: treat drive names specially

If the directory name has the form 'x:' followed by one or more
slashes or backslashes, and if there's at least one backslash, assume
that the user is referring to 'x:\', the root directory of drive x,
and don't strip the backslash.

Previously all trailing slashes and backslashes were stripped, and the
name was treated as a relative file name containing a literal colon.

Addresses https://cygwin.com/ml/cygwin/2019-08/msg00334.html.
This commit is contained in:
Ken Brown 2019-09-27 14:00:52 -04:00
parent faf0c0f8b0
commit 1e8370c1c1
2 changed files with 32 additions and 5 deletions

View File

@ -313,15 +313,27 @@ mkdir (const char *dir, mode_t mode)
/* Following Linux, and intentionally ignoring POSIX, do not /* Following Linux, and intentionally ignoring POSIX, do not
resolve the last component of DIR if it is a symlink, even if resolve the last component of DIR if it is a symlink, even if
DIR has a trailing slash. Achieve this by stripping trailing DIR has a trailing slash. Achieve this by stripping trailing
slashes or backslashes. */ slashes or backslashes.
Exception: If DIR == 'x:' followed by one or more slashes or
backslashes, and if there's at least one backslash, assume
that the user is referring to the root directory of drive x.
Retain one backslash in this case. */
if (isdirsep (dir[strlen (dir) - 1])) if (isdirsep (dir[strlen (dir) - 1]))
{ {
/* This converts // to /, but since both give EEXIST, we're okay. */ /* This converts // to /, but since both give EEXIST, we're okay. */
char *buf; char *buf;
char *p = stpcpy (buf = tp.c_get (), dir) - 1; char *p = stpcpy (buf = tp.c_get (), dir) - 1;
bool msdos = false;
dir = buf; dir = buf;
while (p > dir && isdirsep (*p)) while (p > dir && isdirsep (*p))
*p-- = '\0'; {
if (*p == '\\')
msdos = true;
*p-- = '\0';
}
if (msdos && p == dir + 1 && isdrive (dir))
p[1] = '\\';
} }
if (!(fh = build_fh_name (dir, PC_SYM_NOFOLLOW))) if (!(fh = build_fh_name (dir, PC_SYM_NOFOLLOW)))
__leave; /* errno already set */; __leave; /* errno already set */;
@ -360,20 +372,31 @@ rmdir (const char *dir)
set_errno (ENOENT); set_errno (ENOENT);
__leave; __leave;
} }
/* Following Linux, and intentionally ignoring POSIX, do not /* Following Linux, and intentionally ignoring POSIX, do not
resolve the last component of DIR if it is a symlink, even if resolve the last component of DIR if it is a symlink, even if
DIR has a trailing slash. Achieve this by stripping trailing DIR has a trailing slash. Achieve this by stripping trailing
slashes or backslashes. */ slashes or backslashes.
Exception: If DIR == 'x:' followed by one or more slashes or
backslashes, and if there's at least one backslash, assume
that the user is referring to the root directory of drive x.
Retain one backslash in this case. */
if (isdirsep (dir[strlen (dir) - 1])) if (isdirsep (dir[strlen (dir) - 1]))
{ {
/* This converts // to /, but since both give ENOTEMPTY, /* This converts // to /, but since both give ENOTEMPTY,
we're okay. */ we're okay. */
char *buf; char *buf;
char *p = stpcpy (buf = tp.c_get (), dir) - 1; char *p = stpcpy (buf = tp.c_get (), dir) - 1;
bool msdos = false;
dir = buf; dir = buf;
while (p > dir && isdirsep (*p)) while (p > dir && isdirsep (*p))
*p-- = '\0'; {
if (*p == '\\')
msdos = true;
*p-- = '\0';
}
if (msdos && p == dir + 1 && isdrive (dir))
p[1] = '\\';
} }
if (!(fh = build_fh_name (dir, PC_SYM_NOFOLLOW))) if (!(fh = build_fh_name (dir, PC_SYM_NOFOLLOW)))
__leave; /* errno already set */; __leave; /* errno already set */;

View File

@ -87,3 +87,7 @@ Bug Fixes
- Fix an assertion failure on an invalid path. - Fix an assertion failure on an invalid path.
Addresses: https://cygwin.com/ml/cygwin/2019-09/msg00228.html Addresses: https://cygwin.com/ml/cygwin/2019-09/msg00228.html
- If the argument to mkdir(2) or rmdir(2) is 'x:\', don't strip the
trailing backslash.
Addresses: https://cygwin.com/ml/cygwin/2019-08/msg00334.html