From: utzoo!decvax!harpo!eagle!mhuxt!mhuxj!mhuxa!houxm!ihnp4!ixn5c!inuxc!pur-ee!uiucdcs!adams Newsgroups: net.sources Title: chall, recursive chown, chgrp - (nf) Article-I.D.: uiucdcs.1920 Posted: Fri Apr 22 22:39:05 1983 Received: Sun Apr 24 06:11:11 1983 #N:uiucdcs:12600008:000:8218 uiucdcs!adams Apr 22 17:58:00 1983 What follows is the source and man page (in a Bourne RunMe script) for chall, a recursive chown/chgrp. Syntax is as follows: % chall owner group dir1 dir2 ... E.g., % chall uucp bin /usr/lib/uucp /usr/spool/uucp The example appropriately changes the ownership (and group ownship) of /usr/lib/uucp, /usr/spool/uucp and all of their descendants. For more info, read the man page. Instructions: Save this in a file, and get rid of this garbage at top type /bin/sh RunMe type make chall Known lacks: Reading the directory could be more efficient. (E.g. I could use 4.1aBSD's directory readers, which has the side effect of making this work for 4.1c) N.b. I don't know if this will work on 4.1c. Error recovery is less than gracious. If ANYTHING goes wrong, chall just dies (by design). I prolly ought to have it continue its task. It has only been tested on 4.1,4.1aBSD, and one hybrid UNIX running on a Cadlinc Sun 68000. If it does not port easily to your machine, please let me know. I have one open file for every directory level. This means that you may only go (NOFILE -3) levels deep. On my machine this means I can get to *almost* everything if I execute %chall adams bin / (No, I didn't try it...) RCS: I use purdue's RCS (Revision Control System), and it has left its mark in my code. To get RCS, contact purdue!wft or Tichy@purdue. Distribution: I don't care where it goes, but don't sell it, and if you find any bugs (in my code?), let me know. Rob Adams (217) 333 3536 uiucdcs!adams By the way: There is a reason I don't just use find(1). This runs (seriously) 100 times as fast. Really. ============================ RunMe Follows... ======================== # This is the RunMe script for chall, from the University of Illinois # This Software may be distributed provided # 1) It may NOT be sold. # 2) The recipient agrees to (1). # # Rob Adams # decvax!pur-ee!uiucdcs!adams # parsec!uiucdcs!adams # UofIllinois echo Extracting Makefile cat << !EOF!EOF! > Makefile chall: main.o walk.o assert.o cc -o chall main.o walk.o assert.o clean: rm -f *.o chall core !EOF!EOF! echo Extracting main.c cat << !EOF!EOF! > main.c static char *RCSid = "$Header: /mnt/staff/adams/Cprogs/Chall/RCS/main.c,v 1.5 83/04/21 07:13:03 adams Exp $"; /* Modification history -- * * $Log: /mnt/staff/adams/Cprogs/Chall/RCS/main.c,v $ * Revision 1.5 83/04/21 07:13:03 adams * Added multiple directories. * * Revision 1.4 83/04/21 06:59:56 adams * indented right. * R * * Revision 1.3 83/04/11 04:52:30 adams * Added su-checking. * * Revision 1.2 83/04/11 04:47:38 adams * changed all the asserts to my new format. * * Revision 1.1 83/04/11 03:30:24 adams * Initial revision * * */ /* chall -- change the ownership and group ownership of a directory * and all its descendants. (I wonder if that is spelled right.. hmmm) */ /* * * The line marked $Header: and the comment marked $Log: are courtesy * RCS, the Revision Control System written and distributed by * Walter F. Tichy at University of Purdue. I have found it invaluable * in generating code, and recommend it whole-heartedly. For more * information, contact the author. (purdue!wft or Tichy@purdue) * */ /* * original work by Rob Adams, (uiucdcs!adams) on 11 April. * */ #include #include #include main (argc, argv) int argc; char *argv[]; { /* some defs here later. */ int uid, gid; int i; int walked; struct passwd *pswd, *getpwnam (); struct group *grp, *getgrnam (); /* Were we called correctly? */ assert ((argc >= 4), 's', "Usage: chall owner group file"); /* Am I su? non-roots fail */ assert (((getuid () == 0) || (geteuid () == 0)), 's', "You must be SU to run chall"); /* parse the args, first the name (argv[1]) */ setpwent (); pswd = getpwnam (argv[1]); endpwent (); assert ((pswd != (struct passwd *) 0), 's', "Cant get user info"); uid = pswd -> pw_uid; /* now comes the group. */ setgrent (); grp = getgrnam (argv[2]); endgrent (); assert ((grp != (struct group *) 0), 's', "Cant get group info"); gid = grp -> gr_gid; /* now, lets just pass everything off to the tree walker. */ for (i = 3; i < argc; i++) { walked = walk (uid, gid, argv[i]); assert (walked == 0, 's', "Something is wrong, but I don't know what"); } exit (walked); /* I know, walked is guaranteed to be 0 here */ } !EOF!EOF! echo Extracting walk.c cat << !EOF!EOF! > walk.c static char *RCSid = "$Header: RCS/walk.v Revision 1.2 83/04/11 04:48:28 adams Exp$"; /* * walk() * This guy does all the recursion. It should propagate * errors backwards, but I don't know if I will do that tonight. * */ #include #include #include #include walk (uid, gid, filename) int uid, gid; char *filename; { char file_nullend[2 * DIRSIZ]; /* better safe, et al... */ struct stat status; struct direct dirbuf; long lseek(); /* for lint */ int stated; int isdir; int walked; long lseeked; int chowned = 0; int dot; int readed; /* see if filename is a non-directory or directory */ stated = stat (filename, &status); assert ((stated == 0),'p',"chall: stat()"); isdir = ((status.st_mode & S_IFMT) == S_IFDIR); if (!isdir) /* I know, I really don't need isdir, but I like it */ { chowned = chown(filename, uid, gid); assert ((chowned == 0),'p',"chall: chown()"); return (0); } /* else */ chdir (filename); chowned = chown(".", uid, gid); assert ((chowned == 0),'p',"chall: chown()"); dot = open (".", 0); /* 0 means read-only */ assert ((dot != (-1)),'p', "chall: read()"); lseeked = lseek (dot, (long) 2*sizeof (dirbuf),0); assert ((lseeked != (-1)),'p',"chall: lseek()"); /* blow off . and .. */ while (1 == 1) { /* I suppose I should make a condition in there, but I am lazy.. */ readed = read (dot, (char *) & dirbuf, sizeof (dirbuf)); if (readed == 0) /* EOF */ break; assert ((readed == sizeof (dirbuf)),'p', "chall: read()"); if (dirbuf.d_ino == 0) continue; /* I need to terminate filename with a null char. */ (void) strncpy (file_nullend, dirbuf.d_name, DIRSIZ + 1); walked = walk (uid, gid, file_nullend); assert ((walked == 0),'s',"Something mysterious just happend"); } close (dot); chdir (".."); return (0); /* just emptied a directory */ } !EOF!EOF! echo Extracting assert.c cat << !EOF!EOF! > assert.c static char *RCSid = "$Header: RCS/assert.v Revision 1.2 83/04/11 04:47:02 adams Exp$"; /* * assert(bool,char,char *) * * program verification... * * If bool is true then return, else print a message and exit(-1) * * If char = p then perror(3) is used. * */ #include assert (bool, c, s) int bool; char c; char *s; { if (bool) return; if (c == 'p') perror (s); else fprintf (stderr, "%s\n", s); exit (-1); } !EOF!EOF! echo Extracting man'(8)' page cat << !EOF!EOF! > chown.8 .TH CHOWN 8 .UC 4 .SH NAME chown, chgrp, chall \- change owner or group .SH SYNOPSIS .B /etc/chown owner file ... .PP .B /etc/chgrp group file ... .PP .B chall owner group directory ... .SH DESCRIPTION .I Chown changes the owner of the .I files to .IR owner . The owner may be either a decimal UID or a login name found in the password file. .PP .I Chgrp changes the group-ID of the .I files to .IR group . The group may be either a decimal GID or a group name found in the group-ID file. .PP .I Chall changes the owner and group, of any file in a directory and its subdirectories. The owner and group must be logical names, not decimal UID or GID's. .PP Only the super-user can change owner or group, in order to simplify as yet unimplemented accounting procedures. .SH FILES /etc/passwd .br /etc/group .SH "SEE ALSO" chown(2), passwd(5), group(5) !EOF!EOF!