OSDN Git Service

possible to extract 4G over files
[lha/lha.git] / src / lhext.c
1 /* ------------------------------------------------------------------------ */
2 /* LHa for UNIX                                                             */
3 /*              lhext.c -- LHarc extract                                    */
4 /*                                                                          */
5 /*      Copyright (C) MCMLXXXIX Yooichi.Tagawa                              */
6 /*      Modified                Nobutaka Watazaki                           */
7 /*                                                                          */
8 /*  Ver. 0.00  Original                             1988.05.23  Y.Tagawa    */
9 /*  Ver. 1.00  Fixed                                1989.09.22  Y.Tagawa    */
10 /*  Ver. 0.03  LHa for UNIX                         1991.12.17  M.Oki       */
11 /*  Ver. 1.12  LHa for UNIX                         1993.10.01  N.Watazaki  */
12 /*  Ver. 1.13b Symbolic Link Update Bug Fix         1994.06.21  N.Watazaki  */
13 /*  Ver. 1.14  Source All chagned                   1995.01.14  N.Watazaki  */
14 /*  Ver. 1.14e bugfix                               1999.04.30  T.Okamoto   */
15 /* ------------------------------------------------------------------------ */
16 #include "lha.h"
17 /* ------------------------------------------------------------------------ */
18 static int      skip_flg = FALSE;   /* FALSE..No Skip , TRUE..Skip */
19 static char    *methods[] =
20 {
21     LZHUFF0_METHOD, LZHUFF1_METHOD, LZHUFF2_METHOD, LZHUFF3_METHOD,
22     LZHUFF4_METHOD, LZHUFF5_METHOD, LZHUFF6_METHOD, LZHUFF7_METHOD,
23     LARC_METHOD, LARC5_METHOD, LARC4_METHOD,
24     LZHDIRS_METHOD,
25     NULL
26 };
27
28 static void add_dirinfo(char* name, LzHeader* hdr);
29 static void adjust_dirinfo();
30
31 #ifdef HAVE_LIBAPPLEFILE
32 static boolean decode_macbinary(FILE *ofp, size_t size, const char *outPath);
33 #endif
34
35 /* ------------------------------------------------------------------------ */
36 static          boolean
37 inquire_extract(name)
38     char           *name;
39 {
40     struct stat     stbuf;
41
42     skip_flg = FALSE;
43     if (stat(name, &stbuf) >= 0) {
44         if (!is_regularfile(&stbuf)) {
45             error("\"%s\" already exists (not a file)", name);
46             return FALSE;
47         }
48
49         if (noexec) {
50             printf("EXTRACT %s but file is exist.\n", name);
51             return FALSE;
52         }
53         else if (!force) {
54             if (!isatty(0)) {
55                 warning("skip to extract %s.", name);
56                 return FALSE;
57             }
58
59             switch (inquire("OverWrite ?(Yes/[No]/All/Skip)", name, "YyNnAaSs\n")) {
60             case 0:
61             case 1:/* Y/y */
62                 break;
63             case 2:
64             case 3:/* N/n */
65             case 8:/* Return */
66                 return FALSE;
67             case 4:
68             case 5:/* A/a */
69                 force = TRUE;
70                 break;
71             case 6:
72             case 7:/* S/s */
73                 skip_flg = TRUE;
74                 break;
75             }
76         }
77     }
78
79     if (noexec)
80         printf("EXTRACT %s\n", name);
81
82     return TRUE;
83 }
84
85 /* ------------------------------------------------------------------------ */
86 static          boolean
87 make_parent_path(name)
88     char           *name;
89 {
90     char            path[FILENAME_LENGTH];
91     struct stat     stbuf;
92     register char  *p;
93
94     /* make parent directory name into PATH for recursive call */
95     str_safe_copy(path, name, sizeof(path));
96     for (p = path + strlen(path); p > path; p--)
97         if (p[-1] == '/') {
98             *--p = '\0';
99             break;
100         }
101
102     if (p == path) {
103         message("invalid path name \"%s\"", name);
104         return FALSE;   /* no more parent. */
105     }
106
107     if (GETSTAT(path, &stbuf) >= 0) {
108         if (is_directory(&stbuf))
109             return TRUE;
110     }
111
112     if (verbose)
113         message("Making directory \"%s\".", path);
114
115 #if defined __MINGW32__
116     if (mkdir(path) >= 0)
117         return TRUE;
118 #else
119     if (mkdir(path, 0777) >= 0) /* try */
120         return TRUE;    /* successful done. */
121 #endif
122
123     if (!make_parent_path(path))
124         return FALSE;
125
126 #if defined __MINGW32__
127     if (mkdir(path) < 0) {      /* try again */
128         error("Cannot make directory \"%s\"", path);
129         return FALSE;
130     }
131 #else
132     if (mkdir(path, 0777) < 0) {    /* try again */
133         error("Cannot make directory \"%s\"", path);
134         return FALSE;
135     }
136 #endif
137
138     return TRUE;
139 }
140
141 /* ------------------------------------------------------------------------ */
142 static FILE    *
143 open_with_make_path(name)
144     char           *name;
145 {
146     FILE           *fp;
147
148     if ((fp = fopen(name, WRITE_BINARY)) == NULL) {
149         if (!make_parent_path(name) ||
150             (fp = fopen(name, WRITE_BINARY)) == NULL)
151             error("Cannot extract a file \"%s\"", name);
152     }
153     return fp;
154 }
155
156 /* ------------------------------------------------------------------------ */
157 static void
158 adjust_info(name, hdr)
159     char           *name;
160     LzHeader       *hdr;
161 {
162     struct utimbuf utimebuf;
163
164     /* adjust file stamp */
165     utimebuf.actime = utimebuf.modtime = hdr->unix_last_modified_stamp;
166
167     if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
168         utime(name, &utimebuf);
169
170     if (hdr->extend_type == EXTEND_UNIX
171         || hdr->extend_type == EXTEND_OS68K
172         || hdr->extend_type == EXTEND_XOSK) {
173 #ifdef NOT_COMPATIBLE_MODE
174         /* Please need your modification in this space. */
175 #ifdef __DJGPP__
176         if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
177             chmod(name, hdr->unix_mode);
178 #endif /* __DJGPP__ */
179 #else
180         if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
181             chmod(name, hdr->unix_mode);
182 #endif
183         if (!getuid()){
184             uid_t uid = hdr->unix_uid;
185             gid_t gid = hdr->unix_gid;
186
187 #if HAVE_GETPWNAM && HAVE_GETGRNAM
188             if (hdr->user[0]) {
189                 struct passwd *ent = getpwnam(hdr->user);
190                 if (ent) uid = ent->pw_uid;
191             }
192             if (hdr->group[0]) {
193                 struct group *ent = getgrnam(hdr->group);
194                 if (ent) gid = ent->gr_gid;
195             }
196 #endif
197
198 #if HAVE_LCHOWN
199             if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK)
200                 lchown(name, uid, gid);
201             else
202 #endif /* HAVE_LCHWON */
203                 chown(name, uid, gid);
204         }
205     }
206 #if __CYGWIN__
207     else {
208         /* On Cygwin, execute permission should be set for .exe or .dll. */
209         mode_t m;
210
211         umask(m = umask(0));    /* get current umask */
212         chmod(name, 0777 & ~m);
213     }
214 #endif
215 }
216
217 /* ------------------------------------------------------------------------ */
218 static off_t
219 extract_one(afp, hdr)
220     FILE           *afp;    /* archive file */
221     LzHeader       *hdr;
222 {
223     FILE           *fp; /* output file */
224 #if HAVE_LIBAPPLEFILE
225     FILE           *tfp; /* temporary output file */
226 #endif
227     struct stat     stbuf;
228     char            name[FILENAME_LENGTH];
229     unsigned int crc;
230     int             method;
231     boolean         save_quiet, save_verbose, up_flag;
232     char           *q = hdr->name, c;
233     off_t read_size = 0;
234
235     if (ignore_directory && strrchr(hdr->name, '/')) {
236         q = (char *) strrchr(hdr->name, '/') + 1;
237     }
238     else {
239         if (is_directory_traversal(q)) {
240             fprintf(stderr, "Possible directory traversal hack attempt in %s\n", q);
241             exit(111);
242         }
243
244         if (*q == '/') {
245             while (*q == '/') { q++; }
246
247             /*
248              * if OSK then strip device name
249              */
250             if (hdr->extend_type == EXTEND_OS68K
251                 || hdr->extend_type == EXTEND_XOSK) {
252                 do
253                     c = (*q++);
254                 while (c && c != '/');
255                 if (!c || !*q)
256                     q = ".";    /* if device name only */
257             }
258         }
259     }
260
261     if (extract_directory)
262         xsnprintf(name, sizeof(name), "%s/%s", extract_directory, q);
263     else
264         str_safe_copy(name, q, sizeof(name));
265
266     /* LZHDIRS_METHOD¤ò»ý¤Ä¥Ø¥Ã¥À¤ò¥Á¥§¥Ã¥¯¤¹¤ë */
267     /* 1999.4.30 t.okamoto */
268     for (method = 0;; method++) {
269         if (methods[method] == NULL) {
270             error("Unknown method \"%.*s\"; \"%s\" will be skiped ...",
271                   5, hdr->method, name);
272             return read_size;
273         }
274         if (memcmp(hdr->method, methods[method], 5) == 0)
275             break;
276     }
277
278     if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_REGULAR
279         && method != LZHDIRS_METHOD_NUM) {
280     extract_regular:
281 #if 0
282         for (method = 0;; method++) {
283             if (methods[method] == NULL) {
284                 error("Unknown method \"%.*s\"; \"%s\" will be skiped ...",
285                       5, hdr->method, name);
286                 return read_size;
287             }
288             if (memcmp(hdr->method, methods[method], 5) == 0)
289                 break;
290         }
291 #endif
292
293         reading_filename = archive_name;
294         writing_filename = name;
295         if (output_to_stdout || verify_mode) {
296             /* "Icon\r" should be a resource fork file encoded in MacBinary
297                format, so that it should be skipped. */
298             if (hdr->extend_type == EXTEND_MACOS
299                 && strcmp(basename(name), "Icon\r") == 0
300                 && decode_macbinary_contents) {
301                 return read_size;
302             }
303
304             if (noexec) {
305                 printf("%s %s\n", verify_mode ? "VERIFY" : "EXTRACT", name);
306                 return read_size;
307             }
308
309             save_quiet = quiet;
310             save_verbose = verbose;
311             if (!quiet && output_to_stdout) {
312                 printf("::::::::\n%s\n::::::::\n", name);
313                 quiet = TRUE;
314                 verbose = FALSE;
315             }
316             else if (verify_mode) {
317                 quiet = FALSE;
318                 verbose = TRUE;
319             }
320
321 #if defined(__MINGW32__) || defined(__DJGPP__)
322             {
323                 int old_mode;
324                 fflush(stdout);
325                 old_mode = setmode(fileno(stdout), O_BINARY);
326 #endif
327
328 #if HAVE_LIBAPPLEFILE
329             /* On default, MacLHA encodes into MacBinary. */
330             if (hdr->extend_type == EXTEND_MACOS && !verify_mode && decode_macbinary_contents) {
331                 /* build temporary file */
332                 tfp = NULL; /* avoid compiler warnings `uninitialized' */
333                 tfp = build_temporary_file();
334
335                 crc = decode_lzhuf(afp, tfp,
336                                    hdr->original_size, hdr->packed_size,
337                                    name, method, &read_size);
338                 fclose(tfp);
339                 decode_macbinary(stdout, hdr->original_size, name);
340                 unlink(temporary_name);
341             } else {
342                 crc = decode_lzhuf(afp, stdout,
343                                    hdr->original_size, hdr->packed_size,
344                                    name, method, &read_size);
345             }
346 #else
347             crc = decode_lzhuf(afp, stdout,
348                                hdr->original_size, hdr->packed_size,
349                                name, method, &read_size);
350 #endif /* HAVE_LIBAPPLEFILE */
351 #if defined(__MINGW32__) || defined(__DJGPP__)
352                 fflush(stdout);
353                 setmode(fileno(stdout), old_mode);
354             }
355 #endif
356             quiet = save_quiet;
357             verbose = save_verbose;
358         }
359         else {
360 #ifndef __APPLE__
361             /* "Icon\r" should be a resource fork of parent folder's icon,
362                so that it can be skipped when system is not Mac OS X. */
363             if (hdr->extend_type == EXTEND_MACOS
364                 && strcmp(basename(name), "Icon\r") == 0
365                 && decode_macbinary_contents) {
366                 make_parent_path(name); /* create directory only */
367                 return read_size;
368             }
369 #endif /* __APPLE__ */
370             if (skip_flg == FALSE)  {
371                 up_flag = inquire_extract(name);
372                 if (up_flag == FALSE && force == FALSE) {
373                     return read_size;
374                 }
375             }
376
377             if (skip_flg == TRUE) { /* if skip_flg */
378                 if (stat(name, &stbuf) == 0 && force != TRUE) {
379                     if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
380                         if (quiet != TRUE)
381                             printf("%s : Skipped...\n", name);
382                         return read_size;
383                     }
384                 }
385             }
386             if (noexec) {
387                 return read_size;
388             }
389
390             signal(SIGINT, interrupt);
391 #ifdef SIGHUP
392             signal(SIGHUP, interrupt);
393 #endif
394
395             unlink(name);
396             remove_extracting_file_when_interrupt = TRUE;
397
398             if ((fp = open_with_make_path(name)) != NULL) {
399 #if HAVE_LIBAPPLEFILE
400                 if (hdr->extend_type == EXTEND_MACOS && !verify_mode && decode_macbinary_contents) {
401                     /* build temporary file */
402                     tfp = NULL; /* avoid compiler warnings `uninitialized' */
403                     tfp = build_temporary_file();
404
405                     crc = decode_lzhuf(afp, tfp,
406                                        hdr->original_size, hdr->packed_size,
407                                        name, method, &read_size);
408                     fclose(tfp);
409                     decode_macbinary(fp, hdr->original_size, name);
410 #ifdef __APPLE__
411                     /* TODO: set resource fork */
412                     /* after processing, "Icon\r" is not needed. */
413                     if (strcmp(basename(name), "Icon\r") == 0) {
414                         unlink(name);
415                     }
416 #endif /* __APPLE__ */
417                     unlink(temporary_name);
418                 } else {
419                     crc = decode_lzhuf(afp, fp,
420                                        hdr->original_size, hdr->packed_size,
421                                        name, method, &read_size);
422                 }
423 #else /* HAVE_LIBAPPLEFILE */
424                 crc = decode_lzhuf(afp, fp,
425                                    hdr->original_size, hdr->packed_size,
426                                    name, method, &read_size);
427 #endif /* HAVE_LIBAPPLEFILE */
428                 fclose(fp);
429             }
430             remove_extracting_file_when_interrupt = FALSE;
431             signal(SIGINT, SIG_DFL);
432 #ifdef SIGHUP
433             signal(SIGHUP, SIG_DFL);
434 #endif
435             if (!fp)
436                 return read_size;
437         }
438
439         if (hdr->has_crc && crc != hdr->crc)
440             error("CRC error: \"%s\"", name);
441     }
442     else if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_DIRECTORY
443              || (hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK
444              || method == LZHDIRS_METHOD_NUM) {
445         /* ¢¬¤³¤ì¤Ç¡¢Symbolic Link ¤Ï¡¢Âç¾æÉפ«¡© */
446         if (!ignore_directory && !verify_mode) {
447             if (noexec) {
448                 if (quiet != TRUE)
449                     printf("EXTRACT %s (directory)\n", name);
450                 return read_size;
451             }
452             /* NAME has trailing SLASH '/', (^_^) */
453             if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK) {
454                 int             l_code;
455
456 #ifdef S_IFLNK
457                 if (skip_flg == FALSE)  {
458                     up_flag = inquire_extract(name);
459                     if (up_flag == FALSE && force == FALSE) {
460                         return read_size;
461                     }
462                 } else {
463                     if (GETSTAT(name, &stbuf) == 0 && force != TRUE) {
464                         if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
465                             if (quiet != TRUE)
466                                 printf("%s : Skipped...\n", name);
467                             return read_size;
468                         }
469                     }
470                 }
471
472                 unlink(name);
473                 make_parent_path(name);
474                 l_code = symlink(hdr->realname, name);
475                 if (l_code < 0) {
476                     if (quiet != TRUE)
477                         warning("Can't make Symbolic Link \"%s\" -> \"%s\"",
478                                 name, hdr->realname);
479                 }
480                 if (quiet != TRUE) {
481                     message("Symbolic Link %s -> %s",
482                             name, hdr->realname);
483                 }
484 #else
485                 warning("Can't make Symbolic Link %s -> %s",
486                         name, hdr->realname);
487                 return read_size;
488 #endif
489             }
490             else { /* make directory */
491                 if (!output_to_stdout && !make_parent_path(name))
492                     return read_size;
493                 /* save directory information */
494                 add_dirinfo(name, hdr);
495             }
496         }
497     }
498     else {
499         if (force)              /* force extract */
500             goto extract_regular;
501         else
502             error("Unknown file type: \"%s\". use `f' option to force extract.", name);
503     }
504
505     if (!output_to_stdout)
506         adjust_info(name, hdr);
507
508     return read_size;
509 }
510
511 /* ------------------------------------------------------------------------ */
512 /* EXTRACT COMMAND MAIN                                                     */
513 /* ------------------------------------------------------------------------ */
514 void
515 cmd_extract()
516 {
517     LzHeader        hdr;
518     off_t           pos;
519     FILE           *afp;
520     off_t read_size;
521
522     /* open archive file */
523     if ((afp = open_old_archive()) == NULL)
524         fatal_error("Cannot open archive file \"%s\"", archive_name);
525
526     if (archive_is_msdos_sfx1(archive_name))
527         seek_lha_header(afp);
528
529     /* extract each files */
530     while (get_header(afp, &hdr)) {
531         if (need_file(hdr.name)) {
532             pos = ftello(afp);
533             read_size = extract_one(afp, &hdr);
534             if (read_size != hdr.packed_size) {
535                 /* when error occurred in extract_one(), should adjust
536                    point of file stream */
537                 if (pos != -1 && afp != stdin)
538                     fseeko(afp, pos + hdr.packed_size - read_size, SEEK_SET);
539                 else {
540                     off_t i = hdr.packed_size - read_size;
541                     while (i--) fgetc(afp);
542                 }
543             }
544         } else {
545             if (afp != stdin)
546                 fseeko(afp, hdr.packed_size, SEEK_CUR);
547             else {
548                 off_t i = hdr.packed_size;
549                 while (i--) fgetc(afp);
550             }
551         }
552     }
553
554     /* close archive file */
555     fclose(afp);
556
557     /* adjust directory information */
558     adjust_dirinfo();
559
560     return;
561 }
562
563 int
564 is_directory_traversal(char *path)
565 {
566     int state = 0;
567
568     for (; *path; path++) {
569         switch (state) {
570         case 0:
571             if (*path == '.') state = 1;
572             else state = 3;
573             break;
574         case 1:
575             if (*path == '.') state = 2;
576             else if (*path == '/') state = 0;
577             else state = 3;
578             break;
579         case 2:
580             if (*path == '/') return 1;
581             else state = 3;
582             break;
583         case 3:
584             if (*path == '/') state = 0;
585             break;
586         }
587     }
588
589     return state == 2;
590 }
591
592 /*
593  * restore directory information (time stamp).
594  * added by A.Iriyama  2003.12.12
595  */
596
597 typedef struct lhdDirectoryInfo_t {
598     struct lhdDirectoryInfo_t *next;
599     LzHeader hdr;
600 } LzHeaderList;
601
602 static LzHeaderList *dirinfo;
603
604 static void add_dirinfo(char *name, LzHeader *hdr)
605 {
606     LzHeaderList *p;
607
608     if (memcmp(hdr->method, LZHDIRS_METHOD, 5) != 0)
609         return;
610
611     p = xmalloc(sizeof(LzHeaderList));
612
613     memcpy(&p->hdr, hdr, sizeof(LzHeader));
614     strncpy(p->hdr.name, name, sizeof(p->hdr.name));
615     p->hdr.name[sizeof(p->hdr.name)-1] = 0;
616
617     {
618         LzHeaderList *tmp = dirinfo;
619         dirinfo = p;
620         dirinfo->next = tmp;
621     }
622 }
623
624 static void adjust_dirinfo()
625 {
626     while (dirinfo) {
627         adjust_info(dirinfo->hdr.name, &dirinfo->hdr);
628
629         {
630             LzHeaderList *tmp = dirinfo;
631             dirinfo = dirinfo->next;
632             free(tmp);
633         }
634     }
635 }
636
637 #if HAVE_LIBAPPLEFILE
638 static boolean
639 decode_macbinary(ofp, size, outPath)
640     FILE *ofp;
641     off_t size;
642     const char *outPath;
643 {
644     af_file_t *afp = NULL;
645     FILE *ifp = NULL;
646     unsigned char *datap;
647     off_t dlen;
648
649     if ((afp = af_open(temporary_name)) != NULL) {
650         /* fetch datafork */
651         datap = af_data(afp, &dlen);
652         fwrite(datap, sizeof(unsigned char), dlen, ofp);
653         af_close(afp);
654         return TRUE;
655     } else { /* it may be not encoded in MacBinary */
656         /* try to copy */
657         if ((ifp = fopen(temporary_name, READ_BINARY)) == NULL) {
658             error("Cannot open a temporary file \"%s\"", temporary_name);
659             return FALSE;
660         }
661         copyfile(ifp, ofp, size, 0, 0);
662         fclose(ifp);
663         return TRUE;
664     }
665
666     return FALSE;
667 }
668 #endif /* HAVE_LIBAPPLEFILE */