|
|
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.