Пример #1
0
int
add_name(char *oname, int onamelen, char *nname)
{
	NAMT *pt;
	u_int indx;

	if (ntab == NULL) {
		/*
		 * should never happen
		 */
		tty_warn(0, "No interactive rename table, links may fail\n");
		return 0;
	}

	/*
	 * look to see if we have already mapped this file, if so we
	 * will update it
	 */
	indx = st_hash(oname, onamelen, N_TAB_SZ);
	if ((pt = ntab[indx]) != NULL) {
		/*
		 * look down the has chain for the file
		 */
		while ((pt != NULL) && (strcmp(oname, pt->oname) != 0))
			pt = pt->fow;

		if (pt != NULL) {
			/*
			 * found an old mapping, replace it with the new one
			 * the user just input (if it is different)
			 */
			if (strcmp(nname, pt->nname) == 0)
				return 0;

			(void)free((char *)pt->nname);
			if ((pt->nname = strdup(nname)) == NULL) {
				tty_warn(1, "Cannot update rename table");
				return -1;
			}
			return 0;
		}
	}

	/*
	 * this is a new mapping, add it to the table
	 */
	if ((pt = (NAMT *)malloc(sizeof(NAMT))) != NULL) {
		if ((pt->oname = strdup(oname)) != NULL) {
			if ((pt->nname = strdup(nname)) != NULL) {
				pt->fow = ntab[indx];
				ntab[indx] = pt;
				return 0;
			}
			(void)free((char *)pt->oname);
		}
		(void)free((char *)pt);
	}
	tty_warn(1, "Interactive rename table out of memory");
	return -1;
}
Пример #2
0
__dead static void
sig_cleanup(int which_sig)
{
	/*
	 * restore modes and times for any dirs we may have created
	 * or any dirs we may have read. Set vflag and vfpart so the user
	 * will clearly see the message on a line by itself.
	 */
	vflag = vfpart = 1;
#ifdef SIGXCPU
	if (which_sig == SIGXCPU)
		tty_warn(1, "CPU time limit reached, cleaning up.");
	else
#endif
		tty_warn(1, "Signal caught, cleaning up.");

	/* delete any open temporary file */
	if (xtmp_name)
		(void)unlink(xtmp_name);
	ar_close();
	proc_dir();
	if (tflag)
		atdir_end();

	(void)raise_default_signal(which_sig);
	exit(1);
}
Пример #3
0
int
opt_add(const char *str)
{
	OPLIST *opt;
	char *frpt;
	char *pt;
	char *endpt;
	char *dstr;

	if ((str == NULL) || (*str == '\0')) {
		tty_warn(0, "Invalid option name");
		return -1;
	}
	if ((dstr = strdup(str)) == NULL) {
		tty_warn(0, "Unable to allocate space for option list");
		return -1;
	}
	frpt = endpt = dstr;

	/*
	 * break into name and values pieces and stuff each one into a
	 * OPLIST structure. When we know the format, the format specific
	 * option function will go through this list
	 */
	while ((frpt != NULL) && (*frpt != '\0')) {
		if ((endpt = strchr(frpt, ',')) != NULL)
			*endpt = '\0';
		if ((pt = strchr(frpt, '=')) == NULL) {
			tty_warn(0, "Invalid options format");
			free(dstr);
			return -1;
		}
		if ((opt = (OPLIST *)malloc(sizeof(OPLIST))) == NULL) {
			tty_warn(0, "Unable to allocate space for option list");
			free(dstr);
			return -1;
		}
		*pt++ = '\0';
		opt->name = frpt;
		opt->value = pt;
		opt->fow = NULL;
		if (endpt != NULL)
			frpt = endpt + 1;
		else
			frpt = NULL;
		if (ophead == NULL) {
			optail = ophead = opt;
			continue;
		}
		optail->fow = opt;
		optail = opt;
	}
	return 0;
}
Пример #4
0
int
set_crc(ARCHD *arcn, int fd)
{
	int i;
	int res;
	off_t cpcnt = 0L;
	u_long size;
	unsigned long crc = 0L;
	char tbuf[FILEBLK];
	struct stat sb;

	if (fd < 0) {
		/*
		 * hmm, no fd, should never happen. well no crc then.
		 */
		arcn->crc = 0L;
		return 0;
	}

	if ((size = (u_long)arcn->sb.st_blksize) > (u_long)sizeof(tbuf))
		size = (u_long)sizeof(tbuf);

	/*
	 * read all the bytes we think that there are in the file. If the user
	 * is trying to archive an active file, forget this file.
	 */
	for(;;) {
		if ((res = read(fd, tbuf, size)) <= 0)
			break;
		cpcnt += res;
		for (i = 0; i < res; ++i)
			crc += (tbuf[i] & 0xff);
	}

	/*
	 * safety check. we want to avoid archiving files that are active as
	 * they can create inconsistent archive copies.
	 */
	if (cpcnt != arcn->sb.st_size)
		tty_warn(1, "File changed size %s", arcn->org_name);
	else if (fstat(fd, &sb) < 0)
		syswarn(1, errno, "Failed stat on %s", arcn->org_name);
	else if (arcn->sb.st_mtime != sb.st_mtime)
		tty_warn(1, "File %s was modified during read", arcn->org_name);
	else if (lseek(fd, (off_t)0L, SEEK_SET) < 0)
		syswarn(1, errno, "File rewind failed on: %s", arcn->org_name);
	else {
		arcn->crc = crc;
		return 0;
	}
	return -1;
}
Пример #5
0
void
ftree_chk(void)
{
	FTREE *ft;
	int wban = 0;

	/*
	 * make sure all dir access times were reset.
	 */
	if (tflag)
		atdir_end();

	/*
	 * walk down list and check reference count. Print out those members
	 * that never had a match
	 */
	for (ft = fthead; ft != NULL; ft = ft->fow) {
		if (ft->refcnt != 0)
			continue;
		if (wban == 0) {
			tty_warn(1,
			    "WARNING! These file names were not selected:");
			++wban;
		}
		(void)fprintf(stderr, "%s\n", ft->fname);
	}
}
Пример #6
0
int
lnk_creat(ARCHD *arcn, int *payload)
{
	struct stat sb;

	/*
	 * Check if this hardlink carries the "payload". In "cpio" archives
	 * it's usually the last record of a set of hardlinks which includes
	 * the contents of the file.
	 *
	 */
	*payload = S_ISREG(arcn->sb.st_mode) &&
	    (arcn->sb.st_size > 0) && (arcn->sb.st_size <= arcn->skip);

	/*
	 * We may be running as root, so we have to be sure that link target
	 * is not a directory, so we lstat and check. XXX: This is still racy.
	 */
	if (lstat(arcn->ln_name, &sb) != -1 && S_ISDIR(sb.st_mode)) {
		tty_warn(1, "A hard link to the directory %s is not allowed",
		    arcn->ln_name);
		return -1;
	}

	return mk_link(arcn->ln_name, &sb, arcn->name, 0);
}
Пример #7
0
int
ar_app_ok(void)
{
	if (artyp == ISPIPE) {
		tty_warn(1,
		    "Cannot append to an archive obtained from a pipe.");
		return -1;
	}

	if (!invld_rec)
		return 0;
	tty_warn(1,
	    "Cannot append, device record size %d does not support %s spec",
	    rdblksz, argv0);
	return -1;
}
Пример #8
0
int
lnk_start(void)
{
	if (ltab != NULL)
		return 0;
	if ((ltab = (HRDLNK **)calloc(L_TAB_SZ, sizeof(HRDLNK *))) == NULL) {
		tty_warn(1, "Cannot allocate memory for hard link table");
		return -1;
	}
	return 0;
}
Пример #9
0
int
dev_start(void)
{
	if (dtab != NULL)
		return 0;
	if ((dtab = (DEVT **)calloc(D_TAB_SZ, sizeof(DEVT *))) == NULL) {
		tty_warn(1, "Cannot allocate memory for device mapping table");
		return -1;
	}
	return 0;
}
Пример #10
0
int
name_start(void)
{
	if (ntab != NULL)
		return 0;
	if ((ntab = (NAMT **)calloc(N_TAB_SZ, sizeof(NAMT *))) == NULL) {
		tty_warn(1,
		    "Cannot allocate memory for interactive rename table");
		return -1;
	}
	return 0;
}
Пример #11
0
int
atdir_start(void)
{
	if (atab != NULL)
		return 0;
	if ((atab = (ATDIR **)calloc(A_TAB_SZ, sizeof(ATDIR *))) == NULL) {
		tty_warn(1,
		    "Cannot allocate space for directory access time table");
		return -1;
	}
	return 0;
}
Пример #12
0
void
add_atdir(char *fname, dev_t dev, ino_t ino, time_t mtime, time_t atime)
{
	ATDIR *pt;
	u_int indx;

	if (atab == NULL)
		return;

	/*
	 * make sure this directory is not already in the table, if so just
	 * return (the older entry always has the correct time). The only
	 * way this will happen is when the same subtree can be traversed by
	 * different args to pax and the -n option is aborting fts out of a
	 * subtree before all the post-order visits have been made.
	 */
	indx = ((unsigned)ino) % A_TAB_SZ;
	if ((pt = atab[indx]) != NULL) {
		while (pt != NULL) {
			if ((pt->ino == ino) && (pt->dev == dev))
				break;
			pt = pt->fow;
		}

		/*
		 * oops, already there. Leave it alone.
		 */
		if (pt != NULL)
			return;
	}

	/*
	 * add it to the front of the hash chain
	 */
	if ((pt = (ATDIR *)malloc(sizeof(ATDIR))) != NULL) {
		if ((pt->name = strdup(fname)) != NULL) {
			pt->dev = dev;
			pt->ino = ino;
			pt->mtime = mtime;
			pt->atime = atime;
			pt->fow = atab[indx];
			atab[indx] = pt;
			return;
		}
		(void)free((char *)pt);
	}

	tty_warn(1, "Directory access time reset table ran out of memory");
	return;
}
Пример #13
0
int
ftree_add(char *str, int isdir)
{
	FTREE *ft;
	int len;

	/*
	 * simple check for bad args
	 */
	if ((str == NULL) || (*str == '\0')) {
		tty_warn(0, "Invalid file name argument");
		return -1;
	}

	/*
	 * allocate FTREE node and add to the end of the linked list (args are
	 * processed in the same order they were passed to pax). Get rid of any
	 * trailing / the user may pass us. (watch out for / by itself).
	 */
	if ((ft = (FTREE *)malloc(sizeof(FTREE))) == NULL) {
		tty_warn(0, "Unable to allocate memory for filename");
		return -1;
	}

	if (((len = strlen(str) - 1) > 0) && (str[len] == '/'))
		str[len] = '\0';
	ft->fname = str;
	ft->refcnt = -isdir;
	ft->fow = NULL;
	if (fthead == NULL) {
		fttail = fthead = ft;
		return 0;
	}
	fttail->fow = ft;
	fttail = ft;
	return 0;
}
Пример #14
0
int
pat_add(char *str, char *chdn, int flags)
{
	PATTERN *pt;

	/*
	 * throw out the junk
	 */
	if ((str == NULL) || (*str == '\0')) {
		tty_warn(1, "Empty pattern string");
		return -1;
	}

	/*
	 * allocate space for the pattern and store the pattern. the pattern is
	 * part of argv so do not bother to copy it, just point at it. Add the
	 * node to the end of the pattern list
	 */
	if ((pt = (PATTERN *)malloc(sizeof(PATTERN))) == NULL) {
		tty_warn(1, "Unable to allocate memory for pattern string");
		return -1;
	}

	pt->pstr = str;
	pt->pend = NULL;
	pt->plen = strlen(str);
	pt->fow = NULL;
	pt->flgs = flags;
	pt->chdname = chdn;
	if (pathead == NULL) {
		pattail = pathead = pt;
		return 0;
	}
	pattail->fow = pt;
	pattail = pt;
	return 0;
}
Пример #15
0
static DEVT *
chk_dev(dev_t dev, int add)
{
	DEVT *pt;
	u_int indx;

	if (dtab == NULL)
		return(NULL);
	/*
	 * look to see if this device is already in the table
	 */
	indx = ((unsigned)dev) % D_TAB_SZ;
	if ((pt = dtab[indx]) != NULL) {
		while ((pt != NULL) && (pt->dev != dev))
			pt = pt->fow;

		/*
		 * found it, return a pointer to it
		 */
		if (pt != NULL)
			return(pt);
	}

	/*
	 * not in table, we add it only if told to as this may just be a check
	 * to see if a device number is being used.
	 */
	if (add == 0)
		return(NULL);

	/*
	 * allocate a node for this device and add it to the front of the hash
	 * chain. Note we do not assign remaps values here, so the pt->list
	 * list must be NULL.
	 */
	if ((pt = (DEVT *)malloc(sizeof(DEVT))) == NULL) {
		tty_warn(1, "Device map table out of memory");
		return(NULL);
	}
	pt->dev = dev;
	pt->list = NULL;
	pt->fow = dtab[indx];
	dtab[indx] = pt;
	return(pt);
}
Пример #16
0
static int
fix_path( char *or_name, int *or_len, char *dir_name, int dir_len)
{
	char *src;
	char *dest;
	char *start;
	int len;

	/*
	 * we shift the or_name to the right enough to tack in the dir_name
	 * at the front. We make sure we have enough space for it all before
	 * we start. since dest always ends in a slash, we skip of or_name
	 * if it also starts with one.
	 */
	start = or_name;
	src = start + *or_len;
	dest = src + dir_len;
	if (*start == '/') {
		++start;
		--dest;
	}
	if ((len = dest - or_name) > PAXPATHLEN) {
		tty_warn(1, "File name %s/%s, too long", dir_name, start);
		return -1;
	}
	*or_len = len;

	/*
	 * enough space, shift
	 */
	while (src >= start)
		*dest-- = *src--;
	src = dir_name + dir_len - 1;

	/*
	 * splice in the destination directory name
	 */
	while (src >= dir_name)
		*dest-- = *src--;

	*(or_name + len) = '\0';
	return 0;
}
Пример #17
0
void
pat_chk(void)
{
	PATTERN *pt;
	int wban = 0;

	/*
	 * walk down the list checking the flags to make sure MTCH was set,
	 * if not complain
	 */
	for (pt = pathead; pt != NULL; pt = pt->fow) {
		if (pt->flgs & MTCH)
			continue;
		if (!wban) {
			tty_warn(1, "WARNING! These patterns were not matched:");
			++wban;
		}
		(void)fprintf(stderr, "%s\n", pt->pstr);
	}
}
Пример #18
0
int
dir_start(void)
{
#ifdef DIRS_USE_FILE
	if (dirfd != -1)
		return 0;

	/*
	 * unlink the file so it goes away at termination by itself
	 */
	memcpy(tempbase, _TFILE_BASE, sizeof(_TFILE_BASE));
	if ((dirfd = mkstemp(tempfile)) >= 0) {
		(void)unlink(tempfile);
		return 0;
	}
	tty_warn(1, "Unable to create temporary file for directory times: %s",
	    tempfile);
	return -1;
#else
	return (0);
#endif /* DIRS_USE_FILE */
}
Пример #19
0
int
bad_opt(void)
{
	OPLIST *opt;

	if (ophead == NULL)
		return 0;
	/*
	 * print all we were given
	 */
	tty_warn(1," These format options are not supported for %s",
	    frmt->name);
	while ((opt = opt_next()) != NULL)
		(void)fprintf(stderr, "\t%s = %s\n", opt->name, opt->value);
	if (strcmp(NM_TAR, argv0) == 0)
		tar_usage();
#ifndef NO_CPIO
	else if (strcmp(NM_CPIO, argv0) == 0)
		cpio_usage();
#endif
	else
		pax_usage();
	return 0;
}
Пример #20
0
int
chk_same(ARCHD *arcn)
{
	struct stat sb;

	/*
	 * if file does not exist, return. if file exists and -k, skip it
	 * quietly
	 */
	if (lstat(arcn->name, &sb) < 0)
		return 1;
	if (kflag)
		return 0;

	/*
	 * better make sure the user does not have src == dest by mistake
	 */
	if ((arcn->sb.st_dev == sb.st_dev) && (arcn->sb.st_ino == sb.st_ino)) {
		tty_warn(1, "Unable to copy %s, file would overwrite itself",
		    arcn->name);
		return 0;
	}
	return 1;
}
Пример #21
0
int
ftime_start(void)
{
	if (ftab != NULL)
		return 0;
	if ((ftab = (FTM **)calloc(F_TAB_SZ, sizeof(FTM *))) == NULL) {
		tty_warn(1, "Cannot allocate memory for file time table");
		return -1;
	}

	/*
	 * get random name and create temporary scratch file, unlink name
	 * so it will get removed on exit
	 */
	memcpy(tempbase, _TFILE_BASE, sizeof(_TFILE_BASE));
	if ((ffd = mkstemp(tempfile)) == -1) {
		syswarn(1, errno, "Unable to create temporary file: %s",
		    tempfile);
		return -1;
	}

	(void)unlink(tempfile);
	return 0;
}
Пример #22
0
int
next_file(ARCHD *arcn)
{
#ifndef SMALL
	static	char	curdir[PAXPATHLEN+2], curpath[PAXPATHLEN+2];
	static	int	curdirlen;

	struct stat	statbuf;
	FTSENT		Mftent;
#endif	/* SMALL */
	int		cnt;
	time_t		atime, mtime;
	char		*curlink;
#define MFTENT_DUMMY_DEV	UINT_MAX

	curlink = NULL;
#ifndef SMALL
	/*
	 * if parsing an mtree(8) specfile, build up `dummy' ftsent
	 * from specfile info, and jump below to complete setup of arcn.
	 */
	if (Mflag) {
		int	skipoptional;

 next_ftnode:
		skipoptional = 0;
		if (ftnode == NULL)		/* tree is empty */
			return (-1);

						/* get current name */
		if (snprintf(curpath, sizeof(curpath), "%s%s%s",
		    curdir, curdirlen ? "/" : "", ftnode->name)
		    >= sizeof(curpath)) {
			tty_warn(1, "line %lu: %s: %s", (u_long)ftnode->lineno,
			    curdir, strerror(ENAMETOOLONG));
			return (-1);
		}
		ftnode->flags |= F_VISIT;	/* mark node visited */

						/* construct dummy FTSENT */
		Mftent.fts_path = curpath;
		Mftent.fts_statp = &statbuf;
		Mftent.fts_pointer = ftnode;
		ftent = &Mftent;
						/* look for existing file */
		if (lstat(Mftent.fts_path, &statbuf) == -1) {
			if (ftnode->flags & F_OPT)
				skipoptional = 1;

						/* missing: fake up stat info */
			memset(&statbuf, 0, sizeof(statbuf));
			statbuf.st_dev = MFTENT_DUMMY_DEV;
			statbuf.st_ino = ftnode->lineno;
			statbuf.st_size = 0;
#define NODETEST(t, m)							\
			if (!(t)) {					\
				tty_warn(1, "line %lu: %s: %s not specified", \
				    (u_long)ftnode->lineno,		\
				    ftent->fts_path, m);		\
				return -1;				\
			}
			statbuf.st_mode = nodetoino(ftnode->type);
			NODETEST(ftnode->flags & F_TYPE, "type");
			NODETEST(ftnode->flags & F_MODE, "mode");
			if (!(ftnode->flags & F_TIME))
				statbuf.st_mtime = starttime;
			NODETEST(ftnode->flags & (F_GID | F_GNAME), "group");
			NODETEST(ftnode->flags & (F_UID | F_UNAME), "user");
			if (ftnode->type == F_BLOCK || ftnode->type == F_CHAR)
				NODETEST(ftnode->flags & F_DEV,
				    "device number");
			if (ftnode->type == F_LINK)
				NODETEST(ftnode->flags & F_SLINK, "symlink");
			/* don't require F_FLAGS or F_SIZE */
#undef NODETEST
		} else {
			if (ftnode->flags & F_TYPE && nodetoino(ftnode->type)
			    != (statbuf.st_mode & S_IFMT)) {
				tty_warn(1,
			    "line %lu: %s: type mismatch: specfile %s, tree %s",
				    (u_long)ftnode->lineno, ftent->fts_path,
				    inotype(nodetoino(ftnode->type)),
				    inotype(statbuf.st_mode));
				return -1;
			}
			if (ftnode->type == F_DIR && (ftnode->flags & F_OPT))
				skipoptional = 1;
		}
		/*
		 * override settings with those from specfile
		 */
		if (ftnode->flags & F_MODE) {
			statbuf.st_mode &= ~ALLPERMS; 
			statbuf.st_mode |= (ftnode->st_mode & ALLPERMS);
		}
		if (ftnode->flags & (F_GID | F_GNAME))
			statbuf.st_gid = ftnode->st_gid;
		if (ftnode->flags & (F_UID | F_UNAME))
			statbuf.st_uid = ftnode->st_uid;
#if HAVE_STRUCT_STAT_ST_FLAGS
		if (ftnode->flags & F_FLAGS)
			statbuf.st_flags = ftnode->st_flags;
#endif
		if (ftnode->flags & F_TIME)
#if BSD4_4 && !HAVE_NBTOOL_CONFIG_H
			statbuf.st_mtimespec = ftnode->st_mtimespec;
#else
			statbuf.st_mtime = ftnode->st_mtimespec.tv_sec;
#endif
		if (ftnode->flags & F_DEV)
			statbuf.st_rdev = ftnode->st_rdev;
		if (ftnode->flags & F_SLINK)
			curlink = ftnode->slink;
				/* ignore F_SIZE */

		/*
		 * find next node
		 */
		if (ftnode->type == F_DIR && ftnode->child != NULL) {
					/* directory with unseen child */
			ftnode = ftnode->child;
			curdirlen = strlcpy(curdir, curpath, sizeof(curdir));
		} else do {
			if (ftnode->next != NULL) {
					/* next node at current level */
				ftnode = ftnode->next;
			} else {	/* move back to parent */
					/* reset time only on first cd.. */
				if (Mftent.fts_pointer == ftnode && tflag &&
				    (get_atdir(MFTENT_DUMMY_DEV, ftnode->lineno,
				    &mtime, &atime) == 0)) {
					set_ftime(ftent->fts_path,
					    mtime, atime, 1, 0);
				}
				ftnode = ftnode->parent;
				if (ftnode->parent == ftnode)
					ftnode = NULL;
				else {
					curdirlen -= strlen(ftnode->name) + 1;
					curdir[curdirlen] = '\0';
				}
			}
		} while (ftnode != NULL && ftnode->flags & F_VISIT);
		if (skipoptional)	/* skip optional entries */
			goto next_ftnode;
		goto got_ftent;
	}
#endif	/* SMALL */

	/*
	 * ftree_sel() might have set the ftree_skip flag if the user has the
	 * -n option and a file was selected from this file arg tree. (-n says
	 * only one member is matched for each pattern) ftree_skip being 1
	 * forces us to go to the next arg now.
	 */
	if (ftree_skip) {
		/*
		 * clear and go to next arg
		 */
		ftree_skip = 0;
		if (ftree_arg() < 0)
			return -1;
	}

	if (ftsp == NULL)
		return -1;
	/*
	 * loop until we get a valid file to process
	 */
	for(;;) {
		if ((ftent = fts_read(ftsp)) == NULL) {
			/*
			 * out of files in this tree, go to next arg, if none
			 * we are done
			 */
			if (ftree_arg() < 0)
				return -1;
			continue;
		}

		/*
		 * handle each type of fts_read() flag
		 */
		switch(ftent->fts_info) {
		case FTS_D:
		case FTS_DEFAULT:
		case FTS_F:
		case FTS_SL:
		case FTS_SLNONE:
			/*
			 * these are all ok
			 */
			break;
		case FTS_DP:
			/*
			 * already saw this directory. If the user wants file
			 * access times reset, we use this to restore the
			 * access time for this directory since this is the
			 * last time we will see it in this file subtree
			 * remember to force the time (this is -t on a read
			 * directory, not a created directory).
			 */
			if (!tflag || (get_atdir(
			    ftent->fts_statp->st_dev, ftent->fts_statp->st_ino,
			    &mtime, &atime) < 0))
				continue;
			set_ftime(ftent->fts_path, mtime, atime, 1, 0);
			continue;
		case FTS_DC:
			/*
			 * fts claims a file system cycle
			 */
			tty_warn(1,"File system cycle found at %s",
			    ftent->fts_path);
			continue;
		case FTS_DNR:
			syswarn(1, FTS_ERRNO(ftent),
			    "Unable to read directory %s", ftent->fts_path);
			continue;
		case FTS_ERR:
			syswarn(1, FTS_ERRNO(ftent),
			    "File system traversal error");
			continue;
		case FTS_NS:
		case FTS_NSOK:
			syswarn(1, FTS_ERRNO(ftent),
			    "Unable to access %s", ftent->fts_path);
			continue;
		}

#ifndef SMALL
 got_ftent:
#endif	/* SMALL */
		/*
		 * ok got a file tree node to process. copy info into arcn
		 * structure (initialize as required)
		 */
		arcn->skip = 0;
		arcn->pad = 0;
		arcn->ln_nlen = 0;
		arcn->ln_name[0] = '\0';
		arcn->sb = *(ftent->fts_statp);

		/*
		 * file type based set up and copy into the arcn struct
		 * SIDE NOTE:
		 * we try to reset the access time on all files and directories
		 * we may read when the -t flag is specified. files are reset
		 * when we close them after copying. we reset the directories
		 * when we are done with their file tree (we also clean up at
		 * end in case we cut short a file tree traversal). However
		 * there is no way to reset access times on symlinks.
		 */
		switch(S_IFMT & arcn->sb.st_mode) {
		case S_IFDIR:
			arcn->type = PAX_DIR;
			if (!tflag)
				break;
			add_atdir(ftent->fts_path, arcn->sb.st_dev,
			    arcn->sb.st_ino, arcn->sb.st_mtime,
			    arcn->sb.st_atime);
			break;
		case S_IFCHR:
			arcn->type = PAX_CHR;
			break;
		case S_IFBLK:
			arcn->type = PAX_BLK;
			break;
		case S_IFREG:
			/*
			 * only regular files with have data to store on the
			 * archive. all others will store a zero length skip.
			 * the skip field is used by pax for actual data it has
			 * to read (or skip over).
			 */
			arcn->type = PAX_REG;
			arcn->skip = arcn->sb.st_size;
			break;
		case S_IFLNK:
			arcn->type = PAX_SLK;
			if (curlink != NULL) {
				cnt = strlcpy(arcn->ln_name, curlink,
				    sizeof(arcn->ln_name));
			/*
			 * have to read the symlink path from the file
			 */
			} else if ((cnt =
			    readlink(ftent->fts_path, arcn->ln_name,
			    sizeof(arcn->ln_name) - 1)) < 0) {
				syswarn(1, errno, "Unable to read symlink %s",
				    ftent->fts_path);
				continue;
			}
			/*
			 * set link name length, watch out readlink does not
			 * always null terminate the link path
			 */
			arcn->ln_name[cnt] = '\0';
			arcn->ln_nlen = cnt;
			break;
#ifdef S_IFSOCK
		case S_IFSOCK:
			/*
			 * under BSD storing a socket is senseless but we will
			 * let the format specific write function make the
			 * decision of what to do with it.
			 */
			arcn->type = PAX_SCK;
			break;
#endif
		case S_IFIFO:
			arcn->type = PAX_FIF;
			break;
		}
		break;
	}

	/*
	 * copy file name, set file name length
	 */
	arcn->nlen = strlcpy(arcn->name, ftent->fts_path, sizeof(arcn->name));
	arcn->org_name = arcn->fts_name;
	strlcpy(arcn->fts_name, ftent->fts_path, sizeof arcn->fts_name);
	if (strcmp(NM_CPIO, argv0) == 0) {
		/*
		 * cpio does *not* descend directories listed in the
		 * arguments, unlike pax/tar, so needs special handling
		 * here.  failure to do so results in massive amounts
		 * of duplicated files in the output. We kill fts after
		 * the first name is extracted, what a waste.
		 */
		ftcur->refcnt = 1;
		(void)ftree_arg();
	}
	return 0;
}
Пример #23
0
int
map_dev(ARCHD *arcn, u_long dev_mask, u_long ino_mask)
{
	DEVT *pt;
	DLIST *dpt;
	static dev_t lastdev = 0;	/* next device number to try */
	int trc_ino = 0;
	int trc_dev = 0;
	ino_t trunc_bits = 0;
	ino_t nino;

	if (dtab == NULL)
		return 0;
	/*
	 * check for device and inode truncation, and extract the truncated
	 * bit pattern.
	 */
	if ((arcn->sb.st_dev & (dev_t)dev_mask) != arcn->sb.st_dev)
		++trc_dev;
	if ((nino = arcn->sb.st_ino & (ino_t)ino_mask) != arcn->sb.st_ino) {
		++trc_ino;
		trunc_bits = arcn->sb.st_ino & (ino_t)(~ino_mask);
	}

	/*
	 * see if this device is already being mapped, look up the device
	 * then find the truncation bit pattern which applies
	 */
	if ((pt = chk_dev(arcn->sb.st_dev, 0)) != NULL) {
		/*
		 * this device is already marked to be remapped
		 */
		for (dpt = pt->list; dpt != NULL; dpt = dpt->fow)
			if (dpt->trunc_bits == trunc_bits)
				break;

		if (dpt != NULL) {
			/*
			 * we are being remapped for this device and pattern
			 * change the device number to be stored and return
			 */
			arcn->sb.st_dev = dpt->dev;
			arcn->sb.st_ino = nino;
			return 0;
		}
	} else {
		/*
		 * this device is not being remapped YET. if we do not have any
		 * form of truncation, we do not need a remap
		 */
		if (!trc_ino && !trc_dev)
			return 0;

		/*
		 * we have truncation, have to add this as a device to remap
		 */
		if ((pt = chk_dev(arcn->sb.st_dev, 1)) == NULL)
			goto bad;

		/*
		 * if we just have a truncated inode, we have to make sure that
		 * all future inodes that do not truncate (they have the
		 * truncation pattern of all 0's) continue to map to the same
		 * device number. We probably have already written inodes with
		 * this device number to the archive with the truncation
		 * pattern of all 0's. So we add the mapping for all 0's to the
		 * same device number.
		 */
		if (!trc_dev && (trunc_bits != 0)) {
			if ((dpt = (DLIST *)malloc(sizeof(DLIST))) == NULL)
				goto bad;
			dpt->trunc_bits = 0;
			dpt->dev = arcn->sb.st_dev;
			dpt->fow = pt->list;
			pt->list = dpt;
		}
	}

	/*
	 * look for a device number not being used. We must watch for wrap
	 * around on lastdev (so we do not get stuck looking forever!)
	 */
	while (++lastdev > 0) {
		if (chk_dev(lastdev, 0) != NULL)
			continue;
		/*
		 * found an unused value. If we have reached truncation point
		 * for this format we are hosed, so we give up. Otherwise we
		 * mark it as being used.
		 */
		if (((lastdev & ((dev_t)dev_mask)) != lastdev) ||
		    (chk_dev(lastdev, 1) == NULL))
			goto bad;
		break;
	}

	if ((lastdev <= 0) || ((dpt = (DLIST *)malloc(sizeof(DLIST))) == NULL))
		goto bad;

	/*
	 * got a new device number, store it under this truncation pattern.
	 * change the device number this file is being stored with.
	 */
	dpt->trunc_bits = trunc_bits;
	dpt->dev = lastdev;
	dpt->fow = pt->list;
	pt->list = dpt;
	arcn->sb.st_dev = lastdev;
	arcn->sb.st_ino = nino;
	return 0;

    bad:
	tty_warn(1,
	    "Unable to fix truncated inode/device field when storing %s",
	    arcn->name);
	tty_warn(0, "Archive may create improper hard links when extracted");
	return 0;
}
Пример #24
0
int
node_creat(ARCHD *arcn)
{
	int res;
	int ign = 0;
	int oerrno;
	int pass = 0;
	mode_t file_mode;
	struct stat sb;
	char target[MAXPATHLEN];
	char *nm = arcn->name;
	int len;

	/*
	 * create node based on type, if that fails try to unlink the node and
	 * try again. finally check the path and try again. As noted in the
	 * file and link creation routines, this method seems to exhibit the
	 * best performance in general use workloads.
	 */
	file_mode = arcn->sb.st_mode & FILEBITS(arcn->type == PAX_DIR);

	for (;;) {
		switch (arcn->type) {
		case PAX_DIR:
			/*
			 * If -h (or -L) was given in tar-mode, follow the
			 * potential symlink chain before trying to create the
			 * directory.
			 */
			if (strcmp(NM_TAR, argv0) == 0 && Lflag) {
				while (lstat(nm, &sb) == 0 &&
				    S_ISLNK(sb.st_mode)) {
					len = readlink(nm, target,
					    sizeof target - 1);
					if (len == -1) {
						syswarn(0, errno,
						   "cannot follow symlink %s "
						   "in chain for %s",
						    nm, arcn->name);
						res = -1;
						goto badlink;
					}
					target[len] = '\0';
					nm = target;
				}
			}
			res = domkdir(nm, file_mode);
badlink:
			if (ign)
				res = 0;
			break;
		case PAX_CHR:
			file_mode |= S_IFCHR;
			res = mknod(nm, file_mode, arcn->sb.st_rdev);
			break;
		case PAX_BLK:
			file_mode |= S_IFBLK;
			res = mknod(nm, file_mode, arcn->sb.st_rdev);
			break;
		case PAX_FIF:
			res = mkfifo(nm, file_mode);
			break;
		case PAX_SCK:
			/*
			 * Skip sockets, operation has no meaning under BSD
			 */
			tty_warn(0,
			    "%s skipped. Sockets cannot be copied or extracted",
			    nm);
			return (-1);
		case PAX_SLK:
			res = symlink(arcn->ln_name, nm);
			break;
		case PAX_CTG:
		case PAX_HLK:
		case PAX_HRG:
		case PAX_REG:
		default:
			/*
			 * we should never get here
			 */
			tty_warn(0, "%s has an unknown file type, skipping",
			    nm);
			return (-1);
		}

		/*
		 * if we were able to create the node break out of the loop,
		 * otherwise try to unlink the node and try again. if that
		 * fails check the full path and try a final time.
		 */
		if (res == 0)
			break;

		/*
		 * we failed to make the node
		 */
		oerrno = errno;
		switch (pass++) {
		case 0:
			if ((ign = unlnk_exist(nm, arcn->type)) < 0)
				return (-1);
			continue;

		case 1:
			if (nodirs ||
			    chk_path(nm, arcn->sb.st_uid,
			    arcn->sb.st_gid) < 0) {
				syswarn(1, oerrno, "Cannot create %s", nm);
				return (-1);
			}
			continue;
		}

		/*
		 * it must be a file that exists but we can't create or
		 * remove, but we must avoid the infinite loop.
		 */
		break;
	}

	/*
	 * we were able to create the node. set uid/gid, modes and times
	 */
	if (pids)
		res = set_ids(nm, arcn->sb.st_uid, arcn->sb.st_gid);
	else
		res = 0;

	/*
	 * IMPORTANT SECURITY NOTE:
	 * if not preserving mode or we cannot set uid/gid, then PROHIBIT any
	 * set uid/gid bits
	 */
	if (!pmode || res)
		arcn->sb.st_mode &= ~SETBITS(arcn->type == PAX_DIR);
	if (pmode)
		set_pmode(arcn->name, arcn->sb.st_mode);

	if (arcn->type == PAX_DIR && strcmp(NM_CPIO, argv0) != 0) {
		/*
		 * Dirs must be processed again at end of extract to set times
		 * and modes to agree with those stored in the archive. However
		 * to allow extract to continue, we may have to also set owner
		 * rights. This allows nodes in the archive that are children
		 * of this directory to be extracted without failure. Both time
		 * and modes will be fixed after the entire archive is read and
		 * before pax exits.
		 */
		if (access(nm, R_OK | W_OK | X_OK) < 0) {
			if (lstat(nm, &sb) < 0) {
				syswarn(0, errno,"Cannot access %s (stat)",
				    arcn->name);
				set_pmode(nm,file_mode | S_IRWXU);
			} else {
				/*
				 * We have to add rights to the dir, so we make
				 * sure to restore the mode. The mode must be
				 * restored AS CREATED and not as stored if
				 * pmode is not set.
				 */
				set_pmode(nm, ((sb.st_mode &
				    FILEBITS(arcn->type == PAX_DIR)) |
				    S_IRWXU));
				if (!pmode)
					arcn->sb.st_mode = sb.st_mode;
			}

			/*
			 * we have to force the mode to what was set here,
			 * since we changed it from the default as created.
			 */
			add_dir(nm, arcn->nlen, &(arcn->sb), 1);
		} else if (pmode || patime || pmtime)
			add_dir(nm, arcn->nlen, &(arcn->sb), 0);
	}

	if (patime || pmtime)
		set_ftime(arcn->name, arcn->sb.st_mtime,
		    arcn->sb.st_atime, 0, (arcn->type == PAX_SLK) ? 1 : 0);

#if HAVE_STRUCT_STAT_ST_FLAGS
	if (pfflags && arcn->type != PAX_SLK)
		set_chflags(arcn->name, arcn->sb.st_flags);
#endif
	return 0;
}
Пример #25
0
int
chk_ftime(ARCHD *arcn)
{
	FTM *pt;
	int namelen;
	u_int indx;
	char ckname[PAXPATHLEN+1];

	/*
	 * no info, go ahead and add to archive
	 */
	if (ftab == NULL)
		return 0;

	/*
	 * hash the pathname and look up in table
	 */
	namelen = arcn->nlen;
	indx = st_hash(arcn->name, namelen, F_TAB_SZ);
	if ((pt = ftab[indx]) != NULL) {
		/*
		 * the hash chain is not empty, walk down looking for match
		 * only read up the path names if the lengths match, speeds
		 * up the search a lot
		 */
		while (pt != NULL) {
			if (pt->namelen == namelen) {
				/*
				 * potential match, have to read the name
				 * from the scratch file.
				 */
				if (lseek(ffd,pt->seek,SEEK_SET) != pt->seek) {
					syswarn(1, errno,
					    "Failed ftime table seek");
					return -1;
				}
				if (xread(ffd, ckname, namelen) != namelen) {
					syswarn(1, errno,
					    "Failed ftime table read");
					return -1;
				}

				/*
				 * if the names match, we are done
				 */
				if (!strncmp(ckname, arcn->name, namelen))
					break;
			}

			/*
			 * try the next entry on the chain
			 */
			pt = pt->fow;
		}

		if (pt != NULL) {
			/*
			 * found the file, compare the times, save the newer
			 */
			if (arcn->sb.st_mtime > pt->mtime) {
				/*
				 * file is newer
				 */
				pt->mtime = arcn->sb.st_mtime;
				return 0;
			}
			/*
			 * file is older
			 */
			return 1;
		}
	}

	/*
	 * not in table, add it
	 */
	if ((pt = (FTM *)malloc(sizeof(FTM))) != NULL) {
		/*
		 * add the name at the end of the scratch file, saving the
		 * offset. add the file to the head of the hash chain
		 */
		if ((pt->seek = lseek(ffd, (off_t)0, SEEK_END)) >= 0) {
			if (xwrite(ffd, arcn->name, namelen) == namelen) {
				pt->mtime = arcn->sb.st_mtime;
				pt->namelen = namelen;
				pt->fow = ftab[indx];
				ftab[indx] = pt;
				return 0;
			}
			syswarn(1, errno, "Failed write to file time table");
		} else
			syswarn(1, errno, "Failed seek on file time table");
	} else
		tty_warn(1, "File time table ran out of memory");

	if (pt != NULL)
		(void)free((char *)pt);
	return -1;
}
Пример #26
0
int
chk_lnk(ARCHD *arcn)
{
	HRDLNK *pt;
	HRDLNK **ppt;
	u_int indx;

	if (ltab == NULL)
		return -1;
	/*
	 * ignore those nodes that cannot have hard links
	 */
	if ((arcn->type == PAX_DIR) || (arcn->sb.st_nlink <= 1))
		return 0;

	/*
	 * hash inode number and look for this file
	 */
	indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ;
	if ((pt = ltab[indx]) != NULL) {
		/*
		 * it's hash chain in not empty, walk down looking for it
		 */
		ppt = &(ltab[indx]);
		while (pt != NULL) {
			if ((pt->ino == arcn->sb.st_ino) &&
			    (pt->dev == arcn->sb.st_dev))
				break;
			ppt = &(pt->fow);
			pt = pt->fow;
		}

		if (pt != NULL) {
			/*
			 * found a link. set the node type and copy in the
			 * name of the file it is to link to. we need to
			 * handle hardlinks to regular files differently than
			 * other links.
			 */
			arcn->ln_nlen = strlcpy(arcn->ln_name, pt->name,
				sizeof(arcn->ln_name));
			if (arcn->type == PAX_REG)
				arcn->type = PAX_HRG;
			else
				arcn->type = PAX_HLK;

			/*
			 * if we have found all the links to this file, remove
			 * it from the database
			 */
			if (--pt->nlink <= 1) {
				*ppt = pt->fow;
				(void)free((char *)pt->name);
				(void)free((char *)pt);
			}
			return 1;
		}
	}

	/*
	 * we never saw this file before. It has links so we add it to the
	 * front of this hash chain
	 */
	if ((pt = (HRDLNK *)malloc(sizeof(HRDLNK))) != NULL) {
		if ((pt->name = strdup(arcn->name)) != NULL) {
			pt->dev = arcn->sb.st_dev;
			pt->ino = arcn->sb.st_ino;
			pt->nlink = arcn->sb.st_nlink;
			pt->fow = ltab[indx];
			ltab[indx] = pt;
			return 0;
		}
		(void)free((char *)pt);
	}

	tty_warn(1, "Hard link table out of memory");
	return -1;
}
Пример #27
0
int
file_write(int fd, char *str, int cnt, int *rem, int *isempt, int sz,
	char *name)
{
	char *pt;
	char *end;
	int wcnt;
	char *st = str;
	char **strp;
	size_t *lenp;

	/*
	 * while we have data to process
	 */
	while (cnt) {
		if (!*rem) {
			/*
			 * We are now at the start of file system block again
			 * (or what we think one is...). start looking for
			 * empty blocks again
			 */
			*isempt = 1;
			*rem = sz;
		}

		/*
		 * only examine up to the end of the current file block or
		 * remaining characters to write, whatever is smaller
		 */
		wcnt = MIN(cnt, *rem);
		cnt -= wcnt;
		*rem -= wcnt;
		if (*isempt) {
			/*
			 * have not written to this block yet, so we keep
			 * looking for zero's
			 */
			pt = st;
			end = st + wcnt;

			/*
			 * look for a zero filled buffer
			 */
			while ((pt < end) && (*pt == '\0'))
				++pt;

			if (pt == end) {
				/*
				 * skip, buf is empty so far
				 */
				if (fd > -1 &&
				    lseek(fd, (off_t)wcnt, SEEK_CUR) < 0) {
					syswarn(1, errno, "File seek on %s",
					    name);
					return -1;
				}
				st = pt;
				continue;
			}
			/*
			 * drat, the buf is not zero filled
			 */
			*isempt = 0;
		}

		/*
		 * have non-zero data in this file system block, have to write
		 */
		switch (fd) {
		case -PAX_GLF:
			strp = &gnu_name_string;
			lenp = &gnu_name_length;
			break;
		case -PAX_GLL:
			strp = &gnu_link_string;
			lenp = &gnu_link_length;
			break;
		default:
			strp = NULL;
			lenp = NULL;
			break;
		}
		if (strp) {
			char *nstr = *strp ? realloc(*strp, *lenp + wcnt + 1) :
				malloc(wcnt + 1);
			if (nstr == NULL) {
				tty_warn(1, "Out of memory");
				return -1;
			}
			(void)strlcpy(&nstr[*lenp], st, wcnt + 1);
			*strp = nstr;
			*lenp += wcnt;
		} else if (xwrite(fd, st, wcnt) != wcnt) {
			syswarn(1, errno, "Failed write to file %s", name);
			return -1;
		}
		st += wcnt;
	}
	return st - str;
}
Пример #28
0
void
proc_dir(void)
{
#ifdef DIRS_USE_FILE
	char name[PAXPATHLEN+1];
	DIRDATA dblk;
	u_long cnt;

	if (dirfd < 0)
		return;
	/*
	 * read backwards through the file and process each directory
	 */
	for (cnt = 0; cnt < dircnt; ++cnt) {
		/*
		 * read the trailer, then the file name, if this fails
		 * just give up.
		 */
		if (lseek(dirfd, -((off_t)sizeof(dblk)), SEEK_CUR) < 0)
			break;
		if (xread(dirfd,(char *)&dblk, sizeof(dblk)) != sizeof(dblk))
			break;
		if (lseek(dirfd, dblk.npos, SEEK_SET) < 0)
			break;
		if (xread(dirfd, name, dblk.nlen) != dblk.nlen)
			break;
		if (lseek(dirfd, dblk.npos, SEEK_SET) < 0)
			break;

		/*
		 * frc_mode set, make sure we set the file modes even if
		 * the user didn't ask for it (see file_subs.c for more info)
		 */
		if (pmode || dblk.frc_mode)
			set_pmode(name, dblk.mode);
		if (patime || pmtime)
			set_ftime(name, dblk.mtime, dblk.atime, 0, 0);
		if (pfflags)
			set_chflags(name, dblk.fflags);
	}

	(void)close(dirfd);
	dirfd = -1;
	if (cnt != dircnt)
		tty_warn(1,
		    "Unable to set mode and times for created directories");
	return;
#else
	DIRDATA *dblk;

	for (dblk = dirdata_head; dblk != NULL; dblk = dirdata_head) {
		dirdata_head = dblk->next;

		/*
		 * frc_mode set, make sure we set the file modes even if
		 * the user didn't ask for it (see file_subs.c for more info)
		 */
		if (pmode || dblk->frc_mode)
			set_pmode(dblk->name, dblk->mode);
		if (patime || pmtime)
			set_ftime(dblk->name, dblk->mtime, dblk->atime, 0, 0);
		if (pfflags)
			set_chflags(dblk->name, dblk->fflags);

		free(dblk->name);
		free(dblk);
	}
#endif /* DIRS_USE_FILE */
}
Пример #29
0
void
add_dir(char *name, int nlen, struct stat *psb, int frc_mode)
{
#ifdef DIRS_USE_FILE
	DIRDATA dblk;

	if (dirfd < 0)
		return;

	/*
	 * get current position (where file name will start) so we can store it
	 * in the trailer
	 */
	if ((dblk.npos = lseek(dirfd, 0L, SEEK_CUR)) < 0) {
		tty_warn(1,
		    "Unable to store mode and times for directory: %s",name);
		return;
	}

	/*
	 * write the file name followed by the trailer
	 */
	dblk.nlen = nlen + 1;
	dblk.mode = psb->st_mode & 0xffff;
	dblk.mtime = psb->st_mtime;
	dblk.atime = psb->st_atime;
#if HAVE_FILE_FLAGS
	dblk.fflags = psb->st_flags;
#else
	dblk.fflags = 0;
#endif
	dblk.frc_mode = frc_mode;
	if ((xwrite(dirfd, name, dblk.nlen) == dblk.nlen) &&
	    (xwrite(dirfd, (char *)&dblk, sizeof(dblk)) == sizeof(dblk))) {
		++dircnt;
		return;
	}

	tty_warn(1,
	    "Unable to store mode and times for created directory: %s",name);
	return;
#else
	DIRDATA *dblk;

	if ((dblk = malloc(sizeof(*dblk))) == NULL ||
	    (dblk->name = strdup(name)) == NULL) {
		tty_warn(1,
		    "Unable to store mode and times for directory: %s",name);
		if (dblk != NULL)
			free(dblk);
		return;
	}

	dblk->mode = psb->st_mode & 0xffff;
	dblk->mtime = psb->st_mtime;
	dblk->atime = psb->st_atime;
#if HAVE_FILE_FLAGS
	dblk->fflags = psb->st_flags;
#else
	dblk->fflags = 0;
#endif
	dblk->frc_mode = frc_mode;

	dblk->next = dirdata_head;
	dirdata_head = dblk;
	return;
#endif /* DIRS_USE_FILE */
}
Пример #30
0
int
ftree_start()
{

#ifndef SMALL
	/*
	 * if -M is given, the list of filenames on stdin is actually
	 * an mtree(8) specfile, so parse the specfile into a NODE *
	 * tree at ftnode, for use by next_file()
	 */
	if (Mflag) {
		if (fthead != NULL) {
			tty_warn(1,
	    "The -M flag is only supported when reading file list from stdin");
			return -1;
		}
		ftnode = spec(stdin);
		if (ftnode != NULL &&
		    (ftnode->type != F_DIR || strcmp(ftnode->name, ".") != 0)) {
			tty_warn(1,
			    "First node of specfile is not `.' directory");
			return -1;
		}
		return 0;
	}
#endif	/* SMALL */

	/*
	 * set up the operation mode of fts, open the first file arg. We must
	 * use FTS_NOCHDIR, as the user may have to open multiple archives and
	 * if fts did a chdir off into the boondocks, we may create an archive
	 * volume in an place where the user did not expect to.
	 */
	ftsopts = FTS_NOCHDIR;

	/*
	 * optional user flags that effect file traversal
	 * -H command line symlink follow only (half follow)
	 * -L follow sylinks (logical)
	 * -P do not follow sylinks (physical). This is the default.
	 * -X do not cross over mount points
	 * -t preserve access times on files read.
	 * -n select only the first member of a file tree when a match is found
	 * -d do not extract subtrees rooted at a directory arg.
	 */
	if (Lflag)
		ftsopts |= FTS_LOGICAL;
	else
		ftsopts |= FTS_PHYSICAL;
	if (Hflag)
		ftsopts |= FTS_COMFOLLOW;
	if (Xflag)
		ftsopts |= FTS_XDEV;

	if ((fthead == NULL) && ((farray[0] = malloc(PAXPATHLEN+2)) == NULL)) {
		tty_warn(1, "Unable to allocate memory for file name buffer");
		return -1;
	}

	if (ftree_arg() < 0)
		return -1;
	if (tflag && (atdir_start() < 0))
		return -1;
	return 0;
}