xref: /csrg-svn/bin/mv/mv.c (revision 8839)
1*8839Smckusick static	char *sccsid = "@(#)mv.c	4.6 (Berkeley) 82/10/23";
2*8839Smckusick 
31216Sbill /*
41216Sbill  * mv file1 file2
51216Sbill  */
61216Sbill 
71216Sbill #include <stdio.h>
81216Sbill #include <sys/types.h>
91216Sbill #include <sys/stat.h>
101216Sbill #include <signal.h>
111216Sbill 
121216Sbill #define	DOT	"."
131216Sbill #define	DOTDOT	".."
141216Sbill #define	DELIM	'/'
151216Sbill #define SDELIM "/"
16*8839Smckusick #define	MAXN	300
171216Sbill #define MODEBITS 07777
181216Sbill #define ROOTINO 2
191216Sbill 
201216Sbill char	*pname();
211216Sbill char	*sprintf();
221216Sbill char	*dname();
231216Sbill struct	stat s1, s2;
241216Sbill int	iflag = 0;	/* interactive flag. If this flag is set,
251216Sbill 			 * the user is queried before files are
261216Sbill 			 * destroyed by cp.
271216Sbill 			 */
281216Sbill int	fflag = 0;	/* force flag. supercedes all restrictions */
291216Sbill 
301216Sbill main(argc, argv)
311216Sbill register char *argv[];
321216Sbill {
331216Sbill 	register i, r;
341931Sroot 	register char *arg;
351216Sbill 
361216Sbill 	/* get the flag(s) */
371216Sbill 
381216Sbill 	if (argc < 2)
391216Sbill 		goto usage;
401931Sroot 	while (argc>1 && *argv[1] == '-') {
411216Sbill 		argc--;
421931Sroot 		arg = *++argv;
431216Sbill 
441931Sroot 		/*
451931Sroot 		 *  all files following a null option are considered file names
461931Sroot 		 */
471931Sroot 		if (*(arg+1) == '\0') break;
481931Sroot 
491931Sroot 		while (*++arg != '\0')
501931Sroot 			switch (*arg) {
511931Sroot 
521216Sbill 			/* interactive mode */
531216Sbill 			case 'i':
541216Sbill 				iflag++;
551216Sbill 				break;
561216Sbill 
571216Sbill 			/* force moves */
581216Sbill 			case 'f':
591216Sbill 				fflag++;
601216Sbill 				break;
611216Sbill 
621216Sbill 			/* don't live with bad options */
631216Sbill 			default:
641216Sbill 				goto usage;
651216Sbill 			}
661216Sbill 	}
671216Sbill 	if (argc < 3)
681216Sbill 		goto usage;
69*8839Smckusick 	if (lstat(argv[1], &s1) < 0) {
701216Sbill 		fprintf(stderr, "mv: cannot access %s\n", argv[1]);
711216Sbill 		return(1);
721216Sbill 	}
731216Sbill 	if ((s1.st_mode & S_IFMT) == S_IFDIR) {
741216Sbill 		if (argc != 3)
751216Sbill 			goto usage;
761216Sbill 		return mvdir(argv[1], argv[2]);
771216Sbill 	}
781216Sbill 	setuid(getuid());
791216Sbill 	if (argc > 3)
801216Sbill 		if (stat(argv[argc-1], &s2) < 0 || (s2.st_mode & S_IFMT) != S_IFDIR)
811216Sbill 			goto usage;
821216Sbill 	r = 0;
831216Sbill 	for (i=1; i<argc-1; i++)
841216Sbill 		r |= move(argv[i], argv[argc-1]);
851216Sbill 	return(r);
861216Sbill usage:
871931Sroot 	fprintf(stderr, "usage: mv [-if] f1 f2; or mv [-if] d1 d2; or mv [-if] f1 ... fn d1\n");
881216Sbill 	return(1);
891216Sbill }
901216Sbill 
911216Sbill move(source, target)
921216Sbill char *source, *target;
931216Sbill {
941216Sbill 	register c, i;
951216Sbill 	int	status;
961216Sbill 	char	buf[MAXN];
971216Sbill 
98*8839Smckusick 	if (lstat(source, &s1) < 0) {
991216Sbill 		fprintf(stderr, "mv: cannot access %s\n", source);
1001216Sbill 		return(1);
1011216Sbill 	}
1021216Sbill 	if ((s1.st_mode & S_IFMT) == S_IFDIR) {
1031216Sbill 		fprintf(stderr, "mv: directory rename only\n");
1041216Sbill 		return(1);
1051216Sbill 	}
1061216Sbill 	if (stat(target, &s2) >= 0) {
1071216Sbill 		if ((s2.st_mode & S_IFMT) == S_IFDIR) {
1081216Sbill 			sprintf(buf, "%s/%s", target, dname(source));
1091216Sbill 			target = buf;
1101216Sbill 		}
111*8839Smckusick 		if (lstat(target, &s2) >= 0) {
1121216Sbill 			if ((s2.st_mode & S_IFMT) == S_IFDIR) {
1131216Sbill 				fprintf(stderr, "mv: %s is a directory\n", target);
1141216Sbill 				return(1);
1151216Sbill 			} else if (iflag && !fflag) {
1161216Sbill 				fprintf(stderr, "remove %s? ", target);
1171216Sbill 				i = c = getchar();
1181216Sbill 				while (c != '\n' && c != EOF)
1191216Sbill 					c = getchar();
1201216Sbill 				if (i != 'y')
1211216Sbill 					return(1);
1221216Sbill 			}
1231216Sbill 			if (s1.st_dev==s2.st_dev && s1.st_ino==s2.st_ino) {
1241216Sbill 				fprintf(stderr, "mv: %s and %s are identical\n",
1251216Sbill 						source, target);
1261216Sbill 				return(1);
1271216Sbill 			}
1281216Sbill 			if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) {
1291216Sbill 				fprintf(stderr, "override protection %o for %s? ",
1301216Sbill 					s2.st_mode & MODEBITS, target);
1311216Sbill 				i = c = getchar();
1321216Sbill 				while (c != '\n' && c != EOF)
1331216Sbill 					c = getchar();
1341216Sbill 				if (i != 'y')
1351216Sbill 					return(1);
1361216Sbill 			}
1371216Sbill 			if (unlink(target) < 0) {
1381216Sbill 				fprintf(stderr, "mv: cannot unlink %s\n", target);
1391216Sbill 				return(1);
1401216Sbill 			}
1411216Sbill 		}
1421216Sbill 	}
143*8839Smckusick 	if ((s1.st_mode & S_IFMT) == S_IFLNK) {
144*8839Smckusick 		register m;
145*8839Smckusick 		char symln[MAXN];
146*8839Smckusick 
147*8839Smckusick 		if (readlink(source, symln, sizeof (symln)) < 0) {
148*8839Smckusick 			perror(source);
149*8839Smckusick 			return (1);
150*8839Smckusick 		}
151*8839Smckusick 		m = umask(~(s1.st_mode & MODEBITS));
152*8839Smckusick 		if (symlink(symln, target) < 0) {
153*8839Smckusick 			perror(target);
154*8839Smckusick 			return (1);
155*8839Smckusick 		}
156*8839Smckusick 		umask(m);
157*8839Smckusick 	} else if (link(source, target) < 0) {
1581216Sbill 		i = fork();
1591216Sbill 		if (i == -1) {
1601216Sbill 			fprintf(stderr, "mv: try again\n");
1611216Sbill 			return(1);
1621216Sbill 		}
1631216Sbill 		if (i == 0) {
1641216Sbill 			execl("/bin/cp", "cp", source, target, 0);
1651216Sbill 			fprintf(stderr, "mv: cannot exec cp\n");
1661216Sbill 			exit(1);
1671216Sbill 		}
1681216Sbill 		while ((c = wait(&status)) != i && c != -1)
1691216Sbill 			;
1701216Sbill 		if (status != 0)
1711216Sbill 			return(1);
1721216Sbill 		utime(target, &s1.st_atime);
1731216Sbill 	}
1741216Sbill 	if (unlink(source) < 0) {
1751216Sbill 		fprintf(stderr, "mv: cannot unlink %s\n", source);
1761216Sbill 		return(1);
1771216Sbill 	}
1781216Sbill 	return(0);
1791216Sbill }
1801216Sbill 
1811216Sbill mvdir(source, target)
1821216Sbill char *source, *target;
1831216Sbill {
1841216Sbill 	register char *p;
1851216Sbill 	register i;
1861216Sbill 	char buf[MAXN];
1871216Sbill 	char c,cc;
1881216Sbill 
1891216Sbill 	if (stat(target, &s2) >= 0) {
1901216Sbill 		if ((s2.st_mode&S_IFMT) != S_IFDIR) {
1911216Sbill 			fprintf(stderr, "mv: %s exists\n", target);
1921216Sbill 			return(1);
1931216Sbill 		}
1945759Smckusic 		p = dname(source);
1955759Smckusic 		if (strlen(target) > MAXN-strlen(p)-2) {
1961216Sbill 			fprintf(stderr, "mv :target name too long\n");
1971216Sbill 			return(1);
1981216Sbill 		}
1991216Sbill 		strcpy(buf, target);
2001216Sbill 		target = buf;
2011216Sbill 		strcat(buf, SDELIM);
2025759Smckusic 		strcat(buf, p);
2031216Sbill 		if (stat(target, &s2) >= 0) {
2041216Sbill 			fprintf(stderr, "mv: %s exists\n", buf);
2051216Sbill 			return(1);
2061216Sbill 		}
2071216Sbill 	}
2081216Sbill 	if (strcmp(source, target) == 0) {
2091216Sbill 		fprintf(stderr, "mv: ?? source == target, source exists and target doesnt\n");
2101216Sbill 		return(1);
2111216Sbill 	}
2121216Sbill 	p = dname(source);
2131216Sbill 	if (!strcmp(p, DOT) || !strcmp(p, DOTDOT) || !strcmp(p, "") || p[strlen(p)-1]=='/') {
2141216Sbill 		fprintf(stderr, "mv: cannot rename %s\n", p);
2151216Sbill 		return(1);
2161216Sbill 	}
2171216Sbill 	if (stat(pname(source), &s1) < 0 || stat(pname(target), &s2) < 0) {
2181216Sbill 		fprintf(stderr, "mv: cannot locate parent\n");
2191216Sbill 		return(1);
2201216Sbill 	}
2211216Sbill 	if (access(pname(target), 2) < 0) {
2221216Sbill 		fprintf(stderr, "mv: no write access to %s\n", pname(target));
2231216Sbill 		return(1);
2241216Sbill 	}
2251216Sbill 	if (access(pname(source), 2) < 0) {
2261216Sbill 		fprintf(stderr, "mv: no write access to %s\n", pname(source));
2271216Sbill 		return(1);
2281216Sbill 	}
2291216Sbill 	if (s1.st_dev != s2.st_dev) {
2301216Sbill 		fprintf(stderr, "mv: cannot move directories across devices\n");
2311216Sbill 		return(1);
2321216Sbill 	}
2331216Sbill 	if (s1.st_ino != s2.st_ino) {
2341216Sbill 		char dst[MAXN+5];
2351216Sbill 
2361216Sbill 		if (chkdot(source) || chkdot(target)) {
2371216Sbill 			fprintf(stderr, "mv: Sorry, path names including %s aren't allowed\n", DOTDOT);
2381216Sbill 			return(1);
2391216Sbill 		}
2401216Sbill 		stat(source, &s1);
2411216Sbill 		if (check(pname(target), s1.st_ino))
2421216Sbill 			return(1);
2431216Sbill 		for (i = 1; i <= NSIG; i++)
2441216Sbill 			signal(i, SIG_IGN);
2451216Sbill 		if (link(source, target) < 0) {
2461216Sbill 			fprintf(stderr, "mv: cannot link %s to %s\n", target, source);
2471216Sbill 			return(1);
2481216Sbill 		}
2491216Sbill 		if (unlink(source) < 0) {
2501216Sbill 			fprintf(stderr, "mv: %s: cannot unlink\n", source);
2511216Sbill 			unlink(target);
2521216Sbill 			return(1);
2531216Sbill 		}
2541216Sbill 		strcat(dst, target);
2551216Sbill 		strcat(dst, "/");
2561216Sbill 		strcat(dst, DOTDOT);
2571216Sbill 		if (unlink(dst) < 0) {
2581216Sbill 			fprintf(stderr, "mv: %s: cannot unlink\n", dst);
2591216Sbill 			if (link(target, source) >= 0)
2601216Sbill 				unlink(target);
2611216Sbill 			return(1);
2621216Sbill 		}
2631216Sbill 		if (link(pname(target), dst) < 0) {
2641216Sbill 			fprintf(stderr, "mv: cannot link %s to %s\n",
2651216Sbill 				dst, pname(target));
2661216Sbill 			if (link(pname(source), dst) >= 0)
2671216Sbill 				if (link(target, source) >= 0)
2681216Sbill 					unlink(target);
2691216Sbill 			return(1);
2701216Sbill 		}
2711216Sbill 		return(0);
2721216Sbill 	}
2731216Sbill 	if (link(source, target) < 0) {
2741216Sbill 		fprintf(stderr, "mv: cannot link %s and %s\n",
2751216Sbill 			source, target);
2761216Sbill 		return(1);
2771216Sbill 	}
2781216Sbill 	if (unlink(source) < 0) {
2791216Sbill 		fprintf(stderr, "mv: ?? cannot unlink %s\n", source);
2801216Sbill 		return(1);
2811216Sbill 	}
2821216Sbill 	return(0);
2831216Sbill }
2841216Sbill 
2851216Sbill char *
2861216Sbill pname(name)
2871216Sbill register char *name;
2881216Sbill {
2891216Sbill 	register c;
2901216Sbill 	register char *p, *q;
2911216Sbill 	static	char buf[MAXN];
2921216Sbill 
2931216Sbill 	p = q = buf;
2941216Sbill 	while (c = *p++ = *name++)
2951216Sbill 		if (c == DELIM)
2961216Sbill 			q = p-1;
2971216Sbill 	if (q == buf && *q == DELIM)
2981216Sbill 		q++;
2991216Sbill 	*q = 0;
3001216Sbill 	return buf[0]? buf : DOT;
3011216Sbill }
3021216Sbill 
3031216Sbill char *
3041216Sbill dname(name)
3051216Sbill register char *name;
3061216Sbill {
3071216Sbill 	register char *p;
3081216Sbill 
3091216Sbill 	p = name;
3101216Sbill 	while (*p)
3111216Sbill 		if (*p++ == DELIM && *p)
3121216Sbill 			name = p;
3131216Sbill 	return name;
3141216Sbill }
3151216Sbill 
3161216Sbill check(spth, dinode)
3171216Sbill char *spth;
3181216Sbill ino_t dinode;
3191216Sbill {
3201216Sbill 	char nspth[MAXN];
3211216Sbill 	struct stat sbuf;
3221216Sbill 
3231216Sbill 	sbuf.st_ino = 0;
3241216Sbill 
3251216Sbill 	strcpy(nspth, spth);
3261216Sbill 	while (sbuf.st_ino != ROOTINO) {
3271216Sbill 		if (stat(nspth, &sbuf) < 0) {
3281216Sbill 			fprintf(stderr, "mv: cannot access %s\n", nspth);
3291216Sbill 			return(1);
3301216Sbill 		}
3311216Sbill 		if (sbuf.st_ino == dinode) {
3321216Sbill 			fprintf(stderr, "mv: cannot move a directory into itself\n");
3331216Sbill 			return(1);
3341216Sbill 		}
3351216Sbill 		if (strlen(nspth) > MAXN-2-sizeof(DOTDOT)) {
3361216Sbill 			fprintf(stderr, "mv: name too long\n");
3371216Sbill 			return(1);
3381216Sbill 		}
3391216Sbill 		strcat(nspth, SDELIM);
3401216Sbill 		strcat(nspth, DOTDOT);
3411216Sbill 	}
3421216Sbill 	return(0);
3431216Sbill }
3441216Sbill 
3451216Sbill chkdot(s)
3461216Sbill register char *s;
3471216Sbill {
3481216Sbill 	do {
3491216Sbill 		if (strcmp(dname(s), DOTDOT) == 0)
3501216Sbill 			return(1);
3511216Sbill 		s = pname(s);
3521216Sbill 	} while (strcmp(s, DOT) != 0 && strcmp(s, SDELIM) != 0);
3531216Sbill 	return(0);
3541216Sbill }
355