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