walk.c revision 1.1
1/* $NetBSD: walk.c,v 1.1 2001/10/26 06:16:19 lukem Exp $ */ 2 3/* 4 * Copyright 2001 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Luke Mewburn for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 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 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 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 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 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38/* 39 * The function link_check() was inspired from NetBSD's usr.bin/du/du.c, 40 * which has the following copyright notice: 41 * 42 * 43 * Copyright (c) 1989, 1993, 1994 44 * The Regents of the University of California. All rights reserved. 45 * 46 * This code is derived from software contributed to Berkeley by 47 * Chris Newcomb. 48 * 49 * Redistribution and use in source and binary forms, with or without 50 * modification, are permitted provided that the following conditions 51 * are met: 52 * 1. Redistributions of source code must retain the above copyright 53 * notice, this list of conditions and the following disclaimer. 54 * 2. Redistributions in binary form must reproduce the above copyright 55 * notice, this list of conditions and the following disclaimer in the 56 * documentation and/or other materials provided with the distribution. 57 * 3. All advertising materials mentioning features or use of this software 58 * must display the following acknowledgement: 59 * This product includes software developed by the University of 60 * California, Berkeley and its contributors. 61 * 4. Neither the name of the University nor the names of its contributors 62 * may be used to endorse or promote products derived from this software 63 * without specific prior written permission. 64 * 65 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 66 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 67 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 68 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 69 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 70 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 71 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 72 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 73 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 74 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 75 * SUCH DAMAGE. 76 */ 77 78#include <sys/param.h> 79 80#include <assert.h> 81#include <err.h> 82#include <errno.h> 83#include <fcntl.h> 84#include <stdio.h> 85#include <dirent.h> 86#include <stdlib.h> 87#include <string.h> 88#include <unistd.h> 89 90#include "makefs.h" 91#include "mtree.h" 92 93static void apply_specdir(const char *, NODE *, fsnode *); 94static void apply_specentry(const char *, NODE *, fsnode *); 95static fsnode *create_fsnode(const char *, struct stat *); 96static fsnode *link_check(fsnode *); 97 98 99/* 100 * walk_dir -- 101 * build a tree of fsnodes from `dir', with a parent fsnode of `parent' 102 * (which may be NULL for the root of the tree). 103 * each "level" is a directory, with the "." entry guaranteed to be 104 * at the start of the list, and without ".." entries. 105 */ 106fsnode * 107walk_dir(const char *dir, fsnode *parent) 108{ 109 fsnode *first, *cur, *prev; 110 DIR *dirp; 111 struct dirent *dent; 112 char path[MAXPATHLEN + 1]; 113 struct stat stbuf; 114 115 assert(dir != NULL); 116 117 if (debug & DEBUG_WALK_DIR) 118 printf("walk_dir: %s %p\n", dir, parent); 119 if ((dirp = opendir(dir)) == NULL) 120 err(1, "Can't opendir `%s'", dir); 121 first = prev = NULL; 122 while ((dent = readdir(dirp)) != NULL) { 123 if (strcmp(dent->d_name, "..") == 0) 124 continue; 125 if (debug & DEBUG_WALK_DIR_NODE) 126 printf("scanning %s/%s\n", dir, dent->d_name); 127 if (snprintf(path, sizeof(path), "%s/%s", dir, dent->d_name) 128 >= sizeof(path)) 129 errx(1, "Pathname too long."); 130 if (lstat(path, &stbuf) == -1) 131 err(1, "Can't lstat `%s'", path); 132 if (S_ISSOCK(stbuf.st_mode & S_IFMT)) { 133 if (debug & DEBUG_WALK_DIR_NODE) 134 printf(" skipping socket %s\n", path); 135 continue; 136 } 137 138 cur = create_fsnode(dent->d_name, &stbuf); 139 cur->parent = parent; 140 if (strcmp(dent->d_name, ".") == 0) { 141 /* ensure "." is at the start of the list */ 142 cur->next = first; 143 first = cur; 144 if (! prev) 145 prev = cur; 146 } else { /* not "." */ 147 if (prev) 148 prev->next = cur; 149 prev = cur; 150 if (!first) 151 first = cur; 152 if (S_ISDIR(cur->type)) { 153 cur->child = walk_dir(path, cur); 154 continue; 155 } 156 } 157 if (cur->statbuf.st_nlink > 1) { 158 cur->dup = link_check(cur); 159 if (cur->dup) 160 cur->dup->nlink++; 161 } 162 if (S_ISLNK(cur->type)) { 163 char slink[PATH_MAX+1]; 164 int llen; 165 166 llen = readlink(path, slink, PATH_MAX); 167 if (llen == -1) 168 err(1, "Readlink `%s'", path); 169 slink[llen] = '\0'; 170 if ((cur->symlink = strdup(slink)) == NULL) 171 err(1, "Memory allocation error"); 172 } 173 } 174 for (cur = first; cur != NULL; cur = cur->next) 175 cur->first = first; 176 if (closedir(dirp) == -1) 177 err(1, "Can't closedir `%s'", dir); 178 return (first); 179} 180 181static fsnode * 182create_fsnode(const char *name, struct stat *statbuf) 183{ 184 fsnode *cur; 185 186 if ((cur = calloc(1, sizeof(fsnode))) == NULL || 187 (cur->name = strdup(name)) == NULL) 188 err(1, "Memory allocation error"); 189 cur->statbuf = *statbuf; 190 cur->type = (cur->statbuf.st_mode & S_IFMT); 191 cur->nlink = 1; 192 return (cur); 193} 194 195 196/* 197 * apply_specfile -- 198 * read in the mtree(8) specfile, and apply it to the tree 199 * at dir,parent. parameters in parent on equivalent types 200 * will be changed to those found in specfile, and missing 201 * entries will be added. 202 */ 203void 204apply_specfile(const char *specfile, const char *dir, fsnode *parent) 205{ 206 struct timeval start; 207 FILE *fp; 208 NODE *root; 209 210 assert(specfile != NULL); 211 assert(parent != NULL); 212 213 if (debug & DEBUG_APPLY_SPECFILE) 214 printf("apply_specfile: %s, %s %p\n", specfile, dir, parent); 215 216 /* read in the specfile */ 217 if ((fp = fopen(specfile, "r")) == NULL) 218 err(1, "Can't open `%s'", specfile); 219 TIMER_START(start); 220 root = spec(fp); 221 TIMER_RESULTS(start, "spec"); 222 if (fclose(fp) == EOF) 223 err(1, "Can't close `%s'", specfile); 224 225 /* perform some sanity checks */ 226 if (root == NULL) 227 errx(1, "Specfile `%s' did not contain a tree", specfile); 228 assert(strcmp(root->name, ".") == 0); 229 assert(root->type == F_DIR); 230 231 /* merge in the changes */ 232 apply_specdir(dir, root, parent); 233} 234 235static void 236apply_specdir(const char *dir, NODE *specnode, fsnode *dirnode) 237{ 238 char path[MAXPATHLEN + 1]; 239 NODE *curnode; 240 fsnode *curfsnode; 241 242 assert(specnode != NULL); 243 assert(dirnode != NULL); 244 245 if (debug & DEBUG_APPLY_SPECFILE) 246 printf("apply_specdir: %s %p %p\n", dir, specnode, dirnode); 247 248 if (specnode->type != F_DIR) 249 errx(1, "Specfile node `%s/%s' is not a directory", 250 dir, specnode->name); 251 if (dirnode->type != S_IFDIR) 252 errx(1, "Directory node `%s/%s' is not a directory", 253 dir, dirnode->name); 254 255 apply_specentry(dir, specnode, dirnode); 256 257 /* now walk specnode->child matching up with dirnode */ 258 for (curnode = specnode->child; curnode != NULL; 259 curnode = curnode->next) { 260 if (debug & DEBUG_APPLY_SPECENTRY) 261 printf("apply_specdir: spec %s\n", 262 curnode->name); 263 for (curfsnode = dirnode->next; curfsnode != NULL; 264 curfsnode = curfsnode->next) { 265 if (debug & DEBUG_APPLY_SPECENTRY) 266 printf("apply_specdir: dirent %s\n", 267 curfsnode->name); 268 if (strcmp(curnode->name, curfsnode->name) == 0) 269 break; 270 } 271 if (curfsnode == NULL) { /* need new entry */ 272 struct stat stbuf; 273 274 /* check that enough info is provided */ 275#define NODETEST(t, m) \ 276 if (!(t)) \ 277 errx(1, "`%s/%s': %s not provided", \ 278 dir, curnode->name, m) 279 NODETEST(curnode->flags & F_TYPE, "type"); 280 NODETEST(curnode->flags & F_MODE, "mode"); 281 /* XXX: require F_TIME ? */ 282 NODETEST(curnode->flags & F_GID || 283 curnode->flags & F_GNAME, "group"); 284 NODETEST(curnode->flags & F_UID || 285 curnode->flags & F_UNAME, "user"); 286 if (curnode->type == F_BLOCK || curnode->type == F_CHAR) 287 NODETEST(curnode->flags & F_DEV, 288 "device number"); 289#undef NODETEST 290 291 if (debug & DEBUG_APPLY_SPECFILE) 292 printf("apply_specdir: adding %s\n", 293 curnode->name); 294 /* build minimal fsnode */ 295 memset(&stbuf, 0, sizeof(stbuf)); 296 stbuf.st_mode = nodetoino(curnode->type); 297 stbuf.st_mtime = stbuf.st_atime = 298 stbuf.st_ctime = start_time.tv_sec; 299 stbuf.st_mtimensec = stbuf.st_atimensec = 300 stbuf.st_ctimensec = start_time.tv_nsec; 301 curfsnode = create_fsnode(curnode->name, &stbuf); 302 curfsnode->parent = dirnode->parent; 303 curfsnode->first = dirnode; 304 curfsnode->next = dirnode->next; 305 dirnode->next = curfsnode; 306 if (curfsnode->type == S_IFDIR) { 307 /* for dirs, make "." entry as well */ 308 curfsnode->child = create_fsnode(".", &stbuf); 309 curfsnode->child->parent = curfsnode; 310 curfsnode->child->first = curfsnode->child; 311 } 312 if (curfsnode->type == S_IFLNK) { 313 assert(specnode->slink != NULL); 314 /* for symlinks, copy the target */ 315 if ((curfsnode->symlink = 316 strdup(curnode->slink)) == NULL) 317 err(1, "Memory allocation error"); 318 } 319 } 320 apply_specentry(dir, curnode, curfsnode); 321 if (curnode->type == F_DIR) { 322 if (curfsnode->type != S_IFDIR) 323 errx(1, "`%s/%s' is not a directory", 324 dir, curfsnode->name); 325 assert (curfsnode->child != NULL); 326 if (snprintf(path, sizeof(path), "%s/%s", 327 dir, curnode->name) >= sizeof(path)) 328 errx(1, "Pathname too long."); 329 apply_specdir(path, curnode, curfsnode->child); 330 } 331 } 332} 333 334static void 335apply_specentry(const char *dir, NODE *specnode, fsnode *dirnode) 336{ 337 338 assert(specnode != NULL); 339 assert(dirnode != NULL); 340 341 if (nodetoino(specnode->type) != dirnode->type) 342 errx(1, "`%s/%s' type mismatch: specfile %s, tree %s", 343 dir, specnode->name, inode_type(nodetoino(specnode->type)), 344 inode_type(dirnode->type)); 345 346 if (debug & DEBUG_APPLY_SPECENTRY) 347 printf("apply_specentry: %s/%s\n", dir, dirnode->name); 348 349#define ASEPRINT(t, b, o, n) \ 350 if (debug & DEBUG_APPLY_SPECENTRY) \ 351 printf("\t\t\tchanging %s from " b " to " b "\n", \ 352 t, o, n) 353 354 if (specnode->flags & (F_GID | F_GNAME)) { 355 ASEPRINT("gid", "%d", 356 dirnode->statbuf.st_gid, specnode->st_gid); 357 dirnode->statbuf.st_gid = specnode->st_gid; 358 } 359 if (specnode->flags & F_MODE) { 360 ASEPRINT("mode", "%#o", 361 dirnode->statbuf.st_mode & ALLPERMS, specnode->st_mode); 362 dirnode->statbuf.st_mode &= ~ALLPERMS; 363 dirnode->statbuf.st_mode |= (specnode->st_mode & ALLPERMS); 364 } 365 /* XXX: ignoring F_NLINK for now */ 366 if (specnode->flags & F_SIZE) { 367 ASEPRINT("size", "%lld", 368 (long long)dirnode->statbuf.st_size, 369 (long long)specnode->st_size); 370 dirnode->statbuf.st_size = specnode->st_size; 371 } 372 if (specnode->flags & F_SLINK) { 373 assert(dirnode->symlink != NULL); 374 assert(specnode->slink != NULL); 375 ASEPRINT("symlink", "%s", dirnode->symlink, specnode->slink); 376 free(dirnode->symlink); 377 if ((dirnode->symlink = strdup(specnode->slink)) == NULL) 378 err(1, "Memory allocation error"); 379 } 380 if (specnode->flags & F_TIME) { 381 ASEPRINT("time", "%ld", 382 dirnode->statbuf.st_mtime, specnode->st_mtime); 383 dirnode->statbuf.st_mtime = specnode->st_mtime; 384 dirnode->statbuf.st_mtimensec = specnode->st_mtimensec; 385 dirnode->statbuf.st_atime = specnode->st_mtime; 386 dirnode->statbuf.st_atimensec = specnode->st_mtimensec; 387 dirnode->statbuf.st_ctime = start_time.tv_sec; 388 dirnode->statbuf.st_ctimensec = start_time.tv_nsec; 389 } 390 if (specnode->flags & (F_UID | F_UNAME)) { 391 ASEPRINT("uid", "%d", 392 dirnode->statbuf.st_uid, specnode->st_uid); 393 dirnode->statbuf.st_uid = specnode->st_uid; 394 } 395 if (specnode->flags & F_FLAGS) { 396 ASEPRINT("flags", "%#lX", 397 (u_long)dirnode->statbuf.st_flags, 398 (u_long)specnode->st_flags); 399 dirnode->statbuf.st_flags = specnode->st_flags; 400 } 401 if (specnode->flags & F_DEV) { 402 ASEPRINT("rdev", "%#x", 403 dirnode->statbuf.st_rdev, specnode->st_rdev); 404 dirnode->statbuf.st_rdev = specnode->st_rdev; 405 } 406#undef ASEPRINT 407} 408 409 410/* 411 * dump_fsnodes -- 412 * dump the fsnodes from `cur', based in the directory `dir' 413 */ 414void 415dump_fsnodes(const char *dir, fsnode *root) 416{ 417 fsnode *cur; 418 char path[MAXPATHLEN + 1]; 419 420 assert (dir != NULL); 421 printf("dump_fsnodes: %s %p\n", dir, root); 422 for (cur = root; cur != NULL; cur = cur->next) { 423 if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name) 424 >= sizeof(path)) 425 errx(1, "Pathname too long."); 426 427 if (debug & DEBUG_DUMP_FSNODES_VERBOSE) 428 printf("cur=%8p parent=%8p first=%8p ", 429 cur, cur->parent, cur->first); 430 printf("%7s: %s", inode_type(cur->type), path); 431 if (S_ISLNK(cur->type)) { 432 assert(cur->symlink != NULL); 433 printf(" -> %s", cur->symlink); 434 } else { 435 assert (cur->symlink == NULL); 436 } 437 if (cur->dup != NULL) { 438 printf(", hard-linked to %s", cur->dup->name); 439 } 440 if (cur->nlink > 1) 441 printf(", nlinks=%d", cur->nlink); 442 putchar('\n'); 443 444 if (cur->child) { 445 assert (cur->type == S_IFDIR); 446 dump_fsnodes(path, cur->child); 447 } 448 } 449 printf("dump_fsnodes: finished %s\n", dir); 450} 451 452 453/* 454 * inode_type -- 455 * for a given inode type `mode', return a descriptive string. 456 * for most cases, uses inotype() from mtree/misc.c 457 */ 458const char * 459inode_type(mode_t mode) 460{ 461 462 if (mode & S_IFLNK) 463 return ("symlink"); /* inotype() returns "link"... */ 464 return (inotype(mode)); 465} 466 467 468typedef struct { 469 int32_t dev; 470 int32_t ino; 471 fsnode *dup; 472} dupnode; 473 474/* 475 * link_check -- 476 * return pointer to fsnode matching `entry's st_ino & st_dev if it exists, 477 * otherwise add `entry' to table and return NULL 478 */ 479static fsnode * 480link_check(fsnode *entry) 481{ 482 static dupnode *dups; 483 static int ndups, maxdups; 484 485 int i; 486 487 assert (entry != NULL); 488 489 /* XXX; maybe traverse in reverse for speed? */ 490 for (i = 0; i < ndups; i++) { 491 if (dups[i].dev == entry->statbuf.st_dev && 492 dups[i].ino == entry->statbuf.st_ino) { 493 if (debug & DEBUG_WALK_DIR_LINKCHECK) 494 printf( 495 "link_check: %s (%d,%d) linked to %s\n", 496 entry->name, entry->statbuf.st_dev, 497 entry->statbuf.st_ino, dups[i].dup->name); 498 return (dups[i].dup); 499 } 500 } 501 502 if (debug & DEBUG_WALK_DIR_LINKCHECK) 503 printf("link_check: no match for %s (%d, %d)\n", 504 entry->name, entry->statbuf.st_dev, entry->statbuf.st_ino); 505 if (ndups == maxdups) { 506 maxdups += 128; 507 if ((dups = realloc(dups, sizeof(dupnode) * maxdups)) == NULL) 508 err(1, "Memory allocation error"); 509 } 510 dups[ndups].dev = entry->statbuf.st_dev; 511 dups[ndups].ino = entry->statbuf.st_ino; 512 dups[ndups].dup = entry; 513 ndups++; 514 515 return (NULL); 516} 517