aboutsummaryrefslogtreecommitdiffstats
path: root/pseudo_client.c
diff options
context:
space:
mode:
Diffstat (limited to 'pseudo_client.c')
-rw-r--r--pseudo_client.c836
1 files changed, 836 insertions, 0 deletions
diff --git a/pseudo_client.c b/pseudo_client.c
new file mode 100644
index 0000000..eb3cf81
--- /dev/null
+++ b/pseudo_client.c
@@ -0,0 +1,836 @@
+/*
+ * pseudo_client.c, pseudo client library code
+ *
+ * Copyright (c) 2008-2010 Wind River Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the Lesser GNU General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the Lesser GNU General Public License for more details.
+ *
+ * You should have received a copy of the Lesser GNU General Public License
+ * version 2.1 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <stdio.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "pseudo.h"
+#include "pseudo_ipc.h"
+#include "pseudo_client.h"
+
+static int connect_fd = -1;
+static int server_pid = 0;
+int pseudo_dir_fd = -1;
+
+static char **fd_paths = 0;
+static int nfds = 0;
+static char cwdbuf[PATH_MAX * 2], *cwd;
+static int cwdlen = 0;
+static int messages = 0;
+static struct timeval message_time = { 0 };
+static int pseudo_inited = 0;
+
+/* note: these are int, not uid_t/gid_t, so I can use 'em with scanf */
+int pseudo_ruid;
+int pseudo_euid;
+int pseudo_suid;
+int pseudo_fuid;
+int pseudo_rgid;
+int pseudo_egid;
+int pseudo_sgid;
+int pseudo_fgid;
+
+void
+pseudo_client_touchuid(void) {
+ static char uidbuf[256];
+ snprintf(uidbuf, 256, "%d,%d,%d,%d",
+ pseudo_ruid, pseudo_euid, pseudo_suid, pseudo_fuid);
+ setenv("PSEUDO_UIDS", uidbuf, 1);
+}
+
+void
+pseudo_client_touchgid(void) {
+ static char gidbuf[256];
+ snprintf(gidbuf, 256, "%d,%d,%d,%d",
+ pseudo_rgid, pseudo_egid, pseudo_sgid, pseudo_fgid);
+ setenv("PSEUDO_GIDS", gidbuf, 1);
+}
+
+static char *
+fd_path(int fd) {
+ if (fd >= 0 && fd < nfds) {
+ return fd_paths[fd];
+ }
+ if (fd == AT_FDCWD) {
+ return cwd;
+ }
+ return 0;
+}
+
+static void
+pseudo_client_path(int fd, const char *path) {
+ if (fd < 0)
+ return;
+
+ if (fd >= nfds) {
+ int i;
+ pseudo_debug(2, "expanding fds from %d to %d\n",
+ nfds, fd + 1);
+ fd_paths = realloc(fd_paths, (fd + 1) * sizeof(char *));
+ for (i = nfds; i < fd + 1; ++i)
+ fd_paths[i] = 0;
+ nfds = fd + 1;
+ } else {
+ if (fd_paths[fd]) {
+ pseudo_debug(2, "reopening fd %d [%s] -- didn't see close\n",
+ fd, fd_paths[fd]);
+ }
+ /* yes, it is safe to free null pointers. yay for C89 */
+ free(fd_paths[fd]);
+ fd_paths[fd] = 0;
+ }
+ if (path) {
+ fd_paths[fd] = strdup(path);
+ }
+}
+
+static void
+pseudo_client_close(int fd) {
+ if (fd < 0 || fd >= nfds)
+ return;
+
+ free(fd_paths[fd]);
+ fd_paths[fd] = 0;
+}
+
+void
+pseudo_client_reset() {
+ pseudo_antimagic();
+ pseudo_new_pid();
+ if (connect_fd != -1) {
+ close(connect_fd);
+ connect_fd = -1;
+ }
+ cwd = getcwd(cwdbuf, PATH_MAX);
+ cwdlen = strlen(cwd);
+ if (!pseudo_inited) {
+ char *env;
+
+ env = getenv("PSEUDO_UIDS");
+ if (env)
+ sscanf(env, "%d,%d,%d,%d",
+ &pseudo_ruid, &pseudo_euid,
+ &pseudo_suid, &pseudo_fuid);
+
+ env = getenv("PSEUDO_GIDS");
+ if (env)
+ sscanf(env, "%d,%d,%d,%d",
+ &pseudo_rgid, &pseudo_egid,
+ &pseudo_sgid, &pseudo_fuid);
+
+ pseudo_inited = 1;
+ }
+ pseudo_magic();
+}
+
+/* spawn server */
+static int
+client_spawn_server(void) {
+ int status;
+ FILE *fp;
+ extern char **environ;
+ int cwd_fd;
+
+ if ((server_pid = fork()) != 0) {
+ if (server_pid == -1) {
+ pseudo_diag("couldn't fork server: errno %d\n", errno);
+ return 1;
+ }
+ pseudo_debug(4, "spawned server, pid %d\n", server_pid);
+ /* wait for the child process to terminate, indicating server
+ * is ready
+ */
+ waitpid(server_pid, &status, 0);
+ server_pid = -2;
+ cwd_fd = open(".", O_RDONLY);
+ if (cwd_fd == -1) {
+ pseudo_diag("Couldn't stash directory before opening pidfile: %s",
+ strerror(errno));
+ close(connect_fd);
+ connect_fd = -1;
+ return 1;
+ }
+ if (fchdir(pseudo_dir_fd)) {
+ pseudo_diag("Couldn't change to server dir [%d]: %s\n",
+ pseudo_dir_fd, strerror(errno));
+ }
+ fp = fopen(PSEUDO_PIDFILE, "r");
+ if (fchdir(cwd_fd) == -1) {
+ pseudo_diag("return to previous directory failed: %s\n",
+ strerror(errno));
+ }
+ close(cwd_fd);
+ if (fp) {
+ if (fscanf(fp, "%d", &server_pid) != 1) {
+ pseudo_debug(1, "Opened server PID file, but didn't get a pid.\n");
+ }
+ fclose(fp);
+ } else {
+ pseudo_diag("no pid file (%s): %s\n",
+ PSEUDO_PIDFILE, strerror(errno));
+ }
+ pseudo_debug(2, "read new pid file: %d\n", server_pid);
+ /* at this point, we should have a new server_pid */
+ return 0;
+ } else {
+ char *base_args[] = { NULL, NULL, NULL };
+ char **argv;
+ int args;
+ int fd;
+
+ pseudo_new_pid();
+ base_args[0] = "bin/pseudo";
+ base_args[1] = "-d";
+ if (getenv("PSEUDO_OPTS")) {
+ char *option_string = strdup(getenv("PSEUDO_OPTS"));
+ char *s;
+ int arg;
+
+ /* count arguments in PSEUDO_OPTS, starting at 2
+ * for pseudo/-d/NULL, plus one for the option string.
+ * The number of additional arguments may be less
+ * than the number of spaces, but can't be more.
+ */
+ args = 4;
+ for (s = option_string; *s; ++s)
+ if (*s == ' ')
+ ++args;
+
+ argv = malloc(args * sizeof(char *));
+ argv[0] = base_args[0];
+ argv[1] = base_args[1];
+ arg = 2;
+ while ((s = strsep(&option_string, " ")) != NULL) {
+ if (*s) {
+ argv[arg++] = strdup(s);
+ }
+ }
+ argv[arg] = 0;
+ } else {
+ argv = base_args;
+ }
+ pseudo_dropenv();
+ pseudo_debug(4, "calling execve on %s\n", argv[0]);
+ /* and now, execute the server */
+ if (fchdir(pseudo_dir_fd)) {
+ pseudo_diag("Couldn't change to server dir [%d]: %s\n",
+ pseudo_dir_fd, strerror(errno));
+ }
+ /* close any higher-numbered fds which might be open,
+ * such as sockets. We don't have to worry about 0 and 1;
+ * the server closes them already, and more importantly,
+ * they can't have been opened or closed without us already
+ * having spawned a server... The issue is just socket()
+ * calls which could result in fds being left open, and those
+ * can't overwrite fds 0-2 unless we closed them...
+ *
+ * No, really. It works.
+ */
+ for (fd = 3; fd < 1024; ++fd) {
+ if (fd != pseudo_util_debug_fd)
+ close(fd);
+ }
+ execve(argv[0], argv, environ);
+ pseudo_diag("critical failure: exec of pseudo daemon failed: %s\n", strerror(errno));
+ exit(1);
+ }
+}
+
+static int
+client_ping(void) {
+ pseudo_msg_t ping;
+ pseudo_msg_t *ack;
+ char *tag = getenv("PSEUDO_TAG");
+
+ ping.type = PSEUDO_MSG_PING;
+ ping.op = OP_NONE;
+ if (tag) {
+ ping.pathlen = strlen(tag);
+ } else {
+ ping.pathlen = 0;
+ }
+ ping.client = getpid();
+ ping.result = 0;
+ errno = 0;
+ pseudo_debug(4, "sending ping\n");
+ if (pseudo_msg_send(connect_fd, &ping, ping.pathlen, tag)) {
+ pseudo_debug(3, "error pinging server: errno %d\n", errno);
+ return 1;
+ }
+ ack = pseudo_msg_receive(connect_fd);
+ if (!ack) {
+ pseudo_debug(2, "no ping response from server: %s\n", strerror(errno));
+ /* and that's not good, so... */
+ server_pid = 0;
+ return 1;
+ }
+ if (ack->type != PSEUDO_MSG_ACK) {
+ pseudo_debug(1, "invalid ping response from server: expected ack, got %d\n", ack->type);
+ /* and that's not good, so... */
+ server_pid = 0;
+ return 1;
+ }
+ pseudo_debug(5, "ping ok\n");
+ return 0;
+}
+
+int
+pseudo_fd(int fd, int how) {
+ int newfd;
+
+ if (fd < 0)
+ return(-1);
+
+ /* If already above PSEUDO_MIN_FD, no need to move */
+ if ((how == MOVE_FD) && (fd >= PSEUDO_MIN_FD)) {
+ newfd = fd;
+ } else {
+ newfd = fcntl(fd, F_DUPFD, PSEUDO_MIN_FD);
+
+ if (how == MOVE_FD)
+ close(fd);
+ }
+
+ /* Set close on exec, even if we didn't move it. */
+ if ((newfd >= 0) && (fcntl(newfd, F_SETFD, FD_CLOEXEC) < 0))
+ pseudo_diag("can't set close on exec flag: %s\n",
+ strerror(errno));
+
+ return(newfd);
+}
+
+static int
+client_connect(void) {
+ /* we have a server pid, is it responsive? */
+ struct sockaddr_un sun = { AF_UNIX, PSEUDO_SOCKET };
+ int cwd_fd;
+
+ connect_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ connect_fd = pseudo_fd(connect_fd, MOVE_FD);
+ if (connect_fd == -1) {
+ pseudo_diag("can't create socket: %s\n", strerror(errno));
+ return 1;
+ }
+
+ pseudo_debug(3, "connecting socket...\n");
+ cwd_fd = open(".", O_RDONLY);
+ if (cwd_fd == -1) {
+ pseudo_diag("Couldn't stash directory before opening socket: %s",
+ strerror(errno));
+ close(connect_fd);
+ connect_fd = -1;
+ return 1;
+ }
+ if (fchdir(pseudo_dir_fd) == -1) {
+ pseudo_diag("Couldn't chdir to server directory [%d]: %s\n",
+ pseudo_dir_fd, strerror(errno));
+ close(connect_fd);
+ close(cwd_fd);
+ connect_fd = -1;
+ return 1;
+ }
+ if (connect(connect_fd, (struct sockaddr *) &sun, sizeof(sun)) == -1) {
+ pseudo_debug(3, "can't connect socket to pseudo.socket: (%s)\n", strerror(errno));
+ close(connect_fd);
+ if (fchdir(cwd_fd) == -1) {
+ pseudo_diag("return to previous directory failed: %s\n",
+ strerror(errno));
+ }
+ close(cwd_fd);
+ connect_fd = -1;
+ return 1;
+ }
+ if (fchdir(cwd_fd) == -1) {
+ pseudo_diag("return to previous directory failed: %s\n",
+ strerror(errno));
+ }
+ close(cwd_fd);
+ pseudo_debug(4, "connected socket.\n");
+ return 0;
+}
+
+static int
+pseudo_client_setup(void) {
+ FILE *fp;
+ server_pid = 0;
+ int cwd_fd;
+
+ /* avoid descriptor leak, I hope */
+ if (connect_fd >= 0) {
+ close(connect_fd);
+ connect_fd = -1;
+ }
+
+ cwd_fd = open(".", O_RDONLY);
+ if (cwd_fd == -1) {
+ pseudo_diag("Couldn't stash directory before opening pidfile: %s",
+ strerror(errno));
+ close(connect_fd);
+ connect_fd = -1;
+ return 1;
+ }
+ if (fchdir(pseudo_dir_fd) != 1) {
+ fp = fopen(PSEUDO_PIDFILE, "r");
+ if (fchdir(cwd_fd) == -1) {
+ pseudo_diag("return to previous directory failed: %s\n",
+ strerror(errno));
+ }
+ } else {
+ fp = NULL;
+ pseudo_diag("couldn't change to pseudo working directory for pid file.\n");
+ }
+ close(cwd_fd);
+ if (fp) {
+ if (fscanf(fp, "%d", &server_pid) != 1) {
+ pseudo_debug(1, "Opened server PID file, but didn't get a pid.\n");
+ }
+ fclose(fp);
+ }
+ if (server_pid) {
+ if (kill(server_pid, 0) == -1) {
+ pseudo_debug(1, "couldn't find server at pid %d: %s\n",
+ server_pid, strerror(errno));
+ server_pid = 0;
+ }
+ }
+ if (!server_pid) {
+ if (client_spawn_server()) {
+ return 1;
+ }
+ }
+ if (!client_connect() && !client_ping()) {
+ return 0;
+ }
+ pseudo_debug(2, "server seems to be gone, trying to restart\n");
+ if (client_spawn_server()) {
+ pseudo_debug(1, "failed to spawn server, giving up.\n");
+ return 1;
+ } else {
+ pseudo_debug_verbose();
+ pseudo_debug(2, "restarted, new pid %d\n", server_pid);
+ if (!client_connect() && !client_ping()) {
+ pseudo_debug_terse();
+ return 0;
+ }
+ pseudo_debug_terse();
+ }
+ pseudo_debug(1, "couldn't get a server, giving up.\n");
+ return 1;
+}
+
+static pseudo_msg_t *
+pseudo_client_request(pseudo_msg_t *msg, size_t len, const char *path) {
+ pseudo_msg_t *response = 0;
+ int tries = 0;
+ int rc;
+
+ if (!msg)
+ return 0;
+
+ do {
+ do {
+ pseudo_debug(4, "sending a message: ino %llu\n",
+ (unsigned long long) msg->ino);
+ if (connect_fd < 0) {
+ pseudo_debug(2, "trying to get server\n");
+ if (pseudo_client_setup()) {
+ return 0;
+ }
+ }
+ rc = pseudo_msg_send(connect_fd, msg, len, path);
+ if (rc != 0) {
+ pseudo_debug(2, "msg_send: %d%s\n",
+ rc,
+ rc == -1 ? " (sigpipe)" :
+ " (short write)");
+ pseudo_client_setup();
+ ++tries;
+ if (tries > 3) {
+ pseudo_debug(1, "can't get server going again.\n");
+ return 0;
+ }
+ }
+ } while (rc != 0);
+ pseudo_debug(5, "sent!\n");
+ response = pseudo_msg_receive(connect_fd);
+ if (!response) {
+ ++tries;
+ if (tries > 3) {
+ pseudo_debug(1, "can't get responses.\n");
+ return 0;
+ }
+ }
+ } while (response == 0);
+ if (response->type != PSEUDO_MSG_ACK) {
+ pseudo_debug(2, "got non-ack response %d\n", response->type);
+ return 0;
+ } else {
+ pseudo_debug(4, "got response type %d\n", response->type);
+ }
+ return response;
+}
+
+int
+pseudo_client_shutdown(void) {
+ pseudo_msg_t msg;
+ pseudo_msg_t *ack;
+ char *pseudo_path;
+
+ pseudo_path = pseudo_prefix_path(NULL);
+ if (pseudo_dir_fd == -1) {
+ if (pseudo_path) {
+ pseudo_dir_fd = open(pseudo_path, O_RDONLY);
+ pseudo_dir_fd = pseudo_fd(pseudo_dir_fd, COPY_FD);
+ free(pseudo_path);
+ } else {
+ pseudo_diag("No prefix available to to find server.\n");
+ exit(1);
+ }
+ if (pseudo_dir_fd == -1) {
+ pseudo_diag("Can't open prefix path (%s) for server.\n",
+ strerror(errno));
+ exit(1);
+ }
+ }
+ if (client_connect()) {
+ pseudo_diag("Pseudo server seems to be already offline.\n");
+ return 0;
+ }
+ memset(&msg, 0, sizeof(pseudo_msg_t));
+ msg.type = PSEUDO_MSG_SHUTDOWN;
+ msg.op = OP_NONE;
+ msg.client = getpid();
+ pseudo_debug(2, "sending shutdown request\n");
+ if (pseudo_msg_send(connect_fd, &msg, 0, NULL)) {
+ pseudo_debug(1, "error requesting shutdown: errno %d\n", errno);
+ return 1;
+ }
+ ack = pseudo_msg_receive(connect_fd);
+ if (!ack) {
+ pseudo_diag("server did not respond to shutdown query.\n");
+ return 1;
+ }
+ if (ack->type == PSEUDO_MSG_ACK) {
+ return 0;
+ }
+ pseudo_diag("Server refused shutdown. Remaining client fds: %d\n", ack->fd);
+ pseudo_diag("Client pids: %s\n", ack->path);
+ pseudo_diag("Server will shut down after all clients exit.\n");
+ return 0;
+}
+
+static char *
+base_path(int dirfd, const char *path, int leave_last) {
+ char *basepath = 0;
+ size_t pathlen = -1;
+ size_t baselen;
+ char *newpath;
+
+ if (dirfd != -1 && dirfd != AT_FDCWD) {
+ if (dirfd >= 0) {
+ basepath = fd_path(dirfd);
+ baselen = strlen(basepath);
+ } else {
+ pseudo_diag("got *at() syscall for unknown directory, fd %d\n", dirfd);
+ }
+ } else {
+ basepath = cwd;
+ baselen = cwdlen;
+ }
+ if (!basepath) {
+ pseudo_diag("unknown base path for fd %d, path %s\n", dirfd, path);
+ return 0;
+ }
+
+ pathlen = baselen + strlen(path) + 2;
+ newpath = pseudo_fix_path(basepath, path, baselen, NULL, leave_last);
+ return newpath;
+}
+
+pseudo_msg_t *
+pseudo_client_op(op_id_t op, int flags, int fd, int dirfd, const char *path, const struct stat64 *buf, ...) {
+ pseudo_msg_t *result = 0;
+ pseudo_msg_t msg = { .type = PSEUDO_MSG_OP };
+ char *newpath = 0;
+ size_t pathlen = -1;
+ int do_request = 0;
+ char *oldpath = 0;
+
+ /* disable wrappers */
+ pseudo_antimagic();
+
+ if (op == OP_RENAME) {
+ va_list ap;
+ va_start(ap, buf);
+ oldpath = va_arg(ap, char *);
+ va_end(ap);
+ /* last argument is the previous path of the file */
+ if (!oldpath) {
+ pseudo_diag("rename (%s) without old path.\n",
+ path ? path : "<nil>");
+ pseudo_magic();
+ return 0;
+ }
+ if (!path) {
+ pseudo_diag("rename (%s) without new path.\n",
+ path ? path : "<nil>");
+ pseudo_magic();
+ return 0;
+ }
+ if (oldpath[0] != '/') {
+ oldpath = base_path(dirfd, oldpath, 1);
+ } else {
+ oldpath = pseudo_fix_path(NULL, oldpath, 0, NULL, 1);
+ }
+ }
+
+ if (path) {
+ /* fixup relative path */
+ if (*path != '/') {
+ newpath = base_path(dirfd, path, flags);
+ } else {
+ newpath = pseudo_fix_path(NULL, path, 0, NULL, flags);
+ }
+ if (newpath) {
+ pathlen = strlen(newpath) + 1;
+ } else {
+ pseudo_diag("couldn't allocate space for a path (%s). Sorry.\n", path);
+ free(oldpath);
+ pseudo_magic();
+ return 0;
+ }
+ if (oldpath) {
+ size_t full_len = strlen(oldpath) + 1 + pathlen;
+ char *both_paths = malloc(full_len);
+ if (!both_paths) {
+ pseudo_diag("can't allocate space for paths for a rename operation. Sorry.\n");
+ free(newpath);
+ free(oldpath);
+ pseudo_magic();
+ return 0;
+ }
+ snprintf(both_paths, full_len, "%s%c%s",
+ newpath, 0, oldpath);
+ pseudo_debug(2, "rename: %s -> %s [%d]\n",
+ both_paths + pathlen, both_paths, (int) full_len);
+ free(newpath);
+ newpath = both_paths;
+ pathlen = full_len;
+ }
+ } else if (fd >= 0 && fd <= nfds) {
+ path = fd_path(fd);
+ if (!path)
+ msg.pathlen = 0;
+ else
+ msg.pathlen = strlen(path) + 1;
+ } else {
+ path = 0;
+ msg.pathlen = 0;
+ }
+ pseudo_debug(2, "%s%s", pseudo_op_name(op),
+ (dirfd != -1 && dirfd != AT_FDCWD && op != OP_DUP) ? "at" : "");
+ if (oldpath) {
+ pseudo_debug(2, " %s ->", (char *) oldpath);
+ free(oldpath);
+ }
+ if (newpath || path) {
+ pseudo_debug(2, " %s", newpath ? newpath : path);
+ }
+ if (fd != -1) {
+ pseudo_debug(2, " [fd %d]", fd);
+ }
+ if (buf) {
+ pseudo_debug(2, " (+buf)");
+ pseudo_msg_stat(&msg, buf);
+ if (buf && fd != -1) {
+ pseudo_debug(2, " [dev/ino: %d/%llu]",
+ (int) buf->st_dev, (unsigned long long) buf->st_ino);
+ }
+ pseudo_debug(2, " (0%o)", (int) buf->st_mode);
+ }
+ pseudo_debug(2, ": ");
+ msg.type = PSEUDO_MSG_OP;
+ msg.op = op;
+ msg.fd = fd;
+ msg.result = RESULT_NONE;
+ msg.client = getpid();
+
+ /* do stuff */
+ pseudo_debug(4, "processing request [ino %llu]\n", (unsigned long long) msg.ino);
+ switch (msg.op) {
+ case OP_CHDIR:
+ cwd = getcwd(cwdbuf, PATH_MAX);
+ cwdlen = strlen(cwd);
+ do_request = 0;
+ break;
+ case OP_OPEN:
+ pseudo_client_path(fd, newpath ? newpath : path);
+ do_request = 1;
+ break;
+ case OP_CLOSE:
+ /* no request needed */
+ if (fd >= 0) {
+ if (fd == connect_fd) {
+ connect_fd = pseudo_fd(connect_fd, COPY_FD);
+ if (connect_fd == -1) {
+ pseudo_diag("tried to close connection, couldn't dup: errno %d\n", errno);
+ }
+ } else if (fd == pseudo_util_debug_fd) {
+ pseudo_util_debug_fd = pseudo_fd(fd, COPY_FD);
+ } else if (fd == pseudo_dir_fd) {
+ pseudo_dir_fd = pseudo_fd(fd, COPY_FD);
+ }
+ }
+ pseudo_client_close(fd);
+ do_request = 0;
+ break;
+ case OP_DUP:
+ /* just copy the path over */
+ pseudo_debug(2, "dup: fd_path(%d) = %p [%s], dup to %d\n",
+ fd, fd_path(fd), fd_path(fd) ? fd_path(fd) : "<nil>",
+ dirfd);
+ pseudo_client_path(dirfd, fd_path(fd));
+ break;
+ /* operations for which we should use the magic uid/gid */
+ case OP_CHMOD:
+ case OP_CREAT:
+ case OP_FCHMOD:
+ case OP_MKDIR:
+ case OP_MKNOD:
+ case OP_SYMLINK:
+ msg.uid = pseudo_fuid;
+ msg.gid = pseudo_fgid;
+ pseudo_debug(2, "fuid: %d ", pseudo_fuid);
+ /* fallthrough. chown/fchown uid/gid already calculated, and
+ * a link or rename should not change a file's ownership.
+ * (operations which can create should be CREAT or MKNOD
+ * or MKDIR)
+ */
+ case OP_LINK:
+ case OP_CHOWN:
+ case OP_FCHOWN:
+ case OP_FSTAT:
+ case OP_RENAME:
+ case OP_STAT:
+ case OP_UNLINK:
+ do_request = 1;
+ break;
+ default:
+ pseudo_diag("error: unknown or unimplemented operator %d (%s)", op, pseudo_op_name(op));
+ break;
+ }
+ if (do_request) {
+ struct timeval tv1, tv2;
+ pseudo_debug(4, "sending request [ino %llu]\n", (unsigned long long) msg.ino);
+ gettimeofday(&tv1, NULL);
+ result = pseudo_client_request(&msg, pathlen, newpath ? newpath : path);
+ gettimeofday(&tv2, NULL);
+ ++messages;
+ message_time.tv_sec += (tv2.tv_sec - tv1.tv_sec);
+ message_time.tv_usec += (tv2.tv_usec - tv1.tv_usec);
+ if (message_time.tv_usec < 0) {
+ message_time.tv_usec += 1000000;
+ --message_time.tv_sec;
+ } else while (message_time.tv_usec > 1000000) {
+ message_time.tv_usec -= 1000000;
+ ++message_time.tv_sec;
+ }
+ if (result) {
+ pseudo_debug(2, "(%d) %s", getpid(), pseudo_res_name(result->result));
+ if (op == OP_STAT || op == OP_FSTAT) {
+ pseudo_debug(2, " mode 0%o uid %d:%d",
+ (int) result->mode,
+ (int) result->uid,
+ (int) result->gid);
+ } else if (op == OP_CHMOD || op == OP_FCHMOD) {
+ pseudo_debug(2, " mode 0%o",
+ (int) result->mode);
+ } else if (op == OP_CHOWN || op == OP_FCHOWN) {
+ pseudo_debug(2, " uid %d:%d",
+ (int) result->uid,
+ (int) result->gid);
+ }
+ } else {
+ pseudo_debug(2, "(%d) no answer", getpid());
+ }
+ } else {
+ pseudo_debug(2, "(%d) (no request)", getpid());
+ }
+ pseudo_debug(2, "\n");
+
+ free(newpath);
+
+ if (do_request && (messages % 1000 == 0)) {
+ pseudo_debug(2, "%d messages handled in %.4f seconds\n",
+ messages,
+ (double) message_time.tv_sec +
+ (double) message_time.tv_usec / 1000000.0);
+ }
+ /* reenable wrappers */
+ pseudo_magic();
+
+ return result;
+}
+
+void
+pseudo_stat32_from64(struct stat *buf32, struct stat64 *buf) {
+ buf32->st_dev = buf->st_dev;
+ buf32->st_ino = buf->st_ino;
+ buf32->st_mode = buf->st_mode;
+ buf32->st_nlink = buf->st_nlink;
+ buf32->st_uid = buf->st_uid;
+ buf32->st_gid = buf->st_gid;
+ buf32->st_rdev = buf->st_rdev;
+ buf32->st_size = buf->st_size;
+ buf32->st_blksize = buf->st_blksize;
+ buf32->st_blocks = buf->st_blocks;
+ buf32->st_atime = buf->st_atime;
+ buf32->st_mtime = buf->st_mtime;
+ buf32->st_ctime = buf->st_ctime;
+}
+
+void
+pseudo_stat64_from32(struct stat64 *buf64, struct stat *buf) {
+ buf64->st_dev = buf->st_dev;
+ buf64->st_ino = buf->st_ino;
+ buf64->st_mode = buf->st_mode;
+ buf64->st_nlink = buf->st_nlink;
+ buf64->st_uid = buf->st_uid;
+ buf64->st_gid = buf->st_gid;
+ buf64->st_rdev = buf->st_rdev;
+ buf64->st_size = buf->st_size;
+ buf64->st_blksize = buf->st_blksize;
+ buf64->st_blocks = buf->st_blocks;
+ buf64->st_atime = buf->st_atime;
+ buf64->st_mtime = buf->st_mtime;
+ buf64->st_ctime = buf->st_ctime;
+}