tables.c

Go to the documentation of this file.
00001 /*-
00002  * Copyright (c) 1992 Keith Muller.
00003  * Copyright (c) 1992, 1993
00004  *      The Regents of the University of California.  All rights reserved.
00005  *
00006  * This code is derived from software contributed to Berkeley by
00007  * Keith Muller of the University of California, San Diego.
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted provided that the following conditions
00011  * are met:
00012  * 1. Redistributions of source code must retain the above copyright
00013  *    notice, this list of conditions and the following disclaimer.
00014  * 2. Redistributions in binary form must reproduce the above copyright
00015  *    notice, this list of conditions and the following disclaimer in the
00016  *    documentation and/or other materials provided with the distribution.
00017  * 4. Neither the name of the University nor the names of its contributors
00018  *    may be used to endorse or promote products derived from this software
00019  *    without specific prior written permission.
00020  *
00021  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00022  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00023  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00024  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00025  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00026  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00027  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00028  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00030  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00031  * SUCH DAMAGE.
00032  */
00033 
00034 #ifndef lint
00035 #if 0
00036 static char sccsid[] = "@(#)tables.c    8.1 (Berkeley) 5/31/93";
00037 #endif
00038 #endif /* not lint */
00039 
00040 #include <sys/types.h>
00041 #include <sys/time.h>
00042 #include <sys/stat.h>
00043 #include <fcntl.h>
00044 #include <errno.h>
00045 #include <stdio.h>
00046 #include <stdlib.h>
00047 #include <string.h>
00048 #include <unistd.h>
00049 #include "pax.h"
00050 #include "tables.h"
00051 #include "extern.h"
00052 
00053 /*
00054  * Routines for controlling the contents of all the different databases pax
00055  * keeps. Tables are dynamically created only when they are needed. The
00056  * goal was speed and the ability to work with HUGE archives. The databases
00057  * were kept simple, but do have complex rules for when the contents change.
00058  * As of this writing, the POSIX library functions were more complex than
00059  * needed for this application (pax databases have very short lifetimes and
00060  * do not survive after pax is finished). Pax is required to handle very
00061  * large archives. These database routines carefully combine memory usage and
00062  * temporary file storage in ways which will not significantly impact runtime
00063  * performance while allowing the largest possible archives to be handled.
00064  * Trying to force the fit to the POSIX databases routines was not considered
00065  * time well spent.
00066  */
00067 
00068 static HRDLNK **ltab = NULL;    /* hard link table for detecting hard links */
00069 static FTM **ftab = NULL;       /* file time table for updating arch */
00070 static NAMT **ntab = NULL;      /* interactive rename storage table */
00071 static DEVT **dtab = NULL;      /* device/inode mapping tables */
00072 static ATDIR **atab = NULL;     /* file tree directory time reset table */
00073 static int dirfd = -1;          /* storage for setting created dir time/mode */
00074 static u_long dircnt;           /* entries in dir time/mode storage */
00075 static int ffd = -1;            /* tmp file for file time table name storage */
00076 
00077 static DEVT *chk_dev(dev_t, int);
00078 
00079 /*
00080  * hard link table routines
00081  *
00082  * The hard link table tries to detect hard links to files using the device and
00083  * inode values. We do this when writing an archive, so we can tell the format
00084  * write routine that this file is a hard link to another file. The format
00085  * write routine then can store this file in whatever way it wants (as a hard
00086  * link if the format supports that like tar, or ignore this info like cpio).
00087  * (Actually a field in the format driver table tells us if the format wants
00088  * hard link info. if not, we do not waste time looking for them). We also use
00089  * the same table when reading an archive. In that situation, this table is
00090  * used by the format read routine to detect hard links from stored dev and
00091  * inode numbers (like cpio). This will allow pax to create a link when one
00092  * can be detected by the archive format.
00093  */
00094 
00095 /*
00096  * lnk_start
00097  *      Creates the hard link table.
00098  * Return:
00099  *      0 if created, -1 if failure
00100  */
00101 
00102 int
00103 lnk_start(void)
00104 {
00105         if (ltab != NULL)
00106                 return(0);
00107         if ((ltab = (HRDLNK **)calloc(L_TAB_SZ, sizeof(HRDLNK *))) == NULL) {
00108                 paxwarn(1, "Cannot allocate memory for hard link table");
00109                 return(-1);
00110         }
00111         return(0);
00112 }
00113 
00114 /*
00115  * chk_lnk()
00116  *      Looks up entry in hard link hash table. If found, it copies the name
00117  *      of the file it is linked to (we already saw that file) into ln_name.
00118  *      lnkcnt is decremented and if goes to 1 the node is deleted from the
00119  *      database. (We have seen all the links to this file). If not found,
00120  *      we add the file to the database if it has the potential for having
00121  *      hard links to other files we may process (it has a link count > 1)
00122  * Return:
00123  *      if found returns 1; if not found returns 0; -1 on error
00124  */
00125 
00126 int
00127 chk_lnk(ARCHD *arcn)
00128 {
00129         HRDLNK *pt;
00130         HRDLNK **ppt;
00131         u_int indx;
00132 
00133         if (ltab == NULL)
00134                 return(-1);
00135         /*
00136          * ignore those nodes that cannot have hard links
00137          */
00138         if ((arcn->type == PAX_DIR) || (arcn->sb.st_nlink <= 1))
00139                 return(0);
00140 
00141         /*
00142          * hash inode number and look for this file
00143          */
00144         indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ;
00145         if ((pt = ltab[indx]) != NULL) {
00146                 /*
00147                  * it's hash chain in not empty, walk down looking for it
00148                  */
00149                 ppt = &(ltab[indx]);
00150                 while (pt != NULL) {
00151                         if ((pt->ino == arcn->sb.st_ino) &&
00152                             (pt->dev == arcn->sb.st_dev))
00153                                 break;
00154                         ppt = &(pt->fow);
00155                         pt = pt->fow;
00156                 }
00157 
00158                 if (pt != NULL) {
00159                         /*
00160                          * found a link. set the node type and copy in the
00161                          * name of the file it is to link to. we need to
00162                          * handle hardlinks to regular files differently than
00163                          * other links.
00164                          */
00165                         arcn->ln_nlen = l_strncpy(arcn->ln_name, pt->name,
00166                                 sizeof(arcn->ln_name) - 1);
00167                         arcn->ln_name[arcn->ln_nlen] = '\0';
00168                         if (arcn->type == PAX_REG)
00169                                 arcn->type = PAX_HRG;
00170                         else
00171                                 arcn->type = PAX_HLK;
00172 
00173                         /*
00174                          * if we have found all the links to this file, remove
00175                          * it from the database
00176                          */
00177                         if (--pt->nlink <= 1) {
00178                                 *ppt = pt->fow;
00179                                 (void)free((char *)pt->name);
00180                                 (void)free((char *)pt);
00181                         }
00182                         return(1);
00183                 }
00184         }
00185 
00186         /*
00187          * we never saw this file before. It has links so we add it to the
00188          * front of this hash chain
00189          */
00190         if ((pt = (HRDLNK *)malloc(sizeof(HRDLNK))) != NULL) {
00191                 if ((pt->name = strdup(arcn->name)) != NULL) {
00192                         pt->dev = arcn->sb.st_dev;
00193                         pt->ino = arcn->sb.st_ino;
00194                         pt->nlink = arcn->sb.st_nlink;
00195                         pt->fow = ltab[indx];
00196                         ltab[indx] = pt;
00197                         return(0);
00198                 }
00199                 (void)free((char *)pt);
00200         }
00201 
00202         paxwarn(1, "Hard link table out of memory");
00203         return(-1);
00204 }
00205 
00206 /*
00207  * purg_lnk
00208  *      remove reference for a file that we may have added to the data base as
00209  *      a potential source for hard links. We ended up not using the file, so
00210  *      we do not want to accidently point another file at it later on.
00211  */
00212 
00213 void
00214 purg_lnk(ARCHD *arcn)
00215 {
00216         HRDLNK *pt;
00217         HRDLNK **ppt;
00218         u_int indx;
00219 
00220         if (ltab == NULL)
00221                 return;
00222         /*
00223          * do not bother to look if it could not be in the database
00224          */
00225         if ((arcn->sb.st_nlink <= 1) || (arcn->type == PAX_DIR) ||
00226             (arcn->type == PAX_HLK) || (arcn->type == PAX_HRG))
00227                 return;
00228 
00229         /*
00230          * find the hash chain for this inode value, if empty return
00231          */
00232         indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ;
00233         if ((pt = ltab[indx]) == NULL)
00234                 return;
00235 
00236         /*
00237          * walk down the list looking for the inode/dev pair, unlink and
00238          * free if found
00239          */
00240         ppt = &(ltab[indx]);
00241         while (pt != NULL) {
00242                 if ((pt->ino == arcn->sb.st_ino) &&
00243                     (pt->dev == arcn->sb.st_dev))
00244                         break;
00245                 ppt = &(pt->fow);
00246                 pt = pt->fow;
00247         }
00248         if (pt == NULL)
00249                 return;
00250 
00251         /*
00252          * remove and free it
00253          */
00254         *ppt = pt->fow;
00255         (void)free((char *)pt->name);
00256         (void)free((char *)pt);
00257 }
00258 
00259 /*
00260  * lnk_end()
00261  *      Pull apart an existing link table so we can reuse it. We do this between
00262  *      read and write phases of append with update. (The format may have
00263  *      used the link table, and we need to start with a fresh table for the
00264  *      write phase).
00265  */
00266 
00267 void
00268 lnk_end(void)
00269 {
00270         int i;
00271         HRDLNK *pt;
00272         HRDLNK *ppt;
00273 
00274         if (ltab == NULL)
00275                 return;
00276 
00277         for (i = 0; i < L_TAB_SZ; ++i) {
00278                 if (ltab[i] == NULL)
00279                         continue;
00280                 pt = ltab[i];
00281                 ltab[i] = NULL;
00282 
00283                 /*
00284                  * free up each entry on this chain
00285                  */
00286                 while (pt != NULL) {
00287                         ppt = pt;
00288                         pt = ppt->fow;
00289                         (void)free((char *)ppt->name);
00290                         (void)free((char *)ppt);
00291                 }
00292         }
00293         return;
00294 }
00295 
00296 /*
00297  * modification time table routines
00298  *
00299  * The modification time table keeps track of last modification times for all
00300  * files stored in an archive during a write phase when -u is set. We only
00301  * add a file to the archive if it is newer than a file with the same name
00302  * already stored on the archive (if there is no other file with the same
00303  * name on the archive it is added). This applies to writes and appends.
00304  * An append with an -u must read the archive and store the modification time
00305  * for every file on that archive before starting the write phase. It is clear
00306  * that this is one HUGE database. To save memory space, the actual file names
00307  * are stored in a scatch file and indexed by an in memory hash table. The
00308  * hash table is indexed by hashing the file path. The nodes in the table store
00309  * the length of the filename and the lseek offset within the scratch file
00310  * where the actual name is stored. Since there are never any deletions to this
00311  * table, fragmentation of the scratch file is never an issue. Lookups seem to
00312  * not exhibit any locality at all (files in the database are rarely
00313  * looked up more than once...). So caching is just a waste of memory. The
00314  * only limitation is the amount of scatch file space available to store the
00315  * path names.
00316  */
00317 
00318 /*
00319  * ftime_start()
00320  *      create the file time hash table and open for read/write the scratch
00321  *      file. (after created it is unlinked, so when we exit we leave
00322  *      no witnesses).
00323  * Return:
00324  *      0 if the table and file was created ok, -1 otherwise
00325  */
00326 
00327 int
00328 ftime_start(void)
00329 {
00330 
00331         if (ftab != NULL)
00332                 return(0);
00333         if ((ftab = (FTM **)calloc(F_TAB_SZ, sizeof(FTM *))) == NULL) {
00334                 paxwarn(1, "Cannot allocate memory for file time table");
00335                 return(-1);
00336         }
00337 
00338         /*
00339          * get random name and create temporary scratch file, unlink name
00340          * so it will get removed on exit
00341          */
00342         memcpy(tempbase, _TFILE_BASE, sizeof(_TFILE_BASE));
00343         if ((ffd = mkstemp(tempfile)) < 0) {
00344                 syswarn(1, errno, "Unable to create temporary file: %s",
00345                     tempfile);
00346                 return(-1);
00347         }
00348         (void)unlink(tempfile);
00349 
00350         return(0);
00351 }
00352 
00353 /*
00354  * chk_ftime()
00355  *      looks up entry in file time hash table. If not found, the file is
00356  *      added to the hash table and the file named stored in the scratch file.
00357  *      If a file with the same name is found, the file times are compared and
00358  *      the most recent file time is retained. If the new file was younger (or
00359  *      was not in the database) the new file is selected for storage.
00360  * Return:
00361  *      0 if file should be added to the archive, 1 if it should be skipped,
00362  *      -1 on error
00363  */
00364 
00365 int
00366 chk_ftime(ARCHD *arcn)
00367 {
00368         FTM *pt;
00369         int namelen;
00370         u_int indx;
00371         char ckname[PAXPATHLEN+1];
00372 
00373         /*
00374          * no info, go ahead and add to archive
00375          */
00376         if (ftab == NULL)
00377                 return(0);
00378 
00379         /*
00380          * hash the pathname and look up in table
00381          */
00382         namelen = arcn->nlen;
00383         indx = st_hash(arcn->name, namelen, F_TAB_SZ);
00384         if ((pt = ftab[indx]) != NULL) {
00385                 /*
00386                  * the hash chain is not empty, walk down looking for match
00387                  * only read up the path names if the lengths match, speeds
00388                  * up the search a lot
00389                  */
00390                 while (pt != NULL) {
00391                         if (pt->namelen == namelen) {
00392                                 /*
00393                                  * potential match, have to read the name
00394                                  * from the scratch file.
00395                                  */
00396                                 if (lseek(ffd,pt->seek,SEEK_SET) != pt->seek) {
00397                                         syswarn(1, errno,
00398                                             "Failed ftime table seek");
00399                                         return(-1);
00400                                 }
00401                                 if (read(ffd, ckname, namelen) != namelen) {
00402                                         syswarn(1, errno,
00403                                             "Failed ftime table read");
00404                                         return(-1);
00405                                 }
00406 
00407                                 /*
00408                                  * if the names match, we are done
00409                                  */
00410                                 if (!strncmp(ckname, arcn->name, namelen))
00411                                         break;
00412                         }
00413 
00414                         /*
00415                          * try the next entry on the chain
00416                          */
00417                         pt = pt->fow;
00418                 }
00419 
00420                 if (pt != NULL) {
00421                         /*
00422                          * found the file, compare the times, save the newer
00423                          */
00424                         if (arcn->sb.st_mtime > pt->mtime) {
00425                                 /*
00426                                  * file is newer
00427                                  */
00428                                 pt->mtime = arcn->sb.st_mtime;
00429                                 return(0);
00430                         }
00431                         /*
00432                          * file is older
00433                          */
00434                         return(1);
00435                 }
00436         }
00437 
00438         /*
00439          * not in table, add it
00440          */
00441         if ((pt = (FTM *)malloc(sizeof(FTM))) != NULL) {
00442                 /*
00443                  * add the name at the end of the scratch file, saving the
00444                  * offset. add the file to the head of the hash chain
00445                  */
00446                 if ((pt->seek = lseek(ffd, (off_t)0, SEEK_END)) >= 0) {
00447                         if (write(ffd, arcn->name, namelen) == namelen) {
00448                                 pt->mtime = arcn->sb.st_mtime;
00449                                 pt->namelen = namelen;
00450                                 pt->fow = ftab[indx];
00451                                 ftab[indx] = pt;
00452                                 return(0);
00453                         }
00454                         syswarn(1, errno, "Failed write to file time table");
00455                 } else
00456                         syswarn(1, errno, "Failed seek on file time table");
00457         } else
00458                 paxwarn(1, "File time table ran out of memory");
00459 
00460         if (pt != NULL)
00461                 (void)free((char *)pt);
00462         return(-1);
00463 }
00464 
00465 /*
00466  * Interactive rename table routines
00467  *
00468  * The interactive rename table keeps track of the new names that the user
00469  * assigns to files from tty input. Since this map is unique for each file
00470  * we must store it in case there is a reference to the file later in archive
00471  * (a link). Otherwise we will be unable to find the file we know was
00472  * extracted. The remapping of these files is stored in a memory based hash
00473  * table (it is assumed since input must come from /dev/tty, it is unlikely to
00474  * be a very large table).
00475  */
00476 
00477 /*
00478  * name_start()
00479  *      create the interactive rename table
00480  * Return:
00481  *      0 if successful, -1 otherwise
00482  */
00483 
00484 int
00485 name_start(void)
00486 {
00487         if (ntab != NULL)
00488                 return(0);
00489         if ((ntab = (NAMT **)calloc(N_TAB_SZ, sizeof(NAMT *))) == NULL) {
00490                 paxwarn(1, "Cannot allocate memory for interactive rename table");
00491                 return(-1);
00492         }
00493         return(0);
00494 }
00495 
00496 /*
00497  * add_name()
00498  *      add the new name to old name mapping just created by the user.
00499  *      If an old name mapping is found (there may be duplicate names on an
00500  *      archive) only the most recent is kept.
00501  * Return:
00502  *      0 if added, -1 otherwise
00503  */
00504 
00505 int
00506 add_name(char *oname, int onamelen, char *nname)
00507 {
00508         NAMT *pt;
00509         u_int indx;
00510 
00511         if (ntab == NULL) {
00512                 /*
00513                  * should never happen
00514                  */
00515                 paxwarn(0, "No interactive rename table, links may fail\n");
00516                 return(0);
00517         }
00518 
00519         /*
00520          * look to see if we have already mapped this file, if so we
00521          * will update it
00522          */
00523         indx = st_hash(oname, onamelen, N_TAB_SZ);
00524         if ((pt = ntab[indx]) != NULL) {
00525                 /*
00526                  * look down the has chain for the file
00527                  */
00528                 while ((pt != NULL) && (strcmp(oname, pt->oname) != 0))
00529                         pt = pt->fow;
00530 
00531                 if (pt != NULL) {
00532                         /*
00533                          * found an old mapping, replace it with the new one
00534                          * the user just input (if it is different)
00535                          */
00536                         if (strcmp(nname, pt->nname) == 0)
00537                                 return(0);
00538 
00539                         (void)free((char *)pt->nname);
00540                         if ((pt->nname = strdup(nname)) == NULL) {
00541                                 paxwarn(1, "Cannot update rename table");
00542                                 return(-1);
00543                         }
00544                         return(0);
00545                 }
00546         }
00547 
00548         /*
00549          * this is a new mapping, add it to the table
00550          */
00551         if ((pt = (NAMT *)malloc(sizeof(NAMT))) != NULL) {
00552                 if ((pt->oname = strdup(oname)) != NULL) {
00553                         if ((pt->nname = strdup(nname)) != NULL) {
00554                                 pt->fow = ntab[indx];
00555                                 ntab[indx] = pt;
00556                                 return(0);
00557                         }
00558                         (void)free((char *)pt->oname);
00559                 }
00560                 (void)free((char *)pt);
00561         }
00562         paxwarn(1, "Interactive rename table out of memory");
00563         return(-1);
00564 }
00565 
00566 /*
00567  * sub_name()
00568  *      look up a link name to see if it points at a file that has been
00569  *      remapped by the user. If found, the link is adjusted to contain the
00570  *      new name (oname is the link to name)
00571  */
00572 
00573 void
00574 sub_name(char *oname, int *onamelen, size_t onamesize)
00575 {
00576         NAMT *pt;
00577         u_int indx;
00578 
00579         if (ntab == NULL)
00580                 return;
00581         /*
00582          * look the name up in the hash table
00583          */
00584         indx = st_hash(oname, *onamelen, N_TAB_SZ);
00585         if ((pt = ntab[indx]) == NULL)
00586                 return;
00587 
00588         while (pt != NULL) {
00589                 /*
00590                  * walk down the hash chain looking for a match
00591                  */
00592                 if (strcmp(oname, pt->oname) == 0) {
00593                         /*
00594                          * found it, replace it with the new name
00595                          * and return (we know that oname has enough space)
00596                          */
00597                         *onamelen = l_strncpy(oname, pt->nname, onamesize - 1);
00598                         oname[*onamelen] = '\0';
00599                         return;
00600                 }
00601                 pt = pt->fow;
00602         }
00603 
00604         /*
00605          * no match, just return
00606          */
00607         return;
00608 }
00609 
00610 /*
00611  * device/inode mapping table routines
00612  * (used with formats that store device and inodes fields)
00613  *
00614  * device/inode mapping tables remap the device field in an archive header. The
00615  * device/inode fields are used to determine when files are hard links to each
00616  * other. However these values have very little meaning outside of that. This
00617  * database is used to solve one of two different problems.
00618  *
00619  * 1) when files are appended to an archive, while the new files may have hard
00620  * links to each other, you cannot determine if they have hard links to any
00621  * file already stored on the archive from a prior run of pax. We must assume
00622  * that these inode/device pairs are unique only within a SINGLE run of pax
00623  * (which adds a set of files to an archive). So we have to make sure the
00624  * inode/dev pairs we add each time are always unique. We do this by observing
00625  * while the inode field is very dense, the use of the dev field is fairly
00626  * sparse. Within each run of pax, we remap any device number of a new archive
00627  * member that has a device number used in a prior run and already stored in a
00628  * file on the archive. During the read phase of the append, we store the
00629  * device numbers used and mark them to not be used by any file during the
00630  * write phase. If during write we go to use one of those old device numbers,
00631  * we remap it to a new value.
00632  *
00633  * 2) Often the fields in the archive header used to store these values are
00634  * too small to store the entire value. The result is an inode or device value
00635  * which can be truncated. This really can foul up an archive. With truncation
00636  * we end up creating links between files that are really not links (after
00637  * truncation the inodes are the same value). We address that by detecting
00638  * truncation and forcing a remap of the device field to split truncated
00639  * inodes away from each other. Each truncation creates a pattern of bits that
00640  * are removed. We use this pattern of truncated bits to partition the inodes
00641  * on a single device to many different devices (each one represented by the
00642  * truncated bit pattern). All inodes on the same device that have the same
00643  * truncation pattern are mapped to the same new device. Two inodes that
00644  * truncate to the same value clearly will always have different truncation
00645  * bit patterns, so they will be split from away each other. When we spot
00646  * device truncation we remap the device number to a non truncated value.
00647  * (for more info see table.h for the data structures involved).
00648  */
00649 
00650 /*
00651  * dev_start()
00652  *      create the device mapping table
00653  * Return:
00654  *      0 if successful, -1 otherwise
00655  */
00656 
00657 int
00658 dev_start(void)
00659 {
00660         if (dtab != NULL)
00661                 return(0);
00662         if ((dtab = (DEVT **)calloc(D_TAB_SZ, sizeof(DEVT *))) == NULL) {
00663                 paxwarn(1, "Cannot allocate memory for device mapping table");
00664                 return(-1);
00665         }
00666         return(0);
00667 }
00668 
00669 /*
00670  * add_dev()
00671  *      add a device number to the table. this will force the device to be
00672  *      remapped to a new value if it be used during a write phase. This
00673  *      function is called during the read phase of an append to prohibit the
00674  *      use of any device number already in the archive.
00675  * Return:
00676  *      0 if added ok, -1 otherwise
00677  */
00678 
00679 int
00680 add_dev(ARCHD *arcn)
00681 {
00682         if (chk_dev(arcn->sb.st_dev, 1) == NULL)
00683                 return(-1);
00684         return(0);
00685 }
00686 
00687 /*
00688  * chk_dev()
00689  *      check for a device value in the device table. If not found and the add
00690  *      flag is set, it is added. This does NOT assign any mapping values, just
00691  *      adds the device number as one that need to be remapped. If this device
00692  *      is already mapped, just return with a pointer to that entry.
00693  * Return:
00694  *      pointer to the entry for this device in the device map table. Null
00695  *      if the add flag is not set and the device is not in the table (it is
00696  *      not been seen yet). If add is set and the device cannot be added, null
00697  *      is returned (indicates an error).
00698  */
00699 
00700 static DEVT *
00701 chk_dev(dev_t dev, int add)
00702 {
00703         DEVT *pt;
00704         u_int indx;
00705 
00706         if (dtab == NULL)
00707                 return(NULL);
00708         /*
00709          * look to see if this device is already in the table
00710          */
00711         indx = ((unsigned)dev) % D_TAB_SZ;
00712         if ((pt = dtab[indx]) != NULL) {
00713                 while ((pt != NULL) && (pt->dev != dev))
00714                         pt = pt->fow;
00715 
00716                 /*
00717                  * found it, return a pointer to it
00718                  */
00719                 if (pt != NULL)
00720                         return(pt);
00721         }
00722 
00723         /*
00724          * not in table, we add it only if told to as this may just be a check
00725          * to see if a device number is being used.
00726          */
00727         if (add == 0)
00728                 return(NULL);
00729 
00730         /*
00731          * allocate a node for this device and add it to the front of the hash
00732          * chain. Note we do not assign remaps values here, so the pt->list
00733          * list must be NULL.
00734          */
00735         if ((pt = (DEVT *)malloc(sizeof(DEVT))) == NULL) {
00736                 paxwarn(1, "Device map table out of memory");
00737                 return(NULL);
00738         }
00739         pt->dev = dev;
00740         pt->list = NULL;
00741         pt->fow = dtab[indx];
00742         dtab[indx] = pt;
00743         return(pt);
00744 }
00745 /*
00746  * map_dev()
00747  *      given an inode and device storage mask (the mask has a 1 for each bit
00748  *      the archive format is able to store in a header), we check for inode
00749  *      and device truncation and remap the device as required. Device mapping
00750  *      can also occur when during the read phase of append a device number was
00751  *      seen (and was marked as do not use during the write phase). WE ASSUME
00752  *      that unsigned longs are the same size or bigger than the fields used
00753  *      for ino_t and dev_t. If not the types will have to be changed.
00754  * Return:
00755  *      0 if all ok, -1 otherwise.
00756  */
00757 
00758 int
00759 map_dev(ARCHD *arcn, u_long dev_mask, u_long ino_mask)
00760 {
00761         DEVT *pt;
00762         DLIST *dpt;
00763         static dev_t lastdev = 0;       /* next device number to try */
00764         int trc_ino = 0;
00765         int trc_dev = 0;
00766         ino_t trunc_bits = 0;
00767         ino_t nino;
00768 
00769         if (dtab == NULL)
00770                 return(0);
00771         /*
00772          * check for device and inode truncation, and extract the truncated
00773          * bit pattern.
00774          */
00775         if ((arcn->sb.st_dev & (dev_t)dev_mask) != arcn->sb.st_dev)
00776                 ++trc_dev;
00777         if ((nino = arcn->sb.st_ino & (ino_t)ino_mask) != arcn->sb.st_ino) {
00778                 ++trc_ino;
00779                 trunc_bits = arcn->sb.st_ino & (ino_t)(~ino_mask);
00780         }
00781 
00782         /*
00783          * see if this device is already being mapped, look up the device
00784          * then find the truncation bit pattern which applies
00785          */
00786         if ((pt = chk_dev(arcn->sb.st_dev, 0)) != NULL) {
00787                 /*
00788                  * this device is already marked to be remapped
00789                  */
00790                 for (dpt = pt->list; dpt != NULL; dpt = dpt->fow)
00791                         if (dpt->trunc_bits == trunc_bits)
00792                                 break;
00793 
00794                 if (dpt != NULL) {
00795                         /*
00796                          * we are being remapped for this device and pattern
00797                          * change the device number to be stored and return
00798                          */
00799                         arcn->sb.st_dev = dpt->dev;
00800                         arcn->sb.st_ino = nino;
00801                         return(0);
00802                 }
00803         } else {
00804                 /*
00805                  * this device is not being remapped YET. if we do not have any
00806                  * form of truncation, we do not need a remap
00807                  */
00808                 if (!trc_ino && !trc_dev)
00809                         return(0);
00810 
00811                 /*
00812                  * we have truncation, have to add this as a device to remap
00813                  */
00814                 if ((pt = chk_dev(arcn->sb.st_dev, 1)) == NULL)
00815                         goto bad;
00816 
00817                 /*
00818                  * if we just have a truncated inode, we have to make sure that
00819                  * all future inodes that do not truncate (they have the
00820                  * truncation pattern of all 0's) continue to map to the same
00821                  * device number. We probably have already written inodes with
00822                  * this device number to the archive with the truncation
00823                  * pattern of all 0's. So we add the mapping for all 0's to the
00824                  * same device number.
00825                  */
00826                 if (!trc_dev && (trunc_bits != 0)) {
00827                         if ((dpt = (DLIST *)malloc(sizeof(DLIST))) == NULL)
00828                                 goto bad;
00829                         dpt->trunc_bits = 0;
00830                         dpt->dev = arcn->sb.st_dev;
00831                         dpt->fow = pt->list;
00832                         pt->list = dpt;
00833                 }
00834         }
00835 
00836         /*
00837          * look for a device number not being used. We must watch for wrap
00838          * around on lastdev (so we do not get stuck looking forever!)
00839          */
00840         while (++lastdev > 0) {
00841                 if (chk_dev(lastdev, 0) != NULL)
00842                         continue;
00843                 /*
00844                  * found an unused value. If we have reached truncation point
00845                  * for this format we are hosed, so we give up. Otherwise we
00846                  * mark it as being used.
00847                  */
00848                 if (((lastdev & ((dev_t)dev_mask)) != lastdev) ||
00849                     (chk_dev(lastdev, 1) == NULL))
00850                         goto bad;
00851                 break;
00852         }
00853 
00854         if ((lastdev <= 0) || ((dpt = (DLIST *)malloc(sizeof(DLIST))) == NULL))
00855                 goto bad;
00856 
00857         /*
00858          * got a new device number, store it under this truncation pattern.
00859          * change the device number this file is being stored with.
00860          */
00861         dpt->trunc_bits = trunc_bits;
00862         dpt->dev = lastdev;
00863         dpt->fow = pt->list;
00864         pt->list = dpt;
00865         arcn->sb.st_dev = lastdev;
00866         arcn->sb.st_ino = nino;
00867         return(0);
00868 
00869     bad:
00870         paxwarn(1, "Unable to fix truncated inode/device field when storing %s",
00871             arcn->name);
00872         paxwarn(0, "Archive may create improper hard links when extracted");
00873         return(0);
00874 }
00875 
00876 /*
00877  * directory access/mod time reset table routines (for directories READ by pax)
00878  *
00879  * The pax -t flag requires that access times of archive files to be the same
00880  * before being read by pax. For regular files, access time is restored after
00881  * the file has been copied. This database provides the same functionality for
00882  * directories read during file tree traversal. Restoring directory access time
00883  * is more complex than files since directories may be read several times until
00884  * all the descendants in their subtree are visited by fts. Directory access
00885  * and modification times are stored during the fts pre-order visit (done
00886  * before any descendants in the subtree is visited) and restored after the
00887  * fts post-order visit (after all the descendants have been visited). In the
00888  * case of premature exit from a subtree (like from the effects of -n), any
00889  * directory entries left in this database are reset during final cleanup
00890  * operations of pax. Entries are hashed by inode number for fast lookup.
00891  */
00892 
00893 /*
00894  * atdir_start()
00895  *      create the directory access time database for directories READ by pax.
00896  * Return:
00897  *      0 is created ok, -1 otherwise.
00898  */
00899 
00900 int
00901 atdir_start(void)
00902 {
00903         if (atab != NULL)
00904                 return(0);
00905         if ((atab = (ATDIR **)calloc(A_TAB_SZ, sizeof(ATDIR *))) == NULL) {
00906                 paxwarn(1,"Cannot allocate space for directory access time table");
00907                 return(-1);
00908         }
00909         return(0);
00910 }
00911 
00912 
00913 /*
00914  * atdir_end()
00915  *      walk through the directory access time table and reset the access time
00916  *      of any directory who still has an entry left in the database. These
00917  *      entries are for directories READ by pax
00918  */
00919 
00920 void
00921 atdir_end(void)
00922 {
00923         ATDIR *pt;
00924         int i;
00925 
00926         if (atab == NULL)
00927                 return;
00928         /*
00929          * for each non-empty hash table entry reset all the directories
00930          * chained there.
00931          */
00932         for (i = 0; i < A_TAB_SZ; ++i) {
00933                 if ((pt = atab[i]) == NULL)
00934                         continue;
00935                 /*
00936                  * remember to force the times, set_ftime() looks at pmtime
00937                  * and patime, which only applies to things CREATED by pax,
00938                  * not read by pax. Read time reset is controlled by -t.
00939                  */
00940                 for (; pt != NULL; pt = pt->fow)
00941                         set_ftime(pt->name, pt->mtime, pt->atime, 1);
00942         }
00943 }
00944 
00945 /*
00946  * add_atdir()
00947  *      add a directory to the directory access time table. Table is hashed
00948  *      and chained by inode number. This is for directories READ by pax
00949  */
00950 
00951 void
00952 add_atdir(char *fname, dev_t dev, ino_t ino, time_t mtime, time_t atime)
00953 {
00954         ATDIR *pt;
00955         u_int indx;
00956 
00957         if (atab == NULL)
00958                 return;
00959 
00960         /*
00961          * make sure this directory is not already in the table, if so just
00962          * return (the older entry always has the correct time). The only
00963          * way this will happen is when the same subtree can be traversed by
00964          * different args to pax and the -n option is aborting fts out of a
00965          * subtree before all the post-order visits have been made).
00966          */
00967         indx = ((unsigned)ino) % A_TAB_SZ;
00968         if ((pt = atab[indx]) != NULL) {
00969                 while (pt != NULL) {
00970                         if ((pt->ino == ino) && (pt->dev == dev))
00971                                 break;
00972                         pt = pt->fow;
00973                 }
00974 
00975                 /*
00976                  * oops, already there. Leave it alone.
00977                  */
00978                 if (pt != NULL)
00979                         return;
00980         }
00981 
00982         /*
00983          * add it to the front of the hash chain
00984          */
00985         if ((pt = (ATDIR *)malloc(sizeof(ATDIR))) != NULL) {
00986                 if ((pt->name = strdup(fname)) != NULL) {
00987                         pt->dev = dev;
00988                         pt->ino = ino;
00989                         pt->mtime = mtime;
00990                         pt->atime = atime;
00991                         pt->fow = atab[indx];
00992                         atab[indx] = pt;
00993                         return;
00994                 }
00995                 (void)free((char *)pt);
00996         }
00997 
00998         paxwarn(1, "Directory access time reset table ran out of memory");
00999         return;
01000 }
01001 
01002 /*
01003  * get_atdir()
01004  *      look up a directory by inode and device number to obtain the access
01005  *      and modification time you want to set to. If found, the modification
01006  *      and access time parameters are set and the entry is removed from the
01007  *      table (as it is no longer needed). These are for directories READ by
01008  *      pax
01009  * Return:
01010  *      0 if found, -1 if not found.
01011  */
01012 
01013 int
01014 get_atdir(dev_t dev, ino_t ino, time_t *mtime, time_t *atime)
01015 {
01016         ATDIR *pt;
01017         ATDIR **ppt;
01018         u_int indx;
01019 
01020         if (atab == NULL)
01021                 return(-1);
01022         /*
01023          * hash by inode and search the chain for an inode and device match
01024          */
01025         indx = ((unsigned)ino) % A_TAB_SZ;
01026         if ((pt = atab[indx]) == NULL)
01027                 return(-1);
01028 
01029         ppt = &(atab[indx]);
01030         while (pt != NULL) {
01031                 if ((pt->ino == ino) && (pt->dev == dev))
01032                         break;
01033                 /*
01034                  * no match, go to next one
01035                  */
01036                 ppt = &(pt->fow);
01037                 pt = pt->fow;
01038         }
01039 
01040         /*
01041          * return if we did not find it.
01042          */
01043         if (pt == NULL)
01044                 return(-1);
01045 
01046         /*
01047          * found it. return the times and remove the entry from the table.
01048          */
01049         *ppt = pt->fow;
01050         *mtime = pt->mtime;
01051         *atime = pt->atime;
01052         (void)free((char *)pt->name);
01053         (void)free((char *)pt);
01054         return(0);
01055 }
01056 
01057 /*
01058  * directory access mode and time storage routines (for directories CREATED
01059  * by pax).
01060  *
01061  * Pax requires that extracted directories, by default, have their access/mod
01062  * times and permissions set to the values specified in the archive. During the
01063  * actions of extracting (and creating the destination subtree during -rw copy)
01064  * directories extracted may be modified after being created. Even worse is
01065  * that these directories may have been created with file permissions which
01066  * prohibits any descendants of these directories from being extracted. When
01067  * directories are created by pax, access rights may be added to permit the
01068  * creation of files in their subtree. Every time pax creates a directory, the
01069  * times and file permissions specified by the archive are stored. After all
01070  * files have been extracted (or copied), these directories have their times
01071  * and file modes reset to the stored values. The directory info is restored in
01072  * reverse order as entries were added to the data file from root to leaf. To
01073  * restore atime properly, we must go backwards. The data file consists of
01074  * records with two parts, the file name followed by a DIRDATA trailer. The
01075  * fixed sized trailer contains the size of the name plus the off_t location in
01076  * the file. To restore we work backwards through the file reading the trailer
01077  * then the file name.
01078  */
01079 
01080 /*
01081  * dir_start()
01082  *      set up the directory time and file mode storage for directories CREATED
01083  *      by pax.
01084  * Return:
01085  *      0 if ok, -1 otherwise
01086  */
01087 
01088 int
01089 dir_start(void)
01090 {
01091 
01092         if (dirfd != -1)
01093                 return(0);
01094 
01095         /*
01096          * unlink the file so it goes away at termination by itself
01097          */
01098         memcpy(tempbase, _TFILE_BASE, sizeof(_TFILE_BASE));
01099         if ((dirfd = mkstemp(tempfile)) >= 0) {
01100                 (void)unlink(tempfile);
01101                 return(0);
01102         }
01103         paxwarn(1, "Unable to create temporary file for directory times: %s",
01104             tempfile);
01105         return(-1);
01106 }
01107 
01108 /*
01109  * add_dir()
01110  *      add the mode and times for a newly CREATED directory
01111  *      name is name of the directory, psb the stat buffer with the data in it,
01112  *      frc_mode is a flag that says whether to force the setting of the mode
01113  *      (ignoring the user set values for preserving file mode). Frc_mode is
01114  *      for the case where we created a file and found that the resulting
01115  *      directory was not writeable and the user asked for file modes to NOT
01116  *      be preserved. (we have to preserve what was created by default, so we
01117  *      have to force the setting at the end. this is stated explicitly in the
01118  *      pax spec)
01119  */
01120 
01121 void
01122 add_dir(char *name, int nlen, struct stat *psb, int frc_mode)
01123 {
01124         DIRDATA dblk;
01125 
01126         if (dirfd < 0)
01127                 return;
01128 
01129         /*
01130          * get current position (where file name will start) so we can store it
01131          * in the trailer
01132          */
01133         if ((dblk.npos = lseek(dirfd, 0L, SEEK_CUR)) < 0) {
01134                 paxwarn(1,"Unable to store mode and times for directory: %s",name);
01135                 return;
01136         }
01137 
01138         /*
01139          * write the file name followed by the trailer
01140          */
01141         dblk.nlen = nlen + 1;
01142         dblk.mode = psb->st_mode & 0xffff;
01143         dblk.mtime = psb->st_mtime;
01144         dblk.atime = psb->st_atime;
01145         dblk.frc_mode = frc_mode;
01146         if ((write(dirfd, name, dblk.nlen) == dblk.nlen) &&
01147             (write(dirfd, (char *)&dblk, sizeof(dblk)) == sizeof(dblk))) {
01148                 ++dircnt;
01149                 return;
01150         }
01151 
01152         paxwarn(1,"Unable to store mode and times for created directory: %s",name);
01153         return;
01154 }
01155 
01156 /*
01157  * proc_dir()
01158  *      process all file modes and times stored for directories CREATED
01159  *      by pax
01160  */
01161 
01162 void
01163 proc_dir(void)
01164 {
01165         char name[PAXPATHLEN+1];
01166         DIRDATA dblk;
01167         u_long cnt;
01168 
01169         if (dirfd < 0)
01170                 return;
01171         /*
01172          * read backwards through the file and process each directory
01173          */
01174         for (cnt = 0; cnt < dircnt; ++cnt) {
01175                 /*
01176                  * read the trailer, then the file name, if this fails
01177                  * just give up.
01178                  */
01179                 if (lseek(dirfd, -((off_t)sizeof(dblk)), SEEK_CUR) < 0)
01180                         break;
01181                 if (read(dirfd,(char *)&dblk, sizeof(dblk)) != sizeof(dblk))
01182                         break;
01183                 if (lseek(dirfd, dblk.npos, SEEK_SET) < 0)
01184                         break;
01185                 if (read(dirfd, name, dblk.nlen) != dblk.nlen)
01186                         break;
01187                 if (lseek(dirfd, dblk.npos, SEEK_SET) < 0)
01188                         break;
01189 
01190                 /*
01191                  * frc_mode set, make sure we set the file modes even if
01192                  * the user didn't ask for it (see file_subs.c for more info)
01193                  */
01194                 if (pmode || dblk.frc_mode)
01195                         set_pmode(name, dblk.mode);
01196                 if (patime || pmtime)
01197                         set_ftime(name, dblk.mtime, dblk.atime, 0);
01198         }
01199 
01200         (void)close(dirfd);
01201         dirfd = -1;
01202         if (cnt != dircnt)
01203                 paxwarn(1,"Unable to set mode and times for created directories");
01204         return;
01205 }
01206 
01207 /*
01208  * database independent routines
01209  */
01210 
01211 /*
01212  * st_hash()
01213  *      hashes filenames to a u_int for hashing into a table. Looks at the tail
01214  *      end of file, as this provides far better distribution than any other
01215  *      part of the name. For performance reasons we only care about the last
01216  *      MAXKEYLEN chars (should be at LEAST large enough to pick off the file
01217  *      name). Was tested on 500,000 name file tree traversal from the root
01218  *      and gave almost a perfectly uniform distribution of keys when used with
01219  *      prime sized tables (MAXKEYLEN was 128 in test). Hashes (sizeof int)
01220  *      chars at a time and pads with 0 for last addition.
01221  * Return:
01222  *      the hash value of the string MOD (%) the table size.
01223  */
01224 
01225 u_int
01226 st_hash(char *name, int len, int tabsz)
01227 {
01228         char *pt;
01229         char *dest;
01230         char *end;
01231         int i;
01232         u_int key = 0;
01233         int steps;
01234         int res;
01235         u_int val;
01236 
01237         /*
01238          * only look at the tail up to MAXKEYLEN, we do not need to waste
01239          * time here (remember these are pathnames, the tail is what will
01240          * spread out the keys)
01241          */
01242         if (len > MAXKEYLEN) {
01243                 pt = &(name[len - MAXKEYLEN]);
01244                 len = MAXKEYLEN;
01245         } else
01246                 pt = name;
01247 
01248         /*
01249          * calculate the number of u_int size steps in the string and if
01250          * there is a runt to deal with
01251          */
01252         steps = len/sizeof(u_int);
01253         res = len % sizeof(u_int);
01254 
01255         /*
01256          * add up the value of the string in unsigned integer sized pieces
01257          * too bad we cannot have unsigned int aligned strings, then we
01258          * could avoid the expensive copy.
01259          */
01260         for (i = 0; i < steps; ++i) {
01261                 end = pt + sizeof(u_int);
01262                 dest = (char *)&val;
01263                 while (pt < end)
01264                         *dest++ = *pt++;
01265                 key += val;
01266         }
01267 
01268         /*
01269          * add in the runt padded with zero to the right
01270          */
01271         if (res) {
01272                 val = 0;
01273                 end = pt + res;
01274                 dest = (char *)&val;
01275                 while (pt < end)
01276                         *dest++ = *pt++;
01277                 key += val;
01278         }
01279 
01280         /*
01281          * return the result mod the table size
01282          */
01283         return(key % tabsz);
01284 }

Generated on Fri Apr 14 22:57:01 2006 for minix by  doxygen 1.4.6