logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/

fs.c (2720B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. // For copy_file_range
  5. #define _GNU_SOURCE // musl, glibc
  6. #define _DEFAULT_SOURCE // FreeBSD
  7. #include "./fs.h"
  8. #include <errno.h>
  9. #include <stdio.h> // BUFSIZ
  10. #include <string.h> // strrchr
  11. #include <unistd.h> // copy_file_range
  12. #ifdef HAS_SENDFILE
  13. #include <sys/sendfile.h>
  14. #endif
  15. char *
  16. static_basename(char *p)
  17. {
  18. if(!p || !*p) return p;
  19. size_t i = strlen(p) - 1;
  20. for(; i && p[i] == '/'; i--)
  21. ;
  22. for(; i && p[i - 1] != '/'; i--)
  23. ;
  24. return p + i;
  25. }
  26. char *
  27. path_split_static(char *path, bool trim)
  28. {
  29. char *child = NULL;
  30. size_t path_len = strlen(path);
  31. // delete trailing slashes
  32. if(trim)
  33. for(int i = path_len - 1; i > 0 && path[i] == '/'; i--)
  34. path[i] = 0;
  35. for(int i = path_len - 1; i > 0; i--)
  36. if(path[i] == '/')
  37. {
  38. path[i] = 0;
  39. child = &path[i + 1];
  40. break;
  41. }
  42. return child;
  43. }
  44. #ifndef MIN
  45. #define MIN(a, b) (((a) < (b)) ? (a) : (b))
  46. #endif
  47. static int
  48. file_sync(int fd)
  49. {
  50. errno = 0;
  51. int ret = fsync(fd);
  52. if(ret == 0) return 0;
  53. // Ignored cases
  54. if(errno == EROFS || errno == EINVAL)
  55. {
  56. errno = 0;
  57. return 0;
  58. }
  59. return ret;
  60. }
  61. ssize_t
  62. manual_file_copy(int fd_in, int fd_out, off_t len, int flags)
  63. {
  64. ssize_t wrote = 0;
  65. do
  66. {
  67. char buf[BUFSIZ];
  68. ssize_t nread = read(fd_in, buf, MIN(BUFSIZ, len));
  69. if(nread < 0) return nread;
  70. if(nread == 0) return wrote;
  71. ssize_t nwrite = write(fd_out, buf, (size_t)nread);
  72. if(nwrite < 0) return nwrite;
  73. len -= nwrite;
  74. wrote += nwrite;
  75. } while(len > 0);
  76. if(file_sync(fd_out) < 0) return -1;
  77. return wrote;
  78. }
  79. #ifdef HAS_COPY_FILE_RANGE
  80. ssize_t
  81. auto_file_copy(int fd_in, int fd_out, off_t len, int flags)
  82. {
  83. ssize_t wrote = 0;
  84. off_t ret = -1;
  85. do
  86. {
  87. ret = copy_file_range(fd_in, NULL, fd_out, NULL, len, 0);
  88. if(ret < 0)
  89. {
  90. if(errno == EXDEV || errno == EINVAL)
  91. {
  92. errno = 0;
  93. return manual_file_copy(fd_in, fd_out, len, flags);
  94. }
  95. return ret;
  96. }
  97. len -= ret;
  98. wrote += ret;
  99. } while(len > 0 && ret > 0);
  100. if(file_sync(fd_out) < 0) return -1;
  101. return wrote;
  102. }
  103. #endif
  104. #ifdef HAS_SENDFILE
  105. ssize_t
  106. auto_fd_copy(int fd_in, int fd_out, size_t len)
  107. {
  108. off_t *off = NULL;
  109. ssize_t wrote = 0;
  110. while(1)
  111. {
  112. ssize_t ret = sendfile(fd_out, fd_in, off, len);
  113. if(ret < 0)
  114. {
  115. switch(errno)
  116. {
  117. case EINVAL:
  118. errno = 0;
  119. return manual_file_copy(fd_in, fd_out, len, 0);
  120. case EAGAIN:
  121. errno = 0;
  122. continue;
  123. default:
  124. return ret;
  125. }
  126. }
  127. wrote += ret;
  128. len -= ret;
  129. if(file_sync(fd_out) < 0) return -1;
  130. if(ret == 0) return wrote;
  131. }
  132. return -1;
  133. }
  134. #endif // HAS_SENDFILE