1214921Scognet/* $NetBSD: walk.c,v 1.24 2008/12/28 21:51:46 christos Exp $ */ 2185222Ssam 3185222Ssam/* 4185222Ssam * Copyright (c) 2001 Wasabi Systems, Inc. 5185222Ssam * All rights reserved. 6185222Ssam * 7185222Ssam * Written by Luke Mewburn for Wasabi Systems, Inc. 8185222Ssam * 9185222Ssam * Redistribution and use in source and binary forms, with or without 10185222Ssam * modification, are permitted provided that the following conditions 11185222Ssam * are met: 12185222Ssam * 1. Redistributions of source code must retain the above copyright 13185222Ssam * notice, this list of conditions and the following disclaimer. 14185222Ssam * 2. Redistributions in binary form must reproduce the above copyright 15185222Ssam * notice, this list of conditions and the following disclaimer in the 16185222Ssam * documentation and/or other materials provided with the distribution. 17185222Ssam * 3. All advertising materials mentioning features or use of this software 18185222Ssam * must display the following acknowledgement: 19185222Ssam * This product includes software developed for the NetBSD Project by 20185222Ssam * Wasabi Systems, Inc. 21185222Ssam * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22185222Ssam * or promote products derived from this software without specific prior 23185222Ssam * written permission. 24185222Ssam * 25185222Ssam * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26185222Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27185222Ssam * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28185222Ssam * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29185222Ssam * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30185222Ssam * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31185222Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32185222Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33185222Ssam * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34185222Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35185222Ssam * POSSIBILITY OF SUCH DAMAGE. 36185222Ssam */ 37185222Ssam 38185222Ssam 39185222Ssam#include <sys/cdefs.h> 40186334Ssam__FBSDID("$FreeBSD$"); 41185222Ssam 42185222Ssam#include <sys/param.h> 43185222Ssam 44185222Ssam#include <assert.h> 45185222Ssam#include <errno.h> 46185222Ssam#include <fcntl.h> 47185222Ssam#include <stdio.h> 48185222Ssam#include <dirent.h> 49185222Ssam#include <stdlib.h> 50185222Ssam#include <string.h> 51185222Ssam#include <unistd.h> 52214921Scognet#include <sys/stat.h> 53185222Ssam 54185222Ssam#include "makefs.h" 55185222Ssam#include "mtree.h" 56214921Scognet#include "extern.h" 57185222Ssam 58214921Scognetstatic void apply_specdir(const char *, NODE *, fsnode *, int); 59185222Ssamstatic void apply_specentry(const char *, NODE *, fsnode *); 60230795Sjkimstatic fsnode *create_fsnode(const char *, const char *, const char *, 61230795Sjkim struct stat *); 62185222Ssam 63185222Ssam 64185222Ssam/* 65185222Ssam * walk_dir -- 66230795Sjkim * build a tree of fsnodes from `root' and `dir', with a parent 67230795Sjkim * fsnode of `parent' (which may be NULL for the root of the tree). 68230795Sjkim * append the tree to a fsnode of `join' if it is not NULL. 69185222Ssam * each "level" is a directory, with the "." entry guaranteed to be 70185222Ssam * at the start of the list, and without ".." entries. 71185222Ssam */ 72185222Ssamfsnode * 73230795Sjkimwalk_dir(const char *root, const char *dir, fsnode *parent, fsnode *join) 74185222Ssam{ 75230795Sjkim fsnode *first, *cur, *prev, *last; 76185222Ssam DIR *dirp; 77185222Ssam struct dirent *dent; 78185222Ssam char path[MAXPATHLEN + 1]; 79185222Ssam struct stat stbuf; 80230795Sjkim char *name, *rp; 81230795Sjkim int dot, len; 82185222Ssam 83230795Sjkim assert(root != NULL); 84185222Ssam assert(dir != NULL); 85185222Ssam 86230795Sjkim len = snprintf(path, sizeof(path), "%s/%s", root, dir); 87230795Sjkim if (len >= (int)sizeof(path)) 88230795Sjkim errx(1, "Pathname too long."); 89185222Ssam if (debug & DEBUG_WALK_DIR) 90230795Sjkim printf("walk_dir: %s %p\n", path, parent); 91230795Sjkim if ((dirp = opendir(path)) == NULL) 92230795Sjkim err(1, "Can't opendir `%s'", path); 93230795Sjkim rp = path + strlen(root) + 1; 94230795Sjkim if (join != NULL) { 95230795Sjkim first = cur = join; 96230795Sjkim while (cur->next != NULL) 97230795Sjkim cur = cur->next; 98230795Sjkim prev = cur; 99230795Sjkim } else 100230795Sjkim first = prev = NULL; 101230795Sjkim last = prev; 102185222Ssam while ((dent = readdir(dirp)) != NULL) { 103230795Sjkim name = dent->d_name; 104230795Sjkim dot = 0; 105230795Sjkim if (name[0] == '.') 106230795Sjkim switch (name[1]) { 107230795Sjkim case '\0': /* "." */ 108230795Sjkim if (join != NULL) 109230795Sjkim continue; 110230795Sjkim dot = 1; 111230795Sjkim break; 112230795Sjkim case '.': /* ".." */ 113230795Sjkim if (name[2] == '\0') 114230795Sjkim continue; 115230795Sjkim /* FALLTHROUGH */ 116230795Sjkim default: 117230795Sjkim dot = 0; 118230795Sjkim } 119185222Ssam if (debug & DEBUG_WALK_DIR_NODE) 120230795Sjkim printf("scanning %s/%s/%s\n", root, dir, name); 121230795Sjkim if (snprintf(path + len, sizeof(path) - len, "/%s", name) >= 122230795Sjkim (int)sizeof(path) - len) 123185222Ssam errx(1, "Pathname too long."); 124185222Ssam if (lstat(path, &stbuf) == -1) 125185222Ssam err(1, "Can't lstat `%s'", path); 126185222Ssam#ifdef S_ISSOCK 127185222Ssam if (S_ISSOCK(stbuf.st_mode & S_IFMT)) { 128185222Ssam if (debug & DEBUG_WALK_DIR_NODE) 129185222Ssam printf(" skipping socket %s\n", path); 130185222Ssam continue; 131185222Ssam } 132185222Ssam#endif 133185222Ssam 134230795Sjkim if (join != NULL) { 135230795Sjkim cur = join->next; 136230795Sjkim for (;;) { 137230795Sjkim if (cur == NULL || strcmp(cur->name, name) == 0) 138230795Sjkim break; 139230795Sjkim if (cur == last) { 140230795Sjkim cur = NULL; 141230795Sjkim break; 142230795Sjkim } 143230795Sjkim cur = cur->next; 144230795Sjkim } 145230795Sjkim if (cur != NULL) { 146230795Sjkim if (S_ISDIR(cur->type) && 147230795Sjkim S_ISDIR(stbuf.st_mode)) { 148230795Sjkim if (debug & DEBUG_WALK_DIR_NODE) 149230795Sjkim printf("merging %s with %p\n", 150230795Sjkim path, cur->child); 151230795Sjkim cur->child = walk_dir(root, rp, cur, 152230795Sjkim cur->child); 153230795Sjkim continue; 154230795Sjkim } 155230795Sjkim errx(1, "Can't merge %s `%s' with existing %s", 156230795Sjkim inode_type(stbuf.st_mode), path, 157230795Sjkim inode_type(cur->type)); 158230795Sjkim } 159230795Sjkim } 160230795Sjkim 161230795Sjkim cur = create_fsnode(root, dir, name, &stbuf); 162185222Ssam cur->parent = parent; 163230795Sjkim if (dot) { 164185222Ssam /* ensure "." is at the start of the list */ 165185222Ssam cur->next = first; 166185222Ssam first = cur; 167185222Ssam if (! prev) 168185222Ssam prev = cur; 169230795Sjkim cur->first = first; 170185222Ssam } else { /* not "." */ 171185222Ssam if (prev) 172185222Ssam prev->next = cur; 173185222Ssam prev = cur; 174185222Ssam if (!first) 175185222Ssam first = cur; 176230795Sjkim cur->first = first; 177185222Ssam if (S_ISDIR(cur->type)) { 178230795Sjkim cur->child = walk_dir(root, rp, cur, NULL); 179185222Ssam continue; 180185222Ssam } 181185222Ssam } 182185222Ssam if (stbuf.st_nlink > 1) { 183185222Ssam fsinode *curino; 184185222Ssam 185185222Ssam curino = link_check(cur->inode); 186185222Ssam if (curino != NULL) { 187185222Ssam free(cur->inode); 188185222Ssam cur->inode = curino; 189185222Ssam cur->inode->nlink++; 190214921Scognet if (debug & DEBUG_WALK_DIR_LINKCHECK) 191214921Scognet printf("link_check: found [%llu, %llu]\n", 192214921Scognet (unsigned long long)curino->st.st_dev, 193214921Scognet (unsigned long long)curino->st.st_ino); 194185222Ssam } 195185222Ssam } 196185222Ssam if (S_ISLNK(cur->type)) { 197185222Ssam char slink[PATH_MAX+1]; 198185222Ssam int llen; 199185222Ssam 200185222Ssam llen = readlink(path, slink, sizeof(slink) - 1); 201185222Ssam if (llen == -1) 202185222Ssam err(1, "Readlink `%s'", path); 203185222Ssam slink[llen] = '\0'; 204185222Ssam if ((cur->symlink = strdup(slink)) == NULL) 205185222Ssam err(1, "Memory allocation error"); 206185222Ssam } 207185222Ssam } 208230795Sjkim assert(first != NULL); 209230795Sjkim if (join == NULL) 210230795Sjkim for (cur = first->next; cur != NULL; cur = cur->next) 211230795Sjkim cur->first = first; 212185222Ssam if (closedir(dirp) == -1) 213230795Sjkim err(1, "Can't closedir `%s/%s'", root, dir); 214185222Ssam return (first); 215185222Ssam} 216185222Ssam 217185222Ssamstatic fsnode * 218230795Sjkimcreate_fsnode(const char *root, const char *path, const char *name, 219230795Sjkim struct stat *stbuf) 220185222Ssam{ 221185222Ssam fsnode *cur; 222185222Ssam 223185222Ssam if ((cur = calloc(1, sizeof(fsnode))) == NULL || 224230795Sjkim (cur->path = strdup(path)) == NULL || 225185222Ssam (cur->name = strdup(name)) == NULL || 226185222Ssam (cur->inode = calloc(1, sizeof(fsinode))) == NULL) 227185222Ssam err(1, "Memory allocation error"); 228230795Sjkim cur->root = root; 229185222Ssam cur->type = stbuf->st_mode & S_IFMT; 230185222Ssam cur->inode->nlink = 1; 231185222Ssam cur->inode->st = *stbuf; 232185222Ssam return (cur); 233185222Ssam} 234185222Ssam 235185222Ssam/* 236214921Scognet * free_fsnodes -- 237214921Scognet * Removes node from tree and frees it and all of 238228990Suqs * its descendants. 239214921Scognet */ 240214921Scognetvoid 241214921Scognetfree_fsnodes(fsnode *node) 242214921Scognet{ 243214921Scognet fsnode *cur, *next; 244214921Scognet 245214921Scognet assert(node != NULL); 246214921Scognet 247214921Scognet /* for ".", start with actual parent node */ 248214921Scognet if (node->first == node) { 249214921Scognet assert(node->name[0] == '.' && node->name[1] == '\0'); 250214921Scognet if (node->parent) { 251214921Scognet assert(node->parent->child == node); 252214921Scognet node = node->parent; 253214921Scognet } 254214921Scognet } 255214921Scognet 256214921Scognet /* Find ourselves in our sibling list and unlink */ 257214921Scognet if (node->first != node) { 258214921Scognet for (cur = node->first; cur->next; cur = cur->next) { 259214921Scognet if (cur->next == node) { 260214921Scognet cur->next = node->next; 261214921Scognet node->next = NULL; 262214921Scognet break; 263214921Scognet } 264214921Scognet } 265214921Scognet } 266214921Scognet 267214921Scognet for (cur = node; cur != NULL; cur = next) { 268214921Scognet next = cur->next; 269214921Scognet if (cur->child) { 270214921Scognet cur->child->parent = NULL; 271214921Scognet free_fsnodes(cur->child); 272214921Scognet } 273214921Scognet if (cur->inode->nlink-- == 1) 274214921Scognet free(cur->inode); 275214921Scognet if (cur->symlink) 276214921Scognet free(cur->symlink); 277230795Sjkim free(cur->path); 278214921Scognet free(cur->name); 279214921Scognet free(cur); 280214921Scognet } 281214921Scognet} 282214921Scognet 283214921Scognet/* 284185222Ssam * apply_specfile -- 285185222Ssam * read in the mtree(8) specfile, and apply it to the tree 286185222Ssam * at dir,parent. parameters in parent on equivalent types 287185222Ssam * will be changed to those found in specfile, and missing 288185222Ssam * entries will be added. 289185222Ssam */ 290185222Ssamvoid 291214921Scognetapply_specfile(const char *specfile, const char *dir, fsnode *parent, int speconly) 292185222Ssam{ 293185222Ssam struct timeval start; 294185222Ssam FILE *fp; 295185222Ssam NODE *root; 296185222Ssam 297185222Ssam assert(specfile != NULL); 298185222Ssam assert(parent != NULL); 299185222Ssam 300185222Ssam if (debug & DEBUG_APPLY_SPECFILE) 301185222Ssam printf("apply_specfile: %s, %s %p\n", specfile, dir, parent); 302185222Ssam 303185222Ssam /* read in the specfile */ 304185222Ssam if ((fp = fopen(specfile, "r")) == NULL) 305185222Ssam err(1, "Can't open `%s'", specfile); 306185222Ssam TIMER_START(start); 307250407Sbrooks root = spec(fp); 308185222Ssam TIMER_RESULTS(start, "spec"); 309185222Ssam if (fclose(fp) == EOF) 310185222Ssam err(1, "Can't close `%s'", specfile); 311185222Ssam 312185222Ssam /* perform some sanity checks */ 313185222Ssam if (root == NULL) 314185222Ssam errx(1, "Specfile `%s' did not contain a tree", specfile); 315185222Ssam assert(strcmp(root->name, ".") == 0); 316185222Ssam assert(root->type == F_DIR); 317185222Ssam 318185222Ssam /* merge in the changes */ 319214921Scognet apply_specdir(dir, root, parent, speconly); 320214921Scognet 321185222Ssam} 322185222Ssam 323185222Ssamstatic void 324214921Scognetapply_specdir(const char *dir, NODE *specnode, fsnode *dirnode, int speconly) 325185222Ssam{ 326185222Ssam char path[MAXPATHLEN + 1]; 327185222Ssam NODE *curnode; 328185222Ssam fsnode *curfsnode; 329185222Ssam 330185222Ssam assert(specnode != NULL); 331185222Ssam assert(dirnode != NULL); 332185222Ssam 333185222Ssam if (debug & DEBUG_APPLY_SPECFILE) 334185222Ssam printf("apply_specdir: %s %p %p\n", dir, specnode, dirnode); 335185222Ssam 336185222Ssam if (specnode->type != F_DIR) 337185222Ssam errx(1, "Specfile node `%s/%s' is not a directory", 338185222Ssam dir, specnode->name); 339185222Ssam if (dirnode->type != S_IFDIR) 340185222Ssam errx(1, "Directory node `%s/%s' is not a directory", 341185222Ssam dir, dirnode->name); 342185222Ssam 343185222Ssam apply_specentry(dir, specnode, dirnode); 344185222Ssam 345214921Scognet /* Remove any filesystem nodes not found in specfile */ 346214921Scognet /* XXX inefficient. This is O^2 in each dir and it would 347214921Scognet * have been better never to have walked this part of the tree 348214921Scognet * to begin with 349214921Scognet */ 350214921Scognet if (speconly) { 351214921Scognet fsnode *next; 352214921Scognet assert(dirnode->name[0] == '.' && dirnode->name[1] == '\0'); 353214921Scognet for (curfsnode = dirnode->next; curfsnode != NULL; curfsnode = next) { 354214921Scognet next = curfsnode->next; 355214921Scognet for (curnode = specnode->child; curnode != NULL; 356214921Scognet curnode = curnode->next) { 357214921Scognet if (strcmp(curnode->name, curfsnode->name) == 0) 358214921Scognet break; 359214921Scognet } 360214921Scognet if (curnode == NULL) { 361214921Scognet if (debug & DEBUG_APPLY_SPECONLY) { 362214921Scognet printf("apply_specdir: trimming %s/%s %p\n", dir, curfsnode->name, curfsnode); 363214921Scognet } 364214921Scognet free_fsnodes(curfsnode); 365214921Scognet } 366214921Scognet } 367214921Scognet } 368214921Scognet 369185222Ssam /* now walk specnode->child matching up with dirnode */ 370185222Ssam for (curnode = specnode->child; curnode != NULL; 371185222Ssam curnode = curnode->next) { 372185222Ssam if (debug & DEBUG_APPLY_SPECENTRY) 373185222Ssam printf("apply_specdir: spec %s\n", 374185222Ssam curnode->name); 375185222Ssam for (curfsnode = dirnode->next; curfsnode != NULL; 376185222Ssam curfsnode = curfsnode->next) { 377185222Ssam#if 0 /* too verbose for now */ 378185222Ssam if (debug & DEBUG_APPLY_SPECENTRY) 379185222Ssam printf("apply_specdir: dirent %s\n", 380185222Ssam curfsnode->name); 381185222Ssam#endif 382185222Ssam if (strcmp(curnode->name, curfsnode->name) == 0) 383185222Ssam break; 384185222Ssam } 385185222Ssam if (snprintf(path, sizeof(path), "%s/%s", 386185222Ssam dir, curnode->name) >= sizeof(path)) 387185222Ssam errx(1, "Pathname too long."); 388185222Ssam if (curfsnode == NULL) { /* need new entry */ 389185222Ssam struct stat stbuf; 390185222Ssam 391185222Ssam /* 392185222Ssam * don't add optional spec entries 393185222Ssam * that lack an existing fs entry 394185222Ssam */ 395185222Ssam if ((curnode->flags & F_OPT) && 396185222Ssam lstat(path, &stbuf) == -1) 397185222Ssam continue; 398185222Ssam 399185222Ssam /* check that enough info is provided */ 400185222Ssam#define NODETEST(t, m) \ 401185222Ssam if (!(t)) \ 402185222Ssam errx(1, "`%s': %s not provided", path, m) 403185222Ssam NODETEST(curnode->flags & F_TYPE, "type"); 404185222Ssam NODETEST(curnode->flags & F_MODE, "mode"); 405185222Ssam /* XXX: require F_TIME ? */ 406185222Ssam NODETEST(curnode->flags & F_GID || 407185222Ssam curnode->flags & F_GNAME, "group"); 408185222Ssam NODETEST(curnode->flags & F_UID || 409185222Ssam curnode->flags & F_UNAME, "user"); 410214921Scognet/* if (curnode->type == F_BLOCK || curnode->type == F_CHAR) 411214921Scognet NODETEST(curnode->flags & F_DEV, 412214921Scognet "device number");*/ 413185222Ssam#undef NODETEST 414185222Ssam 415185222Ssam if (debug & DEBUG_APPLY_SPECFILE) 416185222Ssam printf("apply_specdir: adding %s\n", 417185222Ssam curnode->name); 418185222Ssam /* build minimal fsnode */ 419185222Ssam memset(&stbuf, 0, sizeof(stbuf)); 420185222Ssam stbuf.st_mode = nodetoino(curnode->type); 421185222Ssam stbuf.st_nlink = 1; 422185222Ssam stbuf.st_mtime = stbuf.st_atime = 423185222Ssam stbuf.st_ctime = start_time.tv_sec; 424185222Ssam#if HAVE_STRUCT_STAT_ST_MTIMENSEC 425185222Ssam stbuf.st_mtimensec = stbuf.st_atimensec = 426185222Ssam stbuf.st_ctimensec = start_time.tv_nsec; 427185222Ssam#endif 428230795Sjkim curfsnode = create_fsnode(".", ".", curnode->name, 429230795Sjkim &stbuf); 430185222Ssam curfsnode->parent = dirnode->parent; 431185222Ssam curfsnode->first = dirnode; 432185222Ssam curfsnode->next = dirnode->next; 433185222Ssam dirnode->next = curfsnode; 434185222Ssam if (curfsnode->type == S_IFDIR) { 435185222Ssam /* for dirs, make "." entry as well */ 436230795Sjkim curfsnode->child = create_fsnode(".", ".", ".", 437230795Sjkim &stbuf); 438185222Ssam curfsnode->child->parent = curfsnode; 439185222Ssam curfsnode->child->first = curfsnode->child; 440185222Ssam } 441185222Ssam if (curfsnode->type == S_IFLNK) { 442185222Ssam assert(curnode->slink != NULL); 443185222Ssam /* for symlinks, copy the target */ 444185222Ssam if ((curfsnode->symlink = 445185222Ssam strdup(curnode->slink)) == NULL) 446185222Ssam err(1, "Memory allocation error"); 447185222Ssam } 448185222Ssam } 449185222Ssam apply_specentry(dir, curnode, curfsnode); 450185222Ssam if (curnode->type == F_DIR) { 451185222Ssam if (curfsnode->type != S_IFDIR) 452185222Ssam errx(1, "`%s' is not a directory", path); 453185222Ssam assert (curfsnode->child != NULL); 454214921Scognet apply_specdir(path, curnode, curfsnode->child, speconly); 455185222Ssam } 456185222Ssam } 457185222Ssam} 458185222Ssam 459185222Ssamstatic void 460185222Ssamapply_specentry(const char *dir, NODE *specnode, fsnode *dirnode) 461185222Ssam{ 462185222Ssam 463185222Ssam assert(specnode != NULL); 464185222Ssam assert(dirnode != NULL); 465185222Ssam 466185222Ssam if (nodetoino(specnode->type) != dirnode->type) 467185222Ssam errx(1, "`%s/%s' type mismatch: specfile %s, tree %s", 468185222Ssam dir, specnode->name, inode_type(nodetoino(specnode->type)), 469185222Ssam inode_type(dirnode->type)); 470185222Ssam 471185222Ssam if (debug & DEBUG_APPLY_SPECENTRY) 472185222Ssam printf("apply_specentry: %s/%s\n", dir, dirnode->name); 473185222Ssam 474185222Ssam#define ASEPRINT(t, b, o, n) \ 475185222Ssam if (debug & DEBUG_APPLY_SPECENTRY) \ 476185222Ssam printf("\t\t\tchanging %s from " b " to " b "\n", \ 477185222Ssam t, o, n) 478185222Ssam 479185222Ssam if (specnode->flags & (F_GID | F_GNAME)) { 480185222Ssam ASEPRINT("gid", "%d", 481185222Ssam dirnode->inode->st.st_gid, specnode->st_gid); 482185222Ssam dirnode->inode->st.st_gid = specnode->st_gid; 483185222Ssam } 484185222Ssam if (specnode->flags & F_MODE) { 485185222Ssam ASEPRINT("mode", "%#o", 486185222Ssam dirnode->inode->st.st_mode & ALLPERMS, specnode->st_mode); 487185222Ssam dirnode->inode->st.st_mode &= ~ALLPERMS; 488185222Ssam dirnode->inode->st.st_mode |= (specnode->st_mode & ALLPERMS); 489185222Ssam } 490185222Ssam /* XXX: ignoring F_NLINK for now */ 491185222Ssam if (specnode->flags & F_SIZE) { 492185222Ssam ASEPRINT("size", "%lld", 493185222Ssam (long long)dirnode->inode->st.st_size, 494185222Ssam (long long)specnode->st_size); 495185222Ssam dirnode->inode->st.st_size = specnode->st_size; 496185222Ssam } 497185222Ssam if (specnode->flags & F_SLINK) { 498185222Ssam assert(dirnode->symlink != NULL); 499185222Ssam assert(specnode->slink != NULL); 500185222Ssam ASEPRINT("symlink", "%s", dirnode->symlink, specnode->slink); 501185222Ssam free(dirnode->symlink); 502185222Ssam if ((dirnode->symlink = strdup(specnode->slink)) == NULL) 503185222Ssam err(1, "Memory allocation error"); 504185222Ssam } 505185222Ssam if (specnode->flags & F_TIME) { 506185222Ssam ASEPRINT("time", "%ld", 507185222Ssam (long)dirnode->inode->st.st_mtime, 508185222Ssam (long)specnode->st_mtimespec.tv_sec); 509185222Ssam dirnode->inode->st.st_mtime = specnode->st_mtimespec.tv_sec; 510185222Ssam dirnode->inode->st.st_atime = specnode->st_mtimespec.tv_sec; 511185222Ssam dirnode->inode->st.st_ctime = start_time.tv_sec; 512185222Ssam#if HAVE_STRUCT_STAT_ST_MTIMENSEC 513185222Ssam dirnode->inode->st.st_mtimensec = specnode->st_mtimespec.tv_nsec; 514185222Ssam dirnode->inode->st.st_atimensec = specnode->st_mtimespec.tv_nsec; 515185222Ssam dirnode->inode->st.st_ctimensec = start_time.tv_nsec; 516185222Ssam#endif 517185222Ssam } 518185222Ssam if (specnode->flags & (F_UID | F_UNAME)) { 519185222Ssam ASEPRINT("uid", "%d", 520185222Ssam dirnode->inode->st.st_uid, specnode->st_uid); 521185222Ssam dirnode->inode->st.st_uid = specnode->st_uid; 522185222Ssam } 523185222Ssam#if HAVE_STRUCT_STAT_ST_FLAGS 524185222Ssam if (specnode->flags & F_FLAGS) { 525185222Ssam ASEPRINT("flags", "%#lX", 526185222Ssam (unsigned long)dirnode->inode->st.st_flags, 527185222Ssam (unsigned long)specnode->st_flags); 528185222Ssam dirnode->inode->st.st_flags = specnode->st_flags; 529185222Ssam } 530185222Ssam#endif 531214921Scognet/* if (specnode->flags & F_DEV) { 532214921Scognet ASEPRINT("rdev", "%#llx", 533214921Scognet (unsigned long long)dirnode->inode->st.st_rdev, 534214921Scognet (unsigned long long)specnode->st_rdev); 535214921Scognet dirnode->inode->st.st_rdev = specnode->st_rdev; 536214921Scognet }*/ 537185222Ssam#undef ASEPRINT 538185222Ssam 539185222Ssam dirnode->flags |= FSNODE_F_HASSPEC; 540185222Ssam} 541185222Ssam 542185222Ssam 543185222Ssam/* 544185222Ssam * dump_fsnodes -- 545230795Sjkim * dump the fsnodes from `cur' 546185222Ssam */ 547185222Ssamvoid 548230795Sjkimdump_fsnodes(fsnode *root) 549185222Ssam{ 550185222Ssam fsnode *cur; 551185222Ssam char path[MAXPATHLEN + 1]; 552185222Ssam 553230795Sjkim printf("dump_fsnodes: %s %p\n", root->path, root); 554185222Ssam for (cur = root; cur != NULL; cur = cur->next) { 555230795Sjkim if (snprintf(path, sizeof(path), "%s/%s", cur->path, 556230795Sjkim cur->name) >= (int)sizeof(path)) 557185222Ssam errx(1, "Pathname too long."); 558185222Ssam 559185222Ssam if (debug & DEBUG_DUMP_FSNODES_VERBOSE) 560185222Ssam printf("cur=%8p parent=%8p first=%8p ", 561185222Ssam cur, cur->parent, cur->first); 562185222Ssam printf("%7s: %s", inode_type(cur->type), path); 563185222Ssam if (S_ISLNK(cur->type)) { 564185222Ssam assert(cur->symlink != NULL); 565185222Ssam printf(" -> %s", cur->symlink); 566185222Ssam } else { 567185222Ssam assert (cur->symlink == NULL); 568185222Ssam } 569185222Ssam if (cur->inode->nlink > 1) 570185222Ssam printf(", nlinks=%d", cur->inode->nlink); 571185222Ssam putchar('\n'); 572185222Ssam 573185222Ssam if (cur->child) { 574185222Ssam assert (cur->type == S_IFDIR); 575230795Sjkim dump_fsnodes(cur->child); 576185222Ssam } 577185222Ssam } 578230795Sjkim printf("dump_fsnodes: finished %s/%s\n", root->path, root->name); 579185222Ssam} 580185222Ssam 581185222Ssam 582185222Ssam/* 583185222Ssam * inode_type -- 584185222Ssam * for a given inode type `mode', return a descriptive string. 585214921Scognet * for most cases, uses inotype() from mtree/misc.c 586185222Ssam */ 587185222Ssamconst char * 588185222Ssaminode_type(mode_t mode) 589185222Ssam{ 590186256Ssam if (S_ISREG(mode)) 591186256Ssam return ("file"); 592185222Ssam if (S_ISLNK(mode)) 593186256Ssam return ("symlink"); 594186256Ssam if (S_ISDIR(mode)) 595186256Ssam return ("dir"); 596186256Ssam if (S_ISLNK(mode)) 597186256Ssam return ("link"); 598186256Ssam if (S_ISFIFO(mode)) 599186256Ssam return ("fifo"); 600186256Ssam if (S_ISSOCK(mode)) 601186256Ssam return ("socket"); 602186256Ssam /* XXX should not happen but handle them */ 603186256Ssam if (S_ISCHR(mode)) 604186256Ssam return ("char"); 605186256Ssam if (S_ISBLK(mode)) 606186256Ssam return ("block"); 607186256Ssam return ("unknown"); 608185222Ssam} 609185222Ssam 610185222Ssam 611185222Ssam/* 612185222Ssam * link_check -- 613214921Scognet * return pointer to fsinode matching `entry's st_ino & st_dev if it exists, 614185222Ssam * otherwise add `entry' to table and return NULL 615185222Ssam */ 616214921Scognet/* This was borrowed from du.c and tweaked to keep an fsnode 617214921Scognet * pointer instead. -- dbj@netbsd.org 618214921Scognet */ 619247052Sbrooksfsinode * 620185222Ssamlink_check(fsinode *entry) 621185222Ssam{ 622214921Scognet static struct entry { 623214921Scognet fsinode *data; 624214921Scognet } *htable; 625214921Scognet static int htshift; /* log(allocated size) */ 626214921Scognet static int htmask; /* allocated size - 1 */ 627214921Scognet static int htused; /* 2*number of insertions */ 628214921Scognet int h, h2; 629214921Scognet uint64_t tmp; 630214921Scognet /* this constant is (1<<64)/((1+sqrt(5))/2) 631214921Scognet * aka (word size)/(golden ratio) 632214921Scognet */ 633214921Scognet const uint64_t HTCONST = 11400714819323198485ULL; 634214921Scognet const int HTBITS = 64; 635214921Scognet 636214921Scognet /* Never store zero in hashtable */ 637214921Scognet assert(entry); 638185222Ssam 639214921Scognet /* Extend hash table if necessary, keep load under 0.5 */ 640214921Scognet if (htused<<1 >= htmask) { 641214921Scognet struct entry *ohtable; 642185222Ssam 643214921Scognet if (!htable) 644214921Scognet htshift = 10; /* starting hashtable size */ 645214921Scognet else 646214921Scognet htshift++; /* exponential hashtable growth */ 647185222Ssam 648214921Scognet htmask = (1 << htshift) - 1; 649214921Scognet htused = 0; 650214921Scognet 651214921Scognet ohtable = htable; 652214921Scognet htable = calloc(htmask+1, sizeof(*htable)); 653214921Scognet if (!htable) 654214921Scognet err(1, "Memory allocation error"); 655214921Scognet 656214921Scognet /* populate newly allocated hashtable */ 657214921Scognet if (ohtable) { 658214921Scognet int i; 659214921Scognet for (i = 0; i <= htmask>>1; i++) 660214921Scognet if (ohtable[i].data) 661214921Scognet link_check(ohtable[i].data); 662214921Scognet free(ohtable); 663185222Ssam } 664185222Ssam } 665185222Ssam 666214921Scognet /* multiplicative hashing */ 667214921Scognet tmp = entry->st.st_dev; 668214921Scognet tmp <<= HTBITS>>1; 669214921Scognet tmp |= entry->st.st_ino; 670214921Scognet tmp *= HTCONST; 671214921Scognet h = tmp >> (HTBITS - htshift); 672214921Scognet h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */ 673214921Scognet 674214921Scognet /* open address hashtable search with double hash probing */ 675214921Scognet while (htable[h].data) { 676214921Scognet if ((htable[h].data->st.st_ino == entry->st.st_ino) && 677214921Scognet (htable[h].data->st.st_dev == entry->st.st_dev)) { 678214921Scognet return htable[h].data; 679214921Scognet } 680214921Scognet h = (h + h2) & htmask; 681185222Ssam } 682185222Ssam 683214921Scognet /* Insert the current entry into hashtable */ 684214921Scognet htable[h].data = entry; 685214921Scognet htused++; 686214921Scognet return NULL; 687185222Ssam} 688