ftree.c revision 11746
198944Sobrien/*-
2130803Smarcel * Copyright (c) 1992 Keith Muller.
398944Sobrien * Copyright (c) 1992, 1993
498944Sobrien *	The Regents of the University of California.  All rights reserved.
598944Sobrien *
698944Sobrien * This code is derived from software contributed to Berkeley by
798944Sobrien * Keith Muller of the University of California, San Diego.
898944Sobrien *
998944Sobrien * Redistribution and use in source and binary forms, with or without
1098944Sobrien * modification, are permitted provided that the following conditions
1198944Sobrien * are met:
1298944Sobrien * 1. Redistributions of source code must retain the above copyright
1398944Sobrien *    notice, this list of conditions and the following disclaimer.
1498944Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1598944Sobrien *    notice, this list of conditions and the following disclaimer in the
1698944Sobrien *    documentation and/or other materials provided with the distribution.
1798944Sobrien * 3. All advertising materials mentioning features or use of this software
1898944Sobrien *    must display the following acknowledgement:
1998944Sobrien *	This product includes software developed by the University of
2098944Sobrien *	California, Berkeley and its contributors.
2198944Sobrien * 4. Neither the name of the University nor the names of its contributors
2298944Sobrien *    may be used to endorse or promote products derived from this software
23130803Smarcel *    without specific prior written permission.
2498944Sobrien *
25130803Smarcel * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26130803Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27130803Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2898944Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2998944Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3098944Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3198944Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3298944Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3398944Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3498944Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35130803Smarcel * SUCH DAMAGE.
3698944Sobrien *
3798944Sobrien *	$Id: ftree.c,v 1.3 1995/05/30 00:06:57 rgrimes Exp $
3898944Sobrien */
3998944Sobrien
4098944Sobrien#ifndef lint
4198944Sobrienstatic char sccsid[] = "@(#)ftree.c	8.2 (Berkeley) 4/18/94";
4298944Sobrien#endif /* not lint */
4398944Sobrien
4498944Sobrien#include <sys/types.h>
4598944Sobrien#include <sys/time.h>
4698944Sobrien#include <sys/stat.h>
4798944Sobrien#include <sys/param.h>
4898944Sobrien#include <unistd.h>
4998944Sobrien#include <string.h>
5098944Sobrien#include <stdio.h>
5198944Sobrien#include <errno.h>
5298944Sobrien#include <stdlib.h>
5398944Sobrien#include <fts.h>
5498944Sobrien#include "pax.h"
5598944Sobrien#include "ftree.h"
5698944Sobrien#include "extern.h"
57130803Smarcel
58130803Smarcel/*
59130803Smarcel * routines to interface with the fts library function.
60130803Smarcel *
61130803Smarcel * file args supplied to pax are stored on a single linked list (of type FTREE)
62130803Smarcel * and given to fts to be processed one at a time. pax "selects" files from
63130803Smarcel * the expansion of each arg into the corresponding file tree (if the arg is a
64130803Smarcel * directory, otherwise the node itself is just passed to pax). The selection
65130803Smarcel * is modified by the -n and -u flags. The user is informed when a specific
66130803Smarcel * file arg does not generate any selected files. -n keeps expanding the file
67130803Smarcel * tree arg until one of its files is selected, then skips to the next file
68130803Smarcel * arg. when the user does not supply the file trees as command line args to
69130803Smarcel * pax, they are read from stdin
70130803Smarcel */
71130803Smarcel
72130803Smarcelstatic FTS *ftsp = NULL;		/* curent FTS handle */
73130803Smarcelstatic int ftsopts;			/* options to be used on fts_open */
74130803Smarcelstatic char *farray[2];			/* array for passing each arg to fts */
75130803Smarcelstatic FTREE *fthead = NULL;		/* head of linked list of file args */
76130803Smarcelstatic FTREE *fttail = NULL;		/* tail of linked list of file args */
77130803Smarcelstatic FTREE *ftcur = NULL;		/* current file arg being processed */
78130803Smarcelstatic FTSENT *ftent = NULL;		/* current file tree entry */
79130803Smarcelstatic int ftree_skip;			/* when set skip to next file arg */
80130803Smarcel
81130803Smarcelstatic int ftree_arg __P((void));
82130803Smarcel
83130803Smarcel/*
84130803Smarcel * ftree_start()
85130803Smarcel *	initialize the options passed to fts_open() during this run of pax
86130803Smarcel *	options are based on the selection of pax options by the user
87130803Smarcel *	fts_start() also calls fts_arg() to open the first valid file arg. We
88130803Smarcel *	also attempt to reset directory access times when -t (tflag) is set.
89130803Smarcel * Return:
90130803Smarcel *	0 if there is at least one valid file arg to process, -1 otherwise
91130803Smarcel */
92130803Smarcel
93130803Smarcel#if __STDC__
94130803Smarcelint
95130803Smarcelftree_start(void)
96130803Smarcel#else
97130803Smarcelint
98130803Smarcelftree_start()
99130803Smarcel#endif
100130803Smarcel{
101130803Smarcel	/*
102130803Smarcel	 * set up the operation mode of fts, open the first file arg. We must
103130803Smarcel	 * use FTS_NOCHDIR, as the user may have to open multiple archives and
104130803Smarcel	 * if fts did a chdir off into the boondocks, we may create an archive
105130803Smarcel	 * volume in an place where the user did not expect to.
106130803Smarcel	 */
107130803Smarcel	ftsopts = FTS_NOCHDIR;
108130803Smarcel
109130803Smarcel	/*
110130803Smarcel	 * optional user flags that effect file traversal
111130803Smarcel	 * -H command line symlink follow only (half follow)
112130803Smarcel	 * -L follow sylinks (logical)
113130803Smarcel	 * -P do not follow sylinks (physical). This is the default.
114130803Smarcel	 * -X do not cross over mount points
115130803Smarcel	 * -t preserve access times on files read.
116130803Smarcel	 * -n select only the first member of a file tree when a match is found
117130803Smarcel	 * -d do not extract subtrees rooted at a directory arg.
118130803Smarcel	 */
119130803Smarcel	if (Lflag)
120130803Smarcel		ftsopts |= FTS_LOGICAL;
121130803Smarcel	else
122130803Smarcel		ftsopts |= FTS_PHYSICAL;
123130803Smarcel	if (Hflag)
124130803Smarcel#	ifdef NET2_FTS
125		warn(0, "The -H flag is not supported on this version");
126#	else
127		ftsopts |= FTS_COMFOLLOW;
128#	endif
129	if (Xflag)
130		ftsopts |= FTS_XDEV;
131
132	if ((fthead == NULL) && ((farray[0] = malloc(PAXPATHLEN+2)) == NULL)) {
133		warn(1, "Unable to allocate memory for file name buffer");
134		return(-1);
135	}
136
137	if (ftree_arg() < 0)
138		return(-1);
139	if (tflag && (atdir_start() < 0))
140		return(-1);
141	return(0);
142}
143
144/*
145 * ftree_add()
146 *	add the arg to the linked list of files to process. Each will be
147 *	processed by fts one at a time
148 * Return:
149 *	0 if added to the linked list, -1 if failed
150 */
151
152#if __STDC__
153int
154ftree_add(register char *str)
155#else
156int
157ftree_add(str)
158	register char *str;
159#endif
160{
161	register FTREE *ft;
162	register int len;
163
164	/*
165	 * simple check for bad args
166	 */
167	if ((str == NULL) || (*str == '\0')) {
168		warn(0, "Invalid file name arguement");
169		return(-1);
170	}
171
172	/*
173	 * allocate FTREE node and add to the end of the linked list (args are
174	 * processed in the same order they were passed to pax). Get rid of any
175	 * trailing / the user may pass us. (watch out for / by itself).
176	 */
177	if ((ft = (FTREE *)malloc(sizeof(FTREE))) == NULL) {
178		warn(0, "Unable to allocate memory for filename");
179		return(-1);
180	}
181
182	if (((len = strlen(str) - 1) > 0) && (str[len] == '/'))
183		str[len] = '\0';
184	ft->fname = str;
185	ft->refcnt = 0;
186	ft->fow = NULL;
187	if (fthead == NULL) {
188		fttail = fthead = ft;
189		return(0);
190	}
191	fttail->fow = ft;
192	fttail = ft;
193	return(0);
194}
195
196/*
197 * ftree_sel()
198 *	this entry has been selected by pax. bump up reference count and handle
199 *	-n and -d processing.
200 */
201
202#if __STDC__
203void
204ftree_sel(register ARCHD *arcn)
205#else
206void
207ftree_sel(arcn)
208	register ARCHD *arcn;
209#endif
210{
211	/*
212	 * set reference bit for this pattern. This linked list is only used
213	 * when file trees are supplied pax as args. The list is not used when
214	 * the trees are read from stdin.
215	 */
216	if (ftcur != NULL)
217		ftcur->refcnt = 1;
218
219	/*
220	 * if -n we are done with this arg, force a skip to the next arg when
221	 * pax asks for the next file in next_file().
222	 * if -d we tell fts only to match the directory (if the arg is a dir)
223	 * and not the entire file tree rooted at that point.
224	 */
225	if (nflag)
226		ftree_skip = 1;
227
228	if (!dflag || (arcn->type != PAX_DIR))
229		return;
230
231	if (ftent != NULL)
232		(void)fts_set(ftsp, ftent, FTS_SKIP);
233}
234
235/*
236 * ftree_chk()
237 *	called at end on pax execution. Prints all those file args that did not
238 *	have a selected member (reference count still 0)
239 */
240
241#if __STDC__
242void
243ftree_chk(void)
244#else
245void
246ftree_chk()
247#endif
248{
249	register FTREE *ft;
250	register int wban = 0;
251
252	/*
253	 * make sure all dir access times were reset.
254	 */
255	if (tflag)
256		atdir_end();
257
258	/*
259	 * walk down list and check reference count. Print out those members
260	 * that never had a match
261	 */
262	for (ft = fthead; ft != NULL; ft = ft->fow) {
263		if (ft->refcnt > 0)
264			continue;
265		if (wban == 0) {
266			warn(1,"WARNING! These file names were not selected:");
267			++wban;
268		}
269		(void)fprintf(stderr, "%s\n", ft->fname);
270	}
271}
272
273/*
274 * ftree_arg()
275 *	Get the next file arg for fts to process. Can be from either the linked
276 *	list or read from stdin when the user did not them as args to pax. Each
277 *	arg is processed until the first successful fts_open().
278 * Return:
279 *	0 when the next arg is ready to go, -1 if out of file args (or EOF on
280 *	stdin).
281 */
282
283#if __STDC__
284static int
285ftree_arg(void)
286#else
287static int
288ftree_arg()
289#endif
290{
291	register char *pt;
292
293	/*
294	 * close off the current file tree
295	 */
296	if (ftsp != NULL) {
297		(void)fts_close(ftsp);
298		ftsp = NULL;
299	}
300
301	/*
302	 * keep looping until we get a valid file tree to process. Stop when we
303	 * reach the end of the list (or get an eof on stdin)
304	 */
305	for(;;) {
306		if (fthead == NULL) {
307			/*
308			 * the user didn't supply any args, get the file trees
309			 * to process from stdin;
310			 */
311			if (fgets(farray[0], PAXPATHLEN+1, stdin) == NULL)
312				return(-1);
313			if ((pt = strchr(farray[0], '\n')) != NULL)
314				*pt = '\0';
315		} else {
316			/*
317			 * the user supplied the file args as arguements to pax
318			 */
319			if (ftcur == NULL)
320				ftcur = fthead;
321			else if ((ftcur = ftcur->fow) == NULL)
322				return(-1);
323			farray[0] = ftcur->fname;
324		}
325
326		/*
327		 * watch it, fts wants the file arg stored in a array of char
328		 * ptrs, with the last one a null. we use a two element array
329		 * and set farray[0] to point at the buffer with the file name
330		 * in it. We cannnot pass all the file args to fts at one shot
331		 * as we need to keep a handle on which file arg generates what
332		 * files (the -n and -d flags need this). If the open is
333		 * successful, return a 0.
334		 */
335		if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL)
336			break;
337	}
338	return(0);
339}
340
341/*
342 * next_file()
343 *	supplies the next file to process in the supplied archd structure.
344 * Return:
345 *	0 when contents of arcn have been set with the next file, -1 when done.
346 */
347
348#if __STDC__
349int
350next_file(register ARCHD *arcn)
351#else
352int
353next_file(arcn)
354	register ARCHD *arcn;
355#endif
356{
357	register int cnt;
358	time_t atime;
359	time_t mtime;
360
361	/*
362	 * ftree_sel() might have set the ftree_skip flag if the user has the
363	 * -n option and a file was selected from this file arg tree. (-n says
364	 * only one member is matched for each pattern) ftree_skip being 1
365	 * forces us to go to the next arg now.
366	 */
367	if (ftree_skip) {
368		/*
369		 * clear and go to next arg
370		 */
371		ftree_skip = 0;
372		if (ftree_arg() < 0)
373			return(-1);
374	}
375
376	/*
377	 * loop until we get a valid file to process
378	 */
379	for(;;) {
380		if ((ftent = fts_read(ftsp)) == NULL) {
381			/*
382			 * out of files in this tree, go to next arg, if none
383			 * we are done
384			 */
385			if (ftree_arg() < 0)
386				return(-1);
387			continue;
388		}
389
390		/*
391		 * handle each type of fts_read() flag
392		 */
393		switch(ftent->fts_info) {
394		case FTS_D:
395		case FTS_DEFAULT:
396		case FTS_F:
397		case FTS_SL:
398		case FTS_SLNONE:
399			/*
400			 * these are all ok
401			 */
402			break;
403		case FTS_DP:
404			/*
405			 * already saw this directory. If the user wants file
406			 * access times reset, we use this to restore the
407			 * access time for this directory since this is the
408			 * last time we will see it in this file subtree
409			 * remember to force the time (this is -t on a read
410			 * directory, not a created directory).
411			 */
412#			ifdef NET2_FTS
413			if (!tflag || (get_atdir(ftent->fts_statb.st_dev,
414			    ftent->fts_statb.st_ino, &mtime, &atime) < 0))
415#			else
416			if (!tflag || (get_atdir(ftent->fts_statp->st_dev,
417			    ftent->fts_statp->st_ino, &mtime, &atime) < 0))
418#			endif
419				continue;
420			set_ftime(ftent->fts_path, mtime, atime, 1);
421			continue;
422		case FTS_DC:
423			/*
424			 * fts claims a file system cycle
425			 */
426			warn(1,"File system cycle found at %s",ftent->fts_path);
427			continue;
428		case FTS_DNR:
429#			ifdef NET2_FTS
430			syswarn(1, errno,
431#			else
432			syswarn(1, ftent->fts_errno,
433#			endif
434			    "Unable to read directory %s", ftent->fts_path);
435			continue;
436		case FTS_ERR:
437#			ifdef NET2_FTS
438			syswarn(1, errno,
439#			else
440			syswarn(1, ftent->fts_errno,
441#			endif
442			    "File system traversal error");
443			continue;
444		case FTS_NS:
445		case FTS_NSOK:
446#			ifdef NET2_FTS
447			syswarn(1, errno,
448#			else
449			syswarn(1, ftent->fts_errno,
450#			endif
451			    "Unable to access %s", ftent->fts_path);
452			continue;
453		}
454
455		/*
456		 * ok got a file tree node to process. copy info into arcn
457		 * structure (initialize as required)
458		 */
459		arcn->skip = 0;
460		arcn->pad = 0;
461		arcn->ln_nlen = 0;
462		arcn->ln_name[0] = '\0';
463#		ifdef NET2_FTS
464		arcn->sb = ftent->fts_statb;
465#		else
466		arcn->sb = *(ftent->fts_statp);
467#		endif
468
469		/*
470		 * file type based set up and copy into the arcn struct
471		 * SIDE NOTE:
472		 * we try to reset the access time on all files and directories
473		 * we may read when the -t flag is specified. files are reset
474		 * when we close them after copying. we reset the directories
475		 * when we are done with their file tree (we also clean up at
476		 * end in case we cut short a file tree traversal). However
477		 * there is no way to reset access times on symlinks.
478		 */
479		switch(S_IFMT & arcn->sb.st_mode) {
480		case S_IFDIR:
481			arcn->type = PAX_DIR;
482			if (!tflag)
483				break;
484			add_atdir(ftent->fts_path, arcn->sb.st_dev,
485			    arcn->sb.st_ino, arcn->sb.st_mtime,
486			    arcn->sb.st_atime);
487			break;
488		case S_IFCHR:
489			arcn->type = PAX_CHR;
490			break;
491		case S_IFBLK:
492			arcn->type = PAX_BLK;
493			break;
494		case S_IFREG:
495			/*
496			 * only regular files with have data to store on the
497			 * archive. all others will store a zero length skip.
498			 * the skip field is used by pax for actual data it has
499			 * to read (or skip over).
500			 */
501			arcn->type = PAX_REG;
502			arcn->skip = arcn->sb.st_size;
503			break;
504		case S_IFLNK:
505			arcn->type = PAX_SLK;
506			/*
507			 * have to read the symlink path from the file
508			 */
509			if ((cnt = readlink(ftent->fts_path, arcn->ln_name,
510			    PAXPATHLEN)) < 0) {
511				syswarn(1, errno, "Unable to read symlink %s",
512				    ftent->fts_path);
513				continue;
514			}
515			/*
516			 * set link name length, watch out readlink does not
517			 * allways null terminate the link path
518			 */
519			arcn->ln_name[cnt] = '\0';
520			arcn->ln_nlen = cnt;
521			break;
522		case S_IFSOCK:
523			/*
524			 * under BSD storing a socket is senseless but we will
525			 * let the format specific write function make the
526			 * decision of what to do with it.
527			 */
528			arcn->type = PAX_SCK;
529			break;
530		case S_IFIFO:
531			arcn->type = PAX_FIF;
532			break;
533		}
534		break;
535	}
536
537	/*
538	 * copy file name, set file name length
539	 */
540	arcn->nlen = l_strncpy(arcn->name, ftent->fts_path, PAXPATHLEN+1);
541	arcn->name[arcn->nlen] = '\0';
542	arcn->org_name = ftent->fts_path;
543	return(0);
544}
545