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