| @@ -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 | |
63 | static void apply_specdir(const char *, NODE *, fsnode *, int); | | 63 | static void apply_specdir(const char *, NODE *, fsnode *, int); |
64 | static void apply_specentry(const char *, NODE *, fsnode *); | | 64 | static void apply_specentry(const char *, NODE *, fsnode *); |
65 | static fsnode *create_fsnode(const char *, const char *, const char *, | | 65 | static fsnode *create_fsnode(const char *, const char *, const char *, |
66 | struct stat *); | | 66 | struct stat *); |
67 | static fsinode *link_check(fsinode *); | | 67 | static 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 | */ |
75 | static int | | 75 | static int |
76 | fsnode_cmp(const void *vleft, const void *vright) | | 76 | fsnode_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 | |
| | | 89 | static fsnode * |
| | | 90 | fsnode_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 | */ |
97 | fsnode * | | 128 | fsnode * |
98 | walk_dir(const char *root, const char *dir, fsnode *parent, fsnode *join, | | 129 | walk_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 | |
307 | static fsnode * | | 309 | static fsnode * |
308 | create_fsnode(const char *root, const char *path, const char *name, | | 310 | create_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 | */ |
343 | void | | 345 | void |
344 | free_fsnodes(fsnode *node) | | 346 | free_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 | */ |
393 | void | | 395 | void |
394 | apply_specfile(const char *specfile, const char *dir, fsnode *parent, int speconly) | | 396 | apply_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 | |
428 | static void | | 430 | static void |
429 | apply_specdir(const char *dir, NODE *specnode, fsnode *dirnode, int speconly) | | 431 | apply_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 | |
564 | static void | | 566 | static void |
565 | apply_specentry(const char *dir, NODE *specnode, fsnode *dirnode) | | 567 | apply_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 | */ |
652 | void | | 654 | void |
653 | dump_fsnodes(fsnode *root) | | 655 | dump_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 | */ |
692 | const char * | | 694 | const char * |
693 | inode_type(mode_t mode) | | 695 | inode_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 | */ |
710 | static fsinode * | | 712 | static fsinode * |
711 | link_check(fsinode *entry) | | 713 | link_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 | } |