commit 795f2b44b7f692151556782f142a4a6e7d45d892 Author: Peter Seebach Date: Thu Feb 2 15:49:21 2012 -0600 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. Also fix the crash if base_path gets called with an fd for which we have no path. Upstream-Status: Backport diff --git a/ChangeLog.txt b/ChangeLog.txt index 9625b38..25bd463 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,6 +1,9 @@ 2012-02-02: * (seebs) stash dir name for DIR * from opendir using dirfd. * (seebs) add closedir. + * (seebs) add initial pass at renameat() + * (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 diff --git a/makewrappers b/makewrappers index 20bbf2b..bf344d6 100755 --- a/makewrappers +++ b/makewrappers @@ -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.dirfds = {} self.hand_wrapped = None # used for the copyright date when creating stub functions self.date = datetime.date.today().year @@ -239,6 +240,7 @@ 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)) @@ -246,7 +248,9 @@ class Function: # ignore varargs, they never get these special treatments if arg.vararg: pass - elif arg.name == 'dirfd': + elif arg.name[-5:] == 'dirfd': + if len(arg.name) > 5: + self.dirfds[arg.name[:-5]] = True self.dirfd = 'dirfd' elif arg.name == 'flags': self.flags = 'flags' @@ -325,9 +329,13 @@ class Function: """create/allocate canonical paths""" alloc_paths = [] for path in self.paths_to_munge: + prefix = path[:-4] + if not prefix in self.dirfds: + prefix = '' + print "for path %s: prefix <%s>" % ( path, 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): diff --git a/ports/unix/guts/renameat.c b/ports/unix/guts/renameat.c index c8203b7..f13cd1e 100644 --- a/ports/unix/guts/renameat.c +++ b/ports/unix/guts/renameat.c @@ -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 : "", + newdirfd, newpath ? newpath : ""); + +#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; * } */ diff --git a/pseudo_client.c b/pseudo_client.c index 48607c2..4a30420 100644 --- a/pseudo_client.c +++ b/pseudo_client.c @@ -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) {