xref: /illumos-gate/usr/src/contrib/ast/src/lib/libcmd/cp.c (revision 3e14f97f)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1992-2010 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) 2010-01-20 $\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_fix.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(ERROR_SYSTEM|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 	void*		cleanup = context;
671 
672 	cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
673 	if (!(sh = CMD_CONTEXT(context)) || !(state = (State_t*)sh->ptr))
674 	{
675 		if (!(state = newof(0, State_t, 1, 0)))
676 			error(ERROR_SYSTEM|3, "out of space");
677 		if (sh)
678 			sh->ptr = state;
679 	}
680 	else
681 		memset(state, 0, offsetof(State_t, INITSTATE));
682 	state->context = context;
683 	state->presiz = -1;
684 	backup_type = 0;
685 	state->flags = FTS_NOCHDIR|FTS_NOSEEDOTDIR;
686 	state->uid = geteuid();
687 	state->wflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY;
688 	if (!state->tmp && !(state->tmp = sfstropen()))
689 		error(ERROR_SYSTEM|3, "out of space [tmp string]");
690 	sfputr(state->tmp, usage_head, -1);
691 	standard = !strcmp(astconf("CONFORMANCE", NiL, NiL), "standard");
692 	switch (error_info.id[0])
693 	{
694 	case 'c':
695 	case 'C':
696 		sfputr(state->tmp, usage_cp, -1);
697 		state->op = CP;
698 		state->stat = stat;
699 		path_resolve = -1;
700 		break;
701 	case 'l':
702 	case 'L':
703 		sfputr(state->tmp, usage_ln, -1);
704 		state->op = LN;
705 		state->flags |= FTS_PHYSICAL;
706 		state->link = link;
707 		state->stat = lstat;
708 		path_resolve = 1;
709 		break;
710 	case 'm':
711 	case 'M':
712 		sfputr(state->tmp, usage_mv, -1);
713 		state->op = MV;
714 		state->flags |= FTS_PHYSICAL;
715 		state->preserve = 1;
716 		state->stat = lstat;
717 		path_resolve = 1;
718 		break;
719 	default:
720 		error(3, "not implemented");
721 		break;
722 	}
723 	sfputr(state->tmp, usage_tail, -1);
724 	if (!(usage = sfstruse(state->tmp)))
725 		error(ERROR_SYSTEM|3, "%s: out of space", state->path);
726 	state->opname = state->op == CP ? ERROR_translate(0, 0, 0, "overwrite") : ERROR_translate(0, 0, 0, "replace");
727 	for (;;)
728 	{
729 		switch (optget(argv, usage))
730 		{
731 		case 'a':
732 			state->flags |= FTS_PHYSICAL;
733 			state->preserve = 1;
734 			state->recursive = 1;
735 			path_resolve = 1;
736 			continue;
737 		case 'b':
738 			state->backup = 1;
739 			continue;
740 		case 'f':
741 			state->force = 1;
742 			if (state->op != CP || !standard)
743 				state->interactive = 0;
744 			continue;
745 		case 'h':
746 			state->hierarchy = 1;
747 			continue;
748 		case 'i':
749 			state->interactive = 1;
750 			if (state->op != CP || !standard)
751 				state->force = 0;
752 			continue;
753 		case 'l':
754 			state->op = LN;
755 			state->link = link;
756 			state->stat = lstat;
757 			continue;
758 		case 'p':
759 			state->preserve = 1;
760 			continue;
761 		case 'r':
762 			state->recursive = 1;
763 			if (path_resolve < 0)
764 				path_resolve = 0;
765 			continue;
766 		case 's':
767 			state->op = LN;
768 			state->link = pathsetlink;
769 			state->stat = lstat;
770 			continue;
771 		case 'u':
772 			state->update = 1;
773 			continue;
774 		case 'v':
775 			state->verbose = 1;
776 			continue;
777 		case 'x':
778 			state->flags |= FTS_XDEV;
779 			continue;
780 		case 'F':
781 #if _lib_fsync
782 			state->sync = 1;
783 #else
784 			error(1, "%s not implemented on this system", opt_info.name);
785 #endif
786 			continue;
787 		case 'H':
788 			state->flags |= FTS_META|FTS_PHYSICAL;
789 			path_resolve = 1;
790 			continue;
791 		case 'L':
792 			state->flags &= ~FTS_PHYSICAL;
793 			path_resolve = 1;
794 			continue;
795 		case 'P':
796 			state->flags &= ~FTS_META;
797 			state->flags |= FTS_PHYSICAL;
798 			path_resolve = 1;
799 			continue;
800 		case 'R':
801 			state->recursive = 1;
802 			state->flags &= ~FTS_META;
803 			state->flags |= FTS_PHYSICAL;
804 			path_resolve = 1;
805 			continue;
806 		case 'S':
807 			state->suffix = opt_info.arg;
808 			continue;
809 		case 'V':
810 			backup_type = opt_info.arg;
811 			continue;
812 		case '?':
813 			error(ERROR_USAGE|4, "%s", opt_info.arg);
814 			continue;
815 		case ':':
816 			error(2, "%s", opt_info.arg);
817 			continue;
818 		}
819 		break;
820 	}
821 	argc -= opt_info.index + 1;
822 	argv += opt_info.index;
823 	if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--"))
824 	{
825 		argc--;
826 		argv++;
827 	}
828 	if (!(v = (char**)stkalloc(stkstd, (argc + 2) * sizeof(char*))))
829 		error(ERROR_SYSTEM|3, "out of space");
830 	memcpy(v, argv, (argc + 1) * sizeof(char*));
831 	argv = v;
832 	if (!standard)
833 	{
834 		state->wflags |= O_EXCL;
835 		if (!argc)
836 		{
837 			argc++;
838 			argv[1] = (char*)dot;
839 		}
840 	}
841 	if (state->backup)
842 	{
843 		if (!(file = backup_type) && !(backup_type = getenv("VERSION_CONTROL")))
844 			state->backup = BAK_existing;
845 		else
846 			switch (strkey(backup_type))
847 			{
848 			case HASHKEY6('e','x','i','s','t','i'):
849 			case HASHKEY5('e','x','i','s','t'):
850 			case HASHKEY4('e','x','i','s'):
851 			case HASHKEY3('e','x','i'):
852 			case HASHKEY2('e','x'):
853 			case HASHKEY1('e'):
854 			case HASHKEY3('n','i','l'):
855 			case HASHKEY2('n','i'):
856 				state->backup = BAK_existing;
857 				break;
858 			case HASHKEY5('n','e','v','e','r'):
859 			case HASHKEY4('n','e','v','e'):
860 			case HASHKEY3('n','e','v'):
861 			case HASHKEY2('n','e'):
862 			case HASHKEY6('s','i','m','p','l','e'):
863 			case HASHKEY5('s','i','m','p','l'):
864 			case HASHKEY4('s','i','m','p'):
865 			case HASHKEY3('s','i','m'):
866 			case HASHKEY2('s','i'):
867 			case HASHKEY1('s'):
868 				state->backup = BAK_simple;
869 				break;
870 			case HASHKEY6('n','u','m','b','e','r'):
871 			case HASHKEY5('n','u','m','b','e'):
872 			case HASHKEY4('n','u','m','b'):
873 			case HASHKEY3('n','u','m'):
874 			case HASHKEY2('n','u'):
875 			case HASHKEY1('t'):
876 				state->backup = BAK_number;
877 				break;
878 			default:
879 				if (file)
880 					error(2, "%s: unknown backup type", backup_type);
881 				break;
882 			}
883 		if (!state->suffix && !(state->suffix = getenv("SIMPLE_BACKUP_SUFFIX")))
884 			state->suffix = "~";
885 		state->suflen = strlen(state->suffix);
886 	}
887 	if (argc <= 0 || error_info.errors)
888 		error(ERROR_USAGE|4, "%s", optusage(NiL));
889 	if (!path_resolve)
890 		state->flags |= fts_flags();
891 	file = argv[argc];
892 	argv[argc] = 0;
893 	if (s = strrchr(file, '/'))
894 	{
895 		while (*s == '/')
896 			s++;
897 		if (!(!*s || *s == '.' && (!*++s || *s == '.' && !*++s)))
898 			s = 0;
899 	}
900 	if (file != (char*)dot)
901 		pathcanon(file, 0);
902 	if (!(state->directory = !stat(file, &st) && S_ISDIR(st.st_mode)) && argc > 1)
903 		error(ERROR_USAGE|4, "%s", optusage(NiL));
904 	if (s && !state->directory)
905 		error(3, "%s: not a directory", file);
906 	if ((state->fs3d = fs3d(FS3D_TEST)) && strmatch(file, "...|*/...|.../*"))
907 		state->official = 1;
908 	state->postsiz = strlen(file);
909 	if (state->pathsiz < roundof(state->postsiz + 2, PATH_CHUNK) && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + 2, PATH_CHUNK), 0)))
910 		error(ERROR_SYSTEM|3, "out of space");
911 	memcpy(state->path, file, state->postsiz + 1);
912 	if (state->directory && state->path[state->postsiz - 1] != '/')
913 		state->path[state->postsiz++] = '/';
914 	if (state->hierarchy)
915 	{
916 		if (!state->directory)
917 			error(3, "%s: last argument must be a directory", file);
918 		state->missmode = st.st_mode;
919 	}
920 	state->perm = state->uid ? S_IPERM : (S_IPERM & ~S_ISVTX);
921 	if (!state->recursive)
922 		state->flags |= FTS_TOP;
923 	if (fts = fts_open(argv, state->flags, NiL))
924 	{
925 		while (!sh_checksig(context) && (ent = fts_read(fts)) && !visit(state, ent));
926 		fts_close(fts);
927 	}
928 	else if (state->link != pathsetlink)
929 		switch (state->op)
930 		{
931 		case CP:
932 			error(ERROR_SYSTEM|2, "%s: cannot copy", argv[0]);
933 			break;
934 		case LN:
935 			error(ERROR_SYSTEM|2, "%s: cannot link", argv[0]);
936 			break;
937 		case MV:
938 			error(ERROR_SYSTEM|2, "%s: cannot move", argv[0]);
939 			break;
940 		}
941 	else if ((*state->link)(*argv, state->path))
942 		error(ERROR_SYSTEM|2, "%s: cannot link to %s", *argv, state->path);
943 	if (cleanup && !sh)
944 	{
945 		if (state->path)
946 			free(state->path);
947 		free(state);
948 	}
949 	return error_info.errors != 0;
950 }
951