xref: /titanic_41/usr/src/lib/libcmd/common/cp.c (revision 37714ae43602c675f9dc59b070bfdf9fa702872c)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1992-2008 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                  David Korn <dgk@research.att.com>                   *
19 *                                                                      *
20 ***********************************************************************/
21 #pragma prototyped
22 /*
23  * Glenn Fowler
24  * AT&T Research
25  *
26  * cp/ln/mv -- copy/link/move files
27  */
28 
29 static const char usage_head[] =
30 "[-?@(#)$Id: cp (AT&T Research) 2007-12-13 $\n]"
31 USAGE_LICENSE
32 ;
33 
34 static const char usage_cp[] =
35 "[+NAME?cp - copy files]"
36 "[+DESCRIPTION?If the last argument names an existing directory, \bcp\b"
37 "	copies each \afile\a into a file with the same name in that"
38 "	directory. Otherwise, if only two files are given, \bcp\b copies"
39 "	the first onto the second. It is an error if the last argument is"
40 "	not a directory and more than two files are given. By default"
41 "	directories are not copied.]"
42 
43 "[a:archive?Preserve as much as possible of the structure and attributes of"
44 "	the original files in the copy. Equivalent to \b--physical\b"
45 "	\b--preserve\b \b--recursive\b.]"
46 "[p:preserve?Preserve file owner, group, permissions and timestamps.]"
47 "[h:hierarchy|parents?Form the name of each destination file by appending"
48 "	to the target directory a slash and the specified source file name."
49 "	The last argument must be an existing directory. Missing destination"
50 "	directories are created.]"
51 "[H:metaphysical?Follow command argument symbolic links, otherwise don't"
52 "	follow.]"
53 "[l:link?Make hard links to destination files instead of copies.]"
54 "[L:logical|dereference?Follow symbolic links and copy the files"
55 "	they point to.]"
56 "[P|d:physical|nodereference?Don't follow symbolic links; copy symbolic"
57 "	rather than the files they point to.]"
58 ;
59 
60 static const char usage_ln[] =
61 "[+NAME?ln - link files]"
62 "[+DESCRIPTION?If the last argument names an existing directory, \bln\b"
63 "	links each \afile\a into a file with the same name in that"
64 "	directory. Otherwise, if only two files are given, \bln\b links"
65 "	the first onto the second. It is an error if the last argument is"
66 "	not a directory and more than two files are given. By default"
67 "	directories are not linked.]"
68 ;
69 
70 static const char usage_mv[] =
71 "[+NAME?mv - rename files]"
72 "[+DESCRIPTION?If the last argument names an existing directory, \bmv\b"
73 "	renames each \afile\a into a file with the same name in that"
74 "	directory. Otherwise, if only two files are given, \bmv\b renames"
75 "	the first onto the second. It is an error if the last argument is"
76 "	not a directory and more than two files are given. If a source and"
77 "	destination file reside on different filesystems then \bmv\b copies"
78 "	the file contents to the destination and then deletes the source"
79 "	file.]"
80 ;
81 
82 static const char usage_tail[] =
83 "[f:force?Replace existing destination files.]"
84 "[i:interactive|prompt?Prompt whether to replace existing destination files."
85 "	An affirmative response (\by\b or \bY\b) replaces the file, a quit"
86 "	response (\bq\b or \bQ\b) exits immediately, and all other"
87 "	responses skip the file.]"
88 "[r|R:recursive?Operate on the contents of directories recursively.]"
89 "[s:symlink|symbolic-link?Make symbolic links to destination files.]"
90 "[u:update?Replace a destination file only if its modification time is older"
91 "	than the corresponding source file modification time.]"
92 "[v:verbose?Print the name of each file before operating on it.]"
93 "[b:backup?Make backups of files that are about to be replaced. See"
94 "	\b--suffix\b and \b--version-control\b for more information.]"
95 "[F:fsync|sync?\bfsync\b(2) each file after it is copied.]"
96 "[S:backup-suffix|suffix?A backup file is made by renaming the file to the"
97 "	same name with the backup suffix appended. The backup suffix is"
98 "	determined in this order: this option, the \bSIMPLE_BACKUP_SUFFIX\b,"
99 "	environment variable, or the default value \b~\b.]:[suffix]"
100 "[V:backup-type|version-control?The backup type is determined in this order:"
101 "	this option, the \bVERSION_CONTROL\b environment variable, or the"
102 "	default value \bexisting\b. \atype\a may be one of:]:[type]{"
103 "		[+numbered|t?Always make numbered backups. The numbered backup"
104 "			suffix is \b.\aSNS\a, where \aS\a is the"
105 "			\bbackup-suffix\b and \aN\a is the version number,"
106 "			starting at 1, incremented with each version.]"
107 "		[+existing|nil?Make numbered backups of files that already"
108 "			have them, otherwise simple backups.]"
109 "		[+simple|never?Always make simple backups.]"
110 "}"
111 "[x|X|l:xdev|local|mount|one-file-system?Do not descend into directories in"
112 "	different filesystems than their parents.]"
113 
114 "\n"
115 "\nsource destination\n"
116 "file ... directory\n"
117 "\n"
118 
119 "[+SEE ALSO?\bpax\b(1), \bfsync\b(2), \brename\b(2), \bunlink\b(2),"
120 "	\bremove\b(3)]"
121 ;
122 
123 #include <cmd.h>
124 #include <ls.h>
125 #include <times.h>
126 #include <fts.h>
127 #include <fs3d.h>
128 #include <hashkey.h>
129 #include <stk.h>
130 #include <tmx.h>
131 
132 #define PATH_CHUNK	256
133 
134 #define CP		1
135 #define LN		2
136 #define MV		3
137 
138 #define BAK_replace	0		/* no backup -- just replace	*/
139 #define BAK_existing	1		/* number if already else simple*/
140 #define BAK_number	2		/* append .suffix number suffix	*/
141 #define BAK_simple	3		/* append suffix		*/
142 
143 typedef struct State_s			/* program state		*/
144 {
145 	int		backup;		/* BAK_* type			*/
146 	int		directory;	/* destination is directory	*/
147 	int		flags;		/* FTS_* flags			*/
148 	int		force;		/* force approval		*/
149 	int		fs3d;		/* 3d fs enabled		*/
150 	int		hierarchy;	/* preserve hierarchy		*/
151 	int		interactive;	/* prompt for approval		*/
152 	int		missmode;	/* default missing dir mode	*/
153 	int		official;	/* move to next view		*/
154 	int		op;		/* {CP,LN,MV}			*/
155 	int		perm;		/* permissions to preserve	*/
156 	int		postsiz;	/* state.path post index	*/
157 	int		presiz;		/* state.path pre index		*/
158 	int		preserve;	/* preserve { id mode time }	*/
159 	int		recursive;	/* subtrees too			*/
160 	int		suflen;		/* strlen(state.suffix)		*/
161 	int		sync;		/* fsync() each file after copy	*/
162 	int		uid;		/* caller uid			*/
163 	int		update;		/* replace only if newer	*/
164 	int		verbose;	/* list each file before op	*/
165 	int		wflags;		/* open() for write flags	*/
166 
167 	int		(*link)(const char*, const char*);	/* link	*/
168 	int		(*stat)(const char*, struct stat*);	/* stat	*/
169 
170 #define INITSTATE	pathsiz		/* (re)init state before this	*/
171 	int		pathsiz;	/* state.path buffer size	*/
172 
173 
174 	char*		path;		/* to pathname buffer		*/
175 	char*		opname;		/* state.op message string	*/
176 	char*		suffix;		/* backup suffix		*/
177 
178 	Sfio_t*		tmp;		/* tmp string stream		*/
179 
180 	char		text[PATH_MAX];	/* link text buffer		*/
181 } State_t;
182 
183 static const char	dot[2] = { '.' };
184 
185 /*
186  * preserve support
187  */
188 
189 static void
190 preserve(State_t* state, const char* path, struct stat* ns, struct stat* os)
191 {
192 	int	n;
193 
194 	if (tmxtouch(path, tmxgetatime(os), tmxgetmtime(os), TMX_NOTIME, 0))
195 		error(ERROR_SYSTEM|2, "%s: cannot reset access and modify times", path);
196 	n = ((ns->st_uid != os->st_uid) << 1) | (ns->st_gid != os->st_gid);
197 	if (n && chown(state->path, os->st_uid, os->st_gid))
198 		switch (n)
199 		{
200 		case 01:
201 			error(ERROR_SYSTEM|2, "%s: cannot reset group to %s", path, fmtgid(os->st_gid));
202 			break;
203 		case 02:
204 			error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s", path, fmtuid(os->st_uid));
205 			break;
206 		case 03:
207 			error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s and group to %s", path, fmtuid(os->st_uid), fmtgid(os->st_gid));
208 			break;
209 		}
210 }
211 
212 /*
213  * visit a single file and state.op to the destination
214  */
215 
216 static int
217 visit(State_t* state, register FTSENT* ent)
218 {
219 	register char*	base;
220 	register int	n;
221 	register int	len;
222 	int		rm;
223 	int		rfd;
224 	int		wfd;
225 	int		m;
226 	int		v;
227 	char*		s;
228 	char*		e;
229 	char*		protection;
230 	Sfio_t*		ip;
231 	Sfio_t*		op;
232 	FTS*		fts;
233 	FTSENT*		sub;
234 	struct stat	st;
235 
236 	if (ent->fts_info == FTS_DC)
237 	{
238 		error(2, "%s: directory causes cycle", ent->fts_path);
239 		fts_set(NiL, ent, FTS_SKIP);
240 		return 0;
241 	}
242 	if (ent->fts_level == 0)
243 	{
244 		base = ent->fts_name;
245 		len = ent->fts_namelen;
246 		if (state->hierarchy)
247 			state->presiz = -1;
248 		else
249 		{
250 			state->presiz = ent->fts_pathlen;
251 			while (*base == '.' && *(base + 1) == '/')
252 				for (base += 2; *base == '/'; base++);
253 			if (*base == '.' && !*(base + 1))
254 				state->presiz--;
255 			else if (*base)
256 				state->presiz -= base - ent->fts_name;
257 			base = ent->fts_name + len;
258 			while (base > ent->fts_name && *(base - 1) == '/')
259 				base--;
260 			while (base > ent->fts_name && *(base - 1) != '/')
261 				base--;
262 			len -= base - ent->fts_name;
263 			if (state->directory)
264 				state->presiz -= len + 1;
265 		}
266 	}
267 	else
268 	{
269 		base = ent->fts_path + state->presiz + 1;
270 		len = ent->fts_pathlen - state->presiz - 1;
271 	}
272 	len++;
273 	if (state->directory)
274 	{
275 		if ((state->postsiz + len) > state->pathsiz && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + len, PATH_CHUNK), 0)))
276 			error(3, "out of space");
277 		if (state->hierarchy && ent->fts_level == 0 && strchr(base, '/'))
278 		{
279 			s = state->path + state->postsiz;
280 			memcpy(s, base, len);
281 			while (e = strchr(s, '/'))
282 			{
283 				*e = 0;
284 				if (access(state->path, F_OK))
285 				{
286 					st.st_mode = state->missmode;
287 					if (s = strrchr(s, '/'))
288 					{
289 						*s = 0;
290 						stat(state->path, &st);
291 						*s = '/';
292 					}
293 					if (mkdir(state->path, st.st_mode & S_IPERM))
294 					{
295 						error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path);
296 						fts_set(NiL, ent, FTS_SKIP);
297 						return 0;
298 					}
299 				}
300 				*e++ = '/';
301 				s = e;
302 			}
303 		}
304 	}
305 	switch (ent->fts_info)
306 	{
307 	case FTS_DP:
308 		if (state->preserve && state->op != LN || ent->fts_level > 0 && (ent->fts_statp->st_mode & S_IRWXU) != S_IRWXU)
309 		{
310 			if (len && ent->fts_level > 0)
311 				memcpy(state->path + state->postsiz, base, len);
312 			else
313 				state->path[state->postsiz] = 0;
314 			if (stat(state->path, &st))
315 				error(ERROR_SYSTEM|2, "%s: cannot stat", state->path);
316 			else
317 			{
318 				if ((ent->fts_statp->st_mode & S_IPERM) != (st.st_mode & S_IPERM) && chmod(state->path, ent->fts_statp->st_mode & S_IPERM))
319 					error(ERROR_SYSTEM|2, "%s: cannot reset directory mode to %s", state->path, fmtmode(st.st_mode & S_IPERM, 0) + 1);
320 				if (state->preserve)
321 					preserve(state, state->path, &st, ent->fts_statp);
322 			}
323 		}
324 		return 0;
325 	case FTS_DNR:
326 	case FTS_DNX:
327 	case FTS_D:
328 		if (!state->recursive)
329 		{
330 			fts_set(NiL, ent, FTS_SKIP);
331 			if (state->op == CP)
332 				error(1, "%s: directory -- copying as plain file", ent->fts_path);
333 			else if (state->link == link && !state->force)
334 			{
335 				error(2, "%s: cannot link directory", ent->fts_path);
336 				return 0;
337 			}
338 		}
339 		else switch (ent->fts_info)
340 		{
341 		case FTS_DNR:
342 			error(2, "%s: cannot read directory", ent->fts_path);
343 			return 0;
344 		case FTS_DNX:
345 			error(2, "%s: cannot search directory", ent->fts_path);
346 			fts_set(NiL, ent, FTS_SKIP);
347 
348 			/*FALLTHROUGH*/
349 		case FTS_D:
350 			if (state->directory)
351 				memcpy(state->path + state->postsiz, base, len);
352 			if (!(*state->stat)(state->path, &st))
353 			{
354 				if (!S_ISDIR(st.st_mode))
355 				{
356 					error(2, "%s: not a directory -- %s ignored", state->path, ent->fts_path);
357 					return 0;
358 				}
359 			}
360 			else if (mkdir(state->path, (ent->fts_statp->st_mode & S_IPERM)|(ent->fts_info == FTS_D ? S_IRWXU : 0)))
361 			{
362 				error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path);
363 				fts_set(NiL, ent, FTS_SKIP);
364 			}
365 			if (!state->directory)
366 			{
367 				state->directory = 1;
368 				state->path[state->postsiz++] = '/';
369 				state->presiz--;
370 			}
371 			return 0;
372 		}
373 		break;
374 	case FTS_ERR:
375 	case FTS_NS:
376 	case FTS_SLNONE:
377 		if (state->link != pathsetlink)
378 		{
379 			error(2, "%s: not found", ent->fts_path);
380 			return 0;
381 		}
382 		break;
383 #if 0
384 	case FTS_SL:
385 		if (state->op == CP)
386 		{
387 			error(2, "%s: cannot copy non-terminal symbolic link", ent->fts_path);
388 			return 0;
389 		}
390 		break;
391 #endif
392 	}
393 	if (state->directory)
394 		memcpy(state->path + state->postsiz, base, len);
395 	if ((*state->stat)(state->path, &st))
396 		st.st_mode = 0;
397 	else if (state->update && !S_ISDIR(st.st_mode) && (unsigned long)ent->fts_statp->st_mtime < (unsigned long)st.st_mtime)
398 	{
399 		fts_set(NiL, ent, FTS_SKIP);
400 		return 0;
401 	}
402 	else if (!state->fs3d || !iview(&st))
403 	{
404 		/*
405 		 * target is in top 3d view
406 		 */
407 
408 		if (st.st_dev == ent->fts_statp->st_dev && st.st_ino == ent->fts_statp->st_ino)
409 		{
410 			if (state->op == MV)
411 			{
412 				/*
413 				 * let rename() handle it
414 				 */
415 
416 				if (state->verbose)
417 					sfputr(sfstdout, state->path, '\n');
418 				goto operate;
419 			}
420 			if (!state->official)
421 				error(2, "%s: identical to %s", state->path, ent->fts_path);
422 			return 0;
423 		}
424 		if (S_ISDIR(st.st_mode))
425 		{
426 			error(2, "%s: cannot %s existing directory", state->path, state->opname);
427 			return 0;
428 		}
429 		if (state->verbose)
430 			sfputr(sfstdout, state->path, '\n');
431 		rm = state->op == LN || ent->fts_info == FTS_SL;
432 		if (!rm || !state->force)
433 		{
434 			if ((n = open(state->path, O_RDWR|O_BINARY)) >= 0)
435 			{
436 				close(n);
437 				if (state->force)
438 					/* ok */;
439 				else if (state->interactive)
440 				{
441 					if (astquery(-1, "%s %s? ", state->opname, state->path))
442 						return 0;
443 				}
444 				else if (state->op == LN)
445 				{
446 					error(2, "%s: cannot %s existing file", state->path, state->opname);
447 					return 0;
448 				}
449 			}
450 			else if (state->force)
451 				rm = 1;
452 			else
453 			{
454 				protection =
455 #ifdef ETXTBSY
456 				    errno == ETXTBSY ? "``running program''" :
457 #endif
458 				    st.st_uid != state->uid ? "``not owner''" :
459 				    fmtmode(st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO), 0) + 1;
460 				if (state->interactive)
461 				{
462 					if (astquery(-1, "override protection %s for %s? ", protection, state->path))
463 						return 0;
464 					rm = 1;
465 				}
466 				else if (!rm)
467 				{
468 					error(2, "%s: cannot %s %s protection", state->path, state->opname, protection);
469 					return 0;
470 				}
471 			}
472 		}
473 		switch (state->backup)
474 		{
475 		case BAK_existing:
476 		case BAK_number:
477 			v = 0;
478 			if (s = strrchr(state->path, '/'))
479 			{
480 				e = state->path;
481 				*s++ = 0;
482 			}
483 			else
484 			{
485 				e = (char*)dot;
486 				s = state->path;
487 			}
488 			n = strlen(s);
489 			if (fts = fts_open((char**)e, FTS_NOCHDIR|FTS_ONEPATH|FTS_PHYSICAL|FTS_NOPOSTORDER|FTS_NOSTAT|FTS_NOSEEDOTDIR, NiL))
490 			{
491 				while (sub = fts_read(fts))
492 				{
493 					if (strneq(s, sub->fts_name, n) && sub->fts_name[n] == '.' && strneq(sub->fts_name + n + 1, state->suffix, state->suflen) && (m = strtol(sub->fts_name + n + state->suflen + 1, &e, 10)) && streq(e, state->suffix) && m > v)
494 						v = m;
495 					if (sub->fts_level)
496 						fts_set(NiL, sub, FTS_SKIP);
497 				}
498 				fts_close(fts);
499 			}
500 			if (s != state->path)
501 				*--s = '/';
502 			if (v || state->backup == BAK_number)
503 			{
504 				sfprintf(state->tmp, "%s.%s%d%s", state->path, state->suffix, v + 1, state->suffix);
505 				goto backup;
506 			}
507 			/*FALLTHROUGH*/
508 		case BAK_simple:
509 			sfprintf(state->tmp, "%s%s", state->path, state->suffix);
510 		backup:
511 			if (!(s = sfstruse(state->tmp)))
512 				error(ERROR_SYSTEM|3, "%s: out of space", state->path);
513 			if (rename(state->path, s))
514 			{
515 				error(ERROR_SYSTEM|2, "%s: cannot backup to %s", state->path, s);
516 				return 0;
517 			}
518 			break;
519 		default:
520 			if (rm && remove(state->path))
521 			{
522 				error(ERROR_SYSTEM|2, "%s: cannot remove", state->path);
523 				return 0;
524 			}
525 			break;
526 		}
527 	}
528  operate:
529 	switch (state->op)
530 	{
531 	case MV:
532 		for (;;)
533 		{
534 			if (!rename(ent->fts_path, state->path))
535 				return 0;
536 			if (errno == ENOENT)
537 				rm = 1;
538 			else if (!rm && st.st_mode && !remove(state->path))
539 			{
540 				rm = 1;
541 				continue;
542 			}
543 			if (errno != EXDEV && (rm || S_ISDIR(ent->fts_statp->st_mode)))
544 			{
545 				error(ERROR_SYSTEM|2, "%s: cannot rename to %s", ent->fts_path, state->path);
546 				return 0;
547 			}
548 			else
549 				break;
550 		}
551 		/*FALLTHROUGH*/
552 	case CP:
553 		if (S_ISLNK(ent->fts_statp->st_mode))
554 		{
555 			if ((n = pathgetlink(ent->fts_path, state->text, sizeof(state->text) - 1)) < 0)
556 			{
557 				error(ERROR_SYSTEM|2, "%s: cannot read symbolic link text", ent->fts_path);
558 				return 0;
559 			}
560 			state->text[n] = 0;
561 			if (pathsetlink(state->text, state->path))
562 			{
563 				error(ERROR_SYSTEM|2, "%s: cannot copy symbolic link to %s", ent->fts_path, state->path);
564 				return 0;
565 			}
566 		}
567 		else if (state->op == CP || S_ISREG(ent->fts_statp->st_mode) || S_ISDIR(ent->fts_statp->st_mode))
568 		{
569 			if (ent->fts_statp->st_size > 0 && (rfd = open(ent->fts_path, O_RDONLY|O_BINARY)) < 0)
570 			{
571 				error(ERROR_SYSTEM|2, "%s: cannot read", ent->fts_path);
572 				return 0;
573 			}
574 			else if ((wfd = open(state->path, st.st_mode ? (state->wflags & ~O_EXCL) : state->wflags, ent->fts_statp->st_mode & state->perm)) < 0)
575 			{
576 				error(ERROR_SYSTEM|2, "%s: cannot write", state->path);
577 				if (ent->fts_statp->st_size > 0)
578 					close(rfd);
579 				return 0;
580 			}
581 			else if (ent->fts_statp->st_size > 0)
582 			{
583 				if (!(ip = sfnew(NiL, NiL, SF_UNBOUND, rfd, SF_READ)))
584 				{
585 					error(ERROR_SYSTEM|2, "%s: %s read stream error", ent->fts_path, state->path);
586 					close(rfd);
587 					close(wfd);
588 				}
589 				else
590 				{
591 					n = 0;
592 					if (!(op = sfnew(NiL, NiL, SF_UNBOUND, wfd, SF_WRITE)))
593 					{
594 						error(ERROR_SYSTEM|2, "%s: %s write stream error", ent->fts_path, state->path);
595 						close(wfd);
596 						sfclose(ip);
597 					}
598 					else
599 					{
600 						if (sfmove(ip, op, (Sfoff_t)SF_UNBOUND, -1) < 0)
601 							n |= 3;
602 						if (!sfeof(ip))
603 							n |= 1;
604 						if (sfsync(op) || state->sync && fsync(wfd) || sfclose(op))
605 							n |= 2;
606 						if (sfclose(ip))
607 							n |= 1;
608 						if (n)
609 							error(ERROR_SYSTEM|2, "%s: %s %s error", ent->fts_path, state->path, n == 1 ? ERROR_translate(0, 0, 0, "read") : n == 2 ? ERROR_translate(0, 0, 0, "write") : ERROR_translate(0, 0, 0, "io"));
610 					}
611 				}
612 			}
613 			else
614 				close(wfd);
615 		}
616 		else if (S_ISBLK(ent->fts_statp->st_mode) || S_ISCHR(ent->fts_statp->st_mode) || S_ISFIFO(ent->fts_statp->st_mode))
617 		{
618 			if (mknod(state->path, ent->fts_statp->st_mode, idevice(ent->fts_statp)))
619 			{
620 				error(ERROR_SYSTEM|2, "%s: cannot copy special file to %s", ent->fts_path, state->path);
621 				return 0;
622 			}
623 		}
624 		else
625 		{
626 			error(2, "%s: cannot copy -- unknown file type 0%o", ent->fts_path, S_ITYPE(ent->fts_statp->st_mode));
627 			return 0;
628 		}
629 		if (state->preserve)
630 		{
631 			if (ent->fts_info != FTS_SL)
632 			{
633 				if (stat(state->path, &st))
634 					error(ERROR_SYSTEM|2, "%s: cannot stat", state->path);
635 				else
636 				{
637 					if ((ent->fts_statp->st_mode & state->perm) != (st.st_mode & state->perm) && chmod(state->path, ent->fts_statp->st_mode & state->perm))
638 						error(ERROR_SYSTEM|2, "%s: cannot reset mode to %s", state->path, fmtmode(st.st_mode & state->perm, 0) + 1);
639 					preserve(state, state->path, &st, ent->fts_statp);
640 				}
641 			}
642 			if (state->op == MV && remove(ent->fts_path))
643 				error(ERROR_SYSTEM|1, "%s: cannot remove", ent->fts_path);
644 		}
645 		break;
646 	case LN:
647 		if ((*state->link)(ent->fts_path, state->path))
648 			error(ERROR_SYSTEM|2, "%s: cannot link to %s", ent->fts_path, state->path);
649 		break;
650 	}
651 	return 0;
652 }
653 
654 int
655 b_cp(int argc, register char** argv, void* context)
656 {
657 	register char*	file;
658 	register char*	s;
659 	char**		v;
660 	char*		backup_type;
661 	FTS*		fts;
662 	FTSENT*	ent;
663 	const char*	usage;
664 	int		path_resolve;
665 	int		standard;
666 	struct stat	st;
667 	State_t*	state;
668 	Shbltin_t*	sh;
669 
670 	cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
671 	if (!(sh = CMD_CONTEXT(context)) || !(state = (State_t*)sh->ptr))
672 	{
673 		if (!(state = newof(0, State_t, 1, 0)))
674 			error(ERROR_SYSTEM|3, "out of space");
675 		if (sh)
676 			sh->ptr = state;
677 	}
678 	else
679 		memset(state, 0, offsetof(State_t, INITSTATE));
680 	state->presiz = -1;
681 	backup_type = 0;
682 	state->flags = FTS_NOCHDIR|FTS_NOSEEDOTDIR;
683 	state->uid = geteuid();
684 	state->wflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY;
685 	if (!state->tmp && !(state->tmp = sfstropen()))
686 		error(ERROR_SYSTEM|3, "out of space [tmp string]");
687 	sfputr(state->tmp, usage_head, -1);
688 	standard = !strcmp(astconf("CONFORMANCE", NiL, NiL), "standard");
689 	switch (error_info.id[0])
690 	{
691 	case 'c':
692 	case 'C':
693 		sfputr(state->tmp, usage_cp, -1);
694 		state->op = CP;
695 		state->stat = stat;
696 		path_resolve = -1;
697 		break;
698 	case 'l':
699 	case 'L':
700 		sfputr(state->tmp, usage_ln, -1);
701 		state->op = LN;
702 		state->flags |= FTS_PHYSICAL;
703 		state->link = link;
704 		state->stat = lstat;
705 		path_resolve = 1;
706 		break;
707 	case 'm':
708 	case 'M':
709 		sfputr(state->tmp, usage_mv, -1);
710 		state->op = MV;
711 		state->flags |= FTS_PHYSICAL;
712 		state->preserve = 1;
713 		state->stat = lstat;
714 		path_resolve = 1;
715 		break;
716 	default:
717 		error(3, "not implemented");
718 		break;
719 	}
720 	sfputr(state->tmp, usage_tail, -1);
721 	if (!(usage = sfstruse(state->tmp)))
722 		error(ERROR_SYSTEM|3, "%s: out of space", state->path);
723 	state->opname = state->op == CP ? ERROR_translate(0, 0, 0, "overwrite") : ERROR_translate(0, 0, 0, "replace");
724 	for (;;)
725 	{
726 		switch (optget(argv, usage))
727 		{
728 		case 'a':
729 			state->flags |= FTS_PHYSICAL;
730 			state->preserve = 1;
731 			state->recursive = 1;
732 			path_resolve = 1;
733 			continue;
734 		case 'b':
735 			state->backup = 1;
736 			continue;
737 		case 'f':
738 			state->force = 1;
739 			if (state->op != CP || !standard)
740 				state->interactive = 0;
741 			continue;
742 		case 'h':
743 			state->hierarchy = 1;
744 			continue;
745 		case 'i':
746 			state->interactive = 1;
747 			if (state->op != CP || !standard)
748 				state->force = 0;
749 			continue;
750 		case 'l':
751 			state->op = LN;
752 			state->link = link;
753 			state->stat = lstat;
754 			continue;
755 		case 'p':
756 			state->preserve = 1;
757 			continue;
758 		case 'r':
759 			state->recursive = 1;
760 			if (path_resolve < 0)
761 				path_resolve = 0;
762 			continue;
763 		case 's':
764 			state->op = LN;
765 			state->link = pathsetlink;
766 			state->stat = lstat;
767 			continue;
768 		case 'u':
769 			state->update = 1;
770 			continue;
771 		case 'v':
772 			state->verbose = 1;
773 			continue;
774 		case 'x':
775 			state->flags |= FTS_XDEV;
776 			continue;
777 		case 'F':
778 #if _lib_fsync
779 			state->sync = 1;
780 #else
781 			error(1, "%s not implemented on this system", opt_info.name);
782 #endif
783 			continue;
784 		case 'H':
785 			state->flags |= FTS_META|FTS_PHYSICAL;
786 			path_resolve = 1;
787 			continue;
788 		case 'L':
789 			state->flags &= ~FTS_PHYSICAL;
790 			path_resolve = 1;
791 			continue;
792 		case 'P':
793 			state->flags &= ~FTS_META;
794 			state->flags |= FTS_PHYSICAL;
795 			path_resolve = 1;
796 			continue;
797 		case 'R':
798 			state->recursive = 1;
799 			state->flags &= ~FTS_META;
800 			state->flags |= FTS_PHYSICAL;
801 			path_resolve = 1;
802 			continue;
803 		case 'S':
804 			state->suffix = opt_info.arg;
805 			continue;
806 		case 'V':
807 			backup_type = opt_info.arg;
808 			continue;
809 		case '?':
810 			error(ERROR_USAGE|4, "%s", opt_info.arg);
811 			continue;
812 		case ':':
813 			error(2, "%s", opt_info.arg);
814 			continue;
815 		}
816 		break;
817 	}
818 	argc -= opt_info.index + 1;
819 	argv += opt_info.index;
820 	if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--"))
821 	{
822 		argc--;
823 		argv++;
824 	}
825 	if (!(v = (char**)stkalloc(stkstd, (argc + 2) * sizeof(char*))))
826 		error(3, "out of space");
827 	memcpy(v, argv, (argc + 1) * sizeof(char*));
828 	argv = v;
829 	if (!standard)
830 	{
831 		state->wflags |= O_EXCL;
832 		if (!argc)
833 		{
834 			argc++;
835 			argv[1] = (char*)dot;
836 		}
837 	}
838 	if (state->backup)
839 	{
840 		if (!(file = backup_type) && !(backup_type = getenv("VERSION_CONTROL")))
841 			state->backup = BAK_existing;
842 		else
843 			switch (strkey(backup_type))
844 			{
845 			case HASHKEY6('e','x','i','s','t','i'):
846 			case HASHKEY5('e','x','i','s','t'):
847 			case HASHKEY4('e','x','i','s'):
848 			case HASHKEY3('e','x','i'):
849 			case HASHKEY2('e','x'):
850 			case HASHKEY1('e'):
851 			case HASHKEY3('n','i','l'):
852 			case HASHKEY2('n','i'):
853 				state->backup = BAK_existing;
854 				break;
855 			case HASHKEY5('n','e','v','e','r'):
856 			case HASHKEY4('n','e','v','e'):
857 			case HASHKEY3('n','e','v'):
858 			case HASHKEY2('n','e'):
859 			case HASHKEY6('s','i','m','p','l','e'):
860 			case HASHKEY5('s','i','m','p','l'):
861 			case HASHKEY4('s','i','m','p'):
862 			case HASHKEY3('s','i','m'):
863 			case HASHKEY2('s','i'):
864 			case HASHKEY1('s'):
865 				state->backup = BAK_simple;
866 				break;
867 			case HASHKEY6('n','u','m','b','e','r'):
868 			case HASHKEY5('n','u','m','b','e'):
869 			case HASHKEY4('n','u','m','b'):
870 			case HASHKEY3('n','u','m'):
871 			case HASHKEY2('n','u'):
872 			case HASHKEY1('t'):
873 				state->backup = BAK_number;
874 				break;
875 			default:
876 				if (file)
877 					error(2, "%s: unknown backup type", backup_type);
878 				break;
879 			}
880 		if (!state->suffix && !(state->suffix = getenv("SIMPLE_BACKUP_SUFFIX")))
881 			state->suffix = "~";
882 		state->suflen = strlen(state->suffix);
883 	}
884 	if (argc <= 0 || error_info.errors)
885 		error(ERROR_USAGE|4, "%s", optusage(NiL));
886 	if (!path_resolve)
887 		state->flags |= fts_flags();
888 	file = argv[argc];
889 	argv[argc] = 0;
890 	if (s = strrchr(file, '/'))
891 	{
892 		while (*s == '/')
893 			s++;
894 		if (!(!*s || *s == '.' && (!*++s || *s == '.' && !*++s)))
895 			s = 0;
896 	}
897 	if (file != (char*)dot)
898 		pathcanon(file, 0);
899 	if (!(state->directory = !stat(file, &st) && S_ISDIR(st.st_mode)) && argc > 1)
900 		error(ERROR_USAGE|4, "%s", optusage(NiL));
901 	if (s && !state->directory)
902 		error(3, "%s: not a directory", file);
903 	if ((state->fs3d = fs3d(FS3D_TEST)) && strmatch(file, "...|*/...|.../*"))
904 		state->official = 1;
905 	state->postsiz = strlen(file);
906 	if (state->pathsiz < roundof(state->postsiz + 2, PATH_CHUNK) && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + 2, PATH_CHUNK), 0)))
907 		error(3, "out of space");
908 	memcpy(state->path, file, state->postsiz + 1);
909 	if (state->directory && state->path[state->postsiz - 1] != '/')
910 		state->path[state->postsiz++] = '/';
911 	if (state->hierarchy)
912 	{
913 		if (!state->directory)
914 			error(3, "%s: last argument must be a directory", file);
915 		state->missmode = st.st_mode;
916 	}
917 	state->perm = state->uid ? S_IPERM : (S_IPERM & ~S_ISVTX);
918 	if (!state->recursive)
919 		state->flags |= FTS_TOP;
920 	if (fts = fts_open(argv, state->flags, NiL))
921 	{
922 		while (!sh_checksig(context) && (ent = fts_read(fts)) && !visit(state, ent));
923 		fts_close(fts);
924 	}
925 	else if (state->link != pathsetlink)
926 		switch (state->op)
927 		{
928 		case CP:
929 			error(ERROR_SYSTEM|2, "%s: cannot copy", argv[0]);
930 			break;
931 		case LN:
932 			error(ERROR_SYSTEM|2, "%s: cannot link", argv[0]);
933 			break;
934 		case MV:
935 			error(ERROR_SYSTEM|2, "%s: cannot move", argv[0]);
936 			break;
937 		}
938 	else if ((*state->link)(*argv, state->path))
939 		error(ERROR_SYSTEM|2, "%s: cannot link to %s", *argv, state->path);
940 	return error_info.errors != 0;
941 }
942