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