Commit | Line | Data |
---|---|---|
602f2e1c | 1 | /*- |
ba5e8546 KB |
2 | * Copyright (c) 1991, 1993 |
3 | * The Regents of the University of California. All rights reserved. | |
602f2e1c EA |
4 | * |
5 | * %sccs.include.redist.c% | |
6 | */ | |
7 | ||
8 | #ifndef lint | |
ba5e8546 | 9 | static char sccsid[] = "@(#)utils.c 8.1 (Berkeley) %G%"; |
602f2e1c EA |
10 | #endif /* not lint */ |
11 | ||
12 | #include <sys/param.h> | |
13 | #include <sys/stat.h> | |
14 | #include <sys/mman.h> | |
15 | #include <sys/time.h> | |
9d313fa2 | 16 | |
602f2e1c | 17 | #include <errno.h> |
9d313fa2 KB |
18 | #include <fcntl.h> |
19 | #include <fts.h> | |
00215c12 | 20 | #include <stdio.h> |
00215c12 | 21 | #include <stdlib.h> |
2f36fbb8 | 22 | #include <string.h> |
9d313fa2 KB |
23 | #include <unistd.h> |
24 | ||
602f2e1c | 25 | #include "extern.h" |
00215c12 EA |
26 | |
27 | void | |
602f2e1c EA |
28 | copy_file(entp, dne) |
29 | FTSENT *entp; | |
00215c12 EA |
30 | int dne; |
31 | { | |
32 | static char buf[MAXBSIZE]; | |
33 | register int from_fd, to_fd, rcount, wcount; | |
602f2e1c | 34 | struct stat to_stat, *fs; |
00215c12 | 35 | char *p; |
602f2e1c | 36 | |
00215c12 | 37 | |
602f2e1c EA |
38 | if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { |
39 | err("%s: %s", entp->fts_path, strerror(errno)); | |
00215c12 EA |
40 | return; |
41 | } | |
42 | ||
602f2e1c EA |
43 | fs = entp->fts_statp; |
44 | ||
00215c12 EA |
45 | /* |
46 | * If the file exists and we're interactive, verify with the user. | |
47 | * If the file DNE, set the mode to be the from file, minus setuid | |
48 | * bits, modified by the umask; arguably wrong, but it makes copying | |
49 | * executables work right and it's been that way forever. (The | |
50 | * other choice is 666 or'ed with the execute bits on the from file | |
51 | * modified by the umask.) | |
52 | */ | |
53 | if (!dne) { | |
54 | if (iflag) { | |
55 | int checkch, ch; | |
56 | ||
57 | (void)fprintf(stderr, "overwrite %s? ", to.p_path); | |
58 | checkch = ch = getchar(); | |
59 | while (ch != '\n' && ch != EOF) | |
60 | ch = getchar(); | |
61 | if (checkch != 'y') { | |
62 | (void)close(from_fd); | |
63 | return; | |
64 | } | |
65 | } | |
66 | to_fd = open(to.p_path, O_WRONLY|O_TRUNC, 0); | |
67 | } else | |
68 | to_fd = open(to.p_path, O_WRONLY|O_CREAT|O_TRUNC, | |
69 | fs->st_mode & ~(S_ISUID|S_ISGID)); | |
70 | ||
71 | if (to_fd == -1) { | |
72 | err("%s: %s", to.p_path, strerror(errno)); | |
73 | (void)close(from_fd); | |
74 | return; | |
75 | } | |
76 | ||
77 | /* | |
78 | * Mmap and write if less than 8M (the limit is so we don't totally | |
79 | * trash memory on big files. This is really a minor hack, but it | |
80 | * wins some CPU back. | |
81 | */ | |
bd19fe6d | 82 | #ifdef VM_AND_BUFFER_CACHE_FIXED |
00215c12 | 83 | if (fs->st_size <= 8 * 1048576) { |
a7191973 | 84 | if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, |
b98a719e | 85 | 0, from_fd, (off_t)0)) == (char *)-1) |
602f2e1c | 86 | err("%s: %s", entp->fts_path, strerror(errno)); |
00215c12 EA |
87 | if (write(to_fd, p, fs->st_size) != fs->st_size) |
88 | err("%s: %s", to.p_path, strerror(errno)); | |
bd19fe6d KB |
89 | } else |
90 | #endif | |
91 | { | |
00215c12 EA |
92 | while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { |
93 | wcount = write(to_fd, buf, rcount); | |
94 | if (rcount != wcount || wcount == -1) { | |
95 | err("%s: %s", to.p_path, strerror(errno)); | |
96 | break; | |
97 | } | |
98 | } | |
99 | if (rcount < 0) | |
602f2e1c | 100 | err("%s: %s", entp->fts_path, strerror(errno)); |
00215c12 EA |
101 | } |
102 | if (pflag) | |
103 | setfile(fs, to_fd); | |
104 | /* | |
105 | * If the source was setuid or setgid, lose the bits unless the | |
106 | * copy is owned by the same user and group. | |
107 | */ | |
108 | else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid) | |
109 | if (fstat(to_fd, &to_stat)) | |
110 | err("%s: %s", to.p_path, strerror(errno)); | |
111 | #define RETAINBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) | |
112 | else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd, | |
113 | fs->st_mode & RETAINBITS & ~myumask)) | |
114 | err("%s: %s", to.p_path, strerror(errno)); | |
115 | (void)close(from_fd); | |
116 | if (close(to_fd)) | |
117 | err("%s: %s", to.p_path, strerror(errno)); | |
118 | } | |
119 | ||
120 | void | |
602f2e1c EA |
121 | copy_link(p, exists) |
122 | FTSENT *p; | |
00215c12 EA |
123 | int exists; |
124 | { | |
125 | int len; | |
126 | char link[MAXPATHLEN]; | |
127 | ||
602f2e1c EA |
128 | if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) { |
129 | err("readlink: %s: %s", p->fts_path, strerror(errno)); | |
00215c12 EA |
130 | return; |
131 | } | |
132 | link[len] = '\0'; | |
133 | if (exists && unlink(to.p_path)) { | |
134 | err("unlink: %s: %s", to.p_path, strerror(errno)); | |
135 | return; | |
136 | } | |
137 | if (symlink(link, to.p_path)) { | |
138 | err("symlink: %s: %s", link, strerror(errno)); | |
139 | return; | |
140 | } | |
141 | } | |
142 | ||
143 | void | |
144 | copy_fifo(from_stat, exists) | |
145 | struct stat *from_stat; | |
146 | int exists; | |
147 | { | |
148 | if (exists && unlink(to.p_path)) { | |
149 | err("unlink: %s: %s", to.p_path, strerror(errno)); | |
150 | return; | |
151 | } | |
152 | if (mkfifo(to.p_path, from_stat->st_mode)) { | |
153 | err("mkfifo: %s: %s", to.p_path, strerror(errno)); | |
154 | return; | |
155 | } | |
156 | if (pflag) | |
157 | setfile(from_stat, 0); | |
158 | } | |
159 | ||
160 | void | |
161 | copy_special(from_stat, exists) | |
162 | struct stat *from_stat; | |
163 | int exists; | |
164 | { | |
165 | if (exists && unlink(to.p_path)) { | |
166 | err("unlink: %s: %s", to.p_path, strerror(errno)); | |
167 | return; | |
168 | } | |
169 | if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { | |
170 | err("mknod: %s: %s", to.p_path, strerror(errno)); | |
171 | return; | |
172 | } | |
173 | if (pflag) | |
174 | setfile(from_stat, 0); | |
175 | } | |
176 | ||
177 | ||
178 | void | |
179 | setfile(fs, fd) | |
180 | register struct stat *fs; | |
181 | int fd; | |
182 | { | |
183 | static struct timeval tv[2]; | |
184 | ||
185 | fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; | |
186 | ||
9d313fa2 KB |
187 | TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); |
188 | TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); | |
00215c12 EA |
189 | if (utimes(to.p_path, tv)) |
190 | err("utimes: %s: %s", to.p_path, strerror(errno)); | |
191 | /* | |
192 | * Changing the ownership probably won't succeed, unless we're root | |
193 | * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting | |
194 | * the mode; current BSD behavior is to remove all setuid bits on | |
195 | * chown. If chown fails, lose setuid/setgid bits. | |
196 | */ | |
197 | if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : | |
198 | chown(to.p_path, fs->st_uid, fs->st_gid)) { | |
199 | if (errno != EPERM) | |
200 | err("chown: %s: %s", to.p_path, strerror(errno)); | |
201 | fs->st_mode &= ~(S_ISUID|S_ISGID); | |
202 | } | |
203 | if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) | |
204 | err("chown: %s: %s", to.p_path, strerror(errno)); | |
a7b841a8 KB |
205 | |
206 | if (fd ? fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) | |
207 | err("chflags: %s: %s", to.p_path, strerror(errno)); | |
00215c12 EA |
208 | } |
209 | ||
210 | void | |
211 | usage() | |
212 | { | |
213 | (void)fprintf(stderr, | |
602f2e1c | 214 | "usage: cp [-HRfhip] src target;\n cp [-HRfhip] src1 ... srcN directory\n"); |
00215c12 EA |
215 | exit(1); |
216 | } | |
217 | ||
218 | #if __STDC__ | |
219 | #include <stdarg.h> | |
220 | #else | |
221 | #include <varargs.h> | |
222 | #endif | |
223 | ||
224 | void | |
225 | #if __STDC__ | |
226 | err(const char *fmt, ...) | |
227 | #else | |
228 | err(fmt, va_alist) | |
229 | char *fmt; | |
230 | va_dcl | |
231 | #endif | |
232 | { | |
233 | va_list ap; | |
234 | #if __STDC__ | |
235 | va_start(ap, fmt); | |
236 | #else | |
237 | va_start(ap); | |
238 | #endif | |
239 | (void)fprintf(stderr, "%s: ", progname); | |
240 | (void)vfprintf(stderr, fmt, ap); | |
241 | va_end(ap); | |
242 | (void)fprintf(stderr, "\n"); | |
243 | exit_val = 1; | |
244 | } |