1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1992 Keith Muller. 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Keith Muller of the University of California, San Diego. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #ifndef lint 37 #endif /* not lint */ 38 #include <sys/cdefs.h> 39 #include <sys/types.h> 40 #include <sys/time.h> 41 #include <sys/stat.h> 42 #include <unistd.h> 43 #include <string.h> 44 #include <stdio.h> 45 #include <errno.h> 46 #include <stdlib.h> 47 #include <fts.h> 48 #include "pax.h" 49 #include "ftree.h" 50 #include "extern.h" 51 52 /* 53 * routines to interface with the fts library function. 54 * 55 * file args supplied to pax are stored on a single linked list (of type FTREE) 56 * and given to fts to be processed one at a time. pax "selects" files from 57 * the expansion of each arg into the corresponding file tree (if the arg is a 58 * directory, otherwise the node itself is just passed to pax). The selection 59 * is modified by the -n and -u flags. The user is informed when a specific 60 * file arg does not generate any selected files. -n keeps expanding the file 61 * tree arg until one of its files is selected, then skips to the next file 62 * arg. when the user does not supply the file trees as command line args to 63 * pax, they are read from stdin 64 */ 65 66 static FTS *ftsp = NULL; /* current FTS handle */ 67 static int ftsopts; /* options to be used on fts_open */ 68 static char *farray[2]; /* array for passing each arg to fts */ 69 static FTREE *fthead = NULL; /* head of linked list of file args */ 70 static FTREE *fttail = NULL; /* tail of linked list of file args */ 71 static FTREE *ftcur = NULL; /* current file arg being processed */ 72 static FTSENT *ftent = NULL; /* current file tree entry */ 73 static int ftree_skip; /* when set skip to next file arg */ 74 75 static int ftree_arg(void); 76 77 /* 78 * ftree_start() 79 * initialize the options passed to fts_open() during this run of pax 80 * options are based on the selection of pax options by the user 81 * fts_start() also calls fts_arg() to open the first valid file arg. We 82 * also attempt to reset directory access times when -t (tflag) is set. 83 * Return: 84 * 0 if there is at least one valid file arg to process, -1 otherwise 85 */ 86 87 int 88 ftree_start(void) 89 { 90 /* 91 * Set up the operation mode of fts, open the first file arg. We must 92 * use FTS_NOCHDIR, as the user may have to open multiple archives and 93 * if fts did a chdir off into the boondocks, we may create an archive 94 * volume in a place where the user did not expect to. 95 */ 96 ftsopts = FTS_NOCHDIR; 97 98 /* 99 * optional user flags that effect file traversal 100 * -H command line symlink follow only (half follow) 101 * -L follow symlinks (logical) 102 * -P do not follow symlinks (physical). This is the default. 103 * -X do not cross over mount points 104 * -t preserve access times on files read. 105 * -n select only the first member of a file tree when a match is found 106 * -d do not extract subtrees rooted at a directory arg. 107 */ 108 if (Lflag) 109 ftsopts |= FTS_LOGICAL; 110 else 111 ftsopts |= FTS_PHYSICAL; 112 if (Hflag) 113 ftsopts |= FTS_COMFOLLOW; 114 if (Xflag) 115 ftsopts |= FTS_XDEV; 116 117 if ((fthead == NULL) && ((farray[0] = malloc(PAXPATHLEN+2)) == NULL)) { 118 paxwarn(1, "Unable to allocate memory for file name buffer"); 119 return(-1); 120 } 121 122 if (ftree_arg() < 0) 123 return(-1); 124 if (tflag && (atdir_start() < 0)) 125 return(-1); 126 return(0); 127 } 128 129 /* 130 * ftree_add() 131 * add the arg to the linked list of files to process. Each will be 132 * processed by fts one at a time 133 * Return: 134 * 0 if added to the linked list, -1 if failed 135 */ 136 137 int 138 ftree_add(char *str, int chflg) 139 { 140 FTREE *ft; 141 int len; 142 143 /* 144 * simple check for bad args 145 */ 146 if ((str == NULL) || (*str == '\0')) { 147 paxwarn(0, "Invalid file name argument"); 148 return(-1); 149 } 150 151 /* 152 * allocate FTREE node and add to the end of the linked list (args are 153 * processed in the same order they were passed to pax). Get rid of any 154 * trailing / the user may pass us. (watch out for / by itself). 155 */ 156 if ((ft = (FTREE *)malloc(sizeof(FTREE))) == NULL) { 157 paxwarn(0, "Unable to allocate memory for filename"); 158 return(-1); 159 } 160 161 if (((len = strlen(str) - 1) > 0) && (str[len] == '/')) 162 str[len] = '\0'; 163 ft->fname = str; 164 ft->refcnt = 0; 165 ft->chflg = chflg; 166 ft->fow = NULL; 167 if (fthead == NULL) { 168 fttail = fthead = ft; 169 return(0); 170 } 171 fttail->fow = ft; 172 fttail = ft; 173 return(0); 174 } 175 176 /* 177 * ftree_sel() 178 * this entry has been selected by pax. bump up reference count and handle 179 * -n and -d processing. 180 */ 181 182 void 183 ftree_sel(ARCHD *arcn) 184 { 185 /* 186 * set reference bit for this pattern. This linked list is only used 187 * when file trees are supplied pax as args. The list is not used when 188 * the trees are read from stdin. 189 */ 190 if (ftcur != NULL) 191 ftcur->refcnt = 1; 192 193 /* 194 * if -n we are done with this arg, force a skip to the next arg when 195 * pax asks for the next file in next_file(). 196 * if -d we tell fts only to match the directory (if the arg is a dir) 197 * and not the entire file tree rooted at that point. 198 */ 199 if (nflag) 200 ftree_skip = 1; 201 202 if (!dflag || (arcn->type != PAX_DIR)) 203 return; 204 205 if (ftent != NULL) 206 (void)fts_set(ftsp, ftent, FTS_SKIP); 207 } 208 209 /* 210 * ftree_notsel() 211 * this entry has not been selected by pax. 212 */ 213 214 void 215 ftree_notsel(void) 216 { 217 if (ftent != NULL) 218 (void)fts_set(ftsp, ftent, FTS_SKIP); 219 } 220 221 /* 222 * ftree_chk() 223 * called at end on pax execution. Prints all those file args that did not 224 * have a selected member (reference count still 0) 225 */ 226 227 void 228 ftree_chk(void) 229 { 230 FTREE *ft; 231 int wban = 0; 232 233 /* 234 * make sure all dir access times were reset. 235 */ 236 if (tflag) 237 atdir_end(); 238 239 /* 240 * walk down list and check reference count. Print out those members 241 * that never had a match 242 */ 243 for (ft = fthead; ft != NULL; ft = ft->fow) { 244 if ((ft->refcnt > 0) || ft->chflg) 245 continue; 246 if (wban == 0) { 247 paxwarn(1,"WARNING! These file names were not selected:"); 248 ++wban; 249 } 250 (void)fprintf(stderr, "%s\n", ft->fname); 251 } 252 } 253 254 /* 255 * ftree_arg() 256 * Get the next file arg for fts to process. Can be from either the linked 257 * list or read from stdin when the user did not them as args to pax. Each 258 * arg is processed until the first successful fts_open(). 259 * Return: 260 * 0 when the next arg is ready to go, -1 if out of file args (or EOF on 261 * stdin). 262 */ 263 264 static int 265 ftree_arg(void) 266 { 267 char *pt; 268 269 /* 270 * close off the current file tree 271 */ 272 if (ftsp != NULL) { 273 (void)fts_close(ftsp); 274 ftsp = NULL; 275 } 276 277 /* 278 * keep looping until we get a valid file tree to process. Stop when we 279 * reach the end of the list (or get an eof on stdin) 280 */ 281 for(;;) { 282 if (fthead == NULL) { 283 /* 284 * the user didn't supply any args, get the file trees 285 * to process from stdin; 286 */ 287 if (fgets(farray[0], PAXPATHLEN+1, stdin) == NULL) 288 return(-1); 289 if ((pt = strchr(farray[0], '\n')) != NULL) 290 *pt = '\0'; 291 } else { 292 /* 293 * the user supplied the file args as arguments to pax 294 */ 295 if (ftcur == NULL) 296 ftcur = fthead; 297 else if ((ftcur = ftcur->fow) == NULL) 298 return(-1); 299 if (ftcur->chflg) { 300 /* First fchdir() back... */ 301 if (fchdir(cwdfd) < 0) { 302 syswarn(1, errno, 303 "Can't fchdir to starting directory"); 304 return(-1); 305 } 306 if (chdir(ftcur->fname) < 0) { 307 syswarn(1, errno, "Can't chdir to %s", 308 ftcur->fname); 309 return(-1); 310 } 311 continue; 312 } else 313 farray[0] = ftcur->fname; 314 } 315 316 /* 317 * Watch it, fts wants the file arg stored in an array of char 318 * ptrs, with the last one a null. We use a two element array 319 * and set farray[0] to point at the buffer with the file name 320 * in it. We cannot pass all the file args to fts at one shot 321 * as we need to keep a handle on which file arg generates what 322 * files (the -n and -d flags need this). If the open is 323 * successful, return a 0. 324 */ 325 if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL) 326 break; 327 } 328 return(0); 329 } 330 331 /* 332 * next_file() 333 * supplies the next file to process in the supplied archd structure. 334 * Return: 335 * 0 when contents of arcn have been set with the next file, -1 when done. 336 */ 337 338 int 339 next_file(ARCHD *arcn) 340 { 341 int cnt; 342 time_t atime; 343 time_t mtime; 344 345 /* 346 * ftree_sel() might have set the ftree_skip flag if the user has the 347 * -n option and a file was selected from this file arg tree. (-n says 348 * only one member is matched for each pattern) ftree_skip being 1 349 * forces us to go to the next arg now. 350 */ 351 if (ftree_skip) { 352 /* 353 * clear and go to next arg 354 */ 355 ftree_skip = 0; 356 if (ftree_arg() < 0) 357 return(-1); 358 } 359 360 /* 361 * loop until we get a valid file to process 362 */ 363 for(;;) { 364 if ((ftent = fts_read(ftsp)) == NULL) { 365 /* 366 * out of files in this tree, go to next arg, if none 367 * we are done 368 */ 369 if (ftree_arg() < 0) 370 return(-1); 371 continue; 372 } 373 374 /* 375 * handle each type of fts_read() flag 376 */ 377 switch(ftent->fts_info) { 378 case FTS_D: 379 case FTS_DEFAULT: 380 case FTS_F: 381 case FTS_SL: 382 case FTS_SLNONE: 383 /* 384 * these are all ok 385 */ 386 break; 387 case FTS_DP: 388 /* 389 * already saw this directory. If the user wants file 390 * access times reset, we use this to restore the 391 * access time for this directory since this is the 392 * last time we will see it in this file subtree 393 * remember to force the time (this is -t on a read 394 * directory, not a created directory). 395 */ 396 if (!tflag || (get_atdir(ftent->fts_statp->st_dev, 397 ftent->fts_statp->st_ino, &mtime, &atime) < 0)) 398 continue; 399 set_ftime(ftent->fts_path, mtime, atime, 1); 400 continue; 401 case FTS_DC: 402 /* 403 * fts claims a file system cycle 404 */ 405 paxwarn(1,"File system cycle found at %s",ftent->fts_path); 406 continue; 407 case FTS_DNR: 408 syswarn(1, ftent->fts_errno, 409 "Unable to read directory %s", ftent->fts_path); 410 continue; 411 case FTS_ERR: 412 syswarn(1, ftent->fts_errno, 413 "File system traversal error"); 414 continue; 415 case FTS_NS: 416 case FTS_NSOK: 417 syswarn(1, ftent->fts_errno, 418 "Unable to access %s", ftent->fts_path); 419 continue; 420 } 421 422 /* 423 * ok got a file tree node to process. copy info into arcn 424 * structure (initialize as required) 425 */ 426 arcn->skip = 0; 427 arcn->pad = 0; 428 arcn->ln_nlen = 0; 429 arcn->ln_name[0] = '\0'; 430 arcn->sb = *(ftent->fts_statp); 431 432 /* 433 * file type based set up and copy into the arcn struct 434 * SIDE NOTE: 435 * we try to reset the access time on all files and directories 436 * we may read when the -t flag is specified. files are reset 437 * when we close them after copying. we reset the directories 438 * when we are done with their file tree (we also clean up at 439 * end in case we cut short a file tree traversal). However 440 * there is no way to reset access times on symlinks. 441 */ 442 switch(S_IFMT & arcn->sb.st_mode) { 443 case S_IFDIR: 444 arcn->type = PAX_DIR; 445 if (!tflag) 446 break; 447 add_atdir(ftent->fts_path, arcn->sb.st_dev, 448 arcn->sb.st_ino, arcn->sb.st_mtime, 449 arcn->sb.st_atime); 450 break; 451 case S_IFCHR: 452 arcn->type = PAX_CHR; 453 break; 454 case S_IFBLK: 455 arcn->type = PAX_BLK; 456 break; 457 case S_IFREG: 458 /* 459 * only regular files with have data to store on the 460 * archive. all others will store a zero length skip. 461 * the skip field is used by pax for actual data it has 462 * to read (or skip over). 463 */ 464 arcn->type = PAX_REG; 465 arcn->skip = arcn->sb.st_size; 466 break; 467 case S_IFLNK: 468 arcn->type = PAX_SLK; 469 /* 470 * have to read the symlink path from the file 471 */ 472 if ((cnt = readlink(ftent->fts_path, arcn->ln_name, 473 PAXPATHLEN - 1)) < 0) { 474 syswarn(1, errno, "Unable to read symlink %s", 475 ftent->fts_path); 476 continue; 477 } 478 /* 479 * set link name length, watch out readlink does not 480 * always NUL terminate the link path 481 */ 482 arcn->ln_name[cnt] = '\0'; 483 arcn->ln_nlen = cnt; 484 break; 485 case S_IFSOCK: 486 /* 487 * under BSD storing a socket is senseless but we will 488 * let the format specific write function make the 489 * decision of what to do with it. 490 */ 491 arcn->type = PAX_SCK; 492 break; 493 case S_IFIFO: 494 arcn->type = PAX_FIF; 495 break; 496 } 497 break; 498 } 499 500 /* 501 * copy file name, set file name length 502 */ 503 arcn->nlen = l_strncpy(arcn->name, ftent->fts_path, sizeof(arcn->name) - 1); 504 arcn->name[arcn->nlen] = '\0'; 505 arcn->org_name = ftent->fts_path; 506 return(0); 507 } 508