|
|
1.1 root 1: /*
2: * cmd/mv.c
3: * Move (rename) files.
4: * Usage: mv file1 file2
5: * mv file ... directory
6: * Define SLOW for block at a time copying (not recommended).
7: */
8:
9: #include <stdio.h>
10: #include <stdlib.h>
11: #include <sys/stat.h>
12: #include <errno.h>
13: #include <access.h>
14: #include <signal.h>
15: #include <sys/dir.h>
16: #include <string.h>
17:
18: #define NOTREACHED return
19:
20: /* Global. */
21: #ifndef SLOW
22: char buf[50*BUFSIZ];
23: #else
24: char buf[BUFSIZ];
25: #endif
26:
27: char *usage = "\
28: Usage: mv [-f] file1 file2\n\
29: mv [-f] file ... directory\n\
30: ";
31: char *nowrite = "unwritable: %s";
32: char *nolink = "link %s %s failed";
33: char *nounlink = "unlink %s failed";
34: char *cmd = "mv: "; /* for error routines */
35: int myuid;
36: int newid;
37: char *child;
38: struct stat sb1, sb2, sb3;
39: int estat;
40: int fflag;
41: int interrupted;
42:
43: /* External. */
44: long lseek();
45:
46: /* Forward. */
47: int mv();
48: void cp();
49: int iszero();
50: int equals();
51: char *concat();
52: char *getparent();
53: char *getchild();
54: void warn();
55: void nomemory();
56: void onintr();
57: void catch();
58: void fatal();
59:
60: main(argc, argv) int argc; char *argv[];
61: {
62: register int i;
63:
64: if (argc>1 && *argv[1]=='-')
65: if (argv[1][1] == 'f' && argv[1][2] == '\0') {
66: fflag = 1;
67: argc--;
68: argv++;
69: } else {
70: fputs(usage, stderr);
71: exit(EINVAL);
72: NOTREACHED;
73: }
74:
75: catch(SIGINT);
76: catch(SIGHUP);
77: signal(SIGQUIT, SIG_IGN);
78:
79: myuid = getuid();
80:
81: if (--argc >= 2
82: && stat(argv[argc], &sb2) >= 0
83: && (sb2.st_mode&S_IFMT) == S_IFDIR)
84: for (i = 1; i < argc && !interrupted; i += 1) {
85: child = getchild(argv[i]);
86: mv(argv[i], concat(0, argv[argc], child));
87: }
88: else if (argc == 2) {
89: child = getchild(argv[1]);
90: mv(argv[1], argv[2]);
91: } else {
92: fputs(usage, stderr);
93: exit(EINVAL);
94: NOTREACHED;
95: }
96: exit(interrupted ? EINTR : estat);
97: }
98:
99: /*
100: * move f1 to f2 if at all possible.
101: */
102: int
103: mv(f1, f2) char *f1, *f2;
104: {
105: int isdir, isxdev, nocopy;
106: char *p2, *lp2;
107:
108: /* Check existence, format, delete permission on source */
109: if (stat(f1, &sb1) < 0)
110: return (warn(ENOENT, f1));
111: nocopy = (sb1.st_mode&S_IFMT) != S_IFREG;
112: isdir = (sb1.st_mode&S_IFMT) == S_IFDIR;
113: newid = (sb1.st_uid != myuid);
114: if (access(getparent(f1), ADEL) < 0)
115: return (warn(EACCES, f1));
116:
117: /* Check existence, format, create permission on parent of dest */
118: if (stat(p2 = getparent(f2), &sb2) < 0)
119: return (warn(ENOENT, p2));
120: if ((sb2.st_mode&S_IFMT) != S_IFDIR)
121: return (warn(ENOTDIR, p2));
122: if (access(p2, ADEL) < 0)
123: return (warn(EACCES, p2));
124:
125: /* Check for cross device mv's */
126: isxdev = sb1.st_dev != sb2.st_dev;
127: if (isxdev && nocopy)
128: return (warn(EXDEV, "%s to %s", f1, f2));
129:
130: /* Check for legal directory moves */
131: if (isdir) {
132: /* Can't mv . or .. */
133: if (equals(".", child) || equals("..", child))
134: return (warn(-1, "rename %s forbidden", f1));
135: /* Can't mv directory to child of itself */
136: for (lp2 = p2; ; ) {
137: /* If dev and ino of source file appear in the path
138: * from dest parent to root, then the dest
139: * will be a child of the source.
140: */
141: if (sb1.st_dev==sb2.st_dev && sb1.st_ino==sb2.st_ino)
142: return (warn(-1, "%s parent of %s", f1, f2));
143: sb3 = sb2;
144: lp2 = concat(1, lp2, "..");
145: if (stat(lp2, &sb2) < 0)
146: return (warn(errno, "%s", lp2));
147: if (sb2.st_dev==sb3.st_dev && sb2.st_ino==sb3.st_ino)
148: break;
149: }
150: }
151:
152: /* Check for existence, format, and writability of dest */
153: if (stat(f2, &sb2) >= 0) {
154: if ((sb2.st_mode&S_IFMT) == S_IFDIR)
155: return (warn(EISDIR, f2));
156: if (!fflag && access(f2, AWRITE) < 0)
157: return (warn(-1, nowrite, f2));
158:
159: /* Check for identity of source and dest */
160: if (sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino)
161: return (warn(-1, "%s and %s are identical", f1, f2));
162:
163: if (unlink(f2)) {
164: fatal(errno, nounlink, f2);
165: NOTREACHED;
166: }
167: }
168:
169: /* Do the mv, either cp or ln/rm */
170: if (isxdev)
171: cp(f1, f2, sb1.st_mode, sb1.st_uid, sb1.st_gid);
172: else {
173: if (link(f1, f2))
174: return (warn(errno, nolink, f1, f2));
175: if (isdir) {
176: if (unlink(lp2 = concat(1, f2, ".."))) {
177: fatal(errno, nounlink, lp2);
178: NOTREACHED;
179: }
180: if (link(p2, lp2)) {
181: fatal(errno, nolink, p2, lp2);
182: NOTREACHED;
183: }
184: }
185: }
186: if (unlink(f1)) {
187: fatal(errno, nounlink, f1);
188: NOTREACHED;
189: }
190: }
191:
192: /*
193: * Copy f1 to f2.
194: * The mode and ownership must be maintained.
195: */
196: void
197: cp(f1, f2, mode, uid, gid) char *f1, *f2; int mode, uid, gid;
198: {
199: register int fd1, fd2;
200: register int i, n;
201: register fsize_t size;
202: register char *bp;
203:
204: if ((fd1 = open(f1, 0)) < 0) {
205: fatal(errno, "open %s failed", f1);
206: NOTREACHED;
207: }
208: if ((fd2 = creat(f2, 0)) < 0) {
209: fatal(errno, "create %s failed", f2);
210: NOTREACHED;
211: }
212: if (newid)
213: mode &= 0777;
214: chmod(f2, mode&07777);
215: chown(f2, uid, gid);
216: size = sb1.st_size;
217: while ((n = read(fd1, buf, sizeof buf)) != 0) {
218: if (n < 0) {
219: fatal(errno, "read %s failed", f1);
220: NOTREACHED;
221: }
222: for (bp = buf; n > 0; n -= i, bp += i) {
223: i = (n > BUFSIZ) ? BUFSIZ : n;
224: size -= i;
225: if (i == BUFSIZ && iszero(bp) && size > 0 ) {
226: if (lseek(fd2, (long)BUFSIZ, 1) < 0L) {
227: fatal(errno, "seek %s failed", f2);
228: NOTREACHED;
229: }
230: } else if (write(fd2, bp, i) != i) {
231: fatal(errno, "write %s failed", f2);
232: NOTREACHED;
233: }
234: }
235: }
236: close(fd1);
237: close(fd2);
238: }
239:
240: /*
241: * Check for zeroes in a buffer of size BLKSIZ.
242: */
243: int
244: iszero(bufp) char *bufp;
245: {
246: register int i;
247: register char *bp;
248:
249: i = BUFSIZ;
250: bp = bufp;
251: do if (*bp++) return (0); while (--i);
252: return (1);
253: }
254:
255: /*
256: * Check two path names for equality knowing that p2 could be '/' terminated.
257: */
258: int
259: equals(p1, p2) register char *p1, *p2;
260: {
261: while (*p1++ == *p2)
262: if (*p2++ == '\0')
263: return (1);
264: if (*p2 == '/' && *--p1 == '\0')
265: return (1);
266: return (0);
267: }
268:
269: /*
270: * Concatenate s1 and s2 with a '/' between them.
271: */
272: char *
273: concat(l, s1, s2) int l; register char *s1, *s2;
274: {
275: register char *s3;
276: static char *tmp[2];
277: int n;
278:
279: n = strlen(s1) + strlen(s2) + 2;
280: if ((s3 = malloc(n)) == NULL) {
281: nomemory();
282: NOTREACHED;
283: }
284: strcpy(s3, s1);
285: strcat(s3, "/");
286: strcat(s3, s2);
287: if (tmp[l] != NULL)
288: free(tmp[l]);
289: tmp[l] = s3;
290: return (s3);
291: }
292:
293: void
294: warn(err, arg1) int err; char *arg1;
295: {
296: fprintf(stderr, "%s%r", cmd, &arg1);
297: if (err > 0 && err < sys_nerr)
298: fprintf(stderr, " %s", sys_errlist[err]);
299: fputs("\n", stderr);
300: estat = err;
301: }
302:
303: /*
304: * return name of parent
305: */
306: char *
307: getparent(dir) char *dir;
308: {
309: register i;
310: register char *p;
311: static char *par;
312:
313: if (par)
314: free( par);
315: i = strlen( dir);
316: par = malloc( i+1);
317: if (par == NULL) {
318: nomemory( );
319: NOTREACHED;
320: }
321: strcpy( par, dir);
322:
323: for (p=par+i; p>par; )
324: if (*--p != '/')
325: break;
326: for (++p; *--p!='/'; )
327: if (p == par) {
328: *p = '.';
329: break;
330: }
331: *++p = '\0';
332: return (par);
333: }
334:
335: /*
336: * return rightmost component of pathname
337: */
338: char *
339: getchild(dir) register char *dir;
340: {
341: register i;
342: register char *p;
343: static char ch[DIRSIZ+1];
344:
345: i = strlen( dir);
346: if (i == 0) {
347: fatal( -1, "NULL?");
348: NOTREACHED;
349: }
350: for (p=dir+i; *--p=='/'; )
351: if (p == dir) {
352: fatal( -1, "don't be silly");
353: NOTREACHED;
354: }
355: while (p > dir)
356: if (*--p == '/') {
357: ++p;
358: break;
359: }
360: return (strncpy( ch, p, DIRSIZ));
361: }
362:
363: void
364: nomemory( )
365: {
366: fatal( ENOMEM, "out of mem");
367: NOTREACHED;
368: }
369:
370: void
371: onintr( )
372: {
373: signal( SIGINT, SIG_IGN);
374: signal( SIGHUP, SIG_IGN);
375: ++interrupted;
376: }
377:
378: void
379: catch( sig) int sig;
380: {
381: if( signal( sig, SIG_IGN) == SIG_DFL)
382: signal( sig, onintr);
383: }
384:
385: void
386: fatal(err, arg1) unsigned int err; char *arg1;
387: {
388: fputs(cmd, stderr);
389: if (err < sys_nerr) {
390: fputs(sys_errlist[err], stderr);
391: fputs(": ", stderr);
392: }
393: fprintf(stderr, "%r\n", &arg1);
394: exit(err);
395: NOTREACHED;
396: }
397:
398: /* end of mv.c */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.