]> arthur.barton.de Git - netatalk.git/blobdiff - libatalk/adouble/ad_open.c
Fixes
[netatalk.git] / libatalk / adouble / ad_open.c
index f039da9fa37dcb77e24814dc7ab6cc7cc83bace9..537cdf6de3bb0f66a8449288f138786a0dc7ca31 100644 (file)
@@ -50,6 +50,7 @@
 #include <atalk/bstradd.h>
 #include <atalk/compat.h>
 #include <atalk/errchk.h>
+#include <atalk/volume.h>
 
 #include "ad_lock.h"
 
@@ -252,7 +253,7 @@ static int new_ad_header(const char *path, struct adouble *ad, int adflags)
     struct stat         st;
 
     ad->ad_magic = AD_MAGIC;
-    ad->ad_version = ad->ad_flags & 0x0f0000;
+    ad->ad_version = ad->ad_vers & 0x0f0000;
     if (!ad->ad_version) {
         ad->ad_version = AD_VERSION;
     }
@@ -260,9 +261,9 @@ static int new_ad_header(const char *path, struct adouble *ad, int adflags)
 
     memset(ad->ad_data, 0, sizeof(ad->ad_data));
 
-    if (ad->ad_flags == AD_VERSION2)
+    if (ad->ad_vers == AD_VERSION2)
         eid = entry_order2;
-    else if (ad->ad_flags == AD_VERSION_EA)
+    else if (ad->ad_vers == AD_VERSION_EA)
         eid = entry_order_ea;
     else {
         return -1;
@@ -464,6 +465,27 @@ static int ad_header_read_ea(struct adouble *ad, struct stat *hst _U_)
 
     /* Now parse entries */
     parse_entries(ad, buf + AD_HEADER_LEN, nentries);
+
+    return 0;
+}
+
+static int ad_rsrc_len_ea(struct adouble *ad)
+{
+    ssize_t rlen;
+
+    if ((rlen = sys_fgetxattr(ad_data_fileno(ad), AD_EA_RESO, NULL, 0)) <= 0) {
+        switch (errno) {
+        case ENOATTR:
+        case ENOENT:
+            ad->ad_rlen = 0;
+            break;
+        default:
+            LOG(log_error, logtype_default, "ad_refresh_rsrc_len_ea: %s", strerror(errno));
+            ad->ad_rlen = 0;
+            return -1;
+        }
+    }
+    ad->ad_rlen = rlen;
     return 0;
 }
 
@@ -521,7 +543,7 @@ static int ad_chown(const char *path, struct stat *stbuf)
 /* ----------------
    return access right and inode of path parent directory
 */
-static int ad_mode_st(const char *path, int *mode, struct stat *stbuf)
+static int ad_mode_st(const char *path, mode_t *mode, struct stat *stbuf)
 {
     if (*mode == 0) {
         return -1;
@@ -557,11 +579,11 @@ static int ad_header_upgrade_ea(struct adouble *ad _U_, const char *name _U_)
 static int ad_error(struct adouble *ad, int adflags)
 {
     int err = errno;
-    if ((adflags & ADFLAGS_NOHF)) { /* 1 */
-        /* FIXME double check : set header offset ?*/
+    if (adflags & ADFLAGS_NOHF) { /* 1 */
+        ad->ad_adflags &= ~ADFLAGS_HF;
         return 0;
     }
-    if ((adflags & ADFLAGS_DF)) { /* 2 */
+    if (adflags & ADFLAGS_DF) { /* 2 */
         ad_close( ad, ADFLAGS_DF );
         err = errno;
     }
@@ -575,7 +597,9 @@ static int ad2openflags(int adflags)
 
     if (adflags & ADFLAGS_RDWR)
         oflags |= O_RDWR;
-    if (adflags & ADFLAGS_RDONLY)
+    if ((adflags & ADFLAGS_RDONLY) && (adflags & ADFLAGS_SETSHRMD))
+        oflags |= O_RDWR;
+    else
         oflags |= O_RDONLY;
     if (adflags & ADFLAGS_CREATE)
         oflags |= O_CREAT;
@@ -624,16 +648,30 @@ static int ad_open_df(const char *path, int adflags, mode_t mode, struct adouble
     ad->ad_data_fork.adf_fd = open(path, oflags, admode);
 
     if (ad->ad_data_fork.adf_fd == -1) {
-        if (errno != OPEN_NOFOLLOW_ERRNO)
+        switch (errno) {
+        case EACCES:
+        case EPERM:
+        case EROFS:
+            if ((adflags & ADFLAGS_SETSHRMD) && (adflags & ADFLAGS_RDONLY)) {
+                oflags &= ~O_RDWR;
+                oflags |= O_RDONLY;
+                if ((ad->ad_data_fork.adf_fd = open(path, oflags, admode)) == -1)
+                    return -1;
+                break;
+            }
             return -1;
-
-        ad->ad_data_fork.adf_syml = malloc(MAXPATHLEN+1);
-        if ((lsz = readlink(path, ad->ad_data_fork.adf_syml, MAXPATHLEN)) <= 0) {
-            free(ad->ad_data_fork.adf_syml);
+        case OPEN_NOFOLLOW_ERRNO:
+            ad->ad_data_fork.adf_syml = malloc(MAXPATHLEN+1);
+            if ((lsz = readlink(path, ad->ad_data_fork.adf_syml, MAXPATHLEN)) <= 0) {
+                free(ad->ad_data_fork.adf_syml);
+                return -1;
+            }
+            ad->ad_data_fork.adf_syml[lsz] = 0;
+            ad->ad_data_fork.adf_fd = -2; /* -2 means its a symlink */
+            break;
+        default:
             return -1;
         }
-        ad->ad_data_fork.adf_syml[lsz] = 0;
-        ad->ad_data_fork.adf_fd = -2; /* -2 means its a symlink */
     }
 
     if (!st_invalid)
@@ -658,9 +696,9 @@ static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adou
 
     if (ad_meta_fileno(ad) != -1) {
         /* the file is already open, but we want write access: */
-        if (!(adflags & ADFLAGS_RDONLY) &&
+        if ((adflags & ADFLAGS_RDWR) &&
             /* and it was already denied: */
-            !(ad->ad_mdp->adf_flags & O_RDWR)) {
+            (ad->ad_mdp->adf_flags & O_RDONLY)) {
             errno = EACCES;
             return -1;
         }
@@ -674,51 +712,74 @@ static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adou
     ad_p = ad->ad_ops->ad_path(path, adflags);
     oflags = O_NOFOLLOW | ad2openflags(adflags);
     nocreatflags = oflags & ~(O_CREAT | O_EXCL);
+
     ad->ad_mdp->adf_fd = open(ad_p, nocreatflags);
 
-    if ( ad->ad_mdp->adf_fd < 0 ) {
-        if (!(errno == ENOENT && (oflags & O_CREAT)))
-            return ad_error(ad, adflags);
-        /*
-         * We're expecting to create a new adouble header file here
-         * if ((oflags & O_CREAT) ==> (oflags & O_RDWR)
-         */
-        LOG(log_debug, logtype_default, "ad_open(\"%s\"): creating adouble file",
-            fullpathname(path));
-        admode = mode;
-        errno = 0;
-        st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
-        if ((ad->ad_options & ADVOL_UNIXPRIV))
-            admode = mode;
-        admode = ad_hf_mode(admode);
-        if (errno == ENOENT) {
-            if (ad->ad_ops->ad_mkrf( ad_p) < 0) {
-                return ad_error(ad, adflags);
+    if (ad->ad_mdp->adf_fd != -1) {
+        ad->ad_mdp->adf_flags = nocreatflags;
+    } else {
+        switch (errno) {
+        case EACCES:
+        case EPERM:
+        case EROFS:
+            if ((adflags & ADFLAGS_RDONLY) && (adflags & ADFLAGS_SETSHRMD)) {
+                nocreatflags &= ~O_RDWR;
+                nocreatflags |= O_RDONLY;
+                if ((ad->ad_mdp->adf_fd = open(ad_p, nocreatflags)) == -1)
+                    return -1;
+                ad->ad_mdp->adf_flags = nocreatflags;
+                break;
             }
+            return -1;
+        case ENOENT:
+            if (!(oflags & O_CREAT))
+                return ad_error(ad, adflags);
+            /*
+             * We're expecting to create a new adouble header file here
+             * if ((oflags & O_CREAT) ==> (oflags & O_RDWR)
+             */
+            LOG(log_debug, logtype_default, "ad_open(\"%s\"): creating adouble file",
+                fullpathname(path));
             admode = mode;
+            errno = 0;
             st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
             if ((ad->ad_options & ADVOL_UNIXPRIV))
                 admode = mode;
             admode = ad_hf_mode(admode);
-        }
+            if (errno == ENOENT) {
+                if (ad->ad_ops->ad_mkrf( ad_p) < 0) {
+                    return ad_error(ad, adflags);
+                }
+                admode = mode;
+                st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
+                if ((ad->ad_options & ADVOL_UNIXPRIV))
+                    admode = mode;
+                admode = ad_hf_mode(admode);
+            }
 
-        /* retry with O_CREAT */
-        ad->ad_mdp->adf_fd = open(ad_p, oflags, admode);
-        if ( ad->ad_mdp->adf_fd < 0 )
-            return ad_error(ad, adflags);
+            /* retry with O_CREAT */
+            ad->ad_mdp->adf_fd = open(ad_p, oflags, admode);
+            if ( ad->ad_mdp->adf_fd < 0 )
+                return ad_error(ad, adflags);
 
-        ad->ad_mdp->adf_flags = oflags;
-        /* just created, set owner if admin owner (root) */
-        if (!st_invalid)
-            ad_chown(ad_p, &st_dir);
-    } else {
-        ad->ad_mdp->adf_flags = nocreatflags;
-        if (fstat(ad->ad_mdp->adf_fd, &st_meta) == 0 && st_meta.st_size == 0) {
-            /* for 0 length files, treat them as new. */
-            ad->ad_mdp->adf_flags |= O_TRUNC;
-        } else {
-            /* we have valid data in st_meta stat structure, reused it in ad_header_read */
-            pst = &st_meta;
+            ad->ad_mdp->adf_flags = oflags;
+            /* just created, set owner if admin owner (root) */
+            if (!st_invalid)
+                ad_chown(ad_p, &st_dir);
+            break;
+        default:
+            return -1;
+        }
+    }
+
+    if (!(ad->ad_mdp->adf_flags & O_CREAT)) {
+        /* check for 0 length files, treat them as new. */
+        if (fstat(ad->ad_mdp->adf_fd, &st_meta) == 0) {
+            if (st_meta.st_size == 0)
+                ad->ad_mdp->adf_flags |= O_TRUNC;
+            else
+                /* we have valid data in st_meta stat structure, reused it in ad_header_read */
+                pst = &st_meta;
         }
     }
 
@@ -754,14 +815,28 @@ static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble
     ssize_t rforklen;
     int oflags = O_NOFOLLOW;
 
-    oflags = ad2openflags(adflags) & ~(O_CREAT | O_TRUNC);
+    LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\", %04o)", path, mode);
+
+    oflags |= ad2openflags(adflags) & ~(O_CREAT | O_TRUNC);
 
-    if (ad_meta_fileno(ad) == -1) {
-        if ((ad_meta_fileno(ad) = open(path, oflags)) == -1)
+    if (ad_data_fileno(ad) != -1) {
+        /* the file is already open, but we want write access: */
+        if ((adflags & ADFLAGS_RDWR) &&
+            /* and it was already denied: */
+            (ad->ad_data_fork.adf_flags & O_RDONLY)) {
+            LOG(log_error, logtype_default, "ad_open_hf_ea(%s): rw request for ro file: %s",
+                fullpathname(path), strerror(errno));
+            errno = EACCES;
+            return -1;
+        }
+
+        /* it's not new anymore */
+        ad->ad_mdp->adf_flags &= ~( O_TRUNC | O_CREAT );
+    } else {
+        if ((ad_data_fileno(ad) = open(path, oflags)) == -1)
             goto error;
-        ad->ad_mdp->adf_flags = oflags;
-        ad->ad_mdp->adf_refcount = 1;
-        adf_lock_init(ad->ad_mdp);
+        ad->ad_data_fork.adf_flags = oflags;
+        adf_lock_init(&ad->ad_data_fork);
     }
 
     /* Read the adouble header in and parse it.*/
@@ -786,17 +861,17 @@ static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble
         LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): created metadata EA", path);
     }
 
-    ad->ad_mdp->adf_refcount++;
+    ad->ad_data_fork.adf_refcount++;
 
-    if ((rforklen = sys_fgetxattr(ad_meta_fileno(ad), AD_EA_RESO, NULL, 0)) > 0)
+    if ((rforklen = sys_fgetxattr(ad_data_fileno(ad), AD_EA_RESO, NULL, 0)) > 0)
         ad->ad_rlen = rforklen;
 
     return 0;
 
 error:
-    if (ad_meta_fileno(ad) != -1) {
-        close(ad_meta_fileno(ad));
-        ad_meta_fileno(ad) = -1;
+    if (ad_data_fileno(ad) != -1) {
+        close(ad_data_fileno(ad));
+        ad_data_fileno(ad) = -1;
     }
     return ad_error(ad, adflags);
 }
@@ -810,7 +885,7 @@ static int ad_open_hf(const char *path, int adflags, int mode, struct adouble *a
     memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
     ad->ad_rlen = 0;
 
-    switch (ad->ad_flags) {
+    switch (ad->ad_vers) {
     case AD_VERSION2:
         ret = ad_open_hf_v2(path, adflags, mode, ad);
         break;
@@ -825,6 +900,7 @@ static int ad_open_hf(const char *path, int adflags, int mode, struct adouble *a
     return ret;
 }
 
+
 /*!
  * Open ressource fork
  *
@@ -834,51 +910,39 @@ static int ad_open_hf(const char *path, int adflags, int mode, struct adouble *a
 static int ad_open_rf(const char *path, int adflags, int mode, struct adouble *ad)
 {
     int ret = 0;
+    int oflags;
+    ssize_t rlen;
 
-    if (ad->ad_flags != AD_VERSION_EA)
+    if (ad->ad_vers != AD_VERSION_EA)
         return 0;
 
-    LOG(log_debug, logtype_default, "ad_open_rf(\"%s\", %04o)",
-        path, mode);
+    LOG(log_debug, logtype_default, "ad_open_rf(\"%s\", %04o)", path, mode);
 
-    if ((ad->ad_rlen = sys_fgetxattr(ad_meta_fileno(ad), AD_EA_RESO, NULL, 0)) <= 0) {
-        switch (errno) {
-        case ENOATTR:
-            ad->ad_rlen = 0;
-            break;
-        default:
-            LOG(log_warning, logtype_default, "ad_open_rf(\"%s\"): %s",
+    oflags |= ad2openflags(adflags) & ~(O_CREAT | O_TRUNC);
+
+    if (ad_data_fileno(ad) != -1) {
+        /* the file is already open, but we want write access: */
+        if ((adflags & ADFLAGS_RDWR) &&
+            /* and it was already denied: */
+            (ad->ad_data_fork.adf_flags & O_RDONLY)) {
+            LOG(log_error, logtype_default, "ad_open_rf(%s): rw request for ro file: %s",
                 fullpathname(path), strerror(errno));
-            ret = -1;
-            goto exit;
+            errno = EACCES;
+            return -1;
         }
+    } else {
+        if ((ad_data_fileno(ad) = open(path, oflags)) == -1)
+            goto exit;
+        ad->ad_data_fork.adf_flags = oflags;
+        adf_lock_init(&ad->ad_data_fork);
     }
 
-    /* Round up and allocate buffer */
-    size_t roundup = ((ad->ad_rlen / RFORK_EA_ALLOCSIZE) + 1) * RFORK_EA_ALLOCSIZE;
-    if ((ad->ad_resforkbuf = malloc(roundup)) == NULL) {
-        ret = -1;
+    if ((ret = ad_rsrc_len_ea(ad)) != 0)
         goto exit;
-    }
-
-    ad->ad_resforkbufsize = roundup;
 
-    /* Read the EA into the buffer */
-    if (ad->ad_rlen > 0) {
-        if (sys_fgetxattr(ad_meta_fileno(ad), AD_EA_RESO, ad->ad_resforkbuf, ad->ad_rlen) == -1) {
-            ret = -1;
-            goto exit;
-        }       
-    }
+    ad->ad_data_fork.adf_refcount++;
 
 exit:
-    if (ret != 0) {
-        free(ad->ad_resforkbuf);
-        ad->ad_resforkbuf = NULL;
-        ad->ad_rlen = 0;
-        ad->ad_resforkbufsize = 0;
-    }
-
     return ret;
 }
 
@@ -1012,7 +1076,7 @@ int ad_stat(const char *path, struct stat *stbuf)
 /* ----------------
    return access right of path parent directory
 */
-int ad_mode( const char *path, int mode)
+int ad_mode( const char *path, mode_t mode)
 {
     struct stat     stbuf;
     ad_mode_st(path, &mode, &stbuf);
@@ -1022,7 +1086,7 @@ int ad_mode( const char *path, int mode)
 /*
  * Use mkdir() with mode bits taken from ad_mode().
  */
-int ad_mkdir( const char *path, int mode)
+int ad_mkdir( const char *path, mode_t mode)
 {
     int ret;
     int st_invalid;
@@ -1040,9 +1104,9 @@ int ad_mkdir( const char *path, int mode)
     return ret;
 }
 
-void ad_init(struct adouble *ad, int flags, int options)
+static void ad_init_func(struct adouble *ad)
 {
-    switch (flags) {
+    switch (ad->ad_vers) {
     case AD_VERSION2:
         ad->ad_ops = &ad_adouble;
         ad->ad_rfp = &ad->ad_resource_fork;
@@ -1050,17 +1114,13 @@ void ad_init(struct adouble *ad, int flags, int options)
         break;
     case AD_VERSION_EA:
         ad->ad_ops = &ad_adouble_ea;
-        ad->ad_rfp = &ad->ad_data_fork;
-        ad->ad_mdp = &ad->ad_data_fork;
+        ad->ad_rfp = &ad->ad_resource_fork;
+        ad->ad_mdp = &ad->ad_resource_fork;
         break;
     default:
-        LOG(log_error, logtype_default, "ad_init: unknown AD version");
-        errno = EIO;
-        return;
+        AFP_PANIC("ad_init: unknown AD version");
     }
 
-    ad->ad_flags = flags;
-    ad->ad_options = options;
     ad_data_fileno(ad) = -1;
     ad_reso_fileno(ad) = -1;
     ad_meta_fileno(ad) = -1;
@@ -1071,8 +1131,25 @@ void ad_init(struct adouble *ad, int flags, int options)
     ad->ad_resource_fork.adf_refcount = 0;
     ad->ad_resforkbuf = NULL;
     ad->ad_data_fork.adf_refcount = 0;
-    ad->ad_data_fork.adf_syml=0;
+    ad->ad_data_fork.adf_syml = 0;
     ad->ad_inited = 0;
+    return;
+}
+
+void ad_init_old(struct adouble *ad, int flags, int options)
+{
+    ad->ad_vers = flags;
+    ad->ad_options = options;
+    ad_init_func(ad);
+}
+
+void ad_init(struct adouble *ad, const struct vol * restrict vol)
+{
+    ad->ad_vers = vol->v_adouble;
+    ad->ad_options = vol->v_ad_options;
+//    ad->ad_maxeafssize = 3500;  /* FIXME: option from vol */
+    ad->ad_maxeafssize = 0;     /* no limit */
+    ad_init_func(ad);
 }
 
 /*!
@@ -1162,7 +1239,7 @@ int ad_metadata(const char *name, int flags, struct adouble *adp)
 
     oflags = (flags & ADFLAGS_DIR) | ADFLAGS_HF | ADFLAGS_RDONLY;
 
-    if ((ret = ad_open(adp, name, oflags)) < 0 && errno == EACCES) {
+    if ((ret = ad_open(adp, name, oflags | ADFLAGS_SETSHRMD)) < 0 && errno == EACCES) {
         uid = geteuid();
         if (seteuid(0)) {
             LOG(log_error, logtype_default, "ad_metadata(%s): seteuid failed %s", name, strerror(errno));
@@ -1228,11 +1305,24 @@ exit:
 
 int ad_refresh(struct adouble *ad)
 {
-
-    if (ad_meta_fileno(ad) == -1)
+    switch (ad->ad_vers) {
+    case AD_VERSION2:
+        if (ad_meta_fileno(ad) == -1)
+            return -1;
+        return ad->ad_ops->ad_header_read(ad, NULL);
+        break;
+    case AD_VERSION_EA:
+        if (ad_data_fileno(ad) == -1)
+            return -1;
+        if (ad_rsrc_len_ea(ad) != 0)
+            return -1;
+        return ad->ad_ops->ad_header_read(ad, NULL);
+        break;
+    default:
         return -1;
+        break;
+    }
 
-    return ad->ad_ops->ad_header_read(ad, NULL);
 }
 
 int ad_openat(struct adouble  *ad,