hbase

heirloom base
git clone git://git.2f30.org/hbase
Log | Files | Refs | README

cp.c (28500B)


      1 /*
      2  * cp - copy files
      3  *
      4  * Gunnar Ritter, Freiburg i. Br., Germany, July 2002.
      5  */
      6 /*
      7  * Copyright (c) 2003 Gunnar Ritter
      8  *
      9  * This software is provided 'as-is', without any express or implied
     10  * warranty. In no event will the authors be held liable for any damages
     11  * arising from the use of this software.
     12  *
     13  * Permission is granted to anyone to use this software for any purpose,
     14  * including commercial applications, and to alter it and redistribute
     15  * it freely, subject to the following restrictions:
     16  *
     17  * 1. The origin of this software must not be misrepresented; you must not
     18  *    claim that you wrote the original software. If you use this software
     19  *    in a product, an acknowledgment in the product documentation would be
     20  *    appreciated but is not required.
     21  *
     22  * 2. Altered source versions must be plainly marked as such, and must not be
     23  *    misrepresented as being the original software.
     24  *
     25  * 3. This notice may not be removed or altered from any source distribution.
     26  */
     27 
     28 #if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4
     29 #define	USED	__attribute__ ((used))
     30 #elif defined __GNUC__
     31 #define	USED	__attribute__ ((unused))
     32 #else
     33 #define	USED
     34 #endif
     35 #if defined (SUS)
     36 static const char sccsid[] USED = "@(#)cp_sus.sl	1.84 (gritter) 3/4/06";
     37 #elif defined (S42)
     38 static const char sccsid[] USED = "@(#)cp_s42.sl	1.84 (gritter) 3/4/06";
     39 #else
     40 static const char sccsid[] USED = "@(#)cp.sl	1.84 (gritter) 3/4/06";
     41 #endif
     42 
     43 #include	<sys/types.h>
     44 #include	<sys/stat.h>
     45 #include	<sys/socket.h>
     46 #include	<sys/un.h>
     47 #include	<sys/time.h>
     48 #include	<sys/resource.h>
     49 #include	<fcntl.h>
     50 #include	<unistd.h>
     51 #include	<stdio.h>
     52 #include	<string.h>
     53 #include	<stdlib.h>
     54 #include	<malloc.h>
     55 #include	<errno.h>
     56 #include	<libgen.h>
     57 #include	<limits.h>
     58 #include	<dirent.h>
     59 #include	<utime.h>
     60 #include	"sfile.h"
     61 #include	"memalign.h"
     62 #include	"alloca.h"
     63 
     64 #ifndef	S_IFDOOR
     65 #define	S_IFDOOR	0xD000		/* Solaris door */
     66 #endif
     67 #ifndef	S_IFNAM
     68 #define	S_IFNAM		0x5000		/* XENIX special named file */
     69 #endif
     70 #ifndef	S_IFNWK
     71 #define	S_IFNWK		0x9000		/* HP-UX network special file */
     72 #endif
     73 
     74 static enum {
     75 	PERS_CP,
     76 	PERS_MV,
     77 	PERS_LN
     78 } pers;
     79 
     80 enum okay {
     81 	OKAY = 0,
     82 	STOP = 1
     83 };
     84 
     85 struct	islot {
     86 	struct islot	*i_lln;
     87 	struct islot	*i_rln;
     88 	char		*i_name;
     89 	ino_t		i_ino;
     90 };
     91 
     92 struct	dslot {
     93 	struct dslot	*d_nxt;
     94 	struct islot	*d_isl;
     95 	dev_t		d_dev;
     96 };
     97 
     98 static struct dslot	*d0;
     99 
    100 static unsigned	errcnt;			/* count of errors */
    101 static long	bflag;			/* buffer size */
    102 static int	dflag;			/* preserve hard links */
    103 #ifdef	O_DIRECT
    104 static int	Dflag;			/* use direct i/o */
    105 #endif	/* O_DIRECT */
    106 static int	fflag;			/* force */
    107 static int	iflag;			/* ask before overwriting */
    108 static int	nflag;			/* ln: do not remove links */
    109 static int	pflag;			/* preserve owner and times */
    110 static int	rflag;			/* recursive, read FIFOs */
    111 static int	Rflag;			/* recursive, recreate FIFOs */
    112 static int	sflag;			/* make symlinks / show statistics */
    113 static int	HLPflag;		/* -H, -L, or -P */
    114 static int	ontty;			/* stdin is a terminal */
    115 static mode_t	umsk;			/* current umask */
    116 static uid_t	myuid;			/* current uid */
    117 static gid_t	mygid;			/* current gid */
    118 static char	*progname;		/* argv[0] to main() */
    119 static struct islot	*inull;		/* inode tree null element */
    120 static void	(*go)(const char *, const char *, struct stat *, int,
    121 			int (*statfn)(const char *, struct stat *));
    122 
    123 static mode_t
    124 check_suid(const struct stat *sp, mode_t mode)
    125 {
    126 	if (sp->st_uid != myuid || sp->st_gid != mygid) {
    127 		mode &= ~(mode_t)S_ISUID;
    128 		if ((sp->st_mode&S_IFMT) != S_IFDIR || sp->st_mode&0010)
    129 			mode &= ~(mode_t)S_ISGID;
    130 		if ((sp->st_mode&S_IFMT) == S_IFDIR || sp->st_gid != mygid)
    131 			mode &= ~(mode_t)S_ISGID;
    132 	}
    133 	return mode;
    134 }
    135 
    136 static void
    137 nomem(void)
    138 {
    139 	write(2, progname, strlen(progname));
    140 	write(2, ": Insufficient memory space.\n", 29);
    141 	_exit(077);
    142 }
    143 
    144 static void *
    145 srealloc(void *vp, size_t nbytes)
    146 {
    147 	void *p;
    148 
    149 	if ((p = realloc(vp, nbytes)) == NULL)
    150 		nomem();
    151 	return p;
    152 }
    153 
    154 static void *
    155 smalloc(size_t nbytes)
    156 {
    157 	return srealloc(NULL, nbytes);
    158 }
    159 
    160 static void *
    161 scalloc(size_t nelem, size_t nbytes)
    162 {
    163 	void *p;
    164 
    165 	if ((p = calloc(nelem, nbytes)) == NULL)
    166 		nomem();
    167 	return p;
    168 }
    169 
    170 static void
    171 usage(void)
    172 {
    173 	switch (pers) {
    174 	case PERS_CP:
    175 		fprintf(stderr, "\
    176 Usage: %s [-i] [-p] f1 f2\n\
    177        %s [-i] [-p] f1 ... fn d1\n\
    178        %s [-i] [-p] [-r] d1 d2\n",
    179        			progname, progname, progname);
    180 		break;
    181 	case PERS_MV:
    182 		fprintf(stderr, "\
    183 Usage: %s [-f] [-i] f1 f2\n\
    184        %s [-f] [-i] f1 ... fn d1\n\
    185        %s [-f] [-i] d1 d2\n",
    186        			progname, progname, progname);
    187 		break;
    188 	case PERS_LN:
    189 		{
    190 #if defined (SUS)
    191 			const char nstr[] = "";
    192 #else	/* !SUS */
    193 			const char nstr[] = "[-n] ";
    194 #endif	/* !SUS */
    195 			fprintf(stderr, "\
    196 Usage: %s [-f] %s[-s] f1 f2\n\
    197        %s [-f] %s[-s] f1 ... fn d1\n\
    198        %s [-f] %s[-s] d1 d2\n",
    199        				progname, nstr, progname, nstr,
    200 				progname, nstr);
    201 		}
    202 		break;
    203 	}
    204 	exit(2);
    205 }
    206 
    207 static void
    208 freeislots(struct islot *ip)
    209 {
    210 	if (ip == inull)
    211 		return;
    212 	freeislots(ip->i_lln);
    213 	freeislots(ip->i_rln);
    214 	free(ip->i_name);
    215 	free(ip);
    216 }
    217 
    218 static void
    219 freedslots(void)
    220 {
    221 	struct dslot *dp, *dn;
    222 	
    223 	for (dp = d0; dp; dp = dn) {
    224 		dn = dp->d_nxt;
    225 		freeislots(dp->d_isl);
    226 		free(dp);
    227 	}
    228 	d0 = NULL;
    229 }
    230 
    231 static struct islot *
    232 isplay(ino_t ino, struct islot *x)
    233 {
    234 	struct islot	hdr;
    235 	struct islot	*leftmax, *rightmin;
    236 	struct islot	*y;
    237 
    238 	hdr.i_lln = hdr.i_rln = inull;
    239 	leftmax = rightmin = &hdr;
    240 	inull->i_ino = ino;
    241 	while (ino != x->i_ino) {
    242 		if (ino < x->i_ino) {
    243 			if (ino < x->i_lln->i_ino) {
    244 				y = x->i_lln;
    245 				x->i_lln = y->i_rln;
    246 				y->i_rln = x;
    247 				x = y;
    248 			}
    249 			if (x->i_lln == inull)
    250 				break;
    251 			rightmin->i_lln = x;
    252 			rightmin = x;
    253 			x = x->i_lln;
    254 		} else {
    255 			if (ino > x->i_rln->i_ino) {
    256 				y = x->i_rln;
    257 				x->i_rln = y->i_lln;
    258 				y->i_lln = x;
    259 				x = y;
    260 			}
    261 			if (x->i_rln == inull)
    262 				break;
    263 			leftmax->i_rln = x;
    264 			leftmax = x;
    265 			x = x->i_rln;
    266 		}
    267 	}
    268 	leftmax->i_rln = x->i_lln;
    269 	rightmin->i_lln = x->i_rln;
    270 	x->i_lln = hdr.i_rln;
    271 	x->i_rln = hdr.i_lln;
    272 	inull->i_ino = !ino;
    273 	return x;
    274 }
    275 
    276 static struct islot *
    277 ifind(ino_t ino, struct islot **it)
    278 {
    279 	if (*it == NULL)
    280 		return NULL;
    281 	*it = isplay(ino, *it);
    282 	return (*it)->i_ino == ino ? *it : NULL;
    283 }
    284 
    285 static void
    286 iput(struct islot *ik, struct islot **it)
    287 {
    288 	if ((*it) == NULL) {
    289 		ik->i_lln = ik->i_rln = inull;
    290 		(*it) = ik;
    291 	} else {
    292 		/*(*it) = isplay(ik->i_ino, (*it));*/
    293 		/* ifind() is always called before */
    294 		if (ik->i_ino < (*it)->i_ino) {
    295 			ik->i_lln = (*it)->i_lln;
    296 			ik->i_rln = (*it);
    297 			(*it)->i_lln = inull;
    298 			(*it) = ik;
    299 		} else if ((*it)->i_ino < ik->i_ino) {
    300 			ik->i_rln = (*it)->i_rln;
    301 			ik->i_lln = (*it);
    302 			(*it)->i_rln = inull;
    303 			(*it) = ik;
    304 		}
    305 	}
    306 }
    307 static int
    308 canlink(const char *path, const struct stat *sp)
    309 {
    310 	struct dslot *ds, *dp;
    311 	struct islot *ip;
    312 
    313 	for (ds = d0, dp = NULL; ds; dp = ds, ds = ds->d_nxt)
    314 		if (ds->d_dev == sp->st_dev)
    315 			break;
    316 	if (ds == NULL) {
    317 		ds = scalloc(1, sizeof *ds);
    318 		ds->d_dev = sp->st_dev;
    319 		if (d0 == NULL)
    320 			d0 = ds;
    321 		else
    322 			dp->d_nxt = ds;
    323 	}
    324 	if ((ip = ifind(sp->st_ino, &ds->d_isl)) == NULL) {
    325 		ip = scalloc(1, sizeof *ip);
    326 		ip->i_name = smalloc(strlen(path) + 1);
    327 		strcpy(ip->i_name, path);
    328 		ip->i_ino = sp->st_ino;
    329 		iput(ip, &ds->d_isl);
    330 	} else {
    331 		if (link(ip->i_name, path) == 0)
    332 			return 1;
    333 	}
    334 	return 0;
    335 }
    336 
    337 static enum okay
    338 confirm(void)
    339 {
    340 	enum okay yes = STOP;
    341 	char c;
    342 
    343 	if (read(0, &c, 1) == 1) {
    344 		yes = (c == 'y' || c == 'Y') ? OKAY : STOP;
    345 		while (c != '\n' && read(0, &c, 1) == 1);
    346 	}
    347 	return yes;
    348 }
    349 
    350 static void
    351 permissions(const char *path, const struct stat *ssp)
    352 {
    353 	mode_t mode;
    354 
    355 	mode = ssp->st_mode & 07777;
    356 	if (pflag) {
    357 		struct utimbuf ut;
    358 		ut.actime = ssp->st_atime;
    359 		ut.modtime = ssp->st_mtime;
    360 		if (utime(path, &ut) < 0) {
    361 #if defined (SUS) || defined (S42)
    362 			fprintf(stderr, "%s: cannot set times for %s\n%s: %s\n",
    363 					progname, path,
    364 					progname, strerror(errno));
    365 #endif /* SUS || S42 */
    366 			if (pers != PERS_MV)
    367 				errcnt |= 010;
    368 		}
    369 		if (myuid == 0) {
    370 			if (chown(path, ssp->st_uid, ssp->st_gid) < 0) {
    371 #if defined (SUS) || defined (S42)
    372 				fprintf(stderr,
    373 			"%s: cannot change owner and group of %s\n%s: %s\n",
    374 					progname, path,
    375 					progname, strerror(errno));
    376 #endif	/* SUS || S42 */
    377 				if (pers != PERS_MV)
    378 					errcnt |= 010;
    379 				mode &= ~(mode_t)(S_ISUID|S_ISGID);
    380 			}
    381 		} else
    382 			mode = check_suid(ssp, mode);
    383 	} else
    384 		mode = check_suid(ssp, mode & ~umsk);
    385 	if (chmod(path, mode) < 0) {
    386 #if defined (SUS) || defined (S42)
    387 		fprintf(stderr, "%s: cannot set permissions for %s\n%s: %s\n",
    388 				progname, path,
    389 				progname, strerror(errno));
    390 #endif	/* SUS || S42 */
    391 		if (pers != PERS_MV)
    392 			errcnt |= 010;
    393 	}
    394 }
    395 
    396 static size_t
    397 balign(const struct stat *ssp, const struct stat *dsp,
    398 		long long size, size_t prefd)
    399 {
    400 	int	n, m;
    401 	size_t	s;
    402 
    403 	n = (ssp->st_mode&S_IFMT) == S_IFREG && ssp->st_blksize >= 0 ?
    404 		ssp->st_blksize : 512;
    405 	m = (dsp->st_mode&S_IFMT) == S_IFREG && dsp->st_blksize >= 0 ?
    406 		dsp->st_blksize : 512;
    407 	if (prefd <= size && prefd % n == 0 && prefd % m == 0)
    408 		return prefd;
    409 	else if (n % m == 0)
    410 		return n;
    411 	else if (m % n == 0)
    412 		return m;
    413 	else {
    414 		s = n;
    415 		while (s % m)
    416 			s *= 2;
    417 		return s;
    418 	}
    419 }
    420 
    421 /*ARGSUSED*/
    422 void
    423 writerr(void *vp, int count, int written)
    424 {
    425 }
    426 
    427 static long long
    428 fdcopy(const char *src, const struct stat *ssp, const int sfd,
    429 		const char *tgt, const struct stat *dsp, const int dfd)
    430 {
    431 	static long pagesize;
    432 	static char *buf = NULL;
    433 	static size_t bufsize;
    434 	ssize_t rsz, wo, wt;
    435 	size_t	blksize;
    436 	long long	copied = 0;
    437 #ifdef	O_DIRECT
    438 	int	sfl = 0, dfl = 0, haverest = 0, dioen = 0;
    439 	off_t	remsz = 0;
    440 #endif
    441 
    442 #ifdef	__linux__
    443 	if (!bflag && !Dflag && ssp->st_size > 0) {
    444 		long long	sent;
    445 
    446 		if ((sent = sfile(dfd, sfd, ssp->st_mode, ssp->st_size)) ==
    447 				ssp->st_size)
    448 			return sent;
    449 		if (sent < 0)
    450 			goto err;
    451 	}
    452 #endif	/* __linux__ */
    453 	if (pagesize == 0)
    454 		if ((pagesize = sysconf(_SC_PAGESIZE)) < 0)
    455 			pagesize = 4096;
    456 	if (bflag)
    457 		blksize = bflag;
    458 #ifdef	O_DIRECT
    459 	else if (Dflag)
    460 		blksize = balign(ssp, dsp, ssp->st_size, 1048576);
    461 #endif	/* O_DIRECT */
    462 	else
    463 		blksize = balign(ssp, dsp, ssp->st_size, 4096);
    464 	if (blksize > bufsize) {
    465 		if (buf)
    466 			free(buf);
    467 		if ((buf = memalign(pagesize, bufsize = blksize)) == 0)
    468 			nomem();
    469 	}
    470 #ifdef	O_DIRECT
    471 	if (Dflag) {
    472 		if ((ssp->st_mode&S_IFMT) == S_IFREG &&
    473 					ssp->st_size > blksize ||
    474 				(ssp->st_mode&S_IFMT) == S_IFBLK) {
    475 			sfl = fcntl(sfd, F_GETFL);
    476 			fcntl(sfd, F_SETFL, sfl | O_DIRECT);
    477 			remsz = ssp->st_size;
    478 		}
    479 		if ((dsp->st_mode&S_IFMT) == S_IFREG ||
    480 				(dsp->st_mode&S_IFMT) == S_IFBLK) {
    481 			dfl = fcntl(dfd, F_GETFL);
    482 			fcntl(dfd, F_SETFL, dfl | O_DIRECT);
    483 			dioen = 1;
    484 		}
    485 	}
    486 #endif	/* O_DIRECT */
    487 	while ((rsz = read(sfd, buf, blksize)) > 0) {
    488 #ifdef	O_DIRECT
    489 		if (Dflag && rsz < blksize && dioen != 0) {
    490 			fcntl(dfd, F_SETFL, dfl);
    491 			dioen = 0;
    492 		}
    493 #endif	/* O_DIRECT */
    494 		wt = 0;
    495 		do {
    496 			if ((wo = write(dfd, buf + wt, rsz - wt)) < 0) {
    497 #ifdef	__linux__
    498 			err:
    499 #endif	/* __linux__ */
    500 				fprintf(stderr, "%s: %s: write: %s\n",
    501 						progname, tgt,
    502 						strerror(errno));
    503 				errcnt |= 04;
    504 #ifdef	notdef
    505 				if ((dsp->st_mode&S_IFMT) == S_IFREG)
    506 					unlink(tgt);
    507 #endif	/* notdef */
    508 				return copied;
    509 			}
    510 			wt += wo;
    511 			copied += wo;
    512 		} while (wt < rsz);
    513 #ifdef	O_DIRECT
    514 		if (Dflag && ssp->st_size > blksize &&
    515 				(ssp->st_mode&S_IFMT) == S_IFREG) {
    516 			remsz -= rsz;
    517 			if (remsz > 0 && remsz < blksize &&
    518 					haverest == 0 && (bflag ||
    519 					 remsz<(blksize=balign(ssp, dsp,
    520 							ssp->st_size, 4096)))) {
    521 				fcntl(sfd, F_SETFL, sfl);
    522 				haverest = 1;
    523 			}
    524 		}
    525 #endif	/* O_DIRECT */
    526 	}
    527 	if (rsz < 0) {
    528 		fprintf(stderr, "%s: %s: read: %s\n",
    529 				progname, src, strerror(errno));
    530 		errcnt |= 04;
    531 #ifdef	notdef
    532 		if ((dsp->st_mode&S_IFMT) == S_IFREG)
    533 			unlink(tgt);
    534 #endif	/* notdef */
    535 	}
    536 #ifdef	O_DIRECT
    537 	if (haverest) {
    538 #if !defined (__FreeBSD__) && !defined (__DragonFly__) && !defined (__APPLE__)
    539 		fdatasync(dfd);
    540 #else	/* __FreeBSD__, __DragonFly__, __APPLE__ */
    541 		fsync(dfd);
    542 #endif	/* __FreeBSD_, __DragonFly__, __APPLE__ */
    543 	}
    544 #endif	/* O_DIRECT */
    545 	return copied;
    546 }
    547 
    548 static void
    549 filecopy(const char *src, const struct stat *ssp,
    550 		const char *tgt, const struct stat *dsp)
    551 {
    552 	struct stat stbuf;
    553 	mode_t mode;
    554 	int sfd, dfd;
    555 	float	f, s, t;
    556 	struct timeval	tv1, tv2;
    557 	struct rusage	ru1, ru2;
    558 	long long	copied = 0;
    559 
    560 	if (sflag) {
    561 		gettimeofday(&tv1, NULL);
    562 		getrusage(RUSAGE_SELF, &ru1);
    563 	}
    564 	if ((sfd = open(src, O_RDONLY)) < 0) {
    565 		fprintf(stderr, "%s: cannot open %s\n%s: %s\n",
    566 				progname, src,
    567 				src, strerror(errno));
    568 		errcnt |= 01;
    569 		return;
    570 	}
    571 	mode = check_suid(ssp, ssp->st_mode & 07777);
    572 	if ((dfd = creat(tgt, mode)) < 0)
    573 		if (pers != PERS_MV && dsp != NULL && fflag && unlink(tgt) == 0)
    574 			dfd = creat(tgt, mode);
    575 	if (dfd < 0) {
    576 		fprintf(stderr, "%s: cannot create %s\n%s: %s\n",
    577 				progname, tgt,
    578 				progname, strerror(errno));
    579 		errcnt |= 01;
    580 		goto end1;
    581 	}
    582 	if (fstat(dfd, &stbuf) < 0) {
    583 		fprintf(stderr, "%s: fstat for %s failed: %s\n",
    584 				progname, tgt, strerror(errno));
    585 		errcnt |= 04;
    586 		goto end2;
    587 	}
    588 	copied = fdcopy(src, ssp, sfd, tgt, &stbuf, dfd);
    589 end2:
    590 	if (pflag)
    591 		permissions(tgt, ssp);
    592 	if (close(dfd) < 0) {
    593 		fprintf(stderr, "%s: close error on %s: %s\n",
    594 				progname, tgt, strerror(errno));
    595 		errcnt |= 04;
    596 	}
    597 	if (sflag) {
    598 		gettimeofday(&tv2, NULL);
    599 		getrusage(RUSAGE_SELF, &ru2);
    600 #define	tv2f(tv)	((tv).tv_sec + (float)(tv).tv_usec / 1000000)
    601 		f = tv2f(tv2) - tv2f(tv1);
    602 		s = (float)copied / (2<<19);
    603 		t = f ? s / f : s;
    604 		printf("                 ****** %s File Information ******\n"
    605 		       "        Input file              :       %s\n"
    606 		       "        Output file             :       %s\n"
    607 		       "        Real Time (secs)        : %14.6f\n"
    608 		       "        User Time (secs)        : %14.6f\n"
    609 		       "        System Time (secs)      : %14.6f\n"
    610 		       "        File Size (MB)          : %14.6f\n"
    611 		       "        Transfer Rate (MB/s)    : %14.6f\n",
    612 		       progname, src, tgt,
    613 		       f,
    614 		       tv2f(ru2.ru_utime) - tv2f(ru1.ru_utime),
    615 		       tv2f(ru2.ru_stime) - tv2f(ru1.ru_stime),
    616 		       s,
    617 		       t);
    618 	}
    619 end1:
    620 	close(sfd);
    621 }
    622 
    623 static void
    624 ignoring(const char *type, const char *path)
    625 {
    626 	fprintf(stderr, "%s: %signoring %s %s\n", progname,
    627 #if defined (SUS)
    628 			"",
    629 #else	/* !SUS */
    630 			"warning: ",
    631 #endif	/* !SUS */
    632 			type, path);
    633 #if defined (SUS)
    634 	if (pers == PERS_MV)
    635 		errcnt |= 020;
    636 #endif	/* SUS */
    637 }
    638 
    639 static enum okay
    640 do_unlink(const char *tgt, const struct stat *dsp)
    641 {
    642 	if (dsp && unlink(tgt) < 0) {
    643 		fprintf(stderr, "%s: cannot unlink %s\n%s: %s\n",
    644 				progname, tgt,
    645 				progname, strerror(errno));
    646 		errcnt |= 01;
    647 		return STOP;
    648 	}
    649 	return OKAY;
    650 }
    651 
    652 static void
    653 devicecopy(const char *src, const struct stat *ssp,
    654 		const char *tgt, const struct stat *dsp)
    655 {
    656 	if (do_unlink(tgt, dsp) != OKAY)
    657 		return;
    658 	if (mknod(tgt, check_suid(ssp, ssp->st_mode & (07777|S_IFMT)),
    659 				ssp->st_rdev) < 0) {
    660 		fprintf(stderr, "%s: cannot create special file %s\n%s: %s\n",
    661 				progname, tgt,
    662 				progname, strerror(errno));
    663 		errcnt |= 01;
    664 		return;
    665 	}
    666 	if (pflag)
    667 		permissions(tgt, ssp);
    668 }
    669 
    670 static void
    671 symlinkcopy(const char *src, const struct stat *ssp,
    672 		const char *tgt, const struct stat *dsp)
    673 {
    674 	static char *buf;
    675 	static size_t bufsize;
    676 	ssize_t sz;
    677 
    678 	if (buf == NULL)
    679 		buf = smalloc(bufsize = 256);
    680 	for (;;) {
    681 		sz = readlink(src, buf, bufsize - 1);
    682 		if (sz < 0) {
    683 			fprintf(stderr,
    684 				"%s: cannot read symbolic link %s\n%s: %s\n",
    685 					progname, src,
    686 					progname, strerror(errno));
    687 			errcnt |= 01;
    688 			return;
    689 		}
    690 		if (sz == bufsize - 1) {
    691 			buf = srealloc(buf, bufsize += 256);
    692 			continue;
    693 		}
    694 		buf[sz] = '\0';
    695 		break;
    696 	}
    697 	if (do_unlink(tgt, dsp) != OKAY)
    698 		return;
    699 	if (symlink(buf, tgt) < 0) {
    700 		fprintf(stderr, "%s: cannot create symbolic link %s\n%s: %s\n",
    701 				progname, tgt,
    702 				progname, strerror(errno));
    703 		errcnt |= 01;
    704 		return;
    705 	}
    706 	if (myuid == 0 && lchown(tgt, ssp->st_uid, ssp->st_gid) < 0) {
    707 #if defined (SUS)
    708 		fprintf(stderr,
    709 			"%s: cannot change owner and group of %s\n%s: %s\n",
    710 				progname, tgt,
    711 				progname, strerror(errno));
    712 #endif	/* SUS */
    713 		if (pers != PERS_MV)
    714 			errcnt |= 010;
    715 	}
    716 }
    717 
    718 static void
    719 socketcopy(const char *src, const struct stat *ssp,
    720 		const char *tgt, const struct stat *dsp)
    721 {
    722 	int	fd, addrsz;
    723 	struct sockaddr_un	addr;
    724 	size_t	len;
    725 
    726 	if (do_unlink(tgt, dsp) != OKAY)
    727 		return;
    728 	len = strlen(tgt);
    729 	memset(&addr, 0, sizeof addr);
    730 	addr.sun_family = AF_UNIX;
    731 	addrsz = sizeof addr - sizeof addr.sun_path + len;
    732 	if ((len >= sizeof addr.sun_path ? errno = ENAMETOOLONG, fd = -1, 1 :
    733 			(strncpy(addr.sun_path,tgt,sizeof addr.sun_path), 0)) ||
    734 			(fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0 ||
    735 			bind(fd, (struct sockaddr *)&addr, addrsz) < 0) {
    736 		fprintf(stderr, "%s: cannot create socket %s\n%s: %s\n",
    737 				progname, tgt,
    738 				progname, strerror(errno));
    739 		if (fd >= 0)
    740 			close(fd);
    741 		errcnt |= 01;
    742 		return;
    743 	}
    744 	close(fd);
    745 	if (pflag)
    746 		permissions(tgt, ssp);
    747 }
    748 
    749 static void
    750 specialcopy(const char *src, const struct stat *ssp,
    751 		const char *tgt, const struct stat *dsp)
    752 {
    753 	switch (ssp->st_mode & S_IFMT) {
    754 	case S_IFIFO:
    755 	case S_IFCHR:
    756 	case S_IFBLK:
    757 	case S_IFNAM:
    758 	case S_IFNWK:
    759 		devicecopy(src, ssp, tgt, dsp);
    760 		break;
    761 	case S_IFLNK:
    762 		symlinkcopy(src, ssp, tgt, dsp);
    763 		break;
    764 	case S_IFSOCK:
    765 		socketcopy(src, ssp, tgt, dsp);
    766 		break;
    767 	case S_IFDOOR:
    768 		ignoring("door", src);
    769 		break;
    770 	default:
    771 		fprintf(stderr, "%s: %s: unknown file type %o\n",
    772 				progname, src, (int)ssp->st_mode);
    773 		if (pers == PERS_MV)
    774 			errcnt |= 020;
    775 	}
    776 }
    777 
    778 static void
    779 getpath(const char *path, char **file, char **filend, size_t *sz, size_t *slen)
    780 {
    781 	*sz = 14 + strlen(path) + 2;
    782 	*file = smalloc(*sz);
    783 	*filend = *file;
    784 	if (path[0] == '/' && path[1] == '\0')
    785 		*(*filend)++ = '/';
    786 	else {
    787 		register const char *cp = path;
    788 		while ((*(*filend)++ = *cp++) != '\0');
    789 		(*filend)[-1] = '/';
    790 	}
    791 	*slen = *filend - *file;
    792 }
    793 
    794 static void
    795 setpath(const char *base, char **file, char **filend,
    796 		size_t slen, size_t *sz, size_t *ss)
    797 {
    798 	if (slen + (*ss = strlen(base)) >= *sz) {
    799 		*sz += slen + *ss + 15;
    800 		*file = srealloc(*file, *sz);
    801 		*filend = &(*file)[slen];
    802 	}
    803 	strcpy(*filend, base);
    804 }
    805 
    806 static enum okay
    807 trydelete(const char *path, int recursive)
    808 {
    809 	struct stat st;
    810 	enum okay val = OKAY;
    811 
    812 	if (lstat(path, &st) < 0) {
    813 		fprintf(stderr, "%s: cannot stat %s for removal\n%s: %s\n",
    814 				progname, path,
    815 				progname, strerror(errno));
    816 		errcnt |= 040;
    817 		val = STOP;
    818 	} else if ((st.st_mode & S_IFMT) == S_IFDIR) {
    819 		DIR *Dp;
    820 
    821 		if (recursive == 0)
    822 			goto do_rmdir;
    823 		if ((Dp = opendir(path)) != NULL) {
    824 			struct dirent *dp;
    825 			char *copy, *cend;
    826 			size_t sz, slen, ss;
    827 
    828 			getpath(path, &copy, &cend, &sz, &slen);
    829 			while ((dp = readdir(Dp)) != NULL) {
    830 				if (dp->d_name[0] == '.' &&
    831 						(dp->d_name[1] == '\0' ||
    832 					 	(dp->d_name[1] == '.' &&
    833 					 	dp->d_name[2] == '\0')))
    834 					continue;
    835 				setpath(dp->d_name, &copy, &cend,
    836 						slen, &sz, &ss);
    837 				if ((val = trydelete(copy, recursive)) == STOP)
    838 					break;
    839 			}
    840 			free(copy);
    841 			closedir(Dp);
    842 			if (val != STOP) {
    843 do_rmdir:
    844 				if (rmdir(path) < 0) {
    845 					fprintf(stderr,
    846 				"%s: cannot remove directory %s\n%s: %s\n",
    847 					progname, path,
    848 					progname, strerror(errno));
    849 					val = STOP;
    850 				}
    851 			}
    852 		} else {
    853 			fprintf(stderr,
    854 			"%s: cannot open directory %s for removal\n%s: %s\n",
    855 					progname, path,
    856 					progname, strerror(errno));
    857 			errcnt |= 040;
    858 			val = STOP;
    859 		}
    860 	} else {
    861 		if (unlink(path) < 0) {
    862 			fprintf(stderr, "%s: cannot unlink %s\n%s: %s\n",
    863 					progname, path,
    864 					progname, strerror(errno));
    865 			errcnt |= 040;
    866 			val = STOP;
    867 		}
    868 	}
    869 	return val;
    870 }
    871 
    872 static enum okay
    873 tryrename(const char *src, const struct stat *ssp,
    874 		const char *tgt, const struct stat *dsp)
    875 {
    876 	if (dsp && !fflag) {
    877 		if (iflag) {
    878 			fprintf(stderr, "%s: overwrite %s? ",
    879 					progname, tgt);
    880 			if (confirm() != OKAY)
    881 				return STOP;
    882 		} else if (ontty && (dsp->st_mode&S_IFMT) != S_IFLNK &&
    883 				access(tgt, W_OK) < 0) {
    884 			fprintf(stderr, "%s: %s: %o mode? ",
    885 					progname, tgt,
    886 					(int)(dsp->st_mode & 0777));
    887 			if (confirm() != OKAY)
    888 				return STOP;
    889 		}
    890 	}
    891 	if (rename(src, tgt) == 0)
    892 		return STOP;
    893 	if (errno != EXDEV) {
    894 		fprintf(stderr, "%s: cannot rename %s to %s\n%s: %s\n",
    895 				progname, src, tgt,
    896 				progname, strerror(errno));
    897 		errcnt |= 01;
    898 		return STOP;
    899 	}
    900 	if (dsp) {
    901 		if ((dsp->st_mode & S_IFMT) == S_IFDIR &&
    902 				(ssp->st_mode & S_IFMT) != S_IFDIR) {
    903 			fprintf(stderr, "%s: <%s> directory\n",
    904 					progname, tgt);
    905 			errcnt |= 01;
    906 			return STOP;
    907 		}
    908 		if ((dsp->st_mode & S_IFMT) != S_IFDIR &&
    909 				(ssp->st_mode & S_IFMT) == S_IFDIR) {
    910 			fprintf(stderr, "%s: Target must be directory\n",
    911 					progname);
    912 			errcnt |= 01;
    913 			return STOP;
    914 		}
    915 	}
    916 	if (dsp == NULL || trydelete(tgt, 0) == OKAY)
    917 		return OKAY;
    918 	return STOP;
    919 }
    920 
    921 static enum okay
    922 commoncheck(const char *src, const char *tgt, const struct stat *dsp,
    923 	struct stat *ssp,
    924 	int (*statfn)(const char *, struct stat *))
    925 {
    926 	if (statfn(src, ssp) < 0) {
    927 		if (pers == PERS_LN && sflag)
    928 			return OKAY;
    929 		fprintf(stderr, "%s: cannot access %s\n", progname, src);
    930 		errcnt |= 01;
    931 		return STOP;
    932 	}
    933 	if (dsp && (ssp->st_dev == dsp->st_dev && ssp->st_ino == dsp->st_ino)) {
    934 		fprintf(stderr, "%s: %s and %s are identical\n",
    935 				progname, src, tgt);
    936 		errcnt |= 01;
    937 		return STOP;
    938 	}
    939 	return OKAY;
    940 }
    941 
    942 static void
    943 cpmv(const char *src, const char *tgt, struct stat *dsp, int level,
    944 	int (*statfn)(const char *, struct stat *))
    945 {
    946 	struct stat sst;
    947 
    948 	if (commoncheck(src, tgt, dsp, &sst,
    949 			Rflag && level == 0 ?
    950 				pers == PERS_MV || HLPflag == 'P' ?
    951 					lstat : stat :
    952 			statfn) != OKAY)
    953 		return;
    954 	if (pers == PERS_MV && level == 0) {
    955 		if (tryrename(src, &sst, tgt, dsp) == STOP)
    956 			return;
    957 		dsp = NULL;
    958 	}
    959 	if ((sst.st_mode & S_IFMT) == S_IFDIR) {
    960 		DIR *Dp;
    961 		struct dirent *dp;
    962 		char *scp, *send, *dcp, *dend;
    963 		size_t ssz, slen, sss, dsz, dlen, dss;
    964 		int destcreat = 0;
    965 
    966 		if (rflag == 0) {
    967 			fprintf(stderr, "%s: <%s> directory\n",
    968 					progname, src);
    969 			errcnt |= 01;
    970 			return;
    971 		}
    972 		if (dsp && (dsp->st_mode & S_IFMT) != S_IFDIR) {
    973 			fprintf(stderr, "%s: %s: Not a directory.\n",
    974 					progname, tgt);
    975 			errcnt |= 01;
    976 			return;
    977 		}
    978 #if !defined (SUS)
    979 		if (pers == PERS_CP && dsp != NULL && iflag) {
    980 			fprintf(stderr, "%s: overwrite %s? ",
    981 					progname, tgt);
    982 			if (confirm() != OKAY)
    983 				return;
    984 		}
    985 #endif	/* !SUS */
    986 		if (dsp == NULL) {
    987 			if (mkdir(tgt, check_suid(&sst,
    988 					sst.st_mode&07777 | S_IRWXU)) < 0) {
    989 				fprintf(stderr, "%s: %s: %s\n",
    990 						progname, tgt, strerror(errno));
    991 				errcnt |= 01;
    992 				return;
    993 			}
    994 			destcreat = 1;
    995 		}
    996 		if ((Dp = opendir(src)) == NULL) {
    997 			fprintf(stderr, "%s: %s: %s\n",
    998 					progname, src,
    999 					strerror(errno));
   1000 			errcnt |= 01;
   1001 			return;
   1002 		}
   1003 		getpath(src, &scp, &send, &ssz, &slen);
   1004 		getpath(tgt, &dcp, &dend, &dsz, &dlen);
   1005 		while ((dp = readdir(Dp)) != NULL) {
   1006 			struct stat xst;
   1007 			if (dp->d_name[0] == '.' &&
   1008 					(dp->d_name[1] == '\0' ||
   1009 					 (dp->d_name[1] == '.' &&
   1010 					  dp->d_name[2] == '\0')))
   1011 				continue;
   1012 			setpath(dp->d_name, &scp, &send, slen, &ssz, &sss);
   1013 			setpath(dp->d_name, &dcp, &dend, dlen, &dsz, &dss);
   1014 			go(scp, dcp, stat(dcp, &xst) < 0 ? NULL : &xst,
   1015 					level + 1, statfn);
   1016 		}
   1017 		free(scp);
   1018 		free(dcp);
   1019 		if (destcreat)
   1020 			permissions(tgt, &sst);
   1021 		closedir(Dp);
   1022 	} else {
   1023 		if (dsp != NULL && iflag) {
   1024 			fprintf(stderr, "%s: overwrite %s? ",
   1025 					progname, tgt);
   1026 			if (confirm() != OKAY)
   1027 				return;
   1028 		}
   1029 		if (dflag && sst.st_nlink > 1) {
   1030 			if (canlink(tgt, &sst))
   1031 				return;
   1032 		}
   1033 		if ((sst.st_mode & S_IFMT) == S_IFREG || Rflag == 0)
   1034 			filecopy(src, &sst, tgt, dsp);
   1035 		else
   1036 			specialcopy(src, &sst, tgt, dsp);
   1037 	}
   1038 	if (pers == PERS_MV && errcnt == 0 && level == 0)
   1039 		trydelete(src, 1);
   1040 	if ((pers == PERS_CP || pers == PERS_MV) && level == 0 && d0)
   1041 		freedslots();
   1042 }
   1043 
   1044 /*ARGSUSED3*/
   1045 static void
   1046 ln(const char *src, const char *tgt, struct stat *dsp, int level,
   1047 	int (*statfn)(const char *, struct stat *))
   1048 {
   1049 	struct stat sst;
   1050 	int (*how)(const char *, const char *) = sflag ? symlink : link;
   1051 
   1052 	if (commoncheck(src, tgt, dsp, &sst, statfn) != OKAY)
   1053 		return;
   1054 	if ((sst.st_mode&S_IFMT) == S_IFDIR && !sflag) {
   1055 		fprintf(stderr, "%s: <%s> directory\n", progname, src);
   1056 		errcnt |= 01;
   1057 		return;
   1058 	}
   1059 #if (defined (SUS) || defined (S42)) && (defined (__linux__) || defined (__sun))
   1060 	if (sflag == 0) {
   1061 		char	*rpbuf = alloca(PATH_MAX+1);
   1062 		if (realpath(src, rpbuf) == NULL) {
   1063 			fprintf(stderr, "%s: cannot access %s\n",
   1064 					progname, src);
   1065 			errcnt |= 01;
   1066 			return;
   1067 		}
   1068 		src = rpbuf;
   1069 	}
   1070 #endif	/* (SUS || S42) && (__linux__ || __sun) */
   1071 	if (dsp
   1072 #if !defined (SUS)
   1073 			&& !sflag
   1074 #endif	/* !SUS */
   1075 			) {
   1076 		if (nflag && !fflag) {
   1077 			fprintf(stderr, "%s: %s: File exists\n",
   1078 					progname, tgt);
   1079 			errcnt |= 01;
   1080 			return;
   1081 		}
   1082 		if (!fflag && ontty && (dsp->st_mode&S_IFMT) != S_IFLNK &&
   1083 				access(tgt, W_OK) < 0) {
   1084 			fprintf(stderr, "%s: %s: %o mode? ",
   1085 				progname, tgt, (int)(dsp->st_mode & 0777));
   1086 			if (confirm() != OKAY)
   1087 				return;
   1088 		}
   1089 		if (unlink(tgt) < 0) {
   1090 			fprintf(stderr, "%s: cannot unlink %s\n%s: %s\n",
   1091 					progname, tgt,
   1092 					progname, strerror(errno));
   1093 			errcnt |= 01;
   1094 			return;
   1095 		}
   1096 	}
   1097 	if (how(src, tgt) < 0) {
   1098 		if (sflag)
   1099 			fprintf(stderr, "%s: cannot create %s\n%s: %s\n",
   1100 					progname, tgt,
   1101 					progname, strerror(errno));
   1102 		else if (errno == EXDEV)
   1103 			fprintf(stderr, "%s: different file system\n",
   1104 					progname);
   1105 		else
   1106 			fprintf(stderr, "%s: errno: %d no permission for %s\n",
   1107 					progname, errno, tgt);
   1108 		errcnt |= 01;
   1109 	}
   1110 }
   1111 
   1112 static const char *
   1113 getfl(void)
   1114 {
   1115 	const char	*optstring;
   1116 
   1117 	if (progname[0] == 'm' && progname[1] == 'v') {
   1118 		pers = PERS_MV;
   1119 		optstring = "b:fi";
   1120 		dflag = pflag = rflag = Rflag = 1;
   1121 		go = cpmv;
   1122 	} else if (progname[0] == 'l' && progname[1] == 'n') {
   1123 		pers = PERS_LN;
   1124 		optstring = "fns";
   1125 #if defined (SUS)
   1126 		nflag = 1;
   1127 #endif	/* SUS */
   1128 		go = ln;
   1129 	} else {
   1130 		pers = PERS_CP;
   1131 		optstring = "ab:dDfiHLpPrRs";
   1132 		go = cpmv;
   1133 	}
   1134 	return optstring;
   1135 }
   1136 
   1137 int
   1138 main(int argc, char **argv)
   1139 {
   1140 	struct stat dst, ust;
   1141 	const char *optstring;
   1142 	int (*statfn)(const char *, struct stat *);
   1143 	int i, illegal = 0;
   1144 
   1145 #ifdef	__GLIBC__
   1146 	putenv("POSIXLY_CORRECT=1");
   1147 #endif
   1148 	progname = basename(argv[0]);
   1149 	optstring = getfl();
   1150 	while ((i = getopt(argc, argv, optstring)) != EOF) {
   1151 		switch (i) {
   1152 		case 'b':
   1153 			bflag = atol(optarg);
   1154 			break;
   1155 #ifdef	O_DIRECT
   1156 		case 'D':
   1157 			Dflag = 1;
   1158 			break;
   1159 #endif	/* O_DIRECT */
   1160 		case 'd':
   1161 			dflag = 1;
   1162 			break;
   1163 		case 'f':
   1164 			fflag = 1;
   1165 #if defined (SUS)
   1166 			if (pers == PERS_MV)
   1167 				iflag = 0;
   1168 #endif	/* SUS */
   1169 			break;
   1170 		case 'i':
   1171 			iflag = 1;
   1172 #if defined (SUS)
   1173 			if (pers == PERS_MV)
   1174 				fflag = 0;
   1175 #endif	/* SUS */
   1176 			break;
   1177 		case 'n':
   1178 			nflag = 1;
   1179 			break;
   1180 		case 'p':
   1181 			pflag = 1;
   1182 			break;
   1183 		case 'a':
   1184 			dflag = pflag = 1;
   1185 			/*FALLTHRU*/
   1186 		case 'R':
   1187 			Rflag = 1;
   1188 			/*FALLTHRU*/
   1189 		case 'r':
   1190 			rflag = 1;
   1191 			break;
   1192 		case 's':
   1193 			sflag = 1;
   1194 			break;
   1195 		case 'H':
   1196 		case 'L':
   1197 		case 'P':
   1198 			HLPflag = i;
   1199 			break;
   1200 		default:
   1201 			illegal = 1;
   1202 		}
   1203 	}
   1204 	argv += optind, argc -= optind;
   1205 	if (argc < 2) {
   1206 		fprintf(stderr, "%s: Insufficient arguments (%d)\n",
   1207 				progname, argc);
   1208 		illegal = 1;
   1209 	}
   1210 	if (illegal)
   1211 		usage();
   1212 	umask(umsk = umask(0));
   1213 	ontty = isatty(0);
   1214 #if defined (SUS)
   1215 	/* nothing */
   1216 #elif defined (S42)
   1217 	if (pers == PERS_MV && !ontty)
   1218 		iflag = 0;
   1219 #else	/* !SUS, !S42 */
   1220 	if (!ontty)
   1221 		iflag = 0;
   1222 #endif	/* !SUS, !S42 */
   1223 	myuid = geteuid();
   1224 	mygid = getegid();
   1225 	inull = scalloc(1, sizeof *inull);
   1226 	inull->i_lln = inull->i_rln = inull;
   1227 	statfn = (Rflag && HLPflag != 'L'
   1228 #if !defined (SUS) && !defined (S42)
   1229 			|| pers == PERS_LN
   1230 #endif	/* !SUS && !S42 */
   1231 			? lstat : stat);
   1232 	if (lstat(argv[argc-1], &dst) == 0) {
   1233 		if ((dst.st_mode&S_IFMT) != S_IFLNK ||
   1234 				stat(argv[argc-1], &ust) < 0)
   1235 			ust = dst;
   1236 		if ((ust.st_mode&S_IFMT) == S_IFDIR) {
   1237 			char *copy, *cend;
   1238 			size_t sz, slen, ss;
   1239 			unsigned saverrs = errcnt;
   1240 
   1241 			getpath(argv[argc-1], &copy, &cend, &sz, &slen);
   1242 			for (i = 0; i < argc-1; i++) {
   1243 				errcnt = 0;
   1244 				setpath(basename(argv[i]), &copy, &cend,
   1245 						slen, &sz, &ss);
   1246 				go(argv[i], copy, statfn(copy, &dst) < 0 ?
   1247 						NULL : &dst, 0, statfn);
   1248 				saverrs |= errcnt;
   1249 			}
   1250 			errcnt = saverrs;
   1251 		} else if (argc > 2) {
   1252 			fprintf(stderr, "%s: Target must be directory\n",
   1253 					progname);
   1254 			usage();
   1255 		} else
   1256 			go(argv[0], argv[1], pers == PERS_CP ? &ust : &dst,
   1257 					0, statfn);
   1258 	} else if (argc > 2) {
   1259 		fprintf(stderr, "%s: %s not found\n", progname, argv[argc-1]);
   1260 		errcnt |= 01;
   1261 	} else
   1262 		go(argv[0], argv[1], NULL, 0, statfn);
   1263 	return errcnt;
   1264 }