VirtualBox

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

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

Runtime: force zero terminated strings on file read

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