X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Ffiled%2Facl.c;h=d1625474c274fdcec1339faa31797cf850236b06;hb=b3cf813b0d40c94349aed6493306301f58f8b3c7;hp=b8342bac674a53a70b5da1fd476272c5f727cc30;hpb=cd18236042205a23bbd4da5a7b75d11865a27ac2;p=bacula%2Fbacula diff --git a/bacula/src/filed/acl.c b/bacula/src/filed/acl.c index b8342bac67..d1625474c2 100644 --- a/bacula/src/filed/acl.c +++ b/bacula/src/filed/acl.c @@ -28,6 +28,18 @@ /** * 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. * @@ -128,30 +140,243 @@ static bacl_exit_code send_acl_stream(JCR *jcr, int stream) #if defined(HAVE_EXTENDED_ACL) +#include #include +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[1] = { STREAM_ACL_AIX_TEXT, STREAM_ACL_AIX_AIXC, STREAM_ACL_AIX_NFS4 }; +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) { - return bacl_exit_error; + 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()); + } } - return bacl_exit_error; + 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 */ @@ -213,7 +438,7 @@ static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = aix_parse_ #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) @@ -229,6 +454,18 @@ static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = aix_parse_ #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. */ @@ -243,11 +480,18 @@ 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; @@ -387,10 +631,10 @@ static bacl_exit_code generic_get_acl_from_os(JCR *jcr, bacl_type acltype) } #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 @@ -402,6 +646,23 @@ static bacl_exit_code generic_get_acl_from_os(JCR *jcr, bacl_type acltype) 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); @@ -590,17 +851,207 @@ static bacl_exit_code darwin_parse_acl_streams(JCR *jcr, int stream) 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) @@ -1066,7 +1517,7 @@ static bacl_exit_code solaris_parse_acl_streams(JCR *jcr, int stream) } 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: