OSDN Git Service

* src/lhext.c (extract_one, is_directory_traversal): applied a
[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     strcpy(path, name);
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         strcpy(name, q);
254
255
256     /* LZHDIRS_METHOD¤ò»ý¤Ä¥Ø¥Ã¥À¤ò¥Á¥§¥Ã¥¯¤¹¤ë */
257     /* 1999.4.30 t.okamoto */
258     for (method = 0;; method++) {
259         if (methods[method] == NULL) {
260             error("Unknown method \"%.*s\"; \"%s\" will be skiped ...",
261                   5, hdr->method, name);
262             return read_size;
263         }
264         if (memcmp(hdr->method, methods[method], 5) == 0)
265             break;
266     }
267
268     if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_REGULAR
269         && method != LZHDIRS_METHOD_NUM) {
270     extract_regular:
271 #if 0
272         for (method = 0;; method++) {
273             if (methods[method] == NULL) {
274                 error("Unknown method \"%.*s\"; \"%s\" will be skiped ...",
275                       5, hdr->method, name);
276                 return read_size;
277             }
278             if (memcmp(hdr->method, methods[method], 5) == 0)
279                 break;
280         }
281 #endif
282
283         reading_filename = archive_name;
284         writing_filename = name;
285         if (output_to_stdout || verify_mode) {
286             if (noexec) {
287                 printf("%s %s\n", verify_mode ? "VERIFY" : "EXTRACT", name);
288                 return read_size;
289             }
290
291             save_quiet = quiet;
292             save_verbose = verbose;
293             if (!quiet && output_to_stdout) {
294                 printf("::::::::\n%s\n::::::::\n", name);
295                 quiet = TRUE;
296                 verbose = FALSE;
297             }
298             else if (verify_mode) {
299                 quiet = FALSE;
300                 verbose = TRUE;
301             }
302
303 #if __MINGW32__
304             {
305                 int old_mode;
306                 fflush(stdout);
307                 old_mode = setmode(fileno(stdout), O_BINARY);
308 #endif
309
310             crc = decode_lzhuf(afp, stdout,
311                                hdr->original_size, hdr->packed_size,
312                                name, method, &read_size);
313 #if __MINGW32__
314                 fflush(stdout);
315                 setmode(fileno(stdout), old_mode);
316             }
317 #endif
318             quiet = save_quiet;
319             verbose = save_verbose;
320         }
321         else {
322             if (skip_flg == FALSE)  {
323                 up_flag = inquire_extract(name);
324                 if (up_flag == FALSE && force == FALSE) {
325                     return read_size;
326                 }
327             }
328
329             if (skip_flg == TRUE) { /* if skip_flg */
330                 if (stat(name, &stbuf) == 0 && force != TRUE) {
331                     if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
332                         if (quiet != TRUE)
333                             printf("%s : Skipped...\n", name);
334                         return read_size;
335                     }
336                 }
337             }
338             if (noexec) {
339                 return read_size;
340             }
341
342             signal(SIGINT, interrupt);
343 #ifdef SIGHUP
344             signal(SIGHUP, interrupt);
345 #endif
346
347             unlink(name);
348             remove_extracting_file_when_interrupt = TRUE;
349
350             if ((fp = open_with_make_path(name)) != NULL) {
351                 crc = decode_lzhuf(afp, fp,
352                                    hdr->original_size, hdr->packed_size,
353                                    name, method, &read_size);
354                 fclose(fp);
355             }
356             remove_extracting_file_when_interrupt = FALSE;
357             signal(SIGINT, SIG_DFL);
358 #ifdef SIGHUP
359             signal(SIGHUP, SIG_DFL);
360 #endif
361             if (!fp)
362                 return read_size;
363         }
364
365         if (hdr->has_crc && crc != hdr->crc)
366             error("CRC error: \"%s\"", name);
367     }
368     else if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_DIRECTORY
369              || (hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK
370              || method == LZHDIRS_METHOD_NUM) {
371         /* ¢¬¤³¤ì¤Ç¡¢Symbolic Link ¤Ï¡¢Âç¾æÉפ«¡© */
372         if (!ignore_directory && !verify_mode) {
373             if (noexec) {
374                 if (quiet != TRUE)
375                     printf("EXTRACT %s (directory)\n", name);
376                 return read_size;
377             }
378             /* NAME has trailing SLASH '/', (^_^) */
379             if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK) {
380                 int             l_code;
381
382 #ifdef S_IFLNK
383                 if (skip_flg == FALSE)  {
384                     up_flag = inquire_extract(name);
385                     if (up_flag == FALSE && force == FALSE) {
386                         return read_size;
387                     }
388                 } else {
389                     if (GETSTAT(name, &stbuf) == 0 && force != TRUE) {
390                         if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
391                             if (quiet != TRUE)
392                                 printf("%s : Skipped...\n", name);
393                             return read_size;
394                         }
395                     }
396                 }
397
398                 unlink(name);
399                 make_parent_path(name);
400                 l_code = symlink(hdr->realname, name);
401                 if (l_code < 0) {
402                     if (quiet != TRUE)
403                         warning("Can't make Symbolic Link \"%s\" -> \"%s\"",
404                                 hdr->realname, name);
405                 }
406                 if (quiet != TRUE) {
407                     message("Symbolic Link %s -> %s",
408                             hdr->realname, name);
409                 }
410 #else
411                 warning("Can't make Symbolic Link %s -> %s",
412                         hdr->realname, name);
413                 return read_size;
414 #endif
415             } else { /* make directory */
416                 if (!output_to_stdout && !make_parent_path(name))
417                     return read_size;
418                 /* save directory information */
419                 add_dirinfo(name, hdr);
420             }
421         }
422     }
423     else {
424         if (force)              /* force extract */
425             goto extract_regular;
426         else
427             error("Unknown file type: \"%s\". use `f' option to force extract.", name);
428     }
429
430     if (!output_to_stdout)
431         adjust_info(name, hdr);
432
433     return read_size;
434 }
435
436 /* ------------------------------------------------------------------------ */
437 /* EXTRACT COMMAND MAIN                                                     */
438 /* ------------------------------------------------------------------------ */
439 void
440 cmd_extract()
441 {
442     LzHeader        hdr;
443     off_t           pos;
444     FILE           *afp;
445     size_t read_size;
446
447     /* open archive file */
448     if ((afp = open_old_archive()) == NULL)
449         fatal_error("Cannot open archive file \"%s\"", archive_name);
450
451     if (archive_is_msdos_sfx1(archive_name))
452         seek_lha_header(afp);
453
454     /* extract each files */
455     while (get_header(afp, &hdr)) {
456         if (need_file(hdr.name)) {
457             pos = ftello(afp);
458             read_size = extract_one(afp, &hdr);
459             if (read_size != hdr.packed_size) {
460                 /* when error occurred in extract_one(), should adjust
461                    point of file stream */
462                 if (pos != -1 && afp != stdin)
463                     fseeko(afp, pos + hdr.packed_size - read_size, SEEK_SET);
464                 else {
465                     size_t i = hdr.packed_size - read_size;
466                     while (i--) fgetc(afp);
467                 }
468             }
469         } else {
470             if (afp != stdin)
471                 fseeko(afp, hdr.packed_size, SEEK_CUR);
472             else {
473                 size_t i = hdr.packed_size;
474                 while (i--) fgetc(afp);
475             }
476         }
477     }
478
479     /* close archive file */
480     fclose(afp);
481
482     /* adjust directory information */
483     adjust_dirinfo();
484
485     return;
486 }
487
488 int
489 is_directory_traversal(char *string)
490 {
491     unsigned int type = 0; /* 0 = new, 1 = only dots, 2 = other chars than dots */
492     char *temp;
493
494     temp = string;
495
496     while (*temp != 0) {
497         if (temp[0] == '/') {
498             if (type == 1) { return 1; }
499             type = 0;
500             temp++;
501             continue;
502         }
503
504         if ((temp[0] == '.') && (type < 2))
505             type = 1;
506         if (temp[0] != '.')
507             type = 2;
508
509         temp++;
510     } /* while */
511
512     return (type == 1);
513 }
514
515 /*
516  * restore directory information (time stamp).
517  * added by A.Iriyama  2003.12.12
518  */
519
520 typedef struct lhdDirectoryInfo_t {
521     struct lhdDirectoryInfo_t *next;
522     LzHeader hdr;
523 } LzHeaderList;
524
525 static LzHeaderList *dirinfo;
526
527 static void add_dirinfo(char *name, LzHeader *hdr)
528 {
529     LzHeaderList *p;
530
531     if (memcmp(hdr->method, LZHDIRS_METHOD, 5) != 0)
532         return;
533
534     p = xmalloc(sizeof(LzHeaderList));
535
536     memcpy(&p->hdr, hdr, sizeof(LzHeader));
537     strncpy(p->hdr.name, name, sizeof(p->hdr.name));
538     p->hdr.name[sizeof(p->hdr.name)-1] = 0;
539
540     {
541         LzHeaderList *tmp = dirinfo;
542         dirinfo = p;
543         dirinfo->next = tmp;
544     }
545 }
546
547 static void adjust_dirinfo()
548 {
549     while (dirinfo) {
550         adjust_info(dirinfo->hdr.name, &dirinfo->hdr);
551
552         {
553             LzHeaderList *tmp = dirinfo;
554             dirinfo = dirinfo->next;
555             free(tmp);
556         }
557     }
558 }