aboutsummaryrefslogtreecommitdiff
path: root/toys/posix/rm.c
blob: de565a76c189842c156a62dd957d9d8172e22ad0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/* rm.c - remove files
 *
 * Copyright 2012 Rob Landley <rob@landley.net>
 *
 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/rm.html

USE_RM(NEWTOY(rm, "<1fiRr[-fi]", TOYFLAG_BIN))

config RM
  bool "rm"
  default y
  help
    usage: rm [-fiRr] FILE...

    Remove each argument from the filesystem.

    -f	force: remove without confirmation, no error if it doesn't exist
    -i	interactive: prompt for confirmation
    -rR	recursive: remove directory contents
*/

#define FOR_rm
#include "toys.h"

static int do_rm(struct dirtree *try)
{
  int fd = dirtree_parentfd(try), flags = toys.optflags;
  int or = 0, using = 0;

  // Skip . and .. (yes, even explicitly on the command line: posix says to)
  if (!dirtree_notdotdot(try)) return 0;

  if (S_ISDIR(try->st.st_mode)) {
    if (flags & (FLAG_r|FLAG_R)) {
      if (try->data != -1) return DIRTREE_COMEAGAIN;
      using = AT_REMOVEDIR;
    }
  }

  // Prompt if necessary
  if (!(flags & FLAG_f) && faccessat(fd, try->name, W_OK, AT_SYMLINK_NOFOLLOW))
    or++;

  if ((or && isatty(0)) || (flags & FLAG_i)) {
    fprintf(stderr, "rm %s", or ? "ro " : "");
    if (!yesno(try->name, 2)) return 0;
  }

  // Intentionally fail non-recursive attempts to remove even an empty dir
  // because POSIX says to.
  if (unlinkat(fd, try->name, using)) {
    perror_msg("%s", try->name);
    toys.exitval = 1;
  }

  return 0;
}

void rm_main(void)
{
  char **s;

  for (s = toys.optargs; *s; s++) {
    if (!strcmp(*s, "/")) {
      error_msg("rm /. if you mean it");
      toys.exitval = 1;
      continue;
    }

    // There's a race here where a file removed between this access and
    // dirtree's stat would report the nonexistence as an error, but that's
    // not a normal "it didn't exist" so I'm ok with it.
    if ((toys.optflags & FLAG_f) && (access(*s, F_OK) && errno == ENOENT))
      continue;
    dirtree_read(*s, do_rm);
  }
}