bool CTar32::readdir_TAR(CTar32FileStatus &stat) { HEADER tar_header; if(!readTarHeader(tar_header))return false; //get tar format : GNU or POSIX int tar_format=tar_header.getFormat(); // HP-UX's tar command create 100chars filename part. fixed on 2003.12.19 char tmp_name[COUNTOF(tar_header.dbuf.name)+1]; strncpy(tmp_name, tar_header.dbuf.name, COUNTOF(tar_header.dbuf.name)); tmp_name[COUNTOF(tar_header.dbuf.name)] = '\0'; stat.filename = tmp_name; /* tar_header.dbuf.name; */ stat.original_size = parseOctNum(tar_header.dbuf.size,COUNTOF(tar_header.dbuf.size)); if(tar_header.dbuf.typeflag == LNKTYPE){ // Fixed on 2003/11/28. For "spencer_pwb.tar.gz". Thanks to rollo-san. stat.original_size = 0; } stat.blocksize = 512; if(tar_header.dbuf.typeflag == LONGLINK){ // tar_header.dbuf.name == "././@LongLink" //NOTE:TAR32.DLL earlier than 2.33 makes LONGLINK entry with POSIX header tar_format=TAR_FORMAT_GNU; //char longfilename[2000] = ""; std::vector<char> longfilename; size64 readsize = (size_t(stat.original_size-1)/512+1)*512; longfilename.resize((size_t)readsize+1); //TODO:size lost size64 ret = m_pfile->read(&longfilename[0], readsize); if(ret == 0){ throw CTar32Exception("can't get filename(LongLink)",ERROR_HEADER_BROKEN); } longfilename[(size_t)stat.original_size]='\0'; //TODO:size lost if(!readTarHeader(tar_header))return false; stat.filename = &longfilename[0]; stat.original_size = parseOctNum(tar_header.dbuf.size, COUNTOF(tar_header.dbuf.size)); } bool bPaxFilenameSupplied=false; time_t pax_atime=0,pax_ctime=0,pax_mtime=0; if(tar_header.dbuf.typeflag == PAX_GLOBAL || tar_header.dbuf.typeflag == PAX_ENTRTY){ std::vector<char> content; size64 readsize = (size_t(stat.original_size-1)/512+1)*512; content.resize((size_t)readsize+1); //TODO:size lost size64 ret = m_pfile->read(&content[0], readsize); if(ret == 0){ throw CTar32Exception("can't get PAX Extended Global Header",ERROR_HEADER_BROKEN); } content[(size_t)stat.original_size]='\0'; //TODO:size lost if(!readTarHeader(tar_header))return false; stat.original_size = parseOctNum(tar_header.dbuf.size, COUNTOF(tar_header.dbuf.size)); strncpy(tmp_name, tar_header.dbuf.name, COUNTOF(tar_header.dbuf.name)); tmp_name[COUNTOF(tar_header.dbuf.name)] = '\0'; stat.filename = tmp_name; /* tar_header.dbuf.name; */ std::string extFilename; size64 filesize; if(!parsePaxExtHeader(&content[0],content.size(),extFilename,filesize,pax_atime,pax_ctime,pax_mtime)){ if(tar_header.dbuf.typeflag == PAX_GLOBAL){ throw CTar32Exception("Broken PAX Extended Global Header",ERROR_HEADER_BROKEN); }else{ throw CTar32Exception("Broken PAX Extended Header",ERROR_HEADER_BROKEN); } } if(tar_header.dbuf.typeflag == PAX_GLOBAL){ //global header //TODO:need test //if(filesize!=-1)m_currentfile_status.original_size=filesize; }else{ //entry header //TODO:need test //if(filesize!=-1)stat.original_size=filesize; if(!extFilename.empty()){ bPaxFilenameSupplied=true; stat.filename=extFilename; } } } //charset conversion if(m_archive_charset!=CHARSET_DONTCARE && !bPaxFilenameSupplied){ if(m_archive_charset==CHARSET_UNKNOWN){ //detect charset m_archive_charset=detect_charset(stat.filename.c_str()); } switch(m_archive_charset){ case CHARSET_EUCJP: stat.filename=CConvertCharsetHelper::getInstance().eucjp_to_sjis(stat.filename.c_str(),stat.filename.size()); break; case CHARSET_UTF8N: //FALLTHROUGH case CHARSET_UTF8: stat.filename=CConvertCharsetHelper::getInstance().utf8_to_sjis(stat.filename.c_str(),stat.filename.size()); break; case CHARSET_JIS: //FALLTHROUGH /* force to extract even if charset is not supported. */ //throw CTar32Exception("tar header charset error.",ERROR_NOT_SUPPORT); //break; case CHARSET_SJIS: //FALLTHROUGH default: //nothing to do break; } } stat.mode = strtol(tar_header.dbuf.mode, NULL, 8); stat.uid = strtol(tar_header.dbuf.uid , NULL, 8); stat.gid = strtol(tar_header.dbuf.gid , NULL, 8); stat.mtime = strtol(tar_header.dbuf.mtime , NULL, 8); stat.chksum = strtol(tar_header.dbuf.chksum , NULL, 8); stat.typeflag = tar_header.dbuf.typeflag; stat.linkname = tar_header.dbuf.linkname; strncpy(stat.magic_version, tar_header.dbuf.magic,8); strncpy(stat.uname, tar_header.dbuf.uname, 32); strncpy(stat.gname, tar_header.dbuf.gname, 32); stat.devmajor = strtol(tar_header.dbuf.devmajor , NULL, 8); stat.devminor = strtol(tar_header.dbuf.devminor , NULL, 8); if(tar_format==TAR_FORMAT_GNU){ stat.atime = strtol(tar_header.dbuf.exthead.gnu.atime , NULL, 8); stat.ctime = strtol(tar_header.dbuf.exthead.gnu.ctime , NULL, 8); stat.offset = parseOctNum(tar_header.dbuf.exthead.gnu.offset , COUNTOF(tar_header.dbuf.exthead.gnu.offset)); }else{ //POSIX int length=min(COUNTOF(tar_header.dbuf.exthead.posix.prefix),strlen(tar_header.dbuf.exthead.posix.prefix)); if(length>0){ std::string prefix(tar_header.dbuf.exthead.posix.prefix,tar_header.dbuf.exthead.posix.prefix+length); stat.filename= prefix + '/' + stat.filename; } } if(pax_atime!=0)stat.atime=pax_atime; if(pax_ctime!=0)stat.ctime=pax_ctime; if(pax_mtime!=0)stat.mtime=pax_mtime; if(stat.typeflag == DIRTYPE){ stat.mode &= ~_S_IFMT; stat.mode |= _S_IFDIR; } if((stat.mode & _S_IFMT) == _S_IFDIR){ const char * f = stat.filename.c_str(); if((char*)max(_mbsrchr((unsigned char*)f, '/'), _mbsrchr((unsigned char*)f,'\\')) != f+strlen(f)-1){ stat.filename = stat.filename + "/"; } } return true; }
/* * Read a tar file and extract or list the specified files within it. * If the list is empty than all files are extracted or listed. */ static int readTarFile(int tarFd, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag, char** extractList, char** excludeList) { int status; int errorFlag=FALSE; int skipNextHeaderFlag=FALSE; TarHeader rawHeader; TarInfo header; /* Read the tar file, and iterate over it one file at a time */ while ( (status = full_read(tarFd, (char*)&rawHeader, TAR_BLOCK_SIZE)) == TAR_BLOCK_SIZE ) { /* Try to read the header */ if ( readTarHeader(&rawHeader, &header) == FALSE ) { if ( *(header.name) == '\0' ) { goto endgame; } else { errorFlag=TRUE; error_msg("Bad tar header, skipping"); continue; } } if ( *(header.name) == '\0' ) continue; header.tarFd = tarFd; /* Skip funky extra GNU headers that precede long files */ if ( (header.type == GNULONGNAME) || (header.type == GNULONGLINK) ) { skipNextHeaderFlag=TRUE; if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) errorFlag = TRUE; continue; } if ( skipNextHeaderFlag == TRUE ) { skipNextHeaderFlag=FALSE; error_msg(name_longer_than_foo, NAME_SIZE); if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) errorFlag = TRUE; continue; } #if defined BB_FEATURE_TAR_EXCLUDE if (exclude_file(excludeList, header.name)) { /* There are not the droids you're looking for, move along */ /* If it is a regular file, pretend to extract it with * the extractFlag set to FALSE, so the junk in the tarball * is properly skipped over */ if ( header.type==REGTYPE || header.type==REGTYPE0 ) { if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) errorFlag = TRUE; } continue; } #endif if (!extract_file(extractList, header.name)) { /* There are not the droids you're looking for, move along */ /* If it is a regular file, pretend to extract it with * the extractFlag set to FALSE, so the junk in the tarball * is properly skipped over */ if ( header.type==REGTYPE || header.type==REGTYPE0 ) { if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) errorFlag = TRUE; } continue; } if (listFlag == TRUE) { /* Special treatment if the list (-t) flag is on */ if (verboseFlag == TRUE) { int len, len1; char buf[35]; struct tm *tm = localtime (&(header.mtime)); len=printf("%s ", mode_string(header.mode)); my_getpwuid(buf, header.uid); if (! *buf) len+=printf("%d", header.uid); else len+=printf("%s", buf); my_getgrgid(buf, header.gid); if (! *buf) len+=printf("/%-d ", header.gid); else len+=printf("/%-s ", buf); if (header.type==CHRTYPE || header.type==BLKTYPE) { len1=snprintf(buf, sizeof(buf), "%ld,%-ld ", header.devmajor, header.devminor); } else { len1=snprintf(buf, sizeof(buf), "%lu ", (long)header.size); } /* Jump through some hoops to make the columns match up */ for(;(len+len1)<31;len++) printf(" "); printf(buf); /* Use ISO 8610 time format */ if (tm) { printf ("%04d-%02d-%02d %02d:%02d:%02d ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); } } printf("%s", header.name); if (verboseFlag == TRUE) { if (header.type==LNKTYPE) /* If this is a link, say so */ printf(" link to %s", header.linkname); else if (header.type==SYMTYPE) printf(" -> %s", header.linkname); } printf("\n"); } /* List contents if we are supposed to do that */ if (verboseFlag == TRUE && extractFlag == TRUE) { /* Now the normal listing */ FILE *vbFd = stdout; if (tostdoutFlag == TRUE) // If the archive goes to stdout, verbose to stderr vbFd = stderr; fprintf(vbFd, "%s\n", header.name); } /* Remove files if we would overwrite them */ if (extractFlag == TRUE && tostdoutFlag == FALSE) unlink(header.name); /* If we got here, we can be certain we have a legitimate * header to work with. So work with it. */ switch ( header.type ) { case REGTYPE: case REGTYPE0: /* If the name ends in a '/' then assume it is * supposed to be a directory, and fall through */ if (!last_char_is(header.name,'/')) { if (tarExtractRegularFile(&header, extractFlag, tostdoutFlag)==FALSE) errorFlag=TRUE; break; } case DIRTYPE: if (tarExtractDirectory( &header, extractFlag, tostdoutFlag)==FALSE) errorFlag=TRUE; break; case LNKTYPE: if (tarExtractHardLink( &header, extractFlag, tostdoutFlag)==FALSE) errorFlag=TRUE; break; case SYMTYPE: if (tarExtractSymLink( &header, extractFlag, tostdoutFlag)==FALSE) errorFlag=TRUE; break; case CHRTYPE: case BLKTYPE: case FIFOTYPE: if (tarExtractSpecial( &header, extractFlag, tostdoutFlag)==FALSE) errorFlag=TRUE; break; #if 0 /* Handled earlier */ case GNULONGNAME: case GNULONGLINK: skipNextHeaderFlag=TRUE; break; #endif default: error_msg("Unknown file type '%c' in tar file", header.type); close( tarFd); return( FALSE); } } close(tarFd); if (status > 0) { /* Bummer - we read a partial header */ perror_msg("Error reading tar file"); return ( FALSE); } else if (errorFlag==TRUE) { error_msg( "Error exit delayed from previous errors"); return( FALSE); } else return( status); /* Stuff to do when we are done */ endgame: close( tarFd); if ( *(header.name) == '\0' ) { if (errorFlag==TRUE) error_msg( "Error exit delayed from previous errors"); else return( TRUE); } return( FALSE); }