16881f68fSopenharmony_ci/* 26881f68fSopenharmony_ci FUSE: Filesystem in Userspace 36881f68fSopenharmony_ci Copyright (C) 2010 Miklos Szeredi <miklos@szeredi.hu> 46881f68fSopenharmony_ci 56881f68fSopenharmony_ci Functions for dealing with `struct fuse_buf` and `struct 66881f68fSopenharmony_ci fuse_bufvec`. 76881f68fSopenharmony_ci 86881f68fSopenharmony_ci This program can be distributed under the terms of the GNU LGPLv2. 96881f68fSopenharmony_ci See the file COPYING.LIB 106881f68fSopenharmony_ci*/ 116881f68fSopenharmony_ci 126881f68fSopenharmony_ci#define _GNU_SOURCE 136881f68fSopenharmony_ci 146881f68fSopenharmony_ci#include "fuse_config.h" 156881f68fSopenharmony_ci#include "fuse_i.h" 166881f68fSopenharmony_ci#include "fuse_lowlevel.h" 176881f68fSopenharmony_ci#include <string.h> 186881f68fSopenharmony_ci#include <unistd.h> 196881f68fSopenharmony_ci#include <errno.h> 206881f68fSopenharmony_ci#include <assert.h> 216881f68fSopenharmony_ci 226881f68fSopenharmony_cisize_t fuse_buf_size(const struct fuse_bufvec *bufv) 236881f68fSopenharmony_ci{ 246881f68fSopenharmony_ci size_t i; 256881f68fSopenharmony_ci size_t size = 0; 266881f68fSopenharmony_ci 276881f68fSopenharmony_ci for (i = 0; i < bufv->count; i++) { 286881f68fSopenharmony_ci if (bufv->buf[i].size == SIZE_MAX) 296881f68fSopenharmony_ci size = SIZE_MAX; 306881f68fSopenharmony_ci else 316881f68fSopenharmony_ci size += bufv->buf[i].size; 326881f68fSopenharmony_ci } 336881f68fSopenharmony_ci 346881f68fSopenharmony_ci return size; 356881f68fSopenharmony_ci} 366881f68fSopenharmony_ci 376881f68fSopenharmony_cistatic size_t min_size(size_t s1, size_t s2) 386881f68fSopenharmony_ci{ 396881f68fSopenharmony_ci return s1 < s2 ? s1 : s2; 406881f68fSopenharmony_ci} 416881f68fSopenharmony_ci 426881f68fSopenharmony_cistatic ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off, 436881f68fSopenharmony_ci const struct fuse_buf *src, size_t src_off, 446881f68fSopenharmony_ci size_t len) 456881f68fSopenharmony_ci{ 466881f68fSopenharmony_ci ssize_t res = 0; 476881f68fSopenharmony_ci size_t copied = 0; 486881f68fSopenharmony_ci 496881f68fSopenharmony_ci while (len) { 506881f68fSopenharmony_ci if (dst->flags & FUSE_BUF_FD_SEEK) { 516881f68fSopenharmony_ci res = pwrite(dst->fd, (char *)src->mem + src_off, len, 526881f68fSopenharmony_ci dst->pos + dst_off); 536881f68fSopenharmony_ci } else { 546881f68fSopenharmony_ci res = write(dst->fd, (char *)src->mem + src_off, len); 556881f68fSopenharmony_ci } 566881f68fSopenharmony_ci if (res == -1) { 576881f68fSopenharmony_ci if (!copied) 586881f68fSopenharmony_ci return -errno; 596881f68fSopenharmony_ci break; 606881f68fSopenharmony_ci } 616881f68fSopenharmony_ci if (res == 0) 626881f68fSopenharmony_ci break; 636881f68fSopenharmony_ci 646881f68fSopenharmony_ci copied += res; 656881f68fSopenharmony_ci if (!(dst->flags & FUSE_BUF_FD_RETRY)) 666881f68fSopenharmony_ci break; 676881f68fSopenharmony_ci 686881f68fSopenharmony_ci src_off += res; 696881f68fSopenharmony_ci dst_off += res; 706881f68fSopenharmony_ci len -= res; 716881f68fSopenharmony_ci } 726881f68fSopenharmony_ci 736881f68fSopenharmony_ci return copied; 746881f68fSopenharmony_ci} 756881f68fSopenharmony_ci 766881f68fSopenharmony_cistatic ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off, 776881f68fSopenharmony_ci const struct fuse_buf *src, size_t src_off, 786881f68fSopenharmony_ci size_t len) 796881f68fSopenharmony_ci{ 806881f68fSopenharmony_ci ssize_t res = 0; 816881f68fSopenharmony_ci size_t copied = 0; 826881f68fSopenharmony_ci 836881f68fSopenharmony_ci while (len) { 846881f68fSopenharmony_ci if (src->flags & FUSE_BUF_FD_SEEK) { 856881f68fSopenharmony_ci res = pread(src->fd, (char *)dst->mem + dst_off, len, 866881f68fSopenharmony_ci src->pos + src_off); 876881f68fSopenharmony_ci } else { 886881f68fSopenharmony_ci res = read(src->fd, (char *)dst->mem + dst_off, len); 896881f68fSopenharmony_ci } 906881f68fSopenharmony_ci if (res == -1) { 916881f68fSopenharmony_ci if (!copied) 926881f68fSopenharmony_ci return -errno; 936881f68fSopenharmony_ci break; 946881f68fSopenharmony_ci } 956881f68fSopenharmony_ci if (res == 0) 966881f68fSopenharmony_ci break; 976881f68fSopenharmony_ci 986881f68fSopenharmony_ci copied += res; 996881f68fSopenharmony_ci if (!(src->flags & FUSE_BUF_FD_RETRY)) 1006881f68fSopenharmony_ci break; 1016881f68fSopenharmony_ci 1026881f68fSopenharmony_ci dst_off += res; 1036881f68fSopenharmony_ci src_off += res; 1046881f68fSopenharmony_ci len -= res; 1056881f68fSopenharmony_ci } 1066881f68fSopenharmony_ci 1076881f68fSopenharmony_ci return copied; 1086881f68fSopenharmony_ci} 1096881f68fSopenharmony_ci 1106881f68fSopenharmony_cistatic ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off, 1116881f68fSopenharmony_ci const struct fuse_buf *src, size_t src_off, 1126881f68fSopenharmony_ci size_t len) 1136881f68fSopenharmony_ci{ 1146881f68fSopenharmony_ci char buf[4096]; 1156881f68fSopenharmony_ci struct fuse_buf tmp = { 1166881f68fSopenharmony_ci .size = sizeof(buf), 1176881f68fSopenharmony_ci .flags = 0, 1186881f68fSopenharmony_ci }; 1196881f68fSopenharmony_ci ssize_t res; 1206881f68fSopenharmony_ci size_t copied = 0; 1216881f68fSopenharmony_ci 1226881f68fSopenharmony_ci tmp.mem = buf; 1236881f68fSopenharmony_ci 1246881f68fSopenharmony_ci while (len) { 1256881f68fSopenharmony_ci size_t this_len = min_size(tmp.size, len); 1266881f68fSopenharmony_ci size_t read_len; 1276881f68fSopenharmony_ci 1286881f68fSopenharmony_ci res = fuse_buf_read(&tmp, 0, src, src_off, this_len); 1296881f68fSopenharmony_ci if (res < 0) { 1306881f68fSopenharmony_ci if (!copied) 1316881f68fSopenharmony_ci return res; 1326881f68fSopenharmony_ci break; 1336881f68fSopenharmony_ci } 1346881f68fSopenharmony_ci if (res == 0) 1356881f68fSopenharmony_ci break; 1366881f68fSopenharmony_ci 1376881f68fSopenharmony_ci read_len = res; 1386881f68fSopenharmony_ci res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len); 1396881f68fSopenharmony_ci if (res < 0) { 1406881f68fSopenharmony_ci if (!copied) 1416881f68fSopenharmony_ci return res; 1426881f68fSopenharmony_ci break; 1436881f68fSopenharmony_ci } 1446881f68fSopenharmony_ci if (res == 0) 1456881f68fSopenharmony_ci break; 1466881f68fSopenharmony_ci 1476881f68fSopenharmony_ci copied += res; 1486881f68fSopenharmony_ci 1496881f68fSopenharmony_ci if (res < this_len) 1506881f68fSopenharmony_ci break; 1516881f68fSopenharmony_ci 1526881f68fSopenharmony_ci dst_off += res; 1536881f68fSopenharmony_ci src_off += res; 1546881f68fSopenharmony_ci len -= res; 1556881f68fSopenharmony_ci } 1566881f68fSopenharmony_ci 1576881f68fSopenharmony_ci return copied; 1586881f68fSopenharmony_ci} 1596881f68fSopenharmony_ci 1606881f68fSopenharmony_ci#ifdef HAVE_SPLICE 1616881f68fSopenharmony_cistatic ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off, 1626881f68fSopenharmony_ci const struct fuse_buf *src, size_t src_off, 1636881f68fSopenharmony_ci size_t len, enum fuse_buf_copy_flags flags) 1646881f68fSopenharmony_ci{ 1656881f68fSopenharmony_ci int splice_flags = 0; 1666881f68fSopenharmony_ci off_t *srcpos = NULL; 1676881f68fSopenharmony_ci off_t *dstpos = NULL; 1686881f68fSopenharmony_ci off_t srcpos_val; 1696881f68fSopenharmony_ci off_t dstpos_val; 1706881f68fSopenharmony_ci ssize_t res; 1716881f68fSopenharmony_ci size_t copied = 0; 1726881f68fSopenharmony_ci 1736881f68fSopenharmony_ci if (flags & FUSE_BUF_SPLICE_MOVE) 1746881f68fSopenharmony_ci splice_flags |= SPLICE_F_MOVE; 1756881f68fSopenharmony_ci if (flags & FUSE_BUF_SPLICE_NONBLOCK) 1766881f68fSopenharmony_ci splice_flags |= SPLICE_F_NONBLOCK; 1776881f68fSopenharmony_ci 1786881f68fSopenharmony_ci if (src->flags & FUSE_BUF_FD_SEEK) { 1796881f68fSopenharmony_ci srcpos_val = src->pos + src_off; 1806881f68fSopenharmony_ci srcpos = &srcpos_val; 1816881f68fSopenharmony_ci } 1826881f68fSopenharmony_ci if (dst->flags & FUSE_BUF_FD_SEEK) { 1836881f68fSopenharmony_ci dstpos_val = dst->pos + dst_off; 1846881f68fSopenharmony_ci dstpos = &dstpos_val; 1856881f68fSopenharmony_ci } 1866881f68fSopenharmony_ci 1876881f68fSopenharmony_ci while (len) { 1886881f68fSopenharmony_ci res = splice(src->fd, srcpos, dst->fd, dstpos, len, 1896881f68fSopenharmony_ci splice_flags); 1906881f68fSopenharmony_ci if (res == -1) { 1916881f68fSopenharmony_ci if (copied) 1926881f68fSopenharmony_ci break; 1936881f68fSopenharmony_ci 1946881f68fSopenharmony_ci if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE)) 1956881f68fSopenharmony_ci return -errno; 1966881f68fSopenharmony_ci 1976881f68fSopenharmony_ci /* Maybe splice is not supported for this combination */ 1986881f68fSopenharmony_ci return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, 1996881f68fSopenharmony_ci len); 2006881f68fSopenharmony_ci } 2016881f68fSopenharmony_ci if (res == 0) 2026881f68fSopenharmony_ci break; 2036881f68fSopenharmony_ci 2046881f68fSopenharmony_ci copied += res; 2056881f68fSopenharmony_ci if (!(src->flags & FUSE_BUF_FD_RETRY) && 2066881f68fSopenharmony_ci !(dst->flags & FUSE_BUF_FD_RETRY)) { 2076881f68fSopenharmony_ci break; 2086881f68fSopenharmony_ci } 2096881f68fSopenharmony_ci 2106881f68fSopenharmony_ci len -= res; 2116881f68fSopenharmony_ci } 2126881f68fSopenharmony_ci 2136881f68fSopenharmony_ci return copied; 2146881f68fSopenharmony_ci} 2156881f68fSopenharmony_ci#else 2166881f68fSopenharmony_cistatic ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off, 2176881f68fSopenharmony_ci const struct fuse_buf *src, size_t src_off, 2186881f68fSopenharmony_ci size_t len, enum fuse_buf_copy_flags flags) 2196881f68fSopenharmony_ci{ 2206881f68fSopenharmony_ci (void) flags; 2216881f68fSopenharmony_ci 2226881f68fSopenharmony_ci return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); 2236881f68fSopenharmony_ci} 2246881f68fSopenharmony_ci#endif 2256881f68fSopenharmony_ci 2266881f68fSopenharmony_ci 2276881f68fSopenharmony_cistatic ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off, 2286881f68fSopenharmony_ci const struct fuse_buf *src, size_t src_off, 2296881f68fSopenharmony_ci size_t len, enum fuse_buf_copy_flags flags) 2306881f68fSopenharmony_ci{ 2316881f68fSopenharmony_ci int src_is_fd = src->flags & FUSE_BUF_IS_FD; 2326881f68fSopenharmony_ci int dst_is_fd = dst->flags & FUSE_BUF_IS_FD; 2336881f68fSopenharmony_ci 2346881f68fSopenharmony_ci if (!src_is_fd && !dst_is_fd) { 2356881f68fSopenharmony_ci char *dstmem = (char *)dst->mem + dst_off; 2366881f68fSopenharmony_ci char *srcmem = (char *)src->mem + src_off; 2376881f68fSopenharmony_ci 2386881f68fSopenharmony_ci if (dstmem != srcmem) { 2396881f68fSopenharmony_ci if (dstmem + len <= srcmem || srcmem + len <= dstmem) 2406881f68fSopenharmony_ci memcpy(dstmem, srcmem, len); 2416881f68fSopenharmony_ci else 2426881f68fSopenharmony_ci memmove(dstmem, srcmem, len); 2436881f68fSopenharmony_ci } 2446881f68fSopenharmony_ci 2456881f68fSopenharmony_ci return len; 2466881f68fSopenharmony_ci } else if (!src_is_fd) { 2476881f68fSopenharmony_ci return fuse_buf_write(dst, dst_off, src, src_off, len); 2486881f68fSopenharmony_ci } else if (!dst_is_fd) { 2496881f68fSopenharmony_ci return fuse_buf_read(dst, dst_off, src, src_off, len); 2506881f68fSopenharmony_ci } else if (flags & FUSE_BUF_NO_SPLICE) { 2516881f68fSopenharmony_ci return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); 2526881f68fSopenharmony_ci } else { 2536881f68fSopenharmony_ci return fuse_buf_splice(dst, dst_off, src, src_off, len, flags); 2546881f68fSopenharmony_ci } 2556881f68fSopenharmony_ci} 2566881f68fSopenharmony_ci 2576881f68fSopenharmony_cistatic const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv) 2586881f68fSopenharmony_ci{ 2596881f68fSopenharmony_ci if (bufv->idx < bufv->count) 2606881f68fSopenharmony_ci return &bufv->buf[bufv->idx]; 2616881f68fSopenharmony_ci else 2626881f68fSopenharmony_ci return NULL; 2636881f68fSopenharmony_ci} 2646881f68fSopenharmony_ci 2656881f68fSopenharmony_cistatic int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len) 2666881f68fSopenharmony_ci{ 2676881f68fSopenharmony_ci const struct fuse_buf *buf = fuse_bufvec_current(bufv); 2686881f68fSopenharmony_ci 2696881f68fSopenharmony_ci if (!buf) 2706881f68fSopenharmony_ci return 0; 2716881f68fSopenharmony_ci 2726881f68fSopenharmony_ci bufv->off += len; 2736881f68fSopenharmony_ci assert(bufv->off <= buf->size); 2746881f68fSopenharmony_ci if (bufv->off == buf->size) { 2756881f68fSopenharmony_ci assert(bufv->idx < bufv->count); 2766881f68fSopenharmony_ci bufv->idx++; 2776881f68fSopenharmony_ci if (bufv->idx == bufv->count) 2786881f68fSopenharmony_ci return 0; 2796881f68fSopenharmony_ci bufv->off = 0; 2806881f68fSopenharmony_ci } 2816881f68fSopenharmony_ci return 1; 2826881f68fSopenharmony_ci} 2836881f68fSopenharmony_ci 2846881f68fSopenharmony_cissize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv, 2856881f68fSopenharmony_ci enum fuse_buf_copy_flags flags) 2866881f68fSopenharmony_ci{ 2876881f68fSopenharmony_ci size_t copied = 0; 2886881f68fSopenharmony_ci 2896881f68fSopenharmony_ci if (dstv == srcv) 2906881f68fSopenharmony_ci return fuse_buf_size(dstv); 2916881f68fSopenharmony_ci 2926881f68fSopenharmony_ci for (;;) { 2936881f68fSopenharmony_ci const struct fuse_buf *src = fuse_bufvec_current(srcv); 2946881f68fSopenharmony_ci const struct fuse_buf *dst = fuse_bufvec_current(dstv); 2956881f68fSopenharmony_ci size_t src_len; 2966881f68fSopenharmony_ci size_t dst_len; 2976881f68fSopenharmony_ci size_t len; 2986881f68fSopenharmony_ci ssize_t res; 2996881f68fSopenharmony_ci 3006881f68fSopenharmony_ci if (src == NULL || dst == NULL) 3016881f68fSopenharmony_ci break; 3026881f68fSopenharmony_ci 3036881f68fSopenharmony_ci src_len = src->size - srcv->off; 3046881f68fSopenharmony_ci dst_len = dst->size - dstv->off; 3056881f68fSopenharmony_ci len = min_size(src_len, dst_len); 3066881f68fSopenharmony_ci 3076881f68fSopenharmony_ci res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags); 3086881f68fSopenharmony_ci if (res < 0) { 3096881f68fSopenharmony_ci if (!copied) 3106881f68fSopenharmony_ci return res; 3116881f68fSopenharmony_ci break; 3126881f68fSopenharmony_ci } 3136881f68fSopenharmony_ci copied += res; 3146881f68fSopenharmony_ci 3156881f68fSopenharmony_ci if (!fuse_bufvec_advance(srcv, res) || 3166881f68fSopenharmony_ci !fuse_bufvec_advance(dstv, res)) 3176881f68fSopenharmony_ci break; 3186881f68fSopenharmony_ci 3196881f68fSopenharmony_ci if (res < len) 3206881f68fSopenharmony_ci break; 3216881f68fSopenharmony_ci } 3226881f68fSopenharmony_ci 3236881f68fSopenharmony_ci return copied; 3246881f68fSopenharmony_ci} 325