xref: /csrg-svn/bin/pax/ftree.c (revision 66890)
157111Smuller /*-
257111Smuller  * Copyright (c) 1992 Keith Muller.
360676Sbostic  * Copyright (c) 1992, 1993
460676Sbostic  *	The Regents of the University of California.  All rights reserved.
557111Smuller  *
657111Smuller  * This code is derived from software contributed to Berkeley by
757111Smuller  * Keith Muller of the University of California, San Diego.
857111Smuller  *
957111Smuller  * %sccs.include.redist.c%
1057111Smuller  */
1157111Smuller 
1257111Smuller #ifndef lint
13*66890Sbostic static char sccsid[] = "@(#)ftree.c	8.2 (Berkeley) 04/18/94";
1457111Smuller #endif /* not lint */
1557111Smuller 
1657111Smuller #include <sys/types.h>
1757111Smuller #include <sys/time.h>
1857111Smuller #include <sys/stat.h>
1957111Smuller #include <sys/param.h>
2057111Smuller #include <unistd.h>
2157111Smuller #include <string.h>
2257111Smuller #include <stdio.h>
2357111Smuller #include <ctype.h>
2457111Smuller #include <errno.h>
2557111Smuller #include <stdlib.h>
2657111Smuller #include <fts.h>
2757111Smuller #include "pax.h"
2857111Smuller #include "ftree.h"
2957111Smuller #include "extern.h"
3057111Smuller 
3157111Smuller /*
3257111Smuller  * routines to interface with the fts library function.
3357111Smuller  *
3457111Smuller  * file args supplied to pax are stored on a single linked list (of type FTREE)
3557111Smuller  * and given to fts to be processed one at a time. pax "selects" files from
3657111Smuller  * the expansion of each arg into the corresponding file tree (if the arg is a
3757111Smuller  * directory, otherwise the node itself is just passed to pax). The selection
3857111Smuller  * is modified by the -n and -u flags. The user is informed when a specific
3957111Smuller  * file arg does not generate any selected files. -n keeps expanding the file
4057111Smuller  * tree arg until one of its files is selected, then skips to the next file
4157111Smuller  * arg. when the user does not supply the file trees as command line args to
4257111Smuller  * pax, they are read from stdin
4357111Smuller  */
4457111Smuller 
4557111Smuller static FTS *ftsp = NULL;		/* curent FTS handle */
4657111Smuller static int ftsopts;			/* options to be used on fts_open */
4757111Smuller static char *farray[2];			/* array for passing each arg to fts */
4857111Smuller static FTREE *fthead = NULL;		/* head of linked list of file args */
4957111Smuller static FTREE *fttail = NULL;		/* tail of linked list of file args */
5057111Smuller static FTREE *ftcur = NULL;		/* current file arg being processed */
5157111Smuller static FTSENT *ftent = NULL;		/* current file tree entry */
5257111Smuller static int ftree_skip;			/* when set skip to next file arg */
5357111Smuller 
5457111Smuller static int ftree_arg __P((void));
5557111Smuller 
5657111Smuller /*
5757111Smuller  * ftree_start()
5857111Smuller  *	initialize the options passed to fts_open() during this run of pax
5957111Smuller  *	options are based on the selection of pax options by the user
6057111Smuller  *	fts_start() also calls fts_arg() to open the first valid file arg. We
6157111Smuller  *	also attempt to reset directory access times when -t (tflag) is set.
6257111Smuller  * Return:
6357111Smuller  *	0 if there is at least one valid file arg to process, -1 otherwise
6457111Smuller  */
6557111Smuller 
6657111Smuller #if __STDC__
6757111Smuller int
ftree_start(void)6857111Smuller ftree_start(void)
6957111Smuller #else
7057111Smuller int
7157111Smuller ftree_start()
7257111Smuller #endif
7357111Smuller {
7457111Smuller 	/*
7557111Smuller 	 * set up the operation mode of fts, open the first file arg. We must
7657111Smuller 	 * use FTS_NOCHDIR, as the user may have to open multiple archives and
7757111Smuller 	 * if fts did a chdir off into the boondocks, we may create an archive
7857111Smuller 	 * volume in an place where the user did not expect to.
7957111Smuller 	 */
8057111Smuller 	ftsopts = FTS_NOCHDIR;
8157111Smuller 
8257111Smuller 	/*
8357111Smuller 	 * optional user flags that effect file traversal
84*66890Sbostic 	 * -H command line symlink follow only (half follow)
85*66890Sbostic 	 * -L follow sylinks (logical)
86*66890Sbostic 	 * -P do not follow sylinks (physical). This is the default.
8757111Smuller 	 * -X do not cross over mount points
8857111Smuller 	 * -t preserve access times on files read.
8957111Smuller 	 * -n select only the first member of a file tree when a match is found
9057111Smuller 	 * -d do not extract subtrees rooted at a directory arg.
9157111Smuller 	 */
9257111Smuller 	if (Lflag)
9357111Smuller 		ftsopts |= FTS_LOGICAL;
9457111Smuller 	else
9557111Smuller 		ftsopts |= FTS_PHYSICAL;
9657111Smuller 	if (Hflag)
9757111Smuller #	ifdef NET2_FTS
9857111Smuller 		warn(0, "The -H flag is not supported on this version");
9957111Smuller #	else
10057111Smuller 		ftsopts |= FTS_COMFOLLOW;
10157111Smuller #	endif
10257111Smuller 	if (Xflag)
10357111Smuller 		ftsopts |= FTS_XDEV;
10457111Smuller 
10557111Smuller 	if ((fthead == NULL) && ((farray[0] = malloc(PAXPATHLEN+2)) == NULL)) {
10657111Smuller 		warn(1, "Unable to allocate memory for file name buffer");
10757111Smuller 		return(-1);
10857111Smuller 	}
10957111Smuller 
11057111Smuller 	if (ftree_arg() < 0)
11157111Smuller 		return(-1);
11257111Smuller 	if (tflag && (atdir_start() < 0))
11357111Smuller 		return(-1);
11457111Smuller 	return(0);
11557111Smuller }
11657111Smuller 
11757111Smuller /*
11857111Smuller  * ftree_add()
11957111Smuller  *	add the arg to the linked list of files to process. Each will be
12057111Smuller  *	processed by fts one at a time
12157111Smuller  * Return:
12257111Smuller  *	0 if added to the linked list, -1 if failed
12357111Smuller  */
12457111Smuller 
12557111Smuller #if __STDC__
12657111Smuller int
ftree_add(register char * str)12757111Smuller ftree_add(register char *str)
12857111Smuller #else
12957111Smuller int
13057111Smuller ftree_add(str)
13157111Smuller 	register char *str;
13257111Smuller #endif
13357111Smuller {
13457111Smuller 	register FTREE *ft;
13557111Smuller 	register int len;
13657111Smuller 
13757111Smuller 	/*
13857111Smuller 	 * simple check for bad args
13957111Smuller 	 */
14057111Smuller 	if ((str == NULL) || (*str == '\0')) {
14157111Smuller 		warn(0, "Invalid file name arguement");
14257111Smuller 		return(-1);
14357111Smuller 	}
14457111Smuller 
14557111Smuller 	/*
14657111Smuller 	 * allocate FTREE node and add to the end of the linked list (args are
14757111Smuller 	 * processed in the same order they were passed to pax). Get rid of any
14857111Smuller 	 * trailing / the user may pass us. (watch out for / by itself).
14957111Smuller 	 */
15057111Smuller 	if ((ft = (FTREE *)malloc(sizeof(FTREE))) == NULL) {
15157111Smuller 		warn(0, "Unable to allocate memory for filename");
15257111Smuller 		return(-1);
15357111Smuller 	}
15457111Smuller 
15557111Smuller 	if (((len = strlen(str) - 1) > 0) && (str[len] == '/'))
15657111Smuller 		str[len] = '\0';
15757111Smuller 	ft->fname = str;
15857111Smuller 	ft->refcnt = 0;
15957111Smuller 	ft->fow = NULL;
16057111Smuller 	if (fthead == NULL) {
16157111Smuller 		fttail = fthead = ft;
16257111Smuller 		return(0);
16357111Smuller 	}
16457111Smuller 	fttail->fow = ft;
16557111Smuller 	fttail = ft;
16657111Smuller 	return(0);
16757111Smuller }
16857111Smuller 
16957111Smuller /*
17057111Smuller  * ftree_sel()
17157111Smuller  *	this entry has been selected by pax. bump up reference count and handle
17257111Smuller  *	-n and -d processing.
17357111Smuller  */
17457111Smuller 
17557111Smuller #if __STDC__
17657111Smuller void
ftree_sel(register ARCHD * arcn)17757111Smuller ftree_sel(register ARCHD *arcn)
17857111Smuller #else
17957111Smuller void
18057111Smuller ftree_sel(arcn)
18157111Smuller 	register ARCHD *arcn;
18257111Smuller #endif
18357111Smuller {
18457111Smuller 	/*
18557111Smuller 	 * set reference bit for this pattern. This linked list is only used
18657582Smuller 	 * when file trees are supplied pax as args. The list is not used when
18757582Smuller 	 * the trees are read from stdin.
18857111Smuller 	 */
18957581Smuller 	if (ftcur != NULL)
19057111Smuller 		ftcur->refcnt = 1;
19157111Smuller 
19257111Smuller 	/*
19357111Smuller 	 * if -n we are done with this arg, force a skip to the next arg when
19457111Smuller 	 * pax asks for the next file in next_file().
19557111Smuller 	 * if -d we tell fts only to match the directory (if the arg is a dir)
19657111Smuller 	 * and not the entire file tree rooted at that point.
19757111Smuller 	 */
19857111Smuller 	if (nflag)
19957111Smuller 		ftree_skip = 1;
20057111Smuller 
20157111Smuller 	if (!dflag || (arcn->type != PAX_DIR))
20257111Smuller 		return;
20357582Smuller 
20457111Smuller 	if (ftent != NULL)
20557111Smuller 		(void)fts_set(ftsp, ftent, FTS_SKIP);
20657111Smuller }
20757111Smuller 
20857111Smuller /*
20957111Smuller  * ftree_chk()
21057111Smuller  *	called at end on pax execution. Prints all those file args that did not
21157111Smuller  *	have a selected member (reference count still 0)
21257111Smuller  */
21357111Smuller 
21457111Smuller #if __STDC__
21557111Smuller void
ftree_chk(void)21657111Smuller ftree_chk(void)
21757111Smuller #else
21857111Smuller void
21957111Smuller ftree_chk()
22057111Smuller #endif
22157111Smuller {
22257111Smuller 	register FTREE *ft;
22357111Smuller 	register int wban = 0;
22457111Smuller 
22557111Smuller 	/*
22657111Smuller 	 * make sure all dir access times were reset.
22757111Smuller 	 */
22857111Smuller 	if (tflag)
22957111Smuller 		atdir_end();
23057111Smuller 
23157111Smuller 	/*
23257111Smuller 	 * walk down list and check reference count. Print out those members
23357111Smuller 	 * that never had a match
23457111Smuller 	 */
23557111Smuller 	for (ft = fthead; ft != NULL; ft = ft->fow) {
23657111Smuller 		if (ft->refcnt > 0)
23757111Smuller 			continue;
23857111Smuller 		if (wban == 0) {
23957111Smuller 			warn(1,"WARNING! These file names were not selected:");
24057111Smuller 			++wban;
24157111Smuller 		}
24257111Smuller 		(void)fprintf(stderr, "%s\n", ft->fname);
24357111Smuller 	}
24457111Smuller }
24557111Smuller 
24657111Smuller /*
24757111Smuller  * ftree_arg()
24857111Smuller  *	Get the next file arg for fts to process. Can be from either the linked
24957111Smuller  *	list or read from stdin when the user did not them as args to pax. Each
25057111Smuller  *	arg is processed until the first successful fts_open().
25157111Smuller  * Return:
25257111Smuller  *	0 when the next arg is ready to go, -1 if out of file args (or EOF on
25357111Smuller  *	stdin).
25457111Smuller  */
25557111Smuller 
25657111Smuller #if __STDC__
25757111Smuller static int
ftree_arg(void)25857111Smuller ftree_arg(void)
25957111Smuller #else
26057111Smuller static int
26157111Smuller ftree_arg()
26257111Smuller #endif
26357111Smuller {
26457111Smuller 	register char *pt;
26557111Smuller 
26657111Smuller 	/*
26757111Smuller 	 * close off the current file tree
26857111Smuller 	 */
26957111Smuller 	if (ftsp != NULL) {
27057111Smuller 		(void)fts_close(ftsp);
27157111Smuller 		ftsp = NULL;
27257111Smuller 	}
27357582Smuller 
27457111Smuller 	/*
27557111Smuller 	 * keep looping until we get a valid file tree to process. Stop when we
27657111Smuller 	 * reach the end of the list (or get an eof on stdin)
27757111Smuller 	 */
27857111Smuller 	for(;;) {
27957111Smuller 		if (fthead == NULL) {
28057111Smuller 			/*
28157111Smuller 			 * the user didn't supply any args, get the file trees
28257581Smuller 			 * to process from stdin;
28357111Smuller 			 */
28457111Smuller 			if (fgets(farray[0], PAXPATHLEN+1, stdin) == NULL)
28557111Smuller 				return(-1);
28657111Smuller 			if ((pt = strchr(farray[0], '\n')) != NULL)
28757111Smuller 				*pt = '\0';
28857111Smuller 		} else {
28957111Smuller 			/*
29057111Smuller 			 * the user supplied the file args as arguements to pax
29157111Smuller 			 */
29257581Smuller 			if (ftcur == NULL)
29357111Smuller 				ftcur = fthead;
29457582Smuller 			else if ((ftcur = ftcur->fow) == NULL)
29557581Smuller 				return(-1);
29657111Smuller 			farray[0] = ftcur->fname;
29757111Smuller 		}
29857111Smuller 
29957111Smuller 		/*
30057111Smuller 		 * watch it, fts wants the file arg stored in a array of char
30157111Smuller 		 * ptrs, with the last one a null. we use a two element array
30257111Smuller 		 * and set farray[0] to point at the buffer with the file name
30357111Smuller 		 * in it. We cannnot pass all the file args to fts at one shot
30457111Smuller 		 * as we need to keep a handle on which file arg generates what
30557111Smuller 		 * files (the -n and -d flags need this). If the open is
30657111Smuller 		 * successful, return a 0.
30757111Smuller 		 */
30857111Smuller 		if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL)
30957111Smuller 			break;
31057111Smuller 	}
31157111Smuller 	return(0);
31257111Smuller }
31357111Smuller 
31457111Smuller /*
31557111Smuller  * next_file()
31657111Smuller  *	supplies the next file to process in the supplied archd structure.
31757111Smuller  * Return:
31857111Smuller  *	0 when contents of arcn have been set with the next file, -1 when done.
31957111Smuller  */
32057111Smuller 
32157111Smuller #if __STDC__
32257111Smuller int
next_file(register ARCHD * arcn)32357111Smuller next_file(register ARCHD *arcn)
32457111Smuller #else
32557111Smuller int
32657111Smuller next_file(arcn)
32757111Smuller 	register ARCHD *arcn;
32857111Smuller #endif
32957111Smuller {
33057111Smuller 	register int cnt;
33157111Smuller 	time_t atime;
33257111Smuller 	time_t mtime;
33357111Smuller 
33457111Smuller 	/*
33557111Smuller 	 * ftree_sel() might have set the ftree_skip flag if the user has the
33657111Smuller 	 * -n option and a file was selected from this file arg tree. (-n says
33757111Smuller 	 * only one member is matched for each pattern) ftree_skip being 1
33857582Smuller 	 * forces us to go to the next arg now.
33957111Smuller 	 */
34057111Smuller 	if (ftree_skip) {
34157111Smuller 		/*
34257111Smuller 		 * clear and go to next arg
34357111Smuller 		 */
34457111Smuller 		ftree_skip = 0;
34557111Smuller 		if (ftree_arg() < 0)
34657111Smuller 			return(-1);
34757111Smuller 	}
34857111Smuller 
34957111Smuller 	/*
35057111Smuller 	 * loop until we get a valid file to process
35157111Smuller 	 */
35257111Smuller 	for(;;) {
35357111Smuller 		if ((ftent = fts_read(ftsp)) == NULL) {
35457111Smuller 			/*
35557111Smuller 			 * out of files in this tree, go to next arg, if none
35657111Smuller 			 * we are done
35757111Smuller 			 */
35857111Smuller 			if (ftree_arg() < 0)
35957111Smuller 				return(-1);
36057111Smuller 			continue;
36157111Smuller 		}
36257111Smuller 
36357111Smuller 		/*
36457111Smuller 		 * handle each type of fts_read() flag
36557111Smuller 		 */
36657111Smuller 		switch(ftent->fts_info) {
36757111Smuller 		case FTS_D:
36857111Smuller 		case FTS_DEFAULT:
36957111Smuller 		case FTS_F:
37057111Smuller 		case FTS_SL:
37157111Smuller 		case FTS_SLNONE:
37257111Smuller 			/*
37357111Smuller 			 * these are all ok
37457111Smuller 			 */
37557111Smuller 			break;
37657111Smuller 		case FTS_DP:
37757111Smuller 			/*
37857111Smuller 			 * already saw this directory. If the user wants file
37957111Smuller 			 * access times reset, we use this to restore the
38057111Smuller 			 * access time for this directory since this is the
38157111Smuller 			 * last time we will see it in this file subtree
38257111Smuller 			 * remember to force the time (this is -t on a read
38357111Smuller 			 * directory, not a created directory).
38457111Smuller 			 */
38557111Smuller #			ifdef NET2_FTS
38657111Smuller 			if (!tflag || (get_atdir(ftent->fts_statb.st_dev,
38757111Smuller 			    ftent->fts_statb.st_ino, &mtime, &atime) < 0))
38857111Smuller #			else
38957111Smuller 			if (!tflag || (get_atdir(ftent->fts_statp->st_dev,
39057111Smuller 			    ftent->fts_statp->st_ino, &mtime, &atime) < 0))
39157111Smuller #			endif
39257111Smuller 				continue;
39357111Smuller 			set_ftime(ftent->fts_path, mtime, atime, 1);
39457111Smuller 			continue;
39557111Smuller 		case FTS_DC:
39657111Smuller 			/*
39757111Smuller 			 * fts claims a file system cycle
39857111Smuller 			 */
39957111Smuller 			warn(1,"File system cycle found at %s",ftent->fts_path);
40057111Smuller 			continue;
40157111Smuller 		case FTS_DNR:
40257111Smuller #			ifdef NET2_FTS
40357111Smuller 			syswarn(1, errno,
40457111Smuller #			else
40557111Smuller 			syswarn(1, ftent->fts_errno,
40657111Smuller #			endif
40757111Smuller 			    "Unable to read directory %s", ftent->fts_path);
40857111Smuller 			continue;
40957111Smuller 		case FTS_ERR:
41057111Smuller #			ifdef NET2_FTS
41157111Smuller 			syswarn(1, errno,
41257111Smuller #			else
41357111Smuller 			syswarn(1, ftent->fts_errno,
41457111Smuller #			endif
41557111Smuller 			    "File system traversal error");
41657111Smuller 			continue;
41757111Smuller 		case FTS_NS:
41857111Smuller 		case FTS_NSOK:
41957111Smuller #			ifdef NET2_FTS
42057111Smuller 			syswarn(1, errno,
42157111Smuller #			else
42257111Smuller 			syswarn(1, ftent->fts_errno,
42357111Smuller #			endif
42457111Smuller 			    "Unable to access %s", ftent->fts_path);
42557111Smuller 			continue;
42657111Smuller 		}
42757111Smuller 
42857111Smuller 		/*
42957111Smuller 		 * ok got a file tree node to process. copy info into arcn
43057111Smuller 		 * structure (initialize as required)
43157111Smuller 		 */
43257111Smuller 		arcn->skip = 0;
43357111Smuller 		arcn->pad = 0;
43457111Smuller 		arcn->ln_nlen = 0;
43557111Smuller 		arcn->ln_name[0] = '\0';
43657111Smuller #		ifdef NET2_FTS
43757111Smuller 		arcn->sb = ftent->fts_statb;
43857111Smuller #		else
43957111Smuller 		arcn->sb = *(ftent->fts_statp);
44057111Smuller #		endif
44157111Smuller 
44257111Smuller 		/*
44357111Smuller 		 * file type based set up and copy into the arcn struct
44457111Smuller 		 * SIDE NOTE:
44557111Smuller 		 * we try to reset the access time on all files and directories
44657111Smuller 		 * we may read when the -t flag is specified. files are reset
44757111Smuller 		 * when we close them after copying. we reset the directories
44857111Smuller 		 * when we are done with their file tree (we also clean up at
44957111Smuller 		 * end in case we cut short a file tree traversal). However
45057111Smuller 		 * there is no way to reset access times on symlinks.
45157111Smuller 		 */
45257111Smuller 		switch(S_IFMT & arcn->sb.st_mode) {
45357111Smuller 		case S_IFDIR:
45457111Smuller 			arcn->type = PAX_DIR;
45557111Smuller 			if (!tflag)
45657111Smuller 				break;
45757111Smuller 			add_atdir(ftent->fts_path, arcn->sb.st_dev,
45857111Smuller 			    arcn->sb.st_ino, arcn->sb.st_mtime,
45957111Smuller 			    arcn->sb.st_atime);
46057111Smuller 			break;
46157111Smuller 		case S_IFCHR:
46257111Smuller 			arcn->type = PAX_CHR;
46357111Smuller 			break;
46457111Smuller 		case S_IFBLK:
46557111Smuller 			arcn->type = PAX_BLK;
46657111Smuller 			break;
46757111Smuller 		case S_IFREG:
46857111Smuller 			/*
46957111Smuller 			 * only regular files with have data to store on the
47057111Smuller 			 * archive. all others will store a zero length skip.
47157111Smuller 			 * the skip field is used by pax for actual data it has
47257111Smuller 			 * to read (or skip over).
47357111Smuller 			 */
47457111Smuller 			arcn->type = PAX_REG;
47557111Smuller 			arcn->skip = arcn->sb.st_size;
47657111Smuller 			break;
47757111Smuller 		case S_IFLNK:
47857111Smuller 			arcn->type = PAX_SLK;
47957111Smuller 			/*
48057111Smuller 			 * have to read the symlink path from the file
48157111Smuller 			 */
48257111Smuller 			if ((cnt = readlink(ftent->fts_path, arcn->ln_name,
48357111Smuller 			    PAXPATHLEN)) < 0) {
48457111Smuller 				syswarn(1, errno, "Unable to read symlink %s",
48557111Smuller 				    ftent->fts_path);
48657111Smuller 				continue;
48757111Smuller 			}
48857111Smuller 			/*
48957111Smuller 			 * set link name length, watch out readlink does not
49057111Smuller 			 * allways null terminate the link path
49157111Smuller 			 */
49257111Smuller 			arcn->ln_name[cnt] = '\0';
49357111Smuller 			arcn->ln_nlen = cnt;
49457111Smuller 			break;
49557111Smuller 		case S_IFSOCK:
49657111Smuller 			/*
49757111Smuller 			 * under BSD storing a socket is senseless but we will
49857111Smuller 			 * let the format specific write function make the
49957111Smuller 			 * decision of what to do with it.
50057111Smuller 			 */
50157111Smuller 			arcn->type = PAX_SCK;
50257111Smuller 			break;
50357111Smuller 		case S_IFIFO:
50457111Smuller 			arcn->type = PAX_FIF;
50557111Smuller 			break;
50657111Smuller 		}
50757111Smuller 		break;
50857111Smuller 	}
50957111Smuller 
51057111Smuller 	/*
51157111Smuller 	 * copy file name, set file name length
51257111Smuller 	 */
51357111Smuller 	arcn->nlen = l_strncpy(arcn->name, ftent->fts_path, PAXPATHLEN+1);
51457582Smuller 	arcn->name[arcn->nlen] = '\0';
51557111Smuller 	arcn->org_name = ftent->fts_path;
51657111Smuller 	return(0);
51757111Smuller }
518