the path as last parameter.
libarchive/test/test_read_format_lha_withjunk.lzh.uu \
libarchive/test/test_read_format_mtree.mtree.uu \
libarchive/test/test_read_format_mtree_nomagic.mtree.uu \
+ libarchive/test/test_read_format_mtree_nomagic2.mtree.uu \
+ libarchive/test/test_read_format_mtree_nomagic3.mtree.uu \
libarchive/test/test_read_format_rar.rar.uu \
libarchive/test/test_read_format_rar_binary_data.rar.uu \
libarchive/test/test_read_format_rar_compress_best.rar.uu \
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* Copyright (c) 2008 Joerg Sonnenberger
- * Copyright (c) 2011 Michihiro NAKAJIMA
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
static int bid_keycmp(const char *, const char *, ssize_t);
static int cleanup(struct archive_read *);
+static int detect_form(struct archive_read *, int *);
static int mtree_bid(struct archive_read *, int);
static int parse_file(struct archive_read *, struct archive_entry *,
struct mtree *, struct mtree_entry *, int *);
* When "unset" is specified, expects a set of "<space characters>keyword".
*/
static int
-bid_keyword_list(const char *p, ssize_t len, int unset)
+bid_keyword_list(const char *p, ssize_t len, int unset, int last_is_path)
{
int l;
int keycnt = 0;
break;
if (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r'))
break;
- if (!blank) /* No blank character. */
+ if (!blank && !last_is_path) /* No blank character. */
return (-1);
+ if (last_is_path && len == 0)
+ return (keycnt);
if (unset) {
l = bid_keycmp(p, "all", len);
}
static int
-bid_entry(const char *p, ssize_t len)
+bid_entry(const char *p, ssize_t len, ssize_t nl, int *last_is_path)
{
int f = 0;
static const unsigned char safe_char[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
};
+ ssize_t ll = len;
+ const char *pp = p;
+ *last_is_path = 0;
/*
* Skip the path-name which is quoted.
*/
- while (len > 0 && *p != ' ' && *p != '\t') {
- if (!safe_char[*(const unsigned char *)p])
- return (-1);
- ++p;
- --len;
+ while (ll > 0 && *pp != ' ' && *pp != '\t') {
+ if (!safe_char[*(const unsigned char *)pp]) {
+ f = 0;
+ break;
+ }
+ ++pp;
+ --ll;
++f;
}
- /* If a path-name was not found, returns error. */
- if (f == 0)
- return (-1);
+ /* If a path-name was not found at the first, try to check
+ * a mtree format ``NetBSD's mtree -D'' creates, which
+ * places the path-name at the last. */
+ if (f == 0) {
+ const char *pb = p + len - nl;
+ int name_len = 0;
+
+ /* Do not accept multi lines for form D. */
+ if (pb-2 >= p &&
+ pb[-1] == '\\' && (pb[-2] == ' ' || pb[-2] == '\t'))
+ return (-1);
+ if (pb-1 >= p && pb[-1] == '\\')
+ return (-1);
- return (bid_keyword_list(p, len, 0));
+ while (p <= --pb && *pb != ' ' && *pb != '\t') {
+ if (!safe_char[*(const unsigned char *)pb])
+ return (-1);
+ name_len++;
+ }
+ if (name_len == 0)
+ return (-1);
+ ll = len - nl - name_len;
+ pp = p;
+ *last_is_path = 1;
+ }
+
+ return (bid_keyword_list(pp, ll, 0, *last_is_path));
}
#define MAX_BID_ENTRY 3
{
const char *signature = "#mtree";
const char *p;
- ssize_t avail, ravail;
- ssize_t len, nl;
- int detected_bytes = 0, entry_cnt = 0, multiline = 0;
(void)best_bid; /* UNUSED */
/* Now let's look at the actual header and see if it matches. */
- p = __archive_read_ahead(a, strlen(signature), &avail);
+ p = __archive_read_ahead(a, strlen(signature), NULL);
if (p == NULL)
return (-1);
/*
* There is not a mtree signature. Let's try to detect mtree format.
*/
+ return (detect_form(a, NULL));
+}
+
+static int
+detect_form(struct archive_read *a, int *is_form_d)
+{
+ const char *p;
+ ssize_t avail, ravail;
+ ssize_t len, nl;
+ int detected_bytes = 0, entry_cnt = 0, multiline = 0;
+ int form_D = 0;/* The archive is generated by `NetBSD mtree -D'
+ * (In this source we call it `form D') . */
+
+ if (is_form_d != NULL)
+ *is_form_d = 0;
+ p = __archive_read_ahead(a, 1, &avail);
+ if (p == NULL)
+ return (-1);
ravail = avail;
for (;;) {
len = next_line(a, &p, &avail, &ravail, &nl);
} else {
/* A continuance line; the terminal
* character of previous line was '\' character. */
- if (bid_keyword_list(p, len, 0) <= 0)
+ if (bid_keyword_list(p, len, 0, 0) <= 0)
break;
if (multiline == 1)
detected_bytes += len;
continue;
}
if (p[0] != '/') {
- if (bid_entry(p, len) >= 0) {
+ int last_is_path, keywords;
+
+ keywords = bid_entry(p, len, nl, &last_is_path);
+ if (keywords >= 0) {
detected_bytes += len;
- if (p[len-nl-1] == '\\')
+ if (form_D == 0) {
+ if (last_is_path)
+ form_D = 1;
+ else if (keywords > 0)
+ /* This line is not `form D'. */
+ form_D = -1;
+ } else if (form_D == 1) {
+ if (!last_is_path && keywords > 0)
+ /* This this is not `form D'
+ * and We cannot accept mixed
+ * format. */
+ break;
+ }
+ if (!last_is_path && p[len-nl-1] == '\\')
/* This line continues. */
multiline = 1;
else {
} else
break;
} else if (strncmp(p, "/set", 4) == 0) {
- if (bid_keyword_list(p+4, len-4, 0) <= 0)
+ if (bid_keyword_list(p+4, len-4, 0, 0) <= 0)
break;
/* This line continues. */
if (p[len-nl-1] == '\\')
multiline = 2;
} else if (strncmp(p, "/unset", 6) == 0) {
- if (bid_keyword_list(p+6, len-6, 1) <= 0)
+ if (bid_keyword_list(p+6, len-6, 1, 0) <= 0)
break;
/* This line continues. */
if (p[len-nl-1] == '\\')
p += len;
avail -= len;
}
- if (entry_cnt >= MAX_BID_ENTRY || (entry_cnt > 0 && len == 0))
+ if (entry_cnt >= MAX_BID_ENTRY || (entry_cnt > 0 && len == 0)) {
+ if (is_form_d != NULL) {
+ if (form_D == 1)
+ *is_form_d = 1;
+ }
return (32);
+ }
return (0);
}
static int
process_add_entry(struct archive_read *a, struct mtree *mtree,
- struct mtree_option **global, const char *line,
- struct mtree_entry **last_entry)
+ struct mtree_option **global, const char *line, ssize_t line_len,
+ struct mtree_entry **last_entry, int is_form_d)
{
struct mtree_entry *entry;
struct mtree_option *iter;
- const char *next, *eq;
+ const char *next, *eq, *name, *end;
size_t len;
int r;
(*last_entry)->next = entry;
*last_entry = entry;
- len = strcspn(line, " \t\r\n");
+ if (is_form_d) {
+ /*
+ * This form places the file name as last parameter.
+ */
+ name = line + line_len -1;
+ while (line_len > 0) {
+ if (*name != '\r' && *name != '\n' &&
+ *name != '\t' && *name != ' ')
+ break;
+ name--;
+ line_len--;
+ }
+ len = 0;
+ while (line_len > 0) {
+ if (*name == '\r' || *name == '\n' ||
+ *name == '\t' || *name == ' ') {
+ name++;
+ break;
+ }
+ name--;
+ line_len--;
+ len++;
+ }
+ end = name;
+ } else {
+ len = strcspn(line, " \t\r\n");
+ name = line;
+ line += len;
+ end = line + line_len;
+ }
+
if ((entry->name = malloc(len + 1)) == NULL) {
archive_set_error(&a->archive, errno, "Can't allocate memory");
return (ARCHIVE_FATAL);
}
- memcpy(entry->name, line, len);
+ memcpy(entry->name, name, len);
entry->name[len] = '\0';
parse_escapes(entry->name, entry);
- line += len;
for (iter = *global; iter != NULL; iter = iter->next) {
r = add_option(a, &entry->options, iter->value,
strlen(iter->value));
next = line + strspn(line, " \t\r\n");
if (*next == '\0')
return (ARCHIVE_OK);
+ if (next >= end)
+ return (ARCHIVE_OK);
line = next;
next = line + strcspn(line, " \t\r\n");
eq = strchr(line, '=');
char *p;
struct mtree_option *global;
struct mtree_entry *last_entry;
- int r;
+ int r, is_form_d;
mtree->archive_format = ARCHIVE_FORMAT_MTREE;
mtree->archive_format_name = "mtree";
global = NULL;
last_entry = NULL;
+ (void)detect_form(a, &is_form_d);
+
for (counter = 1; ; ++counter) {
len = readline(a, mtree, &p, 65536);
if (len == 0) {
if (*p == '\r' || *p == '\n' || *p == '\0')
continue;
if (*p != '/') {
- r = process_add_entry(a, mtree, &global, p,
- &last_entry);
+ r = process_add_entry(a, mtree, &global, p, len,
+ &last_entry, is_form_d);
} else if (strncmp(p, "/set", 4) == 0) {
if (p[4] != ' ' && p[4] != '\t')
break;
/*-
* Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
assertChdir("..");
}
+/*
+ * Test for a format that NetBSD mtree -C generates.
+ */
+static void
+test_read_format_mtree6(void)
+{
+ const char reffile[] = "test_read_format_mtree_nomagic2.mtree";
+ char buff[16];
+ struct archive_entry *ae;
+ struct archive *a;
+ FILE *f;
+
+ assertMakeDir("mtree6", 0777);
+ assertChdir("mtree6");
+
+ extract_reference_file(reffile);
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_file(a, reffile, 11));
+
+ /*
+ * Read "file", whose data is available on disk.
+ */
+ f = fopen("file", "wb");
+ assert(f != NULL);
+ assertEqualInt(3, fwrite("hi\n", 1, 3, f));
+ fclose(f);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualInt(archive_format(a), ARCHIVE_FORMAT_MTREE);
+ assertEqualString(archive_entry_pathname(ae), "./file");
+ assertEqualInt(archive_entry_uid(ae), 18);
+ assertEqualInt(AE_IFREG, archive_entry_filetype(ae));
+ assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0123);
+ assertEqualInt(archive_entry_size(ae), 3);
+ assertEqualInt(3, archive_read_data(a, buff, 3));
+ assertEqualMem(buff, "hi\n", 3);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "./dir");
+ assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "./dir/file with space");
+ assertEqualInt(archive_entry_uid(ae), 18);
+ assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "./file with space");
+ assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "./dir2");
+ assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "./dir2/dir3a");
+ assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755);
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualInt(6, archive_file_count(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+ assertChdir("..");
+}
+
+/*
+ * Test for a format that NetBSD mtree -D generates.
+ */
+static void
+test_read_format_mtree7(void)
+{
+ const char reffile[] = "test_read_format_mtree_nomagic3.mtree";
+ char buff[16];
+ struct archive_entry *ae;
+ struct archive *a;
+ FILE *f;
+
+ assertMakeDir("mtree7", 0777);
+ assertChdir("mtree7");
+
+ extract_reference_file(reffile);
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_file(a, reffile, 11));
+
+ /*
+ * Read "file", whose data is available on disk.
+ */
+ f = fopen("file", "wb");
+ assert(f != NULL);
+ assertEqualInt(3, fwrite("hi\n", 1, 3, f));
+ fclose(f);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualInt(archive_format(a), ARCHIVE_FORMAT_MTREE);
+ assertEqualString(archive_entry_pathname(ae), "./file");
+ assertEqualInt(archive_entry_uid(ae), 18);
+ assertEqualInt(AE_IFREG, archive_entry_filetype(ae));
+ assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0123);
+ assertEqualInt(archive_entry_size(ae), 3);
+ assertEqualInt(3, archive_read_data(a, buff, 3));
+ assertEqualMem(buff, "hi\n", 3);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "./dir");
+ assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "./dir/file with space");
+ assertEqualInt(archive_entry_uid(ae), 18);
+ assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "./file with space");
+ assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "./dir2");
+ assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "./dir2/dir3a");
+ assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755);
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualInt(6, archive_file_count(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+ assertChdir("..");
+}
+
DEFINE_TEST(test_read_format_mtree)
{
test_read_format_mtree1();
test_read_format_mtree3();
test_read_format_mtree4();
test_read_format_mtree5();
+ test_read_format_mtree6();
+ test_read_format_mtree7();
}
--- /dev/null
+begin 644 test_read_format_mtree_nomagic2.mtree
+M(PHC(%1H:7,@9F]R;2!I<R!G96YE<F%T960@8GD@3F5T0E-$(&UT<F5E("U#
+M"B,*+B]F:6QE('1Y<&4]9FEL92!U:60],3@@;6]D93TP,3(S('-I>F4],PHN
+M+V1I<B!T>7!E/61I<B!M;V1E/3`W-34*+B]D:7(O9FEL95PP-#!W:71H7#`T
+M,'-P86-E('1Y<&4]9FEL92!U:60],3@@;6]D93TP-C0T"BXO9FEL95PP-#!W
+M:71H7#`T,'-P86-E('1Y<&4]9FEL92!M;V1E/3`V-#0*+B]D:7(R('1Y<&4]
+M9&ER(&UO9&4],#<U-0HN+V1I<C(O9&ER,V$@='EP93UD:7(@;6]D93TP-S4U
+!"@``
+`
+end
--- /dev/null
+begin 644 test_read_format_mtree_nomagic3.mtree
+M(PHC(%1H:7,@9F]R;2!I<R!G96YE<F%T960@8GD@3F5T0E-$(&UT<F5E("U$
+M"B,*='EP93UF:6QE('5I9#TQ."!M;V1E/3`Q,C,@<VEZ93TS("XO9FEL90IT
+M>7!E/61I<B!M;V1E/3`W-34@+B]D:7(*='EP93UF:6QE(&UO9&4],#8T-"!U
+M:60],3@@+B]D:7(O9FEL95PP-#!W:71H7#`T,'-P86-E"G1Y<&4]9FEL92!M
+M;V1E/3`V-#0@+B]F:6QE7#`T,'=I=&A<,#0P<W!A8V4*='EP93UD:7(@;6]D
+M93TP-S4U("XO9&ER,@IT>7!E/61I<B!M;V1E/3`W-34@+B]D:7(R+V1I<C-A
+!"@``
+`
+end