changeset 607:8bee9c27c219 0.3.1

Unify chown and chgrp, add support for -hHLP flags.
author Rob Landley <rob@landley.net>
date Sun, 24 Jun 2012 18:35:49 -0500
parents b9ea00fffee5
children b8588cf2fae4
files lib/dirtree.c lib/lib.h toys/chgrp.c toys/chown.c
diffstat 4 files changed, 80 insertions(+), 187 deletions(-) [+]
line wrap: on
line diff
--- 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))
 {
--- 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,
--- 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 <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_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);
 }
--- 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 <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);
-		}
-	}
-}