static int get_phys(void) { int padsz = 0; int res; int phyblk; struct mtop mb; char scbuf[MAXBLK]; /* * move to the file mark, and then back up one record and read it. * this should tell us the physical record size the tape is using. */ if (lstrval == 1) { /* * we know we are at file mark when we get back a 0 from * read() */ while ((res = read(arfd, scbuf, sizeof(scbuf))) > 0) padsz += res; if (res < 0) { syswarn(1, errno, "Unable to locate tape filemark."); return(-1); } } /* * move backwards over the file mark so we are at the end of the * last record. */ mb.mt_op = MTBSF; mb.mt_count = 1; if (ioctl(arfd, MTIOCTOP, &mb) < 0) { syswarn(1, errno, "Unable to backspace over tape filemark."); return(-1); } /* * move backwards so we are in front of the last record and read it to * get physical tape blocksize. */ mb.mt_op = MTBSR; mb.mt_count = 1; if (ioctl(arfd, MTIOCTOP, &mb) < 0) { syswarn(1, errno, "Unable to backspace over last tape block."); return(-1); } if ((phyblk = read(arfd, scbuf, sizeof(scbuf))) <= 0) { syswarn(1, errno, "Cannot determine archive tape blocksize."); return(-1); } /* * read forward to the file mark, then back up in front of the filemark * (this is a bit paranoid, but should be safe to do). */ while ((res = read(arfd, scbuf, sizeof(scbuf))) > 0) ; if (res < 0) { syswarn(1, errno, "Unable to locate tape filemark."); return(-1); } mb.mt_op = MTBSF; mb.mt_count = 1; if (ioctl(arfd, MTIOCTOP, &mb) < 0) { syswarn(1, errno, "Unable to backspace over tape filemark."); return(-1); } /* * set lstrval so we know that the filemark has not been seen */ lstrval = 1; /* * return if there was no padding */ if (padsz == 0) return(phyblk); /* * make sure we can move backwards over the padding. (this should * never fail). */ if (padsz % phyblk) { paxwarn(1, "Tape drive unable to backspace requested amount"); return(-1); } /* * move backwards over the padding so the head is where it was when * we were first called (if required). */ mb.mt_op = MTBSR; mb.mt_count = padsz/phyblk; if (ioctl(arfd, MTIOCTOP, &mb) < 0) { syswarn(1,errno,"Unable to backspace tape over %d pad blocks", mb.mt_count); return(-1); } return(phyblk); }
/* * Create a new client struct from a file descriptor and establish a GSS-API * context as a specified service with an incoming client and fills out the * client struct. Returns a new client struct on success and NULL on failure, * logging an appropriate error message. */ struct client * server_new_client(int fd, gss_cred_id_t creds) { struct client *client; struct sockaddr_storage ss; socklen_t socklen; size_t length; char *buffer; gss_buffer_desc send_tok, recv_tok, name_buf; gss_name_t name = GSS_C_NO_NAME; gss_OID doid; OM_uint32 major = 0; OM_uint32 minor = 0; OM_uint32 acc_minor, time_rec; int flags, status; static const OM_uint32 req_gss_flags = (GSS_C_MUTUAL_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG); /* Create and initialize a new client struct. */ client = xcalloc(1, sizeof(struct client)); client->fd = fd; client->context = GSS_C_NO_CONTEXT; /* Fill in hostname and IP address. */ socklen = sizeof(ss); if (getpeername(fd, (struct sockaddr *) &ss, &socklen) != 0) { syswarn("cannot get peer address"); goto fail; } length = INET6_ADDRSTRLEN; buffer = xmalloc(length); client->ipaddress = buffer; status = getnameinfo((struct sockaddr *) &ss, socklen, buffer, length, NULL, 0, NI_NUMERICHOST); if (status != 0) { syswarn("cannot translate IP address of client: %s", gai_strerror(status)); goto fail; } length = NI_MAXHOST; buffer = xmalloc(length); status = getnameinfo((struct sockaddr *) &ss, socklen, buffer, length, NULL, 0, NI_NAMEREQD); if (status == 0) client->hostname = buffer; else free(buffer); /* Accept the initial (worthless) token. */ status = token_recv(client->fd, &flags, &recv_tok, TOKEN_MAX_LENGTH, TIMEOUT); if (status != TOKEN_OK) { warn_token("receiving initial token", status, major, minor); goto fail; } free(recv_tok.value); if (flags == (TOKEN_NOOP | TOKEN_CONTEXT_NEXT | TOKEN_PROTOCOL)) client->protocol = 2; else if (flags == (TOKEN_NOOP | TOKEN_CONTEXT_NEXT)) client->protocol = 1; else { warn("bad token flags %d in initial token", flags); goto fail; } /* Now, do the real work of negotiating the context. */ do { status = token_recv(client->fd, &flags, &recv_tok, TOKEN_MAX_LENGTH, TIMEOUT); if (status != TOKEN_OK) { warn_token("receiving context token", status, major, minor); goto fail; } if (flags == TOKEN_CONTEXT) client->protocol = 1; else if (flags != (TOKEN_CONTEXT | TOKEN_PROTOCOL)) { warn("bad token flags %d in context token", flags); free(recv_tok.value); goto fail; } debug("received context token (size=%lu)", (unsigned long) recv_tok.length); major = gss_accept_sec_context(&acc_minor, &client->context, creds, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &name, &doid, &send_tok, &client->flags, &time_rec, NULL); free(recv_tok.value); /* Send back a token if we need to. */ if (send_tok.length != 0) { debug("sending context token (size=%lu)", (unsigned long) send_tok.length); flags = TOKEN_CONTEXT; if (client->protocol > 1) flags |= TOKEN_PROTOCOL; status = token_send(client->fd, flags, &send_tok, TIMEOUT); if (status != TOKEN_OK) { warn_token("sending context token", status, major, minor); gss_release_buffer(&minor, &send_tok); goto fail; } gss_release_buffer(&minor, &send_tok); } /* Bail out if we lose. */ if (major != GSS_S_COMPLETE && major != GSS_S_CONTINUE_NEEDED) { warn_gssapi("while accepting context", major, acc_minor); goto fail; } if (major == GSS_S_CONTINUE_NEEDED) debug("continue needed while accepting context"); } while (major == GSS_S_CONTINUE_NEEDED); /* Make sure that the appropriate context flags are set. */ if (client->protocol > 1) { if ((client->flags & req_gss_flags) != req_gss_flags) { warn("client did not negotiate appropriate GSS-API flags"); goto fail; } } /* Based on the protocol, set up the callbacks. */ if (client->protocol == 1) { client->setup = server_v1_command_setup; client->finish = server_v1_send_output; client->error = server_v1_send_error; } else { client->setup = server_v2_command_setup; client->finish = server_v2_command_finish; client->error = server_v2_send_error; } /* Get the display version of the client name and store it. */ major = gss_display_name(&minor, name, &name_buf, &doid); if (major != GSS_S_COMPLETE) { warn_gssapi("while displaying client name", major, minor); goto fail; } gss_release_name(&minor, &name); if (gss_oid_equal(doid, GSS_C_NT_ANONYMOUS)) client->anonymous = true; client->user = xstrndup(name_buf.value, name_buf.length); client->expires = time(NULL) + time_rec; gss_release_buffer(&minor, &name_buf); return client; fail: if (client->context != GSS_C_NO_CONTEXT) gss_delete_sec_context(&minor, &client->context, GSS_C_NO_BUFFER); if (name != GSS_C_NO_NAME) gss_release_name(&minor, &name); free(client->ipaddress); free(client->hostname); free(client); return NULL; }
int ar_open(const char *name) { struct mtget mb; if (arfd != -1) (void)close(arfd); arfd = -1; can_unlnk = did_io = io_ok = invld_rec = 0; artyp = ISREG; flcnt = 0; /* * 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 (chdir(chdname) != 0) { syswarn(1, errno, "Failed chdir to %s", chdname); 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)) { paxwarn(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)) artyp = ioctl(arfd, MTIOCGET, &mb) ? ISCHR : ISTAPE; 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; /* * 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, otherwise we will continue to use rdblksz). We * must to 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, worse case, slow... */ blksz = rdblksz = BLKMULT; break; } lstrval = 1; return(0); }
static int gen_init(void) { struct rlimit reslimit; struct sigaction n_hand; struct sigaction o_hand; /* * Really needed to handle large archives. We can run out of memory for * internal tables really fast when we have a whole lot of files... */ if (getrlimit(RLIMIT_DATA , &reslimit) == 0){ reslimit.rlim_cur = reslimit.rlim_max; (void)setrlimit(RLIMIT_DATA , &reslimit); } /* * should file size limits be waived? if the os limits us, this is * needed if we want to write a large archive */ if (getrlimit(RLIMIT_FSIZE , &reslimit) == 0){ reslimit.rlim_cur = reslimit.rlim_max; (void)setrlimit(RLIMIT_FSIZE , &reslimit); } /* * increase the size the stack can grow to */ if (getrlimit(RLIMIT_STACK , &reslimit) == 0){ reslimit.rlim_cur = reslimit.rlim_max; (void)setrlimit(RLIMIT_STACK , &reslimit); } /* * not really needed, but doesn't hurt */ if (getrlimit(RLIMIT_RSS , &reslimit) == 0){ reslimit.rlim_cur = reslimit.rlim_max; (void)setrlimit(RLIMIT_RSS , &reslimit); } /* * signal handling to reset stored directory times and modes. Since * we deal with broken pipes via failed writes we ignore it. We also * deal with any file size limit thorough failed writes. Cpu time * limits are caught and a cleanup is forced. */ if ((sigemptyset(&s_mask) < 0) || (sigaddset(&s_mask, SIGTERM) < 0) || (sigaddset(&s_mask,SIGINT) < 0)||(sigaddset(&s_mask,SIGHUP) < 0) || (sigaddset(&s_mask,SIGPIPE) < 0)||(sigaddset(&s_mask,SIGQUIT)<0) || (sigaddset(&s_mask,SIGXCPU) < 0)||(sigaddset(&s_mask,SIGXFSZ)<0)) { paxwarn(1, "Unable to set up signal mask"); return(-1); } memset(&n_hand, 0, sizeof n_hand); n_hand.sa_mask = s_mask; n_hand.sa_flags = 0; n_hand.sa_handler = sig_cleanup; if ((sigaction(SIGHUP, &n_hand, &o_hand) < 0) && (o_hand.sa_handler == SIG_IGN) && (sigaction(SIGHUP, &o_hand, &o_hand) < 0)) goto out; if ((sigaction(SIGTERM, &n_hand, &o_hand) < 0) && (o_hand.sa_handler == SIG_IGN) && (sigaction(SIGTERM, &o_hand, &o_hand) < 0)) goto out; if ((sigaction(SIGINT, &n_hand, &o_hand) < 0) && (o_hand.sa_handler == SIG_IGN) && (sigaction(SIGINT, &o_hand, &o_hand) < 0)) goto out; if ((sigaction(SIGQUIT, &n_hand, &o_hand) < 0) && (o_hand.sa_handler == SIG_IGN) && (sigaction(SIGQUIT, &o_hand, &o_hand) < 0)) goto out; if ((sigaction(SIGXCPU, &n_hand, &o_hand) < 0) && (o_hand.sa_handler == SIG_IGN) && (sigaction(SIGXCPU, &o_hand, &o_hand) < 0)) goto out; n_hand.sa_handler = SIG_IGN; if ((sigaction(SIGPIPE, &n_hand, &o_hand) < 0) || (sigaction(SIGXFSZ, &n_hand, &o_hand) < 0)) goto out; return(0); out: syswarn(1, errno, "Unable to set up signal handler"); return(-1); }
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); }
static int mk_link(char *to, struct stat *to_sb, char *from, int ign) { struct stat sb; int oerrno; /* * if from file exists, it has to be unlinked to make the link. If the * file exists and -k is set, skip it quietly */ if (lstat(from, &sb) == 0) { if (kflag) return(0); /* * make sure it is not the same file, protect the user */ if ((to_sb->st_dev==sb.st_dev)&&(to_sb->st_ino == sb.st_ino)) { paxwarn(1, "Unable to link file %s to itself", to); return(-1);; } /* * try to get rid of the file, based on the type */ if (S_ISDIR(sb.st_mode)) { if (rmdir(from) < 0) { syswarn(1, errno, "Unable to remove %s", from); return(-1); } } else if (unlink(from) < 0) { if (!ign) { syswarn(1, errno, "Unable to remove %s", from); return(-1); } return(1); } } /* * from file is gone (or did not exist), try to make the hard link. * if it fails, check the path and try it again (if chk_path() says to * try again) */ for (;;) { if (link(to, from) == 0) break; oerrno = errno; if (!nodirs && chk_path(from, to_sb->st_uid, to_sb->st_gid) == 0) continue; if (!ign) { syswarn(1, oerrno, "Could not link to %s from %s", to, from); return(-1); } return(1); } /* * all right the link was made */ return(0); }
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; /* * 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 */ if (fd == -1) { /* GNU hack */ if (gnu_hack_string) err(1, "WARNING! Major Internal Error! GNU hack Failing!"); gnu_hack_string = malloc(wcnt + 1); if (gnu_hack_string == NULL) { paxwarn(1, "Out of memory"); return(-1); } memcpy(gnu_hack_string, st, wcnt); gnu_hack_string[wcnt] = '\0'; } else if (write(fd, st, wcnt) != wcnt) { syswarn(1, errno, "Failed write to file %s", name); return(-1); } st += wcnt; } return(st - str); }
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 (read(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 (write(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 paxwarn(1, "File time table ran out of memory"); if (pt != NULL) free(pt); return(-1); }
int rd_wrfile(ARCHD *arcn, int ofd, off_t *left) { int cnt = 0; off_t size = arcn->sb.st_size; int res = 0; char *fnm = arcn->name; int isem = 1; int rem; int sz = MINFBSZ; struct stat sb; u_long crc = 0L; /* * pass the blocksize of the file being written to the write routine, * if the size is zero, use the default MINFBSZ */ if (fstat(ofd, &sb) == 0) { if (sb.st_blksize > 0) sz = (int)sb.st_blksize; } else syswarn(0,errno,"Unable to obtain block size for file %s",fnm); rem = sz; *left = 0L; /* * Copy the archive to the file the number of bytes specified. We have * to assume that we want to recover file holes as none of the archive * formats can record the location of file holes. */ while (size > 0L) { cnt = bufend - bufpt; /* * if we get a read error, we do not want to skip, as we may * miss a header, so we do not set left, but if we get a write * error, we do want to skip over the unprocessed data. */ if ((cnt <= 0) && ((cnt = buf_fill()) <= 0)) break; cnt = MIN(cnt, size); if ((res = file_write(ofd,bufpt,cnt,&rem,&isem,sz,fnm)) <= 0) { *left = size; break; } if (docrc) { /* * update the actual crc value */ cnt = res; while (--cnt >= 0) crc += *bufpt++ & 0xff; } else bufpt += res; size -= res; } /* * if the last block has a file hole (all zero), we must make sure this * gets updated in the file. We force the last block of zeros to be * written. just closing with the file offset moved forward may not put * a hole at the end of the file. */ if (isem && (arcn->sb.st_size > 0L)) file_flush(ofd, fnm, isem); /* * if we failed from archive read, we do not want to skip */ if ((size > 0L) && (*left == 0L)) return(-1); /* * some formats record a crc on file data. If so, then we compare the * calculated crc to the crc stored in the archive */ if (docrc && (size == 0L) && (arcn->crc != crc)) paxwarn(1,"Actual crc does not match expected crc %s",arcn->name); 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_FILE_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); } 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( #ifdef NET2_FTS ftent->fts_statb.st_dev, ftent->fts_statb.st_ino, #else ftent->fts_statp->st_dev, ftent->fts_statp->st_ino, #endif &mtime, &atime) < 0)) continue; set_ftime(ftent->fts_path, mtime, atime, 1); 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'; #ifdef NET2_FTS arcn->sb = ftent->fts_statb; #else arcn->sb = *(ftent->fts_statp); #endif /* * 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 * allways 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; }
/* * A wrapper around fwrite that checks the return status and reports an error * if the write failed. This is probably futile for stderr, since reporting * the error will probably also fail, but we can try. */ static void fwrite_checked(const void *data, size_t size, size_t nmemb, FILE *stream) { if (fwrite(data, size, nmemb, stream) != nmemb) syswarn("local write of command output failed"); }
int main(int argc, char **argv) { const char *tmpdir; size_t tdlen; /* may not be a constant, thus initialising early */ listf = stderr; now = time(NULL); /* * Keep a reference to cwd, so we can always come back home. */ cwdfd = binopen2(BO_CLEXEC, ".", O_RDONLY); if (cwdfd < 0) { syswarn(1, errno, "Cannot open current working directory."); return(exit_val); } /* * Where should we put temporary files? */ if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0') tmpdir = _PATH_TMP; tdlen = strlen(tmpdir); while (tdlen > 0 && tmpdir[tdlen - 1] == '/') tdlen--; tempfile = malloc(tdlen + 1 + sizeof(_TFILE_BASE)); if (tempfile == NULL) { paxwarn(1, "%s for %s", "Out of memory", "temp file name"); return (exit_val); } if (tdlen) memcpy(tempfile, tmpdir, tdlen); tempbase = tempfile + tdlen; *tempbase++ = '/'; #if HAVE_SETPGENT /* * keep passwd and group files open for faster lookups. */ setpassent(1); setgroupent(1); #endif /* * parse options, determine operational mode, general init */ options(argc, argv); if ((gen_init() < 0) || (tty_init() < 0)) return(exit_val); #if HAVE_PLEDGE /* * pmode needs to restore setugid bits when extracting or copying, * so can't pledge at all then. */ if (pmode == 0 || (act != EXTRACT && act != COPY)) { if (pledge("stdio rpath wpath cpath fattr dpath getpw proc exec tape", NULL) == -1) err(1, "pledge"); /* Copy mode, or no gzip -- don't need to fork/exec. */ if (compress_program == NULL || act == COPY) { if (pledge("stdio rpath wpath cpath fattr dpath getpw tape", NULL) == -1) err(1, "pledge"); } } #endif /* make list fd independent and line-buffered */ if ((listfd = dup(fileno(listf))) < 0 || !(listf = fdopen(listfd, "wb"))) { syswarn(1, errno, "Cannot open list file descriptor"); return (exit_val); } if (fcntl(listfd, F_SETFD, FD_CLOEXEC) == -1) syswarn(0, errno, "%s on list file descriptor", "Failed to set the close-on-exec flag"); setlinebuf(listf); /* * select a primary operation mode */ switch (act) { case EXTRACT: extract(); break; case ARCHIVE: archive(); break; case APPND: if (compress_program != NULL) errx(1, "cannot compress while appending"); append(); break; case COPY: copy(); break; default: /* for ar_io.c etc. */ act = LIST; /* FALLTHROUGH */ case LIST: list(); break; } return(exit_val); }
/* * Take the amount of memory to allocate in bytes as a command-line argument * and call test_malloc with that amount of memory. */ int main(int argc, char *argv[]) { size_t size, max; size_t limit = 0; int willfail = 0; unsigned char code; if (argc < 3) die("Usage error. Type, size, and limit must be given."); errno = 0; size = strtol(argv[2], 0, 10); if (size == 0 && errno != 0) sysdie("Invalid size"); errno = 0; limit = strtol(argv[3], 0, 10); if (limit == 0 && errno != 0) sysdie("Invalid limit"); /* If the code is capitalized, install our customized error handler. */ code = argv[1][0]; if (isupper(code)) { xmalloc_error_handler = test_handler; code = tolower(code); } /* * Decide if the allocation should fail. If it should, set willfail to 2, * so that if it unexpectedly succeeds, we exit with a status indicating * that the test should be skipped. */ max = size; if (code == 's' || code == 'n' || code == 'a' || code == 'v') { max += size; if (limit > 0) limit += size; } if (limit > 0 && max > limit) willfail = 2; /* * If a memory limit was given and we can set memory limits, set it. * Otherwise, exit 2, signalling to the driver that the test should be * skipped. We do this here rather than in the driver due to some * pathological problems with Linux (setting ulimit in the shell caused * the shell to die). */ if (limit > 0) { #if HAVE_SETRLIMIT && defined(RLIMIT_AS) struct rlimit rl; void *tmp; size_t test_size; rl.rlim_cur = limit; rl.rlim_max = limit; if (setrlimit(RLIMIT_AS, &rl) < 0) { syswarn("Can't set data limit to %lu", (unsigned long) limit); exit(2); } if (size < limit || code == 'r' || code == 'y') { test_size = (code == 'r' || code == 'y') ? 10 : size; if (test_size == 0) test_size = 1; tmp = malloc(test_size); if (tmp == NULL) { syswarn("Can't allocate initial memory of %lu (limit %lu)", (unsigned long) test_size, (unsigned long) limit); exit(2); } free(tmp); } #else warn("Data limits aren't supported."); exit(2); #endif } switch (code) { case 'c': exit(test_calloc(size) ? willfail : 1); case 'm': exit(test_malloc(size) ? willfail : 1); case 'r': exit(test_realloc(size) ? willfail : 1); case 'y': exit(test_reallocarray(4, size / 4) ? willfail : 1); case 's': exit(test_strdup(size) ? willfail : 1); case 'n': exit(test_strndup(size) ? willfail : 1); case 'a': exit(test_asprintf(size) ? willfail : 1); case 'v': exit(test_vasprintf(size) ? willfail : 1); default: die("Unknown mode %c", argv[1][0]); break; } exit(1); }
int list(void) { ARCHD *arcn; int res; 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. We * also save current time for ls_list() so we do not make a system * call for each file we need to print. If verbose (vflag) start up * the name and group caches. */ if ((get_arc() < 0) || ((*frmt->options)() < 0) || ((*frmt->st_rd)() < 0)) return 1; now = time(NULL); /* * step through the archive until the format says it is done */ while (next_head(arcn) == 0) { if (arcn->type == PAX_GLL || arcn->type == PAX_GLF) { /* * we need to read, to get the real filename */ off_t cnt; 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 patterns are matched we are done. */ if ((res = pat_match(arcn)) < 0) break; if ((res == 0) && (sel_chk(arcn) == 0)) { /* * pattern resulted in a selected file */ if (pat_sel(arcn) < 0) break; /* * modify the name as requested by the user if name * survives modification, do a listing of the file */ if ((res = mod_name(arcn, RENM)) < 0) break; if (res == 0) { if (arcn->name[0] == '/' && !check_Aflag()) { memmove(arcn->name, arcn->name + 1, strlen(arcn->name)); } ls_list(arcn, now, stdout); } /* * if there's an error writing to stdout then we must * stop now -- we're probably writing to a pipe that * has been closed by the reader. */ if (ferror(stdout)) { syswarn(1, errno, "Listing incomplete."); break; } } /* * skip to next archive format header using values calculated * by the format header read routine */ if (rd_skip(arcn->skip + arcn->pad) == 1) break; } /* * all done, let format have a chance to cleanup, and make sure that * the patterns supplied by the user were all matched */ (void)(*frmt->end_rd)(); (void)sigprocmask(SIG_BLOCK, &s_mask, NULL); ar_close(); pat_chk(); return 0; }
static void wr_archive(ARCHD *arcn, int is_app) { int res; int hlk; int wr_one; off_t cnt; int (*wrf)(); int fd = -1; time_t now; /* * if this format supports hard link storage, start up the database * that detects them. */ if (((hlk = frmt->hlk) == 1) && (lnk_start() < 0)) return; /* * start up the file traversal code and format specific write */ if ((ftree_start() < 0) || ((*frmt->st_wr)() < 0)) return; wrf = frmt->wr; /* * 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; /* * if this is not append, and there are no files, we do not write a * trailer */ wr_one = is_app; now = time(NULL); /* * while there are files to archive, process them one at at time */ while (next_file(arcn) == 0) { /* * check if this file meets user specified options match. */ if (sel_chk(arcn) != 0) continue; fd = -1; if (uflag) { /* * only archive if this file is newer than a file with * the same name that is already stored on the archive */ if ((res = chk_ftime(arcn)) < 0) break; if (res > 0) continue; } /* * this file is considered selected now. see if this is a hard * link to a file already stored */ ftree_sel(arcn); if (hlk && (chk_lnk(arcn) < 0)) break; if ((arcn->type == PAX_REG) || (arcn->type == PAX_HRG) || (arcn->type == PAX_CTG)) { /* * we will have to read this file. by opening it now we * can avoid writing a header to the archive for a file * we were later unable to read (we also purge it from * the link table). */ if ((fd = open(arcn->org_name, O_RDONLY, 0)) < 0) { syswarn(1,errno, "Unable to open %s to read", arcn->org_name); purg_lnk(arcn); continue; } } /* * Now modify the name as requested by the user */ if ((res = mod_name(arcn)) < 0) { /* * name modification says to skip this file, close the * file and purge link table entry */ rdfile_close(arcn, &fd); purg_lnk(arcn); break; } if ((res > 0) || (docrc && (set_crc(arcn, fd) < 0))) { /* * unable to obtain the crc we need, close the file, * purge link table entry */ rdfile_close(arcn, &fd); purg_lnk(arcn); continue; } if (vflag) { if (vflag > 1) ls_list(arcn, now, listf); else { (void)safe_print(arcn->name, listf); vfpart = 1; } } ++flcnt; /* * looks safe to store the file, have the format specific * routine write routine store the file header on the archive */ if ((res = (*wrf)(arcn)) < 0) { rdfile_close(arcn, &fd); break; } wr_one = 1; if (res > 0) { /* * format write says no file data needs to be stored * so we are done messing with this file */ if (vflag && vfpart) { (void)putc('\n', listf); vfpart = 0; } rdfile_close(arcn, &fd); continue; } /* * Add file data to the archive, quit on write error. if we * cannot write the entire file contents to the archive we * must pad the archive to replace the missing file data * (otherwise during an extract the file header for the file * which FOLLOWS this one will not be where we expect it to * be). */ res = (*frmt->wr_data)(arcn, fd, &cnt); rdfile_close(arcn, &fd); if (vflag && vfpart) { (void)putc('\n', listf); vfpart = 0; } if (res < 0) break; /* * pad as required, cnt is number of bytes not written */ if (((cnt > 0) && (wr_skip(cnt) < 0)) || ((arcn->pad > 0) && (wr_skip(arcn->pad) < 0))) break; } /* * tell format to write trailer; pad to block boundary; reset directory * mode/access times, and check if all patterns supplied by the user * were matched. block off signals to avoid chance for multiple entry * into the cleanup code */ if (wr_one) { (*frmt->end_wr)(); wr_fin(); } (void)sigprocmask(SIG_BLOCK, &s_mask, NULL); ar_close(); if (tflag) proc_dir(); ftree_chk(); }
void cp_file(ARCHD *arcn, int fd1, int fd2) { int cnt; off_t cpcnt = 0L; int res = 0; char *fnm = arcn->name; int no_hole = 0; int isem = 1; int rem; int sz = MINFBSZ; struct stat sb; /* * check for holes in the source file. If none, we will use regular * write instead of file write. */ if (((off_t)(arcn->sb.st_blocks * BLKMULT)) >= arcn->sb.st_size) ++no_hole; /* * pass the blocksize of the file being written to the write routine, * if the size is zero, use the default MINFBSZ */ if (fstat(fd2, &sb) == 0) { if (sb.st_blksize > 0) sz = sb.st_blksize; } else syswarn(0,errno,"Unable to obtain block size for file %s",fnm); rem = sz; /* * read the source file and copy to destination file until EOF */ for(;;) { if ((cnt = read(fd1, buf, blksz)) <= 0) break; if (no_hole) res = write(fd2, buf, cnt); else res = file_write(fd2, buf, cnt, &rem, &isem, sz, fnm); if (res != cnt) break; cpcnt += cnt; } /* * check to make sure the copy is valid. */ if (res < 0) syswarn(1, errno, "Failed write during copy of %s to %s", arcn->org_name, arcn->name); else if (cpcnt != arcn->sb.st_size) paxwarn(1, "File %s changed size during copy to %s", arcn->org_name, arcn->name); else if (fstat(fd1, &sb) < 0) syswarn(1, errno, "Failed stat of %s", arcn->org_name); else if (arcn->sb.st_mtime != sb.st_mtime) paxwarn(1, "File %s was modified during copy to %s", arcn->org_name, arcn->name); /* * if the last block has a file hole (all zero), we must make sure this * gets updated in the file. We force the last block of zeros to be * written. just closing with the file offset moved forward may not put * a hole at the end of the file. */ if (!no_hole && isem && (arcn->sb.st_size > 0L)) file_flush(fd2, fnm, isem); return; }
void copy(void) { ARCHD *arcn; int res; int fddest; char *dest_pt; int dlen; int drem; int fdsrc = -1; struct stat sb; ARCHD archd; char dirbuf[PAXPATHLEN+1]; arcn = &archd; /* * set up the destination dir path and make sure it is a directory. We * make sure we have a trailing / on the destination */ dlen = strlcpy(dirbuf, dirptr, sizeof(dirbuf)); if (dlen >= sizeof(dirbuf) || (dlen == sizeof(dirbuf) - 1 && dirbuf[dlen - 1] != '/')) { paxwarn(1, "directory name is too long %s", dirptr); return; } dest_pt = dirbuf + dlen; if (*(dest_pt-1) != '/') { *dest_pt++ = '/'; *dest_pt = '\0'; ++dlen; } drem = PAXPATHLEN - dlen; if (stat(dirptr, &sb) < 0) { syswarn(1, errno, "Cannot access destination directory %s", dirptr); return; } if (!S_ISDIR(sb.st_mode)) { paxwarn(1, "Destination is not a directory %s", dirptr); return; } /* * start up the hard link table; file traversal routines and the * modification time and access mode database */ if ((lnk_start() < 0) || (ftree_start() < 0) || (dir_start() < 0)) return; /* * 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; /* * set up to cp file trees */ cp_start(); /* * while there are files to archive, process them */ while (next_file(arcn) == 0) { fdsrc = -1; /* * check if this file meets user specified options */ if (sel_chk(arcn) != 0) continue; /* * if there is already a file in the destination directory with * the same name and it is newer, skip the one stored on the * archive. * NOTE: this test is done BEFORE name modifications as * specified by pax. this 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 */ if (uflag || Dflag) { /* * create the destination name */ if (strlcpy(dest_pt, arcn->name + (*arcn->name == '/'), drem + 1) > drem) { paxwarn(1, "Destination pathname too long %s", arcn->name); continue; } /* * if existing file is same age or newer skip */ res = lstat(dirbuf, &sb); *dest_pt = '\0'; if (res == 0) { if (uflag && Dflag) { if ((arcn->sb.st_mtime<=sb.st_mtime) && (arcn->sb.st_ctime<=sb.st_ctime)) continue; } else if (Dflag) { if (arcn->sb.st_ctime <= sb.st_ctime) continue; } else if (arcn->sb.st_mtime <= sb.st_mtime) continue; } } /* * this file is considered selected. See if this is a hard link * to a previous file; modify the name as requested by the * user; set the final destination. */ ftree_sel(arcn); if ((chk_lnk(arcn) < 0) || ((res = mod_name(arcn)) < 0)) break; if ((res > 0) || (set_dest(arcn, dirbuf, dlen) < 0)) { /* * skip file, purge from link table */ purg_lnk(arcn); continue; } /* * Non standard -Y and -Z flag. When the existing file is * same age or newer skip */ 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)) continue; } else if (Yflag) { if (arcn->sb.st_ctime <= sb.st_ctime) continue; } else if (arcn->sb.st_mtime <= sb.st_mtime) continue; } if (vflag) { (void)safe_print(arcn->name, listf); vfpart = 1; } ++flcnt; /* * try to create a hard link to the src file if requested * but make sure we are not trying to overwrite ourselves. */ if (lflag) res = cross_lnk(arcn); else res = chk_same(arcn); if (res <= 0) { if (vflag && vfpart) { (void)putc('\n', listf); vfpart = 0; } continue; } /* * have to create a new file */ if ((arcn->type != PAX_REG) && (arcn->type != PAX_CTG)) { /* * create a link or special file */ if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) res = lnk_creat(arcn); else res = node_creat(arcn); if (res < 0) purg_lnk(arcn); if (vflag && vfpart) { (void)putc('\n', listf); vfpart = 0; } continue; } /* * have to copy a regular file to the destination directory. * first open source file and then create the destination file */ if ((fdsrc = open(arcn->org_name, O_RDONLY, 0)) < 0) { syswarn(1, errno, "Unable to open %s to read", arcn->org_name); purg_lnk(arcn); continue; } if ((fddest = file_creat(arcn)) < 0) { rdfile_close(arcn, &fdsrc); purg_lnk(arcn); continue; } /* * copy source file data to the destination file */ cp_file(arcn, fdsrc, fddest); file_close(arcn, fddest); rdfile_close(arcn, &fdsrc); if (vflag && vfpart) { (void)putc('\n', listf); vfpart = 0; } } /* * restore directory modes and times as required; make sure all * patterns were selected block off signals to avoid chance for * multiple entry into the cleanup code. */ (void)sigprocmask(SIG_BLOCK, &s_mask, NULL); ar_close(); proc_dir(); ftree_chk(); }
int ar_next(void) { static char *arcbuf; char buf[PAXPATHLEN+2]; sigset_t o_mask; /* * WE MUST CLOSE THE DEVICE. A lot of devices must see last close, (so * things like writing EOF etc will be done) (Watch out ar_close() can * also be called via a signal handler, so we must prevent a race. */ if (sigprocmask(SIG_BLOCK, &s_mask, &o_mask) < 0) syswarn(0, errno, "Unable to set signal mask"); ar_close(); if (sigprocmask(SIG_SETMASK, &o_mask, NULL) < 0) syswarn(0, errno, "Unable to restore signal mask"); if (done || !wr_trail || strcmp(NM_TAR, argv0) == 0) return(-1); tty_prnt("\nATTENTION! %s archive volume change required.\n", argv0); /* * if i/o is on stdin or stdout, we cannot reopen it (we do not know * the name), the user will be forced to type it in. */ if (strcmp(arcname, stdo) && strcmp(arcname, stdn) && (artyp != ISREG) && (artyp != ISPIPE)) { if (artyp == ISTAPE) { tty_prnt("%s ready for archive tape volume: %d\n", arcname, arvol); tty_prnt("Load the NEXT TAPE on the tape drive"); } else { tty_prnt("%s ready for archive volume: %d\n", arcname, arvol); tty_prnt("Load the NEXT STORAGE MEDIA (if required)"); } if ((act == ARCHIVE) || (act == APPND)) tty_prnt(" and make sure it is WRITE ENABLED.\n"); else tty_prnt("\n"); for(;;) { tty_prnt("Type \"y\" to continue, \".\" to quit %s,", argv0); tty_prnt(" or \"s\" to switch to new device.\nIf you"); tty_prnt(" cannot change storage media, type \"s\"\n"); tty_prnt("Is the device ready and online? > "); if ((tty_read(buf,sizeof(buf))<0) || !strcmp(buf,".")){ done = 1; lstrval = -1; tty_prnt("Quitting %s!\n", argv0); vfpart = 0; return(-1); } if ((buf[0] == '\0') || (buf[1] != '\0')) { tty_prnt("%s unknown command, try again\n",buf); continue; } switch (buf[0]) { case 'y': case 'Y': /* * we are to continue with the same device */ if (ar_open(arcname) >= 0) return(0); tty_prnt("Cannot re-open %s, try again\n", arcname); continue; case 's': case 'S': /* * user wants to open a different device */ tty_prnt("Switching to a different archive\n"); break; default: tty_prnt("%s unknown command, try again\n",buf); continue; } break; } } else tty_prnt("Ready for archive volume: %d\n", arvol); /* * have to go to a different archive */ for (;;) { tty_prnt("Input archive name or \".\" to quit %s.\n", argv0); tty_prnt("Archive name > "); if ((tty_read(buf, sizeof(buf)) < 0) || !strcmp(buf, ".")) { done = 1; lstrval = -1; tty_prnt("Quitting %s!\n", argv0); vfpart = 0; return(-1); } if (buf[0] == '\0') { tty_prnt("Empty file name, try again\n"); continue; } if (!strcmp(buf, "..")) { tty_prnt("Illegal file name: .. try again\n"); continue; } if (strlen(buf) > PAXPATHLEN) { tty_prnt("File name too long, try again\n"); continue; } /* * try to open new archive */ if (ar_open(buf) >= 0) { free(arcbuf); if ((arcbuf = strdup(buf)) == NULL) { done = 1; lstrval = -1; paxwarn(0, "Cannot save archive name."); return(-1); } arcname = arcbuf; break; } tty_prnt("Cannot open %s, try again\n", buf); continue; } 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; 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 = mkdir(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 */ paxwarn(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 */ paxwarn(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; if ((ign = unlnk_exist(nm, arcn->type)) < 0) return(-1); if (++pass <= 1) continue; if (nodirs || chk_path(nm,arcn->sb.st_uid,arcn->sb.st_gid) < 0) { syswarn(1, oerrno, "Could not create: %s", nm); return(-1); } } /* * we were able to create the node. set uid/gid, modes and times */ if (pids) res = ((arcn->type == PAX_SLK) ? set_lids(nm, arcn->sb.st_uid, arcn->sb.st_gid) : set_ids(nm, arcn->sb.st_uid, arcn->sb.st_gid)); else res = 0; /* * symlinks are done now. */ if (arcn->type == PAX_SLK) return(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); if (pmode) set_pmode(nm, 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,"Could not 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) | 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, strlen(nm), &(arcn->sb), 1); } else if (pmode || patime || pmtime) add_dir(nm, strlen(nm), &(arcn->sb), 0); } if (patime || pmtime) set_ftime(nm, arcn->sb.st_mtime, arcn->sb.st_atime, 0); return(0); }
int ar_read(char *buf, int cnt) { int res = 0; /* * if last i/o was in error, no more reads until reset or new volume */ if (lstrval <= 0) return(lstrval); /* * how we read must be based on device type */ switch (artyp) { case ISTAPE: if ((res = read(arfd, buf, cnt)) > 0) { /* * CAUTION: tape systems may not always return the same * sized records so we leave blksz == MAXBLK. The * physical record size that a tape drive supports is * very hard to determine in a uniform and portable * manner. */ io_ok = 1; if (res != rdblksz) { /* * Record size changed. If this is happens on * any record after the first, we probably have * a tape drive which has a fixed record size * we are getting multiple records in a single * read). Watch out for record blocking that * violates pax spec (must be a multiple of * BLKMULT). */ rdblksz = res; if (rdblksz % BLKMULT) invld_rec = 1; } return(res); } break; case ISREG: case ISBLK: case ISCHR: case ISPIPE: default: /* * Files are so easy to deal with. These other things cannot * be trusted at all. So when we are dealing with character * devices and pipes we just take what they have ready for us * and return. Trying to do anything else with them runs the * risk of failure. */ if ((res = read(arfd, buf, cnt)) > 0) { io_ok = 1; return(res); } break; } /* * We are in trouble at this point, something is broken... */ lstrval = res; if (res < 0) syswarn(1, errno, "Failed read on archive volume %d", arvol); else paxwarn(0, "End of archive volume %d reached", arvol); return(res); }
int main(int argc, char *argv[]) { const char *tmpdir; size_t tdlen; (void) setlocale(LC_ALL, ""); listf = stderr; /* * Keep a reference to cwd, so we can always come back home. */ cwdfd = open(".", O_RDONLY); if (cwdfd < 0) { syswarn(0, errno, "Can't open current working directory."); return(exit_val); } /* * Where should we put temporary files? */ if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0') tmpdir = _PATH_TMP; tdlen = strlen(tmpdir); while(tdlen > 0 && tmpdir[tdlen - 1] == '/') tdlen--; tempfile = malloc(tdlen + 1 + sizeof(_TFILE_BASE)); if (tempfile == NULL) { paxwarn(1, "Cannot allocate memory for temp file name."); return(exit_val); } if (tdlen) memcpy(tempfile, tmpdir, tdlen); tempbase = tempfile + tdlen; *tempbase++ = '/'; /* * parse options, determine operational mode, general init */ options(argc, argv); if ((gen_init() < 0) || (tty_init() < 0)) return(exit_val); /* * select a primary operation mode */ switch(act) { case EXTRACT: extract(); break; case ARCHIVE: archive(); break; case APPND: if (gzip_program != NULL) err(1, "can not gzip while appending"); append(); break; case COPY: copy(); break; default: case LIST: list(); break; } return(exit_val); }
int ar_write(char *buf, int bsz) { int res; off_t cpos; /* * do not allow pax to create a "bad" archive. Once a write fails on * an archive volume prevent further writes to it. */ if (lstrval <= 0) return(lstrval); if ((res = write(arfd, buf, bsz)) == bsz) { wr_trail = 1; io_ok = 1; return(bsz); } /* * write broke, see what we can do with it. We try to send any partial * writes that may violate pax spec to the next archive volume. */ if (res < 0) lstrval = res; else lstrval = 0; switch (artyp) { case ISREG: if ((res > 0) && (res % BLKMULT)) { /* * try to fix up partial writes which are not BLKMULT * in size by forcing the runt record to next archive * volume */ if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) break; cpos -= (off_t)res; if (ftruncate(arfd, cpos) < 0) break; res = lstrval = 0; break; } if (res >= 0) break; /* * if file is out of space, handle it like a return of 0 */ if ((errno == ENOSPC) || (errno == EFBIG) || (errno == EDQUOT)) res = lstrval = 0; break; case ISTAPE: case ISCHR: case ISBLK: if (res >= 0) break; if (errno == EACCES) { paxwarn(0, "Write failed, archive is write protected."); res = lstrval = 0; return(0); } /* * see if we reached the end of media, if so force a change to * the next volume */ if ((errno == ENOSPC) || (errno == EIO) || (errno == ENXIO)) res = lstrval = 0; break; case ISPIPE: default: /* * we cannot fix errors to these devices */ break; } /* * Better tell the user the bad news... * if this is a block aligned archive format, we may have a bad archive * if the format wants the header to start at a BLKMULT boundary. While * we can deal with the mis-aligned data, it violates spec and other * archive readers will likely fail. If the format is not block * aligned, the user may be lucky (and the archive is ok). */ if (res >= 0) { if (res > 0) wr_trail = 1; io_ok = 1; } /* * If we were trying to rewrite the trailer and it didn't work, we * must quit right away. */ if (!wr_trail && (res <= 0)) { paxwarn(1,"Unable to append, trailer re-write failed. Quitting."); return(res); } if (res == 0) paxwarn(0, "End of archive volume %d reached", arvol); else if (res < 0) syswarn(1, errno, "Failed write to archive volume: %d", arvol); else if (!frmt->blkalgn || ((res % frmt->blkalgn) == 0)) paxwarn(0,"WARNING: partial archive write. Archive MAY BE FLAWED"); else paxwarn(1,"WARNING: partial archive write. Archive IS FLAWED"); return(res); }
static int ftree_arg(void) { /* * close off the current file tree */ if (ftsp != NULL) { (void)fts_close(ftsp); ftsp = NULL; } /* * keep looping until we get a valid file tree to process. Stop when we * reach the end of the list (or get an eof on stdin) */ for (;;) { if (fthead == NULL) { /* * the user didn't supply any args, get the file trees * to process from stdin; */ if (getpathname(farray[0], PAXPATHLEN+1) == NULL) return(-1); } else { /* * the user supplied the file args as arguments to pax */ if (ftcur == NULL) ftcur = fthead; else if ((ftcur = ftcur->fow) == NULL) return(-1); if (ftcur->chflg) { /* First fchdir() back... */ if (fchdir(cwdfd) < 0) { syswarn(1, errno, "Can't fchdir to starting directory"); return(-1); } if (chdir(ftcur->fname) < 0) { syswarn(1, errno, "Can't chdir to %s", ftcur->fname); return(-1); } continue; } else farray[0] = ftcur->fname; } /* * watch it, fts wants the file arg stored in a array of char * ptrs, with the last one a null. we use a two element array * and set farray[0] to point at the buffer with the file name * in it. We cannot pass all the file args to fts at one shot * as we need to keep a handle on which file arg generates what * files (the -n and -d flags need this). If the open is * successful, return a 0. */ if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL) break; } return(0); }
int ar_rev(off_t sksz) { off_t cpos; struct mtop mb; int phyblk; /* * make sure we do not have try to reverse on a flawed archive */ if (lstrval < 0) return(lstrval); switch(artyp) { case ISPIPE: if (sksz <= 0) break; /* * cannot go backwards on these critters */ paxwarn(1, "Reverse positioning on pipes is not supported."); lstrval = -1; return(-1); case ISREG: case ISBLK: case ISCHR: default: if (sksz <= 0) break; /* * For things other than files, backwards movement has a very * high probability of failure as we really do not know the * true attributes of the device we are talking to (the device * may not even have the ability to lseek() in any direction). * First we figure out where we are in the archive. */ if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) { syswarn(1, errno, "Unable to obtain current archive byte offset"); lstrval = -1; return(-1); } /* * we may try to go backwards past the start when the archive * is only a single record. If this happens and we are on a * multi volume archive, we need to go to the end of the * previous volume and continue our movement backwards from * there. */ if ((cpos -= sksz) < (off_t)0L) { if (arvol > 1) { /* * this should never happen */ paxwarn(1,"Reverse position on previous volume."); lstrval = -1; return(-1); } cpos = (off_t)0L; } if (lseek(arfd, cpos, SEEK_SET) < 0) { syswarn(1, errno, "Unable to seek archive backwards"); lstrval = -1; return(-1); } break; case ISTAPE: /* * Calculate and move the proper number of PHYSICAL tape * blocks. If the sksz is not an even multiple of the physical * tape size, we cannot do the move (this should never happen). * (We also cannot handler trailers spread over two vols). * get_phys() also makes sure we are in front of the filemark. */ if ((phyblk = get_phys()) <= 0) { lstrval = -1; return(-1); } /* * make sure future tape reads only go by physical tape block * size (set rdblksz to the real size). */ rdblksz = phyblk; /* * if no movement is required, just return (we must be after * get_phys() so the physical blocksize is properly set) */ if (sksz <= 0) break; /* * ok we have to move. Make sure the tape drive can do it. */ if (sksz % phyblk) { paxwarn(1, "Tape drive unable to backspace requested amount"); lstrval = -1; return(-1); } /* * move backwards the requested number of bytes */ mb.mt_op = MTBSR; mb.mt_count = sksz/phyblk; if (ioctl(arfd, MTIOCTOP, &mb) < 0) { syswarn(1,errno, "Unable to backspace tape %d blocks.", mb.mt_count); lstrval = -1; return(-1); } break; } lstrval = 1; return(0); }
void extract(void) { ARCHD *arcn; int res; off_t cnt; ARCHD archd; 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; /* * 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; now = time(NULL); /* * step through each entry on the archive until the format read routine * says it is done */ while (next_head(arcn) == 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 == PAX_GLF ? -1 : -2, &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; } /* * 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. */ 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)) < 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; } /* * Non standard -Y and -Z flag. When the existing file is * same age or newer skip */ 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)) if (chdir(arcn->pat->chdname) != 0) syswarn(1, errno, "Cannot chdir to %s", arcn->pat->chdname); /* * 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); else res = node_creat(arcn); (void)rd_skip(arcn->skip + arcn->pad); if (res < 0) purg_lnk(arcn); if (vflag && vfpart) { (void)putc('\n', listf); vfpart = 0; } goto popd; } /* * we have a file with data here. If we can not create it, skip * over the data and purge the name from hard link table */ if ((fd = file_creat(arcn)) < 0) { (void)rd_skip(arcn->skip + arcn->pad); purg_lnk(arcn); goto popd; } /* * extract the file from the archive and skip over padding and * any unprocessed data */ res = (*frmt->rd_data)(arcn, fd, &cnt); file_close(arcn, fd); if (vflag && vfpart) { (void)putc('\n', listf); vfpart = 0; } if (!res) (void)rd_skip(cnt + arcn->pad); popd: /* * if required, chdir around. */ if ((arcn->pat != NULL) && (arcn->pat->chdname != NULL)) if (fchdir(cwdfd) != 0) syswarn(1, errno, "Can't fchdir to starting directory"); } /* * 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, NULL); ar_close(); proc_dir(); pat_chk(); }
/* * Receive request packet and verify the integrity and format of the reply. * This routine is REQUIRED to sanitize the request packet. All other program * routines can expect that the packet is safe to read once it is passed on. * * Returns a newly-allocated lbcd_request struct on success and NULL on * failure. */ static struct request * request_recv(struct lbcd_config *config, socket_type fd) { struct sockaddr_storage addr; struct sockaddr *sockaddr; socklen_t addrlen; ssize_t result; char raw[LBCD_MAXMESG]; char source[INET6_ADDRSTRLEN] = "UNKNOWN"; struct lbcd_request *packet; unsigned int protocol, id, operation, nservices, i; size_t expected; struct request *request; char *service; /* Receive the UDP packet from the wire. */ addrlen = sizeof(addr); sockaddr = (struct sockaddr *) &addr; result = recvfrom(fd, raw, sizeof(raw), 0, sockaddr, &addrlen); if (result <= 0) { syswarn("cannot receive packet"); return NULL; } /* Format the client address for logging. */ if (!network_sockaddr_sprint(source, sizeof(source), sockaddr)) syswarn("cannot convert client address to string"); /* Ensure the packet is large enough to contain the header. */ if ((size_t) result < sizeof(struct lbcd_header)) { warn("client %s: short packet received (length %lu)", source, (unsigned long) result); return NULL; } /* Extract the header fields. */ packet = (struct lbcd_request *) raw; protocol = ntohs(packet->h.version); id = ntohs(packet->h.id); operation = ntohs(packet->h.op); nservices = ntohs(packet->h.status); /* Now, ensure the request packet is exactly the correct size. */ expected = sizeof(struct lbcd_header); if (protocol == 3) { if (nservices > LBCD_MAX_SERVICES) { warn("client %s: too many services in request (%u)", source, nservices); return NULL; } expected += nservices * sizeof(lbcd_name_type); } if ((size_t) result != expected) { warn("client %s: incorrect packet size (%lu != %lu)", source, (unsigned long) result, (unsigned long) expected); return NULL; } /* The packet appears valid. Create the request struct. */ request = xcalloc(1, sizeof(struct request)); request->source = xstrdup(source); request->addrlen = addrlen; request->addr = xmalloc(addrlen); memcpy(request->addr, &addr, addrlen); request->protocol = protocol; request->id = id; request->operation = operation; request->services = vector_new(); /* Check protocol number. */ if (protocol != 2 && protocol != 3) { warn("client %s: protocol version %u unsupported", source, protocol); send_status(request, fd, LBCD_STATUS_VERSION); goto fail; } /* * Protocol version 3 takes a client-supplied list of services, with the * number of client-provided services given in the otherwise-unused status * field of the request header. */ if (request->protocol == 3) for (i = 0; i < nservices; i++) { service = xstrndup(packet->names[i], sizeof(lbcd_name_type)); if (!service_allowed(config, service)) { warn("client %s: service %s not allowed", source, service); send_status(request, fd, LBCD_STATUS_ERROR); free(service); goto fail; } vector_add(request->services, service); free(service); } return request; fail: request_free(request); return NULL; }