You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

317 lines
6.7 KiB

  1. /*
  2. FUSE: Filesystem in Userspace
  3. Copyright (C) 2010 Miklos Szeredi <miklos@szeredi.hu>
  4. This program can be distributed under the terms of the GNU LGPLv2.
  5. See the file COPYING.LIB
  6. */
  7. #define _GNU_SOURCE
  8. #include "config.h"
  9. #include "fuse_i.h"
  10. #include "fuse_lowlevel.h"
  11. #include <string.h>
  12. #include <unistd.h>
  13. #include <errno.h>
  14. #include <assert.h>
  15. size_t fuse_buf_size(const struct fuse_bufvec *bufv)
  16. {
  17. size_t i;
  18. size_t size = 0;
  19. for (i = 0; i < bufv->count; i++) {
  20. if (bufv->buf[i].size == SIZE_MAX)
  21. size = SIZE_MAX;
  22. else
  23. size += bufv->buf[i].size;
  24. }
  25. return size;
  26. }
  27. static size_t min_size(size_t s1, size_t s2)
  28. {
  29. return s1 < s2 ? s1 : s2;
  30. }
  31. static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off,
  32. const struct fuse_buf *src, size_t src_off,
  33. size_t len)
  34. {
  35. ssize_t res = 0;
  36. size_t copied = 0;
  37. while (len) {
  38. if (dst->flags & FUSE_BUF_FD_SEEK) {
  39. res = pwrite(dst->fd, src->mem + src_off, len,
  40. dst->pos + dst_off);
  41. } else {
  42. res = write(dst->fd, src->mem + src_off, len);
  43. }
  44. if (res == -1) {
  45. if (!copied)
  46. return -errno;
  47. break;
  48. }
  49. if (res == 0)
  50. break;
  51. copied += res;
  52. if (!(dst->flags & FUSE_BUF_FD_RETRY))
  53. break;
  54. src_off += res;
  55. dst_off += res;
  56. len -= res;
  57. }
  58. return copied;
  59. }
  60. static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off,
  61. const struct fuse_buf *src, size_t src_off,
  62. size_t len)
  63. {
  64. ssize_t res = 0;
  65. size_t copied = 0;
  66. while (len) {
  67. if (src->flags & FUSE_BUF_FD_SEEK) {
  68. res = pread(src->fd, dst->mem + dst_off, len,
  69. src->pos + src_off);
  70. } else {
  71. res = read(src->fd, dst->mem + dst_off, len);
  72. }
  73. if (res == -1) {
  74. if (!copied)
  75. return -errno;
  76. break;
  77. }
  78. if (res == 0)
  79. break;
  80. copied += res;
  81. if (!(src->flags & FUSE_BUF_FD_RETRY))
  82. break;
  83. dst_off += res;
  84. src_off += res;
  85. len -= res;
  86. }
  87. return copied;
  88. }
  89. static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off,
  90. const struct fuse_buf *src, size_t src_off,
  91. size_t len)
  92. {
  93. char buf[4096];
  94. struct fuse_buf tmp = {
  95. .size = sizeof(buf),
  96. .flags = 0,
  97. };
  98. ssize_t res;
  99. size_t copied = 0;
  100. tmp.mem = buf;
  101. while (len) {
  102. size_t this_len = min_size(tmp.size, len);
  103. size_t read_len;
  104. res = fuse_buf_read(&tmp, 0, src, src_off, this_len);
  105. if (res < 0) {
  106. if (!copied)
  107. return res;
  108. break;
  109. }
  110. if (res == 0)
  111. break;
  112. read_len = res;
  113. res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len);
  114. if (res < 0) {
  115. if (!copied)
  116. return res;
  117. break;
  118. }
  119. if (res == 0)
  120. break;
  121. copied += res;
  122. if (res < this_len)
  123. break;
  124. dst_off += res;
  125. src_off += res;
  126. len -= res;
  127. }
  128. return copied;
  129. }
  130. #ifdef HAVE_SPLICE
  131. static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
  132. const struct fuse_buf *src, size_t src_off,
  133. size_t len, enum fuse_buf_copy_flags flags)
  134. {
  135. int splice_flags = 0;
  136. off_t *srcpos = NULL;
  137. off_t *dstpos = NULL;
  138. off_t srcpos_val;
  139. off_t dstpos_val;
  140. ssize_t res;
  141. size_t copied = 0;
  142. if (flags & FUSE_BUF_SPLICE_MOVE)
  143. splice_flags |= SPLICE_F_MOVE;
  144. if (flags & FUSE_BUF_SPLICE_NONBLOCK)
  145. splice_flags |= SPLICE_F_NONBLOCK;
  146. if (src->flags & FUSE_BUF_FD_SEEK) {
  147. srcpos_val = src->pos + src_off;
  148. srcpos = &srcpos_val;
  149. }
  150. if (dst->flags & FUSE_BUF_FD_SEEK) {
  151. dstpos_val = dst->pos + dst_off;
  152. dstpos = &dstpos_val;
  153. }
  154. while (len) {
  155. res = splice(src->fd, srcpos, dst->fd, dstpos, len, splice_flags);
  156. if (res == -1) {
  157. if (copied)
  158. break;
  159. if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE))
  160. return -errno;
  161. /* Maybe splice is not supported for this combination */
  162. return fuse_buf_fd_to_fd(dst, dst_off, src, src_off,
  163. len);
  164. }
  165. if (res == 0)
  166. break;
  167. copied += res;
  168. if (!(src->flags & FUSE_BUF_FD_RETRY) &&
  169. !(dst->flags & FUSE_BUF_FD_RETRY)) {
  170. break;
  171. }
  172. len -= res;
  173. }
  174. return copied;
  175. }
  176. #else
  177. static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
  178. const struct fuse_buf *src, size_t src_off,
  179. size_t len, enum fuse_buf_copy_flags flags)
  180. {
  181. (void) flags;
  182. return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
  183. }
  184. #endif
  185. static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off,
  186. const struct fuse_buf *src, size_t src_off,
  187. size_t len, enum fuse_buf_copy_flags flags)
  188. {
  189. int src_is_fd = src->flags & FUSE_BUF_IS_FD;
  190. int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
  191. if (!src_is_fd && !dst_is_fd) {
  192. char *dstmem = dst->mem + dst_off;
  193. char *srcmem = src->mem + src_off;
  194. if (dstmem != srcmem) {
  195. if (dstmem + len <= srcmem || srcmem + len <= dstmem)
  196. memcpy(dstmem, srcmem, len);
  197. else
  198. memmove(dstmem, srcmem, len);
  199. }
  200. return len;
  201. } else if (!src_is_fd) {
  202. return fuse_buf_write(dst, dst_off, src, src_off, len);
  203. } else if (!dst_is_fd) {
  204. return fuse_buf_read(dst, dst_off, src, src_off, len);
  205. } else if (flags & FUSE_BUF_NO_SPLICE) {
  206. return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
  207. } else {
  208. return fuse_buf_splice(dst, dst_off, src, src_off, len, flags);
  209. }
  210. }
  211. static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv)
  212. {
  213. if (bufv->idx < bufv->count)
  214. return &bufv->buf[bufv->idx];
  215. else
  216. return NULL;
  217. }
  218. static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len)
  219. {
  220. const struct fuse_buf *buf = fuse_bufvec_current(bufv);
  221. bufv->off += len;
  222. assert(bufv->off <= buf->size);
  223. if (bufv->off == buf->size) {
  224. assert(bufv->idx < bufv->count);
  225. bufv->idx++;
  226. if (bufv->idx == bufv->count)
  227. return 0;
  228. bufv->off = 0;
  229. }
  230. return 1;
  231. }
  232. ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
  233. enum fuse_buf_copy_flags flags)
  234. {
  235. size_t copied = 0;
  236. if (dstv == srcv)
  237. return fuse_buf_size(dstv);
  238. for (;;) {
  239. const struct fuse_buf *src = fuse_bufvec_current(srcv);
  240. const struct fuse_buf *dst = fuse_bufvec_current(dstv);
  241. size_t src_len;
  242. size_t dst_len;
  243. size_t len;
  244. ssize_t res;
  245. if (src == NULL || dst == NULL)
  246. break;
  247. src_len = src->size - srcv->off;
  248. dst_len = dst->size - dstv->off;
  249. len = min_size(src_len, dst_len);
  250. res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
  251. if (res < 0) {
  252. if (!copied)
  253. return res;
  254. break;
  255. }
  256. copied += res;
  257. if (!fuse_bufvec_advance(srcv, res) ||
  258. !fuse_bufvec_advance(dstv, res))
  259. break;
  260. if (res < len)
  261. break;
  262. }
  263. return copied;
  264. }