/* * Signal that we are now inside a directory pointed to by dir_fd. * The caller can't tell if this is the first time this happens, so * we have to be careful not to call dup() more than once */ static void inside_dir (int dir_fd) { if (ftsoptions & FTS_CWDFD) { assert (dir_fd == AT_FDCWD || dir_fd >= 0); state.cwd_dir_fd = dir_fd; if (curr_fd < 0) { if (AT_FDCWD == dir_fd) { curr_fd = AT_FDCWD; } else if (dir_fd >= 0) { curr_fd = dup_cloexec (dir_fd); } else { /* curr_fd is invalid, but dir_fd is also invalid. * This should not have happened. */ assert (curr_fd >= 0 || dir_fd >= 0); } } } else { /* FTS_CWDFD is not in use. We can always assume that * AT_FDCWD refers to the directory we are currentl searching. * * Therefore there is nothing to do. */ } }
int main (void) { const char *file = "test-dup2.tmp"; char buffer[1]; int fd = open (file, O_CREAT | O_TRUNC | O_RDWR, 0600); /* Assume std descriptors were provided by invoker. */ ASSERT (STDERR_FILENO < fd); ASSERT (is_open (fd)); /* Ignore any other fd's leaked into this process. */ close (fd + 1); close (fd + 2); ASSERT (!is_open (fd + 1)); ASSERT (!is_open (fd + 2)); /* Assigning to self must be a no-op. */ ASSERT (dup2 (fd, fd) == fd); ASSERT (is_open (fd)); /* The source must be valid. */ errno = 0; ASSERT (dup2 (-1, fd) == -1); ASSERT (errno == EBADF); errno = 0; ASSERT (dup2 (99, fd) == -1); ASSERT (errno == EBADF); errno = 0; ASSERT (dup2 (AT_FDCWD, fd) == -1); ASSERT (errno == EBADF); ASSERT (is_open (fd)); /* If the source is not open, then the destination is unaffected. */ errno = 0; ASSERT (dup2 (fd + 1, fd + 1) == -1); ASSERT (errno == EBADF); ASSERT (!is_open (fd + 1)); errno = 0; ASSERT (dup2 (fd + 1, fd) == -1); ASSERT (errno == EBADF); ASSERT (is_open (fd)); /* The destination must be valid. */ errno = 0; ASSERT (dup2 (fd, -2) == -1); ASSERT (errno == EBADF); errno = 0; ASSERT (dup2 (fd, 10000000) == -1); ASSERT (errno == EBADF); /* Using dup2 can skip fds. */ ASSERT (dup2 (fd, fd + 2) == fd + 2); ASSERT (is_open (fd)); ASSERT (!is_open (fd + 1)); ASSERT (is_open (fd + 2)); /* Verify that dup2 closes the previous occupant of a fd. */ ASSERT (open ("/dev/null", O_WRONLY, 0600) == fd + 1); ASSERT (dup2 (fd + 1, fd) == fd); ASSERT (close (fd + 1) == 0); ASSERT (write (fd, "1", 1) == 1); ASSERT (dup2 (fd + 2, fd) == fd); ASSERT (lseek (fd, 0, SEEK_END) == 0); ASSERT (write (fd + 2, "2", 1) == 1); ASSERT (lseek (fd, 0, SEEK_SET) == 0); ASSERT (read (fd, buffer, 1) == 1); ASSERT (*buffer == '2'); #if GNULIB_TEST_CLOEXEC /* Any new fd created by dup2 must not be cloexec. */ ASSERT (close (fd + 2) == 0); ASSERT (dup_cloexec (fd) == fd + 1); ASSERT (!is_inheritable (fd + 1)); ASSERT (dup2 (fd + 1, fd + 1) == fd + 1); ASSERT (!is_inheritable (fd + 1)); ASSERT (dup2 (fd + 1, fd + 2) == fd + 2); ASSERT (!is_inheritable (fd + 1)); ASSERT (is_inheritable (fd + 2)); errno = 0; ASSERT (dup2 (fd + 1, -1) == -1); ASSERT (errno == EBADF); ASSERT (!is_inheritable (fd + 1)); #endif /* On systems that distinguish between text and binary mode, dup2 reuses the mode of the source. */ setmode (fd, O_BINARY); ASSERT (is_mode (fd, O_BINARY)); ASSERT (dup2 (fd, fd + 1) == fd + 1); ASSERT (is_mode (fd + 1, O_BINARY)); setmode (fd, O_TEXT); ASSERT (is_mode (fd, O_TEXT)); ASSERT (dup2 (fd, fd + 1) == fd + 1); ASSERT (is_mode (fd + 1, O_TEXT)); /* Clean up. */ ASSERT (close (fd + 2) == 0); ASSERT (close (fd + 1) == 0); ASSERT (close (fd) == 0); ASSERT (unlink (file) == 0); return 0; }
/* 'fd_cwd' is a file descriptor pointing to an open directory. * 'name' is the path element to search for. 'is_end' is a flag * indicating if this is the last path element. * * We search the directory looking for a path element that case * insensitively matches 'name', returning the actual name in '*name_ret'. * * If this is successful, return 0. If it fails, reply with an error * and return -1. */ static int find_path_element (int fd_cwd, int is_end, const char *name, char **name_ret) { int fd2; DIR *dir; struct dirent *d; fd2 = dup_cloexec (fd_cwd); /* because closedir will close it */ if (fd2 == -1) { reply_with_perror ("dup"); return -1; } dir = fdopendir (fd2); if (dir == NULL) { reply_with_perror ("opendir"); close (fd2); return -1; } for (;;) { errno = 0; d = readdir (dir); if (d == NULL) break; if (STRCASEEQ (d->d_name, name)) break; } if (d == NULL && errno != 0) { reply_with_perror ("readdir"); closedir (dir); return -1; } if (d == NULL && is_end) { /* Last path element: return it as-is, assuming that the user will * create a new file or directory (RHBZ#840115). */ closedir (dir); *name_ret = strdup (name); if (*name_ret == NULL) { reply_with_perror ("strdup"); return -1; } return 0; } if (d == NULL) { reply_with_error ("%s: no file or directory found with this name", name); closedir (dir); return -1; } *name_ret = strdup (d->d_name); if (*name_ret == NULL) { reply_with_perror ("strdup"); closedir (dir); return -1; } /* NB: closedir frees the structure associated with 'd', so we must * do this last. */ if (closedir (dir) == -1) { reply_with_perror ("closedir"); return -1; } return 0; }
int main (void) { const char *file = "test-cloexec.tmp"; int fd = creat (file, 0600); int fd2; /* Assume std descriptors were provided by invoker. */ ASSERT (STDERR_FILENO < fd); ASSERT (is_inheritable (fd)); /* Normal use of set_cloexec_flag. */ ASSERT (set_cloexec_flag (fd, true) == 0); #if !((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__) ASSERT (!is_inheritable (fd)); #endif ASSERT (set_cloexec_flag (fd, false) == 0); ASSERT (is_inheritable (fd)); /* Normal use of dup_cloexec. */ fd2 = dup_cloexec (fd); ASSERT (fd < fd2); ASSERT (!is_inheritable (fd2)); ASSERT (close (fd) == 0); ASSERT (dup_cloexec (fd2) == fd); ASSERT (!is_inheritable (fd)); ASSERT (close (fd2) == 0); /* On systems that distinguish between text and binary mode, dup_cloexec reuses the mode of the source. */ setmode (fd, O_BINARY); ASSERT (is_mode (fd, O_BINARY)); fd2 = dup_cloexec (fd); ASSERT (fd < fd2); ASSERT (is_mode (fd2, O_BINARY)); ASSERT (close (fd2) == 0); setmode (fd, O_TEXT); ASSERT (is_mode (fd, O_TEXT)); fd2 = dup_cloexec (fd); ASSERT (fd < fd2); ASSERT (is_mode (fd2, O_TEXT)); ASSERT (close (fd2) == 0); /* Test error handling. */ errno = 0; ASSERT (set_cloexec_flag (-1, false) == -1); ASSERT (errno == EBADF); errno = 0; ASSERT (set_cloexec_flag (10000000, false) == -1); ASSERT (errno == EBADF); errno = 0; ASSERT (set_cloexec_flag (fd2, false) == -1); ASSERT (errno == EBADF); errno = 0; ASSERT (dup_cloexec (-1) == -1); ASSERT (errno == EBADF); errno = 0; ASSERT (dup_cloexec (10000000) == -1); ASSERT (errno == EBADF); errno = 0; ASSERT (dup_cloexec (fd2) == -1); ASSERT (errno == EBADF); /* Clean up. */ ASSERT (close (fd) == 0); ASSERT (unlink (file) == 0); return 0; }
int main (void) { int i; int fd; /* We close fd 2 later, so save it in fd 10. */ if (dup2 (STDERR_FILENO, BACKUP_STDERR_FILENO) != BACKUP_STDERR_FILENO || (myerr = fdopen (BACKUP_STDERR_FILENO, "w")) == NULL) return 2; /* Create file for later checks. */ fd = creat (witness, 0600); ASSERT (STDERR_FILENO < fd); /* Four iterations, with progressively more standard descriptors closed. */ for (i = -1; i <= STDERR_FILENO; i++) { if (0 <= i) ASSERT (close (i) == 0); /* Detect errors. */ errno = 0; ASSERT (dup (-1) == -1); ASSERT (errno == EBADF); errno = 0; ASSERT (dup (10000000) == -1); ASSERT (errno == EBADF); close (fd + 1); errno = 0; ASSERT (dup (fd + 1) == -1); ASSERT (errno == EBADF); /* Preserve text vs. binary. */ setmode (fd, O_BINARY); ASSERT (dup (fd) == fd + 1); ASSERT (is_open (fd + 1)); ASSERT (is_inheritable (fd + 1)); ASSERT (is_mode (fd + 1, O_BINARY)); ASSERT (close (fd + 1) == 0); setmode (fd, O_TEXT); ASSERT (dup (fd) == fd + 1); ASSERT (is_open (fd + 1)); ASSERT (is_inheritable (fd + 1)); ASSERT (is_mode (fd + 1, O_TEXT)); /* Create cloexec copy. */ ASSERT (close (fd + 1) == 0); ASSERT (fd_safer_flag (dup_cloexec (fd), O_CLOEXEC) == fd + 1); ASSERT (set_cloexec_flag (fd + 1, true) == 0); ASSERT (is_open (fd + 1)); ASSERT (!is_inheritable (fd + 1)); ASSERT (close (fd) == 0); /* dup always creates inheritable copies. Also, check that earliest slot past std fds is used. */ ASSERT (dup (fd + 1) == fd); ASSERT (is_open (fd)); ASSERT (is_inheritable (fd)); ASSERT (close (fd + 1) == 0); } /* Cleanup. */ ASSERT (close (fd) == 0); ASSERT (unlink (witness) == 0); return 0; }
static void run_helper(libxl__egc *egc, libxl__save_helper_state *shs, const char *mode_arg, int stream_fd, int back_channel_fd, const int *preserve_fds, int num_preserve_fds, const unsigned long *argnums, int num_argnums) { STATE_AO_GC(shs->ao); const char *args[HELPER_NR_ARGS + num_argnums]; const char **arg = args; int i, rc; /* Resources we must free */ libxl__carefd *childs_pipes[2] = { 0,0 }; /* Convenience aliases */ const uint32_t domid = shs->domid; shs->rc = 0; shs->completed = 0; shs->pipes[0] = shs->pipes[1] = 0; libxl__save_helper_init(shs); shs->abrt.ao = shs->ao; shs->abrt.callback = helper_stop; rc = libxl__ao_abortable_register(&shs->abrt); if (rc) goto out; shs->stdin_what = GCSPRINTF("domain %"PRIu32" save/restore helper" " stdin pipe", domid); shs->stdout_what = GCSPRINTF("domain %"PRIu32" save/restore helper" " stdout pipe", domid); *arg++ = getenv("LIBXL_SAVE_HELPER") ?: LIBEXEC_BIN "/" "libxl-save-helper"; *arg++ = mode_arg; const char **stream_fd_arg = arg++; const char **back_channel_fd_arg = arg++; for (i=0; i<num_argnums; i++) *arg++ = GCSPRINTF("%lu", argnums[i]); *arg++ = 0; assert(arg == args + ARRAY_SIZE(args)); libxl__carefd_begin(); int childfd; for (childfd=0; childfd<2; childfd++) { /* Setting up the pipe for the child's fd childfd */ int fds[2]; if (libxl_pipe(CTX,fds)) { rc = ERROR_FAIL; libxl__carefd_unlock(); goto out; } int childs_end = childfd==0 ? 0 /*read*/ : 1 /*write*/; int our_end = childfd==0 ? 1 /*write*/ : 0 /*read*/; childs_pipes[childfd] = libxl__carefd_record(CTX, fds[childs_end]); shs->pipes[childfd] = libxl__carefd_record(CTX, fds[our_end]); } libxl__carefd_unlock(); pid_t pid = libxl__ev_child_fork(gc, &shs->child, helper_exited); if (!pid) { stream_fd = dup_cloexec(gc, stream_fd, "migration stream fd"); *stream_fd_arg = GCSPRINTF("%d", stream_fd); if (back_channel_fd >= 0) back_channel_fd = dup_cloexec(gc, back_channel_fd, "migration back channel fd"); *back_channel_fd_arg = GCSPRINTF("%d", back_channel_fd); for (i=0; i<num_preserve_fds; i++) if (preserve_fds[i] >= 0) { assert(preserve_fds[i] > 2); libxl_fd_set_cloexec(CTX, preserve_fds[i], 0); } libxl__exec(gc, libxl__carefd_fd(childs_pipes[0]), libxl__carefd_fd(childs_pipes[1]), -1, args[0], (char**)args, 0); } libxl__carefd_close(childs_pipes[0]); libxl__carefd_close(childs_pipes[1]); rc = libxl__ev_fd_register(gc, &shs->readable, helper_stdout_readable, libxl__carefd_fd(shs->pipes[1]), POLLIN|POLLPRI); if (rc) goto out; return; out: libxl__carefd_close(childs_pipes[0]); libxl__carefd_close(childs_pipes[1]); helper_failed(egc, shs, rc);; }