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, SMB2_IMPERSONATION_IMPERSONATION,
155 FILE_GENERIC_READ|FILE_GENERIC_WRITE, FILE_ATTRIBUTE_NORMAL,
156 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
157 FILE_CREATE, FILE_NON_DIRECTORY_FILE);
160 fuse_reply_err(freq, ENOMEM);
163 tevent_req_set_callback(req, cli_ll_create_done, state);
166 static void cli_ll_create_done(struct tevent_req *req)
168 struct ll_create_state *state = tevent_req_callback_data(
169 req, struct ll_create_state);
170 struct fuse_entry_param e;
171 struct inode_state *ino;
175 status = cli_smb2_create_fnum_recv(req, &fnum, NULL);
177 if (!NT_STATUS_IS_OK(status)) {
178 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
183 state->fi.direct_io = 0;
184 state->fi.keep_cache = 0;
186 ino = inode_state_new(state->mstate, state->path);
188 fuse_reply_err(state->freq, ENOMEM);
192 e = (struct fuse_entry_param) {
194 .generation = 1, /* FIXME */
199 fuse_reply_create(state->freq, &e, &state->fi);
204 struct cli_get_unixattr_state {
205 struct tevent_context *ev;
206 struct cli_state *cli;
207 uint64_t fid_persistent;
208 uint64_t fid_volatile;
210 struct timespec create_time;
211 struct timespec access_time;
212 struct timespec write_time;
213 struct timespec change_time;
219 static void cli_get_unixattr_opened(struct tevent_req *subreq);
220 static void cli_get_unixattr_gotinfo(struct tevent_req *subreq);
221 static void cli_get_unixattr_closed(struct tevent_req *subreq);
224 static struct tevent_req *cli_get_unixattr_send(TALLOC_CTX *mem_ctx,
225 struct tevent_context *ev,
226 struct cli_state *cli,
229 struct tevent_req *req, *subreq;
230 struct cli_get_unixattr_state *state;
232 req = tevent_req_create(mem_ctx, &state,
233 struct cli_get_unixattr_state);
240 subreq = smb2cli_create_send(
241 state, ev, cli->conn, cli->timeout, cli->smb2.session,
242 cli->smb2.tcon, path, SMB2_OPLOCK_LEVEL_NONE,
243 SMB2_IMPERSONATION_IMPERSONATION,
244 SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES, 0,
245 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
247 if (tevent_req_nomem(subreq, req)) {
248 return tevent_req_post(req, ev);
250 tevent_req_set_callback(subreq, cli_get_unixattr_opened, req);
255 static void cli_get_unixattr_opened(struct tevent_req *subreq)
257 struct tevent_req *req = tevent_req_callback_data(
258 subreq, struct tevent_req);
259 struct cli_get_unixattr_state *state = tevent_req_data(
260 req, struct cli_get_unixattr_state);
261 struct cli_state *cli = state->cli;
264 status = smb2cli_create_recv(subreq, &state->fid_persistent,
265 &state->fid_volatile, NULL, NULL, NULL);
267 if (tevent_req_nterror(req, status)) {
268 DBG_DEBUG("smb2cli_create_recv returned %s\n",
273 subreq = smb2cli_query_info_send(
274 state, state->ev, cli->conn, 0,
275 cli->smb2.session, cli->smb2.tcon,
276 1, /* in_info_type */
277 (SMB_FILE_ALL_INFORMATION - 1000), /* in_file_info_class */
278 0xFFFF, /* in_max_output_length */
279 NULL, /* in_input_buffer */
280 0, /* in_additional_info */
282 state->fid_persistent,
283 state->fid_volatile);
284 if (tevent_req_nomem(subreq, req)) {
287 tevent_req_set_callback(subreq, cli_get_unixattr_gotinfo, req);
290 static void cli_get_unixattr_gotinfo(struct tevent_req *subreq)
292 struct tevent_req *req = tevent_req_callback_data(
293 subreq, struct tevent_req);
294 struct cli_get_unixattr_state *state = tevent_req_data(
295 req, struct cli_get_unixattr_state);
296 struct cli_state *cli = state->cli;
300 status = smb2cli_query_info_recv(subreq, state, &outbuf);
302 if (tevent_req_nterror(req, status)) {
303 DBG_DEBUG("smb2cli_query_info_recv returned %s\n",
308 if (outbuf.length < 0x60) {
309 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
313 state->create_time = interpret_long_date((char *)outbuf.data + 0x0);
314 state->access_time = interpret_long_date((char *)outbuf.data + 0x8);
315 state->write_time = interpret_long_date((char *)outbuf.data + 0x10);
316 state->change_time = interpret_long_date((char *)outbuf.data + 0x18);
317 state->mode = IVAL(outbuf.data, 0x20);
318 state->size = BVAL(outbuf.data, 0x30);
319 state->ino = BVAL(outbuf.data, 0x40);
321 subreq = smb2cli_close_send(state, state->ev, cli->conn, 0,
322 cli->smb2.session, cli->smb2.tcon, 0,
323 state->fid_persistent,
324 state->fid_volatile);
325 if (tevent_req_nomem(subreq, req)) {
328 tevent_req_set_callback(subreq, cli_get_unixattr_closed, req);
331 static void cli_get_unixattr_closed(struct tevent_req *subreq)
333 struct tevent_req *req = tevent_req_callback_data(
334 subreq, struct tevent_req);
337 status = smb2cli_close_recv(subreq);
339 if (tevent_req_nterror(req, status)) {
342 tevent_req_done(req);
345 static NTSTATUS cli_get_unixattr_recv(struct tevent_req *req,
348 struct cli_get_unixattr_state *state = tevent_req_data(
349 req, struct cli_get_unixattr_state);
352 if (tevent_req_is_nterror(req, &status)) {
356 if (IS_DOS_DIR(state->mode)) {
357 st->st_mode = (S_IFDIR | 0555);
360 st->st_mode = (S_IFREG | 0444);
364 st->st_size = state->size;
365 st->st_uid = getuid();
366 st->st_gid = getgid();
367 st->st_ino = state->ino;
368 st->st_atime = convert_timespec_to_time_t(state->access_time);
369 st->st_ctime = convert_timespec_to_time_t(state->change_time);
370 st->st_mtime = convert_timespec_to_time_t(state->write_time);
375 struct cli_smb2_listdir_state {
376 struct tevent_context *ev;
377 struct smbXcli_conn *conn;
378 uint32_t timeout_msec;
379 struct smbXcli_session *session;
380 struct smbXcli_tcon *tcon;
384 uint64_t fid_persistent;
385 uint64_t fid_volatile;
390 const char *mntpoint;
391 const char *pathname;
392 NTSTATUS (*fn)(const char *mntpoint, struct file_info *f,
393 const char *mask, void *private_data);
398 static void cli_smb2_listdir_done(struct tevent_req *subreq);
400 static struct tevent_req *cli_smb2_listdir_send(
402 struct tevent_context *ev,
403 struct smbXcli_conn *conn,
404 uint32_t timeout_msec,
405 struct smbXcli_session *session,
406 struct smbXcli_tcon *tcon,
410 uint64_t fid_persistent,
411 uint64_t fid_volatile,
415 const char *mntpoint,
416 const char *pathname,
417 NTSTATUS (*fn)(const char *mntpoint, struct file_info *f,
418 const char *mask, void *private_data),
421 struct tevent_req *req, *subreq;
422 struct cli_smb2_listdir_state *state;
424 req = tevent_req_create(mem_ctx, &state,
425 struct cli_smb2_listdir_state);
431 state->timeout_msec = timeout_msec;
432 state->session = session;
434 state->level = level;
435 state->flags = flags;
436 state->file_index = file_index;
437 state->fid_persistent = fid_persistent;
438 state->fid_volatile = fid_volatile;
440 state->outbuf_len = outbuf_len;
441 state->attribute = attribute;
442 state->mntpoint = mntpoint;
443 state->pathname = pathname;
445 state->private_data = private_data;
447 subreq = smb2cli_query_directory_send(
448 state, state->ev, state->conn, state->timeout_msec,
449 state->session, state->tcon, state->level,
450 state->flags, state->file_index,
451 state->fid_persistent, state->fid_volatile,
452 state->mask, state->outbuf_len);
453 if (tevent_req_nomem(subreq, req)) {
454 return tevent_req_post(req, ev);
456 tevent_req_set_callback(subreq, cli_smb2_listdir_done, req);
460 static NTSTATUS parse_finfo_id_both_directory_info(uint8_t *dir_data,
461 uint32_t dir_data_length,
462 struct file_info *finfo,
463 uint32_t *next_offset)
469 if (dir_data_length < 4) {
470 return NT_STATUS_INFO_LENGTH_MISMATCH;
473 *next_offset = IVAL(dir_data, 0);
475 if (*next_offset > dir_data_length) {
476 return NT_STATUS_INFO_LENGTH_MISMATCH;
479 if (*next_offset != 0) {
480 /* Ensure we only read what in this record. */
481 dir_data_length = *next_offset;
484 if (dir_data_length < 105) {
485 return NT_STATUS_INFO_LENGTH_MISMATCH;
488 finfo->btime_ts = interpret_long_date((const char *)dir_data + 8);
489 finfo->atime_ts = interpret_long_date((const char *)dir_data + 16);
490 finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24);
491 finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32);
492 finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
493 finfo->mode = CVAL(dir_data + 56, 0);
494 namelen = IVAL(dir_data + 60,0);
495 if (namelen > (dir_data_length - 104)) {
496 return NT_STATUS_INFO_LENGTH_MISMATCH;
498 slen = CVAL(dir_data + 68, 0);
500 return NT_STATUS_INFO_LENGTH_MISMATCH;
502 ret = pull_string_talloc(finfo,
504 FLAGS2_UNICODE_STRINGS,
509 if (ret == (size_t)-1) {
510 /* Bad conversion. */
511 return NT_STATUS_INVALID_NETWORK_RESPONSE;
514 ret = pull_string_talloc(finfo,
516 FLAGS2_UNICODE_STRINGS,
521 if (ret == (size_t)-1) {
522 /* Bad conversion. */
523 return NT_STATUS_INVALID_NETWORK_RESPONSE;
528 static void cli_smb2_listdir_done(struct tevent_req *subreq)
530 struct tevent_req *req = tevent_req_callback_data(
531 subreq, struct tevent_req);
532 struct cli_smb2_listdir_state *state = tevent_req_data(
533 req, struct cli_smb2_listdir_state);
536 uint32_t next_offset = 0;
539 status = smb2cli_query_directory_recv(subreq, state, &data,
542 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
543 tevent_req_done(req);
546 if (tevent_req_nterror(req, status)) {
551 struct file_info *finfo;
554 finfo = talloc_zero(state, struct file_info);
555 if (tevent_req_nomem(finfo, req)) {
559 status = parse_finfo_id_both_directory_info(
560 data, data_len, finfo, &next_offset);
562 DEBUG(10, ("%s: parse_finfo_id_both_directory_info returned "
563 "%s\n", __func__, nt_errstr(status)));
565 if (tevent_req_nterror(req, status)) {
569 ok = dir_check_ftype(finfo->mode, state->attribute);
571 DEBUG(10, ("%s: dir_check_ftype(%u,%u) returned %u\n",
572 __func__, (unsigned)finfo->mode,
573 (unsigned)state->attribute, (unsigned)ok));
577 * Only process if attributes match. On SMB1 server
578 * does this, so on SMB2 we need to emulate in the
581 * https://bugzilla.samba.org/show_bug.cgi?id=10260
583 state->processed_file = true;
585 status = state->fn(state->mntpoint, finfo,
587 state->private_data);
588 if (tevent_req_nterror(req, status)) {
595 if (next_offset != 0) {
597 data_len -= next_offset;
599 } while (next_offset != 0);
601 subreq = smb2cli_query_directory_send(
602 state, state->ev, state->conn, state->timeout_msec,
603 state->session, state->tcon, state->level,
604 state->flags, state->file_index,
605 state->fid_persistent, state->fid_volatile,
606 state->mask, state->outbuf_len);
607 if (tevent_req_nomem(subreq, req)) {
610 tevent_req_set_callback(subreq, cli_smb2_listdir_done, req);
613 static NTSTATUS cli_smb2_listdir_recv(struct tevent_req *req)
615 struct cli_smb2_listdir_state *state = tevent_req_data(
616 req, struct cli_smb2_listdir_state);
619 if (tevent_req_is_nterror(req, &status)) {
623 if (!state->processed_file) {
625 * In SMB1 findfirst returns NT_STATUS_NO_SUCH_FILE
626 * if no files match. Emulate this in the client.
628 return NT_STATUS_NO_SUCH_FILE;
634 struct ll_lookup_state {
635 struct mount_state *mstate;
640 static void cli_ll_lookup_done(struct tevent_req *req);
642 static void cli_ll_lookup(fuse_req_t freq, fuse_ino_t parent_ino,
645 struct mount_state *mstate = talloc_get_type_abort(
646 fuse_req_userdata(freq), struct mount_state);
647 struct ll_lookup_state *state;
648 struct tevent_req *req;
649 struct inode_state *parent;
651 DBG_DEBUG("parent_ino=%ju, name=%s\n", (uintmax_t)parent_ino, name);
653 parent = idr_find(mstate->ino_ctx, parent_ino);
654 if (parent == NULL) {
655 DBG_WARNING("could not find parent\n");
656 fuse_reply_err(freq, ENOENT);
660 state = talloc(mstate, struct ll_lookup_state);
662 DBG_WARNING("talloc failed\n");
663 fuse_reply_err(freq, ENOMEM);
666 state->mstate = mstate;
669 state->path = talloc_asprintf(state, "%s%s%s", parent->path,
670 strlen(parent->path) ? "\\": "",
672 if (state->path == NULL) {
674 fuse_reply_err(freq, ENOMEM);
678 req = cli_get_unixattr_send(state, mstate->ev, mstate->cli,
682 fuse_reply_err(freq, ENOMEM);
685 tevent_req_set_callback(req, cli_ll_lookup_done, state);
688 static void cli_ll_lookup_done(struct tevent_req *req)
690 struct ll_lookup_state *state = tevent_req_callback_data(
691 req, struct ll_lookup_state);
692 struct stat sbuf = {0};
693 struct fuse_entry_param e;
694 struct inode_state *ino;
697 status = cli_get_unixattr_recv(req, &sbuf);
699 if (!NT_STATUS_IS_OK(status)) {
700 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
704 ino = inode_state_new(state->mstate, state->path);
706 fuse_reply_err(state->freq, ENOMEM);
710 e = (struct fuse_entry_param) {
713 .generation = 1, /* FIXME */
718 fuse_reply_entry(state->freq, &e);
722 struct ll_getattr_state {
723 struct mount_state *mstate;
725 struct fuse_file_info fi;
728 static void cli_ll_getattr_done(struct tevent_req *req);
730 static void cli_ll_getattr(fuse_req_t freq, fuse_ino_t ino,
731 struct fuse_file_info *fi)
733 struct mount_state *mstate = talloc_get_type_abort(
734 fuse_req_userdata(freq), struct mount_state);
735 struct ll_getattr_state *state;
736 struct inode_state *istate;
737 struct tevent_req *req;
739 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
741 istate = idr_find(mstate->ino_ctx, ino);
742 if (istate == NULL) {
743 fuse_reply_err(freq, ENOENT);
747 state = talloc(mstate, struct ll_getattr_state);
749 fuse_reply_err(freq, ENOMEM);
752 state->mstate = mstate;
755 req = cli_get_unixattr_send(state, mstate->ev, mstate->cli,
759 fuse_reply_err(freq, ENOMEM);
762 tevent_req_set_callback(req, cli_ll_getattr_done, state);
765 static void cli_ll_getattr_done(struct tevent_req *req)
767 struct ll_getattr_state *state = tevent_req_callback_data(
768 req, struct ll_getattr_state);
773 status = cli_get_unixattr_recv(req, &st);
775 if (!NT_STATUS_IS_OK(status)) {
776 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
780 ret = fuse_reply_attr(state->freq, &st, 1);
782 DBG_NOTICE("fuse_reply_attr failed: %s\n",
788 struct ll_open_state {
789 struct mount_state *mstate;
791 struct fuse_file_info fi;
794 static void cli_ll_open_done(struct tevent_req *req);
796 static void cli_ll_open(fuse_req_t freq, fuse_ino_t ino,
797 struct fuse_file_info *fi)
799 struct mount_state *mstate = talloc_get_type_abort(
800 fuse_req_userdata(freq), struct mount_state);
801 struct ll_open_state *state;
802 struct inode_state *istate;
803 struct tevent_req *req;
806 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
808 istate = idr_find(mstate->ino_ctx, ino);
809 if (istate == NULL) {
810 fuse_reply_err(freq, ENOENT);
814 state = talloc(mstate, struct ll_open_state);
816 fuse_reply_err(freq, ENOMEM);
819 state->mstate = mstate;
823 switch (fi->flags & O_ACCMODE) {
825 acc = FILE_GENERIC_READ;
828 acc = FILE_GENERIC_WRITE;
831 acc = FILE_GENERIC_READ|FILE_GENERIC_WRITE;
834 fuse_reply_err(freq, EACCES);
838 req = cli_smb2_create_fnum_send(
839 state, mstate->ev, mstate->cli, istate->path,
840 0, SMB2_IMPERSONATION_IMPERSONATION,
841 acc, FILE_ATTRIBUTE_NORMAL,
842 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
843 FILE_OPEN, FILE_NON_DIRECTORY_FILE);
846 fuse_reply_err(freq, ENOMEM);
849 tevent_req_set_callback(req, cli_ll_open_done, state);
852 static void cli_ll_open_done(struct tevent_req *req)
854 struct ll_open_state *state = tevent_req_callback_data(
855 req, struct ll_open_state);
859 status = cli_smb2_create_fnum_recv(req, &fnum, NULL);
861 if (!NT_STATUS_IS_OK(status)) {
862 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
867 state->fi.direct_io = 0;
868 state->fi.keep_cache = 0;
870 fuse_reply_open(state->freq, &state->fi);
875 struct ll_release_state {
876 struct mount_state *mstate;
881 static void cli_ll_release_done(struct tevent_req *req);
883 static void cli_ll_release(fuse_req_t freq, fuse_ino_t ino,
884 struct fuse_file_info *fi)
886 struct mount_state *mstate = talloc_get_type_abort(
887 fuse_req_userdata(freq), struct mount_state);
888 struct ll_release_state *state;
889 struct inode_state *istate;
890 struct tevent_req *req;
893 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
895 istate = idr_find(mstate->ino_ctx, ino);
896 if (istate == NULL) {
897 fuse_reply_err(freq, ENOENT);
901 state = talloc(mstate, struct ll_release_state);
903 fuse_reply_err(freq, ENOMEM);
906 state->mstate = mstate;
912 req = cli_smb2_close_fnum_send(state, mstate->ev, mstate->cli, fnum);
915 fuse_reply_err(freq, ENOMEM);
918 tevent_req_set_callback(req, cli_ll_release_done, state);
921 static void cli_ll_release_done(struct tevent_req *req)
923 struct ll_release_state *state = tevent_req_callback_data(
924 req, struct ll_release_state);
925 struct inode_state *istate;
928 status = cli_smb2_close_fnum_recv(req);
930 if (!NT_STATUS_IS_OK(status)) {
931 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
935 istate = idr_find(state->mstate->ino_ctx, state->ino);
936 if (istate == NULL) {
937 DEBUG(1, ("%s: inode %ju vanished!\n", __func__,
938 (uintmax_t)state->ino));
942 fuse_reply_err(state->freq, 0);
946 struct ll_read_state {
947 struct mount_state *mstate;
951 static void cli_ll_read_done(struct tevent_req *req);
953 static void cli_ll_read(fuse_req_t freq, fuse_ino_t ino,
954 size_t size, off_t off,
955 struct fuse_file_info *fi)
957 struct mount_state *mstate = talloc_get_type_abort(
958 fuse_req_userdata(freq), struct mount_state);
959 struct ll_read_state *state;
960 struct tevent_req *req;
963 DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino,
964 size, (uintmax_t)off);
966 state = talloc(mstate, struct ll_read_state);
968 fuse_reply_err(freq, ENOMEM);
971 state->mstate = mstate;
976 req = cli_smb2_read_send(state, mstate->ev, mstate->cli,
980 fuse_reply_err(freq, ENOMEM);
983 tevent_req_set_callback(req, cli_ll_read_done, state);
986 static void cli_ll_read_done(struct tevent_req *req)
988 struct ll_read_state *state = tevent_req_callback_data(
989 req, struct ll_read_state);
994 status = cli_smb2_read_recv(req, &received, &rcvbuf);
995 /* no talloc_free here yet */
997 if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
1000 status = NT_STATUS_OK;
1003 if (!NT_STATUS_IS_OK(status)) {
1004 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
1007 fuse_reply_buf(state->freq, (char *)rcvbuf, received);
1011 struct ll_write_state {
1012 struct mount_state *mstate;
1016 static void cli_ll_write_done(struct tevent_req *req);
1018 static void cli_ll_write(fuse_req_t freq, fuse_ino_t ino, const char *buf,
1019 size_t size, off_t off, struct fuse_file_info *fi)
1021 struct mount_state *mstate = talloc_get_type_abort(
1022 fuse_req_userdata(freq), struct mount_state);
1023 struct ll_write_state *state;
1024 struct tevent_req *req;
1027 DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino,
1028 size, (uintmax_t)off);
1030 state = talloc(mstate, struct ll_write_state);
1031 if (state == NULL) {
1032 fuse_reply_err(freq, ENOMEM);
1035 state->mstate = mstate;
1040 req = cli_smb2_write_send(state, mstate->ev, mstate->cli, fnum, 0,
1041 (const uint8_t *)buf, off, size);
1044 fuse_reply_err(freq, ENOMEM);
1047 tevent_req_set_callback(req, cli_ll_write_done, state);
1050 static void cli_ll_write_done(struct tevent_req *req)
1052 struct ll_write_state *state = tevent_req_callback_data(
1053 req, struct ll_write_state);
1057 status = cli_smb2_write_recv(req, &written);
1058 /* no talloc_free here yet */
1059 if (!NT_STATUS_IS_OK(status)) {
1060 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
1063 fuse_reply_write(state->freq, written);
1068 struct ll_dir_state {
1069 uint64_t fid_persistent;
1070 uint64_t fid_volatile;
1072 struct file_info *finfos;
1073 unsigned num_finfos, num_sent;
1076 static bool ll_dir_state_add(struct ll_dir_state *dir_state,
1079 struct file_info *tmp, *finfo;
1081 tmp = talloc_realloc(dir_state, dir_state->finfos,
1082 struct file_info, dir_state->num_finfos+1);
1086 dir_state->finfos = tmp;
1087 finfo = &dir_state->finfos[dir_state->num_finfos];
1089 ZERO_STRUCTP(finfo);
1091 finfo->name = talloc_strdup(dir_state->finfos, name);
1092 if (finfo->name == NULL) {
1095 dir_state->num_finfos += 1;
1100 struct ll_opendir_state {
1101 struct mount_state *mstate;
1103 struct fuse_file_info fi;
1104 struct ll_dir_state *dir_state;
1107 static void cli_ll_opendir_done(struct tevent_req *req);
1109 static void cli_ll_opendir(fuse_req_t freq, fuse_ino_t ino,
1110 struct fuse_file_info *fi)
1112 struct mount_state *mstate = talloc_get_type_abort(
1113 fuse_req_userdata(freq), struct mount_state);
1114 struct cli_state *cli = mstate->cli;
1115 struct ll_opendir_state *state;
1116 struct inode_state *istate;
1117 struct tevent_req *req;
1119 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
1121 istate = idr_find(mstate->ino_ctx, ino);
1122 if (istate == NULL) {
1123 fuse_reply_err(freq, ENOENT);
1127 state = talloc(mstate, struct ll_opendir_state);
1128 if (state == NULL) {
1129 fuse_reply_err(freq, ENOMEM);
1132 state->mstate = mstate;
1136 state->dir_state = talloc_zero(state, struct ll_dir_state);
1137 if (state->dir_state == NULL) {
1139 fuse_reply_err(freq, ENOMEM);
1143 req = smb2cli_create_send(
1144 state, mstate->ev, cli->conn, cli->timeout,
1145 cli->smb2.session, cli->smb2.tcon, istate->path,
1146 0, SMB2_IMPERSONATION_IMPERSONATION,
1147 FILE_GENERIC_READ, FILE_ATTRIBUTE_DIRECTORY,
1148 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
1149 FILE_OPEN, FILE_DIRECTORY_FILE, NULL);
1152 fuse_reply_err(freq, ENOMEM);
1155 tevent_req_set_callback(req, cli_ll_opendir_done, state);
1158 static void cli_ll_opendir_done(struct tevent_req *req)
1160 struct ll_opendir_state *state = tevent_req_callback_data(
1161 req, struct ll_opendir_state);
1164 status = smb2cli_create_recv(req,
1165 &state->dir_state->fid_persistent,
1166 &state->dir_state->fid_volatile,
1170 DEBUG(10, ("%s: smbcli_create_recv returned %s\n", __func__,
1171 nt_errstr(status)));
1173 if (!NT_STATUS_IS_OK(status)) {
1174 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
1178 state->fi.fh = (uint64_t)talloc_move(state->mstate, &state->dir_state);
1179 state->fi.direct_io = 0;
1180 state->fi.keep_cache = 0;
1182 fuse_reply_open(state->freq, &state->fi);
1187 struct ll_readdir_state {
1189 struct ll_dir_state *dir_state;
1192 static void cli_ll_readdir_done(struct tevent_req *subreq);
1193 static NTSTATUS cli_ll_readdir_one(const char *mnt, struct file_info *finfo,
1194 const char *path, void *private_data);
1195 static void cli_ll_readdir_reply_one(fuse_req_t freq,
1196 struct ll_dir_state *dir_state);
1198 static void cli_ll_readdir(fuse_req_t freq, fuse_ino_t ino, size_t size,
1199 off_t off, struct fuse_file_info *fi)
1201 struct mount_state *mstate = talloc_get_type_abort(
1202 fuse_req_userdata(freq), struct mount_state);
1203 struct cli_state *cli = mstate->cli;
1204 struct ll_dir_state *dir_state;
1205 struct ll_readdir_state *state;
1206 struct tevent_req *req;
1208 DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino, size,
1211 dir_state = talloc_get_type_abort((void *)fi->fh, struct ll_dir_state);
1213 if (dir_state->finfos != NULL) {
1214 DBG_DEBUG("finfos=%p\n", dir_state->finfos);
1215 cli_ll_readdir_reply_one(freq, dir_state);
1219 if (!ll_dir_state_add(dir_state, ".") ||
1220 !ll_dir_state_add(dir_state, "..")) {
1221 fuse_reply_err(freq, ENOMEM);
1225 state = talloc(mstate, struct ll_readdir_state);
1226 if (state == NULL) {
1227 fuse_reply_err(freq, ENOMEM);
1231 state->dir_state = dir_state;
1233 req = cli_smb2_listdir_send(
1234 state, mstate->ev, cli->conn, cli->timeout,
1235 cli->smb2.session, cli->smb2.tcon,
1236 SMB2_FIND_ID_BOTH_DIRECTORY_INFO, 0, 0,
1237 dir_state->fid_persistent, dir_state->fid_volatile,
1239 FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM |
1240 FILE_ATTRIBUTE_HIDDEN,
1241 NULL, NULL, cli_ll_readdir_one, dir_state);
1244 fuse_reply_err(freq, ENOMEM);
1247 tevent_req_set_callback(req, cli_ll_readdir_done, state);
1250 static void cli_ll_readdir_reply_one(fuse_req_t freq,
1251 struct ll_dir_state *dir_state)
1253 struct file_info *finfo;
1255 struct stat sbuf = {};
1258 if (dir_state->num_finfos == dir_state->num_sent) {
1259 DEBUG(10, ("%s: Done\n", __func__));
1260 fuse_reply_buf(freq, NULL, 0);
1264 sbuf.st_mode = S_IFREG | 0755;
1265 sbuf.st_ino = random(); /* FIXME :-) */
1266 finfo = &dir_state->finfos[dir_state->num_sent];
1268 DBG_DEBUG("Adding %s\n", finfo->name);
1270 buflen = fuse_add_direntry(freq, buf, sizeof(buf),
1271 finfo->name, &sbuf, 0);
1272 fuse_reply_buf(freq, buf, buflen);
1273 dir_state->num_sent += 1;
1277 static NTSTATUS cli_ll_readdir_one(const char *mnt, struct file_info *finfo,
1278 const char *path, void *private_data)
1280 struct ll_dir_state *dir_state = talloc_get_type_abort(
1281 private_data, struct ll_dir_state);
1283 if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
1284 DEBUG(10, ("%s: Ignoring %s\n", __func__, finfo->name));
1285 return NT_STATUS_OK;
1288 DBG_DEBUG("Got entry %s\n", finfo->name);
1290 if (!ll_dir_state_add(dir_state, finfo->name)) {
1291 return NT_STATUS_NO_MEMORY;
1293 return NT_STATUS_OK;
1296 static void cli_ll_readdir_done(struct tevent_req *req)
1298 struct ll_readdir_state *state = tevent_req_callback_data(
1299 req, struct ll_readdir_state);
1302 status = cli_smb2_listdir_recv(req);
1304 if (!NT_STATUS_IS_OK(status)) {
1305 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
1308 cli_ll_readdir_reply_one(state->freq, state->dir_state);
1313 struct ll_releasedir_state {
1314 struct mount_state *mstate;
1316 struct ll_dir_state *dir_state;
1319 static void cli_ll_releasedir_done(struct tevent_req *req);
1321 static void cli_ll_releasedir(fuse_req_t freq, fuse_ino_t ino,
1322 struct fuse_file_info *fi)
1324 struct mount_state *mstate = talloc_get_type_abort(
1325 fuse_req_userdata(freq), struct mount_state);
1326 struct cli_state *cli = mstate->cli;
1327 struct ll_releasedir_state *state;
1328 struct tevent_req *req;
1330 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
1332 state = talloc(mstate, struct ll_releasedir_state);
1333 if (state == NULL) {
1334 fuse_reply_err(freq, ENOMEM);
1337 state->mstate = mstate;
1340 state->dir_state = talloc_get_type_abort(
1341 (void *)fi->fh, struct ll_dir_state);
1343 req = smb2cli_close_send(state, mstate->ev, cli->conn, cli->timeout,
1344 cli->smb2.session, cli->smb2.tcon, 0,
1345 state->dir_state->fid_persistent,
1346 state->dir_state->fid_volatile);
1349 fuse_reply_err(freq, ENOMEM);
1352 tevent_req_set_callback(req, cli_ll_releasedir_done, state);
1355 static void cli_ll_releasedir_done(struct tevent_req *req)
1357 struct ll_releasedir_state *state = tevent_req_callback_data(
1358 req, struct ll_releasedir_state);
1361 status = smb2cli_close_recv(req);
1363 if (!NT_STATUS_IS_OK(status)) {
1364 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
1367 TALLOC_FREE(state->dir_state);
1368 fuse_reply_err(state->freq, 0);
1372 static struct fuse_lowlevel_ops cli_ll_ops = {
1373 .lookup = cli_ll_lookup,
1374 .getattr = cli_ll_getattr,
1375 .open = cli_ll_open,
1376 .create = cli_ll_create,
1377 .release = cli_ll_release,
1378 .read = cli_ll_read,
1379 .write = cli_ll_write,
1380 .opendir = cli_ll_opendir,
1381 .readdir = cli_ll_readdir,
1382 .releasedir = cli_ll_releasedir,
1385 static void fuse_chan_fd_handler(struct tevent_context *ev,
1386 struct tevent_fd *fde,
1388 void *private_data);
1389 static void fuse_chan_signal_handler(struct tevent_context *ev,
1390 struct tevent_signal *se,
1394 void *private_data);
1396 static int mount_state_destructor(struct mount_state *s);
1398 int do_mount(struct cli_state *cli, const char *mountpoint)
1400 struct mount_state *state;
1401 struct inode_state *ino;
1402 struct fuse_args args = { 0 };
1406 state = talloc_zero(talloc_tos(), struct mount_state);
1407 if (state == NULL) {
1408 fprintf(stderr, "talloc failed\n");
1412 state->ev = tevent_context_init(state);
1413 if (state->ev == NULL) {
1414 fprintf(stderr, "tevent_context_init failed\n");
1419 state->ino_ctx = idr_init(state);
1420 if (state->ino_ctx == NULL) {
1421 fprintf(stderr, "idr_init failed\n");
1426 state->ino_parent = talloc_new(state);
1427 if (state->ino_parent == NULL) {
1428 fprintf(stderr, "talloc_new failed\n");
1433 talloc_set_destructor(state, mount_state_destructor);
1435 ino = inode_state_new(state, "");
1437 fprintf(stderr, "inode_state_new failed\n");
1441 if (ino->ino != FUSE_ROOT_ID) {
1442 fprintf(stderr, "first inode gave %d, expected %d\n",
1443 (int)ino->ino, FUSE_ROOT_ID);
1450 state->ch = fuse_mount(mountpoint, &args);
1451 if (state->ch == NULL) {
1452 perror("fuse_mount failed");
1456 state->bufsize = fuse_chan_bufsize(state->ch);
1457 state->buf = talloc_array(state, char, state->bufsize);
1458 if (state->buf == NULL) {
1459 fprintf(stderr, "talloc failed\n");
1463 fd = fuse_chan_fd(state->ch);
1465 state->fde = tevent_add_fd(state->ev, state, fd, TEVENT_FD_READ,
1466 fuse_chan_fd_handler, state);
1467 if (state->fde == NULL) {
1468 fprintf(stderr, "tevent_add_fd failed\n");
1472 state->signal_ev = tevent_add_signal(state->ev, state, SIGINT, 0,
1473 fuse_chan_signal_handler, state);
1474 if (state->signal_ev == NULL) {
1475 fprintf(stderr, "tevent_add_signal failed\n");
1479 state->se = fuse_lowlevel_new(&args, &cli_ll_ops, sizeof(cli_ll_ops),
1481 if (state->se == NULL) {
1482 perror("fuse_lowlevel_new failed");
1486 fuse_session_add_chan(state->se, state->ch);
1488 while (!state->done) {
1489 ret = tevent_loop_once(state->ev);
1491 perror("tevent_loop_once failed");
1496 fuse_session_remove_chan(state->ch);
1497 fuse_session_destroy(state->se);
1499 fuse_unmount(mountpoint, state->ch);
1505 static int mount_state_destructor(struct mount_state *s)
1507 TALLOC_FREE(s->ino_parent);
1512 static void fuse_chan_fd_handler(struct tevent_context *ev,
1513 struct tevent_fd *fde,
1517 struct mount_state *state = talloc_get_type_abort(
1518 private_data, struct mount_state);
1521 if ((flags & TEVENT_FD_READ) == 0) {
1525 recvd = fuse_chan_recv(&state->ch, state->buf, state->bufsize);
1530 fuse_session_process(state->se, state->buf, recvd, state->ch);
1533 static void fuse_chan_signal_handler(struct tevent_context *ev,
1534 struct tevent_signal *se,
1540 struct mount_state *state = talloc_get_type_abort(
1541 private_data, struct mount_state);