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