/* * Copyright (C) 2012 Sami Kerola */ #include #include #include #include #include #include #include "c.h" #include "fileutils.h" #include "pathnames.h" int mkstemp_cloexec(char *template) { #ifdef HAVE_MKOSTEMP return mkostemp(template, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC); #else int fd, old_flags, errno_save; fd = mkstemp(template); if (fd < 0) return fd; old_flags = fcntl(fd, F_GETFD, 0); if (old_flags < 0) goto unwind; if (fcntl(fd, F_SETFD, old_flags | O_CLOEXEC) < 0) goto unwind; return fd; unwind: errno_save = errno; unlink(template); close(fd); errno = errno_save; return -1; #endif } /* Create open temporary file in safe way. Please notice that the * file permissions are -rw------- by default. */ int xmkstemp(char **tmpname, const char *dir, const char *prefix) { char *localtmp; const char *tmpenv; mode_t old_mode; int fd, rc; /* Some use cases must be capable of being moved atomically * with rename(2), which is the reason why dir is here. */ tmpenv = dir ? dir : getenv("TMPDIR"); if (!tmpenv) tmpenv = _PATH_TMP; rc = asprintf(&localtmp, "%s/%s.XXXXXX", tmpenv, prefix); if (rc < 0) return -1; old_mode = umask(077); fd = mkstemp_cloexec(localtmp); umask(old_mode); if (fd == -1) { free(localtmp); localtmp = NULL; } *tmpname = localtmp; return fd; } int dup_fd_cloexec(int oldfd, int lowfd) { int fd, flags, errno_save; #ifdef F_DUPFD_CLOEXEC fd = fcntl(oldfd, F_DUPFD_CLOEXEC, lowfd); if (fd >= 0) return fd; #endif fd = dup(oldfd); if (fd < 0) return fd; flags = fcntl(fd, F_GETFD); if (flags < 0) goto unwind; if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) goto unwind; return fd; unwind: errno_save = errno; close(fd); errno = errno_save; return -1; } /* * portable getdtablesize() */ int get_fd_tabsize(void) { int m; #if defined(HAVE_GETDTABLESIZE) m = getdtablesize(); #elif defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) struct rlimit rl; getrlimit(RLIMIT_NOFILE, &rl); m = rl.rlim_cur; #elif defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX) m = sysconf(_SC_OPEN_MAX); #else m = OPEN_MAX; #endif return m; } static inline int in_set(int x, const int set[], size_t setsz) { size_t i; for (i = 0; i < setsz; i++) { if (set[i] == x) return 1; } return 0; } void close_all_fds(const int exclude[], size_t exsz) { struct dirent *d; DIR *dir; dir = opendir(_PATH_PROC_FDDIR); if (dir) { while ((d = xreaddir(dir))) { char *end; int fd; errno = 0; fd = strtol(d->d_name, &end, 10); if (errno || end == d->d_name || !end || *end) continue; if (dirfd(dir) == fd) continue; if (in_set(fd, exclude, exsz)) continue; close(fd); } closedir(dir); } else { int fd, tbsz = get_fd_tabsize(); for (fd = 0; fd < tbsz; fd++) { if (!in_set(fd, exclude, exsz)) close(fd); } } } #ifdef TEST_PROGRAM_FILEUTILS int main(int argc, char *argv[]) { if (argc < 2) errx(EXIT_FAILURE, "Usage %s --{mkstemp,close-fds}", argv[0]); if (strcmp(argv[1], "--mkstemp") == 0) { FILE *f; char *tmpname; f = xfmkstemp(&tmpname, NULL, "test"); unlink(tmpname); free(tmpname); fclose(f); } else if (strcmp(argv[1], "--close-fds") == 0) { static const int wanted_fds[] = { STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO }; ignore_result( dup(STDIN_FILENO) ); ignore_result( dup(STDIN_FILENO) ); ignore_result( dup(STDIN_FILENO) ); close_all_fds(wanted_fds, ARRAY_SIZE(wanted_fds)); } return EXIT_SUCCESS; } #endif int mkdir_p(const char *path, mode_t mode) { char *p, *dir; int rc = 0; if (!path || !*path) return -EINVAL; dir = p = strdup(path); if (!dir) return -ENOMEM; if (*p == '/') p++; while (p && *p) { char *e = strchr(p, '/'); if (e) *e = '\0'; if (*p) { rc = mkdir(dir, mode); if (rc && errno != EEXIST) break; rc = 0; } if (!e) break; *e = '/'; p = e + 1; } free(dir); return rc; } /* returns basename and keeps dirname in the @path, if @path is "/" (root) * then returns empty string */ char *stripoff_last_component(char *path) { char *p = path ? strrchr(path, '/') : NULL; if (!p) return NULL; *p = '\0'; return p + 1; }