VirtualBox

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

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

Runtime: initial VFS support for tar

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

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