ftree.c revision 108533
1238106Sdes/*- 2238106Sdes * Copyright (c) 1992 Keith Muller. 3238106Sdes * Copyright (c) 1992, 1993 4238106Sdes * The Regents of the University of California. All rights reserved. 5238106Sdes * 6238106Sdes * This code is derived from software contributed to Berkeley by 7238106Sdes * Keith Muller of the University of California, San Diego. 8238106Sdes * 9238106Sdes * Redistribution and use in source and binary forms, with or without 10238106Sdes * modification, are permitted provided that the following conditions 11238106Sdes * are met: 12238106Sdes * 1. Redistributions of source code must retain the above copyright 13238106Sdes * notice, this list of conditions and the following disclaimer. 14238106Sdes * 2. Redistributions in binary form must reproduce the above copyright 15238106Sdes * notice, this list of conditions and the following disclaimer in the 16238106Sdes * documentation and/or other materials provided with the distribution. 17238106Sdes * 3. All advertising materials mentioning features or use of this software 18238106Sdes * must display the following acknowledgement: 19238106Sdes * This product includes software developed by the University of 20238106Sdes * California, Berkeley and its contributors. 21238106Sdes * 4. Neither the name of the University nor the names of its contributors 22238106Sdes * may be used to endorse or promote products derived from this software 23238106Sdes * without specific prior written permission. 24238106Sdes * 25238106Sdes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26266114Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27266114Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28266114Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29266114Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30266114Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31266114Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32266114Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33266114Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34266114Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35266114Sdes * SUCH DAMAGE. 36238106Sdes */ 37238106Sdes 38238106Sdes#ifndef lint 39238106Sdes#if 0 40238106Sdesstatic char sccsid[] = "@(#)ftree.c 8.2 (Berkeley) 4/18/94"; 41238106Sdes#endif 42238106Sdes#endif /* not lint */ 43238106Sdes#include <sys/cdefs.h> 44238106Sdes__FBSDID("$FreeBSD: head/bin/pax/ftree.c 108533 2003-01-01 18:49:04Z schweikh $"); 45238106Sdes 46238106Sdes#include <sys/types.h> 47276605Sdes#include <sys/time.h> 48238106Sdes#include <sys/stat.h> 49238106Sdes#include <unistd.h> 50238106Sdes#include <string.h> 51238106Sdes#include <stdio.h> 52238106Sdes#include <errno.h> 53238106Sdes#include <stdlib.h> 54238106Sdes#include <fts.h> 55238106Sdes#include "pax.h" 56238106Sdes#include "ftree.h" 57238106Sdes#include "extern.h" 58238106Sdes 59238106Sdes/* 60238106Sdes * routines to interface with the fts library function. 61238106Sdes * 62238106Sdes * file args supplied to pax are stored on a single linked list (of type FTREE) 63238106Sdes * and given to fts to be processed one at a time. pax "selects" files from 64238106Sdes * the expansion of each arg into the corresponding file tree (if the arg is a 65238106Sdes * directory, otherwise the node itself is just passed to pax). The selection 66238106Sdes * is modified by the -n and -u flags. The user is informed when a specific 67238106Sdes * file arg does not generate any selected files. -n keeps expanding the file 68238106Sdes * tree arg until one of its files is selected, then skips to the next file 69238106Sdes * arg. when the user does not supply the file trees as command line args to 70238106Sdes * pax, they are read from stdin 71238106Sdes */ 72238106Sdes 73296415Sdesstatic FTS *ftsp = NULL; /* current FTS handle */ 74238106Sdesstatic int ftsopts; /* options to be used on fts_open */ 75238106Sdesstatic char *farray[2]; /* array for passing each arg to fts */ 76238106Sdesstatic FTREE *fthead = NULL; /* head of linked list of file args */ 77238106Sdesstatic FTREE *fttail = NULL; /* tail of linked list of file args */ 78238106Sdesstatic FTREE *ftcur = NULL; /* current file arg being processed */ 79238106Sdesstatic FTSENT *ftent = NULL; /* current file tree entry */ 80238106Sdesstatic int ftree_skip; /* when set skip to next file arg */ 81238106Sdes 82238106Sdesstatic int ftree_arg(void); 83238106Sdes 84238106Sdes/* 85238106Sdes * ftree_start() 86238106Sdes * initialize the options passed to fts_open() during this run of pax 87238106Sdes * options are based on the selection of pax options by the user 88238106Sdes * fts_start() also calls fts_arg() to open the first valid file arg. We 89238106Sdes * also attempt to reset directory access times when -t (tflag) is set. 90238106Sdes * Return: 91238106Sdes * 0 if there is at least one valid file arg to process, -1 otherwise 92238106Sdes */ 93238106Sdes 94238106Sdesint 95238106Sdesftree_start(void) 96238106Sdes{ 97238106Sdes /* 98238106Sdes * Set up the operation mode of fts, open the first file arg. We must 99276699Sdes * use FTS_NOCHDIR, as the user may have to open multiple archives and 100238106Sdes * if fts did a chdir off into the boondocks, we may create an archive 101238106Sdes * volume in a place where the user did not expect to. 102238106Sdes */ 103238106Sdes ftsopts = FTS_NOCHDIR; 104238106Sdes 105238106Sdes /* 106266114Sdes * optional user flags that effect file traversal 107238106Sdes * -H command line symlink follow only (half follow) 108238106Sdes * -L follow sylinks (logical) 109238106Sdes * -P do not follow sylinks (physical). This is the default. 110295535Sdes * -X do not cross over mount points 111295535Sdes * -t preserve access times on files read. 112282089Sdes * -n select only the first member of a file tree when a match is found 113276605Sdes * -d do not extract subtrees rooted at a directory arg. 114276605Sdes */ 115276605Sdes if (Lflag) 116276605Sdes ftsopts |= FTS_LOGICAL; 117276605Sdes else 118276605Sdes ftsopts |= FTS_PHYSICAL; 119276605Sdes if (Hflag) 120276605Sdes# ifdef NET2_FTS 121276605Sdes paxwarn(0, "The -H flag is not supported on this version"); 122276605Sdes# else 123287917Sdes ftsopts |= FTS_COMFOLLOW; 124287917Sdes# endif 125287917Sdes if (Xflag) 126289063Sdes ftsopts |= FTS_XDEV; 127292206Sdes 128238106Sdes if ((fthead == NULL) && ((farray[0] = malloc(PAXPATHLEN+2)) == NULL)) { 129238106Sdes paxwarn(1, "Unable to allocate memory for file name buffer"); 130238106Sdes return(-1); 131238106Sdes } 132238106Sdes 133276605Sdes if (ftree_arg() < 0) 134238106Sdes return(-1); 135238106Sdes if (tflag && (atdir_start() < 0)) 136238106Sdes return(-1); 137238106Sdes return(0); 138238106Sdes} 139238106Sdes 140238106Sdes/* 141238106Sdes * ftree_add() 142238106Sdes * add the arg to the linked list of files to process. Each will be 143238106Sdes * processed by fts one at a time 144238106Sdes * Return: 145238106Sdes * 0 if added to the linked list, -1 if failed 146238106Sdes */ 147296415Sdes 148238106Sdesint 149238106Sdesftree_add(char *str, int chflg) 150238106Sdes{ 151238106Sdes FTREE *ft; 152238106Sdes int len; 153238106Sdes 154238106Sdes /* 155238106Sdes * simple check for bad args 156238106Sdes */ 157238106Sdes if ((str == NULL) || (*str == '\0')) { 158238106Sdes paxwarn(0, "Invalid file name argument"); 159238106Sdes return(-1); 160238106Sdes } 161238106Sdes 162238106Sdes /* 163238106Sdes * allocate FTREE node and add to the end of the linked list (args are 164238106Sdes * processed in the same order they were passed to pax). Get rid of any 165238106Sdes * trailing / the user may pass us. (watch out for / by itself). 166238106Sdes */ 167238106Sdes if ((ft = (FTREE *)malloc(sizeof(FTREE))) == NULL) { 168238106Sdes paxwarn(0, "Unable to allocate memory for filename"); 169238106Sdes return(-1); 170238106Sdes } 171238106Sdes 172238106Sdes if (((len = strlen(str) - 1) > 0) && (str[len] == '/')) 173238106Sdes str[len] = '\0'; 174238106Sdes ft->fname = str; 175238106Sdes ft->refcnt = 0; 176238106Sdes ft->chflg = chflg; 177238106Sdes ft->fow = NULL; 178238106Sdes if (fthead == NULL) { 179238106Sdes fttail = fthead = ft; 180238106Sdes return(0); 181238106Sdes } 182238106Sdes fttail->fow = ft; 183238106Sdes fttail = ft; 184238106Sdes return(0); 185266114Sdes} 186295535Sdes 187295535Sdes/* 188282089Sdes * ftree_sel() 189287917Sdes * this entry has been selected by pax. bump up reference count and handle 190287917Sdes * -n and -d processing. 191287917Sdes */ 192287917Sdes 193289063Sdesvoid 194292206Sdesftree_sel(ARCHD *arcn) 195238106Sdes{ 196238106Sdes /* 197238106Sdes * set reference bit for this pattern. This linked list is only used 198238106Sdes * when file trees are supplied pax as args. The list is not used when 199238106Sdes * the trees are read from stdin. 200238106Sdes */ 201238106Sdes if (ftcur != NULL) 202238106Sdes ftcur->refcnt = 1; 203238106Sdes 204238106Sdes /* 205238106Sdes * if -n we are done with this arg, force a skip to the next arg when 206238106Sdes * pax asks for the next file in next_file(). 207238106Sdes * if -d we tell fts only to match the directory (if the arg is a dir) 208238106Sdes * and not the entire file tree rooted at that point. 209238106Sdes */ 210238106Sdes if (nflag) 211238106Sdes ftree_skip = 1; 212238106Sdes 213238106Sdes if (!dflag || (arcn->type != PAX_DIR)) 214238106Sdes return; 215238106Sdes 216238106Sdes if (ftent != NULL) 217238106Sdes (void)fts_set(ftsp, ftent, FTS_SKIP); 218238106Sdes} 219238106Sdes 220238106Sdes/* 221238106Sdes * ftree_chk() 222238106Sdes * called at end on pax execution. Prints all those file args that did not 223238106Sdes * have a selected member (reference count still 0) 224238106Sdes */ 225238106Sdes 226238106Sdesvoid 227238106Sdesftree_chk(void) 228238106Sdes{ 229238106Sdes FTREE *ft; 230238106Sdes int wban = 0; 231238106Sdes 232238106Sdes /* 233238106Sdes * make sure all dir access times were reset. 234238106Sdes */ 235238106Sdes if (tflag) 236238106Sdes atdir_end(); 237238106Sdes 238238106Sdes /* 239238106Sdes * walk down list and check reference count. Print out those members 240238106Sdes * that never had a match 241238106Sdes */ 242238106Sdes for (ft = fthead; ft != NULL; ft = ft->fow) { 243238106Sdes if ((ft->refcnt > 0) || ft->chflg) 244238106Sdes continue; 245238106Sdes if (wban == 0) { 246238106Sdes paxwarn(1,"WARNING! These file names were not selected:"); 247238106Sdes ++wban; 248238106Sdes } 249238106Sdes (void)fprintf(stderr, "%s\n", ft->fname); 250238106Sdes } 251238106Sdes} 252238106Sdes 253238106Sdes/* 254238106Sdes * ftree_arg() 255238106Sdes * Get the next file arg for fts to process. Can be from either the linked 256238106Sdes * list or read from stdin when the user did not them as args to pax. Each 257238106Sdes * arg is processed until the first successful fts_open(). 258238106Sdes * Return: 259238106Sdes * 0 when the next arg is ready to go, -1 if out of file args (or EOF on 260238106Sdes * stdin). 261238106Sdes */ 262238106Sdes 263238106Sdesstatic int 264238106Sdesftree_arg(void) 265238106Sdes{ 266238106Sdes char *pt; 267238106Sdes 268238106Sdes /* 269238106Sdes * close off the current file tree 270238106Sdes */ 271238106Sdes if (ftsp != NULL) { 272238106Sdes (void)fts_close(ftsp); 273238106Sdes ftsp = NULL; 274238106Sdes } 275238106Sdes 276238106Sdes /* 277238106Sdes * keep looping until we get a valid file tree to process. Stop when we 278238106Sdes * reach the end of the list (or get an eof on stdin) 279238106Sdes */ 280238106Sdes for(;;) { 281238106Sdes if (fthead == NULL) { 282238106Sdes /* 283238106Sdes * the user didn't supply any args, get the file trees 284238106Sdes * to process from stdin; 285238106Sdes */ 286238106Sdes if (fgets(farray[0], PAXPATHLEN+1, stdin) == NULL) 287238106Sdes return(-1); 288238106Sdes if ((pt = strchr(farray[0], '\n')) != NULL) 289238106Sdes *pt = '\0'; 290238106Sdes } else { 291238106Sdes /* 292238106Sdes * the user supplied the file args as arguments to pax 293238106Sdes */ 294238106Sdes if (ftcur == NULL) 295238106Sdes ftcur = fthead; 296238106Sdes else if ((ftcur = ftcur->fow) == NULL) 297238106Sdes return(-1); 298238106Sdes if (ftcur->chflg) { 299238106Sdes /* First fchdir() back... */ 300238106Sdes if (fchdir(cwdfd) < 0) { 301238106Sdes syswarn(1, errno, 302238106Sdes "Can't fchdir to starting directory"); 303238106Sdes return(-1); 304238106Sdes } 305238106Sdes if (chdir(ftcur->fname) < 0) { 306238106Sdes syswarn(1, errno, "Can't chdir to %s", 307238106Sdes ftcur->fname); 308238106Sdes return(-1); 309238106Sdes } 310238106Sdes continue; 311238106Sdes } else 312238106Sdes farray[0] = ftcur->fname; 313238106Sdes } 314238106Sdes 315238106Sdes /* 316238106Sdes * Watch it, fts wants the file arg stored in an array of char 317238106Sdes * ptrs, with the last one a null. We use a two element array 318238106Sdes * and set farray[0] to point at the buffer with the file name 319238106Sdes * in it. We cannot pass all the file args to fts at one shot 320238106Sdes * as we need to keep a handle on which file arg generates what 321238106Sdes * files (the -n and -d flags need this). If the open is 322238106Sdes * successful, return a 0. 323238106Sdes */ 324238106Sdes if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL) 325238106Sdes break; 326238106Sdes } 327238106Sdes return(0); 328238106Sdes} 329238106Sdes 330238106Sdes/* 331238106Sdes * next_file() 332238106Sdes * supplies the next file to process in the supplied archd structure. 333238106Sdes * Return: 334238106Sdes * 0 when contents of arcn have been set with the next file, -1 when done. 335238106Sdes */ 336238106Sdes 337238106Sdesint 338238106Sdesnext_file(ARCHD *arcn) 339238106Sdes{ 340238106Sdes int cnt; 341238106Sdes time_t atime; 342238106Sdes time_t mtime; 343238106Sdes 344238106Sdes /* 345238106Sdes * ftree_sel() might have set the ftree_skip flag if the user has the 346238106Sdes * -n option and a file was selected from this file arg tree. (-n says 347238106Sdes * only one member is matched for each pattern) ftree_skip being 1 348238106Sdes * forces us to go to the next arg now. 349238106Sdes */ 350238106Sdes if (ftree_skip) { 351238106Sdes /* 352238106Sdes * clear and go to next arg 353238106Sdes */ 354238106Sdes ftree_skip = 0; 355238106Sdes if (ftree_arg() < 0) 356238106Sdes return(-1); 357238106Sdes } 358238106Sdes 359238106Sdes /* 360238106Sdes * loop until we get a valid file to process 361238106Sdes */ 362238106Sdes for(;;) { 363238106Sdes if ((ftent = fts_read(ftsp)) == NULL) { 364238106Sdes /* 365238106Sdes * out of files in this tree, go to next arg, if none 366238106Sdes * we are done 367238106Sdes */ 368238106Sdes if (ftree_arg() < 0) 369238106Sdes return(-1); 370238106Sdes continue; 371238106Sdes } 372238106Sdes 373238106Sdes /* 374238106Sdes * handle each type of fts_read() flag 375238106Sdes */ 376238106Sdes switch(ftent->fts_info) { 377238106Sdes case FTS_D: 378238106Sdes case FTS_DEFAULT: 379238106Sdes case FTS_F: 380238106Sdes case FTS_SL: 381238106Sdes case FTS_SLNONE: 382238106Sdes /* 383238106Sdes * these are all ok 384238106Sdes */ 385238106Sdes break; 386238106Sdes case FTS_DP: 387238106Sdes /* 388238106Sdes * already saw this directory. If the user wants file 389238106Sdes * access times reset, we use this to restore the 390238106Sdes * access time for this directory since this is the 391238106Sdes * last time we will see it in this file subtree 392238106Sdes * remember to force the time (this is -t on a read 393238106Sdes * directory, not a created directory). 394238106Sdes */ 395238106Sdes# ifdef NET2_FTS 396238106Sdes if (!tflag || (get_atdir(ftent->fts_statb.st_dev, 397238106Sdes ftent->fts_statb.st_ino, &mtime, &atime) < 0)) 398238106Sdes# else 399238106Sdes if (!tflag || (get_atdir(ftent->fts_statp->st_dev, 400238106Sdes ftent->fts_statp->st_ino, &mtime, &atime) < 0)) 401238106Sdes# endif 402296415Sdes continue; 403296415Sdes set_ftime(ftent->fts_path, mtime, atime, 1); 404296415Sdes continue; 405296415Sdes case FTS_DC: 406296415Sdes /* 407296415Sdes * fts claims a file system cycle 408296415Sdes */ 409296415Sdes paxwarn(1,"File system cycle found at %s",ftent->fts_path); 410296415Sdes continue; 411296415Sdes case FTS_DNR: 412296415Sdes# ifdef NET2_FTS 413296415Sdes syswarn(1, errno, 414296415Sdes# else 415296415Sdes syswarn(1, ftent->fts_errno, 416296415Sdes# endif 417296415Sdes "Unable to read directory %s", ftent->fts_path); 418296415Sdes continue; 419296415Sdes case FTS_ERR: 420238106Sdes# ifdef NET2_FTS 421238106Sdes syswarn(1, errno, 422238106Sdes# else 423238106Sdes syswarn(1, ftent->fts_errno, 424238106Sdes# endif 425238106Sdes "File system traversal error"); 426238106Sdes continue; 427238106Sdes case FTS_NS: 428238106Sdes case FTS_NSOK: 429238106Sdes# ifdef NET2_FTS 430238106Sdes syswarn(1, errno, 431238106Sdes# else 432238106Sdes syswarn(1, ftent->fts_errno, 433238106Sdes# endif 434238106Sdes "Unable to access %s", ftent->fts_path); 435238106Sdes continue; 436238106Sdes } 437238106Sdes 438238106Sdes /* 439238106Sdes * ok got a file tree node to process. copy info into arcn 440238106Sdes * structure (initialize as required) 441238106Sdes */ 442238106Sdes arcn->skip = 0; 443238106Sdes arcn->pad = 0; 444238106Sdes arcn->ln_nlen = 0; 445238106Sdes arcn->ln_name[0] = '\0'; 446238106Sdes# ifdef NET2_FTS 447238106Sdes arcn->sb = ftent->fts_statb; 448238106Sdes# else 449238106Sdes arcn->sb = *(ftent->fts_statp); 450238106Sdes# endif 451238106Sdes 452238106Sdes /* 453238106Sdes * file type based set up and copy into the arcn struct 454238106Sdes * SIDE NOTE: 455238106Sdes * we try to reset the access time on all files and directories 456238106Sdes * we may read when the -t flag is specified. files are reset 457238106Sdes * when we close them after copying. we reset the directories 458238106Sdes * when we are done with their file tree (we also clean up at 459238106Sdes * end in case we cut short a file tree traversal). However 460238106Sdes * there is no way to reset access times on symlinks. 461238106Sdes */ 462238106Sdes switch(S_IFMT & arcn->sb.st_mode) { 463238106Sdes case S_IFDIR: 464238106Sdes arcn->type = PAX_DIR; 465238106Sdes if (!tflag) 466238106Sdes break; 467238106Sdes add_atdir(ftent->fts_path, arcn->sb.st_dev, 468238106Sdes arcn->sb.st_ino, arcn->sb.st_mtime, 469238106Sdes arcn->sb.st_atime); 470238106Sdes break; 471238106Sdes case S_IFCHR: 472238106Sdes arcn->type = PAX_CHR; 473238106Sdes break; 474238106Sdes case S_IFBLK: 475238106Sdes arcn->type = PAX_BLK; 476238106Sdes break; 477238106Sdes case S_IFREG: 478238106Sdes /* 479238106Sdes * only regular files with have data to store on the 480238106Sdes * archive. all others will store a zero length skip. 481238106Sdes * the skip field is used by pax for actual data it has 482238106Sdes * to read (or skip over). 483238106Sdes */ 484238106Sdes arcn->type = PAX_REG; 485238106Sdes arcn->skip = arcn->sb.st_size; 486238106Sdes break; 487238106Sdes case S_IFLNK: 488238106Sdes arcn->type = PAX_SLK; 489238106Sdes /* 490238106Sdes * have to read the symlink path from the file 491238106Sdes */ 492238106Sdes if ((cnt = readlink(ftent->fts_path, arcn->ln_name, 493238106Sdes PAXPATHLEN - 1)) < 0) { 494238106Sdes syswarn(1, errno, "Unable to read symlink %s", 495238106Sdes ftent->fts_path); 496238106Sdes continue; 497238106Sdes } 498238106Sdes /* 499238106Sdes * set link name length, watch out readlink does not 500238106Sdes * always NUL terminate the link path 501238106Sdes */ 502238106Sdes arcn->ln_name[cnt] = '\0'; 503238106Sdes arcn->ln_nlen = cnt; 504238106Sdes break; 505238106Sdes case S_IFSOCK: 506238106Sdes /* 507238106Sdes * under BSD storing a socket is senseless but we will 508238106Sdes * let the format specific write function make the 509238106Sdes * decision of what to do with it. 510238106Sdes */ 511238106Sdes arcn->type = PAX_SCK; 512238106Sdes break; 513238106Sdes case S_IFIFO: 514238106Sdes arcn->type = PAX_FIF; 515238106Sdes break; 516238106Sdes } 517238106Sdes break; 518238106Sdes } 519238106Sdes 520238106Sdes /* 521238106Sdes * copy file name, set file name length 522238106Sdes */ 523238106Sdes arcn->nlen = l_strncpy(arcn->name, ftent->fts_path, sizeof(arcn->name) - 1); 524238106Sdes arcn->name[arcn->nlen] = '\0'; 525238106Sdes arcn->org_name = ftent->fts_path; 526238106Sdes return(0); 527238106Sdes} 528238106Sdes