int next_file(ARCHD *arcn) { int cnt; time_t atime; time_t mtime; /* * 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); } /* * loop until we get a valid file to process */ for (;;) { if ((ftent = fts_read(ftsp)) == NULL) { if (errno) syswarn(1, errno, "next_file"); /* * 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); continue; case FTS_DC: /* * fts claims a file system cycle */ paxwarn(1,"File system cycle found at %s",ftent->fts_path); continue; case FTS_DNR: syswarn(1, ftent->fts_errno, "Unable to read directory %s", ftent->fts_path); continue; case FTS_ERR: syswarn(1, ftent->fts_errno, "File system traversal error"); continue; case FTS_NS: case FTS_NSOK: syswarn(1, ftent->fts_errno, "Unable to access %s", ftent->fts_path); continue; } /* * 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'; memcpy(&arcn->sb, ftent->fts_statp, sizeof(arcn->sb)); /* * 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; /* * have to read the symlink path from the file */ if ((cnt = readlink(ftent->fts_path, arcn->ln_name, PAXPATHLEN)) < 0) { syswarn(1, errno, "Unable to read symlink %s", ftent->fts_path); continue; } /* * set link name length, watch out readlink does not * always NUL terminate the link path */ arcn->ln_name[cnt] = '\0'; arcn->ln_nlen = cnt; break; 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; 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)); if (arcn->nlen >= sizeof(arcn->name)) arcn->nlen = sizeof(arcn->name) - 1; /* XXX truncate? */ arcn->org_name = ftent->fts_path; 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; }