Implemented force install
[spkg.git] / src / untgz.c
blob40da4537a52ea70abeab1baa3b1f685935b2dfe6
1 /*----------------------------------------------------------------------*\
2 |* spkg - The Unofficial Slackware Linux Package Manager *|
3 |* designed by Ondøej Jirman, 2005 *|
4 |*----------------------------------------------------------------------*|
5 |* No copy/usage restrictions are imposed on anybody. *|
6 \*----------------------------------------------------------------------*/
7 #include <stdio.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <utime.h>
11 #include <fcntl.h>
12 #include <setjmp.h>
13 #include <zlib.h>
15 #include "untgz.h"
16 #include "sys.h"
18 #ifdef __WIN32__
19 #include <windows.h>
20 extern int posix_utime (const char *path, struct utimbuf *buf);
21 extern int link(const char *existing, const char *newfile);
22 #endif
24 /* Enable or disable parent directory modification and access times
25 preservation. */
26 #define UNTGZ_PRESERVE_DIR_TIMES 0
28 #if UNTGZ_PRESERVE_DIR_TIMES == 1
29 #include <libgen.h>
30 #endif
32 #include "bench.h"
34 /* optimal (according to the benchmark), must be multiple of 512 */
35 #define BLOCKBUFSIZE (512*100)
36 #define WRITEBUFSIZE (1024*16)
38 enum {
39 COMPTYPE_GZIP,
40 COMPTYPE_LZMA,
41 COMPTYPE_XZ,
42 COMPTYPE_NONE
45 struct untgz_state_internal {
46 /* error handling */
47 jmp_buf errjmp;
48 struct error* err;
49 mode_t old_umask; /* saved umask */
51 gboolean errblock; /* if set all operation is bloked and error is returned */
52 gboolean data;/* data from the current file were not read, yet */
53 gboolean written;/* current file was written to disk (or buffer) */
54 gboolean eof;/* end of archive reached */
56 gint comptype;
57 gzFile* gzf; /* gzio tar stream */
58 FILE* fp; /* file stream */
60 /* internal block buffer data */
61 gchar bbuf[BLOCKBUFSIZE]; /* block buffer */
62 gchar* bend; /* points to the end of the buffer */
63 gchar* bpos; /* points to the current block */
64 gint blockid; /* block id (512*blockid == position of the block in the input file) */
66 gchar wbuf[WRITEBUFSIZE]; /* write buffer */
69 /* private
70 ************************************************************************/
72 #define AREGTYPE '\0' /* regular file */
73 #define REGTYPE '0' /* regular file */
74 #define LNKTYPE '1' /* link */
75 #define SYMTYPE '2' /* reserved */
76 #define CHRTYPE '3' /* character special */
77 #define BLKTYPE '4' /* block special */
78 #define DIRTYPE '5' /* directory */
79 #define FIFOTYPE '6' /* FIFO special */
80 #define CONTTYPE '7' /* reserved */
82 /* implemented GNU tar extensions */
83 #define GNUTYPE_LONGLINK 'K' /* long link name */
84 #define GNUTYPE_LONGNAME 'L' /* long file name */
86 /* unimplemented GNU tar extensions */
87 #define GNUTYPE_SPARSE 'S' /* sparse file */
88 #define GNUTYPE_DUMPDIR 'D' /* file names from dumped directory */
89 #define GNUTYPE_MULTIVOL 'M' /* continuation of file from another volume */
90 #define GNUTYPE_NAMES 'N' /* file name that does not fit into main hdr */
91 #define GNUTYPE_VOLHDR 'V' /* tape/volume header */
93 #define BLOCKSIZE 512
94 #define SHORTNAMESIZE 100
96 struct tar_header { /* byte offset */
97 char name[100]; /* 0 */
98 char mode[8]; /* 100 */
99 char uid[8]; /* 108 */
100 char gid[8]; /* 116 */
101 char size[12]; /* 124 */
102 char mtime[12]; /* 136 */
103 char chksum[8]; /* 148 */
104 char typeflag; /* 156 */
105 char linkname[100]; /* 157 */
106 char magic[6]; /* 257 */
107 char version[2]; /* 263 */
108 char uname[32]; /* 265 */
109 char gname[32]; /* 297 */
110 char devmajor[8]; /* 329 */
111 char devminor[8]; /* 337 */
112 char prefix[155]; /* 345 */
115 union tar_block {
116 struct tar_header h;
117 guchar b[BLOCKSIZE];
120 #define e_set(n, fmt, args...) e_add(s->i->err, "untgz", __func__, n, fmt, ##args)
121 #define e_jump() longjmp(s->i->errjmp, 1)
123 #define _e_set(e, n, fmt, args...) e_add(e, "untgz", __func__, n, fmt, ##args)
125 #define e_throw(n, fmt, args...) \
126 G_STMT_START { \
127 e_set(n, fmt, ##args); \
128 e_jump(); \
129 } G_STMT_END
131 /* optimized conversion from octal ascii digits to uint */
132 static guint getoct(struct untgz_state* s, gchar *p, guint w)
134 guint r=0, i;
135 static const guchar oct_tab[256] = {
136 [0 ... 255] = 0x10,
137 ['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3,
138 ['4'] = 4, ['5'] = 5, ['6'] = 6, ['7'] = 7,
139 ['\0'] = 0x20, [' '] = 0x20
142 for (i=0; i<w; i++)
144 guchar c = oct_tab[(guchar)p[i]];
145 if (G_UNLIKELY(c == 0x20))
146 break;
147 else if (G_UNLIKELY(c == 0x10))
148 e_throw(E_ERROR|UNTGZ_CORRUPT, "[block:%d] corrupted tgz archive (bad oct - possibly trying to checksum data block)", s->i->blockid);
149 else
150 r = r*8+c;
152 return r;
155 /* header checksum validation */
156 static void validate_header_csum(struct untgz_state* s)
158 struct untgz_state_internal* i = s->i;
159 guint n, head_csum, real_csum=0;
160 union tar_block* b = (union tar_block*)i->bpos;
162 head_csum = getoct(s, b->h.chksum, 6);
163 memcpy(b->h.chksum, " ", 8);
164 for (n=0; G_LIKELY(n<BLOCKSIZE); n+=2)
165 real_csum += (guchar)b->b[n] + (guchar)b->b[n+1];
166 if (G_UNLIKELY(real_csum != head_csum))
167 e_throw(E_ERROR|UNTGZ_CORRUPT, "[block:%d] corrupted tgz archive (invalid header checksum)", i->blockid);
170 /* this function returns pointer to the next tar block
171 * return value is ptr if pointer points to the next block
172 * 0 if eof block occured (header with first byte zero)
173 * longjmp on read error (or incomplete block)
175 static union tar_block* read_next_block(struct untgz_state* s, gboolean is_header)
177 struct untgz_state_internal* i = s->i;
178 i->bpos += BLOCKSIZE;
179 if (i->bpos >= i->bend)
180 { /* reload */
181 continue_timer(6);
182 gint read;
184 if (i->comptype == COMPTYPE_GZIP)
185 read = gzread(i->gzf, i->bbuf, BLOCKBUFSIZE);
186 else
187 read = fread(i->bbuf, 1, BLOCKBUFSIZE, i->fp);
189 stop_timer(6);
190 if (read < BLOCKSIZE)
192 gint err;
193 if (i->comptype == COMPTYPE_GZIP)
194 err = (read == 0 && !gzeof(i->gzf));
195 else
196 err = (read == 0 && !feof(i->fp));
197 if (err)
198 e_throw(E_ERROR|UNTGZ_CORRUPT, "[block:%d] corrupted tgz archive (gzread failed)", i->blockid);
199 else
200 e_throw(E_ERROR|UNTGZ_CORRUPT, "[block:%d] corrupted tgz archive (early EOF)", i->blockid);
202 i->bpos = i->bbuf;
203 i->bend = i->bbuf + read;
205 if (is_header)
207 if (G_UNLIKELY(i->bpos[0] == 0))
208 return 0;
209 continue_timer(8);
210 validate_header_csum(s);
211 stop_timer(8);
213 i->blockid++;
214 return (union tar_block*)i->bpos;
217 /* append two strings (reallocate memmory) */
218 static gchar* strnappend(gchar* dst, gchar* src, gsize size)
220 gsize newsize = 1+size;
221 if (G_UNLIKELY(src == 0))
222 return dst;
223 if (G_LIKELY(dst != 0))
225 newsize += strlen(dst);
226 dst = g_realloc(dst, newsize);
228 else
229 dst = g_malloc0(newsize);
230 dst = strncat(dst, src, size);
231 return dst;
234 /* public
235 ************************************************************************/
237 struct untgz_state* untgz_open(const gchar* tgzfile, struct error* e)
239 struct untgz_state* s=0;
240 gzFile *gzf;
241 FILE* fp;
242 struct stat st;
243 gint comptype;
245 continue_timer(0);
246 continue_timer(1);
248 g_assert(tgzfile != 0);
249 g_assert(e != 0);
251 if (stat(tgzfile, &st) == -1)
253 _e_set(e, E_ERROR, "can't stat file: %s", tgzfile);
254 return NULL;
257 if (g_str_has_suffix(tgzfile, ".tgz"))
258 comptype = COMPTYPE_GZIP;
259 else if (g_str_has_suffix(tgzfile, ".tlz"))
260 comptype = COMPTYPE_LZMA;
261 else if (g_str_has_suffix(tgzfile, ".txz"))
262 comptype = COMPTYPE_XZ;
263 else if (g_str_has_suffix(tgzfile, ".tar"))
264 comptype = COMPTYPE_NONE;
265 else
267 _e_set(e, E_ERROR, "unknown package type: %s", tgzfile);
268 return NULL;
271 if (comptype == COMPTYPE_GZIP)
273 gzf = gzopen(tgzfile, "rb");
274 if (gzf == NULL)
276 _e_set(e, E_ERROR, "can't gzopen file: %s", tgzfile);
277 return NULL;
280 else if (comptype == COMPTYPE_LZMA)
282 gchar* escaped = g_shell_quote(tgzfile);
283 gchar* cmd = g_strdup_printf("lzma -d -c %s", escaped);
284 fp = popen(cmd, "r");
285 g_free(escaped);
286 if (fp == NULL)
288 _e_set(e, E_ERROR, "can't popen command: %s", cmd);
289 g_free(cmd);
290 return NULL;
292 g_free(cmd);
294 else if (comptype == COMPTYPE_XZ)
296 gchar* escaped = g_shell_quote(tgzfile);
297 gchar* cmd = g_strdup_printf("xzdec %s", escaped);
298 fp = popen(cmd, "r");
299 g_free(escaped);
300 if (fp == NULL)
302 _e_set(e, E_ERROR, "can't popen command: %s", cmd);
303 g_free(cmd);
304 return NULL;
306 g_free(cmd);
308 else if (comptype == COMPTYPE_NONE)
310 fp = fopen(tgzfile, "r");
311 if (fp == NULL)
313 _e_set(e, E_ERROR, "can't open file: %s", tgzfile);
314 return NULL;
318 s = g_new0(struct untgz_state,1);
319 s->i = g_new0(struct untgz_state_internal,1);
320 struct untgz_state_internal* i = s->i;
321 s->tgzfile = g_strdup(tgzfile);
322 i->gzf = gzf;
323 i->fp = fp;
324 i->comptype = comptype;
325 s->csize = st.st_size;
326 i->old_umask = umask(0);
327 i->blockid = -1;
328 i->err = e;
330 stop_timer(1);
331 return s;
334 void untgz_close(struct untgz_state* s)
336 g_assert(s != 0);
337 continue_timer(2);
339 struct untgz_state_internal* i = s->i;
341 if (i->comptype == COMPTYPE_GZIP)
342 gzclose(i->gzf);
343 else if (i->comptype == COMPTYPE_LZMA || i->comptype == COMPTYPE_XZ)
344 pclose(i->fp);
345 else
346 fclose(i->fp);
348 umask(i->old_umask);
349 g_free(s->f_name);
350 g_free(s->f_link);
351 g_free(s->tgzfile);
352 s->f_name = s->f_link = s->tgzfile = 0;
353 i->gzf = 0;
354 g_free(i);
355 s->i = 0;
356 g_free(s);
358 stop_timer(2);
359 stop_timer(0);
361 print_timer(0, "[untgz] extraction");
362 print_timer(1, "[untgz] untgz_open");
363 print_timer(2, "[untgz] untgz_close");
364 print_timer(3, "[untgz] untgz_get_header");
365 print_timer(4, "[untgz] untgz_write_file");
366 print_timer(5, "[untgz] untgz_write_data");
368 print_timer(6, "[untgz] gzread");
369 print_timer(7, "[untgz] fwrite");
370 print_timer(8, "[untgz] chksum");
373 gint untgz_get_header(struct untgz_state* s)
375 off_t remaining;
376 union tar_block* b;
377 g_assert(s != 0);
379 continue_timer(3);
380 struct untgz_state_internal* i = s->i;
382 if (G_UNLIKELY(i->errblock))
384 e_set(E_ERROR|UNTGZ_BLOCKED, "[block:%d] untgz is blocked", i->blockid);
385 goto err_0;
387 else if (G_UNLIKELY(i->eof))
388 goto ret_1;
389 if (G_UNLIKELY(setjmp(i->errjmp) != 0))
391 e_set(E_PASS, "error thrown");
392 goto err_0;
395 /* skip data blocks if write_data or write_file was not called after previous get_header */
396 if (s->f_type == UNTGZ_REG && i->data)
398 remaining = s->f_size;
399 while (G_LIKELY(remaining > 0))
401 if (read_next_block(s, 0) == 0)
402 e_throw(E_ERROR|UNTGZ_CORRUPT, "[block:%d] corrupted tgz archive (missing data block)", i->blockid);
403 remaining = remaining<=BLOCKSIZE?0:remaining-BLOCKSIZE;
407 /* reset state */
408 g_free(s->f_name);
409 g_free(s->f_link);
410 s->f_name = s->f_link = 0;
411 s->f_type = UNTGZ_NONE;
412 i->data = 0;
413 i->written = 0;
415 while (1)
417 /* read next header */
418 b = read_next_block(s, 1);
419 if (b == 0)
421 /* empty header => end of archive */
422 i->eof = 1;
423 return 1;
426 remaining = getoct(s, b->h.size, sizeof(b->h.size));
427 /* read longname and/or longlink */
428 if (b->h.typeflag == GNUTYPE_LONGLINK)
430 while (G_LIKELY(remaining > 0))
432 b = read_next_block(s, 0);
433 if (b == 0)
434 e_throw(E_ERROR|UNTGZ_CORRUPT, "[block:%d] corrupted tgz archive (missing longlink data block)", i->blockid);
435 s->f_link = strnappend(s->f_link, (gchar*)b->b, BLOCKSIZE);
436 remaining = remaining<=BLOCKSIZE?0:remaining-BLOCKSIZE;
438 continue;
440 else if (b->h.typeflag == GNUTYPE_LONGNAME)
442 while (G_LIKELY(remaining > 0))
444 b = read_next_block(s, 0);
445 if (b == 0)
446 e_throw(E_ERROR|UNTGZ_CORRUPT, "[block:%d] corrupted tgz archive (missing longname data block)", i->blockid);
447 s->f_name = strnappend(s->f_name, (gchar*)b->b, BLOCKSIZE);
448 remaining = remaining<=BLOCKSIZE?0:remaining-BLOCKSIZE;
450 continue;
452 break;
455 /* parse header */
456 s->f_size = remaining;
457 s->f_mode = getoct(s, b->h.mode, sizeof(b->h.mode)) & 07777;
458 s->f_uid = getoct(s, b->h.uid, sizeof(b->h.uid));
459 s->f_gid = getoct(s, b->h.gid, sizeof(b->h.gid));
460 s->f_mtime = (time_t) getoct(s, b->h.mtime, sizeof(b->h.mtime));
461 s->f_devmaj = getoct(s, b->h.devmajor, sizeof(b->h.devmajor));
462 s->f_devmin = getoct(s, b->h.devminor, sizeof(b->h.devminor));
463 strncpy(s->f_uname, b->h.uname, sizeof(b->h.uname));
464 strncpy(s->f_gname, b->h.gname, sizeof(b->h.gname));
466 switch (b->h.typeflag)
468 case AREGTYPE:
469 case REGTYPE: s->f_type = UNTGZ_REG; i->data = 1; s->usize += remaining; break;
470 case DIRTYPE: s->f_type = UNTGZ_DIR; break;
471 case SYMTYPE: s->f_type = UNTGZ_SYM; break;
472 case LNKTYPE: s->f_type = UNTGZ_LNK; break;
473 case CHRTYPE: s->f_type = UNTGZ_CHR; break;
474 case BLKTYPE: s->f_type = UNTGZ_BLK; break;
475 case FIFOTYPE: s->f_type = UNTGZ_FIFO; break;
476 default: e_throw(E_ERROR|UNTGZ_CORRUPT, "[block:%d] \"corrupted\" tgz archive (unimplemented typeflag [%c])", i->blockid, b->h.typeflag);
479 if (s->f_name == 0)
480 s->f_name = g_strndup(b->h.name, SHORTNAMESIZE);
481 /* just one more (unnecessary) check */
482 else if (strncmp(s->f_name, b->h.name, SHORTNAMESIZE-1)) /* -1 because it's zero terminated */
483 e_throw(E_ERROR|UNTGZ_CORRUPT, "[block:%d] corrupted tgz archive (longname mismatch)", i->blockid);
484 if (s->f_link == 0)
485 s->f_link = g_strndup(b->h.linkname, SHORTNAMESIZE);
486 /* just one more (unnecessary) check */
487 else if (strncmp(s->f_link, b->h.linkname, SHORTNAMESIZE-1)) /* -1 because it's zero terminated */
488 e_throw(E_ERROR|UNTGZ_CORRUPT, "[block:%d] corrupted tgz archive (longlink mismatch)", i->blockid);
490 stop_timer(3);
491 return 0;
492 err_0:
493 stop_timer(3);
494 return -1;
495 ret_1:
496 stop_timer(3);
497 return 1;
500 gint untgz_write_data(struct untgz_state* s, gchar** buf, gsize* len)
502 gchar* buffer=0;
503 union tar_block* b;
504 gsize position=0;
505 gsize remaining;
507 g_assert(s != 0);
508 g_assert(buf != 0);
509 g_assert(len != 0);
511 continue_timer(5);
513 struct untgz_state_internal* i = s->i;
515 if (G_UNLIKELY(i->errblock))
517 e_set(E_ERROR|UNTGZ_BLOCKED, "[block:%d] untgz is blocked", i->blockid);
518 goto err_0;
520 else if (i->eof || s->f_type != UNTGZ_REG || i->data == 0) /* no data for current file */
521 goto ret_1;
522 if (setjmp(i->errjmp) != 0)
524 g_free(buffer);
525 e_set(E_PASS, "error thrown");
526 goto err_0;
529 remaining=s->f_size;
530 buffer = g_malloc(remaining+1);
531 while (G_LIKELY(remaining > 0))
533 b = read_next_block(s, 0);
534 if (b == 0)
535 e_throw(E_ERROR|UNTGZ_CORRUPT, "[block:%d] corrupted tgz archive (missing data block)", i->blockid);
536 memcpy(buffer+position, b, remaining>BLOCKSIZE?BLOCKSIZE:remaining);
537 remaining = remaining<=BLOCKSIZE?0:remaining-BLOCKSIZE;
538 position += BLOCKSIZE;
540 i->data = 0;
541 buffer[s->f_size] = 0; /* zero terminate buffer */
542 *buf = buffer;
543 *len = s->f_size;
545 stop_timer(5);
546 return 0;
547 err_0:
548 stop_timer(5);
549 return -1;
550 ret_1:
551 stop_timer(5);
552 return 1;
555 gint untgz_write_file(struct untgz_state* s, gchar* altname)
557 struct utimbuf t;
558 union tar_block* b;
559 gchar* path;
560 gsize remaining;
561 #if UNTGZ_PRESERVE_DIR_TIMES == 1
562 gchar *dpath_tmp = 0, *dpath;
563 struct utimbuf dt;
564 #endif
566 g_assert(s != 0);
567 struct untgz_state_internal* i = s->i;
568 continue_timer(4);
570 if (G_UNLIKELY(i->errblock))
572 e_set(E_ERROR|UNTGZ_BLOCKED, "[block:%d] untgz is blocked", i->blockid);
573 goto err_0;
575 else if (G_UNLIKELY(i->written || i->eof))
576 goto ret_1;
577 if (G_UNLIKELY(setjmp(i->errjmp) != 0))
579 #if UNTGZ_PRESERVE_DIR_TIMES == 1
580 g_free(dpath_tmp);
581 #endif
582 e_set(E_PASS, "error thrown");
583 goto err_0;
586 if (altname)
587 path = altname;
588 else
589 path = s->f_name;
591 #if UNTGZ_PRESERVE_DIR_TIMES == 1
593 struct stat st;
594 dpath_tmp = g_strdup(path);
595 dpath = dirname(dpath_tmp);
596 if (stat(dpath, &st) == -1)
597 e_throw(E_ERROR, "can't stat parent directory: %s", strerror(errno));
598 dt.actime = st.st_atime;
599 dt.modtime = st.st_mtime;
601 #endif
603 switch (s->f_type)
605 case UNTGZ_REG:
607 if (!i->data)
608 goto ret_1;
609 remaining = s->f_size;
610 FILE* f = fopen(path, "w");
611 if (f == 0)
612 e_throw(E_ERROR|UNTGZ_BADIO, "can't open file for writing: %s", strerror(errno));
613 if (setvbuf(f, i->wbuf, _IOFBF, WRITEBUFSIZE))
614 e_throw(E_ERROR|UNTGZ_BADIO, "can't setup write buffer");
615 while (G_LIKELY(remaining > 0))
617 b = read_next_block(s, 0);
618 if (b == 0)
619 e_throw(E_ERROR|UNTGZ_CORRUPT, "[block:%d] corrupted tgz archive (missing data block)", i->blockid);
620 continue_timer(7);
621 if (fwrite(b->b, remaining>BLOCKSIZE?BLOCKSIZE:remaining, 1, f) != 1)
622 e_throw(E_ERROR|UNTGZ_BADIO, "can't write to a file: %s", strerror(errno));
623 stop_timer(7);
624 remaining = remaining<=BLOCKSIZE?0:remaining-BLOCKSIZE;
626 fclose(f);
627 i->data = 0;
628 break;
630 case UNTGZ_SYM:
631 #ifndef __WIN32__
632 if (symlink(s->f_link, path) == -1)
633 #endif
634 e_throw(E_ERROR|UNTGZ_BADIO, "can't create symlink: %s", strerror(errno));
635 break;
636 case UNTGZ_FIFO:
637 #ifndef __WIN32__
638 if (mkfifo(path, s->f_mode) == -1)
639 #endif
640 e_throw(E_ERROR|UNTGZ_BADIO, "can't create fifo: %s", strerror(errno));
641 break;
642 case UNTGZ_LNK:
643 printf("** ln %s %s", s->f_link, path);
644 if (link(s->f_link, path) == -1)
645 e_throw(E_ERROR|UNTGZ_BADIO, "can't create hardlink: %s", strerror(errno));
646 break;
647 case UNTGZ_CHR:
648 #ifndef __WIN32__
649 if (mknod(path, S_IFCHR, (dev_t)((((s->f_devmaj & 0xFF)
650 << 8) & 0xFF00) | (s->f_devmin & 0xFF))) == -1)
651 #endif
652 e_throw(E_ERROR|UNTGZ_BADIO, "can't create chrdev: %s", strerror(errno));
653 break;
654 case UNTGZ_BLK:
655 #ifndef __WIN32__
656 if (mknod(path, S_IFBLK, (dev_t)((((s->f_devmaj & 0xFF)
657 << 8) & 0xFF00) | (s->f_devmin & 0xFF))) == -1)
658 #endif
659 e_throw(E_ERROR|UNTGZ_BADIO, "can't create blkdev: %s", strerror(errno));
660 break;
661 case UNTGZ_DIR:
662 /* because of the way tar stores directories, there
663 is no need to have mkdir_r here */
664 #ifdef __WIN32__
665 if (mkdir(path) == -1 && errno != EEXIST)
666 #else
667 if (mkdir(path, 0700) == -1 && errno != EEXIST)
668 #endif
669 e_throw(E_ERROR|UNTGZ_BADIO, "can't create directory: %s", strerror(errno));
670 break;
671 default:
672 e_throw(E_ERROR, "unknown file type [%d]", s->f_type);
673 break;
676 #ifndef __WIN32__
677 if (chown(path, s->f_uid, s->f_gid) == -1)
678 e_throw(E_ERROR|UNTGZ_BADMETA, "can't chown file: %s", strerror(errno));
679 #endif
680 if (chmod(path, s->f_mode) == -1)
681 e_throw(E_ERROR|UNTGZ_BADMETA, "can't chmod file: %s", strerror(errno));
682 t.actime = t.modtime = s->f_mtime;
683 #ifdef __WIN32__
684 if (posix_utime(path, &t) == -1)
685 #else
686 if (utime(path, &t) == -1)
687 #endif
689 e_throw(E_ERROR|UNTGZ_BADMETA, "can't utime file: %s", strerror(errno));
692 #if UNTGZ_PRESERVE_DIR_TIMES == 1
693 #ifdef __WIN32__
694 if (posix_utime(dpath, &dt) == -1)
695 #else
696 if (utime(dpath, &dt) == -1)
697 #endif
698 e_throw(E_ERROR|UNTGZ_BADMETA, "can't utime parent directory: %s", strerror(errno));
699 g_free(dpath_tmp);
700 #endif
702 i->written = 1;
703 stop_timer(4);
704 return 0;
705 err_0:
706 stop_timer(4);
707 return -1;
708 ret_1:
709 stop_timer(4);
710 return 1;