xref: /openbsd-src/usr.bin/rsync/copy.c (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
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