xref: /csrg-svn/bin/cp/utils.c (revision 53607)
1*53607Selan #include <stdio.h>
2*53607Selan #include <fts.h>
3*53607Selan #include <stdlib.h>
4*53607Selan 
5*53607Selan 
6*53607Selan void
7*53607Selan copy_file(fs, dne)
8*53607Selan 	struct stat *fs;
9*53607Selan 	int dne;
10*53607Selan {
11*53607Selan 	static char buf[MAXBSIZE];
12*53607Selan 	register int from_fd, to_fd, rcount, wcount;
13*53607Selan 	struct stat to_stat;
14*53607Selan 	char *p;
15*53607Selan 
16*53607Selan 	if ((from_fd = open(from.p_path, O_RDONLY, 0)) == -1) {
17*53607Selan 		err("%s: %s", from.p_path, strerror(errno));
18*53607Selan 		return;
19*53607Selan 	}
20*53607Selan 
21*53607Selan 	/*
22*53607Selan 	 * If the file exists and we're interactive, verify with the user.
23*53607Selan 	 * If the file DNE, set the mode to be the from file, minus setuid
24*53607Selan 	 * bits, modified by the umask; arguably wrong, but it makes copying
25*53607Selan 	 * executables work right and it's been that way forever.  (The
26*53607Selan 	 * other choice is 666 or'ed with the execute bits on the from file
27*53607Selan 	 * modified by the umask.)
28*53607Selan 	 */
29*53607Selan 	if (!dne) {
30*53607Selan 		if (iflag) {
31*53607Selan 			int checkch, ch;
32*53607Selan 
33*53607Selan 			(void)fprintf(stderr, "overwrite %s? ", to.p_path);
34*53607Selan 			checkch = ch = getchar();
35*53607Selan 			while (ch != '\n' && ch != EOF)
36*53607Selan 				ch = getchar();
37*53607Selan 			if (checkch != 'y') {
38*53607Selan 				(void)close(from_fd);
39*53607Selan 				return;
40*53607Selan 			}
41*53607Selan 		}
42*53607Selan 		to_fd = open(to.p_path, O_WRONLY|O_TRUNC, 0);
43*53607Selan 	} else
44*53607Selan 		to_fd = open(to.p_path, O_WRONLY|O_CREAT|O_TRUNC,
45*53607Selan 		    fs->st_mode & ~(S_ISUID|S_ISGID));
46*53607Selan 
47*53607Selan 	if (to_fd == -1) {
48*53607Selan 		err("%s: %s", to.p_path, strerror(errno));
49*53607Selan 		(void)close(from_fd);
50*53607Selan 		return;
51*53607Selan 	}
52*53607Selan 
53*53607Selan 	/*
54*53607Selan 	 * Mmap and write if less than 8M (the limit is so we don't totally
55*53607Selan 	 * trash memory on big files.  This is really a minor hack, but it
56*53607Selan 	 * wins some CPU back.
57*53607Selan 	 */
58*53607Selan 	if (fs->st_size <= 8 * 1048576) {
59*53607Selan 		if ((p = mmap(NULL, fs->st_size, PROT_READ,
60*53607Selan 		    MAP_FILE, from_fd, (off_t)0)) == (char *)-1)
61*53607Selan 			err("%s: %s", from.p_path, strerror(errno));
62*53607Selan 		if (write(to_fd, p, fs->st_size) != fs->st_size)
63*53607Selan 			err("%s: %s", to.p_path, strerror(errno));
64*53607Selan 	} else {
65*53607Selan 		while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
66*53607Selan 			wcount = write(to_fd, buf, rcount);
67*53607Selan 			if (rcount != wcount || wcount == -1) {
68*53607Selan 				err("%s: %s", to.p_path, strerror(errno));
69*53607Selan 				break;
70*53607Selan 			}
71*53607Selan 		}
72*53607Selan 		if (rcount < 0)
73*53607Selan 			err("%s: %s", from.p_path, strerror(errno));
74*53607Selan 	}
75*53607Selan 	if (pflag)
76*53607Selan 		setfile(fs, to_fd);
77*53607Selan 	/*
78*53607Selan 	 * If the source was setuid or setgid, lose the bits unless the
79*53607Selan 	 * copy is owned by the same user and group.
80*53607Selan 	 */
81*53607Selan 	else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid)
82*53607Selan 		if (fstat(to_fd, &to_stat))
83*53607Selan 			err("%s: %s", to.p_path, strerror(errno));
84*53607Selan #define	RETAINBITS	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
85*53607Selan 		else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd,
86*53607Selan 		    fs->st_mode & RETAINBITS & ~myumask))
87*53607Selan 			err("%s: %s", to.p_path, strerror(errno));
88*53607Selan 	(void)close(from_fd);
89*53607Selan 	if (close(to_fd))
90*53607Selan 		err("%s: %s", to.p_path, strerror(errno));
91*53607Selan }
92*53607Selan 
93*53607Selan void
94*53607Selan copy_dir()
95*53607Selan {
96*53607Selan 	struct stat from_stat;
97*53607Selan 	struct dirent *dp, **dir_list;
98*53607Selan 	register int dir_cnt, i;
99*53607Selan 	char *old_from, *old_to;
100*53607Selan 
101*53607Selan 	register FTS *ftsp;
102*53607Selan 	register FTSENT *p;
103*53607Selan 
104*53607Selan 	dir_cnt = scandir(from.p_path, &dir_list, NULL, NULL);
105*53607Selan 	if (dir_cnt == -1) {
106*53607Selan 		(void)fprintf(stderr, "%s: can't read directory %s.\n",
107*53607Selan 		    progname, from.p_path);
108*53607Selan 		exit_val = 1;
109*53607Selan 	}
110*53607Selan 
111*53607Selan 	/*
112*53607Selan 	 * Instead of handling directory entries in the order they appear
113*53607Selan 	 * on disk, do non-directory files before directory files.
114*53607Selan 	 * There are two reasons to do directories last.  The first is
115*53607Selan 	 * efficiency.  Files tend to be in the same cylinder group as
116*53607Selan 	 * their parent, whereas directories tend not to be.  Copying files
117*53607Selan 	 * all at once reduces seeking.  Second, deeply nested tree's
118*53607Selan 	 * could use up all the file descriptors if we didn't close one
119*53607Selan 	 * directory before recursivly starting on the next.
120*53607Selan 	 */
121*53607Selan 	/* copy files */
122*53607Selan 	for (i = 0; i < dir_cnt; ++i) {
123*53607Selan 		dp = dir_list[i];
124*53607Selan 		if (dp->d_namlen <= 2 && dp->d_name[0] == '.'
125*53607Selan 		    && (dp->d_name[1] == NULL || dp->d_name[1] == '.'))
126*53607Selan 			goto done;
127*53607Selan 		if (!(old_from =
128*53607Selan 		    path_append(&from, dp->d_name, (int)dp->d_namlen)))
129*53607Selan 			goto done;
130*53607Selan 
131*53607Selan 		if (statfcn(from.p_path, &from_stat) < 0) {
132*53607Selan 			err("%s: %s", dp->d_name, strerror(errno));
133*53607Selan 			path_restore(&from, old_from);
134*53607Selan 			goto done;
135*53607Selan 		}
136*53607Selan 		if (S_ISDIR(from_stat.st_mode)) {
137*53607Selan 			path_restore(&from, old_from);
138*53607Selan 			continue;
139*53607Selan 		}
140*53607Selan 		if (old_to = path_append(&to, dp->d_name, (int)dp->d_namlen)) {
141*53607Selan 			copy();
142*53607Selan 			path_restore(&to, old_to);
143*53607Selan 		}
144*53607Selan 		path_restore(&from, old_from);
145*53607Selan done:		dir_list[i] = NULL;
146*53607Selan 		free(dp);
147*53607Selan 	}
148*53607Selan 
149*53607Selan 	/* copy directories */
150*53607Selan 	for (i = 0; i < dir_cnt; ++i) {
151*53607Selan 		dp = dir_list[i];
152*53607Selan 		if (!dp)
153*53607Selan 			continue;
154*53607Selan 		if (!(old_from =
155*53607Selan 		    path_append(&from, dp->d_name, (int)dp->d_namlen))) {
156*53607Selan 			free(dp);
157*53607Selan 			continue;
158*53607Selan 		}
159*53607Selan 		if (!(old_to =
160*53607Selan 		    path_append(&to, dp->d_name, (int)dp->d_namlen))) {
161*53607Selan 			free(dp);
162*53607Selan 			path_restore(&from, old_from);
163*53607Selan 			continue;
164*53607Selan 		}
165*53607Selan 		copy();
166*53607Selan 		free(dp);
167*53607Selan 		path_restore(&from, old_from);
168*53607Selan 		path_restore(&to, old_to);
169*53607Selan 	}
170*53607Selan 	free(dir_list);
171*53607Selan }
172*53607Selan 
173*53607Selan void
174*53607Selan copy_link(exists)
175*53607Selan 	int exists;
176*53607Selan {
177*53607Selan 	int len;
178*53607Selan 	char link[MAXPATHLEN];
179*53607Selan 
180*53607Selan 	if ((len = readlink(from.p_path, link, sizeof(link))) == -1) {
181*53607Selan 		err("readlink: %s: %s", from.p_path, strerror(errno));
182*53607Selan 		return;
183*53607Selan 	}
184*53607Selan 	link[len] = '\0';
185*53607Selan 	if (exists && unlink(to.p_path)) {
186*53607Selan 		err("unlink: %s: %s", to.p_path, strerror(errno));
187*53607Selan 		return;
188*53607Selan 	}
189*53607Selan 	if (symlink(link, to.p_path)) {
190*53607Selan 		err("symlink: %s: %s", link, strerror(errno));
191*53607Selan 		return;
192*53607Selan 	}
193*53607Selan }
194*53607Selan 
195*53607Selan void
196*53607Selan copy_fifo(from_stat, exists)
197*53607Selan 	struct stat *from_stat;
198*53607Selan 	int exists;
199*53607Selan {
200*53607Selan 	if (exists && unlink(to.p_path)) {
201*53607Selan 		err("unlink: %s: %s", to.p_path, strerror(errno));
202*53607Selan 		return;
203*53607Selan 	}
204*53607Selan 	if (mkfifo(to.p_path, from_stat->st_mode)) {
205*53607Selan 		err("mkfifo: %s: %s", to.p_path, strerror(errno));
206*53607Selan 		return;
207*53607Selan 	}
208*53607Selan 	if (pflag)
209*53607Selan 		setfile(from_stat, 0);
210*53607Selan }
211*53607Selan 
212*53607Selan void
213*53607Selan copy_special(from_stat, exists)
214*53607Selan 	struct stat *from_stat;
215*53607Selan 	int exists;
216*53607Selan {
217*53607Selan 	if (exists && unlink(to.p_path)) {
218*53607Selan 		err("unlink: %s: %s", to.p_path, strerror(errno));
219*53607Selan 		return;
220*53607Selan 	}
221*53607Selan 	if (mknod(to.p_path, from_stat->st_mode,  from_stat->st_rdev)) {
222*53607Selan 		err("mknod: %s: %s", to.p_path, strerror(errno));
223*53607Selan 		return;
224*53607Selan 	}
225*53607Selan 	if (pflag)
226*53607Selan 		setfile(from_stat, 0);
227*53607Selan }
228*53607Selan 
229*53607Selan 
230*53607Selan void
231*53607Selan setfile(fs, fd)
232*53607Selan 	register struct stat *fs;
233*53607Selan 	int fd;
234*53607Selan {
235*53607Selan 	static struct timeval tv[2];
236*53607Selan 
237*53607Selan 	fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
238*53607Selan 
239*53607Selan 	tv[0].tv_sec = fs->st_atime;
240*53607Selan 	tv[1].tv_sec = fs->st_mtime;
241*53607Selan 	if (utimes(to.p_path, tv))
242*53607Selan 		err("utimes: %s: %s", to.p_path, strerror(errno));
243*53607Selan 	/*
244*53607Selan 	 * Changing the ownership probably won't succeed, unless we're root
245*53607Selan 	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
246*53607Selan 	 * the mode; current BSD behavior is to remove all setuid bits on
247*53607Selan 	 * chown.  If chown fails, lose setuid/setgid bits.
248*53607Selan 	 */
249*53607Selan 	if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
250*53607Selan 	    chown(to.p_path, fs->st_uid, fs->st_gid)) {
251*53607Selan 		if (errno != EPERM)
252*53607Selan 			err("chown: %s: %s", to.p_path, strerror(errno));
253*53607Selan 		fs->st_mode &= ~(S_ISUID|S_ISGID);
254*53607Selan 	}
255*53607Selan 	if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode))
256*53607Selan 		err("chown: %s: %s", to.p_path, strerror(errno));
257*53607Selan }
258*53607Selan 
259*53607Selan void
260*53607Selan usage()
261*53607Selan {
262*53607Selan 	(void)fprintf(stderr,
263*53607Selan "usage: cp [-Rfhip] src target;\n       cp [-Rfhip] src1 ... srcN directory\n");
264*53607Selan 	exit(1);
265*53607Selan }
266*53607Selan 
267*53607Selan #if __STDC__
268*53607Selan #include <stdarg.h>
269*53607Selan #else
270*53607Selan #include <varargs.h>
271*53607Selan #endif
272*53607Selan 
273*53607Selan void
274*53607Selan #if __STDC__
275*53607Selan err(const char *fmt, ...)
276*53607Selan #else
277*53607Selan err(fmt, va_alist)
278*53607Selan 	char *fmt;
279*53607Selan         va_dcl
280*53607Selan #endif
281*53607Selan {
282*53607Selan 	va_list ap;
283*53607Selan #if __STDC__
284*53607Selan 	va_start(ap, fmt);
285*53607Selan #else
286*53607Selan 	va_start(ap);
287*53607Selan #endif
288*53607Selan 	(void)fprintf(stderr, "%s: ", progname);
289*53607Selan 	(void)vfprintf(stderr, fmt, ap);
290*53607Selan 	va_end(ap);
291*53607Selan 	(void)fprintf(stderr, "\n");
292*53607Selan 	exit_val = 1;
293*53607Selan }
294