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
inquire_extract(name)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
make_parent_path(name)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 *
open_with_make_path(name)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
adjust_info(name,hdr)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
extract_one(afp,hdr)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 name, hdr->realname);
404 }
405 if (quiet != TRUE) {
406 message("Symbolic Link %s -> %s",
407 name, hdr->realname);
408 }
409 #else
410 warning("Can't make Symbolic Link %s -> %s",
411 name, hdr->realname);
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
cmd_extract()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
is_directory_traversal(char * path)488 is_directory_traversal(char *path)
489 {
490 int state = 0;
491
492 for (; *path; path++) {
493 switch (state) {
494 case 0:
495 if (*path == '.') state = 1;
496 else state = 3;
497 break;
498 case 1:
499 if (*path == '.') state = 2;
500 else if (*path == '/') state = 0;
501 else state = 3;
502 break;
503 case 2:
504 if (*path == '/') return 1;
505 else state = 3;
506 break;
507 case 3:
508 if (*path == '/') state = 0;
509 break;
510 }
511 }
512
513 return state == 2;
514 }
515
516 /*
517 * restore directory information (time stamp).
518 * added by A.Iriyama 2003.12.12
519 */
520
521 typedef struct lhdDirectoryInfo_t {
522 struct lhdDirectoryInfo_t *next;
523 LzHeader hdr;
524 } LzHeaderList;
525
526 static LzHeaderList *dirinfo;
527
add_dirinfo(char * name,LzHeader * hdr)528 static void add_dirinfo(char *name, LzHeader *hdr)
529 {
530 LzHeaderList *p;
531
532 if (memcmp(hdr->method, LZHDIRS_METHOD, 5) != 0)
533 return;
534
535 p = xmalloc(sizeof(LzHeaderList));
536
537 memcpy(&p->hdr, hdr, sizeof(LzHeader));
538 strncpy(p->hdr.name, name, sizeof(p->hdr.name));
539 p->hdr.name[sizeof(p->hdr.name)-1] = 0;
540
541 {
542 LzHeaderList *tmp = dirinfo;
543 dirinfo = p;
544 dirinfo->next = tmp;
545 }
546 }
547
adjust_dirinfo()548 static void adjust_dirinfo()
549 {
550 while (dirinfo) {
551 adjust_info(dirinfo->hdr.name, &dirinfo->hdr);
552
553 {
554 LzHeaderList *tmp = dirinfo;
555 dirinfo = dirinfo->next;
556 free(tmp);
557 }
558 }
559 }
560