]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Do not treat user or group ACL entries without an id as identical
authorMartin Matuska <martin@matuska.org>
Mon, 14 Nov 2016 21:09:54 +0000 (22:09 +0100)
committerMartin Matuska <martin@matuska.org>
Mon, 14 Nov 2016 21:32:26 +0000 (22:32 +0100)
Add compat test for star archives with POSIX.1e ACLs

Makefile.am
libarchive/archive_acl.c
libarchive/test/CMakeLists.txt
libarchive/test/test_compat_star_acl_posix1e.c [new file with mode: 0644]
libarchive/test/test_compat_star_acl_posix1e.tar.uu [new file with mode: 0644]

index 137bd0affab456a89dd0c96bbac7b1e144a2b1da..441bdbb9e17b069c5cae41e9b1dbc357e11c1185 100644 (file)
@@ -372,6 +372,7 @@ libarchive_test_SOURCES= \
        libarchive/test/test_compat_pax_libarchive_2x.c \
        libarchive/test/test_compat_solaris_tar_acl.c \
        libarchive/test/test_compat_solaris_pax_sparse.c \
+       libarchive/test/test_compat_star_acl_posix1e.c \
        libarchive/test/test_compat_tar_hardlink.c \
        libarchive/test/test_compat_uudecode.c \
        libarchive/test/test_compat_uudecode_large.c \
@@ -626,6 +627,7 @@ libarchive_test_EXTRA_DIST=\
        libarchive/test/test_compat_solaris_pax_sparse_1.pax.Z.uu \
        libarchive/test/test_compat_solaris_pax_sparse_2.pax.Z.uu \
        libarchive/test/test_compat_solaris_tar_acl.tar.uu \
+       libarchive/test/test_compat_star_acl_posix1e.tar.uu \
        libarchive/test/test_compat_tar_hardlink_1.tar.uu \
        libarchive/test/test_compat_uudecode_large.tar.Z.uu \
        libarchive/test/test_compat_xz_1.txz.uu \
index d128920a5d913743bcbac387149431f448d26ce6..ef6399957e3ab7eb99271cc8b721596a381a9456 100644 (file)
@@ -284,8 +284,11 @@ acl_new_entry(struct archive_acl *acl,
        aq = NULL;
        while (ap != NULL) {
                if (ap->type == type && ap->tag == tag && ap->id == id) {
-                       ap->permset = permset;
-                       return (ap);
+                       if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
+                           tag != ARCHIVE_ENTRY_ACL_GROUP)) {
+                               ap->permset = permset;
+                               return (ap);
+                       }
                }
                aq = ap;
                ap = ap->next;
index 1cb21f9c6b7176c987542af4ff0322796310cc57..9d2622b0cda5aa0b933629d686b8336474729694 100644 (file)
@@ -60,6 +60,7 @@ IF(ENABLE_TEST)
     test_compat_pax_libarchive_2x.c
     test_compat_solaris_pax_sparse.c
     test_compat_solaris_tar_acl.c
+    test_compat_star_acl_posix1e.c
     test_compat_tar_hardlink.c
     test_compat_uudecode.c
     test_compat_uudecode_large.c
diff --git a/libarchive/test/test_compat_star_acl_posix1e.c b/libarchive/test/test_compat_star_acl_posix1e.c
new file mode 100644 (file)
index 0000000..10bffd9
--- /dev/null
@@ -0,0 +1,215 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD$");
+
+/*
+ * Verify reading entries with POSIX.1e ACLs from archives created by star
+ *
+ * This should work on all systems, regardless of whether local filesystems
+ * support ACLs or not.
+ */
+
+struct acl_t {
+       int type;  /* Type of ACL: "access" or "default" */
+       int permset; /* Permissions for this class of users. */
+       int tag; /* Owner, User, Owning group, group, other, etc. */
+       const char *name; /* Name of user/group, depending on tag. */
+};
+
+static struct acl_t acls0[] = {
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_USER, "user77" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_MASK, ""},
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE,
+         ARCHIVE_ENTRY_ACL_OTHER, "" },
+};
+
+static struct acl_t acls1[] = {
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_USER, "user77" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0,
+         ARCHIVE_ENTRY_ACL_USER, "user78" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007,
+         ARCHIVE_ENTRY_ACL_GROUP, "group78" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007,
+         ARCHIVE_ENTRY_ACL_MASK, ""}, 
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_OTHER, "" },
+};
+
+static struct acl_t acls2[] = {
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_USER, "user77" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_GROUP, "group78" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_MASK, ""},
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_WRITE,
+         ARCHIVE_ENTRY_ACL_OTHER, "" },
+};
+
+static int
+acl_match(struct acl_t *acl, int type, int permset, int tag, const char *name)
+{
+       if (type != acl->type)
+               return (0);
+       if (permset != acl->permset)
+               return (0);
+       if (tag != acl->tag)
+               return (0);
+       if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
+               return (1);
+       if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ)
+               return (1);
+       if (tag == ARCHIVE_ENTRY_ACL_OTHER)
+               return (1);
+       if (tag == ARCHIVE_ENTRY_ACL_MASK)
+               return (1);
+       if (name == NULL)
+               return (acl->name == NULL || acl->name[0] == '\0');
+       if (acl->name == NULL)
+               return (name == NULL || name[0] == '\0');
+       return (0 == strcmp(name, acl->name));
+}
+
+static void
+compare_acls(struct archive_entry *ae, struct acl_t *acls, int n, int mode,
+    int want_type)
+{
+       int *marker = malloc(sizeof(marker[0]) * n);
+       int i;
+       int r;
+       int type, permset, tag, qual;
+       int matched;
+       const char *name;
+
+       for (i = 0; i < n; i++)
+               marker[i] = i;
+
+       while (0 == (r = archive_entry_acl_next(ae, want_type,
+                        &type, &permset, &tag, &qual, &name))) {
+               for (i = 0, matched = 0; i < n && !matched; i++) {
+                       if (acl_match(&acls[marker[i]], type, permset,
+                               tag, name)) {
+                               /* We found a match; remove it. */
+                               marker[i] = marker[n - 1];
+                               n--;
+                               matched = 1;
+                       }
+               }
+               if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) {
+                       if (!matched) printf("No match for user_obj perm\n");
+                       if (want_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+                               failure("USER_OBJ permset (%02o) != user mode (%02o)",
+                                   permset, 07 & (mode >> 6));
+                               assert((permset << 6) == (mode & 0700));
+                       }
+               } else if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) {
+                       if (!matched) printf("No match for group_obj perm\n");
+                       if (want_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+                               failure("GROUP_OBJ permset %02o != group mode %02o",
+                                   permset, 07 & (mode >> 3));
+                               assert((permset << 3) == (mode & 0070));
+                       }
+               } else if (tag == ARCHIVE_ENTRY_ACL_OTHER) {
+                       if (!matched) printf("No match for other perm\n");
+                       if (want_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+                               failure("OTHER permset (%02o) != other mode (%02o)",
+                                   permset, mode & 07);
+                               assert((permset << 0) == (mode & 0007));
+                       }
+               } else if (tag != ARCHIVE_ENTRY_ACL_MASK) {
+                       failure("Could not find match for ACL "
+                           "(type=%d,permset=%d,tag=%d,name=``%s'')",
+                           type, permset, tag, name);
+                       assert(matched == 1);
+               }
+       }
+       assertEqualInt(ARCHIVE_EOF, r);
+       assert((mode_t)(mode & 0777) == (archive_entry_mode(ae) & 0777));
+       failure("Could not find match for ACL "
+           "(type=%d,permset=%d,tag=%d,name=``%s'')",
+           acls[marker[0]].type, acls[marker[0]].permset,
+           acls[marker[0]].tag, acls[marker[0]].name);
+       assert(n == 0); /* Number of ACLs not matched should == 0 */
+       free(marker);
+}
+
+DEFINE_TEST(test_compat_star_acl_posix1e)
+{
+       char name[] = "test_compat_star_acl_posix1e.tar";
+       struct archive *a;
+       struct archive_entry *ae;
+
+       /* Read archive file */
+       assert(NULL != (a = archive_read_new()));
+        assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+        assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+        extract_reference_file(name);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240));
+
+       /* First item has a few ACLs */
+       assertA(0 == archive_read_next_header(a, &ae));
+       failure("One extended ACL should flag all ACLs to be returned.");
+       assertEqualInt(5, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+       compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), 0142, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+       failure("Basic ACLs should set mode to 0142, not %04o",
+           archive_entry_mode(ae)&0777);
+       assert((archive_entry_mode(ae) & 0777) == 0142);
+
+       /* Second item has pretty extensive ACLs */
+       assertA(0 == archive_read_next_header(a, &ae));
+       assertEqualInt(7, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+       compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), 0543, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+       failure("Basic ACLs should set mode to 0543, not %04o",
+           archive_entry_mode(ae)&0777);
+       assert((archive_entry_mode(ae) & 0777) == 0543);
+
+       /* Third item has default ACLs */
+       assertA(0 == archive_read_next_header(a, &ae));
+       assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+       compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), 0142, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
+       failure("Basic ACLs should set mode to 0142, not %04o",
+           archive_entry_mode(ae)&0777);
+       assert((archive_entry_mode(ae) & 0777) == 0142);
+
+       /* Close the archive. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/libarchive/test/test_compat_star_acl_posix1e.tar.uu b/libarchive/test/test_compat_star_acl_posix1e.tar.uu
new file mode 100644 (file)
index 0000000..81b771b
--- /dev/null
@@ -0,0 +1,231 @@
+begin 644 test_compat_star_acl_posix1e.tar
+M+B\N+T!087A(96%D97(`````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````#`P,#`V,#`@,#`P,#`P,"`P,#`P,#`P(#`P,#`P,#`P,C<R
+M(#`P,#`P,#`P,#`P(#`P,38P-S0@9P``````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````!U<W1A<@`P,')O;W0`
+M````````````````````````````````````=VAE96P`````````````````
+M```````````````````P,#`P,#`P(#`P,#`P,#`@````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````P,#`P,#`P,#`P,"`P,#`P,#`P
+M,#`P,"`````````````````U-R!30TA)3%DN<F5L96%S93US=&%R(#$N-2XS
+M("AA;60V-"UU;FMN;W=N+69R965B<V0Q,2XP*0HR-R!30TA)3%DN87)C:'1Y
+M<&4]97AU<W1A<@HT-R!30TA)3%DN=F]L:&1R+F1U;7!D871E/3$T-SDQ,CDY
+M-30N,#4X-S<R-SDS"C(U(%-#2$E,62YV;VQH9'(N=F]L;F\],0HS,"!30TA)
+M3%DN=F]L:&1R+F)L;V-K<VEZ93TR,`H`````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````"XO+B]`4&%X2&5A
+M9&5R````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````P
+M,#`P-C`P(#`P,#`P,#`@,#`P,#`P,"`P,#`P,#`P,#(U,2`P,#`P,#`P,#`P
+M,"`P,#$V,3$R('@`````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````=7-T87(`,#!R;V]T````````````````
+M`````````````````````'=H965L````````````````````````````````
+M````,#`P,#`P,"`P,#`P,#`P(```````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````,#`P,#`P,#`P,#`@,#`P,#`P,#`P,#`@````````
+M````````,S`@871I;64],30W.3$R,34Y-BXT.#4W,30P,#`*,S`@8W1I;64]
+M,30W.3$R.3<V-RXQ,#@Y.#8P,#`*,S`@;71I;64],30W.3$R,34Y-BXT.#4W
+M,30P,#`*-SD@4T-(24Q9+F%C;"YA8V-E<W,]=7-E<CHZ+2UX+'5S97(Z=7-E
+M<C<W.G(M+2QG<F]U<#HZ<BTM+&UA<VLZ.G(M+2QO=&AE<CHZ+7<M"@``````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````!F:6QE,0``````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````,#`P,#$T,B`P,#`P
+M,#`P(#`P,#`P,#`@,#`P,#`P,#`P,#`@,3,P,3(S,34R-S0@,#`Q-#8W,2`P
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````'5S=&%R`#`P<F]O=```````````````````````````````
+M``````!W:&5E;````````````````````````````````````#`P,#`P,#`@
+M,#`P,#`P,"``````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````#$S,#$R,S$U,C<T(#$S,#$R,S,U,C0W(````````````````"XO+B]`
+M4&%X2&5A9&5R````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````P,#`P-C`P(#`P,#`P,#`@,#`P,#`P,"`P,#`P,#`P,#,Q-"`P,#`P
+M,#`P,#`P,"`P,#$V,3$R('@`````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````=7-T87(`,#!R;V]T````````
+M`````````````````````````````'=H965L````````````````````````
+M````````````,#`P,#`P,"`P,#`P,#`P(```````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````,#`P,#`P,#`P,#`@,#`P,#`P,#`P,#`@
+M````````````````,S`@871I;64],30W.3$R,38P,2XP-C`X-C8P,#`*,S`@
+M8W1I;64],30W.3$R.3<V-RXQ,#@Y-S0P,#`*,S`@;71I;64],30W.3$R,38P
+M,2XP-C`X-C8P,#`*,3$T(%-#2$E,62YA8VPN86-C97-S/75S97(Z.G(M>"QU
+M<V5R.G5S97(W-SIR+2TL=7-E<CIU<V5R-S@Z+2TM+&=R;W5P.CIR+2TL9W)O
+M=7`Z9W)O=7`W.#IR=W@L;6%S:SHZ<G=X+&]T:&5R.CHM=W@*````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````!F:6QE,@``````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````,#`P,#4W
+M,R`P,#`P,#`P(#`P,#`P,#`@,#`P,#`P,#`P,#`@,3,P,3(S,34S,#$@,#`Q
+M-#8V,"`P````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````'5S=&%R`#`P<F]O=```````````````````````
+M``````````````!W:&5E;````````````````````````````````````#`P
+M,#`P,#`@,#`P,#`P,"``````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````#$S,#$R,S$U,S`Q(#$S,#$R,S,U,C0W(```````````````
+M`"XO+B]`4&%X2&5A9&5R````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````P,#`P-C`P(#`P,#`P,#`@,#`P,#`P,"`P,#`P,#`P,#,W
+M,R`P,#`P,#`P,#`P,"`P,#$V,3$W('@`````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````=7-T87(`,#!R;V]T
+M`````````````````````````````````````'=H965L````````````````
+M````````````````````,#`P,#`P,"`P,#`P,#`P(```````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````,#`P,#`P,#`P,#`@,#`P,#`P
+M,#`P,#`@````````````````,S`@871I;64],30W.3$R,3<R-BXX,3<T-3`P
+M,#`*,S`@8W1I;64],30W.3$R,3@S,2XU.#,U-3(P,#`*,S`@;71I;64],30W
+M.3$R,3<R-BXX,3<T-3`P,#`*-C,@4T-(24Q9+F%C;"YA8V-E<W,]=7-E<CHZ
+M+2UX+&=R;W5P.CIR+2TL;6%S:SHZ<BTM+&]T:&5R.CHM=RT*.3@@4T-(24Q9
+M+F%C;"YD969A=6QT/75S97(Z.BTM>"QU<V5R.G5S97(W-SIR+2TL9W)O=7`Z
+M.G(M+2QG<F]U<#IG<F]U<#<X.BTM>"QM87-K.CIR+7@L;W1H97(Z.BUW+0H`
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````!D:7(Q+P``````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M,#`P,#$T,B`P,#`P,#`P(#`P,#`P,#`@,#`P,#`P,#`P,#`@,3,P,3(S,34T
+M-S8@,#`Q-#8R-B`U````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````'5S=&%R`#`P<F]O=```````````````
+M``````````````````````!W:&5E;```````````````````````````````
+M`````#`P,#`P,#`@,#`P,#`P,"``````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````#$S,#$R,S$U-#<V(#$S,#$R,S$U-C0W(```````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+9````````````````````````````````````
+`
+end