VirtualBox

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

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

tar.cpp: Stripped it futher down and documented what RTTarOpenCurrentFile actually need to do, simplifying the weird stuff that it used to do. You can only open the current file once now.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.6 KB
Line 
1/* $Id: tar.cpp 50194 2014-01-23 17:52:28Z vboxsync $ */
2/** @file
3 * IPRT - Tar archive I/O.
4 */
5
6/*
7 * Copyright (C) 2009-2014 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#include <iprt/vfs.h>
42#include <iprt/zip.h>
43
44
45#include "internal/magics.h"
46#include "tar.h"
47
48
49/*******************************************************************************
50* Structures and Typedefs *
51*******************************************************************************/
52/** @name RTTARRECORD::h::linkflag
53 * @{ */
54#define LF_OLDNORMAL '\0' /**< Normal disk file, Unix compatible */
55#define LF_NORMAL '0' /**< Normal disk file */
56#define LF_LINK '1' /**< Link to previously dumped file */
57#define LF_SYMLINK '2' /**< Symbolic link */
58#define LF_CHR '3' /**< Character special file */
59#define LF_BLK '4' /**< Block special file */
60#define LF_DIR '5' /**< Directory */
61#define LF_FIFO '6' /**< FIFO special file */
62#define LF_CONTIG '7' /**< Contiguous file */
63/** @} */
64
65/**
66 * A tar file header.
67 */
68typedef union RTTARRECORD
69{
70 char d[512];
71 struct h
72 {
73 char name[100];
74 char mode[8];
75 char uid[8];
76 char gid[8];
77 char size[12];
78 char mtime[12];
79 char chksum[8];
80 char linkflag;
81 char linkname[100];
82 char magic[8];
83 char uname[32];
84 char gname[32];
85 char devmajor[8];
86 char devminor[8];
87 } h;
88} RTTARRECORD;
89AssertCompileSize(RTTARRECORD, 512);
90AssertCompileMemberOffset(RTTARRECORD, h.size, 100+8*3);
91AssertCompileMemberSize(RTTARRECORD, h.name, RTTAR_NAME_MAX+1);
92/** Pointer to a tar file header. */
93typedef RTTARRECORD *PRTTARRECORD;
94
95/** Pointer to a tar file handle. */
96typedef struct RTTARFILEINTERNAL *PRTTARFILEINTERNAL;
97
98/**
99 * The internal data of a tar handle.
100 */
101typedef struct RTTARINTERNAL
102{
103 /** The magic (RTTAR_MAGIC). */
104 uint32_t u32Magic;
105 /** The handle to the tar file. */
106 RTFILE hTarFile;
107 /** The open mode for hTarFile. */
108 uint32_t fOpenMode;
109 /** Whether a file within the archive is currently open for writing.
110 * Only one can be open. */
111 bool fFileOpenForWrite;
112 /** Whether operating in stream mode. */
113 bool fStreamMode;
114 /** The tar file VFS handle. */
115 RTVFSFILE hVfsFile;
116 /** The tar file system VFS handle. */
117 RTVFSFSSTREAM hVfsFss;
118 /** Set if hVfsFss is at the start of the stream and doesn't need rewinding. */
119 bool fFssAtStart;
120 /** The current stream object (fStreamMode = true). */
121 RTVFSIOSTREAM hVfsCur;
122 /** The name of the current object (fStreamMode = true). */
123 char *pszVfsCurName;
124} RTTARINTERNAL;
125/** Pointer to a the internal data of a tar handle. */
126typedef RTTARINTERNAL* PRTTARINTERNAL;
127
128/**
129 * The internal data of a file within a tar file.
130 */
131typedef struct RTTARFILEINTERNAL
132{
133 /** The magic (RTTARFILE_MAGIC). */
134 uint32_t u32Magic;
135 /** The open mode. */
136 uint32_t fOpenMode;
137 /** Pointer to back to the tar file. */
138 PRTTARINTERNAL pTar;
139 /** The name of the file. */
140 char *pszFilename;
141 /** The offset into the archive where the file header starts. */
142 uint64_t offStart;
143 /** The size of the file. */
144 uint64_t cbSize;
145 /** The size set by RTTarFileSetSize(). */
146 uint64_t cbSetSize;
147 /** The current offset within this file. */
148 uint64_t offCurrent;
149 /** The VFS I/O stream (only for reading atm). */
150 RTVFSIOSTREAM hVfsIos;
151} RTTARFILEINTERNAL;
152/** Pointer to the internal data of a tar file. */
153typedef RTTARFILEINTERNAL *PRTTARFILEINTERNAL;
154
155
156
157/*******************************************************************************
158* Defined Constants And Macros *
159*******************************************************************************/
160
161/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
162/* RTTAR */
163#define RTTAR_VALID_RETURN_RC(hHandle, rc) \
164 do { \
165 AssertPtrReturn((hHandle), (rc)); \
166 AssertReturn((hHandle)->u32Magic == RTTAR_MAGIC, (rc)); \
167 } while (0)
168/* RTTARFILE */
169#define RTTARFILE_VALID_RETURN_RC(hHandle, rc) \
170 do { \
171 AssertPtrReturn((hHandle), (rc)); \
172 AssertReturn((hHandle)->u32Magic == RTTARFILE_MAGIC, (rc)); \
173 } while (0)
174
175/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
176/* RTTAR */
177#define RTTAR_VALID_RETURN(hHandle) RTTAR_VALID_RETURN_RC((hHandle), VERR_INVALID_HANDLE)
178/* RTTARFILE */
179#define RTTARFILE_VALID_RETURN(hHandle) RTTARFILE_VALID_RETURN_RC((hHandle), VERR_INVALID_HANDLE)
180
181/** Validates a handle and returns (void) if not valid. */
182/* RTTAR */
183#define RTTAR_VALID_RETURN_VOID(hHandle) \
184 do { \
185 AssertPtrReturnVoid(hHandle); \
186 AssertReturnVoid((hHandle)->u32Magic == RTTAR_MAGIC); \
187 } while (0)
188/* RTTARFILE */
189#define RTTARFILE_VALID_RETURN_VOID(hHandle) \
190 do { \
191 AssertPtrReturnVoid(hHandle); \
192 AssertReturnVoid((hHandle)->u32Magic == RTTARFILE_MAGIC); \
193 } while (0)
194
195
196RTR3DECL(int) RTTarOpen(PRTTAR phTar, const char *pszTarname, uint32_t fMode, bool fStream)
197{
198 AssertReturn(!fStream || !(fMode & RTFILE_O_WRITE), VERR_INVALID_PARAMETER);
199
200 /*
201 * Create a tar instance.
202 */
203 PRTTARINTERNAL pThis = (PRTTARINTERNAL)RTMemAllocZ(sizeof(RTTARINTERNAL));
204 if (!pThis)
205 return VERR_NO_MEMORY;
206
207 pThis->u32Magic = RTTAR_MAGIC;
208 pThis->fOpenMode = fMode;
209 pThis->fStreamMode = fStream && (fMode & RTFILE_O_READ);
210
211 /*
212 * Open the tar file.
213 */
214 pThis->hVfsFile = NIL_RTVFSFILE;
215 pThis->hVfsFss = NIL_RTVFSFSSTREAM;
216 pThis->fFssAtStart = false;
217 pThis->hVfsCur = NIL_RTVFSIOSTREAM;
218 pThis->pszVfsCurName = NULL;
219
220 int rc;
221 if (!(fMode & RTFILE_O_WRITE))
222 {
223 rc = RTVfsFileOpenNormal(pszTarname, fMode, &pThis->hVfsFile);
224 if (RT_SUCCESS(rc))
225 {
226 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(pThis->hVfsFile);
227 rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0 /*fFlags*/, &pThis->hVfsFss);
228 if (RT_SUCCESS(rc))
229 pThis->fFssAtStart = true;
230 else
231 {
232 RTVfsFileRelease(pThis->hVfsFile);
233 pThis->hVfsFile = NIL_RTVFSFILE;
234 }
235 RTVfsIoStrmRelease(hVfsIos);
236 }
237 }
238 else
239 rc = RTFileOpen(&pThis->hTarFile, pszTarname, fMode);
240 if (RT_SUCCESS(rc))
241 {
242 *phTar = pThis;
243 return VINF_SUCCESS;
244 }
245
246 RTMemFree(pThis);
247 return rc;
248}
249
250RTR3DECL(int) RTTarClose(RTTAR hTar)
251{
252 if (hTar == NIL_RTTAR)
253 return VINF_SUCCESS;
254
255 PRTTARINTERNAL pInt = hTar;
256 RTTAR_VALID_RETURN(pInt);
257
258 int rc = VINF_SUCCESS;
259
260 /* gtar gives a warning, but the documentation says EOF is indicated by a
261 * zero block. Disabled for now. */
262#if 0
263 {
264 /* Append the EOF record which is filled all by zeros */
265 RTTARRECORD record;
266 RT_ZERO(record);
267 rc = RTFileWrite(pInt->hTarFile, &record, sizeof(record), NULL);
268 }
269#endif
270
271 if (pInt->hVfsFss != NIL_RTVFSFSSTREAM)
272 {
273 uint32_t cRefs = RTVfsFsStrmRelease(pInt->hVfsFss); Assert(cRefs != UINT32_MAX);
274 pInt->hVfsFss = NIL_RTVFSFSSTREAM;
275 }
276
277 if (pInt->hVfsFile != NIL_RTVFSFILE)
278 {
279 uint32_t cRefs = RTVfsFileRelease(pInt->hVfsFile); Assert(cRefs != UINT32_MAX);
280 pInt->hVfsFile = NIL_RTVFSFILE;
281 }
282
283 if (pInt->hVfsCur != NIL_RTVFSIOSTREAM)
284 {
285 RTVfsIoStrmRelease(pInt->hVfsCur);
286 pInt->hVfsCur = NIL_RTVFSIOSTREAM;
287 }
288
289 if (pInt->pszVfsCurName)
290 {
291 RTStrFree(pInt->pszVfsCurName);
292 pInt->pszVfsCurName = NULL;
293 }
294
295 if (pInt->hTarFile != NIL_RTFILE)
296 {
297 rc = RTFileClose(pInt->hTarFile);
298 pInt->hTarFile = NIL_RTFILE;
299 }
300
301 pInt->u32Magic = RTTAR_MAGIC_DEAD;
302
303 RTMemFree(pInt);
304
305 return rc;
306}
307
308
309RTR3DECL(int) RTTarSeekNextFile(RTTAR hTar)
310{
311 PRTTARINTERNAL pInt = hTar;
312 RTTAR_VALID_RETURN(pInt);
313
314 if (!pInt->fStreamMode)
315 return VERR_INVALID_STATE;
316
317 /*
318 * Release the current object.
319 */
320 if (pInt->hVfsCur != NIL_RTVFSIOSTREAM)
321 {
322 RTVfsIoStrmRelease(pInt->hVfsCur);
323 pInt->hVfsCur = NIL_RTVFSIOSTREAM;
324 }
325
326 if (pInt->pszVfsCurName)
327 {
328 RTStrFree(pInt->pszVfsCurName);
329 pInt->pszVfsCurName = NULL;
330 }
331
332 /*
333 * Find the next file.
334 */
335 for (;;)
336 {
337 char *pszName;
338 RTVFSOBJTYPE enmType;
339 RTVFSOBJ hVfsObj;
340 int rc = RTVfsFsStrmNext(hTar->hVfsFss, &pszName, &enmType, &hVfsObj);
341 if (rc == VERR_EOF)
342 return VERR_TAR_END_OF_FILE;
343
344 if ( enmType == RTVFSOBJTYPE_FILE
345 || enmType == RTVFSOBJTYPE_IO_STREAM
346 || enmType == RTVFSOBJTYPE_DIR)
347 {
348 pInt->pszVfsCurName = pszName;
349 if (enmType == RTVFSOBJTYPE_DIR)
350 rc = VINF_TAR_DIR_PATH;
351 else
352 {
353 pInt->hVfsCur = RTVfsObjToIoStream(hVfsObj);
354 Assert(pInt->hVfsCur != NIL_RTVFSIOSTREAM);
355 rc = VINF_SUCCESS;
356 }
357 RTVfsObjRelease(hVfsObj);
358 return rc;
359 }
360 RTStrFree(pszName);
361 RTVfsObjRelease(hVfsObj);
362 }
363}
364
365
366RTR3DECL(int) RTTarCurrentFile(RTTAR hTar, char **ppszFilename)
367{
368 /* Validate input. */
369 AssertPtrNullReturn(ppszFilename, VERR_INVALID_POINTER);
370
371 PRTTARINTERNAL pInt = hTar;
372 RTTAR_VALID_RETURN(pInt);
373
374 if (!pInt->fStreamMode)
375 return VERR_INVALID_STATE;
376
377 if (!pInt->pszVfsCurName)
378 {
379 int rc = RTTarSeekNextFile(pInt);
380 if (RT_FAILURE(rc))
381 return rc;
382 }
383 Assert(pInt->pszVfsCurName);
384
385 if (ppszFilename)
386 {
387 *ppszFilename = RTStrDup(pInt->pszVfsCurName);
388 if (!*ppszFilename)
389 return VERR_NO_STR_MEMORY;
390 }
391
392 return pInt->hVfsCur != NIL_RTVFSIOSTREAM ? VINF_SUCCESS : VINF_TAR_DIR_PATH;
393}
394
395
396/**
397 * Creates a tar file handle for a read-only VFS stream object.
398 *
399 * @returns IPRT status code.
400 * @param pszName The file name. Automatically freed on failure.
401 * @param hVfsIos The VFS I/O stream we create the handle around.
402 * The reference is NOT consumed.
403 * @param fOpen The open flags.
404 * @param ppFile Where to return the handle.
405 */
406static int rtTarFileCreateHandleForReadOnly(char *pszName, RTVFSIOSTREAM hVfsIos, uint32_t fOpen, PRTTARFILEINTERNAL *ppFile)
407{
408 int rc;
409 PRTTARFILEINTERNAL pNewFile = (PRTTARFILEINTERNAL)RTMemAllocZ(sizeof(*pNewFile));
410 if (pNewFile)
411 {
412 RTFSOBJINFO ObjInfo;
413 rc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_UNIX);
414 if (RT_SUCCESS(rc))
415 {
416 pNewFile->u32Magic = RTTARFILE_MAGIC;
417 pNewFile->pTar = NULL;
418 pNewFile->pszFilename = pszName;
419 pNewFile->offStart = UINT64_MAX;
420 pNewFile->cbSize = ObjInfo.cbObject;
421 pNewFile->cbSetSize = 0;
422 pNewFile->offCurrent = 0;
423 pNewFile->fOpenMode = fOpen;
424 pNewFile->hVfsIos = hVfsIos;
425
426 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIos); Assert(cRefs != UINT32_MAX); NOREF(cRefs);
427
428 *ppFile = pNewFile;
429 return VINF_SUCCESS;
430 }
431
432 RTMemFree(pNewFile);
433 }
434 else
435 rc = VERR_NO_MEMORY;
436 RTStrFree(pszName);
437 return rc;
438}
439
440
441
442RTR3DECL(int) RTTarFileOpenCurrentFile(RTTAR hTar, PRTTARFILE phFile, char **ppszFilename, uint32_t fOpen)
443{
444 /* Validate input. */
445 AssertPtrReturn(phFile, VERR_INVALID_POINTER);
446 AssertPtrNullReturn(ppszFilename, VERR_INVALID_POINTER);
447 AssertReturn((fOpen & RTFILE_O_READ), VERR_INVALID_PARAMETER); /* Only valid in read mode. */
448
449 PRTTARINTERNAL pInt = hTar;
450 RTTAR_VALID_RETURN(pInt);
451
452 if (!pInt->fStreamMode)
453 return VERR_INVALID_STATE;
454
455 /*
456 * Make sure there is a current file (first call w/o RTTarSeekNextFile call).
457 */
458 if (pInt->hVfsCur == NIL_RTVFSIOSTREAM)
459 {
460 if (pInt->pszVfsCurName)
461 return -VINF_TAR_DIR_PATH;
462
463 int rc = RTTarSeekNextFile(pInt);
464 if (RT_FAILURE(rc))
465 return rc;
466
467 if (pInt->hVfsCur == NIL_RTVFSIOSTREAM)
468 return -VINF_TAR_DIR_PATH;
469 }
470 Assert(pInt->pszVfsCurName);
471
472 /*
473 * Return a copy of the filename if requested.
474 */
475 if (ppszFilename)
476 {
477 *ppszFilename = RTStrDup(pInt->pszVfsCurName);
478 if (!*ppszFilename)
479 return VERR_NO_STR_MEMORY;
480 }
481
482 /*
483 * Create a handle for it.
484 */
485 int rc = rtTarFileCreateHandleForReadOnly(RTStrDup(pInt->pszVfsCurName), pInt->hVfsCur, RTFILE_O_READ, phFile);
486 if (RT_SUCCESS(rc))
487 {
488 /* Force a RTTarSeekNextFile call the next time around. */
489 RTVfsIoStrmRelease(pInt->hVfsCur);
490 pInt->hVfsCur = NIL_RTVFSIOSTREAM;
491 RTStrFree(pInt->pszVfsCurName);
492 pInt->pszVfsCurName = NULL;
493 }
494 else if (ppszFilename)
495 {
496 RTStrFree(*ppszFilename);
497 *ppszFilename = NULL;
498 }
499
500 return rc;
501}
502
503
504/* Only used for write handles. */
505static PRTTARFILEINTERNAL rtTarFileCreateForWrite(PRTTARINTERNAL pInt, const char *pszFilename, uint32_t fOpen)
506{
507 PRTTARFILEINTERNAL pFileInt = (PRTTARFILEINTERNAL)RTMemAllocZ(sizeof(RTTARFILEINTERNAL));
508 if (!pFileInt)
509 return NULL;
510
511 pFileInt->u32Magic = RTTARFILE_MAGIC;
512 pFileInt->pTar = pInt;
513 pFileInt->fOpenMode = fOpen;
514 pFileInt->pszFilename = RTStrDup(pszFilename);
515 if (!pFileInt->pszFilename)
516 {
517 pFileInt->hVfsIos = NIL_RTVFSIOSTREAM;
518 RTMemFree(pFileInt);
519 return NULL;
520 }
521
522 return pFileInt;
523}
524
525
526RTR3DECL(int) RTTarFileOpen(RTTAR hTar, PRTTARFILE phFile, const char *pszFilename, uint32_t fOpen)
527{
528 AssertReturn((fOpen & RTFILE_O_READ) || (fOpen & RTFILE_O_WRITE), VERR_INVALID_PARAMETER);
529
530 PRTTARINTERNAL pInt = hTar;
531 RTTAR_VALID_RETURN(pInt);
532
533 if (!pInt->hTarFile)
534 return VERR_INVALID_HANDLE;
535
536 if (pInt->fStreamMode)
537 return VERR_INVALID_STATE;
538
539 if (fOpen & RTFILE_O_WRITE)
540 {
541 if (!(pInt->fOpenMode & RTFILE_O_WRITE))
542 return VERR_WRITE_PROTECT;
543 if (pInt->fFileOpenForWrite)
544 return VERR_TOO_MANY_OPEN_FILES;
545 }
546
547 int rc = VINF_SUCCESS;
548 if (!(fOpen & RTFILE_O_WRITE))
549 {
550 /*
551 * Rewind the stream if necessary.
552 */
553 if (!pInt->fFssAtStart)
554 {
555 if (pInt->hVfsFss != NIL_RTVFSFSSTREAM)
556 {
557 uint32_t cRefs = RTVfsFsStrmRelease(pInt->hVfsFss); Assert(cRefs != UINT32_MAX);
558 pInt->hVfsFss = NIL_RTVFSFSSTREAM;
559 }
560
561 if (pInt->hVfsFile == NIL_RTVFSFILE)
562 {
563 rc = RTVfsFileFromRTFile(pInt->hTarFile, RTFILE_O_READ, true /*fLeaveOpen*/, &pInt->hVfsFile);
564 if (RT_FAILURE(rc))
565 return rc;
566 }
567 Assert(pInt->hVfsCur == NIL_RTVFSIOSTREAM && pInt->pszVfsCurName == NULL);
568
569 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(pInt->hVfsFile);
570 rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0 /*fFlags*/, &pInt->hVfsFss);
571 RTVfsIoStrmRelease(hVfsIos);
572 if (RT_FAILURE(rc))
573 return rc;
574 }
575
576 /*
577 * Search the file system stream.
578 */
579 pInt->fFssAtStart = false;
580 for (;;)
581 {
582 char *pszName;
583 RTVFSOBJTYPE enmType;
584 RTVFSOBJ hVfsObj;
585 rc = RTVfsFsStrmNext(pInt->hVfsFss, &pszName, &enmType, &hVfsObj);
586 if (rc == VERR_EOF)
587 return VERR_FILE_NOT_FOUND;
588 if (RT_FAILURE(rc))
589 return rc;
590
591 if (!RTStrCmp(pszName, pszFilename))
592 {
593 if (enmType == RTVFSOBJTYPE_FILE || enmType == RTVFSOBJTYPE_IO_STREAM)
594 rc = rtTarFileCreateHandleForReadOnly(pszName, RTVfsObjToIoStream(hVfsObj), fOpen, phFile);
595 else
596 {
597 rc = VERR_UNEXPECTED_FS_OBJ_TYPE;
598 RTStrFree(pszName);
599 }
600 RTVfsObjRelease(hVfsObj);
601 break;
602 }
603 RTStrFree(pszName);
604 RTVfsObjRelease(hVfsObj);
605 } /* Search loop. */
606 }
607 else
608 {
609 PRTTARFILEINTERNAL pFileInt = rtTarFileCreateForWrite(pInt, pszFilename, fOpen);
610 if (!pFileInt)
611 return VERR_NO_MEMORY;
612
613 pInt->fFileOpenForWrite = true;
614
615 /* If we are in write mode, we also in append mode. Add an dummy
616 * header at the end of the current file. It will be filled by the
617 * close operation. */
618 rc = RTFileSeek(pFileInt->pTar->hTarFile, 0, RTFILE_SEEK_END, &pFileInt->offStart);
619 if (RT_SUCCESS(rc))
620 {
621 RTTARRECORD record;
622 RT_ZERO(record);
623 rc = RTFileWrite(pFileInt->pTar->hTarFile, &record, sizeof(RTTARRECORD), NULL);
624 }
625
626 if (RT_SUCCESS(rc))
627 *phFile = (RTTARFILE)pFileInt;
628 else
629 {
630 /* Cleanup on failure */
631 if (pFileInt->pszFilename)
632 RTStrFree(pFileInt->pszFilename);
633 RTMemFree(pFileInt);
634 }
635 }
636
637 return rc;
638}
639
640
641/**
642 * Calculates the TAR header checksums and detects if it's all zeros.
643 *
644 * @returns true if all zeros, false if not.
645 * @param pHdr The header to checksum.
646 * @param pi32Unsigned Where to store the checksum calculated using
647 * unsigned chars. This is the one POSIX
648 * specifies.
649 * @param pi32Signed Where to store the checksum calculated using
650 * signed chars.
651 *
652 * @remarks The reason why we calculate the checksum as both signed and unsigned
653 * has to do with various the char C type being signed on some hosts
654 * and unsigned on others.
655 *
656 * @remarks Borrowed from tarvfs.cpp.
657 */
658static bool rtZipTarCalcChkSum(PCRTZIPTARHDR pHdr, int32_t *pi32Unsigned, int32_t *pi32Signed)
659{
660 int32_t i32Unsigned = 0;
661 int32_t i32Signed = 0;
662
663 /*
664 * Sum up the entire header.
665 */
666 const char *pch = (const char *)pHdr;
667 const char *pchEnd = pch + sizeof(*pHdr);
668 do
669 {
670 i32Unsigned += *(unsigned char *)pch;
671 i32Signed += *(signed char *)pch;
672 } while (++pch != pchEnd);
673
674 /*
675 * Check if it's all zeros and replace the chksum field with spaces.
676 */
677 bool const fZeroHdr = i32Unsigned == 0;
678
679 pch = pHdr->Common.chksum;
680 pchEnd = pch + sizeof(pHdr->Common.chksum);
681 do
682 {
683 i32Unsigned -= *(unsigned char *)pch;
684 i32Signed -= *(signed char *)pch;
685 } while (++pch != pchEnd);
686
687 i32Unsigned += (unsigned char)' ' * sizeof(pHdr->Common.chksum);
688 i32Signed += (signed char)' ' * sizeof(pHdr->Common.chksum);
689
690 *pi32Unsigned = i32Unsigned;
691 if (pi32Signed)
692 *pi32Signed = i32Signed;
693 return fZeroHdr;
694}
695
696
697static void rtTarSizeToRec(PRTTARRECORD pRecord, uint64_t cbSize)
698{
699 /*
700 * Small enough for the standard octal string encoding?
701 *
702 * Note! We could actually use the terminator character as well if we liked,
703 * but let not do that as it's easier to test this way.
704 */
705 if (cbSize < _4G * 2U)
706 RTStrPrintf(pRecord->h.size, sizeof(pRecord->h.size), "%0.11llo", cbSize);
707 else
708 {
709 /*
710 * Base 256 extension. Set the highest bit of the left most character.
711 * We don't deal with negatives here, cause the size have to be greater
712 * than zero.
713 *
714 * Note! The base-256 extension are never used by gtar or libarchive
715 * with the "ustar \0" format version, only the later
716 * "ustar\000" version. However, this shouldn't cause much
717 * trouble as they are not picky about what they read.
718 */
719 size_t cchField = sizeof(pRecord->h.size) - 1;
720 unsigned char *puchField = (unsigned char*)pRecord->h.size;
721 puchField[0] = 0x80;
722 do
723 {
724 puchField[cchField--] = cbSize & 0xff;
725 cbSize >>= 8;
726 } while (cchField);
727 }
728}
729
730
731static int rtTarCreateHeaderRecord(PRTTARRECORD pRecord, const char *pszSrcName, uint64_t cbSize,
732 RTUID uid, RTGID gid, RTFMODE fmode, int64_t mtime)
733{
734 /** @todo check for field overflows. */
735 /* Fill the header record */
736// RT_ZERO(pRecord); - done by the caller.
737 /** @todo use RTStrCopy */
738 size_t cb = RTStrPrintf(pRecord->h.name, sizeof(pRecord->h.name), "%s", pszSrcName);
739 if (cb < strlen(pszSrcName))
740 return VERR_BUFFER_OVERFLOW;
741 RTStrPrintf(pRecord->h.mode, sizeof(pRecord->h.mode), "%0.7o", fmode);
742 RTStrPrintf(pRecord->h.uid, sizeof(pRecord->h.uid), "%0.7o", uid);
743 RTStrPrintf(pRecord->h.gid, sizeof(pRecord->h.gid), "%0.7o", gid);
744 rtTarSizeToRec(pRecord, cbSize);
745 RTStrPrintf(pRecord->h.mtime, sizeof(pRecord->h.mtime), "%0.11llo", mtime);
746 RTStrPrintf(pRecord->h.magic, sizeof(pRecord->h.magic), "ustar ");
747 RTStrPrintf(pRecord->h.uname, sizeof(pRecord->h.uname), "someone");
748 RTStrPrintf(pRecord->h.gname, sizeof(pRecord->h.gname), "someone");
749 pRecord->h.linkflag = LF_NORMAL;
750
751 /* Create the checksum out of the new header */
752 int32_t iUnsignedChksum, iSignedChksum;
753 if (rtZipTarCalcChkSum((PCRTZIPTARHDR)pRecord, &iUnsignedChksum, &iSignedChksum))
754 return VERR_TAR_END_OF_FILE;
755
756 /* Format the checksum */
757 RTStrPrintf(pRecord->h.chksum, sizeof(pRecord->h.chksum), "%0.7o", iUnsignedChksum);
758
759 return VINF_SUCCESS;
760}
761
762
763DECLINLINE(void *) rtTarMemTmpAlloc(size_t *pcbSize)
764{
765 *pcbSize = 0;
766 /* Allocate a reasonably large buffer, fall back on a tiny one.
767 * Note: has to be 512 byte aligned and >= 512 byte. */
768 size_t cbTmp = _1M;
769 void *pvTmp = RTMemTmpAlloc(cbTmp);
770 if (!pvTmp)
771 {
772 cbTmp = sizeof(RTTARRECORD);
773 pvTmp = RTMemTmpAlloc(cbTmp);
774 }
775 *pcbSize = cbTmp;
776 return pvTmp;
777}
778
779
780static int rtTarAppendZeros(PRTTARFILEINTERNAL pFileInt, uint64_t cbSize)
781{
782 /* Allocate a temporary buffer for copying the tar content in blocks. */
783 size_t cbTmp = 0;
784 void *pvTmp = rtTarMemTmpAlloc(&cbTmp);
785 if (!pvTmp)
786 return VERR_NO_MEMORY;
787 RT_BZERO(pvTmp, cbTmp);
788
789 int rc = VINF_SUCCESS;
790 uint64_t cbAllWritten = 0;
791 size_t cbWritten = 0;
792 for (;;)
793 {
794 if (cbAllWritten >= cbSize)
795 break;
796 size_t cbToWrite = RT_MIN(cbSize - cbAllWritten, cbTmp);
797 rc = RTTarFileWriteAt(pFileInt, pFileInt->offCurrent, pvTmp, cbToWrite, &cbWritten);
798 if (RT_FAILURE(rc))
799 break;
800 cbAllWritten += cbWritten;
801 }
802
803 RTMemTmpFree(pvTmp);
804
805 return rc;
806}
807
808
809RTR3DECL(int) RTTarFileClose(RTTARFILE hFile)
810{
811 /* Already closed? */
812 if (hFile == NIL_RTTARFILE)
813 return VINF_SUCCESS;
814
815 PRTTARFILEINTERNAL pFileInt = hFile;
816 RTTARFILE_VALID_RETURN(pFileInt);
817
818 int rc = VINF_SUCCESS;
819
820 /* In write mode: */
821 if ((pFileInt->fOpenMode & (RTFILE_O_WRITE | RTFILE_O_READ)) == RTFILE_O_WRITE)
822 {
823 pFileInt->pTar->fFileOpenForWrite = false;
824 do
825 {
826 /* If the user has called RTTarFileSetSize in the meantime, we have
827 to make sure the file has the right size. */
828 if (pFileInt->cbSetSize > pFileInt->cbSize)
829 {
830 rc = rtTarAppendZeros(pFileInt, pFileInt->cbSetSize - pFileInt->cbSize);
831 if (RT_FAILURE(rc))
832 break;
833 }
834
835 /* If the written size isn't 512 byte aligned, we need to fix this. */
836 RTTARRECORD record;
837 RT_ZERO(record);
838 uint64_t cbSizeAligned = RT_ALIGN(pFileInt->cbSize, sizeof(RTTARRECORD));
839 if (cbSizeAligned != pFileInt->cbSize)
840 {
841 /* Note the RTFile method. We didn't increase the cbSize or cbCurrentPos here. */
842 rc = RTFileWriteAt(pFileInt->pTar->hTarFile,
843 pFileInt->offStart + sizeof(RTTARRECORD) + pFileInt->cbSize,
844 &record,
845 cbSizeAligned - pFileInt->cbSize,
846 NULL);
847 if (RT_FAILURE(rc))
848 break;
849 }
850
851 /* Create a header record for the file */
852 /* Todo: mode, gid, uid, mtime should be setable (or detected myself) */
853 RTTIMESPEC time;
854 RTTimeNow(&time);
855 rc = rtTarCreateHeaderRecord(&record, pFileInt->pszFilename, pFileInt->cbSize,
856 0, 0, 0600, RTTimeSpecGetSeconds(&time));
857 if (RT_FAILURE(rc))
858 break;
859
860 /* Write this at the start of the file data */
861 rc = RTFileWriteAt(pFileInt->pTar->hTarFile, pFileInt->offStart, &record, sizeof(RTTARRECORD), NULL);
862 if (RT_FAILURE(rc))
863 break;
864 }
865 while (0);
866 }
867
868 /*
869 * Now cleanup and delete the handle.
870 */
871 if (pFileInt->pszFilename)
872 RTStrFree(pFileInt->pszFilename);
873 if (pFileInt->hVfsIos != NIL_RTVFSIOSTREAM)
874 {
875 RTVfsIoStrmRelease(pFileInt->hVfsIos);
876 pFileInt->hVfsIos = NIL_RTVFSIOSTREAM;
877 }
878 pFileInt->u32Magic = RTTARFILE_MAGIC_DEAD;
879 RTMemFree(pFileInt);
880
881 return rc;
882}
883
884
885RTR3DECL(int) RTTarFileReadAt(RTTARFILE hFile, uint64_t off, void *pvBuf, size_t cbToRead, size_t *pcbRead)
886{
887 PRTTARFILEINTERNAL pFileInt = hFile;
888 RTTARFILE_VALID_RETURN(pFileInt);
889
890 size_t cbTmpRead = 0;
891 int rc = RTVfsIoStrmReadAt(pFileInt->hVfsIos, off, pvBuf, cbToRead, true /*fBlocking*/, &cbTmpRead);
892 if (RT_SUCCESS(rc))
893 {
894 pFileInt->offCurrent = off + cbTmpRead;
895 if (pcbRead)
896 *pcbRead = cbTmpRead;
897 if (rc == VINF_EOF)
898 rc = pcbRead ? VINF_SUCCESS : VERR_EOF;
899 }
900 else if (pcbRead)
901 *pcbRead = 0;
902 return rc;
903}
904
905
906RTR3DECL(int) RTTarFileWriteAt(RTTARFILE hFile, uint64_t off, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
907{
908 PRTTARFILEINTERNAL pFileInt = hFile;
909 RTTARFILE_VALID_RETURN(pFileInt);
910
911 if ((pFileInt->fOpenMode & RTFILE_O_WRITE) != RTFILE_O_WRITE)
912 return VERR_WRITE_ERROR;
913
914 size_t cbTmpWritten = 0;
915 int rc = RTFileWriteAt(pFileInt->pTar->hTarFile, pFileInt->offStart + 512 + off, pvBuf, cbToWrite, &cbTmpWritten);
916 pFileInt->cbSize += cbTmpWritten;
917 pFileInt->offCurrent = off + cbTmpWritten;
918 if (pcbWritten)
919 *pcbWritten = cbTmpWritten;
920
921 return rc;
922}
923
924
925RTR3DECL(int) RTTarFileGetSize(RTTARFILE hFile, uint64_t *pcbSize)
926{
927 /* Validate input */
928 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
929
930 PRTTARFILEINTERNAL pFileInt = hFile;
931 RTTARFILE_VALID_RETURN(pFileInt);
932
933 *pcbSize = RT_MAX(pFileInt->cbSetSize, pFileInt->cbSize);
934
935 return VINF_SUCCESS;
936}
937
938
939RTR3DECL(int) RTTarFileSetSize(RTTARFILE hFile, uint64_t cbSize)
940{
941 PRTTARFILEINTERNAL pFileInt = hFile;
942 RTTARFILE_VALID_RETURN(pFileInt);
943
944 if ((pFileInt->fOpenMode & RTFILE_O_WRITE) != RTFILE_O_WRITE)
945 return VERR_WRITE_ERROR;
946
947 /** @todo If cbSize is smaller than pFileInt->cbSize we have to
948 * truncate the current file. */
949 pFileInt->cbSetSize = cbSize;
950
951 return VINF_SUCCESS;
952}
953
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