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
12 #include "fuse_lowlevel.h"
18 size_t fuse_buf_size(const struct fuse_bufvec
*bufv
)
23 for (i
= 0; i
< bufv
->count
; i
++) {
24 if (bufv
->buf
[i
].size
== SIZE_MAX
)
27 size
+= bufv
->buf
[i
].size
;
33 static size_t min_size(size_t s1
, size_t s2
)
35 return s1
< s2
? s1
: s2
;
38 static ssize_t
fuse_buf_write(const struct fuse_buf
*dst
, size_t dst_off
,
39 const struct fuse_buf
*src
, size_t src_off
,
46 if (dst
->flags
& FUSE_BUF_FD_SEEK
) {
47 res
= pwrite(dst
->fd
, src
->mem
+ src_off
, len
,
50 res
= write(dst
->fd
, src
->mem
+ src_off
, len
);
61 if (!(dst
->flags
& FUSE_BUF_FD_RETRY
))
72 static ssize_t
fuse_buf_read(const struct fuse_buf
*dst
, size_t dst_off
,
73 const struct fuse_buf
*src
, size_t src_off
,
80 if (src
->flags
& FUSE_BUF_FD_SEEK
) {
81 res
= pread(src
->fd
, dst
->mem
+ dst_off
, len
,
84 res
= read(src
->fd
, dst
->mem
+ dst_off
, len
);
95 if (!(src
->flags
& FUSE_BUF_FD_RETRY
))
106 static ssize_t
fuse_buf_fd_to_fd(const struct fuse_buf
*dst
, size_t dst_off
,
107 const struct fuse_buf
*src
, size_t src_off
,
111 struct fuse_buf tmp
= {
121 size_t this_len
= min_size(tmp
.size
, len
);
124 res
= fuse_buf_read(&tmp
, 0, src
, src_off
, this_len
);
134 res
= fuse_buf_write(dst
, dst_off
, &tmp
, 0, read_len
);
157 static ssize_t
fuse_buf_splice(const struct fuse_buf
*dst
, size_t dst_off
,
158 const struct fuse_buf
*src
, size_t src_off
,
159 size_t len
, enum fuse_buf_copy_flags flags
)
161 int splice_flags
= 0;
162 off_t
*srcpos
= NULL
;
163 off_t
*dstpos
= NULL
;
169 if (flags
& FUSE_BUF_SPLICE_MOVE
)
170 splice_flags
|= SPLICE_F_MOVE
;
171 if (flags
& FUSE_BUF_SPLICE_NONBLOCK
)
172 splice_flags
|= SPLICE_F_NONBLOCK
;
174 if (src
->flags
& FUSE_BUF_FD_SEEK
) {
175 srcpos_val
= src
->pos
+ src_off
;
176 srcpos
= &srcpos_val
;
178 if (dst
->flags
& FUSE_BUF_FD_SEEK
) {
179 dstpos_val
= dst
->pos
+ dst_off
;
180 dstpos
= &dstpos_val
;
184 res
= splice(src
->fd
, srcpos
, dst
->fd
, dstpos
, len
,
190 if (errno
!= EINVAL
|| (flags
& FUSE_BUF_FORCE_SPLICE
))
193 /* Maybe splice is not supported for this combination */
194 return fuse_buf_fd_to_fd(dst
, dst_off
, src
, src_off
,
201 if (!(src
->flags
& FUSE_BUF_FD_RETRY
) &&
202 !(dst
->flags
& FUSE_BUF_FD_RETRY
)) {
212 static ssize_t
fuse_buf_splice(const struct fuse_buf
*dst
, size_t dst_off
,
213 const struct fuse_buf
*src
, size_t src_off
,
214 size_t len
, enum fuse_buf_copy_flags flags
)
218 return fuse_buf_fd_to_fd(dst
, dst_off
, src
, src_off
, len
);
223 static ssize_t
fuse_buf_copy_one(const struct fuse_buf
*dst
, size_t dst_off
,
224 const struct fuse_buf
*src
, size_t src_off
,
225 size_t len
, enum fuse_buf_copy_flags flags
)
227 int src_is_fd
= src
->flags
& FUSE_BUF_IS_FD
;
228 int dst_is_fd
= dst
->flags
& FUSE_BUF_IS_FD
;
230 if (!src_is_fd
&& !dst_is_fd
) {
231 void *dstmem
= dst
->mem
+ dst_off
;
232 void *srcmem
= src
->mem
+ src_off
;
234 if (dstmem
!= srcmem
) {
235 if (dstmem
+ len
<= srcmem
|| srcmem
+ len
<= dstmem
)
236 memcpy(dstmem
, srcmem
, len
);
238 memmove(dstmem
, srcmem
, len
);
242 } else if (!src_is_fd
) {
243 return fuse_buf_write(dst
, dst_off
, src
, src_off
, len
);
244 } else if (!dst_is_fd
) {
245 return fuse_buf_read(dst
, dst_off
, src
, src_off
, len
);
246 } else if (flags
& FUSE_BUF_NO_SPLICE
) {
247 return fuse_buf_fd_to_fd(dst
, dst_off
, src
, src_off
, len
);
249 return fuse_buf_splice(dst
, dst_off
, src
, src_off
, len
, flags
);
253 static const struct fuse_buf
*fuse_bufvec_current(struct fuse_bufvec
*bufv
)
255 if (bufv
->idx
< bufv
->count
)
256 return &bufv
->buf
[bufv
->idx
];
261 static int fuse_bufvec_advance(struct fuse_bufvec
*bufv
, size_t len
)
263 const struct fuse_buf
*buf
= fuse_bufvec_current(bufv
);
266 assert(bufv
->off
<= buf
->size
);
267 if (bufv
->off
== buf
->size
) {
268 assert(bufv
->idx
< bufv
->count
);
270 if (bufv
->idx
== bufv
->count
)
277 ssize_t
fuse_buf_copy(struct fuse_bufvec
*dstv
, struct fuse_bufvec
*srcv
,
278 enum fuse_buf_copy_flags flags
)
283 return fuse_buf_size(dstv
);
286 const struct fuse_buf
*src
= fuse_bufvec_current(srcv
);
287 const struct fuse_buf
*dst
= fuse_bufvec_current(dstv
);
293 if (src
== NULL
|| dst
== NULL
)
296 src_len
= src
->size
- srcv
->off
;
297 dst_len
= dst
->size
- dstv
->off
;
298 len
= min_size(src_len
, dst_len
);
300 res
= fuse_buf_copy_one(dst
, dstv
->off
, src
, srcv
->off
, len
, flags
);
308 if (!fuse_bufvec_advance(srcv
, res
) ||
309 !fuse_bufvec_advance(dstv
, res
))