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