1*8839Smckusick static char *sccsid = "@(#)mv.c 4.6 (Berkeley) 82/10/23"; 2*8839Smckusick 31216Sbill /* 41216Sbill * mv file1 file2 51216Sbill */ 61216Sbill 71216Sbill #include <stdio.h> 81216Sbill #include <sys/types.h> 91216Sbill #include <sys/stat.h> 101216Sbill #include <signal.h> 111216Sbill 121216Sbill #define DOT "." 131216Sbill #define DOTDOT ".." 141216Sbill #define DELIM '/' 151216Sbill #define SDELIM "/" 16*8839Smckusick #define MAXN 300 171216Sbill #define MODEBITS 07777 181216Sbill #define ROOTINO 2 191216Sbill 201216Sbill char *pname(); 211216Sbill char *sprintf(); 221216Sbill char *dname(); 231216Sbill struct stat s1, s2; 241216Sbill int iflag = 0; /* interactive flag. If this flag is set, 251216Sbill * the user is queried before files are 261216Sbill * destroyed by cp. 271216Sbill */ 281216Sbill int fflag = 0; /* force flag. supercedes all restrictions */ 291216Sbill 301216Sbill main(argc, argv) 311216Sbill register char *argv[]; 321216Sbill { 331216Sbill register i, r; 341931Sroot register char *arg; 351216Sbill 361216Sbill /* get the flag(s) */ 371216Sbill 381216Sbill if (argc < 2) 391216Sbill goto usage; 401931Sroot while (argc>1 && *argv[1] == '-') { 411216Sbill argc--; 421931Sroot arg = *++argv; 431216Sbill 441931Sroot /* 451931Sroot * all files following a null option are considered file names 461931Sroot */ 471931Sroot if (*(arg+1) == '\0') break; 481931Sroot 491931Sroot while (*++arg != '\0') 501931Sroot switch (*arg) { 511931Sroot 521216Sbill /* interactive mode */ 531216Sbill case 'i': 541216Sbill iflag++; 551216Sbill break; 561216Sbill 571216Sbill /* force moves */ 581216Sbill case 'f': 591216Sbill fflag++; 601216Sbill break; 611216Sbill 621216Sbill /* don't live with bad options */ 631216Sbill default: 641216Sbill goto usage; 651216Sbill } 661216Sbill } 671216Sbill if (argc < 3) 681216Sbill goto usage; 69*8839Smckusick if (lstat(argv[1], &s1) < 0) { 701216Sbill fprintf(stderr, "mv: cannot access %s\n", argv[1]); 711216Sbill return(1); 721216Sbill } 731216Sbill if ((s1.st_mode & S_IFMT) == S_IFDIR) { 741216Sbill if (argc != 3) 751216Sbill goto usage; 761216Sbill return mvdir(argv[1], argv[2]); 771216Sbill } 781216Sbill setuid(getuid()); 791216Sbill if (argc > 3) 801216Sbill if (stat(argv[argc-1], &s2) < 0 || (s2.st_mode & S_IFMT) != S_IFDIR) 811216Sbill goto usage; 821216Sbill r = 0; 831216Sbill for (i=1; i<argc-1; i++) 841216Sbill r |= move(argv[i], argv[argc-1]); 851216Sbill return(r); 861216Sbill usage: 871931Sroot fprintf(stderr, "usage: mv [-if] f1 f2; or mv [-if] d1 d2; or mv [-if] f1 ... fn d1\n"); 881216Sbill return(1); 891216Sbill } 901216Sbill 911216Sbill move(source, target) 921216Sbill char *source, *target; 931216Sbill { 941216Sbill register c, i; 951216Sbill int status; 961216Sbill char buf[MAXN]; 971216Sbill 98*8839Smckusick if (lstat(source, &s1) < 0) { 991216Sbill fprintf(stderr, "mv: cannot access %s\n", source); 1001216Sbill return(1); 1011216Sbill } 1021216Sbill if ((s1.st_mode & S_IFMT) == S_IFDIR) { 1031216Sbill fprintf(stderr, "mv: directory rename only\n"); 1041216Sbill return(1); 1051216Sbill } 1061216Sbill if (stat(target, &s2) >= 0) { 1071216Sbill if ((s2.st_mode & S_IFMT) == S_IFDIR) { 1081216Sbill sprintf(buf, "%s/%s", target, dname(source)); 1091216Sbill target = buf; 1101216Sbill } 111*8839Smckusick if (lstat(target, &s2) >= 0) { 1121216Sbill if ((s2.st_mode & S_IFMT) == S_IFDIR) { 1131216Sbill fprintf(stderr, "mv: %s is a directory\n", target); 1141216Sbill return(1); 1151216Sbill } else if (iflag && !fflag) { 1161216Sbill fprintf(stderr, "remove %s? ", target); 1171216Sbill i = c = getchar(); 1181216Sbill while (c != '\n' && c != EOF) 1191216Sbill c = getchar(); 1201216Sbill if (i != 'y') 1211216Sbill return(1); 1221216Sbill } 1231216Sbill if (s1.st_dev==s2.st_dev && s1.st_ino==s2.st_ino) { 1241216Sbill fprintf(stderr, "mv: %s and %s are identical\n", 1251216Sbill source, target); 1261216Sbill return(1); 1271216Sbill } 1281216Sbill if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) { 1291216Sbill fprintf(stderr, "override protection %o for %s? ", 1301216Sbill s2.st_mode & MODEBITS, target); 1311216Sbill i = c = getchar(); 1321216Sbill while (c != '\n' && c != EOF) 1331216Sbill c = getchar(); 1341216Sbill if (i != 'y') 1351216Sbill return(1); 1361216Sbill } 1371216Sbill if (unlink(target) < 0) { 1381216Sbill fprintf(stderr, "mv: cannot unlink %s\n", target); 1391216Sbill return(1); 1401216Sbill } 1411216Sbill } 1421216Sbill } 143*8839Smckusick if ((s1.st_mode & S_IFMT) == S_IFLNK) { 144*8839Smckusick register m; 145*8839Smckusick char symln[MAXN]; 146*8839Smckusick 147*8839Smckusick if (readlink(source, symln, sizeof (symln)) < 0) { 148*8839Smckusick perror(source); 149*8839Smckusick return (1); 150*8839Smckusick } 151*8839Smckusick m = umask(~(s1.st_mode & MODEBITS)); 152*8839Smckusick if (symlink(symln, target) < 0) { 153*8839Smckusick perror(target); 154*8839Smckusick return (1); 155*8839Smckusick } 156*8839Smckusick umask(m); 157*8839Smckusick } else if (link(source, target) < 0) { 1581216Sbill i = fork(); 1591216Sbill if (i == -1) { 1601216Sbill fprintf(stderr, "mv: try again\n"); 1611216Sbill return(1); 1621216Sbill } 1631216Sbill if (i == 0) { 1641216Sbill execl("/bin/cp", "cp", source, target, 0); 1651216Sbill fprintf(stderr, "mv: cannot exec cp\n"); 1661216Sbill exit(1); 1671216Sbill } 1681216Sbill while ((c = wait(&status)) != i && c != -1) 1691216Sbill ; 1701216Sbill if (status != 0) 1711216Sbill return(1); 1721216Sbill utime(target, &s1.st_atime); 1731216Sbill } 1741216Sbill if (unlink(source) < 0) { 1751216Sbill fprintf(stderr, "mv: cannot unlink %s\n", source); 1761216Sbill return(1); 1771216Sbill } 1781216Sbill return(0); 1791216Sbill } 1801216Sbill 1811216Sbill mvdir(source, target) 1821216Sbill char *source, *target; 1831216Sbill { 1841216Sbill register char *p; 1851216Sbill register i; 1861216Sbill char buf[MAXN]; 1871216Sbill char c,cc; 1881216Sbill 1891216Sbill if (stat(target, &s2) >= 0) { 1901216Sbill if ((s2.st_mode&S_IFMT) != S_IFDIR) { 1911216Sbill fprintf(stderr, "mv: %s exists\n", target); 1921216Sbill return(1); 1931216Sbill } 1945759Smckusic p = dname(source); 1955759Smckusic if (strlen(target) > MAXN-strlen(p)-2) { 1961216Sbill fprintf(stderr, "mv :target name too long\n"); 1971216Sbill return(1); 1981216Sbill } 1991216Sbill strcpy(buf, target); 2001216Sbill target = buf; 2011216Sbill strcat(buf, SDELIM); 2025759Smckusic strcat(buf, p); 2031216Sbill if (stat(target, &s2) >= 0) { 2041216Sbill fprintf(stderr, "mv: %s exists\n", buf); 2051216Sbill return(1); 2061216Sbill } 2071216Sbill } 2081216Sbill if (strcmp(source, target) == 0) { 2091216Sbill fprintf(stderr, "mv: ?? source == target, source exists and target doesnt\n"); 2101216Sbill return(1); 2111216Sbill } 2121216Sbill p = dname(source); 2131216Sbill if (!strcmp(p, DOT) || !strcmp(p, DOTDOT) || !strcmp(p, "") || p[strlen(p)-1]=='/') { 2141216Sbill fprintf(stderr, "mv: cannot rename %s\n", p); 2151216Sbill return(1); 2161216Sbill } 2171216Sbill if (stat(pname(source), &s1) < 0 || stat(pname(target), &s2) < 0) { 2181216Sbill fprintf(stderr, "mv: cannot locate parent\n"); 2191216Sbill return(1); 2201216Sbill } 2211216Sbill if (access(pname(target), 2) < 0) { 2221216Sbill fprintf(stderr, "mv: no write access to %s\n", pname(target)); 2231216Sbill return(1); 2241216Sbill } 2251216Sbill if (access(pname(source), 2) < 0) { 2261216Sbill fprintf(stderr, "mv: no write access to %s\n", pname(source)); 2271216Sbill return(1); 2281216Sbill } 2291216Sbill if (s1.st_dev != s2.st_dev) { 2301216Sbill fprintf(stderr, "mv: cannot move directories across devices\n"); 2311216Sbill return(1); 2321216Sbill } 2331216Sbill if (s1.st_ino != s2.st_ino) { 2341216Sbill char dst[MAXN+5]; 2351216Sbill 2361216Sbill if (chkdot(source) || chkdot(target)) { 2371216Sbill fprintf(stderr, "mv: Sorry, path names including %s aren't allowed\n", DOTDOT); 2381216Sbill return(1); 2391216Sbill } 2401216Sbill stat(source, &s1); 2411216Sbill if (check(pname(target), s1.st_ino)) 2421216Sbill return(1); 2431216Sbill for (i = 1; i <= NSIG; i++) 2441216Sbill signal(i, SIG_IGN); 2451216Sbill if (link(source, target) < 0) { 2461216Sbill fprintf(stderr, "mv: cannot link %s to %s\n", target, source); 2471216Sbill return(1); 2481216Sbill } 2491216Sbill if (unlink(source) < 0) { 2501216Sbill fprintf(stderr, "mv: %s: cannot unlink\n", source); 2511216Sbill unlink(target); 2521216Sbill return(1); 2531216Sbill } 2541216Sbill strcat(dst, target); 2551216Sbill strcat(dst, "/"); 2561216Sbill strcat(dst, DOTDOT); 2571216Sbill if (unlink(dst) < 0) { 2581216Sbill fprintf(stderr, "mv: %s: cannot unlink\n", dst); 2591216Sbill if (link(target, source) >= 0) 2601216Sbill unlink(target); 2611216Sbill return(1); 2621216Sbill } 2631216Sbill if (link(pname(target), dst) < 0) { 2641216Sbill fprintf(stderr, "mv: cannot link %s to %s\n", 2651216Sbill dst, pname(target)); 2661216Sbill if (link(pname(source), dst) >= 0) 2671216Sbill if (link(target, source) >= 0) 2681216Sbill unlink(target); 2691216Sbill return(1); 2701216Sbill } 2711216Sbill return(0); 2721216Sbill } 2731216Sbill if (link(source, target) < 0) { 2741216Sbill fprintf(stderr, "mv: cannot link %s and %s\n", 2751216Sbill source, target); 2761216Sbill return(1); 2771216Sbill } 2781216Sbill if (unlink(source) < 0) { 2791216Sbill fprintf(stderr, "mv: ?? cannot unlink %s\n", source); 2801216Sbill return(1); 2811216Sbill } 2821216Sbill return(0); 2831216Sbill } 2841216Sbill 2851216Sbill char * 2861216Sbill pname(name) 2871216Sbill register char *name; 2881216Sbill { 2891216Sbill register c; 2901216Sbill register char *p, *q; 2911216Sbill static char buf[MAXN]; 2921216Sbill 2931216Sbill p = q = buf; 2941216Sbill while (c = *p++ = *name++) 2951216Sbill if (c == DELIM) 2961216Sbill q = p-1; 2971216Sbill if (q == buf && *q == DELIM) 2981216Sbill q++; 2991216Sbill *q = 0; 3001216Sbill return buf[0]? buf : DOT; 3011216Sbill } 3021216Sbill 3031216Sbill char * 3041216Sbill dname(name) 3051216Sbill register char *name; 3061216Sbill { 3071216Sbill register char *p; 3081216Sbill 3091216Sbill p = name; 3101216Sbill while (*p) 3111216Sbill if (*p++ == DELIM && *p) 3121216Sbill name = p; 3131216Sbill return name; 3141216Sbill } 3151216Sbill 3161216Sbill check(spth, dinode) 3171216Sbill char *spth; 3181216Sbill ino_t dinode; 3191216Sbill { 3201216Sbill char nspth[MAXN]; 3211216Sbill struct stat sbuf; 3221216Sbill 3231216Sbill sbuf.st_ino = 0; 3241216Sbill 3251216Sbill strcpy(nspth, spth); 3261216Sbill while (sbuf.st_ino != ROOTINO) { 3271216Sbill if (stat(nspth, &sbuf) < 0) { 3281216Sbill fprintf(stderr, "mv: cannot access %s\n", nspth); 3291216Sbill return(1); 3301216Sbill } 3311216Sbill if (sbuf.st_ino == dinode) { 3321216Sbill fprintf(stderr, "mv: cannot move a directory into itself\n"); 3331216Sbill return(1); 3341216Sbill } 3351216Sbill if (strlen(nspth) > MAXN-2-sizeof(DOTDOT)) { 3361216Sbill fprintf(stderr, "mv: name too long\n"); 3371216Sbill return(1); 3381216Sbill } 3391216Sbill strcat(nspth, SDELIM); 3401216Sbill strcat(nspth, DOTDOT); 3411216Sbill } 3421216Sbill return(0); 3431216Sbill } 3441216Sbill 3451216Sbill chkdot(s) 3461216Sbill register char *s; 3471216Sbill { 3481216Sbill do { 3491216Sbill if (strcmp(dname(s), DOTDOT) == 0) 3501216Sbill return(1); 3511216Sbill s = pname(s); 3521216Sbill } while (strcmp(s, DOT) != 0 && strcmp(s, SDELIM) != 0); 3531216Sbill return(0); 3541216Sbill } 355