2 FUSE: Filesystem in Userspace
3 Copyright (C) 2010 Miklos Szeredi <miklos@szeredi.hu>
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB
13 #include "fuse_lowlevel.h"
19 size_t fuse_buf_size(const struct fuse_bufvec
*bufv
)
24 for (i
= 0; i
< bufv
->count
; i
++) {
25 if (bufv
->buf
[i
].size
== SIZE_MAX
)
28 size
+= bufv
->buf
[i
].size
;
34 static size_t min_size(size_t s1
, size_t s2
)
36 return s1
< s2
? s1
: s2
;
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
,
47 if (dst
->flags
& FUSE_BUF_FD_SEEK
) {
48 res
= pwrite(dst
->fd
, src
->mem
+ src_off
, len
,
51 res
= write(dst
->fd
, src
->mem
+ src_off
, len
);
62 if (!(dst
->flags
& FUSE_BUF_FD_RETRY
))
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
,
81 if (src
->flags
& FUSE_BUF_FD_SEEK
) {
82 res
= pread(src
->fd
, dst
->mem
+ dst_off
, len
,
85 res
= read(src
->fd
, dst
->mem
+ dst_off
, len
);
96 if (!(src
->flags
& FUSE_BUF_FD_RETRY
))
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
,
112 struct fuse_buf tmp
= {
122 size_t this_len
= min_size(tmp
.size
, len
);
125 res
= fuse_buf_read(&tmp
, 0, src
, src_off
, this_len
);
135 res
= fuse_buf_write(dst
, dst_off
, &tmp
, 0, read_len
);
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
)
162 int splice_flags
= 0;
163 off_t
*srcpos
= NULL
;
164 off_t
*dstpos
= NULL
;
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
;
175 if (src
->flags
& FUSE_BUF_FD_SEEK
) {
176 srcpos_val
= src
->pos
+ src_off
;
177 srcpos
= &srcpos_val
;
179 if (dst
->flags
& FUSE_BUF_FD_SEEK
) {
180 dstpos_val
= dst
->pos
+ dst_off
;
181 dstpos
= &dstpos_val
;
185 res
= splice(src
->fd
, srcpos
, dst
->fd
, dstpos
, len
,
191 if (errno
!= EINVAL
|| (flags
& FUSE_BUF_FORCE_SPLICE
))
194 /* Maybe splice is not supported for this combination */
195 return fuse_buf_fd_to_fd(dst
, dst_off
, src
, src_off
,
202 if (!(src
->flags
& FUSE_BUF_FD_RETRY
) &&
203 !(dst
->flags
& FUSE_BUF_FD_RETRY
)) {
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
)
219 return fuse_buf_fd_to_fd(dst
, dst_off
, src
, src_off
, len
);
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
)
228 int src_is_fd
= src
->flags
& FUSE_BUF_IS_FD
;
229 int dst_is_fd
= dst
->flags
& FUSE_BUF_IS_FD
;
231 if (!src_is_fd
&& !dst_is_fd
) {
232 void *dstmem
= dst
->mem
+ dst_off
;
233 void *srcmem
= src
->mem
+ src_off
;
235 if (dstmem
!= srcmem
) {
236 if (dstmem
+ len
<= srcmem
|| srcmem
+ len
<= dstmem
)
237 memcpy(dstmem
, srcmem
, len
);
239 memmove(dstmem
, srcmem
, 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
);
250 return fuse_buf_splice(dst
, dst_off
, src
, src_off
, len
, flags
);
254 static const struct fuse_buf
*fuse_bufvec_current(struct fuse_bufvec
*bufv
)
256 if (bufv
->idx
< bufv
->count
)
257 return &bufv
->buf
[bufv
->idx
];
262 static int fuse_bufvec_advance(struct fuse_bufvec
*bufv
, size_t len
)
264 const struct fuse_buf
*buf
= fuse_bufvec_current(bufv
);
267 assert(bufv
->off
<= buf
->size
);
268 if (bufv
->off
== buf
->size
) {
269 assert(bufv
->idx
< bufv
->count
);
271 if (bufv
->idx
== bufv
->count
)
278 ssize_t
fuse_buf_copy(struct fuse_bufvec
*dstv
, struct fuse_bufvec
*srcv
,
279 enum fuse_buf_copy_flags flags
)
284 return fuse_buf_size(dstv
);
287 const struct fuse_buf
*src
= fuse_bufvec_current(srcv
);
288 const struct fuse_buf
*dst
= fuse_bufvec_current(dstv
);
294 if (src
== NULL
|| dst
== NULL
)
297 src_len
= src
->size
- srcv
->off
;
298 dst_len
= dst
->size
- dstv
->off
;
299 len
= min_size(src_len
, dst_len
);
301 res
= fuse_buf_copy_one(dst
, dstv
->off
, src
, srcv
->off
, len
, flags
);
309 if (!fuse_bufvec_advance(srcv
, res
) ||
310 !fuse_bufvec_advance(dstv
, res
))