OSDN Git Service

Avoid compile error on libapplefile
[lha/lha.git] / src / lhext.c
index 2d3d7f6..ba73f96 100644 (file)
@@ -22,6 +22,7 @@ static char    *methods[] =
     LZHUFF4_METHOD, LZHUFF5_METHOD, LZHUFF6_METHOD, LZHUFF7_METHOD,
     LARC_METHOD, LARC5_METHOD, LARC4_METHOD,
     LZHDIRS_METHOD,
+    PMARC0_METHOD, PMARC2_METHOD,
     NULL
 };
 
@@ -29,7 +30,7 @@ static void add_dirinfo(char* name, LzHeader* hdr);
 static void adjust_dirinfo();
 
 #ifdef HAVE_LIBAPPLEFILE
-static boolean decode_macbinary(FILE *ofp, size_t size, const char *outPath);
+static boolean decode_macbinary(FILE *ofp, off_t size, const char *outPath);
 #endif
 
 /* ------------------------------------------------------------------------ */
@@ -82,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)
@@ -232,8 +276,8 @@ extract_one(afp, hdr)
     }
     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 == '/') {
@@ -253,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;
         }
@@ -276,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;
             }
@@ -437,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);
@@ -483,7 +527,7 @@ extract_one(afp, hdr)
 #endif
             }
             else { /* make directory */
-                if (!output_to_stdout && !make_parent_path(name))
+                if (!make_parent_path(name))
                     return read_size;
                 /* save directory information */
                 add_dirinfo(name, hdr);
@@ -497,7 +541,7 @@ extract_one(afp, hdr)
             error("Unknown file type: \"%s\". use `f' option to force extract.", name);
     }
 
-    if (!output_to_stdout) {
+    if (!output_to_stdout && !verify_mode) {
         if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_DIRECTORY)
             adjust_info(name, hdr);
     }
@@ -505,6 +549,25 @@ extract_one(afp, 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                                                     */
 /* ------------------------------------------------------------------------ */
@@ -525,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 {
-                    off_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 {
-                off_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);
             }
         }
     }
@@ -600,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;
@@ -611,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 = &top; 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);
 
         {
@@ -641,7 +736,7 @@ decode_macbinary(ofp, size, outPath)
     af_file_t *afp = NULL;
     FILE *ifp = NULL;
     unsigned char *datap;
-    off_t dlen;
+    size_t dlen;
 
     if ((afp = af_open(temporary_name)) != NULL) {
         /* fetch datafork */