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