]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Resuscitate the old NFS4 support code and make it work on FreeBSD-CURRENT.
authorTim Kientzle <kientzle@acm.org>
Mon, 9 Apr 2012 05:06:43 +0000 (22:06 -0700)
committerTim Kientzle <kientzle@acm.org>
Mon, 9 Apr 2012 05:06:43 +0000 (22:06 -0700)
This includes a new test that archive_write_disk and archive_read_disk
consistently read/write ACL information to/from disk.
This new test currently passes on FreeBSD-CURRENT.

CMakeLists.txt
libarchive/CMakeLists.txt
libarchive/archive_read_disk_entry_from_file.c
libarchive/archive_write_disk_acl.c [new file with mode: 0644]
libarchive/archive_write_disk_posix.c
libarchive/archive_write_disk_private.h
libarchive/test/CMakeLists.txt
libarchive/test/test_acl_freebsd_nfs4.c [new file with mode: 0644]
libarchive/test/test_acl_freebsd_posix1e.c [moved from libarchive/test/test_acl_freebsd.c with 99% similarity]
tar/test/test_basic.c

index 58b2b36671fa916bf41b55b1e077c0167b289b36..9d6f56d6314dfc9d9ddd02573ce47d4847797043 100644 (file)
@@ -1303,6 +1303,8 @@ IF(ENABLE_ACL)
   CHECK_FUNCTION_EXISTS(acl_get_perm_np HAVE_ACL_GET_PERM_NP)
   CHECK_FUNCTION_EXISTS(acl_get_link HAVE_ACL_GET_LINK)
   CHECK_FUNCTION_EXISTS(acl_get_link_np HAVE_ACL_GET_LINK_NP)
+  CHECK_FUNCTION_EXISTS(acl_is_trivial_np HAVE_ACL_IS_TRIVIAL_NP)
+  CHECK_FUNCTION_EXISTS(acl_set_link_np HAVE_ACL_SET_LINK_NP)
 
   # MacOS has an acl.h that isn't POSIX.  It can be detected by
   # checking for ACL_USER
index 73d9baf01e877319f07123933a0d229616ff669b..a88a0eab2f96d5aff3fe5d2dc258bbe8e7bc0152 100644 (file)
@@ -85,6 +85,7 @@ SET(libarchive_SOURCES
   archive_util.c
   archive_virtual.c
   archive_write.c
+  archive_write_disk_acl.c
   archive_write_disk_posix.c
   archive_write_disk_private.h
   archive_write_disk_set_standard_lookup.c
index 7402b8d8f66635d4bafc799a3c0f4a6a21903994..8c6be14f634ba55b02805ca3743a2625359dd144 100644 (file)
@@ -114,7 +114,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010
 #define        ACL_GET_PERM acl_get_perm_np
 #endif
 
-static int setup_acls_posix1e(struct archive_read_disk *,
+static int setup_acls(struct archive_read_disk *,
     struct archive_entry *, int *fd);
 static int setup_mac_metadata(struct archive_read_disk *,
     struct archive_entry *, int *fd);
@@ -168,15 +168,16 @@ archive_read_disk_entry_from_file(struct archive *_a,
                        st = &s;
                }
                archive_entry_copy_stat(entry, st);
-               /* Lookup uname/gname */
-               name = archive_read_disk_uname(_a, archive_entry_uid(entry));
-               if (name != NULL)
-                       archive_entry_copy_uname(entry, name);
-               name = archive_read_disk_gname(_a, archive_entry_gid(entry));
-               if (name != NULL)
-                       archive_entry_copy_gname(entry, name);
        }
 
+       /* Lookup uname/gname */
+       name = archive_read_disk_uname(_a, archive_entry_uid(entry));
+       if (name != NULL)
+               archive_entry_copy_uname(entry, name);
+       name = archive_read_disk_gname(_a, archive_entry_gid(entry));
+       if (name != NULL)
+               archive_entry_copy_gname(entry, name);
+
 #ifdef HAVE_STRUCT_STAT_ST_FLAGS
        /* On FreeBSD, we get flags for free with the stat. */
        /* TODO: Does this belong in copy_stat()? */
@@ -244,7 +245,7 @@ archive_read_disk_entry_from_file(struct archive *_a,
        }
 #endif /* HAVE_READLINK || HAVE_READLINKAT */
 
-       r = setup_acls_posix1e(a, entry, &fd);
+       r = setup_acls(a, entry, &fd);
        r1 = setup_xattrs(a, entry, &fd);
        if (r1 < r)
                r = r1;
@@ -388,11 +389,11 @@ setup_mac_metadata(struct archive_read_disk *a,
 
 
 #ifdef HAVE_POSIX_ACL
-static void setup_acl_posix1e(struct archive_read_disk *a,
+static int translate_acl(struct archive_read_disk *a,
     struct archive_entry *entry, acl_t acl, int archive_entry_acl_type);
 
 static int
-setup_acls_posix1e(struct archive_read_disk *a,
+setup_acls(struct archive_read_disk *a,
     struct archive_entry *entry, int *fd)
 {
        const char      *accpath;
@@ -404,15 +405,33 @@ setup_acls_posix1e(struct archive_read_disk *a,
 
        archive_entry_acl_clear(entry);
 
-       if (*fd < 0 && a->tree != NULL &&
-           (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK)){
-               *fd = a->open_on_current_dir(a->tree, accpath,
-                               O_RDONLY | O_NONBLOCK);
-               if (*fd < 0) {
-                       archive_set_error(&a->archive, errno,
-                           "Couldn't access %s", accpath);
-                       return (ARCHIVE_FAILED);
-               }
+       /* Try NFS4 ACL first. */
+       if (*fd >= 0)
+               acl = acl_get_fd(*fd);
+#if HAVE_ACL_GET_LINK_NP
+       else if (!a->follow_symlinks)
+               acl = acl_get_link_np(accpath, ACL_TYPE_NFS4);
+#else
+       else if ((!a->follow_symlinks)
+           && (archive_entry_filetype(entry) == AE_IFLNK))
+               /* We can't get the ACL of a symlink, so we assume it can't
+                  have one. */
+               acl = NULL;
+#endif
+       else
+               acl = acl_get_file(accpath, ACL_TYPE_NFS4);
+#if HAVE_ACL_IS_TRIVIAL_NP
+       /* Ignore "trivial" ACLs that just mirror the file mode. */
+       acl_is_trivial_np(acl, &r);
+       if (r) {
+               acl_free(acl);
+               acl = NULL;
+       }
+#endif
+       if (acl != NULL) {
+               translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+               acl_free(acl);
+               return (ARCHIVE_OK);
        }
 
        /* Retrieve access ACL from file. */
@@ -431,7 +450,7 @@ setup_acls_posix1e(struct archive_read_disk *a,
        else
                acl = acl_get_file(accpath, ACL_TYPE_ACCESS);
        if (acl != NULL) {
-               setup_acl_posix1e(a, entry, acl,
+               translate_acl(a, entry, acl,
                    ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
                acl_free(acl);
        }
@@ -440,7 +459,7 @@ setup_acls_posix1e(struct archive_read_disk *a,
        if (S_ISDIR(archive_entry_mode(entry))) {
                acl = acl_get_file(accpath, ACL_TYPE_DEFAULT);
                if (acl != NULL) {
-                       setup_acl_posix1e(a, entry, acl,
+                       translate_acl(a, entry, acl,
                            ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
                        acl_free(acl);
                }
@@ -449,69 +468,181 @@ setup_acls_posix1e(struct archive_read_disk *a,
 }
 
 /*
- * Translate POSIX.1e ACL into libarchive internal structure.
+ * Translate system ACL into libarchive internal structure.
  */
-static void
-setup_acl_posix1e(struct archive_read_disk *a,
-    struct archive_entry *entry, acl_t acl, int archive_entry_acl_type)
+
+static struct {
+        int archive_perm;
+        int platform_perm;
+} acl_perm_map[] = {
+        {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
+        {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},
+        {ARCHIVE_ENTRY_ACL_READ, ACL_READ},
+        {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},
+        {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},
+        {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},
+        {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},
+        {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},
+        {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},
+        {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS},
+        {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS},
+        {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},
+        {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},
+        {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},
+        {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},
+        {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL},
+        {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL},
+        {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER},
+        {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
+};
+
+static struct {
+        int archive_inherit;
+        int platform_inherit;
+} acl_inherit_map[] = {
+        {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},
+       {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},
+       {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT},
+       {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY}
+};
+
+static int
+translate_acl(struct archive_read_disk *a,
+    struct archive_entry *entry, acl_t acl, int default_entry_acl_type)
 {
        acl_tag_t        acl_tag;
+       acl_entry_type_t acl_type;
+       acl_flagset_t    acl_flagset;
        acl_entry_t      acl_entry;
        acl_permset_t    acl_permset;
+       int              brand, i, r, entry_acl_type;
        int              s, ae_id, ae_tag, ae_perm;
        const char      *ae_name;
 
+
+       // FreeBSD "brands" ACLs as POSIX.1e or NFSv4
+       // Make sure the "brand" on this ACL is consistent
+       // with the default_entry_acl_type bits provided.
+       acl_get_brand_np(acl, &brand);
+       switch (brand) {
+       case ACL_BRAND_POSIX:
+               switch (default_entry_acl_type) {
+               case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+               case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+                       entry_acl_type = default_entry_acl_type;
+                       break;
+               default:
+                       // XXX set warning message?
+                       return ARCHIVE_FAILED;
+               }
+               break;
+       case ACL_BRAND_NFS4:
+               if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+                       // XXX set warning message?
+                       return ARCHIVE_FAILED;
+               }
+               break;
+       default:
+               // XXX set warning message?
+               return ARCHIVE_FAILED;
+               break;
+       }
+
+
        s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
        while (s == 1) {
                ae_id = -1;
                ae_name = NULL;
+               ae_perm = 0;
 
                acl_get_tag_type(acl_entry, &acl_tag);
-               if (acl_tag == ACL_USER) {
+               switch (acl_tag) {
+               case ACL_USER:
                        ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry);
                        ae_name = archive_read_disk_uname(&a->archive, ae_id);
                        ae_tag = ARCHIVE_ENTRY_ACL_USER;
-               } else if (acl_tag == ACL_GROUP) {
+                       break;
+               case ACL_GROUP:
                        ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry);
                        ae_name = archive_read_disk_gname(&a->archive, ae_id);
                        ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
-               } else if (acl_tag == ACL_MASK) {
+                       break;
+               case ACL_MASK:
                        ae_tag = ARCHIVE_ENTRY_ACL_MASK;
-               } else if (acl_tag == ACL_USER_OBJ) {
+                       break;
+               case ACL_USER_OBJ:
                        ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
-               } else if (acl_tag == ACL_GROUP_OBJ) {
+                       break;
+               case ACL_GROUP_OBJ:
                        ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
-               } else if (acl_tag == ACL_OTHER) {
+                       break;
+               case ACL_OTHER:
                        ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
-               } else {
+                       break;
+               case ACL_EVERYONE:
+                       ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+                       break;
+               default:
                        /* Skip types that libarchive can't support. */
                        continue;
                }
 
-               acl_get_permset(acl_entry, &acl_permset);
-               ae_perm = 0;
+               // XXX acl type maps to allow/deny/audit/YYYY bits
+               // XXX acl_get_entry_type_np on FreeBSD returns EINVAL for
+               // non-NFSv4 ACLs
+               entry_acl_type = default_entry_acl_type;
+               r = acl_get_entry_type_np(acl_entry, &acl_type);
+               if (r == 0) {
+                       switch (acl_type) {
+                       case ACL_ENTRY_TYPE_ALLOW:
+                               entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+                               break;
+                       case ACL_ENTRY_TYPE_DENY:
+                               entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+                               break;
+                       case ACL_ENTRY_TYPE_AUDIT:
+                               entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
+                               break;
+                       case ACL_ENTRY_TYPE_ALARM:
+                               entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+                               break;
+                       }
+               }
+
                /*
-                * acl_get_perm() is spelled differently on different
-                * platforms; see above.
+                * Libarchive stores "flag" (NFSv4 inheritance bits)
+                * in the ae_perm bitmap.
                 */
-               if (ACL_GET_PERM(acl_permset, ACL_EXECUTE))
-                       ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE;
-               if (ACL_GET_PERM(acl_permset, ACL_READ))
-                       ae_perm |= ARCHIVE_ENTRY_ACL_READ;
-               if (ACL_GET_PERM(acl_permset, ACL_WRITE))
-                       ae_perm |= ARCHIVE_ENTRY_ACL_WRITE;
+               acl_get_flagset_np(acl_entry, &acl_flagset);
+                for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) {
+                       if (acl_get_flag_np(acl_flagset,
+                                           acl_inherit_map[i].platform_inherit))
+                               ae_perm |= acl_inherit_map[i].archive_inherit;
 
-               archive_entry_acl_add_entry(entry,
-                   archive_entry_acl_type, ae_perm, ae_tag,
-                   ae_id, ae_name);
+                }
+
+               acl_get_permset(acl_entry, &acl_permset);
+                for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) {
+                       /*
+                        * acl_get_perm() is spelled differently on different
+                        * platforms; see above.
+                        */
+                       if (ACL_GET_PERM(acl_permset, acl_perm_map[i].platform_perm))
+                               ae_perm |= acl_perm_map[i].archive_perm;
+               }
+
+               archive_entry_acl_add_entry(entry, entry_acl_type,
+                                           ae_perm, ae_tag,
+                                           ae_id, ae_name);
 
                s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
        }
+       return (ARCHIVE_OK);
 }
 #else
 static int
-setup_acls_posix1e(struct archive_read_disk *a,
-    struct archive_entry *entry, int *fd)
+setup_acls(struct archive_read_disk *a,
+    struct archive_entry *entry, int fd)
 {
        (void)a;      /* UNUSED */
        (void)entry;  /* UNUSED */
diff --git a/libarchive/archive_write_disk_acl.c b/libarchive/archive_write_disk_acl.c
new file mode 100644 (file)
index 0000000..7953e0c
--- /dev/null
@@ -0,0 +1,249 @@
+/*-
+ * Copyright (c) 2003-2010 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
+ *    in this position and unchanged.
+ * 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 "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_disk.c 201159 2009-12-29 05:35:40Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#define _ACL_PRIVATE /* For debugging */
+#include <sys/acl.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_acl_private.h"
+#include "archive_write_disk_private.h"
+
+#ifndef HAVE_POSIX_ACL
+/* Default empty function body to satisfy mainline code. */
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+        struct archive_acl *abstract_acl)
+{
+       (void)a; /* UNUSED */
+       (void)fd; /* UNUSED */
+       (void)name; /* UNUSED */
+       (void)abstract_acl; /* UNUSED */
+       return (ARCHIVE_OK);
+}
+
+#else
+
+static int     set_acl(struct archive *, int fd, const char *,
+                       struct archive_acl *,
+                       acl_type_t, int archive_entry_acl_type, const char *tn);
+
+/*
+ * XXX TODO: What about ACL types other than ACCESS and DEFAULT?
+ */
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+        struct archive_acl *abstract_acl)
+{
+       int              ret;
+
+       if (archive_acl_count(abstract_acl, ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) > 0) {
+               ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_ACCESS,
+                   ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access");
+               if (ret != ARCHIVE_OK)
+                       return (ret);
+               ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_DEFAULT,
+                   ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default");
+               return (ret);
+       } else if (archive_acl_count(abstract_acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4) > 0) {
+               ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_NFS4,
+                   ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
+               return (ret);
+       } else
+               return ARCHIVE_OK;
+}
+
+static struct {
+       int archive_perm;
+       int platform_perm;
+} acl_perm_map[] = {
+       {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
+       {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},
+       {ARCHIVE_ENTRY_ACL_READ, ACL_READ},
+       {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},
+       {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},
+       {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},
+       {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},
+       {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},
+       {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},
+       {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS},
+       {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS},
+       {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},
+       {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},
+       {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},
+       {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},
+       {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL},
+       {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL},
+       {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER},
+       {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
+};
+
+static struct {
+       int archive_inherit;
+       int platform_inherit;
+} acl_inherit_map[] = {
+       {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},
+       {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},
+       {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT},
+       {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY}
+};
+
+static int
+set_acl(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl,
+    acl_type_t acl_type, int ae_requested_type, const char *tname)
+{
+       acl_t            acl;
+       acl_entry_t      acl_entry;
+       acl_permset_t    acl_permset;
+       acl_flagset_t    acl_flagset;
+       int              ret;
+       int              ae_type, ae_permset, ae_tag, ae_id;
+       uid_t            ae_uid;
+       gid_t            ae_gid;
+       const char      *ae_name;
+       int              entries;
+       int              i;
+
+       ret = ARCHIVE_OK;
+       entries = archive_acl_reset(abstract_acl, ae_requested_type);
+       if (entries == 0)
+               return (ARCHIVE_OK);
+       acl = acl_init(entries);
+       while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
+                  &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+               acl_create_entry(&acl, &acl_entry);
+
+               switch (ae_tag) {
+               case ARCHIVE_ENTRY_ACL_USER:
+                       acl_set_tag_type(acl_entry, ACL_USER);
+                       ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+                       acl_set_qualifier(acl_entry, &ae_uid);
+                       break;
+               case ARCHIVE_ENTRY_ACL_GROUP:
+                       acl_set_tag_type(acl_entry, ACL_GROUP);
+                       ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+                       acl_set_qualifier(acl_entry, &ae_gid);
+                       break;
+               case ARCHIVE_ENTRY_ACL_USER_OBJ:
+                       acl_set_tag_type(acl_entry, ACL_USER_OBJ);
+                       break;
+               case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+                       acl_set_tag_type(acl_entry, ACL_GROUP_OBJ);
+                       break;
+               case ARCHIVE_ENTRY_ACL_MASK:
+                       acl_set_tag_type(acl_entry, ACL_MASK);
+                       break;
+               case ARCHIVE_ENTRY_ACL_OTHER:
+                       acl_set_tag_type(acl_entry, ACL_OTHER);
+                       break;
+               case ARCHIVE_ENTRY_ACL_EVERYONE:
+                       acl_set_tag_type(acl_entry, ACL_EVERYONE);
+                       break;
+               default:
+                       /* XXX */
+                       break;
+               }
+
+               switch (ae_type) {
+               case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+                       acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALLOW);
+                       break;
+               case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+                       acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_DENY);
+                       break;
+               case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+                       acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_AUDIT);
+                       break;
+               case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+                       acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALARM);
+                       break;
+               case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+               case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+                       // These don't translate directly into the system ACL.
+                       break;
+               default:
+                       // XXX error handling here.
+                       break;
+               }
+
+               acl_get_permset(acl_entry, &acl_permset);
+               acl_clear_perms(acl_permset);
+
+               for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) {
+                       if (ae_permset & acl_perm_map[i].archive_perm)
+                               acl_add_perm(acl_permset,
+                                            acl_perm_map[i].platform_perm);
+               }
+
+               acl_get_flagset_np(acl_entry, &acl_flagset);
+               acl_clear_flags_np(acl_flagset);
+               for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) {
+                       if (ae_permset & acl_inherit_map[i].archive_inherit)
+                               acl_add_flag_np(acl_flagset,
+                                               acl_inherit_map[i].platform_inherit);
+               }
+       }
+
+       /* Try restoring the ACL through 'fd' if we can. */
+#if HAVE_ACL_SET_FD
+       if (fd >= 0 && acl_type == ACL_TYPE_ACCESS && acl_set_fd(fd, acl) == 0)
+               ret = ARCHIVE_OK;
+       else
+#else
+#if HAVE_ACL_SET_FD_NP
+       if (fd >= 0 && acl_set_fd_np(fd, acl, acl_type) == 0)
+               ret = ARCHIVE_OK;
+       else
+#endif
+#endif
+#if HAVE_ACL_SET_LINK_NP
+         if (acl_set_link_np(name, acl_type, acl) != 0) {
+               archive_set_error(a, errno, "Failed to set %s acl", tname);
+               ret = ARCHIVE_WARN;
+         }
+#else
+       /* TODO: Skip this if 'name' is a symlink. */
+       if (acl_set_file(name, acl_type, acl) != 0) {
+               archive_set_error(a, errno, "Failed to set %s acl", tname);
+               ret = ARCHIVE_WARN;
+       }
+#endif
+       acl_free(acl);
+       return (ret);
+}
+#endif
index d79b0f685a3dab3c4e9052601bd1fc01ef203494..3488b44dfe17da1239cc17246c35bc00a2aa7884 100644 (file)
@@ -32,9 +32,6 @@ __FBSDID("$FreeBSD$");
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
-#ifdef HAVE_SYS_ACL_H
-#include <sys/acl.h>
-#endif
 #ifdef HAVE_SYS_EXTATTR_H
 #include <sys/extattr.h>
 #endif
@@ -129,6 +126,7 @@ __FBSDID("$FreeBSD$");
 #include "archive_string.h"
 #include "archive_entry.h"
 #include "archive_private.h"
+#include "archive_write_disk_private.h"
 
 #ifndef O_BINARY
 #define O_BINARY 0
@@ -267,11 +265,6 @@ static int create_dir(struct archive_write_disk *, char *);
 static int     create_parent_dir(struct archive_write_disk *, char *);
 static int     older(struct stat *, struct archive_entry *);
 static int     restore_entry(struct archive_write_disk *);
-#ifdef HAVE_POSIX_ACL
-static int     set_acl(struct archive_write_disk *, int fd, const char *, struct archive_acl *,
-                   acl_type_t, int archive_entry_acl_type, const char *tn);
-#endif
-static int     set_acls(struct archive_write_disk *, int fd, const char *, struct archive_acl *);
 static int     set_mac_metadata(struct archive_write_disk *, const char *,
                                 const void *, size_t);
 static int     set_xattrs(struct archive_write_disk *);
@@ -570,6 +563,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
 
        if (a->deferred & TODO_ACLS) {
                fe = current_fixup(a, archive_entry_pathname(entry));
+               fe->fixup |= TODO_ACLS;
                archive_acl_copy(&fe->acl, archive_entry_acl(entry));
        }
 
@@ -878,7 +872,7 @@ _archive_write_disk_finish_entry(struct archive *_a)
         * ACLs that prevent attribute changes (including time).
         */
        if (a->todo & TODO_ACLS) {
-               int r2 = set_acls(a, a->fd,
+               int r2 = archive_write_disk_set_acls(&a->archive, a->fd,
                                  archive_entry_pathname(a->entry),
                                  archive_entry_acl(a->entry));
                if (r2 < ret) ret = r2;
@@ -950,12 +944,12 @@ archive_write_disk_gid(struct archive *_a, const char *name, int64_t id)
 int64_t
 archive_write_disk_uid(struct archive *_a, const char *name, int64_t id)
 {
-       struct archive_write_disk *a = (struct archive_write_disk *)_a;
-       archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
-           ARCHIVE_STATE_ANY, "archive_write_disk_uid");
-       if (a->lookup_uid)
-               return (a->lookup_uid)(a->lookup_uid_data, name, id);
-       return (id);
+       struct archive_write_disk *a = (struct archive_write_disk *)_a;
+       archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+           ARCHIVE_STATE_ANY, "archive_write_disk_uid");
+       if (a->lookup_uid)
+               return (a->lookup_uid)(a->lookup_uid_data, name, id);
+       return (id);
 }
 
 /*
@@ -1381,7 +1375,8 @@ _archive_write_disk_close(struct archive *_a)
                if (p->fixup & TODO_MODE_BASE)
                        chmod(p->name, p->mode);
                if (p->fixup & TODO_ACLS)
-                       set_acls(a, -1, p->name, &p->acl);
+                       archive_write_disk_set_acls(&a->archive,
+                                                   -1, p->name, &p->acl);
                if (p->fixup & TODO_FFLAGS)
                        set_fflags_platform(a, -1, p->name,
                            p->mode, p->fflags_set, 0);
@@ -2543,131 +2538,6 @@ set_mac_metadata(struct archive_write_disk *a, const char *pathname,
 }
 #endif
 
-#ifndef HAVE_POSIX_ACL
-/* Default empty function body to satisfy mainline code. */
-static int
-set_acls(struct archive_write_disk *a, int fd, const char *name,
-        struct archive_acl *aacl)
-{
-       (void)a; /* UNUSED */
-       (void)fd; /* UNUSED */
-       (void)name; /* UNUSED */
-       (void)aacl; /* UNUSED */
-       return (ARCHIVE_OK);
-}
-
-#else
-
-/*
- * XXX TODO: What about ACL types other than ACCESS and DEFAULT?
- */
-static int
-set_acls(struct archive_write_disk *a, int fd, const char *name,
-        struct archive_acl *abstract_acl)
-{
-       int              ret;
-
-       ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_ACCESS,
-           ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access");
-       if (ret != ARCHIVE_OK)
-               return (ret);
-       ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_DEFAULT,
-           ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default");
-       return (ret);
-}
-
-
-static int
-set_acl(struct archive_write_disk *a, int fd, const char *name,
-    struct archive_acl *abstract_acl,
-    acl_type_t acl_type, int ae_requested_type, const char *tname)
-{
-       acl_t            acl;
-       acl_entry_t      acl_entry;
-       acl_permset_t    acl_permset;
-       int              ret, r;
-       int              ae_type, ae_permset, ae_tag, ae_id;
-       uid_t            ae_uid;
-       gid_t            ae_gid;
-       const char      *ae_name;
-       int              entries;
-
-       ret = ARCHIVE_OK;
-       entries = archive_acl_reset(abstract_acl, ae_requested_type);
-       if (entries == 0)
-               return (ARCHIVE_OK);
-       acl = acl_init(entries);
-       while ((r = archive_acl_next(&a->archive, abstract_acl,
-           ae_requested_type, &ae_type, &ae_permset, &ae_tag, &ae_id,
-           &ae_name)) == ARCHIVE_OK) {
-               acl_create_entry(&acl, &acl_entry);
-
-               switch (ae_tag) {
-               case ARCHIVE_ENTRY_ACL_USER:
-                       acl_set_tag_type(acl_entry, ACL_USER);
-                       ae_uid = archive_write_disk_uid(&a->archive,
-                           ae_name, ae_id);
-                       acl_set_qualifier(acl_entry, &ae_uid);
-                       break;
-               case ARCHIVE_ENTRY_ACL_GROUP:
-                       acl_set_tag_type(acl_entry, ACL_GROUP);
-                       ae_gid = archive_write_disk_gid(&a->archive,
-                           ae_name, ae_id);
-                       acl_set_qualifier(acl_entry, &ae_gid);
-                       break;
-               case ARCHIVE_ENTRY_ACL_USER_OBJ:
-                       acl_set_tag_type(acl_entry, ACL_USER_OBJ);
-                       break;
-               case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
-                       acl_set_tag_type(acl_entry, ACL_GROUP_OBJ);
-                       break;
-               case ARCHIVE_ENTRY_ACL_MASK:
-                       acl_set_tag_type(acl_entry, ACL_MASK);
-                       break;
-               case ARCHIVE_ENTRY_ACL_OTHER:
-                       acl_set_tag_type(acl_entry, ACL_OTHER);
-                       break;
-               default:
-                       /* XXX */
-                       break;
-               }
-
-               acl_get_permset(acl_entry, &acl_permset);
-               acl_clear_perms(acl_permset);
-               if (ae_permset & ARCHIVE_ENTRY_ACL_EXECUTE)
-                       acl_add_perm(acl_permset, ACL_EXECUTE);
-               if (ae_permset & ARCHIVE_ENTRY_ACL_WRITE)
-                       acl_add_perm(acl_permset, ACL_WRITE);
-               if (ae_permset & ARCHIVE_ENTRY_ACL_READ)
-                       acl_add_perm(acl_permset, ACL_READ);
-       }
-       if (r == ARCHIVE_FATAL) {
-               acl_free(acl);
-               archive_set_error(&a->archive, errno,
-                   "Failed to archive_acl_next");
-               return (r);
-       }
-
-       /* Try restoring the ACL through 'fd' if we can. */
-#if HAVE_ACL_SET_FD
-       if (fd >= 0 && acl_type == ACL_TYPE_ACCESS && acl_set_fd(fd, acl) == 0)
-               ret = ARCHIVE_OK;
-       else
-#else
-#if HAVE_ACL_SET_FD_NP
-       if (fd >= 0 && acl_set_fd_np(fd, acl, acl_type) == 0)
-               ret = ARCHIVE_OK;
-       else
-#endif
-#endif
-       if (acl_set_file(name, acl_type, acl) != 0) {
-               archive_set_error(&a->archive, errno, "Failed to set %s acl", tname);
-               ret = ARCHIVE_WARN;
-       }
-       acl_free(acl);
-       return (ret);
-}
-#endif
 
 #if HAVE_LSETXATTR || HAVE_LSETEA
 /*
index 707c0cf03b2e385dd3bdfdbe1308da1d8bf45317..d84e7e1cd64cf38b1adf15b75c2c581d67f4377f 100644 (file)
 #ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
 #define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
 
+#include "archive_acl_private.h"
+
 struct archive_write_disk;
 
+int
+archive_write_disk_set_acls(struct archive *, int /* fd */, const char * /* pathname */, struct archive_acl *);
+
 #endif
index afe3593b2e9b119942cb2a000e91bda726771d49..89405ca62e232197273711fcd2c6852622e40196 100644 (file)
@@ -8,7 +8,8 @@ IF(ENABLE_TEST)
    main.c
     read_open_memory.c
     test.h
-    test_acl_freebsd.c
+    test_acl_freebsd_posix1e.c
+    test_acl_freebsd_nfs4.c
     test_acl_nfs4.c
     test_acl_pax.c
     test_acl_posix1e.c
diff --git a/libarchive/test/test_acl_freebsd_nfs4.c b/libarchive/test/test_acl_freebsd_nfs4.c
new file mode 100644 (file)
index 0000000..fd2301d
--- /dev/null
@@ -0,0 +1,547 @@
+/*-
+ * Copyright (c) 2003-2010 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$");
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 8
+#define _ACL_PRIVATE
+#include <sys/acl.h>
+
+struct myacl_t {
+       int type;
+       int permset;
+       int tag;
+       int qual; /* GID or UID of user/group, depending on tag. */
+       const char *name; /* Name of user/group, depending on tag. */
+};
+
+static struct myacl_t acls_reg[] = {
+       /* For this test, we need the file owner to be able to read and write the ACL. */
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+         ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, -1, ""},
+
+       /* An entry for each type. */
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_USER, 108, "user108" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_USER, 109, "user109" },
+
+       /* An entry for each permission. */
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_USER, 112, "user112" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA,
+         ARCHIVE_ENTRY_ACL_USER, 113, "user113" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_DATA,
+         ARCHIVE_ENTRY_ACL_USER, 115, "user115" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_APPEND_DATA,
+         ARCHIVE_ENTRY_ACL_USER, 117, "user117" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS,
+         ARCHIVE_ENTRY_ACL_USER, 119, "user119" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS,
+         ARCHIVE_ENTRY_ACL_USER, 120, "user120" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES,
+         ARCHIVE_ENTRY_ACL_USER, 122, "user122" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES,
+         ARCHIVE_ENTRY_ACL_USER, 123, "user123" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE,
+         ARCHIVE_ENTRY_ACL_USER, 124, "user124" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ACL,
+         ARCHIVE_ENTRY_ACL_USER, 125, "user125" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ACL,
+         ARCHIVE_ENTRY_ACL_USER, 126, "user126" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_OWNER,
+         ARCHIVE_ENTRY_ACL_USER, 127, "user127" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_USER, 128, "user128" },
+
+       /* One entry for each qualifier. */
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_USER, 135, "user135" },
+//     { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
+//       ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_GROUP, 136, "group136" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }
+};
+
+
+static struct myacl_t acls_dir[] = {
+       /* For this test, we need to be able to read and write the ACL. */
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ACL,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, -1, ""},
+
+       /* An entry for each type. */
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
+         ARCHIVE_ENTRY_ACL_USER, 101, "user101" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
+         ARCHIVE_ENTRY_ACL_USER, 102, "user102" },
+
+       /* An entry for each permission. */
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
+         ARCHIVE_ENTRY_ACL_USER, 201, "user201" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_ADD_FILE,
+         ARCHIVE_ENTRY_ACL_USER, 202, "user202" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
+         ARCHIVE_ENTRY_ACL_USER, 203, "user203" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS,
+         ARCHIVE_ENTRY_ACL_USER, 204, "user204" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS,
+         ARCHIVE_ENTRY_ACL_USER, 205, "user205" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE_CHILD,
+         ARCHIVE_ENTRY_ACL_USER, 206, "user206" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES,
+         ARCHIVE_ENTRY_ACL_USER, 207, "user207" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES,
+         ARCHIVE_ENTRY_ACL_USER, 208, "user208" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE,
+         ARCHIVE_ENTRY_ACL_USER, 209, "user209" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ACL,
+         ARCHIVE_ENTRY_ACL_USER, 210, "user210" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ACL,
+         ARCHIVE_ENTRY_ACL_USER, 211, "user211" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_OWNER,
+         ARCHIVE_ENTRY_ACL_USER, 212, "user212" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_USER, 213, "user213" },
+
+       /* One entry with each inheritance value. */
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+         ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT,
+         ARCHIVE_ENTRY_ACL_USER, 301, "user301" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+         ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT,
+         ARCHIVE_ENTRY_ACL_USER, 302, "user302" },
+#if 0
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+         ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT,
+         ARCHIVE_ENTRY_ACL_USER, 303, "user303" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+         ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY,
+         ARCHIVE_ENTRY_ACL_USER, 304, "user304" },
+#endif
+
+#if 0
+       /* FreeBSD does not support audit entries. */
+       { ARCHIVE_ENTRY_ACL_TYPE_AUDIT,
+         ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS,
+         ARCHIVE_ENTRY_ACL_USER, 401, "user401" },
+       { ARCHIVE_ENTRY_ACL_TYPE_AUDIT,
+         ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS,
+         ARCHIVE_ENTRY_ACL_USER, 402, "user402" },
+#endif
+
+       /* One entry for each qualifier. */
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
+         ARCHIVE_ENTRY_ACL_USER, 501, "user501" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
+         ARCHIVE_ENTRY_ACL_GROUP, 502, "group502" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
+         ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }
+};
+
+static void
+set_acls(struct archive_entry *ae, struct myacl_t *acls, int start, int end)
+{
+       int i;
+
+       archive_entry_acl_clear(ae);
+       if (start > 0) {
+               assertEqualInt(ARCHIVE_OK,
+                       archive_entry_acl_add_entry(ae,
+                           acls[0].type, acls[0].permset, acls[0].tag,
+                           acls[0].qual, acls[0].name));
+       }
+       for (i = start; i < end; i++) {
+               assertEqualInt(ARCHIVE_OK,
+                   archive_entry_acl_add_entry(ae,
+                       acls[i].type, acls[i].permset, acls[i].tag,
+                       acls[i].qual, acls[i].name));
+       }
+}
+
+static int
+acl_permset_to_bitmap(acl_permset_t opaque_ps)
+{
+       static struct { int machine; int portable; } perms[] = {
+               {ACL_EXECUTE, ARCHIVE_ENTRY_ACL_EXECUTE},
+               {ACL_WRITE, ARCHIVE_ENTRY_ACL_WRITE},
+               {ACL_READ, ARCHIVE_ENTRY_ACL_READ},
+               {ACL_READ_DATA, ARCHIVE_ENTRY_ACL_READ_DATA},
+               {ACL_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY},
+               {ACL_WRITE_DATA, ARCHIVE_ENTRY_ACL_WRITE_DATA},
+               {ACL_ADD_FILE, ARCHIVE_ENTRY_ACL_ADD_FILE},
+               {ACL_APPEND_DATA, ARCHIVE_ENTRY_ACL_APPEND_DATA},
+               {ACL_ADD_SUBDIRECTORY, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY},
+               {ACL_READ_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS},
+               {ACL_WRITE_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS},
+               {ACL_DELETE_CHILD, ARCHIVE_ENTRY_ACL_DELETE_CHILD},
+               {ACL_READ_ATTRIBUTES, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES},
+               {ACL_WRITE_ATTRIBUTES, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES},
+               {ACL_DELETE, ARCHIVE_ENTRY_ACL_DELETE},
+               {ACL_READ_ACL, ARCHIVE_ENTRY_ACL_READ_ACL},
+               {ACL_WRITE_ACL, ARCHIVE_ENTRY_ACL_WRITE_ACL},
+               {ACL_WRITE_OWNER, ARCHIVE_ENTRY_ACL_WRITE_OWNER},
+               {ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_SYNCHRONIZE}
+       };
+       int i, permset = 0;
+
+       for (i = 0; i < (int)(sizeof(perms)/sizeof(perms[0])); ++i)
+               if (acl_get_perm_np(opaque_ps, perms[i].machine))
+                       permset |= perms[i].portable;
+       return permset;
+}
+
+static int
+acl_flagset_to_bitmap(acl_flagset_t opaque_fs)
+{
+       static struct { int machine; int portable; } flags[] = {
+               {ACL_ENTRY_FILE_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT},
+               {ACL_ENTRY_DIRECTORY_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT},
+               {ACL_ENTRY_NO_PROPAGATE_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT},
+               {ACL_ENTRY_INHERIT_ONLY, ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY},
+       };
+       int i, flagset = 0;
+
+       for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); ++i)
+               if (acl_get_flag_np(opaque_fs, flags[i].machine))
+                       flagset |= flags[i].portable;
+       return flagset;
+}
+
+static int
+acl_match(acl_entry_t aclent, struct myacl_t *myacl)
+{
+       gid_t g, *gp;
+       uid_t u, *up;
+       acl_tag_t tag_type;
+       acl_permset_t opaque_ps;
+       acl_flagset_t opaque_fs;
+       int perms;
+
+       acl_get_tag_type(aclent, &tag_type);
+
+       /* translate the silly opaque permset to a bitmap */
+       acl_get_permset(aclent, &opaque_ps);
+       acl_get_flagset_np(aclent, &opaque_fs);
+       perms = acl_permset_to_bitmap(opaque_ps) | acl_flagset_to_bitmap(opaque_fs);
+       if (perms != myacl->permset)
+               return (0);
+
+       switch (tag_type) {
+       case ACL_USER_OBJ:
+               if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0);
+               break;
+       case ACL_USER:
+               if (myacl->tag != ARCHIVE_ENTRY_ACL_USER)
+                       return (0);
+               up = acl_get_qualifier(aclent);
+               u = *up;
+               acl_free(up);
+               if ((uid_t)myacl->qual != u)
+                       return (0);
+               break;
+       case ACL_GROUP_OBJ:
+               if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0);
+               break;
+       case ACL_GROUP:
+               if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP)
+                       return (0);
+               gp = acl_get_qualifier(aclent);
+               g = *gp;
+               acl_free(gp);
+               if ((gid_t)myacl->qual != g)
+                       return (0);
+               break;
+       case ACL_MASK:
+               if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0);
+               break;
+       case ACL_EVERYONE:
+               if (myacl->tag != ARCHIVE_ENTRY_ACL_EVERYONE) return (0);
+               break;
+       }
+       return (1);
+}
+
+static void
+compare_acls(acl_t acl, struct myacl_t *myacls, const char *filename, int start, int end)
+{
+       int *marker;
+       int entry_id = ACL_FIRST_ENTRY;
+       int matched;
+       int i, n;
+       acl_entry_t acl_entry;
+
+       n = end - start;
+       marker = malloc(sizeof(marker[0]) * (n + 1));
+       for (i = 0; i < n; i++)
+               marker[i] = i + start;
+       /* Always include the first ACE. */
+       if (start > 0) {
+         marker[n] = 0;
+         ++n;
+       }
+
+       /*
+        * Iterate over acls in system acl object, try to match each
+        * one with an item in the myacls array.
+        */
+       while (1 == acl_get_entry(acl, entry_id, &acl_entry)) {
+               /* After the first time... */
+               entry_id = ACL_NEXT_ENTRY;
+
+               /* Search for a matching entry (tag and qualifier) */
+               for (i = 0, matched = 0; i < n && !matched; i++) {
+                       if (acl_match(acl_entry, &myacls[marker[i]])) {
+                               /* We found a match; remove it. */
+                               marker[i] = marker[n - 1];
+                               n--;
+                               matched = 1;
+                       }
+               }
+
+               failure("ACL entry on file %s that shouldn't be there", filename);
+               assert(matched == 1);
+       }
+
+       /* Dump entries in the myacls array that weren't in the system acl. */
+       for (i = 0; i < n; ++i) {
+               failure(" ACL entry %d missing from %s: "
+                   "type=%d,permset=%x,tag=%d,qual=%d,name=``%s''\n",
+                   marker[i], filename,
+                   myacls[marker[i]].type, myacls[marker[i]].permset,
+                   myacls[marker[i]].tag, myacls[marker[i]].qual,
+                   myacls[marker[i]].name);
+               assert(0); /* Record this as a failure. */
+       }
+       free(marker);
+}
+
+static void
+compare_entry_acls(struct archive_entry *ae, struct myacl_t *myacls, const char *filename, int start, int end)
+{
+       int *marker;
+       int matched;
+       int i, n;
+       int type, permset, tag, qual;
+       const char *name;
+
+       /* Count ACL entries in myacls array and allocate an indirect array. */
+       n = end - start;
+       marker = malloc(sizeof(marker[0]) * (n + 1));
+       for (i = 0; i < n; i++)
+               marker[i] = i + start;
+       /* Always include the first ACE. */
+       if (start > 0) {
+         marker[n] = 0;
+         ++n;
+       }
+
+       /*
+        * Iterate over acls in entry, try to match each
+        * one with an item in the myacls array.
+        */
+       assertEqualInt(n, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+       while (ARCHIVE_OK == archive_entry_acl_next(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4, &type, &permset, &tag, &qual, &name)) {
+
+               /* Search for a matching entry (tag and qualifier) */
+               for (i = 0, matched = 0; i < n && !matched; i++) {
+                       if (tag == myacls[marker[i]].tag
+                           && qual == myacls[marker[i]].qual
+                           && permset == myacls[marker[i]].permset
+                           && type == myacls[marker[i]].type) {
+                               /* We found a match; remove it. */
+                               marker[i] = marker[n - 1];
+                               n--;
+                               matched = 1;
+                       }
+               }
+
+               failure("ACL entry on file that shouldn't be there: "
+                       "type=%d,permset=%x,tag=%d,qual=%d",
+                       type,permset,tag,qual);
+               assert(matched == 1);
+       }
+
+       /* Dump entries in the myacls array that weren't in the system acl. */
+       for (i = 0; i < n; ++i) {
+               failure(" ACL entry %d missing from %s: "
+                   "type=%d,permset=%x,tag=%d,qual=%d,name=``%s''\n",
+                   marker[i], filename,
+                   myacls[marker[i]].type, myacls[marker[i]].permset,
+                   myacls[marker[i]].tag, myacls[marker[i]].qual,
+                   myacls[marker[i]].name);
+               assert(0); /* Record this as a failure. */
+       }
+       free(marker);
+}
+#endif
+
+/*
+ * Verify ACL restore-to-disk.  This test is FreeBSD-specific.
+ */
+
+DEFINE_TEST(test_acl_freebsd_nfs4)
+{
+#if !defined(__FreeBSD__)
+       skipping("FreeBSD-specific NFS4 ACL restore test");
+#elif __FreeBSD__ < 8
+       skipping("NFS4 ACLs supported only on FreeBSD 8.0 and later");
+#else
+       char buff[64];
+       struct stat st;
+       struct archive *a;
+       struct archive_entry *ae;
+       int i, n;
+       acl_t acl;
+
+       /*
+        * First, do a quick manual set/read of ACL data to
+        * verify that the local filesystem does support ACLs.
+        * If it doesn't, we'll simply skip the remaining tests.
+        */
+       acl = acl_from_text("owner@:rwxp::allow,group@:rwp:f:allow");
+       assert((void *)acl != NULL);
+       /* Create a test dir and try to set an ACL on it. */
+       if (!assertMakeDir("pretest", 0755)) {
+               acl_free(acl);
+               return;
+       }
+
+       n = acl_set_file("pretest", ACL_TYPE_NFS4, acl);
+       acl_free(acl);
+       if (n != 0 && errno == EOPNOTSUPP) {
+               skipping("NFS4 ACL tests require that NFS4 ACLs"
+                   " be enabled on the filesystem");
+               return;
+       }
+       if (n != 0 && errno == EINVAL) {
+               skipping("This filesystem does not support NFS4 ACLs");
+               return;
+       }
+       failure("acl_set_file(): errno = %d (%s)",
+           errno, strerror(errno));
+       assertEqualInt(0, n);
+
+       /* Create a write-to-disk object. */
+       assert(NULL != (a = archive_write_disk_new()));
+       archive_write_disk_set_options(a,
+           ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL);
+
+       /* Populate an archive entry with some metadata, including ACL info */
+       ae = archive_entry_new();
+       assert(ae != NULL);
+       archive_entry_set_pathname(ae, "testall");
+       archive_entry_set_filetype(ae, AE_IFREG);
+       archive_entry_set_perm(ae, 0654);
+       archive_entry_set_mtime(ae, 123456, 7890);
+       archive_entry_set_size(ae, 0);
+       set_acls(ae, acls_reg, 0, (int)(sizeof(acls_reg)/sizeof(acls_reg[0])));
+
+       /* Write the entry to disk, including ACLs. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+
+       /* Likewise for a dir. */
+       archive_entry_set_pathname(ae, "dirall");
+       archive_entry_set_filetype(ae, AE_IFDIR);
+       archive_entry_set_perm(ae, 0654);
+       archive_entry_set_mtime(ae, 123456, 7890);
+       set_acls(ae, acls_dir, 0, (int)(sizeof(acls_dir)/sizeof(acls_dir[0])));
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+
+       for (i = 0; i < (int)(sizeof(acls_dir)/sizeof(acls_dir[0])); ++i) {
+         sprintf(buff, "dir%d", i);
+         archive_entry_set_pathname(ae, buff);
+         archive_entry_set_filetype(ae, AE_IFDIR);
+         archive_entry_set_perm(ae, 0654);
+         archive_entry_set_mtime(ae, 123456 + i, 7891 + i);
+         set_acls(ae, acls_dir, i, i + 1);
+         assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+       }
+
+       archive_entry_free(ae);
+
+       /* Close the archive. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+       /* Verify the data on disk. */
+       assertEqualInt(0, stat("testall", &st));
+       assertEqualInt(st.st_mtime, 123456);
+       acl = acl_get_file("testall", ACL_TYPE_NFS4);
+       assert(acl != (acl_t)NULL);
+       compare_acls(acl, acls_reg, "testall", 0, (int)(sizeof(acls_reg)/sizeof(acls_reg[0])));
+       acl_free(acl);
+
+       /* Verify single-permission dirs on disk. */
+       for (i = 0; i < (int)(sizeof(acls_dir)/sizeof(acls_dir[0])); ++i) {
+         sprintf(buff, "dir%d", i);
+         assertEqualInt(0, stat(buff, &st));
+         assertEqualInt(st.st_mtime, 123456 + i);
+         acl = acl_get_file(buff, ACL_TYPE_NFS4);
+         assert(acl != (acl_t)NULL);
+         compare_acls(acl, acls_dir, buff, i, i + 1);
+         acl_free(acl);
+       }
+
+       /* Verify "dirall" on disk. */
+       assertEqualInt(0, stat("dirall", &st));
+       assertEqualInt(st.st_mtime, 123456);
+       acl = acl_get_file("dirall", ACL_TYPE_NFS4);
+       assert(acl != (acl_t)NULL);
+       compare_acls(acl, acls_dir, "dirall", 0,  (int)(sizeof(acls_dir)/sizeof(acls_dir[0])));
+       acl_free(acl);
+
+       /* Read and compare ACL via archive_read_disk */
+       a = archive_read_disk_new();
+       assert(a != NULL);
+       ae = archive_entry_new();
+       assert(ae != NULL);
+       archive_entry_set_pathname(ae, "testall");
+       assertEqualInt(ARCHIVE_OK,
+                      archive_read_disk_entry_from_file(a, ae, -1, NULL));
+       compare_entry_acls(ae, acls_reg, "testall", 0, (int)(sizeof(acls_reg)/sizeof(acls_reg[0])));
+       archive_entry_free(ae);
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+       /* Read and compare ACL via archive_read_disk */
+       a = archive_read_disk_new();
+       assert(a != NULL);
+       ae = archive_entry_new();
+       assert(ae != NULL);
+       archive_entry_set_pathname(ae, "dirall");
+       assertEqualInt(ARCHIVE_OK,
+                      archive_read_disk_entry_from_file(a, ae, -1, NULL));
+       compare_entry_acls(ae, acls_dir, "dirall", 0,  (int)(sizeof(acls_dir)/sizeof(acls_dir[0])));
+       archive_entry_free(ae);
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+#endif
+}
similarity index 99%
rename from libarchive/test/test_acl_freebsd.c
rename to libarchive/test/test_acl_freebsd_posix1e.c
index 1680f9f80e553ea73d020e27970501faab84bb96..d2ae6d476936f8fbae32b04bdb3e2a93c918a473 100644 (file)
@@ -185,7 +185,7 @@ compare_acls(acl_t acl, struct myacl_t *myacls)
  * Verify ACL restore-to-disk.  This test is FreeBSD-specific.
  */
 
-DEFINE_TEST(test_acl_freebsd)
+DEFINE_TEST(test_acl_freebsd_posix1e)
 {
 #if !defined(__FreeBSD__)
        skipping("FreeBSD-specific ACL restore test");
index 8832787ee63de67042d6cf92f89d83192be20b31..0008e1cfb70dab6e4156d2b3ea98357330c2476c 100644 (file)
@@ -58,14 +58,19 @@ verify_files(const char *target)
        assertChdir(target);
 
        /* Regular file with 2 links. */
+       failure("%s", target);
        assertIsReg("file", -1);
+       failure("%s", target);
        assertFileSize("file", 10);
+       failure("%s", target);
        assertFileContents("123456789", 10, "file");
        failure("%s", target);
        assertFileNLinks("file", 2);
 
        /* Another name for the same file. */
+       failure("%s", target);
        assertIsReg("linkfile", -1);
+       failure("%s", target);
        assertFileSize("linkfile", 10);
        assertFileContents("123456789", 10, "linkfile");
        assertFileNLinks("linkfile", 2);
@@ -76,6 +81,7 @@ verify_files(const char *target)
                assertIsSymlink("symlink", "file");
 
        /* dir */
+       failure("%s", target);
        assertIsDir("dir", 0775);
        assertChdir("..");
 }