From unknown Mon Aug 18 04:46:43 2025 X-Loop: help-debbugs@gnu.org Subject: bug#20311: [GNULIB 0/4] Improved acl handling Resent-From: Andreas Gruenbacher Original-Sender: "Debbugs-submit" Resent-CC: bug-coreutils@gnu.org Resent-Date: Sun, 12 Apr 2015 16:20:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 20311 X-GNU-PR-Package: coreutils X-GNU-PR-Keywords: To: bug-gnulib@gnu.org, 20311@debbugs.gnu.org X-Debbugs-Original-To: bug-gnulib@gnu.org, bug-coreutils@gnu.org Received: via spool by submit@debbugs.gnu.org id=B.142885558925173 (code B ref -1); Sun, 12 Apr 2015 16:20:02 +0000 Received: (at submit) by debbugs.gnu.org; 12 Apr 2015 16:19:49 +0000 Received: from localhost ([127.0.0.1]:54106 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1YhKbs-0006Xv-GT for submit@debbugs.gnu.org; Sun, 12 Apr 2015 12:19:49 -0400 Received: from eggs.gnu.org ([208.118.235.92]:55288) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1YhJ0s-0004AI-4U for submit@debbugs.gnu.org; Sun, 12 Apr 2015 10:37:30 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YhJ0l-0007ff-JZ for submit@debbugs.gnu.org; Sun, 12 Apr 2015 10:37:24 -0400 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on eggs.gnu.org X-Spam-Level: X-Spam-Status: No, score=0.8 required=5.0 tests=BAYES_50,FREEMAIL_FROM, T_DKIM_INVALID autolearn=disabled version=3.3.2 Received: from lists.gnu.org ([2001:4830:134:3::11]:51446) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YhJ0l-0007fa-GG for submit@debbugs.gnu.org; Sun, 12 Apr 2015 10:37:23 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40468) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YhJ0k-0003Xq-Fn for bug-coreutils@gnu.org; Sun, 12 Apr 2015 10:37:23 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YhJ0j-0007e3-6H for bug-coreutils@gnu.org; Sun, 12 Apr 2015 10:37:22 -0400 Received: from mail-wi0-x231.google.com ([2a00:1450:400c:c05::231]:36747) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YhJ0e-0007cp-Cm; Sun, 12 Apr 2015 10:37:16 -0400 Received: by wizk4 with SMTP id k4so44644132wiz.1; Sun, 12 Apr 2015 07:37:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:subject:date:message-id; bh=GdgfhzF52n05Y+ZJPObRJbzb/5/rPzZ/I8l+DAgJfUs=; b=HFA2b0UYsvQT/hszSqXYqQiqt7z/JytVcdOMJ/hL7nCzZ4a+qXYnnDe7HuqfmTFs1N 4aItcbqn84E6hlIA7m4OF29WE9bz925YF8AT+uKAE+QeHcu1t1vR6NR4Ti7RJJk+PMOK /nnsmocuNGpOpeMbSogHr+m5Zl5Mf7dWmMHphdzy8hEe6CyUnjw6MAC7UyiNQApiFamG uDCMdAF4mpwa0KNW9NXuey3d9O/jeCDRj3aeHZ0Oge4Xn81RLSuNuI8l9GpLOFkp9Kdd 51W7JbLvhCvEbkSnx5pL+wC9re4F8MkY5tm84t3B3cbh9KmSM3IP3y0aBE0YALIBHo2k DPUw== X-Received: by 10.194.110.69 with SMTP id hy5mr19943845wjb.121.1428849435170; Sun, 12 Apr 2015 07:37:15 -0700 (PDT) Received: from nuc.home.com (80-110-92-235.cgn.dynamic.surfer.at. [80.110.92.235]) by mx.google.com with ESMTPSA id dz4sm8507166wib.17.2015.04.12.07.37.13 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 12 Apr 2015 07:37:14 -0700 (PDT) From: Andreas Gruenbacher Date: Sun, 12 Apr 2015 16:36:35 +0200 Message-Id: X-Mailer: git-send-email 2.1.0 X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2001:4830:134:3::11 X-Spam-Score: -4.0 (----) X-Mailman-Approved-At: Sun, 12 Apr 2015 12:19:47 -0400 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -4.0 (----) Hello, while looking into adding richacl support to coreutils, I found the set_acl and copy_acl gnulib functions in a less than ideal state: setting acls was implemented in both functions, with slightly differing behavior. Here is a rewrite based on the existing code which splits setting and setting permissions and implements set_acl and copy_acl based on these primitives; a lot of the mess in the original code disappears. These patches (and the richacl patches on top) are available here: https://github.com/andreas-gruenbacher/gnulib I will follow up with the necessary coreutils changes. Thanks, Andreas Andreas Gruenbacher (4): acl, qacl: split off shared functions into separate object file acl: On Linux, check for acls without libacl qacl: Simplify HP-UP acl_nontrivial check qacl: Reimplement qset_acl and qcopy_acl lib/acl-internal.c | 499 ++++++++++++++++++++++++++++++ lib/acl-internal.h | 44 ++- lib/file-has-acl.c | 477 ++--------------------------- lib/get-permissions.c | 287 +++++++++++++++++ lib/qcopy-acl.c | 542 +------------------------------- lib/qset-acl.c | 639 +------------------------------------- lib/set-permissions.c | 833 ++++++++++++++++++++++++++++++++++++++++++++++++++ m4/acl.m4 | 17 ++ modules/acl | 5 +- modules/qacl | 6 +- 10 files changed, 1730 insertions(+), 1619 deletions(-) create mode 100644 lib/acl-internal.c create mode 100644 lib/get-permissions.c create mode 100644 lib/set-permissions.c -- 2.1.0 From unknown Mon Aug 18 04:46:43 2025 X-Loop: help-debbugs@gnu.org Subject: bug#20311: [GNULIB 3/4] qacl: Simplify HP-UP acl_nontrivial check Resent-From: Andreas Gruenbacher Original-Sender: "Debbugs-submit" Resent-CC: bug-coreutils@gnu.org Resent-Date: Sun, 12 Apr 2015 16:53:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 20311 X-GNU-PR-Package: coreutils X-GNU-PR-Keywords: To: bug-gnulib@gnu.org, 20311@debbugs.gnu.org X-Debbugs-Original-To: bug-gnulib@gnu.org, bug-coreutils@gnu.org Received: via spool by submit@debbugs.gnu.org id=B.142885753428127 (code B ref -1); Sun, 12 Apr 2015 16:53:01 +0000 Received: (at submit) by debbugs.gnu.org; 12 Apr 2015 16:52:14 +0000 Received: from localhost ([127.0.0.1]:54134 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1YhL7F-0007JZ-WD for submit@debbugs.gnu.org; Sun, 12 Apr 2015 12:52:14 -0400 Received: from eggs.gnu.org ([208.118.235.92]:55386) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1YhJ0v-0004AL-9E for submit@debbugs.gnu.org; Sun, 12 Apr 2015 10:37:33 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YhJ0n-0007je-L7 for submit@debbugs.gnu.org; Sun, 12 Apr 2015 10:37:28 -0400 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on eggs.gnu.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,FREEMAIL_FROM, T_DKIM_INVALID autolearn=disabled version=3.3.2 Received: from lists.gnu.org ([2001:4830:134:3::11]:35156) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YhJ0n-0007jC-FJ for submit@debbugs.gnu.org; Sun, 12 Apr 2015 10:37:25 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40501) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YhJ0l-0003aZ-Mi for bug-coreutils@gnu.org; Sun, 12 Apr 2015 10:37:25 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YhJ0k-0007eP-6B for bug-coreutils@gnu.org; Sun, 12 Apr 2015 10:37:23 -0400 Received: from mail-wg0-x232.google.com ([2a00:1450:400c:c00::232]:34917) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YhJ0h-0007dT-Qk; Sun, 12 Apr 2015 10:37:19 -0400 Received: by wgyo15 with SMTP id o15so57704853wgy.2; Sun, 12 Apr 2015 07:37:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:subject:date:message-id:in-reply-to:references:in-reply-to :references; bh=FmQoYkY9keDQtBHWwDKTYZ+syKII6FYtJ1q8WfxkiCY=; b=Djyxi2md7LUHfxYyd9wzxrgWoGSi+3WkiSS+K68tKTzxUNHjUQSSWXVaIOmJsX/VkH 2LFeM9CaR4Zdmn6j8hhD6124zgmYNcqWK6FsnT2NwqeQsC74V0sj+bRhP4bKeqJwBUXU IRJcVJ6DTKRzLtYTWv05SReuHe1qCG6XkqtVHnagKLKhIaAHHTPGMMfyC7bsk5gX0AMV YqPDHiizF1P8tR0twC6JvYz3mZo47gtpqGoPPl8WLBx9rQapZlJwvV5ciBuIT9Ls5OE/ mwi1BujBHHbYyGDDXBwpvzRuYr56RAsJg6vPHlHHR8h2Fik4whsDUuAAGDuwJlLHFQ17 DwrQ== X-Received: by 10.194.243.9 with SMTP id wu9mr19723620wjc.107.1428849439243; Sun, 12 Apr 2015 07:37:19 -0700 (PDT) Received: from nuc.home.com (80-110-92-235.cgn.dynamic.surfer.at. [80.110.92.235]) by mx.google.com with ESMTPSA id dz4sm8507166wib.17.2015.04.12.07.37.17 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 12 Apr 2015 07:37:18 -0700 (PDT) From: Andreas Gruenbacher Date: Sun, 12 Apr 2015 16:36:38 +0200 Message-Id: <923bd4658139f5b796e3a6b672140541f2c7372e.1428848851.git.andreas.gruenbacher@gmail.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: References: In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2001:4830:134:3::11 X-Spam-Score: -4.0 (----) X-Mailman-Approved-At: Sun, 12 Apr 2015 12:52:12 -0400 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -4.0 (----) * lib/acl-internal.c: Remove struct stat parameter from HP-UX's version of acl_nontrivial. Check if the acl has at most three entries instead (it must have exactly three entries according to the HP-UX documentation). Ignore uids and gids as long as an entry is either for a user (i.e., the owner), a group (i.e., the owning group), or others. * lib/acl-internal.h: Change HP-UX's acl_nontrivial prototype. * lib/qcopy-acl.c (qcopy_acl): With that, we no longer need to stat the source file. --- lib/acl-internal.c | 11 ++++++----- lib/acl-internal.h | 2 +- lib/qcopy-acl.c | 17 +++-------------- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/lib/acl-internal.c b/lib/acl-internal.c index 7a465c1..d9bd446 100644 --- a/lib/acl-internal.c +++ b/lib/acl-internal.c @@ -336,18 +336,19 @@ acl_ace_nontrivial (int count, ace_t *entries) /* Return 1 if the given ACL is non-trivial. Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ int -acl_nontrivial (int count, struct acl_entry *entries, struct stat *sb) +acl_nontrivial (int count, struct acl_entry *entries) { int i; + if (count > 3) + return 1; + for (i = 0; i < count; i++) { struct acl_entry *ace = &entries[i]; - if (!((ace->uid == sb->st_uid && ace->gid == ACL_NSGROUP) - || (ace->uid == ACL_NSUSER && ace->gid == sb->st_gid) - || (ace->uid == ACL_NSUSER && ace->gid == ACL_NSGROUP))) - return 1; + if (ace->uid != ACL_NSUSER && ace->gid != ACL_NSGROUP) + return 1; } return 0; } diff --git a/lib/acl-internal.h b/lib/acl-internal.h index 243ca32..9b9fae2 100644 --- a/lib/acl-internal.h +++ b/lib/acl-internal.h @@ -220,7 +220,7 @@ extern int acl_ace_nontrivial (int count, ace_t *entries) _GL_ATTRIBUTE_PURE; /* Return 1 if the given ACL is non-trivial. Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ -extern int acl_nontrivial (int count, struct acl_entry *entries, struct stat *sb); +extern int acl_nontrivial (int count, struct acl_entry *entries); # if HAVE_ACLV_H /* HP-UX >= 11.11 */ diff --git a/lib/qcopy-acl.c b/lib/qcopy-acl.c index aac76a1..bc258ba 100644 --- a/lib/qcopy-acl.c +++ b/lib/qcopy-acl.c @@ -437,20 +437,9 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name, if (ret < 0 && saved_errno == 0) { saved_errno = errno; - if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) - { - struct stat source_statbuf; - - if ((source_desc != -1 - ? fstat (source_desc, &source_statbuf) - : stat (src_name, &source_statbuf)) == 0) - { - if (!acl_nontrivial (count, entries, &source_statbuf)) - saved_errno = 0; - } - else - saved_errno = errno; - } + if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP + && !acl_nontrivial (count, entries)) + saved_errno = 0; } else did_chmod = 1; -- 2.1.0 From unknown Mon Aug 18 04:46:43 2025 X-Loop: help-debbugs@gnu.org Subject: bug#20311: [GNULIB 2/4] acl: On Linux, check for acls without libacl Resent-From: Andreas Gruenbacher Original-Sender: "Debbugs-submit" Resent-CC: bug-coreutils@gnu.org Resent-Date: Sun, 12 Apr 2015 16:53:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 20311 X-GNU-PR-Package: coreutils X-GNU-PR-Keywords: To: bug-gnulib@gnu.org, 20311@debbugs.gnu.org X-Debbugs-Original-To: bug-gnulib@gnu.org, bug-coreutils@gnu.org Received: via spool by submit@debbugs.gnu.org id=B.142885753528135 (code B ref -1); Sun, 12 Apr 2015 16:53:02 +0000 Received: (at submit) by debbugs.gnu.org; 12 Apr 2015 16:52:15 +0000 Received: from localhost ([127.0.0.1]:54136 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1YhL7G-0007Jc-Mi for submit@debbugs.gnu.org; Sun, 12 Apr 2015 12:52:15 -0400 Received: from eggs.gnu.org ([208.118.235.92]:55388) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1YhJ0v-0004AM-9I for submit@debbugs.gnu.org; Sun, 12 Apr 2015 10:37:34 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YhJ0n-0007ib-0t for submit@debbugs.gnu.org; Sun, 12 Apr 2015 10:37:28 -0400 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on eggs.gnu.org X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=BAYES_20,FREEMAIL_FROM, T_DKIM_INVALID autolearn=disabled version=3.3.2 Received: from lists.gnu.org ([2001:4830:134:3::11]:42276) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YhJ0m-0007iH-RW for submit@debbugs.gnu.org; Sun, 12 Apr 2015 10:37:24 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40493) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YhJ0l-0003Zm-F4 for bug-coreutils@gnu.org; Sun, 12 Apr 2015 10:37:24 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YhJ0k-0007eX-Cb for bug-coreutils@gnu.org; Sun, 12 Apr 2015 10:37:23 -0400 Received: from mail-wg0-x236.google.com ([2a00:1450:400c:c00::236]:33933) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YhJ0g-0007dJ-FQ; Sun, 12 Apr 2015 10:37:18 -0400 Received: by wgso17 with SMTP id o17so57712471wgs.1; Sun, 12 Apr 2015 07:37:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:subject:date:message-id:in-reply-to:references:in-reply-to :references; bh=vDiSjsRuJ7T/TGB4hRC30azdZOPBVHzD9N347gtpKNw=; b=gtQQl1ATLHyn88iWXAjfwhRiPBPjmQWf9y+drHDnNN0oJsRjX4La8CtGZKwcp+177h Zh5aCmUcGhPwr2iaLO3EPVoL65T9G4JvXJ+jxISo8eQ21GBRbu6qo42uNMs/s+kLFD5l 81163yx1WIvtFQ2J2YaarsttFW5UghMGHa7r9MScefEgcLdNeg2DmMnI8O0+WK/Pg1qu wUwaFVYjV1Bl/u+0uxY/8WsxJMpMy9Gq8++VcY6dvQIBAlgUykU7dJAjnEWYnUyVbNrH fhebekkNzmBPAf9RBBjPby5hAyTdM7fM39COjf7tV8IdNYGeJotcfa/sZjHwXeBveWmG 1UGQ== X-Received: by 10.194.179.38 with SMTP id dd6mr19778722wjc.149.1428849437868; Sun, 12 Apr 2015 07:37:17 -0700 (PDT) Received: from nuc.home.com (80-110-92-235.cgn.dynamic.surfer.at. [80.110.92.235]) by mx.google.com with ESMTPSA id dz4sm8507166wib.17.2015.04.12.07.37.16 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 12 Apr 2015 07:37:17 -0700 (PDT) From: Andreas Gruenbacher Date: Sun, 12 Apr 2015 16:36:37 +0200 Message-Id: <9f5b8b74b2ed7b923c0b3d590c2213d0a6af077b.1428848851.git.andreas.gruenbacher@gmail.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: References: In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2001:4830:134:3::11 X-Spam-Score: -4.0 (----) X-Mailman-Approved-At: Sun, 12 Apr 2015 12:52:12 -0400 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -4.0 (----) On Linux, use the getxattr syscall instead of the acl_extended_file libacl library function to check for the presence of acls, avoiding a library dependency. * lib/file-has-acl.c: Include xattr headers if we have them. (file_has_acl): On Linux, use getxattr(). * m4/acl.m4 (gl_FUNC_ACL): Define LIB_HAS_ACL as the libraries to link with for file_has_acl(). Check for xattr headers and getxattr(). --- lib/file-has-acl.c | 35 ++++++++++++++++++++++++++++++++++- m4/acl.m4 | 17 +++++++++++++++++ modules/acl | 2 ++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c index de89a75..5dfe9a8 100644 --- a/lib/file-has-acl.c +++ b/lib/file-has-acl.c @@ -29,6 +29,13 @@ #include "acl-internal.h" +#if HAVE_SYS_XATTR_H +# include +#endif + +#if HAVE_LINUX_XATTR_H +# include +#endif /* Return 1 if NAME has a nontrivial access control list, 0 if NAME only has no or a base access control list, and -1 (setting errno) @@ -41,7 +48,33 @@ file_has_acl (char const *name, struct stat const *sb) #if USE_ACL if (! S_ISLNK (sb->st_mode)) { -# if HAVE_ACL_GET_FILE + +# if HAVE_GETXATTR && defined XATTR_NAME_POSIX_ACL_ACCESS && defined XATTR_NAME_POSIX_ACL_DEFAULT + + ssize_t ret; + + ret = getxattr (name, XATTR_NAME_POSIX_ACL_ACCESS, NULL, 0); + if (ret < 0) + { + if (errno != ENODATA) + return -1; + } + else if (ret > 0) + return 1; + if (S_ISDIR (sb->st_mode)) + { + ret = getxattr (name, XATTR_NAME_POSIX_ACL_DEFAULT, NULL, 0); + if (ret < 0) + { + if (errno != ENODATA) + return -1; + } + else if (ret > 0) + return 1; + } + return 0; + +# elif HAVE_ACL_GET_FILE /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */ diff --git a/m4/acl.m4 b/m4/acl.m4 index c77f0ed..a6c2a0d 100644 --- a/m4/acl.m4 +++ b/m4/acl.m4 @@ -16,6 +16,7 @@ AC_DEFUN([gl_FUNC_ACL], AC_CHECK_FUNCS_ONCE([fchmod]) LIB_ACL= + LIB_HAS_ACL= use_acl=0 if test "x$enable_acl" != "xno"; then dnl On all platforms, the ACL related API is declared in . @@ -124,6 +125,21 @@ int type = ACL_TYPE_EXTENDED;]])], LIBS=$ac_save_LIBS fi + + dnl On Linux, testing if a file has an acl can be done with the getxattr + dnl syscall which doesn't require linking against additional libraries. + use_xattrs=0 + AC_CHECK_HEADERS([sys/xattr.h linux/xattr.h]) + if test $ac_cv_header_sys_xattr_h = yes && test $ac_cv_header_linux_xattr_h = yes; then + AC_CHECK_FUNCS([getxattr]) + if test $ac_cv_func_getxattr = yes; then + use_xattrs=1 + fi + fi + if test use_xattrs = 0; then + LIB_HAS_ACL=$LIB_ACL + fi + if test "x$enable_acl$use_acl" = "xyes0"; then AC_MSG_ERROR([ACLs enabled but support not detected]) elif test "x$enable_acl$use_acl" = "xauto0"; then @@ -132,6 +148,7 @@ int type = ACL_TYPE_EXTENDED;]])], fi fi AC_SUBST([LIB_ACL]) + AC_SUBST([LIB_HAS_ACL]) AC_DEFINE_UNQUOTED([USE_ACL], [$use_acl], [Define to nonzero if you want access control list support.]) USE_ACL=$use_acl diff --git a/modules/acl b/modules/acl index fdb0d72..7faec17 100644 --- a/modules/acl +++ b/modules/acl @@ -11,6 +11,7 @@ error gettext-h qacl quote +stdbool configure.ac: @@ -22,6 +23,7 @@ Include: Link: $(LIB_ACL) +$(LIB_HAS_ACL) License: GPL -- 2.1.0 From unknown Mon Aug 18 04:46:43 2025 X-Loop: help-debbugs@gnu.org Subject: bug#20311: [GNULIB 1/4] acl, qacl: split off shared functions into separate object file Resent-From: Andreas Gruenbacher Original-Sender: "Debbugs-submit" Resent-CC: bug-coreutils@gnu.org Resent-Date: Sun, 12 Apr 2015 16:53:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 20311 X-GNU-PR-Package: coreutils X-GNU-PR-Keywords: To: bug-gnulib@gnu.org, 20311@debbugs.gnu.org X-Debbugs-Original-To: bug-gnulib@gnu.org, bug-coreutils@gnu.org Received: via spool by submit@debbugs.gnu.org id=B.142885753828144 (code B ref -1); Sun, 12 Apr 2015 16:53:02 +0000 Received: (at submit) by debbugs.gnu.org; 12 Apr 2015 16:52:18 +0000 Received: from localhost ([127.0.0.1]:54138 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1YhL7H-0007Jk-J3 for submit@debbugs.gnu.org; Sun, 12 Apr 2015 12:52:17 -0400 Received: from eggs.gnu.org ([208.118.235.92]:55583) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1YhJ14-0004B4-Df for submit@debbugs.gnu.org; Sun, 12 Apr 2015 10:37:44 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YhJ0u-0007pK-Nz for submit@debbugs.gnu.org; Sun, 12 Apr 2015 10:37:37 -0400 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on eggs.gnu.org X-Spam-Level: X-Spam-Status: No, score=0.8 required=5.0 tests=BAYES_50,FREEMAIL_FROM, T_DKIM_INVALID autolearn=disabled version=3.3.2 Received: from lists.gnu.org ([2001:4830:134:3::11]:53245) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YhJ0u-0007p8-KX for submit@debbugs.gnu.org; Sun, 12 Apr 2015 10:37:32 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40683) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YhJ0q-0003i6-QM for bug-coreutils@gnu.org; Sun, 12 Apr 2015 10:37:32 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YhJ0m-0007i2-Kf for bug-coreutils@gnu.org; Sun, 12 Apr 2015 10:37:28 -0400 Received: from mail-wg0-x233.google.com ([2a00:1450:400c:c00::233]:34915) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YhJ0f-0007d1-7S; Sun, 12 Apr 2015 10:37:17 -0400 Received: by wgyo15 with SMTP id o15so57704366wgy.2; Sun, 12 Apr 2015 07:37:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:subject:date:message-id:in-reply-to:references:in-reply-to :references:mime-version:content-type:content-transfer-encoding; bh=WqwU7PI2TomsUT/X2TN/Z3yn+O5xa5pQK50cDCXIhOM=; b=SdyrnRR4SwMtRjdcFQDu0wJc5FdcTjaadOUeJN6z+VVSqj5fl9zxfErdmY2xADQf2t Yb9vYWPtavmasA0Md9uobId5F2mN0ttpzhO6g2bo3TjEVnPwh2ct/GAY6HP1hV1pV8mu o1pRKoMpdCWIGkg12p5mT0m3Dy1zWADDVHwuLrSEMaIlHgHYhe3sp4adV2vo7rjiUzFB eXAphC27aH0LyTIuzp5aX0AAXBUtq7+Uxf/WAD5w3vqR2TM1gpOokhhMeWllf7XA+y/+ lqZ1/O6v/ttoUB/4ugLt+n4Zt4TOI45G8KlzAZtJztepqinXY+i6B4vFSQX8112wsV0w rFog== X-Received: by 10.194.210.103 with SMTP id mt7mr19785537wjc.103.1428849436606; Sun, 12 Apr 2015 07:37:16 -0700 (PDT) Received: from nuc.home.com (80-110-92-235.cgn.dynamic.surfer.at. [80.110.92.235]) by mx.google.com with ESMTPSA id dz4sm8507166wib.17.2015.04.12.07.37.15 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 12 Apr 2015 07:37:15 -0700 (PDT) From: Andreas Gruenbacher Date: Sun, 12 Apr 2015 16:36:36 +0200 Message-Id: X-Mailer: git-send-email 2.1.0 In-Reply-To: References: In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2001:4830:134:3::11 X-Spam-Score: -4.0 (----) X-Mailman-Approved-At: Sun, 12 Apr 2015 12:52:13 -0400 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -4.0 (----) Some of the helper functions in file-has-acl.c are used from qcopy-acl.c while file_has_acl() isn't needed there. Split the shared functions off into acl-internal.c. * lib/file-has-acl.c: Remove helper functions here. * lib/acl-internal.c: Add helper functions here. * modules/qacl: Use acl-internal.c instead of file-has-acl.c here. * modules/acl: Add file-has-acl.c now that qacl doesn't use it anymore. --- lib/acl-internal.c | 468 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/file-has-acl.c | 446 -------------------------------------------------- modules/acl | 3 +- modules/qacl | 4 +- 4 files changed, 472 insertions(+), 449 deletions(-) create mode 100644 lib/acl-internal.c diff --git a/lib/acl-internal.c b/lib/acl-internal.c new file mode 100644 index 0000000..7a465c1 --- /dev/null +++ b/lib/acl-internal.c @@ -0,0 +1,468 @@ +/* Test whether a file has a nontrivial access control list. + + Copyright (C) 2002-2003, 2005-2015 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */ + +#include + +#include "acl.h" + +#include "acl-internal.h" + +#if USE_ACL && HAVE_ACL_GET_FILE + +# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */ + +/* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED. + Return 1 if the given ACL is non-trivial. + Return 0 if it is trivial. */ +int +acl_extended_nontrivial (acl_t acl) +{ + /* acl is non-trivial if it is non-empty. */ + return (acl_entries (acl) > 0); +} + +# else /* Linux, FreeBSD, IRIX, Tru64 */ + +/* ACL is an ACL, from a file, stored as type ACL_TYPE_ACCESS. + Return 1 if the given ACL is non-trivial. + Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. + Return -1 and set errno upon failure to determine it. */ +int +acl_access_nontrivial (acl_t acl) +{ + /* acl is non-trivial if it has some entries other than for "user::", + "group::", and "other::". Normally these three should be present + at least, allowing us to write + return (3 < acl_entries (acl)); + but the following code is more robust. */ +# if HAVE_ACL_FIRST_ENTRY /* Linux, FreeBSD */ + + acl_entry_t ace; + int got_one; + + for (got_one = acl_get_entry (acl, ACL_FIRST_ENTRY, &ace); + got_one > 0; + got_one = acl_get_entry (acl, ACL_NEXT_ENTRY, &ace)) + { + acl_tag_t tag; + if (acl_get_tag_type (ace, &tag) < 0) + return -1; + if (!(tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ || tag == ACL_OTHER)) + return 1; + } + return got_one; + +# elif HAVE_ACL_TO_SHORT_TEXT /* IRIX */ + /* Don't use acl_get_entry: it is undocumented. */ + + int count = acl->acl_cnt; + int i; + + for (i = 0; i < count; i++) + { + acl_entry_t ace = &acl->acl_entry[i]; + acl_tag_t tag = ace->ae_tag; + + if (!(tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ + || tag == ACL_OTHER_OBJ)) + return 1; + } + return 0; + +# elif HAVE_ACL_FREE_TEXT /* Tru64 */ + /* Don't use acl_get_entry: it takes only one argument and does not work. */ + + int count = acl->acl_num; + acl_entry_t ace; + + for (ace = acl->acl_first; count > 0; ace = ace->next, count--) + { + acl_tag_t tag; + acl_perm_t perm; + + tag = ace->entry->acl_type; + if (!(tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ || tag == ACL_OTHER)) + return 1; + + perm = ace->entry->acl_perm; + /* On Tru64, perm can also contain non-standard bits such as + PERM_INSERT, PERM_DELETE, PERM_MODIFY, PERM_LOOKUP, ... */ + if ((perm & ~(ACL_READ | ACL_WRITE | ACL_EXECUTE)) != 0) + return 1; + } + return 0; + +# else + + errno = ENOSYS; + return -1; +# endif +} + +# endif + +#elif USE_ACL && HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ + +/* Test an ACL retrieved with GETACL. + Return 1 if the given ACL, consisting of COUNT entries, is non-trivial. + Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ +int +acl_nontrivial (int count, aclent_t *entries) +{ + int i; + + for (i = 0; i < count; i++) + { + aclent_t *ace = &entries[i]; + + /* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat(). + If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat(). + We don't need to check ace->a_id in these cases. */ + if (!(ace->a_type == USER_OBJ + || ace->a_type == GROUP_OBJ + || ace->a_type == OTHER_OBJ + /* Note: Cygwin does not return a CLASS_OBJ ("mask:") entry + sometimes. */ + || ace->a_type == CLASS_OBJ)) + return 1; + } + return 0; +} + +# ifdef ACE_GETACL + +/* A shortcut for a bitmask. */ +# define NEW_ACE_WRITEA_DATA (NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA) + +/* Test an ACL retrieved with ACE_GETACL. + Return 1 if the given ACL, consisting of COUNT entries, is non-trivial. + Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ +int +acl_ace_nontrivial (int count, ace_t *entries) +{ + int i; + + /* The flags in the ace_t structure changed in a binary incompatible way + when ACL_NO_TRIVIAL etc. were introduced in version 1.15. + How to distinguish the two conventions at runtime? + In the old convention, usually three ACEs have a_flags = ACE_OWNER / + ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400. In the new + convention, these values are not used. */ + int old_convention = 0; + + for (i = 0; i < count; i++) + if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER)) + { + old_convention = 1; + break; + } + + if (old_convention) + /* Running on Solaris 10. */ + for (i = 0; i < count; i++) + { + ace_t *ace = &entries[i]; + + /* Note: + If ace->a_flags = ACE_OWNER, ace->a_who is the st_uid from stat(). + If ace->a_flags = ACE_GROUP, ace->a_who is the st_gid from stat(). + We don't need to check ace->a_who in these cases. */ + if (!(ace->a_type == OLD_ALLOW + && (ace->a_flags == OLD_ACE_OWNER + || ace->a_flags == OLD_ACE_GROUP + || ace->a_flags == OLD_ACE_OTHER))) + return 1; + } + else + { + /* Running on Solaris 10 (newer version) or Solaris 11. */ + unsigned int access_masks[6] = + { + 0, /* owner@ deny */ + 0, /* owner@ allow */ + 0, /* group@ deny */ + 0, /* group@ allow */ + 0, /* everyone@ deny */ + 0 /* everyone@ allow */ + }; + + for (i = 0; i < count; i++) + { + ace_t *ace = &entries[i]; + unsigned int index1; + unsigned int index2; + + if (ace->a_type == NEW_ACE_ACCESS_ALLOWED_ACE_TYPE) + index1 = 1; + else if (ace->a_type == NEW_ACE_ACCESS_DENIED_ACE_TYPE) + index1 = 0; + else + return 1; + + if (ace->a_flags == NEW_ACE_OWNER) + index2 = 0; + else if (ace->a_flags == (NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP)) + index2 = 2; + else if (ace->a_flags == NEW_ACE_EVERYONE) + index2 = 4; + else + return 1; + + access_masks[index1 + index2] |= ace->a_access_mask; + } + + /* The same bit shouldn't be both allowed and denied. */ + if (access_masks[0] & access_masks[1]) + return 1; + if (access_masks[2] & access_masks[3]) + return 1; + if (access_masks[4] & access_masks[5]) + return 1; + + /* Check minimum masks. */ + if ((NEW_ACE_WRITE_NAMED_ATTRS + | NEW_ACE_WRITE_ATTRIBUTES + | NEW_ACE_WRITE_ACL + | NEW_ACE_WRITE_OWNER) + & ~ access_masks[1]) + return 1; + access_masks[1] &= ~(NEW_ACE_WRITE_NAMED_ATTRS + | NEW_ACE_WRITE_ATTRIBUTES + | NEW_ACE_WRITE_ACL + | NEW_ACE_WRITE_OWNER); + if ((NEW_ACE_READ_NAMED_ATTRS + | NEW_ACE_READ_ATTRIBUTES + | NEW_ACE_READ_ACL + | NEW_ACE_SYNCHRONIZE) + & ~ access_masks[5]) + return 1; + access_masks[5] &= ~(NEW_ACE_READ_NAMED_ATTRS + | NEW_ACE_READ_ATTRIBUTES + | NEW_ACE_READ_ACL + | NEW_ACE_SYNCHRONIZE); + + /* Check the allowed or denied bits. */ + switch ((access_masks[0] | access_masks[1]) + & ~(NEW_ACE_READ_NAMED_ATTRS + | NEW_ACE_READ_ATTRIBUTES + | NEW_ACE_READ_ACL + | NEW_ACE_SYNCHRONIZE)) + { + case 0: + case NEW_ACE_READ_DATA: + case NEW_ACE_WRITEA_DATA: + case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA: + case NEW_ACE_EXECUTE: + case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE: + case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: + case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: + break; + default: + return 1; + } + switch ((access_masks[2] | access_masks[3]) + & ~(NEW_ACE_READ_NAMED_ATTRS + | NEW_ACE_READ_ATTRIBUTES + | NEW_ACE_READ_ACL + | NEW_ACE_SYNCHRONIZE)) + { + case 0: + case NEW_ACE_READ_DATA: + case NEW_ACE_WRITEA_DATA: + case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA: + case NEW_ACE_EXECUTE: + case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE: + case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: + case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: + break; + default: + return 1; + } + switch ((access_masks[4] | access_masks[5]) + & ~(NEW_ACE_WRITE_NAMED_ATTRS + | NEW_ACE_WRITE_ATTRIBUTES + | NEW_ACE_WRITE_ACL + | NEW_ACE_WRITE_OWNER)) + { + case 0: + case NEW_ACE_READ_DATA: + case NEW_ACE_WRITEA_DATA: + case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA: + case NEW_ACE_EXECUTE: + case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE: + case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: + case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: + break; + default: + return 1; + } + + /* Check that the NEW_ACE_WRITE_DATA and NEW_ACE_APPEND_DATA bits are + either both allowed or both denied. */ + if (((access_masks[0] & NEW_ACE_WRITE_DATA) != 0) + != ((access_masks[0] & NEW_ACE_APPEND_DATA) != 0)) + return 1; + if (((access_masks[2] & NEW_ACE_WRITE_DATA) != 0) + != ((access_masks[2] & NEW_ACE_APPEND_DATA) != 0)) + return 1; + if (((access_masks[4] & NEW_ACE_WRITE_DATA) != 0) + != ((access_masks[4] & NEW_ACE_APPEND_DATA) != 0)) + return 1; + } + + return 0; +} + +# endif + +#elif USE_ACL && HAVE_GETACL /* HP-UX */ + +/* Return 1 if the given ACL is non-trivial. + Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ +int +acl_nontrivial (int count, struct acl_entry *entries, struct stat *sb) +{ + int i; + + for (i = 0; i < count; i++) + { + struct acl_entry *ace = &entries[i]; + + if (!((ace->uid == sb->st_uid && ace->gid == ACL_NSGROUP) + || (ace->uid == ACL_NSUSER && ace->gid == sb->st_gid) + || (ace->uid == ACL_NSUSER && ace->gid == ACL_NSGROUP))) + return 1; + } + return 0; +} + +# if HAVE_ACLV_H /* HP-UX >= 11.11 */ + +/* Return 1 if the given ACL is non-trivial. + Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ +int +aclv_nontrivial (int count, struct acl *entries) +{ + int i; + + for (i = 0; i < count; i++) + { + struct acl *ace = &entries[i]; + + /* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat(). + If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat(). + We don't need to check ace->a_id in these cases. */ + if (!(ace->a_type == USER_OBJ /* no need to check ace->a_id here */ + || ace->a_type == GROUP_OBJ /* no need to check ace->a_id here */ + || ace->a_type == CLASS_OBJ + || ace->a_type == OTHER_OBJ)) + return 1; + } + return 0; +} + +# endif + +#elif USE_ACL && (HAVE_ACLX_GET || HAVE_STATACL) /* AIX */ + +/* Return 1 if the given ACL is non-trivial. + Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ +int +acl_nontrivial (struct acl *a) +{ + /* The normal way to iterate through an ACL is like this: + struct acl_entry *ace; + for (ace = a->acl_ext; ace != acl_last (a); ace = acl_nxt (ace)) + { + struct ace_id *aei; + switch (ace->ace_type) + { + case ACC_PERMIT: + case ACC_DENY: + case ACC_SPECIFY: + ...; + } + for (aei = ace->ace_id; aei != id_last (ace); aei = id_nxt (aei)) + ... + } + */ + return (acl_last (a) != a->acl_ext ? 1 : 0); +} + +# if HAVE_ACLX_GET && defined ACL_AIX_WIP /* newer AIX */ + +/* Return 1 if the given ACL is non-trivial. + Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ +int +acl_nfs4_nontrivial (nfs4_acl_int_t *a) +{ +# if 1 /* let's try this first */ + return (a->aclEntryN > 0 ? 1 : 0); +# else + int count = a->aclEntryN; + int i; + + for (i = 0; i < count; i++) + { + nfs4_ace_int_t *ace = &a->aclEntry[i]; + + if (!((ace->flags & ACE4_ID_SPECIAL) != 0 + && (ace->aceWho.special_whoid == ACE4_WHO_OWNER + || ace->aceWho.special_whoid == ACE4_WHO_GROUP + || ace->aceWho.special_whoid == ACE4_WHO_EVERYONE) + && ace->aceType == ACE4_ACCESS_ALLOWED_ACE_TYPE + && ace->aceFlags == 0 + && (ace->aceMask & ~(ACE4_READ_DATA | ACE4_LIST_DIRECTORY + | ACE4_WRITE_DATA | ACE4_ADD_FILE + | ACE4_EXECUTE)) == 0)) + return 1; + } + return 0; +# endif +} + +# endif + +#elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */ + +/* Test an ACL retrieved with ACL_GET. + Return 1 if the given ACL, consisting of COUNT entries, is non-trivial. + Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ +int +acl_nontrivial (int count, struct acl *entries) +{ + int i; + + for (i = 0; i < count; i++) + { + struct acl *ace = &entries[i]; + + /* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat(). + If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat(). + We don't need to check ace->a_id in these cases. */ + if (!(ace->a_type == USER_OBJ /* no need to check ace->a_id here */ + || ace->a_type == GROUP_OBJ /* no need to check ace->a_id here */ + || ace->a_type == CLASS_OBJ + || ace->a_type == OTHER_OBJ)) + return 1; + } + return 0; +} + +#endif diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c index 2ef4e8b..de89a75 100644 --- a/lib/file-has-acl.c +++ b/lib/file-has-acl.c @@ -30,452 +30,6 @@ #include "acl-internal.h" -#if USE_ACL && HAVE_ACL_GET_FILE - -# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */ - -/* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED. - Return 1 if the given ACL is non-trivial. - Return 0 if it is trivial. */ -int -acl_extended_nontrivial (acl_t acl) -{ - /* acl is non-trivial if it is non-empty. */ - return (acl_entries (acl) > 0); -} - -# else /* Linux, FreeBSD, IRIX, Tru64 */ - -/* ACL is an ACL, from a file, stored as type ACL_TYPE_ACCESS. - Return 1 if the given ACL is non-trivial. - Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. - Return -1 and set errno upon failure to determine it. */ -int -acl_access_nontrivial (acl_t acl) -{ - /* acl is non-trivial if it has some entries other than for "user::", - "group::", and "other::". Normally these three should be present - at least, allowing us to write - return (3 < acl_entries (acl)); - but the following code is more robust. */ -# if HAVE_ACL_FIRST_ENTRY /* Linux, FreeBSD */ - - acl_entry_t ace; - int got_one; - - for (got_one = acl_get_entry (acl, ACL_FIRST_ENTRY, &ace); - got_one > 0; - got_one = acl_get_entry (acl, ACL_NEXT_ENTRY, &ace)) - { - acl_tag_t tag; - if (acl_get_tag_type (ace, &tag) < 0) - return -1; - if (!(tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ || tag == ACL_OTHER)) - return 1; - } - return got_one; - -# elif HAVE_ACL_TO_SHORT_TEXT /* IRIX */ - /* Don't use acl_get_entry: it is undocumented. */ - - int count = acl->acl_cnt; - int i; - - for (i = 0; i < count; i++) - { - acl_entry_t ace = &acl->acl_entry[i]; - acl_tag_t tag = ace->ae_tag; - - if (!(tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ - || tag == ACL_OTHER_OBJ)) - return 1; - } - return 0; - -# elif HAVE_ACL_FREE_TEXT /* Tru64 */ - /* Don't use acl_get_entry: it takes only one argument and does not work. */ - - int count = acl->acl_num; - acl_entry_t ace; - - for (ace = acl->acl_first; count > 0; ace = ace->next, count--) - { - acl_tag_t tag; - acl_perm_t perm; - - tag = ace->entry->acl_type; - if (!(tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ || tag == ACL_OTHER)) - return 1; - - perm = ace->entry->acl_perm; - /* On Tru64, perm can also contain non-standard bits such as - PERM_INSERT, PERM_DELETE, PERM_MODIFY, PERM_LOOKUP, ... */ - if ((perm & ~(ACL_READ | ACL_WRITE | ACL_EXECUTE)) != 0) - return 1; - } - return 0; - -# else - - errno = ENOSYS; - return -1; -# endif -} - -# endif - - -#elif USE_ACL && HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ - -/* Test an ACL retrieved with GETACL. - Return 1 if the given ACL, consisting of COUNT entries, is non-trivial. - Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ -int -acl_nontrivial (int count, aclent_t *entries) -{ - int i; - - for (i = 0; i < count; i++) - { - aclent_t *ace = &entries[i]; - - /* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat(). - If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat(). - We don't need to check ace->a_id in these cases. */ - if (!(ace->a_type == USER_OBJ - || ace->a_type == GROUP_OBJ - || ace->a_type == OTHER_OBJ - /* Note: Cygwin does not return a CLASS_OBJ ("mask:") entry - sometimes. */ - || ace->a_type == CLASS_OBJ)) - return 1; - } - return 0; -} - -# ifdef ACE_GETACL - -/* A shortcut for a bitmask. */ -# define NEW_ACE_WRITEA_DATA (NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA) - -/* Test an ACL retrieved with ACE_GETACL. - Return 1 if the given ACL, consisting of COUNT entries, is non-trivial. - Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ -int -acl_ace_nontrivial (int count, ace_t *entries) -{ - int i; - - /* The flags in the ace_t structure changed in a binary incompatible way - when ACL_NO_TRIVIAL etc. were introduced in version 1.15. - How to distinguish the two conventions at runtime? - In the old convention, usually three ACEs have a_flags = ACE_OWNER / - ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400. In the new - convention, these values are not used. */ - int old_convention = 0; - - for (i = 0; i < count; i++) - if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER)) - { - old_convention = 1; - break; - } - - if (old_convention) - /* Running on Solaris 10. */ - for (i = 0; i < count; i++) - { - ace_t *ace = &entries[i]; - - /* Note: - If ace->a_flags = ACE_OWNER, ace->a_who is the st_uid from stat(). - If ace->a_flags = ACE_GROUP, ace->a_who is the st_gid from stat(). - We don't need to check ace->a_who in these cases. */ - if (!(ace->a_type == OLD_ALLOW - && (ace->a_flags == OLD_ACE_OWNER - || ace->a_flags == OLD_ACE_GROUP - || ace->a_flags == OLD_ACE_OTHER))) - return 1; - } - else - { - /* Running on Solaris 10 (newer version) or Solaris 11. */ - unsigned int access_masks[6] = - { - 0, /* owner@ deny */ - 0, /* owner@ allow */ - 0, /* group@ deny */ - 0, /* group@ allow */ - 0, /* everyone@ deny */ - 0 /* everyone@ allow */ - }; - - for (i = 0; i < count; i++) - { - ace_t *ace = &entries[i]; - unsigned int index1; - unsigned int index2; - - if (ace->a_type == NEW_ACE_ACCESS_ALLOWED_ACE_TYPE) - index1 = 1; - else if (ace->a_type == NEW_ACE_ACCESS_DENIED_ACE_TYPE) - index1 = 0; - else - return 1; - - if (ace->a_flags == NEW_ACE_OWNER) - index2 = 0; - else if (ace->a_flags == (NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP)) - index2 = 2; - else if (ace->a_flags == NEW_ACE_EVERYONE) - index2 = 4; - else - return 1; - - access_masks[index1 + index2] |= ace->a_access_mask; - } - - /* The same bit shouldn't be both allowed and denied. */ - if (access_masks[0] & access_masks[1]) - return 1; - if (access_masks[2] & access_masks[3]) - return 1; - if (access_masks[4] & access_masks[5]) - return 1; - - /* Check minimum masks. */ - if ((NEW_ACE_WRITE_NAMED_ATTRS - | NEW_ACE_WRITE_ATTRIBUTES - | NEW_ACE_WRITE_ACL - | NEW_ACE_WRITE_OWNER) - & ~ access_masks[1]) - return 1; - access_masks[1] &= ~(NEW_ACE_WRITE_NAMED_ATTRS - | NEW_ACE_WRITE_ATTRIBUTES - | NEW_ACE_WRITE_ACL - | NEW_ACE_WRITE_OWNER); - if ((NEW_ACE_READ_NAMED_ATTRS - | NEW_ACE_READ_ATTRIBUTES - | NEW_ACE_READ_ACL - | NEW_ACE_SYNCHRONIZE) - & ~ access_masks[5]) - return 1; - access_masks[5] &= ~(NEW_ACE_READ_NAMED_ATTRS - | NEW_ACE_READ_ATTRIBUTES - | NEW_ACE_READ_ACL - | NEW_ACE_SYNCHRONIZE); - - /* Check the allowed or denied bits. */ - switch ((access_masks[0] | access_masks[1]) - & ~(NEW_ACE_READ_NAMED_ATTRS - | NEW_ACE_READ_ATTRIBUTES - | NEW_ACE_READ_ACL - | NEW_ACE_SYNCHRONIZE)) - { - case 0: - case NEW_ACE_READ_DATA: - case NEW_ACE_WRITEA_DATA: - case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA: - case NEW_ACE_EXECUTE: - case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE: - case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: - case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: - break; - default: - return 1; - } - switch ((access_masks[2] | access_masks[3]) - & ~(NEW_ACE_READ_NAMED_ATTRS - | NEW_ACE_READ_ATTRIBUTES - | NEW_ACE_READ_ACL - | NEW_ACE_SYNCHRONIZE)) - { - case 0: - case NEW_ACE_READ_DATA: - case NEW_ACE_WRITEA_DATA: - case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA: - case NEW_ACE_EXECUTE: - case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE: - case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: - case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: - break; - default: - return 1; - } - switch ((access_masks[4] | access_masks[5]) - & ~(NEW_ACE_WRITE_NAMED_ATTRS - | NEW_ACE_WRITE_ATTRIBUTES - | NEW_ACE_WRITE_ACL - | NEW_ACE_WRITE_OWNER)) - { - case 0: - case NEW_ACE_READ_DATA: - case NEW_ACE_WRITEA_DATA: - case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA: - case NEW_ACE_EXECUTE: - case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE: - case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: - case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: - break; - default: - return 1; - } - - /* Check that the NEW_ACE_WRITE_DATA and NEW_ACE_APPEND_DATA bits are - either both allowed or both denied. */ - if (((access_masks[0] & NEW_ACE_WRITE_DATA) != 0) - != ((access_masks[0] & NEW_ACE_APPEND_DATA) != 0)) - return 1; - if (((access_masks[2] & NEW_ACE_WRITE_DATA) != 0) - != ((access_masks[2] & NEW_ACE_APPEND_DATA) != 0)) - return 1; - if (((access_masks[4] & NEW_ACE_WRITE_DATA) != 0) - != ((access_masks[4] & NEW_ACE_APPEND_DATA) != 0)) - return 1; - } - - return 0; -} - -# endif - -#elif USE_ACL && HAVE_GETACL /* HP-UX */ - -/* Return 1 if the given ACL is non-trivial. - Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ -int -acl_nontrivial (int count, struct acl_entry *entries, struct stat *sb) -{ - int i; - - for (i = 0; i < count; i++) - { - struct acl_entry *ace = &entries[i]; - - if (!((ace->uid == sb->st_uid && ace->gid == ACL_NSGROUP) - || (ace->uid == ACL_NSUSER && ace->gid == sb->st_gid) - || (ace->uid == ACL_NSUSER && ace->gid == ACL_NSGROUP))) - return 1; - } - return 0; -} - -# if HAVE_ACLV_H /* HP-UX >= 11.11 */ - -/* Return 1 if the given ACL is non-trivial. - Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ -int -aclv_nontrivial (int count, struct acl *entries) -{ - int i; - - for (i = 0; i < count; i++) - { - struct acl *ace = &entries[i]; - - /* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat(). - If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat(). - We don't need to check ace->a_id in these cases. */ - if (!(ace->a_type == USER_OBJ /* no need to check ace->a_id here */ - || ace->a_type == GROUP_OBJ /* no need to check ace->a_id here */ - || ace->a_type == CLASS_OBJ - || ace->a_type == OTHER_OBJ)) - return 1; - } - return 0; -} - -# endif - -#elif USE_ACL && (HAVE_ACLX_GET || HAVE_STATACL) /* AIX */ - -/* Return 1 if the given ACL is non-trivial. - Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ -int -acl_nontrivial (struct acl *a) -{ - /* The normal way to iterate through an ACL is like this: - struct acl_entry *ace; - for (ace = a->acl_ext; ace != acl_last (a); ace = acl_nxt (ace)) - { - struct ace_id *aei; - switch (ace->ace_type) - { - case ACC_PERMIT: - case ACC_DENY: - case ACC_SPECIFY: - ...; - } - for (aei = ace->ace_id; aei != id_last (ace); aei = id_nxt (aei)) - ... - } - */ - return (acl_last (a) != a->acl_ext ? 1 : 0); -} - -# if HAVE_ACLX_GET && defined ACL_AIX_WIP /* newer AIX */ - -/* Return 1 if the given ACL is non-trivial. - Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ -int -acl_nfs4_nontrivial (nfs4_acl_int_t *a) -{ -# if 1 /* let's try this first */ - return (a->aclEntryN > 0 ? 1 : 0); -# else - int count = a->aclEntryN; - int i; - - for (i = 0; i < count; i++) - { - nfs4_ace_int_t *ace = &a->aclEntry[i]; - - if (!((ace->flags & ACE4_ID_SPECIAL) != 0 - && (ace->aceWho.special_whoid == ACE4_WHO_OWNER - || ace->aceWho.special_whoid == ACE4_WHO_GROUP - || ace->aceWho.special_whoid == ACE4_WHO_EVERYONE) - && ace->aceType == ACE4_ACCESS_ALLOWED_ACE_TYPE - && ace->aceFlags == 0 - && (ace->aceMask & ~(ACE4_READ_DATA | ACE4_LIST_DIRECTORY - | ACE4_WRITE_DATA | ACE4_ADD_FILE - | ACE4_EXECUTE)) == 0)) - return 1; - } - return 0; -# endif -} - -# endif - -#elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */ - -/* Test an ACL retrieved with ACL_GET. - Return 1 if the given ACL, consisting of COUNT entries, is non-trivial. - Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ -int -acl_nontrivial (int count, struct acl *entries) -{ - int i; - - for (i = 0; i < count; i++) - { - struct acl *ace = &entries[i]; - - /* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat(). - If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat(). - We don't need to check ace->a_id in these cases. */ - if (!(ace->a_type == USER_OBJ /* no need to check ace->a_id here */ - || ace->a_type == GROUP_OBJ /* no need to check ace->a_id here */ - || ace->a_type == CLASS_OBJ - || ace->a_type == OTHER_OBJ)) - return 1; - } - return 0; -} - -#endif - - /* Return 1 if NAME has a nontrivial access control list, 0 if NAME only has no or a base access control list, and -1 (setting errno) on error. SB must be set to the stat buffer of NAME, obtained diff --git a/modules/acl b/modules/acl index 5cb91e0..fdb0d72 100644 --- a/modules/acl +++ b/modules/acl @@ -2,6 +2,7 @@ Description: Access control lists of files, with diagnostics. (Unportable.) Files: +lib/file-has-acl.c lib/copy-acl.c lib/set-acl.c @@ -14,7 +15,7 @@ quote configure.ac: Makefile.am: -lib_SOURCES += copy-acl.c set-acl.c +lib_SOURCES += file-has-acl.c copy-acl.c set-acl.c Include: "acl.h" diff --git a/modules/qacl b/modules/qacl index 78da6e1..bc7382b 100644 --- a/modules/qacl +++ b/modules/qacl @@ -6,7 +6,7 @@ lib/acl.h lib/acl-internal.h lib/acl-errno-valid.c lib/acl_entries.c -lib/file-has-acl.c +lib/acl-internal.c lib/qcopy-acl.c lib/qset-acl.c m4/acl.m4 @@ -21,7 +21,7 @@ configure.ac: gl_FUNC_ACL Makefile.am: -lib_SOURCES += acl-errno-valid.c file-has-acl.c qcopy-acl.c qset-acl.c +lib_SOURCES += acl-errno-valid.c acl-internal.c qcopy-acl.c qset-acl.c Include: "acl.h" -- 2.1.0 From unknown Mon Aug 18 04:46:43 2025 X-Loop: help-debbugs@gnu.org Subject: bug#20311: [GNULIB 4/4] qacl: Reimplement qset_acl and qcopy_acl Resent-From: Andreas Gruenbacher Original-Sender: "Debbugs-submit" Resent-CC: bug-coreutils@gnu.org Resent-Date: Sun, 12 Apr 2015 16:53:04 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 20311 X-GNU-PR-Package: coreutils X-GNU-PR-Keywords: To: bug-gnulib@gnu.org, 20311@debbugs.gnu.org X-Debbugs-Original-To: bug-gnulib@gnu.org, bug-coreutils@gnu.org Received: via spool by submit@debbugs.gnu.org id=B.142885754428169 (code B ref -1); Sun, 12 Apr 2015 16:53:04 +0000 Received: (at submit) by debbugs.gnu.org; 12 Apr 2015 16:52:24 +0000 Received: from localhost ([127.0.0.1]:54144 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1YhL7L-0007K8-5N for submit@debbugs.gnu.org; Sun, 12 Apr 2015 12:52:23 -0400 Received: from eggs.gnu.org ([208.118.235.92]:56068) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1YhJ1Z-0004Dt-Jy for submit@debbugs.gnu.org; Sun, 12 Apr 2015 10:38:18 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YhJ1N-00084k-QE for submit@debbugs.gnu.org; Sun, 12 Apr 2015 10:38:08 -0400 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on eggs.gnu.org X-Spam-Level: X-Spam-Status: No, score=0.8 required=5.0 tests=BAYES_50,FREEMAIL_FROM, T_DKIM_INVALID autolearn=disabled version=3.3.2 Received: from lists.gnu.org ([2001:4830:134:3::11]:55005) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YhJ1N-00084f-MP for submit@debbugs.gnu.org; Sun, 12 Apr 2015 10:38:01 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41110) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YhJ1A-00045L-8g for bug-coreutils@gnu.org; Sun, 12 Apr 2015 10:38:01 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YhJ11-0007rr-Av for bug-coreutils@gnu.org; Sun, 12 Apr 2015 10:37:48 -0400 Received: from mail-wg0-x235.google.com ([2a00:1450:400c:c00::235]:36041) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YhJ0j-0007dz-EJ; Sun, 12 Apr 2015 10:37:21 -0400 Received: by wgsk9 with SMTP id k9so57602234wgs.3; Sun, 12 Apr 2015 07:37:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:subject:date:message-id:in-reply-to:references:in-reply-to :references:mime-version:content-type:content-transfer-encoding; bh=979z2dZYo9vKyNlBihKX92iPFq+XvebBFpZ2NNv59mQ=; b=BKClhqKWFcVhcS5myvWsYzGxjgq6ORtKs2SN1QPBechIxCAvqfvJXwPsVSyFr/W7LK 8amx3fLKjQ3vHR7LJ21A5TsQGOWN8XiijIQk6LWvxYKSmOExlWvJbDvvV5fduBcJN84F yo2p1z0/a8/la5rIM4K/NqpS/3Cg7TAsbmr96He3zW0r35gayHZVCm+4ncpx/1/KzCgz dabYXizOD4W7d8FU1z9f5majWZi4jnAvAnridQdFve43FqYwRUQid8Wc/conhHKssEVi /F+Blr5Cnnf4Q2j9CM8cTP8i8H3E5LhjDA3LNCYp3lMiWkB3ES19UkkqC7okWUQPwmfg OsIw== X-Received: by 10.194.211.8 with SMTP id my8mr11976300wjc.90.1428849440808; Sun, 12 Apr 2015 07:37:20 -0700 (PDT) Received: from nuc.home.com (80-110-92-235.cgn.dynamic.surfer.at. [80.110.92.235]) by mx.google.com with ESMTPSA id dz4sm8507166wib.17.2015.04.12.07.37.19 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 12 Apr 2015 07:37:20 -0700 (PDT) From: Andreas Gruenbacher Date: Sun, 12 Apr 2015 16:36:39 +0200 Message-Id: <33332517845ba0e95c5be192f8812a1426b86cee.1428848851.git.andreas.gruenbacher@gmail.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: References: In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2001:4830:134:3::11 X-Spam-Score: -4.0 (----) X-Mailman-Approved-At: Sun, 12 Apr 2015 12:52:13 -0400 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -4.0 (----) Implement get_permissions and set_permissions primitives for getting all the permissions of a file, storing them, and later setting them. (In the minimal case, the permissions consist only of a file mode.) Reimplement qset_acl and qcopy_acl based on these new primitives: this avoids code duplication and makes error handling more consistent. The Solaris and Cygwin code still uses duplicate code paths for setting a file mode while making sure that no acls exist and setting an explicit acl; this is no worse than before, but could be cleaned up. The AIX code still doesn't read ACLs, it only makes sure that acls don't get in the wy when setting a file mode. * lib/acl-internal.h (struct permission_context): New data structure. (get_permissions, set_permissions, free_permission_context): Declare. * lib/acl-internal.c (free_permission_context): New helper function. * lib/get-permissions.c (get_permissions): New helper function split off from qcopy_acl. * lib/set-permissions.c: (set_acls_from_mode): On Solaris, Cygwin, and AIX, set a file's permissions based on only a file mode. (acl_from_mode, context_acl_from_mode, context_aclv_from_mode): All other platforms construct a temporary acl from the file mode and set that acl in the same way as setting an acl read from the source file. This should help avoid code duplication and inconsistent / buggy behavior. (set_acls): New helper function Split off from qcopy_acl. (chmod_or_fchmod): Moved here from qset-acl.c. (set_permissions): New helper function. * lib/qcopy-acl.c (qcopy_acl): Rewrite using get_permissions and set_permissions. * lib/qset-acl.c (qset_acl): Rewrite using set_permissions. * modules/qacl: Add get-permissions.c and set-permissions.c. --- lib/acl-internal.c | 30 ++ lib/acl-internal.h | 42 +++ lib/get-permissions.c | 287 +++++++++++++++++ lib/qcopy-acl.c | 531 +------------------------------- lib/qset-acl.c | 639 +------------------------------------- lib/set-permissions.c | 833 ++++++++++++++++++++++++++++++++++++++++++++++++++ modules/qacl | 4 +- 7 files changed, 1206 insertions(+), 1160 deletions(-) create mode 100644 lib/get-permissions.c create mode 100644 lib/set-permissions.c diff --git a/lib/acl-internal.c b/lib/acl-internal.c index d9bd446..1c03c84 100644 --- a/lib/acl-internal.c +++ b/lib/acl-internal.c @@ -467,3 +467,33 @@ acl_nontrivial (int count, struct acl *entries) } #endif + +void free_permission_context (struct permission_context *ctx) +{ +#ifdef USE_ACL +# if HAVE_ACL_GET_FILE /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */ + if (ctx->acl) + acl_free (ctx->acl); +# if !HAVE_ACL_TYPE_EXTENDED + if (ctx->default_acl) + acl_free (ctx->default_acl); +# endif + +# elif defined GETACL /* Solaris, Cygwin */ + free (ctx->entries); +# ifdef ACE_GETACL + free (ctx->ace_entries); +# endif + +# elif HAVE_GETACL /* HP-UX */ + +# if HAVE_ACLV_H +# endif + +# elif HAVE_STATACL /* older AIX */ + +# elif HAVE_ACLSORT /* NonStop Kernel */ + +# endif +#endif +} diff --git a/lib/acl-internal.h b/lib/acl-internal.h index 9b9fae2..b632e8e 100644 --- a/lib/acl-internal.h +++ b/lib/acl-internal.h @@ -250,4 +250,46 @@ extern int acl_nontrivial (int count, struct acl *entries); #endif +struct permission_context { + mode_t mode; +#ifdef USE_ACL +# if HAVE_ACL_GET_FILE /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */ + acl_t acl; +# if !HAVE_ACL_TYPE_EXTENDED + acl_t default_acl; +# endif + bool acls_not_supported; + +# elif defined GETACL /* Solaris, Cygwin */ + int count; + aclent_t *entries; +# ifdef ACE_GETACL + int ace_count; + ace_t *ace_entries; +# endif + +# elif HAVE_GETACL /* HP-UX */ + struct acl_entry entries[NACLENTRIES]; + int count; +# if HAVE_ACLV_H + struct acl aclv_entries[NACLVENTRIES]; + int aclv_count; +# endif + +# elif HAVE_STATACL /* older AIX */ + union { struct acl a; char room[4096]; } u; + bool have_u; + +# elif HAVE_ACLSORT /* NonStop Kernel */ + struct acl entries[NACLENTRIES]; + int count; + +# endif +#endif +}; + +int get_permissions (const char *, int, mode_t, struct permission_context *); +int set_permissions (struct permission_context *, const char *, int); +void free_permission_context (struct permission_context *); + _GL_INLINE_HEADER_END diff --git a/lib/get-permissions.c b/lib/get-permissions.c new file mode 100644 index 0000000..121c890 --- /dev/null +++ b/lib/get-permissions.c @@ -0,0 +1,287 @@ +/* get-permissions.c - get permissions of a file + + Copyright (C) 2002-2003, 2005-2015 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */ + +#include + +#include +#include "acl.h" + +#include "acl-internal.h" + +/* Read the permissions of a file into CTX. If DESC is a valid file descriptor, + use file descriptor operations, else use filename based operations on NAME. + MODE is the file mode obtained from a previous stat call. + Return 0 if successful. Return -1 and set errno upon failure. */ + +int +get_permissions (const char *name, int desc, mode_t mode, + struct permission_context *ctx) +{ + memset (ctx, 0, sizeof(*ctx)); + ctx->mode = mode; + +#if USE_ACL && HAVE_ACL_GET_FILE + /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ + /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */ +# if !HAVE_ACL_TYPE_EXTENDED + /* Linux, FreeBSD, IRIX, Tru64 */ + + if (HAVE_ACL_GET_FD && desc != -1) + ctx->acl = acl_get_fd (desc); + else + ctx->acl = acl_get_file (name, ACL_TYPE_ACCESS); + if (ctx->acl == NULL) + return acl_errno_valid (errno) ? -1 : 0; + + /* With POSIX ACLs, a file cannot have "no" acl; a file without + extended permissions has a "minimal" acl which is equivalent to the + file mode. */ + + if (S_ISDIR (mode)) + { + ctx->default_acl = acl_get_file (name, ACL_TYPE_DEFAULT); + if (ctx->default_acl == NULL) + return -1; + } + + return 0; + +# else /* HAVE_ACL_TYPE_EXTENDED */ + /* Mac OS X */ + + /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS) + and acl_get_file (name, ACL_TYPE_DEFAULT) + always return NULL / EINVAL. You have to use + acl_get_file (name, ACL_TYPE_EXTENDED) + or acl_get_fd (open (name, ...)) + to retrieve an ACL. + On the other hand, + acl_set_file (name, ACL_TYPE_ACCESS, acl) + and acl_set_file (name, ACL_TYPE_DEFAULT, acl) + have the same effect as + acl_set_file (name, ACL_TYPE_EXTENDED, acl): + Each of these calls sets the file's ACL. */ + + if (HAVE_ACL_GET_FD && desc != -1) + ctx->acl = acl_get_fd (desc); + else + ctx->acl = acl_get_file (name, ACL_TYPE_EXTENDED); + if (ctx->acl == NULL) + return acl_errno_valid (errno) ? -1 : 0; + return 0; + +# endif + +#elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ + + /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions + of Unixware. The acl() call returns the access and default ACL both + at once. */ +# ifdef ACE_GETACL + /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4 + file systems (whereas the other ones are used in UFS file systems). + There is an API + pathconf (name, _PC_ACL_ENABLED) + fpathconf (desc, _PC_ACL_ENABLED) + that allows to determine which of the two kinds of ACLs is supported + for the given file. But some file systems may implement this call + incorrectly, so better not use it. + When fetching the source ACL, we simply fetch both ACL types. + When setting the destination ACL, we try either ACL types, assuming + that the kernel will translate the ACL from one form to the other. + (See in + the description of ENOTSUP.) */ + for (;;) + { + int ret; + + if (desc != -1) + ret = facl (desc, ACE_GETACLCNT, 0, NULL); + else + ret = acl (name, ACE_GETACLCNT, 0, NULL); + if (ret < 0) + { + if (errno == ENOSYS || errno == EINVAL) + ret = 0; + else + return -1; + } + ctx->ace_count = ret; + + if (ctx->ace_count == 0) + break; + + ctx->ace_entries = (ace_t *) malloc (ctx->ace_count * sizeof (ace_t)); + if (ctx->ace_entries == NULL) + { + errno = ENOMEM; + return -1; + } + + if (desc != -1) + ret = facl (desc, ACE_GETACL, ctx->ace_count, ctx->ace_entries); + else + ret = acl (name, ACE_GETACL, ctx->ace_count, ctx->ace_entries); + if (ret < 0) + { + if (errno == ENOSYS || errno == EINVAL) + { + free (ctx->ace_entries); + ctx->ace_entries = NULL; + ctx->ace_count = 0; + break; + } + else + return -1; + } + if (ret <= ctx->ace_count) + { + ctx->ace_count = ret; + break; + } + /* Huh? The number of ACL entries has increased since the last call. + Repeat. */ + free (ctx->ace_entries); + ctx->ace_entries = NULL; + } +# endif + + for (;;) + { + int ret; + + if (desc != -1) + ret = facl (desc, GETACLCNT, 0, NULL); + else + ret = acl (name, GETACLCNT, 0, NULL); + if (ret < 0) + { + if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP) + ret = 0; + else + return -1; + } + ctx->count = ret; + + if (ctx->count == 0) + break; + + ctx->entries = (aclent_t *) malloc (ctx->count * sizeof (aclent_t)); + if (ctx->entries == NULL) + { + errno = ENOMEM; + return -1; + } + + if (desc != -1) + ret = facl (desc, GETACL, ctx->count, ctx->entries); + else + ret = acl (name, GETACL, ctx->count, ctx->entries); + if (ret < 0) + { + if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP) + { + free (ctx->entries); + ctx->entries = NULL; + ctx->count = 0; + break; + } + else + return -1; + } + if (ret <= ctx->count) + { + ctx->count = ret; + break; + } + /* Huh? The number of ACL entries has increased since the last call. + Repeat. */ + free (ctx->entries); + ctx->entries = NULL; + } + return 0; + +#elif USE_ACL && HAVE_GETACL /* HP-UX */ + + int ret; + + if (desc != -1) + ret = fgetacl (desc, NACLENTRIES, ctx->entries); + else + ret = getacl (name, NACLENTRIES, ctx->entries); + if (ret < 0) + { + if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) + ret = 0; + else + return -1; + } + else if (ret > NACLENTRIES) + /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ + abort (); + ctx->count = ret; + +# if HAVE_ACLV_H + ret = acl ((char *) name, ACL_GET, NACLVENTRIES, ctx->aclv_entries); + if (ret < 0) + { + if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) + ret = 0; + else + return -2; + } + else if (ret > NACLVENTRIES) + /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation. */ + abort (); + ctx->aclv_count = ret; +# endif + return 0; + +#elif USE_ACL && HAVE_ACLX_GET && ACL_AIX_WIP /* AIX */ + + /* TODO (see set_permissions). */ + +#elif USE_ACL && HAVE_STATACL /* older AIX */ + + if (desc != -1) + ret = fstatacl (desc, STX_NORMAL, &ctx->u.a, sizeof (ctx->u)); + else + ret = statacl (name, STX_NORMAL, &ctx->u.a, sizeof (ctx->u)); + if (ret == 0) + ctx->have_u = true; + +#elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */ + + int ret; + + ret = acl ((char *) name, ACL_GET, NACLENTRIES, ctx->entries); + if (ret < 0) + return -1; + else if (ret > NACLENTRIES) + /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ + abort (); + ctx->count = ret; + return 0; + +#else + + return 0; + +#endif +} diff --git a/lib/qcopy-acl.c b/lib/qcopy-acl.c index bc258ba..c450742 100644 --- a/lib/qcopy-acl.c +++ b/lib/qcopy-acl.c @@ -39,534 +39,13 @@ int qcopy_acl (const char *src_name, int source_desc, const char *dst_name, int dest_desc, mode_t mode) { -#if USE_ACL && HAVE_ACL_GET_FILE - /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ - /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */ -# if !HAVE_ACL_TYPE_EXTENDED - /* Linux, FreeBSD, IRIX, Tru64 */ - - acl_t acl; - int ret; - - if (HAVE_ACL_GET_FD && source_desc != -1) - acl = acl_get_fd (source_desc); - else - acl = acl_get_file (src_name, ACL_TYPE_ACCESS); - if (acl == NULL) - { - if (! acl_errno_valid (errno)) - return qset_acl (dst_name, dest_desc, mode); - else - return -2; - } - - if (HAVE_ACL_SET_FD && dest_desc != -1) - ret = acl_set_fd (dest_desc, acl); - else - ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl); - if (ret != 0) - { - int saved_errno = errno; - - if (! acl_errno_valid (errno) && !acl_access_nontrivial (acl)) - { - acl_free (acl); - return chmod_or_fchmod (dst_name, dest_desc, mode); - } - else - { - acl_free (acl); - chmod_or_fchmod (dst_name, dest_desc, mode); - errno = saved_errno; - return -1; - } - } - else - acl_free (acl); - - if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX))) - { - /* We did not call chmod so far, and either the mode and the ACL are - separate or special bits are to be set which don't fit into ACLs. */ - - if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) - return -1; - } - - if (S_ISDIR (mode)) - { - acl = acl_get_file (src_name, ACL_TYPE_DEFAULT); - if (acl == NULL) - return -2; - - if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl)) - { - int saved_errno = errno; - - acl_free (acl); - errno = saved_errno; - return -1; - } - else - acl_free (acl); - } - return 0; - -# else /* HAVE_ACL_TYPE_EXTENDED */ - /* Mac OS X */ - - /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS) - and acl_get_file (name, ACL_TYPE_DEFAULT) - always return NULL / EINVAL. You have to use - acl_get_file (name, ACL_TYPE_EXTENDED) - or acl_get_fd (open (name, ...)) - to retrieve an ACL. - On the other hand, - acl_set_file (name, ACL_TYPE_ACCESS, acl) - and acl_set_file (name, ACL_TYPE_DEFAULT, acl) - have the same effect as - acl_set_file (name, ACL_TYPE_EXTENDED, acl): - Each of these calls sets the file's ACL. */ - - acl_t acl; + struct permission_context ctx; int ret; - if (HAVE_ACL_GET_FD && source_desc != -1) - acl = acl_get_fd (source_desc); - else - acl = acl_get_file (src_name, ACL_TYPE_EXTENDED); - if (acl == NULL) - { - if (!acl_errno_valid (errno)) - return qset_acl (dst_name, dest_desc, mode); - else - return -2; - } - - if (HAVE_ACL_SET_FD && dest_desc != -1) - ret = acl_set_fd (dest_desc, acl); - else - ret = acl_set_file (dst_name, ACL_TYPE_EXTENDED, acl); + ret = get_permissions (src_name, source_desc, mode, &ctx); if (ret != 0) - { - int saved_errno = errno; - - if (!acl_errno_valid (saved_errno) && !acl_extended_nontrivial (acl)) - { - acl_free (acl); - return chmod_or_fchmod (dst_name, dest_desc, mode); - } - else - { - acl_free (acl); - chmod_or_fchmod (dst_name, dest_desc, mode); - errno = saved_errno; - return -1; - } - } - else - acl_free (acl); - - /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */ - return chmod_or_fchmod (dst_name, dest_desc, mode); - -# endif - -#elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ - - /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions - of Unixware. The acl() call returns the access and default ACL both - at once. */ -# ifdef ACE_GETACL - int ace_count; - ace_t *ace_entries; -# endif - int count; - aclent_t *entries; - int did_chmod; - int saved_errno; - int ret; - -# ifdef ACE_GETACL - /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4 - file systems (whereas the other ones are used in UFS file systems). - There is an API - pathconf (name, _PC_ACL_ENABLED) - fpathconf (desc, _PC_ACL_ENABLED) - that allows to determine which of the two kinds of ACLs is supported - for the given file. But some file systems may implement this call - incorrectly, so better not use it. - When fetching the source ACL, we simply fetch both ACL types. - When setting the destination ACL, we try either ACL types, assuming - that the kernel will translate the ACL from one form to the other. - (See in - the description of ENOTSUP.) */ - for (;;) - { - ace_count = (source_desc != -1 - ? facl (source_desc, ACE_GETACLCNT, 0, NULL) - : acl (src_name, ACE_GETACLCNT, 0, NULL)); - - if (ace_count < 0) - { - if (errno == ENOSYS || errno == EINVAL) - { - ace_count = 0; - ace_entries = NULL; - break; - } - else - return -2; - } - - if (ace_count == 0) - { - ace_entries = NULL; - break; - } - - ace_entries = (ace_t *) malloc (ace_count * sizeof (ace_t)); - if (ace_entries == NULL) - { - errno = ENOMEM; - return -2; - } - - ret = (source_desc != -1 - ? facl (source_desc, ACE_GETACL, ace_count, ace_entries) - : acl (src_name, ACE_GETACL, ace_count, ace_entries)); - if (ret < 0) - { - free (ace_entries); - if (errno == ENOSYS || errno == EINVAL) - { - ace_count = 0; - ace_entries = NULL; - break; - } - else - return -2; - } - if (ret == ace_count) - break; - /* Huh? The number of ACL entries changed since the last call. - Repeat. */ - } -# endif - - for (;;) - { - count = (source_desc != -1 - ? facl (source_desc, GETACLCNT, 0, NULL) - : acl (src_name, GETACLCNT, 0, NULL)); - - if (count < 0) - { - if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP) - { - count = 0; - entries = NULL; - break; - } - else - return -2; - } - - if (count == 0) - { - entries = NULL; - break; - } - - entries = (aclent_t *) malloc (count * sizeof (aclent_t)); - if (entries == NULL) - { - errno = ENOMEM; - return -2; - } - - if ((source_desc != -1 - ? facl (source_desc, GETACL, count, entries) - : acl (src_name, GETACL, count, entries)) - == count) - break; - /* Huh? The number of ACL entries changed since the last call. - Repeat. */ - } - - /* Is there an ACL of either kind? */ -# ifdef ACE_GETACL - if (ace_count == 0) -# endif - if (count == 0) - return qset_acl (dst_name, dest_desc, mode); - - did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */ - saved_errno = 0; /* the first non-ignorable error code */ - - if (!MODE_INSIDE_ACL) - { - /* On Cygwin, it is necessary to call chmod before acl, because - chmod can change the contents of the ACL (in ways that don't - change the allowed accesses, but still visible). */ - if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) - saved_errno = errno; - did_chmod = 1; - } - - /* If both ace_entries and entries are available, try SETACL before - ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL - can. */ - - if (count > 0) - { - ret = (dest_desc != -1 - ? facl (dest_desc, SETACL, count, entries) - : acl (dst_name, SETACL, count, entries)); - if (ret < 0 && saved_errno == 0) - { - saved_errno = errno; - if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) - && !acl_nontrivial (count, entries)) - saved_errno = 0; - } - else - did_chmod = 1; - } - free (entries); - -# ifdef ACE_GETACL - if (ace_count > 0) - { - ret = (dest_desc != -1 - ? facl (dest_desc, ACE_SETACL, ace_count, ace_entries) - : acl (dst_name, ACE_SETACL, ace_count, ace_entries)); - if (ret < 0 && saved_errno == 0) - { - saved_errno = errno; - if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP) - && !acl_ace_nontrivial (ace_count, ace_entries)) - saved_errno = 0; - } - } - free (ace_entries); -# endif - - if (MODE_INSIDE_ACL - && did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0)) - { - /* We did not call chmod so far, and either the mode and the ACL are - separate or special bits are to be set which don't fit into ACLs. */ - - if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) - { - if (saved_errno == 0) - saved_errno = errno; - } - } - - if (saved_errno) - { - errno = saved_errno; - return -1; - } - return 0; - -#elif USE_ACL && HAVE_GETACL /* HP-UX */ - - struct acl_entry entries[NACLENTRIES]; - int count; -# if HAVE_ACLV_H - struct acl aclv_entries[NACLVENTRIES]; - int aclv_count; -# endif - int did_chmod; - int saved_errno; - int ret; - - count = (source_desc != -1 - ? fgetacl (source_desc, NACLENTRIES, entries) - : getacl (src_name, NACLENTRIES, entries)); - - if (count < 0) - { - if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) - count = 0; - else - return -2; - } - else if (count > 0) - { - if (count > NACLENTRIES) - /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ - abort (); - } - -# if HAVE_ACLV_H - aclv_count = acl ((char *) src_name, ACL_GET, NACLVENTRIES, aclv_entries); - - if (aclv_count < 0) - { - if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) - count = 0; - else - return -2; - } - else if (aclv_count > 0) - { - if (aclv_count > NACLVENTRIES) - /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation. */ - abort (); - } -# endif - - if (count == 0) -# if HAVE_ACLV_H - if (aclv_count == 0) -# endif - return qset_acl (dst_name, dest_desc, mode); - - did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */ - saved_errno = 0; /* the first non-ignorable error code */ - - if (count > 0) - { - ret = (dest_desc != -1 - ? fsetacl (dest_desc, count, entries) - : setacl (dst_name, count, entries)); - if (ret < 0 && saved_errno == 0) - { - saved_errno = errno; - if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP - && !acl_nontrivial (count, entries)) - saved_errno = 0; - } - else - did_chmod = 1; - } - -# if HAVE_ACLV_H - if (aclv_count > 0) - { - ret = acl ((char *) dst_name, ACL_SET, aclv_count, aclv_entries); - if (ret < 0 && saved_errno == 0) - { - saved_errno = errno; - if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) - { - if (!aclv_nontrivial (aclv_count, aclv_entries)) - saved_errno = 0; - } - } - else - did_chmod = 1; - } -# endif - - if (did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0)) - { - /* We did not call chmod so far, and special bits are to be set which - don't fit into ACLs. */ - - if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) - { - if (saved_errno == 0) - saved_errno = errno; - } - } - - if (saved_errno) - { - errno = saved_errno; - return -1; - } - return 0; - -#elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */ - - /* TODO */ - -#elif USE_ACL && HAVE_STATACL /* older AIX */ - - union { struct acl a; char room[4096]; } u; - int ret; - - if ((source_desc != -1 - ? fstatacl (source_desc, STX_NORMAL, &u.a, sizeof (u)) - : statacl (src_name, STX_NORMAL, &u.a, sizeof (u))) - < 0) return -2; - - ret = (dest_desc != -1 - ? fchacl (dest_desc, &u.a, u.a.acl_len) - : chacl (dst_name, &u.a, u.a.acl_len)); - if (ret < 0) - { - int saved_errno = errno; - - chmod_or_fchmod (dst_name, dest_desc, mode); - errno = saved_errno; - return -1; - } - - /* No need to call chmod_or_fchmod at this point, since the mode bits - S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL. */ - - return 0; - -#elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */ - - struct acl entries[NACLENTRIES]; - int count; - int ret; - - count = acl ((char *) src_name, ACL_GET, NACLENTRIES, entries); - - if (count < 0) - { - if (0) - count = 0; - else - return -2; - } - else if (count > 0) - { - if (count > NACLENTRIES) - /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ - abort (); - } - - if (count == 0) - return qset_acl (dst_name, dest_desc, mode); - - ret = acl ((char *) dst_name, ACL_SET, count, entries); - if (ret < 0) - { - int saved_errno = errno; - - if (0) - { - if (!acl_nontrivial (count, entries)) - return chmod_or_fchmod (dst_name, dest_desc, mode); - } - - chmod_or_fchmod (dst_name, dest_desc, mode); - errno = saved_errno; - return -1; - } - - if (mode & (S_ISUID | S_ISGID | S_ISVTX)) - { - /* We did not call chmod so far, and either the mode and the ACL are - separate or special bits are to be set which don't fit into ACLs. */ - - return chmod_or_fchmod (dst_name, dest_desc, mode); - } - return 0; - -#else - - return qset_acl (dst_name, dest_desc, mode); - -#endif + ret = set_permissions (&ctx, dst_name, dest_desc); + free_permission_context (&ctx); + return ret; } diff --git a/lib/qset-acl.c b/lib/qset-acl.c index bb4b0b2..1ebec22 100644 --- a/lib/qset-acl.c +++ b/lib/qset-acl.c @@ -21,26 +21,12 @@ #define ACL_INTERNAL_INLINE _GL_EXTERN_INLINE +#include #include "acl.h" #include "acl-internal.h" -/* If DESC is a valid file descriptor use fchmod to change the - file's mode to MODE on systems that have fchmod. On systems - that don't have fchmod and if DESC is invalid, use chmod on - NAME instead. - Return 0 if successful. Return -1 and set errno upon failure. */ - -int -chmod_or_fchmod (const char *name, int desc, mode_t mode) -{ - if (HAVE_FCHMOD && desc != -1) - return fchmod (desc, mode); - else - return chmod (name, mode); -} - /* Set the access control lists of a file. If DESC is a valid file descriptor, use file descriptor operations where available, else use filename based operations on NAME. If access control lists are not @@ -52,625 +38,12 @@ chmod_or_fchmod (const char *name, int desc, mode_t mode) int qset_acl (char const *name, int desc, mode_t mode) { -#if USE_ACL -# if HAVE_ACL_GET_FILE - /* POSIX 1003.1e draft 17 (abandoned) specific version. */ - /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */ -# if !HAVE_ACL_TYPE_EXTENDED - /* Linux, FreeBSD, IRIX, Tru64 */ - - /* We must also have acl_from_text and acl_delete_def_file. - (acl_delete_def_file could be emulated with acl_init followed - by acl_set_file, but acl_set_file with an empty acl is - unspecified.) */ - -# ifndef HAVE_ACL_FROM_TEXT -# error Must have acl_from_text (see POSIX 1003.1e draft 17). -# endif -# ifndef HAVE_ACL_DELETE_DEF_FILE -# error Must have acl_delete_def_file (see POSIX 1003.1e draft 17). -# endif - - acl_t acl; - int ret; - - if (HAVE_ACL_FROM_MODE) /* Linux */ - { - acl = acl_from_mode (mode); - if (!acl) - return -1; - } - else /* FreeBSD, IRIX, Tru64 */ - { - /* If we were to create the ACL using the functions acl_init(), - acl_create_entry(), acl_set_tag_type(), acl_set_qualifier(), - acl_get_permset(), acl_clear_perm[s](), acl_add_perm(), we - would need to create a qualifier. I don't know how to do this. - So create it using acl_from_text(). */ - -# if HAVE_ACL_FREE_TEXT /* Tru64 */ - char acl_text[] = "u::---,g::---,o::---,"; -# else /* FreeBSD, IRIX */ - char acl_text[] = "u::---,g::---,o::---"; -# endif - - if (mode & S_IRUSR) acl_text[ 3] = 'r'; - if (mode & S_IWUSR) acl_text[ 4] = 'w'; - if (mode & S_IXUSR) acl_text[ 5] = 'x'; - if (mode & S_IRGRP) acl_text[10] = 'r'; - if (mode & S_IWGRP) acl_text[11] = 'w'; - if (mode & S_IXGRP) acl_text[12] = 'x'; - if (mode & S_IROTH) acl_text[17] = 'r'; - if (mode & S_IWOTH) acl_text[18] = 'w'; - if (mode & S_IXOTH) acl_text[19] = 'x'; - - acl = acl_from_text (acl_text); - if (!acl) - return -1; - } - if (HAVE_ACL_SET_FD && desc != -1) - ret = acl_set_fd (desc, acl); - else - ret = acl_set_file (name, ACL_TYPE_ACCESS, acl); - if (ret != 0) - { - int saved_errno = errno; - acl_free (acl); - if (! acl_errno_valid (errno)) - return chmod_or_fchmod (name, desc, mode); - errno = saved_errno; - return -1; - } - else - acl_free (acl); - - if (S_ISDIR (mode) && acl_delete_def_file (name)) - return -1; - - if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX))) - { - /* We did not call chmod so far, and either the mode and the ACL are - separate or special bits are to be set which don't fit into ACLs. */ - return chmod_or_fchmod (name, desc, mode); - } - return 0; - -# else /* HAVE_ACL_TYPE_EXTENDED */ - /* Mac OS X */ - - /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS) - and acl_get_file (name, ACL_TYPE_DEFAULT) - always return NULL / EINVAL. You have to use - acl_get_file (name, ACL_TYPE_EXTENDED) - or acl_get_fd (open (name, ...)) - to retrieve an ACL. - On the other hand, - acl_set_file (name, ACL_TYPE_ACCESS, acl) - and acl_set_file (name, ACL_TYPE_DEFAULT, acl) - have the same effect as - acl_set_file (name, ACL_TYPE_EXTENDED, acl): - Each of these calls sets the file's ACL. */ - - acl_t acl; + struct permission_context ctx; int ret; - /* Remove the ACL if the file has ACLs. */ - if (HAVE_ACL_GET_FD && desc != -1) - acl = acl_get_fd (desc); - else - acl = acl_get_file (name, ACL_TYPE_EXTENDED); - if (acl) - { - acl_free (acl); - - acl = acl_init (0); - if (acl) - { - if (HAVE_ACL_SET_FD && desc != -1) - ret = acl_set_fd (desc, acl); - else - ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl); - if (ret != 0) - { - int saved_errno = errno; - acl_free (acl); - if (! acl_errno_valid (saved_errno)) - return chmod_or_fchmod (name, desc, mode); - errno = saved_errno; - return -1; - } - acl_free (acl); - } - } - - /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */ - return chmod_or_fchmod (name, desc, mode); -# endif - -# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ - - int done_setacl = 0; - -# ifdef ACE_GETACL - /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4 - file systems (whereas the other ones are used in UFS file systems). */ - - /* The flags in the ace_t structure changed in a binary incompatible way - when ACL_NO_TRIVIAL etc. were introduced in version 1.15. - How to distinguish the two conventions at runtime? - We fetch the existing ACL. In the old convention, usually three ACEs have - a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400. - In the new convention, these values are not used. */ - int convention; - - { - /* Initially, try to read the entries into a stack-allocated buffer. - Use malloc if it does not fit. */ - enum - { - alloc_init = 4000 / sizeof (ace_t), /* >= 3 */ - alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t)) - }; - ace_t buf[alloc_init]; - size_t alloc = alloc_init; - ace_t *entries = buf; - ace_t *malloced = NULL; - int count; - - for (;;) - { - count = (desc != -1 - ? facl (desc, ACE_GETACL, alloc, entries) - : acl (name, ACE_GETACL, alloc, entries)); - if (count < 0 && errno == ENOSPC) - { - /* Increase the size of the buffer. */ - free (malloced); - if (alloc > alloc_max / 2) - { - errno = ENOMEM; - return -1; - } - alloc = 2 * alloc; /* <= alloc_max */ - entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t)); - if (entries == NULL) - { - errno = ENOMEM; - return -1; - } - continue; - } - break; - } - - if (count <= 0) - convention = -1; - else - { - int i; - - convention = 0; - for (i = 0; i < count; i++) - if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER)) - { - convention = 1; - break; - } - } - free (malloced); - } - - if (convention >= 0) - { - ace_t entries[6]; - int count; - int ret; - - if (convention) - { - /* Running on Solaris 10. */ - entries[0].a_type = OLD_ALLOW; - entries[0].a_flags = OLD_ACE_OWNER; - entries[0].a_who = 0; /* irrelevant */ - entries[0].a_access_mask = (mode >> 6) & 7; - entries[1].a_type = OLD_ALLOW; - entries[1].a_flags = OLD_ACE_GROUP; - entries[1].a_who = 0; /* irrelevant */ - entries[1].a_access_mask = (mode >> 3) & 7; - entries[2].a_type = OLD_ALLOW; - entries[2].a_flags = OLD_ACE_OTHER; - entries[2].a_who = 0; - entries[2].a_access_mask = mode & 7; - count = 3; - } - else - { - /* Running on Solaris 10 (newer version) or Solaris 11. - The details here were found through "/bin/ls -lvd somefiles". */ - entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE; - entries[0].a_flags = NEW_ACE_OWNER; - entries[0].a_who = 0; /* irrelevant */ - entries[0].a_access_mask = 0; - entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE; - entries[1].a_flags = NEW_ACE_OWNER; - entries[1].a_who = 0; /* irrelevant */ - entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS - | NEW_ACE_WRITE_ATTRIBUTES - | NEW_ACE_WRITE_ACL - | NEW_ACE_WRITE_OWNER; - if (mode & 0400) - entries[1].a_access_mask |= NEW_ACE_READ_DATA; - else - entries[0].a_access_mask |= NEW_ACE_READ_DATA; - if (mode & 0200) - entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; - else - entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; - if (mode & 0100) - entries[1].a_access_mask |= NEW_ACE_EXECUTE; - else - entries[0].a_access_mask |= NEW_ACE_EXECUTE; - entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE; - entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP; - entries[2].a_who = 0; /* irrelevant */ - entries[2].a_access_mask = 0; - entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE; - entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP; - entries[3].a_who = 0; /* irrelevant */ - entries[3].a_access_mask = 0; - if (mode & 0040) - entries[3].a_access_mask |= NEW_ACE_READ_DATA; - else - entries[2].a_access_mask |= NEW_ACE_READ_DATA; - if (mode & 0020) - entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; - else - entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; - if (mode & 0010) - entries[3].a_access_mask |= NEW_ACE_EXECUTE; - else - entries[2].a_access_mask |= NEW_ACE_EXECUTE; - entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE; - entries[4].a_flags = NEW_ACE_EVERYONE; - entries[4].a_who = 0; - entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS - | NEW_ACE_WRITE_ATTRIBUTES - | NEW_ACE_WRITE_ACL - | NEW_ACE_WRITE_OWNER; - entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE; - entries[5].a_flags = NEW_ACE_EVERYONE; - entries[5].a_who = 0; - entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS - | NEW_ACE_READ_ATTRIBUTES - | NEW_ACE_READ_ACL - | NEW_ACE_SYNCHRONIZE; - if (mode & 0004) - entries[5].a_access_mask |= NEW_ACE_READ_DATA; - else - entries[4].a_access_mask |= NEW_ACE_READ_DATA; - if (mode & 0002) - entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; - else - entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; - if (mode & 0001) - entries[5].a_access_mask |= NEW_ACE_EXECUTE; - else - entries[4].a_access_mask |= NEW_ACE_EXECUTE; - count = 6; - } - if (desc != -1) - ret = facl (desc, ACE_SETACL, count, entries); - else - ret = acl (name, ACE_SETACL, count, entries); - if (ret < 0 && errno != EINVAL && errno != ENOTSUP) - { - if (errno == ENOSYS) - return chmod_or_fchmod (name, desc, mode); - return -1; - } - if (ret == 0) - done_setacl = 1; - } -# endif - - if (!done_setacl) - { - aclent_t entries[3]; - int ret; - - entries[0].a_type = USER_OBJ; - entries[0].a_id = 0; /* irrelevant */ - entries[0].a_perm = (mode >> 6) & 7; - entries[1].a_type = GROUP_OBJ; - entries[1].a_id = 0; /* irrelevant */ - entries[1].a_perm = (mode >> 3) & 7; - entries[2].a_type = OTHER_OBJ; - entries[2].a_id = 0; - entries[2].a_perm = mode & 7; - - if (desc != -1) - ret = facl (desc, SETACL, - sizeof (entries) / sizeof (aclent_t), entries); - else - ret = acl (name, SETACL, - sizeof (entries) / sizeof (aclent_t), entries); - if (ret < 0) - { - if (errno == ENOSYS || errno == EOPNOTSUPP) - return chmod_or_fchmod (name, desc, mode); - return -1; - } - } - - if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX))) - { - /* We did not call chmod so far, so the special bits have not yet - been set. */ - return chmod_or_fchmod (name, desc, mode); - } - return 0; - -# elif HAVE_GETACL /* HP-UX */ - - struct stat statbuf; - int ret; - - if (desc != -1) - ret = fstat (desc, &statbuf); - else - ret = stat (name, &statbuf); - if (ret < 0) - return -1; - - { - struct acl_entry entries[3]; - - entries[0].uid = statbuf.st_uid; - entries[0].gid = ACL_NSGROUP; - entries[0].mode = (mode >> 6) & 7; - entries[1].uid = ACL_NSUSER; - entries[1].gid = statbuf.st_gid; - entries[1].mode = (mode >> 3) & 7; - entries[2].uid = ACL_NSUSER; - entries[2].gid = ACL_NSGROUP; - entries[2].mode = mode & 7; - - if (desc != -1) - ret = fsetacl (desc, sizeof (entries) / sizeof (struct acl_entry), entries); - else - ret = setacl (name, sizeof (entries) / sizeof (struct acl_entry), entries); - } - if (ret < 0) - { - if (!(errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)) - return -1; - -# if HAVE_ACLV_H /* HP-UX >= 11.11 */ - { - struct acl entries[4]; - - entries[0].a_type = USER_OBJ; - entries[0].a_id = 0; /* irrelevant */ - entries[0].a_perm = (mode >> 6) & 7; - entries[1].a_type = GROUP_OBJ; - entries[1].a_id = 0; /* irrelevant */ - entries[1].a_perm = (mode >> 3) & 7; - entries[2].a_type = CLASS_OBJ; - entries[2].a_id = 0; - entries[2].a_perm = (mode >> 3) & 7; - entries[3].a_type = OTHER_OBJ; - entries[3].a_id = 0; - entries[3].a_perm = mode & 7; - - ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries); - if (ret > 0) - abort (); - if (ret < 0) - { - if (0) - return chmod_or_fchmod (name, desc, mode); - return -1; - } - - ret = acl ((char *) name, ACL_SET, - sizeof (entries) / sizeof (struct acl), entries); - if (ret < 0) - { - if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) - return chmod_or_fchmod (name, desc, mode); - return -1; - } - } -# else - return chmod_or_fchmod (name, desc, mode); -# endif - } - - if (mode & (S_ISUID | S_ISGID | S_ISVTX)) - { - /* We did not call chmod so far, so the special bits have not yet - been set. */ - return chmod_or_fchmod (name, desc, mode); - } - return 0; - -# elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */ - - acl_type_list_t types; - size_t types_size = sizeof (types); - acl_type_t type; - - if (aclx_gettypes (name, &types, &types_size) < 0 - || types.num_entries == 0) - return chmod_or_fchmod (name, desc, mode); - - /* XXX Do we need to clear all types of ACLs for the given file, or is it - sufficient to clear the first one? */ - type = types.entries[0]; - if (type.u64 == ACL_AIXC) - { - union { struct acl a; char room[128]; } u; - int ret; - - u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */ - u.a.acl_mode = mode & ~(S_IXACL | 0777); - u.a.u_access = (mode >> 6) & 7; - u.a.g_access = (mode >> 3) & 7; - u.a.o_access = mode & 7; - - if (desc != -1) - ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS, - type, &u.a, u.a.acl_len, mode); - else - ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS, - type, &u.a, u.a.acl_len, mode); - if (!(ret < 0 && errno == ENOSYS)) - return ret; - } - else if (type.u64 == ACL_NFS4) - { - union { nfs4_acl_int_t a; char room[128]; } u; - nfs4_ace_int_t *ace; - int ret; - - u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION; - u.a.aclEntryN = 0; - ace = &u.a.aclEntry[0]; - { - ace->flags = ACE4_ID_SPECIAL; - ace->aceWho.special_whoid = ACE4_WHO_OWNER; - ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE; - ace->aceFlags = 0; - ace->aceMask = - (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0) - | (mode & 0200 - ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA - | ACE4_ADD_SUBDIRECTORY - : 0) - | (mode & 0100 ? ACE4_EXECUTE : 0); - ace->aceWhoString[0] = '\0'; - ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace; - ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4]; - u.a.aclEntryN++; - } - { - ace->flags = ACE4_ID_SPECIAL; - ace->aceWho.special_whoid = ACE4_WHO_GROUP; - ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE; - ace->aceFlags = 0; - ace->aceMask = - (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0) - | (mode & 0020 - ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA - | ACE4_ADD_SUBDIRECTORY - : 0) - | (mode & 0010 ? ACE4_EXECUTE : 0); - ace->aceWhoString[0] = '\0'; - ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace; - ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4]; - u.a.aclEntryN++; - } - { - ace->flags = ACE4_ID_SPECIAL; - ace->aceWho.special_whoid = ACE4_WHO_EVERYONE; - ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE; - ace->aceFlags = 0; - ace->aceMask = - (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0) - | (mode & 0002 - ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA - | ACE4_ADD_SUBDIRECTORY - : 0) - | (mode & 0001 ? ACE4_EXECUTE : 0); - ace->aceWhoString[0] = '\0'; - ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace; - ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4]; - u.a.aclEntryN++; - } - u.a.aclLength = (char *) ace - (char *) &u.a; - - if (desc != -1) - ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS, - type, &u.a, u.a.aclLength, mode); - else - ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS, - type, &u.a, u.a.aclLength, mode); - if (!(ret < 0 && errno == ENOSYS)) - return ret; - } - - return chmod_or_fchmod (name, desc, mode); - -# elif HAVE_STATACL /* older AIX */ - - union { struct acl a; char room[128]; } u; - int ret; - - u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */ - u.a.acl_mode = mode & ~(S_IXACL | 0777); - u.a.u_access = (mode >> 6) & 7; - u.a.g_access = (mode >> 3) & 7; - u.a.o_access = mode & 7; - - if (desc != -1) - ret = fchacl (desc, &u.a, u.a.acl_len); - else - ret = chacl (name, &u.a, u.a.acl_len); - - if (ret < 0 && errno == ENOSYS) - return chmod_or_fchmod (name, desc, mode); - + memset (&ctx, 0, sizeof ctx); + ctx.mode = mode; + ret = set_permissions (&ctx, name, desc); + free_permission_context (&ctx); return ret; - -# elif HAVE_ACLSORT /* NonStop Kernel */ - - struct acl entries[4]; - int ret; - - entries[0].a_type = USER_OBJ; - entries[0].a_id = 0; /* irrelevant */ - entries[0].a_perm = (mode >> 6) & 7; - entries[1].a_type = GROUP_OBJ; - entries[1].a_id = 0; /* irrelevant */ - entries[1].a_perm = (mode >> 3) & 7; - entries[2].a_type = CLASS_OBJ; - entries[2].a_id = 0; - entries[2].a_perm = (mode >> 3) & 7; - entries[3].a_type = OTHER_OBJ; - entries[3].a_id = 0; - entries[3].a_perm = mode & 7; - - ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries); - if (ret > 0) - abort (); - if (ret < 0) - { - if (0) - return chmod_or_fchmod (name, desc, mode); - return -1; - } - - ret = acl ((char *) name, ACL_SET, - sizeof (entries) / sizeof (struct acl), entries); - if (ret < 0) - { - if (0) - return chmod_or_fchmod (name, desc, mode); - return -1; - } - - if (mode & (S_ISUID | S_ISGID | S_ISVTX)) - { - /* We did not call chmod so far, so the special bits have not yet - been set. */ - return chmod_or_fchmod (name, desc, mode); - } - return 0; - -# else /* Unknown flavor of ACLs */ - return chmod_or_fchmod (name, desc, mode); -# endif -#else /* !USE_ACL */ - return chmod_or_fchmod (name, desc, mode); -#endif } diff --git a/lib/set-permissions.c b/lib/set-permissions.c new file mode 100644 index 0000000..47cb91c --- /dev/null +++ b/lib/set-permissions.c @@ -0,0 +1,833 @@ +/* set-permissions.c - set permissions of a file + + Copyright (C) 2002-2003, 2005-2015 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */ + +#include + +#include "acl.h" + +#include "acl-internal.h" + +#if USE_ACL +# if ! defined HAVE_ACL_FROM_MODE && defined HAVE_ACL_FROM_TEXT /* FreeBSD, IRIX, Tru64 */ +static acl_t +acl_from_mode (mode_t mode) +{ +# if HAVE_ACL_FREE_TEXT /* Tru64 */ + char acl_text[] = "u::---,g::---,o::---,"; +# else /* FreeBSD, IRIX */ + char acl_text[] = "u::---,g::---,o::---"; +# endif + + if (mode & S_IRUSR) acl_text[ 3] = 'r'; + if (mode & S_IWUSR) acl_text[ 4] = 'w'; + if (mode & S_IXUSR) acl_text[ 5] = 'x'; + if (mode & S_IRGRP) acl_text[10] = 'r'; + if (mode & S_IWGRP) acl_text[11] = 'w'; + if (mode & S_IXGRP) acl_text[12] = 'x'; + if (mode & S_IROTH) acl_text[17] = 'r'; + if (mode & S_IWOTH) acl_text[18] = 'w'; + if (mode & S_IXOTH) acl_text[19] = 'x'; + + return acl_from_text (acl_text); +} +# endif + +# if HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ +static int +set_acls_from_mode (const char *name, int desc, mode_t mode, bool *must_chmod) +{ +# ifdef ACE_GETACL + /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4 + file systems (whereas the other ones are used in UFS file systems). */ + + /* The flags in the ace_t structure changed in a binary incompatible way + when ACL_NO_TRIVIAL etc. were introduced in version 1.15. + How to distinguish the two conventions at runtime? + We fetch the existing ACL. In the old convention, usually three ACEs have + a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400. + In the new convention, these values are not used. */ + int convention; + + { + /* Initially, try to read the entries into a stack-allocated buffer. + Use malloc if it does not fit. */ + enum + { + alloc_init = 4000 / sizeof (ace_t), /* >= 3 */ + alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t)) + }; + ace_t buf[alloc_init]; + size_t alloc = alloc_init; + ace_t *entries = buf; + ace_t *malloced = NULL; + int count; + + for (;;) + { + count = (desc != -1 + ? facl (desc, ACE_GETACL, alloc, entries) + : acl (name, ACE_GETACL, alloc, entries)); + if (count < 0 && errno == ENOSPC) + { + /* Increase the size of the buffer. */ + free (malloced); + if (alloc > alloc_max / 2) + { + errno = ENOMEM; + return -1; + } + alloc = 2 * alloc; /* <= alloc_max */ + entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t)); + if (entries == NULL) + { + errno = ENOMEM; + return -1; + } + continue; + } + break; + } + + if (count <= 0) + convention = -1; + else + { + int i; + + convention = 0; + for (i = 0; i < count; i++) + if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER)) + { + convention = 1; + break; + } + } + free (malloced); + } + + if (convention >= 0) + { + ace_t entries[6]; + int count; + int ret; + + if (convention) + { + /* Running on Solaris 10. */ + entries[0].a_type = OLD_ALLOW; + entries[0].a_flags = OLD_ACE_OWNER; + entries[0].a_who = 0; /* irrelevant */ + entries[0].a_access_mask = (mode >> 6) & 7; + entries[1].a_type = OLD_ALLOW; + entries[1].a_flags = OLD_ACE_GROUP; + entries[1].a_who = 0; /* irrelevant */ + entries[1].a_access_mask = (mode >> 3) & 7; + entries[2].a_type = OLD_ALLOW; + entries[2].a_flags = OLD_ACE_OTHER; + entries[2].a_who = 0; + entries[2].a_access_mask = mode & 7; + count = 3; + } + else + { + /* Running on Solaris 10 (newer version) or Solaris 11. + The details here were found through "/bin/ls -lvd somefiles". */ + entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE; + entries[0].a_flags = NEW_ACE_OWNER; + entries[0].a_who = 0; /* irrelevant */ + entries[0].a_access_mask = 0; + entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE; + entries[1].a_flags = NEW_ACE_OWNER; + entries[1].a_who = 0; /* irrelevant */ + entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS + | NEW_ACE_WRITE_ATTRIBUTES + | NEW_ACE_WRITE_ACL + | NEW_ACE_WRITE_OWNER; + if (mode & 0400) + entries[1].a_access_mask |= NEW_ACE_READ_DATA; + else + entries[0].a_access_mask |= NEW_ACE_READ_DATA; + if (mode & 0200) + entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; + else + entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; + if (mode & 0100) + entries[1].a_access_mask |= NEW_ACE_EXECUTE; + else + entries[0].a_access_mask |= NEW_ACE_EXECUTE; + entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE; + entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP; + entries[2].a_who = 0; /* irrelevant */ + entries[2].a_access_mask = 0; + entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE; + entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP; + entries[3].a_who = 0; /* irrelevant */ + entries[3].a_access_mask = 0; + if (mode & 0040) + entries[3].a_access_mask |= NEW_ACE_READ_DATA; + else + entries[2].a_access_mask |= NEW_ACE_READ_DATA; + if (mode & 0020) + entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; + else + entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; + if (mode & 0010) + entries[3].a_access_mask |= NEW_ACE_EXECUTE; + else + entries[2].a_access_mask |= NEW_ACE_EXECUTE; + entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE; + entries[4].a_flags = NEW_ACE_EVERYONE; + entries[4].a_who = 0; + entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS + | NEW_ACE_WRITE_ATTRIBUTES + | NEW_ACE_WRITE_ACL + | NEW_ACE_WRITE_OWNER; + entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE; + entries[5].a_flags = NEW_ACE_EVERYONE; + entries[5].a_who = 0; + entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS + | NEW_ACE_READ_ATTRIBUTES + | NEW_ACE_READ_ACL + | NEW_ACE_SYNCHRONIZE; + if (mode & 0004) + entries[5].a_access_mask |= NEW_ACE_READ_DATA; + else + entries[4].a_access_mask |= NEW_ACE_READ_DATA; + if (mode & 0002) + entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; + else + entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; + if (mode & 0001) + entries[5].a_access_mask |= NEW_ACE_EXECUTE; + else + entries[4].a_access_mask |= NEW_ACE_EXECUTE; + count = 6; + } + if (desc != -1) + ret = facl (desc, ACE_SETACL, count, entries); + else + ret = acl (name, ACE_SETACL, count, entries); + if (ret < 0 && errno != EINVAL && errno != ENOTSUP) + { + if (errno == ENOSYS) + { + *must_chmod = true; + return 0; + } + return -1; + } + if (ret == 0) + return 0; + } +# endif + + { + aclent_t entries[3]; + int ret; + + entries[0].a_type = USER_OBJ; + entries[0].a_id = 0; /* irrelevant */ + entries[0].a_perm = (mode >> 6) & 7; + entries[1].a_type = GROUP_OBJ; + entries[1].a_id = 0; /* irrelevant */ + entries[1].a_perm = (mode >> 3) & 7; + entries[2].a_type = OTHER_OBJ; + entries[2].a_id = 0; + entries[2].a_perm = mode & 7; + + if (desc != -1) + ret = facl (desc, SETACL, + sizeof (entries) / sizeof (aclent_t), entries); + else + ret = acl (name, SETACL, + sizeof (entries) / sizeof (aclent_t), entries); + if (ret < 0) + { + if (errno == ENOSYS || errno == EOPNOTSUPP) + { + *must_chmod = true; + return 0; + } + return -1; + } + } +} + +#elif HAVE_GETACL /* HP-UX */ +static int +context_acl_from_mode (struct permission_context *ctx, const char *name, int desc) +{ + struct stat statbuf; + int ret; + + if (desc != -1) + ret = fstat (desc, &statbuf); + else + ret = stat (name, &statbuf); + if (ret < 0) + return -1; + + ctx->entries[0].uid = statbuf.st_uid; + ctx->entries[0].gid = ACL_NSGROUP; + ctx->entries[0].mode = (mode >> 6) & 7; + ctx->entries[1].uid = ACL_NSUSER; + ctx->entries[1].gid = statbuf.st_gid; + ctx->entries[1].mode = (mode >> 3) & 7; + ctx->entries[2].uid = ACL_NSUSER; + ctx->entries[2].gid = ACL_NSGROUP; + ctx->entries[2].mode = mode & 7; + ctx->count = 3; + return 0; +} + +# if HAVE_ACLV_H /* HP-UX >= 11.11 */ +static int +context_aclv_from_mode (struct permission_context *ctx) +{ + int ret; + + ctx->aclv_entries[0].a_type = USER_OBJ; + ctx->aclv_entries[0].a_id = 0; /* irrelevant */ + ctx->aclv_entries[0].a_perm = (mode >> 6) & 7; + ctx->aclv_entries[1].a_type = GROUP_OBJ; + ctx->aclv_entries[1].a_id = 0; /* irrelevant */ + ctx->aclv_entries[1].a_perm = (mode >> 3) & 7; + ctx->aclv_entries[2].a_type = CLASS_OBJ; + ctx->aclv_entries[2].a_id = 0; + ctx->aclv_entries[2].a_perm = (mode >> 3) & 7; + ctx->aclv_entries[3].a_type = OTHER_OBJ; + ctx->aclv_entries[3].a_id = 0; + ctx->aclv_entries[3].a_perm = mode & 7; + ctx->aclv_count = 4; + + ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries); + if (ret > 0) + abort (); + return ret; +} +#endif + +# elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */ +static int +set_acls_from_mode (const char *name, int desc, mode_t mode, bool *must_chmod) +{ + acl_type_list_t types; + size_t types_size = sizeof (types); + acl_type_t type; + + if (aclx_gettypes (name, &types, &types_size) < 0 + || types.num_entries == 0) + { + *must_chmod = true; + return 0; + } + + /* XXX Do we need to clear all types of ACLs for the given file, or is it + sufficient to clear the first one? */ + type = types.entries[0]; + if (type.u64 == ACL_AIXC) + { + union { struct acl a; char room[128]; } u; + int ret; + + u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */ + u.a.acl_mode = mode & ~(S_IXACL | 0777); + u.a.u_access = (mode >> 6) & 7; + u.a.g_access = (mode >> 3) & 7; + u.a.o_access = mode & 7; + + if (desc != -1) + ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS, + type, &u.a, u.a.acl_len, mode); + else + ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS, + type, &u.a, u.a.acl_len, mode); + if (!(ret < 0 && errno == ENOSYS)) + return ret; + } + else if (type.u64 == ACL_NFS4) + { + union { nfs4_acl_int_t a; char room[128]; } u; + nfs4_ace_int_t *ace; + int ret; + + u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION; + u.a.aclEntryN = 0; + ace = &u.a.aclEntry[0]; + { + ace->flags = ACE4_ID_SPECIAL; + ace->aceWho.special_whoid = ACE4_WHO_OWNER; + ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE; + ace->aceFlags = 0; + ace->aceMask = + (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0) + | (mode & 0200 + ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA + | ACE4_ADD_SUBDIRECTORY + : 0) + | (mode & 0100 ? ACE4_EXECUTE : 0); + ace->aceWhoString[0] = '\0'; + ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace; + ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4]; + u.a.aclEntryN++; + } + { + ace->flags = ACE4_ID_SPECIAL; + ace->aceWho.special_whoid = ACE4_WHO_GROUP; + ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE; + ace->aceFlags = 0; + ace->aceMask = + (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0) + | (mode & 0020 + ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA + | ACE4_ADD_SUBDIRECTORY + : 0) + | (mode & 0010 ? ACE4_EXECUTE : 0); + ace->aceWhoString[0] = '\0'; + ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace; + ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4]; + u.a.aclEntryN++; + } + { + ace->flags = ACE4_ID_SPECIAL; + ace->aceWho.special_whoid = ACE4_WHO_EVERYONE; + ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE; + ace->aceFlags = 0; + ace->aceMask = + (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0) + | (mode & 0002 + ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA + | ACE4_ADD_SUBDIRECTORY + : 0) + | (mode & 0001 ? ACE4_EXECUTE : 0); + ace->aceWhoString[0] = '\0'; + ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace; + ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4]; + u.a.aclEntryN++; + } + u.a.aclLength = (char *) ace - (char *) &u.a; + + if (desc != -1) + ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS, + type, &u.a, u.a.aclLength, mode); + else + ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS, + type, &u.a, u.a.aclLength, mode); + if (!(ret < 0 && errno == ENOSYS)) + return ret; + } + + *must_chmod = true; + return 0; +} + +# elif HAVE_STATACL /* older AIX */ +static int +context_acl_from_mode (struct permission_context *ctx) +{ + ctx->u.a.acl_len = (char *) &ctx->u.a.acl_ext[0] - (char *) &ctx->u.a; /* no entries */ + ctx->u.a.acl_mode = ctx->mode & ~(S_IXACL | 0777); + ctx->u.a.u_access = (ctx->mode >> 6) & 7; + ctx->u.a.g_access = (ctx->mode >> 3) & 7; + ctx->u.a.o_access = ctx->mode & 7; + ctx->have_u = true; + return 0; +} + +# elif HAVE_ACLSORT /* NonStop Kernel */ +static int +context_acl_from_mode (struct permission_context *ctx) +{ + int ret; + + ctx->entries[0].a_type = USER_OBJ; + ctx->entries[0].a_id = 0; /* irrelevant */ + ctx->entries[0].a_perm = (mode >> 6) & 7; + ctx->entries[1].a_type = GROUP_OBJ; + ctx->entries[1].a_id = 0; /* irrelevant */ + ctx->entries[1].a_perm = (mode >> 3) & 7; + ctx->entries[2].a_type = CLASS_OBJ; + ctx->entries[2].a_id = 0; + ctx->entries[2].a_perm = (mode >> 3) & 7; + ctx->entries[3].a_type = OTHER_OBJ; + ctx->entries[3].a_id = 0; + ctx->entries[3].a_perm = mode & 7; + ctx->count = 4; + + ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries); + if (ret > 0) + abort (); + return ret; +} +# endif + +static int +set_acls (struct permission_context *ctx, const char *name, int desc, + int from_mode, bool *must_chmod, bool *acls_set) +{ + int ret = 0; + +#if HAVE_ACL_GET_FILE + /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ + /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */ +# if !HAVE_ACL_TYPE_EXTENDED + /* Linux, FreeBSD, IRIX, Tru64 */ + +# ifndef HAVE_ACL_FROM_TEXT +# error Must have acl_from_text (see POSIX 1003.1e draft 17). +# endif +# ifndef HAVE_ACL_DELETE_DEF_FILE +# error Must have acl_delete_def_file (see POSIX 1003.1e draft 17). +# endif + + if (! ctx->acls_not_supported) + { + if (ret == 0 && from_mode) + { + if (ctx->acl) + acl_free (ctx->acl); + ctx->acl = acl_from_mode (ctx->mode); + if (ctx->acl == NULL) + ret = -1; + } + + if (ret == 0 && ctx->acl) + { + if (HAVE_ACL_SET_FD && desc != -1) + ret = acl_set_fd (desc, ctx->acl); + else + ret = acl_set_file (name, ACL_TYPE_ACCESS, ctx->acl); + if (ret != 0) + { + if (! acl_errno_valid (errno)) + { + ctx->acls_not_supported = true; + if (from_mode || acl_access_nontrivial (ctx->acl) == 0) + ret = 0; + } + } + else + { + *acls_set = true; + if (S_ISDIR(ctx->mode)) + { + if (! from_mode && ctx->default_acl) + ret = acl_set_file (name, ACL_TYPE_DEFAULT, + ctx->default_acl); + else + ret = acl_delete_def_file (name); + } + } + } + } + +# else /* HAVE_ACL_TYPE_EXTENDED */ + /* Mac OS X */ + + /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS) + and acl_get_file (name, ACL_TYPE_DEFAULT) + always return NULL / EINVAL. You have to use + acl_get_file (name, ACL_TYPE_EXTENDED) + or acl_get_fd (open (name, ...)) + to retrieve an ACL. + On the other hand, + acl_set_file (name, ACL_TYPE_ACCESS, acl) + and acl_set_file (name, ACL_TYPE_DEFAULT, acl) + have the same effect as + acl_set_file (name, ACL_TYPE_EXTENDED, acl): + Each of these calls sets the file's ACL. */ + + if (ctx->acl == NULL) + { + acl_t acl; + + /* Remove ACLs if the file has ACLs. */ + if (HAVE_ACL_GET_FD && desc != -1) + acl = acl_get_fd (desc); + else + acl = acl_get_file (name, ACL_TYPE_EXTENDED); + if (acl) + { + acl_free (acl); + + acl = acl_init (acl); + if (acl) + { + if (HAVE_ACL_SET_FD && desc != -1) + ret = acl_set_fd (desc, acl); + else + ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl); + acl_free (acl); + } + else + ret = -1; + } + } + else + { + if (HAVE_ACL_SET_FD && desc != -1) + ret = acl_set_fd (desc, acl); + else + ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl); + if (ret != 0) + { + if (! acl_errno_valid (saved_errno) && ! acl_extended_nontrivial (acl)) + ret = 0; + } + } + *acls_set = true; + +# endif + +# elif defined GETACL /* Solaris, Cygwin, not HP-UX */ + + /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions + of Unixware. The acl() call returns the access and default ACL both + at once. */ + + /* If both ace_entries and entries are available, try SETACL before + ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL + can. */ + + if (from_mode) + return set_acls_from_mode (name, desc, ctx->mode, must_chmod); + + if (ret == 0 && ctx->count) + { + if (desc != -1) + ret = facl (desc, SETACL, count, entries); + else + ret = acl (name, SETACL, count, entries); + if (ret < 0) + { + if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) + && acl_nontrivial (count, entries) == 0) + ret = 0; + } + else + *acls_set = true; + } + +# ifdef ACE_GETACL + if (ret == 0 && ctx->ace_count) + { + if (desc != -1) + ret = facl (desc, ACE_SETACL, ace_count, ace_entries); + else + ret = acl (name, ACE_SETACL, ace_count, ace_entries); + if (ret < 0) + { + if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP) + && acl_ace_nontrivial (ace_count, ace_entries) == 0) + ret = 0; + } + else + *acls_set = true; + } +# endif + +#elif HAVE_GETACL /* HP-UX */ + + if (from_mode) + ret = context_acl_from_mode (ctx, name, desc); + + if (ret == 0 && ctx->count > 0) + { + if (desc != -1) + ret = fsetacl (desc, ctx->count, ctx->entries); + else + ret = setacl (name, ctx->count, ctx->entries); + if (ret < 0) + { + if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) + && (from_mode || !acl_nontrivial (ctx->count, ctx->entries, &source_statbuf))) + ret = 0; + } + else + *acls_set = true; + } + +# if HAVE_ACLV_H + if (from_mode) + ret = context_aclv_from_mode (ctx); + + if (ret == 0 && ctx->aclv_count > 0) + { + ret = acl ((char *) name, ACL_SET, ctx->aclv_count, ctx->aclv_entries); + if (ret < 0) + { + if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) + && (from_mode || !aclv_nontrivial (ctx->aclv_count, ctx->aclv_entries))) + ret = 0; + } + else + *acls_set = true; + } +# endif + +# elif HAVE_ACLX_GET && ACL_AIX_WIP /* AIX */ + + /* TODO: Implement setting ACLs once get_permissions() reads them. */ + + if (from_mode) + ret = set_acls_from_mode (name, desc, mode, must_chmod); + +# elif HAVE_STATACL /* older AIX */ + + if (from_mode) + ret = context_acl_from_mode (ctx); + + if (ret == 0 && ctx->have_u) + { + if (desc != -1) + ret = fchacl (desc, &u.a, u.a.acl_len); + else + ret = chacl (name, &u.a, u.a.acl_len); + if (ret < 0) + { + if (errno == ENOSYS && from_mode) + ret = 0; + } + else + *acls_set = true; + } + +# elif HAVE_ACLSORT /* NonStop Kernel */ + + if (from_mode) + ret = context_acl_from_mode (ctx); + + if (ret == 0 && ctx->count) + { + ret = acl ((char *) name, ACL_SET, ctx->count, ctx->entries); + if (ret != 0) + { + if (!acl_nontrivial (ctx->count, ctx->entries)) + ret = 0; + } + else + *acls_set = true; + } + +# else /* No ACLs */ + + /* Nothing to do. */ + +#endif + + return ret; +} +#endif + +/* If DESC is a valid file descriptor use fchmod to change the + file's mode to MODE on systems that have fchmod. On systems + that don't have fchmod and if DESC is invalid, use chmod on + NAME instead. + Return 0 if successful. Return -1 and set errno upon failure. */ + +int +chmod_or_fchmod (const char *name, int desc, mode_t mode) +{ + if (HAVE_FCHMOD && desc != -1) + return fchmod (desc, mode); + else + return chmod (name, mode); +} + +/* Set the permissions in CTX on a file. If DESC is a valid file descriptor, + use file descriptor operations, else use filename based operations on NAME. + If access control lists are not available, fchmod the target file to the + mode in CTX. Also sets the non-permission bits of the destination file + (S_ISUID, S_ISGID, S_ISVTX) to those from the mode in CTX if any are set. + Return 0 if successful. Return -1 and set errno upon failure. */ + +int +set_permissions (struct permission_context *ctx, const char *name, int desc) +{ + bool acls_set _GL_UNUSED = false; + bool early_chmod; + bool must_chmod = false; + int ret = 0; + +#if USE_ACL +# if HAVE_STATACL + /* older AIX */ + /* There is no need to call chmod_or_fchmod, since the mode + bits S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL. */ + + early_chmod = false; +# else + /* All other plaforms */ + /* On Cygwin, it is necessary to call chmod before acl, because + chmod can change the contents of the ACL (in ways that don't + change the allowed accesses, but still visible). */ + + early_chmod = (! MODE_INSIDE_ACL || (ctx->mode & (S_ISUID | S_ISGID | S_ISVTX))); +# endif +#else + /* No ACLs */ + + early_chmod = true; +#endif + + if (early_chmod) + { + ret = chmod_or_fchmod (name, desc, ctx->mode); + if (ret != 0) + return -1; + } + +#if USE_ACL + ret = set_acls (ctx, name, desc, false, &must_chmod, &acls_set); + if (! acls_set) + { + int saved_errno = ret ? errno : 0; + + /* If we can't set an acl which we expect to be able to set, try setting + the permissions to ctx->mode. Doe to possible inherited permissions, + we cannot simply chmod. */ + + acls_set = false; + ret = set_acls (ctx, name, desc, true, &must_chmod, &acls_set); + if (! acls_set) + must_chmod = true; + + if (saved_errno) + { + errno = saved_errno; + ret = -1; + } + } +#endif + + if (must_chmod && ! early_chmod) + { + int saved_errno = ret ? errno : 0; + + ret = chmod_or_fchmod (name, desc, ctx->mode); + + if (saved_errno) + { + errno = saved_errno; + ret = -1; + } + } + + return ret; +} diff --git a/modules/qacl b/modules/qacl index bc7382b..d91507b 100644 --- a/modules/qacl +++ b/modules/qacl @@ -7,8 +7,10 @@ lib/acl-internal.h lib/acl-errno-valid.c lib/acl_entries.c lib/acl-internal.c +lib/get-permissions.c lib/qcopy-acl.c lib/qset-acl.c +lib/set-permissions.c m4/acl.m4 Depends-on: @@ -21,7 +23,7 @@ configure.ac: gl_FUNC_ACL Makefile.am: -lib_SOURCES += acl-errno-valid.c acl-internal.c qcopy-acl.c qset-acl.c +lib_SOURCES += acl-errno-valid.c acl-internal.c qcopy-acl.c qset-acl.c get-permissions.c set-permissions.c Include: "acl.h" -- 2.1.0 From debbugs-submit-bounces@debbugs.gnu.org Sat May 30 20:30:12 2015 Received: (at control) by debbugs.gnu.org; 31 May 2015 00:30:13 +0000 Received: from localhost ([127.0.0.1]:33835 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1Yyr8k-00074N-W2 for submit@debbugs.gnu.org; Sat, 30 May 2015 20:30:11 -0400 Received: from mail2.vodafone.ie ([213.233.128.44]:24323) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1Yyr8e-00073L-8H for control@debbugs.gnu.org; Sat, 30 May 2015 20:30:06 -0400 X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: At4iALlUalVtT7M5/2dsb2JhbABcgxAjb4FcgWKJNrZzgwCBAUwBAQEBAQGBC0EBAwGDfAoqVA0CBSECEQI6BgICCA0IAQGILQGiBI9fhWuddYEhkVwMLxKBMwWmKo8kI4FHAQEIAgGCJT2CeAIBAg Received: from unknown (HELO localhost.localdomain) ([109.79.179.57]) by mail2.vodafone.ie with ESMTP; 31 May 2015 01:29:57 +0100 Message-ID: <556A5605.60604@draigBrady.com> Date: Sun, 31 May 2015 01:29:57 +0100 From: =?UTF-8?B?UMOhZHJhaWcgQnJhZHk=?= User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.6.0 MIME-Version: 1.0 To: GNU bug tracker automated control server Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit X-Spam-Score: 2.0 (++) X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org", has identified this incoming email as possible spam. The original message has been attached to this so you can view it (if it isn't spam) or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: forcemerge 20310 20311 20312 20666 20667 20696 close 20310 [...] Content analysis details: (2.0 points, 10.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [213.233.128.44 listed in list.dnswl.org] 1.8 MISSING_SUBJECT Missing Subject: header 0.2 NO_SUBJECT Extra score for no subject X-Debbugs-Envelope-To: control X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: 2.0 (++) X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org", has identified this incoming email as possible spam. The original message has been attached to this so you can view it (if it isn't spam) or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: forcemerge 20310 20311 20312 20666 20667 20696 close 20310 [...] Content analysis details: (2.0 points, 10.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [213.233.128.44 listed in list.dnswl.org] 1.8 MISSING_SUBJECT Missing Subject: header 0.2 NO_SUBJECT Extra score for no subject forcemerge 20310 20311 20312 20666 20667 20696 close 20310