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.

318 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,
  156. splice_flags);
  157. if (res == -1) {
  158. if (copied)
  159. break;
  160. if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE))
  161. return -errno;
  162. /* Maybe splice is not supported for this combination */
  163. return fuse_buf_fd_to_fd(dst, dst_off, src, src_off,
  164. len);
  165. }
  166. if (res == 0)
  167. break;
  168. copied += res;
  169. if (!(src->flags & FUSE_BUF_FD_RETRY) &&
  170. !(dst->flags & FUSE_BUF_FD_RETRY)) {
  171. break;
  172. }
  173. len -= res;
  174. }
  175. return copied;
  176. }
  177. #else
  178. static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
  179. const struct fuse_buf *src, size_t src_off,
  180. size_t len, enum fuse_buf_copy_flags flags)
  181. {
  182. (void) flags;
  183. return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
  184. }
  185. #endif
  186. static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off,
  187. const struct fuse_buf *src, size_t src_off,
  188. size_t len, enum fuse_buf_copy_flags flags)
  189. {
  190. int src_is_fd = src->flags & FUSE_BUF_IS_FD;
  191. int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
  192. if (!src_is_fd && !dst_is_fd) {
  193. char *dstmem = dst->mem + dst_off;
  194. char *srcmem = src->mem + src_off;
  195. if (dstmem != srcmem) {
  196. if (dstmem + len <= srcmem || srcmem + len <= dstmem)
  197. memcpy(dstmem, srcmem, len);
  198. else
  199. memmove(dstmem, srcmem, len);
  200. }
  201. return len;
  202. } else if (!src_is_fd) {
  203. return fuse_buf_write(dst, dst_off, src, src_off, len);
  204. } else if (!dst_is_fd) {
  205. return fuse_buf_read(dst, dst_off, src, src_off, len);
  206. } else if (flags & FUSE_BUF_NO_SPLICE) {
  207. return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
  208. } else {
  209. return fuse_buf_splice(dst, dst_off, src, src_off, len, flags);
  210. }
  211. }
  212. static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv)
  213. {
  214. if (bufv->idx < bufv->count)
  215. return &bufv->buf[bufv->idx];
  216. else
  217. return NULL;
  218. }
  219. static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len)
  220. {
  221. const struct fuse_buf *buf = fuse_bufvec_current(bufv);
  222. bufv->off += len;
  223. assert(bufv->off <= buf->size);
  224. if (bufv->off == buf->size) {
  225. assert(bufv->idx < bufv->count);
  226. bufv->idx++;
  227. if (bufv->idx == bufv->count)
  228. return 0;
  229. bufv->off = 0;
  230. }
  231. return 1;
  232. }
  233. ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
  234. enum fuse_buf_copy_flags flags)
  235. {
  236. size_t copied = 0;
  237. if (dstv == srcv)
  238. return fuse_buf_size(dstv);
  239. for (;;) {
  240. const struct fuse_buf *src = fuse_bufvec_current(srcv);
  241. const struct fuse_buf *dst = fuse_bufvec_current(dstv);
  242. size_t src_len;
  243. size_t dst_len;
  244. size_t len;
  245. ssize_t res;
  246. if (src == NULL || dst == NULL)
  247. break;
  248. src_len = src->size - srcv->off;
  249. dst_len = dst->size - dstv->off;
  250. len = min_size(src_len, dst_len);
  251. res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
  252. if (res < 0) {
  253. if (!copied)
  254. return res;
  255. break;
  256. }
  257. copied += res;
  258. if (!fuse_bufvec_advance(srcv, res) ||
  259. !fuse_bufvec_advance(dstv, res))
  260. break;
  261. if (res < len)
  262. break;
  263. }
  264. return copied;
  265. }