1 /*
2
3 Copyright (c) 2011, 2012, Simon Howard
4
5 Permission to use, copy, modify, and/or distribute this software
6 for any purpose with or without fee is hereby granted, provided
7 that the above copyright notice and this permission notice appear
8 in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
14 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
17 CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "lha_arch.h"
26 #include "lha_decoder.h"
27 #include "lha_basic_reader.h"
28 #include "public/lha_reader.h"
29 #include "macbinary.h"
30
31 typedef enum {
32
33 // Initial state at start of stream:
34
35 CURR_FILE_START,
36
37 // Current file is a "normal" file (or directory) read from
38 // the input stream.
39
40 CURR_FILE_NORMAL,
41
42 // Current file is a directory that has been popped from the
43 // directory stack.
44
45 CURR_FILE_FAKE_DIR,
46
47 // Current file is a deferred symbolic link that has been left
48 // to the end of the input stream to be created.
49
50 CURR_FILE_DEFERRED_SYMLINK,
51
52 // End of input stream has been reached.
53
54 CURR_FILE_EOF,
55 } CurrFileType;
56
57 struct _LHAReader {
58 LHABasicReader *reader;
59
60 // The current file that we are processing (last file returned
61 // by lha_reader_next_file).
62
63 LHAFileHeader *curr_file;
64 CurrFileType curr_file_type;
65
66 // Pointer to decoder being used to decompress the current file,
67 // or NULL if we have not yet started decompression.
68
69 LHADecoder *decoder;
70
71 // Pointer to "inner" decoder. Most of the time,
72 // decoder == inner_decoder, but when decoding an archive
73 // generated by MacLHA, inner_decoder points to the actual
74 // decompressor.
75
76 LHADecoder *inner_decoder;
77
78 // Policy used to extract directories.
79
80 LHAReaderDirPolicy dir_policy;
81
82 // Directories that have been created by lha_reader_extract but
83 // have not yet had their metadata set. This is a linked list
84 // using the _next field in LHAFileHeader.
85 // In the case of LHA_READER_DIR_END_OF_DIR this is a stack;
86 // in the case of LHA_READER_DIR_END_OF_FILE it is a list.
87
88 LHAFileHeader *dir_stack;
89
90 // Symbolic links containing absolute paths or '..' are not
91 // created immediately - instead, "placeholder" files are created
92 // in their place, and the symbolic links created at the end
93 // of extraction.
94
95 LHAFileHeader *deferred_symlinks;
96 };
97
98 /**
99 * Free the current decoder structure.
100 *
101 * If the reader has an allocated decoder being used to decompress the
102 * current file, the decoder is freed and the decoder pointer reset
103 * to NULL.
104 *
105 * @param reader Pointer to the LHA reader structure.
106 */
107
close_decoder(LHAReader * reader)108 static void close_decoder(LHAReader *reader)
109 {
110 if (reader->decoder != NULL) {
111 if (reader->inner_decoder == reader->decoder) {
112 reader->inner_decoder = NULL;
113 }
114
115 lha_decoder_free(reader->decoder);
116 reader->decoder = NULL;
117 }
118
119 if (reader->inner_decoder != NULL) {
120 lha_decoder_free(reader->inner_decoder);
121 reader->inner_decoder = NULL;
122 }
123 }
124
125 /**
126 * Create the decoder structure to decompress the data from the
127 * current file.
128 *
129 * @param reader Pointer to the LHA reader structure.
130 * @param callback Callback function to invoke to track progress.
131 * @param callback_data Extra pointer to pass to the callback function.
132 * @return Non-zero for success, zero for failure.
133 */
134
open_decoder(LHAReader * reader,LHADecoderProgressCallback callback,void * callback_data)135 static int open_decoder(LHAReader *reader,
136 LHADecoderProgressCallback callback,
137 void *callback_data)
138 {
139 // Can only read from a normal file.
140
141 if (reader->curr_file_type != CURR_FILE_NORMAL) {
142 return 0;
143 }
144
145 reader->inner_decoder = lha_basic_reader_decode(reader->reader);
146
147 if (reader->inner_decoder == NULL) {
148 return 0;
149 }
150
151 // Set progress callback for decoder.
152
153 if (callback != NULL) {
154 lha_decoder_monitor(reader->inner_decoder,
155 callback, callback_data);
156 }
157
158 // Some archives generated by MacLHA have a MacBinary header
159 // attached to the start, which contains MacOS-specific
160 // metadata about the compressed file. These are identified
161 // and stripped off, using a "passthrough" decoder.
162
163 if (reader->curr_file->os_type == LHA_OS_TYPE_MACOS) {
164 reader->decoder = lha_macbinary_passthrough(
165 reader->inner_decoder, reader->curr_file);
166
167 if (reader->decoder == NULL) {
168 return 0;
169 }
170 } else {
171 reader->decoder = reader->inner_decoder;
172 }
173
174 return 1;
175 }
176
lha_reader_new(LHAInputStream * stream)177 LHAReader *lha_reader_new(LHAInputStream *stream)
178 {
179 LHABasicReader *basic_reader;
180 LHAReader *reader;
181
182 reader = calloc(1, sizeof(LHAReader));
183
184 if (reader == NULL) {
185 return NULL;
186 }
187
188 basic_reader = lha_basic_reader_new(stream);
189
190 if (basic_reader == NULL) {
191 free(reader);
192 return NULL;
193 }
194
195 reader->reader = basic_reader;
196 reader->curr_file = NULL;
197 reader->curr_file_type = CURR_FILE_START;
198 reader->decoder = NULL;
199 reader->inner_decoder = NULL;
200 reader->dir_stack = NULL;
201 reader->dir_policy = LHA_READER_DIR_END_OF_DIR;
202 reader->deferred_symlinks = NULL;
203
204 return reader;
205 }
206
lha_reader_free(LHAReader * reader)207 void lha_reader_free(LHAReader *reader)
208 {
209 LHAFileHeader *header;
210
211 // Shut down the current decoder, if there is one.
212
213 close_decoder(reader);
214
215 // Free any file headers in the stack.
216
217 while (reader->dir_stack != NULL) {
218 header = reader->dir_stack;
219 reader->dir_stack = header->_next;
220 lha_file_header_free(header);
221 }
222
223 lha_basic_reader_free(reader->reader);
224 free(reader);
225 }
226
lha_reader_set_dir_policy(LHAReader * reader,LHAReaderDirPolicy policy)227 void lha_reader_set_dir_policy(LHAReader *reader,
228 LHAReaderDirPolicy policy)
229 {
230 reader->dir_policy = policy;
231 }
232
233 /**
234 * Check if the directory at the top of the stack should be popped.
235 *
236 * Extracting a directory is a two stage process; after the directory
237 * is created, it is pushed onto the directory stack. Later the
238 * directory must be popped off the stack and its metadata applied.
239 *
240 * @param reader Pointer to the LHA reader structure.
241 * @return Non-zero if there is a directory at the top of
242 * the stack that should be popped.
243 */
244
end_of_top_dir(LHAReader * reader)245 static int end_of_top_dir(LHAReader *reader)
246 {
247 LHAFileHeader *input;
248
249 // No directories to pop?
250
251 if (reader->dir_stack == NULL) {
252 return 0;
253 }
254
255 // Once the end of the input stream is reached, all that is
256 // left to do is pop off the remaining directories.
257
258 input = lha_basic_reader_curr_file(reader->reader);
259
260 if (input == NULL) {
261 return 1;
262 }
263
264 switch (reader->dir_policy) {
265
266 // Shouldn't happen?
267
268 case LHA_READER_DIR_PLAIN:
269 default:
270 return 1;
271
272 // Don't process directories until we reach the end of
273 // the input stream.
274
275 case LHA_READER_DIR_END_OF_FILE:
276 return 0;
277
278 // Once we reach a file from the input that is not within
279 // the directory at the top of the stack, we have reached
280 // the end of that directory, so we can pop it off.
281
282 case LHA_READER_DIR_END_OF_DIR:
283 return input->path == NULL
284 || strncmp(input->path,
285 reader->dir_stack->path,
286 strlen(reader->dir_stack->path)) != 0;
287 }
288 }
289
290 // Read the next file from the input stream.
291
lha_reader_next_file(LHAReader * reader)292 LHAFileHeader *lha_reader_next_file(LHAReader *reader)
293 {
294 // Free the current decoder if there is one.
295
296 close_decoder(reader);
297
298 // No point continuing once the end of the input stream has
299 // been reached.
300
301 if (reader->curr_file_type == CURR_FILE_EOF) {
302 return NULL;
303 }
304
305 // Advance to the next file from the input stream?
306 // Don't advance until we've done the fake directories first.
307
308 if (reader->curr_file_type == CURR_FILE_START
309 || reader->curr_file_type == CURR_FILE_NORMAL) {
310 lha_basic_reader_next_file(reader->reader);
311 }
312
313 // If the last file we returned was a 'fake' directory, we must
314 // now unreference it.
315
316 if (reader->curr_file_type == CURR_FILE_FAKE_DIR) {
317 lha_file_header_free(reader->curr_file);
318 }
319
320 // Pop off all appropriate directories from the stack first.
321
322 if (end_of_top_dir(reader)) {
323 reader->curr_file = reader->dir_stack;
324 reader->dir_stack = reader->dir_stack->_next;
325 reader->curr_file_type = CURR_FILE_FAKE_DIR;
326 } else {
327 reader->curr_file = lha_basic_reader_curr_file(reader->reader);
328 reader->curr_file_type = CURR_FILE_NORMAL;
329 }
330
331 // Once we reach the end of the file, there may be deferred
332 // symbolic links still to extract, so process those before
333 // giving up and declaring end of file.
334
335 if (reader->curr_file == NULL) {
336 if (reader->deferred_symlinks != NULL) {
337 reader->curr_file = reader->deferred_symlinks;
338 reader->curr_file_type = CURR_FILE_DEFERRED_SYMLINK;
339
340 reader->deferred_symlinks =
341 reader->deferred_symlinks->_next;
342 reader->curr_file->_next = NULL;
343 } else {
344 reader->curr_file_type = CURR_FILE_EOF;
345 }
346 }
347
348 return reader->curr_file;
349 }
350
lha_reader_read(LHAReader * reader,void * buf,size_t buf_len)351 size_t lha_reader_read(LHAReader *reader, void *buf, size_t buf_len)
352 {
353 // The first time that we try to read the current file, we
354 // must create the decoder to decompress it.
355
356 if (reader->decoder == NULL) {
357 if (!open_decoder(reader, NULL, NULL)) {
358 return 0;
359 }
360 }
361
362 // Read from decoder and return the result.
363
364 return lha_decoder_read(reader->decoder, buf, buf_len);
365 }
366
367 /**
368 * Decompress the current file.
369 *
370 * Assumes that @param open_decoder has already been called to
371 * start the decode process.
372 *
373 * @param reader Pointer to the LHA reader structure.
374 * @param output FILE handle to write decompressed data, or NULL
375 * if the decompressed data should be discarded.
376 * @return Non-zero if the file decompressed successfully.
377 */
378
do_decode(LHAReader * reader,FILE * output)379 static int do_decode(LHAReader *reader, FILE *output)
380 {
381 uint8_t buf[64];
382 unsigned int bytes;
383
384 // Decompress the current file.
385
386 do {
387 bytes = lha_reader_read(reader, buf, sizeof(buf));
388
389 if (output != NULL) {
390 if (fwrite(buf, 1, bytes, output) < bytes) {
391 return 0;
392 }
393 }
394
395 } while (bytes > 0);
396
397 // Decoder stores output position and performs running CRC.
398 // At the end of the stream these should match the header values.
399
400 return lha_decoder_get_length(reader->inner_decoder)
401 == reader->curr_file->length
402 && lha_decoder_get_crc(reader->inner_decoder)
403 == reader->curr_file->crc;
404 }
405
lha_reader_check(LHAReader * reader,LHADecoderProgressCallback callback,void * callback_data)406 int lha_reader_check(LHAReader *reader,
407 LHADecoderProgressCallback callback,
408 void *callback_data)
409 {
410 if (reader->curr_file_type != CURR_FILE_NORMAL) {
411 return 0;
412 }
413
414 // CRC checking of directories is not necessary.
415
416 if (!strcmp(reader->curr_file->compress_method,
417 LHA_COMPRESS_TYPE_DIR)) {
418 return 1;
419 }
420
421 // Decode file.
422
423 return open_decoder(reader, callback, callback_data)
424 && do_decode(reader, NULL);
425 }
426
427 /**
428 * Open an output stream into which to decompress the current file.
429 *
430 * @param reader Pointer to the LHA reader structure.
431 * @param filename Name of the file to open.
432 * @return FILE handle of the opened file, or NULL in
433 * case of failure.
434 */
435
open_output_file(LHAReader * reader,char * filename)436 static FILE *open_output_file(LHAReader *reader, char *filename)
437 {
438 int unix_uid = -1, unix_gid = -1, unix_perms = -1;
439
440 if (LHA_FILE_HAVE_EXTRA(reader->curr_file, LHA_FILE_UNIX_UID_GID)) {
441 unix_uid = reader->curr_file->unix_uid;
442 unix_gid = reader->curr_file->unix_gid;
443 }
444
445 if (LHA_FILE_HAVE_EXTRA(reader->curr_file, LHA_FILE_UNIX_PERMS)) {
446 unix_perms = reader->curr_file->unix_perms;
447 }
448
449 return lha_arch_fopen(filename, unix_uid, unix_gid, unix_perms);
450 }
451
452 /**
453 * Set file timestamps for the specified file.
454 *
455 * If possible, the more accurate Windows timestamp values are used;
456 * otherwise normal Unix timestamps are used.
457 *
458 * @param path Path to the file or directory to set.
459 * @param header Pointer to file header structure containing the
460 * timestamps to set.
461 * @return Non-zero if the timestamps were set successfully,
462 * or zero for failure.
463 */
464
set_timestamps_from_header(char * path,LHAFileHeader * header)465 static int set_timestamps_from_header(char *path, LHAFileHeader *header)
466 {
467 #if LHA_ARCH == LHA_ARCH_WINDOWS
468 if (LHA_FILE_HAVE_EXTRA(header, LHA_FILE_WINDOWS_TIMESTAMPS)) {
469 return lha_arch_set_windows_timestamps(
470 path,
471 header->win_creation_time,
472 header->win_modification_time,
473 header->win_access_time
474 );
475 } else // ....
476 #endif
477 if (header->timestamp != 0) {
478 return lha_arch_utime(path, header->timestamp);
479 } else {
480 return 1;
481 }
482 }
483
484 /**
485 * Set directory metadata.
486 *
487 * This is the second stage of directory extraction. Metadata (timestamps
488 * and permissions) should be set on a dictory after the contents of
489 * the directory has been extracted.
490 *
491 * @param header Pointer to file header structure containing the
492 * metadata to set.
493 * @param path Path to the directory on which to set the metadata.
494 * @return Non-zero for success, or zero for failure.
495 */
496
set_directory_metadata(LHAFileHeader * header,char * path)497 static int set_directory_metadata(LHAFileHeader *header, char *path)
498 {
499 // Set timestamp:
500
501 set_timestamps_from_header(path, header);
502
503 // Set owner and group:
504
505 if (LHA_FILE_HAVE_EXTRA(header, LHA_FILE_UNIX_UID_GID)) {
506 if (!lha_arch_chown(path, header->unix_uid,
507 header->unix_gid)) {
508 // On most Unix systems, only root can change
509 // ownership. But if we can't change ownership,
510 // it isn't a fatal error. Ignore the failure
511 // and continue.
512
513 // TODO: Implement some kind of alternate handling
514 // here?
515 /* return 0; */
516 }
517 }
518
519 // Set permissions on directory:
520
521 if (LHA_FILE_HAVE_EXTRA(header, LHA_FILE_UNIX_PERMS)) {
522 if (!lha_arch_chmod(path, header->unix_perms)) {
523 return 0;
524 }
525 }
526
527 return 1;
528 }
529
530 /**
531 * "Extract" (create) a directory.
532 *
533 * The current file is assumed to be a directory. This is the first
534 * stage in extracting a directory; after the directory is created,
535 * it is added to the directory stack so that the metadata apply stage
536 * runs later. (If the LHA_READER_DIR_PLAIN policy is used, metadata
537 * is just applied now).
538 *
539 * @param reader Pointer to the LHA reader structure.
540 * @param path Path to the directory, or NULL to use the path from
541 * the file header.
542 * @return Non-zero for success, or zero for failure.
543 */
544
extract_directory(LHAReader * reader,char * path)545 static int extract_directory(LHAReader *reader, char *path)
546 {
547 LHAFileHeader *header;
548 unsigned int mode;
549
550 header = reader->curr_file;
551
552 // If path is not specified, use the path from the file header.
553
554 if (path == NULL) {
555 path = header->path;
556 }
557
558 // Create directory. If there are permissions to be set, create
559 // the directory with minimal permissions limited to the running
560 // user. Otherwise use the default umask.
561
562 if (LHA_FILE_HAVE_EXTRA(header, LHA_FILE_UNIX_PERMS)) {
563 mode = 0700;
564 } else {
565 mode = 0777;
566 }
567
568 if (!lha_arch_mkdir(path, mode)) {
569
570 // If the attempt to create the directory failed, it may
571 // be because the directory already exists. Return success
572 // if this is the case; it isn't really an error.
573
574 return lha_arch_exists(path) == LHA_FILE_DIRECTORY;
575 }
576
577 // The directory has been created, but the metadata has not yet
578 // been applied. It depends on the directory policy how this
579 // is handled. If we are using LHA_READER_DIR_PLAIN, set
580 // metadata now. Otherwise, save the directory for later.
581
582 if (reader->dir_policy == LHA_READER_DIR_PLAIN) {
583 set_directory_metadata(header, path);
584 } else {
585 lha_file_header_add_ref(header);
586 header->_next = reader->dir_stack;
587 reader->dir_stack = header;
588 }
589
590 return 1;
591 }
592
593 /**
594 * Extract the current file.
595 *
596 * @param reader Pointer to the LHA reader structure.
597 * @param filename Filename into which to extract the file, or NULL
598 * to use the filename from the file header.
599 * @param callback Callback function to invoke to track progress.
600 * @param callback_data Extra pointer to pass to the callback function.
601 * @return Non-zero if the file was successfully extracted,
602 * or zero for failure.
603 */
604
extract_file(LHAReader * reader,char * filename,LHADecoderProgressCallback callback,void * callback_data)605 static int extract_file(LHAReader *reader, char *filename,
606 LHADecoderProgressCallback callback,
607 void *callback_data)
608 {
609 FILE *fstream;
610 char *tmp_filename = NULL;
611 int result;
612
613 // Construct filename?
614
615 if (filename == NULL) {
616 tmp_filename = lha_file_header_full_path(reader->curr_file);
617
618 if (tmp_filename == NULL) {
619 return 0;
620 }
621
622 filename = tmp_filename;
623 }
624
625 // Create decoder. If the file cannot be created, there is no
626 // need to even create an output file. If successful, open the
627 // output file and decode.
628
629 result = 0;
630
631 if (open_decoder(reader, callback, callback_data)) {
632
633 fstream = open_output_file(reader, filename);
634
635 if (fstream != NULL) {
636 result = do_decode(reader, fstream);
637 fclose(fstream);
638 }
639 }
640
641 // Set timestamp on file:
642
643 if (result) {
644 set_timestamps_from_header(filename, reader->curr_file);
645 }
646
647 free(tmp_filename);
648
649 return result;
650 }
651
652 /**
653 * Determine whether a header contains a "dangerous" symbolic link.
654 *
655 * Symbolic links that begin with '/' or contain '..' as a path are
656 * Potentially dangerous and could potentially be used to overwrite
657 * arbitrary files on the filesystem. They therefore need to be
658 * treated specially.
659 *
660 * @param header Pointer to a header structure defining a symbolic
661 * link.
662 * @return Non-zero if the symbolic link is potentially
663 * dangerous.
664 */
665
is_dangerous_symlink(LHAFileHeader * header)666 static int is_dangerous_symlink(LHAFileHeader *header)
667 {
668 char *path_start;
669 char *p;
670
671 if (header->symlink_target == NULL) {
672 return 0;
673 }
674
675 // Absolute path symlinks could be used to point to arbitrary
676 // filesystem locations.
677
678 if (header->symlink_target[0] == '/') {
679 return 1;
680 }
681
682 // Check for paths containing '..'.
683
684 path_start = header->symlink_target;
685
686 for (p = header->symlink_target; *p != '\0'; ++p) {
687 if (*p == '/') {
688 if ((p - path_start) == 2
689 && path_start[0] == '.' && path_start[1] == '.') {
690 return 1;
691 }
692
693 path_start = p + 1;
694 }
695 }
696
697 // The path might also end with '..' (no terminating /)
698
699 if ((p - path_start) == 2
700 && path_start[0] == '.' && path_start[1] == '.') {
701 return 1;
702 }
703
704 return 0;
705 }
706
707 /**
708 * Get the length of a path defined by a file header.
709 *
710 * @param header The file header structure.
711 * @return Length of the header in bytes.
712 */
713
file_header_path_len(LHAFileHeader * header)714 static size_t file_header_path_len(LHAFileHeader *header)
715 {
716 size_t result;
717
718 result = 0;
719
720 if (header->path != NULL) {
721 result += strlen(header->path);
722 }
723 if (header->filename != NULL) {
724 result += strlen(header->filename);
725 }
726
727 return result;
728 }
729
730 /**
731 * Create a "placeholder" symbolic link.
732 *
733 * When a "dangerous" symbolic link is extracted, instead of creating it
734 * immediately, create a "placeholder" empty file to go in its place, and
735 * place it into the deferred_symlinks list to be created later.
736 *
737 * @param reader Pointer to the LHA reader structure.
738 * @param filename Filename into which to extract the symlink.
739 * @return Non-zero if the symlink was extracted successfully,
740 * or zero for failure.
741 */
742
extract_placeholder_symlink(LHAReader * reader,char * filename)743 static int extract_placeholder_symlink(LHAReader *reader, char *filename)
744 {
745 LHAFileHeader **rover;
746 FILE *f;
747
748 f = lha_arch_fopen(filename, -1, -1, 0600);
749
750 if (f == NULL) {
751 return 0;
752 }
753
754 fclose(f);
755
756 // Insert this header into the list of deferred symbolic links.
757 // The list must be maintained in order of decreasing path length,
758 // so that one symbolic link cannot depend on another. For example:
759 //
760 // etc -> /etc
761 // etc/passwd -> /malicious_path/passwd
762
763 rover = &reader->deferred_symlinks;
764
765 while (*rover != NULL
766 && file_header_path_len(*rover)
767 > file_header_path_len(reader->curr_file)) {
768 rover = &(*rover)->_next;
769 }
770
771 reader->curr_file->_next = *rover;
772 *rover = reader->curr_file;
773
774 // Save reference to the header so it won't be freed.
775
776 lha_file_header_add_ref(reader->curr_file);
777
778 return 1;
779 }
780
781 /**
782 * Extract a Unix symbolic link.
783 *
784 * @param reader Pointer to the LHA reader structure.
785 * @param filename Filename into which to extract the symlink, or NULL
786 * to use the filename from the file header.
787 * @return Non-zero if the symlink was extracted successfully,
788 * or zero for failure.
789 */
790
extract_symlink(LHAReader * reader,char * filename)791 static int extract_symlink(LHAReader *reader, char *filename)
792 {
793 char *tmp_filename = NULL;
794 int result;
795
796 // Construct filename?
797
798 if (filename == NULL) {
799 tmp_filename = lha_file_header_full_path(reader->curr_file);
800
801 if (tmp_filename == NULL) {
802 return 0;
803 }
804
805 filename = tmp_filename;
806 }
807
808 if (reader->curr_file_type == CURR_FILE_NORMAL
809 && is_dangerous_symlink(reader->curr_file)) {
810 return extract_placeholder_symlink(reader, filename);
811 }
812
813 result = lha_arch_symlink(filename, reader->curr_file->symlink_target);
814
815 // TODO: Set symlink timestamp.
816
817 free(tmp_filename);
818
819 return result;
820 }
821
822 /**
823 * Extract a "normal" file.
824 *
825 * This just extracts the file header most recently read by the
826 * BasicReader.
827 *
828 * @param reader Pointer to the LHA reader structure.
829 * @param filename Filename into which to extract the file, or NULL
830 * to use the filename from the file header.
831 * @param callback Callback function to invoke to track progress.
832 * @param callback_data Extra pointer to pass to the callback function.
833 * @return Non-zero if the file was successfully extracted,
834 * or zero for failure.
835 */
836
extract_normal(LHAReader * reader,char * filename,LHADecoderProgressCallback callback,void * callback_data)837 static int extract_normal(LHAReader *reader,
838 char *filename,
839 LHADecoderProgressCallback callback,
840 void *callback_data)
841 {
842 if (strcmp(reader->curr_file->compress_method,
843 LHA_COMPRESS_TYPE_DIR) != 0) {
844 return extract_file(reader, filename, callback, callback_data);
845 } else if (reader->curr_file->symlink_target != NULL) {
846 return extract_symlink(reader, filename);
847 } else {
848 return extract_directory(reader, filename);
849 }
850 }
851
lha_reader_extract(LHAReader * reader,char * filename,LHADecoderProgressCallback callback,void * callback_data)852 int lha_reader_extract(LHAReader *reader,
853 char *filename,
854 LHADecoderProgressCallback callback,
855 void *callback_data)
856 {
857 switch (reader->curr_file_type) {
858
859 case CURR_FILE_NORMAL:
860 return extract_normal(reader, filename, callback,
861 callback_data);
862
863 case CURR_FILE_FAKE_DIR:
864 if (filename == NULL) {
865 filename = reader->curr_file->path;
866 }
867 set_directory_metadata(reader->curr_file, filename);
868 return 1;
869
870 case CURR_FILE_DEFERRED_SYMLINK:
871 return extract_symlink(reader, filename);
872
873 case CURR_FILE_START:
874 case CURR_FILE_EOF:
875 break;
876 }
877
878 return 0;
879 }
880
lha_reader_current_is_fake(LHAReader * reader)881 int lha_reader_current_is_fake(LHAReader *reader)
882 {
883 return reader->curr_file_type == CURR_FILE_FAKE_DIR
884 || reader->curr_file_type == CURR_FILE_DEFERRED_SYMLINK;
885 }
886
887