/*-1* SPDX-License-Identifier: BSD-3-Clause2*3* Copyright (c) 1992 Keith Muller.4* Copyright (c) 1992, 19935* The Regents of the University of California. All rights reserved.6*7* This code is derived from software contributed to Berkeley by8* Keith Muller of the University of California, San Diego.9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer.15* 2. Redistributions in binary form must reproduce the above copyright16* notice, this list of conditions and the following disclaimer in the17* documentation and/or other materials provided with the distribution.18* 3. Neither the name of the University nor the names of its contributors19* may be used to endorse or promote products derived from this software20* without specific prior written permission.21*22* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND23* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE24* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE25* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE26* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL27* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS28* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)29* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT30* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY31* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF32* SUCH DAMAGE.33*/3435#include <sys/types.h>36#include <sys/time.h>37#include <sys/stat.h>38#include <unistd.h>39#include <string.h>40#include <stdio.h>41#include <errno.h>42#include <stdlib.h>43#include <fts.h>44#include "pax.h"45#include "ftree.h"46#include "extern.h"4748/*49* routines to interface with the fts library function.50*51* file args supplied to pax are stored on a single linked list (of type FTREE)52* and given to fts to be processed one at a time. pax "selects" files from53* the expansion of each arg into the corresponding file tree (if the arg is a54* directory, otherwise the node itself is just passed to pax). The selection55* is modified by the -n and -u flags. The user is informed when a specific56* file arg does not generate any selected files. -n keeps expanding the file57* tree arg until one of its files is selected, then skips to the next file58* arg. when the user does not supply the file trees as command line args to59* pax, they are read from stdin60*/6162static FTS *ftsp = NULL; /* current FTS handle */63static int ftsopts; /* options to be used on fts_open */64static char *farray[2]; /* array for passing each arg to fts */65static FTREE *fthead = NULL; /* head of linked list of file args */66static FTREE *fttail = NULL; /* tail of linked list of file args */67static FTREE *ftcur = NULL; /* current file arg being processed */68static FTSENT *ftent = NULL; /* current file tree entry */69static int ftree_skip; /* when set skip to next file arg */7071static int ftree_arg(void);7273/*74* ftree_start()75* initialize the options passed to fts_open() during this run of pax76* options are based on the selection of pax options by the user77* fts_start() also calls fts_arg() to open the first valid file arg. We78* also attempt to reset directory access times when -t (tflag) is set.79* Return:80* 0 if there is at least one valid file arg to process, -1 otherwise81*/8283int84ftree_start(void)85{86/*87* Set up the operation mode of fts, open the first file arg. We must88* use FTS_NOCHDIR, as the user may have to open multiple archives and89* if fts did a chdir off into the boondocks, we may create an archive90* volume in a place where the user did not expect to.91*/92ftsopts = FTS_NOCHDIR;9394/*95* optional user flags that effect file traversal96* -H command line symlink follow only (half follow)97* -L follow symlinks (logical)98* -P do not follow symlinks (physical). This is the default.99* -X do not cross over mount points100* -t preserve access times on files read.101* -n select only the first member of a file tree when a match is found102* -d do not extract subtrees rooted at a directory arg.103*/104if (Lflag)105ftsopts |= FTS_LOGICAL;106else107ftsopts |= FTS_PHYSICAL;108if (Hflag)109ftsopts |= FTS_COMFOLLOW;110if (Xflag)111ftsopts |= FTS_XDEV;112113if ((fthead == NULL) && ((farray[0] = malloc(PAXPATHLEN+2)) == NULL)) {114paxwarn(1, "Unable to allocate memory for file name buffer");115return(-1);116}117118if (ftree_arg() < 0)119return(-1);120if (tflag && (atdir_start() < 0))121return(-1);122return(0);123}124125/*126* ftree_add()127* add the arg to the linked list of files to process. Each will be128* processed by fts one at a time129* Return:130* 0 if added to the linked list, -1 if failed131*/132133int134ftree_add(char *str, int chflg)135{136FTREE *ft;137int len;138139/*140* simple check for bad args141*/142if ((str == NULL) || (*str == '\0')) {143paxwarn(0, "Invalid file name argument");144return(-1);145}146147/*148* allocate FTREE node and add to the end of the linked list (args are149* processed in the same order they were passed to pax). Get rid of any150* trailing / the user may pass us. (watch out for / by itself).151*/152if ((ft = (FTREE *)malloc(sizeof(FTREE))) == NULL) {153paxwarn(0, "Unable to allocate memory for filename");154return(-1);155}156157if (((len = strlen(str) - 1) > 0) && (str[len] == '/'))158str[len] = '\0';159ft->fname = str;160ft->refcnt = 0;161ft->chflg = chflg;162ft->fow = NULL;163if (fthead == NULL) {164fttail = fthead = ft;165return(0);166}167fttail->fow = ft;168fttail = ft;169return(0);170}171172/*173* ftree_sel()174* this entry has been selected by pax. bump up reference count and handle175* -n and -d processing.176*/177178void179ftree_sel(ARCHD *arcn)180{181/*182* set reference bit for this pattern. This linked list is only used183* when file trees are supplied pax as args. The list is not used when184* the trees are read from stdin.185*/186if (ftcur != NULL)187ftcur->refcnt = 1;188189/*190* if -n we are done with this arg, force a skip to the next arg when191* pax asks for the next file in next_file().192* if -d we tell fts only to match the directory (if the arg is a dir)193* and not the entire file tree rooted at that point.194*/195if (nflag)196ftree_skip = 1;197198if (!dflag || (arcn->type != PAX_DIR))199return;200201if (ftent != NULL)202(void)fts_set(ftsp, ftent, FTS_SKIP);203}204205/*206* ftree_notsel()207* this entry has not been selected by pax.208*/209210void211ftree_notsel(void)212{213if (ftent != NULL)214(void)fts_set(ftsp, ftent, FTS_SKIP);215}216217/*218* ftree_chk()219* called at end on pax execution. Prints all those file args that did not220* have a selected member (reference count still 0)221*/222223void224ftree_chk(void)225{226FTREE *ft;227int wban = 0;228229/*230* make sure all dir access times were reset.231*/232if (tflag)233atdir_end();234235/*236* walk down list and check reference count. Print out those members237* that never had a match238*/239for (ft = fthead; ft != NULL; ft = ft->fow) {240if ((ft->refcnt > 0) || ft->chflg)241continue;242if (wban == 0) {243paxwarn(1,"WARNING! These file names were not selected:");244++wban;245}246(void)fprintf(stderr, "%s\n", ft->fname);247}248}249250/*251* ftree_arg()252* Get the next file arg for fts to process. Can be from either the linked253* list or read from stdin when the user did not them as args to pax. Each254* arg is processed until the first successful fts_open().255* Return:256* 0 when the next arg is ready to go, -1 if out of file args (or EOF on257* stdin).258*/259260static int261ftree_arg(void)262{263char *pt;264265/*266* close off the current file tree267*/268if (ftsp != NULL) {269(void)fts_close(ftsp);270ftsp = NULL;271}272273/*274* keep looping until we get a valid file tree to process. Stop when we275* reach the end of the list (or get an eof on stdin)276*/277for(;;) {278if (fthead == NULL) {279/*280* the user didn't supply any args, get the file trees281* to process from stdin;282*/283if (fgets(farray[0], PAXPATHLEN+1, stdin) == NULL)284return(-1);285if ((pt = strchr(farray[0], '\n')) != NULL)286*pt = '\0';287} else {288/*289* the user supplied the file args as arguments to pax290*/291if (ftcur == NULL)292ftcur = fthead;293else if ((ftcur = ftcur->fow) == NULL)294return(-1);295if (ftcur->chflg) {296/* First fchdir() back... */297if (fchdir(cwdfd) < 0) {298syswarn(1, errno,299"Can't fchdir to starting directory");300return(-1);301}302if (chdir(ftcur->fname) < 0) {303syswarn(1, errno, "Can't chdir to %s",304ftcur->fname);305return(-1);306}307continue;308} else309farray[0] = ftcur->fname;310}311312/*313* Watch it, fts wants the file arg stored in an array of char314* ptrs, with the last one a null. We use a two element array315* and set farray[0] to point at the buffer with the file name316* in it. We cannot pass all the file args to fts at one shot317* as we need to keep a handle on which file arg generates what318* files (the -n and -d flags need this). If the open is319* successful, return a 0.320*/321if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL)322break;323}324return(0);325}326327/*328* next_file()329* supplies the next file to process in the supplied archd structure.330* Return:331* 0 when contents of arcn have been set with the next file, -1 when done.332*/333334int335next_file(ARCHD *arcn)336{337int cnt;338time_t atime;339time_t mtime;340341/*342* ftree_sel() might have set the ftree_skip flag if the user has the343* -n option and a file was selected from this file arg tree. (-n says344* only one member is matched for each pattern) ftree_skip being 1345* forces us to go to the next arg now.346*/347if (ftree_skip) {348/*349* clear and go to next arg350*/351ftree_skip = 0;352if (ftree_arg() < 0)353return(-1);354}355356/*357* loop until we get a valid file to process358*/359for(;;) {360if ((ftent = fts_read(ftsp)) == NULL) {361/*362* out of files in this tree, go to next arg, if none363* we are done364*/365if (ftree_arg() < 0)366return(-1);367continue;368}369370/*371* handle each type of fts_read() flag372*/373switch(ftent->fts_info) {374case FTS_D:375case FTS_DEFAULT:376case FTS_F:377case FTS_SL:378case FTS_SLNONE:379/*380* these are all ok381*/382break;383case FTS_DP:384/*385* already saw this directory. If the user wants file386* access times reset, we use this to restore the387* access time for this directory since this is the388* last time we will see it in this file subtree389* remember to force the time (this is -t on a read390* directory, not a created directory).391*/392if (!tflag || (get_atdir(ftent->fts_statp->st_dev,393ftent->fts_statp->st_ino, &mtime, &atime) < 0))394continue;395set_ftime(ftent->fts_path, mtime, atime, 1);396continue;397case FTS_DC:398/*399* fts claims a file system cycle400*/401paxwarn(1,"File system cycle found at %s",ftent->fts_path);402continue;403case FTS_DNR:404syswarn(1, ftent->fts_errno,405"Unable to read directory %s", ftent->fts_path);406continue;407case FTS_ERR:408syswarn(1, ftent->fts_errno,409"File system traversal error");410continue;411case FTS_NS:412case FTS_NSOK:413syswarn(1, ftent->fts_errno,414"Unable to access %s", ftent->fts_path);415continue;416}417418/*419* ok got a file tree node to process. copy info into arcn420* structure (initialize as required)421*/422arcn->skip = 0;423arcn->pad = 0;424arcn->ln_nlen = 0;425arcn->ln_name[0] = '\0';426arcn->sb = *(ftent->fts_statp);427428/*429* file type based set up and copy into the arcn struct430* SIDE NOTE:431* we try to reset the access time on all files and directories432* we may read when the -t flag is specified. files are reset433* when we close them after copying. we reset the directories434* when we are done with their file tree (we also clean up at435* end in case we cut short a file tree traversal). However436* there is no way to reset access times on symlinks.437*/438switch(S_IFMT & arcn->sb.st_mode) {439case S_IFDIR:440arcn->type = PAX_DIR;441if (!tflag)442break;443add_atdir(ftent->fts_path, arcn->sb.st_dev,444arcn->sb.st_ino, arcn->sb.st_mtime,445arcn->sb.st_atime);446break;447case S_IFCHR:448arcn->type = PAX_CHR;449break;450case S_IFBLK:451arcn->type = PAX_BLK;452break;453case S_IFREG:454/*455* only regular files with have data to store on the456* archive. all others will store a zero length skip.457* the skip field is used by pax for actual data it has458* to read (or skip over).459*/460arcn->type = PAX_REG;461arcn->skip = arcn->sb.st_size;462break;463case S_IFLNK:464arcn->type = PAX_SLK;465/*466* have to read the symlink path from the file467*/468if ((cnt = readlink(ftent->fts_path, arcn->ln_name,469PAXPATHLEN - 1)) < 0) {470syswarn(1, errno, "Unable to read symlink %s",471ftent->fts_path);472continue;473}474/*475* set link name length, watch out readlink does not476* always NUL terminate the link path477*/478arcn->ln_name[cnt] = '\0';479arcn->ln_nlen = cnt;480break;481case S_IFSOCK:482/*483* under BSD storing a socket is senseless but we will484* let the format specific write function make the485* decision of what to do with it.486*/487arcn->type = PAX_SCK;488break;489case S_IFIFO:490arcn->type = PAX_FIF;491break;492}493break;494}495496/*497* copy file name, set file name length498*/499arcn->nlen = l_strncpy(arcn->name, ftent->fts_path, sizeof(arcn->name) - 1);500arcn->name[arcn->nlen] = '\0';501arcn->org_name = ftent->fts_path;502return(0);503}504505506