Wed Apr 24 14:02:39 2024 UTC (39d)
make a separate sorting function and KNF (thanks rillig)


(christos)
diff -r1.36 -r1.37 src/usr.sbin/makefs/walk.c

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

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