return -1;
}
+static int streams_xattr_openat(struct vfs_handle_struct *handle,
+ const struct files_struct *dirfsp,
+ const struct smb_filename *smb_fname,
+ files_struct *fsp,
+ int flags,
+ mode_t mode)
+{
+ NTSTATUS status;
+ struct streams_xattr_config *config = NULL;
+ struct stream_io *sio = NULL;
+ struct ea_struct ea;
+ char *xattr_name = NULL;
+ int pipe_fds[2];
+ int fakefd = -1;
+ bool set_empty_xattr = false;
+ int ret;
+
+ /*
+ * For now assert this, so the below SMB_VFS_SETXATTR() works.
+ */
+ SMB_ASSERT(dirfsp->fh->fd == AT_FDCWD);
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config, struct streams_xattr_config,
+ return -1);
+
+ DEBUG(10, ("streams_xattr_open called for %s with flags 0x%x\n",
+ smb_fname_str_dbg(smb_fname), flags));
+
+ if (!is_named_stream(smb_fname)) {
+ return SMB_VFS_NEXT_OPENAT(handle,
+ dirfsp,
+ smb_fname,
+ fsp,
+ flags,
+ mode);
+ }
+
+ status = streams_xattr_get_name(handle, talloc_tos(),
+ smb_fname->stream_name, &xattr_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ errno = map_errno_from_nt_status(status);
+ goto fail;
+ }
+
+ status = get_ea_value(talloc_tos(), handle->conn, NULL,
+ smb_fname, xattr_name, &ea);
+
+ DEBUG(10, ("get_ea_value returned %s\n", nt_errstr(status)));
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ /*
+ * The base file is not there. This is an error even if
+ * we got O_CREAT, the higher levels should have created
+ * the base file for us.
+ */
+ DBG_DEBUG("streams_xattr_open: base file %s not around, "
+ "returning ENOENT\n", smb_fname->base_name);
+ errno = ENOENT;
+ goto fail;
+ }
+
+ if (!(flags & O_CREAT)) {
+ errno = ENOATTR;
+ goto fail;
+ }
+
+ set_empty_xattr = true;
+ }
+
+ if (flags & O_TRUNC) {
+ set_empty_xattr = true;
+ }
+
+ if (set_empty_xattr) {
+ /*
+ * The attribute does not exist or needs to be truncated
+ */
+
+ /*
+ * Darn, xattrs need at least 1 byte
+ */
+ char null = '\0';
+
+ DEBUG(10, ("creating or truncating attribute %s on file %s\n",
+ xattr_name, smb_fname->base_name));
+
+ ret = SMB_VFS_SETXATTR(fsp->conn,
+ smb_fname,
+ xattr_name,
+ &null, sizeof(null),
+ flags & O_EXCL ? XATTR_CREATE : 0);
+ if (ret != 0) {
+ goto fail;
+ }
+ }
+
+ /*
+ * Return a valid fd, but ensure any attempt to use it returns an error
+ * (EPIPE).
+ */
+ ret = pipe(pipe_fds);
+ if (ret != 0) {
+ goto fail;
+ }
+
+ close(pipe_fds[1]);
+ pipe_fds[1] = -1;
+ fakefd = pipe_fds[0];
+
+ sio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct stream_io, NULL);
+ if (sio == NULL) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ sio->xattr_name = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
+ xattr_name);
+ if (sio->xattr_name == NULL) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ /*
+ * so->base needs to be a copy of fsp->fsp_name->base_name,
+ * making it identical to streams_xattr_recheck(). If the
+ * open is changing directories, fsp->fsp_name->base_name
+ * will be the full path from the share root, whilst
+ * smb_fname will be relative to the $cwd.
+ */
+ sio->base = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
+ fsp->fsp_name->base_name);
+ if (sio->base == NULL) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ sio->fsp_name_ptr = fsp->fsp_name;
+ sio->handle = handle;
+ sio->fsp = fsp;
+
+ return fakefd;
+
+ fail:
+ if (fakefd >= 0) {
+ close(fakefd);
+ fakefd = -1;
+ }
+
+ return -1;
+}
+
static int streams_xattr_close(vfs_handle_struct *handle,
files_struct *fsp)
{
.fs_capabilities_fn = streams_xattr_fs_capabilities,
.connect_fn = streams_xattr_connect,
.open_fn = streams_xattr_open,
+ .openat_fn = streams_xattr_openat,
.close_fn = streams_xattr_close,
.stat_fn = streams_xattr_stat,
.fstat_fn = streams_xattr_fstat,