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.5 (Berkeley) 04/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, 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, "dfiPRr")) != 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 '?': 75 default: 76 usage(); 77 } 78 argc -= optind; 79 argv += optind; 80 81 if (argc < 1) 82 usage(); 83 84 checkdot(argv); 85 if (!*argv) 86 exit (eval); 87 88 stdin_ok = isatty(STDIN_FILENO); 89 90 if (rflag) 91 rm_tree(argv); 92 else 93 rm_file(argv); 94 exit (eval); 95 } 96 97 void 98 rm_tree(argv) 99 char **argv; 100 { 101 FTS *fts; 102 FTSENT *p; 103 int needstat; 104 105 /* 106 * Remove a file hierarchy. If forcing removal (-f), or interactive 107 * (-i) or can't ask anyway (stdin_ok), don't stat the file. 108 */ 109 needstat = !fflag && !iflag && stdin_ok; 110 111 /* 112 * If the -i option is specified, the user can skip on the pre-order 113 * visit. The fts_number field flags skipped directories. 114 */ 115 #define SKIPPED 1 116 117 if (!(fts = fts_open(argv, 118 needstat ? FTS_PHYSICAL : FTS_PHYSICAL|FTS_NOSTAT, 119 (int (*)())NULL))) 120 err(1, NULL); 121 while ((p = fts_read(fts)) != NULL) { 122 switch (p->fts_info) { 123 case FTS_DNR: 124 if (!fflag || p->fts_errno != ENOENT) { 125 warnx("%s: %s", 126 p->fts_path, strerror(p->fts_errno)); 127 eval = 1; 128 } 129 continue; 130 case FTS_ERR: 131 errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); 132 case FTS_NS: 133 /* 134 * FTS_NS: assume that if can't stat the file, it 135 * can't be unlinked. 136 */ 137 if (!needstat) 138 break; 139 if (!fflag || p->fts_errno != ENOENT) { 140 warnx("%s: %s", 141 p->fts_path, strerror(p->fts_errno)); 142 eval = 1; 143 } 144 continue; 145 case FTS_D: 146 /* Pre-order: give user chance to skip. */ 147 if (iflag && !check(p->fts_path, p->fts_accpath, 148 p->fts_statp)) { 149 (void)fts_set(fts, p, FTS_SKIP); 150 p->fts_number = SKIPPED; 151 } 152 continue; 153 case FTS_DP: 154 /* Post-order: see if user skipped. */ 155 if (p->fts_number == SKIPPED) 156 continue; 157 break; 158 } 159 if (!fflag && 160 !check(p->fts_path, p->fts_accpath, p->fts_statp)) 161 continue; 162 163 /* 164 * If we can't read or search the directory, may still be 165 * able to remove it. Don't print out the un{read,search}able 166 * message unless the remove fails. 167 */ 168 if (p->fts_info == FTS_DP || p->fts_info == FTS_DNR) { 169 if (!rmdir(p->fts_accpath)) 170 continue; 171 if (errno == ENOENT) { 172 if (fflag) 173 continue; 174 } else if (p->fts_info != FTS_DP) 175 warnx("%s: unable to read", p->fts_path); 176 } else { 177 if (Pflag) 178 rm_overwrite(p->fts_accpath, NULL); 179 if (!unlink(p->fts_accpath) || fflag && errno == ENOENT) 180 continue; 181 } 182 warn("%s", p->fts_path); 183 eval = 1; 184 } 185 if (errno) 186 err(1, "fts_read"); 187 } 188 189 void 190 rm_file(argv) 191 char **argv; 192 { 193 struct stat sb; 194 int df, rval; 195 char *f; 196 197 df = dflag; 198 /* 199 * Remove a file. POSIX 1003.2 states that, by default, attempting 200 * to remove a directory is an error, so must always stat the file. 201 */ 202 while ((f = *argv++) != NULL) { 203 /* Assume if can't stat the file, can't unlink it. */ 204 if (lstat(f, &sb)) { 205 if (!fflag || errno != ENOENT) { 206 warn("%s", f); 207 eval = 1; 208 } 209 continue; 210 } 211 if (S_ISDIR(sb.st_mode) && !df) { 212 warnx("%s: is a directory", f); 213 eval = 1; 214 continue; 215 } 216 if (!fflag && !check(f, f, &sb)) 217 continue; 218 if (S_ISDIR(sb.st_mode)) 219 rval = rmdir(f); 220 else { 221 if (Pflag) 222 rm_overwrite(f, &sb); 223 rval = unlink(f); 224 } 225 if (rval && (!fflag || errno != ENOENT)) { 226 warn("%s", f); 227 eval = 1; 228 } 229 } 230 } 231 232 /* 233 * rm_overwrite -- 234 * Overwrite the file 3 times with varying bit patterns. 235 * 236 * XXX 237 * This is a cheap way to *really* delete files. Note that only regular 238 * files are deleted, directories (and therefore names) will remain. 239 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a 240 * System V file system). In a logging file system, you'll have to have 241 * kernel support. 242 */ 243 void 244 rm_overwrite(file, sbp) 245 char *file; 246 struct stat *sbp; 247 { 248 struct stat sb; 249 off_t len; 250 int fd, wlen; 251 char buf[8 * 1024]; 252 253 fd = -1; 254 if (sbp == NULL) { 255 if (lstat(file, &sb)) 256 goto err; 257 sbp = &sb; 258 } 259 if (!S_ISREG(sbp->st_mode)) 260 return; 261 if ((fd = open(file, O_WRONLY, 0)) == -1) 262 goto err; 263 264 #define PASS(byte) { \ 265 memset(buf, byte, sizeof(buf)); \ 266 for (len = sbp->st_size; len > 0; len -= wlen) { \ 267 wlen = len < sizeof(buf) ? len : sizeof(buf); \ 268 if (write(fd, buf, wlen) != wlen) \ 269 goto err; \ 270 } \ 271 } 272 PASS(0xff); 273 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 274 goto err; 275 PASS(0x00); 276 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 277 goto err; 278 PASS(0xff); 279 if (!fsync(fd) && !close(fd)) 280 return; 281 282 err: eval = 1; 283 warn("%s", file); 284 } 285 286 287 int 288 check(path, name, sp) 289 char *path, *name; 290 struct stat *sp; 291 { 292 int ch, first; 293 char modep[15]; 294 295 /* Check -i first. */ 296 if (iflag) 297 (void)fprintf(stderr, "remove %s? ", path); 298 else { 299 /* 300 * If it's not a symbolic link and it's unwritable and we're 301 * talking to a terminal, ask. Symbolic links are excluded 302 * because their permissions are meaningless. Check stdin_ok 303 * first because we may not have stat'ed the file. 304 */ 305 if (!stdin_ok || S_ISLNK(sp->st_mode) || !access(name, W_OK)) 306 return (1); 307 strmode(sp->st_mode, modep); 308 (void)fprintf(stderr, "override %s%s%s/%s for %s? ", 309 modep + 1, modep[9] == ' ' ? "" : " ", 310 user_from_uid(sp->st_uid, 0), 311 group_from_gid(sp->st_gid, 0), path); 312 } 313 (void)fflush(stderr); 314 315 first = ch = getchar(); 316 while (ch != '\n' && ch != EOF) 317 ch = getchar(); 318 return (first == 'y'); 319 } 320 321 #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2])) 322 void 323 checkdot(argv) 324 char **argv; 325 { 326 char *p, **save, **t; 327 int complained; 328 329 complained = 0; 330 for (t = argv; *t;) { 331 if ((p = strrchr(*t, '/')) != NULL) 332 ++p; 333 else 334 p = *t; 335 if (ISDOT(p)) { 336 if (!complained++) 337 warnx("\".\" and \"..\" may not be removed"); 338 eval = 1; 339 for (save = t; (t[0] = t[1]) != NULL; ++t); 340 t = save; 341 } else 342 ++t; 343 } 344 } 345 346 void 347 usage() 348 { 349 350 (void)fprintf(stderr, "usage: rm [-dfiRr] file ...\n"); 351 exit(1); 352 } 353