VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/zip/tar.cpp@ 46971

Last change on this file since 46971 was 46971, checked in by vboxsync, 11 years ago

pr6022. Support handling directories in the TAR has been added. Added several useful checks during import OVA package.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 54.8 KB
Line 
1/* $Id: tar.cpp 46971 2013-07-04 07:46:39Z vboxsync $ */
2/** @file
3 * IPRT - Tar archive I/O.
4 */
5
6/*
7 * Copyright (C) 2009-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/******************************************************************************
29 * Header Files *
30 ******************************************************************************/
31#include "internal/iprt.h"
32#include <iprt/tar.h>
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/err.h>
37#include <iprt/file.h>
38#include <iprt/mem.h>
39#include <iprt/path.h>
40#include <iprt/string.h>
41
42#include "internal/magics.h"
43
44
45/******************************************************************************
46 * Structures and Typedefs *
47 ******************************************************************************/
48
49/** @name RTTARRECORD::h::linkflag
50 * @{ */
51#define LF_OLDNORMAL '\0' /**< Normal disk file, Unix compatible */
52#define LF_NORMAL '0' /**< Normal disk file */
53#define LF_LINK '1' /**< Link to previously dumped file */
54#define LF_SYMLINK '2' /**< Symbolic link */
55#define LF_CHR '3' /**< Character special file */
56#define LF_BLK '4' /**< Block special file */
57#define LF_DIR '5' /**< Directory */
58#define LF_FIFO '6' /**< FIFO special file */
59#define LF_CONTIG '7' /**< Contiguous file */
60/** @} */
61
62/**
63 * A tar file header.
64 */
65typedef union RTTARRECORD
66{
67 char d[512];
68 struct h
69 {
70 char name[100];
71 char mode[8];
72 char uid[8];
73 char gid[8];
74 char size[12];
75 char mtime[12];
76 char chksum[8];
77 char linkflag;
78 char linkname[100];
79 char magic[8];
80 char uname[32];
81 char gname[32];
82 char devmajor[8];
83 char devminor[8];
84 } h;
85} RTTARRECORD;
86AssertCompileSize(RTTARRECORD, 512);
87AssertCompileMemberOffset(RTTARRECORD, h.size, 100+8*3);
88AssertCompileMemberSize(RTTARRECORD, h.name, RTTAR_NAME_MAX+1);
89/** Pointer to a tar file header. */
90typedef RTTARRECORD *PRTTARRECORD;
91
92/** Pointer to a tar file handle. */
93typedef struct RTTARFILEINTERNAL *PRTTARFILEINTERNAL;
94
95/**
96 * The internal data of a tar handle.
97 */
98typedef struct RTTARINTERNAL
99{
100 /** The magic (RTTAR_MAGIC). */
101 uint32_t u32Magic;
102 /** The handle to the tar file. */
103 RTFILE hTarFile;
104 /** The open mode for hTarFile. */
105 uint32_t fOpenMode;
106 /** Whether a file within the archive is currently open for writing.
107 * Only one can be open. */
108 bool fFileOpenForWrite;
109 /** Whether operating in stream mode. */
110 bool fStreamMode;
111 /** The file cache of one file. */
112 PRTTARFILEINTERNAL pFileCache;
113} RTTARINTERNAL;
114/** Pointer to a the internal data of a tar handle. */
115typedef RTTARINTERNAL* PRTTARINTERNAL;
116
117/**
118 * The internal data of a file within a tar file.
119 */
120typedef struct RTTARFILEINTERNAL
121{
122 /** The magic (RTTARFILE_MAGIC). */
123 uint32_t u32Magic;
124 /** Pointer to back to the tar file. */
125 PRTTARINTERNAL pTar;
126 /** The name of the file. */
127 char *pszFilename;
128 /** The offset into the archive where the file header starts. */
129 uint64_t offStart;
130 /** The size of the file. */
131 uint64_t cbSize;
132 /** The size set by RTTarFileSetSize(). */
133 uint64_t cbSetSize;
134 /** The current offset within this file. */
135 uint64_t offCurrent;
136 /** The open mode. */
137 uint32_t fOpenMode;
138 /** The link flag. */
139 char linkflag;
140} RTTARFILEINTERNAL;
141/** Pointer to the internal data of a tar file. */
142typedef RTTARFILEINTERNAL *PRTTARFILEINTERNAL;
143
144#if 0 /* not currently used */
145typedef struct RTTARFILELIST
146{
147 char *pszFilename;
148 RTTARFILELIST *pNext;
149} RTTARFILELIST;
150typedef RTTARFILELIST *PRTTARFILELIST;
151#endif
152
153
154
155/******************************************************************************
156 * Defined Constants And Macros *
157 ******************************************************************************/
158
159/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
160/* RTTAR */
161#define RTTAR_VALID_RETURN_RC(hHandle, rc) \
162 do { \
163 AssertPtrReturn((hHandle), (rc)); \
164 AssertReturn((hHandle)->u32Magic == RTTAR_MAGIC, (rc)); \
165 } while (0)
166/* RTTARFILE */
167#define RTTARFILE_VALID_RETURN_RC(hHandle, rc) \
168 do { \
169 AssertPtrReturn((hHandle), (rc)); \
170 AssertReturn((hHandle)->u32Magic == RTTARFILE_MAGIC, (rc)); \
171 } while (0)
172
173/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
174/* RTTAR */
175#define RTTAR_VALID_RETURN(hHandle) RTTAR_VALID_RETURN_RC((hHandle), VERR_INVALID_HANDLE)
176/* RTTARFILE */
177#define RTTARFILE_VALID_RETURN(hHandle) RTTARFILE_VALID_RETURN_RC((hHandle), VERR_INVALID_HANDLE)
178
179/** Validates a handle and returns (void) if not valid. */
180/* RTTAR */
181#define RTTAR_VALID_RETURN_VOID(hHandle) \
182 do { \
183 AssertPtrReturnVoid(hHandle); \
184 AssertReturnVoid((hHandle)->u32Magic == RTTAR_MAGIC); \
185 } while (0)
186/* RTTARFILE */
187#define RTTARFILE_VALID_RETURN_VOID(hHandle) \
188 do { \
189 AssertPtrReturnVoid(hHandle); \
190 AssertReturnVoid((hHandle)->u32Magic == RTTARFILE_MAGIC); \
191 } while (0)
192
193
194/******************************************************************************
195 * Internal Functions *
196 ******************************************************************************/
197
198DECLINLINE(void) rtTarSizeToRec(PRTTARRECORD pRecord, uint64_t cbSize)
199{
200 /*
201 * Small enough for the standard octal string encoding?
202 *
203 * Note! We could actually use the terminator character as well if we liked,
204 * but let not do that as it's easier to test this way.
205 */
206 if (cbSize < _4G * 2U)
207 RTStrPrintf(pRecord->h.size, sizeof(pRecord->h.size), "%0.11llo", cbSize);
208 else
209 {
210 /*
211 * Base 256 extension. Set the highest bit of the left most character.
212 * We don't deal with negatives here, cause the size have to be greater
213 * than zero.
214 *
215 * Note! The base-256 extension are never used by gtar or libarchive
216 * with the "ustar \0" format version, only the later
217 * "ustar\000" version. However, this shouldn't cause much
218 * trouble as they are not picky about what they read.
219 */
220 size_t cchField = sizeof(pRecord->h.size) - 1;
221 unsigned char *puchField = (unsigned char*)pRecord->h.size;
222 puchField[0] = 0x80;
223 do
224 {
225 puchField[cchField--] = cbSize & 0xff;
226 cbSize >>= 8;
227 } while (cchField);
228 }
229}
230
231DECLINLINE(uint64_t) rtTarRecToSize(PRTTARRECORD pRecord)
232{
233 int64_t cbSize = 0;
234 if (pRecord->h.size[0] & 0x80)
235 {
236 size_t cchField = sizeof(pRecord->h.size);
237 unsigned char const *puchField = (unsigned char const *)pRecord->h.size;
238
239 /*
240 * The first byte has the bit 7 set to indicate base-256, while bit 6
241 * is the signed bit. Bits 5:0 are the most significant value bits.
242 */
243 cbSize = !(0x40 & *puchField) ? 0 : -1;
244 cbSize = (cbSize << 6) | (*puchField & 0x3f);
245 cchField--;
246 puchField++;
247
248 /*
249 * The remaining bytes are used in full.
250 */
251 while (cchField-- > 0)
252 {
253 if (RT_UNLIKELY( cbSize > INT64_MAX / 256
254 || cbSize < INT64_MIN / 256))
255 {
256 cbSize = cbSize < 0 ? INT64_MIN : INT64_MAX;
257 break;
258 }
259 cbSize = (cbSize << 8) | *puchField++;
260 }
261 }
262 else
263 RTStrToInt64Full(pRecord->h.size, 8, &cbSize);
264
265 if (cbSize < 0)
266 cbSize = 0;
267
268 return (uint64_t)cbSize;
269}
270
271DECLINLINE(int) rtTarCalcChkSum(PRTTARRECORD pRecord, uint32_t *pChkSum)
272{
273 uint32_t check = 0;
274 uint32_t zero = 0;
275 for (size_t i = 0; i < sizeof(RTTARRECORD); ++i)
276 {
277 /* Calculate the sum of every byte from the header. The checksum field
278 * itself is counted as all blanks. */
279 if ( i < RT_UOFFSETOF(RTTARRECORD, h.chksum)
280 || i >= RT_UOFFSETOF(RTTARRECORD, h.linkflag))
281 check += pRecord->d[i];
282 else
283 check += ' ';
284 /* Additional check if all fields are zero, which indicate EOF. */
285 zero += pRecord->d[i];
286 }
287
288 /* EOF? */
289 if (!zero)
290 return VERR_TAR_END_OF_FILE;
291
292 *pChkSum = check;
293 return VINF_SUCCESS;
294}
295
296DECLINLINE(int) rtTarReadHeaderRecord(RTFILE hFile, PRTTARRECORD pRecord)
297{
298 int rc = RTFileRead(hFile, pRecord, sizeof(RTTARRECORD), NULL);
299 /* Check for EOF. EOF is valid in this case, cause it indicates no more
300 * data in the tar archive. */
301 if (rc == VERR_EOF)
302 return VERR_TAR_END_OF_FILE;
303 /* Report any other errors */
304 else if (RT_FAILURE(rc))
305 return rc;
306
307 /* Check for data integrity & an EOF record */
308 uint32_t check = 0;
309 rc = rtTarCalcChkSum(pRecord, &check);
310 /* EOF? */
311 if (RT_FAILURE(rc))
312 return rc;
313
314 /* Verify the checksum */
315 uint32_t sum;
316 rc = RTStrToUInt32Full(pRecord->h.chksum, 8, &sum);
317 if (RT_SUCCESS(rc) && sum == check)
318 {
319 /* Make sure the strings are zero terminated. */
320 pRecord->h.name[sizeof(pRecord->h.name) - 1] = 0;
321 pRecord->h.linkname[sizeof(pRecord->h.linkname) - 1] = 0;
322 pRecord->h.magic[sizeof(pRecord->h.magic) - 1] = 0;
323 pRecord->h.uname[sizeof(pRecord->h.uname) - 1] = 0;
324 pRecord->h.gname[sizeof(pRecord->h.gname) - 1] = 0;
325 }
326 else
327 rc = VERR_TAR_CHKSUM_MISMATCH;
328
329 return rc;
330}
331
332DECLINLINE(int) rtTarCreateHeaderRecord(PRTTARRECORD pRecord, const char *pszSrcName, uint64_t cbSize,
333 RTUID uid, RTGID gid, RTFMODE fmode, int64_t mtime)
334{
335 /** @todo check for field overflows. */
336 /* Fill the header record */
337// RT_ZERO(pRecord); - done by the caller.
338 size_t cb = RTStrPrintf(pRecord->h.name, sizeof(pRecord->h.name), "%s", pszSrcName);
339 if (cb < strlen(pszSrcName))
340 return VERR_BUFFER_OVERFLOW;
341 RTStrPrintf(pRecord->h.mode, sizeof(pRecord->h.mode), "%0.7o", fmode);
342 RTStrPrintf(pRecord->h.uid, sizeof(pRecord->h.uid), "%0.7o", uid);
343 RTStrPrintf(pRecord->h.gid, sizeof(pRecord->h.gid), "%0.7o", gid);
344 rtTarSizeToRec(pRecord, cbSize);
345 RTStrPrintf(pRecord->h.mtime, sizeof(pRecord->h.mtime), "%0.11llo", mtime);
346 RTStrPrintf(pRecord->h.magic, sizeof(pRecord->h.magic), "ustar ");
347 RTStrPrintf(pRecord->h.uname, sizeof(pRecord->h.uname), "someone");
348 RTStrPrintf(pRecord->h.gname, sizeof(pRecord->h.gname), "someone");
349 pRecord->h.linkflag = LF_NORMAL;
350
351 /* Create the checksum out of the new header */
352 uint32_t uChkSum = 0;
353 int rc = rtTarCalcChkSum(pRecord, &uChkSum);
354 if (RT_FAILURE(rc))
355 return rc;
356 /* Format the checksum */
357 RTStrPrintf(pRecord->h.chksum, sizeof(pRecord->h.chksum), "%0.7o", uChkSum);
358
359 return VINF_SUCCESS;
360}
361
362DECLINLINE(void *) rtTarMemTmpAlloc(size_t *pcbSize)
363{
364 *pcbSize = 0;
365 /* Allocate a reasonably large buffer, fall back on a tiny one.
366 * Note: has to be 512 byte aligned and >= 512 byte. */
367 size_t cbTmp = _1M;
368 void *pvTmp = RTMemTmpAlloc(cbTmp);
369 if (!pvTmp)
370 {
371 cbTmp = sizeof(RTTARRECORD);
372 pvTmp = RTMemTmpAlloc(cbTmp);
373 }
374 *pcbSize = cbTmp;
375 return pvTmp;
376}
377
378DECLINLINE(int) rtTarAppendZeros(RTTARFILE hFile, uint64_t cbSize)
379{
380 /* Allocate a temporary buffer for copying the tar content in blocks. */
381 size_t cbTmp = 0;
382 void *pvTmp = rtTarMemTmpAlloc(&cbTmp);
383 if (!pvTmp)
384 return VERR_NO_MEMORY;
385 RT_BZERO(pvTmp, cbTmp);
386
387 int rc = VINF_SUCCESS;
388 uint64_t cbAllWritten = 0;
389 size_t cbWritten = 0;
390 for (;;)
391 {
392 if (cbAllWritten >= cbSize)
393 break;
394 size_t cbToWrite = RT_MIN(cbSize - cbAllWritten, cbTmp);
395 rc = RTTarFileWrite(hFile, pvTmp, cbToWrite, &cbWritten);
396 if (RT_FAILURE(rc))
397 break;
398 cbAllWritten += cbWritten;
399 }
400
401 RTMemTmpFree(pvTmp);
402
403 return rc;
404}
405
406DECLINLINE(PRTTARFILEINTERNAL) rtCreateTarFileInternal(PRTTARINTERNAL pInt, const char *pszFilename, uint32_t fOpen)
407{
408 PRTTARFILEINTERNAL pFileInt = (PRTTARFILEINTERNAL)RTMemAllocZ(sizeof(RTTARFILEINTERNAL));
409 if (!pFileInt)
410 return NULL;
411
412 pFileInt->u32Magic = RTTARFILE_MAGIC;
413 pFileInt->pTar = pInt;
414 pFileInt->fOpenMode = fOpen;
415 pFileInt->pszFilename = RTStrDup(pszFilename);
416 if (!pFileInt->pszFilename)
417 {
418 RTMemFree(pFileInt);
419 return NULL;
420 }
421
422 return pFileInt;
423}
424
425DECLINLINE(PRTTARFILEINTERNAL) rtCopyTarFileInternal(PRTTARFILEINTERNAL pInt)
426{
427 PRTTARFILEINTERNAL pNewInt = (PRTTARFILEINTERNAL)RTMemAllocZ(sizeof(RTTARFILEINTERNAL));
428 if (!pNewInt)
429 return NULL;
430
431 memcpy(pNewInt, pInt, sizeof(RTTARFILEINTERNAL));
432 pNewInt->pszFilename = RTStrDup(pInt->pszFilename);
433 if (!pNewInt->pszFilename)
434 {
435 RTMemFree(pNewInt);
436 return NULL;
437 }
438
439 return pNewInt;
440}
441
442DECLINLINE(void) rtDeleteTarFileInternal(PRTTARFILEINTERNAL pInt)
443{
444 if (pInt)
445 {
446 if (pInt->pszFilename)
447 RTStrFree(pInt->pszFilename);
448 pInt->u32Magic = RTTARFILE_MAGIC_DEAD;
449 RTMemFree(pInt);
450 }
451}
452
453static int rtTarExtractFileToFile(RTTARFILE hFile, const char *pszTargetName, const uint64_t cbOverallSize, uint64_t &cbOverallWritten, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
454{
455 /* Open the target file */
456 RTFILE hNewFile;
457 int rc = RTFileOpen(&hNewFile, pszTargetName, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
458 if (RT_FAILURE(rc))
459 return rc;
460
461 void *pvTmp = NULL;
462 do
463 {
464 /* Allocate a temporary buffer for reading the tar content in blocks. */
465 size_t cbTmp = 0;
466 pvTmp = rtTarMemTmpAlloc(&cbTmp);
467 if (!pvTmp)
468 {
469 rc = VERR_NO_MEMORY;
470 break;
471 }
472
473 /* Get the size of the source file */
474 uint64_t cbToCopy = 0;
475 rc = RTTarFileGetSize(hFile, &cbToCopy);
476 if (RT_FAILURE(rc))
477 break;
478
479 /* Copy the content from hFile over to pszTargetName. */
480 uint64_t cbAllWritten = 0; /* Already copied */
481 uint64_t cbRead = 0; /* Actually read in the last step */
482 for (;;)
483 {
484 if (pfnProgressCallback)
485 pfnProgressCallback((unsigned)(100.0 / cbOverallSize * cbOverallWritten), pvUser);
486
487 /* Finished already? */
488 if (cbAllWritten == cbToCopy)
489 break;
490
491 /* Read one block. */
492 cbRead = RT_MIN(cbToCopy - cbAllWritten, cbTmp);
493 rc = RTTarFileRead(hFile, pvTmp, cbRead, NULL);
494 if (RT_FAILURE(rc))
495 break;
496
497 /* Write the block */
498 rc = RTFileWrite(hNewFile, pvTmp, cbRead, NULL);
499 if (RT_FAILURE(rc))
500 break;
501
502 /* Count how many bytes are written already */
503 cbAllWritten += cbRead;
504 cbOverallWritten += cbRead;
505 }
506
507 } while(0);
508
509 /* Cleanup */
510 if (pvTmp)
511 RTMemTmpFree(pvTmp);
512
513 /* Now set all file attributes */
514 if (RT_SUCCESS(rc))
515 {
516 uint32_t mode;
517 rc = RTTarFileGetMode(hFile, &mode);
518 if (RT_SUCCESS(rc))
519 {
520 mode |= RTFS_TYPE_FILE; /* For now we support regular files only */
521 /* Set the mode */
522 rc = RTFileSetMode(hNewFile, mode);
523 }
524 }
525
526 RTFileClose(hNewFile);
527
528 /* Delete the freshly created file in the case of an error */
529 if (RT_FAILURE(rc))
530 RTFileDelete(pszTargetName);
531
532 return rc;
533}
534
535static int rtTarAppendFileFromFile(RTTAR hTar, const char *pszSrcName, const uint64_t cbOverallSize, uint64_t &cbOverallWritten, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
536{
537 /* Open the source file */
538 RTFILE hOldFile;
539 int rc = RTFileOpen(&hOldFile, pszSrcName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
540 if (RT_FAILURE(rc))
541 return rc;
542
543 RTTARFILE hFile = NIL_RTTARFILE;
544 void *pvTmp = NULL;
545 do
546 {
547 /* Get the size of the source file */
548 uint64_t cbToCopy;
549 rc = RTFileGetSize(hOldFile, &cbToCopy);
550 if (RT_FAILURE(rc))
551 break;
552
553 rc = RTTarFileOpen(hTar, &hFile, RTPathFilename(pszSrcName), RTFILE_O_OPEN | RTFILE_O_WRITE);
554 if (RT_FAILURE(rc))
555 break;
556
557 /* Get some info from the source file */
558 RTFSOBJINFO info;
559 RTUID uid = 0;
560 RTGID gid = 0;
561 RTFMODE fmode = 0600; /* Make some save default */
562 int64_t mtime = 0;
563
564 /* This isn't critical. Use the defaults if it fails. */
565 rc = RTFileQueryInfo(hOldFile, &info, RTFSOBJATTRADD_UNIX);
566 if (RT_SUCCESS(rc))
567 {
568 fmode = info.Attr.fMode & RTFS_UNIX_MASK;
569 uid = info.Attr.u.Unix.uid;
570 gid = info.Attr.u.Unix.gid;
571 mtime = RTTimeSpecGetSeconds(&info.ModificationTime);
572 }
573
574 /* Set the mode from the other file */
575 rc = RTTarFileSetMode(hFile, fmode);
576 if (RT_FAILURE(rc))
577 break;
578
579 /* Set the modification time from the other file */
580 RTTIMESPEC time;
581 RTTimeSpecSetSeconds(&time, mtime);
582 rc = RTTarFileSetTime(hFile, &time);
583 if (RT_FAILURE(rc))
584 break;
585
586 /* Set the owner from the other file */
587 rc = RTTarFileSetOwner(hFile, uid, gid);
588 if (RT_FAILURE(rc))
589 break;
590
591 /* Allocate a temporary buffer for copying the tar content in blocks. */
592 size_t cbTmp = 0;
593 pvTmp = rtTarMemTmpAlloc(&cbTmp);
594 if (!pvTmp)
595 {
596 rc = VERR_NO_MEMORY;
597 break;
598 }
599
600 /* Copy the content from pszSrcName over to hFile. This is done block
601 * wise in 512 byte steps. After this copying is finished hFile will be
602 * on a 512 byte boundary, regardless if the file copied is 512 byte
603 * size aligned. */
604 uint64_t cbAllWritten = 0; /* Already copied */
605 uint64_t cbRead = 0; /* Actually read in the last step */
606 for (;;)
607 {
608 if (pfnProgressCallback)
609 pfnProgressCallback((unsigned)(100.0 / cbOverallSize * cbOverallWritten), pvUser);
610 if (cbAllWritten >= cbToCopy)
611 break;
612
613 /* Read one block. Either its the buffer size or the rest of the
614 * file. */
615 cbRead = RT_MIN(cbToCopy - cbAllWritten, cbTmp);
616 rc = RTFileRead(hOldFile, pvTmp, cbRead, NULL);
617 if (RT_FAILURE(rc))
618 break;
619
620 /* Write one block. */
621 rc = RTTarFileWriteAt(hFile, cbAllWritten, pvTmp, cbRead, NULL);
622 if (RT_FAILURE(rc))
623 break;
624
625 /* Count how many bytes (of the original file) are written already */
626 cbAllWritten += cbRead;
627 cbOverallWritten += cbRead;
628 }
629 } while (0);
630
631 /* Cleanup */
632 if (pvTmp)
633 RTMemTmpFree(pvTmp);
634
635 if (hFile)
636 RTTarFileClose(hFile);
637
638 RTFileClose(hOldFile);
639
640 return rc;
641}
642
643static int rtTarSkipData(RTFILE hFile, PRTTARRECORD pRecord)
644{
645 int rc = VINF_SUCCESS;
646 /* Seek over the data parts (512 bytes aligned) */
647 int64_t offSeek = RT_ALIGN(rtTarRecToSize(pRecord), sizeof(RTTARRECORD));
648 if (offSeek > 0)
649 rc = RTFileSeek(hFile, offSeek, RTFILE_SEEK_CURRENT, NULL);
650 return rc;
651}
652
653static int rtTarFindFile(RTFILE hFile, const char *pszFile, uint64_t *poff, uint64_t *pcbSize)
654{
655 /* Assume we are on the file head. */
656 int rc = VINF_SUCCESS;
657 bool fFound = false;
658 RTTARRECORD record;
659 for (;;)
660 {
661 /* Read & verify a header record */
662 rc = rtTarReadHeaderRecord(hFile, &record);
663 /* Check for error or EOF. */
664 if (RT_FAILURE(rc))
665 break;
666
667 /* We support normal files only */
668 if ( record.h.linkflag == LF_OLDNORMAL
669 || record.h.linkflag == LF_NORMAL)
670 {
671 if (!RTStrCmp(record.h.name, pszFile))
672 {
673 /* Get the file size */
674 *pcbSize = rtTarRecToSize(&record);
675 /* Seek back, to position the file pointer at the start of the header. */
676 rc = RTFileSeek(hFile, -(int64_t)sizeof(RTTARRECORD), RTFILE_SEEK_CURRENT, poff);
677 fFound = true;
678 break;
679 }
680 }
681 rc = rtTarSkipData(hFile, &record);
682 if (RT_FAILURE(rc))
683 break;
684 }
685
686 if (rc == VERR_TAR_END_OF_FILE)
687 rc = VINF_SUCCESS;
688
689 /* Something found? */
690 if ( RT_SUCCESS(rc)
691 && !fFound)
692 rc = VERR_FILE_NOT_FOUND;
693
694 return rc;
695}
696
697#ifdef SOME_UNUSED_FUNCTION
698static int rtTarGetFilesOverallSize(RTFILE hFile, const char * const *papszFiles, size_t cFiles, uint64_t *pcbOverallSize)
699{
700 int rc = VINF_SUCCESS;
701 size_t cFound = 0;
702 RTTARRECORD record;
703 for (;;)
704 {
705 /* Read & verify a header record */
706 rc = rtTarReadHeaderRecord(hFile, &record);
707 /* Check for error or EOF. */
708 if (RT_FAILURE(rc))
709 break;
710
711 /* We support normal files only */
712 if ( record.h.linkflag == LF_OLDNORMAL
713 || record.h.linkflag == LF_NORMAL)
714 {
715 for (size_t i = 0; i < cFiles; ++i)
716 {
717 if (!RTStrCmp(record.h.name, papszFiles[i]))
718 {
719 /* Sum up the overall size */
720 *pcbOverallSize += rtTarRecToSize(&record);
721 ++cFound;
722 break;
723 }
724 }
725 if ( cFound == cFiles
726 || RT_FAILURE(rc))
727 break;
728 }
729 rc = rtTarSkipData(hFile, &record);
730 if (RT_FAILURE(rc))
731 break;
732 }
733 if (rc == VERR_TAR_END_OF_FILE)
734 rc = VINF_SUCCESS;
735
736 /* Make sure the file pointer is at the begin of the file again. */
737 if (RT_SUCCESS(rc))
738 rc = RTFileSeek(hFile, 0, RTFILE_SEEK_BEGIN, 0);
739 return rc;
740}
741#endif /* SOME_UNUSED_FUNCTION */
742
743/******************************************************************************
744 * Public Functions *
745 ******************************************************************************/
746
747RTR3DECL(int) RTTarOpen(PRTTAR phTar, const char *pszTarname, uint32_t fMode, bool fStream)
748{
749 /*
750 * Create a tar instance.
751 */
752 PRTTARINTERNAL pThis = (PRTTARINTERNAL)RTMemAllocZ(sizeof(RTTARINTERNAL));
753 if (!pThis)
754 return VERR_NO_MEMORY;
755
756 pThis->u32Magic = RTTAR_MAGIC;
757 pThis->fOpenMode = fMode;
758 pThis->fStreamMode = fStream && (fMode & RTFILE_O_READ);
759
760 /*
761 * Open the tar file.
762 */
763 int rc = RTFileOpen(&pThis->hTarFile, pszTarname, fMode);
764 if (RT_SUCCESS(rc))
765 {
766 *phTar = pThis;
767 return VINF_SUCCESS;
768 }
769
770 RTMemFree(pThis);
771 return rc;
772}
773
774RTR3DECL(int) RTTarClose(RTTAR hTar)
775{
776 if (hTar == NIL_RTTAR)
777 return VINF_SUCCESS;
778
779 PRTTARINTERNAL pInt = hTar;
780 RTTAR_VALID_RETURN(pInt);
781
782 int rc = VINF_SUCCESS;
783
784 /* gtar gives a warning, but the documentation says EOF is indicated by a
785 * zero block. Disabled for now. */
786#if 0
787 {
788 /* Append the EOF record which is filled all by zeros */
789 RTTARRECORD record;
790 RT_ZERO(record);
791 rc = RTFileWrite(pInt->hTarFile, &record, sizeof(record), NULL);
792 }
793#endif
794
795 if (pInt->hTarFile != NIL_RTFILE)
796 rc = RTFileClose(pInt->hTarFile);
797
798 /* Delete any remaining cached file headers. */
799 if (pInt->pFileCache)
800 {
801 rtDeleteTarFileInternal(pInt->pFileCache);
802 pInt->pFileCache = NULL;
803 }
804
805 pInt->u32Magic = RTTAR_MAGIC_DEAD;
806
807 RTMemFree(pInt);
808
809 return rc;
810}
811
812RTR3DECL(int) RTTarFileOpen(RTTAR hTar, PRTTARFILE phFile, const char *pszFilename, uint32_t fOpen)
813{
814 AssertReturn((fOpen & RTFILE_O_READ) || (fOpen & RTFILE_O_WRITE), VERR_INVALID_PARAMETER);
815
816 PRTTARINTERNAL pInt = hTar;
817 RTTAR_VALID_RETURN(pInt);
818
819 if (!pInt->hTarFile)
820 return VERR_INVALID_HANDLE;
821
822 if (pInt->fStreamMode)
823 return VERR_INVALID_STATE;
824
825 if (fOpen & RTFILE_O_WRITE)
826 {
827 if (!(pInt->fOpenMode & RTFILE_O_WRITE))
828 return VERR_WRITE_PROTECT;
829 if (pInt->fFileOpenForWrite)
830 return VERR_TOO_MANY_OPEN_FILES;
831 }
832
833 PRTTARFILEINTERNAL pFileInt = rtCreateTarFileInternal(pInt, pszFilename, fOpen);
834 if (!pFileInt)
835 return VERR_NO_MEMORY;
836
837 int rc = VINF_SUCCESS;
838 do /* break loop */
839 {
840 if (pFileInt->fOpenMode & RTFILE_O_WRITE)
841 {
842 pInt->fFileOpenForWrite = true;
843
844 /* If we are in write mode, we also in append mode. Add an dummy
845 * header at the end of the current file. It will be filled by the
846 * close operation. */
847 rc = RTFileSeek(pFileInt->pTar->hTarFile, 0, RTFILE_SEEK_END, &pFileInt->offStart);
848 if (RT_FAILURE(rc))
849 break;
850 RTTARRECORD record;
851 RT_ZERO(record);
852 rc = RTFileWrite(pFileInt->pTar->hTarFile, &record, sizeof(RTTARRECORD), NULL);
853 if (RT_FAILURE(rc))
854 break;
855 }
856 else if (pFileInt->fOpenMode & RTFILE_O_READ)
857 {
858 /* We need to be on the start of the file */
859 rc = RTFileSeek(pFileInt->pTar->hTarFile, 0, RTFILE_SEEK_BEGIN, NULL);
860 if (RT_FAILURE(rc))
861 break;
862
863 /* Search for the file. */
864 rc = rtTarFindFile(pFileInt->pTar->hTarFile, pszFilename, &pFileInt->offStart, &pFileInt->cbSize);
865 if (RT_FAILURE(rc))
866 break;
867 }
868 else
869 {
870 /** @todo is something missing here? */
871 }
872
873 } while (0);
874
875 /* Cleanup on failure */
876 if (RT_FAILURE(rc))
877 {
878 if (pFileInt->pszFilename)
879 RTStrFree(pFileInt->pszFilename);
880 RTMemFree(pFileInt);
881 }
882 else
883 *phFile = (RTTARFILE)pFileInt;
884
885 return rc;
886}
887
888RTR3DECL(int) RTTarFileClose(RTTARFILE hFile)
889{
890 /* Already closed? */
891 if (hFile == NIL_RTTARFILE)
892 return VINF_SUCCESS;
893
894 PRTTARFILEINTERNAL pFileInt = hFile;
895 RTTARFILE_VALID_RETURN(pFileInt);
896
897 int rc = VINF_SUCCESS;
898
899 /* In write mode: */
900 if (pFileInt->fOpenMode & RTFILE_O_READ)
901 {
902 /* In read mode, we want to make sure to stay at the aligned end of this
903 * file, so the next file could be read immediately. */
904 uint64_t offCur = RTFileTell(pFileInt->pTar->hTarFile);
905
906 /* Check that the file pointer is somewhere within the last open file.
907 * If we are at the beginning (nothing read yet) nothing will be done.
908 * A user could open/close a file more than once, without reading
909 * something. */
910 if ( pFileInt->offStart + sizeof(RTTARRECORD) < offCur
911 && offCur < RT_ALIGN(pFileInt->offStart + sizeof(RTTARRECORD) + pFileInt->cbSize, sizeof(RTTARRECORD)))
912 {
913 /* Seek to the next file header. */
914 uint64_t offNext = RT_ALIGN(pFileInt->offStart + sizeof(RTTARRECORD) + pFileInt->cbSize, sizeof(RTTARRECORD));
915 rc = RTFileSeek(pFileInt->pTar->hTarFile, offNext - offCur, RTFILE_SEEK_CURRENT, NULL);
916 }
917 }
918 else if (pFileInt->fOpenMode & RTFILE_O_WRITE)
919 {
920 pFileInt->pTar->fFileOpenForWrite = false;
921 do
922 {
923 /* If the user has called RTTarFileSetSize in the meantime, we have
924 to make sure the file has the right size. */
925 if (pFileInt->cbSetSize > pFileInt->cbSize)
926 {
927 rc = rtTarAppendZeros(hFile, pFileInt->cbSetSize - pFileInt->cbSize);
928 if (RT_FAILURE(rc))
929 break;
930 }
931
932 /* If the written size isn't 512 byte aligned, we need to fix this. */
933 RTTARRECORD record;
934 RT_ZERO(record);
935 uint64_t cbSizeAligned = RT_ALIGN(pFileInt->cbSize, sizeof(RTTARRECORD));
936 if (cbSizeAligned != pFileInt->cbSize)
937 {
938 /* Note the RTFile method. We didn't increase the cbSize or cbCurrentPos here. */
939 rc = RTFileWriteAt(pFileInt->pTar->hTarFile,
940 pFileInt->offStart + sizeof(RTTARRECORD) + pFileInt->cbSize,
941 &record,
942 cbSizeAligned - pFileInt->cbSize,
943 NULL);
944 if (RT_FAILURE(rc))
945 break;
946 }
947
948 /* Create a header record for the file */
949 /* Todo: mode, gid, uid, mtime should be setable (or detected myself) */
950 RTTIMESPEC time;
951 RTTimeNow(&time);
952 rc = rtTarCreateHeaderRecord(&record, pFileInt->pszFilename, pFileInt->cbSize,
953 0, 0, 0600, RTTimeSpecGetSeconds(&time));
954 if (RT_FAILURE(rc))
955 break;
956
957 /* Write this at the start of the file data */
958 rc = RTFileWriteAt(pFileInt->pTar->hTarFile, pFileInt->offStart, &record, sizeof(RTTARRECORD), NULL);
959 if (RT_FAILURE(rc))
960 break;
961 }
962 while(0);
963 }
964
965 /* Now cleanup and delete the handle */
966 rtDeleteTarFileInternal(pFileInt);
967
968 return rc;
969}
970
971RTR3DECL(int) RTTarFileSeek(RTTARFILE hFile, uint64_t offSeek, unsigned uMethod, uint64_t *poffActual)
972{
973 PRTTARFILEINTERNAL pFileInt = hFile;
974 RTTARFILE_VALID_RETURN(pFileInt);
975
976 if (pFileInt->pTar->fStreamMode)
977 return VERR_INVALID_STATE;
978
979 switch (uMethod)
980 {
981 case RTFILE_SEEK_BEGIN:
982 {
983 if (offSeek > pFileInt->cbSize)
984 return VERR_SEEK_ON_DEVICE;
985 pFileInt->offCurrent = offSeek;
986 break;
987 }
988 case RTFILE_SEEK_CURRENT:
989 {
990 if (pFileInt->offCurrent + offSeek > pFileInt->cbSize)
991 return VERR_SEEK_ON_DEVICE;
992 pFileInt->offCurrent += offSeek;
993 break;
994 }
995 case RTFILE_SEEK_END:
996 {
997 if ((int64_t)pFileInt->cbSize - (int64_t)offSeek < 0)
998 return VERR_NEGATIVE_SEEK;
999 pFileInt->offCurrent = pFileInt->cbSize - offSeek;
1000 break;
1001 }
1002 default: AssertFailedReturn(VERR_INVALID_PARAMETER);
1003 }
1004
1005 if (poffActual)
1006 *poffActual = pFileInt->offCurrent;
1007
1008 return VINF_SUCCESS;
1009}
1010
1011RTR3DECL(uint64_t) RTTarFileTell(RTTARFILE hFile)
1012{
1013 PRTTARFILEINTERNAL pFileInt = hFile;
1014 RTTARFILE_VALID_RETURN_RC(pFileInt, UINT64_MAX);
1015
1016 return pFileInt->offCurrent;
1017}
1018
1019RTR3DECL(int) RTTarFileRead(RTTARFILE hFile, void *pvBuf, size_t cbToRead, size_t *pcbRead)
1020{
1021 PRTTARFILEINTERNAL pFileInt = hFile;
1022 RTTARFILE_VALID_RETURN(pFileInt);
1023
1024 /* Todo: optimize this, by checking the current pos */
1025 return RTTarFileReadAt(hFile, pFileInt->offCurrent, pvBuf, cbToRead, pcbRead);
1026}
1027
1028RTR3DECL(int) RTTarFileReadAt(RTTARFILE hFile, uint64_t off, void *pvBuf, size_t cbToRead, size_t *pcbRead)
1029{
1030 PRTTARFILEINTERNAL pFileInt = hFile;
1031 RTTARFILE_VALID_RETURN(pFileInt);
1032
1033 /* Check that we not read behind the end of file. If so return immediately. */
1034 if (off > pFileInt->cbSize)
1035 {
1036 if (pcbRead)
1037 *pcbRead = 0;
1038 return VINF_SUCCESS; /* ??? VERR_EOF */
1039 }
1040
1041 size_t cbToCopy = RT_MIN(pFileInt->cbSize - off, cbToRead);
1042 size_t cbTmpRead = 0;
1043 int rc = RTFileReadAt(pFileInt->pTar->hTarFile, pFileInt->offStart + 512 + off, pvBuf, cbToCopy, &cbTmpRead);
1044 pFileInt->offCurrent = off + cbTmpRead;
1045 if (pcbRead)
1046 *pcbRead = cbTmpRead;
1047
1048 return rc;
1049}
1050
1051RTR3DECL(int) RTTarFileWrite(RTTARFILE hFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
1052{
1053 PRTTARFILEINTERNAL pFileInt = hFile;
1054 RTTARFILE_VALID_RETURN(pFileInt);
1055
1056 /** @todo Optimize this, by checking the current pos */
1057 return RTTarFileWriteAt(hFile, pFileInt->offCurrent, pvBuf, cbToWrite, pcbWritten);
1058}
1059
1060RTR3DECL(int) RTTarFileWriteAt(RTTARFILE hFile, uint64_t off, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
1061{
1062 PRTTARFILEINTERNAL pFileInt = hFile;
1063 RTTARFILE_VALID_RETURN(pFileInt);
1064
1065 if ((pFileInt->fOpenMode & RTFILE_O_WRITE) != RTFILE_O_WRITE)
1066 return VERR_WRITE_ERROR;
1067
1068 size_t cbTmpWritten = 0;
1069 int rc = RTFileWriteAt(pFileInt->pTar->hTarFile, pFileInt->offStart + 512 + off, pvBuf, cbToWrite, &cbTmpWritten);
1070 pFileInt->cbSize += cbTmpWritten;
1071 pFileInt->offCurrent = off + cbTmpWritten;
1072 if (pcbWritten)
1073 *pcbWritten = cbTmpWritten;
1074
1075 return rc;
1076}
1077
1078RTR3DECL(int) RTTarFileGetSize(RTTARFILE hFile, uint64_t *pcbSize)
1079{
1080 /* Validate input */
1081 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
1082
1083 PRTTARFILEINTERNAL pFileInt = hFile;
1084 RTTARFILE_VALID_RETURN(pFileInt);
1085
1086 *pcbSize = RT_MAX(pFileInt->cbSetSize, pFileInt->cbSize);
1087
1088 return VINF_SUCCESS;
1089}
1090
1091RTR3DECL(int) RTTarFileSetSize(RTTARFILE hFile, uint64_t cbSize)
1092{
1093 PRTTARFILEINTERNAL pFileInt = hFile;
1094 RTTARFILE_VALID_RETURN(pFileInt);
1095
1096 if ((pFileInt->fOpenMode & RTFILE_O_WRITE) != RTFILE_O_WRITE)
1097 return VERR_WRITE_ERROR;
1098
1099 /** @todo If cbSize is smaller than pFileInt->cbSize we have to
1100 * truncate the current file. */
1101 pFileInt->cbSetSize = cbSize;
1102
1103 return VINF_SUCCESS;
1104}
1105
1106RTR3DECL(int) RTTarFileGetMode(RTTARFILE hFile, uint32_t *pfMode)
1107{
1108 /* Validate input */
1109 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
1110
1111 PRTTARFILEINTERNAL pFileInt = hFile;
1112 RTTARFILE_VALID_RETURN(pFileInt);
1113
1114 /* Read the mode out of the header entry */
1115 char szMode[RT_SIZEOFMEMB(RTTARRECORD, h.mode)+1];
1116 int rc = RTFileReadAt(pFileInt->pTar->hTarFile,
1117 pFileInt->offStart + RT_OFFSETOF(RTTARRECORD, h.mode),
1118 szMode,
1119 RT_SIZEOFMEMB(RTTARRECORD, h.mode),
1120 NULL);
1121 if (RT_FAILURE(rc))
1122 return rc;
1123 szMode[sizeof(szMode) - 1] = '\0';
1124
1125 /* Convert it to an integer */
1126 return RTStrToUInt32Full(szMode, 8, pfMode);
1127}
1128
1129RTR3DECL(int) RTTarFileSetMode(RTTARFILE hFile, uint32_t fMode)
1130{
1131 PRTTARFILEINTERNAL pFileInt = hFile;
1132 RTTARFILE_VALID_RETURN(pFileInt);
1133
1134 if ((pFileInt->fOpenMode & RTFILE_O_WRITE) != RTFILE_O_WRITE)
1135 return VERR_WRITE_ERROR;
1136
1137 /* Convert the mode to an string. */
1138 char szMode[RT_SIZEOFMEMB(RTTARRECORD, h.mode)];
1139 RTStrPrintf(szMode, sizeof(szMode), "%0.7o", fMode);
1140
1141 /* Write it directly into the header */
1142 return RTFileWriteAt(pFileInt->pTar->hTarFile,
1143 pFileInt->offStart + RT_OFFSETOF(RTTARRECORD, h.mode),
1144 szMode,
1145 RT_SIZEOFMEMB(RTTARRECORD, h.mode),
1146 NULL);
1147}
1148
1149RTR3DECL(int) RTTarFileGetTime(RTTARFILE hFile, PRTTIMESPEC pTime)
1150{
1151 PRTTARFILEINTERNAL pFileInt = hFile;
1152 RTTARFILE_VALID_RETURN(pFileInt);
1153
1154 /* Read the time out of the header entry */
1155 char szModTime[RT_SIZEOFMEMB(RTTARRECORD, h.mtime) + 1];
1156 int rc = RTFileReadAt(pFileInt->pTar->hTarFile,
1157 pFileInt->offStart + RT_OFFSETOF(RTTARRECORD, h.mtime),
1158 szModTime,
1159 RT_SIZEOFMEMB(RTTARRECORD, h.mtime),
1160 NULL);
1161 if (RT_FAILURE(rc))
1162 return rc;
1163 szModTime[sizeof(szModTime) - 1] = '\0';
1164
1165 /* Convert it to an integer */
1166 int64_t cSeconds;
1167 rc = RTStrToInt64Full(szModTime, 12, &cSeconds);
1168
1169 /* And back to our time structure */
1170 if (RT_SUCCESS(rc))
1171 RTTimeSpecSetSeconds(pTime, cSeconds);
1172
1173 return rc;
1174}
1175
1176RTR3DECL(int) RTTarFileSetTime(RTTARFILE hFile, PRTTIMESPEC pTime)
1177{
1178 PRTTARFILEINTERNAL pFileInt = hFile;
1179 RTTARFILE_VALID_RETURN(pFileInt);
1180
1181 if ((pFileInt->fOpenMode & RTFILE_O_WRITE) != RTFILE_O_WRITE)
1182 return VERR_WRITE_ERROR;
1183
1184 /* Convert the time to an string. */
1185 char szModTime[RT_SIZEOFMEMB(RTTARRECORD, h.mtime)];
1186 RTStrPrintf(szModTime, sizeof(szModTime), "%0.11llo", RTTimeSpecGetSeconds(pTime));
1187
1188 /* Write it directly into the header */
1189 return RTFileWriteAt(pFileInt->pTar->hTarFile,
1190 pFileInt->offStart + RT_OFFSETOF(RTTARRECORD, h.mtime),
1191 szModTime,
1192 RT_SIZEOFMEMB(RTTARRECORD, h.mtime),
1193 NULL);
1194}
1195
1196RTR3DECL(int) RTTarFileGetOwner(RTTARFILE hFile, uint32_t *pUid, uint32_t *pGid)
1197{
1198 PRTTARFILEINTERNAL pFileInt = hFile;
1199 RTTARFILE_VALID_RETURN(pFileInt);
1200
1201 /* Read the uid and gid out of the header entry */
1202 AssertCompileAdjacentMembers(RTTARRECORD, h.uid, h.gid);
1203 char szUidGid[RT_SIZEOFMEMB(RTTARRECORD, h.uid) + RT_SIZEOFMEMB(RTTARRECORD, h.gid) + 1];
1204 int rc = RTFileReadAt(pFileInt->pTar->hTarFile,
1205 pFileInt->offStart + RT_OFFSETOF(RTTARRECORD, h.uid),
1206 szUidGid,
1207 sizeof(szUidGid) - 1,
1208 NULL);
1209 if (RT_FAILURE(rc))
1210 return rc;
1211 szUidGid[sizeof(szUidGid) - 1] = '\0';
1212
1213 /* Convert it to integer */
1214 rc = RTStrToUInt32Full(&szUidGid[RT_SIZEOFMEMB(RTTARRECORD, h.uid)], 8, pGid);
1215 if (RT_SUCCESS(rc))
1216 {
1217 szUidGid[RT_SIZEOFMEMB(RTTARRECORD, h.uid)] = '\0';
1218 rc = RTStrToUInt32Full(szUidGid, 8, pUid);
1219 }
1220 return rc;
1221}
1222
1223RTR3DECL(int) RTTarFileSetOwner(RTTARFILE hFile, uint32_t uid, uint32_t gid)
1224{
1225 PRTTARFILEINTERNAL pFileInt = hFile;
1226 RTTARFILE_VALID_RETURN(pFileInt);
1227
1228 if ((pFileInt->fOpenMode & RTFILE_O_WRITE) != RTFILE_O_WRITE)
1229 return VERR_WRITE_ERROR;
1230 AssertReturn(uid == (uint32_t)-1 || uid <= 07777777, VERR_OUT_OF_RANGE);
1231 AssertReturn(gid == (uint32_t)-1 || gid <= 07777777, VERR_OUT_OF_RANGE);
1232
1233 int rc = VINF_SUCCESS;
1234
1235 if (uid != (uint32_t)-1)
1236 {
1237 /* Convert the uid to an string. */
1238 char szUid[RT_SIZEOFMEMB(RTTARRECORD, h.uid)];
1239 RTStrPrintf(szUid, sizeof(szUid), "%0.7o", uid);
1240
1241 /* Write it directly into the header */
1242 rc = RTFileWriteAt(pFileInt->pTar->hTarFile,
1243 pFileInt->offStart + RT_OFFSETOF(RTTARRECORD, h.uid),
1244 szUid,
1245 RT_SIZEOFMEMB(RTTARRECORD, h.uid),
1246 NULL);
1247 if (RT_FAILURE(rc))
1248 return rc;
1249 }
1250
1251 if (gid != (uint32_t)-1)
1252 {
1253 /* Convert the gid to an string. */
1254 char szGid[RT_SIZEOFMEMB(RTTARRECORD, h.gid)];
1255 RTStrPrintf(szGid, sizeof(szGid), "%0.7o", gid);
1256
1257 /* Write it directly into the header */
1258 rc = RTFileWriteAt(pFileInt->pTar->hTarFile,
1259 pFileInt->offStart + RT_OFFSETOF(RTTARRECORD, h.gid),
1260 szGid,
1261 RT_SIZEOFMEMB(RTTARRECORD, h.gid),
1262 NULL);
1263 if (RT_FAILURE(rc))
1264 return rc;
1265 }
1266
1267 return rc;
1268}
1269
1270/******************************************************************************
1271 * Convenience Functions *
1272 ******************************************************************************/
1273
1274RTR3DECL(int) RTTarFileExists(const char *pszTarFile, const char *pszFile)
1275{
1276 /* Validate input */
1277 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
1278 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
1279
1280 /* Open the tar file */
1281 RTTAR hTar;
1282 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, false /*fStream*/);
1283 if (RT_FAILURE(rc))
1284 return rc;
1285
1286 /* Just try to open that file readonly. If this succeed the file exists. */
1287 RTTARFILE hFile;
1288 rc = RTTarFileOpen(hTar, &hFile, pszFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
1289 if (RT_SUCCESS(rc))
1290 RTTarFileClose(hFile);
1291
1292 RTTarClose(hTar);
1293
1294 return rc;
1295}
1296
1297RTR3DECL(int) RTTarList(const char *pszTarFile, char ***ppapszFiles, size_t *pcFiles)
1298{
1299 /* Validate input */
1300 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
1301 AssertPtrReturn(ppapszFiles, VERR_INVALID_POINTER);
1302 AssertPtrReturn(pcFiles, VERR_INVALID_POINTER);
1303
1304 /* Open the tar file */
1305 RTTAR hTar;
1306 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, false /*fStream*/);
1307 if (RT_FAILURE(rc))
1308 return rc;
1309
1310 /* This is done by internal methods, cause we didn't have a RTTARDIR
1311 * interface, yet. This should be fixed someday. */
1312
1313 PRTTARINTERNAL pInt = hTar;
1314 char **papszFiles = NULL;
1315 size_t cFiles = 0;
1316 do /* break loop */
1317 {
1318 /* Initialize the file name array with one slot */
1319 size_t cFilesAlloc = 1;
1320 papszFiles = (char **)RTMemAlloc(sizeof(char *));
1321 if (!papszFiles)
1322 {
1323 rc = VERR_NO_MEMORY;
1324 break;
1325 }
1326
1327 /* Iterate through the tar file record by record. Skip data records as we
1328 * didn't need them. */
1329 RTTARRECORD record;
1330 for (;;)
1331 {
1332 /* Read & verify a header record */
1333 rc = rtTarReadHeaderRecord(pInt->hTarFile, &record);
1334 /* Check for error or EOF. */
1335 if (RT_FAILURE(rc))
1336 break;
1337 /* We support normal files only */
1338 if ( record.h.linkflag == LF_OLDNORMAL
1339 || record.h.linkflag == LF_NORMAL)
1340 {
1341 if (cFiles >= cFilesAlloc)
1342 {
1343 /* Double the array size, make sure the size doesn't wrap. */
1344 void *pvNew = NULL;
1345 size_t cbNew = cFilesAlloc * sizeof(char *) * 2;
1346 if (cbNew / sizeof(char *) / 2 == cFilesAlloc)
1347 pvNew = RTMemRealloc(papszFiles, cbNew);
1348 if (!pvNew)
1349 {
1350 rc = VERR_NO_MEMORY;
1351 break;
1352 }
1353 papszFiles = (char **)pvNew;
1354 cFilesAlloc *= 2;
1355 }
1356
1357 /* Duplicate the name */
1358 papszFiles[cFiles] = RTStrDup(record.h.name);
1359 if (!papszFiles[cFiles])
1360 {
1361 rc = VERR_NO_MEMORY;
1362 break;
1363 }
1364 cFiles++;
1365 }
1366 rc = rtTarSkipData(pInt->hTarFile, &record);
1367 if (RT_FAILURE(rc))
1368 break;
1369 }
1370 } while(0);
1371
1372 if (rc == VERR_TAR_END_OF_FILE)
1373 rc = VINF_SUCCESS;
1374
1375 /* Return the file array on success, dispose of it on failure. */
1376 if (RT_SUCCESS(rc))
1377 {
1378 *pcFiles = cFiles;
1379 *ppapszFiles = papszFiles;
1380 }
1381 else
1382 {
1383 while (cFiles-- > 0)
1384 RTStrFree(papszFiles[cFiles]);
1385 RTMemFree(papszFiles);
1386 }
1387
1388 RTTarClose(hTar);
1389
1390 return rc;
1391}
1392
1393RTR3DECL(int) RTTarExtractFileToBuf(const char *pszTarFile, void **ppvBuf, size_t *pcbSize, const char *pszFile,
1394 PFNRTPROGRESS pfnProgressCallback, void *pvUser)
1395{
1396 /*
1397 * Validate input
1398 */
1399 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
1400 AssertPtrReturn(ppvBuf, VERR_INVALID_POINTER);
1401 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
1402 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
1403 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_POINTER);
1404 AssertPtrNullReturn(pvUser, VERR_INVALID_POINTER);
1405
1406 /** @todo progress bar - is this TODO still valid? */
1407
1408 int rc = VINF_SUCCESS;
1409 RTTAR hTar = NIL_RTTAR;
1410 RTTARFILE hFile = NIL_RTTARFILE;
1411 char *pvTmp = NULL;
1412 uint64_t cbToCopy= 0;
1413 do /* break loop */
1414 {
1415 rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, false /*fStream*/);
1416 if (RT_FAILURE(rc))
1417 break;
1418 rc = RTTarFileOpen(hTar, &hFile, pszFile, RTFILE_O_OPEN | RTFILE_O_READ);
1419 if (RT_FAILURE(rc))
1420 break;
1421 rc = RTTarFileGetSize(hFile, &cbToCopy);
1422 if (RT_FAILURE(rc))
1423 break;
1424
1425 /* Allocate the memory for the file content. */
1426 pvTmp = (char *)RTMemAlloc(cbToCopy);
1427 if (!pvTmp)
1428 {
1429 rc = VERR_NO_MEMORY;
1430 break;
1431 }
1432 size_t cbRead = 0;
1433 size_t cbAllRead = 0;
1434 for (;;)
1435 {
1436 if (pfnProgressCallback)
1437 pfnProgressCallback((unsigned)(100.0 / cbToCopy * cbAllRead), pvUser);
1438 if (cbAllRead == cbToCopy)
1439 break;
1440 rc = RTTarFileReadAt(hFile, 0, &pvTmp[cbAllRead], cbToCopy - cbAllRead, &cbRead);
1441 if (RT_FAILURE(rc))
1442 break;
1443 cbAllRead += cbRead;
1444 }
1445 } while (0);
1446
1447 /* Set output values on success */
1448 if (RT_SUCCESS(rc))
1449 {
1450 *pcbSize = cbToCopy;
1451 *ppvBuf = pvTmp;
1452 }
1453
1454 /* Cleanup */
1455 if ( RT_FAILURE(rc)
1456 && pvTmp)
1457 RTMemFree(pvTmp);
1458 if (hFile)
1459 RTTarFileClose(hFile);
1460 if (hTar)
1461 RTTarClose(hTar);
1462
1463 return rc;
1464}
1465
1466RTR3DECL(int) RTTarExtractFiles(const char *pszTarFile, const char *pszOutputDir, const char * const *papszFiles,
1467 size_t cFiles, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
1468{
1469 /* Validate input */
1470 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
1471 AssertPtrReturn(pszOutputDir, VERR_INVALID_POINTER);
1472 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER);
1473 AssertReturn(cFiles, VERR_INVALID_PARAMETER);
1474 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_POINTER);
1475 AssertPtrNullReturn(pvUser, VERR_INVALID_POINTER);
1476
1477 /* Open the tar file */
1478 RTTAR hTar;
1479 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, false /*fStream*/);
1480 if (RT_FAILURE(rc))
1481 return rc;
1482
1483 do /* break loop */
1484 {
1485 /* Get the overall size of all files to extract out of the tar archive
1486 headers. Only necessary if there is a progress callback. */
1487 uint64_t cbOverallSize = 0;
1488 if (pfnProgressCallback)
1489 {
1490// rc = rtTarGetFilesOverallSize(hFile, papszFiles, cFiles, &cbOverallSize);
1491// if (RT_FAILURE(rc))
1492// break;
1493 }
1494
1495 uint64_t cbOverallWritten = 0;
1496 for (size_t i = 0; i < cFiles; ++i)
1497 {
1498 RTTARFILE hFile;
1499 rc = RTTarFileOpen(hTar, &hFile, papszFiles[i], RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
1500 if (RT_FAILURE(rc))
1501 break;
1502 char *pszTargetFile = RTPathJoinA(pszOutputDir, papszFiles[i]);
1503 if (pszTargetFile)
1504 rc = rtTarExtractFileToFile(hFile, pszTargetFile, cbOverallSize, cbOverallWritten, pfnProgressCallback, pvUser);
1505 else
1506 rc = VERR_NO_STR_MEMORY;
1507 RTStrFree(pszTargetFile);
1508 RTTarFileClose(hFile);
1509 if (RT_FAILURE(rc))
1510 break;
1511 }
1512 } while (0);
1513
1514 RTTarClose(hTar);
1515
1516 return rc;
1517}
1518
1519RTR3DECL(int) RTTarExtractAll(const char *pszTarFile, const char *pszOutputDir, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
1520{
1521 /* Validate input */
1522 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
1523 AssertPtrReturn(pszOutputDir, VERR_INVALID_POINTER);
1524 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_POINTER);
1525 AssertPtrNullReturn(pvUser, VERR_INVALID_POINTER);
1526
1527 char **papszFiles;
1528 size_t cFiles;
1529
1530 /* First fetch the files names contained in the tar file */
1531 int rc = RTTarList(pszTarFile, &papszFiles, &cFiles);
1532 if (RT_FAILURE(rc))
1533 return rc;
1534
1535 /* Extract all files */
1536 return RTTarExtractFiles(pszTarFile, pszOutputDir, papszFiles, cFiles, pfnProgressCallback, pvUser);
1537}
1538
1539RTR3DECL(int) RTTarCreate(const char *pszTarFile, const char * const *papszFiles, size_t cFiles, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
1540{
1541 /* Validate input */
1542 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
1543 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER);
1544 AssertReturn(cFiles, VERR_INVALID_PARAMETER);
1545 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_POINTER);
1546 AssertPtrNullReturn(pvUser, VERR_INVALID_POINTER);
1547
1548 RTTAR hTar;
1549 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_CREATE | RTFILE_O_READWRITE | RTFILE_O_DENY_NONE, false /*fStream*/);
1550 if (RT_FAILURE(rc))
1551 return rc;
1552
1553 /* Get the overall size of all files to pack into the tar archive. Only
1554 necessary if there is a progress callback. */
1555 uint64_t cbOverallSize = 0;
1556 if (pfnProgressCallback)
1557 for (size_t i = 0; i < cFiles; ++i)
1558 {
1559 uint64_t cbSize;
1560 rc = RTFileQuerySize(papszFiles[i], &cbSize);
1561 if (RT_FAILURE(rc))
1562 break;
1563 cbOverallSize += cbSize;
1564 }
1565 uint64_t cbOverallWritten = 0;
1566 for (size_t i = 0; i < cFiles; ++i)
1567 {
1568 rc = rtTarAppendFileFromFile(hTar, papszFiles[i], cbOverallSize, cbOverallWritten, pfnProgressCallback, pvUser);
1569 if (RT_FAILURE(rc))
1570 break;
1571 }
1572
1573 /* Cleanup */
1574 RTTarClose(hTar);
1575
1576 return rc;
1577}
1578
1579/******************************************************************************
1580 * Streaming Functions *
1581 ******************************************************************************/
1582
1583RTR3DECL(int) RTTarCurrentFile(RTTAR hTar, char **ppszFilename)
1584{
1585 /* Validate input. */
1586 AssertPtrNullReturn(ppszFilename, VERR_INVALID_POINTER);
1587
1588 PRTTARINTERNAL pInt = hTar;
1589 RTTAR_VALID_RETURN(pInt);
1590
1591 /* Open and close the file on the current position. This makes sure the
1592 * cache is filled in case we never read something before. On success it
1593 * will return the current filename. */
1594 RTTARFILE hFile;
1595 int rc = RTTarFileOpenCurrentFile(hTar, &hFile, ppszFilename, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
1596 if (RT_SUCCESS(rc))
1597 RTTarFileClose(hFile);
1598
1599 return rc;
1600}
1601
1602RTR3DECL(int) RTTarSeekNextFile(RTTAR hTar)
1603{
1604 PRTTARINTERNAL pInt = hTar;
1605 RTTAR_VALID_RETURN(pInt);
1606
1607 int rc = VINF_SUCCESS;
1608
1609 if (!pInt->fStreamMode)
1610 return VERR_INVALID_STATE;
1611
1612 /* If there is nothing in the cache, it means we never read something. Just
1613 * ask for the current filename to fill the cache. */
1614 if (!pInt->pFileCache)
1615 {
1616 rc = RTTarCurrentFile(hTar, NULL);
1617 if (RT_FAILURE(rc))
1618 return rc;
1619 }
1620
1621 /* Check that the file pointer is somewhere within the last open file.
1622 * If not we are somehow busted. */
1623 uint64_t offCur = RTFileTell(pInt->hTarFile);
1624 if (!( pInt->pFileCache->offStart <= offCur
1625 && offCur <= pInt->pFileCache->offStart + sizeof(RTTARRECORD) + pInt->pFileCache->cbSize))
1626 return VERR_INVALID_STATE;
1627
1628 /* Seek to the next file header. */
1629 uint64_t offNext = RT_ALIGN(pInt->pFileCache->offStart + sizeof(RTTARRECORD) + pInt->pFileCache->cbSize, sizeof(RTTARRECORD));
1630 if (pInt->pFileCache->cbSize != 0)
1631 {
1632 rc = RTFileSeek(pInt->hTarFile, offNext - offCur, RTFILE_SEEK_CURRENT, NULL);
1633 if (RT_FAILURE(rc))
1634 return rc;
1635 }
1636 else
1637 {
1638 /* Else delete the last open file cache. Might be recreated below. */
1639 rtDeleteTarFileInternal(pInt->pFileCache);
1640 pInt->pFileCache = NULL;
1641 }
1642
1643 /* Again check the current filename to fill the cache with the new value. */
1644 return RTTarCurrentFile(hTar, NULL);
1645}
1646
1647RTR3DECL(int) RTTarFileOpenCurrentFile(RTTAR hTar, PRTTARFILE phFile, char **ppszFilename, uint32_t fOpen)
1648{
1649 /* Validate input. */
1650 AssertPtrReturn(phFile, VERR_INVALID_POINTER);
1651 AssertPtrNullReturn(ppszFilename, VERR_INVALID_POINTER);
1652 AssertReturn((fOpen & RTFILE_O_READ), VERR_INVALID_PARAMETER); /* Only valid in read mode. */
1653
1654 PRTTARINTERNAL pInt = hTar;
1655 RTTAR_VALID_RETURN(pInt);
1656
1657 if (!pInt->fStreamMode)
1658 return VERR_INVALID_STATE;
1659
1660 int rc = VINF_SUCCESS;
1661
1662 /* Is there some cached entry? */
1663 if (pInt->pFileCache)
1664 {
1665 if (pInt->pFileCache->offStart + sizeof(RTTARRECORD) < RTFileTell(pInt->hTarFile))
1666 {
1667 /* Else delete the last open file cache. Might be recreated below. */
1668 rtDeleteTarFileInternal(pInt->pFileCache);
1669 pInt->pFileCache = NULL;
1670 }
1671 else/* Are we still direct behind that header? */
1672 {
1673 /* Yes, so the streaming can start. Just return the cached file
1674 * structure to the caller. */
1675 *phFile = rtCopyTarFileInternal(pInt->pFileCache);
1676 if (ppszFilename)
1677 *ppszFilename = RTStrDup(pInt->pFileCache->pszFilename);
1678 if (pInt->pFileCache->linkflag == LF_DIR)
1679 return VINF_TAR_DIR_PATH;
1680 else
1681 return VINF_SUCCESS;
1682 }
1683
1684 }
1685
1686 PRTTARFILEINTERNAL pFileInt = NULL;
1687 do /* break loop */
1688 {
1689 /* Try to read a header entry from the current position. If we aren't
1690 * on a header record, the header checksum will show and an error will
1691 * be returned. */
1692 RTTARRECORD record;
1693 /* Read & verify a header record */
1694 rc = rtTarReadHeaderRecord(pInt->hTarFile, &record);
1695 /* Check for error or EOF. */
1696 if (RT_FAILURE(rc))
1697 break;
1698
1699 /* We support normal files only */
1700 if ( record.h.linkflag == LF_OLDNORMAL
1701 || record.h.linkflag == LF_NORMAL
1702 || record.h.linkflag == LF_DIR)
1703 {
1704 pFileInt = rtCreateTarFileInternal(pInt, record.h.name, fOpen);
1705 if (!pFileInt)
1706 {
1707 rc = VERR_NO_MEMORY;
1708 break;
1709 }
1710
1711 /* Get the file size */
1712 pFileInt->cbSize = rtTarRecToSize(&record);
1713 /* The start is -512 from here. */
1714 pFileInt->offStart = RTFileTell(pInt->hTarFile) - sizeof(RTTARRECORD);
1715 /* remember the type of a file */
1716 pFileInt->linkflag = record.h.linkflag;
1717
1718 /* Copy the new file structure to our cache. */
1719 pInt->pFileCache = rtCopyTarFileInternal(pFileInt);
1720 if (ppszFilename)
1721 *ppszFilename = RTStrDup(pFileInt->pszFilename);
1722
1723 if (pFileInt->linkflag == LF_DIR)
1724 {
1725 rc = VINF_TAR_DIR_PATH;
1726 }
1727 }
1728 } while (0);
1729
1730 if (RT_FAILURE(rc))
1731 {
1732 if (pFileInt)
1733 rtDeleteTarFileInternal(pFileInt);
1734 }
1735 else
1736 *phFile = pFileInt;
1737
1738 return rc;
1739}
1740
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette