Changeset 67116 in vbox for trunk/src/VBox/Runtime/common/zip/tarvfswriter.cpp
- Timestamp:
- May 26, 2017 12:51:18 PM (8 years ago)
- File:
-
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/common/zip/tarvfswriter.cpp
r67070 r67116 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT - TAR Virtual Filesystem .3 * IPRT - TAR Virtual Filesystem, Writer. 4 4 */ 5 5 6 6 /* 7 * Copyright (C) 2010-201 6Oracle Corporation7 * Copyright (C) 2010-2017 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 32 32 #include <iprt/zip.h> 33 33 34 #include <iprt/asm.h>35 34 #include <iprt/assert.h> 36 #include <iprt/ctype.h>37 35 #include <iprt/err.h> 38 #include <iprt/ poll.h>39 #include <iprt/ file.h>36 #include <iprt/mem.h> 37 #include <iprt/path.h> 40 38 #include <iprt/string.h> 41 39 #include <iprt/vfs.h> … … 49 47 *********************************************************************************************************************************/ 50 48 /** 51 * TAR reader state machine states. 52 */ 53 typedef enum RTZIPTARREADERSTATE 54 { 55 /** Invalid state. */ 56 RTZIPTARREADERSTATE_INVALID = 0, 57 /** Expecting the next file/dir/whatever entry. */ 58 RTZIPTARREADERSTATE_FIRST, 59 /** Expecting more zero headers or the end of the stream. */ 60 RTZIPTARREADERSTATE_ZERO, 61 /** Expecting a GNU long name. */ 62 RTZIPTARREADERSTATE_GNU_LONGNAME, 63 /** Expecting a GNU long link. */ 64 RTZIPTARREADERSTATE_GNU_LONGLINK, 65 /** Expecting a normal header or another GNU specific one. */ 66 RTZIPTARREADERSTATE_GNU_NEXT, 67 /** End of valid states (not included). */ 68 RTZIPTARREADERSTATE_END 69 } RTZIPTARREADERSTATE; 70 71 /** 72 * Tar reader instance data. 73 */ 74 typedef struct RTZIPTARREADER 75 { 76 /** Zero header counter. */ 77 uint32_t cZeroHdrs; 78 /** The state machine state. */ 79 RTZIPTARREADERSTATE enmState; 80 /** The type of the previous TAR header. 81 * @remarks Same a enmType for the first header in the TAR stream. */ 82 RTZIPTARTYPE enmPrevType; 83 /** The type of the current TAR header. */ 84 RTZIPTARTYPE enmType; 85 /** The current header. */ 86 RTZIPTARHDR Hdr; 87 /** The expected long name/link length (GNU). */ 88 uint32_t cbGnuLongExpect; 89 /** The current long name/link length (GNU). */ 90 uint32_t offGnuLongCur; 91 /** The name of the current object. 92 * This is for handling GNU and PAX long names. */ 93 char szName[RTPATH_MAX]; 94 /** The current link target if symlink or hardlink. */ 95 char szTarget[RTPATH_MAX]; 96 } RTZIPTARREADER; 97 /** Pointer to the TAR reader instance data. */ 98 typedef RTZIPTARREADER *PRTZIPTARREADER; 99 100 /** 101 * Tar directory, character device, block device, fifo socket or symbolic link. 102 */ 103 typedef struct RTZIPTARBASEOBJ 104 { 105 /** The stream offset of the (first) header. */ 106 RTFOFF offHdr; 107 /** Pointer to the reader instance data (resides in the filesystem 108 * stream). 109 * @todo Fix this so it won't go stale... Back ref from this obj to fss? */ 110 PRTZIPTARREADER pTarReader; 111 /** The object info with unix attributes. */ 112 RTFSOBJINFO ObjInfo; 113 } RTZIPTARBASEOBJ; 114 /** Pointer to a TAR filesystem stream base object. */ 115 typedef RTZIPTARBASEOBJ *PRTZIPTARBASEOBJ; 116 117 118 /** 119 * Tar file represented as a VFS I/O stream. 120 */ 121 typedef struct RTZIPTARIOSTREAM 122 { 123 /** The basic TAR object data. */ 124 RTZIPTARBASEOBJ BaseObj; 125 /** The number of bytes in the file. */ 126 RTFOFF cbFile; 127 /** The current file position. */ 128 RTFOFF offFile; 129 /** The start position in the hVfsIos (for seekable hVfsIos). */ 130 RTFOFF offStart; 131 /** The number of padding bytes following the file. */ 132 uint32_t cbPadding; 133 /** Set if we've reached the end of the file. */ 134 bool fEndOfStream; 135 /** The input I/O stream. */ 49 * Tar filesystem stream private data. 50 */ 51 typedef struct RTZIPTARFSSTREAMWRITER 52 { 53 /** The output I/O stream. */ 136 54 RTVFSIOSTREAM hVfsIos; 137 } RTZIPTARIOSTREAM; 138 /** Pointer to a the private data of a TAR file I/O stream. */ 139 typedef RTZIPTARIOSTREAM *PRTZIPTARIOSTREAM; 140 141 142 /** 143 * Tar filesystem stream private data. 144 */ 145 typedef struct RTZIPTARFSSTREAM 146 { 147 /** The input I/O stream. */ 148 RTVFSIOSTREAM hVfsIos; 149 150 /** The current object (referenced). */ 151 RTVFSOBJ hVfsCurObj; 152 /** Pointer to the private data if hVfsCurObj is representing a file. */ 153 PRTZIPTARIOSTREAM pCurIosData; 154 155 /** The start offset. */ 156 RTFOFF offStart; 157 /** The offset of the next header. */ 158 RTFOFF offNextHdr; 159 160 /** Set if we've reached the end of the stream. */ 161 bool fEndOfStream; 55 162 56 /** Set if we've encountered a fatal error. */ 163 57 int rcFatal; 164 165 /** The TAR reader instance data. */ 166 RTZIPTARREADER TarReader; 167 } RTZIPTARFSSTREAM; 58 /** Flags. */ 59 uint32_t fFlags; 60 61 /** Number of bytes written. */ 62 uint64_t cbWritten; 63 64 /** Number of headers returned by rtZipTarFssWriter_ObjInfoToHdr. */ 65 uint32_t cHdrs; 66 /** Header buffers returned by rtZipTarFssWriter_ObjInfoToHdr. */ 67 RTZIPTARHDR aHdrs[3]; 68 } RTZIPTARFSSTREAMWRITER; 168 69 /** Pointer to a the private data of a TAR filesystem stream. */ 169 typedef RTZIPTARFSSTREAM *PRTZIPTARFSSTREAM; 170 171 172 173 /** 174 * Converts a numeric header field to the C native type. 70 typedef RTZIPTARFSSTREAMWRITER *PRTZIPTARFSSTREAMWRITER; 71 72 73 /** 74 * Calculates the header checksum and stores it in the chksum field. 175 75 * 176 76 * @returns IPRT status code. 177 * 178 * @param pszField The TAR header field. 179 * @param cchField The length of the field. 180 * @param fOctalOnly Must be octal. 181 * @param pi64 Where to store the value. 182 */ 183 static int rtZipTarHdrFieldToNum(const char *pszField, size_t cchField, bool fOctalOnly, int64_t *pi64) 184 { 185 unsigned char const *puchField = (unsigned char const *)pszField; 186 size_t const cchFieldOrg = cchField; 187 if ( fOctalOnly 188 || !(*puchField & 0x80)) 189 { 190 /* 191 * Skip leading spaces. Include zeros to save a few slower loops below. 192 */ 193 unsigned char ch; 194 while (cchField > 0 && ((ch = *puchField) == ' '|| ch == '0')) 195 cchField--, puchField++; 196 197 /* 198 * Convert octal digits. 199 */ 200 int64_t i64 = 0; 201 while (cchField > 0) 202 { 203 unsigned char uDigit = *puchField - '0'; 204 if (uDigit >= 8) 205 break; 206 i64 <<= 3; 207 i64 |= uDigit; 208 209 puchField++; 210 cchField--; 211 } 212 *pi64 = i64; 213 214 /* 215 * Was it terminated correctly? 216 */ 217 while (cchField > 0) 218 { 219 ch = *puchField++; 220 if (ch != 0 && ch != ' ') 221 return cchField < cchFieldOrg 222 ? VERR_TAR_BAD_NUM_FIELD_TERM 223 : VERR_TAR_BAD_NUM_FIELD; 224 cchField--; 225 } 77 * @param pHdr The header. 78 */ 79 static int rtZipTarFssWriter_ChecksumHdr(PRTZIPTARHDR pHdr) 80 { 81 int32_t iUnsignedChksum; 82 rtZipTarCalcChkSum(pHdr, &iUnsignedChksum, NULL); 83 84 int rc = RTStrFormatU32(pHdr->Common.chksum, sizeof(pHdr->Common.chksum), iUnsignedChksum, 85 8 /*uBase*/, -1 /*cchWidth*/, sizeof(pHdr->Common.chksum) - 1, RTSTR_F_ZEROPAD); 86 AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE); 87 return VINF_SUCCESS; 88 } 89 90 91 /** 92 * Creates one or more tar headers for the object. 93 * 94 * Returns RTZIPTARFSSTREAMWRITER::aHdrs and RTZIPTARFSSTREAMWRITER::cHdrs. 95 * 96 * @returns IPRT status code. 97 * @param pThis The TAR writer instance. 98 * @param pszPath The path to the file. 99 * @param hVfsIos The I/O stream of the file. 100 * @param fFlags The RTVFSFSSTREAMOPS::pfnAdd flags. 101 * @param pObjInfo The object information. 102 * @param pszOwnerNm The owner name. 103 * @param pszGroupNm The group name. 104 * @param chType The tar record type, UINT8_MAX for default. 105 */ 106 static int rtZipTarFssWriter_ObjInfoToHdr(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, PCRTFSOBJINFO pObjInfo, 107 const char *pszOwnerNm, const char *pszGroupNm, uint8_t chType) 108 { 109 pThis->cHdrs = 0; 110 RT_ZERO(pThis->aHdrs[0]); 111 112 /* 113 * The path name first. Make sure to flip DOS slashes. 114 */ 115 size_t cchPath = strlen(pszPath); 116 if (cchPath < sizeof(pThis->aHdrs[0].Common.name)) 117 { 118 memcpy(pThis->aHdrs[0].Common.name, pszPath, cchPath + 1); 119 #if RTPATH_STYLE == RTPATH_STR_F_STYLE_UNIX 120 char *pszDosSlash = strchr(pThis->aHdrs[0].Common.name, '\\'); 121 while (pszDosSlash) 122 { 123 *pszDosSlash = '/'; 124 pszDosSlash = strchr(pszDosSlash + 1, '\\'); 125 } 126 #endif 226 127 } 227 128 else 228 129 { 229 /* 230 * The first byte has the bit 7 set to indicate base-256, while bit 6 231 * is the signed bit. Bits 5:0 are the most significant value bits. 232 */ 233 uint64_t u64; 234 if (!(0x40 & *puchField)) 235 { 236 /* Positive or zero value. */ 237 u64 = *puchField & 0x3f; 238 cchField--; 239 puchField++; 240 241 while (cchField-- > 0) 130 /** @todo implement gnu and pax long name extensions. */ 131 return VERR_TAR_NAME_TOO_LONG; 132 } 133 134 /* 135 * File mode. ASSUME that the unix part of the IPRT mode mask is 136 * compatible with the TAR/Unix world. 137 */ 138 int rc; 139 rc = RTStrFormatU32(pThis->aHdrs[0].Common.mode, sizeof(pThis->aHdrs[0].Common.mode), pObjInfo->Attr.fMode & RTFS_UNIX_MASK, 140 8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.mode) - 1, RTSTR_F_ZEROPAD); 141 AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE); 142 143 144 /* 145 * uid & gid. Just guard against NIL values as they won't fit. 146 */ 147 uint32_t uValue = pObjInfo->Attr.u.Unix.uid != NIL_RTUID ? pObjInfo->Attr.u.Unix.uid : 0; 148 rc = RTStrFormatU32(pThis->aHdrs[0].Common.uid, sizeof(pThis->aHdrs[0].Common.uid), uValue, 149 8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.uid) - 1, RTSTR_F_ZEROPAD); 150 AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE); 151 152 uValue = pObjInfo->Attr.u.Unix.gid != NIL_RTUID ? pObjInfo->Attr.u.Unix.gid : 0; 153 rc = RTStrFormatU32(pThis->aHdrs[0].Common.gid, sizeof(pThis->aHdrs[0].Common.gid), uValue, 154 8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.gid) - 1, RTSTR_F_ZEROPAD); 155 AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE); 156 157 /* 158 * Is the size small enough for the standard octal string encoding? 159 * 160 * Note! We could actually use the terminator character as well if we liked, 161 * but let not do that as it's easier to test this way. 162 */ 163 uint64_t cbObject = pObjInfo->cbObject; 164 if (cbObject < _4G * 2U) 165 { 166 rc = RTStrFormatU64(pThis->aHdrs[0].Common.size, sizeof(pThis->aHdrs[0].Common.size), cbObject, 167 8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.size) - 1, RTSTR_F_ZEROPAD); 168 AssertRCReturn(rc, rc); 169 } 170 /* 171 * No, use the base 256 extension. Set the highest bit of the left most 172 * character. We don't deal with negatives here, cause the size have to 173 * be greater than zero. 174 * 175 * Note! The base-256 extension are never used by gtar or libarchive 176 * with the "ustar \0" format version, only the later 177 * "ustar\000" version. However, this shouldn't cause much 178 * trouble as they are not picky about what they read. 179 */ 180 else 181 { 182 size_t cchField = sizeof(pThis->aHdrs[0].Common.size) - 1; 183 unsigned char *puchField = (unsigned char*)pThis->aHdrs[0].Common.size; 184 puchField[0] = 0x80; 185 do 186 { 187 puchField[cchField--] = cbObject & 0xff; 188 cbObject >>= 8; 189 } while (cchField); 190 } 191 192 /* 193 * Modification time relative to unix epoc. 194 */ 195 rc = RTStrFormatU64(pThis->aHdrs[0].Common.mtime, sizeof(pThis->aHdrs[0].Common.mtime), 196 RTTimeSpecGetSeconds(&pObjInfo->ModificationTime), 197 8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.mtime) - 1, RTSTR_F_ZEROPAD); 198 AssertRCReturn(rc, rc); 199 200 /* Skipping checksum for now */ 201 202 /* 203 * The type flag. 204 */ 205 if (chType == UINT8_MAX) 206 switch (pObjInfo->Attr.fMode & RTFS_TYPE_MASK) 207 { 208 case RTFS_TYPE_FIFO: chType = RTZIPTAR_TF_FIFO; break; 209 case RTFS_TYPE_DEV_CHAR: chType = RTZIPTAR_TF_CHR; break; 210 case RTFS_TYPE_DIRECTORY: chType = RTZIPTAR_TF_DIR; break; 211 case RTFS_TYPE_DEV_BLOCK: chType = RTZIPTAR_TF_BLK; break; 212 case RTFS_TYPE_FILE: chType = RTZIPTAR_TF_NORMAL; break; 213 case RTFS_TYPE_SYMLINK: chType = RTZIPTAR_TF_SYMLINK; break; 214 case RTFS_TYPE_SOCKET: chType = RTZIPTAR_TF_FIFO; break; 215 case RTFS_TYPE_WHITEOUT: AssertFailedReturn(VERR_WRONG_TYPE); 216 } 217 pThis->aHdrs[0].Common.typeflag = chType; 218 219 /* No link name, at least not for now. Caller might set it. */ 220 221 /* 222 * Set TAR record magic and version. 223 */ 224 memcpy(pThis->aHdrs[0].Common.magic, RT_STR_TUPLE("ustar ")); 225 pThis->aHdrs[0].Common.version[0] = ' '; 226 pThis->aHdrs[0].Common.version[1] = ' '; 227 228 /* 229 * Owner and group names. Silently truncate them for now. 230 */ 231 RTStrCopy(pThis->aHdrs[0].Common.uname, sizeof(pThis->aHdrs[0].Common.uname), pszOwnerNm); 232 RTStrCopy(pThis->aHdrs[0].Common.gname, sizeof(pThis->aHdrs[0].Common.uname), pszGroupNm); 233 234 /* 235 * Char/block device numbers. 236 */ 237 if ( RTFS_IS_DEV_BLOCK(pObjInfo->Attr.fMode) 238 || RTFS_IS_DEV_CHAR(pObjInfo->Attr.fMode) ) 239 { 240 rc = RTStrFormatU32(pThis->aHdrs[0].Common.devmajor, sizeof(pThis->aHdrs[0].Common.devmajor), 241 RTDEV_MAJOR(pObjInfo->Attr.u.Unix.Device), 242 8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.devmajor) - 1, RTSTR_F_ZEROPAD); 243 AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE); 244 245 rc = RTStrFormatU32(pThis->aHdrs[0].Common.devminor, sizeof(pThis->aHdrs[0].Common.devmajor), 246 RTDEV_MINOR(pObjInfo->Attr.u.Unix.Device), 247 8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.devmajor) - 1, RTSTR_F_ZEROPAD); 248 AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE); 249 } 250 251 /* 252 * Finally the checksum. 253 */ 254 return rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]); 255 } 256 257 258 /** 259 * Adds a file to the stream. 260 * 261 * @returns IPRT status code. 262 * @param pThis The TAR writer instance. 263 * @param pszPath The path to the file. 264 * @param hVfsIos The I/O stream of the file. 265 * @param fFlags The RTVFSFSSTREAMOPS::pfnAdd flags. 266 * @param pObjInfo The object information. 267 * @param pszOwnerNm The owner name. 268 * @param pszGroupNm The group name. 269 */ 270 static int rtZipTarFssWriter_AddFile(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos, uint32_t fFlags, 271 PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm) 272 { 273 RT_NOREF(fFlags); 274 275 /* 276 * Append the header. 277 */ 278 int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX); 279 if (RT_SUCCESS(rc)) 280 { 281 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL); 282 if (RT_SUCCESS(rc)) 283 { 284 pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]); 285 286 /* 287 * Allocate a tmporary buffer 288 */ 289 uint8_t *pbFree; 290 size_t cbBuf = _512K; 291 uint8_t *pbBuf = pbFree = (uint8_t *)RTMemTmpAlloc(cbBuf); 292 if (!pbBuf) 242 293 { 243 if (RT_LIKELY(u64 <= (uint64_t)INT64_MAX / 256)) 244 u64 = (u64 << 8) | *puchField++; 245 else 246 return VERR_TAR_NUM_VALUE_TOO_LARGE; 247 } 248 } 249 else 250 { 251 /* Negative value (could be used in timestamp). We do manual sign extending here. */ 252 u64 = (UINT64_MAX << 6) | (*puchField & 0x3f); 253 cchField--; 254 puchField++; 255 256 while (cchField-- > 0) 257 { 258 if (RT_LIKELY(u64 >= (uint64_t)(INT64_MIN / 256))) 259 u64 = (u64 << 8) | *puchField++; 260 else 261 return VERR_TAR_NUM_VALUE_TOO_LARGE; 262 } 263 } 264 *pi64 = (int64_t)u64; 265 } 266 267 return VINF_SUCCESS; 268 } 269 270 271 /** 272 * Calculates the TAR header checksums and detects if it's all zeros. 273 * 274 * @returns true if all zeros, false if not. 275 * @param pHdr The header to checksum. 276 * @param pi32Unsigned Where to store the checksum calculated using 277 * unsigned chars. This is the one POSIX 278 * specifies. 279 * @param pi32Signed Where to store the checksum calculated using 280 * signed chars. 281 * 282 * @remarks The reason why we calculate the checksum as both signed and unsigned 283 * has to do with various the char C type being signed on some hosts 284 * and unsigned on others. 285 */ 286 static bool rtZipTarCalcChkSum(PCRTZIPTARHDR pHdr, int32_t *pi32Unsigned, int32_t *pi32Signed) 287 { 288 int32_t i32Unsigned = 0; 289 int32_t i32Signed = 0; 290 291 /* 292 * Sum up the entire header. 293 */ 294 const char *pch = (const char *)pHdr; 295 const char *pchEnd = pch + sizeof(*pHdr); 296 do 297 { 298 i32Unsigned += *(unsigned char *)pch; 299 i32Signed += *(signed char *)pch; 300 } while (++pch != pchEnd); 301 302 /* 303 * Check if it's all zeros and replace the chksum field with spaces. 304 */ 305 bool const fZeroHdr = i32Unsigned == 0; 306 307 pch = pHdr->Common.chksum; 308 pchEnd = pch + sizeof(pHdr->Common.chksum); 309 do 310 { 311 i32Unsigned -= *(unsigned char *)pch; 312 i32Signed -= *(signed char *)pch; 313 } while (++pch != pchEnd); 314 315 i32Unsigned += (unsigned char)' ' * sizeof(pHdr->Common.chksum); 316 i32Signed += (signed char)' ' * sizeof(pHdr->Common.chksum); 317 318 *pi32Unsigned = i32Unsigned; 319 if (pi32Signed) 320 *pi32Signed = i32Signed; 321 return fZeroHdr; 322 } 323 324 325 /** 326 * Validates the TAR header. 327 * 328 * @returns VINF_SUCCESS if valid, VERR_TAR_ZERO_HEADER if all zeros, and 329 * the appropriate VERR_TAR_XXX otherwise. 330 * @param pTar The TAR header. 331 * @param penmType Where to return the type of header on success. 332 */ 333 static int rtZipTarHdrValidate(PCRTZIPTARHDR pTar, PRTZIPTARTYPE penmType) 334 { 335 /* 336 * Calc the checksum first since this enables us to detect zero headers. 337 */ 338 int32_t i32ChkSum; 339 int32_t i32ChkSumSignedAlt; 340 if (rtZipTarCalcChkSum(pTar, &i32ChkSum, &i32ChkSumSignedAlt)) 341 return VERR_TAR_ZERO_HEADER; 342 343 /* 344 * Read the checksum field and match the checksums. 345 */ 346 int64_t i64HdrChkSum; 347 int rc = rtZipTarHdrFieldToNum(pTar->Common.chksum, sizeof(pTar->Common.chksum), true /*fOctalOnly*/, &i64HdrChkSum); 348 if (RT_FAILURE(rc)) 349 return VERR_TAR_BAD_CHKSUM_FIELD; 350 if ( i32ChkSum != i64HdrChkSum 351 && i32ChkSumSignedAlt != i64HdrChkSum) /** @todo test this */ 352 return VERR_TAR_CHKSUM_MISMATCH; 353 354 /* 355 * Detect the TAR type. 356 */ 357 RTZIPTARTYPE enmType; 358 if ( pTar->Common.magic[0] == 'u' 359 && pTar->Common.magic[1] == 's' 360 && pTar->Common.magic[2] == 't' 361 && pTar->Common.magic[3] == 'a' 362 && pTar->Common.magic[4] == 'r') 363 { 364 /** @todo detect star headers */ 365 if ( pTar->Common.magic[5] == '\0' 366 && pTar->Common.version[0] == '0' 367 && pTar->Common.version[1] == '0') 368 enmType = RTZIPTARTYPE_POSIX; 369 else if ( pTar->Common.magic[5] == ' ' 370 && pTar->Common.version[0] == ' ' 371 && pTar->Common.version[1] == '\0') 372 enmType = RTZIPTARTYPE_GNU; 373 else if ( pTar->Common.magic[5] == '\0' /* VMWare ambiguity - they probably mean posix but */ 374 && pTar->Common.version[0] == ' ' /* got the version wrong. */ 375 && pTar->Common.version[1] == '\0') 376 enmType = RTZIPTARTYPE_POSIX; 377 else 378 return VERR_TAR_NOT_USTAR_V00; 379 } 380 else 381 enmType = RTZIPTARTYPE_ANCIENT; 382 *penmType = enmType; 383 384 /* 385 * Perform some basic checks. 386 */ 387 switch (enmType) 388 { 389 case RTZIPTARTYPE_POSIX: 390 if ( !RT_C_IS_ALNUM(pTar->Common.typeflag) 391 && pTar->Common.typeflag != '\0') 392 return VERR_TAR_UNKNOWN_TYPE_FLAG; 393 break; 394 395 case RTZIPTARTYPE_GNU: 396 switch (pTar->Common.typeflag) 397 { 398 case RTZIPTAR_TF_OLDNORMAL: 399 case RTZIPTAR_TF_NORMAL: 400 case RTZIPTAR_TF_CONTIG: 401 case RTZIPTAR_TF_DIR: 402 case RTZIPTAR_TF_CHR: 403 case RTZIPTAR_TF_BLK: 404 case RTZIPTAR_TF_LINK: 405 case RTZIPTAR_TF_SYMLINK: 406 case RTZIPTAR_TF_FIFO: 407 break; 408 409 case RTZIPTAR_TF_GNU_LONGLINK: 410 case RTZIPTAR_TF_GNU_LONGNAME: 411 break; 412 413 case RTZIPTAR_TF_GNU_DUMPDIR: 414 case RTZIPTAR_TF_GNU_MULTIVOL: 415 case RTZIPTAR_TF_GNU_SPARSE: 416 case RTZIPTAR_TF_GNU_VOLDHR: 417 /** @todo Implement full GNU TAR support. .*/ 418 return VERR_TAR_UNSUPPORTED_GNU_HDR_TYPE; 419 420 default: 421 return VERR_TAR_UNKNOWN_TYPE_FLAG; 422 } 423 break; 424 425 case RTZIPTARTYPE_ANCIENT: 426 switch (pTar->Common.typeflag) 427 { 428 case RTZIPTAR_TF_OLDNORMAL: 429 case RTZIPTAR_TF_NORMAL: 430 case RTZIPTAR_TF_CONTIG: 431 case RTZIPTAR_TF_DIR: 432 case RTZIPTAR_TF_LINK: 433 case RTZIPTAR_TF_SYMLINK: 434 case RTZIPTAR_TF_FIFO: 435 break; 436 default: 437 return VERR_TAR_UNKNOWN_TYPE_FLAG; 438 } 439 break; 440 default: /* shut up gcc */ 441 AssertFailedReturn(VERR_INTERNAL_ERROR_3); 442 } 443 444 return VINF_SUCCESS; 445 } 446 447 448 /** 449 * Parses and validates the first TAR header of a archive/file/dir/whatever. 450 * 451 * @returns IPRT status code. 452 * @param pThis The TAR reader stat. 453 * @param pTar The TAR header that has been read. 454 * @param fFirst Set if this is the first header, otherwise 455 * clear. 456 */ 457 static int rtZipTarReaderParseNextHeader(PRTZIPTARREADER pThis, PCRTZIPTARHDR pHdr, bool fFirst) 458 { 459 int rc; 460 461 /* 462 * Basic header validation and detection first. 463 */ 464 RTZIPTARTYPE enmType; 465 rc = rtZipTarHdrValidate(pHdr, &enmType); 466 if (RT_FAILURE_NP(rc)) 467 { 468 if (rc == VERR_TAR_ZERO_HEADER) 469 { 470 pThis->cZeroHdrs = 1; 471 pThis->enmState = RTZIPTARREADERSTATE_ZERO; 472 return VINF_SUCCESS; 473 } 474 return rc; 475 } 476 if (fFirst) 477 { 478 pThis->enmType = enmType; 479 if (pThis->enmPrevType == RTZIPTARTYPE_INVALID) 480 pThis->enmPrevType = enmType; 481 } 482 483 /* 484 * Handle the header by type. 485 */ 486 switch (pHdr->Common.typeflag) 487 { 488 case RTZIPTAR_TF_OLDNORMAL: 489 case RTZIPTAR_TF_NORMAL: 490 case RTZIPTAR_TF_CONTIG: 491 case RTZIPTAR_TF_LINK: 492 case RTZIPTAR_TF_SYMLINK: 493 case RTZIPTAR_TF_CHR: 494 case RTZIPTAR_TF_BLK: 495 case RTZIPTAR_TF_FIFO: 496 case RTZIPTAR_TF_DIR: 497 /* 498 * Extract the name first. 499 */ 500 if (!pHdr->Common.name[0]) 501 return VERR_TAR_EMPTY_NAME; 502 if (pThis->enmType == RTZIPTARTYPE_POSIX) 503 { 504 Assert(pThis->offGnuLongCur == 0); Assert(pThis->szName[0] == '\0'); 505 pThis->szName[0] = '\0'; 506 if (pHdr->Posix.prefix[0]) 294 cbBuf = _32K; 295 pbBuf = pbFree = (uint8_t *)RTMemTmpAlloc(cbBuf); 296 if (!pbBuf) 507 297 { 508 rc = RTStrCopyEx(pThis->szName, sizeof(pThis->szName), pHdr->Posix.prefix, sizeof(pHdr->Posix.prefix)); 509 AssertRC(rc); /* shall not fail */ 510 rc = RTStrCat(pThis->szName, sizeof(pThis->szName), "/"); 511 AssertRC(rc); /* ditto */ 512 } 513 rc = RTStrCatEx(pThis->szName, sizeof(pThis->szName), pHdr->Common.name, sizeof(pHdr->Common.name)); 514 AssertRCReturn(rc, rc); 515 } 516 else if (pThis->enmType == RTZIPTARTYPE_GNU) 517 { 518 if (!pThis->szName[0]) 519 { 520 rc = RTStrCopyEx(pThis->szName, sizeof(pThis->szName), pHdr->Common.name, sizeof(pHdr->Common.name)); 521 AssertRCReturn(rc, rc); 298 cbBuf = sizeof(pThis->aHdrs); 299 pbBuf = (uint8_t *)&pThis->aHdrs[0]; 522 300 } 523 301 } 524 else 302 303 /* 304 * Copy the bytes. Padding the last buffer to a multiple of 512. 305 */ 306 uint64_t cbLeft = pObjInfo->cbObject; 307 while (cbLeft > 0 && RT_SUCCESS(rc)) 525 308 { 526 /* Old TAR */ 527 Assert(pThis->offGnuLongCur == 0); Assert(pThis->szName[0] == '\0'); 528 rc = RTStrCopyEx(pThis->szName, sizeof(pThis->szName), pHdr->Common.name, sizeof(pHdr->Common.name)); 529 AssertRCReturn(rc, rc); 309 size_t cbRead = cbLeft > cbBuf ? cbBuf : (size_t)cbBuf; 310 rc = RTVfsIoStrmRead(hVfsIos, pbBuf, cbRead, true /*fBlocking*/, NULL); 311 if (RT_FAILURE(rc)) 312 break; 313 314 size_t cbToWrite = cbRead; 315 if (cbRead & (sizeof(RTZIPTARHDR) - 1)) 316 { 317 size_t cbToZero = sizeof(RTZIPTARHDR) - (cbRead & (sizeof(RTZIPTARHDR) - 1)); 318 memset(&pbBuf[cbRead], 0, cbToZero); 319 cbToWrite += cbToZero; 320 } 321 322 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbToWrite, true /*fBlocking*/, NULL); 323 if (RT_FAILURE(rc)) 324 break; 325 pThis->cbWritten += cbToWrite; 326 cbLeft -= cbRead; 530 327 } 531 328 329 RTMemTmpFree(pbFree); 330 if (RT_SUCCESS(rc)) 331 return VINF_SUCCESS; 332 } 333 pThis->rcFatal = rc; 334 } 335 return rc; 336 } 337 338 339 /** 340 * Adds a symbolic link to the stream. 341 * 342 * @returns IPRT status code. 343 * @param pThis The TAR writer instance. 344 * @param pszPath The path to the object. 345 * @param hVfsSymlink The symbolic link object to add. 346 * @param pObjInfo The object information. 347 * @param pszOwnerNm The owner name. 348 * @param pszGroupNm The group name. 349 */ 350 static int rtZipTarFssWriter_AddSymlink(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSSYMLINK hVfsSymlink, 351 PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm) 352 { 353 /* 354 * Read the symlink target first and check that it's not too long. 355 * Flip DOS slashes. 356 */ 357 char szTarget[RTPATH_MAX]; 358 int rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget)); 359 if (RT_SUCCESS(rc)) 360 { 361 #if RTPATH_STYLE == RTPATH_STR_F_STYLE_UNIX 362 char *pszDosSlash = strchr(szTarget, '\\'); 363 while (pszDosSlash) 364 { 365 *pszDosSlash = '/'; 366 pszDosSlash = strchr(pszDosSlash + 1, '\\'); 367 } 368 #endif 369 size_t cchTarget = strlen(szTarget); 370 if (cchTarget < sizeof(pThis->aHdrs[0].Common.linkname)) 371 { 532 372 /* 533 * Extract the link target.373 * Create a header, add the link target and push it out. 534 374 */ 535 if ( pHdr->Common.typeflag == RTZIPTAR_TF_LINK536 || pHdr->Common.typeflag == RTZIPTAR_TF_SYMLINK)375 rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX); 376 if (RT_SUCCESS(rc)) 537 377 { 538 if ( pThis->enmType == RTZIPTARTYPE_POSIX 539 || pThis->enmType == RTZIPTARTYPE_ANCIENT 540 || (pThis->enmType == RTZIPTARTYPE_GNU && pThis->szTarget[0] == '\0') 541 ) 378 memcpy(pThis->aHdrs[0].Common.linkname, szTarget, cchTarget + 1); 379 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]); 380 if (RT_SUCCESS(rc)) 542 381 { 543 Assert(pThis->szTarget[0] == '\0'); 544 rc = RTStrCopyEx(pThis->szTarget, sizeof(pThis->szTarget), 545 pHdr->Common.linkname, sizeof(pHdr->Common.linkname)); 546 AssertRCReturn(rc, rc); 382 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), 383 true /*fBlocking*/, NULL); 384 if (RT_SUCCESS(rc)) 385 { 386 pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]); 387 return VINF_SUCCESS; 388 } 389 pThis->rcFatal = rc; 547 390 } 548 391 } 549 else 550 pThis->szTarget[0] = '\0'; 551 552 pThis->Hdr = *pHdr; 553 break; 554 555 case RTZIPTAR_TF_X_HDR: 556 case RTZIPTAR_TF_X_GLOBAL: 557 /** @todo implement PAX */ 558 return VERR_TAR_UNSUPPORTED_PAX_TYPE; 559 560 case RTZIPTAR_TF_SOLARIS_XHDR: 561 /** @todo implement solaris / pax attribute lists. */ 562 return VERR_TAR_UNSUPPORTED_SOLARIS_HDR_TYPE; 563 564 565 /* 566 * A GNU long name or long link is a dummy record followed by one or 567 * more 512 byte string blocks holding the long name/link. The name 568 * lenght is encoded in the size field, null terminator included. If 569 * it is a symlink or hard link the long name may be followed by a 570 * long link sequence. 571 */ 572 case RTZIPTAR_TF_GNU_LONGNAME: 573 case RTZIPTAR_TF_GNU_LONGLINK: 574 { 575 if (strcmp(pHdr->Gnu.name, "././@LongLink")) 576 return VERR_TAR_MALFORMED_GNU_LONGXXXX; 577 578 int64_t cb64; 579 rc = rtZipTarHdrFieldToNum(pHdr->Gnu.size, sizeof(pHdr->Gnu.size), false /*fOctalOnly*/, &cb64); 580 if (RT_FAILURE(rc) || cb64 < 0 || cb64 > _1M) 581 return VERR_TAR_MALFORMED_GNU_LONGXXXX; 582 uint32_t cb = (uint32_t)cb64; 583 if (cb >= sizeof(pThis->szName)) 584 return VERR_TAR_NAME_TOO_LONG; 585 586 pThis->cbGnuLongExpect = cb; 587 pThis->offGnuLongCur = 0; 588 pThis->enmState = pHdr->Common.typeflag == RTZIPTAR_TF_GNU_LONGNAME 589 ? RTZIPTARREADERSTATE_GNU_LONGNAME 590 : RTZIPTARREADERSTATE_GNU_LONGLINK; 591 break; 592 } 593 594 case RTZIPTAR_TF_GNU_DUMPDIR: 595 case RTZIPTAR_TF_GNU_MULTIVOL: 596 case RTZIPTAR_TF_GNU_SPARSE: 597 case RTZIPTAR_TF_GNU_VOLDHR: 598 /** @todo Implement or skip GNU headers */ 599 return VERR_TAR_UNSUPPORTED_GNU_HDR_TYPE; 600 601 default: 602 return VERR_TAR_UNKNOWN_TYPE_FLAG; 603 } 604 605 return VINF_SUCCESS; 606 } 607 608 609 /** 610 * Parses and validates a TAR header. 392 } 393 else 394 { 395 /** @todo implement gnu and pax long name extensions. */ 396 rc = VERR_TAR_NAME_TOO_LONG; 397 } 398 } 399 return rc; 400 } 401 402 403 /** 404 * Adds a simple object to the stream. 405 * 406 * Simple objects only contains metadata, no actual data bits. Directories, 407 * devices, fifos, sockets and such. 611 408 * 612 409 * @returns IPRT status code. 613 * @param pThis The TAR reader stat. 614 * @param pTar The TAR header that has been read. 615 */ 616 static int rtZipTarReaderParseHeader(PRTZIPTARREADER pThis, PCRTZIPTARHDR pHdr) 617 { 618 switch (pThis->enmState) 619 { 620 /* 621 * The first record for a file/directory/whatever. 622 */ 623 case RTZIPTARREADERSTATE_FIRST: 624 pThis->Hdr.Common.typeflag = 0x7f; 625 pThis->enmPrevType = pThis->enmType; 626 pThis->enmType = RTZIPTARTYPE_INVALID; 627 pThis->offGnuLongCur = 0; 628 pThis->cbGnuLongExpect = 0; 629 pThis->szName[0] = '\0'; 630 pThis->szTarget[0] = '\0'; 631 return rtZipTarReaderParseNextHeader(pThis, pHdr, true /*fFirst*/); 632 633 /* 634 * There should only be so many zero headers at the end of the file as 635 * it is a function of the block size used when writing. Don't go on 636 * reading them forever in case someone points us to /dev/zero. 637 */ 638 case RTZIPTARREADERSTATE_ZERO: 639 if (!ASMMemIsZero(pHdr, sizeof(*pHdr))) 640 return VERR_TAR_ZERO_HEADER; 641 pThis->cZeroHdrs++; 642 if (pThis->cZeroHdrs <= _64K / 512 + 2) 643 return VINF_SUCCESS; 644 return VERR_TAR_ZERO_HEADER; 645 646 case RTZIPTARREADERSTATE_GNU_LONGNAME: 647 case RTZIPTARREADERSTATE_GNU_LONGLINK: 648 { 649 size_t cbIncoming = RTStrNLen((const char *)pHdr->ab, sizeof(*pHdr)); 650 if (cbIncoming < sizeof(*pHdr)) 651 cbIncoming += 1; 652 653 if (cbIncoming + pThis->offGnuLongCur > pThis->cbGnuLongExpect) 654 return VERR_TAR_MALFORMED_GNU_LONGXXXX; 655 if ( cbIncoming < sizeof(*pHdr) 656 && cbIncoming + pThis->offGnuLongCur != pThis->cbGnuLongExpect) 657 return VERR_TAR_MALFORMED_GNU_LONGXXXX; 658 659 char *pszDst = pThis->enmState == RTZIPTARREADERSTATE_GNU_LONGNAME ? pThis->szName : pThis->szTarget; 660 pszDst += pThis->offGnuLongCur; 661 memcpy(pszDst, pHdr->ab, cbIncoming); 662 663 pThis->offGnuLongCur += (uint32_t)cbIncoming; 664 if (pThis->offGnuLongCur == pThis->cbGnuLongExpect) 665 pThis->enmState = RTZIPTARREADERSTATE_GNU_NEXT; 410 * @param pThis The TAR writer instance. 411 * @param pszPath The path to the object. 412 * @param pObjInfo The object information. 413 * @param pszOwnerNm The owner name. 414 * @param pszGroupNm The group name. 415 */ 416 static int rtZipTarFssWriter_AddSimpleObject(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, PCRTFSOBJINFO pObjInfo, 417 const char *pszOwnerNm, const char *pszGroupNm) 418 { 419 int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX); 420 if (RT_SUCCESS(rc)) 421 { 422 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL); 423 if (RT_SUCCESS(rc)) 424 { 425 pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]); 666 426 return VINF_SUCCESS; 667 427 } 668 669 case RTZIPTARREADERSTATE_GNU_NEXT: 670 pThis->enmState = RTZIPTARREADERSTATE_FIRST; 671 return rtZipTarReaderParseNextHeader(pThis, pHdr, false /*fFirst*/); 672 673 default: 674 return VERR_INTERNAL_ERROR_5; 675 } 676 } 677 678 679 /** 680 * Translate a TAR header to an IPRT object info structure with additional UNIX 681 * attributes. 682 * 683 * This completes the validation done by rtZipTarHdrValidate. 684 * 685 * @returns VINF_SUCCESS if valid, appropriate VERR_TAR_XXX if not. 686 * @param pThis The TAR reader instance. 687 * @param pObjInfo The object info structure (output). 688 */ 689 static int rtZipTarReaderGetFsObjInfo(PRTZIPTARREADER pThis, PRTFSOBJINFO pObjInfo) 690 { 691 /* 692 * Zap the whole structure, this takes care of unused space in the union. 693 */ 694 RT_ZERO(*pObjInfo); 695 696 /* 697 * Convert the TAR field in RTFSOBJINFO order. 698 */ 699 int rc; 700 int64_t i64Tmp; 701 #define GET_TAR_NUMERIC_FIELD_RET(a_Var, a_Field) \ 702 do { \ 703 rc = rtZipTarHdrFieldToNum(a_Field, sizeof(a_Field), false /*fOctalOnly*/, &i64Tmp); \ 704 if (RT_FAILURE(rc)) \ 705 return rc; \ 706 (a_Var) = i64Tmp; \ 707 if ((a_Var) != i64Tmp) \ 708 return VERR_TAR_NUM_VALUE_TOO_LARGE; \ 709 } while (0) 710 711 GET_TAR_NUMERIC_FIELD_RET(pObjInfo->cbObject, pThis->Hdr.Common.size); 712 pObjInfo->cbAllocated = RT_ALIGN_64(pObjInfo->cbObject, 512); 713 int64_t c64SecModTime; 714 GET_TAR_NUMERIC_FIELD_RET(c64SecModTime, pThis->Hdr.Common.mtime); 715 RTTimeSpecSetSeconds(&pObjInfo->ChangeTime, c64SecModTime); 716 RTTimeSpecSetSeconds(&pObjInfo->ModificationTime, c64SecModTime); 717 RTTimeSpecSetSeconds(&pObjInfo->AccessTime, c64SecModTime); 718 RTTimeSpecSetSeconds(&pObjInfo->BirthTime, c64SecModTime); 719 if (c64SecModTime != RTTimeSpecGetSeconds(&pObjInfo->ModificationTime)) 720 return VERR_TAR_NUM_VALUE_TOO_LARGE; 721 GET_TAR_NUMERIC_FIELD_RET(pObjInfo->Attr.fMode, pThis->Hdr.Common.mode); 722 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX; 723 GET_TAR_NUMERIC_FIELD_RET(pObjInfo->Attr.u.Unix.uid, pThis->Hdr.Common.uid); 724 GET_TAR_NUMERIC_FIELD_RET(pObjInfo->Attr.u.Unix.gid, pThis->Hdr.Common.gid); 725 pObjInfo->Attr.u.Unix.cHardlinks = 1; 726 pObjInfo->Attr.u.Unix.INodeIdDevice = 0; 727 pObjInfo->Attr.u.Unix.INodeId = 0; 728 pObjInfo->Attr.u.Unix.fFlags = 0; 729 pObjInfo->Attr.u.Unix.GenerationId = 0; 730 pObjInfo->Attr.u.Unix.Device = 0; 731 switch (pThis->enmType) 732 { 733 case RTZIPTARTYPE_POSIX: 734 case RTZIPTARTYPE_GNU: 735 if ( pThis->Hdr.Common.typeflag == RTZIPTAR_TF_CHR 736 || pThis->Hdr.Common.typeflag == RTZIPTAR_TF_BLK) 737 { 738 uint32_t uMajor, uMinor; 739 GET_TAR_NUMERIC_FIELD_RET(uMajor, pThis->Hdr.Common.devmajor); 740 GET_TAR_NUMERIC_FIELD_RET(uMinor, pThis->Hdr.Common.devminor); 741 pObjInfo->Attr.u.Unix.Device = RTDEV_MAKE(uMajor, uMinor); 742 if ( uMajor != RTDEV_MAJOR(pObjInfo->Attr.u.Unix.Device) 743 || uMinor != RTDEV_MINOR(pObjInfo->Attr.u.Unix.Device)) 744 return VERR_TAR_DEV_VALUE_TOO_LARGE; 745 } 746 break; 747 748 default: 749 if ( pThis->Hdr.Common.typeflag == RTZIPTAR_TF_CHR 750 || pThis->Hdr.Common.typeflag == RTZIPTAR_TF_BLK) 751 return VERR_TAR_UNKNOWN_TYPE_FLAG; 752 } 753 754 #undef GET_TAR_NUMERIC_FIELD_RET 755 756 /* 757 * Massage the result a little bit. 758 * Also validate some more now that we've got the numbers to work with. 759 */ 760 if ( (pObjInfo->Attr.fMode & ~RTFS_UNIX_MASK) 761 && pThis->enmType == RTZIPTARTYPE_POSIX) 762 return VERR_TAR_BAD_MODE_FIELD; 763 pObjInfo->Attr.fMode &= RTFS_UNIX_MASK; 764 765 RTFMODE fModeType = 0; 766 switch (pThis->Hdr.Common.typeflag) 767 { 768 case RTZIPTAR_TF_OLDNORMAL: 769 case RTZIPTAR_TF_NORMAL: 770 case RTZIPTAR_TF_CONTIG: 771 { 772 const char *pszEnd = strchr(pThis->szName, '\0'); 773 if (pszEnd == &pThis->szName[0] || pszEnd[-1] != '/') 774 fModeType |= RTFS_TYPE_FILE; 775 else 776 fModeType |= RTFS_TYPE_DIRECTORY; 777 break; 778 } 779 780 case RTZIPTAR_TF_LINK: 781 if (pObjInfo->cbObject != 0) 782 #if 0 /* too strict */ 783 return VERR_TAR_SIZE_NOT_ZERO; 784 #else 785 pObjInfo->cbObject = pObjInfo->cbAllocated = 0; 786 #endif 787 fModeType |= RTFS_TYPE_FILE; /* no better idea for now */ 788 break; 789 790 case RTZIPTAR_TF_SYMLINK: 791 fModeType |= RTFS_TYPE_SYMLINK; 792 break; 793 794 case RTZIPTAR_TF_CHR: 795 fModeType |= RTFS_TYPE_DEV_CHAR; 796 break; 797 798 case RTZIPTAR_TF_BLK: 799 fModeType |= RTFS_TYPE_DEV_BLOCK; 800 break; 801 802 case RTZIPTAR_TF_DIR: 803 fModeType |= RTFS_TYPE_DIRECTORY; 804 break; 805 806 case RTZIPTAR_TF_FIFO: 807 fModeType |= RTFS_TYPE_FIFO; 808 break; 809 810 case RTZIPTAR_TF_GNU_LONGLINK: 811 case RTZIPTAR_TF_GNU_LONGNAME: 812 /* ASSUMES RTFS_TYPE_XXX uses the same values as GNU stored in the mode field. */ 813 fModeType = pObjInfo->Attr.fMode & RTFS_TYPE_MASK; 814 switch (fModeType) 815 { 816 case RTFS_TYPE_FILE: 817 case RTFS_TYPE_DIRECTORY: 818 case RTFS_TYPE_SYMLINK: 819 case RTFS_TYPE_DEV_BLOCK: 820 case RTFS_TYPE_DEV_CHAR: 821 case RTFS_TYPE_FIFO: 822 break; 823 824 default: 825 case 0: 826 return VERR_TAR_UNKNOWN_TYPE_FLAG; /** @todo new status code */ 827 } 828 829 default: 830 return VERR_TAR_UNKNOWN_TYPE_FLAG; /* Should've been caught in validate. */ 831 } 832 if ( (pObjInfo->Attr.fMode & RTFS_TYPE_MASK) 833 && (pObjInfo->Attr.fMode & RTFS_TYPE_MASK) != fModeType) 834 return VERR_TAR_MODE_WITH_TYPE; 835 pObjInfo->Attr.fMode &= ~RTFS_TYPE_MASK; 836 pObjInfo->Attr.fMode |= fModeType; 837 838 switch (pThis->Hdr.Common.typeflag) 839 { 840 case RTZIPTAR_TF_CHR: 841 case RTZIPTAR_TF_BLK: 842 case RTZIPTAR_TF_DIR: 843 case RTZIPTAR_TF_FIFO: 844 pObjInfo->cbObject = 0; 845 pObjInfo->cbAllocated = 0; 846 break; 847 } 848 849 return VINF_SUCCESS; 850 } 851 852 853 /** 854 * Checks if the reader is expecting more headers. 855 * 856 * @returns true / false. 857 * @param pThis The TAR reader instance. 858 */ 859 static bool rtZipTarReaderExpectingMoreHeaders(PRTZIPTARREADER pThis) 860 { 861 return pThis->enmState != RTZIPTARREADERSTATE_FIRST; 862 } 863 864 865 /** 866 * Checks if we're at the end of the TAR file. 867 * 868 * @returns true / false. 869 * @param pThis The TAR reader instance. 870 */ 871 static bool rtZipTarReaderIsAtEnd(PRTZIPTARREADER pThis) 872 { 873 /* Turns out our own tar writer code doesn't get this crap right. 874 Kludge our way around it. */ 875 if (!pThis->cZeroHdrs) 876 return pThis->enmPrevType == RTZIPTARTYPE_GNU ? true /* IPRT tar.cpp */ : false; 877 878 /* Here is a kludge to try deal with archivers not putting at least two 879 zero headers at the end. Afraid it may require further relaxing 880 later on, but let's try be strict about things for now. */ 881 return pThis->cZeroHdrs >= (pThis->enmPrevType == RTZIPTARTYPE_POSIX ? 2U : 1U); 882 } 883 884 885 /** 886 * Checks if the current TAR object is a hard link or not. 887 * 888 * @returns true if it is, false if not. 889 * @param pThis The TAR reader instance. 890 */ 891 static bool rtZipTarReaderIsHardlink(PRTZIPTARREADER pThis) 892 { 893 return pThis->Hdr.Common.typeflag == RTZIPTAR_TF_LINK; 894 } 895 896 897 /** 898 * Checks if the TAR header includes a POSIX or GNU user name field. 899 * 900 * @returns true / false. 901 * @param pThis The TAR reader instance. 902 */ 903 DECLINLINE(bool) rtZipTarReaderHasUserName(PRTZIPTARREADER pThis) 904 { 905 return pThis->Hdr.Common.uname[0] != '\0' 906 && ( pThis->enmType == RTZIPTARTYPE_POSIX 907 || pThis->enmType == RTZIPTARTYPE_GNU); 908 } 909 910 911 /** 912 * Checks if the TAR header includes a POSIX or GNU group name field. 913 * 914 * @returns true / false. 915 * @param pThis The TAR reader instance. 916 */ 917 DECLINLINE(bool) rtZipTarReaderHasGroupName(PRTZIPTARREADER pThis) 918 { 919 return pThis->Hdr.Common.gname[0] != '\0' 920 && ( pThis->enmType == RTZIPTARTYPE_POSIX 921 || pThis->enmType == RTZIPTARTYPE_GNU); 922 } 923 924 925 /* 926 * 927 * T h e V F S F i l e s y s t e m S t r e a m B i t s. 928 * T h e V F S F i l e s y s t e m S t r e a m B i t s. 929 * T h e V F S F i l e s y s t e m S t r e a m B i t s. 930 * 931 */ 428 pThis->rcFatal = rc; 429 } 430 return rc; 431 } 432 932 433 933 434 /** 934 435 * @interface_method_impl{RTVFSOBJOPS,pfnClose} 935 436 */ 936 static DECLCALLBACK(int) rtZipTarFssBaseObj_Close(void *pvThis) 937 { 938 PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis; 939 940 /* Currently there is nothing we really have to do here. */ 941 pThis->offHdr = -1; 942 943 return VINF_SUCCESS; 944 } 945 946 947 /** 948 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo} 949 */ 950 static DECLCALLBACK(int) rtZipTarFssBaseObj_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr) 951 { 952 PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis; 953 954 /* 955 * Copy the desired data. 956 */ 957 switch (enmAddAttr) 958 { 959 case RTFSOBJATTRADD_NOTHING: 960 case RTFSOBJATTRADD_UNIX: 961 *pObjInfo = pThis->ObjInfo; 962 break; 963 964 case RTFSOBJATTRADD_UNIX_OWNER: 965 *pObjInfo = pThis->ObjInfo; 966 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER; 967 pObjInfo->Attr.u.UnixOwner.uid = pThis->ObjInfo.Attr.u.Unix.uid; 968 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; 969 if (rtZipTarReaderHasUserName(pThis->pTarReader)) 970 RTStrCopy(pObjInfo->Attr.u.UnixOwner.szName, sizeof(pObjInfo->Attr.u.UnixOwner.szName), 971 pThis->pTarReader->Hdr.Common.uname); 972 break; 973 974 case RTFSOBJATTRADD_UNIX_GROUP: 975 *pObjInfo = pThis->ObjInfo; 976 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP; 977 pObjInfo->Attr.u.UnixGroup.gid = pThis->ObjInfo.Attr.u.Unix.gid; 978 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0'; 979 if (rtZipTarReaderHasGroupName(pThis->pTarReader)) 980 RTStrCopy(pObjInfo->Attr.u.UnixGroup.szName, sizeof(pObjInfo->Attr.u.UnixGroup.szName), 981 pThis->pTarReader->Hdr.Common.gname); 982 break; 983 984 case RTFSOBJATTRADD_EASIZE: 985 *pObjInfo = pThis->ObjInfo; 986 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE; 987 RT_ZERO(pObjInfo->Attr.u); 988 break; 989 990 default: 991 return VERR_NOT_SUPPORTED; 992 } 993 994 return VINF_SUCCESS; 995 } 996 997 998 /** 999 * Tar filesystem base object operations. 1000 */ 1001 static const RTVFSOBJOPS g_rtZipTarFssBaseObjOps = 1002 { 1003 RTVFSOBJOPS_VERSION, 1004 RTVFSOBJTYPE_BASE, 1005 "TarFsStream::Obj", 1006 rtZipTarFssBaseObj_Close, 1007 rtZipTarFssBaseObj_QueryInfo, 1008 RTVFSOBJOPS_VERSION 1009 }; 1010 1011 1012 /** 1013 * @interface_method_impl{RTVFSOBJOPS,pfnClose} 1014 */ 1015 static DECLCALLBACK(int) rtZipTarFssIos_Close(void *pvThis) 1016 { 1017 PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis; 437 static DECLCALLBACK(int) rtZipTarFssWriter_Close(void *pvThis) 438 { 439 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis; 1018 440 1019 441 RTVfsIoStrmRelease(pThis->hVfsIos); 1020 442 pThis->hVfsIos = NIL_RTVFSIOSTREAM; 1021 443 1022 return rtZipTarFssBaseObj_Close(&pThis->BaseObj); 444 /** @todo investigate zero end records. */ 445 446 return VINF_SUCCESS; 1023 447 } 1024 448 … … 1027 451 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo} 1028 452 */ 1029 static DECLCALLBACK(int) rtZipTarFssIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr) 1030 { 1031 PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis; 1032 return rtZipTarFssBaseObj_QueryInfo(&pThis->BaseObj, pObjInfo, enmAddAttr); 1033 } 1034 1035 1036 /** 1037 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead} 1038 */ 1039 static DECLCALLBACK(int) rtZipTarFssIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead) 1040 { 1041 PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis; 1042 Assert(pSgBuf->cSegs == 1); 1043 1044 /* 1045 * Make offset into a real offset so it's possible to do random access 1046 * on TAR files that are seekable. Fend of reads beyond the end of the 1047 * stream. 1048 */ 1049 if (off < 0) 1050 off = pThis->offFile; 1051 if (off >= pThis->cbFile) 1052 return pcbRead ? VINF_EOF : VERR_EOF; 1053 1054 1055 Assert(pThis->cbFile >= pThis->offFile); 1056 uint64_t cbLeft = (uint64_t)(pThis->cbFile - pThis->offFile); 1057 size_t cbToRead = pSgBuf->paSegs[0].cbSeg; 1058 if (cbToRead > cbLeft) 1059 { 1060 if (!pcbRead) 1061 return VERR_EOF; 1062 cbToRead = (size_t)cbLeft; 1063 } 1064 1065 /* 1066 * Do the reading. 1067 */ 1068 size_t cbReadStack = 0; 1069 if (!pcbRead) 1070 pcbRead = &cbReadStack; 1071 int rc = RTVfsIoStrmReadAt(pThis->hVfsIos, pThis->offStart + off, pSgBuf->paSegs[0].pvSeg, cbToRead, fBlocking, pcbRead); 1072 pThis->offFile = off + *pcbRead; 1073 if (pThis->offFile >= pThis->cbFile) 1074 { 1075 Assert(pThis->offFile == pThis->cbFile); 1076 pThis->fEndOfStream = true; 1077 RTVfsIoStrmSkip(pThis->hVfsIos, pThis->cbPadding); 1078 } 1079 1080 return rc; 1081 } 1082 1083 1084 /** 1085 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite} 1086 */ 1087 static DECLCALLBACK(int) rtZipTarFssIos_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten) 1088 { 1089 /* Cannot write to a read-only I/O stream. */ 1090 NOREF(pvThis); NOREF(off); NOREF(pSgBuf); NOREF(fBlocking); NOREF(pcbWritten); 1091 return VERR_ACCESS_DENIED; 1092 } 1093 1094 1095 /** 1096 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush} 1097 */ 1098 static DECLCALLBACK(int) rtZipTarFssIos_Flush(void *pvThis) 1099 { 1100 /* It's a read only stream, nothing dirty to flush. */ 1101 NOREF(pvThis); 1102 return VINF_SUCCESS; 1103 } 1104 1105 1106 /** 1107 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne} 1108 */ 1109 static DECLCALLBACK(int) rtZipTarFssIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr, 1110 uint32_t *pfRetEvents) 1111 { 1112 PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis; 1113 1114 /* When we've reached the end, read will be set to indicate it. */ 1115 if ( (fEvents & RTPOLL_EVT_READ) 1116 && pThis->fEndOfStream) 1117 { 1118 int rc = RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, 0, fIntr, pfRetEvents); 1119 if (RT_SUCCESS(rc)) 1120 *pfRetEvents |= RTPOLL_EVT_READ; 1121 else 1122 *pfRetEvents = RTPOLL_EVT_READ; 1123 return VINF_SUCCESS; 1124 } 1125 1126 return RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents); 1127 } 1128 1129 1130 /** 1131 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell} 1132 */ 1133 static DECLCALLBACK(int) rtZipTarFssIos_Tell(void *pvThis, PRTFOFF poffActual) 1134 { 1135 PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis; 1136 *poffActual = pThis->offFile; 1137 return VINF_SUCCESS; 1138 } 1139 1140 1141 /** 1142 * Tar I/O stream operations. 1143 */ 1144 static const RTVFSIOSTREAMOPS g_rtZipTarFssIosOps = 1145 { 1146 { /* Obj */ 1147 RTVFSOBJOPS_VERSION, 1148 RTVFSOBJTYPE_IO_STREAM, 1149 "TarFsStream::IoStream", 1150 rtZipTarFssIos_Close, 1151 rtZipTarFssIos_QueryInfo, 1152 RTVFSOBJOPS_VERSION 1153 }, 1154 RTVFSIOSTREAMOPS_VERSION, 1155 RTVFSIOSTREAMOPS_FEAT_NO_SG, 1156 rtZipTarFssIos_Read, 1157 rtZipTarFssIos_Write, 1158 rtZipTarFssIos_Flush, 1159 rtZipTarFssIos_PollOne, 1160 rtZipTarFssIos_Tell, 1161 NULL /*Skip*/, 1162 NULL /*ZeroFill*/, 1163 RTVFSIOSTREAMOPS_VERSION 1164 }; 1165 1166 1167 /** 1168 * @interface_method_impl{RTVFSOBJOPS,pfnClose} 1169 */ 1170 static DECLCALLBACK(int) rtZipTarFssSym_Close(void *pvThis) 1171 { 1172 PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis; 1173 return rtZipTarFssBaseObj_Close(pThis); 1174 } 1175 1176 1177 /** 1178 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo} 1179 */ 1180 static DECLCALLBACK(int) rtZipTarFssSym_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr) 1181 { 1182 PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis; 1183 return rtZipTarFssBaseObj_QueryInfo(pThis, pObjInfo, enmAddAttr); 1184 } 1185 1186 /** 1187 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode} 1188 */ 1189 static DECLCALLBACK(int) rtZipTarFssSym_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask) 1190 { 1191 NOREF(pvThis); NOREF(fMode); NOREF(fMask); 1192 return VERR_ACCESS_DENIED; 1193 } 1194 1195 1196 /** 1197 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes} 1198 */ 1199 static DECLCALLBACK(int) rtZipTarFssSym_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, 1200 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime) 1201 { 1202 NOREF(pvThis); NOREF(pAccessTime); NOREF(pModificationTime); NOREF(pChangeTime); NOREF(pBirthTime); 1203 return VERR_ACCESS_DENIED; 1204 } 1205 1206 1207 /** 1208 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner} 1209 */ 1210 static DECLCALLBACK(int) rtZipTarFssSym_SetOwner(void *pvThis, RTUID uid, RTGID gid) 1211 { 1212 NOREF(pvThis); NOREF(uid); NOREF(gid); 1213 return VERR_ACCESS_DENIED; 1214 } 1215 1216 1217 /** 1218 * @interface_method_impl{RTVFSSYMLINKOPS,pfnRead} 1219 */ 1220 static DECLCALLBACK(int) rtZipTarFssSym_Read(void *pvThis, char *pszTarget, size_t cbTarget) 1221 { 1222 PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis; 1223 return RTStrCopy(pszTarget, cbTarget, pThis->pTarReader->szTarget); 1224 } 1225 1226 1227 /** 1228 * Tar symbolic (and hardlink) operations. 1229 */ 1230 static const RTVFSSYMLINKOPS g_rtZipTarFssSymOps = 1231 { 1232 { /* Obj */ 1233 RTVFSOBJOPS_VERSION, 1234 RTVFSOBJTYPE_SYMLINK, 1235 "TarFsStream::Symlink", 1236 rtZipTarFssSym_Close, 1237 rtZipTarFssSym_QueryInfo, 1238 RTVFSOBJOPS_VERSION 1239 }, 1240 RTVFSSYMLINKOPS_VERSION, 1241 0, 1242 { /* ObjSet */ 1243 RTVFSOBJSETOPS_VERSION, 1244 RT_OFFSETOF(RTVFSSYMLINKOPS, Obj) - RT_OFFSETOF(RTVFSSYMLINKOPS, ObjSet), 1245 rtZipTarFssSym_SetMode, 1246 rtZipTarFssSym_SetTimes, 1247 rtZipTarFssSym_SetOwner, 1248 RTVFSOBJSETOPS_VERSION 1249 }, 1250 rtZipTarFssSym_Read, 1251 RTVFSSYMLINKOPS_VERSION 1252 }; 1253 1254 1255 /** 1256 * @interface_method_impl{RTVFSOBJOPS,pfnClose} 1257 */ 1258 static DECLCALLBACK(int) rtZipTarFss_Close(void *pvThis) 1259 { 1260 PRTZIPTARFSSTREAM pThis = (PRTZIPTARFSSTREAM)pvThis; 1261 1262 RTVfsObjRelease(pThis->hVfsCurObj); 1263 pThis->hVfsCurObj = NIL_RTVFSOBJ; 1264 pThis->pCurIosData = NULL; 1265 1266 RTVfsIoStrmRelease(pThis->hVfsIos); 1267 pThis->hVfsIos = NIL_RTVFSIOSTREAM; 1268 1269 return VINF_SUCCESS; 1270 } 1271 1272 1273 /** 1274 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo} 1275 */ 1276 static DECLCALLBACK(int) rtZipTarFss_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr) 1277 { 1278 PRTZIPTARFSSTREAM pThis = (PRTZIPTARFSSTREAM)pvThis; 453 static DECLCALLBACK(int) rtZipTarFssWriter_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr) 454 { 455 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis; 1279 456 /* Take the lazy approach here, with the sideffect of providing some info 1280 457 that is actually kind of useful. */ … … 1284 461 1285 462 /** 1286 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnNext} 1287 */ 1288 static DECLCALLBACK(int) rtZipTarFss_Next(void *pvThis, char **ppszName, RTVFSOBJTYPE *penmType, PRTVFSOBJ phVfsObj) 1289 { 1290 PRTZIPTARFSSTREAM pThis = (PRTZIPTARFSSTREAM)pvThis; 1291 1292 /* 1293 * Dispense with the current object. 1294 */ 1295 if (pThis->hVfsCurObj != NIL_RTVFSOBJ) 1296 { 1297 if (pThis->pCurIosData) 1298 { 1299 pThis->pCurIosData->fEndOfStream = true; 1300 pThis->pCurIosData->offFile = pThis->pCurIosData->cbFile; 1301 pThis->pCurIosData = NULL; 1302 } 1303 1304 RTVfsObjRelease(pThis->hVfsCurObj); 1305 pThis->hVfsCurObj = NIL_RTVFSOBJ; 1306 } 1307 1308 /* 1309 * Check if we've already reached the end in some way. 1310 */ 1311 if (pThis->fEndOfStream) 1312 return VERR_EOF; 1313 if (pThis->rcFatal != VINF_SUCCESS) 1314 return pThis->rcFatal; 1315 1316 /* 1317 * Make sure the input stream is in the right place. 1318 */ 1319 RTFOFF offHdr = RTVfsIoStrmTell(pThis->hVfsIos); 1320 while ( offHdr >= 0 1321 && offHdr < pThis->offNextHdr) 1322 { 1323 int rc = RTVfsIoStrmSkip(pThis->hVfsIos, pThis->offNextHdr - offHdr); 1324 if (RT_FAILURE(rc)) 1325 { 1326 /** @todo Ignore if we're at the end of the stream? */ 1327 return pThis->rcFatal = rc; 1328 } 1329 1330 offHdr = RTVfsIoStrmTell(pThis->hVfsIos); 1331 } 1332 1333 if (offHdr < 0) 1334 return pThis->rcFatal = (int)offHdr; 1335 if (offHdr > pThis->offNextHdr) 1336 return pThis->rcFatal = VERR_INTERNAL_ERROR_3; 1337 1338 /* 1339 * Consume TAR headers. 1340 */ 1341 size_t cbHdrs = 0; 1342 int rc; 1343 do 1344 { 1345 /* 1346 * Read the next header. 1347 */ 1348 RTZIPTARHDR Hdr; 1349 size_t cbRead; 1350 rc = RTVfsIoStrmRead(pThis->hVfsIos, &Hdr, sizeof(Hdr), true /*fBlocking*/, &cbRead); 1351 if (RT_FAILURE(rc)) 1352 return pThis->rcFatal = rc; 1353 if (rc == VINF_EOF && cbRead == 0) 1354 { 1355 pThis->fEndOfStream = true; 1356 return rtZipTarReaderIsAtEnd(&pThis->TarReader) ? VERR_EOF : VERR_TAR_UNEXPECTED_EOS; 1357 } 1358 if (cbRead != sizeof(Hdr)) 1359 return pThis->rcFatal = VERR_TAR_UNEXPECTED_EOS; 1360 1361 cbHdrs += sizeof(Hdr); 1362 1363 /* 1364 * Parse the it. 1365 */ 1366 rc = rtZipTarReaderParseHeader(&pThis->TarReader, &Hdr); 1367 if (RT_FAILURE(rc)) 1368 return pThis->rcFatal = rc; 1369 } while (rtZipTarReaderExpectingMoreHeaders(&pThis->TarReader)); 1370 1371 pThis->offNextHdr = offHdr + cbHdrs; 1372 1373 /* 1374 * Fill an object info structure from the current TAR state. 1375 */ 1376 RTFSOBJINFO Info; 1377 rc = rtZipTarReaderGetFsObjInfo(&pThis->TarReader, &Info); 1378 if (RT_FAILURE(rc)) 1379 return pThis->rcFatal = rc; 1380 1381 /* 1382 * Create an object of the appropriate type. 1383 */ 1384 RTVFSOBJTYPE enmType; 1385 RTVFSOBJ hVfsObj; 1386 RTFMODE fType = Info.Attr.fMode & RTFS_TYPE_MASK; 1387 if (rtZipTarReaderIsHardlink(&pThis->TarReader)) 1388 fType = RTFS_TYPE_SYMLINK; 1389 switch (fType) 1390 { 1391 /* 1392 * Files are represented by a VFS I/O stream. 1393 */ 1394 case RTFS_TYPE_FILE: 1395 { 1396 RTVFSIOSTREAM hVfsIos; 1397 PRTZIPTARIOSTREAM pIosData; 1398 rc = RTVfsNewIoStream(&g_rtZipTarFssIosOps, 1399 sizeof(*pIosData), 1400 RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, 1401 NIL_RTVFS, 1402 NIL_RTVFSLOCK, 1403 &hVfsIos, 1404 (void **)&pIosData); 1405 if (RT_FAILURE(rc)) 1406 return pThis->rcFatal = rc; 1407 1408 pIosData->BaseObj.offHdr = offHdr; 1409 pIosData->BaseObj.pTarReader= &pThis->TarReader; 1410 pIosData->BaseObj.ObjInfo = Info; 1411 pIosData->cbFile = Info.cbObject; 1412 pIosData->offFile = 0; 1413 pIosData->offStart = RTVfsIoStrmTell(pThis->hVfsIos); 1414 pIosData->cbPadding = (uint32_t)(Info.cbAllocated - Info.cbObject); 1415 pIosData->fEndOfStream = false; 1416 pIosData->hVfsIos = pThis->hVfsIos; 1417 RTVfsIoStrmRetain(pThis->hVfsIos); 1418 1419 pThis->pCurIosData = pIosData; 1420 pThis->offNextHdr += pIosData->cbFile + pIosData->cbPadding; 1421 1422 enmType = RTVFSOBJTYPE_IO_STREAM; 1423 hVfsObj = RTVfsObjFromIoStream(hVfsIos); 1424 RTVfsIoStrmRelease(hVfsIos); 1425 break; 1426 } 1427 1428 /* 1429 * We represent hard links using a symbolic link object. This fits 1430 * best with the way TAR stores it and there is currently no better 1431 * fitting VFS type alternative. 1432 */ 1433 case RTFS_TYPE_SYMLINK: 1434 { 1435 RTVFSSYMLINK hVfsSym; 1436 PRTZIPTARBASEOBJ pBaseObjData; 1437 rc = RTVfsNewSymlink(&g_rtZipTarFssSymOps, 1438 sizeof(*pBaseObjData), 1439 NIL_RTVFS, 1440 NIL_RTVFSLOCK, 1441 &hVfsSym, 1442 (void **)&pBaseObjData); 1443 if (RT_FAILURE(rc)) 1444 return pThis->rcFatal = rc; 1445 1446 pBaseObjData->offHdr = offHdr; 1447 pBaseObjData->pTarReader= &pThis->TarReader; 1448 pBaseObjData->ObjInfo = Info; 1449 1450 enmType = RTVFSOBJTYPE_SYMLINK; 1451 hVfsObj = RTVfsObjFromSymlink(hVfsSym); 1452 RTVfsSymlinkRelease(hVfsSym); 1453 break; 1454 } 1455 1456 /* 1457 * All other objects are repesented using a VFS base object since they 1458 * carry no data streams (unless some TAR extension implements extended 1459 * attributes / alternative streams). 1460 */ 1461 case RTFS_TYPE_DEV_BLOCK: 1462 case RTFS_TYPE_DEV_CHAR: 1463 case RTFS_TYPE_DIRECTORY: 1464 case RTFS_TYPE_FIFO: 1465 { 1466 PRTZIPTARBASEOBJ pBaseObjData; 1467 rc = RTVfsNewBaseObj(&g_rtZipTarFssBaseObjOps, 1468 sizeof(*pBaseObjData), 1469 NIL_RTVFS, 1470 NIL_RTVFSLOCK, 1471 &hVfsObj, 1472 (void **)&pBaseObjData); 1473 if (RT_FAILURE(rc)) 1474 return pThis->rcFatal = rc; 1475 1476 pBaseObjData->offHdr = offHdr; 1477 pBaseObjData->pTarReader= &pThis->TarReader; 1478 pBaseObjData->ObjInfo = Info; 1479 1480 enmType = RTVFSOBJTYPE_BASE; 1481 break; 1482 } 1483 1484 default: 1485 AssertFailed(); 1486 return pThis->rcFatal = VERR_INTERNAL_ERROR_5; 1487 } 1488 pThis->hVfsCurObj = hVfsObj; 1489 1490 /* 1491 * Set the return data and we're done. 1492 */ 1493 if (ppszName) 1494 { 1495 rc = RTStrDupEx(ppszName, pThis->TarReader.szName); 1496 if (RT_FAILURE(rc)) 1497 return rc; 1498 } 1499 1500 if (phVfsObj) 1501 { 1502 RTVfsObjRetain(hVfsObj); 1503 *phVfsObj = hVfsObj; 1504 } 1505 1506 if (penmType) 1507 *penmType = enmType; 1508 1509 return VINF_SUCCESS; 463 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnAdd} 464 */ 465 static DECLCALLBACK(int) rtZipTarFssWriter_Add(void *pvThis, const char *pszPath, RTVFSOBJ hVfsObj, uint32_t fFlags) 466 { 467 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis; 468 RT_NOREF(pThis, pszPath, hVfsObj, fFlags); 469 470 /* 471 * Refuse to do anything if we've encountered a fatal error. 472 * Assert this because the caller should know better than calling us again. 473 */ 474 AssertRCReturn(pThis->rcFatal, pThis->rcFatal); 475 476 /* 477 * Query information about the object. 478 */ 479 RTFSOBJINFO ObjInfo; 480 int rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_UNIX); 481 AssertRCReturn(rc, rc); 482 483 RTFSOBJINFO ObjOwnerName; 484 rc = RTVfsObjQueryInfo(hVfsObj, &ObjOwnerName, RTFSOBJATTRADD_UNIX_OWNER); 485 if (RT_FAILURE(rc) || ObjOwnerName.Attr.u.UnixOwner.szName[0] == '\0') 486 strcpy(ObjOwnerName.Attr.u.UnixOwner.szName, "someone"); 487 488 RTFSOBJINFO ObjGrpName; 489 rc = RTVfsObjQueryInfo(hVfsObj, &ObjGrpName, RTFSOBJATTRADD_UNIX_GROUP); 490 if (RT_FAILURE(rc) || ObjGrpName.Attr.u.UnixGroup.szName[0] == '\0') 491 strcpy(ObjGrpName.Attr.u.UnixGroup.szName, "somegroup"); 492 493 /* 494 * Do type specific handling. 495 */ 496 if (RTFS_IS_FILE(ObjInfo.Attr.fMode)) 497 { 498 RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj); 499 AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_WRONG_TYPE); 500 rc = rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, fFlags, &ObjInfo, 501 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName); 502 RTVfsIoStrmRelease(hVfsIos); 503 } 504 else if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode)) 505 { 506 RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj); 507 AssertReturn(hVfsSymlink != NIL_RTVFSSYMLINK, VERR_WRONG_TYPE); 508 rc = rtZipTarFssWriter_AddSymlink(pThis, pszPath, hVfsSymlink, &ObjInfo, 509 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName); 510 RTVfsSymlinkRelease(hVfsSymlink); 511 } 512 else 513 rc = rtZipTarFssWriter_AddSimpleObject(pThis, pszPath, &ObjInfo, 514 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName); 515 516 return rc; 1510 517 } 1511 518 … … 1520 527 RTVFSOBJOPS_VERSION, 1521 528 RTVFSOBJTYPE_FS_STREAM, 1522 "TarFsStream ",1523 rtZipTarFss _Close,1524 rtZipTarFss _QueryInfo,529 "TarFsStreamWriter", 530 rtZipTarFssWriter_Close, 531 rtZipTarFssWriter_QueryInfo, 1525 532 RTVFSOBJOPS_VERSION 1526 533 }, 1527 534 RTVFSFSSTREAMOPS_VERSION, 1528 535 0, 1529 rtZipTarFss_Next, 536 NULL, 537 rtZipTarFssWriter_Add, 1530 538 RTVFSFSSTREAMOPS_VERSION 1531 539 }; 1532 540 1533 541 1534 RTDECL(int) RTZipTarFsStream FromIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)542 RTDECL(int) RTZipTarFsStreamToIoStream(RTVFSIOSTREAM hVfsIosOut, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss) 1535 543 { 1536 544 /* … … 1539 547 AssertPtrReturn(phVfsFss, VERR_INVALID_HANDLE); 1540 548 *phVfsFss = NIL_RTVFSFSSTREAM; 1541 AssertPtrReturn(hVfsIos In, VERR_INVALID_HANDLE);549 AssertPtrReturn(hVfsIosOut, VERR_INVALID_HANDLE); 1542 550 AssertReturn(!fFlags, VERR_INVALID_PARAMETER); 1543 551 1544 RTFOFF const offStart = RTVfsIoStrmTell(hVfsIosIn); 1545 AssertReturn(offStart >= 0, (int)offStart); 1546 1547 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosIn); 552 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosOut); 1548 553 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE); 1549 554 … … 1551 556 * Retain the input stream and create a new filesystem stream handle. 1552 557 */ 1553 PRTZIPTARFSSTREAM pThis; 1554 RTVFSFSSTREAM hVfsFss; 1555 int rc = RTVfsNewFsStream(&rtZipTarFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFss, (void **)&pThis); 558 PRTZIPTARFSSTREAMWRITER pThis; 559 RTVFSFSSTREAM hVfsFss; 560 int rc = RTVfsNewFsStream(&rtZipTarFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, false /*fReadOnly*/, 561 &hVfsFss, (void **)&pThis); 1556 562 if (RT_SUCCESS(rc)) 1557 563 { 1558 pThis->hVfsIos = hVfsIosIn; 1559 pThis->hVfsCurObj = NIL_RTVFSOBJ; 1560 pThis->pCurIosData = NULL; 1561 pThis->offStart = offStart; 1562 pThis->offNextHdr = offStart; 1563 pThis->fEndOfStream = false; 1564 pThis->rcFatal = VINF_SUCCESS; 1565 pThis->TarReader.enmPrevType= RTZIPTARTYPE_INVALID; 1566 pThis->TarReader.enmType = RTZIPTARTYPE_INVALID; 1567 pThis->TarReader.enmState = RTZIPTARREADERSTATE_FIRST; 1568 1569 /* Don't check if it's a TAR stream here, do that in the 1570 rtZipTarFss_Next. */ 564 pThis->hVfsIos = hVfsIosOut; 565 pThis->rcFatal = VINF_SUCCESS; 1571 566 1572 567 *phVfsFss = hVfsFss; … … 1574 569 } 1575 570 1576 RTVfsIoStrmRelease(hVfsIos In);571 RTVfsIoStrmRelease(hVfsIosOut); 1577 572 return rc; 1578 573 }
Note:
See TracChangeset
for help on using the changeset viewer.