- Timestamp:
- Jan 23, 2014 5:52:28 PM (11 years ago)
- Location:
- trunk
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/iprt/mangling.h
r50154 r50194 1497 1497 # define RTSystemShutdown RT_MANGLER(RTSystemShutdown) 1498 1498 # define RTTarClose RT_MANGLER(RTTarClose) 1499 # define RTTarCreate RT_MANGLER(RTTarCreate)1500 1499 # define RTTarCurrentFile RT_MANGLER(RTTarCurrentFile) 1501 1500 # define RTTarFileClose RT_MANGLER(RTTarFileClose) … … 1503 1502 # define RTTarFileOpen RT_MANGLER(RTTarFileOpen) 1504 1503 # define RTTarFileOpenCurrentFile RT_MANGLER(RTTarFileOpenCurrentFile) 1505 # define RTTarFileRead RT_MANGLER(RTTarFileRead)1506 1504 # define RTTarFileReadAt RT_MANGLER(RTTarFileReadAt) 1507 # define RTTarFileSeek RT_MANGLER(RTTarFileSeek)1508 # define RTTarFileSetMode RT_MANGLER(RTTarFileSetMode)1509 # define RTTarFileSetOwner RT_MANGLER(RTTarFileSetOwner)1510 1505 # define RTTarFileSetSize RT_MANGLER(RTTarFileSetSize) 1511 # define RTTarFileSetTime RT_MANGLER(RTTarFileSetTime)1512 # define RTTarFileTell RT_MANGLER(RTTarFileTell)1513 # define RTTarFileWrite RT_MANGLER(RTTarFileWrite)1514 1506 # define RTTarFileWriteAt RT_MANGLER(RTTarFileWriteAt) 1515 # define RTTarList RT_MANGLER(RTTarList)1516 1507 # define RTTarOpen RT_MANGLER(RTTarOpen) 1517 1508 # define RTTarSeekNextFile RT_MANGLER(RTTarSeekNextFile) -
trunk/include/iprt/tar.h
r50154 r50194 4 4 5 5 /* 6 * Copyright (C) 2009-201 0Oracle Corporation6 * Copyright (C) 2009-2014 Oracle Corporation 7 7 * 8 8 * This file is part of VirtualBox Open Source Edition (OSE), as … … 35 35 /** @defgroup grp_rt_tar RTTar - Tar archive I/O 36 36 * @ingroup grp_rt 37 * 38 * @deprecated Only used for legacy code and writing. Migrate new code to the 39 * VFS interface, add the write part when needed. 40 * 37 41 * @{ 38 42 */ … … 77 81 RTR3DECL(int) RTTarOpen(PRTTAR phTar, const char *pszTarname, uint32_t fMode, bool fStream); 78 82 79 #if 080 /**81 * Opens a Tar archive by handle.82 *83 * Use the mask to specify the access type. In create mode the target file84 * have not to exists.85 *86 * @returns IPRT status code.87 *88 * @param phTar Where to store the RTTAR handle.89 * @param hFile The file handle of the tar file. This is expected90 * to be a regular file at the moment.91 * @param fStream Open the file in stream mode. Within this mode no92 * seeking is allowed. Use this together with93 * RTTarFileCurrent, RTTarFileOpenCurrent,94 * RTTarFileSeekNextFile and the read method to95 * sequential read a tar file. Currently ignored with96 * RTFILE_O_WRITE.97 */98 RTR3DECL(int) RTTarOpenByHandle(PRTTAR phTar, RTFILE hFile, uint32_t fMode, bool fStream);99 #endif100 101 83 /** 102 84 * Close the Tar archive. … … 107 89 */ 108 90 RTR3DECL(int) RTTarClose(RTTAR hTar); 91 92 /** 93 * Jumps to the next file from the current RTTar position. 94 * 95 * @returns IPRT status code. 96 * 97 * @param hTar Handle to the RTTAR interface. 98 */ 99 RTR3DECL(int) RTTarSeekNextFile(RTTAR hTar); 100 101 /** 102 * Return the filename where RTTar currently stays at. 103 * 104 * @returns IPRT status code. 105 * 106 * @param hTar Handle to the RTTAR interface. 107 * @param ppszFilename On success the filename. 108 */ 109 RTR3DECL(int) RTTarCurrentFile(RTTAR hTar, char **ppszFilename); 110 111 /** 112 * Opens the file where RTTar currently stays at. 113 * 114 * The current file can only be opened once. The next call will open the next 115 * file, implicitly calling RTTarSeekNextFile(). 116 * 117 * @returns IPRT status code. 118 * 119 * @param hTar Handle to the RTTAR interface. 120 * @param phFile Where to store the handle to the opened file. 121 * @param ppszFilename On success the filename. 122 * @param fOpen Open flags, i.e a combination of the RTFILE_O_* defines. 123 * The ACCESS, ACTION flags are mandatory! Currently 124 * only RTFILE_O_OPEN | RTFILE_O_READ is supported. 125 */ 126 RTR3DECL(int) RTTarFileOpenCurrentFile(RTTAR hTar, PRTTARFILE phFile, char **ppszFilename, uint32_t fOpen); 109 127 110 128 /** … … 139 157 140 158 /** 141 * Changes the read & write position in a file.142 *143 * @returns IPRT status code.144 *145 * @param hFile Handle to the file.146 * @param offSeek Offset to seek.147 * @param uMethod Seek method, i.e. one of the RTFILE_SEEK_* defines.148 * @param poffActual Where to store the new file position.149 * NULL is allowed.150 */151 RTR3DECL(int) RTTarFileSeek(RTTARFILE hFile, uint64_t offSeek, unsigned uMethod, uint64_t *poffActual);152 153 /**154 * Gets the current file position.155 *156 * @returns File offset.157 * @returns UINT64_MAX on failure.158 *159 * @param hFile Handle to the file.160 */161 RTR3DECL(uint64_t) RTTarFileTell(RTTARFILE hFile);162 163 /**164 * Read bytes from a file.165 *166 * @returns IPRT status code.167 *168 * @param hFile Handle to the file.169 * @param pvBuf Where to put the bytes we read.170 * @param cbToRead How much to read.171 * @param *pcbRead How much we actually read .172 * If NULL an error will be returned for a partial read.173 */174 RTR3DECL(int) RTTarFileRead(RTTARFILE hFile, void *pvBuf, size_t cbToRead, size_t *pcbRead);175 176 /**177 159 * Read bytes from a file at a given offset. 178 160 * This function may modify the file position. … … 184 166 * @param pvBuf Where to put the bytes we read. 185 167 * @param cbToRead How much to read. 186 * @param *pcbRead How much we actually read .187 * If NULLan error will be returned for a partial read.168 * @param pcbRead Where to return how much we actually read. If NULL 169 * an error will be returned for a partial read. 188 170 */ 189 171 RTR3DECL(int) RTTarFileReadAt(RTTARFILE hFile, uint64_t off, void *pvBuf, size_t cbToRead, size_t *pcbRead); 190 191 /**192 * Write bytes to a file.193 *194 * @returns IPRT status code.195 *196 * @param hFile Handle to the file.197 * @param pvBuf What to write.198 * @param cbToWrite How much to write.199 * @param *pcbWritten How much we actually wrote.200 * If NULL an error will be returned for a partial write.201 */202 RTR3DECL(int) RTTarFileWrite(RTTARFILE hFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten);203 172 204 173 /** … … 212 181 * @param pvBuf What to write. 213 182 * @param cbToWrite How much to write. 214 * @param *pcbWritten How much we actually wrote.215 * If NULLan error will be returned for a partial write.183 * @param pcbWritten Where to return how much we actually wrote. If NULL 184 * an error will be returned for a partial write. 216 185 */ 217 186 RTR3DECL(int) RTTarFileWriteAt(RTTARFILE hFile, uint64_t off, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten); … … 237 206 RTR3DECL(int) RTTarFileSetSize(RTTARFILE hFile, uint64_t cbSize); 238 207 239 /**240 * Changes the mode flags of an open file.241 *242 * @returns IPRT status code.243 *244 * @param hFile Handle to the file.245 * @param fMode The new file mode, see @ref grp_rt_fs for details.246 */247 RTR3DECL(int) RTTarFileSetMode(RTTARFILE hFile, uint32_t fMode);248 249 /**250 * Sets the modification timestamp of the file.251 *252 * @returns IPRT status code.253 *254 * @param pFile Handle to the file.255 * @param pTime The time to store.256 */257 RTR3DECL(int) RTTarFileSetTime(RTTARFILE hFile, PRTTIMESPEC pTime);258 259 /**260 * Changes the owner and/or group of an open file.261 *262 * @returns IPRT status code.263 *264 * @param hFile Handle to the file.265 * @param uid The new file owner user id. Use -1 (or ~0) to leave this unchanged.266 * @param gid The new group id. Use -1 (or ~0) to leave this unchanged.267 */268 RTR3DECL(int) RTTarFileSetOwner(RTTARFILE hFile, uint32_t uid, uint32_t gid);269 270 /******************************************************************************271 * Convenience Functions *272 ******************************************************************************/273 274 /**275 * Create a file list from a Tar archive.276 *277 * @note Currently only regular files are supported.278 *279 * @returns IPRT status code.280 *281 * @param pszTarFile Tar file to list files from.282 * @param ppapszFiles On success an array with array with the filenames is283 * returned. The names must be freed with RTStrFree and284 * the array with RTMemFree.285 * @param pcFiles On success the number of entries in ppapszFiles.286 */287 RTR3DECL(int) RTTarList(const char *pszTarFile, char ***ppapszFiles, size_t *pcFiles);288 289 /**290 * Create a Tar archive out of the given files.291 *292 * @note Currently only regular files are supported.293 *294 * @returns IPRT status code.295 *296 * @param pszTarFile Where to create the Tar archive.297 * @param papszFiles Which files should be included.298 * @param cFiles The number of files in papszFiles.299 * @param pfnProgressCallback Progress callback function. Optional.300 * @param pvUser User defined data for the progress301 * callback. Optional.302 */303 RTR3DECL(int) RTTarCreate(const char *pszTarFile, const char * const *papszFiles, size_t cFiles, PFNRTPROGRESS pfnProgressCallback, void *pvUser);304 305 /******************************************************************************306 * Streaming Functions *307 ******************************************************************************/308 309 /**310 * Return the filename where RTTar currently stays at.311 *312 * @returns IPRT status code.313 *314 * @param hTar Handle to the RTTAR interface.315 * @param ppszFilename On success the filename.316 */317 RTR3DECL(int) RTTarCurrentFile(RTTAR hTar, char **ppszFilename);318 319 /**320 * Jumps to the next file from the current RTTar position.321 *322 * @returns IPRT status code.323 *324 * @param hTar Handle to the RTTAR interface.325 */326 RTR3DECL(int) RTTarSeekNextFile(RTTAR hTar);327 328 /**329 * Opens the file where RTTar currently stays at.330 *331 * @returns IPRT status code.332 *333 * @param hTar Handle to the RTTAR interface.334 * @param phFile Where to store the handle to the opened file.335 * @param ppszFilename On success the filename.336 * @param fOpen Open flags, i.e a combination of the RTFILE_O_* defines.337 * The ACCESS, ACTION flags are mandatory! Currently338 * only RTFILE_O_OPEN | RTFILE_O_READ is supported.339 */340 RTR3DECL(int) RTTarFileOpenCurrentFile(RTTAR hTar, PRTTARFILE phFile, char **ppszFilename, uint32_t fOpen);341 342 343 208 /** @} */ 344 209 345 210 RT_C_DECLS_END 346 211 347 #endif /* ___iprt_tar_h */348 212 #endif 213 -
trunk/src/VBox/Runtime/common/zip/tar.cpp
r50180 r50194 25 25 */ 26 26 27 #define RT_USE_TAR_VFS_FOR_ALL_READS // the old code sticks around for a short while for debugging the new. 28 29 /****************************************************************************** 30 * Header Files * 31 ******************************************************************************/ 27 28 /******************************************************************************* 29 * Header Files * 30 *******************************************************************************/ 32 31 #include "internal/iprt.h" 33 32 #include <iprt/tar.h> … … 40 39 #include <iprt/path.h> 41 40 #include <iprt/string.h> 42 #ifdef RT_USE_TAR_VFS_FOR_ALL_READS 43 # include <iprt/vfs.h> 44 # include <iprt/zip.h> 45 #endif /* RT_USE_TAR_VFS_FOR_ALL_READS */ 41 #include <iprt/vfs.h> 42 #include <iprt/zip.h> 46 43 47 44 … … 50 47 51 48 52 /****************************************************************************** 53 * Structures and Typedefs * 54 ******************************************************************************/ 55 49 /******************************************************************************* 50 * Structures and Typedefs * 51 *******************************************************************************/ 56 52 /** @name RTTARRECORD::h::linkflag 57 53 * @{ */ … … 116 112 /** Whether operating in stream mode. */ 117 113 bool fStreamMode; 118 #ifndef RT_USE_TAR_VFS_FOR_ALL_READS119 /** The file cache of one file. */120 PRTTARFILEINTERNAL pFileCache;121 #else /* RT_USE_TAR_VFS_FOR_ALL_READS */122 114 /** The tar file VFS handle. */ 123 115 RTVFSFILE hVfsFile; … … 130 122 /** The name of the current object (fStreamMode = true). */ 131 123 char *pszVfsCurName; 132 #endif /* RT_USE_TAR_VFS_FOR_ALL_READS */133 124 } RTTARINTERNAL; 134 125 /** Pointer to a the internal data of a tar handle. */ … … 156 147 /** The current offset within this file. */ 157 148 uint64_t offCurrent; 158 #ifndef RT_USE_TAR_VFS_FOR_ALL_READS159 /** The link flag. */160 char linkflag;161 #endif162 #ifdef RT_USE_TAR_VFS_FOR_ALL_READS163 149 /** The VFS I/O stream (only for reading atm). */ 164 150 RTVFSIOSTREAM hVfsIos; 165 #endif166 151 } RTTARFILEINTERNAL; 167 152 /** Pointer to the internal data of a tar file. */ … … 170 155 171 156 172 /****************************************************************************** 173 * Defined Constants And Macros*174 157 /******************************************************************************* 158 * Defined Constants And Macros * 159 *******************************************************************************/ 175 160 176 161 /** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */ … … 209 194 210 195 211 /****************************************************************************** 212 * Internal Functions * 213 ******************************************************************************/ 214 215 DECLINLINE(void) rtTarSizeToRec(PRTTARRECORD pRecord, uint64_t cbSize) 196 RTR3DECL(int) RTTarOpen(PRTTAR phTar, const char *pszTarname, uint32_t fMode, bool fStream) 197 { 198 AssertReturn(!fStream || !(fMode & RTFILE_O_WRITE), VERR_INVALID_PARAMETER); 199 200 /* 201 * Create a tar instance. 202 */ 203 PRTTARINTERNAL pThis = (PRTTARINTERNAL)RTMemAllocZ(sizeof(RTTARINTERNAL)); 204 if (!pThis) 205 return VERR_NO_MEMORY; 206 207 pThis->u32Magic = RTTAR_MAGIC; 208 pThis->fOpenMode = fMode; 209 pThis->fStreamMode = fStream && (fMode & RTFILE_O_READ); 210 211 /* 212 * Open the tar file. 213 */ 214 pThis->hVfsFile = NIL_RTVFSFILE; 215 pThis->hVfsFss = NIL_RTVFSFSSTREAM; 216 pThis->fFssAtStart = false; 217 pThis->hVfsCur = NIL_RTVFSIOSTREAM; 218 pThis->pszVfsCurName = NULL; 219 220 int rc; 221 if (!(fMode & RTFILE_O_WRITE)) 222 { 223 rc = RTVfsFileOpenNormal(pszTarname, fMode, &pThis->hVfsFile); 224 if (RT_SUCCESS(rc)) 225 { 226 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(pThis->hVfsFile); 227 rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0 /*fFlags*/, &pThis->hVfsFss); 228 if (RT_SUCCESS(rc)) 229 pThis->fFssAtStart = true; 230 else 231 { 232 RTVfsFileRelease(pThis->hVfsFile); 233 pThis->hVfsFile = NIL_RTVFSFILE; 234 } 235 RTVfsIoStrmRelease(hVfsIos); 236 } 237 } 238 else 239 rc = RTFileOpen(&pThis->hTarFile, pszTarname, fMode); 240 if (RT_SUCCESS(rc)) 241 { 242 *phTar = pThis; 243 return VINF_SUCCESS; 244 } 245 246 RTMemFree(pThis); 247 return rc; 248 } 249 250 RTR3DECL(int) RTTarClose(RTTAR hTar) 251 { 252 if (hTar == NIL_RTTAR) 253 return VINF_SUCCESS; 254 255 PRTTARINTERNAL pInt = hTar; 256 RTTAR_VALID_RETURN(pInt); 257 258 int rc = VINF_SUCCESS; 259 260 /* gtar gives a warning, but the documentation says EOF is indicated by a 261 * zero block. Disabled for now. */ 262 #if 0 263 { 264 /* Append the EOF record which is filled all by zeros */ 265 RTTARRECORD record; 266 RT_ZERO(record); 267 rc = RTFileWrite(pInt->hTarFile, &record, sizeof(record), NULL); 268 } 269 #endif 270 271 if (pInt->hVfsFss != NIL_RTVFSFSSTREAM) 272 { 273 uint32_t cRefs = RTVfsFsStrmRelease(pInt->hVfsFss); Assert(cRefs != UINT32_MAX); 274 pInt->hVfsFss = NIL_RTVFSFSSTREAM; 275 } 276 277 if (pInt->hVfsFile != NIL_RTVFSFILE) 278 { 279 uint32_t cRefs = RTVfsFileRelease(pInt->hVfsFile); Assert(cRefs != UINT32_MAX); 280 pInt->hVfsFile = NIL_RTVFSFILE; 281 } 282 283 if (pInt->hVfsCur != NIL_RTVFSIOSTREAM) 284 { 285 RTVfsIoStrmRelease(pInt->hVfsCur); 286 pInt->hVfsCur = NIL_RTVFSIOSTREAM; 287 } 288 289 if (pInt->pszVfsCurName) 290 { 291 RTStrFree(pInt->pszVfsCurName); 292 pInt->pszVfsCurName = NULL; 293 } 294 295 if (pInt->hTarFile != NIL_RTFILE) 296 { 297 rc = RTFileClose(pInt->hTarFile); 298 pInt->hTarFile = NIL_RTFILE; 299 } 300 301 pInt->u32Magic = RTTAR_MAGIC_DEAD; 302 303 RTMemFree(pInt); 304 305 return rc; 306 } 307 308 309 RTR3DECL(int) RTTarSeekNextFile(RTTAR hTar) 310 { 311 PRTTARINTERNAL pInt = hTar; 312 RTTAR_VALID_RETURN(pInt); 313 314 if (!pInt->fStreamMode) 315 return VERR_INVALID_STATE; 316 317 /* 318 * Release the current object. 319 */ 320 if (pInt->hVfsCur != NIL_RTVFSIOSTREAM) 321 { 322 RTVfsIoStrmRelease(pInt->hVfsCur); 323 pInt->hVfsCur = NIL_RTVFSIOSTREAM; 324 } 325 326 if (pInt->pszVfsCurName) 327 { 328 RTStrFree(pInt->pszVfsCurName); 329 pInt->pszVfsCurName = NULL; 330 } 331 332 /* 333 * Find the next file. 334 */ 335 for (;;) 336 { 337 char *pszName; 338 RTVFSOBJTYPE enmType; 339 RTVFSOBJ hVfsObj; 340 int rc = RTVfsFsStrmNext(hTar->hVfsFss, &pszName, &enmType, &hVfsObj); 341 if (rc == VERR_EOF) 342 return VERR_TAR_END_OF_FILE; 343 344 if ( enmType == RTVFSOBJTYPE_FILE 345 || enmType == RTVFSOBJTYPE_IO_STREAM 346 || enmType == RTVFSOBJTYPE_DIR) 347 { 348 pInt->pszVfsCurName = pszName; 349 if (enmType == RTVFSOBJTYPE_DIR) 350 rc = VINF_TAR_DIR_PATH; 351 else 352 { 353 pInt->hVfsCur = RTVfsObjToIoStream(hVfsObj); 354 Assert(pInt->hVfsCur != NIL_RTVFSIOSTREAM); 355 rc = VINF_SUCCESS; 356 } 357 RTVfsObjRelease(hVfsObj); 358 return rc; 359 } 360 RTStrFree(pszName); 361 RTVfsObjRelease(hVfsObj); 362 } 363 } 364 365 366 RTR3DECL(int) RTTarCurrentFile(RTTAR hTar, char **ppszFilename) 367 { 368 /* Validate input. */ 369 AssertPtrNullReturn(ppszFilename, VERR_INVALID_POINTER); 370 371 PRTTARINTERNAL pInt = hTar; 372 RTTAR_VALID_RETURN(pInt); 373 374 if (!pInt->fStreamMode) 375 return VERR_INVALID_STATE; 376 377 if (!pInt->pszVfsCurName) 378 { 379 int rc = RTTarSeekNextFile(pInt); 380 if (RT_FAILURE(rc)) 381 return rc; 382 } 383 Assert(pInt->pszVfsCurName); 384 385 if (ppszFilename) 386 { 387 *ppszFilename = RTStrDup(pInt->pszVfsCurName); 388 if (!*ppszFilename) 389 return VERR_NO_STR_MEMORY; 390 } 391 392 return pInt->hVfsCur != NIL_RTVFSIOSTREAM ? VINF_SUCCESS : VINF_TAR_DIR_PATH; 393 } 394 395 396 /** 397 * Creates a tar file handle for a read-only VFS stream object. 398 * 399 * @returns IPRT status code. 400 * @param pszName The file name. Automatically freed on failure. 401 * @param hVfsIos The VFS I/O stream we create the handle around. 402 * The reference is NOT consumed. 403 * @param fOpen The open flags. 404 * @param ppFile Where to return the handle. 405 */ 406 static int rtTarFileCreateHandleForReadOnly(char *pszName, RTVFSIOSTREAM hVfsIos, uint32_t fOpen, PRTTARFILEINTERNAL *ppFile) 407 { 408 int rc; 409 PRTTARFILEINTERNAL pNewFile = (PRTTARFILEINTERNAL)RTMemAllocZ(sizeof(*pNewFile)); 410 if (pNewFile) 411 { 412 RTFSOBJINFO ObjInfo; 413 rc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_UNIX); 414 if (RT_SUCCESS(rc)) 415 { 416 pNewFile->u32Magic = RTTARFILE_MAGIC; 417 pNewFile->pTar = NULL; 418 pNewFile->pszFilename = pszName; 419 pNewFile->offStart = UINT64_MAX; 420 pNewFile->cbSize = ObjInfo.cbObject; 421 pNewFile->cbSetSize = 0; 422 pNewFile->offCurrent = 0; 423 pNewFile->fOpenMode = fOpen; 424 pNewFile->hVfsIos = hVfsIos; 425 426 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIos); Assert(cRefs != UINT32_MAX); NOREF(cRefs); 427 428 *ppFile = pNewFile; 429 return VINF_SUCCESS; 430 } 431 432 RTMemFree(pNewFile); 433 } 434 else 435 rc = VERR_NO_MEMORY; 436 RTStrFree(pszName); 437 return rc; 438 } 439 440 441 442 RTR3DECL(int) RTTarFileOpenCurrentFile(RTTAR hTar, PRTTARFILE phFile, char **ppszFilename, uint32_t fOpen) 443 { 444 /* Validate input. */ 445 AssertPtrReturn(phFile, VERR_INVALID_POINTER); 446 AssertPtrNullReturn(ppszFilename, VERR_INVALID_POINTER); 447 AssertReturn((fOpen & RTFILE_O_READ), VERR_INVALID_PARAMETER); /* Only valid in read mode. */ 448 449 PRTTARINTERNAL pInt = hTar; 450 RTTAR_VALID_RETURN(pInt); 451 452 if (!pInt->fStreamMode) 453 return VERR_INVALID_STATE; 454 455 /* 456 * Make sure there is a current file (first call w/o RTTarSeekNextFile call). 457 */ 458 if (pInt->hVfsCur == NIL_RTVFSIOSTREAM) 459 { 460 if (pInt->pszVfsCurName) 461 return -VINF_TAR_DIR_PATH; 462 463 int rc = RTTarSeekNextFile(pInt); 464 if (RT_FAILURE(rc)) 465 return rc; 466 467 if (pInt->hVfsCur == NIL_RTVFSIOSTREAM) 468 return -VINF_TAR_DIR_PATH; 469 } 470 Assert(pInt->pszVfsCurName); 471 472 /* 473 * Return a copy of the filename if requested. 474 */ 475 if (ppszFilename) 476 { 477 *ppszFilename = RTStrDup(pInt->pszVfsCurName); 478 if (!*ppszFilename) 479 return VERR_NO_STR_MEMORY; 480 } 481 482 /* 483 * Create a handle for it. 484 */ 485 int rc = rtTarFileCreateHandleForReadOnly(RTStrDup(pInt->pszVfsCurName), pInt->hVfsCur, RTFILE_O_READ, phFile); 486 if (RT_SUCCESS(rc)) 487 { 488 /* Force a RTTarSeekNextFile call the next time around. */ 489 RTVfsIoStrmRelease(pInt->hVfsCur); 490 pInt->hVfsCur = NIL_RTVFSIOSTREAM; 491 RTStrFree(pInt->pszVfsCurName); 492 pInt->pszVfsCurName = NULL; 493 } 494 else if (ppszFilename) 495 { 496 RTStrFree(*ppszFilename); 497 *ppszFilename = NULL; 498 } 499 500 return rc; 501 } 502 503 504 /* Only used for write handles. */ 505 static PRTTARFILEINTERNAL rtTarFileCreateForWrite(PRTTARINTERNAL pInt, const char *pszFilename, uint32_t fOpen) 506 { 507 PRTTARFILEINTERNAL pFileInt = (PRTTARFILEINTERNAL)RTMemAllocZ(sizeof(RTTARFILEINTERNAL)); 508 if (!pFileInt) 509 return NULL; 510 511 pFileInt->u32Magic = RTTARFILE_MAGIC; 512 pFileInt->pTar = pInt; 513 pFileInt->fOpenMode = fOpen; 514 pFileInt->pszFilename = RTStrDup(pszFilename); 515 if (!pFileInt->pszFilename) 516 { 517 pFileInt->hVfsIos = NIL_RTVFSIOSTREAM; 518 RTMemFree(pFileInt); 519 return NULL; 520 } 521 522 return pFileInt; 523 } 524 525 526 RTR3DECL(int) RTTarFileOpen(RTTAR hTar, PRTTARFILE phFile, const char *pszFilename, uint32_t fOpen) 527 { 528 AssertReturn((fOpen & RTFILE_O_READ) || (fOpen & RTFILE_O_WRITE), VERR_INVALID_PARAMETER); 529 530 PRTTARINTERNAL pInt = hTar; 531 RTTAR_VALID_RETURN(pInt); 532 533 if (!pInt->hTarFile) 534 return VERR_INVALID_HANDLE; 535 536 if (pInt->fStreamMode) 537 return VERR_INVALID_STATE; 538 539 if (fOpen & RTFILE_O_WRITE) 540 { 541 if (!(pInt->fOpenMode & RTFILE_O_WRITE)) 542 return VERR_WRITE_PROTECT; 543 if (pInt->fFileOpenForWrite) 544 return VERR_TOO_MANY_OPEN_FILES; 545 } 546 547 int rc = VINF_SUCCESS; 548 if (!(fOpen & RTFILE_O_WRITE)) 549 { 550 /* 551 * Rewind the stream if necessary. 552 */ 553 if (!pInt->fFssAtStart) 554 { 555 if (pInt->hVfsFss != NIL_RTVFSFSSTREAM) 556 { 557 uint32_t cRefs = RTVfsFsStrmRelease(pInt->hVfsFss); Assert(cRefs != UINT32_MAX); 558 pInt->hVfsFss = NIL_RTVFSFSSTREAM; 559 } 560 561 if (pInt->hVfsFile == NIL_RTVFSFILE) 562 { 563 rc = RTVfsFileFromRTFile(pInt->hTarFile, RTFILE_O_READ, true /*fLeaveOpen*/, &pInt->hVfsFile); 564 if (RT_FAILURE(rc)) 565 return rc; 566 } 567 Assert(pInt->hVfsCur == NIL_RTVFSIOSTREAM && pInt->pszVfsCurName == NULL); 568 569 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(pInt->hVfsFile); 570 rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0 /*fFlags*/, &pInt->hVfsFss); 571 RTVfsIoStrmRelease(hVfsIos); 572 if (RT_FAILURE(rc)) 573 return rc; 574 } 575 576 /* 577 * Search the file system stream. 578 */ 579 pInt->fFssAtStart = false; 580 for (;;) 581 { 582 char *pszName; 583 RTVFSOBJTYPE enmType; 584 RTVFSOBJ hVfsObj; 585 rc = RTVfsFsStrmNext(pInt->hVfsFss, &pszName, &enmType, &hVfsObj); 586 if (rc == VERR_EOF) 587 return VERR_FILE_NOT_FOUND; 588 if (RT_FAILURE(rc)) 589 return rc; 590 591 if (!RTStrCmp(pszName, pszFilename)) 592 { 593 if (enmType == RTVFSOBJTYPE_FILE || enmType == RTVFSOBJTYPE_IO_STREAM) 594 rc = rtTarFileCreateHandleForReadOnly(pszName, RTVfsObjToIoStream(hVfsObj), fOpen, phFile); 595 else 596 { 597 rc = VERR_UNEXPECTED_FS_OBJ_TYPE; 598 RTStrFree(pszName); 599 } 600 RTVfsObjRelease(hVfsObj); 601 break; 602 } 603 RTStrFree(pszName); 604 RTVfsObjRelease(hVfsObj); 605 } /* Search loop. */ 606 } 607 else 608 { 609 PRTTARFILEINTERNAL pFileInt = rtTarFileCreateForWrite(pInt, pszFilename, fOpen); 610 if (!pFileInt) 611 return VERR_NO_MEMORY; 612 613 pInt->fFileOpenForWrite = true; 614 615 /* If we are in write mode, we also in append mode. Add an dummy 616 * header at the end of the current file. It will be filled by the 617 * close operation. */ 618 rc = RTFileSeek(pFileInt->pTar->hTarFile, 0, RTFILE_SEEK_END, &pFileInt->offStart); 619 if (RT_SUCCESS(rc)) 620 { 621 RTTARRECORD record; 622 RT_ZERO(record); 623 rc = RTFileWrite(pFileInt->pTar->hTarFile, &record, sizeof(RTTARRECORD), NULL); 624 } 625 626 if (RT_SUCCESS(rc)) 627 *phFile = (RTTARFILE)pFileInt; 628 else 629 { 630 /* Cleanup on failure */ 631 if (pFileInt->pszFilename) 632 RTStrFree(pFileInt->pszFilename); 633 RTMemFree(pFileInt); 634 } 635 } 636 637 return rc; 638 } 639 640 641 /** 642 * Calculates the TAR header checksums and detects if it's all zeros. 643 * 644 * @returns true if all zeros, false if not. 645 * @param pHdr The header to checksum. 646 * @param pi32Unsigned Where to store the checksum calculated using 647 * unsigned chars. This is the one POSIX 648 * specifies. 649 * @param pi32Signed Where to store the checksum calculated using 650 * signed chars. 651 * 652 * @remarks The reason why we calculate the checksum as both signed and unsigned 653 * has to do with various the char C type being signed on some hosts 654 * and unsigned on others. 655 * 656 * @remarks Borrowed from tarvfs.cpp. 657 */ 658 static bool rtZipTarCalcChkSum(PCRTZIPTARHDR pHdr, int32_t *pi32Unsigned, int32_t *pi32Signed) 659 { 660 int32_t i32Unsigned = 0; 661 int32_t i32Signed = 0; 662 663 /* 664 * Sum up the entire header. 665 */ 666 const char *pch = (const char *)pHdr; 667 const char *pchEnd = pch + sizeof(*pHdr); 668 do 669 { 670 i32Unsigned += *(unsigned char *)pch; 671 i32Signed += *(signed char *)pch; 672 } while (++pch != pchEnd); 673 674 /* 675 * Check if it's all zeros and replace the chksum field with spaces. 676 */ 677 bool const fZeroHdr = i32Unsigned == 0; 678 679 pch = pHdr->Common.chksum; 680 pchEnd = pch + sizeof(pHdr->Common.chksum); 681 do 682 { 683 i32Unsigned -= *(unsigned char *)pch; 684 i32Signed -= *(signed char *)pch; 685 } while (++pch != pchEnd); 686 687 i32Unsigned += (unsigned char)' ' * sizeof(pHdr->Common.chksum); 688 i32Signed += (signed char)' ' * sizeof(pHdr->Common.chksum); 689 690 *pi32Unsigned = i32Unsigned; 691 if (pi32Signed) 692 *pi32Signed = i32Signed; 693 return fZeroHdr; 694 } 695 696 697 static void rtTarSizeToRec(PRTTARRECORD pRecord, uint64_t cbSize) 216 698 { 217 699 /* … … 246 728 } 247 729 248 #ifndef RT_USE_TAR_VFS_FOR_ALL_READS 249 DECLINLINE(uint64_t) rtTarRecToSize(PRTTARRECORD pRecord) 250 { 251 int64_t cbSize = 0; 252 if (pRecord->h.size[0] & 0x80) 253 { 254 size_t cchField = sizeof(pRecord->h.size); 255 unsigned char const *puchField = (unsigned char const *)pRecord->h.size; 256 257 /* 258 * The first byte has the bit 7 set to indicate base-256, while bit 6 259 * is the signed bit. Bits 5:0 are the most significant value bits. 260 */ 261 cbSize = !(0x40 & *puchField) ? 0 : -1; 262 cbSize = (cbSize << 6) | (*puchField & 0x3f); 263 cchField--; 264 puchField++; 265 266 /* 267 * The remaining bytes are used in full. 268 */ 269 while (cchField-- > 0) 270 { 271 if (RT_UNLIKELY( cbSize > INT64_MAX / 256 272 || cbSize < INT64_MIN / 256)) 273 { 274 cbSize = cbSize < 0 ? INT64_MIN : INT64_MAX; 275 break; 276 } 277 cbSize = (cbSize << 8) | *puchField++; 278 } 279 } 280 else 281 RTStrToInt64Full(pRecord->h.size, 8, &cbSize); 282 283 if (cbSize < 0) 284 cbSize = 0; 285 286 return (uint64_t)cbSize; 287 } 288 #endif /* !RT_USE_TAR_VFS_FOR_ALL_READS */ 289 290 /** 291 * Calculates the TAR header checksums and detects if it's all zeros. 292 * 293 * @returns true if all zeros, false if not. 294 * @param pHdr The header to checksum. 295 * @param pi32Unsigned Where to store the checksum calculated using 296 * unsigned chars. This is the one POSIX 297 * specifies. 298 * @param pi32Signed Where to store the checksum calculated using 299 * signed chars. 300 * 301 * @remarks The reason why we calculate the checksum as both signed and unsigned 302 * has to do with various the char C type being signed on some hosts 303 * and unsigned on others. 304 * 305 * @remarks Borrowed from tarvfs.cpp. 306 */ 307 static bool rtZipTarCalcChkSum(PCRTZIPTARHDR pHdr, int32_t *pi32Unsigned, int32_t *pi32Signed) 308 { 309 int32_t i32Unsigned = 0; 310 int32_t i32Signed = 0; 311 312 /* 313 * Sum up the entire header. 314 */ 315 const char *pch = (const char *)pHdr; 316 const char *pchEnd = pch + sizeof(*pHdr); 317 do 318 { 319 i32Unsigned += *(unsigned char *)pch; 320 i32Signed += *(signed char *)pch; 321 } while (++pch != pchEnd); 322 323 /* 324 * Check if it's all zeros and replace the chksum field with spaces. 325 */ 326 bool const fZeroHdr = i32Unsigned == 0; 327 328 pch = pHdr->Common.chksum; 329 pchEnd = pch + sizeof(pHdr->Common.chksum); 330 do 331 { 332 i32Unsigned -= *(unsigned char *)pch; 333 i32Signed -= *(signed char *)pch; 334 } while (++pch != pchEnd); 335 336 i32Unsigned += (unsigned char)' ' * sizeof(pHdr->Common.chksum); 337 i32Signed += (signed char)' ' * sizeof(pHdr->Common.chksum); 338 339 *pi32Unsigned = i32Unsigned; 340 if (pi32Signed) 341 *pi32Signed = i32Signed; 342 return fZeroHdr; 343 } 344 345 #ifndef RT_USE_TAR_VFS_FOR_ALL_READS 346 DECLINLINE(int) rtTarReadHeaderRecord(RTFILE hFile, PRTTARRECORD pRecord) 347 { 348 int rc = RTFileRead(hFile, pRecord, sizeof(RTTARRECORD), NULL); 349 /* Check for EOF. EOF is valid in this case, cause it indicates no more 350 * data in the tar archive. */ 351 if (rc == VERR_EOF) 352 return VERR_TAR_END_OF_FILE; 353 /* Report any other errors */ 354 else if (RT_FAILURE(rc)) 355 return rc; 356 357 /* Check for data integrity & an EOF record */ 358 int32_t iUnsignedChksum, iSignedChksum; 359 if (rtZipTarCalcChkSum((PCRTZIPTARHDR)pRecord, &iUnsignedChksum, &iSignedChksum)) 360 return VERR_TAR_END_OF_FILE; 361 362 /* Verify the checksum */ 363 uint32_t sum; 364 rc = RTStrToUInt32Full(pRecord->h.chksum, 8, &sum); 365 if ( RT_SUCCESS(rc) 366 && ( sum == (uint32_t)iSignedChksum 367 || sum == (uint32_t)iUnsignedChksum) ) 368 { 369 /* Make sure the strings are zero terminated. */ 370 pRecord->h.name[sizeof(pRecord->h.name) - 1] = 0; 371 pRecord->h.linkname[sizeof(pRecord->h.linkname) - 1] = 0; 372 pRecord->h.magic[sizeof(pRecord->h.magic) - 1] = 0; 373 pRecord->h.uname[sizeof(pRecord->h.uname) - 1] = 0; 374 pRecord->h.gname[sizeof(pRecord->h.gname) - 1] = 0; 375 } 376 else 377 rc = VERR_TAR_CHKSUM_MISMATCH; 378 379 return rc; 380 } 381 #endif /* !RT_USE_TAR_VFS_FOR_ALL_READS */ 382 383 DECLINLINE(int) rtTarCreateHeaderRecord(PRTTARRECORD pRecord, const char *pszSrcName, uint64_t cbSize, 384 RTUID uid, RTGID gid, RTFMODE fmode, int64_t mtime) 730 731 static int rtTarCreateHeaderRecord(PRTTARRECORD pRecord, const char *pszSrcName, uint64_t cbSize, 732 RTUID uid, RTGID gid, RTFMODE fmode, int64_t mtime) 385 733 { 386 734 /** @todo check for field overflows. */ … … 412 760 } 413 761 762 414 763 DECLINLINE(void *) rtTarMemTmpAlloc(size_t *pcbSize) 415 764 { … … 428 777 } 429 778 430 DECLINLINE(int) rtTarAppendZeros(RTTARFILE hFile, uint64_t cbSize) 779 780 static int rtTarAppendZeros(PRTTARFILEINTERNAL pFileInt, uint64_t cbSize) 431 781 { 432 782 /* Allocate a temporary buffer for copying the tar content in blocks. */ … … 445 795 break; 446 796 size_t cbToWrite = RT_MIN(cbSize - cbAllWritten, cbTmp); 447 rc = RTTarFileWrite (hFile, pvTmp, cbToWrite, &cbWritten);797 rc = RTTarFileWriteAt(pFileInt, pFileInt->offCurrent, pvTmp, cbToWrite, &cbWritten); 448 798 if (RT_FAILURE(rc)) 449 799 break; … … 456 806 } 457 807 458 /* Only used for write handles when RT_USE_TAR_VFS_FOR_ALL_READS is defined. */459 DECLINLINE(PRTTARFILEINTERNAL) rtCreateTarFileInternal(PRTTARINTERNAL pInt, const char *pszFilename, uint32_t fOpen)460 {461 PRTTARFILEINTERNAL pFileInt = (PRTTARFILEINTERNAL)RTMemAllocZ(sizeof(RTTARFILEINTERNAL));462 if (!pFileInt)463 return NULL;464 465 pFileInt->u32Magic = RTTARFILE_MAGIC;466 pFileInt->pTar = pInt;467 pFileInt->fOpenMode = fOpen;468 pFileInt->pszFilename = RTStrDup(pszFilename);469 if (!pFileInt->pszFilename)470 {471 #ifdef RT_USE_TAR_VFS_FOR_ALL_READS472 pFileInt->hVfsIos = NIL_RTVFSIOSTREAM;473 #endif474 RTMemFree(pFileInt);475 return NULL;476 }477 478 return pFileInt;479 }480 481 #ifdef RT_USE_TAR_VFS_FOR_ALL_READS482 483 /**484 * Creates a tar file handle for a read-only VFS stream object.485 *486 * @returns IPRT status code.487 * @param pszName The file name. Automatically freed on failure.488 * @param hVfsIos The VFS I/O stream we create the handle around.489 * The reference is NOT consumed.490 * @param fOpen The open flags.491 * @param ppFile Where to return the handle.492 */493 static int rtTarFileCreateHandleForReadOnly(char *pszName, RTVFSIOSTREAM hVfsIos, uint32_t fOpen, PRTTARFILEINTERNAL *ppFile)494 {495 int rc;496 PRTTARFILEINTERNAL pNewFile = (PRTTARFILEINTERNAL)RTMemAllocZ(sizeof(*pNewFile));497 if (pNewFile)498 {499 RTFSOBJINFO ObjInfo;500 rc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_UNIX);501 if (RT_SUCCESS(rc))502 {503 pNewFile->u32Magic = RTTARFILE_MAGIC;504 pNewFile->pTar = NULL;505 pNewFile->pszFilename = pszName;506 pNewFile->offStart = UINT64_MAX;507 pNewFile->cbSize = ObjInfo.cbObject;508 pNewFile->cbSetSize = 0;509 pNewFile->offCurrent = 0;510 pNewFile->fOpenMode = fOpen;511 pNewFile->hVfsIos = hVfsIos;512 513 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIos); Assert(cRefs != UINT32_MAX); NOREF(cRefs);514 515 *ppFile = pNewFile;516 return VINF_SUCCESS;517 }518 519 RTMemFree(pNewFile);520 }521 else522 rc = VERR_NO_MEMORY;523 RTStrFree(pszName);524 return rc;525 }526 527 #else /* !RT_USE_TAR_VFS_FOR_ALL_READS */528 529 DECLINLINE(PRTTARFILEINTERNAL) rtCopyTarFileInternal(PRTTARFILEINTERNAL pInt)530 {531 PRTTARFILEINTERNAL pNewInt = (PRTTARFILEINTERNAL)RTMemAllocZ(sizeof(RTTARFILEINTERNAL));532 if (!pNewInt)533 return NULL;534 535 memcpy(pNewInt, pInt, sizeof(RTTARFILEINTERNAL));536 pNewInt->pszFilename = RTStrDup(pInt->pszFilename);537 if (!pNewInt->pszFilename)538 {539 RTMemFree(pNewInt);540 return NULL;541 }542 543 return pNewInt;544 }545 546 #endif /* !RT_USE_TAR_VFS_FOR_ALL_READS */547 548 DECLINLINE(void) rtDeleteTarFileInternal(PRTTARFILEINTERNAL pInt)549 {550 if (pInt)551 {552 if (pInt->pszFilename)553 RTStrFree(pInt->pszFilename);554 #ifdef RT_USE_TAR_VFS_FOR_ALL_READS555 if (pInt->hVfsIos != NIL_RTVFSIOSTREAM)556 {557 RTVfsIoStrmRelease(pInt->hVfsIos);558 pInt->hVfsIos = NIL_RTVFSIOSTREAM;559 }560 #endif561 562 pInt->u32Magic = RTTARFILE_MAGIC_DEAD;563 RTMemFree(pInt);564 }565 }566 567 static int rtTarAppendFileFromFile(RTTAR hTar, const char *pszSrcName, const uint64_t cbOverallSize, uint64_t &cbOverallWritten, PFNRTPROGRESS pfnProgressCallback, void *pvUser)568 {569 /* Open the source file */570 RTFILE hOldFile;571 int rc = RTFileOpen(&hOldFile, pszSrcName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);572 if (RT_FAILURE(rc))573 return rc;574 575 RTTARFILE hFile = NIL_RTTARFILE;576 void *pvTmp = NULL;577 do578 {579 /* Get the size of the source file */580 uint64_t cbToCopy;581 rc = RTFileGetSize(hOldFile, &cbToCopy);582 if (RT_FAILURE(rc))583 break;584 585 rc = RTTarFileOpen(hTar, &hFile, RTPathFilename(pszSrcName), RTFILE_O_OPEN | RTFILE_O_WRITE);586 if (RT_FAILURE(rc))587 break;588 589 /* Get some info from the source file */590 RTFSOBJINFO info;591 RTUID uid = 0;592 RTGID gid = 0;593 RTFMODE fmode = 0600; /* Make some save default */594 int64_t mtime = 0;595 596 /* This isn't critical. Use the defaults if it fails. */597 rc = RTFileQueryInfo(hOldFile, &info, RTFSOBJATTRADD_UNIX);598 if (RT_SUCCESS(rc))599 {600 fmode = info.Attr.fMode & RTFS_UNIX_MASK;601 uid = info.Attr.u.Unix.uid;602 gid = info.Attr.u.Unix.gid;603 mtime = RTTimeSpecGetSeconds(&info.ModificationTime);604 }605 606 /* Set the mode from the other file */607 rc = RTTarFileSetMode(hFile, fmode);608 if (RT_FAILURE(rc))609 break;610 611 /* Set the modification time from the other file */612 RTTIMESPEC time;613 RTTimeSpecSetSeconds(&time, mtime);614 rc = RTTarFileSetTime(hFile, &time);615 if (RT_FAILURE(rc))616 break;617 618 /* Set the owner from the other file */619 rc = RTTarFileSetOwner(hFile, uid, gid);620 if (RT_FAILURE(rc))621 break;622 623 /* Allocate a temporary buffer for copying the tar content in blocks. */624 size_t cbTmp = 0;625 pvTmp = rtTarMemTmpAlloc(&cbTmp);626 if (!pvTmp)627 {628 rc = VERR_NO_MEMORY;629 break;630 }631 632 /* Copy the content from pszSrcName over to hFile. This is done block633 * wise in 512 byte steps. After this copying is finished hFile will be634 * on a 512 byte boundary, regardless if the file copied is 512 byte635 * size aligned. */636 uint64_t cbAllWritten = 0; /* Already copied */637 uint64_t cbRead = 0; /* Actually read in the last step */638 for (;;)639 {640 if (pfnProgressCallback)641 pfnProgressCallback((unsigned)(100.0 / cbOverallSize * cbOverallWritten), pvUser);642 if (cbAllWritten >= cbToCopy)643 break;644 645 /* Read one block. Either its the buffer size or the rest of the646 * file. */647 cbRead = RT_MIN(cbToCopy - cbAllWritten, cbTmp);648 rc = RTFileRead(hOldFile, pvTmp, cbRead, NULL);649 if (RT_FAILURE(rc))650 break;651 652 /* Write one block. */653 rc = RTTarFileWriteAt(hFile, cbAllWritten, pvTmp, cbRead, NULL);654 if (RT_FAILURE(rc))655 break;656 657 /* Count how many bytes (of the original file) are written already */658 cbAllWritten += cbRead;659 cbOverallWritten += cbRead;660 }661 } while (0);662 663 /* Cleanup */664 if (pvTmp)665 RTMemTmpFree(pvTmp);666 667 if (hFile)668 RTTarFileClose(hFile);669 670 RTFileClose(hOldFile);671 672 return rc;673 }674 675 #ifndef RT_USE_TAR_VFS_FOR_ALL_READS676 677 static int rtTarSkipData(RTFILE hFile, PRTTARRECORD pRecord)678 {679 int rc = VINF_SUCCESS;680 /* Seek over the data parts (512 bytes aligned) */681 int64_t offSeek = RT_ALIGN(rtTarRecToSize(pRecord), sizeof(RTTARRECORD));682 if (offSeek > 0)683 rc = RTFileSeek(hFile, offSeek, RTFILE_SEEK_CURRENT, NULL);684 return rc;685 }686 687 static int rtTarFindFile(RTFILE hFile, const char *pszFile, uint64_t *poff, uint64_t *pcbSize)688 {689 /* Assume we are on the file head. */690 int rc = VINF_SUCCESS;691 bool fFound = false;692 RTTARRECORD record;693 for (;;)694 {695 /* Read & verify a header record */696 rc = rtTarReadHeaderRecord(hFile, &record);697 /* Check for error or EOF. */698 if (RT_FAILURE(rc))699 break;700 701 /* We support normal files only */702 if ( record.h.linkflag == LF_OLDNORMAL703 || record.h.linkflag == LF_NORMAL)704 {705 if (!RTStrCmp(record.h.name, pszFile))706 {707 /* Get the file size */708 *pcbSize = rtTarRecToSize(&record);709 /* Seek back, to position the file pointer at the start of the header. */710 rc = RTFileSeek(hFile, -(int64_t)sizeof(RTTARRECORD), RTFILE_SEEK_CURRENT, poff);711 fFound = true;712 break;713 }714 }715 rc = rtTarSkipData(hFile, &record);716 if (RT_FAILURE(rc))717 break;718 }719 720 if (rc == VERR_TAR_END_OF_FILE)721 rc = VINF_SUCCESS;722 723 /* Something found? */724 if ( RT_SUCCESS(rc)725 && !fFound)726 rc = VERR_FILE_NOT_FOUND;727 728 return rc;729 }730 731 #endif /* !RT_USE_TAR_VFS_FOR_ALL_READS */732 733 734 /******************************************************************************735 * Public Functions *736 ******************************************************************************/737 738 RTR3DECL(int) RTTarOpen(PRTTAR phTar, const char *pszTarname, uint32_t fMode, bool fStream)739 {740 AssertReturn(!fStream || !(fMode & RTFILE_O_WRITE), VERR_INVALID_PARAMETER);741 742 /*743 * Create a tar instance.744 */745 PRTTARINTERNAL pThis = (PRTTARINTERNAL)RTMemAllocZ(sizeof(RTTARINTERNAL));746 if (!pThis)747 return VERR_NO_MEMORY;748 749 pThis->u32Magic = RTTAR_MAGIC;750 pThis->fOpenMode = fMode;751 pThis->fStreamMode = fStream && (fMode & RTFILE_O_READ);752 753 /*754 * Open the tar file.755 */756 int rc;757 #ifdef RT_USE_TAR_VFS_FOR_ALL_READS758 pThis->hVfsFile = NIL_RTVFSFILE;759 pThis->hVfsFss = NIL_RTVFSFSSTREAM;760 pThis->fFssAtStart = false;761 pThis->hVfsCur = NIL_RTVFSIOSTREAM;762 pThis->pszVfsCurName = NULL;763 764 if (!(fMode & RTFILE_O_WRITE))765 {766 rc = RTVfsFileOpenNormal(pszTarname, fMode, &pThis->hVfsFile);767 if (RT_SUCCESS(rc))768 {769 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(pThis->hVfsFile);770 rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0 /*fFlags*/, &pThis->hVfsFss);771 if (RT_SUCCESS(rc))772 pThis->fFssAtStart = true;773 else774 {775 RTVfsFileRelease(pThis->hVfsFile);776 pThis->hVfsFile = NIL_RTVFSFILE;777 }778 RTVfsIoStrmRelease(hVfsIos);779 }780 }781 else782 #endif783 rc = RTFileOpen(&pThis->hTarFile, pszTarname, fMode);784 if (RT_SUCCESS(rc))785 {786 *phTar = pThis;787 return VINF_SUCCESS;788 }789 790 RTMemFree(pThis);791 return rc;792 }793 794 RTR3DECL(int) RTTarClose(RTTAR hTar)795 {796 if (hTar == NIL_RTTAR)797 return VINF_SUCCESS;798 799 PRTTARINTERNAL pInt = hTar;800 RTTAR_VALID_RETURN(pInt);801 802 int rc = VINF_SUCCESS;803 804 /* gtar gives a warning, but the documentation says EOF is indicated by a805 * zero block. Disabled for now. */806 #if 0807 {808 /* Append the EOF record which is filled all by zeros */809 RTTARRECORD record;810 RT_ZERO(record);811 rc = RTFileWrite(pInt->hTarFile, &record, sizeof(record), NULL);812 }813 #endif814 815 #ifdef RT_USE_TAR_VFS_FOR_ALL_READS816 if (pInt->hVfsFss != NIL_RTVFSFSSTREAM)817 {818 uint32_t cRefs = RTVfsFsStrmRelease(pInt->hVfsFss); Assert(cRefs != UINT32_MAX);819 pInt->hVfsFss = NIL_RTVFSFSSTREAM;820 }821 822 if (pInt->hVfsFile != NIL_RTVFSFILE)823 {824 uint32_t cRefs = RTVfsFileRelease(pInt->hVfsFile); Assert(cRefs != UINT32_MAX);825 pInt->hVfsFile = NIL_RTVFSFILE;826 }827 828 if (pInt->hVfsCur != NIL_RTVFSIOSTREAM)829 {830 RTVfsIoStrmRelease(pInt->hVfsCur);831 pInt->hVfsCur = NIL_RTVFSIOSTREAM;832 }833 834 if (pInt->pszVfsCurName)835 {836 RTStrFree(pInt->pszVfsCurName);837 pInt->pszVfsCurName = NULL;838 }839 #endif /* RT_USE_TAR_VFS_FOR_ALL_READS */840 841 if (pInt->hTarFile != NIL_RTFILE)842 {843 rc = RTFileClose(pInt->hTarFile);844 pInt->hTarFile = NIL_RTFILE;845 }846 847 #ifndef RT_USE_TAR_VFS_FOR_ALL_READS848 /* Delete any remaining cached file headers. */849 if (pInt->pFileCache)850 {851 rtDeleteTarFileInternal(pInt->pFileCache);852 pInt->pFileCache = NULL;853 }854 #endif855 856 pInt->u32Magic = RTTAR_MAGIC_DEAD;857 858 RTMemFree(pInt);859 860 return rc;861 }862 863 RTR3DECL(int) RTTarFileOpen(RTTAR hTar, PRTTARFILE phFile, const char *pszFilename, uint32_t fOpen)864 {865 AssertReturn((fOpen & RTFILE_O_READ) || (fOpen & RTFILE_O_WRITE), VERR_INVALID_PARAMETER);866 867 PRTTARINTERNAL pInt = hTar;868 RTTAR_VALID_RETURN(pInt);869 870 if (!pInt->hTarFile)871 return VERR_INVALID_HANDLE;872 873 if (pInt->fStreamMode)874 return VERR_INVALID_STATE;875 876 if (fOpen & RTFILE_O_WRITE)877 {878 if (!(pInt->fOpenMode & RTFILE_O_WRITE))879 return VERR_WRITE_PROTECT;880 if (pInt->fFileOpenForWrite)881 return VERR_TOO_MANY_OPEN_FILES;882 }883 884 int rc = VINF_SUCCESS;885 #ifdef RT_USE_TAR_VFS_FOR_ALL_READS886 if (!(fOpen & RTFILE_O_WRITE))887 {888 /*889 * Rewind the stream if necessary.890 */891 if (!pInt->fFssAtStart)892 {893 if (pInt->hVfsFss != NIL_RTVFSFSSTREAM)894 {895 uint32_t cRefs = RTVfsFsStrmRelease(pInt->hVfsFss); Assert(cRefs != UINT32_MAX);896 pInt->hVfsFss = NIL_RTVFSFSSTREAM;897 }898 899 if (pInt->hVfsFile == NIL_RTVFSFILE)900 {901 rc = RTVfsFileFromRTFile(pInt->hTarFile, RTFILE_O_READ, true /*fLeaveOpen*/, &pInt->hVfsFile);902 if (RT_FAILURE(rc))903 return rc;904 }905 Assert(pInt->hVfsCur == NIL_RTVFSIOSTREAM && pInt->pszVfsCurName == NULL);906 907 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(pInt->hVfsFile);908 rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0 /*fFlags*/, &pInt->hVfsFss);909 RTVfsIoStrmRelease(hVfsIos);910 if (RT_FAILURE(rc))911 return rc;912 }913 914 /*915 * Search the file system stream.916 */917 pInt->fFssAtStart = false;918 for (;;)919 {920 char *pszName;921 RTVFSOBJTYPE enmType;922 RTVFSOBJ hVfsObj;923 rc = RTVfsFsStrmNext(pInt->hVfsFss, &pszName, &enmType, &hVfsObj);924 if (rc == VERR_EOF)925 return VERR_FILE_NOT_FOUND;926 if (RT_FAILURE(rc))927 return rc;928 929 if (!RTStrCmp(pszName, pszFilename))930 {931 if (enmType == RTVFSOBJTYPE_FILE || enmType == RTVFSOBJTYPE_IO_STREAM)932 rc = rtTarFileCreateHandleForReadOnly(pszName, RTVfsObjToIoStream(hVfsObj), fOpen, phFile);933 else934 {935 rc = VERR_UNEXPECTED_FS_OBJ_TYPE;936 RTStrFree(pszName);937 }938 RTVfsObjRelease(hVfsObj);939 break;940 }941 RTStrFree(pszName);942 RTVfsObjRelease(hVfsObj);943 } /* Search loop. */944 }945 else946 #endif /* RT_USE_TAR_VFS_FOR_ALL_READS */947 {948 PRTTARFILEINTERNAL pFileInt = rtCreateTarFileInternal(pInt, pszFilename, fOpen);949 if (!pFileInt)950 return VERR_NO_MEMORY;951 952 do /* break loop */953 {954 #ifndef RT_USE_TAR_VFS_FOR_ALL_READS955 if (pFileInt->fOpenMode & RTFILE_O_WRITE)956 #endif957 {958 pInt->fFileOpenForWrite = true;959 960 /* If we are in write mode, we also in append mode. Add an dummy961 * header at the end of the current file. It will be filled by the962 * close operation. */963 rc = RTFileSeek(pFileInt->pTar->hTarFile, 0, RTFILE_SEEK_END, &pFileInt->offStart);964 if (RT_FAILURE(rc))965 break;966 RTTARRECORD record;967 RT_ZERO(record);968 rc = RTFileWrite(pFileInt->pTar->hTarFile, &record, sizeof(RTTARRECORD), NULL);969 if (RT_FAILURE(rc))970 break;971 }972 #ifndef RT_USE_TAR_VFS_FOR_ALL_READS973 else974 {975 Assert(pFileInt->fOpenMode & RTFILE_O_READ); /* see first assertion */976 977 /* We need to be on the start of the file */978 rc = RTFileSeek(pFileInt->pTar->hTarFile, 0, RTFILE_SEEK_BEGIN, NULL);979 if (RT_FAILURE(rc))980 break;981 982 /* Search for the file. */983 rc = rtTarFindFile(pFileInt->pTar->hTarFile, pszFilename, &pFileInt->offStart, &pFileInt->cbSize);984 if (RT_FAILURE(rc))985 break;986 }987 #endif988 } while (0);989 990 /* Cleanup on failure */991 if (RT_FAILURE(rc))992 {993 if (pFileInt->pszFilename)994 RTStrFree(pFileInt->pszFilename);995 RTMemFree(pFileInt);996 }997 else998 *phFile = (RTTARFILE)pFileInt;999 }1000 1001 return rc;1002 }1003 808 1004 809 RTR3DECL(int) RTTarFileClose(RTTARFILE hFile) … … 1013 818 int rc = VINF_SUCCESS; 1014 819 1015 /* In read mode: */ 1016 if (pFileInt->fOpenMode & RTFILE_O_READ) 1017 { 1018 #ifndef RT_USE_TAR_VFS_FOR_ALL_READS 1019 /* In read mode, we want to make sure to stay at the aligned end of this 1020 * file, so the next file could be read immediately. */ 1021 uint64_t offCur = RTFileTell(pFileInt->pTar->hTarFile); 1022 1023 /* Check that the file pointer is somewhere within the last open file. 1024 * If we are at the beginning (nothing read yet) nothing will be done. 1025 * A user could open/close a file more than once, without reading 1026 * something. */ 1027 if ( pFileInt->offStart + sizeof(RTTARRECORD) < offCur 1028 && offCur < RT_ALIGN(pFileInt->offStart + sizeof(RTTARRECORD) + pFileInt->cbSize, sizeof(RTTARRECORD))) 1029 { 1030 /* Seek to the next file header. */ 1031 uint64_t offNext = RT_ALIGN(pFileInt->offStart + sizeof(RTTARRECORD) + pFileInt->cbSize, sizeof(RTTARRECORD)); 1032 rc = RTFileSeek(pFileInt->pTar->hTarFile, offNext - offCur, RTFILE_SEEK_CURRENT, NULL); 1033 } 1034 #endif 1035 } 1036 else if (pFileInt->fOpenMode & RTFILE_O_WRITE) 820 /* In write mode: */ 821 if ((pFileInt->fOpenMode & (RTFILE_O_WRITE | RTFILE_O_READ)) == RTFILE_O_WRITE) 1037 822 { 1038 823 pFileInt->pTar->fFileOpenForWrite = false; … … 1043 828 if (pFileInt->cbSetSize > pFileInt->cbSize) 1044 829 { 1045 rc = rtTarAppendZeros( hFile, pFileInt->cbSetSize - pFileInt->cbSize);830 rc = rtTarAppendZeros(pFileInt, pFileInt->cbSetSize - pFileInt->cbSize); 1046 831 if (RT_FAILURE(rc)) 1047 832 break; … … 1081 866 } 1082 867 1083 /* Now cleanup and delete the handle */ 1084 rtDeleteTarFileInternal(pFileInt); 868 /* 869 * Now cleanup and delete the handle. 870 */ 871 if (pFileInt->pszFilename) 872 RTStrFree(pFileInt->pszFilename); 873 if (pFileInt->hVfsIos != NIL_RTVFSIOSTREAM) 874 { 875 RTVfsIoStrmRelease(pFileInt->hVfsIos); 876 pFileInt->hVfsIos = NIL_RTVFSIOSTREAM; 877 } 878 pFileInt->u32Magic = RTTARFILE_MAGIC_DEAD; 879 RTMemFree(pFileInt); 1085 880 1086 881 return rc; 1087 882 } 1088 883 1089 #ifndef RT_USE_TAR_VFS_FOR_ALL_READS 1090 RTR3DECL(int) RTTarFile Seek(RTTARFILE hFile, uint64_t offSeek, unsigned uMethod, uint64_t *poffActual)884 885 RTR3DECL(int) RTTarFileReadAt(RTTARFILE hFile, uint64_t off, void *pvBuf, size_t cbToRead, size_t *pcbRead) 1091 886 { 1092 887 PRTTARFILEINTERNAL pFileInt = hFile; 1093 888 RTTARFILE_VALID_RETURN(pFileInt); 1094 1095 if (pFileInt->pTar->fStreamMode)1096 return VERR_INVALID_STATE;1097 1098 switch (uMethod)1099 {1100 case RTFILE_SEEK_BEGIN:1101 {1102 if (offSeek > pFileInt->cbSize)1103 return VERR_SEEK_ON_DEVICE;1104 pFileInt->offCurrent = offSeek;1105 break;1106 }1107 case RTFILE_SEEK_CURRENT:1108 {1109 if (pFileInt->offCurrent + offSeek > pFileInt->cbSize)1110 return VERR_SEEK_ON_DEVICE;1111 pFileInt->offCurrent += offSeek;1112 break;1113 }1114 case RTFILE_SEEK_END:1115 {1116 if ((int64_t)pFileInt->cbSize - (int64_t)offSeek < 0)1117 return VERR_NEGATIVE_SEEK;1118 pFileInt->offCurrent = pFileInt->cbSize - offSeek;1119 break;1120 }1121 default: AssertFailedReturn(VERR_INVALID_PARAMETER);1122 }1123 1124 if (poffActual)1125 *poffActual = pFileInt->offCurrent;1126 1127 return VINF_SUCCESS;1128 }1129 #endif /* !RT_USE_TAR_VFS_FOR_ALL_READS */1130 1131 1132 #ifndef RT_USE_TAR_VFS_FOR_ALL_READS1133 RTR3DECL(uint64_t) RTTarFileTell(RTTARFILE hFile)1134 {1135 PRTTARFILEINTERNAL pFileInt = hFile;1136 RTTARFILE_VALID_RETURN_RC(pFileInt, UINT64_MAX);1137 1138 return pFileInt->offCurrent;1139 }1140 #endif /* !RT_USE_TAR_VFS_FOR_ALL_READS */1141 1142 RTR3DECL(int) RTTarFileRead(RTTARFILE hFile, void *pvBuf, size_t cbToRead, size_t *pcbRead)1143 {1144 PRTTARFILEINTERNAL pFileInt = hFile;1145 RTTARFILE_VALID_RETURN(pFileInt);1146 1147 return RTTarFileReadAt(hFile, pFileInt->offCurrent, pvBuf, cbToRead, pcbRead);1148 }1149 1150 RTR3DECL(int) RTTarFileReadAt(RTTARFILE hFile, uint64_t off, void *pvBuf, size_t cbToRead, size_t *pcbRead)1151 {1152 PRTTARFILEINTERNAL pFileInt = hFile;1153 RTTARFILE_VALID_RETURN(pFileInt);1154 1155 #ifdef RT_USE_TAR_VFS_FOR_ALL_READS1156 889 1157 890 size_t cbTmpRead = 0; … … 1167 900 else if (pcbRead) 1168 901 *pcbRead = 0; 1169 #else1170 1171 /* Check that we not read behind the end of file. If so return immediately. */1172 if (off > pFileInt->cbSize)1173 {1174 if (pcbRead)1175 *pcbRead = 0;1176 return VINF_SUCCESS; /* ??? VERR_EOF */1177 }1178 1179 size_t cbToCopy = RT_MIN(pFileInt->cbSize - off, cbToRead);1180 size_t cbTmpRead = 0;1181 int rc = RTFileReadAt(pFileInt->pTar->hTarFile, pFileInt->offStart + 512 + off, pvBuf, cbToCopy, &cbTmpRead);1182 pFileInt->offCurrent = off + cbTmpRead;1183 if (pcbRead)1184 *pcbRead = cbTmpRead;1185 1186 #endif /* !RT_USE_TAR_VFS_FOR_ALL_READS */1187 1188 902 return rc; 1189 903 } 1190 904 1191 RTR3DECL(int) RTTarFileWrite(RTTARFILE hFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)1192 {1193 PRTTARFILEINTERNAL pFileInt = hFile;1194 RTTARFILE_VALID_RETURN(pFileInt);1195 1196 /** @todo Optimize this, by checking the current pos */1197 return RTTarFileWriteAt(hFile, pFileInt->offCurrent, pvBuf, cbToWrite, pcbWritten);1198 }1199 905 1200 906 RTR3DECL(int) RTTarFileWriteAt(RTTARFILE hFile, uint64_t off, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten) … … 1216 922 } 1217 923 924 1218 925 RTR3DECL(int) RTTarFileGetSize(RTTARFILE hFile, uint64_t *pcbSize) 1219 926 { … … 1228 935 return VINF_SUCCESS; 1229 936 } 937 1230 938 1231 939 RTR3DECL(int) RTTarFileSetSize(RTTARFILE hFile, uint64_t cbSize) … … 1244 952 } 1245 953 1246 RTR3DECL(int) RTTarFileSetMode(RTTARFILE hFile, uint32_t fMode)1247 {1248 PRTTARFILEINTERNAL pFileInt = hFile;1249 RTTARFILE_VALID_RETURN(pFileInt);1250 1251 if ((pFileInt->fOpenMode & RTFILE_O_WRITE) != RTFILE_O_WRITE)1252 return VERR_WRITE_ERROR;1253 1254 /* Convert the mode to an string. */1255 char szMode[RT_SIZEOFMEMB(RTTARRECORD, h.mode)];1256 RTStrPrintf(szMode, sizeof(szMode), "%0.7o", fMode);1257 1258 /* Write it directly into the header */1259 return RTFileWriteAt(pFileInt->pTar->hTarFile,1260 pFileInt->offStart + RT_OFFSETOF(RTTARRECORD, h.mode),1261 szMode,1262 RT_SIZEOFMEMB(RTTARRECORD, h.mode),1263 NULL);1264 }1265 1266 RTR3DECL(int) RTTarFileSetTime(RTTARFILE hFile, PRTTIMESPEC pTime)1267 {1268 PRTTARFILEINTERNAL pFileInt = hFile;1269 RTTARFILE_VALID_RETURN(pFileInt);1270 1271 if ((pFileInt->fOpenMode & RTFILE_O_WRITE) != RTFILE_O_WRITE)1272 return VERR_WRITE_ERROR;1273 1274 /* Convert the time to an string. */1275 char szModTime[RT_SIZEOFMEMB(RTTARRECORD, h.mtime)];1276 RTStrPrintf(szModTime, sizeof(szModTime), "%0.11llo", RTTimeSpecGetSeconds(pTime));1277 1278 /* Write it directly into the header */1279 return RTFileWriteAt(pFileInt->pTar->hTarFile,1280 pFileInt->offStart + RT_OFFSETOF(RTTARRECORD, h.mtime),1281 szModTime,1282 RT_SIZEOFMEMB(RTTARRECORD, h.mtime),1283 NULL);1284 }1285 1286 RTR3DECL(int) RTTarFileSetOwner(RTTARFILE hFile, uint32_t uid, uint32_t gid)1287 {1288 PRTTARFILEINTERNAL pFileInt = hFile;1289 RTTARFILE_VALID_RETURN(pFileInt);1290 1291 if ((pFileInt->fOpenMode & RTFILE_O_WRITE) != RTFILE_O_WRITE)1292 return VERR_WRITE_ERROR;1293 AssertReturn(uid == (uint32_t)-1 || uid <= 07777777, VERR_OUT_OF_RANGE);1294 AssertReturn(gid == (uint32_t)-1 || gid <= 07777777, VERR_OUT_OF_RANGE);1295 1296 int rc = VINF_SUCCESS;1297 1298 if (uid != (uint32_t)-1)1299 {1300 /* Convert the uid to an string. */1301 char szUid[RT_SIZEOFMEMB(RTTARRECORD, h.uid)];1302 RTStrPrintf(szUid, sizeof(szUid), "%0.7o", uid);1303 1304 /* Write it directly into the header */1305 rc = RTFileWriteAt(pFileInt->pTar->hTarFile,1306 pFileInt->offStart + RT_OFFSETOF(RTTARRECORD, h.uid),1307 szUid,1308 RT_SIZEOFMEMB(RTTARRECORD, h.uid),1309 NULL);1310 if (RT_FAILURE(rc))1311 return rc;1312 }1313 1314 if (gid != (uint32_t)-1)1315 {1316 /* Convert the gid to an string. */1317 char szGid[RT_SIZEOFMEMB(RTTARRECORD, h.gid)];1318 RTStrPrintf(szGid, sizeof(szGid), "%0.7o", gid);1319 1320 /* Write it directly into the header */1321 rc = RTFileWriteAt(pFileInt->pTar->hTarFile,1322 pFileInt->offStart + RT_OFFSETOF(RTTARRECORD, h.gid),1323 szGid,1324 RT_SIZEOFMEMB(RTTARRECORD, h.gid),1325 NULL);1326 if (RT_FAILURE(rc))1327 return rc;1328 }1329 1330 return rc;1331 }1332 1333 /******************************************************************************1334 * Convenience Functions *1335 ******************************************************************************/1336 1337 RTR3DECL(int) RTTarList(const char *pszTarFile, char ***ppapszFiles, size_t *pcFiles)1338 {1339 /* Validate input */1340 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);1341 AssertPtrReturn(ppapszFiles, VERR_INVALID_POINTER);1342 AssertPtrReturn(pcFiles, VERR_INVALID_POINTER);1343 1344 /* Open the tar file */1345 RTTAR hTar;1346 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, false /*fStream*/);1347 if (RT_FAILURE(rc))1348 return rc;1349 1350 #ifdef RT_USE_TAR_VFS_FOR_ALL_READS1351 /*1352 * Enumerate the VFS file system stream.1353 */1354 size_t cFiles = 0;1355 size_t cFilesAllocated = 0;1356 char **papszFiles = NULL;1357 for (;;)1358 {1359 char *pszName;1360 RTVFSOBJTYPE enmType;1361 RTVFSOBJ hVfsObj;1362 rc = RTVfsFsStrmNext(hTar->hVfsFss, &pszName, &enmType, &hVfsObj);1363 if (rc == VERR_EOF)1364 {1365 RTTarClose(hTar);1366 *pcFiles = cFiles;1367 *ppapszFiles = papszFiles;1368 return VINF_SUCCESS;1369 }1370 if (RT_FAILURE(rc))1371 break;1372 1373 if (cFiles >= cFilesAllocated)1374 {1375 size_t cNew = !cFilesAllocated ? 64 : cFilesAllocated < _1M ? cFilesAllocated * 2 : cFilesAllocated + _1M;1376 void *pvNew = RTMemRealloc(papszFiles, cNew * sizeof(char *));1377 if (!pvNew)1378 {1379 rc = VERR_NO_MEMORY;1380 RTStrFree(pszName);1381 RTVfsObjRelease(hVfsObj);1382 break;1383 }1384 cFilesAllocated = cNew;1385 papszFiles = (char **)pvNew;1386 }1387 1388 papszFiles[cFiles++] = pszName;1389 1390 RTVfsObjRelease(hVfsObj);1391 } /* Search loop. */1392 1393 /*1394 * Failed, clean up and return.1395 */1396 if (papszFiles)1397 {1398 while (cFiles-- > 0)1399 RTStrFree(papszFiles[cFiles]);1400 RTMemFree(papszFiles);1401 }1402 1403 #else /* !RT_USE_TAR_VFS_FOR_ALL_READS */1404 1405 /* This is done by internal methods, cause we didn't have a RTTARDIR1406 * interface, yet. This should be fixed someday. */1407 1408 PRTTARINTERNAL pInt = hTar;1409 char **papszFiles = NULL;1410 size_t cFiles = 0;1411 do /* break loop */1412 {1413 /* Initialize the file name array with one slot */1414 size_t cFilesAlloc = 1;1415 papszFiles = (char **)RTMemAlloc(sizeof(char *));1416 if (!papszFiles)1417 {1418 rc = VERR_NO_MEMORY;1419 break;1420 }1421 1422 /* Iterate through the tar file record by record. Skip data records as we1423 * didn't need them. */1424 RTTARRECORD record;1425 for (;;)1426 {1427 /* Read & verify a header record */1428 rc = rtTarReadHeaderRecord(pInt->hTarFile, &record);1429 /* Check for error or EOF. */1430 if (RT_FAILURE(rc))1431 break;1432 /* We support normal files only */1433 if ( record.h.linkflag == LF_OLDNORMAL1434 || record.h.linkflag == LF_NORMAL)1435 {1436 if (cFiles >= cFilesAlloc)1437 {1438 /* Double the array size, make sure the size doesn't wrap. */1439 void *pvNew = NULL;1440 size_t cbNew = cFilesAlloc * sizeof(char *) * 2;1441 if (cbNew / sizeof(char *) / 2 == cFilesAlloc)1442 pvNew = RTMemRealloc(papszFiles, cbNew);1443 if (!pvNew)1444 {1445 rc = VERR_NO_MEMORY;1446 break;1447 }1448 papszFiles = (char **)pvNew;1449 cFilesAlloc *= 2;1450 }1451 1452 /* Duplicate the name */1453 papszFiles[cFiles] = RTStrDup(record.h.name);1454 if (!papszFiles[cFiles])1455 {1456 rc = VERR_NO_MEMORY;1457 break;1458 }1459 cFiles++;1460 }1461 rc = rtTarSkipData(pInt->hTarFile, &record);1462 if (RT_FAILURE(rc))1463 break;1464 }1465 } while (0);1466 1467 if (rc == VERR_TAR_END_OF_FILE)1468 rc = VINF_SUCCESS;1469 1470 /* Return the file array on success, dispose of it on failure. */1471 if (RT_SUCCESS(rc))1472 {1473 *pcFiles = cFiles;1474 *ppapszFiles = papszFiles;1475 }1476 else1477 {1478 while (cFiles-- > 0)1479 RTStrFree(papszFiles[cFiles]);1480 RTMemFree(papszFiles);1481 }1482 #endif /* !RT_USE_TAR_VFS_FOR_ALL_READS */1483 1484 RTTarClose(hTar);1485 1486 return rc;1487 }1488 1489 RTR3DECL(int) RTTarCreate(const char *pszTarFile, const char * const *papszFiles, size_t cFiles, PFNRTPROGRESS pfnProgressCallback, void *pvUser)1490 {1491 /* Validate input */1492 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);1493 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER);1494 AssertReturn(cFiles, VERR_INVALID_PARAMETER);1495 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_POINTER);1496 AssertPtrNullReturn(pvUser, VERR_INVALID_POINTER);1497 1498 RTTAR hTar;1499 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_CREATE | RTFILE_O_READWRITE | RTFILE_O_DENY_NONE, false /*fStream*/);1500 if (RT_FAILURE(rc))1501 return rc;1502 1503 /* Get the overall size of all files to pack into the tar archive. Only1504 necessary if there is a progress callback. */1505 uint64_t cbOverallSize = 0;1506 if (pfnProgressCallback)1507 for (size_t i = 0; i < cFiles; ++i)1508 {1509 uint64_t cbSize;1510 rc = RTFileQuerySize(papszFiles[i], &cbSize);1511 if (RT_FAILURE(rc))1512 break;1513 cbOverallSize += cbSize;1514 }1515 uint64_t cbOverallWritten = 0;1516 for (size_t i = 0; i < cFiles; ++i)1517 {1518 rc = rtTarAppendFileFromFile(hTar, papszFiles[i], cbOverallSize, cbOverallWritten, pfnProgressCallback, pvUser);1519 if (RT_FAILURE(rc))1520 break;1521 }1522 1523 /* Cleanup */1524 RTTarClose(hTar);1525 1526 return rc;1527 }1528 1529 /******************************************************************************1530 * Streaming Functions *1531 ******************************************************************************/1532 1533 RTR3DECL(int) RTTarCurrentFile(RTTAR hTar, char **ppszFilename)1534 {1535 /* Validate input. */1536 AssertPtrNullReturn(ppszFilename, VERR_INVALID_POINTER);1537 1538 PRTTARINTERNAL pInt = hTar;1539 RTTAR_VALID_RETURN(pInt);1540 1541 #ifdef RT_USE_TAR_VFS_FOR_ALL_READS1542 if (!pInt->fStreamMode)1543 return VERR_INVALID_STATE;1544 1545 if (!pInt->pszVfsCurName)1546 {1547 int rc = RTTarSeekNextFile(pInt);1548 if (RT_FAILURE(rc))1549 return rc;1550 }1551 Assert(pInt->pszVfsCurName);1552 1553 if (ppszFilename)1554 {1555 *ppszFilename = RTStrDup(pInt->pszVfsCurName);1556 if (!*ppszFilename)1557 return VERR_NO_STR_MEMORY;1558 }1559 1560 return pInt->hVfsCur != NIL_RTVFSIOSTREAM ? VINF_SUCCESS : VINF_TAR_DIR_PATH;1561 1562 #else /* !RT_USE_TAR_VFS_FOR_ALL_READS */1563 /* Open and close the file on the current position. This makes sure the1564 * cache is filled in case we never read something before. On success it1565 * will return the current filename. */1566 RTTARFILE hFile;1567 int rc = RTTarFileOpenCurrentFile(hTar, &hFile, ppszFilename, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);1568 if (RT_SUCCESS(rc))1569 RTTarFileClose(hFile);1570 1571 return rc;1572 #endif /* !RT_USE_TAR_VFS_FOR_ALL_READS */1573 }1574 1575 RTR3DECL(int) RTTarSeekNextFile(RTTAR hTar)1576 {1577 PRTTARINTERNAL pInt = hTar;1578 RTTAR_VALID_RETURN(pInt);1579 1580 if (!pInt->fStreamMode)1581 return VERR_INVALID_STATE;1582 1583 #ifdef RT_USE_TAR_VFS_FOR_ALL_READS1584 /*1585 * Release the current object.1586 */1587 if (pInt->hVfsCur != NIL_RTVFSIOSTREAM)1588 {1589 RTVfsIoStrmRelease(pInt->hVfsCur);1590 pInt->hVfsCur = NIL_RTVFSIOSTREAM;1591 }1592 1593 if (pInt->pszVfsCurName)1594 {1595 RTStrFree(pInt->pszVfsCurName);1596 pInt->pszVfsCurName = NULL;1597 }1598 1599 /*1600 * Find the next file.1601 */1602 for (;;)1603 {1604 char *pszName;1605 RTVFSOBJTYPE enmType;1606 RTVFSOBJ hVfsObj;1607 int rc = RTVfsFsStrmNext(hTar->hVfsFss, &pszName, &enmType, &hVfsObj);1608 if (rc == VERR_EOF)1609 return VERR_TAR_END_OF_FILE;1610 1611 if ( enmType == RTVFSOBJTYPE_FILE1612 || enmType == RTVFSOBJTYPE_IO_STREAM1613 || enmType == RTVFSOBJTYPE_DIR)1614 {1615 pInt->pszVfsCurName = pszName;1616 if (enmType == RTVFSOBJTYPE_DIR)1617 rc = VINF_TAR_DIR_PATH;1618 else1619 {1620 pInt->hVfsCur = RTVfsObjToIoStream(hVfsObj);1621 Assert(pInt->hVfsCur != NIL_RTVFSIOSTREAM);1622 rc = VINF_SUCCESS;1623 }1624 RTVfsObjRelease(hVfsObj);1625 return rc;1626 }1627 RTStrFree(pszName);1628 RTVfsObjRelease(hVfsObj);1629 }1630 1631 #else /* !RT_USE_TAR_VFS_FOR_ALL_READS */1632 int rc = VINF_SUCCESS;1633 1634 /* If there is nothing in the cache, it means we never read something. Just1635 * ask for the current filename to fill the cache. */1636 if (!pInt->pFileCache)1637 {1638 rc = RTTarCurrentFile(hTar, NULL);1639 if (RT_FAILURE(rc))1640 return rc;1641 }1642 1643 /* Check that the file pointer is somewhere within the last open file.1644 * If not we are somehow busted. */1645 uint64_t offCur = RTFileTell(pInt->hTarFile);1646 if (!( pInt->pFileCache->offStart <= offCur1647 && offCur <= pInt->pFileCache->offStart + sizeof(RTTARRECORD) + pInt->pFileCache->cbSize))1648 return VERR_INVALID_STATE;1649 1650 /* Seek to the next file header. */1651 uint64_t offNext = RT_ALIGN(pInt->pFileCache->offStart + sizeof(RTTARRECORD) + pInt->pFileCache->cbSize, sizeof(RTTARRECORD));1652 if (pInt->pFileCache->cbSize != 0)1653 {1654 rc = RTFileSeek(pInt->hTarFile, offNext - offCur, RTFILE_SEEK_CURRENT, NULL);1655 if (RT_FAILURE(rc))1656 return rc;1657 }1658 else1659 {1660 /* Else delete the last open file cache. Might be recreated below. */1661 rtDeleteTarFileInternal(pInt->pFileCache);1662 pInt->pFileCache = NULL;1663 }1664 1665 /* Again check the current filename to fill the cache with the new value. */1666 return RTTarCurrentFile(hTar, NULL);1667 #endif /* !RT_USE_TAR_VFS_FOR_ALL_READS */1668 }1669 1670 RTR3DECL(int) RTTarFileOpenCurrentFile(RTTAR hTar, PRTTARFILE phFile, char **ppszFilename, uint32_t fOpen)1671 {1672 /* Validate input. */1673 AssertPtrReturn(phFile, VERR_INVALID_POINTER);1674 AssertPtrNullReturn(ppszFilename, VERR_INVALID_POINTER);1675 AssertReturn((fOpen & RTFILE_O_READ), VERR_INVALID_PARAMETER); /* Only valid in read mode. */1676 1677 PRTTARINTERNAL pInt = hTar;1678 RTTAR_VALID_RETURN(pInt);1679 1680 if (!pInt->fStreamMode)1681 return VERR_INVALID_STATE;1682 1683 #ifdef RT_USE_TAR_VFS_FOR_ALL_READS1684 /*1685 * Make sure there is a current file (first call w/o RTTarSeekNextFile call).1686 */1687 if (pInt->hVfsCur == NIL_RTVFSIOSTREAM)1688 {1689 if (pInt->pszVfsCurName)1690 return -VINF_TAR_DIR_PATH;1691 1692 int rc = RTTarSeekNextFile(pInt);1693 if (RT_FAILURE(rc))1694 return rc;1695 1696 if (pInt->hVfsCur == NIL_RTVFSIOSTREAM)1697 return -VINF_TAR_DIR_PATH;1698 }1699 Assert(pInt->pszVfsCurName);1700 1701 /*1702 * Return a copy of the filename if requested.1703 */1704 if (ppszFilename)1705 {1706 *ppszFilename = RTStrDup(pInt->pszVfsCurName);1707 if (!*ppszFilename)1708 return VERR_NO_STR_MEMORY;1709 }1710 1711 /*1712 * Create a handle for it.1713 */1714 int rc = rtTarFileCreateHandleForReadOnly(RTStrDup(pInt->pszVfsCurName), pInt->hVfsCur, RTFILE_O_READ, phFile);1715 if (RT_FAILURE(rc) && ppszFilename)1716 {1717 RTStrFree(*ppszFilename);1718 *ppszFilename = NULL;1719 }1720 1721 #else /* !RT_USE_TAR_VFS_FOR_ALL_READS */1722 1723 int rc = VINF_SUCCESS;1724 1725 /* Is there some cached entry? */1726 if (pInt->pFileCache)1727 {1728 if (pInt->pFileCache->offStart + sizeof(RTTARRECORD) < RTFileTell(pInt->hTarFile))1729 {1730 /* Else delete the last open file cache. Might be recreated below. */1731 rtDeleteTarFileInternal(pInt->pFileCache);1732 pInt->pFileCache = NULL;1733 }1734 else/* Are we still directly behind that header? */1735 {1736 /* Yes, so the streaming can start. Just return the cached file1737 * structure to the caller. */1738 *phFile = rtCopyTarFileInternal(pInt->pFileCache);1739 if (ppszFilename)1740 *ppszFilename = RTStrDup(pInt->pFileCache->pszFilename);1741 if (pInt->pFileCache->linkflag == LF_DIR)1742 return VINF_TAR_DIR_PATH;1743 return VINF_SUCCESS;1744 }1745 1746 }1747 1748 PRTTARFILEINTERNAL pFileInt = NULL;1749 do /* break loop */1750 {1751 /* Try to read a header entry from the current position. If we aren't1752 * on a header record, the header checksum will show and an error will1753 * be returned. */1754 RTTARRECORD record;1755 /* Read & verify a header record */1756 rc = rtTarReadHeaderRecord(pInt->hTarFile, &record);1757 /* Check for error or EOF. */1758 if (RT_FAILURE(rc))1759 break;1760 1761 /* We support normal files only */1762 if ( record.h.linkflag == LF_OLDNORMAL1763 || record.h.linkflag == LF_NORMAL1764 || record.h.linkflag == LF_DIR)1765 {1766 pFileInt = rtCreateTarFileInternal(pInt, record.h.name, fOpen);1767 if (!pFileInt)1768 {1769 rc = VERR_NO_MEMORY;1770 break;1771 }1772 1773 /* Get the file size */1774 pFileInt->cbSize = rtTarRecToSize(&record);1775 /* The start is -512 from here. */1776 pFileInt->offStart = RTFileTell(pInt->hTarFile) - sizeof(RTTARRECORD);1777 /* remember the type of a file */1778 pFileInt->linkflag = record.h.linkflag;1779 1780 /* Copy the new file structure to our cache. */1781 pInt->pFileCache = rtCopyTarFileInternal(pFileInt);1782 if (ppszFilename)1783 *ppszFilename = RTStrDup(pFileInt->pszFilename);1784 1785 if (pFileInt->linkflag == LF_DIR)1786 rc = VINF_TAR_DIR_PATH;1787 }1788 } while (0);1789 1790 if (RT_FAILURE(rc))1791 {1792 if (pFileInt)1793 rtDeleteTarFileInternal(pFileInt);1794 }1795 else1796 *phFile = pFileInt;1797 1798 #endif /* !RT_USE_TAR_VFS_FOR_ALL_READS */1799 return rc;1800 }1801
Note:
See TracChangeset
for help on using the changeset viewer.