walk.c revision 186256
1185222Ssam/* $NetBSD: walk.c,v 1.17 2004/06/20 22:20:18 jmc 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 * The function link_check() was inspired from NetBSD's usr.bin/du/du.c, 40185222Ssam * which has the following copyright notice: 41185222Ssam * 42185222Ssam * 43185222Ssam * Copyright (c) 1989, 1993, 1994 44185222Ssam * The Regents of the University of California. All rights reserved. 45185222Ssam * 46185222Ssam * This code is derived from software contributed to Berkeley by 47185222Ssam * Chris Newcomb. 48185222Ssam * 49185222Ssam * Redistribution and use in source and binary forms, with or without 50185222Ssam * modification, are permitted provided that the following conditions 51185222Ssam * are met: 52185222Ssam * 1. Redistributions of source code must retain the above copyright 53185222Ssam * notice, this list of conditions and the following disclaimer. 54185222Ssam * 2. Redistributions in binary form must reproduce the above copyright 55185222Ssam * notice, this list of conditions and the following disclaimer in the 56185222Ssam * documentation and/or other materials provided with the distribution. 57185222Ssam * 3. Neither the name of the University nor the names of its contributors 58185222Ssam * may be used to endorse or promote products derived from this software 59185222Ssam * without specific prior written permission. 60185222Ssam * 61185222Ssam * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 62185222Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 63185222Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 64185222Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 65185222Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 66185222Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 67185222Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 68185222Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 69185222Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 70185222Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 71185222Ssam * SUCH DAMAGE. 72185222Ssam */ 73185222Ssam 74185222Ssam#if HAVE_NBTOOL_CONFIG_H 75185222Ssam#include "nbtool_config.h" 76185222Ssam#endif 77185222Ssam 78185222Ssam#include <sys/cdefs.h> 79185222Ssam#if defined(__RCSID) && !defined(__lint) 80185222Ssam__RCSID("$NetBSD: walk.c,v 1.17 2004/06/20 22:20:18 jmc Exp $"); 81185222Ssam#endif /* !__lint */ 82185222Ssam 83185222Ssam#include <sys/param.h> 84185222Ssam 85185222Ssam#include <assert.h> 86185222Ssam#include <errno.h> 87185222Ssam#include <fcntl.h> 88185222Ssam#include <stdio.h> 89185222Ssam#include <dirent.h> 90185222Ssam#include <stdlib.h> 91185222Ssam#include <string.h> 92185222Ssam#include <unistd.h> 93185222Ssam 94185222Ssam#include "makefs.h" 95186256Ssam 96185222Ssam#include "mtree.h" 97186256Ssam#include "extern.h" /* NB: mtree */ 98185222Ssam 99185222Ssamstatic void apply_specdir(const char *, NODE *, fsnode *); 100185222Ssamstatic void apply_specentry(const char *, NODE *, fsnode *); 101185222Ssamstatic fsnode *create_fsnode(const char *, struct stat *); 102185222Ssamstatic fsinode *link_check(fsinode *); 103185222Ssam 104185222Ssam 105185222Ssam/* 106185222Ssam * walk_dir -- 107185222Ssam * build a tree of fsnodes from `dir', with a parent fsnode of `parent' 108185222Ssam * (which may be NULL for the root of the tree). 109185222Ssam * each "level" is a directory, with the "." entry guaranteed to be 110185222Ssam * at the start of the list, and without ".." entries. 111185222Ssam */ 112185222Ssamfsnode * 113185222Ssamwalk_dir(const char *dir, fsnode *parent) 114185222Ssam{ 115185222Ssam fsnode *first, *cur, *prev; 116185222Ssam DIR *dirp; 117185222Ssam struct dirent *dent; 118185222Ssam char path[MAXPATHLEN + 1]; 119185222Ssam struct stat stbuf; 120185222Ssam 121185222Ssam assert(dir != NULL); 122185222Ssam 123185222Ssam if (debug & DEBUG_WALK_DIR) 124185222Ssam printf("walk_dir: %s %p\n", dir, parent); 125185222Ssam if ((dirp = opendir(dir)) == NULL) 126185222Ssam err(1, "Can't opendir `%s'", dir); 127185222Ssam first = prev = NULL; 128185222Ssam while ((dent = readdir(dirp)) != NULL) { 129185222Ssam if (strcmp(dent->d_name, "..") == 0) 130185222Ssam continue; 131185222Ssam if (debug & DEBUG_WALK_DIR_NODE) 132185222Ssam printf("scanning %s/%s\n", dir, dent->d_name); 133185222Ssam if (snprintf(path, sizeof(path), "%s/%s", dir, dent->d_name) 134185222Ssam >= sizeof(path)) 135185222Ssam errx(1, "Pathname too long."); 136185222Ssam if (lstat(path, &stbuf) == -1) 137185222Ssam err(1, "Can't lstat `%s'", path); 138185222Ssam#ifdef S_ISSOCK 139185222Ssam if (S_ISSOCK(stbuf.st_mode & S_IFMT)) { 140185222Ssam if (debug & DEBUG_WALK_DIR_NODE) 141185222Ssam printf(" skipping socket %s\n", path); 142185222Ssam continue; 143185222Ssam } 144185222Ssam#endif 145185222Ssam 146185222Ssam cur = create_fsnode(dent->d_name, &stbuf); 147185222Ssam cur->parent = parent; 148185222Ssam if (strcmp(dent->d_name, ".") == 0) { 149185222Ssam /* ensure "." is at the start of the list */ 150185222Ssam cur->next = first; 151185222Ssam first = cur; 152185222Ssam if (! prev) 153185222Ssam prev = cur; 154185222Ssam } else { /* not "." */ 155185222Ssam if (prev) 156185222Ssam prev->next = cur; 157185222Ssam prev = cur; 158185222Ssam if (!first) 159185222Ssam first = cur; 160185222Ssam if (S_ISDIR(cur->type)) { 161185222Ssam cur->child = walk_dir(path, cur); 162185222Ssam continue; 163185222Ssam } 164185222Ssam } 165185222Ssam if (stbuf.st_nlink > 1) { 166185222Ssam fsinode *curino; 167185222Ssam 168185222Ssam curino = link_check(cur->inode); 169185222Ssam if (curino != NULL) { 170185222Ssam free(cur->inode); 171185222Ssam cur->inode = curino; 172185222Ssam cur->inode->nlink++; 173185222Ssam } 174185222Ssam } 175185222Ssam if (S_ISLNK(cur->type)) { 176185222Ssam char slink[PATH_MAX+1]; 177185222Ssam int llen; 178185222Ssam 179185222Ssam llen = readlink(path, slink, sizeof(slink) - 1); 180185222Ssam if (llen == -1) 181185222Ssam err(1, "Readlink `%s'", path); 182185222Ssam slink[llen] = '\0'; 183185222Ssam if ((cur->symlink = strdup(slink)) == NULL) 184185222Ssam err(1, "Memory allocation error"); 185185222Ssam } 186185222Ssam } 187185222Ssam for (cur = first; cur != NULL; cur = cur->next) 188185222Ssam cur->first = first; 189185222Ssam if (closedir(dirp) == -1) 190185222Ssam err(1, "Can't closedir `%s'", dir); 191185222Ssam return (first); 192185222Ssam} 193185222Ssam 194185222Ssamstatic fsnode * 195185222Ssamcreate_fsnode(const char *name, struct stat *stbuf) 196185222Ssam{ 197185222Ssam fsnode *cur; 198185222Ssam 199185222Ssam if ((cur = calloc(1, sizeof(fsnode))) == NULL || 200185222Ssam (cur->name = strdup(name)) == NULL || 201185222Ssam (cur->inode = calloc(1, sizeof(fsinode))) == NULL) 202185222Ssam err(1, "Memory allocation error"); 203185222Ssam cur->type = stbuf->st_mode & S_IFMT; 204185222Ssam cur->inode->nlink = 1; 205185222Ssam cur->inode->st = *stbuf; 206185222Ssam return (cur); 207185222Ssam} 208185222Ssam 209185222Ssam/* 210185222Ssam * apply_specfile -- 211185222Ssam * read in the mtree(8) specfile, and apply it to the tree 212185222Ssam * at dir,parent. parameters in parent on equivalent types 213185222Ssam * will be changed to those found in specfile, and missing 214185222Ssam * entries will be added. 215185222Ssam */ 216185222Ssamvoid 217185222Ssamapply_specfile(const char *specfile, const char *dir, fsnode *parent) 218185222Ssam{ 219185222Ssam struct timeval start; 220185222Ssam FILE *fp; 221185222Ssam NODE *root; 222185222Ssam 223185222Ssam assert(specfile != NULL); 224185222Ssam assert(parent != NULL); 225185222Ssam 226185222Ssam if (debug & DEBUG_APPLY_SPECFILE) 227185222Ssam printf("apply_specfile: %s, %s %p\n", specfile, dir, parent); 228185222Ssam 229185222Ssam /* read in the specfile */ 230185222Ssam if ((fp = fopen(specfile, "r")) == NULL) 231185222Ssam err(1, "Can't open `%s'", specfile); 232185222Ssam TIMER_START(start); 233186256Ssam root = mtree_readspec(fp); 234185222Ssam TIMER_RESULTS(start, "spec"); 235185222Ssam if (fclose(fp) == EOF) 236185222Ssam err(1, "Can't close `%s'", specfile); 237185222Ssam 238185222Ssam /* perform some sanity checks */ 239185222Ssam if (root == NULL) 240185222Ssam errx(1, "Specfile `%s' did not contain a tree", specfile); 241185222Ssam assert(strcmp(root->name, ".") == 0); 242185222Ssam assert(root->type == F_DIR); 243185222Ssam 244185222Ssam /* merge in the changes */ 245185222Ssam apply_specdir(dir, root, parent); 246185222Ssam} 247185222Ssam 248186256Ssamstatic u_int 249186256Ssamnodetoino(u_int type) 250186256Ssam{ 251186256Ssam 252186256Ssam switch (type) { 253186256Ssam case F_BLOCK: 254186256Ssam return S_IFBLK; 255186256Ssam case F_CHAR: 256186256Ssam return S_IFCHR; 257186256Ssam case F_DIR: 258186256Ssam return S_IFDIR; 259186256Ssam case F_FIFO: 260186256Ssam return S_IFIFO; 261186256Ssam case F_FILE: 262186256Ssam return S_IFREG; 263186256Ssam case F_LINK: 264186256Ssam return S_IFLNK; 265186256Ssam case F_SOCK: 266186256Ssam return S_IFSOCK; 267186256Ssam default: 268186256Ssam printf("unknown type %d", type); 269186256Ssam abort(); 270186256Ssam } 271186256Ssam /* NOTREACHED */ 272186256Ssam} 273186256Ssam 274185222Ssamstatic void 275185222Ssamapply_specdir(const char *dir, NODE *specnode, fsnode *dirnode) 276185222Ssam{ 277185222Ssam char path[MAXPATHLEN + 1]; 278185222Ssam NODE *curnode; 279185222Ssam fsnode *curfsnode; 280185222Ssam 281185222Ssam assert(specnode != NULL); 282185222Ssam assert(dirnode != NULL); 283185222Ssam 284185222Ssam if (debug & DEBUG_APPLY_SPECFILE) 285185222Ssam printf("apply_specdir: %s %p %p\n", dir, specnode, dirnode); 286185222Ssam 287185222Ssam if (specnode->type != F_DIR) 288185222Ssam errx(1, "Specfile node `%s/%s' is not a directory", 289185222Ssam dir, specnode->name); 290185222Ssam if (dirnode->type != S_IFDIR) 291185222Ssam errx(1, "Directory node `%s/%s' is not a directory", 292185222Ssam dir, dirnode->name); 293185222Ssam 294185222Ssam apply_specentry(dir, specnode, dirnode); 295185222Ssam 296185222Ssam /* now walk specnode->child matching up with dirnode */ 297185222Ssam for (curnode = specnode->child; curnode != NULL; 298185222Ssam curnode = curnode->next) { 299185222Ssam if (debug & DEBUG_APPLY_SPECENTRY) 300185222Ssam printf("apply_specdir: spec %s\n", 301185222Ssam curnode->name); 302185222Ssam for (curfsnode = dirnode->next; curfsnode != NULL; 303185222Ssam curfsnode = curfsnode->next) { 304185222Ssam#if 0 /* too verbose for now */ 305185222Ssam if (debug & DEBUG_APPLY_SPECENTRY) 306185222Ssam printf("apply_specdir: dirent %s\n", 307185222Ssam curfsnode->name); 308185222Ssam#endif 309185222Ssam if (strcmp(curnode->name, curfsnode->name) == 0) 310185222Ssam break; 311185222Ssam } 312185222Ssam if (snprintf(path, sizeof(path), "%s/%s", 313185222Ssam dir, curnode->name) >= sizeof(path)) 314185222Ssam errx(1, "Pathname too long."); 315185222Ssam if (curfsnode == NULL) { /* need new entry */ 316185222Ssam struct stat stbuf; 317185222Ssam 318185222Ssam /* 319185222Ssam * don't add optional spec entries 320185222Ssam * that lack an existing fs entry 321185222Ssam */ 322185222Ssam if ((curnode->flags & F_OPT) && 323185222Ssam lstat(path, &stbuf) == -1) 324185222Ssam continue; 325185222Ssam 326185222Ssam /* check that enough info is provided */ 327185222Ssam#define NODETEST(t, m) \ 328185222Ssam if (!(t)) \ 329185222Ssam errx(1, "`%s': %s not provided", path, m) 330185222Ssam NODETEST(curnode->flags & F_TYPE, "type"); 331185222Ssam NODETEST(curnode->flags & F_MODE, "mode"); 332185222Ssam /* XXX: require F_TIME ? */ 333185222Ssam NODETEST(curnode->flags & F_GID || 334185222Ssam curnode->flags & F_GNAME, "group"); 335185222Ssam NODETEST(curnode->flags & F_UID || 336185222Ssam curnode->flags & F_UNAME, "user"); 337185222Ssam#undef NODETEST 338185222Ssam 339185222Ssam if (debug & DEBUG_APPLY_SPECFILE) 340185222Ssam printf("apply_specdir: adding %s\n", 341185222Ssam curnode->name); 342185222Ssam /* build minimal fsnode */ 343185222Ssam memset(&stbuf, 0, sizeof(stbuf)); 344185222Ssam stbuf.st_mode = nodetoino(curnode->type); 345185222Ssam stbuf.st_nlink = 1; 346185222Ssam stbuf.st_mtime = stbuf.st_atime = 347185222Ssam stbuf.st_ctime = start_time.tv_sec; 348185222Ssam#if HAVE_STRUCT_STAT_ST_MTIMENSEC 349185222Ssam stbuf.st_mtimensec = stbuf.st_atimensec = 350185222Ssam stbuf.st_ctimensec = start_time.tv_nsec; 351185222Ssam#endif 352185222Ssam curfsnode = create_fsnode(curnode->name, &stbuf); 353185222Ssam curfsnode->parent = dirnode->parent; 354185222Ssam curfsnode->first = dirnode; 355185222Ssam curfsnode->next = dirnode->next; 356185222Ssam dirnode->next = curfsnode; 357185222Ssam if (curfsnode->type == S_IFDIR) { 358185222Ssam /* for dirs, make "." entry as well */ 359185222Ssam curfsnode->child = create_fsnode(".", &stbuf); 360185222Ssam curfsnode->child->parent = curfsnode; 361185222Ssam curfsnode->child->first = curfsnode->child; 362185222Ssam } 363185222Ssam if (curfsnode->type == S_IFLNK) { 364185222Ssam assert(curnode->slink != NULL); 365185222Ssam /* for symlinks, copy the target */ 366185222Ssam if ((curfsnode->symlink = 367185222Ssam strdup(curnode->slink)) == NULL) 368185222Ssam err(1, "Memory allocation error"); 369185222Ssam } 370185222Ssam } 371185222Ssam apply_specentry(dir, curnode, curfsnode); 372185222Ssam if (curnode->type == F_DIR) { 373185222Ssam if (curfsnode->type != S_IFDIR) 374185222Ssam errx(1, "`%s' is not a directory", path); 375185222Ssam assert (curfsnode->child != NULL); 376185222Ssam apply_specdir(path, curnode, curfsnode->child); 377185222Ssam } 378185222Ssam } 379185222Ssam} 380185222Ssam 381185222Ssamstatic void 382185222Ssamapply_specentry(const char *dir, NODE *specnode, fsnode *dirnode) 383185222Ssam{ 384185222Ssam 385185222Ssam assert(specnode != NULL); 386185222Ssam assert(dirnode != NULL); 387185222Ssam 388185222Ssam if (nodetoino(specnode->type) != dirnode->type) 389185222Ssam errx(1, "`%s/%s' type mismatch: specfile %s, tree %s", 390185222Ssam dir, specnode->name, inode_type(nodetoino(specnode->type)), 391185222Ssam inode_type(dirnode->type)); 392185222Ssam 393185222Ssam if (debug & DEBUG_APPLY_SPECENTRY) 394185222Ssam printf("apply_specentry: %s/%s\n", dir, dirnode->name); 395185222Ssam 396185222Ssam#define ASEPRINT(t, b, o, n) \ 397185222Ssam if (debug & DEBUG_APPLY_SPECENTRY) \ 398185222Ssam printf("\t\t\tchanging %s from " b " to " b "\n", \ 399185222Ssam t, o, n) 400185222Ssam 401185222Ssam if (specnode->flags & (F_GID | F_GNAME)) { 402185222Ssam ASEPRINT("gid", "%d", 403185222Ssam dirnode->inode->st.st_gid, specnode->st_gid); 404185222Ssam dirnode->inode->st.st_gid = specnode->st_gid; 405185222Ssam } 406185222Ssam if (specnode->flags & F_MODE) { 407185222Ssam ASEPRINT("mode", "%#o", 408185222Ssam dirnode->inode->st.st_mode & ALLPERMS, specnode->st_mode); 409185222Ssam dirnode->inode->st.st_mode &= ~ALLPERMS; 410185222Ssam dirnode->inode->st.st_mode |= (specnode->st_mode & ALLPERMS); 411185222Ssam } 412185222Ssam /* XXX: ignoring F_NLINK for now */ 413185222Ssam if (specnode->flags & F_SIZE) { 414185222Ssam ASEPRINT("size", "%lld", 415185222Ssam (long long)dirnode->inode->st.st_size, 416185222Ssam (long long)specnode->st_size); 417185222Ssam dirnode->inode->st.st_size = specnode->st_size; 418185222Ssam } 419185222Ssam if (specnode->flags & F_SLINK) { 420185222Ssam assert(dirnode->symlink != NULL); 421185222Ssam assert(specnode->slink != NULL); 422185222Ssam ASEPRINT("symlink", "%s", dirnode->symlink, specnode->slink); 423185222Ssam free(dirnode->symlink); 424185222Ssam if ((dirnode->symlink = strdup(specnode->slink)) == NULL) 425185222Ssam err(1, "Memory allocation error"); 426185222Ssam } 427185222Ssam if (specnode->flags & F_TIME) { 428185222Ssam ASEPRINT("time", "%ld", 429185222Ssam (long)dirnode->inode->st.st_mtime, 430185222Ssam (long)specnode->st_mtimespec.tv_sec); 431185222Ssam dirnode->inode->st.st_mtime = specnode->st_mtimespec.tv_sec; 432185222Ssam dirnode->inode->st.st_atime = specnode->st_mtimespec.tv_sec; 433185222Ssam dirnode->inode->st.st_ctime = start_time.tv_sec; 434185222Ssam#if HAVE_STRUCT_STAT_ST_MTIMENSEC 435185222Ssam dirnode->inode->st.st_mtimensec = specnode->st_mtimespec.tv_nsec; 436185222Ssam dirnode->inode->st.st_atimensec = specnode->st_mtimespec.tv_nsec; 437185222Ssam dirnode->inode->st.st_ctimensec = start_time.tv_nsec; 438185222Ssam#endif 439185222Ssam } 440185222Ssam if (specnode->flags & (F_UID | F_UNAME)) { 441185222Ssam ASEPRINT("uid", "%d", 442185222Ssam dirnode->inode->st.st_uid, specnode->st_uid); 443185222Ssam dirnode->inode->st.st_uid = specnode->st_uid; 444185222Ssam } 445185222Ssam#if HAVE_STRUCT_STAT_ST_FLAGS 446185222Ssam if (specnode->flags & F_FLAGS) { 447185222Ssam ASEPRINT("flags", "%#lX", 448185222Ssam (unsigned long)dirnode->inode->st.st_flags, 449185222Ssam (unsigned long)specnode->st_flags); 450185222Ssam dirnode->inode->st.st_flags = specnode->st_flags; 451185222Ssam } 452185222Ssam#endif 453185222Ssam#undef ASEPRINT 454185222Ssam 455185222Ssam dirnode->flags |= FSNODE_F_HASSPEC; 456185222Ssam} 457185222Ssam 458185222Ssam 459185222Ssam/* 460185222Ssam * dump_fsnodes -- 461185222Ssam * dump the fsnodes from `cur', based in the directory `dir' 462185222Ssam */ 463185222Ssamvoid 464185222Ssamdump_fsnodes(const char *dir, fsnode *root) 465185222Ssam{ 466185222Ssam fsnode *cur; 467185222Ssam char path[MAXPATHLEN + 1]; 468185222Ssam 469185222Ssam assert (dir != NULL); 470185222Ssam printf("dump_fsnodes: %s %p\n", dir, root); 471185222Ssam for (cur = root; cur != NULL; cur = cur->next) { 472185222Ssam if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name) 473185222Ssam >= sizeof(path)) 474185222Ssam errx(1, "Pathname too long."); 475185222Ssam 476185222Ssam if (debug & DEBUG_DUMP_FSNODES_VERBOSE) 477185222Ssam printf("cur=%8p parent=%8p first=%8p ", 478185222Ssam cur, cur->parent, cur->first); 479185222Ssam printf("%7s: %s", inode_type(cur->type), path); 480185222Ssam if (S_ISLNK(cur->type)) { 481185222Ssam assert(cur->symlink != NULL); 482185222Ssam printf(" -> %s", cur->symlink); 483185222Ssam } else { 484185222Ssam assert (cur->symlink == NULL); 485185222Ssam } 486185222Ssam if (cur->inode->nlink > 1) 487185222Ssam printf(", nlinks=%d", cur->inode->nlink); 488185222Ssam putchar('\n'); 489185222Ssam 490185222Ssam if (cur->child) { 491185222Ssam assert (cur->type == S_IFDIR); 492185222Ssam dump_fsnodes(path, cur->child); 493185222Ssam } 494185222Ssam } 495185222Ssam printf("dump_fsnodes: finished %s\n", dir); 496185222Ssam} 497185222Ssam 498185222Ssam 499185222Ssam/* 500185222Ssam * inode_type -- 501185222Ssam * for a given inode type `mode', return a descriptive string. 502185222Ssam */ 503185222Ssamconst char * 504185222Ssaminode_type(mode_t mode) 505185222Ssam{ 506185222Ssam 507186256Ssam if (S_ISREG(mode)) 508186256Ssam return ("file"); 509185222Ssam if (S_ISLNK(mode)) 510186256Ssam return ("symlink"); 511186256Ssam if (S_ISDIR(mode)) 512186256Ssam return ("dir"); 513186256Ssam if (S_ISLNK(mode)) 514186256Ssam return ("link"); 515186256Ssam if (S_ISFIFO(mode)) 516186256Ssam return ("fifo"); 517186256Ssam if (S_ISSOCK(mode)) 518186256Ssam return ("socket"); 519186256Ssam /* XXX should not happen but handle them */ 520186256Ssam if (S_ISCHR(mode)) 521186256Ssam return ("char"); 522186256Ssam if (S_ISBLK(mode)) 523186256Ssam return ("block"); 524186256Ssam return ("unknown"); 525185222Ssam} 526185222Ssam 527185222Ssam 528185222Ssam/* 529185222Ssam * link_check -- 530185222Ssam * return pointer to fsnode matching `entry's st_ino & st_dev if it exists, 531185222Ssam * otherwise add `entry' to table and return NULL 532185222Ssam */ 533185222Ssamstatic fsinode * 534185222Ssamlink_check(fsinode *entry) 535185222Ssam{ 536185222Ssam static struct dupnode { 537185222Ssam uint32_t dev; 538185222Ssam uint64_t ino; 539185222Ssam fsinode *dup; 540185222Ssam } *dups, *newdups; 541185222Ssam static int ndups, maxdups; 542185222Ssam 543185222Ssam int i; 544185222Ssam 545185222Ssam assert (entry != NULL); 546185222Ssam 547185222Ssam /* XXX; maybe traverse in reverse for speed? */ 548185222Ssam for (i = 0; i < ndups; i++) { 549185222Ssam if (dups[i].dev == entry->st.st_dev && 550185222Ssam dups[i].ino == entry->st.st_ino) { 551185222Ssam if (debug & DEBUG_WALK_DIR_LINKCHECK) 552185222Ssam printf("link_check: found [%d,%d]\n", 553185222Ssam entry->st.st_dev, entry->st.st_ino); 554185222Ssam return (dups[i].dup); 555185222Ssam } 556185222Ssam } 557185222Ssam 558185222Ssam if (debug & DEBUG_WALK_DIR_LINKCHECK) 559185222Ssam printf("link_check: no match for [%d, %d]\n", 560185222Ssam entry->st.st_dev, entry->st.st_ino); 561185222Ssam if (ndups == maxdups) { 562185222Ssam if ((newdups = realloc(dups, sizeof(struct dupnode) * (maxdups + 128))) 563185222Ssam == NULL) 564185222Ssam err(1, "Memory allocation error"); 565185222Ssam dups = newdups; 566185222Ssam maxdups += 128; 567185222Ssam } 568185222Ssam dups[ndups].dev = entry->st.st_dev; 569185222Ssam dups[ndups].ino = entry->st.st_ino; 570185222Ssam dups[ndups].dup = entry; 571185222Ssam ndups++; 572185222Ssam 573185222Ssam return (NULL); 574185222Ssam} 575