Tue Apr 23 22:12:16 2024 UTC (39d)
makefs: Sort directory contents by name (Jan-Benedict Glaw)

`makefs` inserts nodes into its internal data structures in the order as
returned by `readdir()` calls. As this is unpredictable, sort entries by
name before creating the target filesystem.

  This is done by first converting the (per-directory) linked list into
a plain array, sort it, finally re-link the list. Special case for the
sorting function: The "." directory entry seems to be ment to be always
at the front, so always check that first.


(christos)
diff -r1.33 -r1.34 src/usr.sbin/makefs/walk.c

cvs diff -r1.33 -r1.34 src/usr.sbin/makefs/walk.c (switch to unified diff)

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