]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/filed/acl.c
Fix typo
[bacula/bacula] / bacula / src / filed / acl.c
index b8342bac674a53a70b5da1fd476272c5f727cc30..d1625474c274fdcec1339faa31797cf850236b06 100644 (file)
 /**
  * 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 <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[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: