vfs_fruit: fix fruit_check_access()
[samba.git] / source3 / modules / vfs_fruit.c
index 85376968ca13c84249f9a6ebf4fe750f4583ecd9..0636ce94fd0281c4883de10ab798c7bc55c7c035 100644 (file)
@@ -1576,6 +1576,40 @@ static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
        return true;
 }
 
+static bool filter_empty_rsrc_stream(unsigned int *num_streams,
+                                    struct stream_struct **streams)
+{
+       struct stream_struct *tmp = *streams;
+       unsigned int i;
+
+       if (*num_streams == 0) {
+               return true;
+       }
+
+       for (i = 0; i < *num_streams; i++) {
+               if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
+                       break;
+               }
+       }
+
+       if (i == *num_streams) {
+               return true;
+       }
+
+       if (tmp[i].size > 0) {
+               return true;
+       }
+
+       TALLOC_FREE(tmp[i].name);
+       if (*num_streams - 1 > i) {
+               memmove(&tmp[i], &tmp[i+1],
+                       (*num_streams - i - 1) * sizeof(struct stream_struct));
+       }
+
+       *num_streams -= 1;
+       return true;
+}
+
 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
                             struct stream_struct **streams,
                             const char *name)
@@ -1771,6 +1805,8 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
        struct byte_range_lock *br_lck = NULL;
        bool open_for_reading, open_for_writing, deny_read, deny_write;
        off_t off;
+       bool have_read = false;
+       int flags;
 
        /* FIXME: hardcoded data fork, add resource fork */
        enum apple_fork fork_type = APPLE_FORK_DATA;
@@ -1782,6 +1818,26 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
                  deny_mode & DENY_READ ? "DENY_READ" : "-",
                  deny_mode & DENY_WRITE ? "DENY_WRITE" : "-"));
 
+       if (fsp->fh->fd == -1) {
+               return NT_STATUS_OK;
+       }
+
+       flags = fcntl(fsp->fh->fd, F_GETFL);
+       if (flags == -1) {
+               DBG_ERR("fcntl get flags [%s] fd [%d] failed [%s]\n",
+                       fsp_str_dbg(fsp), fsp->fh->fd, strerror(errno));
+               return map_nt_error_from_unix(errno);
+       }
+
+       if (flags & (O_RDONLY|O_RDWR)) {
+               /*
+                * Applying fcntl read locks requires an fd opened for
+                * reading. This means we won't be applying locks for
+                * files openend write-only, but what can we do...
+                */
+               have_read = true;
+       }
+
        /*
         * Check read access and deny read mode
         */
@@ -1803,7 +1859,7 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
                }
 
                /* Set locks */
-               if (access_mask & FILE_READ_DATA) {
+               if ((access_mask & FILE_READ_DATA) && have_read) {
                        off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
                        br_lck = do_lock(
                                handle->conn->sconn->msg_ctx, fsp,
@@ -1817,7 +1873,7 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
                        TALLOC_FREE(br_lck);
                }
 
-               if (deny_mode & DENY_READ) {
+               if ((deny_mode & DENY_READ) && have_read) {
                        off = denymode_to_netatalk_brl(fork_type, DENY_READ);
                        br_lck = do_lock(
                                handle->conn->sconn->msg_ctx, fsp,
@@ -1853,7 +1909,7 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
                }
 
                /* Set locks */
-               if (access_mask & FILE_WRITE_DATA) {
+               if ((access_mask & FILE_WRITE_DATA) && have_read) {
                        off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
                        br_lck = do_lock(
                                handle->conn->sconn->msg_ctx, fsp,
@@ -1867,7 +1923,7 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
                        TALLOC_FREE(br_lck);
 
                }
-               if (deny_mode & DENY_WRITE) {
+               if ((deny_mode & DENY_WRITE) && have_read) {
                        off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
                        br_lck = do_lock(
                                handle->conn->sconn->msg_ctx, fsp,
@@ -2174,12 +2230,89 @@ static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
        return true;
 }
 
+static uint64_t readdir_attr_rfork_size_adouble(
+       struct vfs_handle_struct *handle,
+       const struct smb_filename *smb_fname)
+{
+       struct adouble *ad = NULL;
+       uint64_t rfork_size;
+
+       ad = ad_get(talloc_tos(), handle, smb_fname->base_name,
+                   ADOUBLE_RSRC);
+       if (ad == NULL) {
+               return 0;
+       }
+
+       rfork_size = ad_getentrylen(ad, ADEID_RFORK);
+       TALLOC_FREE(ad);
+
+       return rfork_size;
+}
+
+static uint64_t readdir_attr_rfork_size_stream(
+       struct vfs_handle_struct *handle,
+       const struct smb_filename *smb_fname)
+{
+       struct smb_filename *stream_name = NULL;
+       int ret;
+       uint64_t rfork_size;
+
+       stream_name = synthetic_smb_fname(talloc_tos(),
+                                         smb_fname->base_name,
+                                         AFPRESOURCE_STREAM_NAME,
+                                         NULL, 0);
+       if (stream_name == NULL) {
+               return 0;
+       }
+
+       ret = SMB_VFS_STAT(handle->conn, stream_name);
+       if (ret != 0) {
+               TALLOC_FREE(stream_name);
+               return 0;
+       }
+
+       rfork_size = stream_name->st.st_ex_size;
+       TALLOC_FREE(stream_name);
+
+       return rfork_size;
+}
+
+static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
+                                       const struct smb_filename *smb_fname)
+{
+       struct fruit_config_data *config = NULL;
+       uint64_t rfork_size;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct fruit_config_data,
+                               return 0);
+
+       switch (config->rsrc) {
+       case FRUIT_RSRC_ADFILE:
+       case FRUIT_RSRC_XATTR:
+               rfork_size = readdir_attr_rfork_size_adouble(handle,
+                                                            smb_fname);
+               break;
+
+       case FRUIT_META_STREAM:
+               rfork_size = readdir_attr_rfork_size_stream(handle,
+                                                           smb_fname);
+               break;
+
+       default:
+               DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
+               rfork_size = 0;
+               break;
+       }
+
+       return rfork_size;
+}
+
 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
                                     const struct smb_filename *smb_fname,
                                     struct readdir_attr_data *attr_data)
 {
        NTSTATUS status = NT_STATUS_OK;
-       struct adouble *ad = NULL;
        struct fruit_config_data *config = NULL;
        bool ok;
 
@@ -2196,13 +2329,10 @@ static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
         */
 
        if (config->readdir_attr_rsize) {
-               ad = ad_get(talloc_tos(), handle, smb_fname->base_name,
-                           ADOUBLE_RSRC);
-               if (ad) {
-                       attr_data->attr_data.aapl.rfork_size = ad_getentrylen(
-                               ad, ADEID_RFORK);
-                       TALLOC_FREE(ad);
-               }
+               uint64_t rfork_size;
+
+               rfork_size = readdir_attr_rfork_size(handle, smb_fname);
+               attr_data->attr_data.aapl.rfork_size = rfork_size;
        }
 
        /*
@@ -2421,8 +2551,8 @@ static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
        baseflags &= ~O_EXCL;
        baseflags &= ~O_CREAT;
 
-       hostfd = SMB_VFS_OPEN(handle->conn, smb_fname_base, fsp,
-                             baseflags, mode);
+       hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
+                                  baseflags, mode);
 
        /*
         * It is legit to open a stream on a directory, but the base
@@ -2431,8 +2561,8 @@ static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
        if ((hostfd == -1) && (errno == EISDIR)) {
                baseflags &= ~O_ACCMODE;
                baseflags |= O_RDONLY;
-               hostfd = SMB_VFS_OPEN(handle->conn, smb_fname_base, fsp,
-                                     baseflags, mode);
+               hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
+                                          baseflags, mode);
        }
 
        TALLOC_FREE(smb_fname_base);
@@ -2483,7 +2613,7 @@ exit:
                         * full fsp yet
                         */
                        fsp->fh->fd = hostfd;
-                       SMB_VFS_CLOSE(fsp);
+                       SMB_VFS_NEXT_CLOSE(handle, fsp);
                }
                hostfd = -1;
                errno = saved_errno;
@@ -2523,47 +2653,18 @@ static int fruit_open_meta(vfs_handle_struct *handle,
        return rc;
 }
 
-static int fruit_open_rsrc(vfs_handle_struct *handle,
-                          struct smb_filename *smb_fname,
-                          files_struct *fsp, int flags, mode_t mode)
+static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
+                                  struct smb_filename *smb_fname,
+                                  files_struct *fsp,
+                                  int flags,
+                                  mode_t mode)
 {
        int rc = 0;
-       struct fruit_config_data *config = NULL;
        struct adouble *ad = NULL;
        struct smb_filename *smb_fname_base = NULL;
        char *adpath = NULL;
        int hostfd = -1;
 
-       DEBUG(10, ("fruit_open_rsrc for %s\n", smb_fname_str_dbg(smb_fname)));
-
-       SMB_VFS_HANDLE_GET_DATA(handle, config,
-                               struct fruit_config_data, return -1);
-
-       switch (config->rsrc) {
-       case FRUIT_RSRC_STREAM:
-               return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
-       case FRUIT_RSRC_XATTR:
-#ifdef HAVE_ATTROPEN
-               hostfd = attropen(smb_fname->base_name,
-                                 AFPRESOURCE_EA_NETATALK, flags, mode);
-               if (hostfd == -1) {
-                       return -1;
-               }
-               ad = ad_init(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
-                            handle, ADOUBLE_RSRC, fsp);
-               if (ad == NULL) {
-                       rc = -1;
-                       goto exit;
-               }
-               goto exit;
-#else
-               errno = ENOTSUP;
-               return -1;
-#endif
-       default:
-               break;
-       }
-
        if (!(flags & O_CREAT) && !VALID_STAT(smb_fname->st)) {
                rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
                if (rc != 0) {
@@ -2602,8 +2703,8 @@ static int fruit_open_rsrc(vfs_handle_struct *handle,
                flags |= O_RDWR;
        }
 
-       hostfd = SMB_VFS_OPEN(handle->conn, smb_fname_base, fsp,
-                             flags, mode);
+       hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
+                                  flags, mode);
        if (hostfd == -1) {
                rc = -1;
                goto exit;
@@ -2660,6 +2761,72 @@ exit:
        return hostfd;
 }
 
+static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
+                                struct smb_filename *smb_fname,
+                                files_struct *fsp,
+                                int flags,
+                                mode_t mode)
+{
+#ifdef HAVE_ATTROPEN
+       int fd = -1;
+       struct adouble *ad = NULL;
+
+       ad = ad_init(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
+                    handle, ADOUBLE_RSRC, fsp);
+       if (ad == NULL) {
+               return -1;
+       }
+
+       fd = attropen(smb_fname->base_name,
+                         AFPRESOURCE_EA_NETATALK, flags, mode);
+       if (fd == -1) {
+               return -1;
+       }
+
+       return fd;
+
+#else
+       errno = ENOSYS;
+       return -1;
+#endif
+}
+
+static int fruit_open_rsrc(vfs_handle_struct *handle,
+                          struct smb_filename *smb_fname,
+                          files_struct *fsp, int flags, mode_t mode)
+{
+       int fd;
+       struct fruit_config_data *config = NULL;
+
+       DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct fruit_config_data, return -1);
+
+       switch (config->rsrc) {
+       case FRUIT_RSRC_STREAM:
+               fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+               break;
+
+       case FRUIT_RSRC_ADFILE:
+               fd = fruit_open_rsrc_adouble(handle, smb_fname,
+                                            fsp, flags, mode);
+               break;
+
+       case FRUIT_RSRC_XATTR:
+               fd = fruit_open_rsrc_xattr(handle, smb_fname,
+                                          fsp, flags, mode);
+               break;
+
+       default:
+               DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
+               return -1;
+       }
+
+       DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
+       return fd;
+}
+
 static int fruit_open(vfs_handle_struct *handle,
                       struct smb_filename *smb_fname,
                       files_struct *fsp, int flags, mode_t mode)
@@ -2688,19 +2855,27 @@ static int fruit_rename(struct vfs_handle_struct *handle,
        char *src_adouble_path = NULL;
        char *dst_adouble_path = NULL;
        struct fruit_config_data *config = NULL;
+       struct smb_filename *src_adp_smb_fname = NULL;
+       struct smb_filename *dst_adp_smb_fname = NULL;
 
-       rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct fruit_config_data, return -1);
 
-       if (!VALID_STAT(smb_fname_src->st)
-           || !S_ISREG(smb_fname_src->st.st_ex_mode)) {
-               return rc;
+       if (!VALID_STAT(smb_fname_src->st)) {
+               DBG_ERR("Need valid stat for [%s]\n",
+                       smb_fname_str_dbg(smb_fname_src));
+               return -1;
        }
 
-       SMB_VFS_HANDLE_GET_DATA(handle, config,
-                               struct fruit_config_data, return -1);
+       rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
+       if (rc != 0) {
+               return -1;
+       }
 
-       if (config->rsrc == FRUIT_RSRC_XATTR) {
-               return rc;
+       if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
+           (!S_ISREG(smb_fname_src->st.st_ex_mode)))
+       {
+               return 0;
        }
 
        rc = adouble_path(talloc_tos(), smb_fname_src->base_name,
@@ -2708,46 +2883,99 @@ static int fruit_rename(struct vfs_handle_struct *handle,
        if (rc != 0) {
                goto done;
        }
+       src_adp_smb_fname = synthetic_smb_fname(talloc_tos(),
+                                               src_adouble_path,
+                                               NULL, NULL,
+                                               smb_fname_src->flags);
+       TALLOC_FREE(src_adouble_path);
+       if (src_adp_smb_fname == NULL) {
+               rc = -1;
+               goto done;
+       }
+
        rc = adouble_path(talloc_tos(), smb_fname_dst->base_name,
                          &dst_adouble_path);
        if (rc != 0) {
                goto done;
        }
+       dst_adp_smb_fname = synthetic_smb_fname(talloc_tos(),
+                                               dst_adouble_path,
+                                               NULL, NULL,
+                                               smb_fname_dst->flags);
+       TALLOC_FREE(dst_adouble_path);
+       if (dst_adp_smb_fname == NULL) {
+               rc = -1;
+               goto done;
+       }
 
-       DEBUG(10, ("fruit_rename: %s -> %s\n",
-                  src_adouble_path, dst_adouble_path));
+       DBG_DEBUG("%s -> %s\n",
+                 smb_fname_str_dbg(src_adp_smb_fname),
+                 smb_fname_str_dbg(dst_adp_smb_fname));
 
-       rc = rename(src_adouble_path, dst_adouble_path);
+       rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
        if (errno == ENOENT) {
                rc = 0;
        }
 
-       TALLOC_FREE(src_adouble_path);
-       TALLOC_FREE(dst_adouble_path);
-
 done:
+       TALLOC_FREE(src_adp_smb_fname);
+       TALLOC_FREE(dst_adp_smb_fname);
        return rc;
 }
 
-static int fruit_unlink(vfs_handle_struct *handle,
-                       const struct smb_filename *smb_fname)
+static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
+                                   const struct smb_filename *smb_fname)
+{
+       return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
+}
+
+static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
+                                     const struct smb_filename *smb_fname)
+{
+       return SMB_VFS_REMOVEXATTR(handle->conn,
+                                  smb_fname->base_name,
+                                  AFPINFO_EA_NETATALK);
+}
+
+static int fruit_unlink_meta(vfs_handle_struct *handle,
+                            const struct smb_filename *smb_fname)
 {
-       int rc = -1;
        struct fruit_config_data *config = NULL;
+       int rc;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data, return -1);
 
-       if (!is_ntfs_stream_smb_fname(smb_fname)) {
-               char *adp = NULL;
+       switch (config->meta) {
+       case FRUIT_META_STREAM:
+               rc = fruit_unlink_meta_stream(handle, smb_fname);
+               break;
 
-               rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
-               if (rc != 0) {
-                       return -1;
-               }
+       case FRUIT_META_NETATALK:
+               rc = fruit_unlink_meta_netatalk(handle, smb_fname);
+               break;
 
-               if (config->rsrc != FRUIT_RSRC_ADFILE) {
-                       return 0;
+       default:
+               DBG_ERR("Unsupported meta config [%d]\n", config->meta);
+               return -1;
+       }
+
+       return rc;
+}
+
+static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
+                                   const struct smb_filename *smb_fname,
+                                   bool force_unlink)
+{
+       int ret;
+
+       if (!force_unlink) {
+               struct smb_filename *smb_fname_cp = NULL;
+               off_t size;
+
+               smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
+               if (smb_fname_cp == NULL) {
+                       return -1;
                }
 
                /*
@@ -2755,54 +2983,183 @@ static int fruit_unlink(vfs_handle_struct *handle,
                 * vfs_streaminfo, as a result stream cleanup/deletion of file
                 * deletion doesn't remove the resourcefork stream.
                 */
-               rc = adouble_path(talloc_tos(),
-                                 smb_fname->base_name, &adp);
-               if (rc != 0) {
-                       return -1;
-               }
 
-               /* FIXME: direct unlink(), missing smb_fname */
-               DBG_DEBUG("fruit_unlink: %s\n", adp);
-               rc = unlink(adp);
-               if ((rc == -1) && (errno == ENOENT)) {
-                       rc = 0;
+               ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
+               if (ret != 0) {
+                       TALLOC_FREE(smb_fname_cp);
+                       DBG_ERR("stat [%s] failed [%s]\n",
+                               smb_fname_str_dbg(smb_fname_cp), strerror(errno));
+                       return -1;
                }
 
-               TALLOC_FREE(adp);
-               return 0;
-       }
+               size = smb_fname_cp->st.st_ex_size;
+               TALLOC_FREE(smb_fname_cp);
 
-       if (is_afpinfo_stream(smb_fname)) {
-               if (config->meta == FRUIT_META_STREAM) {
-                       rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
-               } else {
-                       rc = SMB_VFS_REMOVEXATTR(handle->conn,
-                                                smb_fname->base_name,
-                                                AFPINFO_EA_NETATALK);
+               if (size > 0) {
+                       /* OS X ignores resource fork stream delete requests */
+                       return 0;
                }
-
-               return rc;
        }
 
-       if (is_afpresource_stream(smb_fname)) {
-               /* OS X ignores deletes on the AFP_Resource stream */
-               return 0;
+       ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
+       if ((ret != 0) && (errno == ENOENT) && force_unlink) {
+               ret = 0;
        }
 
-       return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
-
-
-       return 0;
+       return ret;
 }
 
-static int fruit_chmod(vfs_handle_struct *handle,
-                      const struct smb_filename *smb_fname,
-                      mode_t mode)
+static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
+                                    const struct smb_filename *smb_fname,
+                                    bool force_unlink)
+{
+       int rc;
+       char *adp = NULL;
+       struct adouble *ad = NULL;
+       struct smb_filename *adp_smb_fname = NULL;
+
+       if (!force_unlink) {
+               ad = ad_get(talloc_tos(), handle, smb_fname->base_name,
+                           ADOUBLE_RSRC);
+               if (ad == NULL) {
+                       errno = ENOENT;
+                       return -1;
+               }
+
+
+               /*
+                * 0 byte resource fork streams are not listed by
+                * vfs_streaminfo, as a result stream cleanup/deletion of file
+                * deletion doesn't remove the resourcefork stream.
+                */
+
+               if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
+                       /* OS X ignores resource fork stream delete requests */
+                       TALLOC_FREE(ad);
+                       return 0;
+               }
+
+               TALLOC_FREE(ad);
+       }
+
+       rc = adouble_path(talloc_tos(), smb_fname->base_name, &adp);
+       if (rc != 0) {
+               return -1;
+       }
+
+       adp_smb_fname = synthetic_smb_fname(talloc_tos(), adp,
+                                           NULL, NULL,
+                                           smb_fname->flags);
+       TALLOC_FREE(adp);
+       if (adp_smb_fname == NULL) {
+               return -1;
+       }
+
+       rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
+       TALLOC_FREE(adp_smb_fname);
+       if ((rc != 0) && (errno == ENOENT) && force_unlink) {
+               rc = 0;
+       }
+
+       return rc;
+}
+
+static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
+                                  const struct smb_filename *smb_fname,
+                                  bool force_unlink)
+{
+       /*
+        * OS X ignores resource fork stream delete requests, so nothing to do
+        * here. Removing the file will remove the xattr anyway, so we don't
+        * have to take care of removing 0 byte resource forks that could be
+        * left behind.
+        */
+       return 0;
+}
+
+static int fruit_unlink_rsrc(vfs_handle_struct *handle,
+                            const struct smb_filename *smb_fname,
+                            bool force_unlink)
+{
+       struct fruit_config_data *config = NULL;
+       int rc;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct fruit_config_data, return -1);
+
+       switch (config->rsrc) {
+       case FRUIT_RSRC_STREAM:
+               rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
+               break;
+
+       case FRUIT_RSRC_ADFILE:
+               rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
+               break;
+
+       case FRUIT_RSRC_XATTR:
+               rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
+               break;
+
+       default:
+               DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
+               return -1;
+       }
+
+       return rc;
+}
+
+static int fruit_unlink(vfs_handle_struct *handle,
+                       const struct smb_filename *smb_fname)
+{
+       int rc;
+       struct fruit_config_data *config = NULL;
+       struct smb_filename *rsrc_smb_fname = NULL;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct fruit_config_data, return -1);
+
+       if (is_afpinfo_stream(smb_fname)) {
+               return fruit_unlink_meta(handle, smb_fname);
+       } else if (is_afpresource_stream(smb_fname)) {
+               return fruit_unlink_rsrc(handle, smb_fname, false);
+       } if (is_ntfs_stream_smb_fname(smb_fname)) {
+               return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
+       }
+
+       /*
+        * A request to delete the base file. Because 0 byte resource
+        * fork streams are not listed by fruit_streaminfo,
+        * delete_all_streams() can't remove 0 byte resource fork
+        * streams, so we have to cleanup this here.
+        */
+       rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
+                                            smb_fname->base_name,
+                                            AFPRESOURCE_STREAM_NAME,
+                                            NULL,
+                                            smb_fname->flags);
+       if (rsrc_smb_fname == NULL) {
+               return -1;
+       }
+
+       rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
+       if ((rc != 0) && (errno != ENOENT)) {
+               DBG_ERR("Forced unlink of [%s] failed [%s]\n",
+                       smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
+               TALLOC_FREE(rsrc_smb_fname);
+               return -1;
+       }
+       TALLOC_FREE(rsrc_smb_fname);
+
+       return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
+}
+
+static int fruit_chmod(vfs_handle_struct *handle,
+                      const struct smb_filename *smb_fname,
+                      mode_t mode)
 {
        int rc = -1;
        char *adp = NULL;
        struct fruit_config_data *config = NULL;
-       SMB_STRUCT_STAT sb;
        const char *path = smb_fname->base_name;
        struct smb_filename *smb_fname_adp = NULL;
 
@@ -2814,14 +3171,16 @@ static int fruit_chmod(vfs_handle_struct *handle,
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data, return -1);
 
-       if (config->rsrc == FRUIT_RSRC_XATTR) {
+       if (config->rsrc != FRUIT_RSRC_ADFILE) {
                return 0;
        }
 
-       /* FIXME: direct sys_lstat(), missing smb_fname */
-       rc = sys_lstat(path, &sb, false);
-       if (rc != 0 || !S_ISREG(sb.st_ex_mode)) {
-               return rc;
+       if (!VALID_STAT(smb_fname->st)) {
+               return 0;
+       }
+
+       if (!S_ISREG(smb_fname->st.st_ex_mode)) {
+               return 0;
        }
 
        rc = adouble_path(talloc_tos(), path, &adp);
@@ -2861,7 +3220,6 @@ static int fruit_chown(vfs_handle_struct *handle,
        char *adp = NULL;
        struct fruit_config_data *config = NULL;
        struct smb_filename *adp_smb_fname = NULL;
-       SMB_STRUCT_STAT sb;
 
        rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
        if (rc != 0) {
@@ -2871,14 +3229,16 @@ static int fruit_chown(vfs_handle_struct *handle,
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data, return -1);
 
-       if (config->rsrc == FRUIT_RSRC_XATTR) {
-               return rc;
+       if (config->rsrc != FRUIT_RSRC_ADFILE) {
+               return 0;
        }
 
-       /* FIXME: direct sys_lstat(), need non-const smb_fname */
-       rc = sys_lstat(smb_fname->base_name, &sb, false);
-       if (rc != 0 || !S_ISREG(sb.st_ex_mode)) {
-               return rc;
+       if (!VALID_STAT(smb_fname->st)) {
+               return 0;
+       }
+
+       if (!S_ISREG(smb_fname->st.st_ex_mode)) {
+               return 0;
        }
 
        rc = adouble_path(talloc_tos(), smb_fname->base_name, &adp);
@@ -2916,12 +3276,11 @@ static int fruit_rmdir(struct vfs_handle_struct *handle,
        DIR *dh = NULL;
        struct dirent *de;
        struct fruit_config_data *config;
-       const char *path = smb_fname->base_name;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data, return -1);
 
-       if (!handle->conn->cwd || !path || (config->rsrc == FRUIT_RSRC_XATTR)) {
+       if (config->rsrc != FRUIT_RSRC_ADFILE) {
                goto exit_rmdir;
        }
 
@@ -2929,24 +3288,58 @@ static int fruit_rmdir(struct vfs_handle_struct *handle,
         * Due to there is no way to change bDeleteVetoFiles variable
         * from this module, need to clean up ourselves
         */
-       dh = opendir(path);
+
+       dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
        if (dh == NULL) {
                goto exit_rmdir;
        }
 
-       while ((de = readdir(dh)) != NULL) {
-               if ((strncmp(de->d_name,
-                            ADOUBLE_NAME_PREFIX,
-                            strlen(ADOUBLE_NAME_PREFIX))) == 0) {
-                       char *p = talloc_asprintf(talloc_tos(),
-                                                 "%s/%s",
-                                                 path, de->d_name);
-                       if (p == NULL) {
-                               goto exit_rmdir;
-                       }
-                       DEBUG(10, ("fruit_rmdir: delete %s\n", p));
-                       (void)unlink(p);
+       while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
+               int match;
+               struct adouble *ad = NULL;
+               char *p = NULL;
+               struct smb_filename *ad_smb_fname = NULL;
+               int ret;
+
+               match = strncmp(de->d_name,
+                               ADOUBLE_NAME_PREFIX,
+                               strlen(ADOUBLE_NAME_PREFIX));
+               if (match != 0) {
+                       continue;
+               }
+
+               p = talloc_asprintf(talloc_tos(), "%s/%s",
+                                   smb_fname->base_name, de->d_name);
+               if (p == NULL) {
+                       DBG_ERR("talloc_asprintf failed\n");
+                       return -1;
+               }
+
+               /*
+                * Check whether it's a valid AppleDouble file, if
+                * yes, delete it, ignore it otherwise.
+                */
+               ad = ad_get(talloc_tos(), handle, p, ADOUBLE_RSRC);
+               if (ad == NULL) {
                        TALLOC_FREE(p);
+                       continue;
+               }
+               TALLOC_FREE(ad);
+
+               ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
+                                                   NULL, NULL,
+                                                   smb_fname->flags);
+               TALLOC_FREE(p);
+               if (ad_smb_fname == NULL) {
+                       DBG_ERR("synthetic_smb_fname failed\n");
+                       return -1;
+               }
+
+               ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
+               TALLOC_FREE(ad_smb_fname);
+               if (ret != 0) {
+                       DBG_ERR("Deleting [%s] failed\n",
+                               smb_fname_str_dbg(ad_smb_fname));
                }
        }
 
@@ -2967,9 +3360,6 @@ static ssize_t fruit_pread(vfs_handle_struct *handle,
        struct fruit_config_data *config = NULL;
        AfpInfo *ai = NULL;
        ssize_t len = -1;
-       char *name = NULL;
-       char *tmp_base_name = NULL;
-       NTSTATUS status;
        size_t to_return = n;
 
        DEBUG(10, ("fruit_pread: offset=%d, size=%d\n", (int)offset, (int)n));
@@ -2981,25 +3371,6 @@ static ssize_t fruit_pread(vfs_handle_struct *handle,
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data, return -1);
 
-       /* fsp_name is not converted with vfs_catia */
-       tmp_base_name = fsp->base_fsp->fsp_name->base_name;
-       status = SMB_VFS_TRANSLATE_NAME(handle->conn,
-                                       fsp->base_fsp->fsp_name->base_name,
-                                       vfs_translate_to_unix,
-                                       talloc_tos(), &name);
-       if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
-               name = talloc_strdup(talloc_tos(), tmp_base_name);
-               if (name == NULL) {
-                       rc = -1;
-                       goto exit;
-               }
-       } else if (!NT_STATUS_IS_OK(status)) {
-               errno = map_errno_from_nt_status(status);
-               rc = -1;
-               goto exit;
-       }
-       fsp->base_fsp->fsp_name->base_name = name;
-
        if (is_afpinfo_stream(fsp->fsp_name)) {
                /*
                 * OS X has a off-by-1 error in the offset calculation, so we're
@@ -3080,8 +3451,6 @@ static ssize_t fruit_pread(vfs_handle_struct *handle,
                }
        }
 exit:
-       fsp->base_fsp->fsp_name->base_name = tmp_base_name;
-       TALLOC_FREE(name);
        TALLOC_FREE(ai);
        if (rc != 0) {
                len = -1;
@@ -3100,9 +3469,6 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
        struct fruit_config_data *config = NULL;
        AfpInfo *ai = NULL;
        ssize_t len;
-       char *name = NULL;
-       char *tmp_base_name = NULL;
-       NTSTATUS status;
 
        DEBUG(10, ("fruit_pwrite: offset=%d, size=%d\n", (int)offset, (int)n));
 
@@ -3113,24 +3479,6 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data, return -1);
 
-       tmp_base_name = fsp->base_fsp->fsp_name->base_name;
-       status = SMB_VFS_TRANSLATE_NAME(handle->conn,
-                                       fsp->base_fsp->fsp_name->base_name,
-                                       vfs_translate_to_unix,
-                                       talloc_tos(), &name);
-       if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
-               name = talloc_strdup(talloc_tos(), tmp_base_name);
-               if (name == NULL) {
-                       rc = -1;
-                       goto exit;
-               }
-       } else if (!NT_STATUS_IS_OK(status)) {
-               errno = map_errno_from_nt_status(status);
-               rc = -1;
-               goto exit;
-       }
-       fsp->base_fsp->fsp_name->base_name = name;
-
        if (is_afpinfo_stream(fsp->fsp_name)) {
                /*
                 * Writing an all 0 blob to the metadata stream
@@ -3208,7 +3556,7 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
                }
 
                memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
-               rc = ad_write(ad, name);
+               rc = ad_write(ad, fsp->base_fsp->fsp_name->base_name);
        } else {
                len = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
                                    offset + ad_getentryoff(ad, ADEID_RFORK));
@@ -3218,7 +3566,7 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
                }
 
                if (config->rsrc == FRUIT_RSRC_ADFILE) {
-                       rc = ad_read(ad, name);
+                       rc = ad_read(ad, fsp->base_fsp->fsp_name->base_name);
                        if (rc == -1) {
                                goto exit;
                        }
@@ -3226,14 +3574,12 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
 
                        if ((len + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
                                ad_setentrylen(ad, ADEID_RFORK, len + offset);
-                               rc = ad_write(ad, name);
+                               rc = ad_write(ad, fsp->base_fsp->fsp_name->base_name);
                        }
                }
        }
 
 exit:
-       fsp->base_fsp->fsp_name->base_name = tmp_base_name;
-       TALLOC_FREE(name);
        TALLOC_FREE(ai);
        if (rc != 0) {
                return -1;
@@ -3329,15 +3675,12 @@ static int fruit_stat_meta(vfs_handle_struct *handle,
        return ret;
 }
 
-static int fruit_stat_rsrc(vfs_handle_struct *handle,
-                          struct smb_filename *smb_fname,
-                          bool follow_links)
-
+static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
+                                   struct smb_filename *smb_fname,
+                                   bool follow_links)
 {
        struct adouble *ad = NULL;
-
-       DEBUG(10, ("fruit_stat_rsrc called for %s\n",
-                  smb_fname_str_dbg(smb_fname)));
+       int ret;
 
        ad = ad_get(talloc_tos(), handle, smb_fname->base_name, ADOUBLE_RSRC);
        if (ad == NULL) {
@@ -3346,7 +3689,8 @@ static int fruit_stat_rsrc(vfs_handle_struct *handle,
        }
 
        /* Populate the stat struct with info from the base file. */
-       if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
+       ret = fruit_stat_base(handle, smb_fname, follow_links);
+       if (ret != 0) {
                TALLOC_FREE(ad);
                return -1;
        }
@@ -3358,6 +3702,96 @@ static int fruit_stat_rsrc(vfs_handle_struct *handle,
        return 0;
 }
 
+static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
+                                 struct smb_filename *smb_fname,
+                                 bool follow_links)
+{
+       int ret;
+
+       if (follow_links) {
+               ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
+       } else {
+               ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
+       }
+
+       return ret;
+}
+
+static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
+                                struct smb_filename *smb_fname,
+                                bool follow_links)
+{
+#ifdef HAVE_ATTROPEN
+       int ret;
+       int fd = -1;
+
+       /* Populate the stat struct with info from the base file. */
+       ret = fruit_stat_base(handle, smb_fname, follow_links);
+       if (ret != 0) {
+               return -1;
+       }
+
+       fd = attropen(smb_fname->base_name,
+                     AFPRESOURCE_EA_NETATALK,
+                     O_RDONLY);
+       if (fd == -1) {
+               return 0;
+       }
+
+       ret = sys_fstat(fd, &smb_fname->st, false);
+       if (ret != 0) {
+               close(fd);
+               DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
+                       AFPRESOURCE_EA_NETATALK);
+               return -1;
+       }
+       close(fd);
+       fd = -1;
+
+       smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
+                                             smb_fname->stream_name);
+
+       return ret;
+
+#else
+       errno = ENOSYS;
+       return -1;
+#endif
+}
+
+static int fruit_stat_rsrc(vfs_handle_struct *handle,
+                          struct smb_filename *smb_fname,
+                          bool follow_links)
+{
+       struct fruit_config_data *config = NULL;
+       int ret;
+
+       DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct fruit_config_data, return -1);
+
+       switch (config->rsrc) {
+       case FRUIT_RSRC_STREAM:
+               ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
+               break;
+
+       case FRUIT_RSRC_XATTR:
+               ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
+               break;
+
+       case FRUIT_RSRC_ADFILE:
+               ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
+               break;
+
+       default:
+               DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
+               return -1;
+       }
+
+       return ret;
+}
+
 static int fruit_stat(vfs_handle_struct *handle,
                      struct smb_filename *smb_fname)
 {
@@ -3489,38 +3923,12 @@ static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
                       SMB_STRUCT_STAT *sbuf)
 {
        int rc;
-       char *name = NULL;
-       char *tmp_base_name = NULL;
-       NTSTATUS status;
        struct adouble *ad = (struct adouble *)
                VFS_FETCH_FSP_EXTENSION(handle, fsp);
 
        DEBUG(10, ("fruit_fstat called for %s\n",
                   smb_fname_str_dbg(fsp->fsp_name)));
 
-       if (fsp->base_fsp) {
-               tmp_base_name = fsp->base_fsp->fsp_name->base_name;
-               /* fsp_name is not converted with vfs_catia */
-               status = SMB_VFS_TRANSLATE_NAME(
-                       handle->conn,
-                       fsp->base_fsp->fsp_name->base_name,
-                       vfs_translate_to_unix,
-                       talloc_tos(), &name);
-
-               if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
-                       name = talloc_strdup(talloc_tos(), tmp_base_name);
-                       if (name == NULL) {
-                               rc = -1;
-                               goto exit;
-                       }
-               } else if (!NT_STATUS_IS_OK(status)) {
-                       errno = map_errno_from_nt_status(status);
-                       rc = -1;
-                       goto exit;
-               }
-               fsp->base_fsp->fsp_name->base_name = name;
-       }
-
        if (ad == NULL || fsp->base_fsp == NULL) {
                rc = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
                goto exit;
@@ -3555,13 +3963,229 @@ exit:
        DEBUG(10, ("fruit_fstat %s, size: %zd\n",
                   smb_fname_str_dbg(fsp->fsp_name),
                   (ssize_t)sbuf->st_ex_size));
-       if (tmp_base_name) {
-               fsp->base_fsp->fsp_name->base_name = tmp_base_name;
-       }
-       TALLOC_FREE(name);
        return rc;
 }
 
+static NTSTATUS fruit_streaminfo_meta_stream(
+       vfs_handle_struct *handle,
+       struct files_struct *fsp,
+       const struct smb_filename *smb_fname,
+       TALLOC_CTX *mem_ctx,
+       unsigned int *pnum_streams,
+       struct stream_struct **pstreams)
+{
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS fruit_streaminfo_meta_netatalk(
+       vfs_handle_struct *handle,
+       struct files_struct *fsp,
+       const struct smb_filename *smb_fname,
+       TALLOC_CTX *mem_ctx,
+       unsigned int *pnum_streams,
+       struct stream_struct **pstreams)
+{
+       struct adouble *ad = NULL;
+       bool is_fi_empty;
+       bool ok;
+
+       /* Remove the Netatalk xattr from the list */
+       ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
+                             ":" NETATALK_META_XATTR ":$DATA");
+       if (!ok) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ad = ad_get(talloc_tos(), handle,
+                   smb_fname->base_name, ADOUBLE_META);
+       if (ad == NULL) {
+               return NT_STATUS_OK;
+       }
+
+       is_fi_empty = ad_empty_finderinfo(ad);
+       TALLOC_FREE(ad);
+
+       if (is_fi_empty) {
+               return NT_STATUS_OK;
+       }
+
+       ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
+                             AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
+                             smb_roundup(handle->conn, AFP_INFO_SIZE));
+       if (!ok) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
+                                     struct files_struct *fsp,
+                                     const struct smb_filename *smb_fname,
+                                     TALLOC_CTX *mem_ctx,
+                                     unsigned int *pnum_streams,
+                                     struct stream_struct **pstreams)
+{
+       struct fruit_config_data *config = NULL;
+       NTSTATUS status;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
+                               return NT_STATUS_INTERNAL_ERROR);
+
+       switch (config->meta) {
+       case FRUIT_META_NETATALK:
+               status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
+                                                       mem_ctx, pnum_streams,
+                                                       pstreams);
+               break;
+
+       case FRUIT_META_STREAM:
+               status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
+                                                     mem_ctx, pnum_streams,
+                                                     pstreams);
+               break;
+
+       default:
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       return status;
+}
+
+static NTSTATUS fruit_streaminfo_rsrc_stream(
+       vfs_handle_struct *handle,
+       struct files_struct *fsp,
+       const struct smb_filename *smb_fname,
+       TALLOC_CTX *mem_ctx,
+       unsigned int *pnum_streams,
+       struct stream_struct **pstreams)
+{
+       bool ok;
+
+       ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
+       if (!ok) {
+               DBG_ERR("Filtering resource stream failed\n");
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS fruit_streaminfo_rsrc_xattr(
+       vfs_handle_struct *handle,
+       struct files_struct *fsp,
+       const struct smb_filename *smb_fname,
+       TALLOC_CTX *mem_ctx,
+       unsigned int *pnum_streams,
+       struct stream_struct **pstreams)
+{
+       bool ok;
+
+       ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
+       if (!ok) {
+               DBG_ERR("Filtering resource stream failed\n");
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS fruit_streaminfo_rsrc_adouble(
+       vfs_handle_struct *handle,
+       struct files_struct *fsp,
+       const struct smb_filename *smb_fname,
+       TALLOC_CTX *mem_ctx,
+       unsigned int *pnum_streams,
+       struct stream_struct **pstreams)
+{
+       struct stream_struct *stream = *pstreams;
+       unsigned int num_streams = *pnum_streams;
+       struct adouble *ad = NULL;
+       bool ok;
+       size_t rlen;
+       int i;
+
+       /*
+        * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
+        * and if yes, remove it from the list
+        */
+       for (i = 0; i < num_streams; i++) {
+               if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
+                       break;
+               }
+       }
+
+       if (i < num_streams) {
+               DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
+                           smb_fname_str_dbg(smb_fname));
+
+               ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
+                                     AFPRESOURCE_STREAM);
+               if (!ok) {
+                       return NT_STATUS_INTERNAL_ERROR;
+               }
+       }
+
+       ad = ad_get(talloc_tos(), handle, smb_fname->base_name,
+                   ADOUBLE_RSRC);
+       if (ad == NULL) {
+               return NT_STATUS_OK;
+       }
+
+       rlen = ad_getentrylen(ad, ADEID_RFORK);
+       TALLOC_FREE(ad);
+
+       if (rlen == 0) {
+               return NT_STATUS_OK;
+       }
+
+       ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
+                             AFPRESOURCE_STREAM_NAME, rlen,
+                             smb_roundup(handle->conn, rlen));
+       if (!ok) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
+                                     struct files_struct *fsp,
+                                     const struct smb_filename *smb_fname,
+                                     TALLOC_CTX *mem_ctx,
+                                     unsigned int *pnum_streams,
+                                     struct stream_struct **pstreams)
+{
+       struct fruit_config_data *config = NULL;
+       NTSTATUS status;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
+                               return NT_STATUS_INTERNAL_ERROR);
+
+       switch (config->rsrc) {
+       case FRUIT_RSRC_STREAM:
+               status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
+                                                     mem_ctx, pnum_streams,
+                                                     pstreams);
+               break;
+
+       case FRUIT_RSRC_XATTR:
+               status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
+                                                    mem_ctx, pnum_streams,
+                                                    pstreams);
+               break;
+
+       case FRUIT_RSRC_ADFILE:
+               status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
+                                                      mem_ctx, pnum_streams,
+                                                      pstreams);
+               break;
+
+       default:
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       return status;
+}
+
 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
                                 struct files_struct *fsp,
                                 const struct smb_filename *smb_fname,
@@ -3570,48 +4194,12 @@ static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
                                 struct stream_struct **pstreams)
 {
        struct fruit_config_data *config = NULL;
-       struct adouble *ad = NULL;
        NTSTATUS status;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
                                return NT_STATUS_UNSUCCESSFUL);
-       DEBUG(10, ("fruit_streaminfo called for %s\n", smb_fname->base_name));
-
-       if (config->meta == FRUIT_META_NETATALK) {
-               bool ok;
-
-               ad = ad_get(talloc_tos(), handle,
-                           smb_fname->base_name, ADOUBLE_META);
-               if ((ad != NULL) && !ad_empty_finderinfo(ad)) {
-                       ok = add_fruit_stream(
-                               mem_ctx, pnum_streams, pstreams,
-                               AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
-                               smb_roundup(handle->conn, AFP_INFO_SIZE));
-                       if (!ok) {
-                               TALLOC_FREE(ad);
-                               return NT_STATUS_NO_MEMORY;
-                       }
-               }
-               TALLOC_FREE(ad);
-       }
 
-       if (config->rsrc != FRUIT_RSRC_STREAM) {
-               ad = ad_get(talloc_tos(), handle, smb_fname->base_name,
-                           ADOUBLE_RSRC);
-               if (ad && (ad_getentrylen(ad, ADEID_RFORK) > 0)) {
-                       if (!add_fruit_stream(
-                                   mem_ctx, pnum_streams, pstreams,
-                                   AFPRESOURCE_STREAM_NAME,
-                                   ad_getentrylen(ad, ADEID_RFORK),
-                                   smb_roundup(handle->conn,
-                                               ad_getentrylen(
-                                                       ad, ADEID_RFORK)))) {
-                               TALLOC_FREE(ad);
-                               return NT_STATUS_NO_MEMORY;
-                       }
-               }
-               TALLOC_FREE(ad);
-       }
+       DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
 
        status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
                                         pnum_streams, pstreams);
@@ -3619,13 +4207,16 @@ static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
                return status;
        }
 
-       if (config->meta == FRUIT_META_NETATALK) {
-               /* Remove the Netatalk xattr from the list */
-               if (!del_fruit_stream(mem_ctx, pnum_streams, pstreams,
-                                     ":" NETATALK_META_XATTR ":$DATA")) {
-                               TALLOC_FREE(ad);
-                               return NT_STATUS_NO_MEMORY;
-               }
+       status = fruit_streaminfo_meta(handle, fsp, smb_fname,
+                                      mem_ctx, pnum_streams, pstreams);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
+                                      mem_ctx, pnum_streams, pstreams);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
        return NT_STATUS_OK;
@@ -3637,9 +4228,15 @@ static int fruit_ntimes(vfs_handle_struct *handle,
 {
        int rc = 0;
        struct adouble *ad = NULL;
+       struct fruit_config_data *config = NULL;
 
-       if (null_timespec(ft->create_time)) {
-               goto exit;
+       SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
+                               return -1);
+
+       if ((config->meta != FRUIT_META_NETATALK) ||
+           null_timespec(ft->create_time))
+       {
+               return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
        }
 
        DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
@@ -3687,50 +4284,100 @@ static int fruit_fallocate(struct vfs_handle_struct *handle,
        return -1;
 }
 
+static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
+                                     struct files_struct *fsp,
+                                     off_t offset)
+{
+       if (offset == 0) {
+               return SMB_VFS_FREMOVEXATTR(fsp, AFPRESOURCE_EA_NETATALK);
+       }
+
+#ifdef HAVE_ATTROPEN
+       return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
+#endif
+       return 0;
+}
+
+static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
+                                       struct files_struct *fsp,
+                                       off_t offset)
+{
+       int rc;
+        struct adouble *ad =
+               (struct adouble *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       off_t ad_off = ad_getentryoff(ad, ADEID_RFORK);
+
+       if (!fruit_fsp_recheck(ad, fsp)) {
+               return -1;
+       }
+
+       rc = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset + ad_off);
+       if (rc != 0) {
+               return -1;
+       }
+
+       ad_setentrylen(ad, ADEID_RFORK, offset);
+
+       rc = ad_write(ad, NULL);
+       if (rc != 0) {
+               DBG_ERR("ad_write [%s] failed [%s]\n",
+                       fsp_str_dbg(fsp), strerror(errno));
+               return -1;
+       }
+
+       DBG_DEBUG("Path [%s] offset [%jd]\n",
+                 fsp_str_dbg(fsp), (intmax_t)offset);
+
+       return 0;
+}
+
+static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
+                                      struct files_struct *fsp,
+                                      off_t offset)
+{
+       if (offset == 0) {
+               return SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
+       }
+
+       return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
+}
+
 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
                                struct files_struct *fsp,
-                               off_t offset,
-                               struct adouble *ad)
+                               off_t offset)
 {
-       int rc;
+       int ret;
        struct fruit_config_data *config;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data, return -1);
 
-       if (config->rsrc == FRUIT_RSRC_XATTR && offset == 0) {
-               return SMB_VFS_FREMOVEXATTR(fsp,
-                                           AFPRESOURCE_EA_NETATALK);
-       }
+       switch (config->rsrc) {
+       case FRUIT_RSRC_XATTR:
+               ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
+               break;
 
-       rc = SMB_VFS_NEXT_FTRUNCATE(
-               handle, fsp,
-               offset + ad_getentryoff(ad, ADEID_RFORK));
-       if (rc != 0) {
+       case FRUIT_RSRC_ADFILE:
+               ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
+               break;
+
+       case FRUIT_RSRC_STREAM:
+               ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
+               break;
+
+       default:
+               DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
                return -1;
        }
 
-       if (config->rsrc == FRUIT_RSRC_ADFILE) {
-               ad_setentrylen(ad, ADEID_RFORK, offset);
-               rc = ad_write(ad, NULL);
-               if (rc != 0) {
-                       return -1;
-               }
-               DEBUG(10, ("fruit_ftruncate_rsrc file %s offset %jd\n",
-                          fsp_str_dbg(fsp), (intmax_t)offset));
-       }
 
-       return 0;
+       return ret;
 }
 
 static int fruit_ftruncate(struct vfs_handle_struct *handle,
                           struct files_struct *fsp,
                           off_t offset)
 {
-       int rc = 0;
-        struct adouble *ad =
-               (struct adouble *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
-
        DBG_DEBUG("fruit_ftruncate called for file %s offset %.0f\n",
                   fsp_str_dbg(fsp), (double)offset);
 
@@ -3749,25 +4396,11 @@ static int fruit_ftruncate(struct vfs_handle_struct *handle,
                return 0;
        }
 
-       if (ad == NULL) {
-               return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
-       }
-
-       if (!fruit_fsp_recheck(ad, fsp)) {
-               return -1;
-       }
-
-       switch (ad->ad_type) {
-       case ADOUBLE_RSRC:
-               rc = fruit_ftruncate_rsrc(handle, fsp, offset, ad);
-               break;
-
-       default:
-               DBG_ERR("unexpected ad_type [%d]\n", ad->ad_type);
-               return -1;
+       if (is_afpresource_stream(fsp->fsp_name)) {
+               return fruit_ftruncate_rsrc(handle, fsp, offset);
        }
 
-       return rc;
+       return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
 }
 
 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,