/bin/rm/rm.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 637 lines · 504 code · 45 blank · 88 comment · 217 complexity · be93fe1ebced176421533923f1c29e94 MD5 · raw file

  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. * 4. Neither the name of the University nor the names of its contributors
  14. * may be used to endorse or promote products derived from this software
  15. * without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  18. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  21. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27. * SUCH DAMAGE.
  28. */
  29. #if 0
  30. #ifndef lint
  31. static const char copyright[] =
  32. "@(#) Copyright (c) 1990, 1993, 1994\n\
  33. The Regents of the University of California. All rights reserved.\n";
  34. #endif /* not lint */
  35. #ifndef lint
  36. static char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94";
  37. #endif /* not lint */
  38. #endif
  39. #include <sys/cdefs.h>
  40. __FBSDID("$FreeBSD$");
  41. #include <sys/stat.h>
  42. #include <sys/param.h>
  43. #include <sys/mount.h>
  44. #include <err.h>
  45. #include <errno.h>
  46. #include <fcntl.h>
  47. #include <fts.h>
  48. #include <grp.h>
  49. #include <pwd.h>
  50. #include <stdint.h>
  51. #include <stdio.h>
  52. #include <stdlib.h>
  53. #include <string.h>
  54. #include <sysexits.h>
  55. #include <unistd.h>
  56. static int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok;
  57. static int rflag, Iflag;
  58. static uid_t uid;
  59. static volatile sig_atomic_t info;
  60. int check(char *, char *, struct stat *);
  61. int check2(char **);
  62. void checkdot(char **);
  63. void checkslash(char **);
  64. void rm_file(char **);
  65. int rm_overwrite(char *, struct stat *);
  66. void rm_tree(char **);
  67. static void siginfo(int __unused);
  68. void usage(void);
  69. /*
  70. * rm --
  71. * This rm is different from historic rm's, but is expected to match
  72. * POSIX 1003.2 behavior. The most visible difference is that -f
  73. * has two specific effects now, ignore non-existent files and force
  74. * file removal.
  75. */
  76. int
  77. main(int argc, char *argv[])
  78. {
  79. int ch;
  80. char *p;
  81. /*
  82. * Test for the special case where the utility is called as
  83. * "unlink", for which the functionality provided is greatly
  84. * simplified.
  85. */
  86. if ((p = strrchr(argv[0], '/')) == NULL)
  87. p = argv[0];
  88. else
  89. ++p;
  90. if (strcmp(p, "unlink") == 0) {
  91. while (getopt(argc, argv, "") != -1)
  92. usage();
  93. argc -= optind;
  94. argv += optind;
  95. if (argc != 1)
  96. usage();
  97. rm_file(&argv[0]);
  98. exit(eval);
  99. }
  100. Pflag = rflag = 0;
  101. while ((ch = getopt(argc, argv, "dfiIPRrvW")) != -1)
  102. switch(ch) {
  103. case 'd':
  104. dflag = 1;
  105. break;
  106. case 'f':
  107. fflag = 1;
  108. iflag = 0;
  109. break;
  110. case 'i':
  111. fflag = 0;
  112. iflag = 1;
  113. break;
  114. case 'I':
  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. if (argc < 1) {
  136. if (fflag)
  137. return (0);
  138. usage();
  139. }
  140. checkdot(argv);
  141. if (getenv("POSIXLY_CORRECT") == NULL)
  142. checkslash(argv);
  143. uid = geteuid();
  144. (void)signal(SIGINFO, siginfo);
  145. if (*argv) {
  146. stdin_ok = isatty(STDIN_FILENO);
  147. if (Iflag) {
  148. if (check2(argv) == 0)
  149. exit (1);
  150. }
  151. if (rflag)
  152. rm_tree(argv);
  153. else
  154. rm_file(argv);
  155. }
  156. exit (eval);
  157. }
  158. void
  159. rm_tree(char **argv)
  160. {
  161. FTS *fts;
  162. FTSENT *p;
  163. int needstat;
  164. int flags;
  165. int rval;
  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. * If the -i option is specified, the user can skip on the pre-order
  173. * visit. The fts_number field flags skipped directories.
  174. */
  175. #define SKIPPED 1
  176. flags = FTS_PHYSICAL;
  177. if (!needstat)
  178. flags |= FTS_NOSTAT;
  179. if (Wflag)
  180. flags |= FTS_WHITEOUT;
  181. if (!(fts = fts_open(argv, flags, NULL))) {
  182. if (fflag && errno == ENOENT)
  183. return;
  184. err(1, "fts_open");
  185. }
  186. while ((p = fts_read(fts)) != NULL) {
  187. switch (p->fts_info) {
  188. case FTS_DNR:
  189. if (!fflag || p->fts_errno != ENOENT) {
  190. warnx("%s: %s",
  191. p->fts_path, strerror(p->fts_errno));
  192. eval = 1;
  193. }
  194. continue;
  195. case FTS_ERR:
  196. errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno));
  197. case FTS_NS:
  198. /*
  199. * Assume that since fts_read() couldn't stat the
  200. * file, it can't be unlinked.
  201. */
  202. if (!needstat)
  203. break;
  204. if (!fflag || p->fts_errno != ENOENT) {
  205. warnx("%s: %s",
  206. p->fts_path, strerror(p->fts_errno));
  207. eval = 1;
  208. }
  209. continue;
  210. case FTS_D:
  211. /* Pre-order: give user chance to skip. */
  212. if (!fflag && !check(p->fts_path, p->fts_accpath,
  213. p->fts_statp)) {
  214. (void)fts_set(fts, p, FTS_SKIP);
  215. p->fts_number = SKIPPED;
  216. }
  217. else if (!uid &&
  218. (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
  219. !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
  220. lchflags(p->fts_accpath,
  221. p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0)
  222. goto err;
  223. continue;
  224. case FTS_DP:
  225. /* Post-order: see if user skipped. */
  226. if (p->fts_number == SKIPPED)
  227. continue;
  228. break;
  229. default:
  230. if (!fflag &&
  231. !check(p->fts_path, p->fts_accpath, p->fts_statp))
  232. continue;
  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 = lchflags(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. if (rval == 0 && info) {
  255. info = 0;
  256. (void)printf("%s\n",
  257. p->fts_path);
  258. }
  259. continue;
  260. }
  261. break;
  262. case FTS_W:
  263. rval = undelete(p->fts_accpath);
  264. if (rval == 0 && (fflag && errno == ENOENT)) {
  265. if (vflag)
  266. (void)printf("%s\n",
  267. p->fts_path);
  268. if (info) {
  269. info = 0;
  270. (void)printf("%s\n",
  271. p->fts_path);
  272. }
  273. continue;
  274. }
  275. break;
  276. case FTS_NS:
  277. /*
  278. * Assume that since fts_read() couldn't stat
  279. * the file, it can't be unlinked.
  280. */
  281. if (fflag)
  282. continue;
  283. /* FALLTHROUGH */
  284. case FTS_F:
  285. case FTS_NSOK:
  286. if (Pflag)
  287. if (!rm_overwrite(p->fts_accpath, p->fts_info ==
  288. FTS_NSOK ? NULL : p->fts_statp))
  289. continue;
  290. /* FALLTHROUGH */
  291. default:
  292. rval = unlink(p->fts_accpath);
  293. if (rval == 0 || (fflag && errno == ENOENT)) {
  294. if (rval == 0 && vflag)
  295. (void)printf("%s\n",
  296. p->fts_path);
  297. if (rval == 0 && info) {
  298. info = 0;
  299. (void)printf("%s\n",
  300. p->fts_path);
  301. }
  302. continue;
  303. }
  304. }
  305. }
  306. err:
  307. warn("%s", p->fts_path);
  308. eval = 1;
  309. }
  310. if (errno)
  311. err(1, "fts_read");
  312. fts_close(fts);
  313. }
  314. void
  315. rm_file(char **argv)
  316. {
  317. struct stat sb;
  318. int rval;
  319. char *f;
  320. /*
  321. * Remove a file. POSIX 1003.2 states that, by default, attempting
  322. * to remove a directory is an error, so must always stat the file.
  323. */
  324. while ((f = *argv++) != NULL) {
  325. /* Assume if can't stat the file, can't unlink it. */
  326. if (lstat(f, &sb)) {
  327. if (Wflag) {
  328. sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
  329. } else {
  330. if (!fflag || errno != ENOENT) {
  331. warn("%s", f);
  332. eval = 1;
  333. }
  334. continue;
  335. }
  336. } else if (Wflag) {
  337. warnx("%s: %s", f, strerror(EEXIST));
  338. eval = 1;
  339. continue;
  340. }
  341. if (S_ISDIR(sb.st_mode) && !dflag) {
  342. warnx("%s: is a directory", f);
  343. eval = 1;
  344. continue;
  345. }
  346. if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb))
  347. continue;
  348. rval = 0;
  349. if (!uid && !S_ISWHT(sb.st_mode) &&
  350. (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
  351. !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE)))
  352. rval = lchflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE));
  353. if (rval == 0) {
  354. if (S_ISWHT(sb.st_mode))
  355. rval = undelete(f);
  356. else if (S_ISDIR(sb.st_mode))
  357. rval = rmdir(f);
  358. else {
  359. if (Pflag)
  360. if (!rm_overwrite(f, &sb))
  361. continue;
  362. rval = unlink(f);
  363. }
  364. }
  365. if (rval && (!fflag || errno != ENOENT)) {
  366. warn("%s", f);
  367. eval = 1;
  368. }
  369. if (vflag && rval == 0)
  370. (void)printf("%s\n", f);
  371. if (info && rval == 0) {
  372. info = 0;
  373. (void)printf("%s\n", f);
  374. }
  375. }
  376. }
  377. /*
  378. * rm_overwrite --
  379. * Overwrite the file 3 times with varying bit patterns.
  380. *
  381. * XXX
  382. * This is a cheap way to *really* delete files. Note that only regular
  383. * files are deleted, directories (and therefore names) will remain.
  384. * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
  385. * System V file system). In a logging or COW file system, you'll have to
  386. * have kernel support.
  387. */
  388. int
  389. rm_overwrite(char *file, struct stat *sbp)
  390. {
  391. struct stat sb, sb2;
  392. struct statfs fsb;
  393. off_t len;
  394. int bsize, fd, wlen;
  395. char *buf = NULL;
  396. fd = -1;
  397. if (sbp == NULL) {
  398. if (lstat(file, &sb))
  399. goto err;
  400. sbp = &sb;
  401. }
  402. if (!S_ISREG(sbp->st_mode))
  403. return (1);
  404. if (sbp->st_nlink > 1 && !fflag) {
  405. warnx("%s (inode %ju): not overwritten due to multiple links",
  406. file, (uintmax_t)sbp->st_ino);
  407. return (0);
  408. }
  409. if ((fd = open(file, O_WRONLY|O_NONBLOCK|O_NOFOLLOW, 0)) == -1)
  410. goto err;
  411. if (fstat(fd, &sb2))
  412. goto err;
  413. if (sb2.st_dev != sbp->st_dev || sb2.st_ino != sbp->st_ino ||
  414. !S_ISREG(sb2.st_mode)) {
  415. errno = EPERM;
  416. goto err;
  417. }
  418. if (fstatfs(fd, &fsb) == -1)
  419. goto err;
  420. bsize = MAX(fsb.f_iosize, 1024);
  421. if ((buf = malloc(bsize)) == NULL)
  422. err(1, "%s: malloc", file);
  423. #define PASS(byte) { \
  424. memset(buf, byte, bsize); \
  425. for (len = sbp->st_size; len > 0; len -= wlen) { \
  426. wlen = len < bsize ? len : bsize; \
  427. if (write(fd, buf, wlen) != wlen) \
  428. goto err; \
  429. } \
  430. }
  431. PASS(0xff);
  432. if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
  433. goto err;
  434. PASS(0x00);
  435. if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
  436. goto err;
  437. PASS(0xff);
  438. if (!fsync(fd) && !close(fd)) {
  439. free(buf);
  440. return (1);
  441. }
  442. err: eval = 1;
  443. if (buf)
  444. free(buf);
  445. if (fd != -1)
  446. close(fd);
  447. warn("%s", file);
  448. return (0);
  449. }
  450. int
  451. check(char *path, char *name, struct stat *sp)
  452. {
  453. int ch, first;
  454. char modep[15], *flagsp;
  455. /* Check -i first. */
  456. if (iflag)
  457. (void)fprintf(stderr, "remove %s? ", path);
  458. else {
  459. /*
  460. * If it's not a symbolic link and it's unwritable and we're
  461. * talking to a terminal, ask. Symbolic links are excluded
  462. * because their permissions are meaningless. Check stdin_ok
  463. * first because we may not have stat'ed the file.
  464. */
  465. if (!stdin_ok || S_ISLNK(sp->st_mode) ||
  466. (!access(name, W_OK) &&
  467. !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
  468. (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid)))
  469. return (1);
  470. strmode(sp->st_mode, modep);
  471. if ((flagsp = fflagstostr(sp->st_flags)) == NULL)
  472. err(1, "fflagstostr");
  473. if (Pflag)
  474. errx(1,
  475. "%s: -P was specified, but file is not writable",
  476. path);
  477. (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ",
  478. modep + 1, modep[9] == ' ' ? "" : " ",
  479. user_from_uid(sp->st_uid, 0),
  480. group_from_gid(sp->st_gid, 0),
  481. *flagsp ? flagsp : "", *flagsp ? " " : "",
  482. path);
  483. free(flagsp);
  484. }
  485. (void)fflush(stderr);
  486. first = ch = getchar();
  487. while (ch != '\n' && ch != EOF)
  488. ch = getchar();
  489. return (first == 'y' || first == 'Y');
  490. }
  491. #define ISSLASH(a) ((a)[0] == '/' && (a)[1] == '\0')
  492. void
  493. checkslash(char **argv)
  494. {
  495. char **t, **u;
  496. int complained;
  497. complained = 0;
  498. for (t = argv; *t;) {
  499. if (ISSLASH(*t)) {
  500. if (!complained++)
  501. warnx("\"/\" may not be removed");
  502. eval = 1;
  503. for (u = t; u[0] != NULL; ++u)
  504. u[0] = u[1];
  505. } else {
  506. ++t;
  507. }
  508. }
  509. }
  510. int
  511. check2(char **argv)
  512. {
  513. struct stat st;
  514. int first;
  515. int ch;
  516. int fcount = 0;
  517. int dcount = 0;
  518. int i;
  519. const char *dname = NULL;
  520. for (i = 0; argv[i]; ++i) {
  521. if (lstat(argv[i], &st) == 0) {
  522. if (S_ISDIR(st.st_mode)) {
  523. ++dcount;
  524. dname = argv[i]; /* only used if 1 dir */
  525. } else {
  526. ++fcount;
  527. }
  528. }
  529. }
  530. first = 0;
  531. while (first != 'n' && first != 'N' && first != 'y' && first != 'Y') {
  532. if (dcount && rflag) {
  533. fprintf(stderr, "recursively remove");
  534. if (dcount == 1)
  535. fprintf(stderr, " %s", dname);
  536. else
  537. fprintf(stderr, " %d dirs", dcount);
  538. if (fcount == 1)
  539. fprintf(stderr, " and 1 file");
  540. else if (fcount > 1)
  541. fprintf(stderr, " and %d files", fcount);
  542. } else if (dcount + fcount > 3) {
  543. fprintf(stderr, "remove %d files", dcount + fcount);
  544. } else {
  545. return(1);
  546. }
  547. fprintf(stderr, "? ");
  548. fflush(stderr);
  549. first = ch = getchar();
  550. while (ch != '\n' && ch != EOF)
  551. ch = getchar();
  552. if (ch == EOF)
  553. break;
  554. }
  555. return (first == 'y' || first == 'Y');
  556. }
  557. #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
  558. void
  559. checkdot(char **argv)
  560. {
  561. char *p, **save, **t;
  562. int complained;
  563. complained = 0;
  564. for (t = argv; *t;) {
  565. if ((p = strrchr(*t, '/')) != NULL)
  566. ++p;
  567. else
  568. p = *t;
  569. if (ISDOT(p)) {
  570. if (!complained++)
  571. warnx("\".\" and \"..\" may not be removed");
  572. eval = 1;
  573. for (save = t; (t[0] = t[1]) != NULL; ++t)
  574. continue;
  575. t = save;
  576. } else
  577. ++t;
  578. }
  579. }
  580. void
  581. usage(void)
  582. {
  583. (void)fprintf(stderr, "%s\n%s\n",
  584. "usage: rm [-f | -i] [-dIPRrvW] file ...",
  585. " unlink file");
  586. exit(EX_USAGE);
  587. }
  588. static void
  589. siginfo(int sig __unused)
  590. {
  591. info = 1;
  592. }