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 "fuse_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 if (!buf)
270 return 0;
271
272 bufv->off += len;
273 assert(bufv->off <= buf->size);
274 if (bufv->off == buf->size) {
275 assert(bufv->idx < bufv->count);
276 bufv->idx++;
277 if (bufv->idx == bufv->count)
278 return 0;
279 bufv->off = 0;
280 }
281 return 1;
282 }
283
fuse_buf_copy(struct fuse_bufvec * dstv,struct fuse_bufvec * srcv,enum fuse_buf_copy_flags flags)284 ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
285 enum fuse_buf_copy_flags flags)
286 {
287 size_t copied = 0;
288
289 if (dstv == srcv)
290 return fuse_buf_size(dstv);
291
292 for (;;) {
293 const struct fuse_buf *src = fuse_bufvec_current(srcv);
294 const struct fuse_buf *dst = fuse_bufvec_current(dstv);
295 size_t src_len;
296 size_t dst_len;
297 size_t len;
298 ssize_t res;
299
300 if (src == NULL || dst == NULL)
301 break;
302
303 src_len = src->size - srcv->off;
304 dst_len = dst->size - dstv->off;
305 len = min_size(src_len, dst_len);
306
307 res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
308 if (res < 0) {
309 if (!copied)
310 return res;
311 break;
312 }
313 copied += res;
314
315 if (!fuse_bufvec_advance(srcv, res) ||
316 !fuse_bufvec_advance(dstv, res))
317 break;
318
319 if (res < len)
320 break;
321 }
322
323 return copied;
324 }
325