xref: /minix3/usr.sbin/makefs/walk.c (revision 9f988b79349f9b89ecc822458c30ec8897558560)
1*9f988b79SJean-Baptiste Boric /*	$NetBSD: walk.c,v 1.28 2013/02/03 06:16:53 christos Exp $	*/
2*9f988b79SJean-Baptiste Boric 
3*9f988b79SJean-Baptiste Boric /*
4*9f988b79SJean-Baptiste Boric  * Copyright (c) 2001 Wasabi Systems, Inc.
5*9f988b79SJean-Baptiste Boric  * All rights reserved.
6*9f988b79SJean-Baptiste Boric  *
7*9f988b79SJean-Baptiste Boric  * Written by Luke Mewburn for Wasabi Systems, Inc.
8*9f988b79SJean-Baptiste Boric  *
9*9f988b79SJean-Baptiste Boric  * Redistribution and use in source and binary forms, with or without
10*9f988b79SJean-Baptiste Boric  * modification, are permitted provided that the following conditions
11*9f988b79SJean-Baptiste Boric  * are met:
12*9f988b79SJean-Baptiste Boric  * 1. Redistributions of source code must retain the above copyright
13*9f988b79SJean-Baptiste Boric  *    notice, this list of conditions and the following disclaimer.
14*9f988b79SJean-Baptiste Boric  * 2. Redistributions in binary form must reproduce the above copyright
15*9f988b79SJean-Baptiste Boric  *    notice, this list of conditions and the following disclaimer in the
16*9f988b79SJean-Baptiste Boric  *    documentation and/or other materials provided with the distribution.
17*9f988b79SJean-Baptiste Boric  * 3. All advertising materials mentioning features or use of this software
18*9f988b79SJean-Baptiste Boric  *    must display the following acknowledgement:
19*9f988b79SJean-Baptiste Boric  *      This product includes software developed for the NetBSD Project by
20*9f988b79SJean-Baptiste Boric  *      Wasabi Systems, Inc.
21*9f988b79SJean-Baptiste Boric  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22*9f988b79SJean-Baptiste Boric  *    or promote products derived from this software without specific prior
23*9f988b79SJean-Baptiste Boric  *    written permission.
24*9f988b79SJean-Baptiste Boric  *
25*9f988b79SJean-Baptiste Boric  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26*9f988b79SJean-Baptiste Boric  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27*9f988b79SJean-Baptiste Boric  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28*9f988b79SJean-Baptiste Boric  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29*9f988b79SJean-Baptiste Boric  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30*9f988b79SJean-Baptiste Boric  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31*9f988b79SJean-Baptiste Boric  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32*9f988b79SJean-Baptiste Boric  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33*9f988b79SJean-Baptiste Boric  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34*9f988b79SJean-Baptiste Boric  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35*9f988b79SJean-Baptiste Boric  * POSSIBILITY OF SUCH DAMAGE.
36*9f988b79SJean-Baptiste Boric  */
37*9f988b79SJean-Baptiste Boric 
38*9f988b79SJean-Baptiste Boric #if HAVE_NBTOOL_CONFIG_H
39*9f988b79SJean-Baptiste Boric #include "nbtool_config.h"
40*9f988b79SJean-Baptiste Boric #endif
41*9f988b79SJean-Baptiste Boric 
42*9f988b79SJean-Baptiste Boric #include <sys/cdefs.h>
43*9f988b79SJean-Baptiste Boric #if defined(__RCSID) && !defined(__lint)
44*9f988b79SJean-Baptiste Boric __RCSID("$NetBSD: walk.c,v 1.28 2013/02/03 06:16:53 christos Exp $");
45*9f988b79SJean-Baptiste Boric #endif	/* !__lint */
46*9f988b79SJean-Baptiste Boric 
47*9f988b79SJean-Baptiste Boric #include <sys/param.h>
48*9f988b79SJean-Baptiste Boric #include <sys/stat.h>
49*9f988b79SJean-Baptiste Boric 
50*9f988b79SJean-Baptiste Boric #include <assert.h>
51*9f988b79SJean-Baptiste Boric #include <errno.h>
52*9f988b79SJean-Baptiste Boric #include <fcntl.h>
53*9f988b79SJean-Baptiste Boric #include <stdio.h>
54*9f988b79SJean-Baptiste Boric #include <dirent.h>
55*9f988b79SJean-Baptiste Boric #include <stdlib.h>
56*9f988b79SJean-Baptiste Boric #include <string.h>
57*9f988b79SJean-Baptiste Boric #include <unistd.h>
58*9f988b79SJean-Baptiste Boric #include <util.h>
59*9f988b79SJean-Baptiste Boric 
60*9f988b79SJean-Baptiste Boric #include "makefs.h"
61*9f988b79SJean-Baptiste Boric #include "mtree.h"
62*9f988b79SJean-Baptiste Boric 
63*9f988b79SJean-Baptiste Boric static	void	 apply_specdir(const char *, NODE *, fsnode *, int);
64*9f988b79SJean-Baptiste Boric static	void	 apply_specentry(const char *, NODE *, fsnode *);
65*9f988b79SJean-Baptiste Boric static	fsnode	*create_fsnode(const char *, const char *, const char *,
66*9f988b79SJean-Baptiste Boric 			       struct stat *);
67*9f988b79SJean-Baptiste Boric static	fsinode	*link_check(fsinode *);
68*9f988b79SJean-Baptiste Boric 
69*9f988b79SJean-Baptiste Boric 
70*9f988b79SJean-Baptiste Boric /*
71*9f988b79SJean-Baptiste Boric  * walk_dir --
72*9f988b79SJean-Baptiste Boric  *	build a tree of fsnodes from `root' and `dir', with a parent
73*9f988b79SJean-Baptiste Boric  *	fsnode of `parent' (which may be NULL for the root of the tree).
74*9f988b79SJean-Baptiste Boric  *	append the tree to a fsnode of `join' if it is not NULL.
75*9f988b79SJean-Baptiste Boric  *	each "level" is a directory, with the "." entry guaranteed to be
76*9f988b79SJean-Baptiste Boric  *	at the start of the list, and without ".." entries.
77*9f988b79SJean-Baptiste Boric  */
78*9f988b79SJean-Baptiste Boric fsnode *
walk_dir(const char * root,const char * dir,fsnode * parent,fsnode * join,int replace)79*9f988b79SJean-Baptiste Boric walk_dir(const char *root, const char *dir, fsnode *parent, fsnode *join,
80*9f988b79SJean-Baptiste Boric     int replace)
81*9f988b79SJean-Baptiste Boric {
82*9f988b79SJean-Baptiste Boric 	fsnode		*first, *cur, *prev, *last;
83*9f988b79SJean-Baptiste Boric 	DIR		*dirp;
84*9f988b79SJean-Baptiste Boric 	struct dirent	*dent;
85*9f988b79SJean-Baptiste Boric 	char		path[MAXPATHLEN + 1];
86*9f988b79SJean-Baptiste Boric 	struct stat	stbuf;
87*9f988b79SJean-Baptiste Boric 	char		*name, *rp;
88*9f988b79SJean-Baptiste Boric 	int		dot, len;
89*9f988b79SJean-Baptiste Boric 
90*9f988b79SJean-Baptiste Boric 	assert(root != NULL);
91*9f988b79SJean-Baptiste Boric 	assert(dir != NULL);
92*9f988b79SJean-Baptiste Boric 
93*9f988b79SJean-Baptiste Boric 	len = snprintf(path, sizeof(path), "%s/%s", root, dir);
94*9f988b79SJean-Baptiste Boric 	if (len >= (int)sizeof(path))
95*9f988b79SJean-Baptiste Boric 		errx(1, "Pathname too long.");
96*9f988b79SJean-Baptiste Boric 	if (debug & DEBUG_WALK_DIR)
97*9f988b79SJean-Baptiste Boric 		printf("walk_dir: %s %p\n", path, parent);
98*9f988b79SJean-Baptiste Boric 	if ((dirp = opendir(path)) == NULL)
99*9f988b79SJean-Baptiste Boric 		err(1, "Can't opendir `%s'", path);
100*9f988b79SJean-Baptiste Boric 	rp = path + strlen(root) + 1;
101*9f988b79SJean-Baptiste Boric 	if (join != NULL) {
102*9f988b79SJean-Baptiste Boric 		first = cur = join;
103*9f988b79SJean-Baptiste Boric 		while (cur->next != NULL)
104*9f988b79SJean-Baptiste Boric 			cur = cur->next;
105*9f988b79SJean-Baptiste Boric 		prev = last = cur;
106*9f988b79SJean-Baptiste Boric 	} else
107*9f988b79SJean-Baptiste Boric 		last = first = prev = NULL;
108*9f988b79SJean-Baptiste Boric 	while ((dent = readdir(dirp)) != NULL) {
109*9f988b79SJean-Baptiste Boric 		name = dent->d_name;
110*9f988b79SJean-Baptiste Boric 		dot = 0;
111*9f988b79SJean-Baptiste Boric 		if (name[0] == '.')
112*9f988b79SJean-Baptiste Boric 			switch (name[1]) {
113*9f988b79SJean-Baptiste Boric 			case '\0':	/* "." */
114*9f988b79SJean-Baptiste Boric 				if (join != NULL)
115*9f988b79SJean-Baptiste Boric 					continue;
116*9f988b79SJean-Baptiste Boric 				dot = 1;
117*9f988b79SJean-Baptiste Boric 				break;
118*9f988b79SJean-Baptiste Boric 			case '.':	/* ".." */
119*9f988b79SJean-Baptiste Boric 				if (name[2] == '\0')
120*9f988b79SJean-Baptiste Boric 					continue;
121*9f988b79SJean-Baptiste Boric 				/* FALLTHROUGH */
122*9f988b79SJean-Baptiste Boric 			default:
123*9f988b79SJean-Baptiste Boric 				dot = 0;
124*9f988b79SJean-Baptiste Boric 			}
125*9f988b79SJean-Baptiste Boric 		if (debug & DEBUG_WALK_DIR_NODE)
126*9f988b79SJean-Baptiste Boric 			printf("scanning %s/%s/%s\n", root, dir, name);
127*9f988b79SJean-Baptiste Boric 		if (snprintf(path + len, sizeof(path) - len, "/%s", name) >=
128*9f988b79SJean-Baptiste Boric 		    (int)sizeof(path) - len)
129*9f988b79SJean-Baptiste Boric 			errx(1, "Pathname too long.");
130*9f988b79SJean-Baptiste Boric 		if (lstat(path, &stbuf) == -1)
131*9f988b79SJean-Baptiste Boric 			err(1, "Can't lstat `%s'", path);
132*9f988b79SJean-Baptiste Boric #ifdef S_ISSOCK
133*9f988b79SJean-Baptiste Boric 		if (S_ISSOCK(stbuf.st_mode & S_IFMT)) {
134*9f988b79SJean-Baptiste Boric 			if (debug & DEBUG_WALK_DIR_NODE)
135*9f988b79SJean-Baptiste Boric 				printf("  skipping socket %s\n", path);
136*9f988b79SJean-Baptiste Boric 			continue;
137*9f988b79SJean-Baptiste Boric 		}
138*9f988b79SJean-Baptiste Boric #endif
139*9f988b79SJean-Baptiste Boric 
140*9f988b79SJean-Baptiste Boric 		if (join != NULL) {
141*9f988b79SJean-Baptiste Boric 			cur = join->next;
142*9f988b79SJean-Baptiste Boric 			for (;;) {
143*9f988b79SJean-Baptiste Boric 				if (cur == NULL || strcmp(cur->name, name) == 0)
144*9f988b79SJean-Baptiste Boric 					break;
145*9f988b79SJean-Baptiste Boric 				if (cur == last) {
146*9f988b79SJean-Baptiste Boric 					cur = NULL;
147*9f988b79SJean-Baptiste Boric 					break;
148*9f988b79SJean-Baptiste Boric 				}
149*9f988b79SJean-Baptiste Boric 				cur = cur->next;
150*9f988b79SJean-Baptiste Boric 			}
151*9f988b79SJean-Baptiste Boric 			if (cur != NULL) {
152*9f988b79SJean-Baptiste Boric 				if (S_ISDIR(cur->type) &&
153*9f988b79SJean-Baptiste Boric 				    S_ISDIR(stbuf.st_mode)) {
154*9f988b79SJean-Baptiste Boric 					if (debug & DEBUG_WALK_DIR_NODE)
155*9f988b79SJean-Baptiste Boric 						printf("merging %s with %p\n",
156*9f988b79SJean-Baptiste Boric 						    path, cur->child);
157*9f988b79SJean-Baptiste Boric 					cur->child = walk_dir(root, rp, cur,
158*9f988b79SJean-Baptiste Boric 					    cur->child, replace);
159*9f988b79SJean-Baptiste Boric 					continue;
160*9f988b79SJean-Baptiste Boric 				}
161*9f988b79SJean-Baptiste Boric 				if (!replace)
162*9f988b79SJean-Baptiste Boric 					errx(1, "Can't merge %s `%s' with "
163*9f988b79SJean-Baptiste Boric 					    "existing %s",
164*9f988b79SJean-Baptiste Boric 					    inode_type(stbuf.st_mode), path,
165*9f988b79SJean-Baptiste Boric 					    inode_type(cur->type));
166*9f988b79SJean-Baptiste Boric 				else {
167*9f988b79SJean-Baptiste Boric 					if (debug & DEBUG_WALK_DIR_NODE)
168*9f988b79SJean-Baptiste Boric 						printf("replacing %s %s\n",
169*9f988b79SJean-Baptiste Boric 						    inode_type(stbuf.st_mode),
170*9f988b79SJean-Baptiste Boric 						    path);
171*9f988b79SJean-Baptiste Boric 					if (cur == join->next)
172*9f988b79SJean-Baptiste Boric 						join->next = cur->next;
173*9f988b79SJean-Baptiste Boric 					else {
174*9f988b79SJean-Baptiste Boric 						fsnode *p;
175*9f988b79SJean-Baptiste Boric 						for (p = join->next;
176*9f988b79SJean-Baptiste Boric 						    p->next != cur; p = p->next)
177*9f988b79SJean-Baptiste Boric 							continue;
178*9f988b79SJean-Baptiste Boric 						p->next = cur->next;
179*9f988b79SJean-Baptiste Boric 					}
180*9f988b79SJean-Baptiste Boric 					free(cur);
181*9f988b79SJean-Baptiste Boric 				}
182*9f988b79SJean-Baptiste Boric 			}
183*9f988b79SJean-Baptiste Boric 		}
184*9f988b79SJean-Baptiste Boric 
185*9f988b79SJean-Baptiste Boric 		cur = create_fsnode(root, dir, name, &stbuf);
186*9f988b79SJean-Baptiste Boric 		cur->parent = parent;
187*9f988b79SJean-Baptiste Boric 		if (dot) {
188*9f988b79SJean-Baptiste Boric 				/* ensure "." is at the start of the list */
189*9f988b79SJean-Baptiste Boric 			cur->next = first;
190*9f988b79SJean-Baptiste Boric 			first = cur;
191*9f988b79SJean-Baptiste Boric 			if (! prev)
192*9f988b79SJean-Baptiste Boric 				prev = cur;
193*9f988b79SJean-Baptiste Boric 			cur->first = first;
194*9f988b79SJean-Baptiste Boric 		} else {			/* not "." */
195*9f988b79SJean-Baptiste Boric 			if (prev)
196*9f988b79SJean-Baptiste Boric 				prev->next = cur;
197*9f988b79SJean-Baptiste Boric 			prev = cur;
198*9f988b79SJean-Baptiste Boric 			if (!first)
199*9f988b79SJean-Baptiste Boric 				first = cur;
200*9f988b79SJean-Baptiste Boric 			cur->first = first;
201*9f988b79SJean-Baptiste Boric 			if (S_ISDIR(cur->type)) {
202*9f988b79SJean-Baptiste Boric 				cur->child = walk_dir(root, rp, cur, NULL,
203*9f988b79SJean-Baptiste Boric 				    replace);
204*9f988b79SJean-Baptiste Boric 				continue;
205*9f988b79SJean-Baptiste Boric 			}
206*9f988b79SJean-Baptiste Boric 		}
207*9f988b79SJean-Baptiste Boric 		if (stbuf.st_nlink > 1) {
208*9f988b79SJean-Baptiste Boric 			fsinode	*curino;
209*9f988b79SJean-Baptiste Boric 
210*9f988b79SJean-Baptiste Boric 			curino = link_check(cur->inode);
211*9f988b79SJean-Baptiste Boric 			if (curino != NULL) {
212*9f988b79SJean-Baptiste Boric 				free(cur->inode);
213*9f988b79SJean-Baptiste Boric 				cur->inode = curino;
214*9f988b79SJean-Baptiste Boric 				cur->inode->nlink++;
215*9f988b79SJean-Baptiste Boric 				if (debug & DEBUG_WALK_DIR_LINKCHECK)
216*9f988b79SJean-Baptiste Boric 					printf("link_check: found [%llu, %llu]\n",
217*9f988b79SJean-Baptiste Boric 					    (unsigned long long)curino->st.st_dev,
218*9f988b79SJean-Baptiste Boric 					    (unsigned long long)curino->st.st_ino);
219*9f988b79SJean-Baptiste Boric 			}
220*9f988b79SJean-Baptiste Boric 		}
221*9f988b79SJean-Baptiste Boric 		if (S_ISLNK(cur->type)) {
222*9f988b79SJean-Baptiste Boric 			char	slink[PATH_MAX+1];
223*9f988b79SJean-Baptiste Boric 			int	llen;
224*9f988b79SJean-Baptiste Boric 
225*9f988b79SJean-Baptiste Boric 			llen = readlink(path, slink, sizeof(slink) - 1);
226*9f988b79SJean-Baptiste Boric 			if (llen == -1)
227*9f988b79SJean-Baptiste Boric 				err(1, "Readlink `%s'", path);
228*9f988b79SJean-Baptiste Boric 			slink[llen] = '\0';
229*9f988b79SJean-Baptiste Boric 			cur->symlink = estrdup(slink);
230*9f988b79SJean-Baptiste Boric 		}
231*9f988b79SJean-Baptiste Boric 	}
232*9f988b79SJean-Baptiste Boric 	assert(first != NULL);
233*9f988b79SJean-Baptiste Boric 	if (join == NULL)
234*9f988b79SJean-Baptiste Boric 		for (cur = first->next; cur != NULL; cur = cur->next)
235*9f988b79SJean-Baptiste Boric 			cur->first = first;
236*9f988b79SJean-Baptiste Boric 	if (closedir(dirp) == -1)
237*9f988b79SJean-Baptiste Boric 		err(1, "Can't closedir `%s/%s'", root, dir);
238*9f988b79SJean-Baptiste Boric 	return (first);
239*9f988b79SJean-Baptiste Boric }
240*9f988b79SJean-Baptiste Boric 
241*9f988b79SJean-Baptiste Boric static fsnode *
create_fsnode(const char * root,const char * path,const char * name,struct stat * stbuf)242*9f988b79SJean-Baptiste Boric create_fsnode(const char *root, const char *path, const char *name,
243*9f988b79SJean-Baptiste Boric     struct stat *stbuf)
244*9f988b79SJean-Baptiste Boric {
245*9f988b79SJean-Baptiste Boric 	fsnode *cur;
246*9f988b79SJean-Baptiste Boric 
247*9f988b79SJean-Baptiste Boric 	cur = ecalloc(1, sizeof(*cur));
248*9f988b79SJean-Baptiste Boric 	cur->path = estrdup(path);
249*9f988b79SJean-Baptiste Boric 	cur->name = estrdup(name);
250*9f988b79SJean-Baptiste Boric 	cur->inode = ecalloc(1, sizeof(*cur->inode));
251*9f988b79SJean-Baptiste Boric 	cur->root = root;
252*9f988b79SJean-Baptiste Boric 	cur->type = stbuf->st_mode & S_IFMT;
253*9f988b79SJean-Baptiste Boric 	cur->inode->nlink = 1;
254*9f988b79SJean-Baptiste Boric 	cur->inode->st = *stbuf;
255*9f988b79SJean-Baptiste Boric 	return (cur);
256*9f988b79SJean-Baptiste Boric }
257*9f988b79SJean-Baptiste Boric 
258*9f988b79SJean-Baptiste Boric /*
259*9f988b79SJean-Baptiste Boric  * free_fsnodes --
260*9f988b79SJean-Baptiste Boric  *	Removes node from tree and frees it and all of
261*9f988b79SJean-Baptiste Boric  *   its decendents.
262*9f988b79SJean-Baptiste Boric  */
263*9f988b79SJean-Baptiste Boric void
free_fsnodes(fsnode * node)264*9f988b79SJean-Baptiste Boric free_fsnodes(fsnode *node)
265*9f988b79SJean-Baptiste Boric {
266*9f988b79SJean-Baptiste Boric 	fsnode	*cur, *next;
267*9f988b79SJean-Baptiste Boric 
268*9f988b79SJean-Baptiste Boric 	assert(node != NULL);
269*9f988b79SJean-Baptiste Boric 
270*9f988b79SJean-Baptiste Boric 	/* for ".", start with actual parent node */
271*9f988b79SJean-Baptiste Boric 	if (node->first == node) {
272*9f988b79SJean-Baptiste Boric 		assert(node->name[0] == '.' && node->name[1] == '\0');
273*9f988b79SJean-Baptiste Boric 		if (node->parent) {
274*9f988b79SJean-Baptiste Boric 			assert(node->parent->child == node);
275*9f988b79SJean-Baptiste Boric 			node = node->parent;
276*9f988b79SJean-Baptiste Boric 		}
277*9f988b79SJean-Baptiste Boric 	}
278*9f988b79SJean-Baptiste Boric 
279*9f988b79SJean-Baptiste Boric 	/* Find ourselves in our sibling list and unlink */
280*9f988b79SJean-Baptiste Boric 	if (node->first != node) {
281*9f988b79SJean-Baptiste Boric 		for (cur = node->first; cur->next; cur = cur->next) {
282*9f988b79SJean-Baptiste Boric 			if (cur->next == node) {
283*9f988b79SJean-Baptiste Boric 				cur->next = node->next;
284*9f988b79SJean-Baptiste Boric 				node->next = NULL;
285*9f988b79SJean-Baptiste Boric 				break;
286*9f988b79SJean-Baptiste Boric 			}
287*9f988b79SJean-Baptiste Boric 		}
288*9f988b79SJean-Baptiste Boric 	}
289*9f988b79SJean-Baptiste Boric 
290*9f988b79SJean-Baptiste Boric 	for (cur = node; cur != NULL; cur = next) {
291*9f988b79SJean-Baptiste Boric 		next = cur->next;
292*9f988b79SJean-Baptiste Boric 		if (cur->child) {
293*9f988b79SJean-Baptiste Boric 			cur->child->parent = NULL;
294*9f988b79SJean-Baptiste Boric 			free_fsnodes(cur->child);
295*9f988b79SJean-Baptiste Boric 		}
296*9f988b79SJean-Baptiste Boric 		if (cur->inode->nlink-- == 1)
297*9f988b79SJean-Baptiste Boric 			free(cur->inode);
298*9f988b79SJean-Baptiste Boric 		if (cur->symlink)
299*9f988b79SJean-Baptiste Boric 			free(cur->symlink);
300*9f988b79SJean-Baptiste Boric 		free(cur->path);
301*9f988b79SJean-Baptiste Boric 		free(cur->name);
302*9f988b79SJean-Baptiste Boric 		free(cur);
303*9f988b79SJean-Baptiste Boric 	}
304*9f988b79SJean-Baptiste Boric }
305*9f988b79SJean-Baptiste Boric 
306*9f988b79SJean-Baptiste Boric /*
307*9f988b79SJean-Baptiste Boric  * apply_specfile --
308*9f988b79SJean-Baptiste Boric  *	read in the mtree(8) specfile, and apply it to the tree
309*9f988b79SJean-Baptiste Boric  *	at dir,parent. parameters in parent on equivalent types
310*9f988b79SJean-Baptiste Boric  *	will be changed to those found in specfile, and missing
311*9f988b79SJean-Baptiste Boric  *	entries will be added.
312*9f988b79SJean-Baptiste Boric  */
313*9f988b79SJean-Baptiste Boric void
apply_specfile(const char * specfile,const char * dir,fsnode * parent,int speconly)314*9f988b79SJean-Baptiste Boric apply_specfile(const char *specfile, const char *dir, fsnode *parent, int speconly)
315*9f988b79SJean-Baptiste Boric {
316*9f988b79SJean-Baptiste Boric 	struct timeval	 start;
317*9f988b79SJean-Baptiste Boric 	FILE	*fp;
318*9f988b79SJean-Baptiste Boric 	NODE	*root;
319*9f988b79SJean-Baptiste Boric 
320*9f988b79SJean-Baptiste Boric 	assert(specfile != NULL);
321*9f988b79SJean-Baptiste Boric 	assert(parent != NULL);
322*9f988b79SJean-Baptiste Boric 
323*9f988b79SJean-Baptiste Boric 	if (debug & DEBUG_APPLY_SPECFILE)
324*9f988b79SJean-Baptiste Boric 		printf("apply_specfile: %s, %s %p\n", specfile, dir, parent);
325*9f988b79SJean-Baptiste Boric 
326*9f988b79SJean-Baptiste Boric 				/* read in the specfile */
327*9f988b79SJean-Baptiste Boric 	if ((fp = fopen(specfile, "r")) == NULL)
328*9f988b79SJean-Baptiste Boric 		err(1, "Can't open `%s'", specfile);
329*9f988b79SJean-Baptiste Boric 	TIMER_START(start);
330*9f988b79SJean-Baptiste Boric 	root = spec(fp);
331*9f988b79SJean-Baptiste Boric 	TIMER_RESULTS(start, "spec");
332*9f988b79SJean-Baptiste Boric 	if (fclose(fp) == EOF)
333*9f988b79SJean-Baptiste Boric 		err(1, "Can't close `%s'", specfile);
334*9f988b79SJean-Baptiste Boric 
335*9f988b79SJean-Baptiste Boric 				/* perform some sanity checks */
336*9f988b79SJean-Baptiste Boric 	if (root == NULL)
337*9f988b79SJean-Baptiste Boric 		errx(1, "Specfile `%s' did not contain a tree", specfile);
338*9f988b79SJean-Baptiste Boric 	assert(strcmp(root->name, ".") == 0);
339*9f988b79SJean-Baptiste Boric 	assert(root->type == F_DIR);
340*9f988b79SJean-Baptiste Boric 
341*9f988b79SJean-Baptiste Boric 				/* merge in the changes */
342*9f988b79SJean-Baptiste Boric 	apply_specdir(dir, root, parent, speconly);
343*9f988b79SJean-Baptiste Boric 
344*9f988b79SJean-Baptiste Boric 	free_nodes(root);
345*9f988b79SJean-Baptiste Boric }
346*9f988b79SJean-Baptiste Boric 
347*9f988b79SJean-Baptiste Boric static void
apply_specdir(const char * dir,NODE * specnode,fsnode * dirnode,int speconly)348*9f988b79SJean-Baptiste Boric apply_specdir(const char *dir, NODE *specnode, fsnode *dirnode, int speconly)
349*9f988b79SJean-Baptiste Boric {
350*9f988b79SJean-Baptiste Boric 	char	 path[MAXPATHLEN + 1];
351*9f988b79SJean-Baptiste Boric 	NODE	*curnode;
352*9f988b79SJean-Baptiste Boric 	fsnode	*curfsnode;
353*9f988b79SJean-Baptiste Boric 
354*9f988b79SJean-Baptiste Boric 	assert(specnode != NULL);
355*9f988b79SJean-Baptiste Boric 	assert(dirnode != NULL);
356*9f988b79SJean-Baptiste Boric 
357*9f988b79SJean-Baptiste Boric 	if (debug & DEBUG_APPLY_SPECFILE)
358*9f988b79SJean-Baptiste Boric 		printf("apply_specdir: %s %p %p\n", dir, specnode, dirnode);
359*9f988b79SJean-Baptiste Boric 
360*9f988b79SJean-Baptiste Boric 	if (specnode->type != F_DIR)
361*9f988b79SJean-Baptiste Boric 		errx(1, "Specfile node `%s/%s' is not a directory",
362*9f988b79SJean-Baptiste Boric 		    dir, specnode->name);
363*9f988b79SJean-Baptiste Boric 	if (dirnode->type != S_IFDIR)
364*9f988b79SJean-Baptiste Boric 		errx(1, "Directory node `%s/%s' is not a directory",
365*9f988b79SJean-Baptiste Boric 		    dir, dirnode->name);
366*9f988b79SJean-Baptiste Boric 
367*9f988b79SJean-Baptiste Boric 	apply_specentry(dir, specnode, dirnode);
368*9f988b79SJean-Baptiste Boric 
369*9f988b79SJean-Baptiste Boric 	/* Remove any filesystem nodes not found in specfile */
370*9f988b79SJean-Baptiste Boric 	/* XXX inefficient.  This is O^2 in each dir and it would
371*9f988b79SJean-Baptiste Boric 	 * have been better never to have walked this part of the tree
372*9f988b79SJean-Baptiste Boric 	 * to begin with
373*9f988b79SJean-Baptiste Boric 	 */
374*9f988b79SJean-Baptiste Boric 	if (speconly) {
375*9f988b79SJean-Baptiste Boric 		fsnode *next;
376*9f988b79SJean-Baptiste Boric 		assert(dirnode->name[0] == '.' && dirnode->name[1] == '\0');
377*9f988b79SJean-Baptiste Boric 		for (curfsnode = dirnode->next; curfsnode != NULL; curfsnode = next) {
378*9f988b79SJean-Baptiste Boric 			next = curfsnode->next;
379*9f988b79SJean-Baptiste Boric 			for (curnode = specnode->child; curnode != NULL;
380*9f988b79SJean-Baptiste Boric 			     curnode = curnode->next) {
381*9f988b79SJean-Baptiste Boric 				if (strcmp(curnode->name, curfsnode->name) == 0)
382*9f988b79SJean-Baptiste Boric 					break;
383*9f988b79SJean-Baptiste Boric 			}
384*9f988b79SJean-Baptiste Boric 			if (curnode == NULL) {
385*9f988b79SJean-Baptiste Boric 				if (debug & DEBUG_APPLY_SPECONLY) {
386*9f988b79SJean-Baptiste Boric 					printf("apply_specdir: trimming %s/%s %p\n", dir, curfsnode->name, curfsnode);
387*9f988b79SJean-Baptiste Boric 				}
388*9f988b79SJean-Baptiste Boric 				free_fsnodes(curfsnode);
389*9f988b79SJean-Baptiste Boric 			}
390*9f988b79SJean-Baptiste Boric 		}
391*9f988b79SJean-Baptiste Boric 	}
392*9f988b79SJean-Baptiste Boric 
393*9f988b79SJean-Baptiste Boric 			/* now walk specnode->child matching up with dirnode */
394*9f988b79SJean-Baptiste Boric 	for (curnode = specnode->child; curnode != NULL;
395*9f988b79SJean-Baptiste Boric 	    curnode = curnode->next) {
396*9f988b79SJean-Baptiste Boric 		if (debug & DEBUG_APPLY_SPECENTRY)
397*9f988b79SJean-Baptiste Boric 			printf("apply_specdir:  spec %s\n",
398*9f988b79SJean-Baptiste Boric 			    curnode->name);
399*9f988b79SJean-Baptiste Boric 		for (curfsnode = dirnode->next; curfsnode != NULL;
400*9f988b79SJean-Baptiste Boric 		    curfsnode = curfsnode->next) {
401*9f988b79SJean-Baptiste Boric #if 0	/* too verbose for now */
402*9f988b79SJean-Baptiste Boric 			if (debug & DEBUG_APPLY_SPECENTRY)
403*9f988b79SJean-Baptiste Boric 				printf("apply_specdir:  dirent %s\n",
404*9f988b79SJean-Baptiste Boric 				    curfsnode->name);
405*9f988b79SJean-Baptiste Boric #endif
406*9f988b79SJean-Baptiste Boric 			if (strcmp(curnode->name, curfsnode->name) == 0)
407*9f988b79SJean-Baptiste Boric 				break;
408*9f988b79SJean-Baptiste Boric 		}
409*9f988b79SJean-Baptiste Boric 		if ((size_t)snprintf(path, sizeof(path), "%s/%s",
410*9f988b79SJean-Baptiste Boric 		    dir, curnode->name) >= sizeof(path))
411*9f988b79SJean-Baptiste Boric 			errx(1, "Pathname too long.");
412*9f988b79SJean-Baptiste Boric 		if (curfsnode == NULL) {	/* need new entry */
413*9f988b79SJean-Baptiste Boric 			struct stat	stbuf;
414*9f988b79SJean-Baptiste Boric 
415*9f988b79SJean-Baptiste Boric 					    /*
416*9f988b79SJean-Baptiste Boric 					     * don't add optional spec entries
417*9f988b79SJean-Baptiste Boric 					     * that lack an existing fs entry
418*9f988b79SJean-Baptiste Boric 					     */
419*9f988b79SJean-Baptiste Boric 			if ((curnode->flags & F_OPT) &&
420*9f988b79SJean-Baptiste Boric 			    lstat(path, &stbuf) == -1)
421*9f988b79SJean-Baptiste Boric 					continue;
422*9f988b79SJean-Baptiste Boric 
423*9f988b79SJean-Baptiste Boric 					/* check that enough info is provided */
424*9f988b79SJean-Baptiste Boric #define NODETEST(t, m)							\
425*9f988b79SJean-Baptiste Boric 			if (!(t))					\
426*9f988b79SJean-Baptiste Boric 				errx(1, "`%s': %s not provided", path, m)
427*9f988b79SJean-Baptiste Boric 			NODETEST(curnode->flags & F_TYPE, "type");
428*9f988b79SJean-Baptiste Boric 			NODETEST(curnode->flags & F_MODE, "mode");
429*9f988b79SJean-Baptiste Boric 				/* XXX: require F_TIME ? */
430*9f988b79SJean-Baptiste Boric 			NODETEST(curnode->flags & F_GID ||
431*9f988b79SJean-Baptiste Boric 			    curnode->flags & F_GNAME, "group");
432*9f988b79SJean-Baptiste Boric 			NODETEST(curnode->flags & F_UID ||
433*9f988b79SJean-Baptiste Boric 			    curnode->flags & F_UNAME, "user");
434*9f988b79SJean-Baptiste Boric 			if (curnode->type == F_BLOCK || curnode->type == F_CHAR)
435*9f988b79SJean-Baptiste Boric 				NODETEST(curnode->flags & F_DEV,
436*9f988b79SJean-Baptiste Boric 				    "device number");
437*9f988b79SJean-Baptiste Boric #undef NODETEST
438*9f988b79SJean-Baptiste Boric 
439*9f988b79SJean-Baptiste Boric 			if (debug & DEBUG_APPLY_SPECFILE)
440*9f988b79SJean-Baptiste Boric 				printf("apply_specdir: adding %s\n",
441*9f988b79SJean-Baptiste Boric 				    curnode->name);
442*9f988b79SJean-Baptiste Boric 					/* build minimal fsnode */
443*9f988b79SJean-Baptiste Boric 			memset(&stbuf, 0, sizeof(stbuf));
444*9f988b79SJean-Baptiste Boric 			stbuf.st_mode = nodetoino(curnode->type);
445*9f988b79SJean-Baptiste Boric 			stbuf.st_nlink = 1;
446*9f988b79SJean-Baptiste Boric 			stbuf.st_mtime = stbuf.st_atime =
447*9f988b79SJean-Baptiste Boric 			    stbuf.st_ctime = start_time.tv_sec;
448*9f988b79SJean-Baptiste Boric #if HAVE_STRUCT_STAT_ST_MTIMENSEC
449*9f988b79SJean-Baptiste Boric 			stbuf.st_mtimensec = stbuf.st_atimensec =
450*9f988b79SJean-Baptiste Boric 			    stbuf.st_ctimensec = start_time.tv_nsec;
451*9f988b79SJean-Baptiste Boric #endif
452*9f988b79SJean-Baptiste Boric 			curfsnode = create_fsnode(".", ".", curnode->name,
453*9f988b79SJean-Baptiste Boric 			    &stbuf);
454*9f988b79SJean-Baptiste Boric 			curfsnode->parent = dirnode->parent;
455*9f988b79SJean-Baptiste Boric 			curfsnode->first = dirnode;
456*9f988b79SJean-Baptiste Boric 			curfsnode->next = dirnode->next;
457*9f988b79SJean-Baptiste Boric 			dirnode->next = curfsnode;
458*9f988b79SJean-Baptiste Boric 			if (curfsnode->type == S_IFDIR) {
459*9f988b79SJean-Baptiste Boric 					/* for dirs, make "." entry as well */
460*9f988b79SJean-Baptiste Boric 				curfsnode->child = create_fsnode(".", ".", ".",
461*9f988b79SJean-Baptiste Boric 				    &stbuf);
462*9f988b79SJean-Baptiste Boric 				curfsnode->child->parent = curfsnode;
463*9f988b79SJean-Baptiste Boric 				curfsnode->child->first = curfsnode->child;
464*9f988b79SJean-Baptiste Boric 			}
465*9f988b79SJean-Baptiste Boric 			if (curfsnode->type == S_IFLNK) {
466*9f988b79SJean-Baptiste Boric 				assert(curnode->slink != NULL);
467*9f988b79SJean-Baptiste Boric 					/* for symlinks, copy the target */
468*9f988b79SJean-Baptiste Boric 				curfsnode->symlink = estrdup(curnode->slink);
469*9f988b79SJean-Baptiste Boric 			}
470*9f988b79SJean-Baptiste Boric 		}
471*9f988b79SJean-Baptiste Boric 		apply_specentry(dir, curnode, curfsnode);
472*9f988b79SJean-Baptiste Boric 		if (curnode->type == F_DIR) {
473*9f988b79SJean-Baptiste Boric 			if (curfsnode->type != S_IFDIR)
474*9f988b79SJean-Baptiste Boric 				errx(1, "`%s' is not a directory", path);
475*9f988b79SJean-Baptiste Boric 			assert (curfsnode->child != NULL);
476*9f988b79SJean-Baptiste Boric 			apply_specdir(path, curnode, curfsnode->child, speconly);
477*9f988b79SJean-Baptiste Boric 		}
478*9f988b79SJean-Baptiste Boric 	}
479*9f988b79SJean-Baptiste Boric }
480*9f988b79SJean-Baptiste Boric 
481*9f988b79SJean-Baptiste Boric static void
apply_specentry(const char * dir,NODE * specnode,fsnode * dirnode)482*9f988b79SJean-Baptiste Boric apply_specentry(const char *dir, NODE *specnode, fsnode *dirnode)
483*9f988b79SJean-Baptiste Boric {
484*9f988b79SJean-Baptiste Boric 
485*9f988b79SJean-Baptiste Boric 	assert(specnode != NULL);
486*9f988b79SJean-Baptiste Boric 	assert(dirnode != NULL);
487*9f988b79SJean-Baptiste Boric 
488*9f988b79SJean-Baptiste Boric 	if (nodetoino(specnode->type) != dirnode->type)
489*9f988b79SJean-Baptiste Boric 		errx(1, "`%s/%s' type mismatch: specfile %s, tree %s",
490*9f988b79SJean-Baptiste Boric 		    dir, specnode->name, inode_type(nodetoino(specnode->type)),
491*9f988b79SJean-Baptiste Boric 		    inode_type(dirnode->type));
492*9f988b79SJean-Baptiste Boric 
493*9f988b79SJean-Baptiste Boric 	if (debug & DEBUG_APPLY_SPECENTRY)
494*9f988b79SJean-Baptiste Boric 		printf("apply_specentry: %s/%s\n", dir, dirnode->name);
495*9f988b79SJean-Baptiste Boric 
496*9f988b79SJean-Baptiste Boric #define ASEPRINT(t, b, o, n) \
497*9f988b79SJean-Baptiste Boric 		if (debug & DEBUG_APPLY_SPECENTRY) \
498*9f988b79SJean-Baptiste Boric 			printf("\t\t\tchanging %s from " b " to " b "\n", \
499*9f988b79SJean-Baptiste Boric 			    t, o, n)
500*9f988b79SJean-Baptiste Boric 
501*9f988b79SJean-Baptiste Boric 	if (specnode->flags & (F_GID | F_GNAME)) {
502*9f988b79SJean-Baptiste Boric 		ASEPRINT("gid", "%d",
503*9f988b79SJean-Baptiste Boric 		    dirnode->inode->st.st_gid, specnode->st_gid);
504*9f988b79SJean-Baptiste Boric 		dirnode->inode->st.st_gid = specnode->st_gid;
505*9f988b79SJean-Baptiste Boric 	}
506*9f988b79SJean-Baptiste Boric 	if (specnode->flags & F_MODE) {
507*9f988b79SJean-Baptiste Boric 		ASEPRINT("mode", "%#o",
508*9f988b79SJean-Baptiste Boric 		    dirnode->inode->st.st_mode & ALLPERMS, specnode->st_mode);
509*9f988b79SJean-Baptiste Boric 		dirnode->inode->st.st_mode &= ~ALLPERMS;
510*9f988b79SJean-Baptiste Boric 		dirnode->inode->st.st_mode |= (specnode->st_mode & ALLPERMS);
511*9f988b79SJean-Baptiste Boric 	}
512*9f988b79SJean-Baptiste Boric 		/* XXX: ignoring F_NLINK for now */
513*9f988b79SJean-Baptiste Boric 	if (specnode->flags & F_SIZE) {
514*9f988b79SJean-Baptiste Boric 		ASEPRINT("size", "%lld",
515*9f988b79SJean-Baptiste Boric 		    (long long)dirnode->inode->st.st_size,
516*9f988b79SJean-Baptiste Boric 		    (long long)specnode->st_size);
517*9f988b79SJean-Baptiste Boric 		dirnode->inode->st.st_size = specnode->st_size;
518*9f988b79SJean-Baptiste Boric 	}
519*9f988b79SJean-Baptiste Boric 	if (specnode->flags & F_SLINK) {
520*9f988b79SJean-Baptiste Boric 		assert(dirnode->symlink != NULL);
521*9f988b79SJean-Baptiste Boric 		assert(specnode->slink != NULL);
522*9f988b79SJean-Baptiste Boric 		ASEPRINT("symlink", "%s", dirnode->symlink, specnode->slink);
523*9f988b79SJean-Baptiste Boric 		free(dirnode->symlink);
524*9f988b79SJean-Baptiste Boric 		dirnode->symlink = estrdup(specnode->slink);
525*9f988b79SJean-Baptiste Boric 	}
526*9f988b79SJean-Baptiste Boric 	if (specnode->flags & F_TIME) {
527*9f988b79SJean-Baptiste Boric 		ASEPRINT("time", "%ld",
528*9f988b79SJean-Baptiste Boric 		    (long)dirnode->inode->st.st_mtime,
529*9f988b79SJean-Baptiste Boric 		    (long)specnode->st_mtimespec.tv_sec);
530*9f988b79SJean-Baptiste Boric 		dirnode->inode->st.st_mtime =		specnode->st_mtimespec.tv_sec;
531*9f988b79SJean-Baptiste Boric 		dirnode->inode->st.st_atime =		specnode->st_mtimespec.tv_sec;
532*9f988b79SJean-Baptiste Boric 		dirnode->inode->st.st_ctime =		start_time.tv_sec;
533*9f988b79SJean-Baptiste Boric #if HAVE_STRUCT_STAT_ST_MTIMENSEC
534*9f988b79SJean-Baptiste Boric 		dirnode->inode->st.st_mtimensec =	specnode->st_mtimespec.tv_nsec;
535*9f988b79SJean-Baptiste Boric 		dirnode->inode->st.st_atimensec =	specnode->st_mtimespec.tv_nsec;
536*9f988b79SJean-Baptiste Boric 		dirnode->inode->st.st_ctimensec =	start_time.tv_nsec;
537*9f988b79SJean-Baptiste Boric #endif
538*9f988b79SJean-Baptiste Boric 	}
539*9f988b79SJean-Baptiste Boric 	if (specnode->flags & (F_UID | F_UNAME)) {
540*9f988b79SJean-Baptiste Boric 		ASEPRINT("uid", "%d",
541*9f988b79SJean-Baptiste Boric 		    dirnode->inode->st.st_uid, specnode->st_uid);
542*9f988b79SJean-Baptiste Boric 		dirnode->inode->st.st_uid = specnode->st_uid;
543*9f988b79SJean-Baptiste Boric 	}
544*9f988b79SJean-Baptiste Boric #if HAVE_STRUCT_STAT_ST_FLAGS
545*9f988b79SJean-Baptiste Boric 	if (specnode->flags & F_FLAGS) {
546*9f988b79SJean-Baptiste Boric 		ASEPRINT("flags", "%#lX",
547*9f988b79SJean-Baptiste Boric 		    (unsigned long)dirnode->inode->st.st_flags,
548*9f988b79SJean-Baptiste Boric 		    (unsigned long)specnode->st_flags);
549*9f988b79SJean-Baptiste Boric 		dirnode->inode->st.st_flags = specnode->st_flags;
550*9f988b79SJean-Baptiste Boric 	}
551*9f988b79SJean-Baptiste Boric #endif
552*9f988b79SJean-Baptiste Boric 	if (specnode->flags & F_DEV) {
553*9f988b79SJean-Baptiste Boric 		ASEPRINT("rdev", "%#llx",
554*9f988b79SJean-Baptiste Boric 		    (unsigned long long)dirnode->inode->st.st_rdev,
555*9f988b79SJean-Baptiste Boric 		    (unsigned long long)specnode->st_rdev);
556*9f988b79SJean-Baptiste Boric 		dirnode->inode->st.st_rdev = specnode->st_rdev;
557*9f988b79SJean-Baptiste Boric 	}
558*9f988b79SJean-Baptiste Boric #undef ASEPRINT
559*9f988b79SJean-Baptiste Boric 
560*9f988b79SJean-Baptiste Boric 	dirnode->flags |= FSNODE_F_HASSPEC;
561*9f988b79SJean-Baptiste Boric }
562*9f988b79SJean-Baptiste Boric 
563*9f988b79SJean-Baptiste Boric 
564*9f988b79SJean-Baptiste Boric /*
565*9f988b79SJean-Baptiste Boric  * dump_fsnodes --
566*9f988b79SJean-Baptiste Boric  *	dump the fsnodes from `cur'
567*9f988b79SJean-Baptiste Boric  */
568*9f988b79SJean-Baptiste Boric void
dump_fsnodes(fsnode * root)569*9f988b79SJean-Baptiste Boric dump_fsnodes(fsnode *root)
570*9f988b79SJean-Baptiste Boric {
571*9f988b79SJean-Baptiste Boric 	fsnode	*cur;
572*9f988b79SJean-Baptiste Boric 	char	path[MAXPATHLEN + 1];
573*9f988b79SJean-Baptiste Boric 
574*9f988b79SJean-Baptiste Boric 	printf("dump_fsnodes: %s %p\n", root->path, root);
575*9f988b79SJean-Baptiste Boric 	for (cur = root; cur != NULL; cur = cur->next) {
576*9f988b79SJean-Baptiste Boric 		if (snprintf(path, sizeof(path), "%s/%s", cur->path,
577*9f988b79SJean-Baptiste Boric 		    cur->name) >= (int)sizeof(path))
578*9f988b79SJean-Baptiste Boric 			errx(1, "Pathname too long.");
579*9f988b79SJean-Baptiste Boric 
580*9f988b79SJean-Baptiste Boric 		if (debug & DEBUG_DUMP_FSNODES_VERBOSE)
581*9f988b79SJean-Baptiste Boric 			printf("cur=%8p parent=%8p first=%8p ",
582*9f988b79SJean-Baptiste Boric 			    cur, cur->parent, cur->first);
583*9f988b79SJean-Baptiste Boric 		printf("%7s: %s", inode_type(cur->type), path);
584*9f988b79SJean-Baptiste Boric 		if (S_ISLNK(cur->type)) {
585*9f988b79SJean-Baptiste Boric 			assert(cur->symlink != NULL);
586*9f988b79SJean-Baptiste Boric 			printf(" -> %s", cur->symlink);
587*9f988b79SJean-Baptiste Boric 		} else {
588*9f988b79SJean-Baptiste Boric 			assert (cur->symlink == NULL);
589*9f988b79SJean-Baptiste Boric 		}
590*9f988b79SJean-Baptiste Boric 		if (cur->inode->nlink > 1)
591*9f988b79SJean-Baptiste Boric 			printf(", nlinks=%d", cur->inode->nlink);
592*9f988b79SJean-Baptiste Boric 		putchar('\n');
593*9f988b79SJean-Baptiste Boric 
594*9f988b79SJean-Baptiste Boric 		if (cur->child) {
595*9f988b79SJean-Baptiste Boric 			assert (cur->type == S_IFDIR);
596*9f988b79SJean-Baptiste Boric 			dump_fsnodes(cur->child);
597*9f988b79SJean-Baptiste Boric 		}
598*9f988b79SJean-Baptiste Boric 	}
599*9f988b79SJean-Baptiste Boric 	printf("dump_fsnodes: finished %s/%s\n", root->path, root->name);
600*9f988b79SJean-Baptiste Boric }
601*9f988b79SJean-Baptiste Boric 
602*9f988b79SJean-Baptiste Boric 
603*9f988b79SJean-Baptiste Boric /*
604*9f988b79SJean-Baptiste Boric  * inode_type --
605*9f988b79SJean-Baptiste Boric  *	for a given inode type `mode', return a descriptive string.
606*9f988b79SJean-Baptiste Boric  *	for most cases, uses inotype() from mtree/misc.c
607*9f988b79SJean-Baptiste Boric  */
608*9f988b79SJean-Baptiste Boric const char *
inode_type(mode_t mode)609*9f988b79SJean-Baptiste Boric inode_type(mode_t mode)
610*9f988b79SJean-Baptiste Boric {
611*9f988b79SJean-Baptiste Boric 
612*9f988b79SJean-Baptiste Boric 	if (S_ISLNK(mode))
613*9f988b79SJean-Baptiste Boric 		return ("symlink");	/* inotype() returns "link"...  */
614*9f988b79SJean-Baptiste Boric 	return (inotype(mode));
615*9f988b79SJean-Baptiste Boric }
616*9f988b79SJean-Baptiste Boric 
617*9f988b79SJean-Baptiste Boric 
618*9f988b79SJean-Baptiste Boric /*
619*9f988b79SJean-Baptiste Boric  * link_check --
620*9f988b79SJean-Baptiste Boric  *	return pointer to fsinode matching `entry's st_ino & st_dev if it exists,
621*9f988b79SJean-Baptiste Boric  *	otherwise add `entry' to table and return NULL
622*9f988b79SJean-Baptiste Boric  */
623*9f988b79SJean-Baptiste Boric /* This was borrowed from du.c and tweaked to keep an fsnode
624*9f988b79SJean-Baptiste Boric  * pointer instead. -- dbj@netbsd.org
625*9f988b79SJean-Baptiste Boric  */
626*9f988b79SJean-Baptiste Boric static fsinode *
link_check(fsinode * entry)627*9f988b79SJean-Baptiste Boric link_check(fsinode *entry)
628*9f988b79SJean-Baptiste Boric {
629*9f988b79SJean-Baptiste Boric 	static struct entry {
630*9f988b79SJean-Baptiste Boric 		fsinode *data;
631*9f988b79SJean-Baptiste Boric 	} *htable;
632*9f988b79SJean-Baptiste Boric 	static int htshift;  /* log(allocated size) */
633*9f988b79SJean-Baptiste Boric 	static int htmask;   /* allocated size - 1 */
634*9f988b79SJean-Baptiste Boric 	static int htused;   /* 2*number of insertions */
635*9f988b79SJean-Baptiste Boric 	int h, h2;
636*9f988b79SJean-Baptiste Boric 	uint64_t tmp;
637*9f988b79SJean-Baptiste Boric 	/* this constant is (1<<64)/((1+sqrt(5))/2)
638*9f988b79SJean-Baptiste Boric 	 * aka (word size)/(golden ratio)
639*9f988b79SJean-Baptiste Boric 	 */
640*9f988b79SJean-Baptiste Boric 	const uint64_t HTCONST = 11400714819323198485ULL;
641*9f988b79SJean-Baptiste Boric 	const int HTBITS = 64;
642*9f988b79SJean-Baptiste Boric 
643*9f988b79SJean-Baptiste Boric 	/* Never store zero in hashtable */
644*9f988b79SJean-Baptiste Boric 	assert(entry);
645*9f988b79SJean-Baptiste Boric 
646*9f988b79SJean-Baptiste Boric 	/* Extend hash table if necessary, keep load under 0.5 */
647*9f988b79SJean-Baptiste Boric 	if (htused<<1 >= htmask) {
648*9f988b79SJean-Baptiste Boric 		struct entry *ohtable;
649*9f988b79SJean-Baptiste Boric 
650*9f988b79SJean-Baptiste Boric 		if (!htable)
651*9f988b79SJean-Baptiste Boric 			htshift = 10;   /* starting hashtable size */
652*9f988b79SJean-Baptiste Boric 		else
653*9f988b79SJean-Baptiste Boric 			htshift++;   /* exponential hashtable growth */
654*9f988b79SJean-Baptiste Boric 
655*9f988b79SJean-Baptiste Boric 		htmask  = (1 << htshift) - 1;
656*9f988b79SJean-Baptiste Boric 		htused = 0;
657*9f988b79SJean-Baptiste Boric 
658*9f988b79SJean-Baptiste Boric 		ohtable = htable;
659*9f988b79SJean-Baptiste Boric 		htable = ecalloc(htmask+1, sizeof(*htable));
660*9f988b79SJean-Baptiste Boric 		/* populate newly allocated hashtable */
661*9f988b79SJean-Baptiste Boric 		if (ohtable) {
662*9f988b79SJean-Baptiste Boric 			int i;
663*9f988b79SJean-Baptiste Boric 			for (i = 0; i <= htmask>>1; i++)
664*9f988b79SJean-Baptiste Boric 				if (ohtable[i].data)
665*9f988b79SJean-Baptiste Boric 					link_check(ohtable[i].data);
666*9f988b79SJean-Baptiste Boric 			free(ohtable);
667*9f988b79SJean-Baptiste Boric 		}
668*9f988b79SJean-Baptiste Boric 	}
669*9f988b79SJean-Baptiste Boric 
670*9f988b79SJean-Baptiste Boric 	/* multiplicative hashing */
671*9f988b79SJean-Baptiste Boric 	tmp = entry->st.st_dev;
672*9f988b79SJean-Baptiste Boric 	tmp <<= HTBITS>>1;
673*9f988b79SJean-Baptiste Boric 	tmp |=  entry->st.st_ino;
674*9f988b79SJean-Baptiste Boric 	tmp *= HTCONST;
675*9f988b79SJean-Baptiste Boric 	h  = tmp >> (HTBITS - htshift);
676*9f988b79SJean-Baptiste Boric 	h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */
677*9f988b79SJean-Baptiste Boric 
678*9f988b79SJean-Baptiste Boric 	/* open address hashtable search with double hash probing */
679*9f988b79SJean-Baptiste Boric 	while (htable[h].data) {
680*9f988b79SJean-Baptiste Boric 		if ((htable[h].data->st.st_ino == entry->st.st_ino) &&
681*9f988b79SJean-Baptiste Boric 		    (htable[h].data->st.st_dev == entry->st.st_dev)) {
682*9f988b79SJean-Baptiste Boric 			return htable[h].data;
683*9f988b79SJean-Baptiste Boric 		}
684*9f988b79SJean-Baptiste Boric 		h = (h + h2) & htmask;
685*9f988b79SJean-Baptiste Boric 	}
686*9f988b79SJean-Baptiste Boric 
687*9f988b79SJean-Baptiste Boric 	/* Insert the current entry into hashtable */
688*9f988b79SJean-Baptiste Boric 	htable[h].data = entry;
689*9f988b79SJean-Baptiste Boric 	htused++;
690*9f988b79SJean-Baptiste Boric 	return NULL;
691*9f988b79SJean-Baptiste Boric }
692