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