1 /* $OpenBSD: copy.c,v 1.1 2021/10/22 11:10:34 claudio Exp $ */ 2 /* 3 * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> /* MAXBSIZE */ 19 20 #include <err.h> 21 #include <fcntl.h> 22 #include <unistd.h> 23 24 #include "extern.h" 25 26 /* 27 * Return true if all bytes in buffer are zero. 28 * A buffer of zero lenght is also considered a zero buffer. 29 */ 30 static int 31 iszero(const void *b, size_t len) 32 { 33 const unsigned char *c = b; 34 35 for (; len > 0; len--) { 36 if (*c++ != '\0') 37 return 0; 38 } 39 return 1; 40 } 41 42 static int 43 copy_internal(int fromfd, int tofd) 44 { 45 char buf[MAXBSIZE]; 46 ssize_t r, w; 47 48 while ((r = read(fromfd, buf, sizeof(buf))) > 0) { 49 if (iszero(buf, sizeof(buf))) { 50 if (lseek(tofd, r, SEEK_CUR) == -1) 51 return -1; 52 } else { 53 w = write(tofd, buf, r); 54 if (r != w || w == -1) 55 return -1; 56 } 57 } 58 if (r == -1) 59 return -1; 60 if (ftruncate(tofd, lseek(tofd, 0, SEEK_CUR)) == -1) 61 return -1; 62 return 0; 63 } 64 65 void 66 copy_file(int rootfd, const char *basedir, const struct flist *f) 67 { 68 int fromfd, tofd, dfd; 69 70 dfd = openat(rootfd, basedir, O_RDONLY | O_DIRECTORY, 0); 71 if (dfd == -1) 72 err(ERR_FILE_IO, "%s: openat", basedir); 73 74 fromfd = openat(dfd, f->path, O_RDONLY | O_NOFOLLOW, 0); 75 if (fromfd == -1) 76 err(ERR_FILE_IO, "%s/%s: openat", basedir, f->path); 77 close(dfd); 78 79 tofd = openat(rootfd, f->path, 80 O_WRONLY | O_NOFOLLOW | O_TRUNC | O_CREAT | O_EXCL, 81 0600); 82 if (tofd == -1) 83 err(ERR_FILE_IO, "%s: openat", f->path); 84 85 if (copy_internal(fromfd, tofd) == -1) 86 err(ERR_FILE_IO, "%s: copy file", f->path); 87 88 close(fromfd); 89 close(tofd); 90 } 91