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