xref: /openbsd-src/bin/pax/tables.c (revision c01bd7436f6090b56269cc0f7f3902a85d8a1424)
1*c01bd743Sespie /*	$OpenBSD: tables.c,v 1.55 2023/11/26 16:04:17 espie Exp $	*/
2df930be7Sderaadt /*	$NetBSD: tables.c,v 1.4 1995/03/21 09:07:45 cgd Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*-
5df930be7Sderaadt  * Copyright (c) 1992 Keith Muller.
6df930be7Sderaadt  * Copyright (c) 1992, 1993
7df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
8df930be7Sderaadt  *
9df930be7Sderaadt  * This code is derived from software contributed to Berkeley by
10df930be7Sderaadt  * Keith Muller of the University of California, San Diego.
11df930be7Sderaadt  *
12df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
13df930be7Sderaadt  * modification, are permitted provided that the following conditions
14df930be7Sderaadt  * are met:
15df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
16df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
17df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
18df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
19df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
2029295d1cSmillert  * 3. Neither the name of the University nor the names of its contributors
21df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
22df930be7Sderaadt  *    without specific prior written permission.
23df930be7Sderaadt  *
24df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34df930be7Sderaadt  * SUCH DAMAGE.
35df930be7Sderaadt  */
36df930be7Sderaadt 
37df930be7Sderaadt #include <sys/types.h>
38df930be7Sderaadt #include <sys/stat.h>
3919777ba5Sguenther #include <errno.h>
402dbd6dc5Sguenther #include <fcntl.h>
4108961bb1Sguenther #include <limits.h>
42205f2385Sguenther #include <signal.h>
43df930be7Sderaadt #include <stdio.h>
4419777ba5Sguenther #include <stdlib.h>
45df930be7Sderaadt #include <string.h>
46df930be7Sderaadt #include <unistd.h>
4719777ba5Sguenther 
48df930be7Sderaadt #include "pax.h"
49df930be7Sderaadt #include "extern.h"
50*c01bd743Sespie static u_int st_hash(const char *, int, int);
51df930be7Sderaadt 
52df930be7Sderaadt /*
53df930be7Sderaadt  * Routines for controlling the contents of all the different databases pax
54df930be7Sderaadt  * keeps. Tables are dynamically created only when they are needed. The
55df930be7Sderaadt  * goal was speed and the ability to work with HUGE archives. The databases
56df930be7Sderaadt  * were kept simple, but do have complex rules for when the contents change.
57df930be7Sderaadt  * As of this writing, the posix library functions were more complex than
58df930be7Sderaadt  * needed for this application (pax databases have very short lifetimes and
59df930be7Sderaadt  * do not survive after pax is finished). Pax is required to handle very
60df930be7Sderaadt  * large archives. These database routines carefully combine memory usage and
61df930be7Sderaadt  * temporary file storage in ways which will not significantly impact runtime
62df930be7Sderaadt  * performance while allowing the largest possible archives to be handled.
63f0f82a05Sjmc  * Trying to force the fit to the posix database routines was not considered
64df930be7Sderaadt  * time well spent.
65df930be7Sderaadt  */
66df930be7Sderaadt 
6719777ba5Sguenther /*
6819777ba5Sguenther  * data structures and constants used by the different databases kept by pax
6919777ba5Sguenther  */
7019777ba5Sguenther 
7119777ba5Sguenther /*
7219777ba5Sguenther  * Hash Table Sizes MUST BE PRIME, if set too small performance suffers.
7319777ba5Sguenther  * Probably safe to expect 500000 inodes per tape. Assuming good key
7419777ba5Sguenther  * distribution (inodes) chains of under 50 long (worst case) is ok.
7519777ba5Sguenther  */
7619777ba5Sguenther #define L_TAB_SZ	2503		/* hard link hash table size */
7719777ba5Sguenther #define F_TAB_SZ	50503		/* file time hash table size */
7819777ba5Sguenther #define N_TAB_SZ	541		/* interactive rename hash table */
7919777ba5Sguenther #define D_TAB_SZ	317		/* unique device mapping table */
8019777ba5Sguenther #define A_TAB_SZ	317		/* ftree dir access time reset table */
8119777ba5Sguenther #define SL_TAB_SZ	317		/* escape symlink tables */
8219777ba5Sguenther #define MAXKEYLEN	64		/* max number of chars for hash */
8319777ba5Sguenther #define DIRP_SIZE	64		/* initial size of created dir table */
8419777ba5Sguenther 
8519777ba5Sguenther /*
8619777ba5Sguenther  * file hard link structure (hashed by dev/ino and chained) used to find the
8719777ba5Sguenther  * hard links in a file system or with some archive formats (cpio)
8819777ba5Sguenther  */
8919777ba5Sguenther typedef struct hrdlnk {
9019777ba5Sguenther 	ino_t		ino;	/* files inode number */
9119777ba5Sguenther 	char		*name;	/* name of first file seen with this ino/dev */
9219777ba5Sguenther 	dev_t		dev;	/* files device number */
9319777ba5Sguenther 	u_long		nlink;	/* expected link count */
9419777ba5Sguenther 	struct hrdlnk	*fow;
9519777ba5Sguenther } HRDLNK;
9619777ba5Sguenther 
9719777ba5Sguenther /*
9819777ba5Sguenther  * Archive write update file time table (the -u, -C flag), hashed by filename.
9919777ba5Sguenther  * Filenames are stored in a scratch file at seek offset into the file. The
10019777ba5Sguenther  * file time (mod time) and the file name length (for a quick check) are
10119777ba5Sguenther  * stored in a hash table node. We were forced to use a scratch file because
10219777ba5Sguenther  * with -u, the mtime for every node in the archive must always be available
10319777ba5Sguenther  * to compare against (and this data can get REALLY large with big archives).
10419777ba5Sguenther  * By being careful to read only when we have a good chance of a match, the
10519777ba5Sguenther  * performance loss is not measurable (and the size of the archive we can
10619777ba5Sguenther  * handle is greatly increased).
10719777ba5Sguenther  */
10819777ba5Sguenther typedef struct ftm {
10919777ba5Sguenther 	off_t		seek;		/* location in scratch file */
11019777ba5Sguenther 	struct timespec	mtim;		/* files last modification time */
11119777ba5Sguenther 	struct ftm	*fow;
11219777ba5Sguenther 	int		namelen;	/* file name length */
11319777ba5Sguenther } FTM;
11419777ba5Sguenther 
11519777ba5Sguenther /*
11619777ba5Sguenther  * Interactive rename table (-i flag), hashed by orig filename.
11719777ba5Sguenther  * We assume this will not be a large table as this mapping data can only be
11819777ba5Sguenther  * obtained through interactive input by the user. Nobody is going to type in
11919777ba5Sguenther  * changes for 500000 files? We use chaining to resolve collisions.
12019777ba5Sguenther  */
12119777ba5Sguenther 
12219777ba5Sguenther typedef struct namt {
12319777ba5Sguenther 	char		*oname;		/* old name */
12419777ba5Sguenther 	char		*nname;		/* new name typed in by the user */
12519777ba5Sguenther 	struct namt	*fow;
12619777ba5Sguenther } NAMT;
12719777ba5Sguenther 
12819777ba5Sguenther /*
12919777ba5Sguenther  * Unique device mapping tables. Some protocols (e.g. cpio) require that the
13019777ba5Sguenther  * <c_dev,c_ino> pair will uniquely identify a file in an archive unless they
13119777ba5Sguenther  * are links to the same file. Appending to archives can break this. For those
13219777ba5Sguenther  * protocols that have this requirement we map c_dev to a unique value not seen
13319777ba5Sguenther  * in the archive when we append. We also try to handle inode truncation with
13419777ba5Sguenther  * this table. (When the inode field in the archive header are too small, we
13519777ba5Sguenther  * remap the dev on writes to remove accidental collisions).
13619777ba5Sguenther  *
13719777ba5Sguenther  * The list is hashed by device number using chain collision resolution. Off of
13819777ba5Sguenther  * each DEVT are linked the various remaps for this device based on those bits
13919777ba5Sguenther  * in the inode which were truncated. For example if we are just remapping to
14019777ba5Sguenther  * avoid a device number during an update append, off the DEVT we would have
14119777ba5Sguenther  * only a single DLIST that has a truncation id of 0 (no inode bits were
14219777ba5Sguenther  * stripped for this device so far). When we spot inode truncation we create
14319777ba5Sguenther  * a new mapping based on the set of bits in the inode which were stripped off.
14419777ba5Sguenther  * so if the top four bits of the inode are stripped and they have a pattern of
14519777ba5Sguenther  * 0110...... (where . are those bits not truncated) we would have a mapping
14619777ba5Sguenther  * assigned for all inodes that has the same 0110.... pattern (with this dev
14719777ba5Sguenther  * number of course). This keeps the mapping sparse and should be able to store
14819777ba5Sguenther  * close to the limit of files which can be represented by the optimal
14919777ba5Sguenther  * combination of dev and inode bits, and without creating a fouled up archive.
15019777ba5Sguenther  * Note we also remap truncated devs in the same way (an exercise for the
15119777ba5Sguenther  * dedicated reader; always wanted to say that...:)
15219777ba5Sguenther  */
15319777ba5Sguenther 
15419777ba5Sguenther typedef struct devt {
15519777ba5Sguenther 	dev_t		dev;	/* the orig device number we now have to map */
15619777ba5Sguenther 	struct devt	*fow;	/* new device map list */
15719777ba5Sguenther 	struct dlist	*list;	/* map list based on inode truncation bits */
15819777ba5Sguenther } DEVT;
15919777ba5Sguenther 
16019777ba5Sguenther typedef struct dlist {
16119777ba5Sguenther 	ino_t trunc_bits;	/* truncation pattern for a specific map */
16219777ba5Sguenther 	dev_t dev;		/* the new device id we use */
16319777ba5Sguenther 	struct dlist *fow;
16419777ba5Sguenther } DLIST;
16519777ba5Sguenther 
16619777ba5Sguenther /*
16719777ba5Sguenther  * ftree directory access time reset table. When we are done with a
16819777ba5Sguenther  * subtree we reset the access and mod time of the directory when the tflag is
16919777ba5Sguenther  * set. Not really explicitly specified in the pax spec, but easy and fast to
17019777ba5Sguenther  * do (and this may have even been intended in the spec, it is not clear).
17119777ba5Sguenther  * table is hashed by inode with chaining.
17219777ba5Sguenther  */
17319777ba5Sguenther 
17419777ba5Sguenther typedef struct atdir {
17519777ba5Sguenther 	struct file_times ft;
17619777ba5Sguenther 	struct atdir *fow;
17719777ba5Sguenther } ATDIR;
17819777ba5Sguenther 
17919777ba5Sguenther /*
18019777ba5Sguenther  * created directory time and mode storage entry. After pax is finished during
18119777ba5Sguenther  * extraction or copy, we must reset directory access modes and times that
18219777ba5Sguenther  * may have been modified after creation (they no longer have the specified
18319777ba5Sguenther  * times and/or modes). We must reset time in the reverse order of creation,
18419777ba5Sguenther  * because entries are added  from the top of the file tree to the bottom.
18519777ba5Sguenther  * We MUST reset times from leaf to root (it will not work the other
18619777ba5Sguenther  * direction).
18719777ba5Sguenther  */
18819777ba5Sguenther 
18919777ba5Sguenther typedef struct dirdata {
19019777ba5Sguenther 	struct file_times ft;
19119777ba5Sguenther 	u_int16_t mode;		/* file mode to restore */
19219777ba5Sguenther 	u_int16_t frc_mode;	/* do we force mode settings? */
19319777ba5Sguenther } DIRDATA;
19419777ba5Sguenther 
195df930be7Sderaadt static HRDLNK **ltab = NULL;	/* hard link table for detecting hard links */
196df930be7Sderaadt static FTM **ftab = NULL;	/* file time table for updating arch */
197df930be7Sderaadt static NAMT **ntab = NULL;	/* interactive rename storage table */
1989a195818Skrw #ifndef NOCPIO
199df930be7Sderaadt static DEVT **dtab = NULL;	/* device/inode mapping tables */
2009a195818Skrw #endif
201df930be7Sderaadt static ATDIR **atab = NULL;	/* file tree directory time reset table */
2029116436cSotto static DIRDATA *dirp = NULL;	/* storage for setting created dir time/mode */
2039116436cSotto static size_t dirsize;		/* size of dirp table */
20424c549d1Sguenther static size_t dircnt = 0;	/* entries in dir time/mode storage */
205df930be7Sderaadt static int ffd = -1;		/* tmp file for file time table name storage */
206df930be7Sderaadt 
207df930be7Sderaadt /*
208df930be7Sderaadt  * hard link table routines
209df930be7Sderaadt  *
210df930be7Sderaadt  * The hard link table tries to detect hard links to files using the device and
211df930be7Sderaadt  * inode values. We do this when writing an archive, so we can tell the format
212df930be7Sderaadt  * write routine that this file is a hard link to another file. The format
213df930be7Sderaadt  * write routine then can store this file in whatever way it wants (as a hard
214df930be7Sderaadt  * link if the format supports that like tar, or ignore this info like cpio).
215df930be7Sderaadt  * (Actually a field in the format driver table tells us if the format wants
216df930be7Sderaadt  * hard link info. if not, we do not waste time looking for them). We also use
217df930be7Sderaadt  * the same table when reading an archive. In that situation, this table is
218df930be7Sderaadt  * used by the format read routine to detect hard links from stored dev and
219df930be7Sderaadt  * inode numbers (like cpio). This will allow pax to create a link when one
220df930be7Sderaadt  * can be detected by the archive format.
221df930be7Sderaadt  */
222df930be7Sderaadt 
223df930be7Sderaadt /*
224df930be7Sderaadt  * lnk_start
225df930be7Sderaadt  *	Creates the hard link table.
226df930be7Sderaadt  * Return:
227df930be7Sderaadt  *	0 if created, -1 if failure
228df930be7Sderaadt  */
229df930be7Sderaadt 
230df930be7Sderaadt int
lnk_start(void)231df930be7Sderaadt lnk_start(void)
232df930be7Sderaadt {
233df930be7Sderaadt 	if (ltab != NULL)
234df930be7Sderaadt 		return(0);
235f38a3474Sguenther 	if ((ltab = calloc(L_TAB_SZ, sizeof(HRDLNK *))) == NULL) {
23642cf9836Stholo 		paxwarn(1, "Cannot allocate memory for hard link table");
237df930be7Sderaadt 		return(-1);
238df930be7Sderaadt 	}
239df930be7Sderaadt 	return(0);
240df930be7Sderaadt }
241df930be7Sderaadt 
242df930be7Sderaadt /*
243df930be7Sderaadt  * chk_lnk()
244df930be7Sderaadt  *	Looks up entry in hard link hash table. If found, it copies the name
245df930be7Sderaadt  *	of the file it is linked to (we already saw that file) into ln_name.
246df930be7Sderaadt  *	lnkcnt is decremented and if goes to 1 the node is deleted from the
247df930be7Sderaadt  *	database. (We have seen all the links to this file). If not found,
248df930be7Sderaadt  *	we add the file to the database if it has the potential for having
249df930be7Sderaadt  *	hard links to other files we may process (it has a link count > 1)
250df930be7Sderaadt  * Return:
251df930be7Sderaadt  *	if found returns 1; if not found returns 0; -1 on error
252df930be7Sderaadt  */
253df930be7Sderaadt 
254df930be7Sderaadt int
chk_lnk(ARCHD * arcn)255be87792eSmillert chk_lnk(ARCHD *arcn)
256df930be7Sderaadt {
257be87792eSmillert 	HRDLNK *pt;
258be87792eSmillert 	HRDLNK **ppt;
259be87792eSmillert 	u_int indx;
260df930be7Sderaadt 
261df930be7Sderaadt 	if (ltab == NULL)
262df930be7Sderaadt 		return(-1);
263df930be7Sderaadt 	/*
264df930be7Sderaadt 	 * ignore those nodes that cannot have hard links
265df930be7Sderaadt 	 */
266df930be7Sderaadt 	if ((arcn->type == PAX_DIR) || (arcn->sb.st_nlink <= 1))
267df930be7Sderaadt 		return(0);
268df930be7Sderaadt 
269df930be7Sderaadt 	/*
270df930be7Sderaadt 	 * hash inode number and look for this file
271df930be7Sderaadt 	 */
272df930be7Sderaadt 	indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ;
273df930be7Sderaadt 	if ((pt = ltab[indx]) != NULL) {
274df930be7Sderaadt 		/*
275a7a4d07aSotto 		 * its hash chain in not empty, walk down looking for it
276df930be7Sderaadt 		 */
277df930be7Sderaadt 		ppt = &(ltab[indx]);
278df930be7Sderaadt 		while (pt != NULL) {
279df930be7Sderaadt 			if ((pt->ino == arcn->sb.st_ino) &&
280df930be7Sderaadt 			    (pt->dev == arcn->sb.st_dev))
281df930be7Sderaadt 				break;
282df930be7Sderaadt 			ppt = &(pt->fow);
283df930be7Sderaadt 			pt = pt->fow;
284df930be7Sderaadt 		}
285df930be7Sderaadt 
286df930be7Sderaadt 		if (pt != NULL) {
287df930be7Sderaadt 			/*
288df930be7Sderaadt 			 * found a link. set the node type and copy in the
289df930be7Sderaadt 			 * name of the file it is to link to. we need to
290df930be7Sderaadt 			 * handle hardlinks to regular files differently than
291df930be7Sderaadt 			 * other links.
292df930be7Sderaadt 			 */
2938f3d3452Smickey 			arcn->ln_nlen = strlcpy(arcn->ln_name, pt->name,
2948f3d3452Smickey 				sizeof(arcn->ln_name));
295ad38961eSbeck 			/* XXX truncate? */
296a38f4902Sotto 			if ((size_t)arcn->nlen >= sizeof(arcn->name))
297ad38961eSbeck 				arcn->nlen = sizeof(arcn->name) - 1;
298df930be7Sderaadt 			if (arcn->type == PAX_REG)
299df930be7Sderaadt 				arcn->type = PAX_HRG;
300df930be7Sderaadt 			else
301df930be7Sderaadt 				arcn->type = PAX_HLK;
302df930be7Sderaadt 
303df930be7Sderaadt 			/*
304df930be7Sderaadt 			 * if we have found all the links to this file, remove
305df930be7Sderaadt 			 * it from the database
306df930be7Sderaadt 			 */
307df930be7Sderaadt 			if (--pt->nlink <= 1) {
308df930be7Sderaadt 				*ppt = pt->fow;
309f38a3474Sguenther 				free(pt->name);
310f38a3474Sguenther 				free(pt);
311df930be7Sderaadt 			}
312df930be7Sderaadt 			return(1);
313df930be7Sderaadt 		}
314df930be7Sderaadt 	}
315df930be7Sderaadt 
316df930be7Sderaadt 	/*
317df930be7Sderaadt 	 * we never saw this file before. It has links so we add it to the
318df930be7Sderaadt 	 * front of this hash chain
319df930be7Sderaadt 	 */
320f38a3474Sguenther 	if ((pt = malloc(sizeof(HRDLNK))) != NULL) {
321df930be7Sderaadt 		if ((pt->name = strdup(arcn->name)) != NULL) {
322df930be7Sderaadt 			pt->dev = arcn->sb.st_dev;
323df930be7Sderaadt 			pt->ino = arcn->sb.st_ino;
324df930be7Sderaadt 			pt->nlink = arcn->sb.st_nlink;
325df930be7Sderaadt 			pt->fow = ltab[indx];
326df930be7Sderaadt 			ltab[indx] = pt;
327df930be7Sderaadt 			return(0);
328df930be7Sderaadt 		}
329f38a3474Sguenther 		free(pt);
330df930be7Sderaadt 	}
331df930be7Sderaadt 
33242cf9836Stholo 	paxwarn(1, "Hard link table out of memory");
333df930be7Sderaadt 	return(-1);
334df930be7Sderaadt }
335df930be7Sderaadt 
336df930be7Sderaadt /*
337df930be7Sderaadt  * purg_lnk
338df930be7Sderaadt  *	remove reference for a file that we may have added to the data base as
339df930be7Sderaadt  *	a potential source for hard links. We ended up not using the file, so
340df930be7Sderaadt  *	we do not want to accidently point another file at it later on.
341df930be7Sderaadt  */
342df930be7Sderaadt 
343df930be7Sderaadt void
purg_lnk(ARCHD * arcn)344be87792eSmillert purg_lnk(ARCHD *arcn)
345df930be7Sderaadt {
346be87792eSmillert 	HRDLNK *pt;
347be87792eSmillert 	HRDLNK **ppt;
348be87792eSmillert 	u_int indx;
349df930be7Sderaadt 
350df930be7Sderaadt 	if (ltab == NULL)
351df930be7Sderaadt 		return;
352df930be7Sderaadt 	/*
353df930be7Sderaadt 	 * do not bother to look if it could not be in the database
354df930be7Sderaadt 	 */
355df930be7Sderaadt 	if ((arcn->sb.st_nlink <= 1) || (arcn->type == PAX_DIR) ||
3565316f7a4Sguenther 	    PAX_IS_HARDLINK(arcn->type))
357df930be7Sderaadt 		return;
358df930be7Sderaadt 
359df930be7Sderaadt 	/*
360df930be7Sderaadt 	 * find the hash chain for this inode value, if empty return
361df930be7Sderaadt 	 */
362df930be7Sderaadt 	indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ;
363df930be7Sderaadt 	if ((pt = ltab[indx]) == NULL)
364df930be7Sderaadt 		return;
365df930be7Sderaadt 
366df930be7Sderaadt 	/*
367df930be7Sderaadt 	 * walk down the list looking for the inode/dev pair, unlink and
368df930be7Sderaadt 	 * free if found
369df930be7Sderaadt 	 */
370df930be7Sderaadt 	ppt = &(ltab[indx]);
371df930be7Sderaadt 	while (pt != NULL) {
372df930be7Sderaadt 		if ((pt->ino == arcn->sb.st_ino) &&
373df930be7Sderaadt 		    (pt->dev == arcn->sb.st_dev))
374df930be7Sderaadt 			break;
375df930be7Sderaadt 		ppt = &(pt->fow);
376df930be7Sderaadt 		pt = pt->fow;
377df930be7Sderaadt 	}
378df930be7Sderaadt 	if (pt == NULL)
379df930be7Sderaadt 		return;
380df930be7Sderaadt 
381df930be7Sderaadt 	/*
382df930be7Sderaadt 	 * remove and free it
383df930be7Sderaadt 	 */
384df930be7Sderaadt 	*ppt = pt->fow;
385f38a3474Sguenther 	free(pt->name);
386f38a3474Sguenther 	free(pt);
387df930be7Sderaadt }
388df930be7Sderaadt 
389df930be7Sderaadt /*
390df930be7Sderaadt  * lnk_end()
391df930be7Sderaadt  *	pull apart a existing link table so we can reuse it. We do this between
392df930be7Sderaadt  *	read and write phases of append with update. (The format may have
393df930be7Sderaadt  *	used the link table, and we need to start with a fresh table for the
394df930be7Sderaadt  *	write phase
395df930be7Sderaadt  */
396df930be7Sderaadt 
397df930be7Sderaadt void
lnk_end(void)398df930be7Sderaadt lnk_end(void)
399df930be7Sderaadt {
400be87792eSmillert 	int i;
401be87792eSmillert 	HRDLNK *pt;
402be87792eSmillert 	HRDLNK *ppt;
403df930be7Sderaadt 
404df930be7Sderaadt 	if (ltab == NULL)
405df930be7Sderaadt 		return;
406df930be7Sderaadt 
407df930be7Sderaadt 	for (i = 0; i < L_TAB_SZ; ++i) {
408df930be7Sderaadt 		if (ltab[i] == NULL)
409df930be7Sderaadt 			continue;
410df930be7Sderaadt 		pt = ltab[i];
411df930be7Sderaadt 		ltab[i] = NULL;
412df930be7Sderaadt 
413df930be7Sderaadt 		/*
414df930be7Sderaadt 		 * free up each entry on this chain
415df930be7Sderaadt 		 */
416df930be7Sderaadt 		while (pt != NULL) {
417df930be7Sderaadt 			ppt = pt;
418df930be7Sderaadt 			pt = ppt->fow;
419f38a3474Sguenther 			free(ppt->name);
420f38a3474Sguenther 			free(ppt);
421df930be7Sderaadt 		}
422df930be7Sderaadt 	}
423df930be7Sderaadt }
424df930be7Sderaadt 
425df930be7Sderaadt /*
426df930be7Sderaadt  * modification time table routines
427df930be7Sderaadt  *
428df930be7Sderaadt  * The modification time table keeps track of last modification times for all
429df930be7Sderaadt  * files stored in an archive during a write phase when -u is set. We only
430df930be7Sderaadt  * add a file to the archive if it is newer than a file with the same name
431df930be7Sderaadt  * already stored on the archive (if there is no other file with the same
432df930be7Sderaadt  * name on the archive it is added). This applies to writes and appends.
433df930be7Sderaadt  * An append with an -u must read the archive and store the modification time
434df930be7Sderaadt  * for every file on that archive before starting the write phase. It is clear
435df930be7Sderaadt  * that this is one HUGE database. To save memory space, the actual file names
436f0f82a05Sjmc  * are stored in a scratch file and indexed by an in-memory hash table. The
437df930be7Sderaadt  * hash table is indexed by hashing the file path. The nodes in the table store
438df930be7Sderaadt  * the length of the filename and the lseek offset within the scratch file
439f0f82a05Sjmc  * where the actual name is stored. Since there are never any deletions from
440f0f82a05Sjmc  * this table, fragmentation of the scratch file is never a issue. Lookups
441f0f82a05Sjmc  * seem to not exhibit any locality at all (files in the database are rarely
442f0f82a05Sjmc  * looked up more than once...), so caching is just a waste of memory. The
443f0f82a05Sjmc  * only limitation is the amount of scratch file space available to store the
444df930be7Sderaadt  * path names.
445df930be7Sderaadt  */
446df930be7Sderaadt 
447df930be7Sderaadt /*
448df930be7Sderaadt  * ftime_start()
449df930be7Sderaadt  *	create the file time hash table and open for read/write the scratch
450df930be7Sderaadt  *	file. (after created it is unlinked, so when we exit we leave
451df930be7Sderaadt  *	no witnesses).
452df930be7Sderaadt  * Return:
453df930be7Sderaadt  *	0 if the table and file was created ok, -1 otherwise
454df930be7Sderaadt  */
455df930be7Sderaadt 
456df930be7Sderaadt int
ftime_start(void)457df930be7Sderaadt ftime_start(void)
458df930be7Sderaadt {
459df930be7Sderaadt 
460df930be7Sderaadt 	if (ftab != NULL)
461df930be7Sderaadt 		return(0);
462f38a3474Sguenther 	if ((ftab = calloc(F_TAB_SZ, sizeof(FTM *))) == NULL) {
46342cf9836Stholo 		paxwarn(1, "Cannot allocate memory for file time table");
464df930be7Sderaadt 		return(-1);
465df930be7Sderaadt 	}
466df930be7Sderaadt 
467df930be7Sderaadt 	/*
468df930be7Sderaadt 	 * get random name and create temporary scratch file, unlink name
469df930be7Sderaadt 	 * so it will get removed on exit
470df930be7Sderaadt 	 */
471f9da32f6Smillert 	memcpy(tempbase, _TFILE_BASE, sizeof(_TFILE_BASE));
472c2d43ecaSderaadt 	if ((ffd = mkstemp(tempfile)) == -1) {
473f9da32f6Smillert 		syswarn(1, errno, "Unable to create temporary file: %s",
474f9da32f6Smillert 		    tempfile);
475fc709564Saaron 		return(-1);
476fc709564Saaron 	}
477f9da32f6Smillert 	(void)unlink(tempfile);
478d1e56374Stholo 
479df930be7Sderaadt 	return(0);
480df930be7Sderaadt }
481df930be7Sderaadt 
482df930be7Sderaadt /*
483df930be7Sderaadt  * chk_ftime()
484df930be7Sderaadt  *	looks up entry in file time hash table. If not found, the file is
485df930be7Sderaadt  *	added to the hash table and the file named stored in the scratch file.
486df930be7Sderaadt  *	If a file with the same name is found, the file times are compared and
487df930be7Sderaadt  *	the most recent file time is retained. If the new file was younger (or
488df930be7Sderaadt  *	was not in the database) the new file is selected for storage.
489df930be7Sderaadt  * Return:
490df930be7Sderaadt  *	0 if file should be added to the archive, 1 if it should be skipped,
491df930be7Sderaadt  *	-1 on error
492df930be7Sderaadt  */
493df930be7Sderaadt 
494df930be7Sderaadt int
chk_ftime(ARCHD * arcn)495be87792eSmillert chk_ftime(ARCHD *arcn)
496df930be7Sderaadt {
497be87792eSmillert 	FTM *pt;
498be87792eSmillert 	int namelen;
499be87792eSmillert 	u_int indx;
500df930be7Sderaadt 	char ckname[PAXPATHLEN+1];
501df930be7Sderaadt 
502df930be7Sderaadt 	/*
503df930be7Sderaadt 	 * no info, go ahead and add to archive
504df930be7Sderaadt 	 */
505df930be7Sderaadt 	if (ftab == NULL)
506df930be7Sderaadt 		return(0);
507df930be7Sderaadt 
508df930be7Sderaadt 	/*
509df930be7Sderaadt 	 * hash the pathname and look up in table
510df930be7Sderaadt 	 */
511df930be7Sderaadt 	namelen = arcn->nlen;
512df930be7Sderaadt 	indx = st_hash(arcn->name, namelen, F_TAB_SZ);
513df930be7Sderaadt 	if ((pt = ftab[indx]) != NULL) {
514df930be7Sderaadt 		/*
515df930be7Sderaadt 		 * the hash chain is not empty, walk down looking for match
516df930be7Sderaadt 		 * only read up the path names if the lengths match, speeds
517df930be7Sderaadt 		 * up the search a lot
518df930be7Sderaadt 		 */
519df930be7Sderaadt 		while (pt != NULL) {
520df930be7Sderaadt 			if (pt->namelen == namelen) {
521df930be7Sderaadt 				/*
522df930be7Sderaadt 				 * potential match, have to read the name
523df930be7Sderaadt 				 * from the scratch file.
524df930be7Sderaadt 				 */
525df930be7Sderaadt 				if (lseek(ffd,pt->seek,SEEK_SET) != pt->seek) {
526df930be7Sderaadt 					syswarn(1, errno,
527df930be7Sderaadt 					    "Failed ftime table seek");
528df930be7Sderaadt 					return(-1);
529df930be7Sderaadt 				}
530df930be7Sderaadt 				if (read(ffd, ckname, namelen) != namelen) {
531df930be7Sderaadt 					syswarn(1, errno,
532df930be7Sderaadt 					    "Failed ftime table read");
533df930be7Sderaadt 					return(-1);
534df930be7Sderaadt 				}
535df930be7Sderaadt 
536df930be7Sderaadt 				/*
537df930be7Sderaadt 				 * if the names match, we are done
538df930be7Sderaadt 				 */
539df930be7Sderaadt 				if (!strncmp(ckname, arcn->name, namelen))
540df930be7Sderaadt 					break;
541df930be7Sderaadt 			}
542df930be7Sderaadt 
543df930be7Sderaadt 			/*
544df930be7Sderaadt 			 * try the next entry on the chain
545df930be7Sderaadt 			 */
546df930be7Sderaadt 			pt = pt->fow;
547df930be7Sderaadt 		}
548df930be7Sderaadt 
549df930be7Sderaadt 		if (pt != NULL) {
550df930be7Sderaadt 			/*
551df930be7Sderaadt 			 * found the file, compare the times, save the newer
552df930be7Sderaadt 			 */
5538b72bc25Sguenther 			if (timespeccmp(&arcn->sb.st_mtim, &pt->mtim, >)) {
554df930be7Sderaadt 				/*
555df930be7Sderaadt 				 * file is newer
556df930be7Sderaadt 				 */
5578b72bc25Sguenther 				pt->mtim = arcn->sb.st_mtim;
558df930be7Sderaadt 				return(0);
559df930be7Sderaadt 			}
560df930be7Sderaadt 			/*
561df930be7Sderaadt 			 * file is older
562df930be7Sderaadt 			 */
563df930be7Sderaadt 			return(1);
564df930be7Sderaadt 		}
565df930be7Sderaadt 	}
566df930be7Sderaadt 
567df930be7Sderaadt 	/*
568df930be7Sderaadt 	 * not in table, add it
569df930be7Sderaadt 	 */
570f38a3474Sguenther 	if ((pt = malloc(sizeof(FTM))) != NULL) {
571df930be7Sderaadt 		/*
572df930be7Sderaadt 		 * add the name at the end of the scratch file, saving the
573df930be7Sderaadt 		 * offset. add the file to the head of the hash chain
574df930be7Sderaadt 		 */
5759a58b8c6Sguenther 		if ((pt->seek = lseek(ffd, 0, SEEK_END)) >= 0) {
576df930be7Sderaadt 			if (write(ffd, arcn->name, namelen) == namelen) {
5778b72bc25Sguenther 				pt->mtim = arcn->sb.st_mtim;
578df930be7Sderaadt 				pt->namelen = namelen;
579df930be7Sderaadt 				pt->fow = ftab[indx];
580df930be7Sderaadt 				ftab[indx] = pt;
581df930be7Sderaadt 				return(0);
582df930be7Sderaadt 			}
583df930be7Sderaadt 			syswarn(1, errno, "Failed write to file time table");
584df930be7Sderaadt 		} else
585df930be7Sderaadt 			syswarn(1, errno, "Failed seek on file time table");
586df930be7Sderaadt 	} else
58742cf9836Stholo 		paxwarn(1, "File time table ran out of memory");
588df930be7Sderaadt 
589df930be7Sderaadt 	if (pt != NULL)
590f38a3474Sguenther 		free(pt);
591df930be7Sderaadt 	return(-1);
592df930be7Sderaadt }
593df930be7Sderaadt 
594df930be7Sderaadt /*
5952dbd6dc5Sguenther  * escaping (absolute or w/"..") symlink table routines
5962dbd6dc5Sguenther  *
5972dbd6dc5Sguenther  * By default, an archive shouldn't be able extract to outside of the
5982dbd6dc5Sguenther  * current directory.  What should we do if the archive contains a symlink
5992dbd6dc5Sguenther  * whose value is either absolute or contains ".." components?  What we'll
6002dbd6dc5Sguenther  * do is initially create the path as an empty file (to block attempts to
6012dbd6dc5Sguenther  * reference _through_ it) and instead record its path and desired
6022dbd6dc5Sguenther  * final value and mode.  Then once all the other archive
6032dbd6dc5Sguenther  * members are created (but before the pass to set timestamps on
6042dbd6dc5Sguenther  * directories) we'll process those records, replacing the placeholder with
6052dbd6dc5Sguenther  * the correct symlink and setting them to the correct mode, owner, group,
6062dbd6dc5Sguenther  * and timestamps.
6072dbd6dc5Sguenther  *
6082dbd6dc5Sguenther  * Note: we also need to handle hardlinks to symlinks (barf) as well as
6092dbd6dc5Sguenther  * hardlinks whose target is replaced by a later entry in the archive (barf^2).
6102dbd6dc5Sguenther  *
6112dbd6dc5Sguenther  * So we track things by dev+ino of the placeholder file, associating with
6122dbd6dc5Sguenther  * that the value and mode of the final symlink and a list of paths that
6132dbd6dc5Sguenther  * should all be hardlinks of that.  We'll 'store' the symlink's desired
6142dbd6dc5Sguenther  * timestamps, owner, and group by setting them on the placeholder file.
6152dbd6dc5Sguenther  *
6162dbd6dc5Sguenther  * The operations are:
6172dbd6dc5Sguenther  * a) create an escaping symlink: create the placeholder file and add an entry
6182dbd6dc5Sguenther  *    for the new link
6192dbd6dc5Sguenther  * b) create a hardlink: do the link.  If the target turns out to be a
6202dbd6dc5Sguenther  *    zero-length file whose dev+ino are in the symlink table, then add this
6212dbd6dc5Sguenther  *    path to the list of names for that link
6222dbd6dc5Sguenther  * c) perform deferred processing: for each entry, check each associated path:
6232dbd6dc5Sguenther  *    if it's a zero-length file with the correct dev+ino then recreate it as
6242dbd6dc5Sguenther  *    the specified symlink or hardlink to the first such
6252dbd6dc5Sguenther  */
6262dbd6dc5Sguenther 
6272dbd6dc5Sguenther struct slpath {
6282dbd6dc5Sguenther 	char	*sp_path;
6292dbd6dc5Sguenther 	struct	slpath *sp_next;
6302dbd6dc5Sguenther };
6312dbd6dc5Sguenther struct slinode {
6322dbd6dc5Sguenther 	ino_t	sli_ino;
6332dbd6dc5Sguenther 	char	*sli_value;
6342dbd6dc5Sguenther 	struct	slpath sli_paths;
6352dbd6dc5Sguenther 	struct	slinode *sli_fow;		/* hash table chain */
6362dbd6dc5Sguenther 	dev_t	sli_dev;
6372dbd6dc5Sguenther 	mode_t	sli_mode;
6382dbd6dc5Sguenther };
6392dbd6dc5Sguenther 
6402dbd6dc5Sguenther static struct slinode **slitab = NULL;
6412dbd6dc5Sguenther 
6422dbd6dc5Sguenther /*
6432dbd6dc5Sguenther  * sltab_start()
6442dbd6dc5Sguenther  *	create the hash table
6452dbd6dc5Sguenther  * Return:
6462dbd6dc5Sguenther  *	0 if the table and file was created ok, -1 otherwise
6472dbd6dc5Sguenther  */
6482dbd6dc5Sguenther 
6492dbd6dc5Sguenther int
sltab_start(void)6502dbd6dc5Sguenther sltab_start(void)
6512dbd6dc5Sguenther {
6522dbd6dc5Sguenther 
6532dbd6dc5Sguenther 	if ((slitab = calloc(SL_TAB_SZ, sizeof *slitab)) == NULL) {
6542dbd6dc5Sguenther 		syswarn(1, errno, "symlink table");
6552dbd6dc5Sguenther 		return(-1);
6562dbd6dc5Sguenther 	}
6572dbd6dc5Sguenther 
6582dbd6dc5Sguenther 	return(0);
6592dbd6dc5Sguenther }
6602dbd6dc5Sguenther 
6612dbd6dc5Sguenther /*
6622dbd6dc5Sguenther  * sltab_add_sym()
6632dbd6dc5Sguenther  *	Create the placeholder and tracking info for an escaping symlink.
6642dbd6dc5Sguenther  * Return:
6652dbd6dc5Sguenther  *	0 on success, -1 otherwise
6662dbd6dc5Sguenther  */
6672dbd6dc5Sguenther 
6682dbd6dc5Sguenther int
sltab_add_sym(const char * path0,const char * value0,mode_t mode)6692dbd6dc5Sguenther sltab_add_sym(const char *path0, const char *value0, mode_t mode)
6702dbd6dc5Sguenther {
6712dbd6dc5Sguenther 	struct stat sb;
6722dbd6dc5Sguenther 	struct slinode *s;
6732dbd6dc5Sguenther 	struct slpath *p;
6742dbd6dc5Sguenther 	char *path, *value;
6752dbd6dc5Sguenther 	u_int indx;
6762dbd6dc5Sguenther 	int fd;
6772dbd6dc5Sguenther 
6782dbd6dc5Sguenther 	/* create the placeholder */
6792dbd6dc5Sguenther 	fd = open(path0, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
6802dbd6dc5Sguenther 	if (fd == -1)
6812dbd6dc5Sguenther 		return (-1);
6822dbd6dc5Sguenther 	if (fstat(fd, &sb) == -1) {
6832dbd6dc5Sguenther 		unlink(path0);
6842dbd6dc5Sguenther 		close(fd);
6852dbd6dc5Sguenther 		return (-1);
6862dbd6dc5Sguenther 	}
6872dbd6dc5Sguenther 	close(fd);
6882dbd6dc5Sguenther 
6892dbd6dc5Sguenther 	if (havechd && *path0 != '/') {
6902dbd6dc5Sguenther 		if ((path = realpath(path0, NULL)) == NULL) {
6912dbd6dc5Sguenther 			syswarn(1, errno, "Cannot canonicalize %s", path0);
6922dbd6dc5Sguenther 			unlink(path0);
6932dbd6dc5Sguenther 			return (-1);
6942dbd6dc5Sguenther 		}
6952dbd6dc5Sguenther 	} else if ((path = strdup(path0)) == NULL) {
6962dbd6dc5Sguenther 		syswarn(1, errno, "defered symlink path");
6972dbd6dc5Sguenther 		unlink(path0);
6982dbd6dc5Sguenther 		return (-1);
6992dbd6dc5Sguenther 	}
7002dbd6dc5Sguenther 	if ((value = strdup(value0)) == NULL) {
7012dbd6dc5Sguenther 		syswarn(1, errno, "defered symlink value");
7022dbd6dc5Sguenther 		unlink(path);
7032dbd6dc5Sguenther 		free(path);
7042dbd6dc5Sguenther 		return (-1);
7052dbd6dc5Sguenther 	}
7062dbd6dc5Sguenther 
7072dbd6dc5Sguenther 	/* now check the hash table for conflicting entry */
7082dbd6dc5Sguenther 	indx = (sb.st_ino ^ sb.st_dev) % SL_TAB_SZ;
7092dbd6dc5Sguenther 	for (s = slitab[indx]; s != NULL; s = s->sli_fow) {
7102dbd6dc5Sguenther 		if (s->sli_ino != sb.st_ino || s->sli_dev != sb.st_dev)
7112dbd6dc5Sguenther 			continue;
7122dbd6dc5Sguenther 
7132dbd6dc5Sguenther 		/*
7142dbd6dc5Sguenther 		 * One of our placeholders got removed behind our back and
7152dbd6dc5Sguenther 		 * we've reused the inode.  Weird, but clean up the mess.
7162dbd6dc5Sguenther 		 */
7172dbd6dc5Sguenther 		free(s->sli_value);
7182dbd6dc5Sguenther 		free(s->sli_paths.sp_path);
7192dbd6dc5Sguenther 		p = s->sli_paths.sp_next;
7202dbd6dc5Sguenther 		while (p != NULL) {
7212dbd6dc5Sguenther 			struct slpath *next_p = p->sp_next;
7222dbd6dc5Sguenther 
7232dbd6dc5Sguenther 			free(p->sp_path);
7242dbd6dc5Sguenther 			free(p);
7252dbd6dc5Sguenther 			p = next_p;
7262dbd6dc5Sguenther 		}
7272dbd6dc5Sguenther 		goto set_value;
7282dbd6dc5Sguenther 	}
7292dbd6dc5Sguenther 
7302dbd6dc5Sguenther 	/* Normal case: create a new node */
7312dbd6dc5Sguenther 	if ((s = malloc(sizeof *s)) == NULL) {
7322dbd6dc5Sguenther 		syswarn(1, errno, "defered symlink");
7332dbd6dc5Sguenther 		unlink(path);
7342dbd6dc5Sguenther 		free(path);
7352dbd6dc5Sguenther 		free(value);
7362dbd6dc5Sguenther 		return (-1);
7372dbd6dc5Sguenther 	}
7382dbd6dc5Sguenther 	s->sli_ino = sb.st_ino;
7392dbd6dc5Sguenther 	s->sli_dev = sb.st_dev;
7402dbd6dc5Sguenther 	s->sli_fow = slitab[indx];
7412dbd6dc5Sguenther 	slitab[indx] = s;
7422dbd6dc5Sguenther 
7432dbd6dc5Sguenther set_value:
7442dbd6dc5Sguenther 	s->sli_paths.sp_path = path;
7452dbd6dc5Sguenther 	s->sli_paths.sp_next = NULL;
7462dbd6dc5Sguenther 	s->sli_value = value;
7472dbd6dc5Sguenther 	s->sli_mode = mode;
7482dbd6dc5Sguenther 	return (0);
7492dbd6dc5Sguenther }
7502dbd6dc5Sguenther 
7512dbd6dc5Sguenther /*
7522dbd6dc5Sguenther  * sltab_add_link()
7532dbd6dc5Sguenther  *	A hardlink was created; if it looks like a placeholder, handle the
7542dbd6dc5Sguenther  *	tracking.
7552dbd6dc5Sguenther  * Return:
7562dbd6dc5Sguenther  *	0 if things are ok, -1 if something went wrong
7572dbd6dc5Sguenther  */
7582dbd6dc5Sguenther 
7592dbd6dc5Sguenther int
sltab_add_link(const char * path,const struct stat * sb)7602dbd6dc5Sguenther sltab_add_link(const char *path, const struct stat *sb)
7612dbd6dc5Sguenther {
7622dbd6dc5Sguenther 	struct slinode *s;
7632dbd6dc5Sguenther 	struct slpath *p;
7642dbd6dc5Sguenther 	u_int indx;
7652dbd6dc5Sguenther 
7662dbd6dc5Sguenther 	if (!S_ISREG(sb->st_mode) || sb->st_size != 0)
7672dbd6dc5Sguenther 		return (1);
7682dbd6dc5Sguenther 
7692dbd6dc5Sguenther 	/* find the hash table entry for this hardlink */
7702dbd6dc5Sguenther 	indx = (sb->st_ino ^ sb->st_dev) % SL_TAB_SZ;
7712dbd6dc5Sguenther 	for (s = slitab[indx]; s != NULL; s = s->sli_fow) {
7722dbd6dc5Sguenther 		if (s->sli_ino != sb->st_ino || s->sli_dev != sb->st_dev)
7732dbd6dc5Sguenther 			continue;
7742dbd6dc5Sguenther 
7752dbd6dc5Sguenther 		if ((p = malloc(sizeof *p)) == NULL) {
7762dbd6dc5Sguenther 			syswarn(1, errno, "deferred symlink hardlink");
7772dbd6dc5Sguenther 			return (-1);
7782dbd6dc5Sguenther 		}
7792dbd6dc5Sguenther 		if (havechd && *path != '/') {
7802dbd6dc5Sguenther 			if ((p->sp_path = realpath(path, NULL)) == NULL) {
7812dbd6dc5Sguenther 				syswarn(1, errno, "Cannot canonicalize %s",
7822dbd6dc5Sguenther 				    path);
7832dbd6dc5Sguenther 				free(p);
7842dbd6dc5Sguenther 				return (-1);
7852dbd6dc5Sguenther 			}
7862dbd6dc5Sguenther 		} else if ((p->sp_path = strdup(path)) == NULL) {
7872dbd6dc5Sguenther 			syswarn(1, errno, "defered symlink hardlink path");
7882dbd6dc5Sguenther 			free(p);
7892dbd6dc5Sguenther 			return (-1);
7902dbd6dc5Sguenther 		}
7912dbd6dc5Sguenther 
7922dbd6dc5Sguenther 		/* link it in */
7932dbd6dc5Sguenther 		p->sp_next = s->sli_paths.sp_next;
7942dbd6dc5Sguenther 		s->sli_paths.sp_next = p;
7952dbd6dc5Sguenther 		return (0);
7962dbd6dc5Sguenther 	}
7972dbd6dc5Sguenther 
7982dbd6dc5Sguenther 	/* not found */
7992dbd6dc5Sguenther 	return (1);
8002dbd6dc5Sguenther }
8012dbd6dc5Sguenther 
8022dbd6dc5Sguenther 
8032dbd6dc5Sguenther static int
sltab_process_one(struct slinode * s,struct slpath * p,const char * first,int in_sig)8042dbd6dc5Sguenther sltab_process_one(struct slinode *s, struct slpath *p, const char *first,
8052dbd6dc5Sguenther     int in_sig)
8062dbd6dc5Sguenther {
8072dbd6dc5Sguenther 	struct stat sb;
8082dbd6dc5Sguenther 	char *path = p->sp_path;
8092dbd6dc5Sguenther 	mode_t mode;
8102dbd6dc5Sguenther 	int err;
8112dbd6dc5Sguenther 
8122dbd6dc5Sguenther 	/*
8132dbd6dc5Sguenther 	 * is it the expected placeholder?  This can fail legimately
8142dbd6dc5Sguenther 	 * if the archive overwrote the link with another, later entry,
8152dbd6dc5Sguenther 	 * so don't warn.
8162dbd6dc5Sguenther 	 */
8172dbd6dc5Sguenther 	if (stat(path, &sb) != 0 || !S_ISREG(sb.st_mode) || sb.st_size != 0 ||
8182dbd6dc5Sguenther 	    sb.st_ino != s->sli_ino || sb.st_dev != s->sli_dev)
8192dbd6dc5Sguenther 		return (0);
8202dbd6dc5Sguenther 
8212dbd6dc5Sguenther 	if (unlink(path) && errno != ENOENT) {
8222dbd6dc5Sguenther 		if (!in_sig)
8232dbd6dc5Sguenther 			syswarn(1, errno, "deferred symlink removal");
8242dbd6dc5Sguenther 		return (0);
8252dbd6dc5Sguenther 	}
8262dbd6dc5Sguenther 
8272dbd6dc5Sguenther 	err = 0;
8282dbd6dc5Sguenther 	if (first != NULL) {
8292dbd6dc5Sguenther 		/* add another hardlink to the existing symlink */
8302dbd6dc5Sguenther 		if (linkat(AT_FDCWD, first, AT_FDCWD, path, 0) == 0)
8312dbd6dc5Sguenther 			return (0);
8322dbd6dc5Sguenther 
8332dbd6dc5Sguenther 		/*
8342dbd6dc5Sguenther 		 * Couldn't hardlink the symlink for some reason, so we'll
8352dbd6dc5Sguenther 		 * try creating it as its own symlink, but save the error
8362dbd6dc5Sguenther 		 * for reporting if that fails.
8372dbd6dc5Sguenther 		 */
8382dbd6dc5Sguenther 		err = errno;
8392dbd6dc5Sguenther 	}
8402dbd6dc5Sguenther 
8412dbd6dc5Sguenther 	if (symlink(s->sli_value, path)) {
8422dbd6dc5Sguenther 		if (!in_sig) {
8432dbd6dc5Sguenther 			const char *qualifier = "";
8442dbd6dc5Sguenther 			if (err)
8452dbd6dc5Sguenther 				qualifier = " hardlink";
8462dbd6dc5Sguenther 			else
8472dbd6dc5Sguenther 				err = errno;
8482dbd6dc5Sguenther 
8492dbd6dc5Sguenther 			syswarn(1, err, "deferred symlink%s: %s",
8502dbd6dc5Sguenther 			    qualifier, path);
8512dbd6dc5Sguenther 		}
8522dbd6dc5Sguenther 		return (0);
8532dbd6dc5Sguenther 	}
8542dbd6dc5Sguenther 
8552dbd6dc5Sguenther 	/* success, so set the id, mode, and times */
8562dbd6dc5Sguenther 	mode = s->sli_mode;
8572dbd6dc5Sguenther 	if (pids) {
8582dbd6dc5Sguenther 		/* if can't set the ids, force the set[ug]id bits off */
8592dbd6dc5Sguenther 		if (set_ids(path, sb.st_uid, sb.st_gid))
8602dbd6dc5Sguenther 			mode &= ~(SETBITS);
8612dbd6dc5Sguenther 	}
8622dbd6dc5Sguenther 
8632dbd6dc5Sguenther 	if (pmode)
8642dbd6dc5Sguenther 		set_pmode(path, mode);
8652dbd6dc5Sguenther 
8662dbd6dc5Sguenther 	if (patime || pmtime)
8678b72bc25Sguenther 		set_ftime(path, &sb.st_mtim, &sb.st_atim, 0);
8682dbd6dc5Sguenther 
8692dbd6dc5Sguenther 	/*
8702dbd6dc5Sguenther 	 * If we tried to link to first but failed, then this new symlink
8712dbd6dc5Sguenther 	 * might be a better one to try in the future.  Guess from the errno.
8722dbd6dc5Sguenther 	 */
8732dbd6dc5Sguenther 	if (err == 0 || err == ENOENT || err == EMLINK || err == EOPNOTSUPP)
8742dbd6dc5Sguenther 		return (1);
8752dbd6dc5Sguenther 	return (0);
8762dbd6dc5Sguenther }
8772dbd6dc5Sguenther 
8782dbd6dc5Sguenther /*
8792dbd6dc5Sguenther  * sltab_process()
8802dbd6dc5Sguenther  *	Do all the delayed process for escape symlinks
8812dbd6dc5Sguenther  */
8822dbd6dc5Sguenther 
8832dbd6dc5Sguenther void
sltab_process(int in_sig)8842dbd6dc5Sguenther sltab_process(int in_sig)
8852dbd6dc5Sguenther {
8862dbd6dc5Sguenther 	struct slinode *s;
8872dbd6dc5Sguenther 	struct slpath *p;
8882dbd6dc5Sguenther 	char *first;
8892dbd6dc5Sguenther 	u_int indx;
8902dbd6dc5Sguenther 
8912dbd6dc5Sguenther 	if (slitab == NULL)
8922dbd6dc5Sguenther 		return;
8932dbd6dc5Sguenther 
8942dbd6dc5Sguenther 	/* walk across the entire hash table */
8952dbd6dc5Sguenther 	for (indx = 0; indx < SL_TAB_SZ; indx++) {
8962dbd6dc5Sguenther 		while ((s = slitab[indx]) != NULL) {
8972dbd6dc5Sguenther 			/* pop this entry */
8982dbd6dc5Sguenther 			slitab[indx] = s->sli_fow;
8992dbd6dc5Sguenther 
9002dbd6dc5Sguenther 			first = NULL;
9012dbd6dc5Sguenther 			p = &s->sli_paths;
9022dbd6dc5Sguenther 			while (1) {
9032dbd6dc5Sguenther 				struct slpath *next_p;
9042dbd6dc5Sguenther 
9052dbd6dc5Sguenther 				if (sltab_process_one(s, p, first, in_sig)) {
9062dbd6dc5Sguenther 					if (!in_sig)
9072dbd6dc5Sguenther 						free(first);
9082dbd6dc5Sguenther 					first = p->sp_path;
9092dbd6dc5Sguenther 				} else if (!in_sig)
9102dbd6dc5Sguenther 					free(p->sp_path);
9112dbd6dc5Sguenther 
9122dbd6dc5Sguenther 				if ((next_p = p->sp_next) == NULL)
9132dbd6dc5Sguenther 					break;
9142dbd6dc5Sguenther 				*p = *next_p;
9152dbd6dc5Sguenther 				if (!in_sig)
9162dbd6dc5Sguenther 					free(next_p);
9172dbd6dc5Sguenther 			}
9182dbd6dc5Sguenther 			if (!in_sig) {
9192dbd6dc5Sguenther 				free(first);
9202dbd6dc5Sguenther 				free(s->sli_value);
9212dbd6dc5Sguenther 				free(s);
9222dbd6dc5Sguenther 			}
9232dbd6dc5Sguenther 		}
9242dbd6dc5Sguenther 	}
9252dbd6dc5Sguenther 	if (!in_sig)
9262dbd6dc5Sguenther 		free(slitab);
9272dbd6dc5Sguenther 	slitab = NULL;
9282dbd6dc5Sguenther }
9292dbd6dc5Sguenther 
9302dbd6dc5Sguenther 
9312dbd6dc5Sguenther /*
932df930be7Sderaadt  * Interactive rename table routines
933df930be7Sderaadt  *
934df930be7Sderaadt  * The interactive rename table keeps track of the new names that the user
9354eb0b000Smillert  * assigns to files from tty input. Since this map is unique for each file
936df930be7Sderaadt  * we must store it in case there is a reference to the file later in archive
937df930be7Sderaadt  * (a link). Otherwise we will be unable to find the file we know was
938df930be7Sderaadt  * extracted. The remapping of these files is stored in a memory based hash
939df930be7Sderaadt  * table (it is assumed since input must come from /dev/tty, it is unlikely to
940df930be7Sderaadt  * be a very large table).
941df930be7Sderaadt  */
942df930be7Sderaadt 
943df930be7Sderaadt /*
944df930be7Sderaadt  * name_start()
945df930be7Sderaadt  *	create the interactive rename table
946df930be7Sderaadt  * Return:
947df930be7Sderaadt  *	0 if successful, -1 otherwise
948df930be7Sderaadt  */
949df930be7Sderaadt 
950df930be7Sderaadt int
name_start(void)951df930be7Sderaadt name_start(void)
952df930be7Sderaadt {
953df930be7Sderaadt 	if (ntab != NULL)
954df930be7Sderaadt 		return(0);
955f38a3474Sguenther 	if ((ntab = calloc(N_TAB_SZ, sizeof(NAMT *))) == NULL) {
95642cf9836Stholo 		paxwarn(1, "Cannot allocate memory for interactive rename table");
957df930be7Sderaadt 		return(-1);
958df930be7Sderaadt 	}
959df930be7Sderaadt 	return(0);
960df930be7Sderaadt }
961df930be7Sderaadt 
962df930be7Sderaadt /*
963df930be7Sderaadt  * add_name()
964df930be7Sderaadt  *	add the new name to old name mapping just created by the user.
965df930be7Sderaadt  *	If an old name mapping is found (there may be duplicate names on an
966df930be7Sderaadt  *	archive) only the most recent is kept.
967df930be7Sderaadt  * Return:
968df930be7Sderaadt  *	0 if added, -1 otherwise
969df930be7Sderaadt  */
970df930be7Sderaadt 
971df930be7Sderaadt int
add_name(char * oname,int onamelen,char * nname)972be87792eSmillert add_name(char *oname, int onamelen, char *nname)
973df930be7Sderaadt {
974be87792eSmillert 	NAMT *pt;
975be87792eSmillert 	u_int indx;
976df930be7Sderaadt 
977df930be7Sderaadt 	if (ntab == NULL) {
978df930be7Sderaadt 		/*
979df930be7Sderaadt 		 * should never happen
980df930be7Sderaadt 		 */
981908645a8Sderaadt 		paxwarn(0, "No interactive rename table, links may fail");
982df930be7Sderaadt 		return(0);
983df930be7Sderaadt 	}
984df930be7Sderaadt 
985df930be7Sderaadt 	/*
986df930be7Sderaadt 	 * look to see if we have already mapped this file, if so we
987df930be7Sderaadt 	 * will update it
988df930be7Sderaadt 	 */
989df930be7Sderaadt 	indx = st_hash(oname, onamelen, N_TAB_SZ);
990df930be7Sderaadt 	if ((pt = ntab[indx]) != NULL) {
991df930be7Sderaadt 		/*
992df930be7Sderaadt 		 * look down the has chain for the file
993df930be7Sderaadt 		 */
994df930be7Sderaadt 		while ((pt != NULL) && (strcmp(oname, pt->oname) != 0))
995df930be7Sderaadt 			pt = pt->fow;
996df930be7Sderaadt 
997df930be7Sderaadt 		if (pt != NULL) {
998df930be7Sderaadt 			/*
999df930be7Sderaadt 			 * found an old mapping, replace it with the new one
1000df930be7Sderaadt 			 * the user just input (if it is different)
1001df930be7Sderaadt 			 */
1002df930be7Sderaadt 			if (strcmp(nname, pt->nname) == 0)
1003df930be7Sderaadt 				return(0);
1004df930be7Sderaadt 
1005f38a3474Sguenther 			free(pt->nname);
1006df930be7Sderaadt 			if ((pt->nname = strdup(nname)) == NULL) {
100742cf9836Stholo 				paxwarn(1, "Cannot update rename table");
1008df930be7Sderaadt 				return(-1);
1009df930be7Sderaadt 			}
1010df930be7Sderaadt 			return(0);
1011df930be7Sderaadt 		}
1012df930be7Sderaadt 	}
1013df930be7Sderaadt 
1014df930be7Sderaadt 	/*
1015df930be7Sderaadt 	 * this is a new mapping, add it to the table
1016df930be7Sderaadt 	 */
1017f38a3474Sguenther 	if ((pt = malloc(sizeof(NAMT))) != NULL) {
1018df930be7Sderaadt 		if ((pt->oname = strdup(oname)) != NULL) {
1019df930be7Sderaadt 			if ((pt->nname = strdup(nname)) != NULL) {
1020df930be7Sderaadt 				pt->fow = ntab[indx];
1021df930be7Sderaadt 				ntab[indx] = pt;
1022df930be7Sderaadt 				return(0);
1023df930be7Sderaadt 			}
1024f38a3474Sguenther 			free(pt->oname);
1025df930be7Sderaadt 		}
1026f38a3474Sguenther 		free(pt);
1027df930be7Sderaadt 	}
102842cf9836Stholo 	paxwarn(1, "Interactive rename table out of memory");
1029df930be7Sderaadt 	return(-1);
1030df930be7Sderaadt }
1031df930be7Sderaadt 
1032df930be7Sderaadt /*
1033df930be7Sderaadt  * sub_name()
1034df930be7Sderaadt  *	look up a link name to see if it points at a file that has been
1035df930be7Sderaadt  *	remapped by the user. If found, the link is adjusted to contain the
1036df930be7Sderaadt  *	new name (oname is the link to name)
1037df930be7Sderaadt  */
1038df930be7Sderaadt 
1039df930be7Sderaadt void
sub_name(char * oname,int * onamelen,int onamesize)104076c0e1cfSotto sub_name(char *oname, int *onamelen, int onamesize)
1041df930be7Sderaadt {
1042be87792eSmillert 	NAMT *pt;
1043be87792eSmillert 	u_int indx;
1044df930be7Sderaadt 
1045df930be7Sderaadt 	if (ntab == NULL)
1046df930be7Sderaadt 		return;
1047df930be7Sderaadt 	/*
1048df930be7Sderaadt 	 * look the name up in the hash table
1049df930be7Sderaadt 	 */
1050df930be7Sderaadt 	indx = st_hash(oname, *onamelen, N_TAB_SZ);
1051df930be7Sderaadt 	if ((pt = ntab[indx]) == NULL)
1052df930be7Sderaadt 		return;
1053df930be7Sderaadt 
1054df930be7Sderaadt 	while (pt != NULL) {
1055df930be7Sderaadt 		/*
105676025ccfSmillert 		 * walk down the hash chain looking for a match
1057df930be7Sderaadt 		 */
1058df930be7Sderaadt 		if (strcmp(oname, pt->oname) == 0) {
1059df930be7Sderaadt 			/*
1060df930be7Sderaadt 			 * found it, replace it with the new name
1061df930be7Sderaadt 			 * and return (we know that oname has enough space)
1062df930be7Sderaadt 			 */
10638f3d3452Smickey 			*onamelen = strlcpy(oname, pt->nname, onamesize);
1064ad38961eSbeck 			if (*onamelen >= onamesize)
1065ad38961eSbeck 				*onamelen = onamesize - 1; /* XXX truncate? */
1066df930be7Sderaadt 			return;
1067df930be7Sderaadt 		}
1068df930be7Sderaadt 		pt = pt->fow;
1069df930be7Sderaadt 	}
1070df930be7Sderaadt 
1071df930be7Sderaadt 	/*
1072df930be7Sderaadt 	 * no match, just return
1073df930be7Sderaadt 	 */
1074df930be7Sderaadt }
1075df930be7Sderaadt 
10762dbd6dc5Sguenther #ifndef NOCPIO
1077df930be7Sderaadt /*
1078df930be7Sderaadt  * device/inode mapping table routines
1079df930be7Sderaadt  * (used with formats that store device and inodes fields)
1080df930be7Sderaadt  *
1081df930be7Sderaadt  * device/inode mapping tables remap the device field in a archive header. The
1082df930be7Sderaadt  * device/inode fields are used to determine when files are hard links to each
1083df930be7Sderaadt  * other. However these values have very little meaning outside of that. This
1084df930be7Sderaadt  * database is used to solve one of two different problems.
1085df930be7Sderaadt  *
1086df930be7Sderaadt  * 1) when files are appended to an archive, while the new files may have hard
1087df930be7Sderaadt  * links to each other, you cannot determine if they have hard links to any
1088df930be7Sderaadt  * file already stored on the archive from a prior run of pax. We must assume
1089df930be7Sderaadt  * that these inode/device pairs are unique only within a SINGLE run of pax
1090df930be7Sderaadt  * (which adds a set of files to an archive). So we have to make sure the
1091df930be7Sderaadt  * inode/dev pairs we add each time are always unique. We do this by observing
1092df930be7Sderaadt  * while the inode field is very dense, the use of the dev field is fairly
1093df930be7Sderaadt  * sparse. Within each run of pax, we remap any device number of a new archive
1094df930be7Sderaadt  * member that has a device number used in a prior run and already stored in a
1095df930be7Sderaadt  * file on the archive. During the read phase of the append, we store the
1096df930be7Sderaadt  * device numbers used and mark them to not be used by any file during the
1097df930be7Sderaadt  * write phase. If during write we go to use one of those old device numbers,
1098df930be7Sderaadt  * we remap it to a new value.
1099df930be7Sderaadt  *
1100df930be7Sderaadt  * 2) Often the fields in the archive header used to store these values are
1101df930be7Sderaadt  * too small to store the entire value. The result is an inode or device value
1102df930be7Sderaadt  * which can be truncated. This really can foul up an archive. With truncation
1103df930be7Sderaadt  * we end up creating links between files that are really not links (after
1104df930be7Sderaadt  * truncation the inodes are the same value). We address that by detecting
1105df930be7Sderaadt  * truncation and forcing a remap of the device field to split truncated
1106df930be7Sderaadt  * inodes away from each other. Each truncation creates a pattern of bits that
1107df930be7Sderaadt  * are removed. We use this pattern of truncated bits to partition the inodes
1108df930be7Sderaadt  * on a single device to many different devices (each one represented by the
1109df930be7Sderaadt  * truncated bit pattern). All inodes on the same device that have the same
1110df930be7Sderaadt  * truncation pattern are mapped to the same new device. Two inodes that
1111df930be7Sderaadt  * truncate to the same value clearly will always have different truncation
1112df930be7Sderaadt  * bit patterns, so they will be split from away each other. When we spot
1113df930be7Sderaadt  * device truncation we remap the device number to a non truncated value.
1114df930be7Sderaadt  * (for more info see table.h for the data structures involved).
1115df930be7Sderaadt  */
1116df930be7Sderaadt 
11172dbd6dc5Sguenther static DEVT *chk_dev(dev_t, int);
11182dbd6dc5Sguenther 
1119df930be7Sderaadt /*
1120df930be7Sderaadt  * dev_start()
1121df930be7Sderaadt  *	create the device mapping table
1122df930be7Sderaadt  * Return:
1123df930be7Sderaadt  *	0 if successful, -1 otherwise
1124df930be7Sderaadt  */
1125df930be7Sderaadt 
1126df930be7Sderaadt int
dev_start(void)1127df930be7Sderaadt dev_start(void)
1128df930be7Sderaadt {
1129df930be7Sderaadt 	if (dtab != NULL)
1130df930be7Sderaadt 		return(0);
1131f38a3474Sguenther 	if ((dtab = calloc(D_TAB_SZ, sizeof(DEVT *))) == NULL) {
113242cf9836Stholo 		paxwarn(1, "Cannot allocate memory for device mapping table");
1133df930be7Sderaadt 		return(-1);
1134df930be7Sderaadt 	}
1135df930be7Sderaadt 	return(0);
1136df930be7Sderaadt }
1137df930be7Sderaadt 
1138df930be7Sderaadt /*
1139df930be7Sderaadt  * add_dev()
1140df930be7Sderaadt  *	add a device number to the table. this will force the device to be
1141df930be7Sderaadt  *	remapped to a new value if it be used during a write phase. This
1142df930be7Sderaadt  *	function is called during the read phase of an append to prohibit the
1143df930be7Sderaadt  *	use of any device number already in the archive.
1144df930be7Sderaadt  * Return:
1145df930be7Sderaadt  *	0 if added ok, -1 otherwise
1146df930be7Sderaadt  */
1147df930be7Sderaadt 
1148df930be7Sderaadt int
add_dev(ARCHD * arcn)1149be87792eSmillert add_dev(ARCHD *arcn)
1150df930be7Sderaadt {
1151df930be7Sderaadt 	if (chk_dev(arcn->sb.st_dev, 1) == NULL)
1152df930be7Sderaadt 		return(-1);
1153df930be7Sderaadt 	return(0);
1154df930be7Sderaadt }
1155df930be7Sderaadt 
1156df930be7Sderaadt /*
1157df930be7Sderaadt  * chk_dev()
1158df930be7Sderaadt  *	check for a device value in the device table. If not found and the add
1159df930be7Sderaadt  *	flag is set, it is added. This does NOT assign any mapping values, just
1160df930be7Sderaadt  *	adds the device number as one that need to be remapped. If this device
11614eb0b000Smillert  *	is already mapped, just return with a pointer to that entry.
1162df930be7Sderaadt  * Return:
1163df930be7Sderaadt  *	pointer to the entry for this device in the device map table. Null
1164df930be7Sderaadt  *	if the add flag is not set and the device is not in the table (it is
1165df930be7Sderaadt  *	not been seen yet). If add is set and the device cannot be added, null
1166df930be7Sderaadt  *	is returned (indicates an error).
1167df930be7Sderaadt  */
1168df930be7Sderaadt 
1169df930be7Sderaadt static DEVT *
chk_dev(dev_t dev,int add)1170df930be7Sderaadt chk_dev(dev_t dev, int add)
1171df930be7Sderaadt {
1172be87792eSmillert 	DEVT *pt;
1173be87792eSmillert 	u_int indx;
1174df930be7Sderaadt 
1175df930be7Sderaadt 	if (dtab == NULL)
1176df930be7Sderaadt 		return(NULL);
1177df930be7Sderaadt 	/*
1178df930be7Sderaadt 	 * look to see if this device is already in the table
1179df930be7Sderaadt 	 */
1180df930be7Sderaadt 	indx = ((unsigned)dev) % D_TAB_SZ;
1181df930be7Sderaadt 	if ((pt = dtab[indx]) != NULL) {
1182df930be7Sderaadt 		while ((pt != NULL) && (pt->dev != dev))
1183df930be7Sderaadt 			pt = pt->fow;
1184df930be7Sderaadt 
1185df930be7Sderaadt 		/*
1186df930be7Sderaadt 		 * found it, return a pointer to it
1187df930be7Sderaadt 		 */
1188df930be7Sderaadt 		if (pt != NULL)
1189df930be7Sderaadt 			return(pt);
1190df930be7Sderaadt 	}
1191df930be7Sderaadt 
1192df930be7Sderaadt 	/*
1193df930be7Sderaadt 	 * not in table, we add it only if told to as this may just be a check
1194df930be7Sderaadt 	 * to see if a device number is being used.
1195df930be7Sderaadt 	 */
1196df930be7Sderaadt 	if (add == 0)
1197df930be7Sderaadt 		return(NULL);
1198df930be7Sderaadt 
1199df930be7Sderaadt 	/*
1200df930be7Sderaadt 	 * allocate a node for this device and add it to the front of the hash
1201df930be7Sderaadt 	 * chain. Note we do not assign remaps values here, so the pt->list
1202df930be7Sderaadt 	 * list must be NULL.
1203df930be7Sderaadt 	 */
1204f38a3474Sguenther 	if ((pt = malloc(sizeof(DEVT))) == NULL) {
120542cf9836Stholo 		paxwarn(1, "Device map table out of memory");
1206df930be7Sderaadt 		return(NULL);
1207df930be7Sderaadt 	}
1208df930be7Sderaadt 	pt->dev = dev;
1209df930be7Sderaadt 	pt->list = NULL;
1210df930be7Sderaadt 	pt->fow = dtab[indx];
1211df930be7Sderaadt 	dtab[indx] = pt;
1212df930be7Sderaadt 	return(pt);
1213df930be7Sderaadt }
1214df930be7Sderaadt /*
1215df930be7Sderaadt  * map_dev()
1216df930be7Sderaadt  *	given an inode and device storage mask (the mask has a 1 for each bit
1217df930be7Sderaadt  *	the archive format is able to store in a header), we check for inode
1218df930be7Sderaadt  *	and device truncation and remap the device as required. Device mapping
1219df930be7Sderaadt  *	can also occur when during the read phase of append a device number was
1220df930be7Sderaadt  *	seen (and was marked as do not use during the write phase). WE ASSUME
1221df930be7Sderaadt  *	that unsigned longs are the same size or bigger than the fields used
1222df930be7Sderaadt  *	for ino_t and dev_t. If not the types will have to be changed.
1223df930be7Sderaadt  * Return:
1224df930be7Sderaadt  *	0 if all ok, -1 otherwise.
1225df930be7Sderaadt  */
1226df930be7Sderaadt 
1227df930be7Sderaadt int
map_dev(ARCHD * arcn,u_long dev_mask,u_long ino_mask)1228be87792eSmillert map_dev(ARCHD *arcn, u_long dev_mask, u_long ino_mask)
1229df930be7Sderaadt {
1230be87792eSmillert 	DEVT *pt;
1231be87792eSmillert 	DLIST *dpt;
1232df930be7Sderaadt 	static dev_t lastdev = 0;	/* next device number to try */
1233df930be7Sderaadt 	int trc_ino = 0;
1234df930be7Sderaadt 	int trc_dev = 0;
1235df930be7Sderaadt 	ino_t trunc_bits = 0;
1236df930be7Sderaadt 	ino_t nino;
1237df930be7Sderaadt 
1238df930be7Sderaadt 	if (dtab == NULL)
1239df930be7Sderaadt 		return(0);
1240df930be7Sderaadt 	/*
1241df930be7Sderaadt 	 * check for device and inode truncation, and extract the truncated
1242df930be7Sderaadt 	 * bit pattern.
1243df930be7Sderaadt 	 */
1244df930be7Sderaadt 	if ((arcn->sb.st_dev & (dev_t)dev_mask) != arcn->sb.st_dev)
1245df930be7Sderaadt 		++trc_dev;
1246df930be7Sderaadt 	if ((nino = arcn->sb.st_ino & (ino_t)ino_mask) != arcn->sb.st_ino) {
1247df930be7Sderaadt 		++trc_ino;
1248df930be7Sderaadt 		trunc_bits = arcn->sb.st_ino & (ino_t)(~ino_mask);
1249df930be7Sderaadt 	}
1250df930be7Sderaadt 
1251df930be7Sderaadt 	/*
1252df930be7Sderaadt 	 * see if this device is already being mapped, look up the device
1253df930be7Sderaadt 	 * then find the truncation bit pattern which applies
1254df930be7Sderaadt 	 */
1255df930be7Sderaadt 	if ((pt = chk_dev(arcn->sb.st_dev, 0)) != NULL) {
1256df930be7Sderaadt 		/*
1257df930be7Sderaadt 		 * this device is already marked to be remapped
1258df930be7Sderaadt 		 */
1259df930be7Sderaadt 		for (dpt = pt->list; dpt != NULL; dpt = dpt->fow)
1260df930be7Sderaadt 			if (dpt->trunc_bits == trunc_bits)
1261df930be7Sderaadt 				break;
1262df930be7Sderaadt 
1263df930be7Sderaadt 		if (dpt != NULL) {
1264df930be7Sderaadt 			/*
1265df930be7Sderaadt 			 * we are being remapped for this device and pattern
1266df930be7Sderaadt 			 * change the device number to be stored and return
1267df930be7Sderaadt 			 */
1268df930be7Sderaadt 			arcn->sb.st_dev = dpt->dev;
1269df930be7Sderaadt 			arcn->sb.st_ino = nino;
1270df930be7Sderaadt 			return(0);
1271df930be7Sderaadt 		}
1272df930be7Sderaadt 	} else {
1273df930be7Sderaadt 		/*
1274df930be7Sderaadt 		 * this device is not being remapped YET. if we do not have any
1275df930be7Sderaadt 		 * form of truncation, we do not need a remap
1276df930be7Sderaadt 		 */
1277df930be7Sderaadt 		if (!trc_ino && !trc_dev)
1278df930be7Sderaadt 			return(0);
1279df930be7Sderaadt 
1280df930be7Sderaadt 		/*
1281df930be7Sderaadt 		 * we have truncation, have to add this as a device to remap
1282df930be7Sderaadt 		 */
1283df930be7Sderaadt 		if ((pt = chk_dev(arcn->sb.st_dev, 1)) == NULL)
1284df930be7Sderaadt 			goto bad;
1285df930be7Sderaadt 
1286df930be7Sderaadt 		/*
1287df930be7Sderaadt 		 * if we just have a truncated inode, we have to make sure that
1288df930be7Sderaadt 		 * all future inodes that do not truncate (they have the
1289df930be7Sderaadt 		 * truncation pattern of all 0's) continue to map to the same
1290df930be7Sderaadt 		 * device number. We probably have already written inodes with
1291df930be7Sderaadt 		 * this device number to the archive with the truncation
1292df930be7Sderaadt 		 * pattern of all 0's. So we add the mapping for all 0's to the
1293df930be7Sderaadt 		 * same device number.
1294df930be7Sderaadt 		 */
1295df930be7Sderaadt 		if (!trc_dev && (trunc_bits != 0)) {
1296f38a3474Sguenther 			if ((dpt = malloc(sizeof(DLIST))) == NULL)
1297df930be7Sderaadt 				goto bad;
1298df930be7Sderaadt 			dpt->trunc_bits = 0;
1299df930be7Sderaadt 			dpt->dev = arcn->sb.st_dev;
1300df930be7Sderaadt 			dpt->fow = pt->list;
1301df930be7Sderaadt 			pt->list = dpt;
1302df930be7Sderaadt 		}
1303df930be7Sderaadt 	}
1304df930be7Sderaadt 
1305df930be7Sderaadt 	/*
1306df930be7Sderaadt 	 * look for a device number not being used. We must watch for wrap
1307df930be7Sderaadt 	 * around on lastdev (so we do not get stuck looking forever!)
1308df930be7Sderaadt 	 */
1309df930be7Sderaadt 	while (++lastdev > 0) {
1310df930be7Sderaadt 		if (chk_dev(lastdev, 0) != NULL)
1311df930be7Sderaadt 			continue;
1312df930be7Sderaadt 		/*
1313df930be7Sderaadt 		 * found an unused value. If we have reached truncation point
1314df930be7Sderaadt 		 * for this format we are hosed, so we give up. Otherwise we
1315df930be7Sderaadt 		 * mark it as being used.
1316df930be7Sderaadt 		 */
1317df930be7Sderaadt 		if (((lastdev & ((dev_t)dev_mask)) != lastdev) ||
1318df930be7Sderaadt 		    (chk_dev(lastdev, 1) == NULL))
1319df930be7Sderaadt 			goto bad;
1320df930be7Sderaadt 		break;
1321df930be7Sderaadt 	}
1322df930be7Sderaadt 
1323f38a3474Sguenther 	if ((lastdev <= 0) || ((dpt = malloc(sizeof(DLIST))) == NULL))
1324df930be7Sderaadt 		goto bad;
1325df930be7Sderaadt 
1326df930be7Sderaadt 	/*
1327df930be7Sderaadt 	 * got a new device number, store it under this truncation pattern.
1328df930be7Sderaadt 	 * change the device number this file is being stored with.
1329df930be7Sderaadt 	 */
1330df930be7Sderaadt 	dpt->trunc_bits = trunc_bits;
1331df930be7Sderaadt 	dpt->dev = lastdev;
1332df930be7Sderaadt 	dpt->fow = pt->list;
1333df930be7Sderaadt 	pt->list = dpt;
1334df930be7Sderaadt 	arcn->sb.st_dev = lastdev;
1335df930be7Sderaadt 	arcn->sb.st_ino = nino;
1336df930be7Sderaadt 	return(0);
1337df930be7Sderaadt 
1338df930be7Sderaadt     bad:
133942cf9836Stholo 	paxwarn(1, "Unable to fix truncated inode/device field when storing %s",
1340df930be7Sderaadt 	    arcn->name);
134142cf9836Stholo 	paxwarn(0, "Archive may create improper hard links when extracted");
1342df930be7Sderaadt 	return(0);
1343df930be7Sderaadt }
13442dbd6dc5Sguenther #endif /* NOCPIO */
1345df930be7Sderaadt 
1346df930be7Sderaadt /*
1347df930be7Sderaadt  * directory access/mod time reset table routines (for directories READ by pax)
1348df930be7Sderaadt  *
1349f0f82a05Sjmc  * The pax -t flag requires that access times of archive files be the same
1350df930be7Sderaadt  * before being read by pax. For regular files, access time is restored after
1351df930be7Sderaadt  * the file has been copied. This database provides the same functionality for
1352df930be7Sderaadt  * directories read during file tree traversal. Restoring directory access time
1353df930be7Sderaadt  * is more complex than files since directories may be read several times until
1354df930be7Sderaadt  * all the descendants in their subtree are visited by fts. Directory access
1355df930be7Sderaadt  * and modification times are stored during the fts pre-order visit (done
1356f0f82a05Sjmc  * before any descendants in the subtree are visited) and restored after the
1357df930be7Sderaadt  * fts post-order visit (after all the descendants have been visited). In the
1358df930be7Sderaadt  * case of premature exit from a subtree (like from the effects of -n), any
1359df930be7Sderaadt  * directory entries left in this database are reset during final cleanup
1360df930be7Sderaadt  * operations of pax. Entries are hashed by inode number for fast lookup.
1361df930be7Sderaadt  */
1362df930be7Sderaadt 
1363df930be7Sderaadt /*
1364df930be7Sderaadt  * atdir_start()
1365df930be7Sderaadt  *	create the directory access time database for directories READ by pax.
1366df930be7Sderaadt  * Return:
1367df930be7Sderaadt  *	0 is created ok, -1 otherwise.
1368df930be7Sderaadt  */
1369df930be7Sderaadt 
1370df930be7Sderaadt int
atdir_start(void)1371df930be7Sderaadt atdir_start(void)
1372df930be7Sderaadt {
1373df930be7Sderaadt 	if (atab != NULL)
1374df930be7Sderaadt 		return(0);
1375f38a3474Sguenther 	if ((atab = calloc(A_TAB_SZ, sizeof(ATDIR *))) == NULL) {
137642cf9836Stholo 		paxwarn(1,"Cannot allocate space for directory access time table");
1377df930be7Sderaadt 		return(-1);
1378df930be7Sderaadt 	}
1379df930be7Sderaadt 	return(0);
1380df930be7Sderaadt }
1381df930be7Sderaadt 
1382df930be7Sderaadt 
1383df930be7Sderaadt /*
1384df930be7Sderaadt  * atdir_end()
1385df930be7Sderaadt  *	walk through the directory access time table and reset the access time
1386df930be7Sderaadt  *	of any directory who still has an entry left in the database. These
1387df930be7Sderaadt  *	entries are for directories READ by pax
1388df930be7Sderaadt  */
1389df930be7Sderaadt 
1390df930be7Sderaadt void
atdir_end(void)1391df930be7Sderaadt atdir_end(void)
1392df930be7Sderaadt {
1393be87792eSmillert 	ATDIR *pt;
1394be87792eSmillert 	int i;
1395df930be7Sderaadt 
1396df930be7Sderaadt 	if (atab == NULL)
1397df930be7Sderaadt 		return;
1398df930be7Sderaadt 	/*
1399df930be7Sderaadt 	 * for each non-empty hash table entry reset all the directories
1400df930be7Sderaadt 	 * chained there.
1401df930be7Sderaadt 	 */
1402df930be7Sderaadt 	for (i = 0; i < A_TAB_SZ; ++i) {
1403df930be7Sderaadt 		if ((pt = atab[i]) == NULL)
1404df930be7Sderaadt 			continue;
1405df930be7Sderaadt 		/*
1406df930be7Sderaadt 		 * remember to force the times, set_ftime() looks at pmtime
1407df930be7Sderaadt 		 * and patime, which only applies to things CREATED by pax,
1408df930be7Sderaadt 		 * not read by pax. Read time reset is controlled by -t.
1409df930be7Sderaadt 		 */
1410df930be7Sderaadt 		for (; pt != NULL; pt = pt->fow)
14112dbd6dc5Sguenther 			set_attr(&pt->ft, 1, 0, 0, 0);
1412df930be7Sderaadt 	}
1413df930be7Sderaadt }
1414df930be7Sderaadt 
1415df930be7Sderaadt /*
1416df930be7Sderaadt  * add_atdir()
1417df930be7Sderaadt  *	add a directory to the directory access time table. Table is hashed
1418df930be7Sderaadt  *	and chained by inode number. This is for directories READ by pax
1419df930be7Sderaadt  */
1420df930be7Sderaadt 
1421df930be7Sderaadt void
add_atdir(char * fname,dev_t dev,ino_t ino,const struct timespec * mtimp,const struct timespec * atimp)14228b72bc25Sguenther add_atdir(char *fname, dev_t dev, ino_t ino, const struct timespec *mtimp,
14238b72bc25Sguenther     const struct timespec *atimp)
1424df930be7Sderaadt {
1425be87792eSmillert 	ATDIR *pt;
14260a8bc8ffSguenther 	sigset_t allsigs, savedsigs;
1427be87792eSmillert 	u_int indx;
1428df930be7Sderaadt 
1429df930be7Sderaadt 	if (atab == NULL)
1430df930be7Sderaadt 		return;
1431df930be7Sderaadt 
1432df930be7Sderaadt 	/*
1433df930be7Sderaadt 	 * make sure this directory is not already in the table, if so just
1434df930be7Sderaadt 	 * return (the older entry always has the correct time). The only
1435df930be7Sderaadt 	 * way this will happen is when the same subtree can be traversed by
1436df930be7Sderaadt 	 * different args to pax and the -n option is aborting fts out of a
1437f0f82a05Sjmc 	 * subtree before all the post-order visits have been made.
1438df930be7Sderaadt 	 */
1439df930be7Sderaadt 	indx = ((unsigned)ino) % A_TAB_SZ;
1440df930be7Sderaadt 	if ((pt = atab[indx]) != NULL) {
1441df930be7Sderaadt 		while (pt != NULL) {
14422dbd6dc5Sguenther 			if ((pt->ft.ft_ino == ino) && (pt->ft.ft_dev == dev))
1443df930be7Sderaadt 				break;
1444df930be7Sderaadt 			pt = pt->fow;
1445df930be7Sderaadt 		}
1446df930be7Sderaadt 
1447df930be7Sderaadt 		/*
1448df930be7Sderaadt 		 * oops, already there. Leave it alone.
1449df930be7Sderaadt 		 */
1450df930be7Sderaadt 		if (pt != NULL)
1451df930be7Sderaadt 			return;
1452df930be7Sderaadt 	}
1453df930be7Sderaadt 
1454df930be7Sderaadt 	/*
1455df930be7Sderaadt 	 * add it to the front of the hash chain
1456df930be7Sderaadt 	 */
14570a8bc8ffSguenther 	sigfillset(&allsigs);
14580a8bc8ffSguenther 	sigprocmask(SIG_BLOCK, &allsigs, &savedsigs);
14590a8bc8ffSguenther 	if ((pt = malloc(sizeof *pt)) != NULL) {
14602dbd6dc5Sguenther 		if ((pt->ft.ft_name = strdup(fname)) != NULL) {
14612dbd6dc5Sguenther 			pt->ft.ft_dev = dev;
14622dbd6dc5Sguenther 			pt->ft.ft_ino = ino;
14638b72bc25Sguenther 			pt->ft.ft_mtim = *mtimp;
14648b72bc25Sguenther 			pt->ft.ft_atim = *atimp;
1465df930be7Sderaadt 			pt->fow = atab[indx];
1466df930be7Sderaadt 			atab[indx] = pt;
14670a8bc8ffSguenther 			sigprocmask(SIG_SETMASK, &savedsigs, NULL);
1468df930be7Sderaadt 			return;
1469df930be7Sderaadt 		}
1470f38a3474Sguenther 		free(pt);
1471df930be7Sderaadt 	}
1472df930be7Sderaadt 
14730a8bc8ffSguenther 	sigprocmask(SIG_SETMASK, &savedsigs, NULL);
147442cf9836Stholo 	paxwarn(1, "Directory access time reset table ran out of memory");
1475df930be7Sderaadt }
1476df930be7Sderaadt 
1477df930be7Sderaadt /*
1478df930be7Sderaadt  * get_atdir()
1479df930be7Sderaadt  *	look up a directory by inode and device number to obtain the access
1480df930be7Sderaadt  *	and modification time you want to set to. If found, the modification
1481df930be7Sderaadt  *	and access time parameters are set and the entry is removed from the
1482df930be7Sderaadt  *	table (as it is no longer needed). These are for directories READ by
1483df930be7Sderaadt  *	pax
1484df930be7Sderaadt  * Return:
1485df930be7Sderaadt  *	0 if found, -1 if not found.
1486df930be7Sderaadt  */
1487df930be7Sderaadt 
1488df930be7Sderaadt int
do_atdir(const char * name,dev_t dev,ino_t ino)14892dbd6dc5Sguenther do_atdir(const char *name, dev_t dev, ino_t ino)
1490df930be7Sderaadt {
1491be87792eSmillert 	ATDIR *pt;
1492be87792eSmillert 	ATDIR **ppt;
14930a8bc8ffSguenther 	sigset_t allsigs, savedsigs;
1494be87792eSmillert 	u_int indx;
1495df930be7Sderaadt 
1496df930be7Sderaadt 	if (atab == NULL)
1497df930be7Sderaadt 		return(-1);
1498df930be7Sderaadt 	/*
1499df930be7Sderaadt 	 * hash by inode and search the chain for an inode and device match
1500df930be7Sderaadt 	 */
1501df930be7Sderaadt 	indx = ((unsigned)ino) % A_TAB_SZ;
1502df930be7Sderaadt 	if ((pt = atab[indx]) == NULL)
1503df930be7Sderaadt 		return(-1);
1504df930be7Sderaadt 
1505df930be7Sderaadt 	ppt = &(atab[indx]);
1506df930be7Sderaadt 	while (pt != NULL) {
15072dbd6dc5Sguenther 		if ((pt->ft.ft_ino == ino) && (pt->ft.ft_dev == dev))
1508df930be7Sderaadt 			break;
1509df930be7Sderaadt 		/*
1510df930be7Sderaadt 		 * no match, go to next one
1511df930be7Sderaadt 		 */
1512df930be7Sderaadt 		ppt = &(pt->fow);
1513df930be7Sderaadt 		pt = pt->fow;
1514df930be7Sderaadt 	}
1515df930be7Sderaadt 
1516df930be7Sderaadt 	/*
1517df930be7Sderaadt 	 * return if we did not find it.
1518df930be7Sderaadt 	 */
15192dbd6dc5Sguenther 	if (pt == NULL || pt->ft.ft_name == NULL ||
15202dbd6dc5Sguenther 	    strcmp(name, pt->ft.ft_name) == 0)
1521df930be7Sderaadt 		return(-1);
1522df930be7Sderaadt 
1523df930be7Sderaadt 	/*
15242dbd6dc5Sguenther 	 * found it. set the times and remove the entry from the table.
1525df930be7Sderaadt 	 */
15262dbd6dc5Sguenther 	set_attr(&pt->ft, 1, 0, 0, 0);
15270a8bc8ffSguenther 	sigfillset(&allsigs);
15280a8bc8ffSguenther 	sigprocmask(SIG_BLOCK, &allsigs, &savedsigs);
1529df930be7Sderaadt 	*ppt = pt->fow;
15300a8bc8ffSguenther 	sigprocmask(SIG_SETMASK, &savedsigs, NULL);
15312dbd6dc5Sguenther 	free(pt->ft.ft_name);
1532f38a3474Sguenther 	free(pt);
1533df930be7Sderaadt 	return(0);
1534df930be7Sderaadt }
1535df930be7Sderaadt 
1536df930be7Sderaadt /*
1537df930be7Sderaadt  * directory access mode and time storage routines (for directories CREATED
1538df930be7Sderaadt  * by pax).
1539df930be7Sderaadt  *
1540df930be7Sderaadt  * Pax requires that extracted directories, by default, have their access/mod
1541df930be7Sderaadt  * times and permissions set to the values specified in the archive. During the
1542df930be7Sderaadt  * actions of extracting (and creating the destination subtree during -rw copy)
1543df930be7Sderaadt  * directories extracted may be modified after being created. Even worse is
1544df930be7Sderaadt  * that these directories may have been created with file permissions which
1545df930be7Sderaadt  * prohibits any descendants of these directories from being extracted. When
1546df930be7Sderaadt  * directories are created by pax, access rights may be added to permit the
1547df930be7Sderaadt  * creation of files in their subtree. Every time pax creates a directory, the
1548df930be7Sderaadt  * times and file permissions specified by the archive are stored. After all
1549df930be7Sderaadt  * files have been extracted (or copied), these directories have their times
1550df930be7Sderaadt  * and file modes reset to the stored values. The directory info is restored in
15512dbd6dc5Sguenther  * reverse order as entries were added from root to leaf: to restore atime
15522dbd6dc5Sguenther  * properly, we must go backwards.
1553df930be7Sderaadt  */
1554df930be7Sderaadt 
1555df930be7Sderaadt /*
1556df930be7Sderaadt  * dir_start()
1557df930be7Sderaadt  *	set up the directory time and file mode storage for directories CREATED
1558df930be7Sderaadt  *	by pax.
1559df930be7Sderaadt  * Return:
1560df930be7Sderaadt  *	0 if ok, -1 otherwise
1561df930be7Sderaadt  */
1562df930be7Sderaadt 
1563df930be7Sderaadt int
dir_start(void)1564df930be7Sderaadt dir_start(void)
1565df930be7Sderaadt {
15669116436cSotto 	if (dirp != NULL)
1567df930be7Sderaadt 		return(0);
1568df930be7Sderaadt 
15699116436cSotto 	dirsize = DIRP_SIZE;
1570f38a3474Sguenther 	if ((dirp = reallocarray(NULL, dirsize, sizeof(DIRDATA))) == NULL) {
15719116436cSotto 		paxwarn(1, "Unable to allocate memory for directory times");
1572df930be7Sderaadt 		return(-1);
1573df930be7Sderaadt 	}
15749116436cSotto 	return(0);
15759116436cSotto }
1576df930be7Sderaadt 
1577df930be7Sderaadt /*
1578df930be7Sderaadt  * add_dir()
1579df930be7Sderaadt  *	add the mode and times for a newly CREATED directory
1580df930be7Sderaadt  *	name is name of the directory, psb the stat buffer with the data in it,
1581df930be7Sderaadt  *	frc_mode is a flag that says whether to force the setting of the mode
1582df930be7Sderaadt  *	(ignoring the user set values for preserving file mode). Frc_mode is
1583df930be7Sderaadt  *	for the case where we created a file and found that the resulting
1584df930be7Sderaadt  *	directory was not writeable and the user asked for file modes to NOT
1585df930be7Sderaadt  *	be preserved. (we have to preserve what was created by default, so we
1586df930be7Sderaadt  *	have to force the setting at the end. this is stated explicitly in the
1587df930be7Sderaadt  *	pax spec)
1588df930be7Sderaadt  */
1589df930be7Sderaadt 
1590df930be7Sderaadt void
add_dir(char * name,struct stat * psb,int frc_mode)15919116436cSotto add_dir(char *name, struct stat *psb, int frc_mode)
1592df930be7Sderaadt {
15939116436cSotto 	DIRDATA *dblk;
15940a8bc8ffSguenther 	sigset_t allsigs, savedsigs;
159508961bb1Sguenther 	char realname[PATH_MAX], *rp;
1596df930be7Sderaadt 
15979116436cSotto 	if (dirp == NULL)
1598df930be7Sderaadt 		return;
1599df930be7Sderaadt 
1600a7a4d07aSotto 	if (havechd && *name != '/') {
1601a7a4d07aSotto 		if ((rp = realpath(name, realname)) == NULL) {
1602a7a4d07aSotto 			paxwarn(1, "Cannot canonicalize %s", name);
1603a7a4d07aSotto 			return;
1604a7a4d07aSotto 		}
1605a7a4d07aSotto 		name = rp;
1606a7a4d07aSotto 	}
16079116436cSotto 	if (dircnt == dirsize) {
16083a8f00daSderaadt 		dblk = reallocarray(dirp, dirsize * 2, sizeof(DIRDATA));
16099116436cSotto 		if (dblk == NULL) {
16109116436cSotto 			paxwarn(1, "Unable to store mode and times for created"
16119116436cSotto 			    " directory: %s", name);
1612df930be7Sderaadt 			return;
1613df930be7Sderaadt 		}
16140a8bc8ffSguenther 		sigprocmask(SIG_BLOCK, &allsigs, &savedsigs);
16159116436cSotto 		dirp = dblk;
16169116436cSotto 		dirsize *= 2;
16170a8bc8ffSguenther 		sigprocmask(SIG_SETMASK, &savedsigs, NULL);
16189116436cSotto 	}
16199116436cSotto 	dblk = &dirp[dircnt];
16202dbd6dc5Sguenther 	if ((dblk->ft.ft_name = strdup(name)) == NULL) {
16219116436cSotto 		paxwarn(1, "Unable to store mode and times for created"
16229116436cSotto 		    " directory: %s", name);
16239116436cSotto 		return;
16249116436cSotto 	}
16258b72bc25Sguenther 	dblk->ft.ft_mtim = psb->st_mtim;
16268b72bc25Sguenther 	dblk->ft.ft_atim = psb->st_atim;
16272dbd6dc5Sguenther 	dblk->ft.ft_ino = psb->st_ino;
16282dbd6dc5Sguenther 	dblk->ft.ft_dev = psb->st_dev;
16292dbd6dc5Sguenther 	dblk->mode = psb->st_mode & ABITS;
16309116436cSotto 	dblk->frc_mode = frc_mode;
16310a8bc8ffSguenther 	sigprocmask(SIG_BLOCK, &allsigs, &savedsigs);
1632df930be7Sderaadt 	++dircnt;
16330a8bc8ffSguenther 	sigprocmask(SIG_SETMASK, &savedsigs, NULL);
1634df930be7Sderaadt }
1635df930be7Sderaadt 
1636df930be7Sderaadt /*
16372dbd6dc5Sguenther  * delete_dir()
16382dbd6dc5Sguenther  *	When we rmdir a directory, we may want to make sure we don't
16392dbd6dc5Sguenther  *	later warn about being unable to set its mode and times.
16402dbd6dc5Sguenther  */
16412dbd6dc5Sguenther 
16422dbd6dc5Sguenther void
delete_dir(dev_t dev,ino_t ino)16432dbd6dc5Sguenther delete_dir(dev_t dev, ino_t ino)
16442dbd6dc5Sguenther {
16452dbd6dc5Sguenther 	DIRDATA *dblk;
16462dbd6dc5Sguenther 	char *name;
16472dbd6dc5Sguenther 	size_t i;
16482dbd6dc5Sguenther 
16492dbd6dc5Sguenther 	if (dirp == NULL)
16502dbd6dc5Sguenther 		return;
16512dbd6dc5Sguenther 	for (i = 0; i < dircnt; i++) {
16522dbd6dc5Sguenther 		dblk = &dirp[i];
16532dbd6dc5Sguenther 
16542dbd6dc5Sguenther 		if (dblk->ft.ft_name == NULL)
16552dbd6dc5Sguenther 			continue;
16562dbd6dc5Sguenther 		if (dblk->ft.ft_dev == dev && dblk->ft.ft_ino == ino) {
16572dbd6dc5Sguenther 			name = dblk->ft.ft_name;
16582dbd6dc5Sguenther 			dblk->ft.ft_name = NULL;
16592dbd6dc5Sguenther 			free(name);
16602dbd6dc5Sguenther 			break;
16612dbd6dc5Sguenther 		}
16622dbd6dc5Sguenther 	}
16632dbd6dc5Sguenther }
16642dbd6dc5Sguenther 
16652dbd6dc5Sguenther /*
16660a8bc8ffSguenther  * proc_dir(int in_sig)
1667df930be7Sderaadt  *	process all file modes and times stored for directories CREATED
16680a8bc8ffSguenther  *	by pax.  If in_sig is set, we're in a signal handler and can't
16690a8bc8ffSguenther  *	free stuff.
1670df930be7Sderaadt  */
1671df930be7Sderaadt 
1672df930be7Sderaadt void
proc_dir(int in_sig)16730a8bc8ffSguenther proc_dir(int in_sig)
1674df930be7Sderaadt {
16759116436cSotto 	DIRDATA *dblk;
167624c549d1Sguenther 	size_t cnt;
1677df930be7Sderaadt 
16789116436cSotto 	if (dirp == NULL)
1679df930be7Sderaadt 		return;
1680df930be7Sderaadt 	/*
1681df930be7Sderaadt 	 * read backwards through the file and process each directory
1682df930be7Sderaadt 	 */
16839116436cSotto 	cnt = dircnt;
168424c549d1Sguenther 	while (cnt-- > 0) {
16852dbd6dc5Sguenther 		dblk = &dirp[cnt];
16862dbd6dc5Sguenther 		/*
16872dbd6dc5Sguenther 		 * If we remove a directory we created, we replace the
16882dbd6dc5Sguenther 		 * ft_name with NULL.  Ignore those.
16892dbd6dc5Sguenther 		 */
16902dbd6dc5Sguenther 		if (dblk->ft.ft_name == NULL)
16912dbd6dc5Sguenther 			continue;
16922dbd6dc5Sguenther 
1693df930be7Sderaadt 		/*
1694df930be7Sderaadt 		 * frc_mode set, make sure we set the file modes even if
1695df930be7Sderaadt 		 * the user didn't ask for it (see file_subs.c for more info)
1696df930be7Sderaadt 		 */
16972dbd6dc5Sguenther 		set_attr(&dblk->ft, 0, dblk->mode, pmode || dblk->frc_mode,
16982dbd6dc5Sguenther 		    in_sig);
16990a8bc8ffSguenther 		if (!in_sig)
17002dbd6dc5Sguenther 			free(dblk->ft.ft_name);
1701df930be7Sderaadt 	}
1702df930be7Sderaadt 
17030a8bc8ffSguenther 	if (!in_sig)
17049116436cSotto 		free(dirp);
17059116436cSotto 	dirp = NULL;
17069116436cSotto 	dircnt = 0;
1707df930be7Sderaadt }
1708df930be7Sderaadt 
1709df930be7Sderaadt /*
1710df930be7Sderaadt  * database independent routines
1711df930be7Sderaadt  */
1712df930be7Sderaadt 
1713df930be7Sderaadt /*
1714df930be7Sderaadt  * st_hash()
1715df930be7Sderaadt  *	hashes filenames to a u_int for hashing into a table. Looks at the tail
1716df930be7Sderaadt  *	end of file, as this provides far better distribution than any other
1717df930be7Sderaadt  *	part of the name. For performance reasons we only care about the last
1718df930be7Sderaadt  *	MAXKEYLEN chars (should be at LEAST large enough to pick off the file
1719df930be7Sderaadt  *	name). Was tested on 500,000 name file tree traversal from the root
1720df930be7Sderaadt  *	and gave almost a perfectly uniform distribution of keys when used with
1721df930be7Sderaadt  *	prime sized tables (MAXKEYLEN was 128 in test). Hashes (sizeof int)
1722df930be7Sderaadt  *	chars at a time and pads with 0 for last addition.
1723df930be7Sderaadt  * Return:
1724df930be7Sderaadt  *	the hash value of the string MOD (%) the table size.
1725df930be7Sderaadt  */
1726df930be7Sderaadt 
1727*c01bd743Sespie static u_int
st_hash(const char * name,int len,int tabsz)17285dbb3ba1Sguenther st_hash(const char *name, int len, int tabsz)
1729df930be7Sderaadt {
17305dbb3ba1Sguenther 	const char *pt;
1731be87792eSmillert 	char *dest;
17325dbb3ba1Sguenther 	const char *end;
1733be87792eSmillert 	int i;
1734be87792eSmillert 	u_int key = 0;
1735be87792eSmillert 	int steps;
1736be87792eSmillert 	int res;
1737df930be7Sderaadt 	u_int val;
1738df930be7Sderaadt 
1739df930be7Sderaadt 	/*
1740df930be7Sderaadt 	 * only look at the tail up to MAXKEYLEN, we do not need to waste
1741df930be7Sderaadt 	 * time here (remember these are pathnames, the tail is what will
1742df930be7Sderaadt 	 * spread out the keys)
1743df930be7Sderaadt 	 */
1744df930be7Sderaadt 	if (len > MAXKEYLEN) {
1745df930be7Sderaadt 		pt = &(name[len - MAXKEYLEN]);
1746df930be7Sderaadt 		len = MAXKEYLEN;
1747df930be7Sderaadt 	} else
1748df930be7Sderaadt 		pt = name;
1749df930be7Sderaadt 
1750df930be7Sderaadt 	/*
1751df930be7Sderaadt 	 * calculate the number of u_int size steps in the string and if
1752df930be7Sderaadt 	 * there is a runt to deal with
1753df930be7Sderaadt 	 */
1754df930be7Sderaadt 	steps = len/sizeof(u_int);
1755df930be7Sderaadt 	res = len % sizeof(u_int);
1756df930be7Sderaadt 
1757df930be7Sderaadt 	/*
1758df930be7Sderaadt 	 * add up the value of the string in unsigned integer sized pieces
1759df930be7Sderaadt 	 * too bad we cannot have unsigned int aligned strings, then we
1760df930be7Sderaadt 	 * could avoid the expensive copy.
1761df930be7Sderaadt 	 */
1762df930be7Sderaadt 	for (i = 0; i < steps; ++i) {
1763df930be7Sderaadt 		end = pt + sizeof(u_int);
1764df930be7Sderaadt 		dest = (char *)&val;
1765df930be7Sderaadt 		while (pt < end)
1766df930be7Sderaadt 			*dest++ = *pt++;
1767df930be7Sderaadt 		key += val;
1768df930be7Sderaadt 	}
1769df930be7Sderaadt 
1770df930be7Sderaadt 	/*
1771df930be7Sderaadt 	 * add in the runt padded with zero to the right
1772df930be7Sderaadt 	 */
1773df930be7Sderaadt 	if (res) {
1774df930be7Sderaadt 		val = 0;
1775df930be7Sderaadt 		end = pt + res;
1776df930be7Sderaadt 		dest = (char *)&val;
1777df930be7Sderaadt 		while (pt < end)
1778df930be7Sderaadt 			*dest++ = *pt++;
1779df930be7Sderaadt 		key += val;
1780df930be7Sderaadt 	}
1781df930be7Sderaadt 
1782df930be7Sderaadt 	/*
1783df930be7Sderaadt 	 * return the result mod the table size
1784df930be7Sderaadt 	 */
1785df930be7Sderaadt 	return(key % tabsz);
1786df930be7Sderaadt }
1787