OSDN Git Service

* src/prototypes.h (str_safe_copy): newly added.
[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 /* ------------------------------------------------------------------------ */
32 static          boolean
33 inquire_extract(name)
34     char           *name;
35 {
36     struct stat     stbuf;
37
38     skip_flg = FALSE;
39     if (stat(name, &stbuf) >= 0) {
40         if (!is_regularfile(&stbuf)) {
41             error("\"%s\" already exists (not a file)", name);
42             return FALSE;
43         }
44
45         if (noexec) {
46             printf("EXTRACT %s but file is exist.\n", name);
47             return FALSE;
48         }
49         else if (!force) {
50             if (!isatty(0)) {
51                 warning("skip to extract %s.", name);
52                 return FALSE;
53             }
54
55             switch (inquire("OverWrite ?(Yes/[No]/All/Skip)", name, "YyNnAaSs\n")) {
56             case 0:
57             case 1:/* Y/y */
58                 break;
59             case 2:
60             case 3:/* N/n */
61             case 8:/* Return */
62                 return FALSE;
63             case 4:
64             case 5:/* A/a */
65                 force = TRUE;
66                 break;
67             case 6:
68             case 7:/* S/s */
69                 skip_flg = TRUE;
70                 break;
71             }
72         }
73     }
74
75     if (noexec)
76         printf("EXTRACT %s\n", name);
77
78     return TRUE;
79 }
80
81 /* ------------------------------------------------------------------------ */
82 static          boolean
83 make_parent_path(name)
84     char           *name;
85 {
86     char            path[FILENAME_LENGTH];
87     struct stat     stbuf;
88     register char  *p;
89
90     /* make parent directory name into PATH for recursive call */
91     str_safe_copy(path, name, sizeof(path));
92     for (p = path + strlen(path); p > path; p--)
93         if (p[-1] == '/') {
94             *--p = '\0';
95             break;
96         }
97
98     if (p == path) {
99         message("invalid path name \"%s\"", name);
100         return FALSE;   /* no more parent. */
101     }
102
103     if (GETSTAT(path, &stbuf) >= 0) {
104         if (is_directory(&stbuf))
105             return TRUE;
106     }
107
108     if (verbose)
109         message("Making directory \"%s\".", path);
110
111 #if defined __MINGW32__
112     if (mkdir(path) >= 0)
113         return TRUE;
114 #else
115     if (mkdir(path, 0777) >= 0) /* try */
116         return TRUE;    /* successful done. */
117 #endif
118
119     if (!make_parent_path(path))
120         return FALSE;
121
122 #if defined __MINGW32__
123     if (mkdir(path) < 0) {      /* try again */
124         error("Cannot make directory \"%s\"", path);
125         return FALSE;
126     }
127 #else
128     if (mkdir(path, 0777) < 0) {    /* try again */
129         error("Cannot make directory \"%s\"", path);
130         return FALSE;
131     }
132 #endif
133
134     return TRUE;
135 }
136
137 /* ------------------------------------------------------------------------ */
138 static FILE    *
139 open_with_make_path(name)
140     char           *name;
141 {
142     FILE           *fp;
143
144     if ((fp = fopen(name, WRITE_BINARY)) == NULL) {
145         if (!make_parent_path(name) ||
146             (fp = fopen(name, WRITE_BINARY)) == NULL)
147             error("Cannot extract a file \"%s\"", name);
148     }
149     return fp;
150 }
151
152 /* ------------------------------------------------------------------------ */
153 static void
154 adjust_info(name, hdr)
155     char           *name;
156     LzHeader       *hdr;
157 {
158     struct utimbuf utimebuf;
159
160     /* adjust file stamp */
161     utimebuf.actime = utimebuf.modtime = hdr->unix_last_modified_stamp;
162
163     if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
164         utime(name, &utimebuf);
165
166     if (hdr->extend_type == EXTEND_UNIX
167         || hdr->extend_type == EXTEND_OS68K
168         || hdr->extend_type == EXTEND_XOSK) {
169 #ifdef NOT_COMPATIBLE_MODE
170         Please need your modification in this space.
171 #else
172         if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
173             chmod(name, hdr->unix_mode);
174 #endif
175         if (!getuid()){
176             uid_t uid = hdr->unix_uid;
177             gid_t gid = hdr->unix_gid;
178
179 #if HAVE_GETPWNAM && HAVE_GETGRNAM
180             if (hdr->user[0]) {
181                 struct passwd *ent = getpwnam(hdr->user);
182                 if (ent) uid = ent->pw_uid;
183             }
184             if (hdr->group[0]) {
185                 struct group *ent = getgrnam(hdr->group);
186                 if (ent) gid = ent->gr_gid;
187             }
188 #endif
189
190 #if HAVE_LCHOWN
191             if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK)
192                 lchown(name, uid, gid);
193             else
194 #endif /* HAVE_LCHWON */
195                 chown(name, uid, gid);
196         }
197     }
198 #if __CYGWIN__
199     else {
200         /* On Cygwin, execute permission should be set for .exe or .dll. */
201         mode_t m;
202
203         umask(m = umask(0));    /* get current umask */
204         chmod(name, 0777 & ~m);
205     }
206 #endif
207 }
208
209 /* ------------------------------------------------------------------------ */
210 static size_t
211 extract_one(afp, hdr)
212     FILE           *afp;    /* archive file */
213     LzHeader       *hdr;
214 {
215     FILE           *fp; /* output file */
216     struct stat     stbuf;
217     char            name[FILENAME_LENGTH];
218     unsigned int crc;
219     int             method;
220     boolean         save_quiet, save_verbose, up_flag;
221     char           *q = hdr->name, c;
222     size_t read_size = 0;
223
224     if (ignore_directory && strrchr(hdr->name, '/')) {
225         q = (char *) strrchr(hdr->name, '/') + 1;
226     }
227     else {
228         if (is_directory_traversal(q)) {
229             fprintf(stderr, "Possible directory traversal hack attempt in %s\n", q);
230             exit(111);
231         }
232
233         if (*q == '/') {
234             while (*q == '/') { q++; }
235
236             /*
237              * if OSK then strip device name
238              */
239             if (hdr->extend_type == EXTEND_OS68K
240                 || hdr->extend_type == EXTEND_XOSK) {
241                 do
242                     c = (*q++);
243                 while (c && c != '/');
244                 if (!c || !*q)
245                     q = ".";    /* if device name only */
246             }
247         }
248     }
249
250     if (extract_directory)
251         xsnprintf(name, sizeof(name), "%s/%s", extract_directory, q);
252     else
253         str_safe_copy(name, q, sizeof(name));
254
255     /* LZHDIRS_METHOD¤ò»ý¤Ä¥Ø¥Ã¥À¤ò¥Á¥§¥Ã¥¯¤¹¤ë */
256     /* 1999.4.30 t.okamoto */
257     for (method = 0;; method++) {
258         if (methods[method] == NULL) {
259             error("Unknown method \"%.*s\"; \"%s\" will be skiped ...",
260                   5, hdr->method, name);
261             return read_size;
262         }
263         if (memcmp(hdr->method, methods[method], 5) == 0)
264             break;
265     }
266
267     if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_REGULAR
268         && method != LZHDIRS_METHOD_NUM) {
269     extract_regular:
270 #if 0
271         for (method = 0;; method++) {
272             if (methods[method] == NULL) {
273                 error("Unknown method \"%.*s\"; \"%s\" will be skiped ...",
274                       5, hdr->method, name);
275                 return read_size;
276             }
277             if (memcmp(hdr->method, methods[method], 5) == 0)
278                 break;
279         }
280 #endif
281
282         reading_filename = archive_name;
283         writing_filename = name;
284         if (output_to_stdout || verify_mode) {
285             if (noexec) {
286                 printf("%s %s\n", verify_mode ? "VERIFY" : "EXTRACT", name);
287                 return read_size;
288             }
289
290             save_quiet = quiet;
291             save_verbose = verbose;
292             if (!quiet && output_to_stdout) {
293                 printf("::::::::\n%s\n::::::::\n", name);
294                 quiet = TRUE;
295                 verbose = FALSE;
296             }
297             else if (verify_mode) {
298                 quiet = FALSE;
299                 verbose = TRUE;
300             }
301
302 #if __MINGW32__
303             {
304                 int old_mode;
305                 fflush(stdout);
306                 old_mode = setmode(fileno(stdout), O_BINARY);
307 #endif
308
309             crc = decode_lzhuf(afp, stdout,
310                                hdr->original_size, hdr->packed_size,
311                                name, method, &read_size);
312 #if __MINGW32__
313                 fflush(stdout);
314                 setmode(fileno(stdout), old_mode);
315             }
316 #endif
317             quiet = save_quiet;
318             verbose = save_verbose;
319         }
320         else {
321             if (skip_flg == FALSE)  {
322                 up_flag = inquire_extract(name);
323                 if (up_flag == FALSE && force == FALSE) {
324                     return read_size;
325                 }
326             }
327
328             if (skip_flg == TRUE) { /* if skip_flg */
329                 if (stat(name, &stbuf) == 0 && force != TRUE) {
330                     if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
331                         if (quiet != TRUE)
332                             printf("%s : Skipped...\n", name);
333                         return read_size;
334                     }
335                 }
336             }
337             if (noexec) {
338                 return read_size;
339             }
340
341             signal(SIGINT, interrupt);
342 #ifdef SIGHUP
343             signal(SIGHUP, interrupt);
344 #endif
345
346             unlink(name);
347             remove_extracting_file_when_interrupt = TRUE;
348
349             if ((fp = open_with_make_path(name)) != NULL) {
350                 crc = decode_lzhuf(afp, fp,
351                                    hdr->original_size, hdr->packed_size,
352                                    name, method, &read_size);
353                 fclose(fp);
354             }
355             remove_extracting_file_when_interrupt = FALSE;
356             signal(SIGINT, SIG_DFL);
357 #ifdef SIGHUP
358             signal(SIGHUP, SIG_DFL);
359 #endif
360             if (!fp)
361                 return read_size;
362         }
363
364         if (hdr->has_crc && crc != hdr->crc)
365             error("CRC error: \"%s\"", name);
366     }
367     else if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_DIRECTORY
368              || (hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK
369              || method == LZHDIRS_METHOD_NUM) {
370         /* ¢¬¤³¤ì¤Ç¡¢Symbolic Link ¤Ï¡¢Âç¾æÉפ«¡© */
371         if (!ignore_directory && !verify_mode) {
372             if (noexec) {
373                 if (quiet != TRUE)
374                     printf("EXTRACT %s (directory)\n", name);
375                 return read_size;
376             }
377             /* NAME has trailing SLASH '/', (^_^) */
378             if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK) {
379                 int             l_code;
380
381 #ifdef S_IFLNK
382                 if (skip_flg == FALSE)  {
383                     up_flag = inquire_extract(name);
384                     if (up_flag == FALSE && force == FALSE) {
385                         return read_size;
386                     }
387                 } else {
388                     if (GETSTAT(name, &stbuf) == 0 && force != TRUE) {
389                         if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
390                             if (quiet != TRUE)
391                                 printf("%s : Skipped...\n", name);
392                             return read_size;
393                         }
394                     }
395                 }
396
397                 unlink(name);
398                 make_parent_path(name);
399                 l_code = symlink(hdr->realname, name);
400                 if (l_code < 0) {
401                     if (quiet != TRUE)
402                         warning("Can't make Symbolic Link \"%s\" -> \"%s\"",
403                                 hdr->realname, name);
404                 }
405                 if (quiet != TRUE) {
406                     message("Symbolic Link %s -> %s",
407                             hdr->realname, name);
408                 }
409 #else
410                 warning("Can't make Symbolic Link %s -> %s",
411                         hdr->realname, name);
412                 return read_size;
413 #endif
414             } else { /* make directory */
415                 if (!output_to_stdout && !make_parent_path(name))
416                     return read_size;
417                 /* save directory information */
418                 add_dirinfo(name, hdr);
419             }
420         }
421     }
422     else {
423         if (force)              /* force extract */
424             goto extract_regular;
425         else
426             error("Unknown file type: \"%s\". use `f' option to force extract.", name);
427     }
428
429     if (!output_to_stdout)
430         adjust_info(name, hdr);
431
432     return read_size;
433 }
434
435 /* ------------------------------------------------------------------------ */
436 /* EXTRACT COMMAND MAIN                                                     */
437 /* ------------------------------------------------------------------------ */
438 void
439 cmd_extract()
440 {
441     LzHeader        hdr;
442     off_t           pos;
443     FILE           *afp;
444     size_t read_size;
445
446     /* open archive file */
447     if ((afp = open_old_archive()) == NULL)
448         fatal_error("Cannot open archive file \"%s\"", archive_name);
449
450     if (archive_is_msdos_sfx1(archive_name))
451         seek_lha_header(afp);
452
453     /* extract each files */
454     while (get_header(afp, &hdr)) {
455         if (need_file(hdr.name)) {
456             pos = ftello(afp);
457             read_size = extract_one(afp, &hdr);
458             if (read_size != hdr.packed_size) {
459                 /* when error occurred in extract_one(), should adjust
460                    point of file stream */
461                 if (pos != -1 && afp != stdin)
462                     fseeko(afp, pos + hdr.packed_size - read_size, SEEK_SET);
463                 else {
464                     size_t i = hdr.packed_size - read_size;
465                     while (i--) fgetc(afp);
466                 }
467             }
468         } else {
469             if (afp != stdin)
470                 fseeko(afp, hdr.packed_size, SEEK_CUR);
471             else {
472                 size_t i = hdr.packed_size;
473                 while (i--) fgetc(afp);
474             }
475         }
476     }
477
478     /* close archive file */
479     fclose(afp);
480
481     /* adjust directory information */
482     adjust_dirinfo();
483
484     return;
485 }
486
487 int
488 is_directory_traversal(char *string)
489 {
490     unsigned int type = 0; /* 0 = new, 1 = only dots, 2 = other chars than dots */
491     char *temp;
492
493     temp = string;
494
495     while (*temp != 0) {
496         if (temp[0] == '/') {
497             if (type == 1) { return 1; }
498             type = 0;
499             temp++;
500             continue;
501         }
502
503         if ((temp[0] == '.') && (type < 2))
504             type = 1;
505         if (temp[0] != '.')
506             type = 2;
507
508         temp++;
509     } /* while */
510
511     return (type == 1);
512 }
513
514 /*
515  * restore directory information (time stamp).
516  * added by A.Iriyama  2003.12.12
517  */
518
519 typedef struct lhdDirectoryInfo_t {
520     struct lhdDirectoryInfo_t *next;
521     LzHeader hdr;
522 } LzHeaderList;
523
524 static LzHeaderList *dirinfo;
525
526 static void add_dirinfo(char *name, LzHeader *hdr)
527 {
528     LzHeaderList *p;
529
530     if (memcmp(hdr->method, LZHDIRS_METHOD, 5) != 0)
531         return;
532
533     p = xmalloc(sizeof(LzHeaderList));
534
535     memcpy(&p->hdr, hdr, sizeof(LzHeader));
536     strncpy(p->hdr.name, name, sizeof(p->hdr.name));
537     p->hdr.name[sizeof(p->hdr.name)-1] = 0;
538
539     {
540         LzHeaderList *tmp = dirinfo;
541         dirinfo = p;
542         dirinfo->next = tmp;
543     }
544 }
545
546 static void adjust_dirinfo()
547 {
548     while (dirinfo) {
549         adjust_info(dirinfo->hdr.name, &dirinfo->hdr);
550
551         {
552             LzHeaderList *tmp = dirinfo;
553             dirinfo = dirinfo->next;
554             free(tmp);
555         }
556     }
557 }