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