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