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