1 /*-
2 * Copyright (c) 1991, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * Bernd Schubert <bernd-schubert@gmx.de>:
30 * This file was taken from OpenBSD and modified to fit the unionfs requirements.
31 */
32
33
34 #include <err.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <utime.h>
42 #include <sys/mman.h>
43 #include <sys/stat.h>
44
45 #include "unionfs.h"
46 #include "cow_utils.h"
47 #include "debug.h"
48 #include "general.h"
49 #include "usyslog.h"
50
51 // BSD seems to know S_ISTXT itself
52 #ifndef S_ISTXT
53 #define S_ISTXT S_ISVTX
54 #endif
55
56 /**
57 * set the stat() data of a file
58 **/
setfile(const char * path,struct stat * fs)59 int setfile(const char *path, struct stat *fs)
60 {
61 DBG("%s\n", path);
62
63 struct timespec ut[2];
64 int rval = 0;
65
66 fs->st_mode &= S_ISUID | S_ISGID | S_ISTXT | S_IRWXU | S_IRWXG | S_IRWXO;
67
68 ut[0] = fs->st_atim;
69 ut[1] = fs->st_mtim;
70 if (utimensat(AT_FDCWD, path, ut, 0)) {
71 USYSLOG(LOG_WARNING, "utimensat: %s", path);
72 rval = 1;
73 }
74 /*
75 * Changing the ownership probably won't succeed, unless we're root
76 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
77 * the mode; current BSD behavior is to remove all setuid bits on
78 * chown. If chown fails, lose setuid/setgid bits.
79 */
80 if (chown(path, fs->st_uid, fs->st_gid)) {
81 if (errno != EPERM) {
82 USYSLOG(LOG_WARNING, "chown: %s", path);
83 rval = 1;
84 }
85 fs->st_mode &= ~(S_ISTXT | S_ISUID | S_ISGID);
86 }
87
88 if (chmod(path, fs->st_mode)) {
89 USYSLOG(LOG_WARNING, "chown: %s", path);
90 rval = 1;
91 }
92
93 #ifdef HAVE_CHFLAGS
94 /*
95 * XXX
96 * NFS doesn't support chflags; ignore errors unless there's reason
97 * to believe we're losing bits. (Note, this still won't be right
98 * if the server supports flags and we were trying to *remove* flags
99 * on a file that we copied, i.e., that we didn't create.)
100 */
101 errno = 0;
102 if (chflags(path, fs->st_flags)) {
103 if (errno != EOPNOTSUPP || fs->st_flags != 0) {
104 USYSLOG(LOG_WARNING, "chflags: %s", path);
105 rval = 1;
106 }
107 RETURN(rval);
108 }
109 #endif
110 RETURN(rval);
111 }
112
113 /**
114 * set the stat() data of a link
115 **/
setlink(const char * path,struct stat * fs)116 static int setlink(const char *path, struct stat *fs)
117 {
118 DBG("%s\n", path);
119
120 if (lchown(path, fs->st_uid, fs->st_gid)) {
121 if (errno != EPERM) {
122 USYSLOG(LOG_WARNING, "lchown: %s", path);
123 RETURN(1);
124 }
125 }
126 RETURN(0);
127 }
128
129
130 /**
131 * copy an ordinary file with all of its stat() data
132 **/
copy_file(struct cow * cow)133 int copy_file(struct cow *cow)
134 {
135 DBG("from %s to %s\n", cow->from_path, cow->to_path);
136
137 static char buf[MAXBSIZE];
138 struct stat to_stat, *fs;
139 int from_fd, rcount, to_fd, wcount;
140 int rval = 0;
141 #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
142 char *p;
143 #endif
144
145 if ((from_fd = open(cow->from_path, O_RDONLY, 0)) == -1) {
146 USYSLOG(LOG_WARNING, "%s", cow->from_path);
147 RETURN(1);
148 }
149
150 fs = cow->stat;
151
152 to_fd = open(cow->to_path, O_WRONLY | O_TRUNC | O_CREAT,
153 fs->st_mode & ~(S_ISTXT | S_ISUID | S_ISGID));
154
155 if (to_fd == -1) {
156 USYSLOG(LOG_WARNING, "%s", cow->to_path);
157 (void)close(from_fd);
158 RETURN(1);
159 }
160
161 /*
162 * Mmap and write if less than 8M (the limit is so we don't totally
163 * trash memory on big files. This is really a minor hack, but it
164 * wins some CPU back.
165 */
166 #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
167 if (fs->st_size > 0 && fs->st_size <= 8 * 1048576) {
168 if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
169 MAP_FILE|MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
170 USYSLOG(LOG_WARNING, "mmap: %s", cow->from_path);
171 rval = 1;
172 } else {
173 madvise(p, fs->st_size, MADV_SEQUENTIAL);
174 if (write(to_fd, p, fs->st_size) != fs->st_size) {
175 USYSLOG(LOG_WARNING, "%s", cow->to_path);
176 rval = 1;
177 }
178 /* Some systems don't unmap on close(2). */
179 if (munmap(p, fs->st_size) < 0) {
180 USYSLOG(LOG_WARNING, "%s", cow->from_path);
181 rval = 1;
182 }
183 }
184 } else
185 #endif
186 {
187 while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
188 wcount = write(to_fd, buf, rcount);
189 if (rcount != wcount || wcount == -1) {
190 USYSLOG(LOG_WARNING, "%s", cow->to_path);
191 rval = 1;
192 break;
193 }
194 }
195 if (rcount < 0) {
196 USYSLOG(LOG_WARNING, "copy failed: %s", cow->from_path);
197 rval = 1;
198 }
199 }
200
201 if (rval == 1) {
202 (void)close(from_fd);
203 (void)close(to_fd);
204 RETURN(1);
205 }
206
207 if (setfile(cow->to_path, cow->stat))
208 rval = 1;
209 /*
210 * If the source was setuid or setgid, lose the bits unless the
211 * copy is owned by the same user and group.
212 */
213 #define RETAINBITS \
214 (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
215 else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == cow->uid) {
216 if (fstat(to_fd, &to_stat)) {
217 USYSLOG(LOG_WARNING, "%s", cow->to_path);
218 rval = 1;
219 } else if (fs->st_gid == to_stat.st_gid &&
220 fchmod(to_fd, fs->st_mode & RETAINBITS & ~cow->umask)) {
221 USYSLOG(LOG_WARNING, "%s", cow->to_path);
222 rval = 1;
223 }
224 }
225 (void)close(from_fd);
226 if (close(to_fd)) {
227 USYSLOG(LOG_WARNING, "%s", cow->to_path);
228 rval = 1;
229 }
230
231 RETURN(rval);
232 }
233
234 /**
235 * copy a link, actually we recreate the link and only copy its stat() data.
236 */
copy_link(struct cow * cow)237 int copy_link(struct cow *cow)
238 {
239 DBG("from %s to %s\n", cow->from_path, cow->to_path);
240
241 int len;
242 char link[PATHLEN_MAX];
243
244 if ((len = readlink(cow->from_path, link, sizeof(link)-1)) == -1) {
245 USYSLOG(LOG_WARNING, "readlink: %s", cow->from_path);
246 RETURN(1);
247 }
248
249 link[len] = '\0';
250
251 if (symlink(link, cow->to_path)) {
252 USYSLOG(LOG_WARNING, "symlink: %s", link);
253 RETURN(1);
254 }
255
256 RETURN(setlink(cow->to_path, cow->stat));
257 }
258
259 /**
260 * copy a fifo, actually we recreate the fifo and only copy
261 * its stat() data
262 **/
copy_fifo(struct cow * cow)263 int copy_fifo(struct cow *cow)
264 {
265 DBG("from %s to %s\n", cow->from_path, cow->to_path);
266
267 if (mkfifo(cow->to_path, cow->stat->st_mode)) {
268 USYSLOG(LOG_WARNING, "mkfifo: %s", cow->to_path);
269 RETURN(1);
270 }
271 RETURN(setfile(cow->to_path, cow->stat));
272 }
273
274 /**
275 * copy a special file, actually we recreate this file and only copy
276 * its stat() data
277 */
copy_special(struct cow * cow)278 int copy_special(struct cow *cow)
279 {
280 DBG("from %s to %s\n", cow->from_path, cow->to_path);
281
282 if (mknod(cow->to_path, cow->stat->st_mode, cow->stat->st_rdev)) {
283 USYSLOG(LOG_WARNING, "mknod: %s", cow->to_path);
284 RETURN(1);
285 }
286 RETURN(setfile(cow->to_path, cow->stat));
287 }
288