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