aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog.txt1
-rw-r--r--guts/rename.c20
-rw-r--r--guts/rmdir.c20
-rw-r--r--guts/unlinkat.c23
-rwxr-xr-xmakewrappers1
-rw-r--r--pseudo.c10
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
diff --git a/pseudo.c b/pseudo.c
index 484c76c..bedce6b 100644
--- a/pseudo.c
+++ b/pseudo.c
@@ -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: