VirtualBox

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

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

Runtime: cast

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.1 KB
Line 
1/* $Id: tar.cpp 31624 2010-08-13 00:31:22Z vboxsync $ */
2/** @file
3 * IPRT - Tar archive I/O.
4 */
5
6/*
7 * Copyright (C) 2009 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/*******************************************************************************
43* Structures and Typedefs *
44*******************************************************************************/
45
46/** @name RTTARRECORD::h::linkflag
47 * @{ */
48#define LF_OLDNORMAL '\0' /**< Normal disk file, Unix compatible */
49#define LF_NORMAL '0' /**< Normal disk file */
50#define LF_LINK '1' /**< Link to previously dumped file */
51#define LF_SYMLINK '2' /**< Symbolic link */
52#define LF_CHR '3' /**< Character special file */
53#define LF_BLK '4' /**< Block special file */
54#define LF_DIR '5' /**< Directory */
55#define LF_FIFO '6' /**< FIFO special file */
56#define LF_CONTIG '7' /**< Contiguous file */
57/** @} */
58
59typedef union RTTARRECORD
60{
61 char d[512];
62 struct h
63 {
64 char name[100];
65 char mode[8];
66 char uid[8];
67 char gid[8];
68 char size[12];
69 char mtime[12];
70 char chksum[8];
71 char linkflag;
72 char linkname[100];
73 char magic[8];
74 char uname[32];
75 char gname[32];
76 char devmajor[8];
77 char devminor[8];
78 } h;
79} RTTARRECORD;
80typedef RTTARRECORD *PRTTARRECORD;
81AssertCompileSize(RTTARRECORD, 512);
82AssertCompileMemberOffset(RTTARRECORD, h.size, 100+8*3);
83
84#if 0 /* not currently used */
85typedef struct RTTARFILELIST
86{
87 char *pszFilename;
88 RTTARFILELIST *pNext;
89} RTTARFILELIST;
90typedef RTTARFILELIST *PRTTARFILELIST;
91#endif
92
93/*******************************************************************************
94* Internal Functions *
95*******************************************************************************/
96
97static int rtTarCalcChkSum(PRTTARRECORD pRecord, uint32_t *pChkSum)
98{
99 uint32_t check = 0;
100 uint32_t zero = 0;
101 for (size_t i = 0; i < sizeof(RTTARRECORD); ++i)
102 {
103 /* Calculate the sum of every byte from the header. The checksum field
104 * itself is counted as all blanks. */
105 if ( i < RT_UOFFSETOF(RTTARRECORD, h.chksum)
106 || i >= RT_UOFFSETOF(RTTARRECORD, h.linkflag))
107 check += pRecord->d[i];
108 else
109 check += ' ';
110 /* Additional check if all fields are zero, which indicate EOF. */
111 zero += pRecord->d[i];
112 }
113
114 /* EOF? */
115 if (!zero)
116 return VERR_EOF;
117
118 *pChkSum = check;
119 return VINF_SUCCESS;
120}
121
122static int rtTarCheckHeader(PRTTARRECORD pRecord)
123{
124 uint32_t check;
125 int rc = rtTarCalcChkSum(pRecord, &check);
126 /* EOF? */
127 if (RT_FAILURE(rc))
128 return rc;
129
130 /* Verify the checksum */
131 uint32_t sum;
132 rc = RTStrToUInt32Full(pRecord->h.chksum, 8, &sum);
133 if (RT_SUCCESS(rc) && sum == check)
134 return VINF_SUCCESS;
135 return VERR_TAR_CHKSUM_MISMATCH;
136}
137
138static int rtTarCopyFileFrom(RTFILE hFile, const char *pszTargetName, PRTTARRECORD pRecord, const uint64_t cbOverallSize, uint64_t &cbOverallWritten, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
139{
140 RTFILE hNewFile;
141 /* Open the target file */
142 int rc = RTFileOpen(&hNewFile, pszTargetName, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
143 if (RT_FAILURE(rc))
144 return rc;
145
146/**@todo r=bird: Use a bigger buffer here, see comment in rtTarCopyFileTo. */
147
148 uint64_t cbToCopy = RTStrToUInt64(pRecord->h.size);
149 size_t cbAllWritten = 0;
150 RTTARRECORD record;
151 /* Copy the content from hFile over to pszTargetName. This is done block
152 * wise in 512 byte steps. After this copying is finished hFile will be on
153 * a 512 byte boundary, regardless if the file copied is 512 byte size
154 * aligned. */
155 for (;;)
156 {
157 if (pfnProgressCallback)
158 pfnProgressCallback((unsigned)(100.0 / cbOverallSize * cbOverallWritten), pvUser);
159 /* Finished already? */
160 if (cbAllWritten == cbToCopy)
161 break;
162 /* Read one block */
163 rc = RTFileRead(hFile, &record, sizeof(record), NULL);
164 if (RT_FAILURE(rc))
165 break;
166 size_t cbToWrite = sizeof(record);
167 /* Check for the last block which has not to be 512 bytes in size. */
168 if (cbAllWritten + cbToWrite > cbToCopy)
169 cbToWrite = cbToCopy - cbAllWritten;
170 /* Write the block */
171 rc = RTFileWrite(hNewFile, &record, cbToWrite, NULL);
172 if (RT_FAILURE(rc))
173 break;
174 /* Count how many bytes are written already */
175 cbAllWritten += cbToWrite;
176 cbOverallWritten += cbToWrite;
177 }
178
179 /* Now set all file attributes */
180 if (RT_SUCCESS(rc))
181 {
182 int32_t mode;
183 rc = RTStrToInt32Full(pRecord->h.mode, 8, &mode);
184 if (RT_SUCCESS(rc))
185 {
186 mode |= RTFS_TYPE_FILE; /* For now we support regular files only */
187 /* Set the mode */
188 rc = RTFileSetMode(hNewFile, mode);
189 }
190 }
191 /* Make sure the called doesn't mix truncated tar files with the official
192 * end indicated by rtTarCalcChkSum. */
193 else if (rc == VERR_EOF)
194 rc = VERR_FILE_IO_ERROR;
195
196 RTFileClose(hNewFile);
197
198 /* Delete the freshly created file in the case of an error */
199 if (RT_FAILURE(rc))
200 RTFileDelete(pszTargetName);
201
202 return rc;
203}
204
205static int rtTarCopyFileTo(RTFILE hFile, const char *pszSrcName, const uint64_t cbOverallSize, uint64_t &cbOverallWritten, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
206{
207 RTFILE hOldFile;
208 /* Open the source file */
209 int rc = RTFileOpen(&hOldFile, pszSrcName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
210 if (RT_FAILURE(rc))
211 return rc;
212
213 /* Get the size of the source file */
214 uint64_t cbSize;
215 rc = RTFileGetSize(hOldFile, &cbSize);
216 if (RT_FAILURE(rc))
217 {
218 RTFileClose(hOldFile);
219 return rc;
220 }
221 /* Get some info from the source file */
222 RTFSOBJINFO info;
223 RTUID uid = 0;
224 RTGID gid = 0;
225 RTFMODE fmode = 0600; /* Make some save default */
226 int64_t mtime = 0;
227 /* This isn't critical. Use the defaults if it fails. */
228 rc = RTFileQueryInfo(hOldFile, &info, RTFSOBJATTRADD_UNIX);
229 if (RT_SUCCESS(rc))
230 {
231 fmode = info.Attr.fMode & RTFS_UNIX_MASK;
232 uid = info.Attr.u.Unix.uid;
233 gid = info.Attr.u.Unix.gid;
234 mtime = RTTimeSpecGetSeconds(&info.ModificationTime);
235 }
236
237 /* Fill the header record */
238 RTTARRECORD record;
239 RT_ZERO(record);
240 RTStrPrintf(record.h.name, sizeof(record.h.name), "%s", RTPathFilename(pszSrcName));
241 RTStrPrintf(record.h.mode, sizeof(record.h.mode), "%0.7o", fmode);
242 RTStrPrintf(record.h.uid, sizeof(record.h.uid), "%0.7o", uid);
243 RTStrPrintf(record.h.gid, sizeof(record.h.gid), "%0.7o", gid);
244 RTStrPrintf(record.h.size, sizeof(record.h.size), "%0.11o", cbSize);
245 RTStrPrintf(record.h.mtime, sizeof(record.h.mtime), "%0.11o", mtime);
246 RTStrPrintf(record.h.magic, sizeof(record.h.magic), "ustar ");
247 RTStrPrintf(record.h.uname, sizeof(record.h.uname), "someone");
248 RTStrPrintf(record.h.gname, sizeof(record.h.gname), "someone");
249 record.h.linkflag = LF_NORMAL;
250
251 /* Create the checksum out of the new header */
252 uint32_t chksum;
253 rc = rtTarCalcChkSum(&record, &chksum);
254 if (RT_SUCCESS(rc))
255 {
256 RTStrPrintf(record.h.chksum, sizeof(record.h.chksum), "%0.7o", chksum);
257
258 /* Write the header first */
259 rc = RTFileWrite(hFile, &record, sizeof(record), NULL);
260 if (RT_SUCCESS(rc))
261 {
262/** @todo r=bird: using a 64KB buffer here instead of 0.5KB would probably be
263 * a good thing. */
264 uint64_t cbAllWritten = 0;
265 /* Copy the content from pszSrcName over to hFile. This is done block
266 * wise in 512 byte steps. After this copying is finished hFile will be
267 * on a 512 byte boundary, regardless if the file copied is 512 byte
268 * size aligned. */
269 for (;;)
270 {
271 if (pfnProgressCallback)
272 pfnProgressCallback((unsigned)(100.0 / cbOverallSize * cbOverallWritten), pvUser);
273 if (cbAllWritten >= cbSize)
274 break;
275 size_t cbToRead = sizeof(record);
276 /* Last record? */
277 if (cbAllWritten + cbToRead > cbSize)
278 {
279 /* Initialize with zeros */
280 RT_ZERO(record);
281 cbToRead = cbSize - cbAllWritten;
282 }
283 /* Read one block */
284 rc = RTFileRead(hOldFile, &record, cbToRead, NULL);
285 if (RT_FAILURE(rc))
286 break;
287 /* Write one block */
288 rc = RTFileWrite(hFile, &record, sizeof(record), NULL);
289 if (RT_FAILURE(rc))
290 break;
291 /* Count how many bytes are written already */
292 cbAllWritten += sizeof(record);
293 cbOverallWritten += sizeof(record);
294 }
295
296 /* Make sure the called doesn't mix truncated tar files with the
297 * official end indicated by rtTarCalcChkSum. */
298 if (rc == VERR_EOF)
299 rc = VERR_FILE_IO_ERROR;
300 }
301 }
302
303 RTFileClose(hOldFile);
304 return rc;
305}
306
307static int rtTarSkipData(RTFILE hFile, PRTTARRECORD pRecord)
308{
309 int rc = VINF_SUCCESS;
310 /* Seek over the data parts (512 bytes aligned) */
311 int64_t offSeek = RT_ALIGN(RTStrToInt64(pRecord->h.size), sizeof(RTTARRECORD));
312 if (offSeek > 0)
313 rc = RTFileSeek(hFile, offSeek, RTFILE_SEEK_CURRENT, NULL);
314 return rc;
315}
316
317static int rtTarGetFilesOverallSize(RTFILE hFile, const char * const *papszFiles, size_t cFiles, uint64_t *pcbOverallSize)
318{
319 int rc;
320 size_t cFound = 0;
321 RTTARRECORD record;
322 for (;;)
323 {
324/** @todo r=bird: the reading, validation and EOF check done here should be
325 * moved to a separate helper function. That would make it easiser to
326 * distinguish genuine-end-of-tar-file and VERR_EOF caused by a
327 * trunacted file. That said, rtTarSkipData won't return VERR_EOF, at
328 * least not on unix, since it's not a sin to seek beyond the end of a
329 * file. */
330 rc = RTFileRead(hFile, &record, sizeof(record), NULL);
331 /* Check for error or EOF. */
332 if (RT_FAILURE(rc))
333 break;
334 /* Check for EOF & data integrity */
335 rc = rtTarCheckHeader(&record);
336 if (RT_FAILURE(rc))
337 break;
338 /* We support normal files only */
339 if ( record.h.linkflag == LF_OLDNORMAL
340 || record.h.linkflag == LF_NORMAL)
341 {
342 for (size_t i = 0; i < cFiles; ++i)
343 {
344 if (!RTStrCmp(record.h.name, papszFiles[i]))
345 {
346 uint64_t cbSize;
347 /* Get the file size */
348 rc = RTStrToUInt64Full(record.h.size, 8, &cbSize);
349 /* Sum up the overall size */
350 *pcbOverallSize += cbSize;
351 ++cFound;
352 break;
353 }
354 }
355 if ( cFound == cFiles
356 || RT_FAILURE(rc))
357 break;
358 }
359 rc = rtTarSkipData(hFile, &record);
360 if (RT_FAILURE(rc))
361 break;
362 }
363 /* Make sure the file pointer is at the begin of the file again. */
364 if (RT_SUCCESS(rc))
365 rc = RTFileSeek(hFile, 0, RTFILE_SEEK_BEGIN, 0);
366 return rc;
367}
368
369/*******************************************************************************
370* Public Functions *
371*******************************************************************************/
372
373RTR3DECL(int) RTTarQueryFileExists(const char *pszTarFile, const char *pszFile)
374{
375 /* Validate input */
376 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
377 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
378
379 /* Open the tar file */
380 RTFILE hFile;
381 int rc = RTFileOpen(&hFile, pszTarFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
382 if (RT_FAILURE(rc))
383 return rc;
384
385 bool fFound = false;
386 RTTARRECORD record;
387 for (;;)
388 {
389/** @todo r=bird: the reading, validation and EOF check done here should be
390 * moved to a separate helper function. That would make it easiser to
391 * distinguish genuine-end-of-tar-file and VERR_EOF caused by a
392 * trunacted file. That said, rtTarSkipData won't return VERR_EOF, at
393 * least not on unix, since it's not a sin to seek beyond the end of a
394 * file. */
395 rc = RTFileRead(hFile, &record, sizeof(record), NULL);
396 /* Check for error or EOF. */
397 if (RT_FAILURE(rc))
398 break;
399 /* Check for EOF & data integrity */
400 rc = rtTarCheckHeader(&record);
401 if (RT_FAILURE(rc))
402 break;
403 /* We support normal files only */
404 if ( record.h.linkflag == LF_OLDNORMAL
405 || record.h.linkflag == LF_NORMAL)
406 {
407 if (!RTStrCmp(record.h.name, pszFile))
408 {
409 fFound = true;
410 break;
411 }
412 }
413 rc = rtTarSkipData(hFile, &record);
414 if (RT_FAILURE(rc))
415 break;
416 }
417
418 RTFileClose(hFile);
419
420 if (rc == VERR_EOF)
421 rc = VINF_SUCCESS;
422
423 /* Something found? */
424 if ( RT_SUCCESS(rc)
425 && !fFound)
426 rc = VERR_FILE_NOT_FOUND;
427
428 return rc;
429}
430
431RTR3DECL(int) RTTarList(const char *pszTarFile, char ***ppapszFiles, size_t *pcFiles)
432{
433 /* Validate input */
434 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
435 AssertPtrReturn(ppapszFiles, VERR_INVALID_POINTER);
436 AssertPtrReturn(pcFiles, VERR_INVALID_POINTER);
437
438 /* Open the tar file */
439 RTFILE hFile;
440 int rc = RTFileOpen(&hFile, pszTarFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
441 if (RT_FAILURE(rc))
442 return rc;
443
444 /* Initialize the file name array with one slot */
445 size_t cFilesAlloc = 1;
446 char **papszFiles = (char**)RTMemAlloc(sizeof(char *));
447 if (!papszFiles)
448 {
449 RTFileClose(hFile);
450 return VERR_NO_MEMORY;
451 }
452
453 /* Iterate through the tar file record by record. Skip data records as we
454 * didn't need them. */
455 RTTARRECORD record;
456 size_t cFiles = 0;
457 for (;;)
458 {
459 rc = RTFileRead(hFile, &record, sizeof(record), NULL);
460 /* Check for error or EOF. */
461 if (RT_FAILURE(rc))
462 break;
463 /* Check for EOF & data integrity */
464 rc = rtTarCheckHeader(&record);
465 if (RT_FAILURE(rc))
466 break;
467 /* We support normal files only */
468 if ( record.h.linkflag == LF_OLDNORMAL
469 || record.h.linkflag == LF_NORMAL)
470 {
471 if (cFiles >= cFilesAlloc)
472 {
473 /* Double the array size, make sure the size doesn't wrap. */
474 void *pvNew = NULL;
475 size_t cbNew = cFilesAlloc * sizeof(char *) * 2;
476 if (cbNew / sizeof(char *) / 2 == cFilesAlloc)
477 pvNew = RTMemRealloc(papszFiles, cbNew);
478 if (!pvNew)
479 {
480 rc = VERR_NO_MEMORY;
481 break;
482 }
483 papszFiles = (char **)pvNew;
484 cFilesAlloc *= 2;
485 }
486
487 /* Duplicate the name */
488 papszFiles[cFiles] = RTStrDup(record.h.name);
489 if (!papszFiles[cFiles])
490 {
491 rc = VERR_NO_MEMORY;
492 break;
493 }
494 cFiles++;
495 }
496 rc = rtTarSkipData(hFile, &record);
497 if (RT_FAILURE(rc))
498 break;
499 }
500
501 RTFileClose(hFile);
502
503 if (rc == VERR_EOF)
504 rc = VINF_SUCCESS;
505
506 /* Return the file array on success, dispose of it on failure. */
507 if (RT_SUCCESS(rc))
508 {
509 *pcFiles = cFiles;
510 *ppapszFiles = papszFiles;
511 }
512 else
513 {
514 while (cFiles-- > 0)
515 RTStrFree(papszFiles[cFiles]);
516 RTMemFree(papszFiles);
517 }
518 return rc;
519}
520
521RTR3DECL(int) RTTarExtractFiles(const char *pszTarFile, const char *pszOutputDir, const char * const *papszFiles, size_t cFiles, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
522{
523 /* Validate input */
524 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
525 AssertPtrReturn(pszOutputDir, VERR_INVALID_POINTER);
526 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER);
527
528 /* Open the tar file */
529 RTFILE hFile;
530 int rc = RTFileOpen(&hFile, pszTarFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
531 if (RT_FAILURE(rc))
532 return rc;
533
534 /* Get the overall size of all files to extract out of the tar archive
535 headers. Only necessary if there is a progress callback. */
536 uint64_t cbOverallSize = 0;
537 if (pfnProgressCallback)
538 rc = rtTarGetFilesOverallSize(hFile, papszFiles, cFiles, &cbOverallSize);
539 if (RT_SUCCESS(rc))
540 {
541 /* Iterate through the tar file record by record. */
542 RTTARRECORD record;
543 char **paExtracted = (char **)RTMemTmpAllocZ(sizeof(char *) * cFiles);
544 if (paExtracted)
545 {
546 size_t cExtracted = 0;
547 uint64_t cbOverallWritten = 0;
548 for (;;)
549 {
550 rc = RTFileRead(hFile, &record, sizeof(record), NULL);
551 /* Check for error or EOF. */
552 if (RT_FAILURE(rc))
553 break;
554 /* Check for EOF & data integrity */
555 rc = rtTarCheckHeader(&record);
556 if (RT_FAILURE(rc))
557 break;
558 /* We support normal files only */
559 if ( record.h.linkflag == LF_OLDNORMAL
560 || record.h.linkflag == LF_NORMAL)
561 {
562 bool fFound = false;
563 for (size_t i = 0; i < cFiles; ++i)
564 {
565 if (!RTStrCmp(record.h.name, papszFiles[i]))
566 {
567 fFound = true;
568 if (cExtracted < cFiles)
569 {
570 char *pszTargetFile;
571 rc = RTStrAPrintf(&pszTargetFile, "%s/%s", pszOutputDir, papszFiles[i]);
572 if (rc > 0)
573 {
574 rc = rtTarCopyFileFrom(hFile, pszTargetFile, &record, cbOverallSize, cbOverallWritten, pfnProgressCallback, pvUser);
575 if (RT_SUCCESS(rc))
576 paExtracted[cExtracted++] = pszTargetFile;
577 else
578 RTStrFree(pszTargetFile);
579 }
580 else
581 rc = VERR_NO_MEMORY;
582 }
583 else
584 rc = VERR_ALREADY_EXISTS;
585 break;
586 }
587 }
588 if (RT_FAILURE(rc))
589 break;
590 /* If the current record isn't a file in the file list we have to
591 * skip the data */
592 if (!fFound)
593 {
594 rc = rtTarSkipData(hFile, &record);
595 if (RT_FAILURE(rc))
596 break;
597 }
598 }
599 }
600
601 if (rc == VERR_EOF)
602 rc = VINF_SUCCESS;
603
604 /* If we didn't found all files, indicate an error */
605 if (cExtracted != cFiles && RT_SUCCESS(rc))
606 rc = VERR_FILE_NOT_FOUND;
607
608 /* Cleanup the names of the extracted files, deleting them on failure. */
609 while (cExtracted-- > 0)
610 {
611 if (RT_FAILURE(rc))
612 RTFileDelete(paExtracted[cExtracted]);
613 RTStrFree(paExtracted[cExtracted]);
614 }
615 RTMemTmpFree(paExtracted);
616 }
617 else
618 rc = VERR_NO_TMP_MEMORY;
619 }
620
621 RTFileClose(hFile);
622 return rc;
623}
624
625RTR3DECL(int) RTTarExtractByIndex(const char *pszTarFile, const char *pszOutputDir, size_t iIndex, char **ppszFileName, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
626{
627 /* Validate input */
628 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
629 AssertPtrReturn(pszOutputDir, VERR_INVALID_POINTER);
630
631 /* Open the tar file */
632 RTFILE hFile;
633 int rc = RTFileOpen(&hFile, pszTarFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
634 if (RT_FAILURE(rc))
635 return rc;
636
637 /* Iterate through the tar file record by record. */
638 RTTARRECORD record;
639 size_t iFile = 0;
640 bool fFound = false;
641 for (;;)
642 {
643 rc = RTFileRead(hFile, &record, sizeof(record), NULL);
644 /* Check for error or EOF. */
645 if (RT_FAILURE(rc))
646 break;
647 /* Check for EOF & data integrity */
648 rc = rtTarCheckHeader(&record);
649 if (RT_FAILURE(rc))
650 break;
651 /* We support normal files only */
652 if ( record.h.linkflag == LF_OLDNORMAL
653 || record.h.linkflag == LF_NORMAL)
654 {
655 if (iIndex == iFile)
656 {
657 fFound = true;
658 char *pszTargetName;
659 rc = RTStrAPrintf(&pszTargetName, "%s/%s", pszOutputDir, record.h.name);
660 if (rc > 0)
661 {
662 uint64_t cbOverallSize;
663 uint64_t cbOverallWritten = 0;
664 /* Get the file size */
665 rc = RTStrToUInt64Full(record.h.size, 8, &cbOverallSize);
666 if (RT_FAILURE(rc))
667 break;
668 rc = rtTarCopyFileFrom(hFile, pszTargetName, &record, cbOverallSize, cbOverallWritten, pfnProgressCallback, pvUser);
669 /* On success pass on the filename if requested. */
670 if ( RT_SUCCESS(rc)
671 && ppszFileName)
672 *ppszFileName = pszTargetName;
673 else
674 RTStrFree(pszTargetName);
675 }
676 else
677 rc = VERR_NO_MEMORY;
678 break;
679 }
680 }
681 rc = rtTarSkipData(hFile, &record);
682 if (RT_FAILURE(rc))
683 break;
684 ++iFile;
685 }
686
687 RTFileClose(hFile);
688
689 if (rc == VERR_EOF)
690 rc = VINF_SUCCESS;
691
692 /* If we didn't found the index, indicate an error */
693 if (!fFound && RT_SUCCESS(rc))
694 rc = VERR_FILE_NOT_FOUND;
695
696 return rc;
697}
698
699RTR3DECL(int) RTTarExtractAll(const char *pszTarFile, const char *pszOutputDir, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
700{
701 /* Validate input */
702 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
703 AssertPtrReturn(pszOutputDir, VERR_INVALID_POINTER);
704
705 char **papszFiles;
706 size_t cFiles;
707
708 /* First fetch the files names contained in the tar file */
709 int rc = RTTarList(pszTarFile, &papszFiles, &cFiles);
710 if (RT_FAILURE(rc))
711 return rc;
712
713 /* Extract all files */
714 return RTTarExtractFiles(pszTarFile, pszOutputDir, papszFiles, cFiles, pfnProgressCallback, pvUser);
715}
716
717RTR3DECL(int) RTTarCreate(const char *pszTarFile, const char * const *papszFiles, size_t cFiles, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
718{
719 /* Validate input */
720 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
721 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER);
722
723 /* Open the tar file */
724 RTFILE hFile;
725 int rc = RTFileOpen(&hFile, pszTarFile, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
726 if (RT_FAILURE(rc))
727 return rc;
728
729 /* Get the overall size of all files to pack into the tar archive. Only
730 necessary if there is a progress callback. */
731 uint64_t cbOverallSize = 0;
732 if (pfnProgressCallback)
733 for (size_t i = 0; i < cFiles; ++i)
734 {
735 uint64_t cbSize;
736 rc = RTFileQuerySize(papszFiles[i], &cbSize);
737 if (RT_FAILURE(rc))
738 break;
739 cbOverallSize += cbSize;
740 }
741
742 if (RT_SUCCESS(rc))
743 {
744 uint64_t cbOverallWritten = 0;
745
746 for (size_t i = 0; i < cFiles; ++i)
747 {
748 rc = rtTarCopyFileTo(hFile, papszFiles[i], cbOverallSize, cbOverallWritten, pfnProgressCallback, pvUser);
749 if (RT_FAILURE(rc))
750 break;
751 }
752
753 /* gtar gives a warning, but the documentation says EOF is indicated by a
754 * zero block. Disabled for now. */
755#if 0
756 if (RT_SUCCESS(rc))
757 {
758 /* Append the EOF record which is filled all by zeros */
759 RTTARRECORD record;
760 ASMMemFill32(&record, sizeof(record), 0);
761 rc = RTFileWrite(hFile, &record, sizeof(record), NULL);
762 }
763#endif
764 }
765
766 /* Time to close the new tar archive */
767 RTFileClose(hFile);
768
769 /* Delete the freshly created tar archive on failure */
770 if (RT_FAILURE(rc))
771 RTFileDelete(pszTarFile);
772
773 return rc;
774}
775
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