#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/avl.h>
#include <grp.h>
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include <locale.h>
#include <libcmdutils.h>
#include <errno.h>
#include <strings.h>
#include <aclutils.h>
static struct group *gr;
static struct stat stbuf;
static struct stat stbuf2;
static gid_t gid;
static int hflag = 0,
fflag = 0,
rflag = 0,
Hflag = 0,
Lflag = 0,
Pflag = 0,
sflag = 0;
static int status = 0;
static avl_tree_t *tree;
static void usage(void);
static int isnumber(char *);
static int Perror(char *);
static void chgrpr(char *, gid_t);
#define FOLLOW_CL_LINKS (rflag && (Hflag || Lflag))
#define FOLLOW_D_LINKS (Lflag)
#define CHOWN(f, u, g) if (chown(f, u, g) < 0) { \
status += Perror(f); \
}
#define LCHOWN(f, u, g) if (lchown(f, u, g) < 0) { \
status += Perror(f); \
}
#define SETUGID_PRESERVE(dir, mode) \
if (((mode) & (S_ISGID|S_ISUID)) != 0) \
(void) chmod((dir), (mode) & ~S_IFMT)
extern int optind;
int
main(int argc, char *argv[])
{
int c;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
while ((c = getopt(argc, argv, "RhfHLPs")) != EOF)
switch (c) {
case 'R':
rflag++;
break;
case 'h':
hflag++;
break;
case 'f':
fflag++;
break;
case 'H':
Lflag = Pflag = 0;
Hflag++;
break;
case 'L':
Hflag = Pflag = 0;
Lflag++;
break;
case 'P':
Hflag = Lflag = 0;
Pflag++;
break;
case 's':
sflag++;
break;
default:
usage();
}
if (rflag && !(Lflag || Hflag || Pflag || hflag)) {
Pflag = 1;
}
argc -= optind;
argv = &argv[optind];
if ((argc < 2) ||
((Hflag || Lflag || Pflag) && !rflag) ||
((Hflag || Lflag || Pflag) && hflag)) {
usage();
}
if (sflag) {
if (sid_to_id(argv[0], B_FALSE, &gid)) {
(void) fprintf(stderr, gettext(
"chgrp: invalid group sid %s\n"), argv[0]);
exit(2);
}
} else if ((gr = getgrnam(argv[0])) != NULL) {
gid = gr->gr_gid;
} else {
if (isnumber(argv[0])) {
errno = 0;
gid = (gid_t)strtoul(argv[0], NULL, 10);
if (errno != 0) {
if (errno == ERANGE) {
(void) fprintf(stderr, gettext(
"chgrp: group id is too large\n"));
exit(2);
} else {
(void) fprintf(stderr, gettext(
"chgrp: invalid group id\n"));
exit(2);
}
}
} else {
(void) fprintf(stderr, "chgrp: ");
(void) fprintf(stderr, gettext("unknown group: %s\n"),
argv[0]);
exit(2);
}
}
for (c = 1; c < argc; c++) {
tree = NULL;
if (lstat(argv[c], &stbuf) < 0) {
status += Perror(argv[c]);
continue;
}
if (rflag && ((stbuf.st_mode & S_IFMT) == S_IFLNK)) {
if (hflag || Pflag) {
LCHOWN(argv[c], -1, gid);
} else {
if (stat(argv[c], &stbuf2) < 0) {
status += Perror(argv[c]);
continue;
}
if (FOLLOW_CL_LINKS) {
if ((stbuf2.st_mode & S_IFMT)
== S_IFDIR) {
if (add_tnode(&tree,
stbuf2.st_dev,
stbuf2.st_ino) == 1) {
chgrpr(argv[c], gid);
SETUGID_PRESERVE(
argv[c],
stbuf2.st_mode &
~S_IFMT);
} else {
status += Perror(
argv[c]);
}
} else {
CHOWN(argv[c], -1, gid);
}
} else {
CHOWN(argv[c], -1, gid);
if ((stbuf2.st_mode & S_IFMT)
== S_IFDIR) {
SETUGID_PRESERVE(argv[c],
stbuf2.st_mode & ~S_IFMT);
}
}
}
} else if (rflag && ((stbuf.st_mode & S_IFMT) == S_IFDIR)) {
if (add_tnode(&tree, stbuf.st_dev,
stbuf.st_ino) == 1) {
chgrpr(argv[c], gid);
SETUGID_PRESERVE(argv[c],
stbuf.st_mode & ~S_IFMT);
} else {
status += Perror(argv[c]);
}
} else {
if (hflag || Pflag) {
LCHOWN(argv[c], -1, gid);
} else {
CHOWN(argv[c], -1, gid);
}
if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
SETUGID_PRESERVE(argv[c],
stbuf.st_mode & ~S_IFMT);
}
}
}
return (status);
}
static void
chgrpr(char *dir, gid_t gid)
{
struct dirent *dp;
DIR *dirp;
struct stat st, st2;
char savedir[1024];
if (getcwd(savedir, 1024) == 0) {
(void) fprintf(stderr, "chgrp: ");
(void) fprintf(stderr, gettext("%s\n"), savedir);
exit(255);
}
CHOWN(dir, -1, gid);
if (chdir(dir) < 0) {
status += Perror(dir);
return;
}
if ((dirp = opendir(".")) == NULL) {
status += Perror(dir);
return;
}
for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
if ((strcmp(dp->d_name, ".") == 0) ||
(strcmp(dp->d_name, "..") == 0)) {
continue;
}
if (lstat(dp->d_name, &st) < 0) {
status += Perror(dp->d_name);
continue;
}
if ((st.st_mode & S_IFMT) == S_IFLNK) {
if (hflag || Pflag) {
LCHOWN(dp->d_name, -1, gid);
} else {
if (stat(dp->d_name, &st2) < 0) {
status += Perror(dp->d_name);
continue;
}
if (FOLLOW_D_LINKS) {
if ((st2.st_mode & S_IFMT) == S_IFDIR) {
int rc;
if ((rc = add_tnode(&tree,
st2.st_dev,
st2.st_ino)) == 1) {
chgrpr(dp->d_name, gid);
SETUGID_PRESERVE(
dp->d_name,
st2.st_mode &
~S_IFMT);
} else if (rc == 0) {
continue;
} else {
status += Perror(
dp->d_name);
continue;
}
} else {
CHOWN(dp->d_name, -1, gid);
}
} else {
CHOWN(dp->d_name, -1, gid);
if ((st2.st_mode & S_IFMT) == S_IFDIR) {
SETUGID_PRESERVE(dp->d_name,
st2.st_mode & ~S_IFMT);
}
}
}
} else if ((st.st_mode & S_IFMT) == S_IFDIR) {
int rc;
if ((rc = add_tnode(&tree, st.st_dev,
st.st_ino)) == 1) {
chgrpr(dp->d_name, gid);
SETUGID_PRESERVE(dp->d_name,
st.st_mode & ~S_IFMT);
} else if (rc == 0) {
continue;
} else {
status += Perror(dp->d_name);
continue;
}
} else {
CHOWN(dp->d_name, -1, gid);
}
}
(void) closedir(dirp);
if (chdir(savedir) < 0) {
(void) fprintf(stderr, "chgrp: ");
(void) fprintf(stderr, gettext("can't change back to %s\n"),
savedir);
exit(255);
}
}
static int
isnumber(char *s)
{
int c;
while ((c = *s++) != '\0')
if (!isdigit(c))
return (0);
return (1);
}
static int
Perror(char *s)
{
if (!fflag) {
(void) fprintf(stderr, "chgrp: ");
perror(s);
}
return (!fflag);
}
static void
usage(void)
{
(void) fprintf(stderr, gettext(
"usage:\n"
"\tchgrp [-fhR] group file ...\n"
"\tchgrp -R [-f] [-H|-L|-P] group file ...\n"
"\tchgrp -s [-fhR] groupsid file ...\n"
"\tchgrp -s -R [-f] [-H|-L|-P] groupsid file ...\n"));
exit(2);
}