@@ -22,8 +22,11 @@
pseudo_debug(PDBGF_FILE, "fopen64 '%s': fd %d <FILE %p>\n", path, fd, (void *) rc);
if (real___fxstat64(_STAT_VER, fd, &buf) != -1) {
if (!existed) {
+ int path_lock_fd = pseudo_client_path_lock(0);
real_fchmod(fd, PSEUDO_FS_MODE(0666 & ~pseudo_umask, 0));
- pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf);
+ if (pseudo_client_path_matches_fd(path, fd, 0))
+ pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf);
+ pseudo_client_path_unlock(path_lock_fd);
}
pseudo_client_op(OP_OPEN, pseudo_access_fopen(mode), fd, -1, path, &buf);
} else {
@@ -21,8 +21,11 @@
pseudo_debug(PDBGF_FILE, "freopen64 '%s': fd %d\n", path, fd);
if (real___fxstat64(_STAT_VER, fd, &buf) != -1) {
if (!existed) {
+ int path_lock_fd = pseudo_client_path_lock(0);
real_fchmod(fd, PSEUDO_FS_MODE(0666 & ~pseudo_umask, 0));
- pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf);
+ if (pseudo_client_path_matches_fd(path, fd, 0))
+ pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf);
+ pseudo_client_path_unlock(path_lock_fd);
}
pseudo_client_op(OP_OPEN, pseudo_access_fopen(mode), fd, -1, path, &buf);
} else {
@@ -147,9 +147,15 @@
if (stat_rc != -1) {
buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, mode);
if (!existed) {
+ int path_lock_fd = pseudo_client_path_lock(0);
real_fchmod(rc, PSEUDO_FS_MODE(mode, 0));
// file has no path, but has been created
- pseudo_client_op(OP_CREAT, 0, -1, dirfd, path, &buf);
+ if (pseudo_client_path_matches_fd(path, rc, flags & O_NOFOLLOW) &&
+ real___fxstat64(_STAT_VER, rc, &buf) != -1) {
+ buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, mode);
+ pseudo_client_op(OP_CREAT, 0, -1, dirfd, path, &buf);
+ }
+ pseudo_client_path_unlock(path_lock_fd);
}
pseudo_client_op(OP_OPEN, PSEUDO_ACCESS(flags), rc, dirfd, path, &buf);
} else {
@@ -182,9 +182,15 @@
if (stat_rc != -1) {
buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, my_how.mode);
if (!existed) {
+ int path_lock_fd = pseudo_client_path_lock(0);
real_fchmod(rc, PSEUDO_FS_MODE(my_how.mode, 0));
// file has no path, but has been created
- pseudo_client_op(OP_CREAT, 0, -1, dirfd, pseudo_path, &buf);
+ if (pseudo_client_path_matches_fd(pseudo_path, rc, my_how.flags & O_NOFOLLOW) &&
+ real___fxstat64(_STAT_VER, rc, &buf) != -1) {
+ buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, my_how.mode);
+ pseudo_client_op(OP_CREAT, 0, -1, dirfd, pseudo_path, &buf);
+ }
+ pseudo_client_path_unlock(path_lock_fd);
}
pseudo_client_op(OP_OPEN, PSEUDO_ACCESS(my_how.flags), rc, dirfd, pseudo_path, &buf);
} else {
@@ -21,8 +21,11 @@
pseudo_debug(PDBGF_OP, "fopen '%s': fd %d <FILE %p>\n", path, fd, (void *) rc);
if (base_fstat(fd, &buf) != -1) {
if (!existed) {
+ int path_lock_fd = pseudo_client_path_lock(0);
real_fchmod(fd, PSEUDO_FS_MODE(0666 & ~pseudo_umask, 0));
- pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf);
+ if (pseudo_client_path_matches_fd(path, fd, 0))
+ pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf);
+ pseudo_client_path_unlock(path_lock_fd);
}
pseudo_client_op(OP_OPEN, pseudo_access_fopen(mode), fd, -1, path, &buf);
} else {
@@ -21,8 +21,11 @@
pseudo_debug(PDBGF_OP, "freopen '%s': fd %d\n", path, fd);
if (base_fstat(fd, &buf) != -1) {
if (!existed) {
+ int path_lock_fd = pseudo_client_path_lock(0);
real_fchmod(fd, PSEUDO_FS_MODE(0666 & ~pseudo_umask, 0));
- pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf);
+ if (pseudo_client_path_matches_fd(path, fd, 0))
+ pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf);
+ pseudo_client_path_unlock(path_lock_fd);
}
pseudo_client_op(OP_OPEN, pseudo_access_fopen(mode), fd, -1, path, &buf);
} else {
@@ -1225,6 +1225,31 @@ pseudo_client_path_unlock(int fd) {
errno = save_errno;
}
+/* Operations that can block, such as open() on a FIFO, can't hold the path
+ * lock across the real call. Instead they take the lock afterwards and
+ * check that the path still names the file they opened before recording
+ * it. With the lock held, no other client is part way through changing the
+ * path, so if it now names a different file, that client has already
+ * recorded the change, and recording ours would be wrong.
+ */
+int
+pseudo_client_path_matches_fd(const char *path, int fd, int nofollow) {
+ PSEUDO_STATBUF pbuf, fbuf;
+ int save_errno = errno;
+ int rc;
+
+ if (!path || fd < 0 || !pseudo_real_fstat || !pseudo_real_lstat || !pseudo_real_stat)
+ return 1;
+ rc = pseudo_real_fstat(fd, &fbuf) == 0 &&
+ (nofollow ? pseudo_real_lstat(path, &pbuf) : pseudo_real_stat(path, &pbuf)) == 0 &&
+ pbuf.st_dev == fbuf.st_dev && pbuf.st_ino == fbuf.st_ino;
+ if (!rc)
+ pseudo_debug(PDBGF_FILE, "'%s' no longer names fd %d, not recording it.\n",
+ path, fd);
+ errno = save_errno;
+ return rc;
+}
+
static int
client_connect(void) {
/* we have a server pid, is it responsive? */
@@ -29,6 +29,7 @@ extern int pseudo_client_shutdown(int);
extern int pseudo_fd(int fd, int how);
extern int pseudo_client_path_lock(int shared);
extern void pseudo_client_path_unlock(int fd);
+extern int pseudo_client_path_matches_fd(const char *path, int fd, int nofollow);
#define MOVE_FD 0
#define COPY_FD 1
#define PSEUDO_MIN_FD 20