aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/dirtree.c24
-rw-r--r--lib/lib.h3
-rw-r--r--toys/chgrp.c103
-rw-r--r--toys/chown.c137
4 files changed, 80 insertions, 187 deletions
diff --git a/lib/dirtree.c b/lib/dirtree.c
index 53d51902..664588d8 100644
--- a/lib/dirtree.c
+++ b/lib/dirtree.c
@@ -84,21 +84,6 @@ int dirtree_parentfd(struct dirtree *node)
return node->parent ? node->parent->data : AT_FDCWD;
}
-// get open filehandle for node in extra, giving caller the option of
-// using DIRTREE_COMEAGAIN or not.
-int dirtree_opennode(struct dirtree *try)
-{
- if (!dirtree_notdotdot(try)) return 0;
- if (S_ISDIR(try->st.st_mode)) {
- if (!try->extra) {
- try->extra = xdup(try->data);
- return DIRTREE_COMEAGAIN;
- }
- } else try->extra = openat(dirtree_parentfd(try), try->name, 0);
-
- return DIRTREE_SAVE|DIRTREE_RECURSE;
-}
-
// Handle callback for a node in the tree. Returns saved node(s) or NULL.
//
// By default, allocates a tree of struct dirtree, not following symlinks
@@ -114,18 +99,14 @@ struct dirtree *handle_callback(struct dirtree *new,
if (!callback) callback = dirtree_notdotdot;
- // Directory always has filehandle for examining contents. Whether or
- // not we'll recurse into it gets decided later.
-
- if (dir) new->data = openat(dirtree_parentfd(new), new->name, 0);
-
flags = callback(new);
if (dir) {
if (flags & (DIRTREE_RECURSE|DIRTREE_COMEAGAIN)) {
+ new->data = openat(dirtree_parentfd(new), new->name, 0);
dirtree_recurse(new, callback, flags & DIRTREE_SYMFOLLOW);
if (flags & DIRTREE_COMEAGAIN) flags = callback(new);
- } else close(new->data);
+ }
}
// If this had children, it was callback's job to free them already.
@@ -180,6 +161,7 @@ void dirtree_recurse(struct dirtree *node,
// Create dirtree from path, using callback to filter nodes.
// If callback == NULL allocate a tree of struct dirtree nodes and return
// pointer to root node.
+// symfollow is just for the top of tree, callback return code controls children
struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node))
{
diff --git a/lib/lib.h b/lib/lib.h
index a427640d..5ed06035 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -65,7 +65,7 @@ void get_optflags(void);
struct dirtree {
struct dirtree *next, *parent, *child;
long extra; // place for user to store their stuff (can be pointer)
- long data; // dirfd for directory, linklen for symlink
+ long data; // dirfd for directory, linklen for symlink, -1 = comeagain
struct stat st;
char *symlink;
char name[];
@@ -75,7 +75,6 @@ struct dirtree *dirtree_add_node(int dirfd, char *name, int symfollow);
char *dirtree_path(struct dirtree *node, int *plen);
int dirtree_notdotdot(struct dirtree *catch);
int dirtree_parentfd(struct dirtree *node);
-int dirtree_opennode(struct dirtree *try);
struct dirtree *handle_callback(struct dirtree *new,
int (*callback)(struct dirtree *node));
void dirtree_recurse(struct dirtree *node,
diff --git a/toys/chgrp.c b/toys/chgrp.c
index 521db0b7..ad4e351a 100644
--- a/toys/chgrp.c
+++ b/toys/chgrp.c
@@ -1,61 +1,80 @@
/* vi: set sw=4 ts=4:
*
- * chgrp.c - Change group ownership
+ * chown.c - Change ownership
*
* Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
*
+ * See http://pubs.opengroup.org/onlinepubs/009695399/utilities/chown.html
* See http://pubs.opengroup.org/onlinepubs/009695399/utilities/chgrp.html
*
- * TODO: Add support for -h
- * TODO: Add support for -H
- * TODO: Add support for -L
- * TODO: Add support for -P
+ * TODO: group only one of [HLP]
-USE_CHGRP(NEWTOY(chgrp, "<2Rfv", TOYFLAG_BIN))
+USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv", TOYFLAG_BIN))
+USE_CHGRP(OLDTOY(chown, chgrp, "<2hPLHRfv", TOYFLAG_BIN))
config CHGRP
- bool "chgrp"
+ bool "chgrp/chown"
default y
help
- usage: chgrp [-R] [-f] [-v] group file...
- Change group ownership of one or more files.
+ usage: chown [-RHLP] [-fvh] [owner][:group] file...
+ usage: chgrp [-RHLP] [-fvh] group file...
+
+ Change ownership of one or more files.
- -R recurse into subdirectories.
-f suppress most error messages.
+ -h change symlinks instead of what they point to
+ -R recurse into subdirectories (implies -h).
+ -H with -R change target of symlink, follow command line symlinks
+ -L with -R change target of symlink, follow all symlinks
+ -P with -R change symlink, do not follow symlinks (default)
-v verbose output.
*/
#include "toys.h"
-#define FLAG_R 4
-#define FLAG_f 2
#define FLAG_v 1
+#define FLAG_f 2
+#define FLAG_R 4
+#define FLAG_H 8
+#define FLAG_L 16
+#define FLAG_P 32
+#define FLAG_h 64
DEFINE_GLOBALS(
+ uid_t owner;
gid_t group;
- char *group_name;
+ char *owner_name, *group_name;
+ int symfollow;
)
#define TT this.chgrp
static int do_chgrp(struct dirtree *node)
{
- int ret, flags = toys.optflags;
+ int fd, ret, flags = toys.optflags;
- ret = dirtree_opennode(node);
- if (!ret || ((flags & FLAG_R) && ret == DIRTREE_COMEAGAIN)) return ret;
+ // Depth first search
+ if (!dirtree_notdotdot(node)) return 0;
+ if ((flags & FLAG_R) && node->data != -1 && S_ISDIR(node->st.st_mode))
+ return DIRTREE_COMEAGAIN|((flags&FLAG_L) ? DIRTREE_SYMFOLLOW : 0);
- if (node->extra != -1) ret = fchown(node->extra, -1, TT.group);
+ fd = dirtree_parentfd(node);
+ ret = fchownat(fd, node->name, TT.owner, TT.group,
+ (flags&(FLAG_L|FLAG_H)) || !(flags&(FLAG_h|FLAG_R))
+ ? 0 : AT_SYMLINK_NOFOLLOW);
if (ret || (flags & FLAG_v)) {
char *path = dirtree_path(node, 0);
if (flags & FLAG_v)
- xprintf("chgrp(%s, %s)\n", TT.group_name, path);
+ xprintf("%s %s%s%s %s\n", toys.which->name,
+ TT.owner_name ? TT.owner_name : "",
+ toys.which->name[2]=='o' && TT.group_name ? ":" : "",
+ TT.group_name ? TT.group_name : "", path);
if (ret == -1 && !(toys.optflags & FLAG_f))
- perror_msg("changing group of '%s' to '%s'", path, TT.group_name);
+ perror_msg("changing owner:group of '%s' to '%s:%s'", path,
+ TT.owner_name, TT.group_name);
free(path);
}
- close(node->extra);
toys.exitval |= ret;
return 0;
@@ -63,13 +82,43 @@ static int do_chgrp(struct dirtree *node)
void chgrp_main(void)
{
- char **s;
- struct group *group;
+ int ischown = toys.which->name[2] == 'o';
+ char **s, *own;
- TT.group_name = *toys.optargs;
- group = getgrnam(TT.group_name);
- if (!group) error_exit("no group '%s'", TT.group_name);
- TT.group = group->gr_gid;
+ // Distinguish chown from chgrp
+ if (ischown) {
+ char *grp;
+ struct passwd *p;
+
+ own = xstrdup(*toys.optargs);
+ if ((grp = strchr(own, ':')) || (grp = strchr(own, '.'))) {
+ *(grp++) = 0;
+ TT.group_name = grp;
+ }
+ if (*own) {
+ TT.owner_name = own;
+ p = getpwnam(own);
+ // TODO: trailing garbage?
+ if (!p && isdigit(*own)) p=getpwuid(atoi(own));
+ if (!p) error_exit("no user '%s'", own);
+ TT.owner = p->pw_uid;
+ }
+ } else TT.group_name = *toys.optargs;
+
+ if (TT.group_name) {
+ struct group *g;
+ g = getgrnam(TT.group_name);
+ if (!g) g=getgrgid(atoi(TT.group_name));
+ if (!g) error_exit("no group '%s'", TT.group_name);
+ TT.group = g->gr_gid;
+ }
+
+ for (s=toys.optargs+1; *s; s++) {
+ struct dirtree *new = dirtree_add_node(AT_FDCWD, *s,
+ toys.optflags&(FLAG_H|FLAG_L));
+ if (new) handle_callback(new, do_chgrp);
+ else toys.exitval = 1;
+ }
- for (s=toys.optargs+1; *s; s++) dirtree_read(*s, do_chgrp);
+ if (CFG_TOYBOX_FREE) free(own);
}
diff --git a/toys/chown.c b/toys/chown.c
deleted file mode 100644
index d3a4c112..00000000
--- a/toys/chown.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/* vi: set sw=4 ts=4:
- *
- * chown.c - Change ownership
- *
- * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
- *
- * See http://pubs.opengroup.org/onlinepubs/009695399/utilities/chown.html
- *
- * TODO: Add support for -h
- * TODO: Add support for -H
- * TODO: Add support for -L
- * TODO: Add support for -P
-
-USE_CHOWN(NEWTOY(chown, "<2Rfv", TOYFLAG_BIN))
-
-config CHOWN
- bool "chown"
- default n
- help
- usage: chown [-R] [-f] [-v] group file...
- Change ownership of one or more files.
-
- -R recurse into subdirectories.
- -f suppress most error messages.
- -v verbose output.
-*/
-
-#include "toys.h"
-
-#define FLAG_R 4
-#define FLAG_f 2
-#define FLAG_v 1
-
-DEFINE_GLOBALS(
- uid_t owner;
- gid_t group;
- char *owner_name;
- char *group_name;
-)
-
-#define TT this.chown
-
-static int do_chown(const char *path) {
- int ret = chown(path, TT.owner, TT.group);
- if (toys.optflags & FLAG_v)
- xprintf("chown(%s:%s, %s)\n", TT.owner_name, TT.group_name, path);
- if (ret == -1 && !(toys.optflags & FLAG_f))
- perror_msg("changing owner of '%s' to '%s:%s'", path,
- TT.owner_name, TT.group_name);
- toys.exitval |= ret;
- return ret;
-}
-
-// Copied from toys/cp.c:cp_node()
-int chown_node(char *path, struct dirtree *node)
-{
- char *s = path + strlen(path);
- struct dirtree *n = node;
-
- for ( ; ; n = n->parent) {
- while (s!=path) {
- if (*(--s) == '/') break;
- }
- if (!n) break;
- }
- if (s != path) s++;
-
- do_chown(s);
-
- return 0;
-}
-
-void chown_main(void)
-{
- char **s;
- char *owner = NULL, *group;
- char *param1 = *toys.optargs;
-
- TT.owner = -1;
- TT.group = -1;
- TT.owner_name = "";
- TT.group_name = "";
-
- group = strchr(param1, ':');
- if (!group)
- group = strchr(param1, '.');
-
- if (group) {
- group++;
- struct group *g = getgrnam(group);
- if (!g) {
- error_msg("invalid group '%s'", group);
- toys.exitval = 1;
- return;
- }
- TT.group = g->gr_gid;
- TT.group_name = group;
- owner = param1;
- owner[group - owner - 1] = '\0';
- } else {
- owner = param1;
- }
-
- if (owner && owner[0]) {
- struct passwd *p = getpwnam(owner);
- if (!p) {
- error_msg("invalid owner '%s'", owner);
- toys.exitval = 1;
- return;
- }
- TT.owner = p->pw_uid;
- TT.owner_name = owner;
- }
-
- if (toys.optflags & FLAG_R) {
- // Recurse into subdirectories
- for (s=toys.optargs + 1; *s; s++) {
- struct stat sb;
- if (stat(*s, &sb) == -1) {
- if (!(toys.optflags & FLAG_f))
- perror_msg("%s", *s);
- continue;
- }
- do_chown(*s);
- if (S_ISDIR(sb.st_mode)) {
- strncpy(toybuf, *s, sizeof(toybuf) - 1);
- toybuf[sizeof(toybuf) - 1] = 0;
- dirtree_read(toybuf, NULL, chown_node);
- }
- }
- } else {
- // Do not recurse
- for (s=toys.optargs + 1; *s; s++) {
- do_chown(*s);
- }
- }
-}