Permalink
Browse files

Fix *at() function interface holes

1.  Fix *at() where dirfd is obtained through dirfd(DIR *).

The dirfd(DIR *) interface allows you to get the fd for a DIR *,
meaning you can use it with openat(), meaning you can need its
path.  This causes a segfault.  Also fixed the base_path
code not to segfault in that case, but first fix the
underlying problem.

2.  Implement renameat()

After three long years, someone tried to use this.  This was impossibly
hard back when pseudo was written, because there was only one dirfd
provided for.  Thing is, now, the canonicalization happens in wrapfuncs,
so a small tweak to makewrappers to recognize that oldpath should use
olddirfd if it exists is enough to get us fully canonicalized paths
when needed.
  • Loading branch information...
wr-seebs committed Feb 2, 2012
1 parent 17c2233 commit 2b43a1b32f47df5aa442a1d579b0c8801389cff0
Showing with 162 additions and 8 deletions.
  1. +8 −0 ChangeLog.txt
  2. +12 −5 makewrappers
  3. +20 −0 ports/unix/guts/closedir.c
  4. +17 −0 ports/unix/guts/opendir.c
  5. +98 −2 ports/unix/guts/renameat.c
  6. +1 −0 ports/unix/wrapfuncs.in
  7. +6 −1 pseudo_client.c
View
@@ -1,3 +1,11 @@
2012-02-02:
* (seebs) stash dir name for DIR * from opendir using dirfd.
* (seebs) add closedir.
* (seebs) add initial pass at renameat()
* (seebs) update makewrappers with smarter *dirfd handling.
* (seebs) in base_path, don't try to strlen the result if
fd_path() returns NULL.
2011-11-02:
* (seebs) Call this 1.2 because the UNLOAD change is moderately
significant, and so's the clone change.
View
@@ -211,12 +211,13 @@ class Function:
self.flags = '0'
self.port = port
self.directory = ''
self.version = 'NULL'
self.version = 'NULL'
# On Darwin, some functions are SECRETLY converted to foo$INODE64
# when called. So we have to look those up for real_*
self.inode64 = None
self.real_func = None
self.paths_to_munge = []
self.specific_dirfds = {}
self.hand_wrapped = None
# used for the copyright date when creating stub functions
self.date = datetime.date.today().year
@@ -239,18 +240,21 @@ class Function:
# * If the arg has a name ending in 'path', we will canonicalize it.
# * If the arg is named 'dirfd' or 'flags', it becomes the default
# values for the dirfd and flags arguments when canonicalizing.
# * If the name ends in dirfd, we do the same fancy stuff.
# * Note that the "comments" field (/* ... */ after the decl) can
# override the dirfd/flags values.
self.args = ArgumentList(bits.group(2))
for arg in self.args.args:
# ignore varargs, they never get these special treatments
if arg.vararg:
pass
elif arg.name == 'dirfd':
elif arg.name.endswith('dirfd'):
if len(arg.name) > 5:
self.specific_dirfds[arg.name[:-5]] = True
self.dirfd = 'dirfd'
elif arg.name == 'flags':
self.flags = 'flags'
elif arg.name[-4:] == 'path':
elif arg.name.endswith('path'):
self.paths_to_munge.append(arg.name)
# pick default values
@@ -325,9 +329,12 @@ class Function:
"""create/allocate canonical paths"""
alloc_paths = []
for path in self.paths_to_munge:
prefix = path[:-4]
if prefix not in self.specific_dirfds:
prefix = ''
alloc_paths.append(
"%s = pseudo_root_path(__func__, __LINE__, %s, %s, %s);" %
(path, self.dirfd, path, self.flags))
"%s = pseudo_root_path(__func__, __LINE__, %s%s, %s, %s);" %
(path, prefix, self.dirfd, path, self.flags))
return "\n\t\t\t".join(alloc_paths)
def free_paths(self):
View
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2012 Wind River Systems; see
* guts/COPYRIGHT for information.
*
* static int
* wrap_closedir(DIR *dirp) {
* int rc = -1;
*/
if (!dirp) {
errno = EFAULT;
return -1;
}
int fd = dirfd(dirp);
pseudo_client_op(OP_CLOSE, 0, fd, -1, 0, 0);
rc = real_closedir(dirp);
/* return rc;
* }
*/
View
@@ -6,8 +6,25 @@
* wrap_opendir(const char *path) {
* DIR * rc = NULL;
*/
struct stat buf;
int save_errno;
rc = real_opendir(path);
if (rc) {
int fd;
save_errno = errno;
fd = dirfd(rc);
if (real_fstat(fd, &buf) == -1) {
pseudo_debug(1, "diropen (fd %d) succeeded, but fstat failed (%s).\n",
fd, strerror(errno));
pseudo_client_op_plain(OP_OPEN, PSA_READ, fd, -1, path, 0);
} else {
pseudo_client_op_plain(OP_OPEN, PSA_READ, fd, -1, path, &buf);
}
errno = save_errno;
}
/* return rc;
* }
View
@@ -1,15 +1,111 @@
/*
* Copyright (c) 2008-2010 Wind River Systems; see
* Copyright (c) 2008-2012 Wind River Systems; see
* guts/COPYRIGHT for information.
*
* static int
* wrap_renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
* int rc = -1;
*/
pseudo_msg_t *msg;
struct stat oldbuf, newbuf;
int oldrc, newrc;
int save_errno;
int old_db_entry = 0;
pseudo_diag("help! unimplemented renameat [%s -> %s].\n", oldpath, newpath);
pseudo_debug(2, "renameat: %d,%s->%d,%s\n",
olddirfd, oldpath ? oldpath : "<nil>",
newdirfd, newpath ? newpath : "<nil>");
#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
if (olddirfd != AT_FDCWD || newdirfd != AT_FDCWD) {
errno = ENOSYS;
return -1;
}
#endif
if (!oldpath || !newpath) {
errno = EFAULT;
return -1;
}
save_errno = errno;
#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
newrc = real_lstat(newpath, &newbuf);
oldrc = real_lstat(oldpath, &oldbuf);
#else
oldrc = real___fxstatat(_STAT_VER, olddirfd, oldpath, &oldbuf, AT_SYMLINK_NOFOLLOW);
newrc = real___fxstatat(_STAT_VER, newdirfd, newpath, &newbuf, AT_SYMLINK_NOFOLLOW);
#endif
errno = save_errno;
/* newpath must be removed. */
/* as with unlink, we have to mark that the file may get deleted */
msg = pseudo_client_op_plain(OP_MAY_UNLINK, 0, -1, newdirfd, newpath, newrc ? NULL : &newbuf);
if (msg && msg->result == RESULT_SUCCEED)
old_db_entry = 1;
rc = real_renameat(olddirfd, oldpath, newdirfd, newpath);
save_errno = errno;
if (old_db_entry) {
if (rc == -1) {
/* since we failed, that wasn't really unlinked -- put
* it back.
*/
pseudo_client_op_plain(OP_CANCEL_UNLINK, 0, -1, newdirfd, newpath, &newbuf);
} else {
/* confirm that the file was removed */
pseudo_client_op_plain(OP_DID_UNLINK, 0, -1, newdirfd, newpath, &newbuf);
}
}
if (rc == -1) {
/* and we're done. */
errno = save_errno;
return rc;
}
save_errno = errno;
/* nothing to do for a "rename" of a link to itself */
if (newrc != -1 && oldrc != -1 &&
newbuf.st_dev == oldbuf.st_dev &&
newbuf.st_ino == oldbuf.st_ino) {
return rc;
}
/* rename(3) is not mv(1). rename(file, dir) fails; you must provide
* the corrected path yourself. You can rename over a directory only
* if the source is a directory. Symlinks are simply removed.
*
* If we got here, the real rename call succeeded. That means newpath
* has been unlinked and oldpath has been linked to it.
*
* There are a ton of special cases to error check. I don't check
* for any of them, because in every such case, the underlying rename
* failed, and there is nothing to do.
* The only tricky part is that, because we used to ignore symlinks,
* we may have to rename or remove directory trees even though in
* theory rename can never destroy a directory tree.
*/
if (!old_db_entry) {
/* create an entry under the old name, which will then be
* renamed; this way, children would get renamed too, if there
* were any.
*/
if (newrc == 0) {
if (newbuf.st_dev != oldbuf.st_dev) {
oldbuf.st_dev = newbuf.st_dev;
oldbuf.st_ino = newbuf.st_ino;
}
}
pseudo_debug(1, "creating new '%s' [%llu] to rename\n",
oldpath, (unsigned long long) oldbuf.st_ino);
pseudo_client_op_plain(OP_LINK, 0, -1, olddirfd, oldpath, &oldbuf);
}
/* special case: use 'fd' for olddirfd, because
* we know it has no other meaning for RENAME
*/
pseudo_client_op_plain(OP_RENAME, 0, olddirfd, newdirfd, newpath, &oldbuf, oldpath);
errno = save_errno;
/* return rc;
* }
*/
View
@@ -21,6 +21,7 @@ long pathconf(const char *path, int name);
char *realpath(const char *name, char *resolved_name); /* version="GLIBC_2.3" */
int remove(const char *path); /* flags=AT_SYMLINK_NOFOLLOW */
DIR *opendir(const char *path);
int closedir(DIR *dirp);
char *tempnam(const char *template, const char *pfx);
char *tmpnam(char *s);
int truncate(const char *path, off_t length);
View
@@ -988,6 +988,8 @@ base_path(int dirfd, const char *path, int leave_last) {
if (dirfd != -1 && dirfd != AT_FDCWD) {
if (dirfd >= 0) {
basepath = fd_path(dirfd);
}
if (basepath) {
baselen = strlen(basepath);
} else {
pseudo_diag("got *at() syscall for unknown directory, fd %d\n", dirfd);
@@ -1128,7 +1130,10 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path
if (path) {
pseudo_debug(2, " %s", path);
}
if (fd != -1) {
/* for OP_RENAME in renameat, "fd" is also used for the
* second dirfd.
*/
if (fd != -1 && op != OP_RENAME) {
pseudo_debug(2, " [fd %d]", fd);
}
if (buf) {

0 comments on commit 2b43a1b

Please sign in to comment.