mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
64b20a1d42
Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Andreas Schneider <asn@samba.org> Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org> Autobuild-Date(master): Tue Mar 14 19:15:03 CET 2017 on sn-devel-144
1541 lines
37 KiB
C
1541 lines
37 KiB
C
/*
|
|
* 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"
|
|
|
|
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;
|
|
}
|
|
|
|
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 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);
|
|
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 */
|
|
(SMB_FILE_ALL_INFORMATION - 1000), /* 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((char *)outbuf.data + 0x0);
|
|
state->access_time = interpret_long_date((char *)outbuf.data + 0x8);
|
|
state->write_time = interpret_long_date((char *)outbuf.data + 0x10);
|
|
state->change_time = interpret_long_date((char *)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 (IS_DOS_DIR(state->mode)) {
|
|
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->atime_ts = interpret_long_date((const char *)dir_data + 16);
|
|
finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24);
|
|
finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32);
|
|
finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
|
|
finfo->mode = CVAL(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->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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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,
|
|
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,
|
|
.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;
|
|
}
|