aboutsummaryrefslogtreecommitdiffstats
path: root/ports
diff options
context:
space:
mode:
authorPeter Seebach <peter.seebach@windriver.com>2012-02-02 11:45:42 -0600
committerPeter Seebach <peter.seebach@windriver.com>2012-02-06 12:17:00 -0600
commit2b43a1b32f47df5aa442a1d579b0c8801389cff0 (patch)
treeb5aea361004115706f970a52869addef7b8ed81a /ports
parent17c2233f93692f79684792750001ee6d13e03925 (diff)
downloadpseudo-2b43a1b32f47df5aa442a1d579b0c8801389cff0.tar.gz
pseudo-2b43a1b32f47df5aa442a1d579b0c8801389cff0.tar.bz2
pseudo-2b43a1b32f47df5aa442a1d579b0c8801389cff0.zip
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.
Diffstat (limited to 'ports')
-rw-r--r--ports/unix/guts/closedir.c20
-rw-r--r--ports/unix/guts/opendir.c17
-rw-r--r--ports/unix/guts/renameat.c100
-rw-r--r--ports/unix/wrapfuncs.in1
4 files changed, 136 insertions, 2 deletions
diff --git a/ports/unix/guts/closedir.c b/ports/unix/guts/closedir.c
new file mode 100644
index 0000000..1085361
--- /dev/null
+++ b/ports/unix/guts/closedir.c
@@ -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;
+ * }
+ */
diff --git a/ports/unix/guts/opendir.c b/ports/unix/guts/opendir.c
index 8eaa71f..e69717e 100644
--- a/ports/unix/guts/opendir.c
+++ b/ports/unix/guts/opendir.c
@@ -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;
* }
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 : "<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;
* }
*/
diff --git a/ports/unix/wrapfuncs.in b/ports/unix/wrapfuncs.in
index e06e404..32250c4 100644
--- a/ports/unix/wrapfuncs.in
+++ b/ports/unix/wrapfuncs.in
@@ -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);