Subversion Repositories

?revision_form?Rev ?revision_input??revision_submit??revision_endform?

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4 magnus 1
/*
2
**  Copyright 1998-2003 University of Illinois Board of Trustees
3
**  Copyright 1998-2003 Mark D. Roth
4
**  All rights reserved.
5
**
6
**  extract.c - libtar code to extract a file from a tar archive
7
**
8
**  Mark D. Roth <roth@uiuc.edu>
9
**  Campus Information Technologies and Educational Services
10
**  University of Illinois at Urbana-Champaign
11
*/
12
 
13
#include <internal.h>
14
 
15
#include <stdio.h>
16
#include <sys/param.h>
17
#include <sys/types.h>
18
#include <fcntl.h>
19
#include <errno.h>
20
#include <utime.h>
21
 
22
#ifdef STDC_HEADERS
23
# include <stdlib.h>
24
# include <string.h>
25
#endif
26
 
27
#ifdef HAVE_UNISTD_H
28
# include <unistd.h>
29
#endif
30
 
31
 
32
struct linkname
33
{
34
        char ln_save[MAXPATHLEN];
35
        char ln_real[MAXPATHLEN];
36
};
37
typedef struct linkname linkname_t;
38
 
39
 
40
static int
41
tar_set_file_perms(TAR *t, char *realname)
42
{
43
        mode_t mode;
44
        uid_t uid;
45
        gid_t gid;
46
        struct utimbuf ut;
47
        char *filename;
48
 
49
        filename = (realname ? realname : th_get_pathname(t));
50
        mode = th_get_mode(t);
51
        uid = th_get_uid(t);
52
        gid = th_get_gid(t);
53
        ut.modtime = ut.actime = th_get_mtime(t);
54
 
55
        /* change owner/group */
56
        if (geteuid() == 0)
57
#ifdef HAVE_LCHOWN
58
                if (lchown(filename, uid, gid) == -1)
59
                {
60
# ifdef DEBUG
61
                        fprintf(stderr, "lchown(\"%s\", %d, %d): %s\n",
62
                                filename, uid, gid, strerror(errno));
63
# endif
64
#else /* ! HAVE_LCHOWN */
65
                if (!TH_ISSYM(t) && chown(filename, uid, gid) == -1)
66
                {
67
# ifdef DEBUG
68
                        fprintf(stderr, "chown(\"%s\", %d, %d): %s\n",
69
                                filename, uid, gid, strerror(errno));
70
# endif
71
#endif /* HAVE_LCHOWN */
72
                        return -1;
73
                }
74
 
75
        /* change access/modification time */
76
        if (!TH_ISSYM(t) && utime(filename, &ut) == -1)
77
        {
78
#ifdef DEBUG
79
                perror("utime()");
80
#endif
81
                return -1;
82
        }
83
 
84
        /* change permissions */
85
        if (!TH_ISSYM(t) && chmod(filename, mode) == -1)
86
        {
87
#ifdef DEBUG
88
                perror("chmod()");
89
#endif
90
                return -1;
91
        }
92
 
93
        return 0;
94
}
95
 
96
 
97
/* switchboard */
98
int
99
tar_extract_file(TAR *t, char *realname)
100
{
101
        int i;
102
        linkname_t *lnp;
103
        char *pathname;
104
 
105
        if (t->options & TAR_NOOVERWRITE)
106
        {
107
                struct stat s;
108
 
109
                if (lstat(realname, &s) == 0 || errno != ENOENT)
110
                {
111
                        errno = EEXIST;
112
                        return -1;
113
                }
114
        }
115
 
116
        if (TH_ISDIR(t))
117
        {
118
                i = tar_extract_dir(t, realname);
119
                if (i == 1)
120
                        i = 0;
121
        }
122
        else if (TH_ISLNK(t))
123
                i = tar_extract_hardlink(t, realname);
124
        else if (TH_ISSYM(t))
125
                i = tar_extract_symlink(t, realname);
126
        else if (TH_ISCHR(t))
127
                i = tar_extract_chardev(t, realname);
128
        else if (TH_ISBLK(t))
129
                i = tar_extract_blockdev(t, realname);
130
        else if (TH_ISFIFO(t))
131
                i = tar_extract_fifo(t, realname);
132
        else /* if (TH_ISREG(t)) */
133
                i = tar_extract_regfile(t, realname);
134
 
135
        if (i != 0)
136
                return i;
137
 
138
        i = tar_set_file_perms(t, realname);
139
        if (i != 0)
140
                return i;
141
 
142
        lnp = (linkname_t *)calloc(1, sizeof(linkname_t));
143
        if (lnp == NULL)
144
                return -1;
145
        pathname = th_get_pathname(t);
146
        strlcpy(lnp->ln_save, pathname, sizeof(lnp->ln_save));
147
        strlcpy(lnp->ln_real, realname, sizeof(lnp->ln_real));
148
#ifdef DEBUG
149
        printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
150
               "value=\"%s\"\n", pathname, realname);
151
#endif
152
        free(pathname);
153
        if (libtar_hash_add(t->h, lnp) != 0)
154
                return -1;
155
 
156
        return 0;
157
}
158
 
159
 
160
/* extract regular file */
161
int
162
tar_extract_regfile(TAR *t, char *realname)
163
{
164
        mode_t mode;
165
        size_t size;
166
        uid_t uid;
167
        gid_t gid;
168
        int fdout;
169
        int i, k;
170
        char buf[T_BLOCKSIZE];
171
        char *filename;
172
 
173
#ifdef DEBUG
174
        printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
175
               realname);
176
#endif
177
 
178
        if (!TH_ISREG(t))
179
        {
180
                errno = EINVAL;
181
                return -1;
182
        }
183
 
184
        filename = (realname ? realname : th_get_pathname(t));
185
        mode = th_get_mode(t);
186
        size = th_get_size(t);
187
        uid = th_get_uid(t);
188
        gid = th_get_gid(t);
189
 
190
        if (mkdirhier(dirname(filename)) == -1)
191
                return -1;
192
 
193
#ifdef DEBUG
194
        printf("  ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
195
               filename, mode, uid, gid, size);
196
#endif
197
        fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
198
#ifdef O_BINARY
199
                     | O_BINARY
200
#endif
201
                    , 0666);
202
        if (fdout == -1)
203
        {
204
#ifdef DEBUG
205
                perror("open()");
206
#endif
207
                return -1;
208
        }
209
 
210
#if 0
211
        /* change the owner.  (will only work if run as root) */
212
        if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
213
        {
214
#ifdef DEBUG
215
                perror("fchown()");
216
#endif
217
                return -1;
218
        }
219
 
220
        /* make sure the mode isn't inheritted from a file we're overwriting */
221
        if (fchmod(fdout, mode & 07777) == -1)
222
        {
223
#ifdef DEBUG
224
                perror("fchmod()");
225
#endif
226
                return -1;
227
        }
228
#endif
229
 
230
        /* extract the file */
231
        for (i = size; i > 0; i -= T_BLOCKSIZE)
232
        {
233
                k = tar_block_read(t, buf);
234
                if (k != T_BLOCKSIZE)
235
                {
236
                        if (k != -1)
237
                                errno = EINVAL;
238
                        return -1;
239
                }
240
 
241
                /* write block to output file */
242
                if (write(fdout, buf,
243
                          ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
244
                        return -1;
245
        }
246
 
247
        /* close output file */
248
        if (close(fdout) == -1)
249
                return -1;
250
 
251
#ifdef DEBUG
252
        printf("### done extracting %s\n", filename);
253
#endif
254
 
255
        return 0;
256
}
257
 
258
 
259
/* skip regfile */
260
int
261
tar_skip_regfile(TAR *t)
262
{
263
        int i, k;
264
        size_t size;
265
        char buf[T_BLOCKSIZE];
266
 
267
        if (!TH_ISREG(t))
268
        {
269
                errno = EINVAL;
270
                return -1;
271
        }
272
 
273
        size = th_get_size(t);
274
        for (i = size; i > 0; i -= T_BLOCKSIZE)
275
        {
276
                k = tar_block_read(t, buf);
277
                if (k != T_BLOCKSIZE)
278
                {
279
                        if (k != -1)
280
                                errno = EINVAL;
281
                        return -1;
282
                }
283
        }
284
 
285
        return 0;
286
}
287
 
288
 
289
/* hardlink */
290
int
291
tar_extract_hardlink(TAR * t, char *realname)
292
{
293
        char *filename;
294
        char *linktgt = NULL;
295
        linkname_t *lnp;
296
        libtar_hashptr_t hp;
297
 
298
        if (!TH_ISLNK(t))
299
        {
300
                errno = EINVAL;
301
                return -1;
302
        }
303
 
304
        filename = (realname ? realname : th_get_pathname(t));
305
        if (mkdirhier(dirname(filename)) == -1)
306
                return -1;
307
        libtar_hashptr_reset(&hp);
308
        if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
309
                               (libtar_matchfunc_t)libtar_str_match) != 0)
310
        {
311
                lnp = (linkname_t *)libtar_hashptr_data(&hp);
312
                linktgt = lnp->ln_real;
313
        }
314
        else
315
                linktgt = th_get_linkname(t);
316
 
317
#ifdef DEBUG
318
        printf("  ==> extracting: %s (link to %s)\n", filename, linktgt);
319
#endif
320
        if (link(linktgt, filename) == -1)
321
        {
322
#ifdef DEBUG
323
                perror("link()");
324
#endif
325
                return -1;
326
        }
327
 
328
        return 0;
329
}
330
 
331
 
332
/* symlink */
333
int
334
tar_extract_symlink(TAR *t, char *realname)
335
{
336
        char *filename;
337
 
338
        if (!TH_ISSYM(t))
339
        {
340
                errno = EINVAL;
341
                return -1;
342
        }
343
 
344
        filename = (realname ? realname : th_get_pathname(t));
345
        if (mkdirhier(dirname(filename)) == -1)
346
                return -1;
347
 
348
        if (unlink(filename) == -1 && errno != ENOENT)
349
                return -1;
350
 
351
#ifdef DEBUG
352
        printf("  ==> extracting: %s (symlink to %s)\n",
353
               filename, th_get_linkname(t));
354
#endif
355
        if (symlink(th_get_linkname(t), filename) == -1)
356
        {
357
#ifdef DEBUG
358
                perror("symlink()");
359
#endif
360
                return -1;
361
        }
362
 
363
        return 0;
364
}
365
 
366
 
367
/* character device */
368
int
369
tar_extract_chardev(TAR *t, char *realname)
370
{
371
        mode_t mode;
372
        unsigned long devmaj, devmin;
373
        char *filename;
374
 
375
        if (!TH_ISCHR(t))
376
        {
377
                errno = EINVAL;
378
                return -1;
379
        }
380
 
381
        filename = (realname ? realname : th_get_pathname(t));
382
        mode = th_get_mode(t);
383
        devmaj = th_get_devmajor(t);
384
        devmin = th_get_devminor(t);
385
 
386
        if (mkdirhier(dirname(filename)) == -1)
387
                return -1;
388
 
389
#ifdef DEBUG
390
        printf("  ==> extracting: %s (character device %ld,%ld)\n",
391
               filename, devmaj, devmin);
392
#endif
393
        if (mknod(filename, mode | S_IFCHR,
394
                  compat_makedev(devmaj, devmin)) == -1)
395
        {
396
#ifdef DEBUG
397
                perror("mknod()");
398
#endif
399
                return -1;
400
        }
401
 
402
        return 0;
403
}
404
 
405
 
406
/* block device */
407
int
408
tar_extract_blockdev(TAR *t, char *realname)
409
{
410
        mode_t mode;
411
        unsigned long devmaj, devmin;
412
        char *filename;
413
 
414
        if (!TH_ISBLK(t))
415
        {
416
                errno = EINVAL;
417
                return -1;
418
        }
419
 
420
        filename = (realname ? realname : th_get_pathname(t));
421
        mode = th_get_mode(t);
422
        devmaj = th_get_devmajor(t);
423
        devmin = th_get_devminor(t);
424
 
425
        if (mkdirhier(dirname(filename)) == -1)
426
                return -1;
427
 
428
#ifdef DEBUG
429
        printf("  ==> extracting: %s (block device %ld,%ld)\n",
430
               filename, devmaj, devmin);
431
#endif
432
        if (mknod(filename, mode | S_IFBLK,
433
                  compat_makedev(devmaj, devmin)) == -1)
434
        {
435
#ifdef DEBUG
436
                perror("mknod()");
437
#endif
438
                return -1;
439
        }
440
 
441
        return 0;
442
}
443
 
444
 
445
/* directory */
446
int
447
tar_extract_dir(TAR *t, char *realname)
448
{
449
        mode_t mode;
450
        char *filename;
451
 
452
        if (!TH_ISDIR(t))
453
        {
454
                errno = EINVAL;
455
                return -1;
456
        }
457
 
458
        filename = (realname ? realname : th_get_pathname(t));
459
        mode = th_get_mode(t);
460
 
461
        if (mkdirhier(dirname(filename)) == -1)
462
                return -1;
463
 
464
#ifdef DEBUG
465
        printf("  ==> extracting: %s (mode %04o, directory)\n", filename,
466
               mode);
467
#endif
468
        if (mkdir(filename, mode) == -1)
469
        {
470
                if (errno == EEXIST)
471
                {
472
                        if (chmod(filename, mode) == -1)
473
                        {
474
#ifdef DEBUG
475
                                perror("chmod()");
476
#endif
477
                                return -1;
478
                        }
479
                        else
480
                        {
481
#ifdef DEBUG
482
                                puts("  *** using existing directory");
483
#endif
484
                                return 1;
485
                        }
486
                }
487
                else
488
                {
489
#ifdef DEBUG
490
                        perror("mkdir()");
491
#endif
492
                        return -1;
493
                }
494
        }
495
 
496
        return 0;
497
}
498
 
499
 
500
/* FIFO */
501
int
502
tar_extract_fifo(TAR *t, char *realname)
503
{
504
        mode_t mode;
505
        char *filename;
506
 
507
        if (!TH_ISFIFO(t))
508
        {
509
                errno = EINVAL;
510
                return -1;
511
        }
512
 
513
        filename = (realname ? realname : th_get_pathname(t));
514
        mode = th_get_mode(t);
515
 
516
        if (mkdirhier(dirname(filename)) == -1)
517
                return -1;
518
 
519
#ifdef DEBUG
520
        printf("  ==> extracting: %s (fifo)\n", filename);
521
#endif
522
        if (mkfifo(filename, mode) == -1)
523
        {
524
#ifdef DEBUG
525
                perror("mkfifo()");
526
#endif
527
                return -1;
528
        }
529
 
530
        return 0;
531
}
532
 
533