Annotation of coherent/d/bin/rm.c, revision 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.