Annotation of coherent/d/bin/rm.c, revision 1.1.1.1

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 */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.