xref: /minix3/bin/pax/tables.c (revision 84d9c625bfea59e274550651111ae9edfdc40fbd)
1*84d9c625SLionel Sambuc /*	$NetBSD: tables.c,v 1.31 2013/10/18 19:53:34 christos Exp $	*/
285fee539SLionel Sambuc 
385fee539SLionel Sambuc /*-
485fee539SLionel Sambuc  * Copyright (c) 1992 Keith Muller.
585fee539SLionel Sambuc  * Copyright (c) 1992, 1993
685fee539SLionel Sambuc  *	The Regents of the University of California.  All rights reserved.
785fee539SLionel Sambuc  *
885fee539SLionel Sambuc  * This code is derived from software contributed to Berkeley by
985fee539SLionel Sambuc  * Keith Muller of the University of California, San Diego.
1085fee539SLionel Sambuc  *
1185fee539SLionel Sambuc  * Redistribution and use in source and binary forms, with or without
1285fee539SLionel Sambuc  * modification, are permitted provided that the following conditions
1385fee539SLionel Sambuc  * are met:
1485fee539SLionel Sambuc  * 1. Redistributions of source code must retain the above copyright
1585fee539SLionel Sambuc  *    notice, this list of conditions and the following disclaimer.
1685fee539SLionel Sambuc  * 2. Redistributions in binary form must reproduce the above copyright
1785fee539SLionel Sambuc  *    notice, this list of conditions and the following disclaimer in the
1885fee539SLionel Sambuc  *    documentation and/or other materials provided with the distribution.
1985fee539SLionel Sambuc  * 3. Neither the name of the University nor the names of its contributors
2085fee539SLionel Sambuc  *    may be used to endorse or promote products derived from this software
2185fee539SLionel Sambuc  *    without specific prior written permission.
2285fee539SLionel Sambuc  *
2385fee539SLionel Sambuc  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2485fee539SLionel Sambuc  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2585fee539SLionel Sambuc  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2685fee539SLionel Sambuc  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2785fee539SLionel Sambuc  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2885fee539SLionel Sambuc  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2985fee539SLionel Sambuc  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3085fee539SLionel Sambuc  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3185fee539SLionel Sambuc  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3285fee539SLionel Sambuc  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3385fee539SLionel Sambuc  * SUCH DAMAGE.
3485fee539SLionel Sambuc  */
3585fee539SLionel Sambuc 
3685fee539SLionel Sambuc #if HAVE_NBTOOL_CONFIG_H
3785fee539SLionel Sambuc #include "nbtool_config.h"
3885fee539SLionel Sambuc #endif
3985fee539SLionel Sambuc 
4085fee539SLionel Sambuc #include <sys/cdefs.h>
4185fee539SLionel Sambuc #if !defined(lint)
4285fee539SLionel Sambuc #if 0
4385fee539SLionel Sambuc static char sccsid[] = "@(#)tables.c	8.1 (Berkeley) 5/31/93";
4485fee539SLionel Sambuc #else
45*84d9c625SLionel Sambuc __RCSID("$NetBSD: tables.c,v 1.31 2013/10/18 19:53:34 christos Exp $");
4685fee539SLionel Sambuc #endif
4785fee539SLionel Sambuc #endif /* not lint */
4885fee539SLionel Sambuc 
4985fee539SLionel Sambuc #include <sys/types.h>
5085fee539SLionel Sambuc #include <sys/time.h>
5185fee539SLionel Sambuc #include <sys/stat.h>
5285fee539SLionel Sambuc #include <sys/param.h>
5385fee539SLionel Sambuc #include <stdio.h>
5485fee539SLionel Sambuc #include <ctype.h>
5585fee539SLionel Sambuc #include <fcntl.h>
5685fee539SLionel Sambuc #include <paths.h>
5785fee539SLionel Sambuc #include <string.h>
5885fee539SLionel Sambuc #include <unistd.h>
5985fee539SLionel Sambuc #include <errno.h>
6085fee539SLionel Sambuc #include <stdlib.h>
6185fee539SLionel Sambuc #include "pax.h"
6285fee539SLionel Sambuc #include "tables.h"
6385fee539SLionel Sambuc #include "extern.h"
6485fee539SLionel Sambuc 
6585fee539SLionel Sambuc /*
6685fee539SLionel Sambuc  * Routines for controlling the contents of all the different databases pax
6785fee539SLionel Sambuc  * keeps. Tables are dynamically created only when they are needed. The
6885fee539SLionel Sambuc  * goal was speed and the ability to work with HUGE archives. The databases
6985fee539SLionel Sambuc  * were kept simple, but do have complex rules for when the contents change.
7085fee539SLionel Sambuc  * As of this writing, the POSIX library functions were more complex than
7185fee539SLionel Sambuc  * needed for this application (pax databases have very short lifetimes and
7285fee539SLionel Sambuc  * do not survive after pax is finished). Pax is required to handle very
7385fee539SLionel Sambuc  * large archives. These database routines carefully combine memory usage and
7485fee539SLionel Sambuc  * temporary file storage in ways which will not significantly impact runtime
7585fee539SLionel Sambuc  * performance while allowing the largest possible archives to be handled.
7685fee539SLionel Sambuc  * Trying to force the fit to the POSIX database routines was not considered
7785fee539SLionel Sambuc  * time well spent.
7885fee539SLionel Sambuc  */
7985fee539SLionel Sambuc 
8085fee539SLionel Sambuc static HRDLNK **ltab = NULL;	/* hard link table for detecting hard links */
8185fee539SLionel Sambuc static FTM **ftab = NULL;	/* file time table for updating arch */
8285fee539SLionel Sambuc static NAMT **ntab = NULL;	/* interactive rename storage table */
8385fee539SLionel Sambuc static DEVT **dtab = NULL;	/* device/inode mapping tables */
8485fee539SLionel Sambuc static ATDIR **atab = NULL;	/* file tree directory time reset table */
8585fee539SLionel Sambuc #ifdef DIRS_USE_FILE
8685fee539SLionel Sambuc static int dirfd = -1;		/* storage for setting created dir time/mode */
8785fee539SLionel Sambuc static u_long dircnt;		/* entries in dir time/mode storage */
8885fee539SLionel Sambuc #endif
8985fee539SLionel Sambuc static int ffd = -1;		/* tmp file for file time table name storage */
9085fee539SLionel Sambuc 
9185fee539SLionel Sambuc static DEVT *chk_dev(dev_t, int);
9285fee539SLionel Sambuc 
9385fee539SLionel Sambuc /*
9485fee539SLionel Sambuc  * hard link table routines
9585fee539SLionel Sambuc  *
9685fee539SLionel Sambuc  * The hard link table tries to detect hard links to files using the device and
9785fee539SLionel Sambuc  * inode values. We do this when writing an archive, so we can tell the format
9885fee539SLionel Sambuc  * write routine that this file is a hard link to another file. The format
9985fee539SLionel Sambuc  * write routine then can store this file in whatever way it wants (as a hard
10085fee539SLionel Sambuc  * link if the format supports that like tar, or ignore this info like cpio).
10185fee539SLionel Sambuc  * (Actually a field in the format driver table tells us if the format wants
10285fee539SLionel Sambuc  * hard link info. if not, we do not waste time looking for them). We also use
10385fee539SLionel Sambuc  * the same table when reading an archive. In that situation, this table is
10485fee539SLionel Sambuc  * used by the format read routine to detect hard links from stored dev and
10585fee539SLionel Sambuc  * inode numbers (like cpio). This will allow pax to create a link when one
10685fee539SLionel Sambuc  * can be detected by the archive format.
10785fee539SLionel Sambuc  */
10885fee539SLionel Sambuc 
10985fee539SLionel Sambuc /*
11085fee539SLionel Sambuc  * lnk_start
11185fee539SLionel Sambuc  *	Creates the hard link table.
11285fee539SLionel Sambuc  * Return:
11385fee539SLionel Sambuc  *	0 if created, -1 if failure
11485fee539SLionel Sambuc  */
11585fee539SLionel Sambuc 
11685fee539SLionel Sambuc int
lnk_start(void)11785fee539SLionel Sambuc lnk_start(void)
11885fee539SLionel Sambuc {
11985fee539SLionel Sambuc 	if (ltab != NULL)
12085fee539SLionel Sambuc 		return 0;
12185fee539SLionel Sambuc 	if ((ltab = (HRDLNK **)calloc(L_TAB_SZ, sizeof(HRDLNK *))) == NULL) {
12285fee539SLionel Sambuc 		tty_warn(1, "Cannot allocate memory for hard link table");
12385fee539SLionel Sambuc 		return -1;
12485fee539SLionel Sambuc 	}
12585fee539SLionel Sambuc 	return 0;
12685fee539SLionel Sambuc }
12785fee539SLionel Sambuc 
12885fee539SLionel Sambuc /*
12985fee539SLionel Sambuc  * chk_lnk()
13085fee539SLionel Sambuc  *	Looks up entry in hard link hash table. If found, it copies the name
13185fee539SLionel Sambuc  *	of the file it is linked to (we already saw that file) into ln_name.
13285fee539SLionel Sambuc  *	lnkcnt is decremented and if goes to 1 the node is deleted from the
13385fee539SLionel Sambuc  *	database. (We have seen all the links to this file). If not found,
13485fee539SLionel Sambuc  *	we add the file to the database if it has the potential for having
13585fee539SLionel Sambuc  *	hard links to other files we may process (it has a link count > 1)
13685fee539SLionel Sambuc  * Return:
13785fee539SLionel Sambuc  *	if found returns 1; if not found returns 0; -1 on error
13885fee539SLionel Sambuc  */
13985fee539SLionel Sambuc 
14085fee539SLionel Sambuc int
chk_lnk(ARCHD * arcn)14185fee539SLionel Sambuc chk_lnk(ARCHD *arcn)
14285fee539SLionel Sambuc {
14385fee539SLionel Sambuc 	HRDLNK *pt;
14485fee539SLionel Sambuc 	HRDLNK **ppt;
14585fee539SLionel Sambuc 	u_int indx;
14685fee539SLionel Sambuc 
14785fee539SLionel Sambuc 	if (ltab == NULL)
14885fee539SLionel Sambuc 		return -1;
14985fee539SLionel Sambuc 	/*
15085fee539SLionel Sambuc 	 * ignore those nodes that cannot have hard links
15185fee539SLionel Sambuc 	 */
15285fee539SLionel Sambuc 	if ((arcn->type == PAX_DIR) || (arcn->sb.st_nlink <= 1))
15385fee539SLionel Sambuc 		return 0;
15485fee539SLionel Sambuc 
15585fee539SLionel Sambuc 	/*
15685fee539SLionel Sambuc 	 * hash inode number and look for this file
15785fee539SLionel Sambuc 	 */
15885fee539SLionel Sambuc 	indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ;
15985fee539SLionel Sambuc 	if ((pt = ltab[indx]) != NULL) {
16085fee539SLionel Sambuc 		/*
16185fee539SLionel Sambuc 		 * its hash chain is not empty, walk down looking for it
16285fee539SLionel Sambuc 		 */
16385fee539SLionel Sambuc 		ppt = &(ltab[indx]);
16485fee539SLionel Sambuc 		while (pt != NULL) {
16585fee539SLionel Sambuc 			if ((pt->ino == arcn->sb.st_ino) &&
16685fee539SLionel Sambuc 			    (pt->dev == arcn->sb.st_dev))
16785fee539SLionel Sambuc 				break;
16885fee539SLionel Sambuc 			ppt = &(pt->fow);
16985fee539SLionel Sambuc 			pt = pt->fow;
17085fee539SLionel Sambuc 		}
17185fee539SLionel Sambuc 
17285fee539SLionel Sambuc 		if (pt != NULL) {
17385fee539SLionel Sambuc 			/*
17485fee539SLionel Sambuc 			 * found a link. set the node type and copy in the
17585fee539SLionel Sambuc 			 * name of the file it is to link to. we need to
17685fee539SLionel Sambuc 			 * handle hardlinks to regular files differently than
17785fee539SLionel Sambuc 			 * other links.
17885fee539SLionel Sambuc 			 */
17985fee539SLionel Sambuc 			arcn->ln_nlen = strlcpy(arcn->ln_name, pt->name,
18085fee539SLionel Sambuc 				sizeof(arcn->ln_name));
18185fee539SLionel Sambuc 			if (arcn->type == PAX_REG)
18285fee539SLionel Sambuc 				arcn->type = PAX_HRG;
18385fee539SLionel Sambuc 			else
18485fee539SLionel Sambuc 				arcn->type = PAX_HLK;
18585fee539SLionel Sambuc 
18685fee539SLionel Sambuc 			/*
18785fee539SLionel Sambuc 			 * if we have found all the links to this file, remove
18885fee539SLionel Sambuc 			 * it from the database
18985fee539SLionel Sambuc 			 */
19085fee539SLionel Sambuc 			if (--pt->nlink <= 1) {
19185fee539SLionel Sambuc 				*ppt = pt->fow;
19285fee539SLionel Sambuc 				(void)free((char *)pt->name);
19385fee539SLionel Sambuc 				(void)free((char *)pt);
19485fee539SLionel Sambuc 			}
19585fee539SLionel Sambuc 			return 1;
19685fee539SLionel Sambuc 		}
19785fee539SLionel Sambuc 	}
19885fee539SLionel Sambuc 
19985fee539SLionel Sambuc 	/*
20085fee539SLionel Sambuc 	 * we never saw this file before. It has links so we add it to the
20185fee539SLionel Sambuc 	 * front of this hash chain
20285fee539SLionel Sambuc 	 */
20385fee539SLionel Sambuc 	if ((pt = (HRDLNK *)malloc(sizeof(HRDLNK))) != NULL) {
20485fee539SLionel Sambuc 		if ((pt->name = strdup(arcn->name)) != NULL) {
20585fee539SLionel Sambuc 			pt->dev = arcn->sb.st_dev;
20685fee539SLionel Sambuc 			pt->ino = arcn->sb.st_ino;
20785fee539SLionel Sambuc 			pt->nlink = arcn->sb.st_nlink;
20885fee539SLionel Sambuc 			pt->fow = ltab[indx];
20985fee539SLionel Sambuc 			ltab[indx] = pt;
21085fee539SLionel Sambuc 			return 0;
21185fee539SLionel Sambuc 		}
21285fee539SLionel Sambuc 		(void)free((char *)pt);
21385fee539SLionel Sambuc 	}
21485fee539SLionel Sambuc 
21585fee539SLionel Sambuc 	tty_warn(1, "Hard link table out of memory");
21685fee539SLionel Sambuc 	return -1;
21785fee539SLionel Sambuc }
21885fee539SLionel Sambuc 
21985fee539SLionel Sambuc /*
22085fee539SLionel Sambuc  * purg_lnk
22185fee539SLionel Sambuc  *	remove reference for a file that we may have added to the data base as
22285fee539SLionel Sambuc  *	a potential source for hard links. We ended up not using the file, so
22385fee539SLionel Sambuc  *	we do not want to accidentally point another file at it later on.
22485fee539SLionel Sambuc  */
22585fee539SLionel Sambuc 
22685fee539SLionel Sambuc void
purg_lnk(ARCHD * arcn)22785fee539SLionel Sambuc purg_lnk(ARCHD *arcn)
22885fee539SLionel Sambuc {
22985fee539SLionel Sambuc 	HRDLNK *pt;
23085fee539SLionel Sambuc 	HRDLNK **ppt;
23185fee539SLionel Sambuc 	u_int indx;
23285fee539SLionel Sambuc 
23385fee539SLionel Sambuc 	if (ltab == NULL)
23485fee539SLionel Sambuc 		return;
23585fee539SLionel Sambuc 	/*
23685fee539SLionel Sambuc 	 * do not bother to look if it could not be in the database
23785fee539SLionel Sambuc 	 */
23885fee539SLionel Sambuc 	if ((arcn->sb.st_nlink <= 1) || (arcn->type == PAX_DIR) ||
23985fee539SLionel Sambuc 	    (arcn->type == PAX_HLK) || (arcn->type == PAX_HRG))
24085fee539SLionel Sambuc 		return;
24185fee539SLionel Sambuc 
24285fee539SLionel Sambuc 	/*
24385fee539SLionel Sambuc 	 * find the hash chain for this inode value, if empty return
24485fee539SLionel Sambuc 	 */
24585fee539SLionel Sambuc 	indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ;
24685fee539SLionel Sambuc 	if ((pt = ltab[indx]) == NULL)
24785fee539SLionel Sambuc 		return;
24885fee539SLionel Sambuc 
24985fee539SLionel Sambuc 	/*
25085fee539SLionel Sambuc 	 * walk down the list looking for the inode/dev pair, unlink and
25185fee539SLionel Sambuc 	 * free if found
25285fee539SLionel Sambuc 	 */
25385fee539SLionel Sambuc 	ppt = &(ltab[indx]);
25485fee539SLionel Sambuc 	while (pt != NULL) {
25585fee539SLionel Sambuc 		if ((pt->ino == arcn->sb.st_ino) &&
25685fee539SLionel Sambuc 		    (pt->dev == arcn->sb.st_dev))
25785fee539SLionel Sambuc 			break;
25885fee539SLionel Sambuc 		ppt = &(pt->fow);
25985fee539SLionel Sambuc 		pt = pt->fow;
26085fee539SLionel Sambuc 	}
26185fee539SLionel Sambuc 	if (pt == NULL)
26285fee539SLionel Sambuc 		return;
26385fee539SLionel Sambuc 
26485fee539SLionel Sambuc 	/*
26585fee539SLionel Sambuc 	 * remove and free it
26685fee539SLionel Sambuc 	 */
26785fee539SLionel Sambuc 	*ppt = pt->fow;
26885fee539SLionel Sambuc 	(void)free((char *)pt->name);
26985fee539SLionel Sambuc 	(void)free((char *)pt);
27085fee539SLionel Sambuc }
27185fee539SLionel Sambuc 
27285fee539SLionel Sambuc /*
27385fee539SLionel Sambuc  * lnk_end()
27485fee539SLionel Sambuc  *	pull apart a existing link table so we can reuse it. We do this between
27585fee539SLionel Sambuc  *	read and write phases of append with update. (The format may have
27685fee539SLionel Sambuc  *	used the link table, and we need to start with a fresh table for the
27785fee539SLionel Sambuc  *	write phase
27885fee539SLionel Sambuc  */
27985fee539SLionel Sambuc 
28085fee539SLionel Sambuc void
lnk_end(void)28185fee539SLionel Sambuc lnk_end(void)
28285fee539SLionel Sambuc {
28385fee539SLionel Sambuc 	int i;
28485fee539SLionel Sambuc 	HRDLNK *pt;
28585fee539SLionel Sambuc 	HRDLNK *ppt;
28685fee539SLionel Sambuc 
28785fee539SLionel Sambuc 	if (ltab == NULL)
28885fee539SLionel Sambuc 		return;
28985fee539SLionel Sambuc 
29085fee539SLionel Sambuc 	for (i = 0; i < L_TAB_SZ; ++i) {
29185fee539SLionel Sambuc 		if (ltab[i] == NULL)
29285fee539SLionel Sambuc 			continue;
29385fee539SLionel Sambuc 		pt = ltab[i];
29485fee539SLionel Sambuc 		ltab[i] = NULL;
29585fee539SLionel Sambuc 
29685fee539SLionel Sambuc 		/*
29785fee539SLionel Sambuc 		 * free up each entry on this chain
29885fee539SLionel Sambuc 		 */
29985fee539SLionel Sambuc 		while (pt != NULL) {
30085fee539SLionel Sambuc 			ppt = pt;
30185fee539SLionel Sambuc 			pt = ppt->fow;
30285fee539SLionel Sambuc 			(void)free((char *)ppt->name);
30385fee539SLionel Sambuc 			(void)free((char *)ppt);
30485fee539SLionel Sambuc 		}
30585fee539SLionel Sambuc 	}
30685fee539SLionel Sambuc 	return;
30785fee539SLionel Sambuc }
30885fee539SLionel Sambuc 
30985fee539SLionel Sambuc /*
31085fee539SLionel Sambuc  * modification time table routines
31185fee539SLionel Sambuc  *
31285fee539SLionel Sambuc  * The modification time table keeps track of last modification times for all
31385fee539SLionel Sambuc  * files stored in an archive during a write phase when -u is set. We only
31485fee539SLionel Sambuc  * add a file to the archive if it is newer than a file with the same name
31585fee539SLionel Sambuc  * already stored on the archive (if there is no other file with the same
31685fee539SLionel Sambuc  * name on the archive it is added). This applies to writes and appends.
31785fee539SLionel Sambuc  * An append with an -u must read the archive and store the modification time
31885fee539SLionel Sambuc  * for every file on that archive before starting the write phase. It is clear
31985fee539SLionel Sambuc  * that this is one HUGE database. To save memory space, the actual file names
32085fee539SLionel Sambuc  * are stored in a scratch file and indexed by an in-memory hash table. The
32185fee539SLionel Sambuc  * hash table is indexed by hashing the file path. The nodes in the table store
32285fee539SLionel Sambuc  * the length of the filename and the lseek offset within the scratch file
32385fee539SLionel Sambuc  * where the actual name is stored. Since there are never any deletions from this
32485fee539SLionel Sambuc  * table, fragmentation of the scratch file is never a issue. Lookups seem to
32585fee539SLionel Sambuc  * not exhibit any locality at all (files in the database are rarely
32685fee539SLionel Sambuc  * looked up more than once...), so caching is just a waste of memory. The
32785fee539SLionel Sambuc  * only limitation is the amount of scratch file space available to store the
32885fee539SLionel Sambuc  * path names.
32985fee539SLionel Sambuc  */
33085fee539SLionel Sambuc 
33185fee539SLionel Sambuc /*
33285fee539SLionel Sambuc  * ftime_start()
33385fee539SLionel Sambuc  *	create the file time hash table and open for read/write the scratch
33485fee539SLionel Sambuc  *	file. (after created it is unlinked, so when we exit we leave
33585fee539SLionel Sambuc  *	no witnesses).
33685fee539SLionel Sambuc  * Return:
33785fee539SLionel Sambuc  *	0 if the table and file was created ok, -1 otherwise
33885fee539SLionel Sambuc  */
33985fee539SLionel Sambuc 
34085fee539SLionel Sambuc int
ftime_start(void)34185fee539SLionel Sambuc ftime_start(void)
34285fee539SLionel Sambuc {
34385fee539SLionel Sambuc 	if (ftab != NULL)
34485fee539SLionel Sambuc 		return 0;
34585fee539SLionel Sambuc 	if ((ftab = (FTM **)calloc(F_TAB_SZ, sizeof(FTM *))) == NULL) {
34685fee539SLionel Sambuc 		tty_warn(1, "Cannot allocate memory for file time table");
34785fee539SLionel Sambuc 		return -1;
34885fee539SLionel Sambuc 	}
34985fee539SLionel Sambuc 
35085fee539SLionel Sambuc 	/*
35185fee539SLionel Sambuc 	 * get random name and create temporary scratch file, unlink name
35285fee539SLionel Sambuc 	 * so it will get removed on exit
35385fee539SLionel Sambuc 	 */
35485fee539SLionel Sambuc 	memcpy(tempbase, _TFILE_BASE, sizeof(_TFILE_BASE));
35585fee539SLionel Sambuc 	if ((ffd = mkstemp(tempfile)) == -1) {
35685fee539SLionel Sambuc 		syswarn(1, errno, "Unable to create temporary file: %s",
35785fee539SLionel Sambuc 		    tempfile);
35885fee539SLionel Sambuc 		return -1;
35985fee539SLionel Sambuc 	}
36085fee539SLionel Sambuc 
36185fee539SLionel Sambuc 	(void)unlink(tempfile);
36285fee539SLionel Sambuc 	return 0;
36385fee539SLionel Sambuc }
36485fee539SLionel Sambuc 
36585fee539SLionel Sambuc /*
36685fee539SLionel Sambuc  * chk_ftime()
36785fee539SLionel Sambuc  *	looks up entry in file time hash table. If not found, the file is
36885fee539SLionel Sambuc  *	added to the hash table and the file named stored in the scratch file.
36985fee539SLionel Sambuc  *	If a file with the same name is found, the file times are compared and
37085fee539SLionel Sambuc  *	the most recent file time is retained. If the new file was younger (or
37185fee539SLionel Sambuc  *	was not in the database) the new file is selected for storage.
37285fee539SLionel Sambuc  * Return:
37385fee539SLionel Sambuc  *	0 if file should be added to the archive, 1 if it should be skipped,
37485fee539SLionel Sambuc  *	-1 on error
37585fee539SLionel Sambuc  */
37685fee539SLionel Sambuc 
37785fee539SLionel Sambuc int
chk_ftime(ARCHD * arcn)37885fee539SLionel Sambuc chk_ftime(ARCHD *arcn)
37985fee539SLionel Sambuc {
38085fee539SLionel Sambuc 	FTM *pt;
38185fee539SLionel Sambuc 	int namelen;
38285fee539SLionel Sambuc 	u_int indx;
38385fee539SLionel Sambuc 	char ckname[PAXPATHLEN+1];
38485fee539SLionel Sambuc 
38585fee539SLionel Sambuc 	/*
38685fee539SLionel Sambuc 	 * no info, go ahead and add to archive
38785fee539SLionel Sambuc 	 */
38885fee539SLionel Sambuc 	if (ftab == NULL)
38985fee539SLionel Sambuc 		return 0;
39085fee539SLionel Sambuc 
39185fee539SLionel Sambuc 	/*
39285fee539SLionel Sambuc 	 * hash the pathname and look up in table
39385fee539SLionel Sambuc 	 */
39485fee539SLionel Sambuc 	namelen = arcn->nlen;
39585fee539SLionel Sambuc 	indx = st_hash(arcn->name, namelen, F_TAB_SZ);
39685fee539SLionel Sambuc 	if ((pt = ftab[indx]) != NULL) {
39785fee539SLionel Sambuc 		/*
39885fee539SLionel Sambuc 		 * the hash chain is not empty, walk down looking for match
39985fee539SLionel Sambuc 		 * only read up the path names if the lengths match, speeds
40085fee539SLionel Sambuc 		 * up the search a lot
40185fee539SLionel Sambuc 		 */
40285fee539SLionel Sambuc 		while (pt != NULL) {
40385fee539SLionel Sambuc 			if (pt->namelen == namelen) {
40485fee539SLionel Sambuc 				/*
40585fee539SLionel Sambuc 				 * potential match, have to read the name
40685fee539SLionel Sambuc 				 * from the scratch file.
40785fee539SLionel Sambuc 				 */
40885fee539SLionel Sambuc 				if (lseek(ffd,pt->seek,SEEK_SET) != pt->seek) {
40985fee539SLionel Sambuc 					syswarn(1, errno,
41085fee539SLionel Sambuc 					    "Failed ftime table seek");
41185fee539SLionel Sambuc 					return -1;
41285fee539SLionel Sambuc 				}
41385fee539SLionel Sambuc 				if (xread(ffd, ckname, namelen) != namelen) {
41485fee539SLionel Sambuc 					syswarn(1, errno,
41585fee539SLionel Sambuc 					    "Failed ftime table read");
41685fee539SLionel Sambuc 					return -1;
41785fee539SLionel Sambuc 				}
41885fee539SLionel Sambuc 
41985fee539SLionel Sambuc 				/*
42085fee539SLionel Sambuc 				 * if the names match, we are done
42185fee539SLionel Sambuc 				 */
42285fee539SLionel Sambuc 				if (!strncmp(ckname, arcn->name, namelen))
42385fee539SLionel Sambuc 					break;
42485fee539SLionel Sambuc 			}
42585fee539SLionel Sambuc 
42685fee539SLionel Sambuc 			/*
42785fee539SLionel Sambuc 			 * try the next entry on the chain
42885fee539SLionel Sambuc 			 */
42985fee539SLionel Sambuc 			pt = pt->fow;
43085fee539SLionel Sambuc 		}
43185fee539SLionel Sambuc 
43285fee539SLionel Sambuc 		if (pt != NULL) {
43385fee539SLionel Sambuc 			/*
43485fee539SLionel Sambuc 			 * found the file, compare the times, save the newer
43585fee539SLionel Sambuc 			 */
43685fee539SLionel Sambuc 			if (arcn->sb.st_mtime > pt->mtime) {
43785fee539SLionel Sambuc 				/*
43885fee539SLionel Sambuc 				 * file is newer
43985fee539SLionel Sambuc 				 */
44085fee539SLionel Sambuc 				pt->mtime = arcn->sb.st_mtime;
44185fee539SLionel Sambuc 				return 0;
44285fee539SLionel Sambuc 			}
44385fee539SLionel Sambuc 			/*
44485fee539SLionel Sambuc 			 * file is older
44585fee539SLionel Sambuc 			 */
44685fee539SLionel Sambuc 			return 1;
44785fee539SLionel Sambuc 		}
44885fee539SLionel Sambuc 	}
44985fee539SLionel Sambuc 
45085fee539SLionel Sambuc 	/*
45185fee539SLionel Sambuc 	 * not in table, add it
45285fee539SLionel Sambuc 	 */
45385fee539SLionel Sambuc 	if ((pt = (FTM *)malloc(sizeof(FTM))) != NULL) {
45485fee539SLionel Sambuc 		/*
45585fee539SLionel Sambuc 		 * add the name at the end of the scratch file, saving the
45685fee539SLionel Sambuc 		 * offset. add the file to the head of the hash chain
45785fee539SLionel Sambuc 		 */
45885fee539SLionel Sambuc 		if ((pt->seek = lseek(ffd, (off_t)0, SEEK_END)) >= 0) {
45985fee539SLionel Sambuc 			if (xwrite(ffd, arcn->name, namelen) == namelen) {
46085fee539SLionel Sambuc 				pt->mtime = arcn->sb.st_mtime;
46185fee539SLionel Sambuc 				pt->namelen = namelen;
46285fee539SLionel Sambuc 				pt->fow = ftab[indx];
46385fee539SLionel Sambuc 				ftab[indx] = pt;
46485fee539SLionel Sambuc 				return 0;
46585fee539SLionel Sambuc 			}
46685fee539SLionel Sambuc 			syswarn(1, errno, "Failed write to file time table");
46785fee539SLionel Sambuc 		} else
46885fee539SLionel Sambuc 			syswarn(1, errno, "Failed seek on file time table");
46985fee539SLionel Sambuc 	} else
47085fee539SLionel Sambuc 		tty_warn(1, "File time table ran out of memory");
47185fee539SLionel Sambuc 
47285fee539SLionel Sambuc 	if (pt != NULL)
47385fee539SLionel Sambuc 		(void)free((char *)pt);
47485fee539SLionel Sambuc 	return -1;
47585fee539SLionel Sambuc }
47685fee539SLionel Sambuc 
47785fee539SLionel Sambuc /*
47885fee539SLionel Sambuc  * Interactive rename table routines
47985fee539SLionel Sambuc  *
48085fee539SLionel Sambuc  * The interactive rename table keeps track of the new names that the user
48185fee539SLionel Sambuc  * assigns to files from tty input. Since this map is unique for each file
48285fee539SLionel Sambuc  * we must store it in case there is a reference to the file later in archive
48385fee539SLionel Sambuc  * (a link). Otherwise we will be unable to find the file we know was
48485fee539SLionel Sambuc  * extracted. The remapping of these files is stored in a memory based hash
48585fee539SLionel Sambuc  * table (it is assumed since input must come from /dev/tty, it is unlikely to
48685fee539SLionel Sambuc  * be a very large table).
48785fee539SLionel Sambuc  */
48885fee539SLionel Sambuc 
48985fee539SLionel Sambuc /*
49085fee539SLionel Sambuc  * name_start()
49185fee539SLionel Sambuc  *	create the interactive rename table
49285fee539SLionel Sambuc  * Return:
49385fee539SLionel Sambuc  *	0 if successful, -1 otherwise
49485fee539SLionel Sambuc  */
49585fee539SLionel Sambuc 
49685fee539SLionel Sambuc int
name_start(void)49785fee539SLionel Sambuc name_start(void)
49885fee539SLionel Sambuc {
49985fee539SLionel Sambuc 	if (ntab != NULL)
50085fee539SLionel Sambuc 		return 0;
50185fee539SLionel Sambuc 	if ((ntab = (NAMT **)calloc(N_TAB_SZ, sizeof(NAMT *))) == NULL) {
50285fee539SLionel Sambuc 		tty_warn(1,
50385fee539SLionel Sambuc 		    "Cannot allocate memory for interactive rename table");
50485fee539SLionel Sambuc 		return -1;
50585fee539SLionel Sambuc 	}
50685fee539SLionel Sambuc 	return 0;
50785fee539SLionel Sambuc }
50885fee539SLionel Sambuc 
50985fee539SLionel Sambuc /*
51085fee539SLionel Sambuc  * add_name()
51185fee539SLionel Sambuc  *	add the new name to old name mapping just created by the user.
51285fee539SLionel Sambuc  *	If an old name mapping is found (there may be duplicate names on an
51385fee539SLionel Sambuc  *	archive) only the most recent is kept.
51485fee539SLionel Sambuc  * Return:
51585fee539SLionel Sambuc  *	0 if added, -1 otherwise
51685fee539SLionel Sambuc  */
51785fee539SLionel Sambuc 
51885fee539SLionel Sambuc int
add_name(char * oname,int onamelen,char * nname)51985fee539SLionel Sambuc add_name(char *oname, int onamelen, char *nname)
52085fee539SLionel Sambuc {
52185fee539SLionel Sambuc 	NAMT *pt;
52285fee539SLionel Sambuc 	u_int indx;
52385fee539SLionel Sambuc 
52485fee539SLionel Sambuc 	if (ntab == NULL) {
52585fee539SLionel Sambuc 		/*
52685fee539SLionel Sambuc 		 * should never happen
52785fee539SLionel Sambuc 		 */
52885fee539SLionel Sambuc 		tty_warn(0, "No interactive rename table, links may fail\n");
52985fee539SLionel Sambuc 		return 0;
53085fee539SLionel Sambuc 	}
53185fee539SLionel Sambuc 
53285fee539SLionel Sambuc 	/*
53385fee539SLionel Sambuc 	 * look to see if we have already mapped this file, if so we
53485fee539SLionel Sambuc 	 * will update it
53585fee539SLionel Sambuc 	 */
53685fee539SLionel Sambuc 	indx = st_hash(oname, onamelen, N_TAB_SZ);
53785fee539SLionel Sambuc 	if ((pt = ntab[indx]) != NULL) {
53885fee539SLionel Sambuc 		/*
53985fee539SLionel Sambuc 		 * look down the has chain for the file
54085fee539SLionel Sambuc 		 */
54185fee539SLionel Sambuc 		while ((pt != NULL) && (strcmp(oname, pt->oname) != 0))
54285fee539SLionel Sambuc 			pt = pt->fow;
54385fee539SLionel Sambuc 
54485fee539SLionel Sambuc 		if (pt != NULL) {
54585fee539SLionel Sambuc 			/*
54685fee539SLionel Sambuc 			 * found an old mapping, replace it with the new one
54785fee539SLionel Sambuc 			 * the user just input (if it is different)
54885fee539SLionel Sambuc 			 */
54985fee539SLionel Sambuc 			if (strcmp(nname, pt->nname) == 0)
55085fee539SLionel Sambuc 				return 0;
55185fee539SLionel Sambuc 
55285fee539SLionel Sambuc 			(void)free((char *)pt->nname);
55385fee539SLionel Sambuc 			if ((pt->nname = strdup(nname)) == NULL) {
55485fee539SLionel Sambuc 				tty_warn(1, "Cannot update rename table");
55585fee539SLionel Sambuc 				return -1;
55685fee539SLionel Sambuc 			}
55785fee539SLionel Sambuc 			return 0;
55885fee539SLionel Sambuc 		}
55985fee539SLionel Sambuc 	}
56085fee539SLionel Sambuc 
56185fee539SLionel Sambuc 	/*
56285fee539SLionel Sambuc 	 * this is a new mapping, add it to the table
56385fee539SLionel Sambuc 	 */
56485fee539SLionel Sambuc 	if ((pt = (NAMT *)malloc(sizeof(NAMT))) != NULL) {
56585fee539SLionel Sambuc 		if ((pt->oname = strdup(oname)) != NULL) {
56685fee539SLionel Sambuc 			if ((pt->nname = strdup(nname)) != NULL) {
56785fee539SLionel Sambuc 				pt->fow = ntab[indx];
56885fee539SLionel Sambuc 				ntab[indx] = pt;
56985fee539SLionel Sambuc 				return 0;
57085fee539SLionel Sambuc 			}
57185fee539SLionel Sambuc 			(void)free((char *)pt->oname);
57285fee539SLionel Sambuc 		}
57385fee539SLionel Sambuc 		(void)free((char *)pt);
57485fee539SLionel Sambuc 	}
57585fee539SLionel Sambuc 	tty_warn(1, "Interactive rename table out of memory");
57685fee539SLionel Sambuc 	return -1;
57785fee539SLionel Sambuc }
57885fee539SLionel Sambuc 
57985fee539SLionel Sambuc /*
58085fee539SLionel Sambuc  * sub_name()
58185fee539SLionel Sambuc  *	look up a link name to see if it points at a file that has been
58285fee539SLionel Sambuc  *	remapped by the user. If found, the link is adjusted to contain the
58385fee539SLionel Sambuc  *	new name (oname is the link to name)
58485fee539SLionel Sambuc  */
58585fee539SLionel Sambuc 
58685fee539SLionel Sambuc void
sub_name(char * oname,int * onamelen,size_t onamesize)58785fee539SLionel Sambuc sub_name(char *oname, int *onamelen, size_t onamesize)
58885fee539SLionel Sambuc {
58985fee539SLionel Sambuc 	NAMT *pt;
59085fee539SLionel Sambuc 	u_int indx;
59185fee539SLionel Sambuc 
59285fee539SLionel Sambuc 	if (ntab == NULL)
59385fee539SLionel Sambuc 		return;
59485fee539SLionel Sambuc 	/*
59585fee539SLionel Sambuc 	 * look the name up in the hash table
59685fee539SLionel Sambuc 	 */
59785fee539SLionel Sambuc 	indx = st_hash(oname, *onamelen, N_TAB_SZ);
59885fee539SLionel Sambuc 	if ((pt = ntab[indx]) == NULL)
59985fee539SLionel Sambuc 		return;
60085fee539SLionel Sambuc 
60185fee539SLionel Sambuc 	while (pt != NULL) {
60285fee539SLionel Sambuc 		/*
60385fee539SLionel Sambuc 		 * walk down the hash chain looking for a match
60485fee539SLionel Sambuc 		 */
60585fee539SLionel Sambuc 		if (strcmp(oname, pt->oname) == 0) {
60685fee539SLionel Sambuc 			/*
60785fee539SLionel Sambuc 			 * found it, replace it with the new name
60885fee539SLionel Sambuc 			 * and return (we know that oname has enough space)
60985fee539SLionel Sambuc 			 */
61085fee539SLionel Sambuc 			*onamelen = strlcpy(oname, pt->nname, onamesize);
61185fee539SLionel Sambuc 			return;
61285fee539SLionel Sambuc 		}
61385fee539SLionel Sambuc 		pt = pt->fow;
61485fee539SLionel Sambuc 	}
61585fee539SLionel Sambuc 
61685fee539SLionel Sambuc 	/*
61785fee539SLionel Sambuc 	 * no match, just return
61885fee539SLionel Sambuc 	 */
61985fee539SLionel Sambuc 	return;
62085fee539SLionel Sambuc }
62185fee539SLionel Sambuc 
62285fee539SLionel Sambuc /*
62385fee539SLionel Sambuc  * device/inode mapping table routines
62485fee539SLionel Sambuc  * (used with formats that store device and inodes fields)
62585fee539SLionel Sambuc  *
62685fee539SLionel Sambuc  * device/inode mapping tables remap the device field in an archive header. The
62785fee539SLionel Sambuc  * device/inode fields are used to determine when files are hard links to each
62885fee539SLionel Sambuc  * other. However these values have very little meaning outside of that. This
62985fee539SLionel Sambuc  * database is used to solve one of two different problems.
63085fee539SLionel Sambuc  *
63185fee539SLionel Sambuc  * 1) when files are appended to an archive, while the new files may have hard
63285fee539SLionel Sambuc  * links to each other, you cannot determine if they have hard links to any
63385fee539SLionel Sambuc  * file already stored on the archive from a prior run of pax. We must assume
63485fee539SLionel Sambuc  * that these inode/device pairs are unique only within a SINGLE run of pax
63585fee539SLionel Sambuc  * (which adds a set of files to an archive). So we have to make sure the
63685fee539SLionel Sambuc  * inode/dev pairs we add each time are always unique. We do this by observing
63785fee539SLionel Sambuc  * while the inode field is very dense, the use of the dev field is fairly
63885fee539SLionel Sambuc  * sparse. Within each run of pax, we remap any device number of a new archive
63985fee539SLionel Sambuc  * member that has a device number used in a prior run and already stored in a
64085fee539SLionel Sambuc  * file on the archive. During the read phase of the append, we store the
64185fee539SLionel Sambuc  * device numbers used and mark them to not be used by any file during the
64285fee539SLionel Sambuc  * write phase. If during write we go to use one of those old device numbers,
64385fee539SLionel Sambuc  * we remap it to a new value.
64485fee539SLionel Sambuc  *
64585fee539SLionel Sambuc  * 2) Often the fields in the archive header used to store these values are
64685fee539SLionel Sambuc  * too small to store the entire value. The result is an inode or device value
64785fee539SLionel Sambuc  * which can be truncated. This really can foul up an archive. With truncation
64885fee539SLionel Sambuc  * we end up creating links between files that are really not links (after
64985fee539SLionel Sambuc  * truncation the inodes are the same value). We address that by detecting
65085fee539SLionel Sambuc  * truncation and forcing a remap of the device field to split truncated
65185fee539SLionel Sambuc  * inodes away from each other. Each truncation creates a pattern of bits that
65285fee539SLionel Sambuc  * are removed. We use this pattern of truncated bits to partition the inodes
65385fee539SLionel Sambuc  * on a single device to many different devices (each one represented by the
65485fee539SLionel Sambuc  * truncated bit pattern). All inodes on the same device that have the same
65585fee539SLionel Sambuc  * truncation pattern are mapped to the same new device. Two inodes that
65685fee539SLionel Sambuc  * truncate to the same value clearly will always have different truncation
65785fee539SLionel Sambuc  * bit patterns, so they will be split from away each other. When we spot
65885fee539SLionel Sambuc  * device truncation we remap the device number to a non truncated value.
65985fee539SLionel Sambuc  * (for more info see table.h for the data structures involved).
66085fee539SLionel Sambuc  */
66185fee539SLionel Sambuc 
66285fee539SLionel Sambuc /*
66385fee539SLionel Sambuc  * dev_start()
66485fee539SLionel Sambuc  *	create the device mapping table
66585fee539SLionel Sambuc  * Return:
66685fee539SLionel Sambuc  *	0 if successful, -1 otherwise
66785fee539SLionel Sambuc  */
66885fee539SLionel Sambuc 
66985fee539SLionel Sambuc int
dev_start(void)67085fee539SLionel Sambuc dev_start(void)
67185fee539SLionel Sambuc {
67285fee539SLionel Sambuc 	if (dtab != NULL)
67385fee539SLionel Sambuc 		return 0;
67485fee539SLionel Sambuc 	if ((dtab = (DEVT **)calloc(D_TAB_SZ, sizeof(DEVT *))) == NULL) {
67585fee539SLionel Sambuc 		tty_warn(1, "Cannot allocate memory for device mapping table");
67685fee539SLionel Sambuc 		return -1;
67785fee539SLionel Sambuc 	}
67885fee539SLionel Sambuc 	return 0;
67985fee539SLionel Sambuc }
68085fee539SLionel Sambuc 
68185fee539SLionel Sambuc /*
68285fee539SLionel Sambuc  * add_dev()
68385fee539SLionel Sambuc  *	add a device number to the table. this will force the device to be
68485fee539SLionel Sambuc  *	remapped to a new value if it be used during a write phase. This
68585fee539SLionel Sambuc  *	function is called during the read phase of an append to prohibit the
68685fee539SLionel Sambuc  *	use of any device number already in the archive.
68785fee539SLionel Sambuc  * Return:
68885fee539SLionel Sambuc  *	0 if added ok, -1 otherwise
68985fee539SLionel Sambuc  */
69085fee539SLionel Sambuc 
69185fee539SLionel Sambuc int
add_dev(ARCHD * arcn)69285fee539SLionel Sambuc add_dev(ARCHD *arcn)
69385fee539SLionel Sambuc {
69485fee539SLionel Sambuc 	if (chk_dev(arcn->sb.st_dev, 1) == NULL)
69585fee539SLionel Sambuc 		return -1;
69685fee539SLionel Sambuc 	return 0;
69785fee539SLionel Sambuc }
69885fee539SLionel Sambuc 
69985fee539SLionel Sambuc /*
70085fee539SLionel Sambuc  * chk_dev()
70185fee539SLionel Sambuc  *	check for a device value in the device table. If not found and the add
70285fee539SLionel Sambuc  *	flag is set, it is added. This does NOT assign any mapping values, just
70385fee539SLionel Sambuc  *	adds the device number as one that need to be remapped. If this device
70485fee539SLionel Sambuc  *	is already mapped, just return with a pointer to that entry.
70585fee539SLionel Sambuc  * Return:
70685fee539SLionel Sambuc  *	pointer to the entry for this device in the device map table. Null
70785fee539SLionel Sambuc  *	if the add flag is not set and the device is not in the table (it is
70885fee539SLionel Sambuc  *	not been seen yet). If add is set and the device cannot be added, null
70985fee539SLionel Sambuc  *	is returned (indicates an error).
71085fee539SLionel Sambuc  */
71185fee539SLionel Sambuc 
71285fee539SLionel Sambuc static DEVT *
chk_dev(dev_t dev,int add)71385fee539SLionel Sambuc chk_dev(dev_t dev, int add)
71485fee539SLionel Sambuc {
71585fee539SLionel Sambuc 	DEVT *pt;
71685fee539SLionel Sambuc 	u_int indx;
71785fee539SLionel Sambuc 
71885fee539SLionel Sambuc 	if (dtab == NULL)
71985fee539SLionel Sambuc 		return NULL;
72085fee539SLionel Sambuc 	/*
72185fee539SLionel Sambuc 	 * look to see if this device is already in the table
72285fee539SLionel Sambuc 	 */
72385fee539SLionel Sambuc 	indx = ((unsigned)dev) % D_TAB_SZ;
72485fee539SLionel Sambuc 	if ((pt = dtab[indx]) != NULL) {
72585fee539SLionel Sambuc 		while ((pt != NULL) && (pt->dev != dev))
72685fee539SLionel Sambuc 			pt = pt->fow;
72785fee539SLionel Sambuc 
72885fee539SLionel Sambuc 		/*
72985fee539SLionel Sambuc 		 * found it, return a pointer to it
73085fee539SLionel Sambuc 		 */
73185fee539SLionel Sambuc 		if (pt != NULL)
73285fee539SLionel Sambuc 			return pt;
73385fee539SLionel Sambuc 	}
73485fee539SLionel Sambuc 
73585fee539SLionel Sambuc 	/*
73685fee539SLionel Sambuc 	 * not in table, we add it only if told to as this may just be a check
73785fee539SLionel Sambuc 	 * to see if a device number is being used.
73885fee539SLionel Sambuc 	 */
73985fee539SLionel Sambuc 	if (add == 0)
74085fee539SLionel Sambuc 		return NULL;
74185fee539SLionel Sambuc 
74285fee539SLionel Sambuc 	/*
74385fee539SLionel Sambuc 	 * allocate a node for this device and add it to the front of the hash
74485fee539SLionel Sambuc 	 * chain. Note we do not assign remaps values here, so the pt->list
74585fee539SLionel Sambuc 	 * list must be NULL.
74685fee539SLionel Sambuc 	 */
74785fee539SLionel Sambuc 	if ((pt = (DEVT *)malloc(sizeof(DEVT))) == NULL) {
74885fee539SLionel Sambuc 		tty_warn(1, "Device map table out of memory");
74985fee539SLionel Sambuc 		return NULL;
75085fee539SLionel Sambuc 	}
75185fee539SLionel Sambuc 	pt->dev = dev;
75285fee539SLionel Sambuc 	pt->list = NULL;
75385fee539SLionel Sambuc 	pt->fow = dtab[indx];
75485fee539SLionel Sambuc 	dtab[indx] = pt;
75585fee539SLionel Sambuc 	return pt;
75685fee539SLionel Sambuc }
75785fee539SLionel Sambuc /*
75885fee539SLionel Sambuc  * map_dev()
75985fee539SLionel Sambuc  *	given an inode and device storage mask (the mask has a 1 for each bit
76085fee539SLionel Sambuc  *	the archive format is able to store in a header), we check for inode
76185fee539SLionel Sambuc  *	and device truncation and remap the device as required. Device mapping
76285fee539SLionel Sambuc  *	can also occur when during the read phase of append a device number was
76385fee539SLionel Sambuc  *	seen (and was marked as do not use during the write phase). WE ASSUME
76485fee539SLionel Sambuc  *	that unsigned longs are the same size or bigger than the fields used
76585fee539SLionel Sambuc  *	for ino_t and dev_t. If not the types will have to be changed.
76685fee539SLionel Sambuc  * Return:
76785fee539SLionel Sambuc  *	0 if all ok, -1 otherwise.
76885fee539SLionel Sambuc  */
76985fee539SLionel Sambuc 
77085fee539SLionel Sambuc int
map_dev(ARCHD * arcn,u_long dev_mask,u_long ino_mask)77185fee539SLionel Sambuc map_dev(ARCHD *arcn, u_long dev_mask, u_long ino_mask)
77285fee539SLionel Sambuc {
77385fee539SLionel Sambuc 	DEVT *pt;
77485fee539SLionel Sambuc 	DLIST *dpt;
77585fee539SLionel Sambuc 	static dev_t lastdev = 0;	/* next device number to try */
77685fee539SLionel Sambuc 	int trc_ino = 0;
77785fee539SLionel Sambuc 	int trc_dev = 0;
77885fee539SLionel Sambuc 	ino_t trunc_bits = 0;
77985fee539SLionel Sambuc 	ino_t nino;
78085fee539SLionel Sambuc 
78185fee539SLionel Sambuc 	if (dtab == NULL)
78285fee539SLionel Sambuc 		return 0;
78385fee539SLionel Sambuc 	/*
78485fee539SLionel Sambuc 	 * check for device and inode truncation, and extract the truncated
78585fee539SLionel Sambuc 	 * bit pattern.
78685fee539SLionel Sambuc 	 */
78785fee539SLionel Sambuc 	if ((arcn->sb.st_dev & (dev_t)dev_mask) != arcn->sb.st_dev)
78885fee539SLionel Sambuc 		++trc_dev;
78985fee539SLionel Sambuc 	if ((nino = arcn->sb.st_ino & (ino_t)ino_mask) != arcn->sb.st_ino) {
79085fee539SLionel Sambuc 		++trc_ino;
79185fee539SLionel Sambuc 		trunc_bits = arcn->sb.st_ino & (ino_t)(~ino_mask);
79285fee539SLionel Sambuc 	}
79385fee539SLionel Sambuc 
79485fee539SLionel Sambuc 	/*
79585fee539SLionel Sambuc 	 * see if this device is already being mapped, look up the device
79685fee539SLionel Sambuc 	 * then find the truncation bit pattern which applies
79785fee539SLionel Sambuc 	 */
79885fee539SLionel Sambuc 	if ((pt = chk_dev(arcn->sb.st_dev, 0)) != NULL) {
79985fee539SLionel Sambuc 		/*
80085fee539SLionel Sambuc 		 * this device is already marked to be remapped
80185fee539SLionel Sambuc 		 */
80285fee539SLionel Sambuc 		for (dpt = pt->list; dpt != NULL; dpt = dpt->fow)
80385fee539SLionel Sambuc 			if (dpt->trunc_bits == trunc_bits)
80485fee539SLionel Sambuc 				break;
80585fee539SLionel Sambuc 
80685fee539SLionel Sambuc 		if (dpt != NULL) {
80785fee539SLionel Sambuc 			/*
80885fee539SLionel Sambuc 			 * we are being remapped for this device and pattern
80985fee539SLionel Sambuc 			 * change the device number to be stored and return
81085fee539SLionel Sambuc 			 */
81185fee539SLionel Sambuc 			arcn->sb.st_dev = dpt->dev;
81285fee539SLionel Sambuc 			arcn->sb.st_ino = nino;
81385fee539SLionel Sambuc 			return 0;
81485fee539SLionel Sambuc 		}
81585fee539SLionel Sambuc 	} else {
81685fee539SLionel Sambuc 		/*
81785fee539SLionel Sambuc 		 * this device is not being remapped YET. if we do not have any
81885fee539SLionel Sambuc 		 * form of truncation, we do not need a remap
81985fee539SLionel Sambuc 		 */
82085fee539SLionel Sambuc 		if (!trc_ino && !trc_dev)
82185fee539SLionel Sambuc 			return 0;
82285fee539SLionel Sambuc 
82385fee539SLionel Sambuc 		/*
82485fee539SLionel Sambuc 		 * we have truncation, have to add this as a device to remap
82585fee539SLionel Sambuc 		 */
82685fee539SLionel Sambuc 		if ((pt = chk_dev(arcn->sb.st_dev, 1)) == NULL)
82785fee539SLionel Sambuc 			goto bad;
82885fee539SLionel Sambuc 
82985fee539SLionel Sambuc 		/*
83085fee539SLionel Sambuc 		 * if we just have a truncated inode, we have to make sure that
83185fee539SLionel Sambuc 		 * all future inodes that do not truncate (they have the
83285fee539SLionel Sambuc 		 * truncation pattern of all 0's) continue to map to the same
83385fee539SLionel Sambuc 		 * device number. We probably have already written inodes with
83485fee539SLionel Sambuc 		 * this device number to the archive with the truncation
83585fee539SLionel Sambuc 		 * pattern of all 0's. So we add the mapping for all 0's to the
83685fee539SLionel Sambuc 		 * same device number.
83785fee539SLionel Sambuc 		 */
83885fee539SLionel Sambuc 		if (!trc_dev && (trunc_bits != 0)) {
83985fee539SLionel Sambuc 			if ((dpt = (DLIST *)malloc(sizeof(DLIST))) == NULL)
84085fee539SLionel Sambuc 				goto bad;
84185fee539SLionel Sambuc 			dpt->trunc_bits = 0;
84285fee539SLionel Sambuc 			dpt->dev = arcn->sb.st_dev;
84385fee539SLionel Sambuc 			dpt->fow = pt->list;
84485fee539SLionel Sambuc 			pt->list = dpt;
84585fee539SLionel Sambuc 		}
84685fee539SLionel Sambuc 	}
84785fee539SLionel Sambuc 
84885fee539SLionel Sambuc 	/*
84985fee539SLionel Sambuc 	 * look for a device number not being used. We must watch for wrap
85085fee539SLionel Sambuc 	 * around on lastdev (so we do not get stuck looking forever!)
85185fee539SLionel Sambuc 	 */
85285fee539SLionel Sambuc 	while (++lastdev > 0) {
85385fee539SLionel Sambuc 		if (chk_dev(lastdev, 0) != NULL)
85485fee539SLionel Sambuc 			continue;
85585fee539SLionel Sambuc 		/*
85685fee539SLionel Sambuc 		 * found an unused value. If we have reached truncation point
85785fee539SLionel Sambuc 		 * for this format we are hosed, so we give up. Otherwise we
85885fee539SLionel Sambuc 		 * mark it as being used.
85985fee539SLionel Sambuc 		 */
86085fee539SLionel Sambuc 		if (((lastdev & ((dev_t)dev_mask)) != lastdev) ||
86185fee539SLionel Sambuc 		    (chk_dev(lastdev, 1) == NULL))
86285fee539SLionel Sambuc 			goto bad;
86385fee539SLionel Sambuc 		break;
86485fee539SLionel Sambuc 	}
86585fee539SLionel Sambuc 
86685fee539SLionel Sambuc 	if ((lastdev <= 0) || ((dpt = (DLIST *)malloc(sizeof(DLIST))) == NULL))
86785fee539SLionel Sambuc 		goto bad;
86885fee539SLionel Sambuc 
86985fee539SLionel Sambuc 	/*
87085fee539SLionel Sambuc 	 * got a new device number, store it under this truncation pattern.
87185fee539SLionel Sambuc 	 * change the device number this file is being stored with.
87285fee539SLionel Sambuc 	 */
87385fee539SLionel Sambuc 	dpt->trunc_bits = trunc_bits;
87485fee539SLionel Sambuc 	dpt->dev = lastdev;
87585fee539SLionel Sambuc 	dpt->fow = pt->list;
87685fee539SLionel Sambuc 	pt->list = dpt;
87785fee539SLionel Sambuc 	arcn->sb.st_dev = lastdev;
87885fee539SLionel Sambuc 	arcn->sb.st_ino = nino;
87985fee539SLionel Sambuc 	return 0;
88085fee539SLionel Sambuc 
88185fee539SLionel Sambuc     bad:
88285fee539SLionel Sambuc 	tty_warn(1,
88385fee539SLionel Sambuc 	    "Unable to fix truncated inode/device field when storing %s",
88485fee539SLionel Sambuc 	    arcn->name);
88585fee539SLionel Sambuc 	tty_warn(0, "Archive may create improper hard links when extracted");
88685fee539SLionel Sambuc 	return 0;
88785fee539SLionel Sambuc }
88885fee539SLionel Sambuc 
88985fee539SLionel Sambuc /*
89085fee539SLionel Sambuc  * directory access/mod time reset table routines (for directories READ by pax)
89185fee539SLionel Sambuc  *
89285fee539SLionel Sambuc  * The pax -t flag requires that access times of archive files to be the same
89385fee539SLionel Sambuc  * as before being read by pax. For regular files, access time is restored after
89485fee539SLionel Sambuc  * the file has been copied. This database provides the same functionality for
89585fee539SLionel Sambuc  * directories read during file tree traversal. Restoring directory access time
89685fee539SLionel Sambuc  * is more complex than files since directories may be read several times until
89785fee539SLionel Sambuc  * all the descendants in their subtree are visited by fts. Directory access
89885fee539SLionel Sambuc  * and modification times are stored during the fts pre-order visit (done
89985fee539SLionel Sambuc  * before any descendants in the subtree is visited) and restored after the
90085fee539SLionel Sambuc  * fts post-order visit (after all the descendants have been visited). In the
90185fee539SLionel Sambuc  * case of premature exit from a subtree (like from the effects of -n), any
90285fee539SLionel Sambuc  * directory entries left in this database are reset during final cleanup
90385fee539SLionel Sambuc  * operations of pax. Entries are hashed by inode number for fast lookup.
90485fee539SLionel Sambuc  */
90585fee539SLionel Sambuc 
90685fee539SLionel Sambuc /*
90785fee539SLionel Sambuc  * atdir_start()
90885fee539SLionel Sambuc  *	create the directory access time database for directories READ by pax.
90985fee539SLionel Sambuc  * Return:
91085fee539SLionel Sambuc  *	0 is created ok, -1 otherwise.
91185fee539SLionel Sambuc  */
91285fee539SLionel Sambuc 
91385fee539SLionel Sambuc int
atdir_start(void)91485fee539SLionel Sambuc atdir_start(void)
91585fee539SLionel Sambuc {
91685fee539SLionel Sambuc 	if (atab != NULL)
91785fee539SLionel Sambuc 		return 0;
91885fee539SLionel Sambuc 	if ((atab = (ATDIR **)calloc(A_TAB_SZ, sizeof(ATDIR *))) == NULL) {
91985fee539SLionel Sambuc 		tty_warn(1,
92085fee539SLionel Sambuc 		    "Cannot allocate space for directory access time table");
92185fee539SLionel Sambuc 		return -1;
92285fee539SLionel Sambuc 	}
92385fee539SLionel Sambuc 	return 0;
92485fee539SLionel Sambuc }
92585fee539SLionel Sambuc 
92685fee539SLionel Sambuc 
92785fee539SLionel Sambuc /*
92885fee539SLionel Sambuc  * atdir_end()
92985fee539SLionel Sambuc  *	walk through the directory access time table and reset the access time
93085fee539SLionel Sambuc  *	of any directory who still has an entry left in the database. These
93185fee539SLionel Sambuc  *	entries are for directories READ by pax
93285fee539SLionel Sambuc  */
93385fee539SLionel Sambuc 
93485fee539SLionel Sambuc void
atdir_end(void)93585fee539SLionel Sambuc atdir_end(void)
93685fee539SLionel Sambuc {
93785fee539SLionel Sambuc 	ATDIR *pt;
93885fee539SLionel Sambuc 	int i;
93985fee539SLionel Sambuc 
94085fee539SLionel Sambuc 	if (atab == NULL)
94185fee539SLionel Sambuc 		return;
94285fee539SLionel Sambuc 	/*
94385fee539SLionel Sambuc 	 * for each non-empty hash table entry reset all the directories
94485fee539SLionel Sambuc 	 * chained there.
94585fee539SLionel Sambuc 	 */
94685fee539SLionel Sambuc 	for (i = 0; i < A_TAB_SZ; ++i) {
94785fee539SLionel Sambuc 		if ((pt = atab[i]) == NULL)
94885fee539SLionel Sambuc 			continue;
94985fee539SLionel Sambuc 		/*
95085fee539SLionel Sambuc 		 * remember to force the times, set_ftime() looks at pmtime
95185fee539SLionel Sambuc 		 * and patime, which only applies to things CREATED by pax,
95285fee539SLionel Sambuc 		 * not read by pax. Read time reset is controlled by -t.
95385fee539SLionel Sambuc 		 */
95485fee539SLionel Sambuc 		for (; pt != NULL; pt = pt->fow)
95585fee539SLionel Sambuc 			set_ftime(pt->name, pt->mtime, pt->atime, 1, 0);
95685fee539SLionel Sambuc 	}
95785fee539SLionel Sambuc }
95885fee539SLionel Sambuc 
95985fee539SLionel Sambuc /*
96085fee539SLionel Sambuc  * add_atdir()
96185fee539SLionel Sambuc  *	add a directory to the directory access time table. Table is hashed
96285fee539SLionel Sambuc  *	and chained by inode number. This is for directories READ by pax
96385fee539SLionel Sambuc  */
96485fee539SLionel Sambuc 
96585fee539SLionel Sambuc void
add_atdir(char * fname,dev_t dev,ino_t ino,time_t mtime,time_t atime)96685fee539SLionel Sambuc add_atdir(char *fname, dev_t dev, ino_t ino, time_t mtime, time_t atime)
96785fee539SLionel Sambuc {
96885fee539SLionel Sambuc 	ATDIR *pt;
96985fee539SLionel Sambuc 	u_int indx;
97085fee539SLionel Sambuc 
97185fee539SLionel Sambuc 	if (atab == NULL)
97285fee539SLionel Sambuc 		return;
97385fee539SLionel Sambuc 
97485fee539SLionel Sambuc 	/*
97585fee539SLionel Sambuc 	 * make sure this directory is not already in the table, if so just
97685fee539SLionel Sambuc 	 * return (the older entry always has the correct time). The only
97785fee539SLionel Sambuc 	 * way this will happen is when the same subtree can be traversed by
97885fee539SLionel Sambuc 	 * different args to pax and the -n option is aborting fts out of a
97985fee539SLionel Sambuc 	 * subtree before all the post-order visits have been made.
98085fee539SLionel Sambuc 	 */
98185fee539SLionel Sambuc 	indx = ((unsigned)ino) % A_TAB_SZ;
98285fee539SLionel Sambuc 	if ((pt = atab[indx]) != NULL) {
98385fee539SLionel Sambuc 		while (pt != NULL) {
98485fee539SLionel Sambuc 			if ((pt->ino == ino) && (pt->dev == dev))
98585fee539SLionel Sambuc 				break;
98685fee539SLionel Sambuc 			pt = pt->fow;
98785fee539SLionel Sambuc 		}
98885fee539SLionel Sambuc 
98985fee539SLionel Sambuc 		/*
99085fee539SLionel Sambuc 		 * oops, already there. Leave it alone.
99185fee539SLionel Sambuc 		 */
99285fee539SLionel Sambuc 		if (pt != NULL)
99385fee539SLionel Sambuc 			return;
99485fee539SLionel Sambuc 	}
99585fee539SLionel Sambuc 
99685fee539SLionel Sambuc 	/*
99785fee539SLionel Sambuc 	 * add it to the front of the hash chain
99885fee539SLionel Sambuc 	 */
99985fee539SLionel Sambuc 	if ((pt = (ATDIR *)malloc(sizeof(ATDIR))) != NULL) {
100085fee539SLionel Sambuc 		if ((pt->name = strdup(fname)) != NULL) {
100185fee539SLionel Sambuc 			pt->dev = dev;
100285fee539SLionel Sambuc 			pt->ino = ino;
100385fee539SLionel Sambuc 			pt->mtime = mtime;
100485fee539SLionel Sambuc 			pt->atime = atime;
100585fee539SLionel Sambuc 			pt->fow = atab[indx];
100685fee539SLionel Sambuc 			atab[indx] = pt;
100785fee539SLionel Sambuc 			return;
100885fee539SLionel Sambuc 		}
100985fee539SLionel Sambuc 		(void)free((char *)pt);
101085fee539SLionel Sambuc 	}
101185fee539SLionel Sambuc 
101285fee539SLionel Sambuc 	tty_warn(1, "Directory access time reset table ran out of memory");
101385fee539SLionel Sambuc 	return;
101485fee539SLionel Sambuc }
101585fee539SLionel Sambuc 
101685fee539SLionel Sambuc /*
101785fee539SLionel Sambuc  * get_atdir()
101885fee539SLionel Sambuc  *	look up a directory by inode and device number to obtain the access
101985fee539SLionel Sambuc  *	and modification time you want to set to. If found, the modification
102085fee539SLionel Sambuc  *	and access time parameters are set and the entry is removed from the
102185fee539SLionel Sambuc  *	table (as it is no longer needed). These are for directories READ by
102285fee539SLionel Sambuc  *	pax
102385fee539SLionel Sambuc  * Return:
102485fee539SLionel Sambuc  *	0 if found, -1 if not found.
102585fee539SLionel Sambuc  */
102685fee539SLionel Sambuc 
102785fee539SLionel Sambuc int
get_atdir(dev_t dev,ino_t ino,time_t * mtime,time_t * atime)102885fee539SLionel Sambuc get_atdir(dev_t dev, ino_t ino, time_t *mtime, time_t *atime)
102985fee539SLionel Sambuc {
103085fee539SLionel Sambuc 	ATDIR *pt;
103185fee539SLionel Sambuc 	ATDIR **ppt;
103285fee539SLionel Sambuc 	u_int indx;
103385fee539SLionel Sambuc 
103485fee539SLionel Sambuc 	if (atab == NULL)
103585fee539SLionel Sambuc 		return -1;
103685fee539SLionel Sambuc 	/*
103785fee539SLionel Sambuc 	 * hash by inode and search the chain for an inode and device match
103885fee539SLionel Sambuc 	 */
103985fee539SLionel Sambuc 	indx = ((unsigned)ino) % A_TAB_SZ;
104085fee539SLionel Sambuc 	if ((pt = atab[indx]) == NULL)
104185fee539SLionel Sambuc 		return -1;
104285fee539SLionel Sambuc 
104385fee539SLionel Sambuc 	ppt = &(atab[indx]);
104485fee539SLionel Sambuc 	while (pt != NULL) {
104585fee539SLionel Sambuc 		if ((pt->ino == ino) && (pt->dev == dev))
104685fee539SLionel Sambuc 			break;
104785fee539SLionel Sambuc 		/*
104885fee539SLionel Sambuc 		 * no match, go to next one
104985fee539SLionel Sambuc 		 */
105085fee539SLionel Sambuc 		ppt = &(pt->fow);
105185fee539SLionel Sambuc 		pt = pt->fow;
105285fee539SLionel Sambuc 	}
105385fee539SLionel Sambuc 
105485fee539SLionel Sambuc 	/*
105585fee539SLionel Sambuc 	 * return if we did not find it.
105685fee539SLionel Sambuc 	 */
105785fee539SLionel Sambuc 	if (pt == NULL)
105885fee539SLionel Sambuc 		return -1;
105985fee539SLionel Sambuc 
106085fee539SLionel Sambuc 	/*
106185fee539SLionel Sambuc 	 * found it. return the times and remove the entry from the table.
106285fee539SLionel Sambuc 	 */
106385fee539SLionel Sambuc 	*ppt = pt->fow;
106485fee539SLionel Sambuc 	*mtime = pt->mtime;
106585fee539SLionel Sambuc 	*atime = pt->atime;
106685fee539SLionel Sambuc 	(void)free((char *)pt->name);
106785fee539SLionel Sambuc 	(void)free((char *)pt);
106885fee539SLionel Sambuc 	return 0;
106985fee539SLionel Sambuc }
107085fee539SLionel Sambuc 
107185fee539SLionel Sambuc /*
107285fee539SLionel Sambuc  * directory access mode and time storage routines (for directories CREATED
107385fee539SLionel Sambuc  * by pax).
107485fee539SLionel Sambuc  *
107585fee539SLionel Sambuc  * Pax requires that extracted directories, by default, have their access/mod
107685fee539SLionel Sambuc  * times and permissions set to the values specified in the archive. During the
107785fee539SLionel Sambuc  * actions of extracting (and creating the destination subtree during -rw copy)
107885fee539SLionel Sambuc  * directories extracted may be modified after being created. Even worse is
107985fee539SLionel Sambuc  * that these directories may have been created with file permissions which
108085fee539SLionel Sambuc  * prohibits any descendants of these directories from being extracted. When
108185fee539SLionel Sambuc  * directories are created by pax, access rights may be added to permit the
108285fee539SLionel Sambuc  * creation of files in their subtree. Every time pax creates a directory, the
108385fee539SLionel Sambuc  * times and file permissions specified by the archive are stored. After all
108485fee539SLionel Sambuc  * files have been extracted (or copied), these directories have their times
108585fee539SLionel Sambuc  * and file modes reset to the stored values. The directory info is restored in
108685fee539SLionel Sambuc  * reverse order as entries were added to the data file from root to leaf. To
108785fee539SLionel Sambuc  * restore atime properly, we must go backwards. The data file consists of
108885fee539SLionel Sambuc  * records with two parts, the file name followed by a DIRDATA trailer. The
108985fee539SLionel Sambuc  * fixed sized trailer contains the size of the name plus the off_t location in
109085fee539SLionel Sambuc  * the file. To restore we work backwards through the file reading the trailer
109185fee539SLionel Sambuc  * then the file name.
109285fee539SLionel Sambuc  */
109385fee539SLionel Sambuc 
109485fee539SLionel Sambuc #ifndef DIRS_USE_FILE
109585fee539SLionel Sambuc static DIRDATA *dirdata_head;
109685fee539SLionel Sambuc #endif
109785fee539SLionel Sambuc 
109885fee539SLionel Sambuc /*
109985fee539SLionel Sambuc  * dir_start()
110085fee539SLionel Sambuc  *	set up the directory time and file mode storage for directories CREATED
110185fee539SLionel Sambuc  *	by pax.
110285fee539SLionel Sambuc  * Return:
110385fee539SLionel Sambuc  *	0 if ok, -1 otherwise
110485fee539SLionel Sambuc  */
110585fee539SLionel Sambuc 
110685fee539SLionel Sambuc int
dir_start(void)110785fee539SLionel Sambuc dir_start(void)
110885fee539SLionel Sambuc {
110985fee539SLionel Sambuc #ifdef DIRS_USE_FILE
111085fee539SLionel Sambuc 	if (dirfd != -1)
111185fee539SLionel Sambuc 		return 0;
111285fee539SLionel Sambuc 
111385fee539SLionel Sambuc 	/*
111485fee539SLionel Sambuc 	 * unlink the file so it goes away at termination by itself
111585fee539SLionel Sambuc 	 */
111685fee539SLionel Sambuc 	memcpy(tempbase, _TFILE_BASE, sizeof(_TFILE_BASE));
111785fee539SLionel Sambuc 	if ((dirfd = mkstemp(tempfile)) >= 0) {
111885fee539SLionel Sambuc 		(void)unlink(tempfile);
111985fee539SLionel Sambuc 		return 0;
112085fee539SLionel Sambuc 	}
112185fee539SLionel Sambuc 	tty_warn(1, "Unable to create temporary file for directory times: %s",
112285fee539SLionel Sambuc 	    tempfile);
112385fee539SLionel Sambuc 	return -1;
112485fee539SLionel Sambuc #else
112585fee539SLionel Sambuc 	return (0);
112685fee539SLionel Sambuc #endif /* DIRS_USE_FILE */
112785fee539SLionel Sambuc }
112885fee539SLionel Sambuc 
112985fee539SLionel Sambuc /*
113085fee539SLionel Sambuc  * add_dir()
113185fee539SLionel Sambuc  *	add the mode and times for a newly CREATED directory
113285fee539SLionel Sambuc  *	name is name of the directory, psb the stat buffer with the data in it,
113385fee539SLionel Sambuc  *	frc_mode is a flag that says whether to force the setting of the mode
113485fee539SLionel Sambuc  *	(ignoring the user set values for preserving file mode). Frc_mode is
113585fee539SLionel Sambuc  *	for the case where we created a file and found that the resulting
113685fee539SLionel Sambuc  *	directory was not writable and the user asked for file modes to NOT
113785fee539SLionel Sambuc  *	be preserved. (we have to preserve what was created by default, so we
113885fee539SLionel Sambuc  *	have to force the setting at the end. this is stated explicitly in the
113985fee539SLionel Sambuc  *	pax spec)
114085fee539SLionel Sambuc  */
114185fee539SLionel Sambuc 
114285fee539SLionel Sambuc void
add_dir(char * name,int nlen,struct stat * psb,int frc_mode)114385fee539SLionel Sambuc add_dir(char *name, int nlen, struct stat *psb, int frc_mode)
114485fee539SLionel Sambuc {
114585fee539SLionel Sambuc #ifdef DIRS_USE_FILE
114685fee539SLionel Sambuc 	DIRDATA dblk;
114785fee539SLionel Sambuc #else
114885fee539SLionel Sambuc 	DIRDATA *dblk;
114985fee539SLionel Sambuc #endif
115085fee539SLionel Sambuc 	char realname[MAXPATHLEN], *rp;
115185fee539SLionel Sambuc 
115285fee539SLionel Sambuc 	if (havechd && *name != '/') {
115385fee539SLionel Sambuc 		if ((rp = realpath(name, realname)) == NULL) {
115485fee539SLionel Sambuc 			tty_warn(1, "Cannot canonicalize %s", name);
115585fee539SLionel Sambuc 			return;
115685fee539SLionel Sambuc 		}
115785fee539SLionel Sambuc 		name = rp;
1158*84d9c625SLionel Sambuc #ifdef DIRS_USE_FILE
115985fee539SLionel Sambuc 		nlen = strlen(name);
1160*84d9c625SLionel Sambuc #endif
116185fee539SLionel Sambuc 	}
116285fee539SLionel Sambuc 
116385fee539SLionel Sambuc #ifdef DIRS_USE_FILE
116485fee539SLionel Sambuc 	if (dirfd < 0)
116585fee539SLionel Sambuc 		return;
116685fee539SLionel Sambuc 
116785fee539SLionel Sambuc 	/*
116885fee539SLionel Sambuc 	 * get current position (where file name will start) so we can store it
116985fee539SLionel Sambuc 	 * in the trailer
117085fee539SLionel Sambuc 	 */
117185fee539SLionel Sambuc 	if ((dblk.npos = lseek(dirfd, 0L, SEEK_CUR)) < 0) {
117285fee539SLionel Sambuc 		tty_warn(1,
117385fee539SLionel Sambuc 		    "Unable to store mode and times for directory: %s",name);
117485fee539SLionel Sambuc 		return;
117585fee539SLionel Sambuc 	}
117685fee539SLionel Sambuc 
117785fee539SLionel Sambuc 	/*
117885fee539SLionel Sambuc 	 * write the file name followed by the trailer
117985fee539SLionel Sambuc 	 */
118085fee539SLionel Sambuc 	dblk.nlen = nlen + 1;
118185fee539SLionel Sambuc 	dblk.mode = psb->st_mode & 0xffff;
118285fee539SLionel Sambuc 	dblk.mtime = psb->st_mtime;
118385fee539SLionel Sambuc 	dblk.atime = psb->st_atime;
118485fee539SLionel Sambuc #if HAVE_STRUCT_STAT_ST_FLAGS
118585fee539SLionel Sambuc 	dblk.fflags = psb->st_flags;
118685fee539SLionel Sambuc #else
118785fee539SLionel Sambuc 	dblk.fflags = 0;
118885fee539SLionel Sambuc #endif
118985fee539SLionel Sambuc 	dblk.frc_mode = frc_mode;
119085fee539SLionel Sambuc 	if ((xwrite(dirfd, name, dblk.nlen) == dblk.nlen) &&
119185fee539SLionel Sambuc 	    (xwrite(dirfd, (char *)&dblk, sizeof(dblk)) == sizeof(dblk))) {
119285fee539SLionel Sambuc 		++dircnt;
119385fee539SLionel Sambuc 		return;
119485fee539SLionel Sambuc 	}
119585fee539SLionel Sambuc 
119685fee539SLionel Sambuc 	tty_warn(1,
119785fee539SLionel Sambuc 	    "Unable to store mode and times for created directory: %s",name);
119885fee539SLionel Sambuc 	return;
119985fee539SLionel Sambuc #else
120085fee539SLionel Sambuc 
120185fee539SLionel Sambuc 	if ((dblk = malloc(sizeof(*dblk))) == NULL ||
120285fee539SLionel Sambuc 	    (dblk->name = strdup(name)) == NULL) {
120385fee539SLionel Sambuc 		tty_warn(1,
120485fee539SLionel Sambuc 		    "Unable to store mode and times for directory: %s",name);
120585fee539SLionel Sambuc 		if (dblk != NULL)
120685fee539SLionel Sambuc 			free(dblk);
120785fee539SLionel Sambuc 		return;
120885fee539SLionel Sambuc 	}
120985fee539SLionel Sambuc 
121085fee539SLionel Sambuc 	dblk->mode = psb->st_mode & 0xffff;
121185fee539SLionel Sambuc 	dblk->mtime = psb->st_mtime;
121285fee539SLionel Sambuc 	dblk->atime = psb->st_atime;
121385fee539SLionel Sambuc #if HAVE_STRUCT_STAT_ST_FLAGS
121485fee539SLionel Sambuc 	dblk->fflags = psb->st_flags;
121585fee539SLionel Sambuc #else
121685fee539SLionel Sambuc 	dblk->fflags = 0;
121785fee539SLionel Sambuc #endif
121885fee539SLionel Sambuc 	dblk->frc_mode = frc_mode;
121985fee539SLionel Sambuc 
122085fee539SLionel Sambuc 	dblk->next = dirdata_head;
122185fee539SLionel Sambuc 	dirdata_head = dblk;
122285fee539SLionel Sambuc 	return;
122385fee539SLionel Sambuc #endif /* DIRS_USE_FILE */
122485fee539SLionel Sambuc }
122585fee539SLionel Sambuc 
122685fee539SLionel Sambuc /*
122785fee539SLionel Sambuc  * proc_dir()
122885fee539SLionel Sambuc  *	process all file modes and times stored for directories CREATED
122985fee539SLionel Sambuc  *	by pax
123085fee539SLionel Sambuc  */
123185fee539SLionel Sambuc 
123285fee539SLionel Sambuc void
proc_dir(void)123385fee539SLionel Sambuc proc_dir(void)
123485fee539SLionel Sambuc {
123585fee539SLionel Sambuc #ifdef DIRS_USE_FILE
123685fee539SLionel Sambuc 	char name[PAXPATHLEN+1];
123785fee539SLionel Sambuc 	DIRDATA dblk;
123885fee539SLionel Sambuc 	u_long cnt;
123985fee539SLionel Sambuc 
124085fee539SLionel Sambuc 	if (dirfd < 0)
124185fee539SLionel Sambuc 		return;
124285fee539SLionel Sambuc 	/*
124385fee539SLionel Sambuc 	 * read backwards through the file and process each directory
124485fee539SLionel Sambuc 	 */
124585fee539SLionel Sambuc 	for (cnt = 0; cnt < dircnt; ++cnt) {
124685fee539SLionel Sambuc 		/*
124785fee539SLionel Sambuc 		 * read the trailer, then the file name, if this fails
124885fee539SLionel Sambuc 		 * just give up.
124985fee539SLionel Sambuc 		 */
125085fee539SLionel Sambuc 		if (lseek(dirfd, -((off_t)sizeof(dblk)), SEEK_CUR) < 0)
125185fee539SLionel Sambuc 			break;
125285fee539SLionel Sambuc 		if (xread(dirfd,(char *)&dblk, sizeof(dblk)) != sizeof(dblk))
125385fee539SLionel Sambuc 			break;
125485fee539SLionel Sambuc 		if (lseek(dirfd, dblk.npos, SEEK_SET) < 0)
125585fee539SLionel Sambuc 			break;
125685fee539SLionel Sambuc 		if (xread(dirfd, name, dblk.nlen) != dblk.nlen)
125785fee539SLionel Sambuc 			break;
125885fee539SLionel Sambuc 		if (lseek(dirfd, dblk.npos, SEEK_SET) < 0)
125985fee539SLionel Sambuc 			break;
126085fee539SLionel Sambuc 
126185fee539SLionel Sambuc 		/*
126285fee539SLionel Sambuc 		 * frc_mode set, make sure we set the file modes even if
126385fee539SLionel Sambuc 		 * the user didn't ask for it (see file_subs.c for more info)
126485fee539SLionel Sambuc 		 */
126585fee539SLionel Sambuc 		if (pmode || dblk.frc_mode)
126685fee539SLionel Sambuc 			set_pmode(name, dblk.mode);
126785fee539SLionel Sambuc 		if (patime || pmtime)
126885fee539SLionel Sambuc 			set_ftime(name, dblk.mtime, dblk.atime, 0, 0);
126985fee539SLionel Sambuc 		if (pfflags)
127085fee539SLionel Sambuc 			set_chflags(name, dblk.fflags);
127185fee539SLionel Sambuc 	}
127285fee539SLionel Sambuc 
127385fee539SLionel Sambuc 	(void)close(dirfd);
127485fee539SLionel Sambuc 	dirfd = -1;
127585fee539SLionel Sambuc 	if (cnt != dircnt)
127685fee539SLionel Sambuc 		tty_warn(1,
127785fee539SLionel Sambuc 		    "Unable to set mode and times for created directories");
127885fee539SLionel Sambuc 	return;
127985fee539SLionel Sambuc #else
128085fee539SLionel Sambuc 	DIRDATA *dblk;
128185fee539SLionel Sambuc 
128285fee539SLionel Sambuc 	for (dblk = dirdata_head; dblk != NULL; dblk = dirdata_head) {
128385fee539SLionel Sambuc 		dirdata_head = dblk->next;
128485fee539SLionel Sambuc 
128585fee539SLionel Sambuc 		/*
128685fee539SLionel Sambuc 		 * frc_mode set, make sure we set the file modes even if
128785fee539SLionel Sambuc 		 * the user didn't ask for it (see file_subs.c for more info)
128885fee539SLionel Sambuc 		 */
128985fee539SLionel Sambuc 		if (pmode || dblk->frc_mode)
129085fee539SLionel Sambuc 			set_pmode(dblk->name, dblk->mode);
129185fee539SLionel Sambuc 		if (patime || pmtime)
129285fee539SLionel Sambuc 			set_ftime(dblk->name, dblk->mtime, dblk->atime, 0, 0);
129385fee539SLionel Sambuc 		if (pfflags)
129485fee539SLionel Sambuc 			set_chflags(dblk->name, dblk->fflags);
129585fee539SLionel Sambuc 
129685fee539SLionel Sambuc 		free(dblk->name);
129785fee539SLionel Sambuc 		free(dblk);
129885fee539SLionel Sambuc 	}
129985fee539SLionel Sambuc #endif /* DIRS_USE_FILE */
130085fee539SLionel Sambuc }
130185fee539SLionel Sambuc 
130285fee539SLionel Sambuc /*
130385fee539SLionel Sambuc  * database independent routines
130485fee539SLionel Sambuc  */
130585fee539SLionel Sambuc 
130685fee539SLionel Sambuc /*
130785fee539SLionel Sambuc  * st_hash()
130885fee539SLionel Sambuc  *	hashes filenames to a u_int for hashing into a table. Looks at the tail
130985fee539SLionel Sambuc  *	end of file, as this provides far better distribution than any other
131085fee539SLionel Sambuc  *	part of the name. For performance reasons we only care about the last
131185fee539SLionel Sambuc  *	MAXKEYLEN chars (should be at LEAST large enough to pick off the file
131285fee539SLionel Sambuc  *	name). Was tested on 500,000 name file tree traversal from the root
131385fee539SLionel Sambuc  *	and gave almost a perfectly uniform distribution of keys when used with
131485fee539SLionel Sambuc  *	prime sized tables (MAXKEYLEN was 128 in test). Hashes (sizeof int)
131585fee539SLionel Sambuc  *	chars at a time and pads with 0 for last addition.
131685fee539SLionel Sambuc  * Return:
131785fee539SLionel Sambuc  *	the hash value of the string MOD (%) the table size.
131885fee539SLionel Sambuc  */
131985fee539SLionel Sambuc 
132085fee539SLionel Sambuc u_int
st_hash(char * name,int len,int tabsz)132185fee539SLionel Sambuc st_hash(char *name, int len, int tabsz)
132285fee539SLionel Sambuc {
132385fee539SLionel Sambuc 	char *pt;
132485fee539SLionel Sambuc 	char *dest;
132585fee539SLionel Sambuc 	char *end;
132685fee539SLionel Sambuc 	int i;
132785fee539SLionel Sambuc 	u_int key = 0;
132885fee539SLionel Sambuc 	int steps;
132985fee539SLionel Sambuc 	int res;
133085fee539SLionel Sambuc 	u_int val;
133185fee539SLionel Sambuc 
133285fee539SLionel Sambuc 	/*
133385fee539SLionel Sambuc 	 * only look at the tail up to MAXKEYLEN, we do not need to waste
133485fee539SLionel Sambuc 	 * time here (remember these are pathnames, the tail is what will
133585fee539SLionel Sambuc 	 * spread out the keys)
133685fee539SLionel Sambuc 	 */
133785fee539SLionel Sambuc 	if (len > MAXKEYLEN) {
133885fee539SLionel Sambuc 		pt = &(name[len - MAXKEYLEN]);
133985fee539SLionel Sambuc 		len = MAXKEYLEN;
134085fee539SLionel Sambuc 	} else
134185fee539SLionel Sambuc 		pt = name;
134285fee539SLionel Sambuc 
134385fee539SLionel Sambuc 	/*
134485fee539SLionel Sambuc 	 * calculate the number of u_int size steps in the string and if
134585fee539SLionel Sambuc 	 * there is a runt to deal with
134685fee539SLionel Sambuc 	 */
134785fee539SLionel Sambuc 	steps = len/sizeof(u_int);
134885fee539SLionel Sambuc 	res = len % sizeof(u_int);
134985fee539SLionel Sambuc 
135085fee539SLionel Sambuc 	/*
135185fee539SLionel Sambuc 	 * add up the value of the string in unsigned integer sized pieces
135285fee539SLionel Sambuc 	 * too bad we cannot have unsigned int aligned strings, then we
135385fee539SLionel Sambuc 	 * could avoid the expensive copy.
135485fee539SLionel Sambuc 	 */
135585fee539SLionel Sambuc 	for (i = 0; i < steps; ++i) {
135685fee539SLionel Sambuc 		end = pt + sizeof(u_int);
135785fee539SLionel Sambuc 		dest = (char *)&val;
135885fee539SLionel Sambuc 		while (pt < end)
135985fee539SLionel Sambuc 			*dest++ = *pt++;
136085fee539SLionel Sambuc 		key += val;
136185fee539SLionel Sambuc 	}
136285fee539SLionel Sambuc 
136385fee539SLionel Sambuc 	/*
136485fee539SLionel Sambuc 	 * add in the runt padded with zero to the right
136585fee539SLionel Sambuc 	 */
136685fee539SLionel Sambuc 	if (res) {
136785fee539SLionel Sambuc 		val = 0;
136885fee539SLionel Sambuc 		end = pt + res;
136985fee539SLionel Sambuc 		dest = (char *)&val;
137085fee539SLionel Sambuc 		while (pt < end)
137185fee539SLionel Sambuc 			*dest++ = *pt++;
137285fee539SLionel Sambuc 		key += val;
137385fee539SLionel Sambuc 	}
137485fee539SLionel Sambuc 
137585fee539SLionel Sambuc 	/*
137685fee539SLionel Sambuc 	 * return the result mod the table size
137785fee539SLionel Sambuc 	 */
137885fee539SLionel Sambuc 	return key % tabsz;
137985fee539SLionel Sambuc }
1380