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"
34 #include "lib/util/idtree.h"
37 struct tevent_context
*ev
;
38 struct cli_state
*cli
;
41 struct tevent_fd
*fde
;
42 struct tevent_signal
*signal_ev
;
45 struct fuse_session
*se
;
50 struct idr_context
*ino_ctx
;
51 TALLOC_CTX
*ino_parent
;
55 struct idr_context
*ino_ctx
;
60 static int inode_state_destructor(struct inode_state
*s
);
62 static struct inode_state
*inode_state_init(TALLOC_CTX
*mem_ctx
,
63 struct idr_context
*ino_ctx
,
66 struct inode_state
*state
;
70 pathlen
= strlen(path
);
72 mem_ctx
, offsetof(struct inode_state
, path
) + pathlen
+ 1);
76 talloc_set_name_const(state
, "struct inode_state");
78 ino
= idr_get_new_above(ino_ctx
, state
, 1, INT32_MAX
);
85 state
->ino_ctx
= ino_ctx
;
86 memcpy(state
->path
, path
, pathlen
+ 1);
88 DBG_DEBUG("Creating ino %d for path %s\n", ino
, path
);
90 talloc_set_destructor(state
, inode_state_destructor
);
95 static struct inode_state
*inode_state_new(struct mount_state
*mstate
,
98 return inode_state_init(mstate
->ino_parent
, mstate
->ino_ctx
, path
);
101 static int inode_state_destructor(struct inode_state
*s
)
103 DBG_DEBUG("destroying inode %ju\n", (uintmax_t)s
->ino
);
104 idr_remove(s
->ino_ctx
, s
->ino
);
108 struct ll_create_state
{
109 struct mount_state
*mstate
;
111 struct fuse_file_info fi
;
115 static void cli_ll_create_done(struct tevent_req
*req
);
117 static void cli_ll_create(fuse_req_t freq
, fuse_ino_t parent
, const char *name
,
118 mode_t mode
, struct fuse_file_info
*fi
)
120 struct mount_state
*mstate
= talloc_get_type_abort(
121 fuse_req_userdata(freq
), struct mount_state
);
122 struct ll_create_state
*state
;
123 struct inode_state
*istate
;
124 struct tevent_req
*req
;
126 DBG_DEBUG("parent=%ju, name=%s, mode=%x\n", (uintmax_t)parent
,
127 name
, (unsigned)mode
);
129 istate
= idr_find(mstate
->ino_ctx
, parent
);
130 if (istate
== NULL
) {
131 fuse_reply_err(freq
, ENOENT
);
135 state
= talloc(mstate
, struct ll_create_state
);
137 fuse_reply_err(freq
, ENOMEM
);
140 state
->mstate
= mstate
;
144 state
->path
= talloc_asprintf(state
, "%s%s%s", istate
->path
,
145 strlen(istate
->path
) ? "\\": "",
147 if (state
->path
== NULL
) {
149 fuse_reply_err(freq
, ENOMEM
);
153 req
= cli_smb2_create_fnum_send(
156 mstate
->cli
, state
->path
,
157 (struct cli_smb2_create_flags
){0},
158 SMB2_IMPERSONATION_IMPERSONATION
,
159 FILE_GENERIC_READ
|FILE_GENERIC_WRITE
,
160 FILE_ATTRIBUTE_NORMAL
,
161 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
163 FILE_NON_DIRECTORY_FILE
,
167 fuse_reply_err(freq
, ENOMEM
);
170 tevent_req_set_callback(req
, cli_ll_create_done
, state
);
173 static void cli_ll_create_done(struct tevent_req
*req
)
175 struct ll_create_state
*state
= tevent_req_callback_data(
176 req
, struct ll_create_state
);
177 struct fuse_entry_param e
;
178 struct inode_state
*ino
;
182 status
= cli_smb2_create_fnum_recv(req
, &fnum
, NULL
, NULL
, NULL
, NULL
);
184 if (!NT_STATUS_IS_OK(status
)) {
185 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
190 state
->fi
.direct_io
= 0;
191 state
->fi
.keep_cache
= 0;
193 ino
= inode_state_new(state
->mstate
, state
->path
);
195 fuse_reply_err(state
->freq
, ENOMEM
);
199 e
= (struct fuse_entry_param
) {
201 .generation
= 1, /* FIXME */
206 fuse_reply_create(state
->freq
, &e
, &state
->fi
);
211 struct cli_get_unixattr_state
{
212 struct tevent_context
*ev
;
213 struct cli_state
*cli
;
214 uint64_t fid_persistent
;
215 uint64_t fid_volatile
;
217 struct timespec create_time
;
218 struct timespec access_time
;
219 struct timespec write_time
;
220 struct timespec change_time
;
226 static void cli_get_unixattr_opened(struct tevent_req
*subreq
);
227 static void cli_get_unixattr_gotinfo(struct tevent_req
*subreq
);
228 static void cli_get_unixattr_closed(struct tevent_req
*subreq
);
231 static struct tevent_req
*cli_get_unixattr_send(TALLOC_CTX
*mem_ctx
,
232 struct tevent_context
*ev
,
233 struct cli_state
*cli
,
236 struct tevent_req
*req
, *subreq
;
237 struct cli_get_unixattr_state
*state
;
239 req
= tevent_req_create(mem_ctx
, &state
,
240 struct cli_get_unixattr_state
);
247 subreq
= smb2cli_create_send(
248 state
, ev
, cli
->conn
, cli
->timeout
, cli
->smb2
.session
,
249 cli
->smb2
.tcon
, path
, SMB2_OPLOCK_LEVEL_NONE
,
250 SMB2_IMPERSONATION_IMPERSONATION
,
251 SYNCHRONIZE_ACCESS
|FILE_READ_ATTRIBUTES
, 0,
252 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
254 if (tevent_req_nomem(subreq
, req
)) {
255 return tevent_req_post(req
, ev
);
257 tevent_req_set_callback(subreq
, cli_get_unixattr_opened
, req
);
262 static void cli_get_unixattr_opened(struct tevent_req
*subreq
)
264 struct tevent_req
*req
= tevent_req_callback_data(
265 subreq
, struct tevent_req
);
266 struct cli_get_unixattr_state
*state
= tevent_req_data(
267 req
, struct cli_get_unixattr_state
);
268 struct cli_state
*cli
= state
->cli
;
271 status
= smb2cli_create_recv(
273 &state
->fid_persistent
,
274 &state
->fid_volatile
,
280 if (tevent_req_nterror(req
, status
)) {
281 DBG_DEBUG("smb2cli_create_recv returned %s\n",
286 subreq
= smb2cli_query_info_send(
293 1, /* in_info_type */
294 FSCC_FILE_ALL_INFORMATION
, /* in_file_info_class */
295 0xFFFF, /* in_max_output_length */
296 NULL
, /* in_input_buffer */
297 0, /* in_additional_info */
299 state
->fid_persistent
,
300 state
->fid_volatile
);
301 if (tevent_req_nomem(subreq
, req
)) {
304 tevent_req_set_callback(subreq
, cli_get_unixattr_gotinfo
, req
);
307 static void cli_get_unixattr_gotinfo(struct tevent_req
*subreq
)
309 struct tevent_req
*req
= tevent_req_callback_data(
310 subreq
, struct tevent_req
);
311 struct cli_get_unixattr_state
*state
= tevent_req_data(
312 req
, struct cli_get_unixattr_state
);
313 struct cli_state
*cli
= state
->cli
;
317 status
= smb2cli_query_info_recv(subreq
, state
, &outbuf
);
319 if (tevent_req_nterror(req
, status
)) {
320 DBG_DEBUG("smb2cli_query_info_recv returned %s\n",
325 if (outbuf
.length
< 0x60) {
326 tevent_req_nterror(req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
330 state
->create_time
= interpret_long_date(BVAL(outbuf
.data
, 0));
331 state
->access_time
= interpret_long_date(BVAL(outbuf
.data
, 0x8));
332 state
->write_time
= interpret_long_date(BVAL(outbuf
.data
, 0x10));
333 state
->change_time
= interpret_long_date(BVAL(outbuf
.data
, 0x18));
334 state
->mode
= IVAL(outbuf
.data
, 0x20);
335 state
->size
= BVAL(outbuf
.data
, 0x30);
336 state
->ino
= BVAL(outbuf
.data
, 0x40);
338 subreq
= smb2cli_close_send(state
, state
->ev
, cli
->conn
, 0,
339 cli
->smb2
.session
, cli
->smb2
.tcon
, 0,
340 state
->fid_persistent
,
341 state
->fid_volatile
);
342 if (tevent_req_nomem(subreq
, req
)) {
345 tevent_req_set_callback(subreq
, cli_get_unixattr_closed
, req
);
348 static void cli_get_unixattr_closed(struct tevent_req
*subreq
)
350 struct tevent_req
*req
= tevent_req_callback_data(
351 subreq
, struct tevent_req
);
354 status
= smb2cli_close_recv(subreq
);
356 if (tevent_req_nterror(req
, status
)) {
359 tevent_req_done(req
);
362 static NTSTATUS
cli_get_unixattr_recv(struct tevent_req
*req
,
365 struct cli_get_unixattr_state
*state
= tevent_req_data(
366 req
, struct cli_get_unixattr_state
);
369 if (tevent_req_is_nterror(req
, &status
)) {
373 if (state
->mode
& FILE_ATTRIBUTE_DIRECTORY
) {
374 st
->st_mode
= (S_IFDIR
| 0555);
377 st
->st_mode
= (S_IFREG
| 0444);
381 st
->st_size
= state
->size
;
382 st
->st_uid
= getuid();
383 st
->st_gid
= getgid();
384 st
->st_ino
= state
->ino
;
385 st
->st_atime
= convert_timespec_to_time_t(state
->access_time
);
386 st
->st_ctime
= convert_timespec_to_time_t(state
->change_time
);
387 st
->st_mtime
= convert_timespec_to_time_t(state
->write_time
);
392 struct cli_smb2_listdir_state
{
393 struct tevent_context
*ev
;
394 struct smbXcli_conn
*conn
;
395 uint32_t timeout_msec
;
396 struct smbXcli_session
*session
;
397 struct smbXcli_tcon
*tcon
;
401 uint64_t fid_persistent
;
402 uint64_t fid_volatile
;
407 const char *mntpoint
;
408 const char *pathname
;
409 NTSTATUS (*fn
)(const char *mntpoint
, struct file_info
*f
,
410 const char *mask
, void *private_data
);
415 static void cli_smb2_listdir_done(struct tevent_req
*subreq
);
417 static struct tevent_req
*cli_smb2_listdir_send(
419 struct tevent_context
*ev
,
420 struct smbXcli_conn
*conn
,
421 uint32_t timeout_msec
,
422 struct smbXcli_session
*session
,
423 struct smbXcli_tcon
*tcon
,
427 uint64_t fid_persistent
,
428 uint64_t fid_volatile
,
432 const char *mntpoint
,
433 const char *pathname
,
434 NTSTATUS (*fn
)(const char *mntpoint
, struct file_info
*f
,
435 const char *mask
, void *private_data
),
438 struct tevent_req
*req
, *subreq
;
439 struct cli_smb2_listdir_state
*state
;
441 req
= tevent_req_create(mem_ctx
, &state
,
442 struct cli_smb2_listdir_state
);
448 state
->timeout_msec
= timeout_msec
;
449 state
->session
= session
;
451 state
->level
= level
;
452 state
->flags
= flags
;
453 state
->file_index
= file_index
;
454 state
->fid_persistent
= fid_persistent
;
455 state
->fid_volatile
= fid_volatile
;
457 state
->outbuf_len
= outbuf_len
;
458 state
->attribute
= attribute
;
459 state
->mntpoint
= mntpoint
;
460 state
->pathname
= pathname
;
462 state
->private_data
= private_data
;
464 subreq
= smb2cli_query_directory_send(
465 state
, state
->ev
, state
->conn
, state
->timeout_msec
,
466 state
->session
, state
->tcon
, state
->level
,
467 state
->flags
, state
->file_index
,
468 state
->fid_persistent
, state
->fid_volatile
,
469 state
->mask
, state
->outbuf_len
);
470 if (tevent_req_nomem(subreq
, req
)) {
471 return tevent_req_post(req
, ev
);
473 tevent_req_set_callback(subreq
, cli_smb2_listdir_done
, req
);
477 static NTSTATUS
parse_finfo_id_both_directory_info(uint8_t *dir_data
,
478 uint32_t dir_data_length
,
479 struct file_info
*finfo
,
480 uint32_t *next_offset
)
486 if (dir_data_length
< 4) {
487 return NT_STATUS_INFO_LENGTH_MISMATCH
;
490 *next_offset
= IVAL(dir_data
, 0);
492 if (*next_offset
> dir_data_length
) {
493 return NT_STATUS_INFO_LENGTH_MISMATCH
;
496 if (*next_offset
!= 0) {
497 /* Ensure we only read what in this record. */
498 dir_data_length
= *next_offset
;
501 if (dir_data_length
< 105) {
502 return NT_STATUS_INFO_LENGTH_MISMATCH
;
505 finfo
->btime_ts
= interpret_long_date(BVAL(dir_data
, 8));
506 finfo
->atime_ts
= interpret_long_date(BVAL(dir_data
, 16));
507 finfo
->mtime_ts
= interpret_long_date(BVAL(dir_data
, 24));
508 finfo
->ctime_ts
= interpret_long_date(BVAL(dir_data
, 32));
509 finfo
->size
= IVAL2_TO_SMB_BIG_UINT(dir_data
+ 40, 0);
510 finfo
->attr
= IVAL(dir_data
+ 56, 0);
511 namelen
= IVAL(dir_data
+ 60,0);
512 if (namelen
> (dir_data_length
- 104)) {
513 return NT_STATUS_INFO_LENGTH_MISMATCH
;
515 slen
= CVAL(dir_data
+ 68, 0);
517 return NT_STATUS_INFO_LENGTH_MISMATCH
;
519 ret
= pull_string_talloc(finfo
,
521 FLAGS2_UNICODE_STRINGS
,
526 if (ret
== (size_t)-1) {
527 /* Bad conversion. */
528 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
531 ret
= pull_string_talloc(finfo
,
533 FLAGS2_UNICODE_STRINGS
,
538 if (ret
== (size_t)-1) {
539 /* Bad conversion. */
540 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
545 static void cli_smb2_listdir_done(struct tevent_req
*subreq
)
547 struct tevent_req
*req
= tevent_req_callback_data(
548 subreq
, struct tevent_req
);
549 struct cli_smb2_listdir_state
*state
= tevent_req_data(
550 req
, struct cli_smb2_listdir_state
);
553 uint32_t next_offset
= 0;
556 status
= smb2cli_query_directory_recv(subreq
, state
, &data
,
559 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
)) {
560 tevent_req_done(req
);
563 if (tevent_req_nterror(req
, status
)) {
568 struct file_info
*finfo
;
571 finfo
= talloc_zero(state
, struct file_info
);
572 if (tevent_req_nomem(finfo
, req
)) {
576 status
= parse_finfo_id_both_directory_info(
577 data
, data_len
, finfo
, &next_offset
);
579 DEBUG(10, ("%s: parse_finfo_id_both_directory_info returned "
580 "%s\n", __func__
, nt_errstr(status
)));
582 if (tevent_req_nterror(req
, status
)) {
586 ok
= dir_check_ftype(finfo
->attr
, state
->attribute
);
588 DEBUG(10, ("%s: dir_check_ftype(%u,%u) returned %u\n",
589 __func__
, (unsigned)finfo
->attr
,
590 (unsigned)state
->attribute
, (unsigned)ok
));
594 * Only process if attributes match. On SMB1 server
595 * does this, so on SMB2 we need to emulate in the
598 * https://bugzilla.samba.org/show_bug.cgi?id=10260
600 state
->processed_file
= true;
602 status
= state
->fn(state
->mntpoint
, finfo
,
604 state
->private_data
);
605 if (tevent_req_nterror(req
, status
)) {
612 if (next_offset
!= 0) {
614 data_len
-= next_offset
;
616 } while (next_offset
!= 0);
618 subreq
= smb2cli_query_directory_send(
619 state
, state
->ev
, state
->conn
, state
->timeout_msec
,
620 state
->session
, state
->tcon
, state
->level
,
621 state
->flags
, state
->file_index
,
622 state
->fid_persistent
, state
->fid_volatile
,
623 state
->mask
, state
->outbuf_len
);
624 if (tevent_req_nomem(subreq
, req
)) {
627 tevent_req_set_callback(subreq
, cli_smb2_listdir_done
, req
);
630 static NTSTATUS
cli_smb2_listdir_recv(struct tevent_req
*req
)
632 struct cli_smb2_listdir_state
*state
= tevent_req_data(
633 req
, struct cli_smb2_listdir_state
);
636 if (tevent_req_is_nterror(req
, &status
)) {
640 if (!state
->processed_file
) {
642 * In SMB1 findfirst returns NT_STATUS_NO_SUCH_FILE
643 * if no files match. Emulate this in the client.
645 return NT_STATUS_NO_SUCH_FILE
;
651 struct ll_lookup_state
{
652 struct mount_state
*mstate
;
657 static void cli_ll_lookup_done(struct tevent_req
*req
);
659 static void cli_ll_lookup(fuse_req_t freq
, fuse_ino_t parent_ino
,
662 struct mount_state
*mstate
= talloc_get_type_abort(
663 fuse_req_userdata(freq
), struct mount_state
);
664 struct ll_lookup_state
*state
;
665 struct tevent_req
*req
;
666 struct inode_state
*parent
;
668 DBG_DEBUG("parent_ino=%ju, name=%s\n", (uintmax_t)parent_ino
, name
);
670 parent
= idr_find(mstate
->ino_ctx
, parent_ino
);
671 if (parent
== NULL
) {
672 DBG_WARNING("could not find parent\n");
673 fuse_reply_err(freq
, ENOENT
);
677 state
= talloc(mstate
, struct ll_lookup_state
);
679 DBG_WARNING("talloc failed\n");
680 fuse_reply_err(freq
, ENOMEM
);
683 state
->mstate
= mstate
;
686 state
->path
= talloc_asprintf(state
, "%s%s%s", parent
->path
,
687 strlen(parent
->path
) ? "\\": "",
689 if (state
->path
== NULL
) {
691 fuse_reply_err(freq
, ENOMEM
);
695 req
= cli_get_unixattr_send(state
, mstate
->ev
, mstate
->cli
,
699 fuse_reply_err(freq
, ENOMEM
);
702 tevent_req_set_callback(req
, cli_ll_lookup_done
, state
);
705 static void cli_ll_lookup_done(struct tevent_req
*req
)
707 struct ll_lookup_state
*state
= tevent_req_callback_data(
708 req
, struct ll_lookup_state
);
709 struct stat sbuf
= {0};
710 struct fuse_entry_param e
;
711 struct inode_state
*ino
;
714 status
= cli_get_unixattr_recv(req
, &sbuf
);
716 if (!NT_STATUS_IS_OK(status
)) {
717 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
721 ino
= inode_state_new(state
->mstate
, state
->path
);
723 fuse_reply_err(state
->freq
, ENOMEM
);
727 e
= (struct fuse_entry_param
) {
730 .generation
= 1, /* FIXME */
735 fuse_reply_entry(state
->freq
, &e
);
740 cli_ll_forget(fuse_req_t freq
, fuse_ino_t ino
, unsigned long nlookup
)
742 struct mount_state
*mstate
=
743 talloc_get_type_abort(fuse_req_userdata(freq
),
745 struct inode_state
*istate
= NULL
;
747 DBG_DEBUG("ino=%ju, nlookup=%lu\n", (uintmax_t)ino
, nlookup
);
749 istate
= idr_find(mstate
->ino_ctx
, ino
);
750 if (istate
== NULL
) {
751 fuse_reply_err(freq
, ENOENT
);
755 fuse_reply_none(freq
);
758 struct ll_getattr_state
{
759 struct mount_state
*mstate
;
761 struct fuse_file_info fi
;
764 static void cli_ll_getattr_done(struct tevent_req
*req
);
766 static void cli_ll_getattr(fuse_req_t freq
, fuse_ino_t ino
,
767 struct fuse_file_info
*fi
)
769 struct mount_state
*mstate
= talloc_get_type_abort(
770 fuse_req_userdata(freq
), struct mount_state
);
771 struct ll_getattr_state
*state
;
772 struct inode_state
*istate
;
773 struct tevent_req
*req
;
775 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino
);
777 istate
= idr_find(mstate
->ino_ctx
, ino
);
778 if (istate
== NULL
) {
779 fuse_reply_err(freq
, ENOENT
);
783 state
= talloc(mstate
, struct ll_getattr_state
);
785 fuse_reply_err(freq
, ENOMEM
);
788 state
->mstate
= mstate
;
791 req
= cli_get_unixattr_send(state
, mstate
->ev
, mstate
->cli
,
795 fuse_reply_err(freq
, ENOMEM
);
798 tevent_req_set_callback(req
, cli_ll_getattr_done
, state
);
801 static void cli_ll_getattr_done(struct tevent_req
*req
)
803 struct ll_getattr_state
*state
= tevent_req_callback_data(
804 req
, struct ll_getattr_state
);
809 status
= cli_get_unixattr_recv(req
, &st
);
811 if (!NT_STATUS_IS_OK(status
)) {
812 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
816 ret
= fuse_reply_attr(state
->freq
, &st
, 1);
818 DBG_NOTICE("fuse_reply_attr failed: %s\n",
824 struct ll_open_state
{
825 struct mount_state
*mstate
;
827 struct fuse_file_info fi
;
830 static void cli_ll_open_done(struct tevent_req
*req
);
832 static void cli_ll_open(fuse_req_t freq
, fuse_ino_t ino
,
833 struct fuse_file_info
*fi
)
835 struct mount_state
*mstate
= talloc_get_type_abort(
836 fuse_req_userdata(freq
), struct mount_state
);
837 struct ll_open_state
*state
;
838 struct inode_state
*istate
;
839 struct tevent_req
*req
;
842 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino
);
844 istate
= idr_find(mstate
->ino_ctx
, ino
);
845 if (istate
== NULL
) {
846 fuse_reply_err(freq
, ENOENT
);
850 state
= talloc(mstate
, struct ll_open_state
);
852 fuse_reply_err(freq
, ENOMEM
);
855 state
->mstate
= mstate
;
859 switch (fi
->flags
& O_ACCMODE
) {
861 acc
= FILE_GENERIC_READ
;
864 acc
= FILE_GENERIC_WRITE
;
867 acc
= FILE_GENERIC_READ
|FILE_GENERIC_WRITE
;
870 fuse_reply_err(freq
, EACCES
);
874 req
= cli_smb2_create_fnum_send(
879 (struct cli_smb2_create_flags
){0},
880 SMB2_IMPERSONATION_IMPERSONATION
,
882 FILE_ATTRIBUTE_NORMAL
,
883 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
885 FILE_NON_DIRECTORY_FILE
,
889 fuse_reply_err(freq
, ENOMEM
);
892 tevent_req_set_callback(req
, cli_ll_open_done
, state
);
895 static void cli_ll_open_done(struct tevent_req
*req
)
897 struct ll_open_state
*state
= tevent_req_callback_data(
898 req
, struct ll_open_state
);
902 status
= cli_smb2_create_fnum_recv(req
, &fnum
, NULL
, NULL
, NULL
, NULL
);
904 if (!NT_STATUS_IS_OK(status
)) {
905 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
910 state
->fi
.direct_io
= 0;
911 state
->fi
.keep_cache
= 0;
913 fuse_reply_open(state
->freq
, &state
->fi
);
918 struct ll_release_state
{
919 struct mount_state
*mstate
;
924 static void cli_ll_release_done(struct tevent_req
*req
);
926 static void cli_ll_release(fuse_req_t freq
, fuse_ino_t ino
,
927 struct fuse_file_info
*fi
)
929 struct mount_state
*mstate
= talloc_get_type_abort(
930 fuse_req_userdata(freq
), struct mount_state
);
931 struct ll_release_state
*state
;
932 struct inode_state
*istate
;
933 struct tevent_req
*req
;
936 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino
);
938 istate
= idr_find(mstate
->ino_ctx
, ino
);
939 if (istate
== NULL
) {
940 fuse_reply_err(freq
, ENOENT
);
944 state
= talloc(mstate
, struct ll_release_state
);
946 fuse_reply_err(freq
, ENOMEM
);
949 state
->mstate
= mstate
;
955 req
= cli_smb2_close_fnum_send(state
, mstate
->ev
, mstate
->cli
, fnum
, 0);
958 fuse_reply_err(freq
, ENOMEM
);
961 tevent_req_set_callback(req
, cli_ll_release_done
, state
);
964 static void cli_ll_release_done(struct tevent_req
*req
)
966 struct ll_release_state
*state
= tevent_req_callback_data(
967 req
, struct ll_release_state
);
968 struct inode_state
*istate
;
971 status
= cli_smb2_close_fnum_recv(req
);
973 if (!NT_STATUS_IS_OK(status
)) {
974 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
978 istate
= idr_find(state
->mstate
->ino_ctx
, state
->ino
);
979 if (istate
== NULL
) {
980 DEBUG(1, ("%s: inode %ju vanished!\n", __func__
,
981 (uintmax_t)state
->ino
));
985 fuse_reply_err(state
->freq
, 0);
989 struct ll_read_state
{
990 struct mount_state
*mstate
;
994 static void cli_ll_read_done(struct tevent_req
*req
);
996 static void cli_ll_read(fuse_req_t freq
, fuse_ino_t ino
,
997 size_t size
, off_t off
,
998 struct fuse_file_info
*fi
)
1000 struct mount_state
*mstate
= talloc_get_type_abort(
1001 fuse_req_userdata(freq
), struct mount_state
);
1002 struct ll_read_state
*state
;
1003 struct tevent_req
*req
;
1006 DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino
,
1007 size
, (uintmax_t)off
);
1009 state
= talloc(mstate
, struct ll_read_state
);
1010 if (state
== NULL
) {
1011 fuse_reply_err(freq
, ENOMEM
);
1014 state
->mstate
= mstate
;
1019 req
= cli_smb2_read_send(state
, mstate
->ev
, mstate
->cli
,
1023 fuse_reply_err(freq
, ENOMEM
);
1026 tevent_req_set_callback(req
, cli_ll_read_done
, state
);
1029 static void cli_ll_read_done(struct tevent_req
*req
)
1031 struct ll_read_state
*state
= tevent_req_callback_data(
1032 req
, struct ll_read_state
);
1037 status
= cli_smb2_read_recv(req
, &received
, &rcvbuf
);
1038 /* no talloc_free here yet */
1040 if (NT_STATUS_EQUAL(status
, NT_STATUS_END_OF_FILE
)) {
1043 status
= NT_STATUS_OK
;
1046 if (!NT_STATUS_IS_OK(status
)) {
1047 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
1050 fuse_reply_buf(state
->freq
, (char *)rcvbuf
, received
);
1054 struct ll_write_state
{
1055 struct mount_state
*mstate
;
1059 static void cli_ll_write_done(struct tevent_req
*req
);
1061 static void cli_ll_write(fuse_req_t freq
, fuse_ino_t ino
, const char *buf
,
1062 size_t size
, off_t off
, struct fuse_file_info
*fi
)
1064 struct mount_state
*mstate
= talloc_get_type_abort(
1065 fuse_req_userdata(freq
), struct mount_state
);
1066 struct ll_write_state
*state
;
1067 struct tevent_req
*req
;
1070 DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino
,
1071 size
, (uintmax_t)off
);
1073 state
= talloc(mstate
, struct ll_write_state
);
1074 if (state
== NULL
) {
1075 fuse_reply_err(freq
, ENOMEM
);
1078 state
->mstate
= mstate
;
1083 req
= cli_smb2_write_send(state
, mstate
->ev
, mstate
->cli
, fnum
, 0,
1084 (const uint8_t *)buf
, off
, size
);
1087 fuse_reply_err(freq
, ENOMEM
);
1090 tevent_req_set_callback(req
, cli_ll_write_done
, state
);
1093 static void cli_ll_write_done(struct tevent_req
*req
)
1095 struct ll_write_state
*state
= tevent_req_callback_data(
1096 req
, struct ll_write_state
);
1100 status
= cli_smb2_write_recv(req
, &written
);
1101 /* no talloc_free here yet */
1102 if (!NT_STATUS_IS_OK(status
)) {
1103 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
1106 fuse_reply_write(state
->freq
, written
);
1111 struct ll_dir_state
{
1112 uint64_t fid_persistent
;
1113 uint64_t fid_volatile
;
1115 struct file_info
*finfos
;
1116 unsigned num_finfos
, num_sent
;
1119 static bool ll_dir_state_add(struct ll_dir_state
*dir_state
,
1122 struct file_info
*tmp
, *finfo
;
1124 tmp
= talloc_realloc(dir_state
, dir_state
->finfos
,
1125 struct file_info
, dir_state
->num_finfos
+1);
1129 dir_state
->finfos
= tmp
;
1130 finfo
= &dir_state
->finfos
[dir_state
->num_finfos
];
1132 ZERO_STRUCTP(finfo
);
1134 finfo
->name
= talloc_strdup(dir_state
->finfos
, name
);
1135 if (finfo
->name
== NULL
) {
1138 dir_state
->num_finfos
+= 1;
1143 struct ll_opendir_state
{
1144 struct mount_state
*mstate
;
1146 struct fuse_file_info fi
;
1147 struct ll_dir_state
*dir_state
;
1150 static void cli_ll_opendir_done(struct tevent_req
*req
);
1152 static void cli_ll_opendir(fuse_req_t freq
, fuse_ino_t ino
,
1153 struct fuse_file_info
*fi
)
1155 struct mount_state
*mstate
= talloc_get_type_abort(
1156 fuse_req_userdata(freq
), struct mount_state
);
1157 struct cli_state
*cli
= mstate
->cli
;
1158 struct ll_opendir_state
*state
;
1159 struct inode_state
*istate
;
1160 struct tevent_req
*req
;
1162 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino
);
1164 istate
= idr_find(mstate
->ino_ctx
, ino
);
1165 if (istate
== NULL
) {
1166 fuse_reply_err(freq
, ENOENT
);
1170 state
= talloc(mstate
, struct ll_opendir_state
);
1171 if (state
== NULL
) {
1172 fuse_reply_err(freq
, ENOMEM
);
1175 state
->mstate
= mstate
;
1179 state
->dir_state
= talloc_zero(state
, struct ll_dir_state
);
1180 if (state
->dir_state
== NULL
) {
1182 fuse_reply_err(freq
, ENOMEM
);
1186 req
= smb2cli_create_send(
1187 state
, mstate
->ev
, cli
->conn
, cli
->timeout
,
1188 cli
->smb2
.session
, cli
->smb2
.tcon
, istate
->path
,
1189 0, SMB2_IMPERSONATION_IMPERSONATION
,
1190 FILE_GENERIC_READ
, FILE_ATTRIBUTE_DIRECTORY
,
1191 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
1192 FILE_OPEN
, FILE_DIRECTORY_FILE
, NULL
);
1195 fuse_reply_err(freq
, ENOMEM
);
1198 tevent_req_set_callback(req
, cli_ll_opendir_done
, state
);
1201 static void cli_ll_opendir_done(struct tevent_req
*req
)
1203 struct ll_opendir_state
*state
= tevent_req_callback_data(
1204 req
, struct ll_opendir_state
);
1207 status
= smb2cli_create_recv(
1209 &state
->dir_state
->fid_persistent
,
1210 &state
->dir_state
->fid_volatile
,
1217 DEBUG(10, ("%s: smbcli_create_recv returned %s\n", __func__
,
1218 nt_errstr(status
)));
1220 if (!NT_STATUS_IS_OK(status
)) {
1221 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
1225 state
->fi
.fh
= (uint64_t)talloc_move(state
->mstate
, &state
->dir_state
);
1226 state
->fi
.direct_io
= 0;
1227 state
->fi
.keep_cache
= 0;
1229 fuse_reply_open(state
->freq
, &state
->fi
);
1234 struct ll_readdir_state
{
1236 struct ll_dir_state
*dir_state
;
1239 static void cli_ll_readdir_done(struct tevent_req
*subreq
);
1240 static NTSTATUS
cli_ll_readdir_one(const char *mnt
, struct file_info
*finfo
,
1241 const char *path
, void *private_data
);
1242 static void cli_ll_readdir_reply_one(fuse_req_t freq
,
1243 struct ll_dir_state
*dir_state
);
1245 static void cli_ll_readdir(fuse_req_t freq
, fuse_ino_t ino
, size_t size
,
1246 off_t off
, struct fuse_file_info
*fi
)
1248 struct mount_state
*mstate
= talloc_get_type_abort(
1249 fuse_req_userdata(freq
), struct mount_state
);
1250 struct cli_state
*cli
= mstate
->cli
;
1251 struct ll_dir_state
*dir_state
;
1252 struct ll_readdir_state
*state
;
1253 struct tevent_req
*req
;
1255 DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino
, size
,
1258 dir_state
= talloc_get_type_abort((void *)fi
->fh
, struct ll_dir_state
);
1260 if (dir_state
->finfos
!= NULL
) {
1261 DBG_DEBUG("finfos=%p\n", dir_state
->finfos
);
1262 cli_ll_readdir_reply_one(freq
, dir_state
);
1266 if (!ll_dir_state_add(dir_state
, ".") ||
1267 !ll_dir_state_add(dir_state
, "..")) {
1268 fuse_reply_err(freq
, ENOMEM
);
1272 state
= talloc(mstate
, struct ll_readdir_state
);
1273 if (state
== NULL
) {
1274 fuse_reply_err(freq
, ENOMEM
);
1278 state
->dir_state
= dir_state
;
1280 req
= cli_smb2_listdir_send(
1281 state
, mstate
->ev
, cli
->conn
, cli
->timeout
,
1282 cli
->smb2
.session
, cli
->smb2
.tcon
,
1283 SMB2_FIND_ID_BOTH_DIRECTORY_INFO
, 0, 0,
1284 dir_state
->fid_persistent
, dir_state
->fid_volatile
,
1286 FILE_ATTRIBUTE_DIRECTORY
| FILE_ATTRIBUTE_SYSTEM
|
1287 FILE_ATTRIBUTE_HIDDEN
,
1288 NULL
, NULL
, cli_ll_readdir_one
, dir_state
);
1291 fuse_reply_err(freq
, ENOMEM
);
1294 tevent_req_set_callback(req
, cli_ll_readdir_done
, state
);
1297 static void cli_ll_readdir_reply_one(fuse_req_t freq
,
1298 struct ll_dir_state
*dir_state
)
1300 struct file_info
*finfo
;
1302 struct stat sbuf
= {};
1305 if (dir_state
->num_finfos
== dir_state
->num_sent
) {
1306 DEBUG(10, ("%s: Done\n", __func__
));
1307 fuse_reply_buf(freq
, NULL
, 0);
1311 sbuf
.st_mode
= S_IFREG
| 0755;
1312 sbuf
.st_ino
= random(); /* FIXME :-) */
1313 finfo
= &dir_state
->finfos
[dir_state
->num_sent
];
1315 DBG_DEBUG("Adding %s\n", finfo
->name
);
1317 buflen
= fuse_add_direntry(freq
, buf
, sizeof(buf
),
1318 finfo
->name
, &sbuf
, 0);
1319 fuse_reply_buf(freq
, buf
, buflen
);
1320 dir_state
->num_sent
+= 1;
1324 static NTSTATUS
cli_ll_readdir_one(const char *mnt
, struct file_info
*finfo
,
1325 const char *path
, void *private_data
)
1327 struct ll_dir_state
*dir_state
= talloc_get_type_abort(
1328 private_data
, struct ll_dir_state
);
1330 if (ISDOT(finfo
->name
) || ISDOTDOT(finfo
->name
)) {
1331 DEBUG(10, ("%s: Ignoring %s\n", __func__
, finfo
->name
));
1332 return NT_STATUS_OK
;
1335 DBG_DEBUG("Got entry %s\n", finfo
->name
);
1337 if (!ll_dir_state_add(dir_state
, finfo
->name
)) {
1338 return NT_STATUS_NO_MEMORY
;
1340 return NT_STATUS_OK
;
1343 static void cli_ll_readdir_done(struct tevent_req
*req
)
1345 struct ll_readdir_state
*state
= tevent_req_callback_data(
1346 req
, struct ll_readdir_state
);
1349 status
= cli_smb2_listdir_recv(req
);
1351 if (!NT_STATUS_IS_OK(status
)) {
1352 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
1355 cli_ll_readdir_reply_one(state
->freq
, state
->dir_state
);
1360 struct ll_releasedir_state
{
1361 struct mount_state
*mstate
;
1363 struct ll_dir_state
*dir_state
;
1366 static void cli_ll_releasedir_done(struct tevent_req
*req
);
1368 static void cli_ll_releasedir(fuse_req_t freq
, fuse_ino_t ino
,
1369 struct fuse_file_info
*fi
)
1371 struct mount_state
*mstate
= talloc_get_type_abort(
1372 fuse_req_userdata(freq
), struct mount_state
);
1373 struct cli_state
*cli
= mstate
->cli
;
1374 struct ll_releasedir_state
*state
;
1375 struct tevent_req
*req
;
1377 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino
);
1379 state
= talloc(mstate
, struct ll_releasedir_state
);
1380 if (state
== NULL
) {
1381 fuse_reply_err(freq
, ENOMEM
);
1384 state
->mstate
= mstate
;
1387 state
->dir_state
= talloc_get_type_abort(
1388 (void *)fi
->fh
, struct ll_dir_state
);
1390 req
= smb2cli_close_send(state
, mstate
->ev
, cli
->conn
, cli
->timeout
,
1391 cli
->smb2
.session
, cli
->smb2
.tcon
, 0,
1392 state
->dir_state
->fid_persistent
,
1393 state
->dir_state
->fid_volatile
);
1396 fuse_reply_err(freq
, ENOMEM
);
1399 tevent_req_set_callback(req
, cli_ll_releasedir_done
, state
);
1402 static void cli_ll_releasedir_done(struct tevent_req
*req
)
1404 struct ll_releasedir_state
*state
= tevent_req_callback_data(
1405 req
, struct ll_releasedir_state
);
1408 status
= smb2cli_close_recv(req
);
1410 if (!NT_STATUS_IS_OK(status
)) {
1411 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
1414 TALLOC_FREE(state
->dir_state
);
1415 fuse_reply_err(state
->freq
, 0);
1419 static struct fuse_lowlevel_ops cli_ll_ops
= {
1420 .lookup
= cli_ll_lookup
,
1421 .forget
= cli_ll_forget
,
1422 .getattr
= cli_ll_getattr
,
1423 .open
= cli_ll_open
,
1424 .create
= cli_ll_create
,
1425 .release
= cli_ll_release
,
1426 .read
= cli_ll_read
,
1427 .write
= cli_ll_write
,
1428 .opendir
= cli_ll_opendir
,
1429 .readdir
= cli_ll_readdir
,
1430 .releasedir
= cli_ll_releasedir
,
1433 static void fuse_chan_fd_handler(struct tevent_context
*ev
,
1434 struct tevent_fd
*fde
,
1436 void *private_data
);
1437 static void fuse_chan_signal_handler(struct tevent_context
*ev
,
1438 struct tevent_signal
*se
,
1442 void *private_data
);
1444 static int mount_state_destructor(struct mount_state
*s
);
1446 int do_mount(struct cli_state
*cli
, const char *mountpoint
)
1448 struct mount_state
*state
;
1449 struct inode_state
*ino
;
1450 struct fuse_args args
= { 0 };
1454 state
= talloc_zero(talloc_tos(), struct mount_state
);
1455 if (state
== NULL
) {
1456 fprintf(stderr
, "talloc failed\n");
1460 state
->ev
= tevent_context_init(state
);
1461 if (state
->ev
== NULL
) {
1462 fprintf(stderr
, "tevent_context_init failed\n");
1467 state
->ino_ctx
= idr_init(state
);
1468 if (state
->ino_ctx
== NULL
) {
1469 fprintf(stderr
, "idr_init failed\n");
1474 state
->ino_parent
= talloc_new(state
);
1475 if (state
->ino_parent
== NULL
) {
1476 fprintf(stderr
, "talloc_new failed\n");
1481 talloc_set_destructor(state
, mount_state_destructor
);
1483 ino
= inode_state_new(state
, "");
1485 fprintf(stderr
, "inode_state_new failed\n");
1489 if (ino
->ino
!= FUSE_ROOT_ID
) {
1490 fprintf(stderr
, "first inode gave %d, expected %d\n",
1491 (int)ino
->ino
, FUSE_ROOT_ID
);
1498 state
->ch
= fuse_mount(mountpoint
, &args
);
1499 if (state
->ch
== NULL
) {
1500 perror("fuse_mount failed");
1504 state
->bufsize
= fuse_chan_bufsize(state
->ch
);
1505 state
->buf
= talloc_array(state
, char, state
->bufsize
);
1506 if (state
->buf
== NULL
) {
1507 fprintf(stderr
, "talloc failed\n");
1511 fd
= fuse_chan_fd(state
->ch
);
1513 state
->fde
= tevent_add_fd(state
->ev
, state
, fd
, TEVENT_FD_READ
,
1514 fuse_chan_fd_handler
, state
);
1515 if (state
->fde
== NULL
) {
1516 fprintf(stderr
, "tevent_add_fd failed\n");
1520 state
->signal_ev
= tevent_add_signal(state
->ev
, state
, SIGINT
, 0,
1521 fuse_chan_signal_handler
, state
);
1522 if (state
->signal_ev
== NULL
) {
1523 fprintf(stderr
, "tevent_add_signal failed\n");
1527 state
->se
= fuse_lowlevel_new(&args
, &cli_ll_ops
, sizeof(cli_ll_ops
),
1529 if (state
->se
== NULL
) {
1530 perror("fuse_lowlevel_new failed");
1534 fuse_session_add_chan(state
->se
, state
->ch
);
1536 while (!state
->done
) {
1537 ret
= tevent_loop_once(state
->ev
);
1539 perror("tevent_loop_once failed");
1544 fuse_session_remove_chan(state
->ch
);
1545 fuse_session_destroy(state
->se
);
1547 fuse_unmount(mountpoint
, state
->ch
);
1553 static int mount_state_destructor(struct mount_state
*s
)
1555 TALLOC_FREE(s
->ino_parent
);
1560 static void fuse_chan_fd_handler(struct tevent_context
*ev
,
1561 struct tevent_fd
*fde
,
1565 struct mount_state
*state
= talloc_get_type_abort(
1566 private_data
, struct mount_state
);
1569 if ((flags
& TEVENT_FD_READ
) == 0) {
1573 recvd
= fuse_chan_recv(&state
->ch
, state
->buf
, state
->bufsize
);
1578 fuse_session_process(state
->se
, state
->buf
, recvd
, state
->ch
);
1581 static void fuse_chan_signal_handler(struct tevent_context
*ev
,
1582 struct tevent_signal
*se
,
1588 struct mount_state
*state
= talloc_get_type_abort(
1589 private_data
, struct mount_state
);