diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /examples/fuse | |
parent | Initial commit. (diff) | |
download | samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip |
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'examples/fuse')
-rw-r--r-- | examples/fuse/README | 7 | ||||
-rw-r--r-- | examples/fuse/clifuse.c | 1591 | ||||
-rw-r--r-- | examples/fuse/clifuse.h | 27 | ||||
-rw-r--r-- | examples/fuse/smb2mount.c | 160 | ||||
-rw-r--r-- | examples/fuse/wscript | 14 | ||||
-rw-r--r-- | examples/fuse/wscript_build | 7 |
6 files changed, 1806 insertions, 0 deletions
diff --git a/examples/fuse/README b/examples/fuse/README new file mode 100644 index 0000000..a1f311d --- /dev/null +++ b/examples/fuse/README @@ -0,0 +1,7 @@ +WARNING: + +This is experimental work-in-progress code, don't expect it to do +anything sensible. + +Eventually this *might* turn into a fuse client filesystem. This is +not a promise for anything. diff --git a/examples/fuse/clifuse.c b/examples/fuse/clifuse.c new file mode 100644 index 0000000..59af160 --- /dev/null +++ b/examples/fuse/clifuse.c @@ -0,0 +1,1591 @@ +/* + * Unix SMB/CIFS implementation. + * fusermount smb2 client + * Copyright (C) Volker Lendecke 2016 + * + * 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 "source3/include/includes.h" +#include "client.h" +#include "trans2.h" +#include "libsmb/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 "clifuse.h" +#include "lib/util/idtree.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[]; +}; + +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; +} + +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, + (struct cli_smb2_create_flags){0}, + SMB2_IMPERSONATION_IMPERSONATION, + FILE_GENERIC_READ|FILE_GENERIC_WRITE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + FILE_CREATE, + FILE_NON_DIRECTORY_FILE, + NULL); + 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, NULL, NULL, 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 cli_get_unixattr_state { + struct tevent_context *ev; + struct cli_state *cli; + uint64_t fid_persistent; + uint64_t fid_volatile; + + struct timespec create_time; + struct timespec access_time; + struct timespec write_time; + struct timespec change_time; + uint32_t mode; + uint64_t ino; + uint64_t size; +}; + +static void cli_get_unixattr_opened(struct tevent_req *subreq); +static void cli_get_unixattr_gotinfo(struct tevent_req *subreq); +static void cli_get_unixattr_closed(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->ev = ev; + state->cli = cli; + + subreq = smb2cli_create_send( + state, ev, cli->conn, cli->timeout, cli->smb2.session, + cli->smb2.tcon, path, SMB2_OPLOCK_LEVEL_NONE, + SMB2_IMPERSONATION_IMPERSONATION, + SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + FILE_OPEN, 0, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_get_unixattr_opened, req); + + return req; +} + +static void cli_get_unixattr_opened(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 cli_state *cli = state->cli; + NTSTATUS status; + + status = smb2cli_create_recv( + subreq, + &state->fid_persistent, + &state->fid_volatile, + NULL, + NULL, + NULL, + NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + DBG_DEBUG("smb2cli_create_recv returned %s\n", + nt_errstr(status)); + return; + } + + subreq = smb2cli_query_info_send( + state, + state->ev, + cli->conn, + 0, + cli->smb2.session, + cli->smb2.tcon, + 1, /* in_info_type */ + FSCC_FILE_ALL_INFORMATION, /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + state->fid_persistent, + state->fid_volatile); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_get_unixattr_gotinfo, req); +} + +static void cli_get_unixattr_gotinfo(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 cli_state *cli = state->cli; + NTSTATUS status; + DATA_BLOB outbuf; + + status = smb2cli_query_info_recv(subreq, state, &outbuf); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + DBG_DEBUG("smb2cli_query_info_recv returned %s\n", + nt_errstr(status)); + return; + } + + if (outbuf.length < 0x60) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + state->create_time = interpret_long_date(BVAL(outbuf.data, 0)); + state->access_time = interpret_long_date(BVAL(outbuf.data, 0x8)); + state->write_time = interpret_long_date(BVAL(outbuf.data, 0x10)); + state->change_time = interpret_long_date(BVAL(outbuf.data, 0x18)); + state->mode = IVAL(outbuf.data, 0x20); + state->size = BVAL(outbuf.data, 0x30); + state->ino = BVAL(outbuf.data, 0x40); + + subreq = smb2cli_close_send(state, state->ev, cli->conn, 0, + cli->smb2.session, cli->smb2.tcon, 0, + state->fid_persistent, + state->fid_volatile); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_get_unixattr_closed, req); +} + +static void cli_get_unixattr_closed(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_get_unixattr_recv(struct tevent_req *req, + struct stat *st) +{ + 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 (state->mode & FILE_ATTRIBUTE_DIRECTORY) { + st->st_mode = (S_IFDIR | 0555); + st->st_nlink = 2; + } else { + st->st_mode = (S_IFREG | 0444); + st->st_nlink = 1; + } + + st->st_size = state->size; + st->st_uid = getuid(); + st->st_gid = getgid(); + st->st_ino = state->ino; + st->st_atime = convert_timespec_to_time_t(state->access_time); + st->st_ctime = convert_timespec_to_time_t(state->change_time); + st->st_mtime = convert_timespec_to_time_t(state->write_time); + + return NT_STATUS_OK; +} + +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); + +static 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 NTSTATUS parse_finfo_id_both_directory_info(uint8_t *dir_data, + uint32_t dir_data_length, + struct file_info *finfo, + uint32_t *next_offset) +{ + size_t namelen = 0; + size_t slen = 0; + size_t ret = 0; + + if (dir_data_length < 4) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + *next_offset = IVAL(dir_data, 0); + + if (*next_offset > dir_data_length) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + if (*next_offset != 0) { + /* Ensure we only read what in this record. */ + dir_data_length = *next_offset; + } + + if (dir_data_length < 105) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + finfo->btime_ts = interpret_long_date(BVAL(dir_data, 8)); + finfo->atime_ts = interpret_long_date(BVAL(dir_data, 16)); + finfo->mtime_ts = interpret_long_date(BVAL(dir_data, 24)); + finfo->ctime_ts = interpret_long_date(BVAL(dir_data, 32)); + finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0); + finfo->attr = IVAL(dir_data + 56, 0); + namelen = IVAL(dir_data + 60,0); + if (namelen > (dir_data_length - 104)) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + slen = CVAL(dir_data + 68, 0); + if (slen > 24) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + ret = pull_string_talloc(finfo, + dir_data, + FLAGS2_UNICODE_STRINGS, + &finfo->short_name, + dir_data + 70, + slen, + STR_UNICODE); + if (ret == (size_t)-1) { + /* Bad conversion. */ + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + ret = pull_string_talloc(finfo, + dir_data, + FLAGS2_UNICODE_STRINGS, + &finfo->name, + dir_data + 104, + namelen, + STR_UNICODE); + if (ret == (size_t)-1) { + /* Bad conversion. */ + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + return NT_STATUS_OK; +} + +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->attr, state->attribute); + + DEBUG(10, ("%s: dir_check_ftype(%u,%u) returned %u\n", + __func__, (unsigned)finfo->attr, + (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); +} + +static 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; +} + +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 = {0}; + 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); +} + +static void +cli_ll_forget(fuse_req_t freq, fuse_ino_t ino, unsigned long nlookup) +{ + struct mount_state *mstate = + talloc_get_type_abort(fuse_req_userdata(freq), + struct mount_state); + struct inode_state *istate = NULL; + + DBG_DEBUG("ino=%ju, nlookup=%lu\n", (uintmax_t)ino, nlookup); + + istate = idr_find(mstate->ino_ctx, ino); + if (istate == NULL) { + fuse_reply_err(freq, ENOENT); + return; + } + TALLOC_FREE(istate); + fuse_reply_none(freq); +} + +struct ll_getattr_state { + struct mount_state *mstate; + fuse_req_t freq; + struct fuse_file_info fi; +}; + +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 *mstate = talloc_get_type_abort( + fuse_req_userdata(freq), struct mount_state); + struct ll_getattr_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_getattr_state); + if (state == NULL) { + fuse_reply_err(freq, ENOMEM); + return; + } + state->mstate = mstate; + state->freq = freq; + + req = cli_get_unixattr_send(state, mstate->ev, mstate->cli, + istate->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 st; + NTSTATUS status; + int ret; + + status = cli_get_unixattr_recv(req, &st); + TALLOC_FREE(req); + if (!NT_STATUS_IS_OK(status)) { + fuse_reply_err(state->freq, map_errno_from_nt_status(status)); + return; + } + + ret = fuse_reply_attr(state->freq, &st, 1); + if (ret != 0) { + DBG_NOTICE("fuse_reply_attr failed: %s\n", + strerror(-errno)); + } +} + + +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, + (struct cli_smb2_create_flags){0}, + SMB2_IMPERSONATION_IMPERSONATION, + acc, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE, + NULL); + 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, NULL, NULL, 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, 0); + 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, + 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, + .forget = cli_ll_forget, + .getattr = cli_ll_getattr, + .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/examples/fuse/clifuse.h b/examples/fuse/clifuse.h new file mode 100644 index 0000000..62a2666 --- /dev/null +++ b/examples/fuse/clifuse.h @@ -0,0 +1,27 @@ +/* + * Unix SMB/CIFS implementation. + * fusermount smb2 client + * Copyright (C) Volker Lendecke 2016 + * + * 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/>. + */ + +#ifndef __EXAMPLES_FUSE_CLIFUSE_H__ +#define __EXAMPLES_FUSE_CLIFUSE_H__ + +struct cli_state; + +int do_mount(struct cli_state *cli, const char *mountpoint); + +#endif diff --git a/examples/fuse/smb2mount.c b/examples/fuse/smb2mount.c new file mode 100644 index 0000000..0594ced --- /dev/null +++ b/examples/fuse/smb2mount.c @@ -0,0 +1,160 @@ +/* + * Unix SMB/CIFS implementation. + * fusermount smb2 client + * + * Copyright (C) Volker Lendecke 2016 + * + * 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 "source3/include/includes.h" +#include "popt.h" +#include "lib/cmdline/cmdline.h" +#include "lib/param/param.h" +#include "client.h" +#include "libsmb/proto.h" +#include "clifuse.h" + +static struct cli_state *connect_one(struct cli_credentials *creds, + const char *server, int port, + const char *share) +{ + struct cli_state *c = NULL; + NTSTATUS nt_status; + uint32_t flags = 0; + + nt_status = cli_full_connection_creds(&c, lp_netbios_name(), server, + NULL, port, + share, "?????", + creds, + flags); + if (!NT_STATUS_IS_OK(nt_status)) { + DBG_ERR("cli_full_connection failed! (%s)\n", + nt_errstr(nt_status)); + return NULL; + } + + return c; +} + +int main(int argc, char *argv[]) +{ + const char **argv_const = discard_const_p(const char *, argv); + TALLOC_CTX *frame = talloc_stackframe(); + struct loadparm_context *lp_ctx = NULL; + poptContext pc; + int opt, ret; + int port = 0; + char *unc, *mountpoint, *server, *share; + struct cli_state *cli; + struct cli_credentials *creds = NULL; + bool ok; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_SAMBA + POPT_COMMON_CREDENTIALS + { "port", 'p', POPT_ARG_INT, &port, 'p', "Port to connect to", + "PORT" }, + POPT_TABLEEND + }; + + smb_init_locale(); + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(frame); + exit(1); + } + lp_ctx = samba_cmdline_get_lp_ctx(); + lpcfg_set_cmdline(lp_ctx, "client min protocol", "SMB2"); + lpcfg_set_cmdline(lp_ctx, "client max protocol", "SMB3_11"); + + pc = samba_popt_get_context(getprogname(), + argc, + argv_const, + long_options, + 0); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(frame); + exit(1); + } + + poptSetOtherOptionHelp(pc, "//server1/share1 mountpoint"); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + case 'p': + break; + default: + fprintf(stderr, "Unknown Option: %c\n", opt); + exit(1); + } + } + + if (!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + return -1; + } + unc = talloc_strdup(frame, poptGetArg(pc)); + if (unc == NULL) { + return -1; + } + string_replace(unc,'/','\\'); + + if (!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + return -1; + } + mountpoint = talloc_strdup(frame, poptGetArg(pc)); + if (mountpoint == NULL) { + return -1; + } + + poptFreeContext(pc); + samba_cmdline_burn(argc, argv); + + server = talloc_strdup(frame, unc+2); + if (!server) { + return -1; + } + share = strchr_m(server,'\\'); + if (!share) { + fprintf(stderr, "Invalid argument: %s\n", server); + return -1; + } + + *share = 0; + share++; + + creds = samba_cmdline_get_creds(); + + cli = connect_one(creds, server, port, share); + if (cli == NULL) { + return -1; + } + + ret = do_mount(cli, mountpoint); + if (ret != 0) { + fprintf(stderr, "mount failed\n"); + return -1; + } + + TALLOC_FREE(frame); + return 0; +} diff --git a/examples/fuse/wscript b/examples/fuse/wscript new file mode 100644 index 0000000..1c85139 --- /dev/null +++ b/examples/fuse/wscript @@ -0,0 +1,14 @@ +#!/usr/bin/env python + +def configure(conf): + # 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) diff --git a/examples/fuse/wscript_build b/examples/fuse/wscript_build new file mode 100644 index 0000000..ceef925 --- /dev/null +++ b/examples/fuse/wscript_build @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +if bld.env.HAVE_FUSE: + bld.SAMBA_BINARY('smb2mount', + source='smb2mount.c clifuse.c', + deps='smbconf CMDLINE_S3 libsmb fuse', + install=False) |