|
|
1.1 ! root 1: /* ! 2: * rm.c ! 3: * 6/4/91 ! 4: * Remove files or directories and their contents. ! 5: * Usage: rm [-firtv] file ... ! 6: * ! 7: * Rec'd from Lauren Weinstein, 7-16-84. ! 8: * This command is setuid to root to allow directory unlinks. ! 9: * Interrupts are handled to prevent mangled directories. ! 10: * Exit status of 1 indicates an error: ! 11: * couldn't find current directory, ! 12: * couldn't find root directory, ! 13: * pathname too long, ! 14: * couldn't find parent directory, ! 15: * couldn't stat argument, ! 16: * directory argument without -r flag, ! 17: * no permission on directory, ! 18: * directory is current directory, ! 19: * directory is root directory, ! 20: * unlink failed, ! 21: * couldn't open directory, ! 22: * couldn't read directory, ! 23: * ran out of memory saving directory entries. ! 24: * Failure to delete a directory because it is not empty due to interactive ! 25: * file selection is not an error. ! 26: * All the synonyms for pwd and root are identified by letting the system ! 27: * map them into stat buffers and comparing device x inode pairs. ! 28: */ ! 29: ! 30: #include <stdio.h> ! 31: #include <sys/stat.h> ! 32: #include <sys/dir.h> ! 33: #include <access.h> ! 34: #include <signal.h> ! 35: ! 36: extern int errno; ! 37: ! 38: char *save(); ! 39: ! 40: #define NFNAME 1000 /* Largest filename expansion in `-r' option */ ! 41: #define equals(s1, s2) (strcmp((s1), (s2)) == 0) ! 42: ! 43: char fname[NFNAME]; /* current argument name */ ! 44: struct stat sb; /* current argument status buffer */ ! 45: FILE *dfp; /* directory read stream */ ! 46: char iobuf[BUFSIZ]; /* stdio buffer */ ! 47: struct direct db; /* directory entry buffer */ ! 48: struct stat dot_sb; /* current directory status buffer */ ! 49: struct stat root_sb; /* root status buffer */ ! 50: int interrupted; /* interrupt flag */ ! 51: char *dot = "."; ! 52: char *dotdot = ".."; ! 53: char *root = "/"; ! 54: char *cmd = "rm: "; /* for messages */ ! 55: ! 56: int fflag; /* Force removal */ ! 57: int iflag; /* Interactive removal */ ! 58: int rflag; /* Recursive removal of directory */ ! 59: int tflag; /* Test, do not perform removes */ ! 60: int vflag; /* Verbose report */ ! 61: int ntflag; /* Non-zero if stdin not a terminal */ ! 62: ! 63: main(argc, argv) int argc; char *argv[]; ! 64: { ! 65: register char *ap; ! 66: register int i; ! 67: register int estat = 0; ! 68: ! 69: while (argc>1 && *argv[1]=='-') { ! 70: for (ap=&argv[1][1]; *ap != '\0'; ap++) ! 71: switch (*ap) { ! 72: case 'f': fflag = 1; break; ! 73: case 'i': iflag = 1; break; ! 74: case 'r': rflag = 1; break; ! 75: case 't': tflag = 1; break; ! 76: case 'v': vflag = 1; break; ! 77: default: ! 78: usage(); ! 79: } ! 80: argc--; ! 81: argv++; ! 82: } ! 83: if (argc < 2) ! 84: usage(); ! 85: if (!isatty(fileno(stdin))) { ! 86: ntflag = 1; ! 87: if (iflag) { ! 88: fprintf(stderr, "rm: -i illegal with redirection\n"); ! 89: exit(1); ! 90: } ! 91: } ! 92: if (stat(dot, &dot_sb)) { ! 93: lerror(dot); ! 94: exit(1); ! 95: } ! 96: if (stat(root, &root_sb)) { ! 97: lerror(root); ! 98: exit(1); ! 99: } ! 100: ! 101: catch(SIGINT); ! 102: catch(SIGHUP); ! 103: signal(SIGQUIT, SIG_IGN); ! 104: ! 105: for (i=1; i<argc; i++) { ! 106: if (copy(fname, argv[i], NFNAME-2)) { ! 107: toolong(argv[i]); ! 108: estat = 1; ! 109: } else ! 110: estat |= (remove() == -1); ! 111: } ! 112: exit(estat); ! 113: } ! 114: ! 115: /* ! 116: * Remove the entry with name fname. ! 117: * Check for all flags and permissions. ! 118: * Return: -1 for errors; 1 for entries remain; 0 for removal. ! 119: */ ! 120: remove() ! 121: { ! 122: register int isdir; ! 123: ! 124: if (stat(fname, &sb)) ! 125: return ((fflag) ? 0 : didnt(NULL)); ! 126: if (isdir = ((sb.st_mode & S_IFMT) == S_IFDIR)) { ! 127: if (!rflag) ! 128: return (didnt("directory")); ! 129: if (access(fname, ALIST|ADEL|ASRCH) < 0) ! 130: return (didnt(NULL)); ! 131: if (sb.st_dev == dot_sb.st_dev && sb.st_ino == dot_sb.st_ino) ! 132: return (didnt("current directory")); ! 133: if (sb.st_dev == root_sb.st_dev && sb.st_ino == root_sb.st_ino) ! 134: return (didnt("root directory")); ! 135: } else if (accparent() < 0) ! 136: return (didnt(NULL)); ! 137: if (iflag) { ! 138: if (!query("%s?", fname)) ! 139: return (report(1)); ! 140: } else if (!fflag && access(fname, AWRITE) < 0) { ! 141: if (ntflag) /* stdin not a terminal? */ ! 142: fprintf(stderr,"%sno write permission for %s", ! 143: cmd, fname); ! 144: else if (!query("override protection %o for %s?", ! 145: (sb.st_mode & 0777), fname)) ! 146: return(report(1)); /* abort */ ! 147: } ! 148: if (isdir) { ! 149: if (isdir = rmdir()) ! 150: return (report(isdir)); ! 151: } else { ! 152: if (rmfile() < 0) ! 153: return (didnt(NULL)); ! 154: } ! 155: return (report(0)); ! 156: } ! 157: ! 158: /* ! 159: * Check that the parent of this file has delete permission. ! 160: * Rmdir checks parents of directories. ! 161: */ ! 162: accparent() ! 163: { ! 164: register char *sp; ! 165: register int c; ! 166: register int accpar; ! 167: ! 168: for (sp = fname; *sp; sp += 1) /* find end */ ! 169: ; ! 170: while (sp > fname && sp[-1] != '/') /* find last / */ ! 171: sp -= 1; ! 172: if (sp > fname) { /* a real name */ ! 173: c = *sp; ! 174: *sp = '\0'; ! 175: accpar = access(fname, ADEL); ! 176: *sp = c; ! 177: } else /* simple name */ ! 178: accpar = access(dot, ADEL); ! 179: return (accpar); ! 180: } ! 181: ! 182: /* ! 183: * Recursive removal of directories. ! 184: * Scan the directory entries and append them to fname successively. ! 185: * Call remove to remove everything except original fname, fname/., and ! 186: * fname/.. which are rmfiled directly. ! 187: */ ! 188: rmdir() ! 189: { ! 190: register char *cp, *np; ! 191: int limit; ! 192: int rmstat = 0; ! 193: char *nbase = NULL, *ntops = NULL; ! 194: ! 195: /* save directory name */ ! 196: cp = fname; ! 197: while (*cp++) ! 198: ; ! 199: cp[-1] = '/'; ! 200: *cp = '\0'; ! 201: limit = NFNAME - 2 - (cp - fname); ! 202: ! 203: /* get the directory */ ! 204: if ((dfp = fopen(fname, "r")) == NULL) ! 205: return (didnt(NULL)); ! 206: setbuf(dfp, iobuf); ! 207: ! 208: /* read and save file names in directory */ ! 209: while (fread(&db, sizeof(db), 1, dfp) == 1) { ! 210: if (db.d_ino == 0 ! 211: || equals(db.d_name, dot) ! 212: || equals(db.d_name, dotdot)) ! 213: continue; ! 214: if ((ntops = save(db.d_name)) == NULL) { ! 215: fclose(dfp); ! 216: return (didnt("out of memory")); ! 217: } ! 218: if (nbase == NULL) ! 219: nbase = ntops; ! 220: } ! 221: fclose(dfp); ! 222: ! 223: /* rescan names, form destinations, and remove */ ! 224: if (nbase != NULL) { ! 225: for (np = nbase; np <= ntops; ) { ! 226: if (copy(cp, np, limit)) { ! 227: toolong(np); ! 228: rmstat = -1; ! 229: continue; ! 230: } ! 231: switch (remove()) { ! 232: case -1: ! 233: rmstat = -1; ! 234: break; ! 235: case 0: ! 236: break; ! 237: case 1: ! 238: rmstat = rmstat == -1 ? -1 : 1; ! 239: break; ! 240: default: ! 241: botch("bad return from return"); ! 242: break; ! 243: } ! 244: while (*np++); ! 245: } ! 246: forget(nbase); ! 247: } ! 248: ! 249: /* Now delete the directory, if it's empty */ ! 250: if (rmstat) { ! 251: cp[-1] = '\0'; ! 252: return (rmstat); ! 253: } ! 254: if (copy(cp, dotdot, limit)) { ! 255: toolong(dotdot); ! 256: cp[-1] = '\0'; ! 257: return (-1); ! 258: } ! 259: if (access(fname, ADEL) < 0) { ! 260: lerror(fname); ! 261: cp[-1] = '\0'; ! 262: return (-1); ! 263: } ! 264: if (rmfile() ! 265: || copy(cp, dot, limit) ! 266: || rmfile() ! 267: || (cp[-1] = '\0') ! 268: || rmfile()) ! 269: botch("directory unlink error"); ! 270: return (rmstat); ! 271: } ! 272: ! 273: /* ! 274: * Unlink a single file if tflag is reset. ! 275: */ ! 276: rmfile() ! 277: { ! 278: return (tflag ? 0 : unlink(fname)); ! 279: } ! 280: ! 281: /* ! 282: * Ask a question about the current file, ! 283: * return one if the answer begins with y or Y. ! 284: */ ! 285: /* VARARGS */ ! 286: query(question) char *question; ! 287: { ! 288: register int c; ! 289: register int answer; ! 290: ! 291: fprintf(stderr, "%s%r [y/n] ", cmd, &question); ! 292: answer = ((c = getchar()) == 'y' || c == 'Y'); ! 293: while (c != EOF && c != '\n') ! 294: c = getchar(); ! 295: return (answer); ! 296: } ! 297: ! 298: /* ! 299: * Report that fname concatenated with the argument string is too long. ! 300: */ ! 301: toolong(cp) char *cp; ! 302: { ! 303: if (!fflag) ! 304: fprintf(stderr, "%s%s%s: too long\n", cmd, fname, cp); ! 305: } ! 306: ! 307: /* ! 308: * Copy src string to dst observing that no more than lim characters can fit. ! 309: * Clean up on error. ! 310: */ ! 311: copy(dst, src, lim) char *dst, *src; int lim; ! 312: { ! 313: register char *dp, *sp, *ep; ! 314: ! 315: dp = dst; ! 316: sp = src; ! 317: ep = dp + lim; ! 318: ! 319: while ((dp < ep) && (*dp++ = *sp++)) ! 320: ; ! 321: if (dp == ep) { ! 322: *dst = '\0'; ! 323: return (-1); ! 324: } ! 325: return (0); ! 326: } ! 327: ! 328: usage() ! 329: { ! 330: fprintf(stderr, "Usage: rm [-firtv] file ...\n"); ! 331: exit(1); ! 332: } ! 333: ! 334: /* ! 335: * Report reason for failure. ! 336: */ ! 337: didnt(reason) char *reason; ! 338: { ! 339: if (fflag) ! 340: return -1; ! 341: if (reason == NULL) ! 342: lerror(fname); ! 343: else ! 344: fprintf(stderr, "%s%s: %s\n", cmd, fname, reason); ! 345: return (report(-1)); ! 346: } ! 347: ! 348: /* ! 349: * Report non-removal/removal and return error code. ! 350: * Since we come here after each file or directory is done, ! 351: * exit if an interrupt was detected. ! 352: */ ! 353: report(err) ! 354: int err; ! 355: { ! 356: if (vflag) ! 357: fprintf(stderr, "%s%s: %sremoved\n", cmd, fname, ! 358: err ? "not ":""); ! 359: if (interrupted) ! 360: exit(1); ! 361: return (err); ! 362: } ! 363: ! 364: onintr() ! 365: { ! 366: signal(SIGINT, SIG_IGN); ! 367: signal(SIGHUP, SIG_IGN); ! 368: ++interrupted; ! 369: } ! 370: ! 371: catch(sig) ! 372: { ! 373: if (signal(sig, SIG_IGN) == SIG_DFL) ! 374: signal(sig, onintr); ! 375: } ! 376: ! 377: /* ! 378: * Simplified heap. ! 379: */ ! 380: char *membase = NULL; ! 381: char *curbase = NULL; ! 382: char *curtops = NULL; ! 383: ! 384: char * ! 385: save(name) register char *name; ! 386: { ! 387: register char *saved; ! 388: register int ntosave; ! 389: extern char *sbrk(); ! 390: ! 391: if (membase == NULL) ! 392: membase = curbase = curtops = sbrk(0); ! 393: ! 394: saved = curbase; ! 395: ntosave = 14; ! 396: do { ! 397: if (saved == curtops) { ! 398: if (saved != sbrk(01000)) ! 399: return (NULL); ! 400: curtops += 01000; ! 401: } ! 402: } while (ntosave-- && (*saved++ = *name++)); ! 403: if (ntosave < 0) ! 404: *saved++ = '\0'; ! 405: name = curbase; ! 406: curbase = saved; ! 407: return (name); ! 408: } ! 409: ! 410: forget(names) register char *names; ! 411: { ! 412: if (names < membase || names >= curtops) ! 413: botch("memory deallocation"); ! 414: curbase = names; ! 415: } ! 416: ! 417: botch(msg) char *msg; ! 418: { ! 419: fprintf(stderr, "%sbotched: %s at %s\n", cmd, msg, fname); ! 420: exit(1); ! 421: } ! 422: ! 423: lerror(msg) char *msg; ! 424: { ! 425: register int err; ! 426: ! 427: err = errno; /* save error code for perror */ ! 428: fputs(cmd, stderr); /* command name */ ! 429: errno = err; /* restore error code */ ! 430: perror(msg); ! 431: } ! 432: ! 433: /* end of rm.c */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.