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