vfs_fruit: avoid dereferencing fsp->base_fsp in fruit_fstat_meta_stream()
[amitay/samba.git] / source3 / modules / vfs_fruit.c
index a39f6d41609b8a9d20b1bb9f9ef0274359283437..19101efba740ce6de504cc97c2219add7101c34d 100644 (file)
@@ -3403,66 +3403,68 @@ static int fruit_connect(vfs_handle_struct *handle,
        return rc;
 }
 
+static int fruit_fake_fd(void)
+{
+       int pipe_fds[2];
+       int fd;
+       int ret;
+
+       /*
+        * Return a valid fd, but ensure any attempt to use it returns
+        * an error (EPIPE). Once we get a write on the handle, we open
+        * the real fd.
+        */
+       ret = pipe(pipe_fds);
+       if (ret != 0) {
+               return -1;
+       }
+       fd = pipe_fds[0];
+       close(pipe_fds[1]);
+
+       return fd;
+}
+
 static int fruit_open_meta_stream(vfs_handle_struct *handle,
                                  struct smb_filename *smb_fname,
                                  files_struct *fsp,
                                  int flags,
                                  mode_t mode)
 {
-       AfpInfo *ai = NULL;
-       char afpinfo_buf[AFP_INFO_SIZE];
-       ssize_t len, written;
-       int hostfd = -1;
-       int rc = -1;
+       struct fruit_config_data *config = NULL;
+       struct fio *fio = NULL;
+       int open_flags = flags & ~O_CREAT;
+       int fd;
 
-       hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
-       if (hostfd == -1) {
-               return -1;
-       }
+       DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
 
-       if (!(flags & (O_CREAT | O_TRUNC))) {
-               return hostfd;
-       }
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct fruit_config_data, return -1);
 
-       ai = afpinfo_new(talloc_tos());
-       if (ai == NULL) {
-               rc = -1;
-               goto fail;
-       }
+       fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
+       fio->type = ADOUBLE_META;
+       fio->config = config;
 
-       len = afpinfo_pack(ai, afpinfo_buf);
-       if (len != AFP_INFO_SIZE) {
-               rc = -1;
-               goto fail;
+       fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
+       if (fd != -1) {
+               return fd;
        }
 
-       /* Set fd, needed in SMB_VFS_NEXT_PWRITE() */
-       fsp->fh->fd = hostfd;
-
-       written = SMB_VFS_NEXT_PWRITE(handle, fsp, afpinfo_buf,
-                                     AFP_INFO_SIZE, 0);
-       fsp->fh->fd = -1;
-       if (written != AFP_INFO_SIZE) {
-               DBG_ERR("bad write [%zd/%d]\n", written, AFP_INFO_SIZE);
-               rc = -1;
-               goto fail;
+       if (!(flags & O_CREAT)) {
+               VFS_REMOVE_FSP_EXTENSION(handle, fsp);
+               return -1;
        }
 
-       rc = 0;
+       fd = fruit_fake_fd();
+       if (fd == -1) {
+               VFS_REMOVE_FSP_EXTENSION(handle, fsp);
+               return -1;
+       }
 
-fail:
-       DBG_DEBUG("rc=%d, fd=%d\n", rc, hostfd);
+       fio->fake_fd = true;
+       fio->flags = flags;
+       fio->mode = mode;
 
-       if (rc != 0) {
-               int saved_errno = errno;
-               if (hostfd >= 0) {
-                       fsp->fh->fd = hostfd;
-                       SMB_VFS_NEXT_CLOSE(handle, fsp);
-               }
-               hostfd = -1;
-               errno = saved_errno;
-       }
-       return hostfd;
+       return fd;
 }
 
 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
@@ -3471,56 +3473,42 @@ static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
                                    int flags,
                                    mode_t mode)
 {
-       int rc;
-       int fakefd = -1;
+       struct fruit_config_data *config = NULL;
+       struct fio *fio = NULL;
        struct adouble *ad = NULL;
-       int fds[2];
+       bool meta_exists = false;
+       int fd;
 
        DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
 
-       /*
-        * Return a valid fd, but ensure any attempt to use it returns an error
-        * (EPIPE). All operations on the smb_fname or the fsp will use path
-        * based syscalls.
-        */
-       rc = pipe(fds);
-       if (rc != 0) {
-               goto exit;
+       ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
+       if (ad != NULL) {
+               meta_exists = true;
        }
-       fakefd = fds[0];
-       close(fds[1]);
 
-       if (flags & (O_CREAT | O_TRUNC)) {
-               /*
-                * The attribute does not exist or needs to be truncated,
-                * create an AppleDouble EA
-                */
-               ad = ad_init(fsp, handle, ADOUBLE_META);
-               if (ad == NULL) {
-                       rc = -1;
-                       goto exit;
-               }
-
-               rc = ad_set(ad, fsp->fsp_name);
-               if (rc != 0) {
-                       rc = -1;
-                       goto exit;
-               }
+       TALLOC_FREE(ad);
 
-               TALLOC_FREE(ad);
+       if (!meta_exists && !(flags & O_CREAT)) {
+               errno = ENOENT;
+               return -1;
        }
 
-exit:
-       DEBUG(10, ("fruit_open meta rc=%d, fd=%d\n", rc, fakefd));
-       if (rc != 0) {
-               int saved_errno = errno;
-               if (fakefd >= 0) {
-                       close(fakefd);
-               }
-               fakefd = -1;
-               errno = saved_errno;
+       fd = fruit_fake_fd();
+       if (fd == -1) {
+               return -1;
        }
-       return fakefd;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct fruit_config_data, return -1);
+
+       fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
+       fio->type = ADOUBLE_META;
+       fio->config = config;
+       fio->fake_fd = true;
+       fio->flags = flags;
+       fio->mode = mode;
+
+       return fd;
 }
 
 static int fruit_open_meta(vfs_handle_struct *handle,
@@ -3529,7 +3517,6 @@ static int fruit_open_meta(vfs_handle_struct *handle,
 {
        int fd;
        struct fruit_config_data *config = NULL;
-       struct fio *fio = NULL;
 
        DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
 
@@ -3554,14 +3541,6 @@ static int fruit_open_meta(vfs_handle_struct *handle,
 
        DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
 
-       if (fd == -1) {
-               return -1;
-       }
-
-       fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
-       fio->type = ADOUBLE_META;
-       fio->config = config;
-
        return fd;
 }
 
@@ -4554,23 +4533,35 @@ static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
                return -1;
        }
 
-       nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
-       if (nwritten != n) {
-               return -1;
-       }
-
-       if (!ai_empty_finderinfo(ai)) {
-               return n;
-       }
+       if (ai_empty_finderinfo(ai)) {
+               /*
+                * Writing an all 0 blob to the metadata stream results in the
+                * stream being removed on a macOS server. This ensures we
+                * behave the same and it verified by the "delete AFP_AfpInfo by
+                * writing all 0" test.
+                */
+               ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
+               if (ret != 0) {
+                       DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
+                               fsp_str_dbg(fsp));
+                       return -1;
+               }
 
-       ok = set_delete_on_close(
+               ok = set_delete_on_close(
                        fsp,
                        true,
                        handle->conn->session_info->security_token,
                        handle->conn->session_info->unix_token);
-       if (!ok) {
-               DBG_ERR("set_delete_on_close on [%s] failed\n",
-                       fsp_str_dbg(fsp));
+               if (!ok) {
+                       DBG_ERR("set_delete_on_close on [%s] failed\n",
+                               fsp_str_dbg(fsp));
+                       return -1;
+               }
+               return n;
+       }
+
+       nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
+       if (nwritten != n) {
                return -1;
        }
 
@@ -4621,6 +4612,12 @@ static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
                return n;
        }
 
+       /*
+        * Writing an all 0 blob to the metadata stream results in the stream
+        * being removed on a macOS server. This ensures we behave the same and
+        * it verified by the "delete AFP_AfpInfo by writing all 0" test.
+        */
+
        ok = set_delete_on_close(
                fsp,
                true,
@@ -4641,34 +4638,67 @@ static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
 {
        struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
        ssize_t nwritten;
+       uint8_t buf[AFP_INFO_SIZE];
+       size_t to_write;
+       size_t to_copy;
+       int cmp;
 
-       /*
-        * Writing an all 0 blob to the metadata stream
-        * results in the stream being removed on a macOS
-        * server. This ensures we behave the same and it
-        * verified by the "delete AFP_AfpInfo by writing all
-        * 0" test.
-        */
-       if (n != AFP_INFO_SIZE || offset != 0) {
-               DBG_ERR("unexpected offset=%jd or size=%jd\n",
-                       (intmax_t)offset, (intmax_t)n);
+       if (fio == NULL) {
+               DBG_ERR("Failed to fetch fsp extension");
                return -1;
        }
 
-       if (fio == NULL) {
-               DBG_ERR("Failed to fetch fsp extension");
+       if (n < 3) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (offset != 0 && n < 60) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       cmp = memcmp(data, "AFP", 3);
+       if (cmp != 0) {
+               errno = EINVAL;
                return -1;
        }
 
+       if (n <= AFP_OFF_FinderInfo) {
+               /*
+                * Nothing to do here really, just return
+                */
+               return n;
+       }
+
+       offset = 0;
+
+       to_copy = n;
+       if (to_copy > AFP_INFO_SIZE) {
+               to_copy = AFP_INFO_SIZE;
+       }
+       memcpy(buf, data, to_copy);
+
+       to_write = n;
+       if (to_write != AFP_INFO_SIZE) {
+               to_write = AFP_INFO_SIZE;
+       }
+
        switch (fio->config->meta) {
        case FRUIT_META_STREAM:
-               nwritten = fruit_pwrite_meta_stream(handle, fsp, data,
-                                                   n, offset);
+               nwritten = fruit_pwrite_meta_stream(handle,
+                                                   fsp,
+                                                   buf,
+                                                   to_write,
+                                                   offset);
                break;
 
        case FRUIT_META_NETATALK:
-               nwritten = fruit_pwrite_meta_netatalk(handle, fsp, data,
-                                                     n, offset);
+               nwritten = fruit_pwrite_meta_netatalk(handle,
+                                                     fsp,
+                                                     buf,
+                                                     to_write,
+                                                     offset);
                break;
 
        default:
@@ -4676,7 +4706,14 @@ static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
                return -1;
        }
 
-       return nwritten;
+       if (nwritten != to_write) {
+               return -1;
+       }
+
+       /*
+        * Return the requested amount, verified against macOS SMB server
+        */
+       return n;
 }
 
 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
@@ -5167,6 +5204,7 @@ static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
                                   SMB_STRUCT_STAT *sbuf)
 {
        struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       struct smb_filename smb_fname;
        ino_t ino;
        int ret;
 
@@ -5186,11 +5224,15 @@ static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
                return 0;
        }
 
-       ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
+       smb_fname = (struct smb_filename) {
+               .base_name = fsp->fsp_name->base_name,
+       };
+
+       ret = fruit_stat_base(handle, &smb_fname, false);
        if (ret != 0) {
                return -1;
        }
-       *sbuf = fsp->base_fsp->fsp_name->st;
+       *sbuf = smb_fname.st;
 
        ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
 
@@ -5352,7 +5394,8 @@ static NTSTATUS delete_invalid_meta_stream(
        const struct smb_filename *smb_fname,
        TALLOC_CTX *mem_ctx,
        unsigned int *pnum_streams,
-       struct stream_struct **pstreams)
+       struct stream_struct **pstreams,
+       off_t size)
 {
        struct smb_filename *sname = NULL;
        int ret;
@@ -5363,6 +5406,10 @@ static NTSTATUS delete_invalid_meta_stream(
                return NT_STATUS_INTERNAL_ERROR;
        }
 
+       if (size == 0) {
+               return NT_STATUS_OK;
+       }
+
        sname = synthetic_smb_fname(talloc_tos(),
                                    smb_fname->base_name,
                                    AFPINFO_STREAM_NAME,
@@ -5391,16 +5438,7 @@ static NTSTATUS fruit_streaminfo_meta_stream(
 {
        struct stream_struct *stream = *pstreams;
        unsigned int num_streams = *pnum_streams;
-       struct smb_filename *sname = NULL;
-       char *full_name = NULL;
-       uint32_t name_hash;
-       struct share_mode_lock *lck = NULL;
-       struct file_id id = {0};
-       bool delete_on_close_set;
        int i;
-       int ret;
-       NTSTATUS status;
-       bool ok;
 
        for (i = 0; i < num_streams; i++) {
                if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
@@ -5416,74 +5454,16 @@ static NTSTATUS fruit_streaminfo_meta_stream(
                DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
                        (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
 
-               return delete_invalid_meta_stream(handle, smb_fname, mem_ctx,
-                                                 pnum_streams, pstreams);
-       }
-
-       /*
-        * Now check if there's a delete-on-close pending on the stream. If so,
-        * hide the stream. This behaviour was verified against a macOS 10.12
-        * SMB server.
-        */
-
-       sname = synthetic_smb_fname(talloc_tos(),
-                                   smb_fname->base_name,
-                                   AFPINFO_STREAM_NAME,
-                                   NULL, 0);
-       if (sname == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto out;
-       }
-
-       ret = fruit_stat_base(handle, sname, false);
-       if (ret != 0) {
-               status = map_nt_error_from_unix(errno);
-               goto out;
-       }
-
-       sname->st.st_ex_ino = fruit_inode(&sname->st, AFPINFO_STREAM);
-
-       id = SMB_VFS_NEXT_FILE_ID_CREATE(handle, &sname->st);
-
-       lck = get_existing_share_mode_lock(talloc_tos(), id);
-       if (lck == NULL) {
-               status = NT_STATUS_OK;
-               goto out;
+               return delete_invalid_meta_stream(handle,
+                                                 smb_fname,
+                                                 mem_ctx,
+                                                 pnum_streams,
+                                                 pstreams,
+                                                 stream[i].size);
        }
 
-       full_name = talloc_asprintf(talloc_tos(),
-                                   "%s%s",
-                                   sname->base_name,
-                                   AFPINFO_STREAM);
-       if (full_name == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto out;
-       }
 
-       status = file_name_hash(handle->conn, full_name, &name_hash);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto out;
-       }
-
-       delete_on_close_set = is_delete_on_close_set(lck, name_hash);
-       if (delete_on_close_set) {
-               ok = del_fruit_stream(mem_ctx,
-                                     pnum_streams,
-                                     pstreams,
-                                     AFPINFO_STREAM);
-               if (!ok) {
-                       status = NT_STATUS_INTERNAL_ERROR;
-                       goto out;
-               }
-       }
-
-       status  = NT_STATUS_OK;
-
-out:
-       TALLOC_FREE(sname);
-       TALLOC_FREE(lck);
-       TALLOC_FREE(full_name);
-       return status;
+       return NT_STATUS_OK;
 }
 
 static NTSTATUS fruit_streaminfo_meta_netatalk(