~ubuntu-branches/ubuntu/raring/virtualbox-ose/raring

« back to all changes in this revision

Viewing changes to src/VBox/Runtime/common/misc/tar.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2011-01-30 23:27:25 UTC
  • mfrom: (0.3.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20110130232725-2ouajjd2ggdet0zd
Tags: 4.0.2-dfsg-1ubuntu1
* Merge from Debian unstable, remaining changes:
  - Add Apport hook.
    - debian/virtualbox-ose.files/source_virtualbox-ose.py
    - debian/virtualbox-ose.install
  - Drop *-source packages.
* Drop ubuntu-01-fix-build-gcc45.patch, fixed upstream.
* Drop ubuntu-02-as-needed.patch, added to the Debian package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: tar.cpp $ */
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
 
/*******************************************************************************
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
 
 
60
 
typedef 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;
81
 
typedef RTTARRECORD *PRTTARRECORD;
82
 
AssertCompileSize(RTTARRECORD, 512);
83
 
AssertCompileMemberOffset(RTTARRECORD, h.size, 100+8*3);
84
 
 
85
 
#if 0 /* not currently used */
86
 
typedef struct RTTARFILELIST
87
 
{
88
 
    char *pszFilename;
89
 
    RTTARFILELIST *pNext;
90
 
} RTTARFILELIST;
91
 
typedef RTTARFILELIST *PRTTARFILELIST;
92
 
#endif
93
 
 
94
 
 
95
 
/*******************************************************************************
96
 
*   Internal Functions                                                         *
97
 
*******************************************************************************/
98
 
 
99
 
static int rtTarCalcChkSum(PRTTARRECORD pRecord, uint32_t *pChkSum)
100
 
{
101
 
    uint32_t check = 0;
102
 
    uint32_t zero = 0;
103
 
    for (size_t i = 0; i < sizeof(RTTARRECORD); ++i)
104
 
    {
105
 
        /* Calculate the sum of every byte from the header. The checksum field
106
 
         * itself is counted as all blanks. */
107
 
        if (   i <  RT_UOFFSETOF(RTTARRECORD, h.chksum)
108
 
            || i >= RT_UOFFSETOF(RTTARRECORD, h.linkflag))
109
 
            check += pRecord->d[i];
110
 
        else
111
 
            check += ' ';
112
 
        /* Additional check if all fields are zero, which indicate EOF. */
113
 
        zero += pRecord->d[i];
114
 
    }
115
 
 
116
 
    /* EOF? */
117
 
    if (!zero)
118
 
        return VERR_EOF;
119
 
 
120
 
    *pChkSum = check;
121
 
    return VINF_SUCCESS;
122
 
}
123
 
 
124
 
static int rtTarCheckHeader(PRTTARRECORD pRecord)
125
 
{
126
 
    uint32_t check;
127
 
    int rc = rtTarCalcChkSum(pRecord, &check);
128
 
    /* EOF? */
129
 
    if (RT_FAILURE(rc))
130
 
        return rc;
131
 
 
132
 
    /* Verify the checksum */
133
 
    uint32_t sum;
134
 
    rc = RTStrToUInt32Full(pRecord->h.chksum, 8, &sum);
135
 
    if (RT_SUCCESS(rc) && sum == check)
136
 
        return VINF_SUCCESS;
137
 
    return VERR_TAR_CHKSUM_MISMATCH;
138
 
}
139
 
 
140
 
static int rtTarCopyFileFrom(RTFILE hFile, const char *pszTargetName, PRTTARRECORD pRecord)
141
 
{
142
 
    RTFILE hNewFile;
143
 
    /* Open the target file */
144
 
    int rc = RTFileOpen(&hNewFile, pszTargetName, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
145
 
    if (RT_FAILURE(rc))
146
 
        return rc;
147
 
 
148
 
/**@todo r=bird: Use a bigger buffer here, see comment in rtTarCopyFileTo. */
149
 
 
150
 
    uint64_t cbToCopy = RTStrToUInt64(pRecord->h.size);
151
 
    size_t cbAllWritten = 0;
152
 
    RTTARRECORD record;
153
 
    /* Copy the content from hFile over to pszTargetName. This is done block
154
 
     * wise in 512 byte steps. After this copying is finished hFile will be on
155
 
     * a 512 byte boundary, regardless if the file copied is 512 byte size
156
 
     * aligned. */
157
 
    for (;;)
158
 
    {
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
 
    }
177
 
 
178
 
    /* Now set all file attributes */
179
 
    if (RT_SUCCESS(rc))
180
 
    {
181
 
        int32_t mode;
182
 
        rc = RTStrToInt32Full(pRecord->h.mode, 8, &mode);
183
 
        if (RT_SUCCESS(rc))
184
 
        {
185
 
            mode |= RTFS_TYPE_FILE; /* For now we support regular files only */
186
 
            /* Set the mode */
187
 
            rc = RTFileSetMode(hNewFile, mode);
188
 
        }
189
 
    }
190
 
    /* Make sure the called doesn't mix truncated tar files with the official
191
 
     * end indicated by rtTarCalcChkSum. */
192
 
    else if (rc == VERR_EOF)
193
 
        rc = VERR_FILE_IO_ERROR;
194
 
 
195
 
    RTFileClose(hNewFile);
196
 
 
197
 
    /* Delete the freshly created file in the case of an error */
198
 
    if (RT_FAILURE(rc))
199
 
        RTFileDelete(pszTargetName);
200
 
 
201
 
    return rc;
202
 
}
203
 
 
204
 
static int rtTarCopyFileTo(RTFILE hFile, const char *pszSrcName)
205
 
{
206
 
    RTFILE hOldFile;
207
 
    /* Open the source file */
208
 
    int rc = RTFileOpen(&hOldFile, pszSrcName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
209
 
    if (RT_FAILURE(rc))
210
 
        return rc;
211
 
 
212
 
    /* Get the size of the source file */
213
 
    uint64_t cbSize;
214
 
    rc = RTFileGetSize(hOldFile, &cbSize);
215
 
    if (RT_FAILURE(rc))
216
 
    {
217
 
        RTFileClose(hOldFile);
218
 
        return rc;
219
 
    }
220
 
    /* Get some info from the source file */
221
 
    RTFSOBJINFO info;
222
 
    RTUID uid = 0;
223
 
    RTGID gid = 0;
224
 
    RTFMODE fmode = 0600; /* Make some save default */
225
 
    int64_t mtime = 0;
226
 
    /* This isn't critical. Use the defaults if it fails. */
227
 
    rc = RTFileQueryInfo(hOldFile, &info, RTFSOBJATTRADD_UNIX);
228
 
    if (RT_SUCCESS(rc))
229
 
    {
230
 
        fmode = info.Attr.fMode & RTFS_UNIX_MASK;
231
 
        uid = info.Attr.u.Unix.uid;
232
 
        gid = info.Attr.u.Unix.gid;
233
 
        mtime = RTTimeSpecGetSeconds(&info.ModificationTime);
234
 
    }
235
 
 
236
 
    /* Fill the header record */
237
 
    RTTARRECORD record;
238
 
    RT_ZERO(record);
239
 
    RTStrPrintf(record.h.name,  sizeof(record.h.name),  "%s",     RTPathFilename(pszSrcName));
240
 
    RTStrPrintf(record.h.mode,  sizeof(record.h.mode),  "%0.7o",  fmode);
241
 
    RTStrPrintf(record.h.uid,   sizeof(record.h.uid),   "%0.7o",  uid);
242
 
    RTStrPrintf(record.h.gid,   sizeof(record.h.gid),   "%0.7o",  gid);
243
 
    RTStrPrintf(record.h.size,  sizeof(record.h.size),  "%0.11o", cbSize);
244
 
    RTStrPrintf(record.h.mtime, sizeof(record.h.mtime), "%0.11o", mtime);
245
 
    RTStrPrintf(record.h.magic, sizeof(record.h.magic), "ustar  ");
246
 
    RTStrPrintf(record.h.uname, sizeof(record.h.uname), "someone");
247
 
    RTStrPrintf(record.h.gname, sizeof(record.h.gname), "someone");
248
 
    record.h.linkflag = LF_NORMAL;
249
 
 
250
 
    /* Create the checksum out of the new header */
251
 
    uint32_t chksum;
252
 
    rc = rtTarCalcChkSum(&record, &chksum);
253
 
    if (RT_SUCCESS(rc))
254
 
    {
255
 
        RTStrPrintf(record.h.chksum, sizeof(record.h.chksum), "%0.7o", chksum);
256
 
 
257
 
        /* Write the header first */
258
 
        rc = RTFileWrite(hFile, &record, sizeof(record), NULL);
259
 
        if (RT_SUCCESS(rc))
260
 
        {
261
 
/** @todo r=bird: using a 64KB buffer here instead of 0.5KB would probably be
262
 
 *        a good thing. */
263
 
            uint64_t cbAllWritten = 0;
264
 
            /* Copy the content from pszSrcName over to hFile. This is done block
265
 
             * wise in 512 byte steps. After this copying is finished hFile will be
266
 
             * on a 512 byte boundary, regardless if the file copied is 512 byte
267
 
             * size aligned. */
268
 
            for (;;)
269
 
            {
270
 
                if (cbAllWritten >= cbSize)
271
 
                    break;
272
 
                size_t cbToRead = sizeof(record);
273
 
                /* Last record? */
274
 
                if (cbAllWritten + cbToRead > cbSize)
275
 
                {
276
 
                    /* Initialize with zeros */
277
 
                    RT_ZERO(record);
278
 
                    cbToRead = cbSize - cbAllWritten;
279
 
                }
280
 
                /* Read one block */
281
 
                rc = RTFileRead(hOldFile, &record, cbToRead, NULL);
282
 
                if (RT_FAILURE(rc))
283
 
                    break;
284
 
                /* Write one block */
285
 
                rc = RTFileWrite(hFile, &record, sizeof(record), NULL);
286
 
                if (RT_FAILURE(rc))
287
 
                    break;
288
 
                /* Count how many bytes are written already */
289
 
                cbAllWritten += sizeof(record);
290
 
            }
291
 
 
292
 
            /* Make sure the called doesn't mix truncated tar files with the
293
 
             * official end indicated by rtTarCalcChkSum. */
294
 
            if (rc == VERR_EOF)
295
 
                rc = VERR_FILE_IO_ERROR;
296
 
        }
297
 
    }
298
 
 
299
 
    RTFileClose(hOldFile);
300
 
    return rc;
301
 
}
302
 
 
303
 
static int rtTarSkipData(RTFILE hFile, PRTTARRECORD pRecord)
304
 
{
305
 
    int rc = VINF_SUCCESS;
306
 
    /* Seek over the data parts (512 bytes aligned) */
307
 
    int64_t offSeek = RT_ALIGN(RTStrToInt64(pRecord->h.size), sizeof(RTTARRECORD));
308
 
    if (offSeek > 0)
309
 
        rc = RTFileSeek(hFile, offSeek, RTFILE_SEEK_CURRENT, NULL);
310
 
    return rc;
311
 
}
312
 
 
313
 
 
314
 
RTR3DECL(int) RTTarQueryFileExists(const char *pszTarFile, const char *pszFile)
315
 
{
316
 
    /* Validate input */
317
 
    AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
318
 
    AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
319
 
 
320
 
    /* Open the tar file */
321
 
    RTFILE hFile;
322
 
    int rc = RTFileOpen(&hFile, pszTarFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
323
 
    if (RT_FAILURE(rc))
324
 
        return rc;
325
 
 
326
 
    bool fFound = false;
327
 
    RTTARRECORD record;
328
 
    for (;;)
329
 
    {
330
 
/** @todo r=bird: the reading, validation and EOF check done here should be
331
 
 *        moved to a separate helper function. That would make it easiser to
332
 
 *        distinguish genuine-end-of-tar-file and VERR_EOF caused by a
333
 
 *        trunacted file. That said, rtTarSkipData won't return VERR_EOF, at
334
 
 *        least not on unix, since it's not a sin to seek beyond the end of a
335
 
 *        file. */
336
 
        rc = RTFileRead(hFile, &record, sizeof(record), NULL);
337
 
        /* Check for error or EOF. */
338
 
        if (RT_FAILURE(rc))
339
 
            break;
340
 
        /* Check for EOF & data integrity */
341
 
        rc = rtTarCheckHeader(&record);
342
 
        if (RT_FAILURE(rc))
343
 
            break;
344
 
        /* We support normal files only */
345
 
        if (   record.h.linkflag == LF_OLDNORMAL
346
 
            || record.h.linkflag == LF_NORMAL)
347
 
        {
348
 
            if (!RTStrCmp(record.h.name, pszFile))
349
 
            {
350
 
                fFound = true;
351
 
                break;
352
 
            }
353
 
        }
354
 
        rc = rtTarSkipData(hFile, &record);
355
 
        if (RT_FAILURE(rc))
356
 
            break;
357
 
    }
358
 
 
359
 
    RTFileClose(hFile);
360
 
 
361
 
    if (rc == VERR_EOF)
362
 
        rc = VINF_SUCCESS;
363
 
 
364
 
    /* Something found? */
365
 
    if (    RT_SUCCESS(rc)
366
 
        &&  !fFound)
367
 
        rc = VERR_FILE_NOT_FOUND;
368
 
 
369
 
    return rc;
370
 
}
371
 
 
372
 
RTR3DECL(int) RTTarList(const char *pszTarFile, char ***ppapszFiles, size_t *pcFiles)
373
 
{
374
 
    /* Validate input */
375
 
    AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
376
 
    AssertPtrReturn(ppapszFiles, VERR_INVALID_POINTER);
377
 
    AssertPtrReturn(pcFiles, 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
 
    /* Initialize the file name array with one slot */
386
 
    size_t cFilesAlloc = 1;
387
 
    char **papszFiles = (char**)RTMemAlloc(sizeof(char *));
388
 
    if (!papszFiles)
389
 
    {
390
 
        RTFileClose(hFile);
391
 
        return VERR_NO_MEMORY;
392
 
    }
393
 
 
394
 
    /* Iterate through the tar file record by record. Skip data records as we
395
 
     * didn't need them. */
396
 
    RTTARRECORD record;
397
 
    size_t cFiles = 0;
398
 
    for (;;)
399
 
    {
400
 
        rc = RTFileRead(hFile, &record, sizeof(record), NULL);
401
 
        /* Check for error or EOF. */
402
 
        if (RT_FAILURE(rc))
403
 
            break;
404
 
        /* Check for EOF & data integrity */
405
 
        rc = rtTarCheckHeader(&record);
406
 
        if (RT_FAILURE(rc))
407
 
            break;
408
 
        /* We support normal files only */
409
 
        if (   record.h.linkflag == LF_OLDNORMAL
410
 
            || record.h.linkflag == LF_NORMAL)
411
 
        {
412
 
            if (cFiles >= cFilesAlloc)
413
 
            {
414
 
                /* Double the array size, make sure the size doesn't wrap. */
415
 
                void  *pvNew = NULL;
416
 
                size_t cbNew = cFilesAlloc * sizeof(char *) * 2;
417
 
                if (cbNew / sizeof(char *) / 2 == cFilesAlloc)
418
 
                    pvNew = RTMemRealloc(papszFiles, cbNew);
419
 
                if (!pvNew)
420
 
                {
421
 
                    rc = VERR_NO_MEMORY;
422
 
                    break;
423
 
                }
424
 
                papszFiles = (char **)pvNew;
425
 
                cFilesAlloc *= 2;
426
 
            }
427
 
 
428
 
            /* Duplicate the name */
429
 
            papszFiles[cFiles] = RTStrDup(record.h.name);
430
 
            if (!papszFiles[cFiles])
431
 
            {
432
 
                rc = VERR_NO_MEMORY;
433
 
                break;
434
 
            }
435
 
            cFiles++;
436
 
        }
437
 
        rc = rtTarSkipData(hFile, &record);
438
 
        if (RT_FAILURE(rc))
439
 
            break;
440
 
    }
441
 
 
442
 
    RTFileClose(hFile);
443
 
 
444
 
    if (rc == VERR_EOF)
445
 
        rc = VINF_SUCCESS;
446
 
 
447
 
    /* Return the file array on success, dispose of it on failure. */
448
 
    if (RT_SUCCESS(rc))
449
 
    {
450
 
        *pcFiles = cFiles;
451
 
        *ppapszFiles = papszFiles;
452
 
    }
453
 
    else
454
 
    {
455
 
        while (cFiles-- > 0)
456
 
            RTStrFree(papszFiles[cFiles]);
457
 
        RTMemFree(papszFiles);
458
 
    }
459
 
    return rc;
460
 
}
461
 
 
462
 
RTR3DECL(int) RTTarExtractFiles(const char *pszTarFile, const char *pszOutputDir, const char * const *papszFiles, size_t cFiles)
463
 
{
464
 
    /* Validate input */
465
 
    AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
466
 
    AssertPtrReturn(pszOutputDir, VERR_INVALID_POINTER);
467
 
    AssertPtrReturn(papszFiles, VERR_INVALID_POINTER);
468
 
 
469
 
    /* Open the tar file */
470
 
    RTFILE hFile;
471
 
    int rc = RTFileOpen(&hFile, pszTarFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
472
 
    if (RT_FAILURE(rc))
473
 
        return rc;
474
 
 
475
 
    /* Iterate through the tar file record by record. */
476
 
    RTTARRECORD record;
477
 
    char **paExtracted = (char **)RTMemTmpAllocZ(sizeof(char *) * cFiles);
478
 
    if (paExtracted)
479
 
    {
480
 
        size_t cExtracted = 0;
481
 
        for (;;)
482
 
        {
483
 
            rc = RTFileRead(hFile, &record, sizeof(record), NULL);
484
 
            /* Check for error or EOF. */
485
 
            if (RT_FAILURE(rc))
486
 
                break;
487
 
            /* Check for EOF & data integrity */
488
 
            rc = rtTarCheckHeader(&record);
489
 
            if (RT_FAILURE(rc))
490
 
                break;
491
 
            /* We support normal files only */
492
 
            if (   record.h.linkflag == LF_OLDNORMAL
493
 
                || record.h.linkflag == LF_NORMAL)
494
 
            {
495
 
                bool fFound = false;
496
 
                for (size_t i = 0; i < cFiles; ++i)
497
 
                {
498
 
                    if (!RTStrCmp(record.h.name, papszFiles[i]))
499
 
                    {
500
 
                        fFound = true;
501
 
                        if (cExtracted < cFiles)
502
 
                        {
503
 
                            char *pszTargetFile;
504
 
                            rc = RTStrAPrintf(&pszTargetFile, "%s/%s", pszOutputDir, papszFiles[i]);
505
 
                            if (rc > 0)
506
 
                            {
507
 
                                rc = rtTarCopyFileFrom(hFile, pszTargetFile, &record);
508
 
                                if (RT_SUCCESS(rc))
509
 
                                    paExtracted[cExtracted++] = pszTargetFile;
510
 
                                else
511
 
                                    RTStrFree(pszTargetFile);
512
 
                            }
513
 
                            else
514
 
                                rc = VERR_NO_MEMORY;
515
 
                        }
516
 
                        else
517
 
                            rc = VERR_ALREADY_EXISTS;
518
 
                        break;
519
 
                    }
520
 
                }
521
 
                if (RT_FAILURE(rc))
522
 
                    break;
523
 
                /* If the current record isn't a file in the file list we have to
524
 
                 * skip the data */
525
 
                if (!fFound)
526
 
                {
527
 
                    rc = rtTarSkipData(hFile, &record);
528
 
                    if (RT_FAILURE(rc))
529
 
                        break;
530
 
                }
531
 
            }
532
 
        }
533
 
 
534
 
        if (rc == VERR_EOF)
535
 
            rc = VINF_SUCCESS;
536
 
 
537
 
        /* If we didn't found all files, indicate an error */
538
 
        if (cExtracted != cFiles && RT_SUCCESS(rc))
539
 
            rc = VERR_FILE_NOT_FOUND;
540
 
 
541
 
        /* Cleanup the names of the extracted files, deleting them on failure. */
542
 
        while (cExtracted-- > 0)
543
 
        {
544
 
            if (RT_FAILURE(rc))
545
 
                RTFileDelete(paExtracted[cExtracted]);
546
 
            RTStrFree(paExtracted[cExtracted]);
547
 
        }
548
 
        RTMemTmpFree(paExtracted);
549
 
    }
550
 
    else
551
 
        rc = VERR_NO_TMP_MEMORY;
552
 
 
553
 
    RTFileClose(hFile);
554
 
    return rc;
555
 
}
556
 
 
557
 
RTR3DECL(int) RTTarExtractByIndex(const char *pszTarFile, const char *pszOutputDir, size_t iIndex, char **ppszFileName)
558
 
{
559
 
    /* Validate input */
560
 
    AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
561
 
    AssertPtrReturn(pszOutputDir, VERR_INVALID_POINTER);
562
 
 
563
 
    /* Open the tar file */
564
 
    RTFILE hFile;
565
 
    int rc = RTFileOpen(&hFile, pszTarFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
566
 
    if (RT_FAILURE(rc))
567
 
        return rc;
568
 
 
569
 
    /* Iterate through the tar file record by record. */
570
 
    RTTARRECORD record;
571
 
    size_t iFile = 0;
572
 
    bool fFound = false;
573
 
    for (;;)
574
 
    {
575
 
        rc = RTFileRead(hFile, &record, sizeof(record), NULL);
576
 
        /* Check for error or EOF. */
577
 
        if (RT_FAILURE(rc))
578
 
            break;
579
 
        /* Check for EOF & data integrity */
580
 
        rc = rtTarCheckHeader(&record);
581
 
        if (RT_FAILURE(rc))
582
 
            break;
583
 
        /* We support normal files only */
584
 
        if (   record.h.linkflag == LF_OLDNORMAL
585
 
            || record.h.linkflag == LF_NORMAL)
586
 
        {
587
 
            if (iIndex == iFile)
588
 
            {
589
 
                fFound = true;
590
 
                char *pszTargetName;
591
 
                rc = RTStrAPrintf(&pszTargetName, "%s/%s", pszOutputDir, record.h.name);
592
 
                if (rc > 0)
593
 
                {
594
 
                    rc = rtTarCopyFileFrom(hFile, pszTargetName, &record);
595
 
                    /* On success pass on the filename if requested. */
596
 
                    if (    RT_SUCCESS(rc)
597
 
                        &&  ppszFileName)
598
 
                        *ppszFileName = pszTargetName;
599
 
                    else
600
 
                        RTStrFree(pszTargetName);
601
 
                }
602
 
                else
603
 
                    rc = VERR_NO_MEMORY;
604
 
                break;
605
 
            }
606
 
        }
607
 
        rc = rtTarSkipData(hFile, &record);
608
 
        if (RT_FAILURE(rc))
609
 
            break;
610
 
        ++iFile;
611
 
    }
612
 
 
613
 
    RTFileClose(hFile);
614
 
 
615
 
    if (rc == VERR_EOF)
616
 
        rc = VINF_SUCCESS;
617
 
 
618
 
    /* If we didn't found the index, indicate an error */
619
 
    if (!fFound && RT_SUCCESS(rc))
620
 
        rc = VERR_FILE_NOT_FOUND;
621
 
 
622
 
    return rc;
623
 
}
624
 
 
625
 
RTR3DECL(int) RTTarCreate(const char *pszTarFile, const char * const *papszFiles, size_t cFiles)
626
 
{
627
 
    /* Validate input */
628
 
    AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
629
 
    AssertPtrReturn(papszFiles, VERR_INVALID_POINTER);
630
 
 
631
 
    /* Open the tar file */
632
 
    RTFILE hFile;
633
 
    int rc = RTFileOpen(&hFile, pszTarFile, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
634
 
    if (RT_FAILURE(rc))
635
 
        return rc;
636
 
 
637
 
    for (size_t i = 0; i < cFiles; ++i)
638
 
    {
639
 
        rc = rtTarCopyFileTo(hFile, papszFiles[i]);
640
 
        if (RT_FAILURE(rc))
641
 
            break;
642
 
    }
643
 
 
644
 
    /* gtar gives a warning, but the documentation says EOF is indicated by a
645
 
     * zero block. Disabled for now. */
646
 
#if 0
647
 
    if (RT_SUCCESS(rc))
648
 
    {
649
 
        /* Append the EOF record which is filled all by zeros */
650
 
        RTTARRECORD record;
651
 
        ASMMemFill32(&record, sizeof(record), 0);
652
 
        rc = RTFileWrite(hFile, &record, sizeof(record), NULL);
653
 
    }
654
 
#endif
655
 
 
656
 
    /* Time to close the new tar archive */
657
 
    RTFileClose(hFile);
658
 
 
659
 
    /* Delete the freshly created tar archive on failure */
660
 
    if (RT_FAILURE(rc))
661
 
        RTFileDelete(pszTarFile);
662
 
 
663
 
    return rc;
664
 
}
665