VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/tar.cpp@ 33058

Last change on this file since 33058 was 33058, checked in by vboxsync, 14 years ago

Runtime: RTTar setSize fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.4 KB
Line 
1/* $Id: tar.cpp 33058 2010-10-12 12:12:18Z vboxsync $ */
2/** @file
3 * IPRT - Tar archive I/O.
4 */
5
6/*
7 * Copyright (C) 2009-2010 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 "internal/magics.h"
33#include <iprt/tar.h>
34
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/err.h>
38#include <iprt/file.h>
39#include <iprt/mem.h>
40#include <iprt/path.h>
41#include <iprt/string.h>
42
43/******************************************************************************
44 * Structures and Typedefs *
45 ******************************************************************************/
46
47/** @name RTTARRECORD::h::linkflag
48 * @{ */
49#define LF_OLDNORMAL '\0' /**< Normal disk file, Unix compatible */
50#define LF_NORMAL '0' /**< Normal disk file */
51#define LF_LINK '1' /**< Link to previously dumped file */
52#define LF_SYMLINK '2' /**< Symbolic link */
53#define LF_CHR '3' /**< Character special file */
54#define LF_BLK '4' /**< Block special file */
55#define LF_DIR '5' /**< Directory */
56#define LF_FIFO '6' /**< FIFO special file */
57#define LF_CONTIG '7' /**< Contiguous file */
58/** @} */
59
60typedef union RTTARRECORD
61{
62 char d[512];
63 struct h
64 {
65 char name[100];
66 char mode[8];
67 char uid[8];
68 char gid[8];
69 char size[12];
70 char mtime[12];
71 char chksum[8];
72 char linkflag;
73 char linkname[100];
74 char magic[8];
75 char uname[32];
76 char gname[32];
77 char devmajor[8];
78 char devminor[8];
79 } h;
80} RTTARRECORD;
81typedef RTTARRECORD *PRTTARRECORD;
82AssertCompileSize(RTTARRECORD, 512);
83AssertCompileMemberOffset(RTTARRECORD, h.size, 100+8*3);
84
85#if 0 /* not currently used */
86typedef struct RTTARFILELIST
87{
88 char *pszFilename;
89 RTTARFILELIST *pNext;
90} RTTARFILELIST;
91typedef RTTARFILELIST *PRTTARFILELIST;
92#endif
93
94typedef struct RTTARINTERNAL
95{
96 uint32_t u32Magic;
97 RTFILE hTarFile;
98 bool fFileOpenForWrite;
99 uint32_t fOpenMode;
100} RTTARINTERNAL;
101typedef RTTARINTERNAL* PRTTARINTERNAL;
102
103typedef struct RTTARFILEINTERNAL
104{
105 uint32_t u32Magic;
106 PRTTARINTERNAL pTar;
107 char *pszFilename;
108 uint64_t uStart;
109 uint64_t cbSize;
110 uint64_t cbSetSize;
111 uint64_t uCurrentPos;
112 uint32_t fOpenMode;
113} RTTARFILEINTERNAL;
114typedef RTTARFILEINTERNAL* PRTTARFILEINTERNAL;
115
116/******************************************************************************
117 * Defined Constants And Macros *
118 ******************************************************************************/
119
120/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
121/* RTTAR */
122#define RTTAR_VALID_RETURN_RC(hHandle, rc) \
123 do { \
124 AssertPtrReturn((hHandle), (rc)); \
125 AssertReturn((hHandle)->u32Magic == RTTAR_MAGIC, (rc)); \
126 } while (0)
127/* RTTARFILE */
128#define RTTARFILE_VALID_RETURN_RC(hHandle, rc) \
129 do { \
130 AssertPtrReturn((hHandle), (rc)); \
131 AssertReturn((hHandle)->u32Magic == RTTARFILE_MAGIC, (rc)); \
132 } while (0)
133
134/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
135/* RTTAR */
136#define RTTAR_VALID_RETURN(hHandle) RTTAR_VALID_RETURN_RC((hHandle), VERR_INVALID_HANDLE)
137/* RTTARFILE */
138#define RTTARFILE_VALID_RETURN(hHandle) RTTARFILE_VALID_RETURN_RC((hHandle), VERR_INVALID_HANDLE)
139
140/** Validates a handle and returns (void) if not valid. */
141/* RTTAR */
142#define RTTAR_VALID_RETURN_VOID(hHandle) \
143 do { \
144 AssertPtrReturnVoid(hHandle); \
145 AssertReturnVoid((hHandle)->u32Magic == RTTAR_MAGIC); \
146 } while (0)
147/* RTTARFILE */
148#define RTTARFILE_VALID_RETURN_VOID(hHandle) \
149 do { \
150 AssertPtrReturnVoid(hHandle); \
151 AssertReturnVoid((hHandle)->u32Magic == RTTARFILE_MAGIC); \
152 } while (0)
153
154/******************************************************************************
155 * Internal Functions *
156 ******************************************************************************/
157
158DECLINLINE(int) rtTarCalcChkSum(PRTTARRECORD pRecord, uint32_t *pChkSum)
159{
160 uint32_t check = 0;
161 uint32_t zero = 0;
162 for (size_t i = 0; i < sizeof(RTTARRECORD); ++i)
163 {
164 /* Calculate the sum of every byte from the header. The checksum field
165 * itself is counted as all blanks. */
166 if ( i < RT_UOFFSETOF(RTTARRECORD, h.chksum)
167 || i >= RT_UOFFSETOF(RTTARRECORD, h.linkflag))
168 check += pRecord->d[i];
169 else
170 check += ' ';
171 /* Additional check if all fields are zero, which indicate EOF. */
172 zero += pRecord->d[i];
173 }
174
175 /* EOF? */
176 if (!zero)
177 return VERR_TAR_END_OF_FILE;
178
179 *pChkSum = check;
180 return VINF_SUCCESS;
181}
182
183DECLINLINE(int) rtTarReadHeaderRecord(RTFILE hFile, PRTTARRECORD pRecord)
184{
185 int rc = RTFileRead(hFile, pRecord, sizeof(RTTARRECORD), NULL);
186 /* Check for EOF. EOF is valid in this case, cause it indicates no more
187 * data in the tar archive. */
188 if (rc == VERR_EOF)
189 return VERR_TAR_END_OF_FILE;
190 /* Report any other errors */
191 else if (RT_FAILURE(rc))
192 return rc;
193
194 /* Check for data integrity & an EOF record */
195 uint32_t check = 0;
196 rc = rtTarCalcChkSum(pRecord, &check);
197 /* EOF? */
198 if (RT_FAILURE(rc))
199 return rc;
200
201 /* Verify the checksum */
202 uint32_t sum;
203 rc = RTStrToUInt32Full(pRecord->h.chksum, 8, &sum);
204 if (RT_SUCCESS(rc) && sum == check)
205 return VINF_SUCCESS;
206 return VERR_TAR_CHKSUM_MISMATCH;
207}
208
209DECLINLINE(int) rtTarCreateHeaderRecord(PRTTARRECORD pRecord, const char *pszSrcName, uint64_t cbSize, RTUID uid, RTGID gid, RTFMODE fmode, int64_t mtime)
210{
211 /* Fill the header record */
212// RT_ZERO(pRecord);
213 RTStrPrintf(pRecord->h.name, sizeof(pRecord->h.name), "%s", pszSrcName);
214 RTStrPrintf(pRecord->h.mode, sizeof(pRecord->h.mode), "%0.7o", fmode);
215 RTStrPrintf(pRecord->h.uid, sizeof(pRecord->h.uid), "%0.7o", uid);
216 RTStrPrintf(pRecord->h.gid, sizeof(pRecord->h.gid), "%0.7o", gid);
217 RTStrPrintf(pRecord->h.size, sizeof(pRecord->h.size), "%0.11o", cbSize);
218 RTStrPrintf(pRecord->h.mtime, sizeof(pRecord->h.mtime), "%0.11o", mtime);
219 RTStrPrintf(pRecord->h.magic, sizeof(pRecord->h.magic), "ustar ");
220 RTStrPrintf(pRecord->h.uname, sizeof(pRecord->h.uname), "someone");
221 RTStrPrintf(pRecord->h.gname, sizeof(pRecord->h.gname), "someone");
222 pRecord->h.linkflag = LF_NORMAL;
223
224 /* Create the checksum out of the new header */
225 uint32_t chksum = 0;
226 int rc = rtTarCalcChkSum(pRecord, &chksum);
227 if (RT_FAILURE(rc))
228 return rc;
229 /* Format the checksum */
230 RTStrPrintf(pRecord->h.chksum, sizeof(pRecord->h.chksum), "%0.7o", chksum);
231
232 return VINF_SUCCESS;
233}
234
235DECLINLINE(void*) rtTarMemTmpAlloc(size_t *pcbSize)
236{
237 *pcbSize = 0;
238 /* Allocate a reasonably large buffer, fall back on a tiny one. Note:
239 * has to be 512 byte aligned and >= 512 byte. */
240 size_t cbTmp = _1M;
241 void *pvTmp = RTMemTmpAlloc(cbTmp);
242 if (!pvTmp)
243 {
244 cbTmp = sizeof(RTTARRECORD);
245 pvTmp = RTMemTmpAlloc(cbTmp);
246 }
247 *pcbSize = cbTmp;
248 return pvTmp;
249}
250
251DECLINLINE(int) rtTarAppendZeros(RTTARFILE hFile, uint64_t cbSize)
252{
253 /* Allocate a temporary buffer for copying the tar content in blocks. */
254 size_t cbTmp = 0;
255 void *pvTmp = rtTarMemTmpAlloc(&cbTmp);
256 if (!pvTmp)
257 return VERR_NO_MEMORY;
258 RT_BZERO(pvTmp, cbTmp);
259
260 int rc = VINF_SUCCESS;
261 uint64_t cbAllWritten = 0;
262 size_t cbWritten = 0;
263 for (;;)
264 {
265 if (cbAllWritten >= cbSize)
266 break;
267 size_t cbToWrite = RT_MIN(cbSize - cbAllWritten, cbTmp);
268 rc = RTTarFileWrite(hFile, pvTmp, cbToWrite, &cbWritten);
269 if (RT_FAILURE(rc))
270 break;
271 cbAllWritten += cbWritten;
272 }
273
274 RTMemTmpFree(pvTmp);
275
276 return rc;
277}
278
279static int rtTarExtractFileToFile(RTTARFILE hFile, const char *pszTargetName, const uint64_t cbOverallSize, uint64_t &cbOverallWritten, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
280{
281 RTFILE hNewFile;
282 /* Open the target file */
283 int rc = RTFileOpen(&hNewFile, pszTargetName, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
284 if (RT_FAILURE(rc))
285 return rc;
286
287 void *pvTmp = 0;
288 do
289 {
290 /* Allocate a temporary buffer for reading the tar content in blocks. */
291 size_t cbTmp = 0;
292 pvTmp = rtTarMemTmpAlloc(&cbTmp);
293 if (!pvTmp)
294 {
295 rc = VERR_NO_MEMORY;
296 break;
297 }
298 /* Get the size of the source file */
299 uint64_t cbToCopy = 0;
300 rc = RTTarFileGetSize(hFile, &cbToCopy);
301 if (RT_FAILURE(rc))
302 break;
303 /* Copy the content from hFile over to pszTargetName. */
304 uint64_t cbAllWritten = 0; /* Already copied */
305 uint64_t cbRead = 0; /* Actually read in the last step */
306 for (;;)
307 {
308 if (pfnProgressCallback)
309 pfnProgressCallback((unsigned)(100.0 / cbOverallSize * cbOverallWritten), pvUser);
310 /* Finished already? */
311 if (cbAllWritten == cbToCopy)
312 break;
313 /* Read one block. */
314 cbRead = RT_MIN(cbToCopy - cbAllWritten, cbTmp);
315 rc = RTTarFileRead(hFile, pvTmp, cbRead, NULL);
316 if (RT_FAILURE(rc))
317 break;
318 /* Write the block */
319 rc = RTFileWrite(hNewFile, pvTmp, cbRead, NULL);
320 if (RT_FAILURE(rc))
321 break;
322 /* Count how many bytes are written already */
323 cbAllWritten += cbRead;
324 cbOverallWritten += cbRead;
325 }
326
327 }
328 while(0);
329
330 /* Cleanup */
331 if (pvTmp)
332 RTMemTmpFree(pvTmp);
333
334 /* Now set all file attributes */
335 if (RT_SUCCESS(rc))
336 {
337 uint32_t mode;
338 rc = RTTarFileGetMode(hFile, &mode);
339 if (RT_SUCCESS(rc))
340 {
341 mode |= RTFS_TYPE_FILE; /* For now we support regular files only */
342 /* Set the mode */
343 rc = RTFileSetMode(hNewFile, mode);
344 }
345 }
346
347 RTFileClose(hNewFile);
348
349 /* Delete the freshly created file in the case of an error */
350 if (RT_FAILURE(rc))
351 RTFileDelete(pszTargetName);
352
353 return rc;
354}
355
356static int rtTarAppendFileFromFile(RTTAR hTar, const char *pszSrcName, const uint64_t cbOverallSize, uint64_t &cbOverallWritten, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
357{
358 RTFILE hOldFile;
359 /* Open the source file */
360 int rc = RTFileOpen(&hOldFile, pszSrcName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
361 if (RT_FAILURE(rc))
362 return rc;
363
364 RTTARFILE hFile = NIL_RTTARFILE;
365 void *pvTmp = 0;
366 do
367 {
368 /* Get the size of the source file */
369 uint64_t cbToCopy;
370 rc = RTFileGetSize(hOldFile, &cbToCopy);
371 if (RT_FAILURE(rc))
372 break;
373
374 rc = RTTarFileOpen(hTar, &hFile, RTPathFilename(pszSrcName), RTFILE_O_OPEN | RTFILE_O_WRITE);
375 if (RT_FAILURE(rc))
376 break;
377
378 /* Get some info from the source file */
379 RTFSOBJINFO info;
380 RTUID uid = 0;
381 RTGID gid = 0;
382 RTFMODE fmode = 0600; /* Make some save default */
383 int64_t mtime = 0;
384 /* This isn't critical. Use the defaults if it fails. */
385 rc = RTFileQueryInfo(hOldFile, &info, RTFSOBJATTRADD_UNIX);
386 if (RT_SUCCESS(rc))
387 {
388 fmode = info.Attr.fMode & RTFS_UNIX_MASK;
389 uid = info.Attr.u.Unix.uid;
390 gid = info.Attr.u.Unix.gid;
391 mtime = RTTimeSpecGetSeconds(&info.ModificationTime);
392 }
393 /* Set the mode from the other file */
394 rc = RTTarFileSetMode(hFile, fmode);
395 if (RT_FAILURE(rc))
396 break;
397 /* Set the modification time from the other file */
398 RTTIMESPEC time;
399 RTTimeSpecSetSeconds(&time, mtime);
400 rc = RTTarFileSetTime(hFile, &time);
401 if (RT_FAILURE(rc))
402 break;
403 /* Set the owner from the other file */
404 rc = RTTarFileSetOwner(hFile, uid, gid);
405 if (RT_FAILURE(rc))
406 break;
407
408 /* Allocate a temporary buffer for copying the tar content in blocks. */
409 size_t cbTmp = 0;
410 pvTmp = rtTarMemTmpAlloc(&cbTmp);
411 if (!pvTmp)
412 {
413 rc = VERR_NO_MEMORY;
414 break;
415 }
416
417 uint64_t cbAllWritten = 0; /* Already copied */
418 uint64_t cbRead = 0; /* Actually read in the last step */
419 uint64_t cbWrite = 0; /* Actually write in the last step */
420 /* Copy the content from pszSrcName over to hFile. This is done block
421 * wise in 512 byte steps. After this copying is finished hFile will be
422 * on a 512 byte boundary, regardless if the file copied is 512 byte
423 * size aligned. */
424 for (;;)
425 {
426 if (pfnProgressCallback)
427 pfnProgressCallback((unsigned)(100.0 / cbOverallSize * cbOverallWritten), pvUser);
428 if (cbAllWritten >= cbToCopy)
429 break;
430 /* Read one block. Either its the buffer size or the rest of the
431 * file. */
432 cbRead = RT_MIN(cbToCopy - cbAllWritten, cbTmp);
433 /* Read one block */
434 rc = RTFileRead(hOldFile, pvTmp, cbRead, NULL);
435 if (RT_FAILURE(rc))
436 break;
437 /* Write one block. */
438 rc = RTTarFileWriteAt(hFile, cbAllWritten, pvTmp, cbRead, NULL);
439 if (RT_FAILURE(rc))
440 break;
441 /* Count how many bytes (of the original file) are written already */
442 cbAllWritten += cbRead;
443 cbOverallWritten += cbRead;
444 }
445 }
446 while(0);
447
448 /* Cleanup */
449 if (pvTmp)
450 RTMemTmpFree(pvTmp);
451
452 if (hFile)
453 RTTarFileClose(hFile);
454
455 RTFileClose(hOldFile);
456
457 return rc;
458}
459
460static int rtTarSkipData(RTFILE hFile, PRTTARRECORD pRecord)
461{
462 int rc = VINF_SUCCESS;
463 /* Seek over the data parts (512 bytes aligned) */
464 int64_t offSeek = RT_ALIGN(RTStrToInt64(pRecord->h.size), sizeof(RTTARRECORD));
465 if (offSeek > 0)
466 rc = RTFileSeek(hFile, offSeek, RTFILE_SEEK_CURRENT, NULL);
467 return rc;
468}
469
470static int rtTarFindFile(RTFILE hFile, const char *pszFile, uint64_t *puOffset, uint64_t *pcbSize)
471{
472 /* Assume we are on the file head. */
473 int rc = VINF_SUCCESS;
474 bool fFound = false;
475 RTTARRECORD record;
476 for (;;)
477 {
478 /* Read & verify a header record */
479 rc = rtTarReadHeaderRecord(hFile, &record);
480 /* Check for error or EOF. */
481 if (RT_FAILURE(rc))
482 break;
483 /* We support normal files only */
484 if ( record.h.linkflag == LF_OLDNORMAL
485 || record.h.linkflag == LF_NORMAL)
486 {
487 if (!RTStrCmp(record.h.name, pszFile))
488 {
489 /* Get the file size */
490 rc = RTStrToUInt64Full(record.h.size, 8, pcbSize);
491 if (RT_FAILURE(rc))
492 break;
493 /* Seek back, to positionate the file pointer at the start of the header. */
494 rc = RTFileSeek(hFile, -(int64_t)sizeof(RTTARRECORD), RTFILE_SEEK_CURRENT, puOffset);
495 fFound = true;
496 break;
497 }
498 }
499 rc = rtTarSkipData(hFile, &record);
500 if (RT_FAILURE(rc))
501 break;
502 }
503
504 if (rc == VERR_TAR_END_OF_FILE)
505 rc = VINF_SUCCESS;
506
507 /* Something found? */
508 if ( RT_SUCCESS(rc)
509 && !fFound)
510 rc = VERR_FILE_NOT_FOUND;
511
512 return rc;
513}
514
515static int rtTarGetFilesOverallSize(RTFILE hFile, const char * const *papszFiles, size_t cFiles, uint64_t *pcbOverallSize)
516{
517 int rc = VINF_SUCCESS;
518 size_t cFound = 0;
519 RTTARRECORD record;
520 for (;;)
521 {
522 /* Read & verify a header record */
523 rc = rtTarReadHeaderRecord(hFile, &record);
524 /* Check for error or EOF. */
525 if (RT_FAILURE(rc))
526 break;
527 /* We support normal files only */
528 if ( record.h.linkflag == LF_OLDNORMAL
529 || record.h.linkflag == LF_NORMAL)
530 {
531 for (size_t i = 0; i < cFiles; ++i)
532 {
533 if (!RTStrCmp(record.h.name, papszFiles[i]))
534 {
535 uint64_t cbSize;
536 /* Get the file size */
537 rc = RTStrToUInt64Full(record.h.size, 8, &cbSize);
538 /* Sum up the overall size */
539 *pcbOverallSize += cbSize;
540 ++cFound;
541 break;
542 }
543 }
544 if ( cFound == cFiles
545 || RT_FAILURE(rc))
546 break;
547 }
548 rc = rtTarSkipData(hFile, &record);
549 if (RT_FAILURE(rc))
550 break;
551 }
552 if (rc == VERR_TAR_END_OF_FILE)
553 rc = VINF_SUCCESS;
554
555 /* Make sure the file pointer is at the begin of the file again. */
556 if (RT_SUCCESS(rc))
557 rc = RTFileSeek(hFile, 0, RTFILE_SEEK_BEGIN, 0);
558 return rc;
559}
560
561/******************************************************************************
562 * Public Functions *
563 ******************************************************************************/
564
565RTR3DECL(int) RTTarOpen(PRTTAR phTar, const char* pszTarname, uint32_t fMode)
566{
567 PRTTARINTERNAL pInt = (PRTTARINTERNAL)RTMemAllocZ(sizeof(RTTARINTERNAL));
568 if (!pInt)
569 return VERR_NO_MEMORY;
570
571 pInt->u32Magic = RTTAR_MAGIC;
572 pInt->fOpenMode = fMode;
573
574 int rc = VINF_SUCCESS;
575 do
576 {
577 /* Open the tar file. */
578 rc = RTFileOpen(&pInt->hTarFile, pszTarname, fMode);
579 if (RT_FAILURE(rc))
580 break;
581 }
582 while(0);
583
584 if (RT_FAILURE(rc))
585 {
586 /* Todo: remove if created by us */
587 if (pInt->hTarFile)
588 RTFileClose(pInt->hTarFile);
589 RTMemFree(pInt);
590 }else
591 *phTar = (RTTAR)pInt;
592
593 return rc;
594}
595
596RTR3DECL(int) RTTarClose(RTTAR hTar)
597{
598 if (hTar == NIL_RTTAR)
599 return VINF_SUCCESS;
600
601 PRTTARINTERNAL pInt = hTar;
602 RTTAR_VALID_RETURN(pInt);
603
604 int rc = VINF_SUCCESS;
605
606 /* gtar gives a warning, but the documentation says EOF is indicated by a
607 * zero block. Disabled for now. */
608#if 0
609 {
610 /* Append the EOF record which is filled all by zeros */
611 RTTARRECORD record;
612 RT_ZERO(record);
613 rc = RTFileWrite(pInt->hTarFile, &record, sizeof(record), NULL);
614 }
615#endif
616
617 if (pInt->hTarFile)
618 rc = RTFileClose(pInt->hTarFile);
619
620 pInt->u32Magic = RTTAR_MAGIC_DEAD;
621
622 RTMemFree(pInt);
623
624 return rc;
625}
626
627RTR3DECL(int) RTTarFileOpen(RTTAR hTar, PRTTARFILE phFile, const char *pszFilename, uint32_t fOpen)
628{
629 AssertReturn((fOpen & RTFILE_O_READ) || (fOpen & RTFILE_O_WRITE), VERR_INVALID_PARAMETER);
630
631 PRTTARINTERNAL pInt = hTar;
632 RTTAR_VALID_RETURN(pInt);
633
634 if (!pInt->hTarFile)
635 return VERR_INVALID_HANDLE;
636
637 if (fOpen & RTFILE_O_WRITE)
638 {
639 if (!(pInt->fOpenMode & RTFILE_O_WRITE))
640 return VERR_WRITE_PROTECT;
641 if (pInt->fFileOpenForWrite)
642 return VERR_TOO_MANY_OPEN_FILES;
643 }
644
645 PRTTARFILEINTERNAL pFileInt = (PRTTARFILEINTERNAL)RTMemAllocZ(sizeof(RTTARFILEINTERNAL));
646 if (!pFileInt)
647 return VERR_NO_MEMORY;
648
649 pFileInt->u32Magic = RTTARFILE_MAGIC;
650
651 int rc = VINF_SUCCESS;
652 do
653 {
654 pFileInt->pTar = pInt;
655 pFileInt->pszFilename = RTStrDup(pszFilename);
656 pFileInt->uStart = 0;
657 pFileInt->cbSize = 0;
658 pFileInt->cbSetSize = 0;
659 pFileInt->fOpenMode = fOpen;
660 pFileInt->uCurrentPos = 0;
661
662 if (pFileInt->fOpenMode & RTFILE_O_WRITE)
663 {
664 pInt->fFileOpenForWrite = true;
665 /* If we are in write mode, we also in append mode. Add an dummy
666 * header at the end of the current file. It will be filled by the
667 * close operation. */
668 rc = RTFileSeek(pFileInt->pTar->hTarFile, 0, RTFILE_SEEK_END, &pFileInt->uStart);
669 if (RT_FAILURE(rc))
670 break;
671 RTTARRECORD record;
672 RT_ZERO(record);
673 rc = RTFileWrite(pFileInt->pTar->hTarFile, &record, sizeof(RTTARRECORD), NULL);
674 if (RT_FAILURE(rc))
675 break;
676 }
677 else if (pFileInt->fOpenMode & RTFILE_O_READ)
678 {
679 /* We need to be on the start of the file */
680 rc = RTFileSeek(pFileInt->pTar->hTarFile, 0, RTFILE_SEEK_BEGIN, NULL);
681 if (RT_FAILURE(rc))
682 break;
683 /* Search for the file. */
684 rc = rtTarFindFile(pFileInt->pTar->hTarFile, pszFilename, &pFileInt->uStart, &pFileInt->cbSize);
685 if (RT_FAILURE(rc))
686 break;
687 }
688 else
689 {
690 }
691
692 }
693 while(0);
694
695 /* Cleanup on failure */
696 if (RT_FAILURE(rc))
697 {
698 if (pFileInt->pszFilename)
699 RTStrFree(pFileInt->pszFilename);
700 RTMemFree(pFileInt);
701 }
702 else
703 *phFile = (RTTARFILE)pFileInt;
704
705 return rc;
706}
707
708RTR3DECL(int) RTTarFileClose(RTTARFILE hFile)
709{
710 /* Already closed? */
711 if (hFile == NIL_RTTARFILE)
712 return VINF_SUCCESS;
713
714 PRTTARFILEINTERNAL pFileInt = hFile;
715 RTTARFILE_VALID_RETURN(pFileInt);
716
717 int rc = VINF_SUCCESS;
718
719 /* In write mode: */
720 if (pFileInt->fOpenMode & RTFILE_O_WRITE)
721 {
722 pFileInt->pTar->fFileOpenForWrite = false;
723 do
724 {
725 /* If the user has called RTTarFileSetSize in the meantime, we have
726 to make sure the file has the right size. */
727 if (pFileInt->cbSetSize > pFileInt->cbSize)
728 {
729 rc = rtTarAppendZeros(hFile, pFileInt->cbSetSize - pFileInt->cbSize);
730 if (RT_FAILURE(rc))
731 break;
732 }
733 /* If the written size isn't 512 byte aligned, we need to fix this. */
734 RTTARRECORD record;
735 RT_ZERO(record);
736 uint64_t cbSizeAligned = RT_ALIGN(pFileInt->cbSize, sizeof(RTTARRECORD));
737 if (cbSizeAligned != pFileInt->cbSize)
738 {
739 /* Note the RTFile method. We didn't increase the cbSize or cbCurrentPos here. */
740 rc = RTFileWriteAt(pFileInt->pTar->hTarFile, pFileInt->uStart + sizeof(RTTARRECORD) + pFileInt->cbSize, &record, cbSizeAligned - pFileInt->cbSize, NULL);
741 if (RT_FAILURE(rc))
742 break;
743 }
744 /* Create a header record for the file */
745 /* Todo: mode, gid, uid, mtime should be setable (or detected myself) */
746 RTTIMESPEC time;
747 RTTimeNow(&time);
748 rc = rtTarCreateHeaderRecord(&record, pFileInt->pszFilename, pFileInt->cbSize, 0, 0, 0600, RTTimeSpecGetSeconds(&time));
749 if (RT_FAILURE(rc))
750 break;
751 /* Write this at the start of the file data */
752 rc = RTFileWriteAt(pFileInt->pTar->hTarFile, pFileInt->uStart, &record, sizeof(RTTARRECORD), NULL);
753 if (RT_FAILURE(rc))
754 break;
755 }
756 while(0);
757 }
758 /* Nothing special in readmode. */
759
760 /* Now cleanup and delete the handle */
761 RTStrFree(pFileInt->pszFilename);
762 pFileInt->u32Magic = RTTARFILE_MAGIC_DEAD;
763 RTMemFree(pFileInt);
764
765 return rc;
766}
767
768RTR3DECL(int) RTTarFileSeek(RTTARFILE hFile, uint64_t uOffset, unsigned uMethod, uint64_t *poffActual)
769{
770 PRTTARFILEINTERNAL pFileInt = hFile;
771 RTTARFILE_VALID_RETURN(pFileInt);
772
773 switch (uMethod)
774 {
775 case RTFILE_SEEK_BEGIN:
776 {
777 if (uOffset > pFileInt->cbSize)
778 return VERR_SEEK_ON_DEVICE;
779 pFileInt->uCurrentPos = uOffset;
780 break;
781 }
782 case RTFILE_SEEK_CURRENT:
783 {
784 if (pFileInt->uCurrentPos + uOffset > pFileInt->cbSize)
785 return VERR_SEEK_ON_DEVICE;
786 pFileInt->uCurrentPos += uOffset;
787 break;
788 }
789 case RTFILE_SEEK_END:
790 {
791 if ((int64_t)pFileInt->cbSize - (int64_t)uOffset < 0)
792 return VERR_NEGATIVE_SEEK;
793 pFileInt->uCurrentPos = pFileInt->cbSize - uOffset;
794 break;
795 }
796 default: AssertFailedReturn(VERR_INVALID_PARAMETER); break;
797 }
798
799 return VINF_SUCCESS;
800}
801
802RTR3DECL(uint64_t) RTTarFileTell(RTTARFILE hFile)
803{
804 PRTTARFILEINTERNAL pFileInt = hFile;
805 RTTARFILE_VALID_RETURN_RC(pFileInt, ~0ULL);
806
807 return pFileInt->uCurrentPos;
808}
809
810RTR3DECL(int) RTTarFileRead(RTTARFILE hFile, void *pvBuf, size_t cbToRead, size_t *pcbRead)
811{
812 PRTTARFILEINTERNAL pFileInt = hFile;
813 RTTARFILE_VALID_RETURN(pFileInt);
814
815 /* Todo: optimize this, by checking the current pos */
816 return RTTarFileReadAt(hFile, pFileInt->uCurrentPos, pvBuf, cbToRead, pcbRead);
817}
818
819RTR3DECL(int) RTTarFileReadAt(RTTARFILE hFile, uint64_t uOffset, void *pvBuf, size_t cbToRead, size_t *pcbRead)
820{
821 PRTTARFILEINTERNAL pFileInt = hFile;
822 RTTARFILE_VALID_RETURN(pFileInt);
823
824 size_t cbTmpRead = 0;
825 int rc = RTFileReadAt(pFileInt->pTar->hTarFile, pFileInt->uStart + 512 + uOffset, pvBuf, cbToRead, &cbTmpRead);
826 pFileInt->uCurrentPos = uOffset + cbTmpRead;
827 if (pcbRead)
828 *pcbRead = cbTmpRead;
829
830 return rc;
831}
832
833RTR3DECL(int) RTTarFileWrite(RTTARFILE hFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
834{
835 PRTTARFILEINTERNAL pFileInt = hFile;
836 RTTARFILE_VALID_RETURN(pFileInt);
837
838 /* Todo: optimize this, by checking the current pos */
839 return RTTarFileWriteAt(hFile, pFileInt->uCurrentPos, pvBuf, cbToWrite, pcbWritten);
840}
841
842RTR3DECL(int) RTTarFileWriteAt(RTTARFILE hFile, uint64_t uOffset, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
843{
844 PRTTARFILEINTERNAL pFileInt = hFile;
845 RTTARFILE_VALID_RETURN(pFileInt);
846
847 if ((pFileInt->fOpenMode & RTFILE_O_WRITE) != RTFILE_O_WRITE)
848 return VERR_WRITE_ERROR;
849
850 size_t cbTmpWritten = 0;
851 int rc = RTFileWriteAt(pFileInt->pTar->hTarFile, pFileInt->uStart + 512 + uOffset, pvBuf, cbToWrite, &cbTmpWritten);
852 pFileInt->cbSize += cbTmpWritten;
853 pFileInt->uCurrentPos = uOffset + cbTmpWritten;
854 if (pcbWritten)
855 *pcbWritten = cbTmpWritten;
856
857 return rc;
858}
859
860RTR3DECL(int) RTTarFileGetSize(RTTARFILE hFile, uint64_t *pcbSize)
861{
862 /* Validate input */
863 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
864
865 PRTTARFILEINTERNAL pFileInt = hFile;
866 RTTARFILE_VALID_RETURN(pFileInt);
867
868 *pcbSize = RT_MAX(pFileInt->cbSetSize, pFileInt->cbSize);
869
870 return VINF_SUCCESS;
871}
872
873RTR3DECL(int) RTTarFileSetSize(RTTARFILE hFile, uint64_t cbSize)
874{
875 PRTTARFILEINTERNAL pFileInt = hFile;
876 RTTARFILE_VALID_RETURN(pFileInt);
877
878 if ((pFileInt->fOpenMode & RTFILE_O_WRITE) != RTFILE_O_WRITE)
879 return VERR_WRITE_ERROR;
880
881 /* Todo: if cbSize is smaller than pFileInt->cbSize we have to truncate the
882 current file. */
883 pFileInt->cbSetSize = cbSize;
884
885 return VINF_SUCCESS;
886}
887
888RTR3DECL(int) RTTarFileGetMode(RTTARFILE hFile, uint32_t *pfMode)
889{
890 /* Validate input */
891 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
892
893 PRTTARFILEINTERNAL pFileInt = hFile;
894 RTTARFILE_VALID_RETURN(pFileInt);
895
896 /* Read the mode out of the header entry */
897 char mode[8];
898 int rc = RTFileReadAt(pFileInt->pTar->hTarFile, pFileInt->uStart + RT_OFFSETOF(RTTARRECORD, h.mode), mode, 8, NULL);
899 if (RT_FAILURE(rc))
900 return rc;
901 /* Convert it to an integer */
902 return RTStrToUInt32Full(mode, 8, pfMode);
903}
904
905RTR3DECL(int) RTTarFileSetMode(RTTARFILE hFile, uint32_t fMode)
906{
907 PRTTARFILEINTERNAL pFileInt = hFile;
908 RTTARFILE_VALID_RETURN(pFileInt);
909
910 if ((pFileInt->fOpenMode & RTFILE_O_WRITE) != RTFILE_O_WRITE)
911 return VERR_WRITE_ERROR;
912
913 /* Convert the mode to an string. */
914 char mode[8];
915 RTStrPrintf(mode, sizeof(mode), "%0.7o", fMode);
916 /* Write it directly into the header */
917 return RTFileWriteAt(pFileInt->pTar->hTarFile, pFileInt->uStart + RT_OFFSETOF(RTTARRECORD, h.mode), mode, 8, NULL);
918}
919
920RTR3DECL(int) RTTarFileGetTime(RTTARFILE hFile, PRTTIMESPEC pTime)
921{
922 PRTTARFILEINTERNAL pFileInt = hFile;
923 RTTARFILE_VALID_RETURN(pFileInt);
924
925 /* Read the time out of the header entry */
926 char mtime[12];
927 int rc = RTFileReadAt(pFileInt->pTar->hTarFile, pFileInt->uStart + RT_OFFSETOF(RTTARRECORD, h.mtime), mtime, 12, NULL);
928 if (RT_FAILURE(rc))
929 return rc;
930 /* Convert it to an integer */
931 int64_t tmpSeconds;
932 rc = RTStrToInt64Full(mtime, 12, &tmpSeconds);
933 /* And back to our time structure */
934 if (RT_SUCCESS(rc))
935 RTTimeSpecSetSeconds(pTime, tmpSeconds);
936
937 return rc;
938}
939
940RTR3DECL(int) RTTarFileSetTime(RTTARFILE hFile, PRTTIMESPEC pTime)
941{
942 PRTTARFILEINTERNAL pFileInt = hFile;
943 RTTARFILE_VALID_RETURN(pFileInt);
944
945 if ((pFileInt->fOpenMode & RTFILE_O_WRITE) != RTFILE_O_WRITE)
946 return VERR_WRITE_ERROR;
947
948 /* Convert the time to an string. */
949 char mtime[12];
950 RTStrPrintf(mtime, sizeof(mtime), "%0.11o", RTTimeSpecGetSeconds(pTime));
951 /* Write it directly into the header */
952 return RTFileWriteAt(pFileInt->pTar->hTarFile, pFileInt->uStart + RT_OFFSETOF(RTTARRECORD, h.mtime), mtime, 12, NULL);
953}
954
955RTR3DECL(int) RTTarFileGetOwner(RTTARFILE hFile, uint32_t *pUid, uint32_t *pGid)
956{
957 PRTTARFILEINTERNAL pFileInt = hFile;
958 RTTARFILE_VALID_RETURN(pFileInt);
959
960 /* Read the uid and gid out of the header entry */
961 char uid[8];
962 int rc = RTFileReadAt(pFileInt->pTar->hTarFile, pFileInt->uStart + RT_OFFSETOF(RTTARRECORD, h.uid), uid, 8, NULL);
963 if (RT_FAILURE(rc))
964 return rc;
965 char gid[8];
966 rc = RTFileReadAt(pFileInt->pTar->hTarFile, pFileInt->uStart + RT_OFFSETOF(RTTARRECORD, h.gid), gid, 8, NULL);
967 if (RT_FAILURE(rc))
968 return rc;
969 /* Convert it to integer */
970 rc = RTStrToUInt32Full(uid, 8, pUid);
971 if (RT_FAILURE(rc))
972 return rc;
973
974 return RTStrToUInt32Full(gid, 8, pGid);
975}
976
977RTR3DECL(int) RTTarFileSetOwner(RTTARFILE hFile, uint32_t uid, uint32_t gid)
978{
979 PRTTARFILEINTERNAL pFileInt = hFile;
980 RTTARFILE_VALID_RETURN(pFileInt);
981
982 if ((pFileInt->fOpenMode & RTFILE_O_WRITE) != RTFILE_O_WRITE)
983 return VERR_WRITE_ERROR;
984
985 int rc = VINF_SUCCESS;
986
987 if (uid != (uint32_t)-1)
988 {
989 /* Convert the uid to an string. */
990 char tmpUid[8];
991 RTStrPrintf(tmpUid, sizeof(tmpUid), "%0.7o", uid);
992 /* Write it directly into the header */
993 rc = RTFileWriteAt(pFileInt->pTar->hTarFile, pFileInt->uStart + RT_OFFSETOF(RTTARRECORD, h.uid), tmpUid, 8, NULL);
994 if (RT_FAILURE(rc))
995 return rc;
996 }
997 if (gid != (uint32_t)-1)
998 {
999 /* Convert the gid to an string. */
1000 char tmpGid[8];
1001 RTStrPrintf(tmpGid, sizeof(tmpGid), "%0.7o", gid);
1002 /* Write it directly into the header */
1003 rc = RTFileWriteAt(pFileInt->pTar->hTarFile, pFileInt->uStart + RT_OFFSETOF(RTTARRECORD, h.gid), tmpGid, 8, NULL);
1004 if (RT_FAILURE(rc))
1005 return rc;
1006 }
1007
1008 return rc;
1009}
1010
1011/******************************************************************************
1012 * Convenience Functions *
1013 ******************************************************************************/
1014
1015RTR3DECL(int) RTTarFileExists(const char *pszTarFile, const char *pszFile)
1016{
1017 /* Validate input */
1018 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
1019 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
1020
1021 /* Open the tar file */
1022 RTTAR hTar;
1023 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
1024 if (RT_FAILURE(rc))
1025 return rc;
1026
1027 /* Just try to open that file readonly. If this succeed the file exists. */
1028 RTTARFILE hFile;
1029 rc = RTTarFileOpen(hTar, &hFile, pszFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
1030 if (RT_SUCCESS(rc))
1031 RTTarFileClose(hFile);
1032
1033 RTTarClose(hTar);
1034
1035 return rc;
1036}
1037
1038RTR3DECL(int) RTTarList(const char *pszTarFile, char ***ppapszFiles, size_t *pcFiles)
1039{
1040 /* Validate input */
1041 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
1042 AssertPtrReturn(ppapszFiles, VERR_INVALID_POINTER);
1043 AssertPtrReturn(pcFiles, VERR_INVALID_POINTER);
1044
1045 /* Open the tar file */
1046 RTTAR hTar;
1047 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
1048 if (RT_FAILURE(rc))
1049 return rc;
1050
1051 /* This is done by internal methods, cause we didn't have a RTTARDIR
1052 * interface, yet. This should be fixed somedays. */
1053
1054 PRTTARINTERNAL pInt = hTar;
1055 char **papszFiles = 0;
1056 size_t cFiles = 0;
1057 do
1058 {
1059 /* Initialize the file name array with one slot */
1060 size_t cFilesAlloc = 1;
1061 papszFiles = (char**)RTMemAlloc(sizeof(char *));
1062 if (!papszFiles)
1063 {
1064 return VERR_NO_MEMORY;
1065 break;
1066 }
1067
1068 /* Iterate through the tar file record by record. Skip data records as we
1069 * didn't need them. */
1070 RTTARRECORD record;
1071 for (;;)
1072 {
1073 /* Read & verify a header record */
1074 rc = rtTarReadHeaderRecord(pInt->hTarFile, &record);
1075 /* Check for error or EOF. */
1076 if (RT_FAILURE(rc))
1077 break;
1078 /* We support normal files only */
1079 if ( record.h.linkflag == LF_OLDNORMAL
1080 || record.h.linkflag == LF_NORMAL)
1081 {
1082 if (cFiles >= cFilesAlloc)
1083 {
1084 /* Double the array size, make sure the size doesn't wrap. */
1085 void *pvNew = NULL;
1086 size_t cbNew = cFilesAlloc * sizeof(char *) * 2;
1087 if (cbNew / sizeof(char *) / 2 == cFilesAlloc)
1088 pvNew = RTMemRealloc(papszFiles, cbNew);
1089 if (!pvNew)
1090 {
1091 rc = VERR_NO_MEMORY;
1092 break;
1093 }
1094 papszFiles = (char **)pvNew;
1095 cFilesAlloc *= 2;
1096 }
1097
1098 /* Duplicate the name */
1099 papszFiles[cFiles] = RTStrDup(record.h.name);
1100 if (!papszFiles[cFiles])
1101 {
1102 rc = VERR_NO_MEMORY;
1103 break;
1104 }
1105 cFiles++;
1106 }
1107 rc = rtTarSkipData(pInt->hTarFile, &record);
1108 if (RT_FAILURE(rc))
1109 break;
1110 }
1111 }
1112 while(0);
1113
1114 if (rc == VERR_TAR_END_OF_FILE)
1115 rc = VINF_SUCCESS;
1116
1117 /* Return the file array on success, dispose of it on failure. */
1118 if (RT_SUCCESS(rc))
1119 {
1120 *pcFiles = cFiles;
1121 *ppapszFiles = papszFiles;
1122 }
1123 else
1124 {
1125 while (cFiles-- > 0)
1126 RTStrFree(papszFiles[cFiles]);
1127 RTMemFree(papszFiles);
1128 }
1129
1130 RTTarClose(hTar);
1131
1132 return rc;
1133}
1134
1135RTR3DECL(int) RTTarExtractFileToBuf(const char *pszTarFile, void **ppvBuf, size_t *pcbSize, const char *pszFile, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
1136{
1137 /* Validate input */
1138 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
1139 AssertPtrReturn(ppvBuf, VERR_INVALID_POINTER);
1140 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
1141 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
1142 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_POINTER);
1143 AssertPtrNullReturn(pvUser, VERR_INVALID_POINTER);
1144
1145 /* Todo: progress bar */
1146
1147 int rc = VINF_SUCCESS;
1148 RTTAR hTar = NIL_RTTAR;
1149 RTTARFILE hFile = NIL_RTTARFILE;
1150 char *pvTmp = 0;
1151 uint64_t cbToCopy = 0;
1152 do
1153 {
1154 rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
1155 if (RT_FAILURE(rc))
1156 break;
1157 rc = RTTarFileOpen(hTar, &hFile, pszFile, RTFILE_O_OPEN | RTFILE_O_READ);
1158 if (RT_FAILURE(rc))
1159 break;
1160 rc = RTTarFileGetSize(hFile, &cbToCopy);
1161 if (RT_FAILURE(rc))
1162 break;
1163 /* Allocate the memory for the file content. */
1164 pvTmp = (char*)RTMemAlloc(cbToCopy);
1165 if (!pvTmp)
1166 {
1167 rc = VERR_NO_MEMORY;
1168 break;
1169 }
1170 size_t cbRead = 0;
1171 size_t cbAllRead = 0;
1172 for (;;)
1173 {
1174 if (pfnProgressCallback)
1175 pfnProgressCallback((unsigned)(100.0 / cbToCopy * cbAllRead), pvUser);
1176 if (cbAllRead == cbToCopy)
1177 break;
1178 rc = RTTarFileReadAt(hFile, 0, &pvTmp[cbAllRead], cbToCopy - cbAllRead, &cbRead);
1179 if (RT_FAILURE(rc))
1180 break;
1181 cbAllRead += cbRead;
1182 }
1183 }
1184 while(0);
1185
1186 /* Set output values on success */
1187 if (RT_SUCCESS(rc))
1188 {
1189 *pcbSize = cbToCopy;
1190 *ppvBuf = pvTmp;
1191 }
1192
1193 /* Cleanup */
1194 if ( RT_FAILURE(rc)
1195 && pvTmp)
1196 RTMemFree(pvTmp);
1197 if (hFile)
1198 RTTarFileClose(hFile);
1199 if (hTar)
1200 RTTarClose(hTar);
1201
1202 return rc;
1203}
1204
1205RTR3DECL(int) RTTarExtractFiles(const char *pszTarFile, const char *pszOutputDir, const char * const *papszFiles, size_t cFiles, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
1206{
1207 /* Validate input */
1208 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
1209 AssertPtrReturn(pszOutputDir, VERR_INVALID_POINTER);
1210 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER);
1211 AssertReturn(cFiles, VERR_INVALID_PARAMETER);
1212 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_POINTER);
1213 AssertPtrNullReturn(pvUser, VERR_INVALID_POINTER);
1214
1215 /* Open the tar file */
1216 RTTAR hTar;
1217 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
1218 if (RT_FAILURE(rc))
1219 return rc;
1220
1221 do
1222 {
1223 /* Get the overall size of all files to extract out of the tar archive
1224 headers. Only necessary if there is a progress callback. */
1225 uint64_t cbOverallSize = 0;
1226 if (pfnProgressCallback)
1227 {
1228// rc = rtTarGetFilesOverallSize(hFile, papszFiles, cFiles, &cbOverallSize);
1229// if (RT_FAILURE(rc))
1230// break;
1231 }
1232
1233 uint64_t cbOverallWritten = 0;
1234 for(size_t i=0; i < cFiles; ++i)
1235 {
1236 RTTARFILE hFile;
1237 rc = RTTarFileOpen(hTar, &hFile, papszFiles[i], RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
1238 if (RT_FAILURE(rc))
1239 break;
1240 char *pszTargetFile;
1241 rc = RTStrAPrintf(&pszTargetFile, "%s/%s", pszOutputDir, papszFiles[i]);
1242 if (RT_FAILURE(rc))
1243 break;
1244 rc = rtTarExtractFileToFile(hFile, pszTargetFile, cbOverallSize, cbOverallWritten, pfnProgressCallback, pvUser);
1245 RTStrFree(pszTargetFile);
1246 RTTarFileClose(hFile);
1247 if (RT_FAILURE(rc))
1248 break;
1249 }
1250 }
1251 while(0);
1252
1253 RTTarClose(hTar);
1254
1255 return rc;
1256}
1257
1258RTR3DECL(int) RTTarExtractAll(const char *pszTarFile, const char *pszOutputDir, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
1259{
1260 /* Validate input */
1261 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
1262 AssertPtrReturn(pszOutputDir, VERR_INVALID_POINTER);
1263 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_POINTER);
1264 AssertPtrNullReturn(pvUser, VERR_INVALID_POINTER);
1265
1266 char **papszFiles;
1267 size_t cFiles;
1268
1269 /* First fetch the files names contained in the tar file */
1270 int rc = RTTarList(pszTarFile, &papszFiles, &cFiles);
1271 if (RT_FAILURE(rc))
1272 return rc;
1273
1274 /* Extract all files */
1275 return RTTarExtractFiles(pszTarFile, pszOutputDir, papszFiles, cFiles, pfnProgressCallback, pvUser);
1276}
1277
1278RTR3DECL(int) RTTarCreate(const char *pszTarFile, const char * const *papszFiles, size_t cFiles, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
1279{
1280 /* Validate input */
1281 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
1282 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER);
1283 AssertReturn(cFiles, VERR_INVALID_PARAMETER);
1284 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_POINTER);
1285 AssertPtrNullReturn(pvUser, VERR_INVALID_POINTER);
1286
1287 RTTAR hTar;
1288 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_CREATE | RTFILE_O_READWRITE | RTFILE_O_DENY_NONE);
1289 if (RT_FAILURE(rc))
1290 return rc;
1291
1292 /* Get the overall size of all files to pack into the tar archive. Only
1293 necessary if there is a progress callback. */
1294 uint64_t cbOverallSize = 0;
1295 if (pfnProgressCallback)
1296 for (size_t i = 0; i < cFiles; ++i)
1297 {
1298 uint64_t cbSize;
1299 rc = RTFileQuerySize(papszFiles[i], &cbSize);
1300 if (RT_FAILURE(rc))
1301 break;
1302 cbOverallSize += cbSize;
1303 }
1304 uint64_t cbOverallWritten = 0;
1305 for (size_t i = 0; i < cFiles; ++i)
1306 {
1307 rc = rtTarAppendFileFromFile(hTar, papszFiles[i], cbOverallSize, cbOverallWritten, pfnProgressCallback, pvUser);
1308 if (RT_FAILURE(rc))
1309 break;
1310 }
1311
1312 /* Cleanup */
1313 RTTarClose(hTar);
1314
1315 return rc;
1316}
1317
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