view toys/chgrp.c @ 647:3258d9233753

Replace TOY_LIST_LEN with more generic ARRAY_LEN()
author Rob Landley <rob@landley.net>
date Tue, 14 Aug 2012 01:42:06 -0500
parents 8bee9c27c219
children
line wrap: on
line source

/* 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
 * See http://pubs.opengroup.org/onlinepubs/009695399/utilities/chgrp.html
 *
 * TODO: group only one of [HLP]

USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv", TOYFLAG_BIN))
USE_CHGRP(OLDTOY(chown, chgrp, "<2hPLHRfv", TOYFLAG_BIN))

config CHGRP
	bool "chgrp/chown"
	default y
	help
	  usage: chown [-RHLP] [-fvh] [owner][:group] file...
	  usage: chgrp [-RHLP] [-fvh] group file...

	  Change ownership of one or more files.

	  -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_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 *owner_name, *group_name;
	int symfollow;
)

#define TT this.chgrp

static int do_chgrp(struct dirtree *node)
{
	int fd, ret, flags = toys.optflags;

	// 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);

	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("%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 owner:group of '%s' to '%s:%s'", path,
				TT.owner_name, TT.group_name);
		free(path);
	}
	toys.exitval |= ret;

	return 0;
}

void chgrp_main(void)
{
	int ischown = toys.which->name[2] == 'o';
	char **s, *own;

	// 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;
	}

	if (CFG_TOYBOX_FREE) free(own);
}