xref: /titanic_50/usr/src/lib/libcmd/common/cp.c (revision 69b3e10436272b0970d58743de375d0dd61046ce)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1992-2009 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) 2009-06-18 $\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 	void*		context;	/* builtin context		*/
146 	int		backup;		/* BAK_* type			*/
147 	int		directory;	/* destination is directory	*/
148 	int		flags;		/* FTS_* flags			*/
149 	int		force;		/* force approval		*/
150 	int		fs3d;		/* 3d fs enabled		*/
151 	int		hierarchy;	/* preserve hierarchy		*/
152 	int		interactive;	/* prompt for approval		*/
153 	int		missmode;	/* default missing dir mode	*/
154 	int		official;	/* move to next view		*/
155 	int		op;		/* {CP,LN,MV}			*/
156 	int		perm;		/* permissions to preserve	*/
157 	int		postsiz;	/* state.path post index	*/
158 	int		presiz;		/* state.path pre index		*/
159 	int		preserve;	/* preserve { id mode time }	*/
160 	int		recursive;	/* subtrees too			*/
161 	int		suflen;		/* strlen(state.suffix)		*/
162 	int		sync;		/* fsync() each file after copy	*/
163 	int		uid;		/* caller uid			*/
164 	int		update;		/* replace only if newer	*/
165 	int		verbose;	/* list each file before op	*/
166 	int		wflags;		/* open() for write flags	*/
167 
168 	int		(*link)(const char*, const char*);	/* link	*/
169 	int		(*stat)(const char*, struct stat*);	/* stat	*/
170 
171 #define INITSTATE	pathsiz		/* (re)init state before this	*/
172 	int		pathsiz;	/* state.path buffer size	*/
173 
174 
175 	char*		path;		/* to pathname buffer		*/
176 	char*		opname;		/* state.op message string	*/
177 	char*		suffix;		/* backup suffix		*/
178 
179 	Sfio_t*		tmp;		/* tmp string stream		*/
180 
181 	char		text[PATH_MAX];	/* link text buffer		*/
182 } State_t;
183 
184 static const char	dot[2] = { '.' };
185 
186 /*
187  * preserve support
188  */
189 
190 static void
191 preserve(State_t* state, const char* path, struct stat* ns, struct stat* os)
192 {
193 	int	n;
194 
195 	if (tmxtouch(path, tmxgetatime(os), tmxgetmtime(os), TMX_NOTIME, 0))
196 		error(ERROR_SYSTEM|2, "%s: cannot reset access and modify times", path);
197 	n = ((ns->st_uid != os->st_uid) << 1) | (ns->st_gid != os->st_gid);
198 	if (n && chown(state->path, os->st_uid, os->st_gid))
199 		switch (n)
200 		{
201 		case 01:
202 			error(ERROR_SYSTEM|2, "%s: cannot reset group to %s", path, fmtgid(os->st_gid));
203 			break;
204 		case 02:
205 			error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s", path, fmtuid(os->st_uid));
206 			break;
207 		case 03:
208 			error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s and group to %s", path, fmtuid(os->st_uid), fmtgid(os->st_gid));
209 			break;
210 		}
211 }
212 
213 /*
214  * visit a single file and state.op to the destination
215  */
216 
217 static int
218 visit(State_t* state, register FTSENT* ent)
219 {
220 	register char*	base;
221 	register int	n;
222 	register int	len;
223 	int		rm;
224 	int		rfd;
225 	int		wfd;
226 	int		m;
227 	int		v;
228 	char*		s;
229 	char*		e;
230 	char*		protection;
231 	Sfio_t*		ip;
232 	Sfio_t*		op;
233 	FTS*		fts;
234 	FTSENT*		sub;
235 	struct stat	st;
236 
237 	if (ent->fts_info == FTS_DC)
238 	{
239 		error(2, "%s: directory causes cycle", ent->fts_path);
240 		fts_set(NiL, ent, FTS_SKIP);
241 		return 0;
242 	}
243 	if (ent->fts_level == 0)
244 	{
245 		base = ent->fts_name;
246 		len = ent->fts_namelen;
247 		if (state->hierarchy)
248 			state->presiz = -1;
249 		else
250 		{
251 			state->presiz = ent->fts_pathlen;
252 			while (*base == '.' && *(base + 1) == '/')
253 				for (base += 2; *base == '/'; base++);
254 			if (*base == '.' && !*(base + 1))
255 				state->presiz--;
256 			else if (*base)
257 				state->presiz -= base - ent->fts_name;
258 			base = ent->fts_name + len;
259 			while (base > ent->fts_name && *(base - 1) == '/')
260 				base--;
261 			while (base > ent->fts_name && *(base - 1) != '/')
262 				base--;
263 			len -= base - ent->fts_name;
264 			if (state->directory)
265 				state->presiz -= len + 1;
266 		}
267 	}
268 	else
269 	{
270 		base = ent->fts_path + state->presiz + 1;
271 		len = ent->fts_pathlen - state->presiz - 1;
272 	}
273 	len++;
274 	if (state->directory)
275 	{
276 		if ((state->postsiz + len) > state->pathsiz && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + len, PATH_CHUNK), 0)))
277 			error(3, "out of space");
278 		if (state->hierarchy && ent->fts_level == 0 && strchr(base, '/'))
279 		{
280 			s = state->path + state->postsiz;
281 			memcpy(s, base, len);
282 			while (e = strchr(s, '/'))
283 			{
284 				*e = 0;
285 				if (access(state->path, F_OK))
286 				{
287 					st.st_mode = state->missmode;
288 					if (s = strrchr(s, '/'))
289 					{
290 						*s = 0;
291 						stat(state->path, &st);
292 						*s = '/';
293 					}
294 					if (mkdir(state->path, st.st_mode & S_IPERM))
295 					{
296 						error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path);
297 						fts_set(NiL, ent, FTS_SKIP);
298 						return 0;
299 					}
300 				}
301 				*e++ = '/';
302 				s = e;
303 			}
304 		}
305 	}
306 	switch (ent->fts_info)
307 	{
308 	case FTS_DP:
309 		if (state->preserve && state->op != LN || ent->fts_level > 0 && (ent->fts_statp->st_mode & S_IRWXU) != S_IRWXU)
310 		{
311 			if (len && ent->fts_level > 0)
312 				memcpy(state->path + state->postsiz, base, len);
313 			else
314 				state->path[state->postsiz] = 0;
315 			if (stat(state->path, &st))
316 				error(ERROR_SYSTEM|2, "%s: cannot stat", state->path);
317 			else
318 			{
319 				if ((ent->fts_statp->st_mode & S_IPERM) != (st.st_mode & S_IPERM) && chmod(state->path, ent->fts_statp->st_mode & S_IPERM))
320 					error(ERROR_SYSTEM|2, "%s: cannot reset directory mode to %s", state->path, fmtmode(st.st_mode & S_IPERM, 0) + 1);
321 				if (state->preserve)
322 					preserve(state, state->path, &st, ent->fts_statp);
323 			}
324 		}
325 		return 0;
326 	case FTS_DNR:
327 	case FTS_DNX:
328 	case FTS_D:
329 		if (!state->recursive)
330 		{
331 			fts_set(NiL, ent, FTS_SKIP);
332 			if (state->op == CP)
333 				error(1, "%s: directory -- copying as plain file", ent->fts_path);
334 			else if (state->link == link && !state->force)
335 			{
336 				error(2, "%s: cannot link directory", ent->fts_path);
337 				return 0;
338 			}
339 		}
340 		else switch (ent->fts_info)
341 		{
342 		case FTS_DNR:
343 			error(2, "%s: cannot read directory", ent->fts_path);
344 			return 0;
345 		case FTS_DNX:
346 			error(2, "%s: cannot search directory", ent->fts_path);
347 			fts_set(NiL, ent, FTS_SKIP);
348 
349 			/*FALLTHROUGH*/
350 		case FTS_D:
351 			if (state->directory)
352 				memcpy(state->path + state->postsiz, base, len);
353 			if (!(*state->stat)(state->path, &st))
354 			{
355 				if (!S_ISDIR(st.st_mode))
356 				{
357 					error(2, "%s: not a directory -- %s ignored", state->path, ent->fts_path);
358 					return 0;
359 				}
360 			}
361 			else if (mkdir(state->path, (ent->fts_statp->st_mode & S_IPERM)|(ent->fts_info == FTS_D ? S_IRWXU : 0)))
362 			{
363 				error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path);
364 				fts_set(NiL, ent, FTS_SKIP);
365 			}
366 			if (!state->directory)
367 			{
368 				state->directory = 1;
369 				state->path[state->postsiz++] = '/';
370 				state->presiz--;
371 			}
372 			return 0;
373 		}
374 		break;
375 	case FTS_ERR:
376 	case FTS_NS:
377 	case FTS_SLNONE:
378 		if (state->link != pathsetlink)
379 		{
380 			error(2, "%s: not found", ent->fts_path);
381 			return 0;
382 		}
383 		break;
384 #if 0
385 	case FTS_SL:
386 		if (state->op == CP)
387 		{
388 			error(2, "%s: cannot copy non-terminal symbolic link", ent->fts_path);
389 			return 0;
390 		}
391 		break;
392 #endif
393 	}
394 	if (state->directory)
395 		memcpy(state->path + state->postsiz, base, len);
396 	if ((*state->stat)(state->path, &st))
397 		st.st_mode = 0;
398 	else if (state->update && !S_ISDIR(st.st_mode) && (unsigned long)ent->fts_statp->st_mtime < (unsigned long)st.st_mtime)
399 	{
400 		fts_set(NiL, ent, FTS_SKIP);
401 		return 0;
402 	}
403 	else if (!state->fs3d || !iview(&st))
404 	{
405 		/*
406 		 * target is in top 3d view
407 		 */
408 
409 		if (st.st_dev == ent->fts_statp->st_dev && st.st_ino == ent->fts_statp->st_ino)
410 		{
411 			if (state->op == MV)
412 			{
413 				/*
414 				 * let rename() handle it
415 				 */
416 
417 				if (state->verbose)
418 					sfputr(sfstdout, state->path, '\n');
419 				goto operate;
420 			}
421 			if (!state->official)
422 				error(2, "%s: identical to %s", state->path, ent->fts_path);
423 			return 0;
424 		}
425 		if (S_ISDIR(st.st_mode))
426 		{
427 			error(2, "%s: cannot %s existing directory", state->path, state->opname);
428 			return 0;
429 		}
430 		if (state->verbose)
431 			sfputr(sfstdout, state->path, '\n');
432 		rm = state->op == LN || ent->fts_info == FTS_SL;
433 		if (!rm || !state->force)
434 		{
435 			if ((n = open(state->path, O_RDWR|O_BINARY)) >= 0)
436 			{
437 				close(n);
438 				if (state->force)
439 					/* ok */;
440 				else if (state->interactive)
441 				{
442 					if (astquery(-1, "%s %s? ", state->opname, state->path) < 0 || sh_checksig(state->context))
443 						return 0;
444 				}
445 				else if (state->op == LN)
446 				{
447 					error(2, "%s: cannot %s existing file", state->path, state->opname);
448 					return 0;
449 				}
450 			}
451 			else if (state->force)
452 				rm = 1;
453 			else
454 			{
455 				protection =
456 #ifdef ETXTBSY
457 				    errno == ETXTBSY ? "``running program''" :
458 #endif
459 				    st.st_uid != state->uid ? "``not owner''" :
460 				    fmtmode(st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO), 0) + 1;
461 				if (state->interactive)
462 				{
463 					if (astquery(-1, "override protection %s for %s? ", protection, state->path) < 0 || sh_checksig(state->context))
464 						return 0;
465 					rm = 1;
466 				}
467 				else if (!rm)
468 				{
469 					error(2, "%s: cannot %s %s protection", state->path, state->opname, protection);
470 					return 0;
471 				}
472 			}
473 		}
474 		switch (state->backup)
475 		{
476 		case BAK_existing:
477 		case BAK_number:
478 			v = 0;
479 			if (s = strrchr(state->path, '/'))
480 			{
481 				e = state->path;
482 				*s++ = 0;
483 			}
484 			else
485 			{
486 				e = (char*)dot;
487 				s = state->path;
488 			}
489 			n = strlen(s);
490 			if (fts = fts_open((char**)e, FTS_NOCHDIR|FTS_ONEPATH|FTS_PHYSICAL|FTS_NOPOSTORDER|FTS_NOSTAT|FTS_NOSEEDOTDIR, NiL))
491 			{
492 				while (sub = fts_read(fts))
493 				{
494 					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)
495 						v = m;
496 					if (sub->fts_level)
497 						fts_set(NiL, sub, FTS_SKIP);
498 				}
499 				fts_close(fts);
500 			}
501 			if (s != state->path)
502 				*--s = '/';
503 			if (v || state->backup == BAK_number)
504 			{
505 				sfprintf(state->tmp, "%s.%s%d%s", state->path, state->suffix, v + 1, state->suffix);
506 				goto backup;
507 			}
508 			/*FALLTHROUGH*/
509 		case BAK_simple:
510 			sfprintf(state->tmp, "%s%s", state->path, state->suffix);
511 		backup:
512 			if (!(s = sfstruse(state->tmp)))
513 				error(ERROR_SYSTEM|3, "%s: out of space", state->path);
514 			if (rename(state->path, s))
515 			{
516 				error(ERROR_SYSTEM|2, "%s: cannot backup to %s", state->path, s);
517 				return 0;
518 			}
519 			break;
520 		default:
521 			if (rm && remove(state->path))
522 			{
523 				error(ERROR_SYSTEM|2, "%s: cannot remove", state->path);
524 				return 0;
525 			}
526 			break;
527 		}
528 	}
529  operate:
530 	switch (state->op)
531 	{
532 	case MV:
533 		for (;;)
534 		{
535 			if (!rename(ent->fts_path, state->path))
536 				return 0;
537 			if (errno == ENOENT)
538 				rm = 1;
539 			else if (!rm && st.st_mode && !remove(state->path))
540 			{
541 				rm = 1;
542 				continue;
543 			}
544 			if (errno != EXDEV && (rm || S_ISDIR(ent->fts_statp->st_mode)))
545 			{
546 				error(ERROR_SYSTEM|2, "%s: cannot rename to %s", ent->fts_path, state->path);
547 				return 0;
548 			}
549 			else
550 				break;
551 		}
552 		/*FALLTHROUGH*/
553 	case CP:
554 		if (S_ISLNK(ent->fts_statp->st_mode))
555 		{
556 			if ((n = pathgetlink(ent->fts_path, state->text, sizeof(state->text) - 1)) < 0)
557 			{
558 				error(ERROR_SYSTEM|2, "%s: cannot read symbolic link text", ent->fts_path);
559 				return 0;
560 			}
561 			state->text[n] = 0;
562 			if (pathsetlink(state->text, state->path))
563 			{
564 				error(ERROR_SYSTEM|2, "%s: cannot copy symbolic link to %s", ent->fts_path, state->path);
565 				return 0;
566 			}
567 		}
568 		else if (state->op == CP || S_ISREG(ent->fts_statp->st_mode) || S_ISDIR(ent->fts_statp->st_mode))
569 		{
570 			if (ent->fts_statp->st_size > 0 && (rfd = open(ent->fts_path, O_RDONLY|O_BINARY)) < 0)
571 			{
572 				error(ERROR_SYSTEM|2, "%s: cannot read", ent->fts_path);
573 				return 0;
574 			}
575 			else if ((wfd = open(state->path, st.st_mode ? (state->wflags & ~O_EXCL) : state->wflags, ent->fts_statp->st_mode & state->perm)) < 0)
576 			{
577 				error(ERROR_SYSTEM|2, "%s: cannot write", state->path);
578 				if (ent->fts_statp->st_size > 0)
579 					close(rfd);
580 				return 0;
581 			}
582 			else if (ent->fts_statp->st_size > 0)
583 			{
584 				if (!(ip = sfnew(NiL, NiL, SF_UNBOUND, rfd, SF_READ)))
585 				{
586 					error(ERROR_SYSTEM|2, "%s: %s read stream error", ent->fts_path, state->path);
587 					close(rfd);
588 					close(wfd);
589 				}
590 				else
591 				{
592 					n = 0;
593 					if (!(op = sfnew(NiL, NiL, SF_UNBOUND, wfd, SF_WRITE)))
594 					{
595 						error(ERROR_SYSTEM|2, "%s: %s write stream error", ent->fts_path, state->path);
596 						close(wfd);
597 						sfclose(ip);
598 					}
599 					else
600 					{
601 						if (sfmove(ip, op, (Sfoff_t)SF_UNBOUND, -1) < 0)
602 							n |= 3;
603 						if (!sfeof(ip))
604 							n |= 1;
605 						if (sfsync(op) || state->sync && fsync(wfd) || sfclose(op))
606 							n |= 2;
607 						if (sfclose(ip))
608 							n |= 1;
609 						if (n)
610 							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"));
611 					}
612 				}
613 			}
614 			else
615 				close(wfd);
616 		}
617 		else if (S_ISBLK(ent->fts_statp->st_mode) || S_ISCHR(ent->fts_statp->st_mode) || S_ISFIFO(ent->fts_statp->st_mode))
618 		{
619 			if (mknod(state->path, ent->fts_statp->st_mode, idevice(ent->fts_statp)))
620 			{
621 				error(ERROR_SYSTEM|2, "%s: cannot copy special file to %s", ent->fts_path, state->path);
622 				return 0;
623 			}
624 		}
625 		else
626 		{
627 			error(2, "%s: cannot copy -- unknown file type 0%o", ent->fts_path, S_ITYPE(ent->fts_statp->st_mode));
628 			return 0;
629 		}
630 		if (state->preserve)
631 		{
632 			if (ent->fts_info != FTS_SL)
633 			{
634 				if (stat(state->path, &st))
635 					error(ERROR_SYSTEM|2, "%s: cannot stat", state->path);
636 				else
637 				{
638 					if ((ent->fts_statp->st_mode & state->perm) != (st.st_mode & state->perm) && chmod(state->path, ent->fts_statp->st_mode & state->perm))
639 						error(ERROR_SYSTEM|2, "%s: cannot reset mode to %s", state->path, fmtmode(st.st_mode & state->perm, 0) + 1);
640 					preserve(state, state->path, &st, ent->fts_statp);
641 				}
642 			}
643 			if (state->op == MV && remove(ent->fts_path))
644 				error(ERROR_SYSTEM|1, "%s: cannot remove", ent->fts_path);
645 		}
646 		break;
647 	case LN:
648 		if ((*state->link)(ent->fts_path, state->path))
649 			error(ERROR_SYSTEM|2, "%s: cannot link to %s", ent->fts_path, state->path);
650 		break;
651 	}
652 	return 0;
653 }
654 
655 int
656 b_cp(int argc, register char** argv, void* context)
657 {
658 	register char*	file;
659 	register char*	s;
660 	char**		v;
661 	char*		backup_type;
662 	FTS*		fts;
663 	FTSENT*		ent;
664 	const char*	usage;
665 	int		path_resolve;
666 	int		standard;
667 	struct stat	st;
668 	State_t*	state;
669 	Shbltin_t*	sh;
670 
671 	cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
672 	if (!(sh = CMD_CONTEXT(context)) || !(state = (State_t*)sh->ptr))
673 	{
674 		if (!(state = newof(0, State_t, 1, 0)))
675 			error(ERROR_SYSTEM|3, "out of space");
676 		if (sh)
677 			sh->ptr = state;
678 	}
679 	else
680 		memset(state, 0, offsetof(State_t, INITSTATE));
681 	state->context = context;
682 	state->presiz = -1;
683 	backup_type = 0;
684 	state->flags = FTS_NOCHDIR|FTS_NOSEEDOTDIR;
685 	state->uid = geteuid();
686 	state->wflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY;
687 	if (!state->tmp && !(state->tmp = sfstropen()))
688 		error(ERROR_SYSTEM|3, "out of space [tmp string]");
689 	sfputr(state->tmp, usage_head, -1);
690 	standard = !strcmp(astconf("CONFORMANCE", NiL, NiL), "standard");
691 	switch (error_info.id[0])
692 	{
693 	case 'c':
694 	case 'C':
695 		sfputr(state->tmp, usage_cp, -1);
696 		state->op = CP;
697 		state->stat = stat;
698 		path_resolve = -1;
699 		break;
700 	case 'l':
701 	case 'L':
702 		sfputr(state->tmp, usage_ln, -1);
703 		state->op = LN;
704 		state->flags |= FTS_PHYSICAL;
705 		state->link = link;
706 		state->stat = lstat;
707 		path_resolve = 1;
708 		break;
709 	case 'm':
710 	case 'M':
711 		sfputr(state->tmp, usage_mv, -1);
712 		state->op = MV;
713 		state->flags |= FTS_PHYSICAL;
714 		state->preserve = 1;
715 		state->stat = lstat;
716 		path_resolve = 1;
717 		break;
718 	default:
719 		error(3, "not implemented");
720 		break;
721 	}
722 	sfputr(state->tmp, usage_tail, -1);
723 	if (!(usage = sfstruse(state->tmp)))
724 		error(ERROR_SYSTEM|3, "%s: out of space", state->path);
725 	state->opname = state->op == CP ? ERROR_translate(0, 0, 0, "overwrite") : ERROR_translate(0, 0, 0, "replace");
726 	for (;;)
727 	{
728 		switch (optget(argv, usage))
729 		{
730 		case 'a':
731 			state->flags |= FTS_PHYSICAL;
732 			state->preserve = 1;
733 			state->recursive = 1;
734 			path_resolve = 1;
735 			continue;
736 		case 'b':
737 			state->backup = 1;
738 			continue;
739 		case 'f':
740 			state->force = 1;
741 			if (state->op != CP || !standard)
742 				state->interactive = 0;
743 			continue;
744 		case 'h':
745 			state->hierarchy = 1;
746 			continue;
747 		case 'i':
748 			state->interactive = 1;
749 			if (state->op != CP || !standard)
750 				state->force = 0;
751 			continue;
752 		case 'l':
753 			state->op = LN;
754 			state->link = link;
755 			state->stat = lstat;
756 			continue;
757 		case 'p':
758 			state->preserve = 1;
759 			continue;
760 		case 'r':
761 			state->recursive = 1;
762 			if (path_resolve < 0)
763 				path_resolve = 0;
764 			continue;
765 		case 's':
766 			state->op = LN;
767 			state->link = pathsetlink;
768 			state->stat = lstat;
769 			continue;
770 		case 'u':
771 			state->update = 1;
772 			continue;
773 		case 'v':
774 			state->verbose = 1;
775 			continue;
776 		case 'x':
777 			state->flags |= FTS_XDEV;
778 			continue;
779 		case 'F':
780 #if _lib_fsync
781 			state->sync = 1;
782 #else
783 			error(1, "%s not implemented on this system", opt_info.name);
784 #endif
785 			continue;
786 		case 'H':
787 			state->flags |= FTS_META|FTS_PHYSICAL;
788 			path_resolve = 1;
789 			continue;
790 		case 'L':
791 			state->flags &= ~FTS_PHYSICAL;
792 			path_resolve = 1;
793 			continue;
794 		case 'P':
795 			state->flags &= ~FTS_META;
796 			state->flags |= FTS_PHYSICAL;
797 			path_resolve = 1;
798 			continue;
799 		case 'R':
800 			state->recursive = 1;
801 			state->flags &= ~FTS_META;
802 			state->flags |= FTS_PHYSICAL;
803 			path_resolve = 1;
804 			continue;
805 		case 'S':
806 			state->suffix = opt_info.arg;
807 			continue;
808 		case 'V':
809 			backup_type = opt_info.arg;
810 			continue;
811 		case '?':
812 			error(ERROR_USAGE|4, "%s", opt_info.arg);
813 			continue;
814 		case ':':
815 			error(2, "%s", opt_info.arg);
816 			continue;
817 		}
818 		break;
819 	}
820 	argc -= opt_info.index + 1;
821 	argv += opt_info.index;
822 	if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--"))
823 	{
824 		argc--;
825 		argv++;
826 	}
827 	if (!(v = (char**)stkalloc(stkstd, (argc + 2) * sizeof(char*))))
828 		error(3, "out of space");
829 	memcpy(v, argv, (argc + 1) * sizeof(char*));
830 	argv = v;
831 	if (!standard)
832 	{
833 		state->wflags |= O_EXCL;
834 		if (!argc)
835 		{
836 			argc++;
837 			argv[1] = (char*)dot;
838 		}
839 	}
840 	if (state->backup)
841 	{
842 		if (!(file = backup_type) && !(backup_type = getenv("VERSION_CONTROL")))
843 			state->backup = BAK_existing;
844 		else
845 			switch (strkey(backup_type))
846 			{
847 			case HASHKEY6('e','x','i','s','t','i'):
848 			case HASHKEY5('e','x','i','s','t'):
849 			case HASHKEY4('e','x','i','s'):
850 			case HASHKEY3('e','x','i'):
851 			case HASHKEY2('e','x'):
852 			case HASHKEY1('e'):
853 			case HASHKEY3('n','i','l'):
854 			case HASHKEY2('n','i'):
855 				state->backup = BAK_existing;
856 				break;
857 			case HASHKEY5('n','e','v','e','r'):
858 			case HASHKEY4('n','e','v','e'):
859 			case HASHKEY3('n','e','v'):
860 			case HASHKEY2('n','e'):
861 			case HASHKEY6('s','i','m','p','l','e'):
862 			case HASHKEY5('s','i','m','p','l'):
863 			case HASHKEY4('s','i','m','p'):
864 			case HASHKEY3('s','i','m'):
865 			case HASHKEY2('s','i'):
866 			case HASHKEY1('s'):
867 				state->backup = BAK_simple;
868 				break;
869 			case HASHKEY6('n','u','m','b','e','r'):
870 			case HASHKEY5('n','u','m','b','e'):
871 			case HASHKEY4('n','u','m','b'):
872 			case HASHKEY3('n','u','m'):
873 			case HASHKEY2('n','u'):
874 			case HASHKEY1('t'):
875 				state->backup = BAK_number;
876 				break;
877 			default:
878 				if (file)
879 					error(2, "%s: unknown backup type", backup_type);
880 				break;
881 			}
882 		if (!state->suffix && !(state->suffix = getenv("SIMPLE_BACKUP_SUFFIX")))
883 			state->suffix = "~";
884 		state->suflen = strlen(state->suffix);
885 	}
886 	if (argc <= 0 || error_info.errors)
887 		error(ERROR_USAGE|4, "%s", optusage(NiL));
888 	if (!path_resolve)
889 		state->flags |= fts_flags();
890 	file = argv[argc];
891 	argv[argc] = 0;
892 	if (s = strrchr(file, '/'))
893 	{
894 		while (*s == '/')
895 			s++;
896 		if (!(!*s || *s == '.' && (!*++s || *s == '.' && !*++s)))
897 			s = 0;
898 	}
899 	if (file != (char*)dot)
900 		pathcanon(file, 0);
901 	if (!(state->directory = !stat(file, &st) && S_ISDIR(st.st_mode)) && argc > 1)
902 		error(ERROR_USAGE|4, "%s", optusage(NiL));
903 	if (s && !state->directory)
904 		error(3, "%s: not a directory", file);
905 	if ((state->fs3d = fs3d(FS3D_TEST)) && strmatch(file, "...|*/...|.../*"))
906 		state->official = 1;
907 	state->postsiz = strlen(file);
908 	if (state->pathsiz < roundof(state->postsiz + 2, PATH_CHUNK) && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + 2, PATH_CHUNK), 0)))
909 		error(3, "out of space");
910 	memcpy(state->path, file, state->postsiz + 1);
911 	if (state->directory && state->path[state->postsiz - 1] != '/')
912 		state->path[state->postsiz++] = '/';
913 	if (state->hierarchy)
914 	{
915 		if (!state->directory)
916 			error(3, "%s: last argument must be a directory", file);
917 		state->missmode = st.st_mode;
918 	}
919 	state->perm = state->uid ? S_IPERM : (S_IPERM & ~S_ISVTX);
920 	if (!state->recursive)
921 		state->flags |= FTS_TOP;
922 	if (fts = fts_open(argv, state->flags, NiL))
923 	{
924 		while (!sh_checksig(context) && (ent = fts_read(fts)) && !visit(state, ent));
925 		fts_close(fts);
926 	}
927 	else if (state->link != pathsetlink)
928 		switch (state->op)
929 		{
930 		case CP:
931 			error(ERROR_SYSTEM|2, "%s: cannot copy", argv[0]);
932 			break;
933 		case LN:
934 			error(ERROR_SYSTEM|2, "%s: cannot link", argv[0]);
935 			break;
936 		case MV:
937 			error(ERROR_SYSTEM|2, "%s: cannot move", argv[0]);
938 			break;
939 		}
940 	else if ((*state->link)(*argv, state->path))
941 		error(ERROR_SYSTEM|2, "%s: cannot link to %s", *argv, state->path);
942 	return error_info.errors != 0;
943 }
944