2 * (C) Copyright Transarc Corporation 1989
3 * Licensed Materials - Property of Transarc
7 /*------------------------------------------------------------------------
11 * Configuration tree module for package, the AFS workstation
15 * Transarc Corporation & Carnegie Mellon University
16 *------------------------------------------------------------------------*/
18 #include <afs/param.h>
20 #include <sys/types.h>
22 #include <sys/param.h>
29 #include "validupdates.h"
34 extern FILE *yyin; /*Input file for the YACC parser*/
36 /*------------------------------------------------------------------------
40 * Returns a hash value for the given name.
43 * char *name : Ptr to name to hash.
46 * Hash value associated with the given name.
49 * This routine is private to the module.
53 *------------------------------------------------------------------------*/
55 static int namehash(name)
64 hash += (hash << 6) + *name++;
69 /*------------------------------------------------------------------------
70 * [static] AllocConfigNode
73 * Allocate storage for a configuration tree node.
79 * Ptr to the freshly-allocated node if successful,
80 * Otherwise we'll exit the program.
83 * This routine is private to the module.
86 * May exit from package entirely.
87 *------------------------------------------------------------------------*/
89 static CTREEPTR AllocConfigNode()
95 np = (CTREEPTR) emalloc(sizeof(CTREE));
96 bzero((char *)np, sizeof(CTREE));
101 /*------------------------------------------------------------------------
102 * [static] ValidUpdtSpec
105 * Checks whether the given update specification is valid for a file
106 * of the given filetype.
109 * u_short ftype : The type of file to be updated.
110 * u_short uspec : The update spec to check.
113 * TRUE if the check succeeds,
117 * This routine is private to the module.
121 *------------------------------------------------------------------------*/
123 static int ValidUpdtSpec(ftype, uspec)
129 register struct updatetype *u;
132 * Scan the list of valid update specs, succeed if you find an
135 for (u = validupdates; u->filetype != 0; u++)
136 if ((u->filetype == ftype) && (u->updtflags == uspec))
142 /*------------------------------------------------------------------------
143 * [static] ValidateUserName
146 * Given a pointer to a user name, see if that name is a valid one
147 * by possibly looking in the password file. If it's valid, return
148 * the associated uid via the uidp parameter.
151 * char *name : Ptr to user name to validate.
152 * short *uidp : Matching uid is placed here upon success.
159 * This routine is private to the module.
162 * The passwd structure pointer, pw, is a static. Thus, we
163 * recall the last search into the password file. Before
164 * doing a lookup, we check to see if we've lucked out and
165 * the results are already cached.
166 *------------------------------------------------------------------------*/
168 static int ValidateUserName(name, uidp)
170 register short *uidp;
172 { /*ValidateUserName*/
174 register afs_int32 uid;
176 static struct passwd *pw = NULL; /*Ptr to passwd file entry*/
179 * We always know what root is; don't bother with a passwd lookup
182 if (strcmp(name, "root") == 0) {
188 * If we have the results from a previous search, don't do it
189 * again. Otherwise, take the plunge.
191 if (pw == NULL || strcmp(pw->pw_name, name))
195 uid = strtol(name, &nptr, 10);
196 if ((int)(nptr-name) == strlen(name))
197 pw = getpwuid((uid_t)uid);
202 * Found the given name. Return the matching pid.
213 } /*ValidateUserName*/
215 /*------------------------------------------------------------------------
216 * [static] ValidateGroupName
219 * Given a pointer to a group name, see if that name is a valid one
220 * by possibly looking in the group file. If it's valid, return
221 * the associated gid via the gidp parameter.
224 * char *name : Ptr to group name to validate.
225 * short *gidp : Matching gid is placed here upon success.
232 * This routine is private to the module.
235 * The group structure pointer, gr, is a static. Thus, we
236 * recall the last search into the group file. Before
237 * doing a lookup, we check to see if we've lucked out and
238 * the results are already cached.
239 *------------------------------------------------------------------------*/
241 static int ValidateGroupName(name, gidp)
243 register short *gidp;
245 { /*ValidateGroupName*/
247 register afs_int32 gid;
249 static struct group *gr = NULL; /*Ptr to group structure*/
252 * We always know the group number for wheel, so don't bother doing
255 if (strcmp(name, "wheel") == 0) {
261 * If we have the results from a previous search, don't do it
262 * again. Otherwise, take the plunge.
264 if (gr == NULL || strcmp(gr->gr_name, name))
268 gid = strtol(name, &nptr, 10);
269 if ((int)(nptr-name) == strlen(name))
270 gr = getgrgid((gid_t)gid);
275 * Found the given group. Return the matching gid.
283 } /*ValidateGroupName*/
285 /*------------------------------------------------------------------------
286 * InitializeConfigTree
289 * Allocates storage for the root of the configuration tree.
295 * 0 if successful. On failure, package exits.
298 * The newly-allocated space is recorded in the global variable
302 * As described; may exit from package.
303 *------------------------------------------------------------------------*/
305 int InitializeConfigTree()
307 { /*InitializeConfigTree*/
309 config_root = AllocConfigNode();
312 } /*InitializeConfigTree*/
314 /*------------------------------------------------------------------------
318 * Locate the node corresponding to the given name in the entries
319 * corresponding to the specified directory node.
322 * CTREEPTR dp : Config tree node whose entries are to be searched
323 * for the given name.
324 * char *name : Ptr to the string name to search for.
325 * int lmode : Lookup mode, either C_LOCATE or C_CREATE.
328 * Ptr to located node if it existed, else
329 * Ptr to newly-created node if no match found and C_CREATE mode used,
330 * Otherwise a null pointer. Null will also be returned if the given
331 * directory node isn't really for a directory.
334 * Nothing interesting.
337 * May create entries and nodes; may exit package if any of these
339 *------------------------------------------------------------------------*/
341 CTREEPTR LocateChildNode(dp, name, lmode)
342 register CTREEPTR dp;
346 { /*LocateChildNode*/
348 register int hash; /*Hash value for given name*/
349 register ENTRYPTR ep; /*Ptr to entry being examined*/
350 register int found_entry; /*Found entry we want?*/
353 * First, make sure dp corresponds to a directory
355 if (dp->type != S_IFDIR)
359 * Set up to search the entries hanging off the directory node.
360 * Precompute the hash value for the name.
362 hash = namehash(name);
367 * Sweep through the list of entries until we find our match or
370 while ((ep != NULL) && !found_entry) {
372 * We compare the hash value first, and only if that succeeds
373 * do we do the string compare.
375 if ((ep->hash == hash) && (strcmp(ep->name, name) == 0))
379 * No match. Move on to the next entry, if any.
383 } /*Search list of entries*/
386 * If we found it, return the node hanging off the entry.
392 * We didn't find the given name. If we aren't supposed to create
393 * a node for the name, we return failure.
395 if (!(lmode & C_CREATE))
399 * Create a new entry and node to stand for the given name, link it
400 * in, and return it to our caller.
402 ep = (ENTRYPTR)emalloc(sizeof(ENTRY));
403 ep->nodep = AllocConfigNode();
404 ep->name = (char *) emalloc((unsigned)(strlen(name)+1));
406 (void)strcpy(ep->name, name);
407 ep->nextp = dp->entryp;
411 } /*LocateChildNode*/
413 /*------------------------------------------------------------------------
417 * Locates the configuration tree node corresponding to the given
421 * CTREEPTR dp : Config tree node from which search is to begin.
422 * (Actually, config_root!!)
423 * char *path : Path to be searched for.
424 * int lmode : Search mode to use (C_LOCATE, C_CREATE).
427 * Ptr to located node if it existed, else
428 * Ptr to newly-created node if no match found and C_CREATE mode used,
429 * Otherwise a null pointer. Null will also be returned if the given
430 * directory node isn't really for a directory.
433 * The search is breadth-first, plucking out each piece of the path
434 * from left to right.
437 * Exits from package.
438 *------------------------------------------------------------------------*/
440 CTREEPTR LocatePathNode(dp, path, lmode)
441 register CTREEPTR dp;
447 register char *name; /*Points to start of new subdir/file in path*/
448 register char savech; /*Saves chars being murdered during search*/
451 * Skip over leading slashes.
456 while (dp != NULL && *path != '\0') {
458 * Pull off the leftmost portion of the (remaining) pathname,
459 * then search for it through all the entries for the current
460 * directory node serving as the root of the search.
463 while (*path != '\0' && *path != '/')
465 savech = *path; *path = '\0';
466 if ((lmode & C_CREATE) && (dp->type == 0))
468 * This is an unfilled non-leaf node. Mark it as being
474 * Look for the name fragment among all entries corresponding
477 dp = LocateChildNode(dp, name, lmode);
480 * Restore the char we overwrote with a null, then bump the
481 * path to the start of the next component.
490 * dp now contains the path associated with the given path, so
497 /*------------------------------------------------------------------------
501 * Builds a configuration tree from its YACC specification.
504 * FILE *f : File containing the configuration info.
507 * Value returned by the YACC parser (0 for success, else 1), or
508 * Exits if there is insufficient memory in which to build the tree.
511 * Nothing interesting.
514 * May exit from package.
515 *------------------------------------------------------------------------*/
517 int BuildConfigTree(f)
520 { /*BuildConfigTree*/
528 } /*BuildConfigTree*/
530 /*------------------------------------------------------------------------
534 * Adds an entry to the configuration tree.
537 * u_short filetype : Specifies type of file to be updated (regular
538 * file, directory, device, etc.)
539 * u_short updtspec : Specifies actions during update, if necessary.
540 * char *filename : Name of file to be updated.
541 * PROTOTYPE prototype : Prototype for filename (e.g., dev numbers,
542 * directory prefix, etc.)
543 * OWNER ownershipinfo : Ownership info for filename.
544 * MODE mode : Protection (mode) bits for filename.
548 * Error value otherwise.
549 * If there is insufficient memory to add the entry, or if invalid
550 * parameters are encountered, the entire program exits.
553 * Searches always start from the root of the configuration tree.
556 * As advertised; may exit from package.
557 *------------------------------------------------------------------------*/
559 int AddEntry(filetype, updtspec, filename, prototype, ownershipinfo, mode)
569 CTREEPTR np; /*Ptr to config tree node holding info on filename*/
570 short uid, gid; /*Uid, gid returned from validation functions*/
572 debug_message("[AddEntry] Called for filename %s", filename);
575 * Check that the given update specification is a legal one.
577 if (ValidUpdtSpec(filetype, updtspec) == FALSE)
578 fatal("** Invalid update specification for file %s", filename);
581 * Find the node corresponding to the given filename, creating one if
584 if ((np = LocatePathNode(config_root, filename, C_LOCATE|C_CREATE)) == NULL)
585 fatal("** Invalid path encountered: %s", filename);
588 * Start adding entries to np after checking for potential conflicts.
590 * Should we print out a warning if the same file appears twice even
591 * in the absence of a type confict?
593 if ((np->flag & F_TYPE) && (np->type != filetype))
594 fatal("** Type conflict for file %s", filename);
600 if ((np->flag & F_UPDT) && (np->updtspec != updtspec))
601 fatal("** Update specification conflict for file %s", filename);
606 np->updtspec |= updtspec;
609 if ((filetype == S_IFCHR)
610 || (filetype == S_IFBLK)
612 || (filetype == S_IFIFO)
618 if ((np->flag & F_PROTO) && (prototype.flag != P_NONE)) {
619 if ((prototype.flag != P_DEV)
620 || ((np->proto.info.rdev != prototype.info.rdev)))
621 fatal("** Device number conflict for device %s", filename);
624 if (prototype.flag == P_FILE)
625 fatal("** Prototype conflict for device %s", filename);
627 if (prototype.flag == P_DEV) {
629 np->proto.flag = P_DEV;
630 np->proto.info.rdev = prototype.info.rdev;
635 * File prototype, if any
637 if ((np->flag & F_PROTO) && (prototype.flag != P_NONE)) {
638 if ((prototype.flag != P_FILE)
639 || (strcmp(np->proto.info.path, prototype.info.path)))
640 fatal("** Prototype conflict for file %s", filename);
643 if (prototype.flag == P_DEV)
644 fatal("** Prototype conflict for file %s", filename);
646 if (prototype.flag == P_FILE) {
648 np->proto.flag = P_FILE;
649 np->proto.info.path =
650 emalloc((unsigned)(strlen(prototype.info.path)+1));
651 (void) strcpy(np->proto.info.path, prototype.info.path);
655 if (ownershipinfo.username != NULL) {
657 * Ownership info, if any
659 if (ValidateUserName(ownershipinfo.username, &uid) == FALSE)
660 fatal("** Unknown user %s for file %s",
661 ownershipinfo.username, filename);
663 if ((np->flag & F_UID) && (np->uid != uid))
664 fatal("** Uid conflict for file %s (new val: %d, old val: %d)",
665 filename, np->uid, uid);
670 } /*Process user ownership info*/
672 if (ownershipinfo.groupname != NULL) {
673 if (ValidateGroupName(ownershipinfo.groupname, &gid) == FALSE)
674 fatal("** Unknown group %s for file %s",
675 ownershipinfo.groupname, filename);
677 if ((np->flag & F_GID) && (np->gid != gid))
678 fatal("** Gid conflict for file %s (new val: %d, old val: %d)",
679 filename, np->gid, gid);
681 np->flag |= F_GID; np->gid = gid;
683 } /*Process group ownership info*/
685 if (mode.inherit_flag != TRUE) {
686 if (mode.modeval > (u_short)~S_IFMT)
687 fatal("** Bad mode %d for file %s", mode.modeval, filename);
688 if ((np->flag & F_MODE) && ((np->mode & ~S_IFMT) != mode.modeval))
689 fatal("** Mode conflict for file %s", filename);
692 np->mode |= mode.modeval;
694 } /*Mode inherit flag turned off*/
697 * If we reached this point, everything must have been OK
703 /*------------------------------------------------------------------------
707 * Apply the given function to each node of the configuration tree
708 * in pre-order fashion.
711 * int (*func)() : Function to apply.
717 * Nothing interesting.
720 * Whatever the given function does.
721 *------------------------------------------------------------------------*/
723 void ApplyConfigTree(func)
726 { /*ApplyConfigTree*/
728 char *path; /*Path to pass on down*/
731 * Create room for the largest path possible, and set it to the
732 * null string. This forces the application to be started at
735 path = (char *)emalloc(MAXPATHLEN+1);
738 TraverseConfigTree(config_root, path, func);
740 } /*ApplyConfigTree*/
742 /*------------------------------------------------------------------------
746 * Traverses the subtree of the configuration tree rooted at np
747 * in pre-order fashion, applying function func to each node.
750 * CTREEPTR np : Root of config tree to traverse.
751 * char *path : Path on which to start traversal.
752 * int (*func)() : Function to apply to each node.
758 * Nothing interesting.
761 * Whatever func might do.
762 *------------------------------------------------------------------------*/
764 TraverseConfigTree(np, path, func)
765 register CTREEPTR np;
769 { /*TraverseConfigTree*/
771 register char *endp; /*Marks the end of a string*/
772 register ENTRYPTR ep; /*Current entry pointer*/
773 register int len; /*Length of the pathname*/
776 * If the path is empty, start it off with "/".
780 (void) strcpy(path, "/");
783 * Apply the function to the current node.
785 (void)(*func)(np, path);
788 (void) strcpy(path, "");
791 * If we've reached a leaf node (a non-directory), start heading
794 if ((np->type) != S_IFDIR)
798 * We're currently at a directory node. For each entry in the entry
799 * list for this node, conjure up the name associated with the entry's
800 * node and call ourselves recursively. This calling sequence gives
801 * us the preorder traversal.
806 for (ep = np->entryp; ep; ep = ep->nextp) {
808 * Tack on the node's name component to the end of the path and
811 (void) strcpy(endp, ep->name);
812 TraverseConfigTree(ep->nodep, path, func);
816 * We've finished the preorder walk under this node. Terminate
817 * the path properly before returning.
821 } /*TraverseConfigTree*/