# HG changeset patch # User Rob Landley # Date 1340580949 18000 # Node ID 8bee9c27c21917907e5b1b7a191461704b3b13a4 # Parent b9ea00fffee5e2ebedf823a161a149c3de756218 Unify chown and chgrp, add support for -hHLP flags. diff -r b9ea00fffee5 -r 8bee9c27c219 lib/dirtree.c --- a/lib/dirtree.c Sun Jun 24 15:26:53 2012 -0500 +++ b/lib/dirtree.c Sun Jun 24 18:35:49 2012 -0500 @@ -84,21 +84,6 @@ 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 @@ 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 @@ // 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 -r b9ea00fffee5 -r 8bee9c27c219 lib/lib.h --- a/lib/lib.h Sun Jun 24 15:26:53 2012 -0500 +++ b/lib/lib.h Sun Jun 24 18:35:49 2012 -0500 @@ -65,7 +65,7 @@ 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 @@ 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 -r b9ea00fffee5 -r 8bee9c27c219 toys/chgrp.c --- a/toys/chgrp.c Sun Jun 24 15:26:53 2012 -0500 +++ b/toys/chgrp.c Sun Jun 24 18:35:49 2012 -0500 @@ -1,61 +1,80 @@ /* vi: set sw=4 ts=4: * - * chgrp.c - Change group ownership + * chown.c - Change ownership * * Copyright 2012 Georgi Chorbadzhiyski * + * 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_v 1 #define FLAG_f 2 -#define FLAG_v 1 +#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 @@ void chgrp_main(void) { - char **s; - struct group *group; + int ischown = toys.which->name[2] == 'o'; + char **s, *own; + + // Distinguish chown from chgrp + if (ischown) { + char *grp; + struct passwd *p; - 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; + 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; - for (s=toys.optargs+1; *s; s++) dirtree_read(*s, do_chgrp); + 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; + } + + if (CFG_TOYBOX_FREE) free(own); } diff -r b9ea00fffee5 -r 8bee9c27c219 toys/chown.c --- a/toys/chown.c Sun Jun 24 15:26:53 2012 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,137 +0,0 @@ -/* vi: set sw=4 ts=4: - * - * chown.c - Change ownership - * - * Copyright 2012 Georgi Chorbadzhiyski - * - * 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); - } - } -}