1/*- 2 * Copyright (c) 1990, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#ifndef lint 35static const char copyright[] = 36"@(#) Copyright (c) 1990, 1993, 1994\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38#endif /* not lint */ 39 40#ifndef lint 41#if 0 42static char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94"; 43#else 44static const char rcsid[] =
| 1/*- 2 * Copyright (c) 1990, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#ifndef lint 35static const char copyright[] = 36"@(#) Copyright (c) 1990, 1993, 1994\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38#endif /* not lint */ 39 40#ifndef lint 41#if 0 42static char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94"; 43#else 44static const char rcsid[] =
|
57#include <stdio.h> 58#include <stdlib.h> 59#include <string.h> 60#include <sysexits.h> 61#include <unistd.h> 62 63int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok; 64uid_t uid; 65 66int check(char *, char *, struct stat *); 67void checkdot(char **); 68void rm_file(char **); 69void rm_overwrite(char *, struct stat *); 70void rm_tree(char **); 71void usage(void); 72 73/* 74 * rm -- 75 * This rm is different from historic rm's, but is expected to match 76 * POSIX 1003.2 behavior. The most visible difference is that -f 77 * has two specific effects now, ignore non-existent files and force 78 * file removal. 79 */ 80int 81main(int argc, char *argv[]) 82{ 83 int ch, rflag; 84 char *p; 85 86 /* 87 * Test for the special case where the utility is called as 88 * "unlink", for which the functionality provided is greatly 89 * simplified. 90 */ 91 if ((p = rindex(argv[0], '/')) == NULL) 92 p = argv[0]; 93 else 94 ++p; 95 if (strcmp(p, "unlink") == 0) { 96 if (argc == 2) { 97 rm_file(&argv[1]); 98 exit(eval); 99 } else 100 usage(); 101 } 102 103 Pflag = rflag = 0; 104 while ((ch = getopt(argc, argv, "dfiPRrvW")) != -1) 105 switch(ch) { 106 case 'd': 107 dflag = 1; 108 break; 109 case 'f': 110 fflag = 1; 111 iflag = 0; 112 break; 113 case 'i': 114 fflag = 0; 115 iflag = 1; 116 break; 117 case 'P': 118 Pflag = 1; 119 break; 120 case 'R': 121 case 'r': /* Compatibility. */ 122 rflag = 1; 123 break; 124 case 'v': 125 vflag = 1; 126 break; 127 case 'W': 128 Wflag = 1; 129 break; 130 default: 131 usage(); 132 } 133 argc -= optind; 134 argv += optind; 135 136 if (argc < 1) { 137 if (fflag) 138 return 0; 139 usage(); 140 } 141 142 checkdot(argv); 143 uid = geteuid(); 144 145 if (*argv) { 146 stdin_ok = isatty(STDIN_FILENO); 147 148 if (rflag) 149 rm_tree(argv); 150 else 151 rm_file(argv); 152 } 153 154 exit (eval); 155} 156 157void 158rm_tree(char **argv) 159{ 160 FTS *fts; 161 FTSENT *p; 162 int needstat; 163 int flags; 164 int rval; 165 166 /* 167 * Remove a file hierarchy. If forcing removal (-f), or interactive 168 * (-i) or can't ask anyway (stdin_ok), don't stat the file. 169 */ 170 needstat = !uid || (!fflag && !iflag && stdin_ok); 171 172 /* 173 * If the -i option is specified, the user can skip on the pre-order 174 * visit. The fts_number field flags skipped directories. 175 */ 176#define SKIPPED 1 177 178 flags = FTS_PHYSICAL; 179 if (!needstat) 180 flags |= FTS_NOSTAT; 181 if (Wflag) 182 flags |= FTS_WHITEOUT; 183 if (!(fts = fts_open(argv, flags, NULL))) 184 err(1, NULL); 185 while ((p = fts_read(fts)) != NULL) { 186 switch (p->fts_info) { 187 case FTS_DNR: 188 if (!fflag || p->fts_errno != ENOENT) { 189 warnx("%s: %s", 190 p->fts_path, strerror(p->fts_errno)); 191 eval = 1; 192 } 193 continue; 194 case FTS_ERR: 195 errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); 196 case FTS_NS: 197 /* 198 * FTS_NS: assume that if can't stat the file, it 199 * can't be unlinked. 200 */ 201 if (!needstat) 202 break; 203 if (!fflag || p->fts_errno != ENOENT) { 204 warnx("%s: %s", 205 p->fts_path, strerror(p->fts_errno)); 206 eval = 1; 207 } 208 continue; 209 case FTS_D: 210 /* Pre-order: give user chance to skip. */ 211 if (!fflag && !check(p->fts_path, p->fts_accpath, 212 p->fts_statp)) { 213 (void)fts_set(fts, p, FTS_SKIP); 214 p->fts_number = SKIPPED; 215 } 216 else if (!uid && 217 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 218 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 219 chflags(p->fts_accpath, 220 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) 221 goto err; 222 continue; 223 case FTS_DP: 224 /* Post-order: see if user skipped. */ 225 if (p->fts_number == SKIPPED) 226 continue; 227 break; 228 default: 229 if (!fflag && 230 !check(p->fts_path, p->fts_accpath, p->fts_statp)) 231 continue; 232 } 233 234 rval = 0; 235 if (!uid && 236 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 237 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE))) 238 rval = chflags(p->fts_accpath, 239 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); 240 if (rval == 0) { 241 /* 242 * If we can't read or search the directory, may still be 243 * able to remove it. Don't print out the un{read,search}able 244 * message unless the remove fails. 245 */ 246 switch (p->fts_info) { 247 case FTS_DP: 248 case FTS_DNR: 249 rval = rmdir(p->fts_accpath); 250 if (rval == 0 || (fflag && errno == ENOENT)) { 251 if (rval == 0 && vflag) 252 (void)printf("%s\n", 253 p->fts_path); 254 continue; 255 } 256 break; 257 258 case FTS_W: 259 rval = undelete(p->fts_accpath); 260 if (rval == 0 && (fflag && errno == ENOENT)) { 261 if (vflag) 262 (void)printf("%s\n", 263 p->fts_path); 264 continue; 265 } 266 break; 267 268 default: 269 if (Pflag) 270 rm_overwrite(p->fts_accpath, NULL); 271 rval = unlink(p->fts_accpath); 272 if (rval == 0 || (fflag && errno == ENOENT)) { 273 if (rval == 0 && vflag) 274 (void)printf("%s\n", 275 p->fts_path); 276 continue; 277 } 278 } 279 } 280err: 281 warn("%s", p->fts_path); 282 eval = 1; 283 } 284 if (errno) 285 err(1, "fts_read"); 286} 287 288void 289rm_file(char **argv) 290{ 291 struct stat sb; 292 int rval; 293 char *f; 294 295 /* 296 * Remove a file. POSIX 1003.2 states that, by default, attempting 297 * to remove a directory is an error, so must always stat the file. 298 */ 299 while ((f = *argv++) != NULL) { 300 /* Assume if can't stat the file, can't unlink it. */ 301 if (lstat(f, &sb)) { 302 if (Wflag) { 303 sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR; 304 } else { 305 if (!fflag || errno != ENOENT) { 306 warn("%s", f); 307 eval = 1; 308 } 309 continue; 310 } 311 } else if (Wflag) { 312 warnx("%s: %s", f, strerror(EEXIST)); 313 eval = 1; 314 continue; 315 } 316 317 if (S_ISDIR(sb.st_mode) && !dflag) { 318 warnx("%s: is a directory", f); 319 eval = 1; 320 continue; 321 } 322 if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb)) 323 continue; 324 rval = 0; 325 if (!uid && 326 (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) && 327 !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE))) 328 rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE)); 329 if (rval == 0) { 330 if (S_ISWHT(sb.st_mode)) 331 rval = undelete(f); 332 else if (S_ISDIR(sb.st_mode)) 333 rval = rmdir(f); 334 else { 335 if (Pflag) 336 rm_overwrite(f, &sb); 337 rval = unlink(f); 338 } 339 } 340 if (rval && (!fflag || errno != ENOENT)) { 341 warn("%s", f); 342 eval = 1; 343 } 344 if (vflag && rval == 0) 345 (void)printf("%s\n", f); 346 } 347} 348 349/* 350 * rm_overwrite -- 351 * Overwrite the file 3 times with varying bit patterns. 352 * 353 * XXX 354 * This is a cheap way to *really* delete files. Note that only regular 355 * files are deleted, directories (and therefore names) will remain. 356 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a 357 * System V file system). In a logging file system, you'll have to have 358 * kernel support. 359 */ 360void 361rm_overwrite(char *file, struct stat *sbp) 362{ 363 struct stat sb; 364 struct statfs fsb; 365 off_t len; 366 int bsize, fd, wlen; 367 char *buf = NULL; 368 369 fd = -1; 370 if (sbp == NULL) { 371 if (lstat(file, &sb)) 372 goto err; 373 sbp = &sb; 374 } 375 if (!S_ISREG(sbp->st_mode)) 376 return; 377 if ((fd = open(file, O_WRONLY, 0)) == -1) 378 goto err; 379 if (fstatfs(fd, &fsb) == -1) 380 goto err; 381 bsize = MAX(fsb.f_iosize, 1024); 382 if ((buf = malloc(bsize)) == NULL) 383 err(1, "malloc"); 384 385#define PASS(byte) { \ 386 memset(buf, byte, bsize); \ 387 for (len = sbp->st_size; len > 0; len -= wlen) { \ 388 wlen = len < bsize ? len : bsize; \ 389 if (write(fd, buf, wlen) != wlen) \ 390 goto err; \ 391 } \ 392} 393 PASS(0xff); 394 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 395 goto err; 396 PASS(0x00); 397 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 398 goto err; 399 PASS(0xff); 400 if (!fsync(fd) && !close(fd)) { 401 free(buf); 402 return; 403 } 404 405err: eval = 1; 406 if (buf) 407 free(buf); 408 warn("%s", file); 409} 410 411 412int 413check(char *path, char *name, struct stat *sp) 414{ 415 int ch, first; 416 char modep[15], *flagsp; 417 418 /* Check -i first. */ 419 if (iflag) 420 (void)fprintf(stderr, "remove %s? ", path); 421 else { 422 /* 423 * If it's not a symbolic link and it's unwritable and we're 424 * talking to a terminal, ask. Symbolic links are excluded 425 * because their permissions are meaningless. Check stdin_ok 426 * first because we may not have stat'ed the file. 427 */ 428 if (!stdin_ok || S_ISLNK(sp->st_mode) || 429 (!access(name, W_OK) && 430 !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 431 (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))) 432 return (1); 433 strmode(sp->st_mode, modep); 434 if ((flagsp = fflagstostr(sp->st_flags)) == NULL) 435 err(1, NULL); 436 (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ", 437 modep + 1, modep[9] == ' ' ? "" : " ", 438 user_from_uid(sp->st_uid, 0), 439 group_from_gid(sp->st_gid, 0), 440 *flagsp ? flagsp : "", *flagsp ? " " : "", 441 path); 442 free(flagsp); 443 } 444 (void)fflush(stderr); 445 446 first = ch = getchar(); 447 while (ch != '\n' && ch != EOF) 448 ch = getchar(); 449 return (first == 'y' || first == 'Y'); 450} 451 452#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) 453void 454checkdot(char **argv) 455{ 456 char *p, **save, **t; 457 int complained; 458 459 complained = 0; 460 for (t = argv; *t;) { 461 if ((p = strrchr(*t, '/')) != NULL) 462 ++p; 463 else 464 p = *t; 465 if (ISDOT(p)) { 466 if (!complained++) 467 warnx("\".\" and \"..\" may not be removed"); 468 eval = 1; 469 for (save = t; (t[0] = t[1]) != NULL; ++t) 470 continue; 471 t = save; 472 } else 473 ++t; 474 } 475} 476 477void 478usage(void) 479{ 480 481 (void)fprintf(stderr, "%s\n%s\n", 482 "usage: rm [-f | -i] [-dPRrvW] file ...", 483 " unlink file"); 484 exit(EX_USAGE); 485}
| 59#include <stdio.h> 60#include <stdlib.h> 61#include <string.h> 62#include <sysexits.h> 63#include <unistd.h> 64 65int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok; 66uid_t uid; 67 68int check(char *, char *, struct stat *); 69void checkdot(char **); 70void rm_file(char **); 71void rm_overwrite(char *, struct stat *); 72void rm_tree(char **); 73void usage(void); 74 75/* 76 * rm -- 77 * This rm is different from historic rm's, but is expected to match 78 * POSIX 1003.2 behavior. The most visible difference is that -f 79 * has two specific effects now, ignore non-existent files and force 80 * file removal. 81 */ 82int 83main(int argc, char *argv[]) 84{ 85 int ch, rflag; 86 char *p; 87 88 /* 89 * Test for the special case where the utility is called as 90 * "unlink", for which the functionality provided is greatly 91 * simplified. 92 */ 93 if ((p = rindex(argv[0], '/')) == NULL) 94 p = argv[0]; 95 else 96 ++p; 97 if (strcmp(p, "unlink") == 0) { 98 if (argc == 2) { 99 rm_file(&argv[1]); 100 exit(eval); 101 } else 102 usage(); 103 } 104 105 Pflag = rflag = 0; 106 while ((ch = getopt(argc, argv, "dfiPRrvW")) != -1) 107 switch(ch) { 108 case 'd': 109 dflag = 1; 110 break; 111 case 'f': 112 fflag = 1; 113 iflag = 0; 114 break; 115 case 'i': 116 fflag = 0; 117 iflag = 1; 118 break; 119 case 'P': 120 Pflag = 1; 121 break; 122 case 'R': 123 case 'r': /* Compatibility. */ 124 rflag = 1; 125 break; 126 case 'v': 127 vflag = 1; 128 break; 129 case 'W': 130 Wflag = 1; 131 break; 132 default: 133 usage(); 134 } 135 argc -= optind; 136 argv += optind; 137 138 if (argc < 1) { 139 if (fflag) 140 return 0; 141 usage(); 142 } 143 144 checkdot(argv); 145 uid = geteuid(); 146 147 if (*argv) { 148 stdin_ok = isatty(STDIN_FILENO); 149 150 if (rflag) 151 rm_tree(argv); 152 else 153 rm_file(argv); 154 } 155 156 exit (eval); 157} 158 159void 160rm_tree(char **argv) 161{ 162 FTS *fts; 163 FTSENT *p; 164 int needstat; 165 int flags; 166 int rval; 167 168 /* 169 * Remove a file hierarchy. If forcing removal (-f), or interactive 170 * (-i) or can't ask anyway (stdin_ok), don't stat the file. 171 */ 172 needstat = !uid || (!fflag && !iflag && stdin_ok); 173 174 /* 175 * If the -i option is specified, the user can skip on the pre-order 176 * visit. The fts_number field flags skipped directories. 177 */ 178#define SKIPPED 1 179 180 flags = FTS_PHYSICAL; 181 if (!needstat) 182 flags |= FTS_NOSTAT; 183 if (Wflag) 184 flags |= FTS_WHITEOUT; 185 if (!(fts = fts_open(argv, flags, NULL))) 186 err(1, NULL); 187 while ((p = fts_read(fts)) != NULL) { 188 switch (p->fts_info) { 189 case FTS_DNR: 190 if (!fflag || p->fts_errno != ENOENT) { 191 warnx("%s: %s", 192 p->fts_path, strerror(p->fts_errno)); 193 eval = 1; 194 } 195 continue; 196 case FTS_ERR: 197 errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); 198 case FTS_NS: 199 /* 200 * FTS_NS: assume that if can't stat the file, it 201 * can't be unlinked. 202 */ 203 if (!needstat) 204 break; 205 if (!fflag || p->fts_errno != ENOENT) { 206 warnx("%s: %s", 207 p->fts_path, strerror(p->fts_errno)); 208 eval = 1; 209 } 210 continue; 211 case FTS_D: 212 /* Pre-order: give user chance to skip. */ 213 if (!fflag && !check(p->fts_path, p->fts_accpath, 214 p->fts_statp)) { 215 (void)fts_set(fts, p, FTS_SKIP); 216 p->fts_number = SKIPPED; 217 } 218 else if (!uid && 219 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 220 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 221 chflags(p->fts_accpath, 222 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) 223 goto err; 224 continue; 225 case FTS_DP: 226 /* Post-order: see if user skipped. */ 227 if (p->fts_number == SKIPPED) 228 continue; 229 break; 230 default: 231 if (!fflag && 232 !check(p->fts_path, p->fts_accpath, p->fts_statp)) 233 continue; 234 } 235 236 rval = 0; 237 if (!uid && 238 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 239 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE))) 240 rval = chflags(p->fts_accpath, 241 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); 242 if (rval == 0) { 243 /* 244 * If we can't read or search the directory, may still be 245 * able to remove it. Don't print out the un{read,search}able 246 * message unless the remove fails. 247 */ 248 switch (p->fts_info) { 249 case FTS_DP: 250 case FTS_DNR: 251 rval = rmdir(p->fts_accpath); 252 if (rval == 0 || (fflag && errno == ENOENT)) { 253 if (rval == 0 && vflag) 254 (void)printf("%s\n", 255 p->fts_path); 256 continue; 257 } 258 break; 259 260 case FTS_W: 261 rval = undelete(p->fts_accpath); 262 if (rval == 0 && (fflag && errno == ENOENT)) { 263 if (vflag) 264 (void)printf("%s\n", 265 p->fts_path); 266 continue; 267 } 268 break; 269 270 default: 271 if (Pflag) 272 rm_overwrite(p->fts_accpath, NULL); 273 rval = unlink(p->fts_accpath); 274 if (rval == 0 || (fflag && errno == ENOENT)) { 275 if (rval == 0 && vflag) 276 (void)printf("%s\n", 277 p->fts_path); 278 continue; 279 } 280 } 281 } 282err: 283 warn("%s", p->fts_path); 284 eval = 1; 285 } 286 if (errno) 287 err(1, "fts_read"); 288} 289 290void 291rm_file(char **argv) 292{ 293 struct stat sb; 294 int rval; 295 char *f; 296 297 /* 298 * Remove a file. POSIX 1003.2 states that, by default, attempting 299 * to remove a directory is an error, so must always stat the file. 300 */ 301 while ((f = *argv++) != NULL) { 302 /* Assume if can't stat the file, can't unlink it. */ 303 if (lstat(f, &sb)) { 304 if (Wflag) { 305 sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR; 306 } else { 307 if (!fflag || errno != ENOENT) { 308 warn("%s", f); 309 eval = 1; 310 } 311 continue; 312 } 313 } else if (Wflag) { 314 warnx("%s: %s", f, strerror(EEXIST)); 315 eval = 1; 316 continue; 317 } 318 319 if (S_ISDIR(sb.st_mode) && !dflag) { 320 warnx("%s: is a directory", f); 321 eval = 1; 322 continue; 323 } 324 if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb)) 325 continue; 326 rval = 0; 327 if (!uid && 328 (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) && 329 !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE))) 330 rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE)); 331 if (rval == 0) { 332 if (S_ISWHT(sb.st_mode)) 333 rval = undelete(f); 334 else if (S_ISDIR(sb.st_mode)) 335 rval = rmdir(f); 336 else { 337 if (Pflag) 338 rm_overwrite(f, &sb); 339 rval = unlink(f); 340 } 341 } 342 if (rval && (!fflag || errno != ENOENT)) { 343 warn("%s", f); 344 eval = 1; 345 } 346 if (vflag && rval == 0) 347 (void)printf("%s\n", f); 348 } 349} 350 351/* 352 * rm_overwrite -- 353 * Overwrite the file 3 times with varying bit patterns. 354 * 355 * XXX 356 * This is a cheap way to *really* delete files. Note that only regular 357 * files are deleted, directories (and therefore names) will remain. 358 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a 359 * System V file system). In a logging file system, you'll have to have 360 * kernel support. 361 */ 362void 363rm_overwrite(char *file, struct stat *sbp) 364{ 365 struct stat sb; 366 struct statfs fsb; 367 off_t len; 368 int bsize, fd, wlen; 369 char *buf = NULL; 370 371 fd = -1; 372 if (sbp == NULL) { 373 if (lstat(file, &sb)) 374 goto err; 375 sbp = &sb; 376 } 377 if (!S_ISREG(sbp->st_mode)) 378 return; 379 if ((fd = open(file, O_WRONLY, 0)) == -1) 380 goto err; 381 if (fstatfs(fd, &fsb) == -1) 382 goto err; 383 bsize = MAX(fsb.f_iosize, 1024); 384 if ((buf = malloc(bsize)) == NULL) 385 err(1, "malloc"); 386 387#define PASS(byte) { \ 388 memset(buf, byte, bsize); \ 389 for (len = sbp->st_size; len > 0; len -= wlen) { \ 390 wlen = len < bsize ? len : bsize; \ 391 if (write(fd, buf, wlen) != wlen) \ 392 goto err; \ 393 } \ 394} 395 PASS(0xff); 396 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 397 goto err; 398 PASS(0x00); 399 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 400 goto err; 401 PASS(0xff); 402 if (!fsync(fd) && !close(fd)) { 403 free(buf); 404 return; 405 } 406 407err: eval = 1; 408 if (buf) 409 free(buf); 410 warn("%s", file); 411} 412 413 414int 415check(char *path, char *name, struct stat *sp) 416{ 417 int ch, first; 418 char modep[15], *flagsp; 419 420 /* Check -i first. */ 421 if (iflag) 422 (void)fprintf(stderr, "remove %s? ", path); 423 else { 424 /* 425 * If it's not a symbolic link and it's unwritable and we're 426 * talking to a terminal, ask. Symbolic links are excluded 427 * because their permissions are meaningless. Check stdin_ok 428 * first because we may not have stat'ed the file. 429 */ 430 if (!stdin_ok || S_ISLNK(sp->st_mode) || 431 (!access(name, W_OK) && 432 !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 433 (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))) 434 return (1); 435 strmode(sp->st_mode, modep); 436 if ((flagsp = fflagstostr(sp->st_flags)) == NULL) 437 err(1, NULL); 438 (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ", 439 modep + 1, modep[9] == ' ' ? "" : " ", 440 user_from_uid(sp->st_uid, 0), 441 group_from_gid(sp->st_gid, 0), 442 *flagsp ? flagsp : "", *flagsp ? " " : "", 443 path); 444 free(flagsp); 445 } 446 (void)fflush(stderr); 447 448 first = ch = getchar(); 449 while (ch != '\n' && ch != EOF) 450 ch = getchar(); 451 return (first == 'y' || first == 'Y'); 452} 453 454#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) 455void 456checkdot(char **argv) 457{ 458 char *p, **save, **t; 459 int complained; 460 461 complained = 0; 462 for (t = argv; *t;) { 463 if ((p = strrchr(*t, '/')) != NULL) 464 ++p; 465 else 466 p = *t; 467 if (ISDOT(p)) { 468 if (!complained++) 469 warnx("\".\" and \"..\" may not be removed"); 470 eval = 1; 471 for (save = t; (t[0] = t[1]) != NULL; ++t) 472 continue; 473 t = save; 474 } else 475 ++t; 476 } 477} 478 479void 480usage(void) 481{ 482 483 (void)fprintf(stderr, "%s\n%s\n", 484 "usage: rm [-f | -i] [-dPRrvW] file ...", 485 " unlink file"); 486 exit(EX_USAGE); 487}
|