1 /*- 2 * Copyright (c) 1990, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char copyright[] = 10 "@(#) Copyright (c) 1990, 1993, 1994\n\ 11 The Regents of the University of California. All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)rm.c 8.7 (Berkeley) 10/18/94"; 16 #endif /* not lint */ 17 18 #include <sys/types.h> 19 #include <sys/stat.h> 20 21 #include <err.h> 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <fts.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <unistd.h> 29 30 int dflag, eval, fflag, iflag, Pflag, Wflag, stdin_ok; 31 32 int check __P((char *, char *, struct stat *)); 33 void checkdot __P((char **)); 34 void rm_file __P((char **)); 35 void rm_overwrite __P((char *, struct stat *)); 36 void rm_tree __P((char **)); 37 void usage __P((void)); 38 39 /* 40 * rm -- 41 * This rm is different from historic rm's, but is expected to match 42 * POSIX 1003.2 behavior. The most visible difference is that -f 43 * has two specific effects now, ignore non-existent files and force 44 * file removal. 45 */ 46 int 47 main(argc, argv) 48 int argc; 49 char *argv[]; 50 { 51 int ch, rflag; 52 53 Pflag = rflag = 0; 54 while ((ch = getopt(argc, argv, "dfiPRrW")) != EOF) 55 switch(ch) { 56 case 'd': 57 dflag = 1; 58 break; 59 case 'f': 60 fflag = 1; 61 iflag = 0; 62 break; 63 case 'i': 64 fflag = 0; 65 iflag = 1; 66 break; 67 case 'P': 68 Pflag = 1; 69 break; 70 case 'R': 71 case 'r': /* Compatibility. */ 72 rflag = 1; 73 break; 74 case 'W': 75 Wflag = 1; 76 break; 77 case '?': 78 default: 79 usage(); 80 } 81 argc -= optind; 82 argv += optind; 83 84 if (argc < 1) 85 usage(); 86 87 checkdot(argv); 88 if (!*argv) 89 exit (eval); 90 91 stdin_ok = isatty(STDIN_FILENO); 92 93 if (rflag) 94 rm_tree(argv); 95 else 96 rm_file(argv); 97 exit (eval); 98 } 99 100 void 101 rm_tree(argv) 102 char **argv; 103 { 104 FTS *fts; 105 FTSENT *p; 106 int needstat; 107 int flags; 108 109 /* 110 * Remove a file hierarchy. If forcing removal (-f), or interactive 111 * (-i) or can't ask anyway (stdin_ok), don't stat the file. 112 */ 113 needstat = !fflag && !iflag && stdin_ok; 114 115 /* 116 * If the -i option is specified, the user can skip on the pre-order 117 * visit. The fts_number field flags skipped directories. 118 */ 119 #define SKIPPED 1 120 121 flags = FTS_PHYSICAL; 122 if (!needstat) 123 flags |= FTS_NOSTAT; 124 if (Wflag) 125 flags |= FTS_WHITEOUT; 126 if (!(fts = fts_open(argv, flags, (int (*)())NULL))) 127 err(1, NULL); 128 while ((p = fts_read(fts)) != NULL) { 129 switch (p->fts_info) { 130 case FTS_DNR: 131 if (!fflag || p->fts_errno != ENOENT) { 132 warnx("%s: %s", 133 p->fts_path, strerror(p->fts_errno)); 134 eval = 1; 135 } 136 continue; 137 case FTS_ERR: 138 errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); 139 case FTS_NS: 140 /* 141 * FTS_NS: assume that if can't stat the file, it 142 * can't be unlinked. 143 */ 144 if (!needstat) 145 break; 146 if (!fflag || p->fts_errno != ENOENT) { 147 warnx("%s: %s", 148 p->fts_path, strerror(p->fts_errno)); 149 eval = 1; 150 } 151 continue; 152 case FTS_D: 153 /* Pre-order: give user chance to skip. */ 154 if (iflag && !check(p->fts_path, p->fts_accpath, 155 p->fts_statp)) { 156 (void)fts_set(fts, p, FTS_SKIP); 157 p->fts_number = SKIPPED; 158 } 159 continue; 160 case FTS_DP: 161 /* Post-order: see if user skipped. */ 162 if (p->fts_number == SKIPPED) 163 continue; 164 break; 165 } 166 if (!fflag && 167 !check(p->fts_path, p->fts_accpath, p->fts_statp)) 168 continue; 169 170 /* 171 * If we can't read or search the directory, may still be 172 * able to remove it. Don't print out the un{read,search}able 173 * message unless the remove fails. 174 */ 175 switch (p->fts_info) { 176 case FTS_DP: 177 case FTS_DNR: 178 if (!rmdir(p->fts_accpath)) 179 continue; 180 if (errno == ENOENT) { 181 if (fflag) 182 continue; 183 } else if (p->fts_info != FTS_DP) 184 warnx("%s: unable to read", p->fts_path); 185 break; 186 187 case FTS_W: 188 if (!undelete(p->fts_accpath) || 189 fflag && errno == ENOENT) 190 continue; 191 break; 192 193 default: 194 if (Pflag) 195 rm_overwrite(p->fts_accpath, NULL); 196 if (!unlink(p->fts_accpath) || fflag && errno == ENOENT) 197 continue; 198 } 199 warn("%s", p->fts_path); 200 eval = 1; 201 } 202 if (errno) 203 err(1, "fts_read"); 204 } 205 206 void 207 rm_file(argv) 208 char **argv; 209 { 210 struct stat sb; 211 int df, rval; 212 char *f; 213 214 df = dflag; 215 /* 216 * Remove a file. POSIX 1003.2 states that, by default, attempting 217 * to remove a directory is an error, so must always stat the file. 218 */ 219 while ((f = *argv++) != NULL) { 220 /* Assume if can't stat the file, can't unlink it. */ 221 if (lstat(f, &sb)) { 222 if (Wflag) { 223 sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR; 224 } else { 225 if (!fflag || errno != ENOENT) { 226 warn("%s", f); 227 eval = 1; 228 } 229 continue; 230 } 231 } else if (Wflag) { 232 warnx("%s: %s", f, strerror(EEXIST)); 233 eval = 1; 234 continue; 235 } 236 237 if (S_ISDIR(sb.st_mode) && !df) { 238 warnx("%s: is a directory", f); 239 eval = 1; 240 continue; 241 } 242 if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb)) 243 continue; 244 if (S_ISWHT(sb.st_mode)) 245 rval = undelete(f); 246 else if (S_ISDIR(sb.st_mode)) 247 rval = rmdir(f); 248 else { 249 if (Pflag) 250 rm_overwrite(f, &sb); 251 rval = unlink(f); 252 } 253 if (rval && (!fflag || errno != ENOENT)) { 254 warn("%s", f); 255 eval = 1; 256 } 257 } 258 } 259 260 /* 261 * rm_overwrite -- 262 * Overwrite the file 3 times with varying bit patterns. 263 * 264 * XXX 265 * This is a cheap way to *really* delete files. Note that only regular 266 * files are deleted, directories (and therefore names) will remain. 267 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a 268 * System V file system). In a logging file system, you'll have to have 269 * kernel support. 270 */ 271 void 272 rm_overwrite(file, sbp) 273 char *file; 274 struct stat *sbp; 275 { 276 struct stat sb; 277 off_t len; 278 int fd, wlen; 279 char buf[8 * 1024]; 280 281 fd = -1; 282 if (sbp == NULL) { 283 if (lstat(file, &sb)) 284 goto err; 285 sbp = &sb; 286 } 287 if (!S_ISREG(sbp->st_mode)) 288 return; 289 if ((fd = open(file, O_WRONLY, 0)) == -1) 290 goto err; 291 292 #define PASS(byte) { \ 293 memset(buf, byte, sizeof(buf)); \ 294 for (len = sbp->st_size; len > 0; len -= wlen) { \ 295 wlen = len < sizeof(buf) ? len : sizeof(buf); \ 296 if (write(fd, buf, wlen) != wlen) \ 297 goto err; \ 298 } \ 299 } 300 PASS(0xff); 301 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 302 goto err; 303 PASS(0x00); 304 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 305 goto err; 306 PASS(0xff); 307 if (!fsync(fd) && !close(fd)) 308 return; 309 310 err: eval = 1; 311 warn("%s", file); 312 } 313 314 315 int 316 check(path, name, sp) 317 char *path, *name; 318 struct stat *sp; 319 { 320 int ch, first; 321 char modep[15]; 322 323 /* Check -i first. */ 324 if (iflag) 325 (void)fprintf(stderr, "remove %s? ", path); 326 else { 327 /* 328 * If it's not a symbolic link and it's unwritable and we're 329 * talking to a terminal, ask. Symbolic links are excluded 330 * because their permissions are meaningless. Check stdin_ok 331 * first because we may not have stat'ed the file. 332 */ 333 if (!stdin_ok || S_ISLNK(sp->st_mode) || !access(name, W_OK)) 334 return (1); 335 strmode(sp->st_mode, modep); 336 (void)fprintf(stderr, "override %s%s%s/%s for %s? ", 337 modep + 1, modep[9] == ' ' ? "" : " ", 338 user_from_uid(sp->st_uid, 0), 339 group_from_gid(sp->st_gid, 0), path); 340 } 341 (void)fflush(stderr); 342 343 first = ch = getchar(); 344 while (ch != '\n' && ch != EOF) 345 ch = getchar(); 346 return (first == 'y'); 347 } 348 349 #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2])) 350 void 351 checkdot(argv) 352 char **argv; 353 { 354 char *p, **save, **t; 355 int complained; 356 357 complained = 0; 358 for (t = argv; *t;) { 359 if ((p = strrchr(*t, '/')) != NULL) 360 ++p; 361 else 362 p = *t; 363 if (ISDOT(p)) { 364 if (!complained++) 365 warnx("\".\" and \"..\" may not be removed"); 366 eval = 1; 367 for (save = t; (t[0] = t[1]) != NULL; ++t); 368 t = save; 369 } else 370 ++t; 371 } 372 } 373 374 void 375 usage() 376 { 377 378 (void)fprintf(stderr, "usage: rm [-dfiRr] file ...\n"); 379 exit(1); 380 } 381