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