Home | History | Annotate | Line # | Download | only in makefs
walk.c revision 1.15.2.1
      1  1.15.2.1     tron /*	$NetBSD: walk.c,v 1.15.2.1 2004/06/22 07:21:29 tron Exp $	*/
      2       1.1    lukem 
      3       1.1    lukem /*
      4       1.5    lukem  * Copyright (c) 2001 Wasabi Systems, Inc.
      5       1.1    lukem  * All rights reserved.
      6       1.1    lukem  *
      7       1.1    lukem  * Written by Luke Mewburn for Wasabi Systems, Inc.
      8       1.1    lukem  *
      9       1.1    lukem  * Redistribution and use in source and binary forms, with or without
     10       1.1    lukem  * modification, are permitted provided that the following conditions
     11       1.1    lukem  * are met:
     12       1.1    lukem  * 1. Redistributions of source code must retain the above copyright
     13       1.1    lukem  *    notice, this list of conditions and the following disclaimer.
     14       1.1    lukem  * 2. Redistributions in binary form must reproduce the above copyright
     15       1.1    lukem  *    notice, this list of conditions and the following disclaimer in the
     16       1.1    lukem  *    documentation and/or other materials provided with the distribution.
     17       1.1    lukem  * 3. All advertising materials mentioning features or use of this software
     18       1.1    lukem  *    must display the following acknowledgement:
     19       1.1    lukem  *      This product includes software developed for the NetBSD Project by
     20       1.1    lukem  *      Wasabi Systems, Inc.
     21       1.1    lukem  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
     22       1.1    lukem  *    or promote products derived from this software without specific prior
     23       1.1    lukem  *    written permission.
     24       1.1    lukem  *
     25       1.1    lukem  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
     26       1.1    lukem  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     27       1.1    lukem  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     28       1.1    lukem  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
     29       1.1    lukem  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     30       1.1    lukem  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     31       1.1    lukem  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     32       1.1    lukem  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     33       1.1    lukem  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     34       1.1    lukem  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     35       1.1    lukem  * POSSIBILITY OF SUCH DAMAGE.
     36       1.1    lukem  */
     37       1.1    lukem 
     38       1.1    lukem /*
     39       1.1    lukem  * The function link_check() was inspired from NetBSD's usr.bin/du/du.c,
     40       1.1    lukem  * which has the following copyright notice:
     41       1.1    lukem  *
     42       1.1    lukem  *
     43       1.1    lukem  * Copyright (c) 1989, 1993, 1994
     44       1.1    lukem  *	The Regents of the University of California.  All rights reserved.
     45       1.1    lukem  *
     46       1.1    lukem  * This code is derived from software contributed to Berkeley by
     47       1.1    lukem  * Chris Newcomb.
     48       1.1    lukem  *
     49       1.1    lukem  * Redistribution and use in source and binary forms, with or without
     50       1.1    lukem  * modification, are permitted provided that the following conditions
     51       1.1    lukem  * are met:
     52       1.1    lukem  * 1. Redistributions of source code must retain the above copyright
     53       1.1    lukem  *    notice, this list of conditions and the following disclaimer.
     54       1.1    lukem  * 2. Redistributions in binary form must reproduce the above copyright
     55       1.1    lukem  *    notice, this list of conditions and the following disclaimer in the
     56       1.1    lukem  *    documentation and/or other materials provided with the distribution.
     57      1.14      agc  * 3. Neither the name of the University nor the names of its contributors
     58       1.1    lukem  *    may be used to endorse or promote products derived from this software
     59       1.1    lukem  *    without specific prior written permission.
     60       1.1    lukem  *
     61       1.1    lukem  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     62       1.1    lukem  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     63       1.1    lukem  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     64       1.1    lukem  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     65       1.1    lukem  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     66       1.1    lukem  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     67       1.1    lukem  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     68       1.1    lukem  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     69       1.1    lukem  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     70       1.1    lukem  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     71       1.1    lukem  * SUCH DAMAGE.
     72       1.1    lukem  */
     73       1.2    lukem 
     74  1.15.2.1     tron #if HAVE_NBTOOL_CONFIG_H
     75  1.15.2.1     tron #include "nbtool_config.h"
     76  1.15.2.1     tron #endif
     77  1.15.2.1     tron 
     78       1.2    lukem #include <sys/cdefs.h>
     79       1.8       tv #if defined(__RCSID) && !defined(__lint)
     80  1.15.2.1     tron __RCSID("$NetBSD: walk.c,v 1.15.2.1 2004/06/22 07:21:29 tron Exp $");
     81       1.2    lukem #endif	/* !__lint */
     82       1.1    lukem 
     83       1.1    lukem #include <sys/param.h>
     84       1.1    lukem 
     85       1.1    lukem #include <assert.h>
     86       1.1    lukem #include <errno.h>
     87       1.1    lukem #include <fcntl.h>
     88       1.1    lukem #include <stdio.h>
     89       1.1    lukem #include <dirent.h>
     90       1.1    lukem #include <stdlib.h>
     91       1.1    lukem #include <string.h>
     92       1.1    lukem #include <unistd.h>
     93       1.1    lukem 
     94       1.1    lukem #include "makefs.h"
     95       1.1    lukem #include "mtree.h"
     96       1.1    lukem 
     97       1.1    lukem static	void	 apply_specdir(const char *, NODE *, fsnode *);
     98       1.1    lukem static	void	 apply_specentry(const char *, NODE *, fsnode *);
     99       1.1    lukem static	fsnode	*create_fsnode(const char *, struct stat *);
    100       1.6    lukem static	fsinode	*link_check(fsinode *);
    101       1.1    lukem 
    102       1.1    lukem 
    103       1.1    lukem /*
    104       1.1    lukem  * walk_dir --
    105       1.1    lukem  *	build a tree of fsnodes from `dir', with a parent fsnode of `parent'
    106       1.1    lukem  *	(which may be NULL for the root of the tree).
    107       1.1    lukem  *	each "level" is a directory, with the "." entry guaranteed to be
    108       1.1    lukem  *	at the start of the list, and without ".." entries.
    109       1.1    lukem  */
    110       1.1    lukem fsnode *
    111       1.1    lukem walk_dir(const char *dir, fsnode *parent)
    112       1.1    lukem {
    113       1.1    lukem 	fsnode		*first, *cur, *prev;
    114       1.1    lukem 	DIR		*dirp;
    115       1.1    lukem 	struct dirent	*dent;
    116       1.1    lukem 	char		path[MAXPATHLEN + 1];
    117       1.1    lukem 	struct stat	stbuf;
    118       1.1    lukem 
    119       1.1    lukem 	assert(dir != NULL);
    120       1.1    lukem 
    121       1.1    lukem 	if (debug & DEBUG_WALK_DIR)
    122       1.1    lukem 		printf("walk_dir: %s %p\n", dir, parent);
    123       1.1    lukem 	if ((dirp = opendir(dir)) == NULL)
    124       1.1    lukem 		err(1, "Can't opendir `%s'", dir);
    125       1.1    lukem 	first = prev = NULL;
    126       1.1    lukem 	while ((dent = readdir(dirp)) != NULL) {
    127       1.1    lukem 		if (strcmp(dent->d_name, "..") == 0)
    128       1.1    lukem 			continue;
    129       1.1    lukem 		if (debug & DEBUG_WALK_DIR_NODE)
    130       1.1    lukem 			printf("scanning %s/%s\n", dir, dent->d_name);
    131       1.1    lukem 		if (snprintf(path, sizeof(path), "%s/%s", dir, dent->d_name)
    132       1.1    lukem 		    >= sizeof(path))
    133       1.1    lukem 			errx(1, "Pathname too long.");
    134       1.1    lukem 		if (lstat(path, &stbuf) == -1)
    135       1.1    lukem 			err(1, "Can't lstat `%s'", path);
    136  1.15.2.1     tron #ifdef S_ISSOCK
    137       1.1    lukem 		if (S_ISSOCK(stbuf.st_mode & S_IFMT)) {
    138       1.1    lukem 			if (debug & DEBUG_WALK_DIR_NODE)
    139       1.1    lukem 				printf("  skipping socket %s\n", path);
    140       1.1    lukem 			continue;
    141       1.1    lukem 		}
    142  1.15.2.1     tron #endif
    143       1.1    lukem 
    144       1.1    lukem 		cur = create_fsnode(dent->d_name, &stbuf);
    145       1.1    lukem 		cur->parent = parent;
    146       1.1    lukem 		if (strcmp(dent->d_name, ".") == 0) {
    147       1.1    lukem 				/* ensure "." is at the start of the list */
    148       1.1    lukem 			cur->next = first;
    149       1.1    lukem 			first = cur;
    150       1.1    lukem 			if (! prev)
    151       1.1    lukem 				prev = cur;
    152       1.1    lukem 		} else {			/* not "." */
    153       1.1    lukem 			if (prev)
    154       1.1    lukem 				prev->next = cur;
    155       1.1    lukem 			prev = cur;
    156       1.1    lukem 			if (!first)
    157       1.1    lukem 				first = cur;
    158       1.1    lukem 			if (S_ISDIR(cur->type)) {
    159       1.1    lukem 				cur->child = walk_dir(path, cur);
    160       1.1    lukem 				continue;
    161       1.1    lukem 			}
    162       1.1    lukem 		}
    163       1.6    lukem 		if (stbuf.st_nlink > 1) {
    164       1.6    lukem 			fsinode	*curino;
    165       1.6    lukem 
    166       1.6    lukem 			curino = link_check(cur->inode);
    167       1.6    lukem 			if (curino != NULL) {
    168       1.6    lukem 				free(cur->inode);
    169       1.6    lukem 				cur->inode = curino;
    170       1.6    lukem 				cur->inode->nlink++;
    171       1.6    lukem 			}
    172       1.1    lukem 		}
    173       1.1    lukem 		if (S_ISLNK(cur->type)) {
    174       1.1    lukem 			char	slink[PATH_MAX+1];
    175       1.1    lukem 			int	llen;
    176       1.1    lukem 
    177      1.13   itojun 			llen = readlink(path, slink, sizeof(slink) - 1);
    178       1.1    lukem 			if (llen == -1)
    179       1.1    lukem 				err(1, "Readlink `%s'", path);
    180       1.1    lukem 			slink[llen] = '\0';
    181       1.1    lukem 			if ((cur->symlink = strdup(slink)) == NULL)
    182       1.1    lukem 				err(1, "Memory allocation error");
    183       1.1    lukem 		}
    184       1.1    lukem 	}
    185       1.1    lukem 	for (cur = first; cur != NULL; cur = cur->next)
    186       1.1    lukem 		cur->first = first;
    187       1.1    lukem 	if (closedir(dirp) == -1)
    188       1.1    lukem 		err(1, "Can't closedir `%s'", dir);
    189       1.1    lukem 	return (first);
    190       1.1    lukem }
    191       1.1    lukem 
    192       1.1    lukem static fsnode *
    193       1.6    lukem create_fsnode(const char *name, struct stat *stbuf)
    194       1.1    lukem {
    195       1.1    lukem 	fsnode *cur;
    196       1.1    lukem 
    197       1.1    lukem 	if ((cur = calloc(1, sizeof(fsnode))) == NULL ||
    198       1.7    lukem 	    (cur->name = strdup(name)) == NULL ||
    199       1.7    lukem 	    (cur->inode = calloc(1, sizeof(fsinode))) == NULL)
    200       1.1    lukem 		err(1, "Memory allocation error");
    201       1.6    lukem 	cur->type = stbuf->st_mode & S_IFMT;
    202       1.7    lukem 	cur->inode->nlink = 1;
    203       1.7    lukem 	cur->inode->st = *stbuf;
    204       1.1    lukem 	return (cur);
    205       1.1    lukem }
    206       1.1    lukem 
    207       1.1    lukem /*
    208       1.1    lukem  * apply_specfile --
    209       1.1    lukem  *	read in the mtree(8) specfile, and apply it to the tree
    210       1.1    lukem  *	at dir,parent. parameters in parent on equivalent types
    211       1.1    lukem  *	will be changed to those found in specfile, and missing
    212       1.1    lukem  *	entries will be added.
    213       1.1    lukem  */
    214       1.1    lukem void
    215       1.1    lukem apply_specfile(const char *specfile, const char *dir, fsnode *parent)
    216       1.1    lukem {
    217       1.1    lukem 	struct timeval	 start;
    218       1.1    lukem 	FILE	*fp;
    219       1.1    lukem 	NODE	*root;
    220       1.1    lukem 
    221       1.1    lukem 	assert(specfile != NULL);
    222       1.1    lukem 	assert(parent != NULL);
    223       1.1    lukem 
    224       1.1    lukem 	if (debug & DEBUG_APPLY_SPECFILE)
    225       1.1    lukem 		printf("apply_specfile: %s, %s %p\n", specfile, dir, parent);
    226       1.1    lukem 
    227       1.1    lukem 				/* read in the specfile */
    228       1.1    lukem 	if ((fp = fopen(specfile, "r")) == NULL)
    229       1.1    lukem 		err(1, "Can't open `%s'", specfile);
    230       1.1    lukem 	TIMER_START(start);
    231       1.1    lukem 	root = spec(fp);
    232       1.1    lukem 	TIMER_RESULTS(start, "spec");
    233       1.1    lukem 	if (fclose(fp) == EOF)
    234       1.1    lukem 		err(1, "Can't close `%s'", specfile);
    235       1.1    lukem 
    236       1.1    lukem 				/* perform some sanity checks */
    237       1.1    lukem 	if (root == NULL)
    238       1.1    lukem 		errx(1, "Specfile `%s' did not contain a tree", specfile);
    239       1.1    lukem 	assert(strcmp(root->name, ".") == 0);
    240       1.1    lukem 	assert(root->type == F_DIR);
    241       1.1    lukem 
    242       1.1    lukem 				/* merge in the changes */
    243       1.1    lukem 	apply_specdir(dir, root, parent);
    244       1.1    lukem }
    245       1.1    lukem 
    246       1.1    lukem static void
    247       1.1    lukem apply_specdir(const char *dir, NODE *specnode, fsnode *dirnode)
    248       1.1    lukem {
    249       1.1    lukem 	char	 path[MAXPATHLEN + 1];
    250       1.1    lukem 	NODE	*curnode;
    251       1.1    lukem 	fsnode	*curfsnode;
    252       1.1    lukem 
    253       1.1    lukem 	assert(specnode != NULL);
    254       1.1    lukem 	assert(dirnode != NULL);
    255       1.1    lukem 
    256       1.1    lukem 	if (debug & DEBUG_APPLY_SPECFILE)
    257       1.1    lukem 		printf("apply_specdir: %s %p %p\n", dir, specnode, dirnode);
    258       1.1    lukem 
    259       1.1    lukem 	if (specnode->type != F_DIR)
    260       1.1    lukem 		errx(1, "Specfile node `%s/%s' is not a directory",
    261       1.1    lukem 		    dir, specnode->name);
    262       1.1    lukem 	if (dirnode->type != S_IFDIR)
    263       1.1    lukem 		errx(1, "Directory node `%s/%s' is not a directory",
    264       1.1    lukem 		    dir, dirnode->name);
    265       1.1    lukem 
    266       1.1    lukem 	apply_specentry(dir, specnode, dirnode);
    267       1.1    lukem 
    268       1.1    lukem 			/* now walk specnode->child matching up with dirnode */
    269       1.1    lukem 	for (curnode = specnode->child; curnode != NULL;
    270       1.1    lukem 	    curnode = curnode->next) {
    271       1.1    lukem 		if (debug & DEBUG_APPLY_SPECENTRY)
    272       1.1    lukem 			printf("apply_specdir:  spec %s\n",
    273       1.1    lukem 			    curnode->name);
    274       1.1    lukem 		for (curfsnode = dirnode->next; curfsnode != NULL;
    275       1.1    lukem 		    curfsnode = curfsnode->next) {
    276       1.3    lukem #if 0	/* too verbose for now */
    277       1.1    lukem 			if (debug & DEBUG_APPLY_SPECENTRY)
    278       1.1    lukem 				printf("apply_specdir:  dirent %s\n",
    279       1.1    lukem 				    curfsnode->name);
    280       1.3    lukem #endif
    281       1.1    lukem 			if (strcmp(curnode->name, curfsnode->name) == 0)
    282       1.1    lukem 				break;
    283       1.1    lukem 		}
    284       1.9    lukem 		if (snprintf(path, sizeof(path), "%s/%s",
    285       1.9    lukem 		    dir, curnode->name) >= sizeof(path))
    286       1.9    lukem 			errx(1, "Pathname too long.");
    287       1.1    lukem 		if (curfsnode == NULL) {	/* need new entry */
    288       1.1    lukem 			struct stat	stbuf;
    289       1.1    lukem 
    290       1.9    lukem 					    /*
    291       1.9    lukem 					     * don't add optional spec entries
    292       1.9    lukem 					     * that lack an existing fs entry
    293       1.9    lukem 					     */
    294       1.9    lukem 			if ((curnode->flags & F_OPT) &&
    295       1.9    lukem 			    lstat(path, &stbuf) == -1)
    296       1.9    lukem 					continue;
    297       1.9    lukem 
    298       1.1    lukem 					/* check that enough info is provided */
    299       1.1    lukem #define NODETEST(t, m)							\
    300       1.1    lukem 			if (!(t))					\
    301       1.9    lukem 				errx(1, "`%s': %s not provided", path, m)
    302       1.1    lukem 			NODETEST(curnode->flags & F_TYPE, "type");
    303       1.1    lukem 			NODETEST(curnode->flags & F_MODE, "mode");
    304       1.1    lukem 				/* XXX: require F_TIME ? */
    305       1.1    lukem 			NODETEST(curnode->flags & F_GID ||
    306       1.1    lukem 			    curnode->flags & F_GNAME, "group");
    307       1.1    lukem 			NODETEST(curnode->flags & F_UID ||
    308       1.1    lukem 			    curnode->flags & F_UNAME, "user");
    309       1.1    lukem 			if (curnode->type == F_BLOCK || curnode->type == F_CHAR)
    310       1.1    lukem 				NODETEST(curnode->flags & F_DEV,
    311       1.1    lukem 				    "device number");
    312       1.1    lukem #undef NODETEST
    313       1.1    lukem 
    314       1.1    lukem 			if (debug & DEBUG_APPLY_SPECFILE)
    315       1.1    lukem 				printf("apply_specdir: adding %s\n",
    316       1.1    lukem 				    curnode->name);
    317       1.1    lukem 					/* build minimal fsnode */
    318       1.1    lukem 			memset(&stbuf, 0, sizeof(stbuf));
    319       1.1    lukem 			stbuf.st_mode = nodetoino(curnode->type);
    320       1.6    lukem 			stbuf.st_nlink = 1;
    321       1.1    lukem 			stbuf.st_mtime = stbuf.st_atime =
    322       1.1    lukem 			    stbuf.st_ctime = start_time.tv_sec;
    323       1.8       tv #if HAVE_STRUCT_STAT_ST_MTIMENSEC
    324       1.1    lukem 			stbuf.st_mtimensec = stbuf.st_atimensec =
    325       1.1    lukem 			    stbuf.st_ctimensec = start_time.tv_nsec;
    326       1.8       tv #endif
    327       1.1    lukem 			curfsnode = create_fsnode(curnode->name, &stbuf);
    328       1.1    lukem 			curfsnode->parent = dirnode->parent;
    329       1.1    lukem 			curfsnode->first = dirnode;
    330       1.1    lukem 			curfsnode->next = dirnode->next;
    331       1.1    lukem 			dirnode->next = curfsnode;
    332       1.1    lukem 			if (curfsnode->type == S_IFDIR) {
    333       1.1    lukem 					/* for dirs, make "." entry as well */
    334       1.1    lukem 				curfsnode->child = create_fsnode(".", &stbuf);
    335       1.1    lukem 				curfsnode->child->parent = curfsnode;
    336       1.1    lukem 				curfsnode->child->first = curfsnode->child;
    337       1.1    lukem 			}
    338       1.1    lukem 			if (curfsnode->type == S_IFLNK) {
    339       1.3    lukem 				assert(curnode->slink != NULL);
    340       1.1    lukem 					/* for symlinks, copy the target */
    341       1.1    lukem 				if ((curfsnode->symlink =
    342       1.1    lukem 				    strdup(curnode->slink)) == NULL)
    343       1.1    lukem 					err(1, "Memory allocation error");
    344       1.1    lukem 			}
    345       1.1    lukem 		}
    346       1.1    lukem 		apply_specentry(dir, curnode, curfsnode);
    347       1.1    lukem 		if (curnode->type == F_DIR) {
    348       1.1    lukem 			if (curfsnode->type != S_IFDIR)
    349       1.9    lukem 				errx(1, "`%s' is not a directory", path);
    350       1.1    lukem 			assert (curfsnode->child != NULL);
    351       1.1    lukem 			apply_specdir(path, curnode, curfsnode->child);
    352       1.1    lukem 		}
    353       1.1    lukem 	}
    354       1.1    lukem }
    355       1.1    lukem 
    356       1.1    lukem static void
    357       1.1    lukem apply_specentry(const char *dir, NODE *specnode, fsnode *dirnode)
    358       1.1    lukem {
    359       1.1    lukem 
    360       1.1    lukem 	assert(specnode != NULL);
    361       1.1    lukem 	assert(dirnode != NULL);
    362       1.1    lukem 
    363       1.1    lukem 	if (nodetoino(specnode->type) != dirnode->type)
    364       1.1    lukem 		errx(1, "`%s/%s' type mismatch: specfile %s, tree %s",
    365       1.1    lukem 		    dir, specnode->name, inode_type(nodetoino(specnode->type)),
    366       1.1    lukem 		    inode_type(dirnode->type));
    367       1.1    lukem 
    368       1.1    lukem 	if (debug & DEBUG_APPLY_SPECENTRY)
    369       1.1    lukem 		printf("apply_specentry: %s/%s\n", dir, dirnode->name);
    370       1.1    lukem 
    371       1.1    lukem #define ASEPRINT(t, b, o, n) \
    372       1.1    lukem 		if (debug & DEBUG_APPLY_SPECENTRY) \
    373       1.1    lukem 			printf("\t\t\tchanging %s from " b " to " b "\n", \
    374       1.1    lukem 			    t, o, n)
    375       1.1    lukem 
    376       1.1    lukem 	if (specnode->flags & (F_GID | F_GNAME)) {
    377       1.1    lukem 		ASEPRINT("gid", "%d",
    378       1.6    lukem 		    dirnode->inode->st.st_gid, specnode->st_gid);
    379       1.6    lukem 		dirnode->inode->st.st_gid = specnode->st_gid;
    380       1.1    lukem 	}
    381       1.1    lukem 	if (specnode->flags & F_MODE) {
    382       1.1    lukem 		ASEPRINT("mode", "%#o",
    383       1.6    lukem 		    dirnode->inode->st.st_mode & ALLPERMS, specnode->st_mode);
    384       1.6    lukem 		dirnode->inode->st.st_mode &= ~ALLPERMS;
    385       1.6    lukem 		dirnode->inode->st.st_mode |= (specnode->st_mode & ALLPERMS);
    386       1.1    lukem 	}
    387       1.1    lukem 		/* XXX: ignoring F_NLINK for now */
    388       1.1    lukem 	if (specnode->flags & F_SIZE) {
    389       1.1    lukem 		ASEPRINT("size", "%lld",
    390       1.6    lukem 		    (long long)dirnode->inode->st.st_size,
    391       1.1    lukem 		    (long long)specnode->st_size);
    392       1.6    lukem 		dirnode->inode->st.st_size = specnode->st_size;
    393       1.1    lukem 	}
    394       1.1    lukem 	if (specnode->flags & F_SLINK) {
    395       1.1    lukem 		assert(dirnode->symlink != NULL);
    396       1.1    lukem 		assert(specnode->slink != NULL);
    397       1.1    lukem 		ASEPRINT("symlink", "%s", dirnode->symlink, specnode->slink);
    398       1.1    lukem 		free(dirnode->symlink);
    399       1.1    lukem 		if ((dirnode->symlink = strdup(specnode->slink)) == NULL)
    400       1.1    lukem 			err(1, "Memory allocation error");
    401       1.1    lukem 	}
    402       1.1    lukem 	if (specnode->flags & F_TIME) {
    403       1.1    lukem 		ASEPRINT("time", "%ld",
    404       1.6    lukem 		    (long)dirnode->inode->st.st_mtime,
    405       1.8       tv 		    (long)specnode->st_mtimespec.tv_sec);
    406       1.8       tv 		dirnode->inode->st.st_mtime =		specnode->st_mtimespec.tv_sec;
    407       1.8       tv 		dirnode->inode->st.st_atime =		specnode->st_mtimespec.tv_sec;
    408       1.8       tv 		dirnode->inode->st.st_ctime =		start_time.tv_sec;
    409       1.8       tv #if HAVE_STRUCT_STAT_ST_MTIMENSEC
    410  1.15.2.1     tron 		dirnode->inode->st.st_mtimensec =	specnode->st_mtimespec.tv_nsec;
    411  1.15.2.1     tron 		dirnode->inode->st.st_atimensec =	specnode->st_mtimespec.tv_nsec;
    412       1.6    lukem 		dirnode->inode->st.st_ctimensec =	start_time.tv_nsec;
    413       1.8       tv #endif
    414       1.1    lukem 	}
    415       1.1    lukem 	if (specnode->flags & (F_UID | F_UNAME)) {
    416       1.1    lukem 		ASEPRINT("uid", "%d",
    417       1.6    lukem 		    dirnode->inode->st.st_uid, specnode->st_uid);
    418       1.6    lukem 		dirnode->inode->st.st_uid = specnode->st_uid;
    419       1.1    lukem 	}
    420       1.8       tv #if HAVE_STRUCT_STAT_ST_FLAGS
    421       1.1    lukem 	if (specnode->flags & F_FLAGS) {
    422       1.1    lukem 		ASEPRINT("flags", "%#lX",
    423      1.11      uwe 		    (unsigned long)dirnode->inode->st.st_flags,
    424      1.11      uwe 		    (unsigned long)specnode->st_flags);
    425       1.6    lukem 		dirnode->inode->st.st_flags = specnode->st_flags;
    426       1.1    lukem 	}
    427       1.8       tv #endif
    428       1.1    lukem 	if (specnode->flags & F_DEV) {
    429       1.1    lukem 		ASEPRINT("rdev", "%#x",
    430       1.6    lukem 		    dirnode->inode->st.st_rdev, specnode->st_rdev);
    431       1.6    lukem 		dirnode->inode->st.st_rdev = specnode->st_rdev;
    432       1.1    lukem 	}
    433       1.1    lukem #undef ASEPRINT
    434      1.12  thorpej 
    435      1.12  thorpej 	dirnode->flags |= FSNODE_F_HASSPEC;
    436       1.1    lukem }
    437       1.1    lukem 
    438       1.1    lukem 
    439       1.1    lukem /*
    440       1.1    lukem  * dump_fsnodes --
    441       1.1    lukem  *	dump the fsnodes from `cur', based in the directory `dir'
    442       1.1    lukem  */
    443       1.1    lukem void
    444       1.1    lukem dump_fsnodes(const char *dir, fsnode *root)
    445       1.1    lukem {
    446       1.1    lukem 	fsnode	*cur;
    447       1.1    lukem 	char	path[MAXPATHLEN + 1];
    448       1.1    lukem 
    449       1.1    lukem 	assert (dir != NULL);
    450       1.1    lukem 	printf("dump_fsnodes: %s %p\n", dir, root);
    451       1.1    lukem 	for (cur = root; cur != NULL; cur = cur->next) {
    452       1.1    lukem 		if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name)
    453       1.1    lukem 		    >= sizeof(path))
    454       1.1    lukem 			errx(1, "Pathname too long.");
    455       1.1    lukem 
    456       1.1    lukem 		if (debug & DEBUG_DUMP_FSNODES_VERBOSE)
    457       1.1    lukem 			printf("cur=%8p parent=%8p first=%8p ",
    458       1.1    lukem 			    cur, cur->parent, cur->first);
    459       1.1    lukem 		printf("%7s: %s", inode_type(cur->type), path);
    460       1.1    lukem 		if (S_ISLNK(cur->type)) {
    461       1.1    lukem 			assert(cur->symlink != NULL);
    462       1.1    lukem 			printf(" -> %s", cur->symlink);
    463       1.1    lukem 		} else {
    464       1.1    lukem 			assert (cur->symlink == NULL);
    465       1.1    lukem 		}
    466       1.6    lukem 		if (cur->inode->nlink > 1)
    467       1.6    lukem 			printf(", nlinks=%d", cur->inode->nlink);
    468       1.1    lukem 		putchar('\n');
    469       1.1    lukem 
    470       1.1    lukem 		if (cur->child) {
    471       1.1    lukem 			assert (cur->type == S_IFDIR);
    472       1.1    lukem 			dump_fsnodes(path, cur->child);
    473       1.1    lukem 		}
    474       1.1    lukem 	}
    475       1.1    lukem 	printf("dump_fsnodes: finished %s\n", dir);
    476       1.1    lukem }
    477       1.1    lukem 
    478       1.1    lukem 
    479       1.1    lukem /*
    480       1.1    lukem  * inode_type --
    481       1.1    lukem  *	for a given inode type `mode', return a descriptive string.
    482       1.1    lukem  *	for most cases, uses inotype() from mtree/misc.c
    483       1.1    lukem  */
    484       1.1    lukem const char *
    485       1.1    lukem inode_type(mode_t mode)
    486       1.1    lukem {
    487       1.1    lukem 
    488       1.3    lukem 	if (S_ISLNK(mode))
    489       1.1    lukem 		return ("symlink");	/* inotype() returns "link"...  */
    490       1.1    lukem 	return (inotype(mode));
    491       1.1    lukem }
    492       1.1    lukem 
    493       1.1    lukem 
    494       1.1    lukem /*
    495       1.1    lukem  * link_check --
    496       1.1    lukem  *	return pointer to fsnode matching `entry's st_ino & st_dev if it exists,
    497       1.1    lukem  *	otherwise add `entry' to table and return NULL
    498       1.1    lukem  */
    499       1.6    lukem static fsinode *
    500       1.6    lukem link_check(fsinode *entry)
    501       1.1    lukem {
    502       1.6    lukem 	static	struct dupnode {
    503       1.6    lukem 		uint32_t	dev;
    504       1.6    lukem 		uint32_t	ino;
    505       1.6    lukem 		fsinode		*dup;
    506      1.15   itojun 	} *dups, *newdups;
    507       1.1    lukem 	static	int	ndups, maxdups;
    508       1.1    lukem 
    509       1.1    lukem 	int	i;
    510       1.1    lukem 
    511       1.1    lukem 	assert (entry != NULL);
    512       1.1    lukem 
    513       1.1    lukem 		/* XXX; maybe traverse in reverse for speed? */
    514       1.1    lukem 	for (i = 0; i < ndups; i++) {
    515       1.6    lukem 		if (dups[i].dev == entry->st.st_dev &&
    516       1.6    lukem 		    dups[i].ino == entry->st.st_ino) {
    517       1.1    lukem 			if (debug & DEBUG_WALK_DIR_LINKCHECK)
    518       1.6    lukem 				printf("link_check: found [%d,%d]\n",
    519       1.6    lukem 				    entry->st.st_dev, entry->st.st_ino);
    520       1.1    lukem 			return (dups[i].dup);
    521       1.1    lukem 		}
    522       1.1    lukem 	}
    523       1.1    lukem 
    524       1.1    lukem 	if (debug & DEBUG_WALK_DIR_LINKCHECK)
    525       1.6    lukem 		printf("link_check: no match for [%d, %d]\n",
    526       1.6    lukem 		    entry->st.st_dev, entry->st.st_ino);
    527       1.1    lukem 	if (ndups == maxdups) {
    528      1.15   itojun 		if ((newdups = realloc(dups, sizeof(struct dupnode) * (maxdups + 128)))
    529       1.6    lukem 		    == NULL)
    530       1.1    lukem 			err(1, "Memory allocation error");
    531      1.15   itojun 		dups = newdups;
    532      1.15   itojun 		maxdups += 128;
    533       1.1    lukem 	}
    534       1.6    lukem 	dups[ndups].dev = entry->st.st_dev;
    535       1.6    lukem 	dups[ndups].ino = entry->st.st_ino;
    536       1.1    lukem 	dups[ndups].dup = entry;
    537       1.1    lukem 	ndups++;
    538       1.1    lukem 
    539       1.1    lukem 	return (NULL);
    540       1.1    lukem }
    541