1/* chgrp.c - Change user and group ownership
2 *
3 * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
4 *
5 * See http://opengroup.org/onlinepubs/9699919799/utilities/chown.html
6 * See http://opengroup.org/onlinepubs/9699919799/utilities/chgrp.html
7
8USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv[-HLP]", TOYFLAG_BIN))
9USE_CHOWN(OLDTOY(chown, chgrp, TOYFLAG_BIN))
10
11config CHGRP
12  bool "chgrp"
13  default y
14  help
15    usage: chgrp/chown [-RHLP] [-fvh] group file...
16
17    Change group of one or more files.
18
19    -f	suppress most error messages.
20    -h	change symlinks instead of what they point to
21    -R	recurse into subdirectories (implies -h).
22    -H	with -R change target of symlink, follow command line symlinks
23    -L	with -R change target of symlink, follow all symlinks
24    -P	with -R change symlink, do not follow symlinks (default)
25    -v	verbose output.
26
27config CHOWN
28  bool "chown"
29  default y
30  help
31    see: chgrp
32*/
33
34#define FOR_chgrp
35#define FORCE_FLAGS
36#include "toys.h"
37
38GLOBALS(
39  uid_t owner;
40  gid_t group;
41  char *owner_name, *group_name;
42  int symfollow;
43)
44
45static int do_chgrp(struct dirtree *node)
46{
47  int fd, ret, flags = toys.optflags;
48
49  // Depth first search
50  if (!dirtree_notdotdot(node)) return 0;
51  if ((flags & FLAG_R) && !node->again && S_ISDIR(node->st.st_mode))
52    return DIRTREE_COMEAGAIN|(DIRTREE_SYMFOLLOW*!!(flags&FLAG_L));
53
54  fd = dirtree_parentfd(node);
55  ret = fchownat(fd, node->name, TT.owner, TT.group,
56    AT_SYMLINK_NOFOLLOW*(!(flags&(FLAG_L|FLAG_H)) && (flags&(FLAG_h|FLAG_R))));
57
58  if (ret || (flags & FLAG_v)) {
59    char *path = dirtree_path(node, 0);
60    if (flags & FLAG_v)
61      xprintf("%s %s%s%s %s\n", toys.which->name,
62        TT.owner_name ? TT.owner_name : "",
63        toys.which->name[2]=='o' && TT.group_name ? ":" : "",
64        TT.group_name ? TT.group_name : "", path);
65    if (ret == -1 && !(toys.optflags & FLAG_f))
66      perror_msg("'%s' to '%s:%s'", path, TT.owner_name, TT.group_name);
67    free(path);
68  }
69  toys.exitval |= ret;
70
71  return 0;
72}
73
74void chgrp_main(void)
75{
76  int ischown = toys.which->name[2] == 'o';
77  char **s, *own;
78
79  TT.owner = TT.group = -1;
80
81  // Distinguish chown from chgrp
82  if (ischown) {
83    char *grp;
84
85    own = xstrdup(*toys.optargs);
86    if ((grp = strchr(own, ':')) || (grp = strchr(own, '.'))) {
87      *(grp++) = 0;
88      TT.group_name = grp;
89    }
90    if (*own) TT.owner = xgetpwnamid(TT.owner_name = own)->pw_uid;
91  } else TT.group_name = *toys.optargs;
92
93  if (TT.group_name && *TT.group_name)
94    TT.group = xgetgrnamid(TT.group_name)->gr_gid;
95
96  for (s=toys.optargs+1; *s; s++)
97    dirtree_handle_callback(dirtree_start(*s, toys.optflags&(FLAG_H|FLAG_L)),
98      do_chgrp);;
99
100  if (CFG_TOYBOX_FREE && ischown) free(own);
101}
102
103void chown_main()
104{
105  chgrp_main();
106}
107