Home | History | Annotate | Line # | Download | only in pax
ftree.c revision 1.1
      1 /*-
      2  * Copyright (c) 1992 Keith Muller.
      3  * Copyright (c) 1992, 1993
      4  *	The Regents of the University of California.  All rights reserved.
      5  *
      6  * This code is derived from software contributed to Berkeley by
      7  * Keith Muller of the University of California, San Diego.
      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 by the University of
     20  *	California, Berkeley and its contributors.
     21  * 4. Neither the name of the University nor the names of its contributors
     22  *    may be used to endorse or promote products derived from this software
     23  *    without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35  * SUCH DAMAGE.
     36  */
     37 
     38 #ifndef lint
     39 static char sccsid[] = "@(#)ftree.c	8.2 (Berkeley) 4/18/94";
     40 #endif /* not lint */
     41 
     42 #include <sys/types.h>
     43 #include <sys/time.h>
     44 #include <sys/stat.h>
     45 #include <sys/param.h>
     46 #include <unistd.h>
     47 #include <string.h>
     48 #include <stdio.h>
     49 #include <ctype.h>
     50 #include <errno.h>
     51 #include <stdlib.h>
     52 #include <fts.h>
     53 #include "pax.h"
     54 #include "ftree.h"
     55 #include "extern.h"
     56 
     57 /*
     58  * routines to interface with the fts library function.
     59  *
     60  * file args supplied to pax are stored on a single linked list (of type FTREE)
     61  * and given to fts to be processed one at a time. pax "selects" files from
     62  * the expansion of each arg into the corresponding file tree (if the arg is a
     63  * directory, otherwise the node itself is just passed to pax). The selection
     64  * is modified by the -n and -u flags. The user is informed when a specific
     65  * file arg does not generate any selected files. -n keeps expanding the file
     66  * tree arg until one of its files is selected, then skips to the next file
     67  * arg. when the user does not supply the file trees as command line args to
     68  * pax, they are read from stdin
     69  */
     70 
     71 static FTS *ftsp = NULL;		/* curent FTS handle */
     72 static int ftsopts;			/* options to be used on fts_open */
     73 static char *farray[2];			/* array for passing each arg to fts */
     74 static FTREE *fthead = NULL;		/* head of linked list of file args */
     75 static FTREE *fttail = NULL;		/* tail of linked list of file args */
     76 static FTREE *ftcur = NULL;		/* current file arg being processed */
     77 static FTSENT *ftent = NULL;		/* current file tree entry */
     78 static int ftree_skip;			/* when set skip to next file arg */
     79 
     80 static int ftree_arg __P((void));
     81 
     82 /*
     83  * ftree_start()
     84  *	initialize the options passed to fts_open() during this run of pax
     85  *	options are based on the selection of pax options by the user
     86  *	fts_start() also calls fts_arg() to open the first valid file arg. We
     87  *	also attempt to reset directory access times when -t (tflag) is set.
     88  * Return:
     89  *	0 if there is at least one valid file arg to process, -1 otherwise
     90  */
     91 
     92 #if __STDC__
     93 int
     94 ftree_start(void)
     95 #else
     96 int
     97 ftree_start()
     98 #endif
     99 {
    100 	/*
    101 	 * set up the operation mode of fts, open the first file arg. We must
    102 	 * use FTS_NOCHDIR, as the user may have to open multiple archives and
    103 	 * if fts did a chdir off into the boondocks, we may create an archive
    104 	 * volume in an place where the user did not expect to.
    105 	 */
    106 	ftsopts = FTS_NOCHDIR;
    107 
    108 	/*
    109 	 * optional user flags that effect file traversal
    110 	 * -H command line symlink follow only (half follow)
    111 	 * -L follow sylinks (logical)
    112 	 * -P do not follow sylinks (physical). This is the default.
    113 	 * -X do not cross over mount points
    114 	 * -t preserve access times on files read.
    115 	 * -n select only the first member of a file tree when a match is found
    116 	 * -d do not extract subtrees rooted at a directory arg.
    117 	 */
    118 	if (Lflag)
    119 		ftsopts |= FTS_LOGICAL;
    120 	else
    121 		ftsopts |= FTS_PHYSICAL;
    122 	if (Hflag)
    123 #	ifdef NET2_FTS
    124 		warn(0, "The -H flag is not supported on this version");
    125 #	else
    126 		ftsopts |= FTS_COMFOLLOW;
    127 #	endif
    128 	if (Xflag)
    129 		ftsopts |= FTS_XDEV;
    130 
    131 	if ((fthead == NULL) && ((farray[0] = malloc(PAXPATHLEN+2)) == NULL)) {
    132 		warn(1, "Unable to allocate memory for file name buffer");
    133 		return(-1);
    134 	}
    135 
    136 	if (ftree_arg() < 0)
    137 		return(-1);
    138 	if (tflag && (atdir_start() < 0))
    139 		return(-1);
    140 	return(0);
    141 }
    142 
    143 /*
    144  * ftree_add()
    145  *	add the arg to the linked list of files to process. Each will be
    146  *	processed by fts one at a time
    147  * Return:
    148  *	0 if added to the linked list, -1 if failed
    149  */
    150 
    151 #if __STDC__
    152 int
    153 ftree_add(register char *str)
    154 #else
    155 int
    156 ftree_add(str)
    157 	register char *str;
    158 #endif
    159 {
    160 	register FTREE *ft;
    161 	register int len;
    162 
    163 	/*
    164 	 * simple check for bad args
    165 	 */
    166 	if ((str == NULL) || (*str == '\0')) {
    167 		warn(0, "Invalid file name arguement");
    168 		return(-1);
    169 	}
    170 
    171 	/*
    172 	 * allocate FTREE node and add to the end of the linked list (args are
    173 	 * processed in the same order they were passed to pax). Get rid of any
    174 	 * trailing / the user may pass us. (watch out for / by itself).
    175 	 */
    176 	if ((ft = (FTREE *)malloc(sizeof(FTREE))) == NULL) {
    177 		warn(0, "Unable to allocate memory for filename");
    178 		return(-1);
    179 	}
    180 
    181 	if (((len = strlen(str) - 1) > 0) && (str[len] == '/'))
    182 		str[len] = '\0';
    183 	ft->fname = str;
    184 	ft->refcnt = 0;
    185 	ft->fow = NULL;
    186 	if (fthead == NULL) {
    187 		fttail = fthead = ft;
    188 		return(0);
    189 	}
    190 	fttail->fow = ft;
    191 	fttail = ft;
    192 	return(0);
    193 }
    194 
    195 /*
    196  * ftree_sel()
    197  *	this entry has been selected by pax. bump up reference count and handle
    198  *	-n and -d processing.
    199  */
    200 
    201 #if __STDC__
    202 void
    203 ftree_sel(register ARCHD *arcn)
    204 #else
    205 void
    206 ftree_sel(arcn)
    207 	register ARCHD *arcn;
    208 #endif
    209 {
    210 	/*
    211 	 * set reference bit for this pattern. This linked list is only used
    212 	 * when file trees are supplied pax as args. The list is not used when
    213 	 * the trees are read from stdin.
    214 	 */
    215 	if (ftcur != NULL)
    216 		ftcur->refcnt = 1;
    217 
    218 	/*
    219 	 * if -n we are done with this arg, force a skip to the next arg when
    220 	 * pax asks for the next file in next_file().
    221 	 * if -d we tell fts only to match the directory (if the arg is a dir)
    222 	 * and not the entire file tree rooted at that point.
    223 	 */
    224 	if (nflag)
    225 		ftree_skip = 1;
    226 
    227 	if (!dflag || (arcn->type != PAX_DIR))
    228 		return;
    229 
    230 	if (ftent != NULL)
    231 		(void)fts_set(ftsp, ftent, FTS_SKIP);
    232 }
    233 
    234 /*
    235  * ftree_chk()
    236  *	called at end on pax execution. Prints all those file args that did not
    237  *	have a selected member (reference count still 0)
    238  */
    239 
    240 #if __STDC__
    241 void
    242 ftree_chk(void)
    243 #else
    244 void
    245 ftree_chk()
    246 #endif
    247 {
    248 	register FTREE *ft;
    249 	register int wban = 0;
    250 
    251 	/*
    252 	 * make sure all dir access times were reset.
    253 	 */
    254 	if (tflag)
    255 		atdir_end();
    256 
    257 	/*
    258 	 * walk down list and check reference count. Print out those members
    259 	 * that never had a match
    260 	 */
    261 	for (ft = fthead; ft != NULL; ft = ft->fow) {
    262 		if (ft->refcnt > 0)
    263 			continue;
    264 		if (wban == 0) {
    265 			warn(1,"WARNING! These file names were not selected:");
    266 			++wban;
    267 		}
    268 		(void)fprintf(stderr, "%s\n", ft->fname);
    269 	}
    270 }
    271 
    272 /*
    273  * ftree_arg()
    274  *	Get the next file arg for fts to process. Can be from either the linked
    275  *	list or read from stdin when the user did not them as args to pax. Each
    276  *	arg is processed until the first successful fts_open().
    277  * Return:
    278  *	0 when the next arg is ready to go, -1 if out of file args (or EOF on
    279  *	stdin).
    280  */
    281 
    282 #if __STDC__
    283 static int
    284 ftree_arg(void)
    285 #else
    286 static int
    287 ftree_arg()
    288 #endif
    289 {
    290 	register char *pt;
    291 
    292 	/*
    293 	 * close off the current file tree
    294 	 */
    295 	if (ftsp != NULL) {
    296 		(void)fts_close(ftsp);
    297 		ftsp = NULL;
    298 	}
    299 
    300 	/*
    301 	 * keep looping until we get a valid file tree to process. Stop when we
    302 	 * reach the end of the list (or get an eof on stdin)
    303 	 */
    304 	for(;;) {
    305 		if (fthead == NULL) {
    306 			/*
    307 			 * the user didn't supply any args, get the file trees
    308 			 * to process from stdin;
    309 			 */
    310 			if (fgets(farray[0], PAXPATHLEN+1, stdin) == NULL)
    311 				return(-1);
    312 			if ((pt = strchr(farray[0], '\n')) != NULL)
    313 				*pt = '\0';
    314 		} else {
    315 			/*
    316 			 * the user supplied the file args as arguements to pax
    317 			 */
    318 			if (ftcur == NULL)
    319 				ftcur = fthead;
    320 			else if ((ftcur = ftcur->fow) == NULL)
    321 				return(-1);
    322 			farray[0] = ftcur->fname;
    323 		}
    324 
    325 		/*
    326 		 * watch it, fts wants the file arg stored in a array of char
    327 		 * ptrs, with the last one a null. we use a two element array
    328 		 * and set farray[0] to point at the buffer with the file name
    329 		 * in it. We cannnot pass all the file args to fts at one shot
    330 		 * as we need to keep a handle on which file arg generates what
    331 		 * files (the -n and -d flags need this). If the open is
    332 		 * successful, return a 0.
    333 		 */
    334 		if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL)
    335 			break;
    336 	}
    337 	return(0);
    338 }
    339 
    340 /*
    341  * next_file()
    342  *	supplies the next file to process in the supplied archd structure.
    343  * Return:
    344  *	0 when contents of arcn have been set with the next file, -1 when done.
    345  */
    346 
    347 #if __STDC__
    348 int
    349 next_file(register ARCHD *arcn)
    350 #else
    351 int
    352 next_file(arcn)
    353 	register ARCHD *arcn;
    354 #endif
    355 {
    356 	register int cnt;
    357 	time_t atime;
    358 	time_t mtime;
    359 
    360 	/*
    361 	 * ftree_sel() might have set the ftree_skip flag if the user has the
    362 	 * -n option and a file was selected from this file arg tree. (-n says
    363 	 * only one member is matched for each pattern) ftree_skip being 1
    364 	 * forces us to go to the next arg now.
    365 	 */
    366 	if (ftree_skip) {
    367 		/*
    368 		 * clear and go to next arg
    369 		 */
    370 		ftree_skip = 0;
    371 		if (ftree_arg() < 0)
    372 			return(-1);
    373 	}
    374 
    375 	/*
    376 	 * loop until we get a valid file to process
    377 	 */
    378 	for(;;) {
    379 		if ((ftent = fts_read(ftsp)) == NULL) {
    380 			/*
    381 			 * out of files in this tree, go to next arg, if none
    382 			 * we are done
    383 			 */
    384 			if (ftree_arg() < 0)
    385 				return(-1);
    386 			continue;
    387 		}
    388 
    389 		/*
    390 		 * handle each type of fts_read() flag
    391 		 */
    392 		switch(ftent->fts_info) {
    393 		case FTS_D:
    394 		case FTS_DEFAULT:
    395 		case FTS_F:
    396 		case FTS_SL:
    397 		case FTS_SLNONE:
    398 			/*
    399 			 * these are all ok
    400 			 */
    401 			break;
    402 		case FTS_DP:
    403 			/*
    404 			 * already saw this directory. If the user wants file
    405 			 * access times reset, we use this to restore the
    406 			 * access time for this directory since this is the
    407 			 * last time we will see it in this file subtree
    408 			 * remember to force the time (this is -t on a read
    409 			 * directory, not a created directory).
    410 			 */
    411 #			ifdef NET2_FTS
    412 			if (!tflag || (get_atdir(ftent->fts_statb.st_dev,
    413 			    ftent->fts_statb.st_ino, &mtime, &atime) < 0))
    414 #			else
    415 			if (!tflag || (get_atdir(ftent->fts_statp->st_dev,
    416 			    ftent->fts_statp->st_ino, &mtime, &atime) < 0))
    417 #			endif
    418 				continue;
    419 			set_ftime(ftent->fts_path, mtime, atime, 1);
    420 			continue;
    421 		case FTS_DC:
    422 			/*
    423 			 * fts claims a file system cycle
    424 			 */
    425 			warn(1,"File system cycle found at %s",ftent->fts_path);
    426 			continue;
    427 		case FTS_DNR:
    428 #			ifdef NET2_FTS
    429 			syswarn(1, errno,
    430 #			else
    431 			syswarn(1, ftent->fts_errno,
    432 #			endif
    433 			    "Unable to read directory %s", ftent->fts_path);
    434 			continue;
    435 		case FTS_ERR:
    436 #			ifdef NET2_FTS
    437 			syswarn(1, errno,
    438 #			else
    439 			syswarn(1, ftent->fts_errno,
    440 #			endif
    441 			    "File system traversal error");
    442 			continue;
    443 		case FTS_NS:
    444 		case FTS_NSOK:
    445 #			ifdef NET2_FTS
    446 			syswarn(1, errno,
    447 #			else
    448 			syswarn(1, ftent->fts_errno,
    449 #			endif
    450 			    "Unable to access %s", ftent->fts_path);
    451 			continue;
    452 		}
    453 
    454 		/*
    455 		 * ok got a file tree node to process. copy info into arcn
    456 		 * structure (initialize as required)
    457 		 */
    458 		arcn->skip = 0;
    459 		arcn->pad = 0;
    460 		arcn->ln_nlen = 0;
    461 		arcn->ln_name[0] = '\0';
    462 #		ifdef NET2_FTS
    463 		arcn->sb = ftent->fts_statb;
    464 #		else
    465 		arcn->sb = *(ftent->fts_statp);
    466 #		endif
    467 
    468 		/*
    469 		 * file type based set up and copy into the arcn struct
    470 		 * SIDE NOTE:
    471 		 * we try to reset the access time on all files and directories
    472 		 * we may read when the -t flag is specified. files are reset
    473 		 * when we close them after copying. we reset the directories
    474 		 * when we are done with their file tree (we also clean up at
    475 		 * end in case we cut short a file tree traversal). However
    476 		 * there is no way to reset access times on symlinks.
    477 		 */
    478 		switch(S_IFMT & arcn->sb.st_mode) {
    479 		case S_IFDIR:
    480 			arcn->type = PAX_DIR;
    481 			if (!tflag)
    482 				break;
    483 			add_atdir(ftent->fts_path, arcn->sb.st_dev,
    484 			    arcn->sb.st_ino, arcn->sb.st_mtime,
    485 			    arcn->sb.st_atime);
    486 			break;
    487 		case S_IFCHR:
    488 			arcn->type = PAX_CHR;
    489 			break;
    490 		case S_IFBLK:
    491 			arcn->type = PAX_BLK;
    492 			break;
    493 		case S_IFREG:
    494 			/*
    495 			 * only regular files with have data to store on the
    496 			 * archive. all others will store a zero length skip.
    497 			 * the skip field is used by pax for actual data it has
    498 			 * to read (or skip over).
    499 			 */
    500 			arcn->type = PAX_REG;
    501 			arcn->skip = arcn->sb.st_size;
    502 			break;
    503 		case S_IFLNK:
    504 			arcn->type = PAX_SLK;
    505 			/*
    506 			 * have to read the symlink path from the file
    507 			 */
    508 			if ((cnt = readlink(ftent->fts_path, arcn->ln_name,
    509 			    PAXPATHLEN)) < 0) {
    510 				syswarn(1, errno, "Unable to read symlink %s",
    511 				    ftent->fts_path);
    512 				continue;
    513 			}
    514 			/*
    515 			 * set link name length, watch out readlink does not
    516 			 * allways null terminate the link path
    517 			 */
    518 			arcn->ln_name[cnt] = '\0';
    519 			arcn->ln_nlen = cnt;
    520 			break;
    521 		case S_IFSOCK:
    522 			/*
    523 			 * under BSD storing a socket is senseless but we will
    524 			 * let the format specific write function make the
    525 			 * decision of what to do with it.
    526 			 */
    527 			arcn->type = PAX_SCK;
    528 			break;
    529 		case S_IFIFO:
    530 			arcn->type = PAX_FIF;
    531 			break;
    532 		}
    533 		break;
    534 	}
    535 
    536 	/*
    537 	 * copy file name, set file name length
    538 	 */
    539 	arcn->nlen = l_strncpy(arcn->name, ftent->fts_path, PAXPATHLEN+1);
    540 	arcn->name[arcn->nlen] = '\0';
    541 	arcn->org_name = ftent->fts_path;
    542 	return(0);
    543 }
    544