smb2_unix vl-smb2-unix
authorVolker Lendecke <vl@samba.org>
Mon, 16 Jun 2014 10:20:12 +0000 (10:20 +0000)
committerRalph Boehme <slow@samba.org>
Sun, 27 Dec 2015 09:43:26 +0000 (10:43 +0100)
(cherry picked from commit af72d743efb5ba62f8a149a015262b63a9c30840)

13 files changed:
libcli/smb/smb2_constants.h
source3/client/client.c
source3/client/client_proto.h
source3/client/clifuse.c [new file with mode: 0644]
source3/librpc/idl/smbunix.idl [new file with mode: 0644]
source3/librpc/idl/wscript_build
source3/librpc/wscript_build
source3/libsmb/cli_smb2_fnum.c
source3/libsmb/cli_smb2_fnum.h
source3/modules/vfs_smb2posix.c [new file with mode: 0644]
source3/modules/wscript_build
source3/wscript
source3/wscript_build

index f6edf6ba109d9fa5d8591901e6bfd978581e2509..df0c1f33106fd79832c2fdc54f2e8d2953409161 100644 (file)
 #define SMB2_CREATE_TAG_DH2Q "DH2Q"
 #define SMB2_CREATE_TAG_DH2C "DH2C"
 #define SMB2_CREATE_TAG_AAPL "AAPL"
+#define SMB2_CREATE_TAG_POSIXEXT "PosixExt"
 #define SMB2_CREATE_TAG_APP_INSTANCE_ID "\x45\xBC\xA6\x6A\xEF\xA7\xF7\x4A\x90\x08\xFA\x46\x2E\x14\x4D\x74"
 #define SVHDX_OPEN_DEVICE_CONTEXT "\x9C\xCB\xCF\x9E\x04\xC1\xE6\x43\x98\x0E\x15\x8D\xA1\xF6\xEC\x83"
 
index 034b48a26780f59abfc42e35db685d0ca1476db6..b0c1522d05146323a2758e92a0f111ada31fb6c9 100644 (file)
@@ -1412,6 +1412,21 @@ static int cmd_more(void)
        return rc;
 }
 
+#if HAVE_FUSE
+static int cmd_mount(void)
+{
+       TALLOC_CTX *ctx = talloc_tos();
+       char *mountpoint;
+
+       if (!next_token_talloc(ctx, &cmd_ptr, &mountpoint, NULL)) {
+               d_printf("mount <mountpoint>\n");
+               return 1;
+       }
+
+       return do_mount(cli, mountpoint);
+}
+#endif
+
 /****************************************************************************
  Do a mget command.
 ****************************************************************************/
@@ -4921,6 +4936,9 @@ static struct {
   {"mget",cmd_mget,"<mask> get all the matching files",{COMPL_REMOTE,COMPL_NONE}},
   {"mkdir",cmd_mkdir,"<directory> make a directory",{COMPL_NONE,COMPL_NONE}},
   {"more",cmd_more,"<remote name> view a remote file with your pager",{COMPL_REMOTE,COMPL_NONE}},
+#if HAVE_FUSE
+  {"mount", cmd_mount, "<mountpoint> fuse-mount", {COMPL_NONE, COMPL_NONE}},
+#endif
   {"mput",cmd_mput,"<mask> put all matching files",{COMPL_REMOTE,COMPL_NONE}},
   {"newer",cmd_newer,"<file> only mget files newer than the specified local file",{COMPL_LOCAL,COMPL_NONE}},
   {"notify",cmd_notify,"<file>Get notified of dir changes",{COMPL_REMOTE,COMPL_NONE}},
index d3d40363f20cd524e98dfdd8664c9a71a23d43f4..c59da8b0da96f5f2a3acb96dc4dc1ea9bb282399 100644 (file)
@@ -49,4 +49,6 @@ int cmd_iosize(void);
 int do_smb_browse(void);
 int do_smb_browse(void);
 
+int do_mount(struct cli_state *cli, const char *mountpoint);
+
 #endif /*  _CLIENT_PROTO_H_  */
diff --git a/source3/client/clifuse.c b/source3/client/clifuse.c
new file mode 100644 (file)
index 0000000..b772ad7
--- /dev/null
@@ -0,0 +1,1481 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * SMB client
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define FUSE_USE_VERSION 26
+#define _FILE_OFFSET_BITS 64
+#include "fuse/fuse_lowlevel.h"
+
+#include "includes.h"
+#include "client.h"
+#include "libsmb/proto.h"
+#include "client/client_proto.h"
+#include "libsmb/clirap.h"
+#include "libsmb/cli_smb2_fnum.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "libcli/smb/smbXcli_base.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_smbunix.h"
+
+struct mount_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+       bool done;
+
+       struct tevent_fd *fde;
+       struct tevent_signal *signal_ev;
+
+       struct fuse_chan *ch;
+       struct fuse_session *se;
+
+       size_t bufsize;
+       char *buf;
+
+       struct idr_context *ino_ctx;
+       TALLOC_CTX *ino_parent;
+};
+
+struct inode_state {
+       struct idr_context *ino_ctx;
+       fuse_ino_t ino;
+       char path[1];
+};
+
+static int inode_state_destructor(struct inode_state *s);
+
+static struct inode_state *inode_state_init(TALLOC_CTX *mem_ctx,
+                                           struct idr_context *ino_ctx,
+                                           const char *path)
+{
+       struct inode_state *state;
+       size_t pathlen;
+       int ino;
+
+       pathlen = strlen(path);
+       state = talloc_size(
+               mem_ctx, offsetof(struct inode_state, path) + pathlen + 1);
+       if (state == NULL) {
+               return NULL;
+       }
+       talloc_set_name_const(state, "struct inode_state");
+
+       ino = idr_get_new_above(ino_ctx, state, 1, INT32_MAX);
+       if (ino == -1) {
+               TALLOC_FREE(state);
+               return NULL;
+       }
+
+       state->ino = ino;
+       state->ino_ctx = ino_ctx;
+       memcpy(state->path, path, pathlen + 1);
+
+       DBG_DEBUG("Creating ino %d for path %s\n", ino, path);
+
+       talloc_set_destructor(state, inode_state_destructor);
+
+       return state;
+}
+
+static struct inode_state *inode_state_new(struct mount_state *mstate,
+                                          const char *path)
+{
+       return inode_state_init(mstate->ino_parent, mstate->ino_ctx, path);
+}
+
+static int inode_state_destructor(struct inode_state *s)
+{
+       DBG_DEBUG("destroying inode %ju\n", (uintmax_t)s->ino);
+       idr_remove(s->ino_ctx, s->ino);
+       return 0;
+}
+
+static mode_t smb_to_unix_mode(uint32_t filetype, uint64_t perms)
+{
+       return unix_filetype_from_wire(filetype) | wire_perms_to_unix(perms);
+}
+
+static struct smbunix_file_info2 stat2info(const struct stat *pst)
+{
+       dev_t devno;
+
+       if (S_ISBLK(pst->st_mode) || S_ISCHR(pst->st_mode)) {
+               devno = pst->st_rdev;
+       } else {
+               devno = pst->st_dev;
+       }
+
+       return (struct smbunix_file_info2) {
+               .end_of_file = pst->st_size,
+               .creation_time = unix_timespec_to_nt_time(pst->st_ctim),
+               .last_access_time = unix_timespec_to_nt_time(pst->st_atim),
+               .last_modification_time = unix_timespec_to_nt_time(
+                       pst->st_mtim),
+               .uid = pst->st_uid,
+               .gid = pst->st_gid,
+               .dev_major = unix_dev_major(devno),
+               .dev_minor = unix_dev_minor(devno),
+               .permissions = unix_perms_to_wire(pst->st_mode),
+               .nlinks = pst->st_nlink,
+       };
+}
+
+static struct stat info2stat(const struct smb_create_returns *cr,
+                            const struct smbunix_file_info_basic *basic)
+{
+       return (struct stat) {
+               .st_size   = cr->end_of_file,
+               .st_blocks = 0, /* TODO */
+               .st_ctim   = nt_time_to_unix_timespec(cr->creation_time),
+               .st_atim   = nt_time_to_unix_timespec(cr->last_access_time),
+               .st_mtim   = nt_time_to_unix_timespec(cr->last_write_time),
+               .st_uid    = basic->uid,
+               .st_gid    = basic->gid,
+               .st_mode   = smb_to_unix_mode(basic->type, basic->permissions),
+               .st_dev    = makedev(basic->dev_major, basic->dev_minor),
+               .st_nlink  = basic->nlinks
+       };
+}
+
+struct cli_posix_rpc_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+
+       struct smb2_create_blob unix_blob;
+       struct smb2_create_blobs blobs;
+
+       struct smb_create_returns cr;
+       struct smbunix_file_info *unix_info;
+};
+
+static void cli_posix_rpc_opened(struct tevent_req *subreq);
+static void cli_posix_rpc_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_posix_rpc_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+       struct cli_state *cli, const char *path,
+       uint32_t desired_access, uint32_t create_disposition,
+       struct smbunix_req *unix_req)
+{
+       struct tevent_req *req, *subreq;
+       struct cli_posix_rpc_state *state;
+       enum ndr_err_code ndr_err;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_posix_rpc_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->cli = cli;
+
+       state->unix_blob.tag = SMB2_CREATE_TAG_POSIXEXT;
+
+       if (DEBUGLEVEL >= 10) {
+               NDR_PRINT_DEBUG(smbunix_req, unix_req);
+       }
+
+       ndr_err = ndr_push_struct_blob(
+               &state->unix_blob.data, state, unix_req,
+               (ndr_push_flags_fn_t)ndr_push_smbunix_req);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               tevent_req_nterror(req, ndr_map_error2ntstatus(ndr_err));
+               return tevent_req_post(req, ev);
+       }
+
+       state->blobs = (struct smb2_create_blobs) {
+               .num_blobs = 1,
+               .blobs = &state->unix_blob,
+       };
+
+       subreq = smb2cli_create_send(
+               state, ev, cli->conn, cli->timeout, cli->smb2.session,
+               cli->smb2.tcon, path, SMB2_OPLOCK_LEVEL_NONE,
+               SMB2_IMPERSONATION_IMPERSONATION,
+               desired_access, 0,
+               FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+               create_disposition, 0, &state->blobs);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_posix_rpc_opened, req);
+       return req;
+}
+
+static void cli_posix_rpc_opened(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_posix_rpc_state *state = tevent_req_data(
+               req, struct cli_posix_rpc_state);
+       uint64_t p, v;
+       uint32_t i;
+       struct smb2_create_blobs blobs;
+       struct smb2_create_blob *posix = NULL;
+       enum ndr_err_code ndr_err;
+       NTSTATUS status;
+
+       status = smb2cli_create_recv(subreq, &p, &v, &state->cr,
+                                    state, &blobs);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               printf("create_recv returned %s\n", nt_errstr(status));
+               return;
+       }
+
+       for (i=0; i<blobs.num_blobs; i++) {
+               struct smb2_create_blob *blob = &blobs.blobs[i];
+
+               if (strcmp(blob->tag, SMB2_CREATE_TAG_POSIXEXT) == 0) {
+                       posix = blob;
+                       break;
+               }
+       }
+
+       if (posix == NULL) {
+               DBG_WARNING("Received no posix blob\n");
+               tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+               return;
+       }
+
+       state->unix_info = talloc(state, struct smbunix_file_info);
+       if (tevent_req_nomem(state->unix_info, req)) {
+               return;
+       }
+
+       ndr_err = ndr_pull_struct_blob_all(
+               &posix->data, state->unix_info, state->unix_info,
+               (ndr_pull_flags_fn_t)ndr_pull_smbunix_file_info);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               DBG_WARNING("ndr_pull_smbunix_file_info returned %s\n",
+                           ndr_errstr(ndr_err));
+               tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+               return;
+       }
+
+       if (DEBUGLEVEL >= 10) {
+               NDR_PRINT_DEBUG(smbunix_file_info, state->unix_info);
+       }
+
+       subreq = smb2cli_close_send(state, state->ev, state->cli->conn,
+                                   state->cli->timeout,
+                                   state->cli->smb2.session,
+                                   state->cli->smb2.tcon,
+                                   0, p, v);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, cli_posix_rpc_done, req);
+}
+
+static void cli_posix_rpc_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = smb2cli_close_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+       tevent_req_done(req);
+}
+
+static NTSTATUS cli_posix_rpc_recv(struct tevent_req *req,
+                                 struct smb_create_returns *cr,
+                                 TALLOC_CTX *mem_ctx,
+                                 struct smbunix_file_info **unix_info)
+{
+       struct cli_posix_rpc_state *state = tevent_req_data(
+               req, struct cli_posix_rpc_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+
+       if (cr != NULL) {
+               *cr = state->cr;
+       }
+       if (unix_info != NULL) {
+               *unix_info = talloc_move(mem_ctx, &state->unix_info);
+       }
+       return NT_STATUS_OK;
+}
+
+struct cli_get_unixattr_state {
+       struct smbunix_req unix_req;
+       struct stat sbuf;
+};
+
+static void cli_get_unixattr_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_get_unixattr_send(TALLOC_CTX *mem_ctx,
+                                               struct tevent_context *ev,
+                                               struct cli_state *cli,
+                                               const char *path)
+{
+       struct tevent_req *req, *subreq;
+       struct cli_get_unixattr_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_get_unixattr_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->unix_req = (struct smbunix_req) {
+               .cmd = SMBUNIX_CMD_GETINFO,
+               .data.getinfo.level = SMBUNIX_FILE_INFO_BASIC,
+       };
+
+       subreq = cli_posix_rpc_send(
+               state, ev, cli, path,
+               SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|
+               FILE_WRITE_ATTRIBUTES, FILE_OPEN, &state->unix_req);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_get_unixattr_done, req);
+       return req;
+}
+
+static void cli_get_unixattr_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_get_unixattr_state *state = tevent_req_data(
+               req, struct cli_get_unixattr_state);
+       struct smb_create_returns cr;
+       struct smbunix_file_info *unix_info = NULL;
+       NTSTATUS status;
+
+       status = cli_posix_rpc_recv(subreq, &cr, state, &unix_info);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               printf("cli_posix_rpc_recv returned %s\n", nt_errstr(status));
+               return;
+       }
+
+       if (unix_info->level != SMBUNIX_FILE_INFO_BASIC) {
+               printf("Expected SMBUNIX_FILE_INFO_BASIC\n");
+               tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+               return;
+       }
+
+       state->sbuf = info2stat(&cr, &unix_info->info.basic);
+
+       tevent_req_done(req);
+}
+
+static NTSTATUS cli_get_unixattr_recv(struct tevent_req *req,
+                                     struct stat *sbuf)
+{
+       struct cli_get_unixattr_state *state = tevent_req_data(
+               req, struct cli_get_unixattr_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+
+       if (sbuf != NULL) {
+               *sbuf = state->sbuf;
+       }
+       return NT_STATUS_OK;
+}
+
+struct ll_getattr_state {
+       struct mount_state *mnt_state;
+       fuse_req_t freq;
+};
+
+static void cli_ll_getattr_done(struct tevent_req *req);
+
+static void cli_ll_getattr(fuse_req_t freq, fuse_ino_t ino,
+                          struct fuse_file_info *fi)
+{
+       struct mount_state *mnt_state = talloc_get_type_abort(
+               fuse_req_userdata(freq), struct mount_state);
+       struct ll_getattr_state *state;
+       struct tevent_req *req;
+       struct inode_state *inode;
+
+       DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
+
+       inode = idr_find(mnt_state->ino_ctx, ino);
+       if (inode == NULL) {
+               fuse_reply_err(freq, ENOENT);
+               return;
+       }
+
+       state = talloc(mnt_state, struct ll_getattr_state);
+       if (state == NULL) {
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       state->mnt_state = mnt_state;
+       state->freq = freq;
+
+       req = cli_get_unixattr_send(state, mnt_state->ev, mnt_state->cli,
+                                   inode->path);
+       if (req == NULL) {
+               TALLOC_FREE(state);
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       tevent_req_set_callback(req, cli_ll_getattr_done, state);
+}
+
+static void cli_ll_getattr_done(struct tevent_req *req)
+{
+       struct ll_getattr_state *state = tevent_req_callback_data(
+               req, struct ll_getattr_state);
+       struct stat sbuf;
+       NTSTATUS status;
+
+       status = cli_get_unixattr_recv(req, &sbuf);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               fuse_reply_err(state->freq, ENOENT);
+               TALLOC_FREE(state);
+               return;
+       }
+
+       fuse_reply_attr(state->freq, &sbuf, 1.0);
+       TALLOC_FREE(state);
+}
+
+struct ll_setattr_state {
+       struct mount_state *mnt_state;
+       fuse_req_t freq;
+
+       struct smbunix_req unix_req;
+};
+
+static void cli_ll_setattr_done(struct tevent_req *req);
+
+static void cli_ll_setattr(fuse_req_t freq, fuse_ino_t ino, struct stat *attr,
+                          int to_set, struct fuse_file_info *fi)
+{
+       struct mount_state *mnt_state = talloc_get_type_abort(
+               fuse_req_userdata(freq), struct mount_state);
+       struct ll_setattr_state *state;
+       struct tevent_req *req;
+       struct inode_state *inode;
+
+       DBG_DEBUG("ino=%ju, to_set=%x\n", (uintmax_t)ino, to_set);
+
+       inode = idr_find(mnt_state->ino_ctx, ino);
+       if (inode == NULL) {
+               fuse_reply_err(freq, ENOENT);
+               return;
+       }
+
+       state = talloc(mnt_state, struct ll_setattr_state);
+       if (state == NULL) {
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       state->mnt_state = mnt_state;
+       state->freq = freq;
+
+       state->unix_req.cmd = SMBUNIX_CMD_SETINFO;
+       state->unix_req.data.setinfo.to_set = to_set;
+       state->unix_req.data.setinfo.info = stat2info(attr);
+
+       req = cli_posix_rpc_send(state, mnt_state->ev, mnt_state->cli,
+                               inode->path, FILE_WRITE_ATTRIBUTES, FILE_OPEN,
+                               &state->unix_req);
+       if (req == NULL) {
+               TALLOC_FREE(state);
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       tevent_req_set_callback(req, cli_ll_setattr_done, state);
+}
+
+static void cli_ll_setattr_done(struct tevent_req *req)
+{
+       struct ll_setattr_state *state = tevent_req_callback_data(
+               req, struct ll_setattr_state);
+       struct smb_create_returns cr;
+       struct smbunix_file_info *unix_info = NULL;
+       struct stat sbuf;
+       NTSTATUS status;
+
+       status = cli_posix_rpc_recv(req, &cr, state, &unix_info);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               fuse_reply_err(state->freq, map_errno_from_nt_status(status));
+               TALLOC_FREE(state);
+               return;
+       }
+
+       if (unix_info->level != SMBUNIX_FILE_INFO_BASIC) {
+               printf("Expected SMBUNIX_FILE_INFO_BASIC\n");
+               tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+               return;
+       }
+
+       sbuf = info2stat(&cr, &unix_info->info.basic);
+
+       fuse_reply_attr(state->freq, &sbuf, 1.0);
+       TALLOC_FREE(state);
+}
+
+struct ll_create_state {
+       struct mount_state *mstate;
+       fuse_req_t freq;
+       struct fuse_file_info fi;
+       char *path;
+};
+
+static void cli_ll_create_done(struct tevent_req *req);
+
+static void cli_ll_create(fuse_req_t freq, fuse_ino_t parent, const char *name,
+                         mode_t mode, struct fuse_file_info *fi)
+{
+       struct mount_state *mstate = talloc_get_type_abort(
+               fuse_req_userdata(freq), struct mount_state);
+       struct ll_create_state *state;
+       struct inode_state *istate;
+       struct tevent_req *req;
+
+       DBG_DEBUG("parent=%ju, name=%s, mode=%x\n", (uintmax_t)parent,
+                 name, (unsigned)mode);
+
+       istate = idr_find(mstate->ino_ctx, parent);
+       if (istate == NULL) {
+               fuse_reply_err(freq, ENOENT);
+               return;
+       }
+
+       state = talloc(mstate, struct ll_create_state);
+       if (state == NULL) {
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       state->mstate = mstate;
+       state->freq = freq;
+       state->fi = *fi;
+
+       state->path = talloc_asprintf(state, "%s%s%s", istate->path,
+                                     strlen(istate->path) ? "\\": "",
+                                     name);
+       if (state->path == NULL) {
+               TALLOC_FREE(state);
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+
+       req = cli_smb2_create_fnum_send(
+               state, mstate->ev, mstate->cli, state->path,
+               0, FILE_GENERIC_READ|FILE_GENERIC_WRITE, FILE_ATTRIBUTE_NORMAL,
+               FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+               FILE_CREATE, FILE_NON_DIRECTORY_FILE);
+       if (req == NULL) {
+               TALLOC_FREE(state);
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       tevent_req_set_callback(req, cli_ll_create_done, state);
+}
+
+static void cli_ll_create_done(struct tevent_req *req)
+{
+       struct ll_create_state *state = tevent_req_callback_data(
+               req, struct ll_create_state);
+       struct fuse_entry_param e;
+       struct inode_state *ino;
+       uint16_t fnum;
+       NTSTATUS status;
+
+       status = cli_smb2_create_fnum_recv(req, &fnum, NULL);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               fuse_reply_err(state->freq, map_errno_from_nt_status(status));
+               return;
+       }
+
+       state->fi.fh = fnum;
+       state->fi.direct_io = 0;
+       state->fi.keep_cache = 0;
+
+       ino = inode_state_new(state->mstate, state->path);
+       if (ino == NULL) {
+               fuse_reply_err(state->freq, ENOMEM);
+               return;
+       }
+
+       e = (struct fuse_entry_param) {
+               .ino = ino->ino,
+               .generation = 1, /* FIXME */
+               .attr_timeout = 1.0,
+               .entry_timeout = 1.0
+       };
+
+       fuse_reply_create(state->freq, &e, &state->fi);
+
+       TALLOC_FREE(state);
+}
+
+struct ll_lookup_state {
+       struct mount_state *mstate;
+       fuse_req_t freq;
+       char *path;
+};
+
+static void cli_ll_lookup_done(struct tevent_req *req);
+
+static void cli_ll_lookup(fuse_req_t freq, fuse_ino_t parent_ino,
+                         const char *name)
+{
+       struct mount_state *mstate = talloc_get_type_abort(
+               fuse_req_userdata(freq), struct mount_state);
+       struct ll_lookup_state *state;
+       struct tevent_req *req;
+       struct inode_state *parent;
+
+       DBG_DEBUG("parent_ino=%ju, name=%s\n", (uintmax_t)parent_ino, name);
+
+       parent = idr_find(mstate->ino_ctx, parent_ino);
+       if (parent == NULL) {
+               DBG_WARNING("could not find parent\n");
+               fuse_reply_err(freq, ENOENT);
+               return;
+       }
+
+       state = talloc(mstate, struct ll_lookup_state);
+       if (state == NULL) {
+               DBG_WARNING("talloc failed\n");
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       state->mstate = mstate;
+       state->freq = freq;
+
+       state->path = talloc_asprintf(state, "%s%s%s", parent->path,
+                                     strlen(parent->path) ? "\\": "",
+                                     name);
+       if (state->path == NULL) {
+               TALLOC_FREE(state);
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+
+       req = cli_get_unixattr_send(state, mstate->ev, mstate->cli,
+                                   state->path);
+       if (req == NULL) {
+               TALLOC_FREE(state);
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       tevent_req_set_callback(req, cli_ll_lookup_done, state);
+}
+
+static void cli_ll_lookup_done(struct tevent_req *req)
+{
+       struct ll_lookup_state *state = tevent_req_callback_data(
+               req, struct ll_lookup_state);
+       struct stat sbuf;
+       struct fuse_entry_param e;
+       struct inode_state *ino;
+       NTSTATUS status;
+
+       status = cli_get_unixattr_recv(req, &sbuf);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               fuse_reply_err(state->freq, map_errno_from_nt_status(status));
+               return;
+       }
+
+       ino = inode_state_new(state->mstate, state->path);
+       if (ino == NULL) {
+               fuse_reply_err(state->freq, ENOMEM);
+               return;
+       }
+
+       e = (struct fuse_entry_param) {
+               .ino = ino->ino,
+               .attr = sbuf,
+               .generation = 1, /* FIXME */
+               .attr_timeout = 1.0,
+               .entry_timeout = 1.0
+       };
+
+       fuse_reply_entry(state->freq, &e);
+       TALLOC_FREE(state);
+}
+
+struct ll_open_state {
+       struct mount_state *mstate;
+       fuse_req_t freq;
+       struct fuse_file_info fi;
+};
+
+static void cli_ll_open_done(struct tevent_req *req);
+
+static void cli_ll_open(fuse_req_t freq, fuse_ino_t ino,
+                       struct fuse_file_info *fi)
+{
+       struct mount_state *mstate = talloc_get_type_abort(
+               fuse_req_userdata(freq), struct mount_state);
+       struct ll_open_state *state;
+       struct inode_state *istate;
+       struct tevent_req *req;
+       uint32_t acc;
+
+       DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
+
+       istate = idr_find(mstate->ino_ctx, ino);
+       if (istate == NULL) {
+               fuse_reply_err(freq, ENOENT);
+               return;
+       }
+
+       state = talloc(mstate, struct ll_open_state);
+       if (state == NULL) {
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       state->mstate = mstate;
+       state->freq = freq;
+       state->fi = *fi;
+
+       switch (fi->flags & O_ACCMODE) {
+       case O_RDONLY:
+               acc = FILE_GENERIC_READ;
+               break;
+       case O_WRONLY:
+               acc = FILE_GENERIC_WRITE;
+               break;
+       case O_RDWR:
+               acc = FILE_GENERIC_READ|FILE_GENERIC_WRITE;
+               break;
+       default:
+               fuse_reply_err(freq, EACCES);
+               return;
+       }
+
+       req = cli_smb2_create_fnum_send(
+               state, mstate->ev, mstate->cli, istate->path,
+               0, acc, FILE_ATTRIBUTE_NORMAL,
+               FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+               FILE_OPEN, FILE_NON_DIRECTORY_FILE);
+       if (req == NULL) {
+               TALLOC_FREE(state);
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       tevent_req_set_callback(req, cli_ll_open_done, state);
+}
+
+static void cli_ll_open_done(struct tevent_req *req)
+{
+       struct ll_open_state *state = tevent_req_callback_data(
+               req, struct ll_open_state);
+       uint16_t fnum;
+       NTSTATUS status;
+
+       status = cli_smb2_create_fnum_recv(req, &fnum, NULL);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               fuse_reply_err(state->freq, map_errno_from_nt_status(status));
+               return;
+       }
+
+       state->fi.fh = fnum;
+       state->fi.direct_io = 0;
+       state->fi.keep_cache = 0;
+
+       fuse_reply_open(state->freq, &state->fi);
+
+       TALLOC_FREE(state);
+}
+
+struct ll_release_state {
+       struct mount_state *mstate;
+       fuse_req_t freq;
+       fuse_ino_t ino;
+};
+
+static void cli_ll_release_done(struct tevent_req *req);
+
+static void cli_ll_release(fuse_req_t freq, fuse_ino_t ino,
+                          struct fuse_file_info *fi)
+{
+       struct mount_state *mstate = talloc_get_type_abort(
+               fuse_req_userdata(freq), struct mount_state);
+       struct ll_release_state *state;
+       struct inode_state *istate;
+       struct tevent_req *req;
+       uint16_t fnum;
+
+       DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
+
+       istate = idr_find(mstate->ino_ctx, ino);
+       if (istate == NULL) {
+               fuse_reply_err(freq, ENOENT);
+               return;
+       }
+
+       state = talloc(mstate, struct ll_release_state);
+       if (state == NULL) {
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       state->mstate = mstate;
+       state->freq = freq;
+       state->ino = ino;
+
+       fnum = fi->fh;
+
+       req = cli_smb2_close_fnum_send(state, mstate->ev, mstate->cli, fnum);
+       if (req == NULL) {
+               TALLOC_FREE(state);
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       tevent_req_set_callback(req, cli_ll_release_done, state);
+}
+
+static void cli_ll_release_done(struct tevent_req *req)
+{
+       struct ll_release_state *state = tevent_req_callback_data(
+               req, struct ll_release_state);
+       struct inode_state *istate;
+       NTSTATUS status;
+
+       status = cli_smb2_close_fnum_recv(req);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               fuse_reply_err(state->freq, map_errno_from_nt_status(status));
+               return;
+       }
+
+       istate = idr_find(state->mstate->ino_ctx, state->ino);
+       if (istate == NULL) {
+               DEBUG(1, ("%s: inode %ju vanished!\n", __func__,
+                         (uintmax_t)state->ino));
+       }
+       TALLOC_FREE(istate);
+
+       fuse_reply_err(state->freq, 0);
+       TALLOC_FREE(state);
+}
+
+struct ll_read_state {
+       struct mount_state *mstate;
+       fuse_req_t freq;
+};
+
+static void cli_ll_read_done(struct tevent_req *req);
+
+static void cli_ll_read(fuse_req_t freq, fuse_ino_t ino,
+                       size_t size, off_t off,
+                       struct fuse_file_info *fi)
+{
+       struct mount_state *mstate = talloc_get_type_abort(
+               fuse_req_userdata(freq), struct mount_state);
+       struct ll_read_state *state;
+       struct tevent_req *req;
+       uint16_t fnum;
+
+       DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino,
+                 size, (uintmax_t)off);
+
+       state = talloc(mstate, struct ll_read_state);
+       if (state == NULL) {
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       state->mstate = mstate;
+       state->freq = freq;
+
+       fnum = fi->fh;
+
+       req = cli_smb2_read_send(state, mstate->ev, mstate->cli,
+                                fnum, off, size);
+       if (req == NULL) {
+               TALLOC_FREE(state);
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       tevent_req_set_callback(req, cli_ll_read_done, state);
+}
+
+static void cli_ll_read_done(struct tevent_req *req)
+{
+       struct ll_read_state *state = tevent_req_callback_data(
+               req, struct ll_read_state);
+       ssize_t received;
+       uint8_t *rcvbuf;
+       NTSTATUS status;
+
+       status = cli_smb2_read_recv(req, &received, &rcvbuf);
+       /* no talloc_free here yet */
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
+               received = 0;
+               rcvbuf = NULL;
+               status = NT_STATUS_OK;
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+               fuse_reply_err(state->freq, map_errno_from_nt_status(status));
+               return;
+       }
+       fuse_reply_buf(state->freq, (char *)rcvbuf, received);
+       TALLOC_FREE(state);
+}
+
+struct ll_write_state {
+       struct mount_state *mstate;
+       fuse_req_t freq;
+};
+
+static void cli_ll_write_done(struct tevent_req *req);
+
+static void cli_ll_write(fuse_req_t freq, fuse_ino_t ino, const char *buf,
+                        size_t size, off_t off, struct fuse_file_info *fi)
+{
+       struct mount_state *mstate = talloc_get_type_abort(
+               fuse_req_userdata(freq), struct mount_state);
+       struct ll_write_state *state;
+       struct tevent_req *req;
+       uint16_t fnum;
+
+       DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino,
+                 size, (uintmax_t)off);
+
+       state = talloc(mstate, struct ll_write_state);
+       if (state == NULL) {
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       state->mstate = mstate;
+       state->freq = freq;
+
+       fnum = fi->fh;
+
+       req = cli_smb2_write_send(state, mstate->ev, mstate->cli, fnum, 0,
+                                 (const uint8_t *)buf, off, size);
+       if (req == NULL) {
+               TALLOC_FREE(state);
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       tevent_req_set_callback(req, cli_ll_write_done, state);
+}
+
+static void cli_ll_write_done(struct tevent_req *req)
+{
+       struct ll_write_state *state = tevent_req_callback_data(
+               req, struct ll_write_state);
+       size_t written;
+       NTSTATUS status;
+
+       status = cli_smb2_write_recv(req, &written);
+       /* no talloc_free here yet */
+       if (!NT_STATUS_IS_OK(status)) {
+               fuse_reply_err(state->freq, map_errno_from_nt_status(status));
+               return;
+       }
+       fuse_reply_write(state->freq, written);
+       TALLOC_FREE(state);
+}
+
+
+struct ll_dir_state {
+       uint64_t fid_persistent;
+       uint64_t fid_volatile;
+
+       struct file_info *finfos;
+       unsigned num_finfos, num_sent;
+};
+
+static bool ll_dir_state_add(struct ll_dir_state *dir_state,
+                            const char *name)
+{
+       struct file_info *tmp, *finfo;
+
+       tmp = talloc_realloc(dir_state, dir_state->finfos,
+                            struct file_info, dir_state->num_finfos+1);
+       if (tmp == NULL) {
+               return false;
+       }
+       dir_state->finfos = tmp;
+       finfo = &dir_state->finfos[dir_state->num_finfos];
+
+       ZERO_STRUCTP(finfo);
+
+       finfo->name = talloc_strdup(dir_state->finfos, name);
+       if (finfo->name == NULL) {
+               return false;
+       }
+       dir_state->num_finfos += 1;
+
+       return true;
+}
+
+struct ll_opendir_state {
+       struct mount_state *mstate;
+       fuse_req_t freq;
+       struct fuse_file_info fi;
+       struct ll_dir_state *dir_state;
+};
+
+static void cli_ll_opendir_done(struct tevent_req *req);
+
+static void cli_ll_opendir(fuse_req_t freq, fuse_ino_t ino,
+                          struct fuse_file_info *fi)
+{
+       struct mount_state *mstate = talloc_get_type_abort(
+               fuse_req_userdata(freq), struct mount_state);
+       struct cli_state *cli = mstate->cli;
+       struct ll_opendir_state *state;
+       struct inode_state *istate;
+       struct tevent_req *req;
+
+       DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
+
+       istate = idr_find(mstate->ino_ctx, ino);
+       if (istate == NULL) {
+               fuse_reply_err(freq, ENOENT);
+               return;
+       }
+
+       state = talloc(mstate, struct ll_opendir_state);
+       if (state == NULL) {
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       state->mstate = mstate;
+       state->freq = freq;
+       state->fi = *fi;
+
+       state->dir_state = talloc_zero(state, struct ll_dir_state);
+       if (state->dir_state == NULL) {
+               TALLOC_FREE(state);
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+
+       req = smb2cli_create_send(
+               state, mstate->ev, cli->conn, cli->timeout,
+               cli->smb2.session, cli->smb2.tcon, istate->path,
+               0, SMB2_IMPERSONATION_IMPERSONATION,
+               FILE_GENERIC_READ, FILE_ATTRIBUTE_DIRECTORY,
+               FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+               FILE_OPEN, FILE_DIRECTORY_FILE, NULL);
+       if (req == NULL) {
+               TALLOC_FREE(state);
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       tevent_req_set_callback(req, cli_ll_opendir_done, state);
+}
+
+static void cli_ll_opendir_done(struct tevent_req *req)
+{
+       struct ll_opendir_state *state = tevent_req_callback_data(
+               req, struct ll_opendir_state);
+       NTSTATUS status;
+
+       status = smb2cli_create_recv(req,
+                                    &state->dir_state->fid_persistent,
+                                    &state->dir_state->fid_volatile,
+                                    NULL, NULL, NULL);
+       TALLOC_FREE(req);
+
+       DEBUG(10, ("%s: smbcli_create_recv returned %s\n", __func__,
+                  nt_errstr(status)));
+
+       if (!NT_STATUS_IS_OK(status)) {
+               fuse_reply_err(state->freq, map_errno_from_nt_status(status));
+               return;
+       }
+
+       state->fi.fh = (uint64_t)talloc_move(state->mstate, &state->dir_state);
+       state->fi.direct_io = 0;
+       state->fi.keep_cache = 0;
+
+       fuse_reply_open(state->freq, &state->fi);
+
+       TALLOC_FREE(state);
+}
+
+struct ll_readdir_state {
+       fuse_req_t freq;
+       struct ll_dir_state *dir_state;
+};
+
+static void cli_ll_readdir_done(struct tevent_req *subreq);
+static NTSTATUS cli_ll_readdir_one(const char *mnt, struct file_info *finfo,
+                                  const char *path, void *private_data);
+static void cli_ll_readdir_reply_one(fuse_req_t freq,
+                                    struct ll_dir_state *dir_state);
+
+static void cli_ll_readdir(fuse_req_t freq, fuse_ino_t ino, size_t size,
+                          off_t off, struct fuse_file_info *fi)
+{
+       struct mount_state *mstate = talloc_get_type_abort(
+               fuse_req_userdata(freq), struct mount_state);
+       struct cli_state *cli = mstate->cli;
+       struct ll_dir_state *dir_state;
+       struct ll_readdir_state *state;
+       struct tevent_req *req;
+
+       DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino, size,
+                 (uintmax_t)off);
+
+       dir_state = talloc_get_type_abort((void *)fi->fh, struct ll_dir_state);
+
+       if (dir_state->finfos != NULL) {
+               DBG_DEBUG("finfos=%p\n", dir_state->finfos);
+               cli_ll_readdir_reply_one(freq, dir_state);
+               return;
+       }
+
+       if (!ll_dir_state_add(dir_state, ".") ||
+           !ll_dir_state_add(dir_state, "..")) {
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+
+       state = talloc(mstate, struct ll_readdir_state);
+       if (state == NULL) {
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       state->freq = freq;
+       state->dir_state = dir_state;
+
+       req = cli_smb2_listdir_send(
+               state, mstate->ev, cli->conn, cli->timeout,
+               cli->smb2.session, cli->smb2.tcon,
+               SMB2_FIND_ID_BOTH_DIRECTORY_INFO, 0, 0,
+               dir_state->fid_persistent, dir_state->fid_volatile,
+               "*", 0xffff,
+               FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM |
+               FILE_ATTRIBUTE_HIDDEN,
+               NULL, NULL, cli_ll_readdir_one, dir_state);
+       if (req == NULL) {
+               TALLOC_FREE(state);
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       tevent_req_set_callback(req, cli_ll_readdir_done, state);
+}
+
+static void cli_ll_readdir_reply_one(fuse_req_t freq,
+                                    struct ll_dir_state *dir_state)
+{
+       struct file_info *finfo;
+       char buf[1024];
+       struct stat sbuf = {};
+       size_t buflen;
+
+       if (dir_state->num_finfos == dir_state->num_sent) {
+               DEBUG(10, ("%s: Done\n", __func__));
+               fuse_reply_buf(freq, NULL, 0);
+               return;
+       }
+
+       sbuf.st_mode = S_IFREG | 0755;
+       sbuf.st_ino = random(); /* FIXME :-) */
+       finfo = &dir_state->finfos[dir_state->num_sent];
+
+       DBG_DEBUG("Adding %s\n", finfo->name);
+
+       buflen = fuse_add_direntry(freq, buf, sizeof(buf),
+                                  finfo->name, &sbuf, 0);
+       fuse_reply_buf(freq, buf, buflen);
+       dir_state->num_sent += 1;
+       return;
+}
+
+static NTSTATUS cli_ll_readdir_one(const char *mnt, struct file_info *finfo,
+                                  const char *path, void *private_data)
+{
+       struct ll_dir_state *dir_state = talloc_get_type_abort(
+               private_data, struct ll_dir_state);
+
+       if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
+               DEBUG(10, ("%s: Ignoring %s\n", __func__, finfo->name));
+               return NT_STATUS_OK;
+       }
+
+       DBG_DEBUG("Got entry %s\n", finfo->name);
+
+       if (!ll_dir_state_add(dir_state, finfo->name)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       return NT_STATUS_OK;
+}
+
+static void cli_ll_readdir_done(struct tevent_req *req)
+{
+       struct ll_readdir_state *state = tevent_req_callback_data(
+               req, struct ll_readdir_state);
+       NTSTATUS status;
+
+       status = cli_smb2_listdir_recv(req);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               fuse_reply_err(state->freq, map_errno_from_nt_status(status));
+               return;
+       }
+       cli_ll_readdir_reply_one(state->freq, state->dir_state);
+       TALLOC_FREE(state);
+}
+
+
+struct ll_releasedir_state {
+       struct mount_state *mstate;
+       fuse_req_t freq;
+       struct ll_dir_state *dir_state;
+};
+
+static void cli_ll_releasedir_done(struct tevent_req *req);
+
+static void cli_ll_releasedir(fuse_req_t freq, fuse_ino_t ino,
+                             struct fuse_file_info *fi)
+{
+       struct mount_state *mstate = talloc_get_type_abort(
+               fuse_req_userdata(freq), struct mount_state);
+       struct cli_state *cli = mstate->cli;
+       struct ll_releasedir_state *state;
+       struct tevent_req *req;
+
+       DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
+
+       state = talloc(mstate, struct ll_releasedir_state);
+       if (state == NULL) {
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       state->mstate = mstate;
+       state->freq = freq;
+
+       state->dir_state = talloc_get_type_abort(
+               (void *)fi->fh, struct ll_dir_state);
+
+       req = smb2cli_close_send(state, mstate->ev, cli->conn, cli->timeout,
+                                cli->smb2.session, cli->smb2.tcon, 0,
+                                state->dir_state->fid_persistent,
+                                state->dir_state->fid_volatile);
+       if (req == NULL) {
+               TALLOC_FREE(state);
+               fuse_reply_err(freq, ENOMEM);
+               return;
+       }
+       tevent_req_set_callback(req, cli_ll_releasedir_done, state);
+}
+
+static void cli_ll_releasedir_done(struct tevent_req *req)
+{
+       struct ll_releasedir_state *state = tevent_req_callback_data(
+               req, struct ll_releasedir_state);
+       NTSTATUS status;
+
+       status = smb2cli_close_recv(req);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               fuse_reply_err(state->freq, map_errno_from_nt_status(status));
+               return;
+       }
+       TALLOC_FREE(state->dir_state);
+       fuse_reply_err(state->freq, 0);
+       TALLOC_FREE(state);
+}
+
+static struct fuse_lowlevel_ops cli_ll_ops = {
+       .lookup = cli_ll_lookup,
+       .getattr = cli_ll_getattr,
+       .setattr = cli_ll_setattr,
+       .open = cli_ll_open,
+       .create = cli_ll_create,
+       .release = cli_ll_release,
+       .read = cli_ll_read,
+       .write = cli_ll_write,
+       .opendir = cli_ll_opendir,
+       .readdir = cli_ll_readdir,
+       .releasedir = cli_ll_releasedir,
+};
+
+static void fuse_chan_fd_handler(struct tevent_context *ev,
+                                struct tevent_fd *fde,
+                                uint16_t flags,
+                                void *private_data);
+static void fuse_chan_signal_handler(struct tevent_context *ev,
+                                    struct tevent_signal *se,
+                                    int signum,
+                                    int count,
+                                    void *siginfo,
+                                    void *private_data);
+
+static int mount_state_destructor(struct mount_state *s);
+
+int do_mount(struct cli_state *cli, const char *mountpoint)
+{
+       struct mount_state *state;
+       struct inode_state *ino;
+       struct fuse_args args = { 0 };
+       int fd;
+       int ret = 1;
+
+       state = talloc_zero(talloc_tos(), struct mount_state);
+       if (state == NULL) {
+               fprintf(stderr, "talloc failed\n");
+               return 1;
+       }
+
+       state->ev = tevent_context_init(state);
+       if (state->ev == NULL) {
+               fprintf(stderr, "tevent_context_init failed\n");
+               TALLOC_FREE(state);
+               return 1;
+       }
+
+       state->ino_ctx = idr_init(state);
+       if (state->ino_ctx == NULL) {
+               fprintf(stderr, "idr_init failed\n");
+               TALLOC_FREE(state);
+               return 1;
+       }
+
+       state->ino_parent = talloc_new(state);
+       if (state->ino_parent == NULL) {
+               fprintf(stderr, "talloc_new failed\n");
+               TALLOC_FREE(state);
+               return 1;
+       }
+
+       talloc_set_destructor(state, mount_state_destructor);
+
+       ino = inode_state_new(state, "");
+       if (ino == NULL) {
+               fprintf(stderr, "inode_state_new failed\n");
+               TALLOC_FREE(state);
+               return 1;
+       }
+       if (ino->ino != FUSE_ROOT_ID) {
+               fprintf(stderr, "first inode gave %d, expected %d\n",
+                       (int)ino->ino, FUSE_ROOT_ID);
+               TALLOC_FREE(state);
+               return 1;
+       }
+
+       state->cli = cli;
+
+       state->ch = fuse_mount(mountpoint, &args);
+       if (state->ch == NULL) {
+               perror("fuse_mount failed");
+               goto fail_free;
+       }
+
+       state->bufsize = fuse_chan_bufsize(state->ch);
+       state->buf = talloc_array(state, char, state->bufsize);
+       if (state->buf == NULL) {
+               fprintf(stderr, "talloc failed\n");
+               goto fail_unmount;
+       }
+
+       fd = fuse_chan_fd(state->ch);
+
+       state->fde = tevent_add_fd(state->ev, state, fd, TEVENT_FD_READ,
+                                  fuse_chan_fd_handler, state);
+       if (state->fde == NULL) {
+               fprintf(stderr, "tevent_add_fd failed\n");
+               goto fail_unmount;
+       }
+
+       state->signal_ev = tevent_add_signal(state->ev, state, SIGINT, 0,
+                                            fuse_chan_signal_handler, state);
+       if (state->signal_ev == NULL) {
+               fprintf(stderr, "tevent_add_signal failed\n");
+               goto fail_unmount;
+       }
+
+       state->se = fuse_lowlevel_new(&args, &cli_ll_ops, sizeof(cli_ll_ops),
+                                     state);
+       if (state->se == NULL) {
+               perror("fuse_lowlevel_new failed");
+               goto fail_unmount;
+       }
+
+       fuse_session_add_chan(state->se, state->ch);
+
+       while (!state->done) {
+               ret = tevent_loop_once(state->ev);
+               if (ret == -1) {
+                       perror("tevent_loop_once failed");
+                       break;
+               }
+       }
+
+       fuse_session_remove_chan(state->ch);
+       fuse_session_destroy(state->se);
+fail_unmount:
+       fuse_unmount(mountpoint, state->ch);
+fail_free:
+       TALLOC_FREE(state);
+       return ret;
+}
+
+static int mount_state_destructor(struct mount_state *s)
+{
+       TALLOC_FREE(s->ino_parent);
+       return 0;
+}
+
+
+static void fuse_chan_fd_handler(struct tevent_context *ev,
+                                struct tevent_fd *fde,
+                                uint16_t flags,
+                                void *private_data)
+{
+       struct mount_state *state = talloc_get_type_abort(
+               private_data, struct mount_state);
+       int recvd;
+
+       if ((flags & TEVENT_FD_READ) == 0) {
+               return;
+       }
+
+       recvd = fuse_chan_recv(&state->ch, state->buf, state->bufsize);
+       if (recvd <= 0) {
+               state->done = true;
+               return;
+       }
+       fuse_session_process(state->se, state->buf, recvd, state->ch);
+}
+
+static void fuse_chan_signal_handler(struct tevent_context *ev,
+                                    struct tevent_signal *se,
+                                    int signum,
+                                    int count,
+                                    void *siginfo,
+                                    void *private_data)
+{
+       struct mount_state *state = talloc_get_type_abort(
+               private_data, struct mount_state);
+       state->done = true;
+}
diff --git a/source3/librpc/idl/smbunix.idl b/source3/librpc/idl/smbunix.idl
new file mode 100644 (file)
index 0000000..26b3fcd
--- /dev/null
@@ -0,0 +1,112 @@
+#include "idl_types.h"
+
+/*
+   IDL structures for messaging code
+*/
+
+[
+  pointer_default(unique)
+]
+interface smbunix
+{
+       typedef enum {
+               SMBUNIX_FILE_TYPE_REG = 0,
+               SMBUNIX_FILE_TYPE_DIR = 1,
+               SMBUNIX_FILE_TYPE_LNK = 2,
+               SMBUNIX_FILE_TYPE_CHR = 3,
+               SMBUNIX_FILE_TYPE_BLK = 4,
+               SMBUNIX_FILE_TYPE_FIFO = 5,
+               SMBUNIX_FILE_TYPE_SOCK = 6
+       } smbunix_file_type;
+
+       typedef struct {
+           hyper uid;
+           hyper gid;
+           smbunix_file_type type;
+           hyper dev_major;
+           hyper dev_minor;
+           hyper unique_id;
+           hyper permissions;
+           hyper nlinks;
+       } smbunix_file_info_basic;
+
+       typedef struct {
+           hyper end_of_file;
+           hyper blocks;
+           hyper change_time;
+           hyper last_access_time;
+           hyper last_modification_time;
+           hyper uid;
+           hyper gid;
+           smbunix_file_type type;
+           hyper dev_major;
+           hyper dev_minor;
+           hyper unique_id;
+           hyper permissions;
+           hyper nlinks;
+           hyper creation_time;
+           uint32 file_flags;
+           uint32 file_flags_mask;
+       } smbunix_file_info2;
+
+       typedef struct {
+           [string,charset(UTF8)] char *target;
+       } smbunix_file_info_link;
+
+       typedef [v1_enum] enum {
+               SMBUNIX_FILE_INFO_NONE = 0,
+               SMBUNIX_FILE_INFO_BASIC = 1,
+               SMBUNIX_FILE_INFO2 = 2,
+               SMBUNIX_FILE_INFO_LINK = 3
+       } smbunix_file_info_level;
+
+       typedef union {
+               [case(SMBUNIX_FILE_INFO_NONE)] hyper *dummy;
+               [case(SMBUNIX_FILE_INFO_BASIC)] smbunix_file_info_basic basic;
+               [case(SMBUNIX_FILE_INFO2)] smbunix_file_info2 info2;
+               [case(SMBUNIX_FILE_INFO_LINK)] smbunix_file_info_link link;
+       } smbunix_file_info_union;
+
+       typedef [public] struct {
+               smbunix_file_info_level level;
+               [switch_is(level)] smbunix_file_info_union info;
+       } smbunix_file_info;
+
+       typedef [v1_enum] enum {
+               SMBUNIX_CMD_GETINFO = 0,
+               SMBUNIX_CMD_SETINFO = 1
+       } smbunix_cmd;
+
+       typedef struct {
+               smbunix_file_info_level level;
+       } smbunix_cmd_getinfo_req_data;
+
+       typedef struct {
+               uint32 to_set;
+               smbunix_file_info2 info;
+       } smbunix_cmd_setinfo_req_data;
+
+       typedef union {
+               [case(SMBUNIX_CMD_GETINFO)] smbunix_cmd_getinfo_req_data getinfo;
+               [case(SMBUNIX_CMD_SETINFO)] smbunix_cmd_setinfo_req_data setinfo;
+       } smbunix_cmd_req_data;
+
+       typedef struct {
+               smbunix_file_info info;
+       } smbunix_cmd_getinfo_res_data;
+
+       typedef union {
+               [case(SMBUNIX_CMD_GETINFO)] smbunix_cmd_getinfo_req_data getinfo;
+               [case(SMBUNIX_CMD_SETINFO)] hyper *dummy;
+       } smbunix_cmd_res_data;
+
+       typedef [public] struct {
+               smbunix_cmd cmd;
+               [switch_is(cmd)] smbunix_cmd_req_data data;
+       } smbunix_req;
+
+       typedef [public] struct {
+               smbunix_cmd cmd;
+               [switch_is(cmd)] smbunix_cmd_res_data data;
+       } smbunix_res;
+}
index 183226e5f9bc299dfe8b6f8691a8cd0f9f10f492..80be7e8b0bafd21c8ab12afd827ce907822878b9 100644 (file)
@@ -7,6 +7,7 @@ topinclude=os.path.join(bld.srcnode.abspath(), 'librpc/idl')
 bld.SAMBA_PIDL_LIST('PIDL',
                     '''libnetapi.idl open_files.idl
                        perfcount.idl secrets.idl libnet_join.idl
+                       smbunix.idl
                        smbXsrv.idl
                        leases_db.idl
                     ''',
index 2445859c1ab05c5f932736349cb2d60a2f3e9aa1..dd714d7bfa0ea9a0fd04dd7efc8045dea0980168 100644 (file)
@@ -15,6 +15,11 @@ bld.SAMBA3_SUBSYSTEM('NDR_OPEN_FILES',
        public_deps='ndr NDR_SERVER_ID NDR_FILE_ID NDR_SECURITY NDR_SMB2_LEASE_STRUCT'
        )
 
+bld.SAMBA3_SUBSYSTEM('NDR_SMBUNIX',
+       source='gen_ndr/ndr_smbunix.c',
+       public_deps='ndr'
+       )
+
 bld.SAMBA3_SUBSYSTEM('NDR_SMBXSRV',
        source='gen_ndr/ndr_smbXsrv.c',
        public_deps='ndr NDR_SERVER_ID NDR_SECURITY NDR_AUTH'
index 816ad1374a30928953a04f5484f8e0436e4d809a..dfddae4239006edd02febe793791b9ad6c999e8b 100644 (file)
@@ -646,6 +646,248 @@ static bool windows_parent_dirname(TALLOC_CTX *mem_ctx,
        return true;
 }
 
+struct cli_smb2_listdir_state {
+       struct tevent_context *ev;
+       struct smbXcli_conn *conn;
+       uint32_t timeout_msec;
+       struct smbXcli_session *session;
+       struct smbXcli_tcon *tcon;
+       uint8_t level;
+       uint8_t flags;
+       uint32_t file_index;
+       uint64_t fid_persistent;
+       uint64_t fid_volatile;
+       const char *mask;
+       uint32_t outbuf_len;
+
+       uint16_t attribute;
+       const char *mntpoint;
+       const char *pathname;
+       NTSTATUS (*fn)(const char *mntpoint, struct file_info *f,
+                      const char *mask, void *private_data);
+       void *private_data;
+       bool processed_file;
+};
+
+static void cli_smb2_listdir_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_listdir_send(
+       TALLOC_CTX *mem_ctx,
+       struct tevent_context *ev,
+       struct smbXcli_conn *conn,
+       uint32_t timeout_msec,
+       struct smbXcli_session *session,
+       struct smbXcli_tcon *tcon,
+       uint8_t level,
+       uint8_t flags,
+       uint32_t file_index,
+       uint64_t fid_persistent,
+       uint64_t fid_volatile,
+       const char *mask,
+       uint32_t outbuf_len,
+       uint16_t attribute,
+       const char *mntpoint,
+       const char *pathname,
+       NTSTATUS (*fn)(const char *mntpoint, struct file_info *f,
+                      const char *mask, void *private_data),
+       void *private_data)
+{
+       struct tevent_req *req, *subreq;
+       struct cli_smb2_listdir_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_smb2_listdir_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->conn = conn;
+       state->timeout_msec = timeout_msec;
+       state->session = session;
+       state->tcon = tcon;
+       state->level = level;
+       state->flags = flags;
+       state->file_index = file_index;
+       state->fid_persistent = fid_persistent;
+       state->fid_volatile = fid_volatile;
+       state->mask = mask;
+       state->outbuf_len = outbuf_len;
+       state->attribute = attribute;
+       state->mntpoint = mntpoint;
+       state->pathname = pathname;
+       state->fn = fn;
+       state->private_data = private_data;
+
+       subreq = smb2cli_query_directory_send(
+               state, state->ev, state->conn, state->timeout_msec,
+               state->session, state->tcon, state->level,
+               state->flags, state->file_index,
+               state->fid_persistent, state->fid_volatile,
+               state->mask, state->outbuf_len);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_smb2_listdir_done, req);
+       return req;
+}
+
+static void cli_smb2_listdir_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_smb2_listdir_state *state = tevent_req_data(
+               req, struct cli_smb2_listdir_state);
+       uint8_t *data;
+       uint32_t data_len;
+       uint32_t next_offset = 0;
+       NTSTATUS status;
+
+       status = smb2cli_query_directory_recv(subreq, state, &data,
+                                             &data_len);
+       TALLOC_FREE(subreq);
+       if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
+               tevent_req_done(req);
+               return;
+       }
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       do {
+               struct file_info *finfo;
+               bool ok;
+
+               finfo = talloc_zero(state, struct file_info);
+               if (tevent_req_nomem(finfo, req)) {
+                       return;
+               }
+
+               status = parse_finfo_id_both_directory_info(
+                       data, data_len, finfo, &next_offset);
+
+               DEBUG(10, ("%s: parse_finfo_id_both_directory_info returned "
+                          "%s\n", __func__, nt_errstr(status)));
+
+               if (tevent_req_nterror(req, status)) {
+                       return;
+               }
+
+               ok = dir_check_ftype(finfo->mode, state->attribute);
+
+               DEBUG(10, ("%s: dir_check_ftype(%u,%u) returned %u\n",
+                          __func__, (unsigned)finfo->mode,
+                          (unsigned)state->attribute, (unsigned)ok));
+
+               if (ok) {
+                       /*
+                        * Only process if attributes match. On SMB1 server
+                        * does this, so on SMB2 we need to emulate in the
+                        * client.
+                        *
+                        * https://bugzilla.samba.org/show_bug.cgi?id=10260
+                        */
+                       state->processed_file = true;
+
+                       status = state->fn(state->mntpoint, finfo,
+                                          state->pathname,
+                                          state->private_data);
+                       if (tevent_req_nterror(req, status)) {
+                               return;
+                       }
+               }
+
+               TALLOC_FREE(finfo);
+
+               if (next_offset != 0) {
+                       data += next_offset;
+                       data_len -= next_offset;
+               }
+       } while (next_offset != 0);
+
+       subreq = smb2cli_query_directory_send(
+               state, state->ev, state->conn, state->timeout_msec,
+               state->session, state->tcon, state->level,
+               state->flags, state->file_index,
+               state->fid_persistent, state->fid_volatile,
+               state->mask, state->outbuf_len);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, cli_smb2_listdir_done, req);
+}
+
+NTSTATUS cli_smb2_listdir_recv(struct tevent_req *req)
+{
+       struct cli_smb2_listdir_state *state = tevent_req_data(
+               req, struct cli_smb2_listdir_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+
+       if (!state->processed_file) {
+               /*
+                * In SMB1 findfirst returns NT_STATUS_NO_SUCH_FILE
+                * if no files match. Emulate this in the client.
+                */
+               return NT_STATUS_NO_SUCH_FILE;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS cli_smb2_listdir(
+       struct smbXcli_conn *conn,
+       uint32_t timeout_msec,
+       struct smbXcli_session *session,
+       struct smbXcli_tcon *tcon,
+       uint8_t level,
+       uint8_t flags,
+       uint32_t file_index,
+       uint64_t fid_persistent,
+       uint64_t fid_volatile,
+       const char *mask,
+       uint32_t outbuf_len,
+       uint16_t attribute,
+       const char *mntpoint,
+       const char *pathname,
+       NTSTATUS (*fn)(const char *mntpoint, struct file_info *f,
+                      const char *mask, void *private_data),
+       void *private_data)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       if (smbXcli_conn_has_async_calls(conn)) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = cli_smb2_listdir_send(
+               frame, ev, conn, timeout_msec, session, tcon, level, flags,
+               file_index, fid_persistent, fid_volatile, mask, outbuf_len,
+               attribute, mntpoint, pathname, fn, private_data);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cli_smb2_listdir_recv(req);
+ fail:
+       TALLOC_FREE(frame);
+       return status;
+}
+
 /***************************************************************
  Wrapper that allows SMB2 to list a directory.
  Synchronous only.
index c97bc76786ab13c149e7de4669e95193d9ecc1ab..19ea2046272e86198c0e4152ac4942a854f14bd5 100644 (file)
@@ -57,6 +57,45 @@ NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum);
 NTSTATUS cli_smb2_mkdir(struct cli_state *cli, const char *dirname);
 NTSTATUS cli_smb2_rmdir(struct cli_state *cli, const char *dirname);
 NTSTATUS cli_smb2_unlink(struct cli_state *cli,const char *fname);
+struct tevent_req *cli_smb2_listdir_send(
+       TALLOC_CTX *mem_ctx,
+       struct tevent_context *ev,
+       struct smbXcli_conn *conn,
+       uint32_t timeout_msec,
+       struct smbXcli_session *session,
+       struct smbXcli_tcon *tcon,
+       uint8_t level,
+       uint8_t flags,
+       uint32_t file_index,
+       uint64_t fid_persistent,
+       uint64_t fid_volatile,
+       const char *mask,
+       uint32_t outbuf_len,
+       uint16_t attribute,
+       const char *mntpoint,
+       const char *pathname,
+       NTSTATUS (*fn)(const char *mntpoint, struct file_info *f,
+                      const char *mask, void *private_data),
+       void *private_data);
+NTSTATUS cli_smb2_listdir_recv(struct tevent_req *req);
+NTSTATUS cli_smb2_listdir(
+       struct smbXcli_conn *conn,
+       uint32_t timeout_msec,
+       struct smbXcli_session *session,
+       struct smbXcli_tcon *tcon,
+       uint8_t level,
+       uint8_t flags,
+       uint32_t file_index,
+       uint64_t fid_persistent,
+       uint64_t fid_volatile,
+       const char *mask,
+       uint32_t outbuf_len,
+       uint16_t attribute,
+       const char *mntpoint,
+       const char *pathname,
+       NTSTATUS (*fn)(const char *mntpoint, struct file_info *f,
+                      const char *mask, void *private_data),
+       void *private_data);
 NTSTATUS cli_smb2_list(struct cli_state *cli,
                        const char *pathname,
                        uint16_t attribute,
diff --git a/source3/modules/vfs_smb2posix.c b/source3/modules/vfs_smb2posix.c
new file mode 100644 (file)
index 0000000..99fe2d1
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * VFS module to intercept smb2 posix extensions
+ *
+ * Copyright (C) 2015 Volker Lendecke
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "smbd/smbd.h"
+#include "system/filesys.h"
+#include "../librpc/gen_ndr/ndr_smbunix.h"
+
+#define FUSE_USE_VERSION 26
+#define _FILE_OFFSET_BITS 64
+#include <fuse/fuse_lowlevel.h>
+
+static uint32_t mode2smbunix_type(mode_t m)
+{
+       if (S_ISREG(m)) {
+               return SMBUNIX_FILE_TYPE_REG;
+       }
+       if (S_ISDIR(m)) {
+               return SMBUNIX_FILE_TYPE_DIR;
+       }
+       if (S_ISLNK(m)) {
+               return SMBUNIX_FILE_TYPE_LNK;
+       }
+       if (S_ISCHR(m)) {
+               return SMBUNIX_FILE_TYPE_CHR;
+       }
+       if (S_ISBLK(m)) {
+               return SMBUNIX_FILE_TYPE_BLK;
+       }
+       if (S_ISFIFO(m)) {
+               return SMBUNIX_FILE_TYPE_FIFO;
+       }
+       if (S_ISSOCK(m)) {
+               return SMBUNIX_FILE_TYPE_SOCK;
+       }
+       return UINT32_MAX;
+}
+
+static dev_t stat2devno(const struct stat_ex *pst)
+{
+       if (S_ISBLK(pst->st_ex_mode) || S_ISCHR(pst->st_ex_mode)) {
+               return pst->st_ex_rdev;
+       }
+       return pst->st_ex_dev;
+}
+
+static struct smbunix_file_info_basic stat2info_basic(
+       struct connection_struct *conn, const struct stat_ex *pst)
+{
+       dev_t devno = stat2devno(pst);
+
+       return (struct smbunix_file_info_basic) {
+               .uid = pst->st_ex_uid,
+               .gid = pst->st_ex_gid,
+               .type = mode2smbunix_type(pst->st_ex_mode),
+               .dev_major = unix_dev_major(devno),
+               .dev_minor = unix_dev_minor(devno),
+               .unique_id = get_FileIndex(conn, pst),
+               .permissions = unix_perms_to_wire(pst->st_ex_mode),
+               .nlinks = pst->st_ex_nlink,
+       };
+}
+
+static NTSTATUS smb2posix_create_file(
+       vfs_handle_struct *handle, struct smb_request *req,
+       uint16_t root_dir_fid, struct smb_filename *smb_fname,
+       uint32_t access_mask, uint32_t share_access,
+       uint32_t create_disposition, uint32_t create_options,
+       uint32_t file_attributes, uint32_t oplock_request,
+       struct smb2_lease *lease, uint64_t allocation_size,
+       uint32_t private_flags, struct security_descriptor *sd,
+       struct ea_list *ea_list, files_struct **result, int *pinfo,
+       const struct smb2_create_blobs *in_context_blobs,
+       struct smb2_create_blobs *out_context_blobs)
+{
+       struct smb2_create_blob *smb2posix;
+       struct smbunix_req *unix_req;
+       struct smbunix_file_info unix_info;
+       DATA_BLOB blob;
+       enum ndr_err_code ndr_err;
+       NTSTATUS status;
+
+       smb2posix = smb2_create_blob_find(in_context_blobs,
+                                         SMB2_CREATE_TAG_POSIXEXT);
+       DBG_DEBUG("smb2posix=%p\n", smb2posix);
+       if (smb2posix == NULL) {
+               status = SMB_VFS_NEXT_CREATE_FILE(
+                       handle, req, root_dir_fid, smb_fname,
+                       access_mask, share_access,
+                       create_disposition, create_options,
+                       file_attributes, oplock_request,
+                       lease,
+                       allocation_size, private_flags,
+                       sd, ea_list, result,
+                       pinfo, in_context_blobs, out_context_blobs);
+               return status;
+       }
+
+       unix_req = talloc(talloc_tos(), struct smbunix_req);
+       if (unix_req == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       ndr_err = ndr_pull_struct_blob_all(
+               &smb2posix->data, unix_req, unix_req,
+               (ndr_pull_flags_fn_t)ndr_pull_smbunix_req);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               TALLOC_FREE(unix_req);
+               return ndr_map_error2ntstatus(ndr_err);
+       }
+
+       if (DEBUGLEVEL >= DBGLVL_DEBUG) {
+               NDR_PRINT_DEBUG(smbunix_req, unix_req);
+       }
+
+       if (unix_req->cmd == SMBUNIX_CMD_SETINFO) {
+               uint32_t to_set;
+               struct smbunix_file_info2 *info2;
+
+               to_set = unix_req->data.setinfo.to_set;
+               info2 = &unix_req->data.setinfo.info;
+
+               if (to_set & FUSE_SET_ATTR_UID) {
+                       DBG_DEBUG("Setting uid to %ju\n",
+                                 (uintmax_t)info2->uid);
+               }
+               if (to_set & FUSE_SET_ATTR_GID) {
+                       DBG_DEBUG("Setting gid to %ju\n",
+                                 (uintmax_t)info2->gid);
+               }
+       }
+
+       status = SMB_VFS_NEXT_CREATE_FILE(
+               handle, req, root_dir_fid, smb_fname,
+               access_mask, share_access,
+               create_disposition, create_options,
+               file_attributes, oplock_request,
+               lease,
+               allocation_size, private_flags,
+               sd, ea_list, result,
+               pinfo, in_context_blobs, out_context_blobs);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       unix_info = (struct smbunix_file_info) {
+               .level = SMBUNIX_FILE_INFO_BASIC,
+               .info.basic = stat2info_basic(handle->conn,
+                                             &((*result)->fsp_name->st))
+       };
+
+       ndr_err = ndr_push_struct_blob(
+               &blob, talloc_tos(), &unix_info,
+               (ndr_push_flags_fn_t)
+               ndr_push_smbunix_file_info);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               DBG_DEBUG("ndr_push_smbunix_file_info failed: %s\n",
+                         ndr_errstr(ndr_err));
+
+               /*
+                * NEXT_CREATE_FILE had succeeded. Just ignoring the posix
+                * request for now. I have to look up how to close a fsp from
+                * here.
+                */
+               return NT_STATUS_OK;
+       }
+
+       status = smb2_create_blob_add(out_context_blobs, out_context_blobs,
+                                     SMB2_CREATE_TAG_POSIXEXT, blob);
+       TALLOC_FREE(blob.data);
+       if (!NT_STATUS_IS_OK(status)) {
+               /*
+                * See above. How do we close the fsp here?
+                */
+               return NT_STATUS_OK;
+       }
+
+       return NT_STATUS_OK;
+}
+
+static struct vfs_fn_pointers vfs_smb2posix_fns = {
+       .create_file_fn = smb2posix_create_file
+};
+
+NTSTATUS vfs_smb2posix_init(void);
+NTSTATUS vfs_smb2posix_init(void)
+{
+       NTSTATUS ret;
+       ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "smb2posix",
+                              &vfs_smb2posix_fns);
+       return ret;
+}
index 4dc66530373d7732da5a1aae7254bbdc0e99a1fe..0e4a7b3308ac652e65ae9021a5bfd9e748bdaec9 100644 (file)
@@ -89,6 +89,14 @@ bld.SAMBA3_MODULE('vfs_fruit',
                  internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_fruit'),
                  enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_fruit'))
 
+bld.SAMBA3_MODULE('vfs_smb2posix',
+                 subsystem='vfs',
+                 source='vfs_smb2posix.c',
+                 deps='samba-util',
+                 init_function='',
+                 internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_smb2posix'),
+                 enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_smb2posix'))
+
 bld.SAMBA3_MODULE('vfs_default_quota',
                  subsystem='vfs',
                  source='vfs_default_quota.c',
index 8741d192ea915406467d4b6faf3350dfac63997b..4bf534885fa6822ca20d86128518afe1782561a8 100644 (file)
@@ -135,6 +135,15 @@ long ret = splice(0,0,1,0,400,SPLICE_F_MOVE);
         headers='fcntl.h'):
         conf.CHECK_DECLS('splice', reverse=True, headers='fcntl.h')
 
+    # Check for fuse support
+    if conf.CHECK_CODE('''
+#define FUSE_USE_VERSION 26
+#define _FILE_OFFSET_BITS 64
+#include "fuse/fuse_lowlevel.h"
+int main(void) { return 0; }
+''', 'HAVE_FUSE_FUSE_LOWLEVEL_H', addmain=False, execute=False) and conf.CHECK_FUNCS_IN('fuse_mount', 'fuse'):
+       conf.DEFINE('HAVE_FUSE', 1)
+
     # Check for inotify support
     conf.CHECK_HEADERS('sys/inotify.h')
     if "HAVE_SYS_INOTIFY_H" in conf.env:
@@ -1578,6 +1587,7 @@ main() {
                                       vfs_media_harmony vfs_unityed_media vfs_fruit vfs_shell_snap
                                       vfs_commit vfs_worm vfs_crossrename vfs_linux_xfs_sgid
                                       vfs_time_audit vfs_offline
+                                      vfs_smb2posix
                                   '''))
     default_shared_modules.extend(TO_LIST('auth_script idmap_tdb2 idmap_script'))
     # these have broken dependencies
index d40dd7edd3f9e450b2e84cc130fba053a040c4ef..6e887a40637db3f4123bbe8eacca166370033cd0 100755 (executable)
@@ -632,6 +632,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
                    LIBASYS
                    sysquotas
                    NDR_SMB_ACL
+                   NDR_SMBUNIX
                    netapi
                    NDR_IOCTL
                    notifyd
@@ -1058,22 +1059,30 @@ bld.SAMBA3_BINARY('rpcclient/rpcclient',
                  RPC_NDR_CLUSAPI
                  ''')
 
+smbclient_source='''client/client.c
+                    client/clitar.c
+                    client/dnsbrowse.c'''
+
+smbclient_deps='''talloc
+                  popt_samba3
+                  param
+                  ndr-standard
+                  SMBREADLINE
+                  libsmb
+                  msrpc3
+                  RPC_NDR_SRVSVC
+                  NDR_SMBUNIX
+                  cli_smb_common
+                  archive
+                  '''
+
+if bld.env.HAVE_FUSE:
+    smbclient_source=smbclient_source + ' client/clifuse.c'
+    smbclient_deps=smbclient_deps + ' fuse'
+
 bld.SAMBA3_BINARY('client/smbclient',
-                 source='''client/client.c
-                 client/clitar.c
-                 client/dnsbrowse.c''',
-                 deps='''
-                 talloc
-                 popt_samba3
-                 param
-                 ndr-standard
-                 SMBREADLINE
-                 libsmb
-                 msrpc3
-                 RPC_NDR_SRVSVC
-                 cli_smb_common
-                archive
-                 ''')
+                 source=smbclient_source,
+                 deps=smbclient_deps)
 
 bld.SAMBA3_BINARY('net',
                  source='''utils/net.c