libfuse
buffer.c
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 
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 
37 static size_t min_size(size_t s1, size_t s2)
38 {
39  return s1 < s2 ? s1 : s2;
40 }
41 
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 
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 
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
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
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 
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 
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 
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 
281 ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
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 }
size_t off
Definition: fuse_common.h:721
off_t pos
Definition: fuse_common.h:696
fuse_buf_copy_flags
Definition: fuse_common.h:621
size_t idx
Definition: fuse_common.h:716
size_t count
Definition: fuse_common.h:711
enum fuse_buf_flags flags
Definition: fuse_common.h:675
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition: buffer.c:281
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition: buffer.c:22
void * mem
Definition: fuse_common.h:682
struct fuse_buf buf[1]
Definition: fuse_common.h:726
size_t size
Definition: fuse_common.h:670