/*
Bacula® - The Network Backup Solution
- Copyright (C) 2004-2009 Free Software Foundation Europe e.V.
+ Copyright (C) 2004-2010 Free Software Foundation Europe e.V.
The main author of Bacula is Kern Sibbald, with contributions from
many others, a complete list can be found in the file AUTHORS.
This program is Free Software; you can redistribute it and/or
- modify it under the terms of version two of the GNU General Public
+ modify it under the terms of version three of the GNU Affero General Public
License as published by the Free Software Foundation and included
in the file LICENSE.
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
+ You should have received a copy of the GNU Affero General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
(FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
Switzerland, email:ftf@fsfeurope.org.
*/
-/*
+/**
* Functions to handle ACLs for bacula.
*
+ * Currently we support the following OSes:
+ * - AIX (pre-5.3 and post 5.3 acls, acl_get and aclx_get interface)
+ * - Darwin
+ * - FreeBSD (POSIX and NFSv4/ZFS acls)
+ * - HPUX
+ * - IRIX
+ * - Linux
+ * - Solaris (POSIX and NFSv4/ZFS acls)
+ * - Tru64
+ *
+ * Next to OS specific acls we support AFS acls using the pioctl interface.
+ *
* We handle two different types of ACLs: access and default ACLS.
* On most systems that support default ACLs they only apply to directories.
*
#include "bacula.h"
#include "filed.h"
-#if !defined(HAVE_ACL)
-/*
+#if !defined(HAVE_ACL) && !defined(HAVE_AFS_ACL)
+/**
* Entry points when compiled without support for ACLs or on an unsupported platform.
*/
bacl_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
return bacl_exit_fatal;
}
-bacl_exit_code parse_acl_stream(JCR *jcr, int stream)
+bacl_exit_code parse_acl_streams(JCR *jcr, int stream)
{
return bacl_exit_fatal;
}
#else
-/*
+/**
* Send an ACL stream to the SD.
*/
static bacl_exit_code send_acl_stream(JCR *jcr, int stream)
return bacl_exit_ok;
#endif
- /*
+ /**
* Sanity check
*/
if (jcr->acl_data->content_length <= 0) {
return bacl_exit_ok;
}
- /*
+ /**
* Send header
*/
if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) {
return bacl_exit_fatal;
}
- /*
+ /**
* Send the buffer to the storage deamon
*/
Dmsg1(400, "Backing up ACL <%s>\n", jcr->acl_data->content);
return bacl_exit_ok;
}
+/**
+ * First the native ACLs.
+ */
+#if defined(HAVE_ACL)
#if defined(HAVE_AIX_OS)
+#if defined(HAVE_EXTENDED_ACL)
+
#include <sys/access.h>
+#include <sys/acl.h>
-/*
+static bool acl_is_trivial(struct acl *acl)
+{
+ return (acl_last(acl) != acl->acl_ext ? false : true);
+}
+
+static bool acl_nfs4_is_trivial(nfs4_acl_int_t *acl)
+{
+ return (acl->aclEntryN > 0 ? false : true);
+}
+
+/**
+ * Define the supported ACL streams for this OS
+ */
+static int os_access_acl_streams[3] = { STREAM_ACL_AIX_TEXT, STREAM_ACL_AIX_AIXC, STREAM_ACL_AIX_NFS4 };
+static int os_default_acl_streams[1] = { -1 };
+
+static bacl_exit_code aix_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
+{
+ berrno be;
+ mode_t mode;
+ acl_type_t type;
+ size_t aclsize, acltxtsize;
+ bacl_exit_code retval = bacl_exit_error;
+ POOLMEM *aclbuf = get_pool_memory(PM_MESSAGE);
+
+ /**
+ * First see how big the buffers should be.
+ */
+ type.u64 = ACL_ANY;
+ if (aclx_get(jcr->last_fname, GET_ACLINFO_ONLY, &type, NULL, &aclsize, NULL) < 0) {
+ switch (errno) {
+ case ENOENT:
+ retval = bacl_exit_ok;
+ goto bail_out;
+ default:
+ Mmsg2(jcr->errmsg, _("aclx_get error on file \"%s\": ERR=%s\n"),
+ jcr->last_fname, be.bstrerror());
+ Dmsg2(100, "aclx_get error file=%s ERR=%s\n",
+ jcr->last_fname, be.bstrerror());
+ goto bail_out;
+ }
+ }
+
+ /**
+ * Make sure the buffers are big enough.
+ */
+ aclbuf = check_pool_memory_size(aclbuf, aclsize + 1);
+
+ /**
+ * Retrieve the ACL info.
+ */
+ if (aclx_get(jcr->last_fname, 0, &type, aclbuf, &aclsize, &mode) < 0) {
+ switch (errno) {
+ case ENOENT:
+ retval = bacl_exit_ok;
+ goto bail_out;
+ default:
+ Mmsg2(jcr->errmsg, _("aclx_get error on file \"%s\": ERR=%s\n"),
+ jcr->last_fname, be.bstrerror());
+ Dmsg2(100, "aclx_get error file=%s ERR=%s\n",
+ jcr->last_fname, be.bstrerror());
+ goto bail_out;
+ }
+ }
+
+ /**
+ * See if the acl is non trivial.
+ */
+ switch (type.u64) {
+ case ACL_AIXC:
+ if (acl_is_trivial((struct acl *)aclbuf)) {
+ retval = bacl_exit_ok;
+ goto bail_out;
+ }
+ break;
+ case ACL_NFS4:
+ if (acl_nfs4_is_trivial((nfs4_acl_int_t *)aclbuf)) {
+ retval = bacl_exit_ok;
+ goto bail_out;
+ }
+ break;
+ default:
+ Mmsg2(jcr->errmsg, _("Unknown acl type encountered on file \"%s\": %ld\n"),
+ jcr->last_fname, type.u64);
+ Dmsg2(100, "Unknown acl type encountered on file \"%s\": %ld\n",
+ jcr->last_fname, type.u64);
+ goto bail_out;
+ }
+
+ /**
+ * We have a non-trivial acl lets convert it into some ASCII form.
+ */
+ acltxtsize = sizeof_pool_memory(jcr->acl_data->content);
+ if (aclx_printStr(jcr->acl_data->content, &acltxtsize, aclbuf,
+ aclsize, type, jcr->last_fname, 0) < 0) {
+ switch (errno) {
+ case ENOSPC:
+ /**
+ * Our buffer is not big enough, acltxtsize should be updated with the value
+ * the aclx_printStr really need. So we increase the buffer and try again.
+ */
+ jcr->acl_data->content = check_pool_memory_size(jcr->acl_data->content, acltxtsize + 1);
+ if (aclx_printStr(jcr->acl_data->content, &acltxtsize, aclbuf,
+ aclsize, type, jcr->last_fname, 0) < 0) {
+ Mmsg1(jcr->errmsg, _("Failed to convert acl into text on file \"%s\"\n"),
+ jcr->last_fname);
+ Dmsg2(100, "Failed to convert acl into text on file \"%s\": %ld\n",
+ jcr->last_fname, type.u64);
+ goto bail_out;
+ }
+ break;
+ default:
+ Mmsg1(jcr->errmsg, _("Failed to convert acl into text on file \"%s\"\n"),
+ jcr->last_fname);
+ Dmsg2(100, "Failed to convert acl into text on file \"%s\": %ld\n",
+ jcr->last_fname, type.u64);
+ goto bail_out;
+ }
+ }
+
+ jcr->acl_data->content_length = strlen(jcr->acl_data->content) + 1;
+ switch (type.u64) {
+ case ACL_AIXC:
+ retval = send_acl_stream(jcr, STREAM_ACL_AIX_AIXC);
+ case ACL_NFS4:
+ retval = send_acl_stream(jcr, STREAM_ACL_AIX_NFS4);
+ }
+
+bail_out:
+ free_pool_memory(aclbuf);
+
+ return retval;
+}
+
+static bacl_exit_code aix_parse_acl_streams(JCR *jcr, int stream)
+{
+ int cnt;
+ berrno be;
+ acl_type_t type;
+ size_t aclsize;
+ bacl_exit_code retval = bacl_exit_error;
+ POOLMEM *aclbuf = get_pool_memory(PM_MESSAGE);
+
+ switch (stream) {
+ case STREAM_ACL_AIX_TEXT:
+ /**
+ * Handle the old stream using the old system call for now.
+ */
+ if (acl_put(jcr->last_fname, jcr->acl_data->content, 0) != 0) {
+ retval = bacl_exit_error;
+ goto bail_out;
+ }
+ retval = bacl_exit_ok;
+ goto bail_out;
+ case STREAM_ACL_AIX_AIXC:
+ type.u64 = ACL_AIXC;
+ break;
+ case STREAM_ACL_AIX_NFS4:
+ type.u64 = ACL_NFS4;
+ break;
+ default:
+ goto bail_out;
+ } /* end switch (stream) */
+
+ /**
+ * Set the acl buffer to an initial size. For now we set it
+ * to the same size as the ASCII representation.
+ */
+ aclbuf = check_pool_memory_size(aclbuf, jcr->acl_data->content_length);
+ aclsize = jcr->acl_data->content_length;
+ if (aclx_scanStr(jcr->acl_data->content, aclbuf, &aclsize, type) < 0) {
+ switch (errno) {
+ case ENOSPC:
+ /**
+ * The buffer isn't big enough. The man page doesn't say that aclsize
+ * is updated to the needed size as what is done with aclx_printStr.
+ * So for now we try to increase the buffer a maximum of 3 times
+ * and retry the conversion.
+ */
+ for (cnt = 0; cnt < 3; cnt++) {
+ aclsize = 2 * aclsize;
+ aclbuf = check_pool_memory_size(aclbuf, aclsize);
+
+ if (aclx_scanStr(jcr->acl_data->content, aclbuf, &aclsize, type) == 0) {
+ break;
+ }
+
+ /**
+ * See why we failed this time, ENOSPC retry if max retries not met,
+ * otherwise abort.
+ */
+ switch (errno) {
+ case ENOSPC:
+ if (cnt < 3) {
+ continue;
+ }
+ /* FALL THROUGH */
+ default:
+ Mmsg2(jcr->errmsg, _("aclx_scanStr error on file \"%s\": ERR=%s\n"),
+ jcr->last_fname, be.bstrerror());
+ Dmsg2(100, "aclx_scanStr error file=%s ERR=%s\n",
+ jcr->last_fname, be.bstrerror());
+ goto bail_out;
+ }
+ }
+ break;
+ default:
+ Mmsg2(jcr->errmsg, _("aclx_scanStr error on file \"%s\": ERR=%s\n"),
+ jcr->last_fname, be.bstrerror());
+ Dmsg2(100, "aclx_scanStr error file=%s ERR=%s\n",
+ jcr->last_fname, be.bstrerror());
+ }
+ }
+
+ if (aclx_put(jcr->last_fname, SET_ACL, type, aclbuf, aclsize, 0) < 0) {
+ switch (errno) {
+ case ENOENT:
+ retval = bacl_exit_ok;
+ goto bail_out;
+ default:
+ Mmsg2(jcr->errmsg, _("aclx_put error on file \"%s\": ERR=%s\n"),
+ jcr->last_fname, be.bstrerror());
+ Dmsg2(100, "aclx_put error file=%s ERR=%s\n",
+ jcr->last_fname, be.bstrerror());
+ goto bail_out;
+ }
+ }
+
+ retval = bacl_exit_ok;
+
+bail_out:
+ free_pool_memory(aclbuf);
+
+ return retval;
+}
+
+#else /* HAVE_EXTENDED_ACL */
+
+#include <sys/access.h>
+
+/**
* Define the supported ACL streams for this OS
*/
static int os_access_acl_streams[1] = { STREAM_ACL_AIX_TEXT };
}
return bacl_exit_ok;
}
+#endif /* HAVE_EXTENDED_ACL */
-/*
+/**
* For this OS setup the build and parse function pointer to the OS specific functions.
*/
static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = aix_build_acl_streams;
#error "configure failed to detect availability of sys/acl.h"
#endif
-/* On IRIX we can get shortened ACLs */
+/**
+ * On IRIX we can get shortened ACLs
+ */
#if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
#define acl_to_text(acl,len) acl_to_short_text((acl), (len))
#endif
-/* In Linux we can get numeric and/or shorted ACLs */
+/**
+ * On Linux we can get numeric and/or shorted ACLs
+ */
#if defined(HAVE_LINUX_OS)
#if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
#define BACL_ALTERNATE_TEXT (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
#endif
#endif
-/*
+/**
+ * On FreeBSD we can get numeric ACLs
+ */
+#if defined(HAVE_FREEBSD_OS)
+#if defined(BACL_WANT_NUMERIC_IDS)
+#define BACL_ALTERNATE_TEXT ACL_TEXT_NUMERIC_IDS
+#endif
+#ifdef BACL_ALTERNATE_TEXT
+#define acl_to_text(acl,len) (acl_to_text_np((acl), (len), BACL_ALTERNATE_TEXT))
+#endif
+#endif
+
+/**
* Some generic functions used by multiple OSes.
*/
static acl_type_t bac_to_os_acltype(bacl_type acltype)
case BACL_TYPE_DEFAULT:
ostype = ACL_TYPE_DEFAULT;
break;
-
+#ifdef ACL_TYPE_NFS4
+ /**
+ * FreeBSD has an additional acl type named ACL_TYPE_NFS4.
+ */
+ case BACL_TYPE_NFS4:
+ ostype = ACL_TYPE_NFS4;
+ break;
+#endif
#ifdef ACL_TYPE_DEFAULT_DIR
case BACL_TYPE_DEFAULT_DIR:
- /*
- * OSF1 has an additional acl type named ACL_TYPE_DEFAULT_DIR.
+ /**
+ * TRU64 has an additional acl type named ACL_TYPE_DEFAULT_DIR.
*/
ostype = ACL_TYPE_DEFAULT_DIR;
break;
#endif
#ifdef ACL_TYPE_EXTENDED
case BACL_TYPE_EXTENDED:
- /*
+ /**
* MacOSX has an additional acl type named ACL_TYPE_EXTENDED.
*/
ostype = ACL_TYPE_EXTENDED;
break;
#endif
default:
- /*
+ /**
* This should never happen, as the per OS version function only tries acl
* types supported on a certain platform.
*/
}
#if !defined(HAVE_DARWIN_OS)
-/*
+/**
* See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
* There is no need to store those acls as we already store the stat bits too.
*/
static bool acl_is_trivial(acl_t acl)
{
- /*
+ /**
* acl is trivial if it has only the following entries:
* "user::",
* "group::",
entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
while (entry_available == 1) {
- /*
+ /**
* Get the tag type of this acl entry.
* If we fail to get the tagtype we call the acl non-trivial.
*/
if (acl_get_tag_type(ace, &tag) < 0)
return true;
- /*
+ /**
* Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
*/
if (tag != ACL_USER_OBJ &&
ace = &acl->acl_entry[n];
tag = ace->ae_tag;
- /*
+ /**
* Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
*/
if (tag != ACL_USER_OBJ &&
while (count > 0) {
tag = ace->entry->acl_type;
- /*
+ /**
* Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
*/
if (tag != ACL_USER_OBJ &&
tag != ACL_GROUP_OBJ &&
tag != ACL_OTHER)
return false;
- /*
+ /**
* On Tru64, perm can also contain non-standard bits such as
* PERM_INSERT, PERM_DELETE, PERM_MODIFY, PERM_LOOKUP, ...
*/
}
#endif
-/*
+/**
* Generic wrapper around acl_get_file call.
*/
static bacl_exit_code generic_get_acl_from_os(JCR *jcr, bacl_type acltype)
acl = acl_get_file(jcr->last_fname, ostype);
if (acl) {
#if defined(HAVE_IRIX_OS)
- /*
+ /**
* From observation, IRIX's acl_get_file() seems to return a
* non-NULL acl with a count field of -1 when a file has no ACL
* defined, while IRIX's acl_to_text() returns NULL when presented
}
#endif
-#if !defined(HAVE_DARWIN_OS)
- /*
+ /**
* Make sure this is not just a trivial ACL.
*/
+#if !defined(HAVE_DARWIN_OS)
if (acltype == BACL_TYPE_ACCESS && acl_is_trivial(acl)) {
- /*
+ /**
* The ACLs simply reflect the (already known) standard permissions
* So we don't send an ACL stream to the SD.
*/
return bacl_exit_ok;
}
#endif
+#if defined(HAVE_FREEBSD_OS) && defined(_PC_ACL_NFS4)
+ if (acltype == BACL_TYPE_NFS4) {
+ int trivial;
+ if (acl_is_trivial_np(acl, &trivial) == 0) {
+ if (trivial == 1) {
+ /**
+ * The ACLs simply reflect the (already known) standard permissions
+ * So we don't send an ACL stream to the SD.
+ */
+ pm_strcpy(jcr->acl_data->content, "");
+ jcr->acl_data->content_length = 0;
+ acl_free(acl);
+ return bacl_exit_ok;
+ }
+ }
+ }
+#endif
if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
return bacl_exit_error;
}
- /*
+ /**
* Handle errors gracefully.
*/
if (acl == (acl_t)NULL) {
switch (errno) {
#if defined(BACL_ENOTSUP)
case BACL_ENOTSUP:
+ /**
+ * If the filesystem reports it doesn't support ACLs we clear the
+ * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
+ * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
+ * when we change from one filesystem to an other.
+ */
+ jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
break; /* not supported */
#endif
case ENOENT:
return bacl_exit_error;
}
}
- /*
+
+ /**
* Not supported, just pretend there is nothing to see
*/
pm_strcpy(jcr->acl_data->content, "");
return bacl_exit_ok;
}
-/*
+/**
* Generic wrapper around acl_set_file call.
*/
static bacl_exit_code generic_set_acl_on_os(JCR *jcr, bacl_type acltype)
acl_type_t ostype;
berrno be;
- /*
+ /**
* If we get empty default ACLs, clear ACLs now
*/
ostype = bac_to_os_acltype(acltype);
}
#ifndef HAVE_FREEBSD_OS
- /*
+ /**
* FreeBSD always fails acl_valid() - at least on valid input...
* As it does the right thing, given valid input, just ignore acl_valid().
*/
}
#endif
- /*
+ /**
* Restore the ACLs, but don't complain about links which really should
* not have attributes, and the file it is linked to may not yet be restored.
* This is only true for the old acl streams as in the new implementation we
return bacl_exit_ok;
}
-/*
+/**
* OS specific functions for handling different types of acl streams.
*/
#if defined(HAVE_DARWIN_OS)
-/*
+/**
* Define the supported ACL streams for this OS
*/
static int os_access_acl_streams[1] = { STREAM_ACL_DARWIN_ACCESS_ACL };
static bacl_exit_code darwin_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
{
#if defined(ACL_TYPE_EXTENDED)
- /*
+ /**
* On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
* and acl_get_file (name, ACL_TYPE_DEFAULT)
* always return NULL / EINVAL. There is no point in making
if (generic_get_acl_from_os(jcr, BACL_TYPE_EXTENDED) == bacl_exit_fatal)
return bacl_exit_fatal;
#else
- /*
+ /**
* Read access ACLs for files, dirs and links
*/
if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_exit_fatal)
#endif
}
-/*
+/**
* For this OS setup the build and parse function pointer to the OS specific functions.
*/
static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = darwin_build_acl_streams;
static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = darwin_parse_acl_streams;
-#elif defined(HAVE_FREEBSD_OS) || \
- defined(HAVE_IRIX_OS) || \
- defined(HAVE_LINUX_OS)
-
-/*
+#elif defined(HAVE_FREEBSD_OS)
+/**
* Define the supported ACL streams for these OSes
*/
-#if defined(HAVE_FREEBSD_OS)
-static int os_access_acl_streams[1] = { STREAM_ACL_FREEBSD_ACCESS_ACL };
+static int os_access_acl_streams[2] = { STREAM_ACL_FREEBSD_ACCESS_ACL, STREAM_ACL_FREEBSD_NFS4_ACL };
static int os_default_acl_streams[1] = { STREAM_ACL_FREEBSD_DEFAULT_ACL };
-#elif defined(HAVE_IRIX_OS)
+
+static bacl_exit_code freebsd_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
+{
+ int acl_enabled = 0;
+ bacl_type acltype = BACL_TYPE_NONE;
+ berrno be;
+
+#if defined(_PC_ACL_NFS4)
+ /**
+ * See if filesystem supports NFS4 acls.
+ */
+ acl_enabled = pathconf(jcr->last_fname, _PC_ACL_NFS4);
+ switch (acl_enabled) {
+ case -1:
+ switch (errno) {
+ case ENOENT:
+ return bacl_exit_ok;
+ default:
+ Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
+ jcr->last_fname, be.bstrerror());
+ Dmsg2(100, "pathconf error file=%s ERR=%s\n",
+ jcr->last_fname, be.bstrerror());
+ return bacl_exit_error;
+ }
+ case 0:
+ break;
+ default:
+ acltype = BACL_TYPE_NFS4;
+ break;
+ }
+#endif
+
+ if (acl_enabled == 0) {
+ /**
+ * See if filesystem supports POSIX acls.
+ */
+ acl_enabled = pathconf(jcr->last_fname, _PC_ACL_EXTENDED);
+ switch (acl_enabled) {
+ case -1:
+ switch (errno) {
+ case ENOENT:
+ return bacl_exit_ok;
+ default:
+ Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
+ jcr->last_fname, be.bstrerror());
+ Dmsg2(100, "pathconf error file=%s ERR=%s\n",
+ jcr->last_fname, be.bstrerror());
+ return bacl_exit_error;
+ }
+ case 0:
+ break;
+ default:
+ acltype = BACL_TYPE_ACCESS;
+ break;
+ }
+ }
+
+ /**
+ * If the filesystem reports it doesn't support ACLs we clear the
+ * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
+ * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
+ * when we change from one filesystem to an other.
+ */
+ if (acl_enabled == 0) {
+ jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
+ pm_strcpy(jcr->acl_data->content, "");
+ jcr->acl_data->content_length = 0;
+ return bacl_exit_ok;
+ }
+
+ /**
+ * Based on the supported ACLs retrieve and store them.
+ */
+ switch (acltype) {
+ case BACL_TYPE_NFS4:
+ /**
+ * Read NFS4 ACLs for files, dirs and links
+ */
+ if (generic_get_acl_from_os(jcr, BACL_TYPE_NFS4) == bacl_exit_fatal)
+ return bacl_exit_fatal;
+
+ if (jcr->acl_data->content_length > 0) {
+ if (send_acl_stream(jcr, STREAM_ACL_FREEBSD_NFS4_ACL) == bacl_exit_fatal)
+ return bacl_exit_fatal;
+ }
+ break;
+ case BACL_TYPE_ACCESS:
+ /**
+ * Read access ACLs for files, dirs and links
+ */
+ if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_exit_fatal)
+ return bacl_exit_fatal;
+
+ if (jcr->acl_data->content_length > 0) {
+ if (send_acl_stream(jcr, STREAM_ACL_FREEBSD_ACCESS_ACL) == bacl_exit_fatal)
+ return bacl_exit_fatal;
+ }
+
+ /**
+ * Directories can have default ACLs too
+ */
+ if (ff_pkt->type == FT_DIREND) {
+ if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bacl_exit_fatal)
+ return bacl_exit_fatal;
+ if (jcr->acl_data->content_length > 0) {
+ if (send_acl_stream(jcr, STREAM_ACL_FREEBSD_DEFAULT_ACL) == bacl_exit_fatal)
+ return bacl_exit_fatal;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return bacl_exit_ok;
+}
+
+static bacl_exit_code freebsd_parse_acl_streams(JCR *jcr, int stream)
+{
+ int acl_enabled = 0;
+ char *acl_type_name;
+ berrno be;
+
+ /**
+ * First make sure the filesystem supports acls.
+ */
+ switch (stream) {
+ case STREAM_UNIX_ACCESS_ACL:
+ case STREAM_ACL_FREEBSD_ACCESS_ACL:
+ case STREAM_UNIX_DEFAULT_ACL:
+ case STREAM_ACL_FREEBSD_DEFAULT_ACL:
+ acl_enabled = pathconf(jcr->last_fname, _PC_ACL_EXTENDED);
+ acl_type_name = "POSIX";
+ break;
+ case STREAM_ACL_FREEBSD_NFS4_ACL:
+#if defined(_PC_ACL_NFS4)
+ acl_enabled = pathconf(jcr->last_fname, _PC_ACL_NFS4);
+#endif
+ acl_type_name = "NFS4";
+ break;
+ default:
+ acl_type_name = "unknown";
+ break;
+ }
+
+ switch (acl_enabled) {
+ case -1:
+ switch (errno) {
+ case ENOENT:
+ return bacl_exit_ok;
+ default:
+ Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
+ jcr->last_fname, be.bstrerror());
+ Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n",
+ jcr->acl_data->content, jcr->last_fname, be.bstrerror());
+ return bacl_exit_error;
+ }
+ case 0:
+ Mmsg2(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without %s acl support\n"),
+ jcr->last_fname, acl_type_name);
+ return bacl_exit_error;
+ default:
+ break;
+ }
+
+ /**
+ * Restore the ACLs.
+ */
+ switch (stream) {
+ case STREAM_UNIX_ACCESS_ACL:
+ case STREAM_ACL_FREEBSD_ACCESS_ACL:
+ return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
+ case STREAM_UNIX_DEFAULT_ACL:
+ case STREAM_ACL_FREEBSD_DEFAULT_ACL:
+ return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
+ case STREAM_ACL_FREEBSD_NFS4_ACL:
+ return generic_set_acl_on_os(jcr, BACL_TYPE_NFS4);
+ default:
+ break;
+ }
+ return bacl_exit_error;
+}
+
+/**
+ * For this OSes setup the build and parse function pointer to the OS specific functions.
+ */
+static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = freebsd_build_acl_streams;
+static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = freebsd_parse_acl_streams;
+
+#elif defined(HAVE_IRIX_OS) || \
+ defined(HAVE_LINUX_OS)
+/**
+ * Define the supported ACL streams for these OSes
+ */
+#if defined(HAVE_IRIX_OS)
static int os_access_acl_streams[1] = { STREAM_ACL_IRIX_ACCESS_ACL };
static int os_default_acl_streams[1] = { STREAM_ACL_IRIX_DEFAULT_ACL };
#elif defined(HAVE_LINUX_OS)
static bacl_exit_code generic_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
{
- /*
+ /**
* Read access ACLs for files, dirs and links
*/
if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_exit_fatal)
return bacl_exit_fatal;
}
- /*
+ /**
* Directories can have default ACLs too
*/
if (ff_pkt->type == FT_DIREND) {
case STREAM_UNIX_DEFAULT_ACL:
return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
default:
- /*
+ /**
* See what type of acl it is.
*/
for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
return bacl_exit_error;
}
-/*
+/**
* For this OSes setup the build and parse function pointer to the OS specific functions.
*/
static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = generic_build_acl_streams;
#elif defined(HAVE_OSF1_OS)
-/*
+/**
* Define the supported ACL streams for this OS
*/
static int os_access_acl_streams[1] = { STREAM_ACL_TRU64_ACCESS_ACL };
static bacl_exit_code tru64_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
{
- /*
+ /**
* Read access ACLs for files, dirs and links
*/
if ((jcr->acl_data->content_length = generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS)) < 0)
if (!send_acl_stream(jcr, STREAM_ACL_TRU64_ACCESS_ACL))
return bacl_exit_error;
}
- /*
+ /**
* Directories can have default ACLs too
*/
if (ff_pkt->type == FT_DIREND) {
if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_ACL))
return bacl_exit_error;
}
- /*
+ /**
* Tru64 has next to BACL_TYPE_DEFAULT also BACL_TYPE_DEFAULT_DIR acls.
* This is an inherited acl for all subdirs.
* See http://www.helsinki.fi/atk/unix/dec_manuals/DOC_40D/AQ0R2DTE/DOCU_018.HTM
return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT_DIR);
}
-/*
+/**
* For this OS setup the build and parse function pointer to the OS specific functions.
*/
static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = tru64_build_acl_streams;
#include <acllib.h>
-/*
+/**
* Define the supported ACL streams for this OS
*/
static int os_access_acl_streams[1] = { STREAM_ACL_HPUX_ACL_ENTRY };
static int os_default_acl_streams[1] = { -1 };
-/*
+/**
* See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
* There is no need to store those acls as we already store the stat bits too.
*/
for (n = 0; n < count; n++) {
ace = entries[n];
- /*
+ /**
* See if this acl just is the stat mode in acl form.
*/
if (!((ace.uid == sb.st_uid && ace.gid == ACL_NSGROUP) ||
return true;
}
-/*
+/**
* OS specific functions for handling different types of acl streams.
*/
static bacl_exit_code hpux_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
switch (errno) {
#if defined(BACL_ENOTSUP)
case BACL_ENOTSUP:
- /*
+ /**
* Not supported, just pretend there is nothing to see
+ *
+ * If the filesystem reports it doesn't support ACLs we clear the
+ * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
+ * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
+ * when we change from one filesystem to an other.
*/
+ jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
pm_strcpy(jcr->acl_data->content, "");
jcr->acl_data->content_length = 0;
return bacl_exit_ok;
}
if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
if (acl_is_trivial(n, acls, ff_pkt->statp)) {
- /*
+ /**
* The ACLs simply reflect the (already known) standard permissions
* So we don't send an ACL stream to the SD.
*/
return bacl_exit_error;
}
- /*
+ /**
* Restore the ACLs, but don't complain about links which really should
* not have attributes, and the file it is linked to may not yet be restored.
* This is only true for the old acl streams as in the new implementation we
return bacl_exit_ok;
}
-/*
+/**
* For this OS setup the build and parse function pointer to the OS specific functions.
*/
static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = hpux_build_acl_streams;
#endif
#if defined(HAVE_EXTENDED_ACL)
-/*
+/**
* We define some internals of the Solaris acl libs here as those
* are not exposed yet. Probably because they want us to see the
* acls as opague data. But as we need to support different platforms
} acl_type_t;
#endif
-/*
+/**
* Two external references to functions in the libsec library function not in current include files.
*/
extern "C" {
char *acl_strerror(int);
}
-/*
+/**
* Define the supported ACL streams for this OS
*/
static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT, STREAM_ACL_SOLARIS_ACE };
static int os_default_acl_streams[1] = { -1 };
-/*
+/**
* As the new libsec interface with acl_totext and acl_fromtext also handles
* the old format from acltotext we can use the new functions even
* for acls retrieved and stored in the database with older fd versions. If the
bacl_exit_code stream_status = bacl_exit_error;
berrno be;
- /*
+ /**
* See if filesystem supports acls.
*/
acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
switch (acl_enabled) {
case 0:
+ /**
+ * If the filesystem reports it doesn't support ACLs we clear the
+ * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
+ * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
+ * when we change from one filesystem to an other.
+ */
+ jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
pm_strcpy(jcr->acl_data->content, "");
jcr->acl_data->content_length = 0;
return bacl_exit_ok;
break;
}
- /*
+ /**
* Get ACL info: don't bother allocating space if there is only a trivial ACL.
*/
if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0) {
}
if (!aclp) {
- /*
+ /**
* The ACLs simply reflect the (already known) standard permissions
* So we don't send an ACL stream to the SD.
*/
}
#if defined(ACL_SID_FMT)
- /*
+ /**
* New format flag added in newer Solaris versions.
*/
flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
case STREAM_UNIX_ACCESS_ACL:
case STREAM_ACL_SOLARIS_ACLENT:
case STREAM_ACL_SOLARIS_ACE:
- /*
+ /**
* First make sure the filesystem supports acls.
*/
acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
return bacl_exit_error;
}
default:
- /*
- * On a filesystem with ACL support make sure this particilar ACL type can be restored.
+ /**
+ * On a filesystem with ACL support make sure this particular ACL type can be restored.
*/
switch (stream) {
case STREAM_ACL_SOLARIS_ACLENT:
- /*
+ /**
* An aclent can be restored on filesystems with _ACL_ACLENT_ENABLED or _ACL_ACE_ENABLED support.
*/
if ((acl_enabled & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0) {
}
break;
case STREAM_ACL_SOLARIS_ACE:
- /*
+ /**
* An ace can only be restored on a filesystem with _ACL_ACE_ENABLED support.
*/
if ((acl_enabled & _ACL_ACE_ENABLED) == 0) {
}
break;
default:
- /*
+ /**
* Stream id which doesn't describe the type of acl which is encoded.
*/
break;
return bacl_exit_error;
}
- /*
+ /**
* Validate that the conversion gave us the correct acl type.
*/
switch (stream) {
}
break;
default:
- /*
+ /**
* Stream id which doesn't describe the type of acl which is encoded.
*/
break;
}
- /*
+ /**
* Restore the ACLs, but don't complain about links which really should
* not have attributes, and the file it is linked to may not yet be restored.
* This is only true for the old acl streams as in the new implementation we
#else /* HAVE_EXTENDED_ACL */
-/*
+/**
* Define the supported ACL streams for this OS
*/
static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT };
static int os_default_acl_streams[1] = { -1 };
-/*
+/**
* See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
* There is no need to store those acls as we already store the stat bits too.
*/
return true;
}
-/*
+/**
* OS specific functions for handling different types of acl streams.
*/
static bacl_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
acls = (aclent_t *)malloc(n * sizeof(aclent_t));
if (acl(jcr->last_fname, GETACL, n, acls) == n) {
if (acl_is_trivial(n, acls)) {
- /*
+ /**
* The ACLs simply reflect the (already known) standard permissions
* So we don't send an ACL stream to the SD.
*/
return bacl_exit_error;
}
- /*
+ /**
* Restore the ACLs, but don't complain about links which really should
* not have attributes, and the file it is linked to may not yet be restored.
*/
}
#endif /* HAVE_EXTENDED_ACL */
-/*
+/**
* For this OS setup the build and parse function pointer to the OS specific functions.
*/
static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = solaris_build_acl_streams;
static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = solaris_parse_acl_streams;
#endif /* HAVE_SUN_OS */
+#endif /* HAVE_ACL */
/*
* Entry points when compiled with support for ACLs on a supported platform.
*/
-/*
+/**
* Read and send an ACL for the last encountered file.
*/
bacl_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
{
- /*
- * Call the appropriate function.
+ /**
+ * See if we are changing from one device to an other.
+ * We save the current device we are scanning and compare
+ * it with the current st_dev in the last stat performed on
+ * the file we are currently storing.
*/
- if (os_build_acl_streams) {
- return (*os_build_acl_streams)(jcr, ff_pkt);
+ if (jcr->acl_data->current_dev != ff_pkt->statp.st_dev) {
+ /**
+ * Reset the acl save flags.
+ */
+ jcr->acl_data->flags = 0;
+
+ jcr->acl_data->flags |= BACL_FLAG_SAVE_NATIVE;
+
+ /**
+ * Save that we started scanning a new filesystem.
+ */
+ jcr->acl_data->current_dev = ff_pkt->statp.st_dev;
}
+
+#if defined(HAVE_ACL)
+ /**
+ * See if the BACL_FLAG_SAVE_NATIVE flag is set which lets us know if we should
+ * save native ACLs.
+ */
+ if (jcr->acl_data->flags & BACL_FLAG_SAVE_NATIVE) {
+ /**
+ * Call the appropriate function.
+ */
+ if (os_build_acl_streams) {
+ return (*os_build_acl_streams)(jcr, ff_pkt);
+ }
+ } else {
+ return bacl_exit_ok;
+ }
+#endif
return bacl_exit_error;
}
unsigned int cnt;
switch (stream) {
+#if defined(HAVE_ACL)
case STREAM_UNIX_ACCESS_ACL:
case STREAM_UNIX_DEFAULT_ACL:
- /*
+ /**
* Handle legacy ACL streams.
*/
if (os_parse_acl_streams) {
break;
default:
if (os_parse_acl_streams) {
- /*
+ /**
* Walk the os_access_acl_streams array with the supported Access ACL streams for this OS.
*/
for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
return (*os_parse_acl_streams)(jcr, stream);
}
}
- /*
+ /**
* Walk the os_default_acl_streams array with the supported Default ACL streams for this OS.
*/
for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
}
}
break;
+#else
+ default:
+ break;
+#endif
}
Qmsg2(jcr, M_WARNING, 0,
_("Can't restore ACLs of %s - incompatible acl stream encountered - %d\n"),