xref: /csrg-svn/bin/mv/mv.c (revision 1216)
1*1216Sbill static char *sccsid = "@(#)mv.c	4.1 (Berkeley) 10/07/80";
2*1216Sbill /*
3*1216Sbill  * mv file1 file2
4*1216Sbill  */
5*1216Sbill 
6*1216Sbill #include <stdio.h>
7*1216Sbill #include <sys/types.h>
8*1216Sbill #include <sys/stat.h>
9*1216Sbill #include <sys/dir.h>
10*1216Sbill #include <signal.h>
11*1216Sbill 
12*1216Sbill #define	DOT	"."
13*1216Sbill #define	DOTDOT	".."
14*1216Sbill #define	DELIM	'/'
15*1216Sbill #define SDELIM "/"
16*1216Sbill #define	MAXN	100
17*1216Sbill #define MODEBITS 07777
18*1216Sbill #define ROOTINO 2
19*1216Sbill 
20*1216Sbill char	*pname();
21*1216Sbill char	*sprintf();
22*1216Sbill char	*dname();
23*1216Sbill struct	stat s1, s2;
24*1216Sbill int	iflag = 0;	/* interactive flag. If this flag is set,
25*1216Sbill 			 * the user is queried before files are
26*1216Sbill 			 * destroyed by cp.
27*1216Sbill 			 */
28*1216Sbill int	fflag = 0;	/* force flag. supercedes all restrictions */
29*1216Sbill 
30*1216Sbill main(argc, argv)
31*1216Sbill register char *argv[];
32*1216Sbill {
33*1216Sbill 	register i, r;
34*1216Sbill 
35*1216Sbill 	/* get the flag(s) */
36*1216Sbill 
37*1216Sbill 	if (argc < 2)
38*1216Sbill 		goto usage;
39*1216Sbill 	if (*argv[1] == '-') {
40*1216Sbill 		argc--;
41*1216Sbill 		while (*++argv[1] != '\0')
42*1216Sbill 			switch (*argv[1]) {
43*1216Sbill 
44*1216Sbill 			/* interactive mode */
45*1216Sbill 			case 'i':
46*1216Sbill 				iflag++;
47*1216Sbill 				break;
48*1216Sbill 
49*1216Sbill 			/* force moves */
50*1216Sbill 			case 'f':
51*1216Sbill 				fflag++;
52*1216Sbill 				break;
53*1216Sbill 
54*1216Sbill 			/* don't live with bad options */
55*1216Sbill 			default:
56*1216Sbill 				goto usage;
57*1216Sbill 			}
58*1216Sbill 		argv++;
59*1216Sbill 	}
60*1216Sbill 	if (argc < 3)
61*1216Sbill 		goto usage;
62*1216Sbill 	if (stat(argv[1], &s1) < 0) {
63*1216Sbill 		fprintf(stderr, "mv: cannot access %s\n", argv[1]);
64*1216Sbill 		return(1);
65*1216Sbill 	}
66*1216Sbill 	if ((s1.st_mode & S_IFMT) == S_IFDIR) {
67*1216Sbill 		if (argc != 3)
68*1216Sbill 			goto usage;
69*1216Sbill 		return mvdir(argv[1], argv[2]);
70*1216Sbill 	}
71*1216Sbill 	setuid(getuid());
72*1216Sbill 	if (argc > 3)
73*1216Sbill 		if (stat(argv[argc-1], &s2) < 0 || (s2.st_mode & S_IFMT) != S_IFDIR)
74*1216Sbill 			goto usage;
75*1216Sbill 	r = 0;
76*1216Sbill 	for (i=1; i<argc-1; i++)
77*1216Sbill 		r |= move(argv[i], argv[argc-1]);
78*1216Sbill 	return(r);
79*1216Sbill usage:
80*1216Sbill 	fprintf(stderr, "usage: mv f1 f2; or mv d1 d2; or mv f1 ... fn d1\n");
81*1216Sbill 	return(1);
82*1216Sbill }
83*1216Sbill 
84*1216Sbill move(source, target)
85*1216Sbill char *source, *target;
86*1216Sbill {
87*1216Sbill 	register c, i;
88*1216Sbill 	int	status;
89*1216Sbill 	char	buf[MAXN];
90*1216Sbill 
91*1216Sbill 	if (stat(source, &s1) < 0) {
92*1216Sbill 		fprintf(stderr, "mv: cannot access %s\n", source);
93*1216Sbill 		return(1);
94*1216Sbill 	}
95*1216Sbill 	if ((s1.st_mode & S_IFMT) == S_IFDIR) {
96*1216Sbill 		fprintf(stderr, "mv: directory rename only\n");
97*1216Sbill 		return(1);
98*1216Sbill 	}
99*1216Sbill 	if (stat(target, &s2) >= 0) {
100*1216Sbill 		if ((s2.st_mode & S_IFMT) == S_IFDIR) {
101*1216Sbill 			sprintf(buf, "%s/%s", target, dname(source));
102*1216Sbill 			target = buf;
103*1216Sbill 		}
104*1216Sbill 		if (stat(target, &s2) >= 0) {
105*1216Sbill 			if ((s2.st_mode & S_IFMT) == S_IFDIR) {
106*1216Sbill 				fprintf(stderr, "mv: %s is a directory\n", target);
107*1216Sbill 				return(1);
108*1216Sbill 			} else if (iflag && !fflag) {
109*1216Sbill 				fprintf(stderr, "remove %s? ", target);
110*1216Sbill 				i = c = getchar();
111*1216Sbill 				while (c != '\n' && c != EOF)
112*1216Sbill 					c = getchar();
113*1216Sbill 				if (i != 'y')
114*1216Sbill 					return(1);
115*1216Sbill 			}
116*1216Sbill 			if (s1.st_dev==s2.st_dev && s1.st_ino==s2.st_ino) {
117*1216Sbill 				fprintf(stderr, "mv: %s and %s are identical\n",
118*1216Sbill 						source, target);
119*1216Sbill 				return(1);
120*1216Sbill 			}
121*1216Sbill 			if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) {
122*1216Sbill 				fprintf(stderr, "override protection %o for %s? ",
123*1216Sbill 					s2.st_mode & MODEBITS, target);
124*1216Sbill 				i = c = getchar();
125*1216Sbill 				while (c != '\n' && c != EOF)
126*1216Sbill 					c = getchar();
127*1216Sbill 				if (i != 'y')
128*1216Sbill 					return(1);
129*1216Sbill 			}
130*1216Sbill 			if (unlink(target) < 0) {
131*1216Sbill 				fprintf(stderr, "mv: cannot unlink %s\n", target);
132*1216Sbill 				return(1);
133*1216Sbill 			}
134*1216Sbill 		}
135*1216Sbill 	}
136*1216Sbill 	if (link(source, target) < 0) {
137*1216Sbill 		i = fork();
138*1216Sbill 		if (i == -1) {
139*1216Sbill 			fprintf(stderr, "mv: try again\n");
140*1216Sbill 			return(1);
141*1216Sbill 		}
142*1216Sbill 		if (i == 0) {
143*1216Sbill 			execl("/bin/cp", "cp", source, target, 0);
144*1216Sbill 			fprintf(stderr, "mv: cannot exec cp\n");
145*1216Sbill 			exit(1);
146*1216Sbill 		}
147*1216Sbill 		while ((c = wait(&status)) != i && c != -1)
148*1216Sbill 			;
149*1216Sbill 		if (status != 0)
150*1216Sbill 			return(1);
151*1216Sbill 		utime(target, &s1.st_atime);
152*1216Sbill 	}
153*1216Sbill 	if (unlink(source) < 0) {
154*1216Sbill 		fprintf(stderr, "mv: cannot unlink %s\n", source);
155*1216Sbill 		return(1);
156*1216Sbill 	}
157*1216Sbill 	return(0);
158*1216Sbill }
159*1216Sbill 
160*1216Sbill mvdir(source, target)
161*1216Sbill char *source, *target;
162*1216Sbill {
163*1216Sbill 	register char *p;
164*1216Sbill 	register i;
165*1216Sbill 	char buf[MAXN];
166*1216Sbill 	char c,cc;
167*1216Sbill 
168*1216Sbill 	if (stat(target, &s2) >= 0) {
169*1216Sbill 		if ((s2.st_mode&S_IFMT) != S_IFDIR) {
170*1216Sbill 			fprintf(stderr, "mv: %s exists\n", target);
171*1216Sbill 			return(1);
172*1216Sbill 		} else if (iflag && !fflag) {
173*1216Sbill 			fprintf(stderr, "remove %s? ", target);
174*1216Sbill 			cc = c = getchar();
175*1216Sbill 			while (c != '\n' && c != EOF)
176*1216Sbill 				c = getchar();
177*1216Sbill 			if (cc != 'y')
178*1216Sbill 				return(1);
179*1216Sbill 		}
180*1216Sbill 		if (strlen(target) > MAXN-DIRSIZ-2) {
181*1216Sbill 			fprintf(stderr, "mv :target name too long\n");
182*1216Sbill 			return(1);
183*1216Sbill 		}
184*1216Sbill 		strcpy(buf, target);
185*1216Sbill 		target = buf;
186*1216Sbill 		strcat(buf, SDELIM);
187*1216Sbill 		strcat(buf, dname(source));
188*1216Sbill 		if (stat(target, &s2) >= 0) {
189*1216Sbill 			fprintf(stderr, "mv: %s exists\n", buf);
190*1216Sbill 			return(1);
191*1216Sbill 		}
192*1216Sbill 	}
193*1216Sbill 	if (strcmp(source, target) == 0) {
194*1216Sbill 		fprintf(stderr, "mv: ?? source == target, source exists and target doesnt\n");
195*1216Sbill 		return(1);
196*1216Sbill 	}
197*1216Sbill 	p = dname(source);
198*1216Sbill 	if (!strcmp(p, DOT) || !strcmp(p, DOTDOT) || !strcmp(p, "") || p[strlen(p)-1]=='/') {
199*1216Sbill 		fprintf(stderr, "mv: cannot rename %s\n", p);
200*1216Sbill 		return(1);
201*1216Sbill 	}
202*1216Sbill 	if (stat(pname(source), &s1) < 0 || stat(pname(target), &s2) < 0) {
203*1216Sbill 		fprintf(stderr, "mv: cannot locate parent\n");
204*1216Sbill 		return(1);
205*1216Sbill 	}
206*1216Sbill 	if (access(pname(target), 2) < 0) {
207*1216Sbill 		fprintf(stderr, "mv: no write access to %s\n", pname(target));
208*1216Sbill 		return(1);
209*1216Sbill 	}
210*1216Sbill 	if (access(pname(source), 2) < 0) {
211*1216Sbill 		fprintf(stderr, "mv: no write access to %s\n", pname(source));
212*1216Sbill 		return(1);
213*1216Sbill 	}
214*1216Sbill 	if (access(source, 2) < 0) {
215*1216Sbill 		fprintf(stderr, "mv: no write access to %s\n", source);
216*1216Sbill 		return(1);
217*1216Sbill 	}
218*1216Sbill 	if (s1.st_dev != s2.st_dev) {
219*1216Sbill 		fprintf(stderr, "mv: cannot move directories across devices\n");
220*1216Sbill 		return(1);
221*1216Sbill 	}
222*1216Sbill 	if (s1.st_ino != s2.st_ino) {
223*1216Sbill 		char dst[MAXN+5];
224*1216Sbill 
225*1216Sbill 		if (chkdot(source) || chkdot(target)) {
226*1216Sbill 			fprintf(stderr, "mv: Sorry, path names including %s aren't allowed\n", DOTDOT);
227*1216Sbill 			return(1);
228*1216Sbill 		}
229*1216Sbill 		stat(source, &s1);
230*1216Sbill 		if (check(pname(target), s1.st_ino))
231*1216Sbill 			return(1);
232*1216Sbill 		for (i = 1; i <= NSIG; i++)
233*1216Sbill 			signal(i, SIG_IGN);
234*1216Sbill 		if (link(source, target) < 0) {
235*1216Sbill 			fprintf(stderr, "mv: cannot link %s to %s\n", target, source);
236*1216Sbill 			return(1);
237*1216Sbill 		}
238*1216Sbill 		if (unlink(source) < 0) {
239*1216Sbill 			fprintf(stderr, "mv: %s: cannot unlink\n", source);
240*1216Sbill 			unlink(target);
241*1216Sbill 			return(1);
242*1216Sbill 		}
243*1216Sbill 		strcat(dst, target);
244*1216Sbill 		strcat(dst, "/");
245*1216Sbill 		strcat(dst, DOTDOT);
246*1216Sbill 		if (unlink(dst) < 0) {
247*1216Sbill 			fprintf(stderr, "mv: %s: cannot unlink\n", dst);
248*1216Sbill 			if (link(target, source) >= 0)
249*1216Sbill 				unlink(target);
250*1216Sbill 			return(1);
251*1216Sbill 		}
252*1216Sbill 		if (link(pname(target), dst) < 0) {
253*1216Sbill 			fprintf(stderr, "mv: cannot link %s to %s\n",
254*1216Sbill 				dst, pname(target));
255*1216Sbill 			if (link(pname(source), dst) >= 0)
256*1216Sbill 				if (link(target, source) >= 0)
257*1216Sbill 					unlink(target);
258*1216Sbill 			return(1);
259*1216Sbill 		}
260*1216Sbill 		return(0);
261*1216Sbill 	}
262*1216Sbill 	if (link(source, target) < 0) {
263*1216Sbill 		fprintf(stderr, "mv: cannot link %s and %s\n",
264*1216Sbill 			source, target);
265*1216Sbill 		return(1);
266*1216Sbill 	}
267*1216Sbill 	if (unlink(source) < 0) {
268*1216Sbill 		fprintf(stderr, "mv: ?? cannot unlink %s\n", source);
269*1216Sbill 		return(1);
270*1216Sbill 	}
271*1216Sbill 	return(0);
272*1216Sbill }
273*1216Sbill 
274*1216Sbill char *
275*1216Sbill pname(name)
276*1216Sbill register char *name;
277*1216Sbill {
278*1216Sbill 	register c;
279*1216Sbill 	register char *p, *q;
280*1216Sbill 	static	char buf[MAXN];
281*1216Sbill 
282*1216Sbill 	p = q = buf;
283*1216Sbill 	while (c = *p++ = *name++)
284*1216Sbill 		if (c == DELIM)
285*1216Sbill 			q = p-1;
286*1216Sbill 	if (q == buf && *q == DELIM)
287*1216Sbill 		q++;
288*1216Sbill 	*q = 0;
289*1216Sbill 	return buf[0]? buf : DOT;
290*1216Sbill }
291*1216Sbill 
292*1216Sbill char *
293*1216Sbill dname(name)
294*1216Sbill register char *name;
295*1216Sbill {
296*1216Sbill 	register char *p;
297*1216Sbill 
298*1216Sbill 	p = name;
299*1216Sbill 	while (*p)
300*1216Sbill 		if (*p++ == DELIM && *p)
301*1216Sbill 			name = p;
302*1216Sbill 	return name;
303*1216Sbill }
304*1216Sbill 
305*1216Sbill check(spth, dinode)
306*1216Sbill char *spth;
307*1216Sbill ino_t dinode;
308*1216Sbill {
309*1216Sbill 	char nspth[MAXN];
310*1216Sbill 	struct stat sbuf;
311*1216Sbill 
312*1216Sbill 	sbuf.st_ino = 0;
313*1216Sbill 
314*1216Sbill 	strcpy(nspth, spth);
315*1216Sbill 	while (sbuf.st_ino != ROOTINO) {
316*1216Sbill 		if (stat(nspth, &sbuf) < 0) {
317*1216Sbill 			fprintf(stderr, "mv: cannot access %s\n", nspth);
318*1216Sbill 			return(1);
319*1216Sbill 		}
320*1216Sbill 		if (sbuf.st_ino == dinode) {
321*1216Sbill 			fprintf(stderr, "mv: cannot move a directory into itself\n");
322*1216Sbill 			return(1);
323*1216Sbill 		}
324*1216Sbill 		if (strlen(nspth) > MAXN-2-sizeof(DOTDOT)) {
325*1216Sbill 			fprintf(stderr, "mv: name too long\n");
326*1216Sbill 			return(1);
327*1216Sbill 		}
328*1216Sbill 		strcat(nspth, SDELIM);
329*1216Sbill 		strcat(nspth, DOTDOT);
330*1216Sbill 	}
331*1216Sbill 	return(0);
332*1216Sbill }
333*1216Sbill 
334*1216Sbill chkdot(s)
335*1216Sbill register char *s;
336*1216Sbill {
337*1216Sbill 	do {
338*1216Sbill 		if (strcmp(dname(s), DOTDOT) == 0)
339*1216Sbill 			return(1);
340*1216Sbill 		s = pname(s);
341*1216Sbill 	} while (strcmp(s, DOT) != 0 && strcmp(s, SDELIM) != 0);
342*1216Sbill 	return(0);
343*1216Sbill }
344