fs.c (2720B)
- // utils-std: Collection of commonly available Unix tools
- // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- // For copy_file_range
- #define _GNU_SOURCE // musl, glibc
- #define _DEFAULT_SOURCE // FreeBSD
- #include "./fs.h"
- #include <errno.h>
- #include <stdio.h> // BUFSIZ
- #include <string.h> // strrchr
- #include <unistd.h> // copy_file_range
- #ifdef HAS_SENDFILE
- #include <sys/sendfile.h>
- #endif
- char *
- static_basename(char *p)
- {
- if(!p || !*p) return p;
- size_t i = strlen(p) - 1;
- for(; i && p[i] == '/'; i--)
- ;
- for(; i && p[i - 1] != '/'; i--)
- ;
- return p + i;
- }
- char *
- path_split_static(char *path, bool trim)
- {
- char *child = NULL;
- size_t path_len = strlen(path);
- // delete trailing slashes
- if(trim)
- for(int i = path_len - 1; i > 0 && path[i] == '/'; i--)
- path[i] = 0;
- for(int i = path_len - 1; i > 0; i--)
- if(path[i] == '/')
- {
- path[i] = 0;
- child = &path[i + 1];
- break;
- }
- return child;
- }
- #ifndef MIN
- #define MIN(a, b) (((a) < (b)) ? (a) : (b))
- #endif
- static int
- file_sync(int fd)
- {
- errno = 0;
- int ret = fsync(fd);
- if(ret == 0) return 0;
- // Ignored cases
- if(errno == EROFS || errno == EINVAL)
- {
- errno = 0;
- return 0;
- }
- return ret;
- }
- ssize_t
- manual_file_copy(int fd_in, int fd_out, off_t len, int flags)
- {
- ssize_t wrote = 0;
- do
- {
- char buf[BUFSIZ];
- ssize_t nread = read(fd_in, buf, MIN(BUFSIZ, len));
- if(nread < 0) return nread;
- if(nread == 0) return wrote;
- ssize_t nwrite = write(fd_out, buf, (size_t)nread);
- if(nwrite < 0) return nwrite;
- len -= nwrite;
- wrote += nwrite;
- } while(len > 0);
- if(file_sync(fd_out) < 0) return -1;
- return wrote;
- }
- #ifdef HAS_COPY_FILE_RANGE
- ssize_t
- auto_file_copy(int fd_in, int fd_out, off_t len, int flags)
- {
- ssize_t wrote = 0;
- off_t ret = -1;
- do
- {
- ret = copy_file_range(fd_in, NULL, fd_out, NULL, len, 0);
- if(ret < 0)
- {
- if(errno == EXDEV || errno == EINVAL)
- {
- errno = 0;
- return manual_file_copy(fd_in, fd_out, len, flags);
- }
- return ret;
- }
- len -= ret;
- wrote += ret;
- } while(len > 0 && ret > 0);
- if(file_sync(fd_out) < 0) return -1;
- return wrote;
- }
- #endif
- #ifdef HAS_SENDFILE
- ssize_t
- auto_fd_copy(int fd_in, int fd_out, size_t len)
- {
- off_t *off = NULL;
- ssize_t wrote = 0;
- while(1)
- {
- ssize_t ret = sendfile(fd_out, fd_in, off, len);
- if(ret < 0)
- {
- switch(errno)
- {
- case EINVAL:
- errno = 0;
- return manual_file_copy(fd_in, fd_out, len, 0);
- case EAGAIN:
- errno = 0;
- continue;
- default:
- return ret;
- }
- }
- wrote += ret;
- len -= ret;
- if(file_sync(fd_out) < 0) return -1;
- if(ret == 0) return wrote;
- }
- return -1;
- }
- #endif // HAS_SENDFILE