diff options
-rw-r--r-- | ChangeLog.txt | 1 | ||||
-rw-r--r-- | guts/rename.c | 20 | ||||
-rw-r--r-- | guts/rmdir.c | 20 | ||||
-rw-r--r-- | guts/unlinkat.c | 23 | ||||
-rwxr-xr-x | makewrappers | 1 | ||||
-rw-r--r-- | pseudo.c | 10 |
6 files changed, 63 insertions, 12 deletions
diff --git a/ChangeLog.txt b/ChangeLog.txt index b6ed10d..d029328 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,6 +1,7 @@ 2010-04-20: * (seebs) add quick sanity-check option for pseudo * (seebs) report on rows deleted + * (seebs) unlink after removing db entry to reduce race conditions 2010-04-19: * (seebs) fix crash if xstat() or similar routine called with null path diff --git a/guts/rename.c b/guts/rename.c index af9758b..9a27538 100644 --- a/guts/rename.c +++ b/guts/rename.c @@ -27,9 +27,24 @@ errno = save_errno; + /* newpath must be removed. */ + /* as with unlink, we have to do the remove before the operation + */ + msg = pseudo_client_op(OP_UNLINK, 0, -1, -1, newpath, newrc ? NULL : &newbuf); rc = real_rename(oldpath, newpath); + save_errno = errno; if (rc == -1) { - /* we failed, and we don't care why */ + newbuf.st_uid = msg->uid; + newbuf.st_gid = msg->uid; + newbuf.st_mode = msg->mode; + newbuf.st_dev = msg->dev; + newbuf.st_ino = msg->ino; + /* since we failed, that wasn't really unlinked -- put + * it back. + */ + pseudo_client_op(OP_LINK, 0, -1, -1, newpath, &newbuf); + /* and we're done. */ + errno = save_errno; return rc; } save_errno = errno; @@ -55,9 +70,6 @@ * theory rename can never destroy a directory tree. */ - /* newpath must be removed. */ - pseudo_client_op(OP_UNLINK, 0, -1, -1, newpath, &newbuf); - /* fill in "correct" details from server */ msg = pseudo_client_op(OP_STAT, 0, -1, -1, oldpath, &oldbuf); if (msg && msg->result == RESULT_SUCCEED) { diff --git a/guts/rmdir.c b/guts/rmdir.c index 811ce87..f3cd9e4 100644 --- a/guts/rmdir.c +++ b/guts/rmdir.c @@ -6,6 +6,7 @@ * wrap_rmdir(const char *path) { * int rc = -1; */ + pseudo_msg_t *old_file; struct stat64 buf; int save_errno; @@ -13,13 +14,24 @@ if (rc == -1) { return rc; } + old_file = pseudo_client_op(OP_UNLINK, 0, -1, -1, path, &buf); rc = real_rmdir(path); - save_errno = errno; - if (rc != -1) { - pseudo_client_op(OP_UNLINK, 0, -1, -1, path, &buf); + if (rc == -1) { + save_errno = errno; + if (old_file && old_file->result == RESULT_SUCCEED) { + pseudo_debug(1, "rmdir failed, trying to relink...\n"); + buf.st_uid = old_file->uid; + buf.st_gid = old_file->uid; + buf.st_mode = old_file->mode; + buf.st_dev = old_file->dev; + buf.st_ino = old_file->ino; + pseudo_client_op(OP_LINK, 0, -1, -1, path, &buf); + } else { + pseudo_debug(1, "rmdir failed, but found no database entry, ignoring.\n"); + } + errno = save_errno; } - errno = save_errno; /* return rc; * } */ diff --git a/guts/unlinkat.c b/guts/unlinkat.c index b146602..13fdc82 100644 --- a/guts/unlinkat.c +++ b/guts/unlinkat.c @@ -6,6 +6,10 @@ * wrap_unlinkat(int dirfd, const char *path, int rflags) { * int rc = -1; */ + pseudo_msg_t *old_file; + int save_errno; + struct stat64 buf; + #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS if (dirfd != AT_FDCWD) { errno = ENOSYS; @@ -22,7 +26,6 @@ return -1; } #endif - struct stat64 buf; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS rc = real___lxstat64(_STAT_VER, path, &buf); @@ -32,15 +35,27 @@ if (rc == -1) { return rc; } + old_file = pseudo_client_op(OP_UNLINK, 0, -1, dirfd, path, &buf); #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS rc = real_unlink(path); #else rc = real_unlinkat(dirfd, path, rflags); #endif - if (rc != -1) { - pseudo_client_op(OP_UNLINK, 0, -1, dirfd, path, &buf); + if (rc == -1) { + save_errno = errno; + if (old_file && old_file->result == RESULT_SUCCEED) { + pseudo_debug(1, "unlink failed, trying to relink...\n"); + buf.st_uid = old_file->uid; + buf.st_gid = old_file->uid; + buf.st_mode = old_file->mode; + buf.st_dev = old_file->dev; + buf.st_ino = old_file->ino; + pseudo_client_op(OP_LINK, 0, -1, dirfd, path, &buf); + } else { + pseudo_debug(1, "unlink failed, but found no database entry, ignoring.\n"); + } + errno = save_errno; } - /* return rc; * } */ diff --git a/makewrappers b/makewrappers index cd6a675..d269aa3 100755 --- a/makewrappers +++ b/makewrappers @@ -273,6 +273,7 @@ EOF pseudo_debug(4, "called: $name\n"); if (pseudo_getlock()) { errno = EBUSY; + sigprocmask(SIG_SETMASK, &saved, NULL); $(write_return $default_value); } $decl_paths @@ -656,6 +656,16 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag) { (unsigned long long) msg->ino); pdb_unlink_file_dev(msg); } + /* if we had a match for this path, stash it in the + * message -- client may want to relink it if the + * real_unlink() fails. + */ + if (found_path) { + *msg = by_path; + msg->result = RESULT_SUCCEED; + } else { + msg->result = RESULT_NONE; + } break; case OP_MKDIR: /* FALLTHROUGH */ case OP_MKNOD: |