X-Git-Url: http://git.osdn.net/view?p=lha%2Flha.git;a=blobdiff_plain;f=src%2Flhext.c;h=ba73f969e13f9faf585aaddd41bb2f82d7427f0d;hp=7a4310999ce4598002915252c17d58726d34d8a5;hb=62e045aaf28eb72fe746540bc50a3f19369e7c3e;hpb=774d372aed28e32d86576c1c701503692a8eeaf4 diff --git a/src/lhext.c b/src/lhext.c index 7a43109..ba73f96 100644 --- a/src/lhext.c +++ b/src/lhext.c @@ -22,12 +22,17 @@ static char *methods[] = LZHUFF4_METHOD, LZHUFF5_METHOD, LZHUFF6_METHOD, LZHUFF7_METHOD, LARC_METHOD, LARC5_METHOD, LARC4_METHOD, LZHDIRS_METHOD, + PMARC0_METHOD, PMARC2_METHOD, NULL }; static void add_dirinfo(char* name, LzHeader* hdr); static void adjust_dirinfo(); +#ifdef HAVE_LIBAPPLEFILE +static boolean decode_macbinary(FILE *ofp, off_t size, const char *outPath); +#endif + /* ------------------------------------------------------------------------ */ static boolean inquire_extract(name) @@ -78,6 +83,49 @@ inquire_extract(name) return TRUE; } +static boolean +make_name_with_pathcheck(char *name, size_t namesz, const char *q) +{ + int offset = 0; + const char *p; + int sz; + struct stat stbuf; + + if (extract_directory) { + sz = xsnprintf(name, namesz, "%s/", extract_directory); + if (sz == -1) { + return FALSE; + } + offset += sz; + } + +#ifdef S_IFLNK + while ((p = strchr(q, '/')) != NULL) { + if (namesz - offset < (p - q) + 2) { + return FALSE; + } + memcpy(name + offset, q, (p - q)); + name[offset + (p - q)] = 0; + + offset += (p - q); + q = p + 1; + + if (lstat(name, &stbuf) < 0) { + name[offset++] = '/'; + break; + } + if (is_symlink(&stbuf)) { + return FALSE; + } + name[offset++] = '/'; + } +#endif + + str_safe_copy(name + offset, q, namesz - offset); + + return TRUE; +} + /* ------------------------------------------------------------------------ */ static boolean make_parent_path(name) @@ -166,12 +214,11 @@ adjust_info(name, hdr) if (hdr->extend_type == EXTEND_UNIX || hdr->extend_type == EXTEND_OS68K || hdr->extend_type == EXTEND_XOSK) { -#ifdef NOT_COMPATIBLE_MODE - Please need your modification in this space. -#else - if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK) + + if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK) { chmod(name, hdr->unix_mode); -#endif + } + if (!getuid()){ uid_t uid = hdr->unix_uid; gid_t gid = hdr->unix_gid; @@ -207,27 +254,30 @@ adjust_info(name, hdr) } /* ------------------------------------------------------------------------ */ -static size_t +static off_t extract_one(afp, hdr) FILE *afp; /* archive file */ LzHeader *hdr; { FILE *fp; /* output file */ +#if HAVE_LIBAPPLEFILE + FILE *tfp; /* temporary output file */ +#endif struct stat stbuf; char name[FILENAME_LENGTH]; unsigned int crc; int method; boolean save_quiet, save_verbose, up_flag; char *q = hdr->name, c; - size_t read_size = 0; + off_t read_size = 0; if (ignore_directory && strrchr(hdr->name, '/')) { q = (char *) strrchr(hdr->name, '/') + 1; } else { if (is_directory_traversal(q)) { - fprintf(stderr, "Possible directory traversal hack attempt in %s\n", q); - exit(111); + error("Possible directory traversal hack attempt in %s", q); + exit(1); } if (*q == '/') { @@ -247,16 +297,16 @@ extract_one(afp, hdr) } } - if (extract_directory) - xsnprintf(name, sizeof(name), "%s/%s", extract_directory, q); - else - str_safe_copy(name, q, sizeof(name)); + if (!make_name_with_pathcheck(name, sizeof(name), q)) { + error("Possible symlink traversal hack attempt in %s", q); + exit(1); + } - /* LZHDIRS_METHOD¤ò»ý¤Ä¥Ø¥Ã¥À¤ò¥Á¥§¥Ã¥¯¤¹¤ë */ + /* LZHDIRS_METHODを持つヘッダをチェックする */ /* 1999.4.30 t.okamoto */ for (method = 0;; method++) { if (methods[method] == NULL) { - error("Unknown method \"%.*s\"; \"%s\" will be skiped ...", + error("Unknown method \"%.*s\"; \"%s\" will be skipped ...", 5, hdr->method, name); return read_size; } @@ -270,7 +320,7 @@ extract_one(afp, hdr) #if 0 for (method = 0;; method++) { if (methods[method] == NULL) { - error("Unknown method \"%.*s\"; \"%s\" will be skiped ...", + error("Unknown method \"%.*s\"; \"%s\" will be skipped ...", 5, hdr->method, name); return read_size; } @@ -282,6 +332,14 @@ extract_one(afp, hdr) reading_filename = archive_name; writing_filename = name; if (output_to_stdout || verify_mode) { + /* "Icon\r" should be a resource fork file encoded in MacBinary + format, so that it should be skipped. */ + if (hdr->extend_type == EXTEND_MACOS + && strcmp(basename(name), "Icon\r") == 0 + && decode_macbinary_contents) { + return read_size; + } + if (noexec) { printf("%s %s\n", verify_mode ? "VERIFY" : "EXTRACT", name); return read_size; @@ -299,17 +357,37 @@ extract_one(afp, hdr) verbose = TRUE; } -#if __MINGW32__ +#if defined(__MINGW32__) || defined(__DJGPP__) { int old_mode; fflush(stdout); old_mode = setmode(fileno(stdout), O_BINARY); #endif +#if HAVE_LIBAPPLEFILE + /* On default, MacLHA encodes into MacBinary. */ + if (hdr->extend_type == EXTEND_MACOS && !verify_mode && decode_macbinary_contents) { + /* build temporary file */ + tfp = NULL; /* avoid compiler warnings `uninitialized' */ + tfp = build_temporary_file(); + + crc = decode_lzhuf(afp, tfp, + hdr->original_size, hdr->packed_size, + name, method, &read_size); + fclose(tfp); + decode_macbinary(stdout, hdr->original_size, name); + unlink(temporary_name); + } else { + crc = decode_lzhuf(afp, stdout, + hdr->original_size, hdr->packed_size, + name, method, &read_size); + } +#else crc = decode_lzhuf(afp, stdout, hdr->original_size, hdr->packed_size, name, method, &read_size); -#if __MINGW32__ +#endif /* HAVE_LIBAPPLEFILE */ +#if defined(__MINGW32__) || defined(__DJGPP__) fflush(stdout); setmode(fileno(stdout), old_mode); } @@ -318,6 +396,16 @@ extract_one(afp, hdr) verbose = save_verbose; } else { +#ifndef __APPLE__ + /* "Icon\r" should be a resource fork of parent folder's icon, + so that it can be skipped when system is not Mac OS X. */ + if (hdr->extend_type == EXTEND_MACOS + && strcmp(basename(name), "Icon\r") == 0 + && decode_macbinary_contents) { + make_parent_path(name); /* create directory only */ + return read_size; + } +#endif /* __APPLE__ */ if (skip_flg == FALSE) { up_flag = inquire_extract(name); if (up_flag == FALSE && force == FALSE) { @@ -347,9 +435,35 @@ extract_one(afp, hdr) remove_extracting_file_when_interrupt = TRUE; if ((fp = open_with_make_path(name)) != NULL) { +#if HAVE_LIBAPPLEFILE + if (hdr->extend_type == EXTEND_MACOS && !verify_mode && decode_macbinary_contents) { + /* build temporary file */ + tfp = NULL; /* avoid compiler warnings `uninitialized' */ + tfp = build_temporary_file(); + + crc = decode_lzhuf(afp, tfp, + hdr->original_size, hdr->packed_size, + name, method, &read_size); + fclose(tfp); + decode_macbinary(fp, hdr->original_size, name); +#ifdef __APPLE__ + /* TODO: set resource fork */ + /* after processing, "Icon\r" is not needed. */ + if (strcmp(basename(name), "Icon\r") == 0) { + unlink(name); + } +#endif /* __APPLE__ */ + unlink(temporary_name); + } else { + crc = decode_lzhuf(afp, fp, + hdr->original_size, hdr->packed_size, + name, method, &read_size); + } +#else /* HAVE_LIBAPPLEFILE */ crc = decode_lzhuf(afp, fp, hdr->original_size, hdr->packed_size, name, method, &read_size); +#endif /* HAVE_LIBAPPLEFILE */ fclose(fp); } remove_extracting_file_when_interrupt = FALSE; @@ -367,8 +481,8 @@ extract_one(afp, hdr) else if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_DIRECTORY || (hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK || method == LZHDIRS_METHOD_NUM) { - /* ¢¬¤³¤ì¤Ç¡¢Symbolic Link ¤Ï¡¢Âç¾æÉפ«¡© */ - if (!ignore_directory && !verify_mode) { + /* ↑これで、Symbolic Link は、大丈夫か? */ + if (!ignore_directory && !verify_mode && !output_to_stdout) { if (noexec) { if (quiet != TRUE) printf("EXTRACT %s (directory)\n", name); @@ -400,19 +514,20 @@ extract_one(afp, hdr) if (l_code < 0) { if (quiet != TRUE) warning("Can't make Symbolic Link \"%s\" -> \"%s\"", - hdr->realname, name); + name, hdr->realname); } if (quiet != TRUE) { message("Symbolic Link %s -> %s", - hdr->realname, name); + name, hdr->realname); } #else warning("Can't make Symbolic Link %s -> %s", - hdr->realname, name); + name, hdr->realname); return read_size; #endif - } else { /* make directory */ - if (!output_to_stdout && !make_parent_path(name)) + } + else { /* make directory */ + if (!make_parent_path(name)) return read_size; /* save directory information */ add_dirinfo(name, hdr); @@ -426,12 +541,33 @@ extract_one(afp, hdr) error("Unknown file type: \"%s\". use `f' option to force extract.", name); } - if (!output_to_stdout) - adjust_info(name, hdr); + if (!output_to_stdout && !verify_mode) { + if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_DIRECTORY) + adjust_info(name, hdr); + } return read_size; } +static int +skip_to_nextpos(FILE *fp, off_t pos, off_t off, off_t read_size) +{ + if (pos != -1) { + if (fseeko(fp, pos + off, SEEK_SET) != 0) { + return -1; + } + } + else { + off_t i = off - read_size; + while (i--) { + if (fgetc(fp) == EOF) { + return -1; + } + } + } + return 0; +} + /* ------------------------------------------------------------------------ */ /* EXTRACT COMMAND MAIN */ /* ------------------------------------------------------------------------ */ @@ -441,7 +577,7 @@ cmd_extract() LzHeader hdr; off_t pos; FILE *afp; - size_t read_size; + off_t read_size; /* open archive file */ if ((afp = open_old_archive()) == NULL) @@ -452,25 +588,19 @@ cmd_extract() /* extract each files */ while (get_header(afp, &hdr)) { + pos = ftello(afp); if (need_file(hdr.name)) { - pos = ftello(afp); read_size = extract_one(afp, &hdr); if (read_size != hdr.packed_size) { /* when error occurred in extract_one(), should adjust point of file stream */ - if (pos != -1 && afp != stdin) - fseeko(afp, pos + hdr.packed_size - read_size, SEEK_SET); - else { - size_t i = hdr.packed_size - read_size; - while (i--) fgetc(afp); + if (skip_to_nextpos(afp, pos, hdr.packed_size, read_size) == -1) { + fatal_error("Cannot seek to next header position from \"%s\"", hdr.name); } } } else { - if (afp != stdin) - fseeko(afp, hdr.packed_size, SEEK_CUR); - else { - size_t i = hdr.packed_size; - while (i--) fgetc(afp); + if (skip_to_nextpos(afp, pos, hdr.packed_size, 0) == -1) { + fatal_error("Cannot seek to next header position from \"%s\"", hdr.name); } } } @@ -514,12 +644,12 @@ is_directory_traversal(char *path) } /* - * restore directory information (time stamp). + * restore directory information (timestamp, permission and uid/gid). * added by A.Iriyama 2003.12.12 */ -typedef struct lhdDirectoryInfo_t { - struct lhdDirectoryInfo_t *next; +typedef struct LzHeaderList_t { + struct LzHeaderList_t *next; LzHeader hdr; } LzHeaderList; @@ -527,7 +657,7 @@ static LzHeaderList *dirinfo; static void add_dirinfo(char *name, LzHeader *hdr) { - LzHeaderList *p; + LzHeaderList *p, *tmp, top; if (memcmp(hdr->method, LZHDIRS_METHOD, 5) != 0) return; @@ -538,16 +668,54 @@ static void add_dirinfo(char *name, LzHeader *hdr) strncpy(p->hdr.name, name, sizeof(p->hdr.name)); p->hdr.name[sizeof(p->hdr.name)-1] = 0; +#if 0 + /* push front */ { - LzHeaderList *tmp = dirinfo; + tmp = dirinfo; dirinfo = p; dirinfo->next = tmp; } +#else + + /* + reverse sorted by pathname order + + p->hdr.name = "a" + + dirinfo->hdr.name = "a/b/d" + dirinfo->next->hdr.name = "a/b/c" + dirinfo->next->next->hdr.name = "a/b" + + result: + + dirinfo->hdr.name = "a/b/d" + dirinfo->next->hdr.name = "a/b/c" + dirinfo->next->next->hdr.name = "a/b" + dirinfo->next->next->next->hdr.name = "a" + */ + + top.next = dirinfo; + + for (tmp = ⊤ tmp->next; tmp = tmp->next) { + if (strcmp(p->hdr.name, tmp->next->hdr.name) > 0) { + p->next = tmp->next; + tmp->next = p; + break; + } + } + if (tmp->next == NULL) { + p->next = NULL; + tmp->next = p; + } + + dirinfo = top.next; +#endif } static void adjust_dirinfo() { while (dirinfo) { + /* message("adjusting [%s]", dirinfo->hdr.name); */ adjust_info(dirinfo->hdr.name, &dirinfo->hdr); { @@ -557,3 +725,36 @@ static void adjust_dirinfo() } } } + +#if HAVE_LIBAPPLEFILE +static boolean +decode_macbinary(ofp, size, outPath) + FILE *ofp; + off_t size; + const char *outPath; +{ + af_file_t *afp = NULL; + FILE *ifp = NULL; + unsigned char *datap; + size_t dlen; + + if ((afp = af_open(temporary_name)) != NULL) { + /* fetch datafork */ + datap = af_data(afp, &dlen); + fwrite(datap, sizeof(unsigned char), dlen, ofp); + af_close(afp); + return TRUE; + } else { /* it may be not encoded in MacBinary */ + /* try to copy */ + if ((ifp = fopen(temporary_name, READ_BINARY)) == NULL) { + error("Cannot open a temporary file \"%s\"", temporary_name); + return FALSE; + } + copyfile(ifp, ofp, size, 0, 0); + fclose(ifp); + return TRUE; + } + + return FALSE; +} +#endif /* HAVE_LIBAPPLEFILE */