xref: /csrg-svn/bin/pax/ftree.c (revision 60676)
157111Smuller /*-
257111Smuller  * Copyright (c) 1992 Keith Muller.
3*60676Sbostic  * Copyright (c) 1992, 1993
4*60676Sbostic  *	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*60676Sbostic static char sccsid[] = "@(#)ftree.c	8.1 (Berkeley) 05/31/93";
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
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
8457111Smuller 	 * -H command line symlink follow only
8557111Smuller 	 * -L follow sylinks
8657111Smuller 	 * -X do not cross over mount points
8757111Smuller 	 * -t preserve access times on files read.
8857111Smuller 	 * -n select only the first member of a file tree when a match is found
8957111Smuller 	 * -d do not extract subtrees rooted at a directory arg.
9057111Smuller 	 */
9157111Smuller 	if (Lflag)
9257111Smuller 		ftsopts |= FTS_LOGICAL;
9357111Smuller 	else
9457111Smuller 		ftsopts |= FTS_PHYSICAL;
9557111Smuller 	if (Hflag)
9657111Smuller #	ifdef NET2_FTS
9757111Smuller 		warn(0, "The -H flag is not supported on this version");
9857111Smuller #	else
9957111Smuller 		ftsopts |= FTS_COMFOLLOW;
10057111Smuller #	endif
10157111Smuller 	if (Xflag)
10257111Smuller 		ftsopts |= FTS_XDEV;
10357111Smuller 
10457111Smuller 	if ((fthead == NULL) && ((farray[0] = malloc(PAXPATHLEN+2)) == NULL)) {
10557111Smuller 		warn(1, "Unable to allocate memory for file name buffer");
10657111Smuller 		return(-1);
10757111Smuller 	}
10857111Smuller 
10957111Smuller 	if (ftree_arg() < 0)
11057111Smuller 		return(-1);
11157111Smuller 	if (tflag && (atdir_start() < 0))
11257111Smuller 		return(-1);
11357111Smuller 	return(0);
11457111Smuller }
11557111Smuller 
11657111Smuller /*
11757111Smuller  * ftree_add()
11857111Smuller  *	add the arg to the linked list of files to process. Each will be
11957111Smuller  *	processed by fts one at a time
12057111Smuller  * Return:
12157111Smuller  *	0 if added to the linked list, -1 if failed
12257111Smuller  */
12357111Smuller 
12457111Smuller #if __STDC__
12557111Smuller int
12657111Smuller ftree_add(register char *str)
12757111Smuller #else
12857111Smuller int
12957111Smuller ftree_add(str)
13057111Smuller 	register char *str;
13157111Smuller #endif
13257111Smuller {
13357111Smuller 	register FTREE *ft;
13457111Smuller 	register int len;
13557111Smuller 
13657111Smuller 	/*
13757111Smuller 	 * simple check for bad args
13857111Smuller 	 */
13957111Smuller 	if ((str == NULL) || (*str == '\0')) {
14057111Smuller 		warn(0, "Invalid file name arguement");
14157111Smuller 		return(-1);
14257111Smuller 	}
14357111Smuller 
14457111Smuller 	/*
14557111Smuller 	 * allocate FTREE node and add to the end of the linked list (args are
14657111Smuller 	 * processed in the same order they were passed to pax). Get rid of any
14757111Smuller 	 * trailing / the user may pass us. (watch out for / by itself).
14857111Smuller 	 */
14957111Smuller 	if ((ft = (FTREE *)malloc(sizeof(FTREE))) == NULL) {
15057111Smuller 		warn(0, "Unable to allocate memory for filename");
15157111Smuller 		return(-1);
15257111Smuller 	}
15357111Smuller 
15457111Smuller 	if (((len = strlen(str) - 1) > 0) && (str[len] == '/'))
15557111Smuller 		str[len] = '\0';
15657111Smuller 	ft->fname = str;
15757111Smuller 	ft->refcnt = 0;
15857111Smuller 	ft->fow = NULL;
15957111Smuller 	if (fthead == NULL) {
16057111Smuller 		fttail = fthead = ft;
16157111Smuller 		return(0);
16257111Smuller 	}
16357111Smuller 	fttail->fow = ft;
16457111Smuller 	fttail = ft;
16557111Smuller 	return(0);
16657111Smuller }
16757111Smuller 
16857111Smuller /*
16957111Smuller  * ftree_sel()
17057111Smuller  *	this entry has been selected by pax. bump up reference count and handle
17157111Smuller  *	-n and -d processing.
17257111Smuller  */
17357111Smuller 
17457111Smuller #if __STDC__
17557111Smuller void
17657111Smuller ftree_sel(register ARCHD *arcn)
17757111Smuller #else
17857111Smuller void
17957111Smuller ftree_sel(arcn)
18057111Smuller 	register ARCHD *arcn;
18157111Smuller #endif
18257111Smuller {
18357111Smuller 	/*
18457111Smuller 	 * set reference bit for this pattern. This linked list is only used
18557582Smuller 	 * when file trees are supplied pax as args. The list is not used when
18657582Smuller 	 * the trees are read from stdin.
18757111Smuller 	 */
18857581Smuller 	if (ftcur != NULL)
18957111Smuller 		ftcur->refcnt = 1;
19057111Smuller 
19157111Smuller 	/*
19257111Smuller 	 * if -n we are done with this arg, force a skip to the next arg when
19357111Smuller 	 * pax asks for the next file in next_file().
19457111Smuller 	 * if -d we tell fts only to match the directory (if the arg is a dir)
19557111Smuller 	 * and not the entire file tree rooted at that point.
19657111Smuller 	 */
19757111Smuller 	if (nflag)
19857111Smuller 		ftree_skip = 1;
19957111Smuller 
20057111Smuller 	if (!dflag || (arcn->type != PAX_DIR))
20157111Smuller 		return;
20257582Smuller 
20357111Smuller 	if (ftent != NULL)
20457111Smuller 		(void)fts_set(ftsp, ftent, FTS_SKIP);
20557111Smuller }
20657111Smuller 
20757111Smuller /*
20857111Smuller  * ftree_chk()
20957111Smuller  *	called at end on pax execution. Prints all those file args that did not
21057111Smuller  *	have a selected member (reference count still 0)
21157111Smuller  */
21257111Smuller 
21357111Smuller #if __STDC__
21457111Smuller void
21557111Smuller ftree_chk(void)
21657111Smuller #else
21757111Smuller void
21857111Smuller ftree_chk()
21957111Smuller #endif
22057111Smuller {
22157111Smuller 	register FTREE *ft;
22257111Smuller 	register int wban = 0;
22357111Smuller 
22457111Smuller 	/*
22557111Smuller 	 * make sure all dir access times were reset.
22657111Smuller 	 */
22757111Smuller 	if (tflag)
22857111Smuller 		atdir_end();
22957111Smuller 
23057111Smuller 	/*
23157111Smuller 	 * walk down list and check reference count. Print out those members
23257111Smuller 	 * that never had a match
23357111Smuller 	 */
23457111Smuller 	for (ft = fthead; ft != NULL; ft = ft->fow) {
23557111Smuller 		if (ft->refcnt > 0)
23657111Smuller 			continue;
23757111Smuller 		if (wban == 0) {
23857111Smuller 			warn(1,"WARNING! These file names were not selected:");
23957111Smuller 			++wban;
24057111Smuller 		}
24157111Smuller 		(void)fprintf(stderr, "%s\n", ft->fname);
24257111Smuller 	}
24357111Smuller }
24457111Smuller 
24557111Smuller /*
24657111Smuller  * ftree_arg()
24757111Smuller  *	Get the next file arg for fts to process. Can be from either the linked
24857111Smuller  *	list or read from stdin when the user did not them as args to pax. Each
24957111Smuller  *	arg is processed until the first successful fts_open().
25057111Smuller  * Return:
25157111Smuller  *	0 when the next arg is ready to go, -1 if out of file args (or EOF on
25257111Smuller  *	stdin).
25357111Smuller  */
25457111Smuller 
25557111Smuller #if __STDC__
25657111Smuller static int
25757111Smuller ftree_arg(void)
25857111Smuller #else
25957111Smuller static int
26057111Smuller ftree_arg()
26157111Smuller #endif
26257111Smuller {
26357111Smuller 	register char *pt;
26457111Smuller 
26557111Smuller 	/*
26657111Smuller 	 * close off the current file tree
26757111Smuller 	 */
26857111Smuller 	if (ftsp != NULL) {
26957111Smuller 		(void)fts_close(ftsp);
27057111Smuller 		ftsp = NULL;
27157111Smuller 	}
27257582Smuller 
27357111Smuller 	/*
27457111Smuller 	 * keep looping until we get a valid file tree to process. Stop when we
27557111Smuller 	 * reach the end of the list (or get an eof on stdin)
27657111Smuller 	 */
27757111Smuller 	for(;;) {
27857111Smuller 		if (fthead == NULL) {
27957111Smuller 			/*
28057111Smuller 			 * the user didn't supply any args, get the file trees
28157581Smuller 			 * to process from stdin;
28257111Smuller 			 */
28357111Smuller 			if (fgets(farray[0], PAXPATHLEN+1, stdin) == NULL)
28457111Smuller 				return(-1);
28557111Smuller 			if ((pt = strchr(farray[0], '\n')) != NULL)
28657111Smuller 				*pt = '\0';
28757111Smuller 		} else {
28857111Smuller 			/*
28957111Smuller 			 * the user supplied the file args as arguements to pax
29057111Smuller 			 */
29157581Smuller 			if (ftcur == NULL)
29257111Smuller 				ftcur = fthead;
29357582Smuller 			else if ((ftcur = ftcur->fow) == NULL)
29457581Smuller 				return(-1);
29557111Smuller 			farray[0] = ftcur->fname;
29657111Smuller 		}
29757111Smuller 
29857111Smuller 		/*
29957111Smuller 		 * watch it, fts wants the file arg stored in a array of char
30057111Smuller 		 * ptrs, with the last one a null. we use a two element array
30157111Smuller 		 * and set farray[0] to point at the buffer with the file name
30257111Smuller 		 * in it. We cannnot pass all the file args to fts at one shot
30357111Smuller 		 * as we need to keep a handle on which file arg generates what
30457111Smuller 		 * files (the -n and -d flags need this). If the open is
30557111Smuller 		 * successful, return a 0.
30657111Smuller 		 */
30757111Smuller 		if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL)
30857111Smuller 			break;
30957111Smuller 	}
31057111Smuller 	return(0);
31157111Smuller }
31257111Smuller 
31357111Smuller /*
31457111Smuller  * next_file()
31557111Smuller  *	supplies the next file to process in the supplied archd structure.
31657111Smuller  * Return:
31757111Smuller  *	0 when contents of arcn have been set with the next file, -1 when done.
31857111Smuller  */
31957111Smuller 
32057111Smuller #if __STDC__
32157111Smuller int
32257111Smuller next_file(register ARCHD *arcn)
32357111Smuller #else
32457111Smuller int
32557111Smuller next_file(arcn)
32657111Smuller 	register ARCHD *arcn;
32757111Smuller #endif
32857111Smuller {
32957111Smuller 	register int cnt;
33057111Smuller 	time_t atime;
33157111Smuller 	time_t mtime;
33257111Smuller 
33357111Smuller 	/*
33457111Smuller 	 * ftree_sel() might have set the ftree_skip flag if the user has the
33557111Smuller 	 * -n option and a file was selected from this file arg tree. (-n says
33657111Smuller 	 * only one member is matched for each pattern) ftree_skip being 1
33757582Smuller 	 * forces us to go to the next arg now.
33857111Smuller 	 */
33957111Smuller 	if (ftree_skip) {
34057111Smuller 		/*
34157111Smuller 		 * clear and go to next arg
34257111Smuller 		 */
34357111Smuller 		ftree_skip = 0;
34457111Smuller 		if (ftree_arg() < 0)
34557111Smuller 			return(-1);
34657111Smuller 	}
34757111Smuller 
34857111Smuller 	/*
34957111Smuller 	 * loop until we get a valid file to process
35057111Smuller 	 */
35157111Smuller 	for(;;) {
35257111Smuller 		if ((ftent = fts_read(ftsp)) == NULL) {
35357111Smuller 			/*
35457111Smuller 			 * out of files in this tree, go to next arg, if none
35557111Smuller 			 * we are done
35657111Smuller 			 */
35757111Smuller 			if (ftree_arg() < 0)
35857111Smuller 				return(-1);
35957111Smuller 			continue;
36057111Smuller 		}
36157111Smuller 
36257111Smuller 		/*
36357111Smuller 		 * handle each type of fts_read() flag
36457111Smuller 		 */
36557111Smuller 		switch(ftent->fts_info) {
36657111Smuller 		case FTS_D:
36757111Smuller 		case FTS_DEFAULT:
36857111Smuller 		case FTS_F:
36957111Smuller 		case FTS_SL:
37057111Smuller 		case FTS_SLNONE:
37157111Smuller 			/*
37257111Smuller 			 * these are all ok
37357111Smuller 			 */
37457111Smuller 			break;
37557111Smuller 		case FTS_DP:
37657111Smuller 			/*
37757111Smuller 			 * already saw this directory. If the user wants file
37857111Smuller 			 * access times reset, we use this to restore the
37957111Smuller 			 * access time for this directory since this is the
38057111Smuller 			 * last time we will see it in this file subtree
38157111Smuller 			 * remember to force the time (this is -t on a read
38257111Smuller 			 * directory, not a created directory).
38357111Smuller 			 */
38457111Smuller #			ifdef NET2_FTS
38557111Smuller 			if (!tflag || (get_atdir(ftent->fts_statb.st_dev,
38657111Smuller 			    ftent->fts_statb.st_ino, &mtime, &atime) < 0))
38757111Smuller #			else
38857111Smuller 			if (!tflag || (get_atdir(ftent->fts_statp->st_dev,
38957111Smuller 			    ftent->fts_statp->st_ino, &mtime, &atime) < 0))
39057111Smuller #			endif
39157111Smuller 				continue;
39257111Smuller 			set_ftime(ftent->fts_path, mtime, atime, 1);
39357111Smuller 			continue;
39457111Smuller 		case FTS_DC:
39557111Smuller 			/*
39657111Smuller 			 * fts claims a file system cycle
39757111Smuller 			 */
39857111Smuller 			warn(1,"File system cycle found at %s",ftent->fts_path);
39957111Smuller 			continue;
40057111Smuller 		case FTS_DNR:
40157111Smuller #			ifdef NET2_FTS
40257111Smuller 			syswarn(1, errno,
40357111Smuller #			else
40457111Smuller 			syswarn(1, ftent->fts_errno,
40557111Smuller #			endif
40657111Smuller 			    "Unable to read directory %s", ftent->fts_path);
40757111Smuller 			continue;
40857111Smuller 		case FTS_ERR:
40957111Smuller #			ifdef NET2_FTS
41057111Smuller 			syswarn(1, errno,
41157111Smuller #			else
41257111Smuller 			syswarn(1, ftent->fts_errno,
41357111Smuller #			endif
41457111Smuller 			    "File system traversal error");
41557111Smuller 			continue;
41657111Smuller 		case FTS_NS:
41757111Smuller 		case FTS_NSOK:
41857111Smuller #			ifdef NET2_FTS
41957111Smuller 			syswarn(1, errno,
42057111Smuller #			else
42157111Smuller 			syswarn(1, ftent->fts_errno,
42257111Smuller #			endif
42357111Smuller 			    "Unable to access %s", ftent->fts_path);
42457111Smuller 			continue;
42557111Smuller 		}
42657111Smuller 
42757111Smuller 		/*
42857111Smuller 		 * ok got a file tree node to process. copy info into arcn
42957111Smuller 		 * structure (initialize as required)
43057111Smuller 		 */
43157111Smuller 		arcn->skip = 0;
43257111Smuller 		arcn->pad = 0;
43357111Smuller 		arcn->ln_nlen = 0;
43457111Smuller 		arcn->ln_name[0] = '\0';
43557111Smuller #		ifdef NET2_FTS
43657111Smuller 		arcn->sb = ftent->fts_statb;
43757111Smuller #		else
43857111Smuller 		arcn->sb = *(ftent->fts_statp);
43957111Smuller #		endif
44057111Smuller 
44157111Smuller 		/*
44257111Smuller 		 * file type based set up and copy into the arcn struct
44357111Smuller 		 * SIDE NOTE:
44457111Smuller 		 * we try to reset the access time on all files and directories
44557111Smuller 		 * we may read when the -t flag is specified. files are reset
44657111Smuller 		 * when we close them after copying. we reset the directories
44757111Smuller 		 * when we are done with their file tree (we also clean up at
44857111Smuller 		 * end in case we cut short a file tree traversal). However
44957111Smuller 		 * there is no way to reset access times on symlinks.
45057111Smuller 		 */
45157111Smuller 		switch(S_IFMT & arcn->sb.st_mode) {
45257111Smuller 		case S_IFDIR:
45357111Smuller 			arcn->type = PAX_DIR;
45457111Smuller 			if (!tflag)
45557111Smuller 				break;
45657111Smuller 			add_atdir(ftent->fts_path, arcn->sb.st_dev,
45757111Smuller 			    arcn->sb.st_ino, arcn->sb.st_mtime,
45857111Smuller 			    arcn->sb.st_atime);
45957111Smuller 			break;
46057111Smuller 		case S_IFCHR:
46157111Smuller 			arcn->type = PAX_CHR;
46257111Smuller 			break;
46357111Smuller 		case S_IFBLK:
46457111Smuller 			arcn->type = PAX_BLK;
46557111Smuller 			break;
46657111Smuller 		case S_IFREG:
46757111Smuller 			/*
46857111Smuller 			 * only regular files with have data to store on the
46957111Smuller 			 * archive. all others will store a zero length skip.
47057111Smuller 			 * the skip field is used by pax for actual data it has
47157111Smuller 			 * to read (or skip over).
47257111Smuller 			 */
47357111Smuller 			arcn->type = PAX_REG;
47457111Smuller 			arcn->skip = arcn->sb.st_size;
47557111Smuller 			break;
47657111Smuller 		case S_IFLNK:
47757111Smuller 			arcn->type = PAX_SLK;
47857111Smuller 			/*
47957111Smuller 			 * have to read the symlink path from the file
48057111Smuller 			 */
48157111Smuller 			if ((cnt = readlink(ftent->fts_path, arcn->ln_name,
48257111Smuller 			    PAXPATHLEN)) < 0) {
48357111Smuller 				syswarn(1, errno, "Unable to read symlink %s",
48457111Smuller 				    ftent->fts_path);
48557111Smuller 				continue;
48657111Smuller 			}
48757111Smuller 			/*
48857111Smuller 			 * set link name length, watch out readlink does not
48957111Smuller 			 * allways null terminate the link path
49057111Smuller 			 */
49157111Smuller 			arcn->ln_name[cnt] = '\0';
49257111Smuller 			arcn->ln_nlen = cnt;
49357111Smuller 			break;
49457111Smuller 		case S_IFSOCK:
49557111Smuller 			/*
49657111Smuller 			 * under BSD storing a socket is senseless but we will
49757111Smuller 			 * let the format specific write function make the
49857111Smuller 			 * decision of what to do with it.
49957111Smuller 			 */
50057111Smuller 			arcn->type = PAX_SCK;
50157111Smuller 			break;
50257111Smuller 		case S_IFIFO:
50357111Smuller 			arcn->type = PAX_FIF;
50457111Smuller 			break;
50557111Smuller 		}
50657111Smuller 		break;
50757111Smuller 	}
50857111Smuller 
50957111Smuller 	/*
51057111Smuller 	 * copy file name, set file name length
51157111Smuller 	 */
51257111Smuller 	arcn->nlen = l_strncpy(arcn->name, ftent->fts_path, PAXPATHLEN+1);
51357582Smuller 	arcn->name[arcn->nlen] = '\0';
51457111Smuller 	arcn->org_name = ftent->fts_path;
51557111Smuller 	return(0);
51657111Smuller }
517