Fix some POSIX-compliance bugs in link, rename, mkdir.

* syscalls.cc (link): Delete obsolete comment.  Reject directories
and missing source up front.
(rename): Use correct errno for trailing '.'.  Detect empty
strings.  Allow trailing slash to newpath iff oldpath is
directory.
* dir.cc (mkdir): Reject dangling symlink with trailing slash.
* fhandler_disk_file.cc (fhandler_disk_file::link): Reject
trailing slash.
* fhandler.cc (fhandler_base::link): Match Linux errno.
This commit is contained in:
Eric Blake
2009-09-26 15:51:53 +00:00
parent 1e6459d3e5
commit 52dba6a5c4
5 changed files with 109 additions and 42 deletions

View File

@ -1124,13 +1124,6 @@ isatty (int fd)
}
EXPORT_ALIAS (isatty, _isatty)
/* Under NT, try to make a hard link using backup API. If that
fails or we are Win 95, just copy the file.
FIXME: We should actually be checking partition type, not OS.
Under NTFS, we should support hard links. On FAT partitions,
we should just copy the file.
*/
extern "C" int
link (const char *oldpath, const char *newpath)
{
@ -1145,6 +1138,10 @@ link (const char *oldpath, const char *newpath)
debug_printf ("got %d error from build_fh_name", fh->error ());
set_errno (fh->error ());
}
else if (fh->pc.isdir ())
set_errno (EPERM); /* We do not permit linking directories. */
else if (!fh->pc.exists ())
set_errno (ENOENT);
else
res = fh->link (newpath);
@ -1651,7 +1648,6 @@ rename (const char *oldpath, const char *newpath)
{
tmp_pathbuf tp;
int res = -1;
char *oldbuf, *newbuf;
path_conv oldpc, newpc, new2pc, *dstpc, *removepc = NULL;
bool old_dir_requested = false, new_dir_requested = false;
bool old_explicit_suffix = false, new_explicit_suffix = false;
@ -1670,16 +1666,21 @@ rename (const char *oldpath, const char *newpath)
if (efault.faulted (EFAULT))
return -1;
if (!*oldpath || !*newpath)
{
set_errno (ENOENT);
goto out;
}
if (has_dot_last_component (oldpath, true))
{
oldpc.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes);
set_errno (oldpc.isdir () ? EBUSY : ENOTDIR);
set_errno (oldpc.isdir () ? EINVAL : ENOTDIR);
goto out;
}
if (has_dot_last_component (newpath, true))
{
newpc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes);
set_errno (!newpc.exists () ? ENOENT : newpc.isdir () ? EBUSY : ENOTDIR);
set_errno (!newpc.exists () ? ENOENT : newpc.isdir () ? EINVAL : ENOTDIR);
goto out;
}
@ -1689,10 +1690,20 @@ rename (const char *oldpath, const char *newpath)
olen = strlen (oldpath);
if (isdirsep (oldpath[olen - 1]))
{
stpcpy (oldbuf = tp.c_get (), oldpath);
while (olen > 0 && isdirsep (oldbuf[olen - 1]))
oldbuf[--olen] = '\0';
oldpath = oldbuf;
char *buf;
char *p = stpcpy (buf = tp.c_get (), oldpath) - 1;
oldpath = buf;
while (p >= oldpath && isdirsep (*p))
*p-- = '\0';
olen = p + 1 - oldpath;
if (!olen)
{
/* The root directory cannot be renamed. This also rejects
the corner case of rename("/","/"), even though it is the
same file. */
set_errno (EINVAL);
goto out;
}
old_dir_requested = true;
}
oldpc.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes);
@ -1724,10 +1735,17 @@ rename (const char *oldpath, const char *newpath)
nlen = strlen (newpath);
if (isdirsep (newpath[nlen - 1]))
{
stpcpy (newbuf = tp.c_get (), newpath);
while (nlen > 0 && isdirsep (newbuf[nlen - 1]))
newbuf[--nlen] = '\0';
newpath = newbuf;
char *buf;
char *p = stpcpy (buf = tp.c_get (), newpath) - 1;
newpath = buf;
while (p >= newpath && isdirsep (*p))
*p-- = '\0';
nlen = p + 1 - newpath;
if (!nlen) /* The root directory is never empty. */
{
set_errno (ENOTEMPTY);
goto out;
}
new_dir_requested = true;
}
newpc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes);
@ -1741,9 +1759,22 @@ rename (const char *oldpath, const char *newpath)
set_errno (EROFS);
goto out;
}
if (new_dir_requested && !newpc.isdir ())
if (new_dir_requested)
{
set_errno (ENOTDIR);
if (!newpc.exists())
{
set_errno (ENOENT);
goto out;
}
if (!newpc.isdir ())
{
set_errno (ENOTDIR);
goto out;
}
}
if (newpc.exists () && (oldpc.isdir () ? !newpc.isdir () : newpc.isdir ()))
{
set_errno (newpc.isdir () ? EISDIR : ENOTDIR);
goto out;
}
if (newpc.known_suffix
@ -1774,22 +1805,23 @@ rename (const char *oldpath, const char *newpath)
}
else if (oldpc.isdir ())
{
if (newpc.exists () && !newpc.isdir ())
{
set_errno (ENOTDIR);
goto out;
}
/* Check for newpath being a subdir of oldpath. */
/* Check for newpath being identical or a subdir of oldpath. */
if (RtlPrefixUnicodeString (oldpc.get_nt_native_path (),
newpc.get_nt_native_path (),
TRUE)
&& newpc.get_nt_native_path ()->Length >
oldpc.get_nt_native_path ()->Length
&& *(PWCHAR) ((PBYTE) newpc.get_nt_native_path ()->Buffer
+ oldpc.get_nt_native_path ()->Length) == L'\\')
TRUE))
{
set_errno (EINVAL);
goto out;
if (newpc.get_nt_native_path ()->Length
== oldpc.get_nt_native_path ()->Length)
{
res = 0;
goto out;
}
if (*(PWCHAR) ((PBYTE) newpc.get_nt_native_path ()->Buffer
+ oldpc.get_nt_native_path ()->Length) == L'\\')
{
set_errno (EINVAL);
goto out;
}
}
}
else if (!newpc.exists ())
@ -1816,11 +1848,6 @@ rename (const char *oldpath, const char *newpath)
.exe suffix must be given explicitly in oldpath. */
rename_append_suffix (newpc, newpath, nlen, ".exe");
}
else if (newpc.isdir ())
{
set_errno (EISDIR);
goto out;
}
else
{
if (equal_path && old_explicit_suffix != new_explicit_suffix)