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