void execcd(void) { word *a = runq->argv->words; word *cdpath; char dir[512]; setstatus("can't cd"); cdpath = vlook("cdpath")->val; switch(count(a)){ default: pfmt(err, "Usage: cd [directory]\n"); break; case 2: if(a->next->word[0]=='/' || cdpath==0) cdpath=&nullpath; for(;cdpath;cdpath = cdpath->next){ strcpy(dir, cdpath->word); if(dir[0]) strcat(dir, "/"); strcat(dir, a->next->word); if(dochdir(dir)>=0){ if(strlen(cdpath->word) && strcmp(cdpath->word, ".")!=0) pfmt(err, "%s\n", dir); setstatus(""); break; } } if(cdpath==0) pfmt(err, "Can't cd %s: %r\n", a->next->word); break; case 1: a = vlook("home")->val; if(count(a)>=1){ if(dochdir(a->word)>=0) setstatus(""); else pfmt(err, "Can't cd %s: %r\n", a->word); } else pfmt(err, "Can't cd -- $home empty\n"); break; } poplist(); }
static void setdir(void) { #if 0 /* let's not require subdirs */ domkdir(PATH_TESTDIR, 0775); dochdir(PATH_TESTDIR); #endif /* 0 */ }
int ar_dochdir(const char *name) { /* First fdochdir() back... */ if (fdochdir(cwdfd) == -1) return -1; if (dochdir(name) == -1) return -1; return 0; }
static void unsetdir(void) { doremove(PATH_KEYS); doremove(PATH_SORTED); #if 0 /* let's not require subdirs */ dochdir(".."); if (rmdir(PATH_TESTDIR) < 0) { complain("%s: rmdir", PATH_TESTDIR); /* but don't exit */ } #endif /* 0 */ }
int ar_open(const char *name) { #ifdef HAVE_SYS_MTIO_H struct mtget mb; #endif if (arfd != -1) (void)close(arfd); arfd = -1; can_unlnk = did_io = io_ok = invld_rec = 0; artyp = ISREG; flcnt = 0; #ifdef SUPPORT_RMT if (name && strchr(name, ':') != NULL && !forcelocal) { artyp = ISRMT; if ((arfd = rmtopen(name, O_RDWR, DMOD)) == -1) { syswarn(0, errno, "Failed open on %s", name); return -1; } if (!isrmt(arfd)) { rmtclose(arfd); tty_warn(0, "Not a remote file: %s", name); return -1; } blksz = rdblksz = 8192; lstrval = 1; return 0; } #endif /* SUPPORT_RMT */ /* * open based on overall operation mode */ switch (act) { case LIST: case EXTRACT: if (name == NULL) { arfd = STDIN_FILENO; arcname = STDN; } else if ((arfd = open(name, EXT_MODE, DMOD)) < 0) syswarn(0, errno, "Failed open to read on %s", name); if (arfd != -1 && gzip_program != NULL) ar_start_gzip(arfd, gzip_program, 0); break; case ARCHIVE: if (name == NULL) { arfd = STDOUT_FILENO; arcname = STDO; } else if ((arfd = open(name, AR_MODE, DMOD)) < 0) syswarn(0, errno, "Failed open to write on %s", name); else can_unlnk = 1; if (arfd != -1 && gzip_program != NULL) ar_start_gzip(arfd, gzip_program, 1); break; case APPND: if (name == NULL) { arfd = STDOUT_FILENO; arcname = STDO; } else if ((arfd = open(name, APP_MODE, DMOD)) < 0) syswarn(0, errno, "Failed open to read/write on %s", name); break; case COPY: /* * arfd not used in COPY mode */ arcname = NONE; lstrval = 1; return 0; } if (arfd < 0) return -1; if (chdname != NULL) if (dochdir(chdname) == -1) return -1; /* * set up is based on device type */ if (fstat(arfd, &arsb) < 0) { syswarn(0, errno, "Failed stat on %s", arcname); (void)close(arfd); arfd = -1; can_unlnk = 0; return -1; } if (S_ISDIR(arsb.st_mode)) { tty_warn(0, "Cannot write an archive on top of a directory %s", arcname); (void)close(arfd); arfd = -1; can_unlnk = 0; return -1; } if (S_ISCHR(arsb.st_mode)) { #ifdef HAVE_SYS_MTIO_H artyp = ioctl(arfd, MTIOCGET, &mb) ? ISCHR : ISTAPE; #else tty_warn(1, "System does not have tape support"); artyp = ISREG; #endif } else if (S_ISBLK(arsb.st_mode)) artyp = ISBLK; else if ((lseek(arfd, (off_t)0L, SEEK_CUR) == -1) && (errno == ESPIPE)) artyp = ISPIPE; else artyp = ISREG; /* * Special handling for empty files. */ if (artyp == ISREG && arsb.st_size == 0) { switch (act) { case LIST: case EXTRACT: return -1; case APPND: act = -ARCHIVE; return -1; case ARCHIVE: break; } } /* * make sure we beyond any doubt that we only can unlink regular files * we created */ if (artyp != ISREG) can_unlnk = 0; /* * if we are writing, we are done */ if (act == ARCHIVE) { blksz = rdblksz = wrblksz; lstrval = 1; return 0; } /* * set default blksz on read. APPNDs writes rdblksz on the last volume * On all new archive volumes, we shift to wrblksz (if the user * specified one, otherwize we will continue to use rdblksz). We * must set blocksize based on what kind of device the archive is * stored. */ switch(artyp) { case ISTAPE: /* * Tape drives come in at least two flavors. Those that support * variable sized records and those that have fixed sized * records. They must be treated differently. For tape drives * that support variable sized records, we must make large * reads to make sure we get the entire record, otherwise we * will just get the first part of the record (up to size we * asked). Tapes with fixed sized records may or may not return * multiple records in a single read. We really do not care * what the physical record size is UNLESS we are going to * append. (We will need the physical block size to rewrite * the trailer). Only when we are appending do we go to the * effort to figure out the true PHYSICAL record size. */ blksz = rdblksz = MAXBLK; break; case ISPIPE: case ISBLK: case ISCHR: /* * Blocksize is not a major issue with these devices (but must * be kept a multiple of 512). If the user specified a write * block size, we use that to read. Under append, we must * always keep blksz == rdblksz. Otherwise we go ahead and use * the device optimal blocksize as (and if) returned by stat * and if it is within pax specs. */ if ((act == APPND) && wrblksz) { blksz = rdblksz = wrblksz; break; } if ((arsb.st_blksize > 0) && (arsb.st_blksize < MAXBLK) && ((arsb.st_blksize % BLKMULT) == 0)) rdblksz = arsb.st_blksize; else rdblksz = DEVBLK; /* * For performance go for large reads when we can without harm */ if ((act == APPND) || (artyp == ISCHR)) blksz = rdblksz; else blksz = MAXBLK; break; case ISREG: /* * if the user specified wrblksz works, use it. Under appends * we must always keep blksz == rdblksz */ if ((act == APPND) && wrblksz && ((arsb.st_size%wrblksz)==0)){ blksz = rdblksz = wrblksz; break; } /* * See if we can find the blocking factor from the file size */ for (rdblksz = MAXBLK; rdblksz > 0; rdblksz -= BLKMULT) if ((arsb.st_size % rdblksz) == 0) break; /* * When we cannot find a match, we may have a flawed archive. */ if (rdblksz <= 0) rdblksz = FILEBLK; /* * for performance go for large reads when we can */ if (act == APPND) blksz = rdblksz; else blksz = MAXBLK; break; default: /* * should never happen, worst case, slow... */ blksz = rdblksz = BLKMULT; break; } lstrval = 1; return 0; }
int file_creat(ARCHD *arcn) { int fd = -1; mode_t file_mode; int oerrno; int rc = 0; char *path_to_open; char *new_path; char *cwd; char cwd_buff[MAXPATHLEN]; /* * Assume file doesn't exist, so just try to create it, most times this * works. We have to take special handling when the file does exist. To * detect this, we use O_EXCL. For example when trying to create a * file and a character device or fifo exists with the same name, we * can accidently open the device by mistake (or block waiting to open). * If we find that the open has failed, then spend the effort to * figure out why. This strategy was found to have better average * performance in common use than checking the file (and the path) * first with lstat. */ file_mode = arcn->sb.st_mode & FILEBITS; if ((fd = open(arcn->name, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, file_mode)) >= 0) return(fd); /* * the file seems to exist. First we try to get rid of it (found to be * the second most common failure when traced). If this fails, only * then we go to the expense to check and create the path to the file */ if (unlnk_exist(arcn->name, arcn->type) != 0) return(-1); path_to_open = arcn->name; new_path = arcn->name; cwd = getcwd(&cwd_buff[0],MAXPATHLEN); if (cwd==NULL) return -1; for (;;) { /* * try to open it again, if this fails, check all the nodes in * the path and give it a final try. if chk_path() finds that * it cannot fix anything, we will skip the last attempt */ if ((fd = open(path_to_open, O_WRONLY | O_CREAT | O_TRUNC, file_mode)) >= 0) { /* clean up the invalid_action */ if (pax_invalid_action>0) { record_pax_invalid_action_results(arcn, path_to_open); } break; } oerrno = errno; if (pax_invalid_action>0) { rc = perform_pax_invalid_action(arcn, oerrno); if (rc == 0) continue; if (rc == 1) { fd = -1; break; } } /* rc == 2 reserved for -o invalid_action=write */ if (nodirs || chk_path(path_to_open,arcn->sb.st_uid,arcn->sb.st_gid, (rc==2) ? &new_path: NULL) < 0) { syswarn((pax_invalid_action==0), oerrno, "Unable to create %s", arcn->name); fd = -1; break; } if (new_path) path_to_open = new_path; /* try again */ } if (strcmp(new_path, arcn->name)!=0) { dochdir(cwd); /* go back to original directory */ } return(fd); }
int extract(void) { ARCHD *arcn; int res; off_t cnt; struct stat sb; int fd; time_t now; arcn = &archd; /* * figure out archive type; pass any format specific options to the * archive option processing routine; call the format init routine; * start up the directory modification time and access mode database */ if ((get_arc() < 0) || ((*frmt->options)() < 0) || ((*frmt->st_rd)() < 0) || (dir_start() < 0)) return 1; now = time((time_t *)NULL); #if !HAVE_NBTOOL_CONFIG_H if (do_chroot) (void)fdochroot(cwdfd); #endif /* * When we are doing interactive rename, we store the mapping of names * so we can fix up hard links files later in the archive. */ if (iflag && (name_start() < 0)) return 1; /* * step through each entry on the archive until the format read routine * says it is done */ while (next_head(arcn) == 0) { int write_to_hard_link = 0; if (arcn->type == PAX_GLL || arcn->type == PAX_GLF) { /* * we need to read, to get the real filename */ if (!(*frmt->rd_data)(arcn, -arcn->type, &cnt)) (void)rd_skip(cnt + arcn->pad); continue; } /* * check for pattern, and user specified options match. When * all the patterns are matched we are done */ if ((res = pat_match(arcn)) < 0) break; if ((res > 0) || (sel_chk(arcn) != 0)) { /* * file is not selected. skip past any file * data and padding and go back for the next * archive member */ (void)rd_skip(arcn->skip + arcn->pad); continue; } if (kflag && (lstat(arcn->name, &sb) == 0)) { (void)rd_skip(arcn->skip + arcn->pad); continue; } /* * with -u or -D only extract when the archive member is newer * than the file with the same name in the file system (no * test of being the same type is required). * NOTE: this test is done BEFORE name modifications as * specified by pax. this operation can be confusing to the * user who might expect the test to be done on an existing * file AFTER the name mod. In honesty the pax spec is probably * flawed in this respect. ignore this for GNU long links. */ if ((uflag || Dflag) && ((lstat(arcn->name, &sb) == 0))) { if (uflag && Dflag) { if ((arcn->sb.st_mtime <= sb.st_mtime) && (arcn->sb.st_ctime <= sb.st_ctime)) { (void)rd_skip(arcn->skip + arcn->pad); continue; } } else if (Dflag) { if (arcn->sb.st_ctime <= sb.st_ctime) { (void)rd_skip(arcn->skip + arcn->pad); continue; } } else if (arcn->sb.st_mtime <= sb.st_mtime) { (void)rd_skip(arcn->skip + arcn->pad); continue; } } /* * this archive member is now been selected. modify the name. */ if ((pat_sel(arcn) < 0) || ((res = mod_name(arcn, RENM)) < 0)) break; if (res > 0) { /* * a bad name mod, skip and purge name from link table */ purg_lnk(arcn); (void)rd_skip(arcn->skip + arcn->pad); continue; } if (arcn->name[0] == '/' && !check_Aflag()) { memmove(arcn->name, arcn->name + 1, strlen(arcn->name)); } /* * Non standard -Y and -Z flag. When the existing file is * same age or newer skip; ignore this for GNU long links. */ if ((Yflag || Zflag) && ((lstat(arcn->name, &sb) == 0))) { if (Yflag && Zflag) { if ((arcn->sb.st_mtime <= sb.st_mtime) && (arcn->sb.st_ctime <= sb.st_ctime)) { (void)rd_skip(arcn->skip + arcn->pad); continue; } } else if (Yflag) { if (arcn->sb.st_ctime <= sb.st_ctime) { (void)rd_skip(arcn->skip + arcn->pad); continue; } } else if (arcn->sb.st_mtime <= sb.st_mtime) { (void)rd_skip(arcn->skip + arcn->pad); continue; } } if (vflag) { if (vflag > 1) ls_list(arcn, now, listf); else { (void)safe_print(arcn->name, listf); vfpart = 1; } } /* * if required, chdir around. */ if ((arcn->pat != NULL) && (arcn->pat->chdname != NULL) && !to_stdout) dochdir(arcn->pat->chdname); if (secure && path_check(arcn, 0) != 0) { (void)rd_skip(arcn->skip + arcn->pad); continue; } /* * all ok, extract this member based on type */ if ((arcn->type != PAX_REG) && (arcn->type != PAX_CTG)) { /* * process archive members that are not regular files. * throw out padding and any data that might follow the * header (as determined by the format). */ if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) res = lnk_creat(arcn, &write_to_hard_link); else res = node_creat(arcn); if (!write_to_hard_link) { (void)rd_skip(arcn->skip + arcn->pad); if (res < 0) purg_lnk(arcn); if (vflag && vfpart) { (void)putc('\n', listf); vfpart = 0; } continue; } } if (to_stdout) fd = STDOUT_FILENO; else { /* * We have a file with data here. If we cannot create * it, skip over the data and purge the name from hard * link table. */ if ((fd = file_creat(arcn, write_to_hard_link)) < 0) { (void)fflush(listf); (void)rd_skip(arcn->skip + arcn->pad); purg_lnk(arcn); continue; } } /* * extract the file from the archive and skip over padding and * any unprocessed data */ res = (*frmt->rd_data)(arcn, fd, &cnt); if (!to_stdout) file_close(arcn, fd); if (vflag && vfpart) { (void)putc('\n', listf); vfpart = 0; } if (!res) (void)rd_skip(cnt + arcn->pad); /* * if required, chdir around. */ if ((arcn->pat != NULL) && (arcn->pat->chdname != NULL)) fdochdir(cwdfd); } /* * all done, restore directory modes and times as required; make sure * all patterns supplied by the user were matched; block off signals * to avoid chance for multiple entry into the cleanup code. */ (void)(*frmt->end_rd)(); (void)sigprocmask(SIG_BLOCK, &s_mask, (sigset_t *)NULL); ar_close(); proc_dir(); pat_chk(); return 0; }