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