GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/rm/rm.c Lines: 70 168 41.7 %
Date: 2016-12-06 Branches: 59 187 31.6 %

Line Branch Exec Source
1
/*	$OpenBSD: rm.c,v 1.39 2016/06/28 18:00:59 tedu Exp $	*/
2
/*	$NetBSD: rm.c,v 1.19 1995/09/07 06:48:50 jtc Exp $	*/
3
4
/*-
5
 * Copyright (c) 1990, 1993, 1994
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. Neither the name of the University nor the names of its contributors
17
 *    may be used to endorse or promote products derived from this software
18
 *    without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 */
32
33
#include <sys/types.h>
34
#include <sys/stat.h>
35
#include <sys/mount.h>
36
37
#include <locale.h>
38
#include <err.h>
39
#include <errno.h>
40
#include <fcntl.h>
41
#include <fts.h>
42
#include <stdio.h>
43
#include <stdlib.h>
44
#include <string.h>
45
#include <unistd.h>
46
#include <limits.h>
47
#include <pwd.h>
48
#include <grp.h>
49
50
#define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
51
52
extern char *__progname;
53
54
int dflag, eval, fflag, iflag, Pflag, stdin_ok;
55
56
int	check(char *, char *, struct stat *);
57
void	checkdot(char **);
58
void	rm_file(char **);
59
int	rm_overwrite(char *, struct stat *);
60
int	pass(int, off_t, char *, size_t);
61
void	rm_tree(char **);
62
void	usage(void);
63
64
/*
65
 * rm --
66
 *	This rm is different from historic rm's, but is expected to match
67
 *	POSIX 1003.2 behavior.  The most visible difference is that -f
68
 *	has two specific effects now, ignore non-existent files and force
69
 * 	file removal.
70
 */
71
int
72
main(int argc, char *argv[])
73
1153
{
74
	int ch, rflag;
75
76
1153
	setlocale(LC_ALL, "");
77
78
1153
	Pflag = rflag = 0;
79
3614
	while ((ch = getopt(argc, argv, "dfiPRr")) != -1)
80

1308
		switch(ch) {
81
		case 'd':
82
			dflag = 1;
83
			break;
84
		case 'f':
85
1149
			fflag = 1;
86
1149
			iflag = 0;
87
1149
			break;
88
		case 'i':
89
			fflag = 0;
90
			iflag = 1;
91
			break;
92
		case 'P':
93
			Pflag = 1;
94
			break;
95
		case 'R':
96
		case 'r':			/* Compatibility. */
97
159
			rflag = 1;
98
159
			break;
99
		default:
100
			usage();
101
		}
102
1153
	argc -= optind;
103
1153
	argv += optind;
104
105
1153
	if (Pflag) {
106
		if (pledge("stdio rpath wpath cpath getpw", NULL) == -1)
107
			err(1, "pledge");
108
	} else {
109
1153
		if (pledge("stdio rpath cpath getpw wpath", NULL) == -1)
110
			err(1, "pledge");
111
	}
112
113

1153
	if (argc < 1 && fflag == 0)
114
		usage();
115
116
1153
	checkdot(argv);
117
118
1153
	if (*argv) {
119
1153
		stdin_ok = isatty(STDIN_FILENO);
120
121
1153
		if (rflag)
122
159
			rm_tree(argv);
123
		else
124
994
			rm_file(argv);
125
	}
126
127
1153
	exit(eval);
128
}
129
130
void
131
rm_tree(char **argv)
132
159
{
133
	FTS *fts;
134
	FTSENT *p;
135
	int needstat;
136
	int flags;
137
138
	/*
139
	 * Remove a file hierarchy.  If forcing removal (-f), or interactive
140
	 * (-i) or can't ask anyway (stdin_ok), don't stat the file.
141
	 */
142

159
	needstat = !fflag && !iflag && stdin_ok;
143
144
	/*
145
	 * If the -i option is specified, the user can skip on the pre-order
146
	 * visit.  The fts_number field flags skipped directories.
147
	 */
148
#define	SKIPPED	1
149
150
159
	flags = FTS_PHYSICAL;
151
159
	if (!needstat)
152
159
		flags |= FTS_NOSTAT;
153
159
	if (!(fts = fts_open(argv, flags, NULL)))
154
		err(1, NULL);
155
9482
	while ((p = fts_read(fts)) != NULL) {
156

9323
		switch (p->fts_info) {
157
		case FTS_DNR:
158
			if (!fflag || p->fts_errno != ENOENT) {
159
				warnx("%s: %s",
160
				    p->fts_path, strerror(p->fts_errno));
161
				eval = 1;
162
			}
163
			continue;
164
		case FTS_ERR:
165
			errc(1, p->fts_errno, "%s", p->fts_path);
166
		case FTS_NS:
167
			/*
168
			 * FTS_NS: assume that if can't stat the file, it
169
			 * can't be unlinked.
170
			 */
171
178
			if (!needstat)
172
178
				break;
173
			if (!fflag || p->fts_errno != ENOENT) {
174
				warnx("%s: %s",
175
				    p->fts_path, strerror(p->fts_errno));
176
				eval = 1;
177
			}
178
			continue;
179
		case FTS_D:
180
			/* Pre-order: give user chance to skip. */
181

825
			if (!fflag && !check(p->fts_path, p->fts_accpath,
182
			    p->fts_statp)) {
183
				(void)fts_set(fts, p, FTS_SKIP);
184
				p->fts_number = SKIPPED;
185
			}
186
			continue;
187
		case FTS_DP:
188
			/* Post-order: see if user skipped. */
189
825
			if (p->fts_number == SKIPPED)
190
				continue;
191
			break;
192
		default:
193

7495
			if (!fflag &&
194
			    !check(p->fts_path, p->fts_accpath, p->fts_statp))
195
				continue;
196
		}
197
198
		/*
199
		 * If we can't read or search the directory, may still be
200
		 * able to remove it.  Don't print out the un{read,search}able
201
		 * message unless the remove fails.
202
		 */
203
8498
		switch (p->fts_info) {
204
		case FTS_DP:
205
		case FTS_DNR:
206

825
			if (!rmdir(p->fts_accpath) ||
207
			    (fflag && errno == ENOENT))
208
				continue;
209
			break;
210
211
		case FTS_F:
212
		case FTS_NSOK:
213
7495
			if (Pflag)
214
				rm_overwrite(p->fts_accpath, p->fts_info ==
215
				    FTS_NSOK ? NULL : p->fts_statp);
216
			/* FALLTHROUGH */
217
		default:
218

7673
			if (!unlink(p->fts_accpath) ||
219
			    (fflag && errno == ENOENT))
220
				continue;
221
		}
222
		warn("%s", p->fts_path);
223
		eval = 1;
224
	}
225
159
	if (errno)
226
		err(1, "fts_read");
227
159
	fts_close(fts);
228
159
}
229
230
void
231
rm_file(char **argv)
232
994
{
233
	struct stat sb;
234
	int rval;
235
	char *f;
236
237
	/*
238
	 * Remove a file.  POSIX 1003.2 states that, by default, attempting
239
	 * to remove a directory is an error, so must always stat the file.
240
	 */
241
4184
	while ((f = *argv++) != NULL) {
242
		/* Assume if can't stat the file, can't unlink it. */
243
2196
		if (lstat(f, &sb)) {
244

1044
			if (!fflag || errno != ENOENT) {
245
				warn("%s", f);
246
				eval = 1;
247
			}
248
			continue;
249
		}
250
251

1152
		if (S_ISDIR(sb.st_mode) && !dflag) {
252
			warnx("%s: is a directory", f);
253
			eval = 1;
254
			continue;
255
		}
256

1152
		if (!fflag && !check(f, f, &sb))
257
			continue;
258
1152
		else if (S_ISDIR(sb.st_mode))
259
			rval = rmdir(f);
260
		else {
261
1152
			if (Pflag)
262
				rm_overwrite(f, &sb);
263
1152
			rval = unlink(f);
264
		}
265

1152
		if (rval && (!fflag || errno != ENOENT)) {
266
			warn("%s", f);
267
			eval = 1;
268
		}
269
	}
270
994
}
271
272
/*
273
 * rm_overwrite --
274
 *	Overwrite the file with varying bit patterns.
275
 *
276
 * XXX
277
 * This is a cheap way to *really* delete files.  Note that only regular
278
 * files are deleted, directories (and therefore names) will remain.
279
 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
280
 * System V file system).  In a logging file system, you'll have to have
281
 * kernel support.
282
 * Returns 1 for success.
283
 */
284
int
285
rm_overwrite(char *file, struct stat *sbp)
286
{
287
	struct stat sb, sb2;
288
	struct statfs fsb;
289
	size_t bsize;
290
	int fd;
291
	char *buf = NULL;
292
293
	fd = -1;
294
	if (sbp == NULL) {
295
		if (lstat(file, &sb))
296
			goto err;
297
		sbp = &sb;
298
	}
299
	if (!S_ISREG(sbp->st_mode))
300
		return (1);
301
	if (sbp->st_nlink > 1) {
302
		warnx("%s (inode %llu): not overwritten due to multiple links",
303
		    file, (unsigned long long)sbp->st_ino);
304
		return (0);
305
	}
306
	if ((fd = open(file, O_WRONLY|O_NONBLOCK|O_NOFOLLOW, 0)) == -1)
307
		goto err;
308
	if (fstat(fd, &sb2))
309
		goto err;
310
	if (sb2.st_dev != sbp->st_dev || sb2.st_ino != sbp->st_ino ||
311
	    !S_ISREG(sb2.st_mode)) {
312
		errno = EPERM;
313
		goto err;
314
	}
315
	if (fstatfs(fd, &fsb) == -1)
316
		goto err;
317
	bsize = MAXIMUM(fsb.f_iosize, 1024U);
318
	if ((buf = malloc(bsize)) == NULL)
319
		err(1, "%s: malloc", file);
320
321
	if (!pass(fd, sbp->st_size, buf, bsize))
322
		goto err;
323
	if (fsync(fd))
324
		goto err;
325
	close(fd);
326
	free(buf);
327
	return (1);
328
329
err:
330
	warn("%s", file);
331
	close(fd);
332
	eval = 1;
333
	free(buf);
334
	return (0);
335
}
336
337
int
338
pass(int fd, off_t len, char *buf, size_t bsize)
339
{
340
	size_t wlen;
341
342
	for (; len > 0; len -= wlen) {
343
		wlen = len < bsize ? len : bsize;
344
		arc4random_buf(buf, wlen);
345
		if (write(fd, buf, wlen) != wlen)
346
			return (0);
347
	}
348
	return (1);
349
}
350
351
int
352
check(char *path, char *name, struct stat *sp)
353
4
{
354
	int ch, first;
355
	char modep[15];
356
357
	/* Check -i first. */
358
4
	if (iflag)
359
		(void)fprintf(stderr, "remove %s? ", path);
360
	else {
361
		/*
362
		 * If it's not a symbolic link and it's unwritable and we're
363
		 * talking to a terminal, ask.  Symbolic links are excluded
364
		 * because their permissions are meaningless.  Check stdin_ok
365
		 * first because we may not have stat'ed the file.
366
		 */
367


4
		if (!stdin_ok || S_ISLNK(sp->st_mode) || !access(name, W_OK) ||
368
		    errno != EACCES)
369
4
			return (1);
370
		strmode(sp->st_mode, modep);
371
		(void)fprintf(stderr, "override %s%s%s/%s for %s? ",
372
		    modep + 1, modep[9] == ' ' ? "" : " ",
373
		    user_from_uid(sp->st_uid, 0),
374
		    group_from_gid(sp->st_gid, 0), path);
375
	}
376
	(void)fflush(stderr);
377
378
	first = ch = getchar();
379
	while (ch != '\n' && ch != EOF)
380
		ch = getchar();
381
	return (first == 'y' || first == 'Y');
382
}
383
384
/*
385
 * POSIX.2 requires that if "." or ".." are specified as the basename
386
 * portion of an operand, a diagnostic message be written to standard
387
 * error and nothing more be done with such operands.
388
 *
389
 * Since POSIX.2 defines basename as the final portion of a path after
390
 * trailing slashes have been removed, we'll remove them here.
391
 */
392
#define ISDOT(a)	((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
393
void
394
checkdot(char **argv)
395
1153
{
396
	char *p, **save, **t;
397
	int complained;
398
	struct stat sb, root;
399
400
1153
	stat("/", &root);
401
1153
	complained = 0;
402
4764
	for (t = argv; *t;) {
403

2458
		if (lstat(*t, &sb) == 0 &&
404
		    root.st_ino == sb.st_ino && root.st_dev == sb.st_dev) {
405
			if (!complained++)
406
				warnx("\"/\" may not be removed");
407
			goto skip;
408
		}
409
		/* strip trailing slashes */
410
2458
		p = strrchr(*t, '\0');
411

4916
		while (--p > *t && *p == '/')
412
			*p = '\0';
413
414
		/* extract basename */
415
2458
		if ((p = strrchr(*t, '/')) != NULL)
416
166
			++p;
417
		else
418
2292
			p = *t;
419
420


2458
		if (ISDOT(p)) {
421
			if (!complained++)
422
				warnx("\".\" and \"..\" may not be removed");
423
skip:
424
			eval = 1;
425
			for (save = t; (t[0] = t[1]) != NULL; ++t)
426
				continue;
427
			t = save;
428
		} else
429
2458
			++t;
430
	}
431
1153
}
432
433
void
434
usage(void)
435
{
436
	(void)fprintf(stderr, "usage: %s [-dfiPRr] file ...\n", __progname);
437
	exit(1);
438
}