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