diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 14:30:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 14:30:35 +0000 |
commit | 378c18e5f024ac5a8aef4cb40d7c9aa9633d144c (patch) | |
tree | 44dfb6ca500d32cabd450649b322a42e70a30683 /lib/fileutils.c | |
parent | Initial commit. (diff) | |
download | util-linux-upstream.tar.xz util-linux-upstream.zip |
Adding upstream version 2.38.1.upstream/2.38.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/fileutils.c')
-rw-r--r-- | lib/fileutils.c | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/lib/fileutils.c b/lib/fileutils.c new file mode 100644 index 0000000..7779e10 --- /dev/null +++ b/lib/fileutils.c @@ -0,0 +1,313 @@ +/* + * This code is in the public domain; do with it what you wish. + * + * Copyright (C) 2012 Sami Kerola <kerolasa@iki.fi> + * Copyright (C) 2012-2020 Karel Zak <kzak@redhat.com> + */ +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <string.h> + +#include "c.h" +#include "all-io.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; +} + +#ifdef F_DUPFD_CLOEXEC +int dup_fd_cloexec(int oldfd, int lowfd) +#else +int dup_fd_cloexec(int oldfd, int lowfd __attribute__((__unused__))) +#endif +{ + 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() + */ +unsigned 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; +} + +void ul_close_all_fds(unsigned int first, unsigned int last) +{ + struct dirent *d; + DIR *dir; + + dir = opendir(_PATH_PROC_FDDIR); + if (dir) { + while ((d = xreaddir(dir))) { + char *end; + unsigned int fd; + int dfd; + + errno = 0; + fd = strtoul(d->d_name, &end, 10); + + if (errno || end == d->d_name || !end || *end) + continue; + dfd = dirfd(dir); + if (dfd < 0) + continue; + if ((unsigned int)dfd == fd) + continue; + if (fd < first || last < fd) + continue; + close(fd); + } + closedir(dir); + } else { + unsigned fd, tbsz = get_fd_tabsize(); + + for (fd = 0; fd < tbsz; fd++) { + if (first <= fd && fd <= last) + close(fd); + } + } +} + +#ifdef TEST_PROGRAM_FILEUTILS +int main(int argc, char *argv[]) +{ + if (argc < 2) + errx(EXIT_FAILURE, "Usage %s --{mkstemp,close-fds,copy-file}", argv[0]); + + if (strcmp(argv[1], "--mkstemp") == 0) { + FILE *f; + char *tmpname = NULL; + + f = xfmkstemp(&tmpname, NULL, "test"); + unlink(tmpname); + free(tmpname); + fclose(f); + + } else if (strcmp(argv[1], "--close-fds") == 0) { + ignore_result( dup(STDIN_FILENO) ); + ignore_result( dup(STDIN_FILENO) ); + ignore_result( dup(STDIN_FILENO) ); + +# ifdef HAVE_CLOSE_RANGE + if (close_range(STDERR_FILENO + 1, ~0U, 0) < 0) +# endif + ul_close_all_fds(STDERR_FILENO + 1, ~0U); + + } else if (strcmp(argv[1], "--copy-file") == 0) { + int ret = ul_copy_file(STDIN_FILENO, STDOUT_FILENO); + if (ret == UL_COPY_READ_ERROR) + err(EXIT_FAILURE, "read"); + else if (ret == UL_COPY_WRITE_ERROR) + err(EXIT_FAILURE, "write"); + } + return EXIT_SUCCESS; +} +#endif + + +int ul_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; +} + +static int copy_file_simple(int from, int to) +{ + ssize_t nr; + char buf[BUFSIZ]; + + while ((nr = read_all(from, buf, sizeof(buf))) > 0) + if (write_all(to, buf, nr) == -1) + return UL_COPY_WRITE_ERROR; + if (nr < 0) + return UL_COPY_READ_ERROR; +#ifdef HAVE_EXPLICIT_BZERO + explicit_bzero(buf, sizeof(buf)); +#endif + return 0; +} + +/* Copies the contents of a file. Returns -1 on read error, -2 on write error. */ +int ul_copy_file(int from, int to) +{ +#ifdef HAVE_SENDFILE + struct stat st; + ssize_t nw; + + if (fstat(from, &st) == -1) + return UL_COPY_READ_ERROR; + if (!S_ISREG(st.st_mode)) + return copy_file_simple(from, to); + if (sendfile_all(to, from, NULL, st.st_size) < 0) + return copy_file_simple(from, to); + /* ensure we either get an EOF or an error */ + while ((nw = sendfile_all(to, from, NULL, 16*1024*1024)) != 0) + if (nw < 0) + return copy_file_simple(from, to); + return 0; +#else + return copy_file_simple(from, to); +#endif +} + +int ul_reopen(int fd, int flags) +{ + ssize_t ssz; + char buf[PATH_MAX]; + char fdpath[ sizeof(_PATH_PROC_FDDIR) + sizeof(stringify_value(INT_MAX)) ]; + + snprintf(fdpath, sizeof(fdpath), _PATH_PROC_FDDIR "/%d", fd); + + ssz = readlink(fdpath, buf, sizeof(buf) - 1); + if (ssz < 0) + return -errno; + + assert(ssz > 0); + + buf[ssz] = '\0'; + + return open(buf, flags); +} |