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