1 static char *sccsid = "@(#)mv.c 4.4 (Berkeley) 81/04/26"; 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 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 (stat(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 (stat(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 (stat(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 (link(source, target) < 0) { 144 i = fork(); 145 if (i == -1) { 146 fprintf(stderr, "mv: try again\n"); 147 return(1); 148 } 149 if (i == 0) { 150 execl("/bin/cp", "cp", source, target, 0); 151 fprintf(stderr, "mv: cannot exec cp\n"); 152 exit(1); 153 } 154 while ((c = wait(&status)) != i && c != -1) 155 ; 156 if (status != 0) 157 return(1); 158 utime(target, &s1.st_atime); 159 } 160 if (unlink(source) < 0) { 161 fprintf(stderr, "mv: cannot unlink %s\n", source); 162 return(1); 163 } 164 return(0); 165 } 166 167 mvdir(source, target) 168 char *source, *target; 169 { 170 register char *p; 171 register i; 172 char buf[MAXN]; 173 char c,cc; 174 175 if (stat(target, &s2) >= 0) { 176 if ((s2.st_mode&S_IFMT) != S_IFDIR) { 177 fprintf(stderr, "mv: %s exists\n", target); 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 (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