2 * Unix SMB/CIFS implementation.
3 * fusermount smb2 client
4 * Copyright (C) Volker Lendecke 2016
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #define FUSE_USE_VERSION 26
21 #define _FILE_OFFSET_BITS 64
22 #include "fuse/fuse_lowlevel.h"
24 #include "source3/include/includes.h"
27 #include "libsmb/proto.h"
28 #include "libsmb/clirap.h"
29 #include "libsmb/cli_smb2_fnum.h"
30 #include "lib/util/tevent_ntstatus.h"
31 #include "libcli/smb/smbXcli_base.h"
32 #include "libcli/security/security.h"
36 struct tevent_context *ev;
37 struct cli_state *cli;
40 struct tevent_fd *fde;
41 struct tevent_signal *signal_ev;
44 struct fuse_session *se;
49 struct idr_context *ino_ctx;
50 TALLOC_CTX *ino_parent;
54 struct idr_context *ino_ctx;
59 static int inode_state_destructor(struct inode_state *s);
61 static struct inode_state *inode_state_init(TALLOC_CTX *mem_ctx,
62 struct idr_context *ino_ctx,
65 struct inode_state *state;
69 pathlen = strlen(path);
71 mem_ctx, offsetof(struct inode_state, path) + pathlen + 1);
75 talloc_set_name_const(state, "struct inode_state");
77 ino = idr_get_new_above(ino_ctx, state, 1, INT32_MAX);
84 state->ino_ctx = ino_ctx;
85 memcpy(state->path, path, pathlen + 1);
87 DBG_DEBUG("Creating ino %d for path %s\n", ino, path);
89 talloc_set_destructor(state, inode_state_destructor);
94 static struct inode_state *inode_state_new(struct mount_state *mstate,
97 return inode_state_init(mstate->ino_parent, mstate->ino_ctx, path);
100 static int inode_state_destructor(struct inode_state *s)
102 DBG_DEBUG("destroying inode %ju\n", (uintmax_t)s->ino);
103 idr_remove(s->ino_ctx, s->ino);
107 struct ll_create_state {
108 struct mount_state *mstate;
110 struct fuse_file_info fi;
114 static void cli_ll_create_done(struct tevent_req *req);
116 static void cli_ll_create(fuse_req_t freq, fuse_ino_t parent, const char *name,
117 mode_t mode, struct fuse_file_info *fi)
119 struct mount_state *mstate = talloc_get_type_abort(
120 fuse_req_userdata(freq), struct mount_state);
121 struct ll_create_state *state;
122 struct inode_state *istate;
123 struct tevent_req *req;
125 DBG_DEBUG("parent=%ju, name=%s, mode=%x\n", (uintmax_t)parent,
126 name, (unsigned)mode);
128 istate = idr_find(mstate->ino_ctx, parent);
129 if (istate == NULL) {
130 fuse_reply_err(freq, ENOENT);
134 state = talloc(mstate, struct ll_create_state);
136 fuse_reply_err(freq, ENOMEM);
139 state->mstate = mstate;
143 state->path = talloc_asprintf(state, "%s%s%s", istate->path,
144 strlen(istate->path) ? "\\": "",
146 if (state->path == NULL) {
148 fuse_reply_err(freq, ENOMEM);
152 req = cli_smb2_create_fnum_send(
153 state, mstate->ev, mstate->cli, state->path,
154 0, FILE_GENERIC_READ|FILE_GENERIC_WRITE, FILE_ATTRIBUTE_NORMAL,
155 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
156 FILE_CREATE, FILE_NON_DIRECTORY_FILE);
159 fuse_reply_err(freq, ENOMEM);
162 tevent_req_set_callback(req, cli_ll_create_done, state);
165 static void cli_ll_create_done(struct tevent_req *req)
167 struct ll_create_state *state = tevent_req_callback_data(
168 req, struct ll_create_state);
169 struct fuse_entry_param e;
170 struct inode_state *ino;
174 status = cli_smb2_create_fnum_recv(req, &fnum, NULL);
176 if (!NT_STATUS_IS_OK(status)) {
177 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
182 state->fi.direct_io = 0;
183 state->fi.keep_cache = 0;
185 ino = inode_state_new(state->mstate, state->path);
187 fuse_reply_err(state->freq, ENOMEM);
191 e = (struct fuse_entry_param) {
193 .generation = 1, /* FIXME */
198 fuse_reply_create(state->freq, &e, &state->fi);
203 struct cli_get_unixattr_state {
204 struct tevent_context *ev;
205 struct cli_state *cli;
206 uint64_t fid_persistent;
207 uint64_t fid_volatile;
209 struct timespec create_time;
210 struct timespec access_time;
211 struct timespec write_time;
212 struct timespec change_time;
218 static void cli_get_unixattr_opened(struct tevent_req *subreq);
219 static void cli_get_unixattr_gotinfo(struct tevent_req *subreq);
220 static void cli_get_unixattr_closed(struct tevent_req *subreq);
223 static struct tevent_req *cli_get_unixattr_send(TALLOC_CTX *mem_ctx,
224 struct tevent_context *ev,
225 struct cli_state *cli,
228 struct tevent_req *req, *subreq;
229 struct cli_get_unixattr_state *state;
231 req = tevent_req_create(mem_ctx, &state,
232 struct cli_get_unixattr_state);
239 subreq = smb2cli_create_send(
240 state, ev, cli->conn, cli->timeout, cli->smb2.session,
241 cli->smb2.tcon, path, SMB2_OPLOCK_LEVEL_NONE,
242 SMB2_IMPERSONATION_IMPERSONATION,
243 SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES, 0,
244 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
246 if (tevent_req_nomem(subreq, req)) {
247 return tevent_req_post(req, ev);
249 tevent_req_set_callback(subreq, cli_get_unixattr_opened, req);
254 static void cli_get_unixattr_opened(struct tevent_req *subreq)
256 struct tevent_req *req = tevent_req_callback_data(
257 subreq, struct tevent_req);
258 struct cli_get_unixattr_state *state = tevent_req_data(
259 req, struct cli_get_unixattr_state);
260 struct cli_state *cli = state->cli;
263 status = smb2cli_create_recv(subreq, &state->fid_persistent,
264 &state->fid_volatile, NULL, NULL, NULL);
266 if (tevent_req_nterror(req, status)) {
267 DBG_DEBUG("smb2cli_create_recv returned %s\n",
272 subreq = smb2cli_query_info_send(
273 state, state->ev, cli->conn, 0,
274 cli->smb2.session, cli->smb2.tcon,
275 1, /* in_info_type */
276 (SMB_FILE_ALL_INFORMATION - 1000), /* in_file_info_class */
277 0xFFFF, /* in_max_output_length */
278 NULL, /* in_input_buffer */
279 0, /* in_additional_info */
281 state->fid_persistent,
282 state->fid_volatile);
283 if (tevent_req_nomem(subreq, req)) {
286 tevent_req_set_callback(subreq, cli_get_unixattr_gotinfo, req);
289 static void cli_get_unixattr_gotinfo(struct tevent_req *subreq)
291 struct tevent_req *req = tevent_req_callback_data(
292 subreq, struct tevent_req);
293 struct cli_get_unixattr_state *state = tevent_req_data(
294 req, struct cli_get_unixattr_state);
295 struct cli_state *cli = state->cli;
299 status = smb2cli_query_info_recv(subreq, state, &outbuf);
301 if (tevent_req_nterror(req, status)) {
302 DBG_DEBUG("smb2cli_query_info_recv returned %s\n",
307 if (outbuf.length < 0x60) {
308 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
312 state->create_time = interpret_long_date((char *)outbuf.data + 0x0);
313 state->access_time = interpret_long_date((char *)outbuf.data + 0x8);
314 state->write_time = interpret_long_date((char *)outbuf.data + 0x10);
315 state->change_time = interpret_long_date((char *)outbuf.data + 0x18);
316 state->mode = IVAL(outbuf.data, 0x20);
317 state->size = BVAL(outbuf.data, 0x30);
318 state->ino = BVAL(outbuf.data, 0x40);
320 subreq = smb2cli_close_send(state, state->ev, cli->conn, 0,
321 cli->smb2.session, cli->smb2.tcon, 0,
322 state->fid_persistent,
323 state->fid_volatile);
324 if (tevent_req_nomem(subreq, req)) {
327 tevent_req_set_callback(subreq, cli_get_unixattr_closed, req);
330 static void cli_get_unixattr_closed(struct tevent_req *subreq)
332 struct tevent_req *req = tevent_req_callback_data(
333 subreq, struct tevent_req);
336 status = smb2cli_close_recv(subreq);
338 if (tevent_req_nterror(req, status)) {
341 tevent_req_done(req);
344 static NTSTATUS cli_get_unixattr_recv(struct tevent_req *req,
347 struct cli_get_unixattr_state *state = tevent_req_data(
348 req, struct cli_get_unixattr_state);
351 if (tevent_req_is_nterror(req, &status)) {
355 if (IS_DOS_DIR(state->mode)) {
356 st->st_mode = (S_IFDIR | 0555);
359 st->st_mode = (S_IFREG | 0444);
363 st->st_size = state->size;
364 st->st_uid = getuid();
365 st->st_gid = getgid();
366 st->st_ino = state->ino;
367 st->st_atime = convert_timespec_to_time_t(state->access_time);
368 st->st_ctime = convert_timespec_to_time_t(state->change_time);
369 st->st_mtime = convert_timespec_to_time_t(state->write_time);
374 struct cli_smb2_listdir_state {
375 struct tevent_context *ev;
376 struct smbXcli_conn *conn;
377 uint32_t timeout_msec;
378 struct smbXcli_session *session;
379 struct smbXcli_tcon *tcon;
383 uint64_t fid_persistent;
384 uint64_t fid_volatile;
389 const char *mntpoint;
390 const char *pathname;
391 NTSTATUS (*fn)(const char *mntpoint, struct file_info *f,
392 const char *mask, void *private_data);
397 static void cli_smb2_listdir_done(struct tevent_req *subreq);
399 static struct tevent_req *cli_smb2_listdir_send(
401 struct tevent_context *ev,
402 struct smbXcli_conn *conn,
403 uint32_t timeout_msec,
404 struct smbXcli_session *session,
405 struct smbXcli_tcon *tcon,
409 uint64_t fid_persistent,
410 uint64_t fid_volatile,
414 const char *mntpoint,
415 const char *pathname,
416 NTSTATUS (*fn)(const char *mntpoint, struct file_info *f,
417 const char *mask, void *private_data),
420 struct tevent_req *req, *subreq;
421 struct cli_smb2_listdir_state *state;
423 req = tevent_req_create(mem_ctx, &state,
424 struct cli_smb2_listdir_state);
430 state->timeout_msec = timeout_msec;
431 state->session = session;
433 state->level = level;
434 state->flags = flags;
435 state->file_index = file_index;
436 state->fid_persistent = fid_persistent;
437 state->fid_volatile = fid_volatile;
439 state->outbuf_len = outbuf_len;
440 state->attribute = attribute;
441 state->mntpoint = mntpoint;
442 state->pathname = pathname;
444 state->private_data = private_data;
446 subreq = smb2cli_query_directory_send(
447 state, state->ev, state->conn, state->timeout_msec,
448 state->session, state->tcon, state->level,
449 state->flags, state->file_index,
450 state->fid_persistent, state->fid_volatile,
451 state->mask, state->outbuf_len);
452 if (tevent_req_nomem(subreq, req)) {
453 return tevent_req_post(req, ev);
455 tevent_req_set_callback(subreq, cli_smb2_listdir_done, req);
459 static NTSTATUS parse_finfo_id_both_directory_info(uint8_t *dir_data,
460 uint32_t dir_data_length,
461 struct file_info *finfo,
462 uint32_t *next_offset)
468 if (dir_data_length < 4) {
469 return NT_STATUS_INFO_LENGTH_MISMATCH;
472 *next_offset = IVAL(dir_data, 0);
474 if (*next_offset > dir_data_length) {
475 return NT_STATUS_INFO_LENGTH_MISMATCH;
478 if (*next_offset != 0) {
479 /* Ensure we only read what in this record. */
480 dir_data_length = *next_offset;
483 if (dir_data_length < 105) {
484 return NT_STATUS_INFO_LENGTH_MISMATCH;
487 finfo->atime_ts = interpret_long_date((const char *)dir_data + 16);
488 finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24);
489 finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32);
490 finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
491 finfo->mode = CVAL(dir_data + 56, 0);
492 namelen = IVAL(dir_data + 60,0);
493 if (namelen > (dir_data_length - 104)) {
494 return NT_STATUS_INFO_LENGTH_MISMATCH;
496 slen = CVAL(dir_data + 68, 0);
498 return NT_STATUS_INFO_LENGTH_MISMATCH;
500 ret = pull_string_talloc(finfo,
502 FLAGS2_UNICODE_STRINGS,
507 if (ret == (size_t)-1) {
508 /* Bad conversion. */
509 return NT_STATUS_INVALID_NETWORK_RESPONSE;
512 ret = pull_string_talloc(finfo,
514 FLAGS2_UNICODE_STRINGS,
519 if (ret == (size_t)-1) {
520 /* Bad conversion. */
521 return NT_STATUS_INVALID_NETWORK_RESPONSE;
526 static void cli_smb2_listdir_done(struct tevent_req *subreq)
528 struct tevent_req *req = tevent_req_callback_data(
529 subreq, struct tevent_req);
530 struct cli_smb2_listdir_state *state = tevent_req_data(
531 req, struct cli_smb2_listdir_state);
534 uint32_t next_offset = 0;
537 status = smb2cli_query_directory_recv(subreq, state, &data,
540 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
541 tevent_req_done(req);
544 if (tevent_req_nterror(req, status)) {
549 struct file_info *finfo;
552 finfo = talloc_zero(state, struct file_info);
553 if (tevent_req_nomem(finfo, req)) {
557 status = parse_finfo_id_both_directory_info(
558 data, data_len, finfo, &next_offset);
560 DEBUG(10, ("%s: parse_finfo_id_both_directory_info returned "
561 "%s\n", __func__, nt_errstr(status)));
563 if (tevent_req_nterror(req, status)) {
567 ok = dir_check_ftype(finfo->mode, state->attribute);
569 DEBUG(10, ("%s: dir_check_ftype(%u,%u) returned %u\n",
570 __func__, (unsigned)finfo->mode,
571 (unsigned)state->attribute, (unsigned)ok));
575 * Only process if attributes match. On SMB1 server
576 * does this, so on SMB2 we need to emulate in the
579 * https://bugzilla.samba.org/show_bug.cgi?id=10260
581 state->processed_file = true;
583 status = state->fn(state->mntpoint, finfo,
585 state->private_data);
586 if (tevent_req_nterror(req, status)) {
593 if (next_offset != 0) {
595 data_len -= next_offset;
597 } while (next_offset != 0);
599 subreq = smb2cli_query_directory_send(
600 state, state->ev, state->conn, state->timeout_msec,
601 state->session, state->tcon, state->level,
602 state->flags, state->file_index,
603 state->fid_persistent, state->fid_volatile,
604 state->mask, state->outbuf_len);
605 if (tevent_req_nomem(subreq, req)) {
608 tevent_req_set_callback(subreq, cli_smb2_listdir_done, req);
611 static NTSTATUS cli_smb2_listdir_recv(struct tevent_req *req)
613 struct cli_smb2_listdir_state *state = tevent_req_data(
614 req, struct cli_smb2_listdir_state);
617 if (tevent_req_is_nterror(req, &status)) {
621 if (!state->processed_file) {
623 * In SMB1 findfirst returns NT_STATUS_NO_SUCH_FILE
624 * if no files match. Emulate this in the client.
626 return NT_STATUS_NO_SUCH_FILE;
632 struct ll_lookup_state {
633 struct mount_state *mstate;
638 static void cli_ll_lookup_done(struct tevent_req *req);
640 static void cli_ll_lookup(fuse_req_t freq, fuse_ino_t parent_ino,
643 struct mount_state *mstate = talloc_get_type_abort(
644 fuse_req_userdata(freq), struct mount_state);
645 struct ll_lookup_state *state;
646 struct tevent_req *req;
647 struct inode_state *parent;
649 DBG_DEBUG("parent_ino=%ju, name=%s\n", (uintmax_t)parent_ino, name);
651 parent = idr_find(mstate->ino_ctx, parent_ino);
652 if (parent == NULL) {
653 DBG_WARNING("could not find parent\n");
654 fuse_reply_err(freq, ENOENT);
658 state = talloc(mstate, struct ll_lookup_state);
660 DBG_WARNING("talloc failed\n");
661 fuse_reply_err(freq, ENOMEM);
664 state->mstate = mstate;
667 state->path = talloc_asprintf(state, "%s%s%s", parent->path,
668 strlen(parent->path) ? "\\": "",
670 if (state->path == NULL) {
672 fuse_reply_err(freq, ENOMEM);
676 req = cli_get_unixattr_send(state, mstate->ev, mstate->cli,
680 fuse_reply_err(freq, ENOMEM);
683 tevent_req_set_callback(req, cli_ll_lookup_done, state);
686 static void cli_ll_lookup_done(struct tevent_req *req)
688 struct ll_lookup_state *state = tevent_req_callback_data(
689 req, struct ll_lookup_state);
690 struct stat sbuf = {0};
691 struct fuse_entry_param e;
692 struct inode_state *ino;
695 status = cli_get_unixattr_recv(req, &sbuf);
697 if (!NT_STATUS_IS_OK(status)) {
698 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
702 ino = inode_state_new(state->mstate, state->path);
704 fuse_reply_err(state->freq, ENOMEM);
708 e = (struct fuse_entry_param) {
711 .generation = 1, /* FIXME */
716 fuse_reply_entry(state->freq, &e);
720 struct ll_open_state {
721 struct mount_state *mstate;
723 struct fuse_file_info fi;
726 static void cli_ll_open_done(struct tevent_req *req);
728 static void cli_ll_open(fuse_req_t freq, fuse_ino_t ino,
729 struct fuse_file_info *fi)
731 struct mount_state *mstate = talloc_get_type_abort(
732 fuse_req_userdata(freq), struct mount_state);
733 struct ll_open_state *state;
734 struct inode_state *istate;
735 struct tevent_req *req;
738 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
740 istate = idr_find(mstate->ino_ctx, ino);
741 if (istate == NULL) {
742 fuse_reply_err(freq, ENOENT);
746 state = talloc(mstate, struct ll_open_state);
748 fuse_reply_err(freq, ENOMEM);
751 state->mstate = mstate;
755 switch (fi->flags & O_ACCMODE) {
757 acc = FILE_GENERIC_READ;
760 acc = FILE_GENERIC_WRITE;
763 acc = FILE_GENERIC_READ|FILE_GENERIC_WRITE;
766 fuse_reply_err(freq, EACCES);
770 req = cli_smb2_create_fnum_send(
771 state, mstate->ev, mstate->cli, istate->path,
772 0, acc, FILE_ATTRIBUTE_NORMAL,
773 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
774 FILE_OPEN, FILE_NON_DIRECTORY_FILE);
777 fuse_reply_err(freq, ENOMEM);
780 tevent_req_set_callback(req, cli_ll_open_done, state);
783 static void cli_ll_open_done(struct tevent_req *req)
785 struct ll_open_state *state = tevent_req_callback_data(
786 req, struct ll_open_state);
790 status = cli_smb2_create_fnum_recv(req, &fnum, NULL);
792 if (!NT_STATUS_IS_OK(status)) {
793 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
798 state->fi.direct_io = 0;
799 state->fi.keep_cache = 0;
801 fuse_reply_open(state->freq, &state->fi);
806 struct ll_release_state {
807 struct mount_state *mstate;
812 static void cli_ll_release_done(struct tevent_req *req);
814 static void cli_ll_release(fuse_req_t freq, fuse_ino_t ino,
815 struct fuse_file_info *fi)
817 struct mount_state *mstate = talloc_get_type_abort(
818 fuse_req_userdata(freq), struct mount_state);
819 struct ll_release_state *state;
820 struct inode_state *istate;
821 struct tevent_req *req;
824 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
826 istate = idr_find(mstate->ino_ctx, ino);
827 if (istate == NULL) {
828 fuse_reply_err(freq, ENOENT);
832 state = talloc(mstate, struct ll_release_state);
834 fuse_reply_err(freq, ENOMEM);
837 state->mstate = mstate;
843 req = cli_smb2_close_fnum_send(state, mstate->ev, mstate->cli, fnum);
846 fuse_reply_err(freq, ENOMEM);
849 tevent_req_set_callback(req, cli_ll_release_done, state);
852 static void cli_ll_release_done(struct tevent_req *req)
854 struct ll_release_state *state = tevent_req_callback_data(
855 req, struct ll_release_state);
856 struct inode_state *istate;
859 status = cli_smb2_close_fnum_recv(req);
861 if (!NT_STATUS_IS_OK(status)) {
862 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
866 istate = idr_find(state->mstate->ino_ctx, state->ino);
867 if (istate == NULL) {
868 DEBUG(1, ("%s: inode %ju vanished!\n", __func__,
869 (uintmax_t)state->ino));
873 fuse_reply_err(state->freq, 0);
877 struct ll_read_state {
878 struct mount_state *mstate;
882 static void cli_ll_read_done(struct tevent_req *req);
884 static void cli_ll_read(fuse_req_t freq, fuse_ino_t ino,
885 size_t size, off_t off,
886 struct fuse_file_info *fi)
888 struct mount_state *mstate = talloc_get_type_abort(
889 fuse_req_userdata(freq), struct mount_state);
890 struct ll_read_state *state;
891 struct tevent_req *req;
894 DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino,
895 size, (uintmax_t)off);
897 state = talloc(mstate, struct ll_read_state);
899 fuse_reply_err(freq, ENOMEM);
902 state->mstate = mstate;
907 req = cli_smb2_read_send(state, mstate->ev, mstate->cli,
911 fuse_reply_err(freq, ENOMEM);
914 tevent_req_set_callback(req, cli_ll_read_done, state);
917 static void cli_ll_read_done(struct tevent_req *req)
919 struct ll_read_state *state = tevent_req_callback_data(
920 req, struct ll_read_state);
925 status = cli_smb2_read_recv(req, &received, &rcvbuf);
926 /* no talloc_free here yet */
928 if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
931 status = NT_STATUS_OK;
934 if (!NT_STATUS_IS_OK(status)) {
935 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
938 fuse_reply_buf(state->freq, (char *)rcvbuf, received);
942 struct ll_write_state {
943 struct mount_state *mstate;
947 static void cli_ll_write_done(struct tevent_req *req);
949 static void cli_ll_write(fuse_req_t freq, fuse_ino_t ino, const char *buf,
950 size_t size, off_t off, struct fuse_file_info *fi)
952 struct mount_state *mstate = talloc_get_type_abort(
953 fuse_req_userdata(freq), struct mount_state);
954 struct ll_write_state *state;
955 struct tevent_req *req;
958 DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino,
959 size, (uintmax_t)off);
961 state = talloc(mstate, struct ll_write_state);
963 fuse_reply_err(freq, ENOMEM);
966 state->mstate = mstate;
971 req = cli_smb2_write_send(state, mstate->ev, mstate->cli, fnum, 0,
972 (const uint8_t *)buf, off, size);
975 fuse_reply_err(freq, ENOMEM);
978 tevent_req_set_callback(req, cli_ll_write_done, state);
981 static void cli_ll_write_done(struct tevent_req *req)
983 struct ll_write_state *state = tevent_req_callback_data(
984 req, struct ll_write_state);
988 status = cli_smb2_write_recv(req, &written);
989 /* no talloc_free here yet */
990 if (!NT_STATUS_IS_OK(status)) {
991 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
994 fuse_reply_write(state->freq, written);
999 struct ll_dir_state {
1000 uint64_t fid_persistent;
1001 uint64_t fid_volatile;
1003 struct file_info *finfos;
1004 unsigned num_finfos, num_sent;
1007 static bool ll_dir_state_add(struct ll_dir_state *dir_state,
1010 struct file_info *tmp, *finfo;
1012 tmp = talloc_realloc(dir_state, dir_state->finfos,
1013 struct file_info, dir_state->num_finfos+1);
1017 dir_state->finfos = tmp;
1018 finfo = &dir_state->finfos[dir_state->num_finfos];
1020 ZERO_STRUCTP(finfo);
1022 finfo->name = talloc_strdup(dir_state->finfos, name);
1023 if (finfo->name == NULL) {
1026 dir_state->num_finfos += 1;
1031 struct ll_opendir_state {
1032 struct mount_state *mstate;
1034 struct fuse_file_info fi;
1035 struct ll_dir_state *dir_state;
1038 static void cli_ll_opendir_done(struct tevent_req *req);
1040 static void cli_ll_opendir(fuse_req_t freq, fuse_ino_t ino,
1041 struct fuse_file_info *fi)
1043 struct mount_state *mstate = talloc_get_type_abort(
1044 fuse_req_userdata(freq), struct mount_state);
1045 struct cli_state *cli = mstate->cli;
1046 struct ll_opendir_state *state;
1047 struct inode_state *istate;
1048 struct tevent_req *req;
1050 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
1052 istate = idr_find(mstate->ino_ctx, ino);
1053 if (istate == NULL) {
1054 fuse_reply_err(freq, ENOENT);
1058 state = talloc(mstate, struct ll_opendir_state);
1059 if (state == NULL) {
1060 fuse_reply_err(freq, ENOMEM);
1063 state->mstate = mstate;
1067 state->dir_state = talloc_zero(state, struct ll_dir_state);
1068 if (state->dir_state == NULL) {
1070 fuse_reply_err(freq, ENOMEM);
1074 req = smb2cli_create_send(
1075 state, mstate->ev, cli->conn, cli->timeout,
1076 cli->smb2.session, cli->smb2.tcon, istate->path,
1077 0, SMB2_IMPERSONATION_IMPERSONATION,
1078 FILE_GENERIC_READ, FILE_ATTRIBUTE_DIRECTORY,
1079 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
1080 FILE_OPEN, FILE_DIRECTORY_FILE, NULL);
1083 fuse_reply_err(freq, ENOMEM);
1086 tevent_req_set_callback(req, cli_ll_opendir_done, state);
1089 static void cli_ll_opendir_done(struct tevent_req *req)
1091 struct ll_opendir_state *state = tevent_req_callback_data(
1092 req, struct ll_opendir_state);
1095 status = smb2cli_create_recv(req,
1096 &state->dir_state->fid_persistent,
1097 &state->dir_state->fid_volatile,
1101 DEBUG(10, ("%s: smbcli_create_recv returned %s\n", __func__,
1102 nt_errstr(status)));
1104 if (!NT_STATUS_IS_OK(status)) {
1105 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
1109 state->fi.fh = (uint64_t)talloc_move(state->mstate, &state->dir_state);
1110 state->fi.direct_io = 0;
1111 state->fi.keep_cache = 0;
1113 fuse_reply_open(state->freq, &state->fi);
1118 struct ll_readdir_state {
1120 struct ll_dir_state *dir_state;
1123 static void cli_ll_readdir_done(struct tevent_req *subreq);
1124 static NTSTATUS cli_ll_readdir_one(const char *mnt, struct file_info *finfo,
1125 const char *path, void *private_data);
1126 static void cli_ll_readdir_reply_one(fuse_req_t freq,
1127 struct ll_dir_state *dir_state);
1129 static void cli_ll_readdir(fuse_req_t freq, fuse_ino_t ino, size_t size,
1130 off_t off, struct fuse_file_info *fi)
1132 struct mount_state *mstate = talloc_get_type_abort(
1133 fuse_req_userdata(freq), struct mount_state);
1134 struct cli_state *cli = mstate->cli;
1135 struct ll_dir_state *dir_state;
1136 struct ll_readdir_state *state;
1137 struct tevent_req *req;
1139 DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino, size,
1142 dir_state = talloc_get_type_abort((void *)fi->fh, struct ll_dir_state);
1144 if (dir_state->finfos != NULL) {
1145 DBG_DEBUG("finfos=%p\n", dir_state->finfos);
1146 cli_ll_readdir_reply_one(freq, dir_state);
1150 if (!ll_dir_state_add(dir_state, ".") ||
1151 !ll_dir_state_add(dir_state, "..")) {
1152 fuse_reply_err(freq, ENOMEM);
1156 state = talloc(mstate, struct ll_readdir_state);
1157 if (state == NULL) {
1158 fuse_reply_err(freq, ENOMEM);
1162 state->dir_state = dir_state;
1164 req = cli_smb2_listdir_send(
1165 state, mstate->ev, cli->conn, cli->timeout,
1166 cli->smb2.session, cli->smb2.tcon,
1167 SMB2_FIND_ID_BOTH_DIRECTORY_INFO, 0, 0,
1168 dir_state->fid_persistent, dir_state->fid_volatile,
1170 FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM |
1171 FILE_ATTRIBUTE_HIDDEN,
1172 NULL, NULL, cli_ll_readdir_one, dir_state);
1175 fuse_reply_err(freq, ENOMEM);
1178 tevent_req_set_callback(req, cli_ll_readdir_done, state);
1181 static void cli_ll_readdir_reply_one(fuse_req_t freq,
1182 struct ll_dir_state *dir_state)
1184 struct file_info *finfo;
1186 struct stat sbuf = {};
1189 if (dir_state->num_finfos == dir_state->num_sent) {
1190 DEBUG(10, ("%s: Done\n", __func__));
1191 fuse_reply_buf(freq, NULL, 0);
1195 sbuf.st_mode = S_IFREG | 0755;
1196 sbuf.st_ino = random(); /* FIXME :-) */
1197 finfo = &dir_state->finfos[dir_state->num_sent];
1199 DBG_DEBUG("Adding %s\n", finfo->name);
1201 buflen = fuse_add_direntry(freq, buf, sizeof(buf),
1202 finfo->name, &sbuf, 0);
1203 fuse_reply_buf(freq, buf, buflen);
1204 dir_state->num_sent += 1;
1208 static NTSTATUS cli_ll_readdir_one(const char *mnt, struct file_info *finfo,
1209 const char *path, void *private_data)
1211 struct ll_dir_state *dir_state = talloc_get_type_abort(
1212 private_data, struct ll_dir_state);
1214 if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
1215 DEBUG(10, ("%s: Ignoring %s\n", __func__, finfo->name));
1216 return NT_STATUS_OK;
1219 DBG_DEBUG("Got entry %s\n", finfo->name);
1221 if (!ll_dir_state_add(dir_state, finfo->name)) {
1222 return NT_STATUS_NO_MEMORY;
1224 return NT_STATUS_OK;
1227 static void cli_ll_readdir_done(struct tevent_req *req)
1229 struct ll_readdir_state *state = tevent_req_callback_data(
1230 req, struct ll_readdir_state);
1233 status = cli_smb2_listdir_recv(req);
1235 if (!NT_STATUS_IS_OK(status)) {
1236 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
1239 cli_ll_readdir_reply_one(state->freq, state->dir_state);
1244 struct ll_releasedir_state {
1245 struct mount_state *mstate;
1247 struct ll_dir_state *dir_state;
1250 static void cli_ll_releasedir_done(struct tevent_req *req);
1252 static void cli_ll_releasedir(fuse_req_t freq, fuse_ino_t ino,
1253 struct fuse_file_info *fi)
1255 struct mount_state *mstate = talloc_get_type_abort(
1256 fuse_req_userdata(freq), struct mount_state);
1257 struct cli_state *cli = mstate->cli;
1258 struct ll_releasedir_state *state;
1259 struct tevent_req *req;
1261 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
1263 state = talloc(mstate, struct ll_releasedir_state);
1264 if (state == NULL) {
1265 fuse_reply_err(freq, ENOMEM);
1268 state->mstate = mstate;
1271 state->dir_state = talloc_get_type_abort(
1272 (void *)fi->fh, struct ll_dir_state);
1274 req = smb2cli_close_send(state, mstate->ev, cli->conn, cli->timeout,
1275 cli->smb2.session, cli->smb2.tcon, 0,
1276 state->dir_state->fid_persistent,
1277 state->dir_state->fid_volatile);
1280 fuse_reply_err(freq, ENOMEM);
1283 tevent_req_set_callback(req, cli_ll_releasedir_done, state);
1286 static void cli_ll_releasedir_done(struct tevent_req *req)
1288 struct ll_releasedir_state *state = tevent_req_callback_data(
1289 req, struct ll_releasedir_state);
1292 status = smb2cli_close_recv(req);
1294 if (!NT_STATUS_IS_OK(status)) {
1295 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
1298 TALLOC_FREE(state->dir_state);
1299 fuse_reply_err(state->freq, 0);
1303 static struct fuse_lowlevel_ops cli_ll_ops = {
1304 .lookup = cli_ll_lookup,
1305 .open = cli_ll_open,
1306 .create = cli_ll_create,
1307 .release = cli_ll_release,
1308 .read = cli_ll_read,
1309 .write = cli_ll_write,
1310 .opendir = cli_ll_opendir,
1311 .readdir = cli_ll_readdir,
1312 .releasedir = cli_ll_releasedir,
1315 static void fuse_chan_fd_handler(struct tevent_context *ev,
1316 struct tevent_fd *fde,
1318 void *private_data);
1319 static void fuse_chan_signal_handler(struct tevent_context *ev,
1320 struct tevent_signal *se,
1324 void *private_data);
1326 static int mount_state_destructor(struct mount_state *s);
1328 int do_mount(struct cli_state *cli, const char *mountpoint)
1330 struct mount_state *state;
1331 struct inode_state *ino;
1332 struct fuse_args args = { 0 };
1336 state = talloc_zero(talloc_tos(), struct mount_state);
1337 if (state == NULL) {
1338 fprintf(stderr, "talloc failed\n");
1342 state->ev = tevent_context_init(state);
1343 if (state->ev == NULL) {
1344 fprintf(stderr, "tevent_context_init failed\n");
1349 state->ino_ctx = idr_init(state);
1350 if (state->ino_ctx == NULL) {
1351 fprintf(stderr, "idr_init failed\n");
1356 state->ino_parent = talloc_new(state);
1357 if (state->ino_parent == NULL) {
1358 fprintf(stderr, "talloc_new failed\n");
1363 talloc_set_destructor(state, mount_state_destructor);
1365 ino = inode_state_new(state, "");
1367 fprintf(stderr, "inode_state_new failed\n");
1371 if (ino->ino != FUSE_ROOT_ID) {
1372 fprintf(stderr, "first inode gave %d, expected %d\n",
1373 (int)ino->ino, FUSE_ROOT_ID);
1380 state->ch = fuse_mount(mountpoint, &args);
1381 if (state->ch == NULL) {
1382 perror("fuse_mount failed");
1386 state->bufsize = fuse_chan_bufsize(state->ch);
1387 state->buf = talloc_array(state, char, state->bufsize);
1388 if (state->buf == NULL) {
1389 fprintf(stderr, "talloc failed\n");
1393 fd = fuse_chan_fd(state->ch);
1395 state->fde = tevent_add_fd(state->ev, state, fd, TEVENT_FD_READ,
1396 fuse_chan_fd_handler, state);
1397 if (state->fde == NULL) {
1398 fprintf(stderr, "tevent_add_fd failed\n");
1402 state->signal_ev = tevent_add_signal(state->ev, state, SIGINT, 0,
1403 fuse_chan_signal_handler, state);
1404 if (state->signal_ev == NULL) {
1405 fprintf(stderr, "tevent_add_signal failed\n");
1409 state->se = fuse_lowlevel_new(&args, &cli_ll_ops, sizeof(cli_ll_ops),
1411 if (state->se == NULL) {
1412 perror("fuse_lowlevel_new failed");
1416 fuse_session_add_chan(state->se, state->ch);
1418 while (!state->done) {
1419 ret = tevent_loop_once(state->ev);
1421 perror("tevent_loop_once failed");
1426 fuse_session_remove_chan(state->ch);
1427 fuse_session_destroy(state->se);
1429 fuse_unmount(mountpoint, state->ch);
1435 static int mount_state_destructor(struct mount_state *s)
1437 TALLOC_FREE(s->ino_parent);
1442 static void fuse_chan_fd_handler(struct tevent_context *ev,
1443 struct tevent_fd *fde,
1447 struct mount_state *state = talloc_get_type_abort(
1448 private_data, struct mount_state);
1451 if ((flags & TEVENT_FD_READ) == 0) {
1455 recvd = fuse_chan_recv(&state->ch, state->buf, state->bufsize);
1460 fuse_session_process(state->se, state->buf, recvd, state->ch);
1463 static void fuse_chan_signal_handler(struct tevent_context *ev,
1464 struct tevent_signal *se,
1470 struct mount_state *state = talloc_get_type_abort(
1471 private_data, struct mount_state);