int add_name(char *oname, int onamelen, char *nname) { NAMT *pt; u_int indx; if (ntab == NULL) { /* * should never happen */ tty_warn(0, "No interactive rename table, links may fail\n"); return 0; } /* * look to see if we have already mapped this file, if so we * will update it */ indx = st_hash(oname, onamelen, N_TAB_SZ); if ((pt = ntab[indx]) != NULL) { /* * look down the has chain for the file */ while ((pt != NULL) && (strcmp(oname, pt->oname) != 0)) pt = pt->fow; if (pt != NULL) { /* * found an old mapping, replace it with the new one * the user just input (if it is different) */ if (strcmp(nname, pt->nname) == 0) return 0; (void)free((char *)pt->nname); if ((pt->nname = strdup(nname)) == NULL) { tty_warn(1, "Cannot update rename table"); return -1; } return 0; } } /* * this is a new mapping, add it to the table */ if ((pt = (NAMT *)malloc(sizeof(NAMT))) != NULL) { if ((pt->oname = strdup(oname)) != NULL) { if ((pt->nname = strdup(nname)) != NULL) { pt->fow = ntab[indx]; ntab[indx] = pt; return 0; } (void)free((char *)pt->oname); } (void)free((char *)pt); } tty_warn(1, "Interactive rename table out of memory"); return -1; }
__dead static void sig_cleanup(int which_sig) { /* * restore modes and times for any dirs we may have created * or any dirs we may have read. Set vflag and vfpart so the user * will clearly see the message on a line by itself. */ vflag = vfpart = 1; #ifdef SIGXCPU if (which_sig == SIGXCPU) tty_warn(1, "CPU time limit reached, cleaning up."); else #endif tty_warn(1, "Signal caught, cleaning up."); /* delete any open temporary file */ if (xtmp_name) (void)unlink(xtmp_name); ar_close(); proc_dir(); if (tflag) atdir_end(); (void)raise_default_signal(which_sig); exit(1); }
int opt_add(const char *str) { OPLIST *opt; char *frpt; char *pt; char *endpt; char *dstr; if ((str == NULL) || (*str == '\0')) { tty_warn(0, "Invalid option name"); return -1; } if ((dstr = strdup(str)) == NULL) { tty_warn(0, "Unable to allocate space for option list"); return -1; } frpt = endpt = dstr; /* * break into name and values pieces and stuff each one into a * OPLIST structure. When we know the format, the format specific * option function will go through this list */ while ((frpt != NULL) && (*frpt != '\0')) { if ((endpt = strchr(frpt, ',')) != NULL) *endpt = '\0'; if ((pt = strchr(frpt, '=')) == NULL) { tty_warn(0, "Invalid options format"); free(dstr); return -1; } if ((opt = (OPLIST *)malloc(sizeof(OPLIST))) == NULL) { tty_warn(0, "Unable to allocate space for option list"); free(dstr); return -1; } *pt++ = '\0'; opt->name = frpt; opt->value = pt; opt->fow = NULL; if (endpt != NULL) frpt = endpt + 1; else frpt = NULL; if (ophead == NULL) { optail = ophead = opt; continue; } optail->fow = opt; optail = opt; } return 0; }
int set_crc(ARCHD *arcn, int fd) { int i; int res; off_t cpcnt = 0L; u_long size; unsigned long crc = 0L; char tbuf[FILEBLK]; struct stat sb; if (fd < 0) { /* * hmm, no fd, should never happen. well no crc then. */ arcn->crc = 0L; return 0; } if ((size = (u_long)arcn->sb.st_blksize) > (u_long)sizeof(tbuf)) size = (u_long)sizeof(tbuf); /* * read all the bytes we think that there are in the file. If the user * is trying to archive an active file, forget this file. */ for(;;) { if ((res = read(fd, tbuf, size)) <= 0) break; cpcnt += res; for (i = 0; i < res; ++i) crc += (tbuf[i] & 0xff); } /* * safety check. we want to avoid archiving files that are active as * they can create inconsistent archive copies. */ if (cpcnt != arcn->sb.st_size) tty_warn(1, "File changed size %s", arcn->org_name); else if (fstat(fd, &sb) < 0) syswarn(1, errno, "Failed stat on %s", arcn->org_name); else if (arcn->sb.st_mtime != sb.st_mtime) tty_warn(1, "File %s was modified during read", arcn->org_name); else if (lseek(fd, (off_t)0L, SEEK_SET) < 0) syswarn(1, errno, "File rewind failed on: %s", arcn->org_name); else { arcn->crc = crc; return 0; } return -1; }
void ftree_chk(void) { FTREE *ft; int wban = 0; /* * make sure all dir access times were reset. */ if (tflag) atdir_end(); /* * walk down list and check reference count. Print out those members * that never had a match */ for (ft = fthead; ft != NULL; ft = ft->fow) { if (ft->refcnt != 0) continue; if (wban == 0) { tty_warn(1, "WARNING! These file names were not selected:"); ++wban; } (void)fprintf(stderr, "%s\n", ft->fname); } }
int lnk_creat(ARCHD *arcn, int *payload) { struct stat sb; /* * Check if this hardlink carries the "payload". In "cpio" archives * it's usually the last record of a set of hardlinks which includes * the contents of the file. * */ *payload = S_ISREG(arcn->sb.st_mode) && (arcn->sb.st_size > 0) && (arcn->sb.st_size <= arcn->skip); /* * We may be running as root, so we have to be sure that link target * is not a directory, so we lstat and check. XXX: This is still racy. */ if (lstat(arcn->ln_name, &sb) != -1 && S_ISDIR(sb.st_mode)) { tty_warn(1, "A hard link to the directory %s is not allowed", arcn->ln_name); return -1; } return mk_link(arcn->ln_name, &sb, arcn->name, 0); }
int ar_app_ok(void) { if (artyp == ISPIPE) { tty_warn(1, "Cannot append to an archive obtained from a pipe."); return -1; } if (!invld_rec) return 0; tty_warn(1, "Cannot append, device record size %d does not support %s spec", rdblksz, argv0); return -1; }
int lnk_start(void) { if (ltab != NULL) return 0; if ((ltab = (HRDLNK **)calloc(L_TAB_SZ, sizeof(HRDLNK *))) == NULL) { tty_warn(1, "Cannot allocate memory for hard link table"); return -1; } return 0; }
int dev_start(void) { if (dtab != NULL) return 0; if ((dtab = (DEVT **)calloc(D_TAB_SZ, sizeof(DEVT *))) == NULL) { tty_warn(1, "Cannot allocate memory for device mapping table"); return -1; } return 0; }
int name_start(void) { if (ntab != NULL) return 0; if ((ntab = (NAMT **)calloc(N_TAB_SZ, sizeof(NAMT *))) == NULL) { tty_warn(1, "Cannot allocate memory for interactive rename table"); return -1; } return 0; }
int atdir_start(void) { if (atab != NULL) return 0; if ((atab = (ATDIR **)calloc(A_TAB_SZ, sizeof(ATDIR *))) == NULL) { tty_warn(1, "Cannot allocate space for directory access time table"); return -1; } return 0; }
void add_atdir(char *fname, dev_t dev, ino_t ino, time_t mtime, time_t atime) { ATDIR *pt; u_int indx; if (atab == NULL) return; /* * make sure this directory is not already in the table, if so just * return (the older entry always has the correct time). The only * way this will happen is when the same subtree can be traversed by * different args to pax and the -n option is aborting fts out of a * subtree before all the post-order visits have been made. */ indx = ((unsigned)ino) % A_TAB_SZ; if ((pt = atab[indx]) != NULL) { while (pt != NULL) { if ((pt->ino == ino) && (pt->dev == dev)) break; pt = pt->fow; } /* * oops, already there. Leave it alone. */ if (pt != NULL) return; } /* * add it to the front of the hash chain */ if ((pt = (ATDIR *)malloc(sizeof(ATDIR))) != NULL) { if ((pt->name = strdup(fname)) != NULL) { pt->dev = dev; pt->ino = ino; pt->mtime = mtime; pt->atime = atime; pt->fow = atab[indx]; atab[indx] = pt; return; } (void)free((char *)pt); } tty_warn(1, "Directory access time reset table ran out of memory"); return; }
int ftree_add(char *str, int isdir) { FTREE *ft; int len; /* * simple check for bad args */ if ((str == NULL) || (*str == '\0')) { tty_warn(0, "Invalid file name argument"); return -1; } /* * allocate FTREE node and add to the end of the linked list (args are * processed in the same order they were passed to pax). Get rid of any * trailing / the user may pass us. (watch out for / by itself). */ if ((ft = (FTREE *)malloc(sizeof(FTREE))) == NULL) { tty_warn(0, "Unable to allocate memory for filename"); return -1; } if (((len = strlen(str) - 1) > 0) && (str[len] == '/')) str[len] = '\0'; ft->fname = str; ft->refcnt = -isdir; ft->fow = NULL; if (fthead == NULL) { fttail = fthead = ft; return 0; } fttail->fow = ft; fttail = ft; return 0; }
int pat_add(char *str, char *chdn, int flags) { PATTERN *pt; /* * throw out the junk */ if ((str == NULL) || (*str == '\0')) { tty_warn(1, "Empty pattern string"); return -1; } /* * allocate space for the pattern and store the pattern. the pattern is * part of argv so do not bother to copy it, just point at it. Add the * node to the end of the pattern list */ if ((pt = (PATTERN *)malloc(sizeof(PATTERN))) == NULL) { tty_warn(1, "Unable to allocate memory for pattern string"); return -1; } pt->pstr = str; pt->pend = NULL; pt->plen = strlen(str); pt->fow = NULL; pt->flgs = flags; pt->chdname = chdn; if (pathead == NULL) { pattail = pathead = pt; return 0; } pattail->fow = pt; pattail = pt; return 0; }
static DEVT * chk_dev(dev_t dev, int add) { DEVT *pt; u_int indx; if (dtab == NULL) return(NULL); /* * look to see if this device is already in the table */ indx = ((unsigned)dev) % D_TAB_SZ; if ((pt = dtab[indx]) != NULL) { while ((pt != NULL) && (pt->dev != dev)) pt = pt->fow; /* * found it, return a pointer to it */ if (pt != NULL) return(pt); } /* * not in table, we add it only if told to as this may just be a check * to see if a device number is being used. */ if (add == 0) return(NULL); /* * allocate a node for this device and add it to the front of the hash * chain. Note we do not assign remaps values here, so the pt->list * list must be NULL. */ if ((pt = (DEVT *)malloc(sizeof(DEVT))) == NULL) { tty_warn(1, "Device map table out of memory"); return(NULL); } pt->dev = dev; pt->list = NULL; pt->fow = dtab[indx]; dtab[indx] = pt; return(pt); }
static int fix_path( char *or_name, int *or_len, char *dir_name, int dir_len) { char *src; char *dest; char *start; int len; /* * we shift the or_name to the right enough to tack in the dir_name * at the front. We make sure we have enough space for it all before * we start. since dest always ends in a slash, we skip of or_name * if it also starts with one. */ start = or_name; src = start + *or_len; dest = src + dir_len; if (*start == '/') { ++start; --dest; } if ((len = dest - or_name) > PAXPATHLEN) { tty_warn(1, "File name %s/%s, too long", dir_name, start); return -1; } *or_len = len; /* * enough space, shift */ while (src >= start) *dest-- = *src--; src = dir_name + dir_len - 1; /* * splice in the destination directory name */ while (src >= dir_name) *dest-- = *src--; *(or_name + len) = '\0'; return 0; }
void pat_chk(void) { PATTERN *pt; int wban = 0; /* * walk down the list checking the flags to make sure MTCH was set, * if not complain */ for (pt = pathead; pt != NULL; pt = pt->fow) { if (pt->flgs & MTCH) continue; if (!wban) { tty_warn(1, "WARNING! These patterns were not matched:"); ++wban; } (void)fprintf(stderr, "%s\n", pt->pstr); } }
int dir_start(void) { #ifdef DIRS_USE_FILE if (dirfd != -1) return 0; /* * unlink the file so it goes away at termination by itself */ memcpy(tempbase, _TFILE_BASE, sizeof(_TFILE_BASE)); if ((dirfd = mkstemp(tempfile)) >= 0) { (void)unlink(tempfile); return 0; } tty_warn(1, "Unable to create temporary file for directory times: %s", tempfile); return -1; #else return (0); #endif /* DIRS_USE_FILE */ }
int bad_opt(void) { OPLIST *opt; if (ophead == NULL) return 0; /* * print all we were given */ tty_warn(1," These format options are not supported for %s", frmt->name); while ((opt = opt_next()) != NULL) (void)fprintf(stderr, "\t%s = %s\n", opt->name, opt->value); if (strcmp(NM_TAR, argv0) == 0) tar_usage(); #ifndef NO_CPIO else if (strcmp(NM_CPIO, argv0) == 0) cpio_usage(); #endif else pax_usage(); return 0; }
int chk_same(ARCHD *arcn) { struct stat sb; /* * if file does not exist, return. if file exists and -k, skip it * quietly */ if (lstat(arcn->name, &sb) < 0) return 1; if (kflag) return 0; /* * better make sure the user does not have src == dest by mistake */ if ((arcn->sb.st_dev == sb.st_dev) && (arcn->sb.st_ino == sb.st_ino)) { tty_warn(1, "Unable to copy %s, file would overwrite itself", arcn->name); return 0; } return 1; }
int ftime_start(void) { if (ftab != NULL) return 0; if ((ftab = (FTM **)calloc(F_TAB_SZ, sizeof(FTM *))) == NULL) { tty_warn(1, "Cannot allocate memory for file time table"); return -1; } /* * get random name and create temporary scratch file, unlink name * so it will get removed on exit */ memcpy(tempbase, _TFILE_BASE, sizeof(_TFILE_BASE)); if ((ffd = mkstemp(tempfile)) == -1) { syswarn(1, errno, "Unable to create temporary file: %s", tempfile); return -1; } (void)unlink(tempfile); return 0; }
int next_file(ARCHD *arcn) { #ifndef SMALL static char curdir[PAXPATHLEN+2], curpath[PAXPATHLEN+2]; static int curdirlen; struct stat statbuf; FTSENT Mftent; #endif /* SMALL */ int cnt; time_t atime, mtime; char *curlink; #define MFTENT_DUMMY_DEV UINT_MAX curlink = NULL; #ifndef SMALL /* * if parsing an mtree(8) specfile, build up `dummy' ftsent * from specfile info, and jump below to complete setup of arcn. */ if (Mflag) { int skipoptional; next_ftnode: skipoptional = 0; if (ftnode == NULL) /* tree is empty */ return (-1); /* get current name */ if (snprintf(curpath, sizeof(curpath), "%s%s%s", curdir, curdirlen ? "/" : "", ftnode->name) >= sizeof(curpath)) { tty_warn(1, "line %lu: %s: %s", (u_long)ftnode->lineno, curdir, strerror(ENAMETOOLONG)); return (-1); } ftnode->flags |= F_VISIT; /* mark node visited */ /* construct dummy FTSENT */ Mftent.fts_path = curpath; Mftent.fts_statp = &statbuf; Mftent.fts_pointer = ftnode; ftent = &Mftent; /* look for existing file */ if (lstat(Mftent.fts_path, &statbuf) == -1) { if (ftnode->flags & F_OPT) skipoptional = 1; /* missing: fake up stat info */ memset(&statbuf, 0, sizeof(statbuf)); statbuf.st_dev = MFTENT_DUMMY_DEV; statbuf.st_ino = ftnode->lineno; statbuf.st_size = 0; #define NODETEST(t, m) \ if (!(t)) { \ tty_warn(1, "line %lu: %s: %s not specified", \ (u_long)ftnode->lineno, \ ftent->fts_path, m); \ return -1; \ } statbuf.st_mode = nodetoino(ftnode->type); NODETEST(ftnode->flags & F_TYPE, "type"); NODETEST(ftnode->flags & F_MODE, "mode"); if (!(ftnode->flags & F_TIME)) statbuf.st_mtime = starttime; NODETEST(ftnode->flags & (F_GID | F_GNAME), "group"); NODETEST(ftnode->flags & (F_UID | F_UNAME), "user"); if (ftnode->type == F_BLOCK || ftnode->type == F_CHAR) NODETEST(ftnode->flags & F_DEV, "device number"); if (ftnode->type == F_LINK) NODETEST(ftnode->flags & F_SLINK, "symlink"); /* don't require F_FLAGS or F_SIZE */ #undef NODETEST } else { if (ftnode->flags & F_TYPE && nodetoino(ftnode->type) != (statbuf.st_mode & S_IFMT)) { tty_warn(1, "line %lu: %s: type mismatch: specfile %s, tree %s", (u_long)ftnode->lineno, ftent->fts_path, inotype(nodetoino(ftnode->type)), inotype(statbuf.st_mode)); return -1; } if (ftnode->type == F_DIR && (ftnode->flags & F_OPT)) skipoptional = 1; } /* * override settings with those from specfile */ if (ftnode->flags & F_MODE) { statbuf.st_mode &= ~ALLPERMS; statbuf.st_mode |= (ftnode->st_mode & ALLPERMS); } if (ftnode->flags & (F_GID | F_GNAME)) statbuf.st_gid = ftnode->st_gid; if (ftnode->flags & (F_UID | F_UNAME)) statbuf.st_uid = ftnode->st_uid; #if HAVE_STRUCT_STAT_ST_FLAGS if (ftnode->flags & F_FLAGS) statbuf.st_flags = ftnode->st_flags; #endif if (ftnode->flags & F_TIME) #if BSD4_4 && !HAVE_NBTOOL_CONFIG_H statbuf.st_mtimespec = ftnode->st_mtimespec; #else statbuf.st_mtime = ftnode->st_mtimespec.tv_sec; #endif if (ftnode->flags & F_DEV) statbuf.st_rdev = ftnode->st_rdev; if (ftnode->flags & F_SLINK) curlink = ftnode->slink; /* ignore F_SIZE */ /* * find next node */ if (ftnode->type == F_DIR && ftnode->child != NULL) { /* directory with unseen child */ ftnode = ftnode->child; curdirlen = strlcpy(curdir, curpath, sizeof(curdir)); } else do { if (ftnode->next != NULL) { /* next node at current level */ ftnode = ftnode->next; } else { /* move back to parent */ /* reset time only on first cd.. */ if (Mftent.fts_pointer == ftnode && tflag && (get_atdir(MFTENT_DUMMY_DEV, ftnode->lineno, &mtime, &atime) == 0)) { set_ftime(ftent->fts_path, mtime, atime, 1, 0); } ftnode = ftnode->parent; if (ftnode->parent == ftnode) ftnode = NULL; else { curdirlen -= strlen(ftnode->name) + 1; curdir[curdirlen] = '\0'; } } } while (ftnode != NULL && ftnode->flags & F_VISIT); if (skipoptional) /* skip optional entries */ goto next_ftnode; goto got_ftent; } #endif /* SMALL */ /* * ftree_sel() might have set the ftree_skip flag if the user has the * -n option and a file was selected from this file arg tree. (-n says * only one member is matched for each pattern) ftree_skip being 1 * forces us to go to the next arg now. */ if (ftree_skip) { /* * clear and go to next arg */ ftree_skip = 0; if (ftree_arg() < 0) return -1; } if (ftsp == NULL) return -1; /* * loop until we get a valid file to process */ for(;;) { if ((ftent = fts_read(ftsp)) == NULL) { /* * out of files in this tree, go to next arg, if none * we are done */ if (ftree_arg() < 0) return -1; continue; } /* * handle each type of fts_read() flag */ switch(ftent->fts_info) { case FTS_D: case FTS_DEFAULT: case FTS_F: case FTS_SL: case FTS_SLNONE: /* * these are all ok */ break; case FTS_DP: /* * already saw this directory. If the user wants file * access times reset, we use this to restore the * access time for this directory since this is the * last time we will see it in this file subtree * remember to force the time (this is -t on a read * directory, not a created directory). */ if (!tflag || (get_atdir( ftent->fts_statp->st_dev, ftent->fts_statp->st_ino, &mtime, &atime) < 0)) continue; set_ftime(ftent->fts_path, mtime, atime, 1, 0); continue; case FTS_DC: /* * fts claims a file system cycle */ tty_warn(1,"File system cycle found at %s", ftent->fts_path); continue; case FTS_DNR: syswarn(1, FTS_ERRNO(ftent), "Unable to read directory %s", ftent->fts_path); continue; case FTS_ERR: syswarn(1, FTS_ERRNO(ftent), "File system traversal error"); continue; case FTS_NS: case FTS_NSOK: syswarn(1, FTS_ERRNO(ftent), "Unable to access %s", ftent->fts_path); continue; } #ifndef SMALL got_ftent: #endif /* SMALL */ /* * ok got a file tree node to process. copy info into arcn * structure (initialize as required) */ arcn->skip = 0; arcn->pad = 0; arcn->ln_nlen = 0; arcn->ln_name[0] = '\0'; arcn->sb = *(ftent->fts_statp); /* * file type based set up and copy into the arcn struct * SIDE NOTE: * we try to reset the access time on all files and directories * we may read when the -t flag is specified. files are reset * when we close them after copying. we reset the directories * when we are done with their file tree (we also clean up at * end in case we cut short a file tree traversal). However * there is no way to reset access times on symlinks. */ switch(S_IFMT & arcn->sb.st_mode) { case S_IFDIR: arcn->type = PAX_DIR; if (!tflag) break; add_atdir(ftent->fts_path, arcn->sb.st_dev, arcn->sb.st_ino, arcn->sb.st_mtime, arcn->sb.st_atime); break; case S_IFCHR: arcn->type = PAX_CHR; break; case S_IFBLK: arcn->type = PAX_BLK; break; case S_IFREG: /* * only regular files with have data to store on the * archive. all others will store a zero length skip. * the skip field is used by pax for actual data it has * to read (or skip over). */ arcn->type = PAX_REG; arcn->skip = arcn->sb.st_size; break; case S_IFLNK: arcn->type = PAX_SLK; if (curlink != NULL) { cnt = strlcpy(arcn->ln_name, curlink, sizeof(arcn->ln_name)); /* * have to read the symlink path from the file */ } else if ((cnt = readlink(ftent->fts_path, arcn->ln_name, sizeof(arcn->ln_name) - 1)) < 0) { syswarn(1, errno, "Unable to read symlink %s", ftent->fts_path); continue; } /* * set link name length, watch out readlink does not * always null terminate the link path */ arcn->ln_name[cnt] = '\0'; arcn->ln_nlen = cnt; break; #ifdef S_IFSOCK case S_IFSOCK: /* * under BSD storing a socket is senseless but we will * let the format specific write function make the * decision of what to do with it. */ arcn->type = PAX_SCK; break; #endif case S_IFIFO: arcn->type = PAX_FIF; break; } break; } /* * copy file name, set file name length */ arcn->nlen = strlcpy(arcn->name, ftent->fts_path, sizeof(arcn->name)); arcn->org_name = arcn->fts_name; strlcpy(arcn->fts_name, ftent->fts_path, sizeof arcn->fts_name); if (strcmp(NM_CPIO, argv0) == 0) { /* * cpio does *not* descend directories listed in the * arguments, unlike pax/tar, so needs special handling * here. failure to do so results in massive amounts * of duplicated files in the output. We kill fts after * the first name is extracted, what a waste. */ ftcur->refcnt = 1; (void)ftree_arg(); } return 0; }
int map_dev(ARCHD *arcn, u_long dev_mask, u_long ino_mask) { DEVT *pt; DLIST *dpt; static dev_t lastdev = 0; /* next device number to try */ int trc_ino = 0; int trc_dev = 0; ino_t trunc_bits = 0; ino_t nino; if (dtab == NULL) return 0; /* * check for device and inode truncation, and extract the truncated * bit pattern. */ if ((arcn->sb.st_dev & (dev_t)dev_mask) != arcn->sb.st_dev) ++trc_dev; if ((nino = arcn->sb.st_ino & (ino_t)ino_mask) != arcn->sb.st_ino) { ++trc_ino; trunc_bits = arcn->sb.st_ino & (ino_t)(~ino_mask); } /* * see if this device is already being mapped, look up the device * then find the truncation bit pattern which applies */ if ((pt = chk_dev(arcn->sb.st_dev, 0)) != NULL) { /* * this device is already marked to be remapped */ for (dpt = pt->list; dpt != NULL; dpt = dpt->fow) if (dpt->trunc_bits == trunc_bits) break; if (dpt != NULL) { /* * we are being remapped for this device and pattern * change the device number to be stored and return */ arcn->sb.st_dev = dpt->dev; arcn->sb.st_ino = nino; return 0; } } else { /* * this device is not being remapped YET. if we do not have any * form of truncation, we do not need a remap */ if (!trc_ino && !trc_dev) return 0; /* * we have truncation, have to add this as a device to remap */ if ((pt = chk_dev(arcn->sb.st_dev, 1)) == NULL) goto bad; /* * if we just have a truncated inode, we have to make sure that * all future inodes that do not truncate (they have the * truncation pattern of all 0's) continue to map to the same * device number. We probably have already written inodes with * this device number to the archive with the truncation * pattern of all 0's. So we add the mapping for all 0's to the * same device number. */ if (!trc_dev && (trunc_bits != 0)) { if ((dpt = (DLIST *)malloc(sizeof(DLIST))) == NULL) goto bad; dpt->trunc_bits = 0; dpt->dev = arcn->sb.st_dev; dpt->fow = pt->list; pt->list = dpt; } } /* * look for a device number not being used. We must watch for wrap * around on lastdev (so we do not get stuck looking forever!) */ while (++lastdev > 0) { if (chk_dev(lastdev, 0) != NULL) continue; /* * found an unused value. If we have reached truncation point * for this format we are hosed, so we give up. Otherwise we * mark it as being used. */ if (((lastdev & ((dev_t)dev_mask)) != lastdev) || (chk_dev(lastdev, 1) == NULL)) goto bad; break; } if ((lastdev <= 0) || ((dpt = (DLIST *)malloc(sizeof(DLIST))) == NULL)) goto bad; /* * got a new device number, store it under this truncation pattern. * change the device number this file is being stored with. */ dpt->trunc_bits = trunc_bits; dpt->dev = lastdev; dpt->fow = pt->list; pt->list = dpt; arcn->sb.st_dev = lastdev; arcn->sb.st_ino = nino; return 0; bad: tty_warn(1, "Unable to fix truncated inode/device field when storing %s", arcn->name); tty_warn(0, "Archive may create improper hard links when extracted"); return 0; }
int node_creat(ARCHD *arcn) { int res; int ign = 0; int oerrno; int pass = 0; mode_t file_mode; struct stat sb; char target[MAXPATHLEN]; char *nm = arcn->name; int len; /* * create node based on type, if that fails try to unlink the node and * try again. finally check the path and try again. As noted in the * file and link creation routines, this method seems to exhibit the * best performance in general use workloads. */ file_mode = arcn->sb.st_mode & FILEBITS(arcn->type == PAX_DIR); for (;;) { switch (arcn->type) { case PAX_DIR: /* * If -h (or -L) was given in tar-mode, follow the * potential symlink chain before trying to create the * directory. */ if (strcmp(NM_TAR, argv0) == 0 && Lflag) { while (lstat(nm, &sb) == 0 && S_ISLNK(sb.st_mode)) { len = readlink(nm, target, sizeof target - 1); if (len == -1) { syswarn(0, errno, "cannot follow symlink %s " "in chain for %s", nm, arcn->name); res = -1; goto badlink; } target[len] = '\0'; nm = target; } } res = domkdir(nm, file_mode); badlink: if (ign) res = 0; break; case PAX_CHR: file_mode |= S_IFCHR; res = mknod(nm, file_mode, arcn->sb.st_rdev); break; case PAX_BLK: file_mode |= S_IFBLK; res = mknod(nm, file_mode, arcn->sb.st_rdev); break; case PAX_FIF: res = mkfifo(nm, file_mode); break; case PAX_SCK: /* * Skip sockets, operation has no meaning under BSD */ tty_warn(0, "%s skipped. Sockets cannot be copied or extracted", nm); return (-1); case PAX_SLK: res = symlink(arcn->ln_name, nm); break; case PAX_CTG: case PAX_HLK: case PAX_HRG: case PAX_REG: default: /* * we should never get here */ tty_warn(0, "%s has an unknown file type, skipping", nm); return (-1); } /* * if we were able to create the node break out of the loop, * otherwise try to unlink the node and try again. if that * fails check the full path and try a final time. */ if (res == 0) break; /* * we failed to make the node */ oerrno = errno; switch (pass++) { case 0: if ((ign = unlnk_exist(nm, arcn->type)) < 0) return (-1); continue; case 1: if (nodirs || chk_path(nm, arcn->sb.st_uid, arcn->sb.st_gid) < 0) { syswarn(1, oerrno, "Cannot create %s", nm); return (-1); } continue; } /* * it must be a file that exists but we can't create or * remove, but we must avoid the infinite loop. */ break; } /* * we were able to create the node. set uid/gid, modes and times */ if (pids) res = set_ids(nm, arcn->sb.st_uid, arcn->sb.st_gid); else res = 0; /* * IMPORTANT SECURITY NOTE: * if not preserving mode or we cannot set uid/gid, then PROHIBIT any * set uid/gid bits */ if (!pmode || res) arcn->sb.st_mode &= ~SETBITS(arcn->type == PAX_DIR); if (pmode) set_pmode(arcn->name, arcn->sb.st_mode); if (arcn->type == PAX_DIR && strcmp(NM_CPIO, argv0) != 0) { /* * Dirs must be processed again at end of extract to set times * and modes to agree with those stored in the archive. However * to allow extract to continue, we may have to also set owner * rights. This allows nodes in the archive that are children * of this directory to be extracted without failure. Both time * and modes will be fixed after the entire archive is read and * before pax exits. */ if (access(nm, R_OK | W_OK | X_OK) < 0) { if (lstat(nm, &sb) < 0) { syswarn(0, errno,"Cannot access %s (stat)", arcn->name); set_pmode(nm,file_mode | S_IRWXU); } else { /* * We have to add rights to the dir, so we make * sure to restore the mode. The mode must be * restored AS CREATED and not as stored if * pmode is not set. */ set_pmode(nm, ((sb.st_mode & FILEBITS(arcn->type == PAX_DIR)) | S_IRWXU)); if (!pmode) arcn->sb.st_mode = sb.st_mode; } /* * we have to force the mode to what was set here, * since we changed it from the default as created. */ add_dir(nm, arcn->nlen, &(arcn->sb), 1); } else if (pmode || patime || pmtime) add_dir(nm, arcn->nlen, &(arcn->sb), 0); } if (patime || pmtime) set_ftime(arcn->name, arcn->sb.st_mtime, arcn->sb.st_atime, 0, (arcn->type == PAX_SLK) ? 1 : 0); #if HAVE_STRUCT_STAT_ST_FLAGS if (pfflags && arcn->type != PAX_SLK) set_chflags(arcn->name, arcn->sb.st_flags); #endif return 0; }
int chk_ftime(ARCHD *arcn) { FTM *pt; int namelen; u_int indx; char ckname[PAXPATHLEN+1]; /* * no info, go ahead and add to archive */ if (ftab == NULL) return 0; /* * hash the pathname and look up in table */ namelen = arcn->nlen; indx = st_hash(arcn->name, namelen, F_TAB_SZ); if ((pt = ftab[indx]) != NULL) { /* * the hash chain is not empty, walk down looking for match * only read up the path names if the lengths match, speeds * up the search a lot */ while (pt != NULL) { if (pt->namelen == namelen) { /* * potential match, have to read the name * from the scratch file. */ if (lseek(ffd,pt->seek,SEEK_SET) != pt->seek) { syswarn(1, errno, "Failed ftime table seek"); return -1; } if (xread(ffd, ckname, namelen) != namelen) { syswarn(1, errno, "Failed ftime table read"); return -1; } /* * if the names match, we are done */ if (!strncmp(ckname, arcn->name, namelen)) break; } /* * try the next entry on the chain */ pt = pt->fow; } if (pt != NULL) { /* * found the file, compare the times, save the newer */ if (arcn->sb.st_mtime > pt->mtime) { /* * file is newer */ pt->mtime = arcn->sb.st_mtime; return 0; } /* * file is older */ return 1; } } /* * not in table, add it */ if ((pt = (FTM *)malloc(sizeof(FTM))) != NULL) { /* * add the name at the end of the scratch file, saving the * offset. add the file to the head of the hash chain */ if ((pt->seek = lseek(ffd, (off_t)0, SEEK_END)) >= 0) { if (xwrite(ffd, arcn->name, namelen) == namelen) { pt->mtime = arcn->sb.st_mtime; pt->namelen = namelen; pt->fow = ftab[indx]; ftab[indx] = pt; return 0; } syswarn(1, errno, "Failed write to file time table"); } else syswarn(1, errno, "Failed seek on file time table"); } else tty_warn(1, "File time table ran out of memory"); if (pt != NULL) (void)free((char *)pt); return -1; }
int chk_lnk(ARCHD *arcn) { HRDLNK *pt; HRDLNK **ppt; u_int indx; if (ltab == NULL) return -1; /* * ignore those nodes that cannot have hard links */ if ((arcn->type == PAX_DIR) || (arcn->sb.st_nlink <= 1)) return 0; /* * hash inode number and look for this file */ indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ; if ((pt = ltab[indx]) != NULL) { /* * it's hash chain in not empty, walk down looking for it */ ppt = &(ltab[indx]); while (pt != NULL) { if ((pt->ino == arcn->sb.st_ino) && (pt->dev == arcn->sb.st_dev)) break; ppt = &(pt->fow); pt = pt->fow; } if (pt != NULL) { /* * found a link. set the node type and copy in the * name of the file it is to link to. we need to * handle hardlinks to regular files differently than * other links. */ arcn->ln_nlen = strlcpy(arcn->ln_name, pt->name, sizeof(arcn->ln_name)); if (arcn->type == PAX_REG) arcn->type = PAX_HRG; else arcn->type = PAX_HLK; /* * if we have found all the links to this file, remove * it from the database */ if (--pt->nlink <= 1) { *ppt = pt->fow; (void)free((char *)pt->name); (void)free((char *)pt); } return 1; } } /* * we never saw this file before. It has links so we add it to the * front of this hash chain */ if ((pt = (HRDLNK *)malloc(sizeof(HRDLNK))) != NULL) { if ((pt->name = strdup(arcn->name)) != NULL) { pt->dev = arcn->sb.st_dev; pt->ino = arcn->sb.st_ino; pt->nlink = arcn->sb.st_nlink; pt->fow = ltab[indx]; ltab[indx] = pt; return 0; } (void)free((char *)pt); } tty_warn(1, "Hard link table out of memory"); return -1; }
int file_write(int fd, char *str, int cnt, int *rem, int *isempt, int sz, char *name) { char *pt; char *end; int wcnt; char *st = str; char **strp; size_t *lenp; /* * while we have data to process */ while (cnt) { if (!*rem) { /* * We are now at the start of file system block again * (or what we think one is...). start looking for * empty blocks again */ *isempt = 1; *rem = sz; } /* * only examine up to the end of the current file block or * remaining characters to write, whatever is smaller */ wcnt = MIN(cnt, *rem); cnt -= wcnt; *rem -= wcnt; if (*isempt) { /* * have not written to this block yet, so we keep * looking for zero's */ pt = st; end = st + wcnt; /* * look for a zero filled buffer */ while ((pt < end) && (*pt == '\0')) ++pt; if (pt == end) { /* * skip, buf is empty so far */ if (fd > -1 && lseek(fd, (off_t)wcnt, SEEK_CUR) < 0) { syswarn(1, errno, "File seek on %s", name); return -1; } st = pt; continue; } /* * drat, the buf is not zero filled */ *isempt = 0; } /* * have non-zero data in this file system block, have to write */ switch (fd) { case -PAX_GLF: strp = &gnu_name_string; lenp = &gnu_name_length; break; case -PAX_GLL: strp = &gnu_link_string; lenp = &gnu_link_length; break; default: strp = NULL; lenp = NULL; break; } if (strp) { char *nstr = *strp ? realloc(*strp, *lenp + wcnt + 1) : malloc(wcnt + 1); if (nstr == NULL) { tty_warn(1, "Out of memory"); return -1; } (void)strlcpy(&nstr[*lenp], st, wcnt + 1); *strp = nstr; *lenp += wcnt; } else if (xwrite(fd, st, wcnt) != wcnt) { syswarn(1, errno, "Failed write to file %s", name); return -1; } st += wcnt; } return st - str; }
void proc_dir(void) { #ifdef DIRS_USE_FILE char name[PAXPATHLEN+1]; DIRDATA dblk; u_long cnt; if (dirfd < 0) return; /* * read backwards through the file and process each directory */ for (cnt = 0; cnt < dircnt; ++cnt) { /* * read the trailer, then the file name, if this fails * just give up. */ if (lseek(dirfd, -((off_t)sizeof(dblk)), SEEK_CUR) < 0) break; if (xread(dirfd,(char *)&dblk, sizeof(dblk)) != sizeof(dblk)) break; if (lseek(dirfd, dblk.npos, SEEK_SET) < 0) break; if (xread(dirfd, name, dblk.nlen) != dblk.nlen) break; if (lseek(dirfd, dblk.npos, SEEK_SET) < 0) break; /* * frc_mode set, make sure we set the file modes even if * the user didn't ask for it (see file_subs.c for more info) */ if (pmode || dblk.frc_mode) set_pmode(name, dblk.mode); if (patime || pmtime) set_ftime(name, dblk.mtime, dblk.atime, 0, 0); if (pfflags) set_chflags(name, dblk.fflags); } (void)close(dirfd); dirfd = -1; if (cnt != dircnt) tty_warn(1, "Unable to set mode and times for created directories"); return; #else DIRDATA *dblk; for (dblk = dirdata_head; dblk != NULL; dblk = dirdata_head) { dirdata_head = dblk->next; /* * frc_mode set, make sure we set the file modes even if * the user didn't ask for it (see file_subs.c for more info) */ if (pmode || dblk->frc_mode) set_pmode(dblk->name, dblk->mode); if (patime || pmtime) set_ftime(dblk->name, dblk->mtime, dblk->atime, 0, 0); if (pfflags) set_chflags(dblk->name, dblk->fflags); free(dblk->name); free(dblk); } #endif /* DIRS_USE_FILE */ }
void add_dir(char *name, int nlen, struct stat *psb, int frc_mode) { #ifdef DIRS_USE_FILE DIRDATA dblk; if (dirfd < 0) return; /* * get current position (where file name will start) so we can store it * in the trailer */ if ((dblk.npos = lseek(dirfd, 0L, SEEK_CUR)) < 0) { tty_warn(1, "Unable to store mode and times for directory: %s",name); return; } /* * write the file name followed by the trailer */ dblk.nlen = nlen + 1; dblk.mode = psb->st_mode & 0xffff; dblk.mtime = psb->st_mtime; dblk.atime = psb->st_atime; #if HAVE_FILE_FLAGS dblk.fflags = psb->st_flags; #else dblk.fflags = 0; #endif dblk.frc_mode = frc_mode; if ((xwrite(dirfd, name, dblk.nlen) == dblk.nlen) && (xwrite(dirfd, (char *)&dblk, sizeof(dblk)) == sizeof(dblk))) { ++dircnt; return; } tty_warn(1, "Unable to store mode and times for created directory: %s",name); return; #else DIRDATA *dblk; if ((dblk = malloc(sizeof(*dblk))) == NULL || (dblk->name = strdup(name)) == NULL) { tty_warn(1, "Unable to store mode and times for directory: %s",name); if (dblk != NULL) free(dblk); return; } dblk->mode = psb->st_mode & 0xffff; dblk->mtime = psb->st_mtime; dblk->atime = psb->st_atime; #if HAVE_FILE_FLAGS dblk->fflags = psb->st_flags; #else dblk->fflags = 0; #endif dblk->frc_mode = frc_mode; dblk->next = dirdata_head; dirdata_head = dblk; return; #endif /* DIRS_USE_FILE */ }
int ftree_start() { #ifndef SMALL /* * if -M is given, the list of filenames on stdin is actually * an mtree(8) specfile, so parse the specfile into a NODE * * tree at ftnode, for use by next_file() */ if (Mflag) { if (fthead != NULL) { tty_warn(1, "The -M flag is only supported when reading file list from stdin"); return -1; } ftnode = spec(stdin); if (ftnode != NULL && (ftnode->type != F_DIR || strcmp(ftnode->name, ".") != 0)) { tty_warn(1, "First node of specfile is not `.' directory"); return -1; } return 0; } #endif /* SMALL */ /* * set up the operation mode of fts, open the first file arg. We must * use FTS_NOCHDIR, as the user may have to open multiple archives and * if fts did a chdir off into the boondocks, we may create an archive * volume in an place where the user did not expect to. */ ftsopts = FTS_NOCHDIR; /* * optional user flags that effect file traversal * -H command line symlink follow only (half follow) * -L follow sylinks (logical) * -P do not follow sylinks (physical). This is the default. * -X do not cross over mount points * -t preserve access times on files read. * -n select only the first member of a file tree when a match is found * -d do not extract subtrees rooted at a directory arg. */ if (Lflag) ftsopts |= FTS_LOGICAL; else ftsopts |= FTS_PHYSICAL; if (Hflag) ftsopts |= FTS_COMFOLLOW; if (Xflag) ftsopts |= FTS_XDEV; if ((fthead == NULL) && ((farray[0] = malloc(PAXPATHLEN+2)) == NULL)) { tty_warn(1, "Unable to allocate memory for file name buffer"); return -1; } if (ftree_arg() < 0) return -1; if (tflag && (atdir_start() < 0)) return -1; return 0; }