static int rd_ln_nm(ARCHD *arcn) { /* * check the length specified for bogus values */ if ((arcn->sb.st_size == 0) || (arcn->sb.st_size >= sizeof(arcn->ln_name))) { paxwarn(1, "Cpio link name length is invalid: %jd", (intmax_t)arcn->sb.st_size); return(-1); } /* * read in the link name and \0 terminate it */ if (rd_wrbuf(arcn->ln_name, (int)arcn->sb.st_size) != (int)arcn->sb.st_size) { paxwarn(1, "Cpio link name read error"); return(-1); } arcn->ln_nlen = arcn->sb.st_size; arcn->ln_name[arcn->ln_nlen] = '\0'; /* * watch out for those empty link names */ if (arcn->ln_name[0] == '\0') { paxwarn(1, "Cpio link name is corrupt"); return(-1); } return(0); }
static int rd_nm(ARCHD *arcn, int nsz) { /* * do not even try bogus values */ if ((nsz == 0) || (nsz > (int)sizeof(arcn->name))) { paxwarn(1, "Cpio file name length %d is out of range", nsz); return(-1); } /* * read the name and make sure it is not empty and is \0 terminated */ if ((rd_wrbuf(arcn->name,nsz) != nsz) || (arcn->name[nsz-1] != '\0') || (arcn->name[0] == '\0')) { paxwarn(1, "Cpio file name in header is corrupted"); return(-1); } return(0); }
int ustar_rd(ARCHD *arcn, char *buf) { HD_USTAR *hd = (HD_USTAR *)buf; char *dest; int cnt = 0; dev_t devmajor; dev_t devminor; unsigned long long val; /* * we only get proper sized buffers */ if (ustar_id(buf, BLKMULT) < 0) return(-1); #ifndef SMALL reset: #endif memset(arcn, 0, sizeof(*arcn)); arcn->org_name = arcn->name; arcn->sb.st_nlink = 1; #ifndef SMALL /* Process Extended headers. */ if (hd->typeflag == XHDRTYPE || hd->typeflag == GHDRTYPE) { if (rd_xheader(arcn, hd->typeflag == GHDRTYPE, (off_t)asc_ul(hd->size, sizeof(hd->size), OCT)) < 0) return (-1); /* Update and check the ustar header. */ if (rd_wrbuf(buf, BLKMULT) != BLKMULT) return (-1); if (ustar_id(buf, BLKMULT) < 0) return(-1); /* if the next block is another extension, reset the values */ if (hd->typeflag == XHDRTYPE || hd->typeflag == GHDRTYPE) goto reset; } #endif if (!arcn->nlen) { /* * See if the filename is split into two parts. if, so join * the parts. We copy the prefix first and add a / between * the prefix and name. */ dest = arcn->name; if (*(hd->prefix) != '\0') { cnt = fieldcpy(dest, sizeof(arcn->name) - 1, hd->prefix, sizeof(hd->prefix)); dest += cnt; *dest++ = '/'; cnt++; } else cnt = 0; if (hd->typeflag != LONGLINKTYPE && hd->typeflag != LONGNAMETYPE) { arcn->nlen = cnt + expandname(dest, sizeof(arcn->name) - cnt, &gnu_name_string, hd->name, sizeof(hd->name)); } } if (!arcn->ln_nlen && hd->typeflag != LONGLINKTYPE && hd->typeflag != LONGNAMETYPE) { arcn->ln_nlen = expandname(arcn->ln_name, sizeof(arcn->ln_name), &gnu_link_string, hd->linkname, sizeof(hd->linkname)); } /* * follow the spec to the letter. we should only have mode bits, strip * off all other crud we may be passed. */ arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode, sizeof(hd->mode), OCT) & 0xfff); arcn->sb.st_size = (off_t)asc_ull(hd->size, sizeof(hd->size), OCT); val = asc_ull(hd->mtime, sizeof(hd->mtime), OCT); if ((time_t)val < 0 || (time_t)val != val) arcn->sb.st_mtime = INT_MAX; /* XXX 2038 */ else arcn->sb.st_mtime = val; arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; /* * If we can find the ascii names for gname and uname in the password * and group files we will use the uid's and gid they bind. Otherwise * we use the uid and gid values stored in the header. (This is what * the posix spec wants). */ hd->gname[sizeof(hd->gname) - 1] = '\0'; if (Nflag || gid_name(hd->gname, &(arcn->sb.st_gid)) < 0) arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT); hd->uname[sizeof(hd->uname) - 1] = '\0'; if (Nflag || uid_name(hd->uname, &(arcn->sb.st_uid)) < 0) arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT); /* * set the defaults, these may be changed depending on the file type */ arcn->pad = 0; arcn->skip = 0; arcn->sb.st_rdev = (dev_t)0; /* * set the mode and PAX type according to the typeflag in the header */ switch (hd->typeflag) { case FIFOTYPE: arcn->type = PAX_FIF; arcn->sb.st_mode |= S_IFIFO; break; case DIRTYPE: arcn->type = PAX_DIR; arcn->sb.st_mode |= S_IFDIR; arcn->sb.st_nlink = 2; /* * Some programs that create ustar archives append a '/' * to the pathname for directories. This clearly violates * ustar specs, but we will silently strip it off anyway. */ if (arcn->name[arcn->nlen - 1] == '/') arcn->name[--arcn->nlen] = '\0'; break; case BLKTYPE: case CHRTYPE: /* * this type requires the rdev field to be set. */ if (hd->typeflag == BLKTYPE) { arcn->type = PAX_BLK; arcn->sb.st_mode |= S_IFBLK; } else { arcn->type = PAX_CHR; arcn->sb.st_mode |= S_IFCHR; } devmajor = (dev_t)asc_ul(hd->devmajor,sizeof(hd->devmajor),OCT); devminor = (dev_t)asc_ul(hd->devminor,sizeof(hd->devminor),OCT); arcn->sb.st_rdev = TODEV(devmajor, devminor); break; case SYMTYPE: case LNKTYPE: if (hd->typeflag == SYMTYPE) { arcn->type = PAX_SLK; arcn->sb.st_mode |= S_IFLNK; } else { arcn->type = PAX_HLK; /* * so printing looks better */ arcn->sb.st_mode |= S_IFREG; arcn->sb.st_nlink = 2; } break; case LONGLINKTYPE: case LONGNAMETYPE: /* * GNU long link/file; we tag these here and let the * pax internals deal with it -- too ugly otherwise. */ arcn->type = hd->typeflag == LONGLINKTYPE ? PAX_GLL : PAX_GLF; arcn->pad = TAR_PAD(arcn->sb.st_size); arcn->skip = arcn->sb.st_size; break; case CONTTYPE: case AREGTYPE: case REGTYPE: default: /* * these types have file data that follows. Set the skip and * pad fields. */ arcn->type = PAX_REG; arcn->pad = TAR_PAD(arcn->sb.st_size); arcn->skip = arcn->sb.st_size; arcn->sb.st_mode |= S_IFREG; break; } return(0); }
static int rd_xheader(ARCHD *arcn, int global, off_t size) { char buf[MAXXHDRSZ]; unsigned long len; char *delim, *keyword; char *nextp, *p, *end; int pad, ret = 0; /* before we alter size, make note of how much we have to skip */ pad = TAR_PAD((unsigned)size); p = end = buf; while (size > 0 || p < end) { if (size > 0) { int rdlen; /* shift stuff down */ if (p > buf) { memmove(buf, p, end - p); end -= p - buf; p = buf; } /* fill starting at end */ rdlen = MINIMUM(size, (buf + sizeof buf) - end); if (rd_wrbuf(end, rdlen) != rdlen) { ret = -1; break; } size -= rdlen; end += rdlen; } /* [p, end) is good */ if (memchr(p, ' ', end - p) == NULL || !isdigit((unsigned char)*p)) { paxwarn(1, "Invalid extended header record"); ret = -1; break; } errno = 0; len = strtoul(p, &delim, 10); if (*delim != ' ' || (errno == ERANGE && len == ULONG_MAX) || len < MINXHDRSZ) { paxwarn(1, "Invalid extended header record length"); ret = -1; break; } if (len > end - p) { paxwarn(1, "Extended header record length %lu is " "out of range", len); /* if we can just toss this record, do so */ len -= end - p; if (len <= size && rd_skip(len) == 0) { size -= len; p = end = buf; continue; } ret = -1; break; } nextp = p + len; keyword = p = delim + 1; p = memchr(p, '=', len); if (!p || nextp[-1] != '\n') { paxwarn(1, "Malformed extended header record"); ret = -1; break; } *p++ = nextp[-1] = '\0'; if (!global) { if (!strcmp(keyword, "path")) { arcn->nlen = strlcpy(arcn->name, p, sizeof(arcn->name)); } else if (!strcmp(keyword, "linkpath")) { arcn->ln_nlen = strlcpy(arcn->ln_name, p, sizeof(arcn->ln_name)); } } p = nextp; } if (rd_skip(size + pad) < 0) return (-1); return (ret); }
static int get_arc(void) { int i; int hdsz = 0; int res; int minhd = BLKMULT; char *hdend; int notice = 0; /* * find the smallest header size in all archive formats and then set up * to read the archive. */ for (i = 0; ford[i] >= 0; ++i) { if (fsub[ford[i]].hsz < minhd) minhd = fsub[ford[i]].hsz; } if (rd_start() < 0) return -1; res = BLKMULT; hdsz = 0; hdend = hdbuf; for(;;) { for (;;) { /* * fill the buffer with at least the smallest header */ i = rd_wrbuf(hdend, res); if (i > 0) hdsz += i; if (hdsz >= minhd) break; /* * if we cannot recover from a read error quit */ if ((i == 0) || (rd_sync() < 0)) goto out; /* * when we get an error none of the data we already * have can be used to create a legal header (we just * got an error in the middle), so we throw it all out * and refill the buffer with fresh data. */ res = BLKMULT; hdsz = 0; hdend = hdbuf; if (!notice) { if (act == APPND) return -1; tty_warn(1, "Cannot identify format. Searching..."); ++notice; } } /* * we have at least the size of the smallest header in any * archive format. Look to see if we have a match. The array * ford[] is used to specify the header id order to reduce the * chance of incorrectly id'ing a valid header (some formats * may be subsets of each other and the order would then be * important). */ for (i = 0; ford[i] >= 0; ++i) { if ((*fsub[ford[i]].id)(hdbuf, hdsz) < 0) continue; frmt = &(fsub[ford[i]]); /* * yuck, to avoid slow special case code in the extract * routines, just push this header back as if it was * not seen. We have left extra space at start of the * buffer for this purpose. This is a bit ugly, but * adding all the special case code is far worse. */ pback(hdbuf, hdsz); return 0; } /* * We have a flawed archive, no match. we start searching, but * we never allow additions to flawed archives */ if (!notice) { if (act == APPND) return -1; tty_warn(1, "Cannot identify format. Searching..."); ++notice; } /* * brute force search for a header that we can id. * we shift through byte at a time. this is slow, but we cannot * determine the nature of the flaw in the archive in a * portable manner */ if (--hdsz > 0) { memmove(hdbuf, hdbuf+1, hdsz); res = BLKMULT - hdsz; hdend = hdbuf + hdsz; } else { res = BLKMULT; hdend = hdbuf; hdsz = 0; } } out: /* * we cannot find a header, bow, apologize and quit */ tty_warn(1, "Sorry, unable to determine archive format."); return -1; }
static int next_head(ARCHD *arcn) { int ret; char *hdend; int res; int shftsz; int hsz; int in_resync = 0; /* set when we are in resync mode */ int cnt = 0; /* counter for trailer function */ int first = 1; /* on 1st read, EOF isn't premature. */ /* * set up initial conditions, we want a whole frmt->hsz block as we * have no data yet. */ res = hsz = frmt->hsz; hdend = hdbuf; shftsz = hsz - 1; for(;;) { /* * keep looping until we get a contiguous FULL buffer * (frmt->hsz is the proper size) */ for (;;) { if ((ret = rd_wrbuf(hdend, res)) == res) break; /* * If we read 0 bytes (EOF) from an archive when we * expect to find a header, we have stepped upon * an archive without the customary block of zeroes * end marker. It's just stupid to error out on * them, so exit gracefully. */ if (first && ret == 0) return -1; first = 0; /* * some kind of archive read problem, try to resync the * storage device, better give the user the bad news. */ if ((ret == 0) || (rd_sync() < 0)) { tty_warn(1, "Premature end of file on archive read"); return -1; } if (!in_resync) { if (act == APPND) { tty_warn(1, "Archive I/O error, cannot continue"); return -1; } tty_warn(1, "Archive I/O error. Trying to recover."); ++in_resync; } /* * oh well, throw it all out and start over */ res = hsz; hdend = hdbuf; } /* * ok we have a contiguous buffer of the right size. Call the * format read routine. If this was not a valid header and this * format stores trailers outside of the header, call the * format specific trailer routine to check for a trailer. We * have to watch out that we do not mis-identify file data or * block padding as a header or trailer. Format specific * trailer functions must NOT check for the trailer while we * are running in resync mode. Some trailer functions may tell * us that this block cannot contain a valid header either, so * we then throw out the entire block and start over. */ if ((*frmt->rd)(arcn, hdbuf) == 0) break; if (!frmt->inhead) { /* * this format has trailers outside of valid headers */ if ((ret = (*frmt->trail)(hdbuf,in_resync,&cnt)) == 0){ /* * valid trailer found, drain input as required */ ar_drain(); return -1; } if (ret == 1) { /* * we are in resync and we were told to throw * the whole block out because none of the * bytes in this block can be used to form a * valid header */ res = hsz; hdend = hdbuf; continue; } } /* * Brute force section. * not a valid header. We may be able to find a header yet. So * we shift over by one byte, and set up to read one byte at a * time from the archive and place it at the end of the buffer. * We will keep moving byte at a time until we find a header or * get a read error and have to start over. */ if (!in_resync) { if (act == APPND) { tty_warn(1, "Unable to append, archive header flaw"); return -1; } tty_warn(1, "Invalid header, starting valid header search."); ++in_resync; } memmove(hdbuf, hdbuf+1, shftsz); res = 1; hdend = hdbuf + shftsz; } /* * ok got a valid header, check for trailer if format encodes it in the * the header. NOTE: the parameters are different than trailer routines * which encode trailers outside of the header! */ if (frmt->inhead && ((*frmt->subtrail)(arcn) == 0)) { /* * valid trailer found, drain input as required */ ar_drain(); return -1; } ++flcnt; return 0; }