buf.c revision 1.1
1/* $NetBSD: buf.c,v 1.1 2022/01/22 07:54:57 pho Exp $ */ 2 3/* 4 * Copyright (c) 2021 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote 16 * products derived from this software without specific prior written 17 * permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 25 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33#if !defined(lint) 34__RCSID("$NetBSD: buf.c,v 1.1 2022/01/22 07:54:57 pho Exp $"); 35#endif /* !lint */ 36 37#include <assert.h> 38#include <errno.h> 39#include <fuse_internal.h> 40#include <machine/vmparam.h> /* for PAGE_SIZE */ 41#include <stdbool.h> 42#include <stdint.h> 43#include <stdlib.h> 44#include <string.h> 45#include <sys/param.h> /* for MIN(a, b) */ 46#include <unistd.h> 47 48size_t 49fuse_buf_size(const struct fuse_bufvec *bufv) { 50 size_t i; 51 size_t total = 0; 52 53 for (i = 0; i < bufv->count; i++) { 54 total += bufv->buf[i].size; 55 } 56 57 return total; 58} 59 60/* Return the pointer to the current buffer in a buffer vector, or 61 * NULL if we have reached the end of the vector. */ 62static const struct fuse_buf* 63fuse_buf_current(const struct fuse_bufvec *bufv) { 64 if (bufv->idx < bufv->count) 65 return &bufv->buf[bufv->idx]; 66 else 67 return NULL; 68} 69 70/* Copy data from one fd to a memory buffer, and return the number of 71 * octets that have been copied, or -1 on failure. */ 72static ssize_t 73fuse_buf_read_fd_to_mem(const struct fuse_buf *dst, size_t dst_off, 74 const struct fuse_buf *src, size_t src_off, 75 size_t len) { 76 ssize_t total = 0; 77 78 while (len > 0) { 79 ssize_t n_read; 80 81 if (src->flags & FUSE_BUF_FD_SEEK) 82 n_read = pread(src->fd, (uint8_t*)dst->mem + dst_off, len, 83 src->pos + (off_t)src_off); 84 else 85 n_read = read(src->fd, (uint8_t*)dst->mem + dst_off, len); 86 87 if (n_read == -1) { 88 if (errno == EINTR) 89 continue; 90 else if (total == 0) 91 return -1; 92 else 93 /* The last pread(2) or read(2) failed but we have 94 * already copied some data. */ 95 break; 96 } 97 else if (n_read == 0) { 98 /* Reached EOF */ 99 break; 100 } 101 else { 102 total += n_read; 103 dst_off += (size_t)n_read; 104 src_off += (size_t)n_read; 105 len -= (size_t)n_read; 106 107 if (src->flags & FUSE_BUF_FD_RETRY) 108 continue; 109 } 110 } 111 112 return total; 113} 114 115/* Copy data from one memory buffer to an fd, and return the number of 116 * octets that have been copied, or -1 on failure. */ 117static ssize_t 118fuse_buf_write_mem_to_fd(const struct fuse_buf *dst, size_t dst_off, 119 const struct fuse_buf *src, size_t src_off, 120 size_t len) { 121 ssize_t total = 0; 122 123 while (len > 0) { 124 ssize_t n_wrote; 125 126 if (dst->flags & FUSE_BUF_FD_SEEK) 127 n_wrote = pwrite(dst->fd, (uint8_t*)src->mem + src_off, len, 128 dst->pos + (off_t)dst_off); 129 else 130 n_wrote = write(dst->fd, (uint8_t*)src->mem + src_off, len); 131 132 if (n_wrote == -1) { 133 if (errno == EINTR) 134 continue; 135 else if (total == 0) 136 return -1; 137 else 138 /* The last pwrite(2) or write(2) failed but we have 139 * already copied some data. */ 140 break; 141 } 142 else if (n_wrote == 0) { 143 break; 144 } 145 else { 146 total += n_wrote; 147 dst_off += (size_t)n_wrote; 148 src_off += (size_t)n_wrote; 149 len -= (size_t)n_wrote; 150 151 if (dst->flags & FUSE_BUF_FD_RETRY) 152 continue; 153 } 154 } 155 156 return total; 157} 158 159/* Copy data from one fd to another, and return the number of octets 160 * that have been copied, or -1 on failure. */ 161static ssize_t 162fuse_buf_copy_fd_to_fd(const struct fuse_buf *dst, size_t dst_off, 163 const struct fuse_buf *src, size_t src_off, 164 size_t len) { 165 ssize_t total = 0; 166 struct fuse_buf tmp; 167 168 tmp.size = PAGE_SIZE; 169 tmp.flags = (enum fuse_buf_flags)0; 170 tmp.mem = malloc(tmp.size); 171 172 if (tmp.mem == NULL) { 173 return -1; 174 } 175 176 while (len) { 177 size_t n_to_read = MIN(tmp.size, len); 178 ssize_t n_read; 179 ssize_t n_wrote; 180 181 n_read = fuse_buf_read_fd_to_mem(&tmp, 0, src, src_off, n_to_read); 182 if (n_read == -1) { 183 if (total == 0) { 184 free(tmp.mem); 185 return -1; 186 } 187 else { 188 /* We have already copied some data. */ 189 break; 190 } 191 } 192 else if (n_read == 0) { 193 /* Reached EOF */ 194 break; 195 } 196 197 n_wrote = fuse_buf_write_mem_to_fd(dst, dst_off, &tmp, 0, (size_t)n_read); 198 if (n_wrote == -1) { 199 if (total == 0) { 200 free(tmp.mem); 201 return -1; 202 } 203 else { 204 /* We have already copied some data. */ 205 break; 206 } 207 } 208 else if (n_wrote == 0) { 209 break; 210 } 211 212 total += n_wrote; 213 dst_off += (size_t)n_wrote; 214 src_off += (size_t)n_wrote; 215 len -= (size_t)n_wrote; 216 217 if (n_wrote < n_read) 218 /* Now we have some data that were read but couldn't be 219 * written, and can't do anything about it. */ 220 break; 221 } 222 223 free(tmp.mem); 224 return total; 225} 226 227/* Copy data from one buffer to another, and return the number of 228 * octets that have been copied. */ 229static ssize_t 230fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off, 231 const struct fuse_buf *src, size_t src_off, 232 size_t len, 233 enum fuse_buf_copy_flags flags __attribute__((__unused__))) { 234 235 const bool dst_is_fd = !!(dst->flags & FUSE_BUF_IS_FD); 236 const bool src_is_fd = !!(src->flags & FUSE_BUF_IS_FD); 237 238 if (!dst_is_fd && !src_is_fd) { 239 void* dst_mem = (uint8_t*)dst->mem + dst_off; 240 void* src_mem = (uint8_t*)src->mem + src_off; 241 242 memmove(dst_mem, src_mem, len); 243 244 return (ssize_t)len; 245 } 246 else if (!dst_is_fd) { 247 return fuse_buf_read_fd_to_mem(dst, dst_off, src, src_off, len); 248 } 249 else if (!src_is_fd) { 250 return fuse_buf_write_mem_to_fd(dst, dst_off, src, src_off, len); 251 } 252 else { 253 return fuse_buf_copy_fd_to_fd(dst, dst_off, src, src_off, len); 254 } 255} 256 257/* Advance the buffer by a given number of octets. Return 0 on 258 * success, or -1 otherwise. Reaching the end of the buffer vector 259 * counts as a failure. */ 260static int 261fuse_buf_advance(struct fuse_bufvec *bufv, size_t len) { 262 const struct fuse_buf *buf = fuse_buf_current(bufv); 263 264 assert(bufv->off + len <= buf->size); 265 bufv->off += len; 266 if (bufv->off == buf->size) { 267 /* Done with the current buffer. Advance to the next one. */ 268 assert(bufv->idx < bufv->count); 269 bufv->idx++; 270 if (bufv->idx == bufv->count) 271 return -1; /* No more buffers in the vector. */ 272 bufv->off = 0; 273 } 274 return 0; 275} 276 277ssize_t 278fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv, 279 enum fuse_buf_copy_flags flags) { 280 ssize_t total = 0; 281 282 while (true) { 283 const struct fuse_buf* dst; 284 const struct fuse_buf* src; 285 size_t src_len; 286 size_t dst_len; 287 size_t len; 288 ssize_t n_copied; 289 290 dst = fuse_buf_current(dstv); 291 src = fuse_buf_current(srcv); 292 if (src == NULL || dst == NULL) 293 break; 294 295 src_len = src->size - srcv->off; 296 dst_len = dst->size - dstv->off; 297 len = MIN(src_len, dst_len); 298 299 n_copied = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags); 300 if (n_copied == -1) { 301 if (total == 0) 302 return -1; 303 else 304 /* Failed to copy the current buffer but we have 305 * already copied some part of the vector. It is 306 * therefore inappropriate to return an error. */ 307 break; 308 } 309 total += n_copied; 310 311 if (fuse_buf_advance(srcv, (size_t)n_copied) != 0 || 312 fuse_buf_advance(dstv, (size_t)n_copied) != 0) 313 break; 314 315 if ((size_t)n_copied < len) 316 break; 317 } 318 319 return total; 320} 321