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(
155 mstate
->cli
, state
->path
,
157 SMB2_IMPERSONATION_IMPERSONATION
,
158 FILE_GENERIC_READ
|FILE_GENERIC_WRITE
,
159 FILE_ATTRIBUTE_NORMAL
,
160 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
162 FILE_NON_DIRECTORY_FILE
,
166 fuse_reply_err(freq
, ENOMEM
);
169 tevent_req_set_callback(req
, cli_ll_create_done
, state
);
172 static void cli_ll_create_done(struct tevent_req
*req
)
174 struct ll_create_state
*state
= tevent_req_callback_data(
175 req
, struct ll_create_state
);
176 struct fuse_entry_param e
;
177 struct inode_state
*ino
;
181 status
= cli_smb2_create_fnum_recv(req
, &fnum
, NULL
, NULL
, NULL
);
183 if (!NT_STATUS_IS_OK(status
)) {
184 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
189 state
->fi
.direct_io
= 0;
190 state
->fi
.keep_cache
= 0;
192 ino
= inode_state_new(state
->mstate
, state
->path
);
194 fuse_reply_err(state
->freq
, ENOMEM
);
198 e
= (struct fuse_entry_param
) {
200 .generation
= 1, /* FIXME */
205 fuse_reply_create(state
->freq
, &e
, &state
->fi
);
210 struct cli_get_unixattr_state
{
211 struct tevent_context
*ev
;
212 struct cli_state
*cli
;
213 uint64_t fid_persistent
;
214 uint64_t fid_volatile
;
216 struct timespec create_time
;
217 struct timespec access_time
;
218 struct timespec write_time
;
219 struct timespec change_time
;
225 static void cli_get_unixattr_opened(struct tevent_req
*subreq
);
226 static void cli_get_unixattr_gotinfo(struct tevent_req
*subreq
);
227 static void cli_get_unixattr_closed(struct tevent_req
*subreq
);
230 static struct tevent_req
*cli_get_unixattr_send(TALLOC_CTX
*mem_ctx
,
231 struct tevent_context
*ev
,
232 struct cli_state
*cli
,
235 struct tevent_req
*req
, *subreq
;
236 struct cli_get_unixattr_state
*state
;
238 req
= tevent_req_create(mem_ctx
, &state
,
239 struct cli_get_unixattr_state
);
246 subreq
= smb2cli_create_send(
247 state
, ev
, cli
->conn
, cli
->timeout
, cli
->smb2
.session
,
248 cli
->smb2
.tcon
, path
, SMB2_OPLOCK_LEVEL_NONE
,
249 SMB2_IMPERSONATION_IMPERSONATION
,
250 SYNCHRONIZE_ACCESS
|FILE_READ_ATTRIBUTES
, 0,
251 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
253 if (tevent_req_nomem(subreq
, req
)) {
254 return tevent_req_post(req
, ev
);
256 tevent_req_set_callback(subreq
, cli_get_unixattr_opened
, req
);
261 static void cli_get_unixattr_opened(struct tevent_req
*subreq
)
263 struct tevent_req
*req
= tevent_req_callback_data(
264 subreq
, struct tevent_req
);
265 struct cli_get_unixattr_state
*state
= tevent_req_data(
266 req
, struct cli_get_unixattr_state
);
267 struct cli_state
*cli
= state
->cli
;
270 status
= smb2cli_create_recv(subreq
, &state
->fid_persistent
,
271 &state
->fid_volatile
, NULL
, NULL
, NULL
);
273 if (tevent_req_nterror(req
, status
)) {
274 DBG_DEBUG("smb2cli_create_recv returned %s\n",
279 subreq
= smb2cli_query_info_send(
280 state
, state
->ev
, cli
->conn
, 0,
281 cli
->smb2
.session
, cli
->smb2
.tcon
,
282 1, /* in_info_type */
283 (SMB_FILE_ALL_INFORMATION
- 1000), /* in_file_info_class */
284 0xFFFF, /* in_max_output_length */
285 NULL
, /* in_input_buffer */
286 0, /* in_additional_info */
288 state
->fid_persistent
,
289 state
->fid_volatile
);
290 if (tevent_req_nomem(subreq
, req
)) {
293 tevent_req_set_callback(subreq
, cli_get_unixattr_gotinfo
, req
);
296 static void cli_get_unixattr_gotinfo(struct tevent_req
*subreq
)
298 struct tevent_req
*req
= tevent_req_callback_data(
299 subreq
, struct tevent_req
);
300 struct cli_get_unixattr_state
*state
= tevent_req_data(
301 req
, struct cli_get_unixattr_state
);
302 struct cli_state
*cli
= state
->cli
;
306 status
= smb2cli_query_info_recv(subreq
, state
, &outbuf
);
308 if (tevent_req_nterror(req
, status
)) {
309 DBG_DEBUG("smb2cli_query_info_recv returned %s\n",
314 if (outbuf
.length
< 0x60) {
315 tevent_req_nterror(req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
319 state
->create_time
= interpret_long_date((char *)outbuf
.data
+ 0x0);
320 state
->access_time
= interpret_long_date((char *)outbuf
.data
+ 0x8);
321 state
->write_time
= interpret_long_date((char *)outbuf
.data
+ 0x10);
322 state
->change_time
= interpret_long_date((char *)outbuf
.data
+ 0x18);
323 state
->mode
= IVAL(outbuf
.data
, 0x20);
324 state
->size
= BVAL(outbuf
.data
, 0x30);
325 state
->ino
= BVAL(outbuf
.data
, 0x40);
327 subreq
= smb2cli_close_send(state
, state
->ev
, cli
->conn
, 0,
328 cli
->smb2
.session
, cli
->smb2
.tcon
, 0,
329 state
->fid_persistent
,
330 state
->fid_volatile
);
331 if (tevent_req_nomem(subreq
, req
)) {
334 tevent_req_set_callback(subreq
, cli_get_unixattr_closed
, req
);
337 static void cli_get_unixattr_closed(struct tevent_req
*subreq
)
339 struct tevent_req
*req
= tevent_req_callback_data(
340 subreq
, struct tevent_req
);
343 status
= smb2cli_close_recv(subreq
);
345 if (tevent_req_nterror(req
, status
)) {
348 tevent_req_done(req
);
351 static NTSTATUS
cli_get_unixattr_recv(struct tevent_req
*req
,
354 struct cli_get_unixattr_state
*state
= tevent_req_data(
355 req
, struct cli_get_unixattr_state
);
358 if (tevent_req_is_nterror(req
, &status
)) {
362 if (IS_DOS_DIR(state
->mode
)) {
363 st
->st_mode
= (S_IFDIR
| 0555);
366 st
->st_mode
= (S_IFREG
| 0444);
370 st
->st_size
= state
->size
;
371 st
->st_uid
= getuid();
372 st
->st_gid
= getgid();
373 st
->st_ino
= state
->ino
;
374 st
->st_atime
= convert_timespec_to_time_t(state
->access_time
);
375 st
->st_ctime
= convert_timespec_to_time_t(state
->change_time
);
376 st
->st_mtime
= convert_timespec_to_time_t(state
->write_time
);
381 struct cli_smb2_listdir_state
{
382 struct tevent_context
*ev
;
383 struct smbXcli_conn
*conn
;
384 uint32_t timeout_msec
;
385 struct smbXcli_session
*session
;
386 struct smbXcli_tcon
*tcon
;
390 uint64_t fid_persistent
;
391 uint64_t fid_volatile
;
396 const char *mntpoint
;
397 const char *pathname
;
398 NTSTATUS (*fn
)(const char *mntpoint
, struct file_info
*f
,
399 const char *mask
, void *private_data
);
404 static void cli_smb2_listdir_done(struct tevent_req
*subreq
);
406 static struct tevent_req
*cli_smb2_listdir_send(
408 struct tevent_context
*ev
,
409 struct smbXcli_conn
*conn
,
410 uint32_t timeout_msec
,
411 struct smbXcli_session
*session
,
412 struct smbXcli_tcon
*tcon
,
416 uint64_t fid_persistent
,
417 uint64_t fid_volatile
,
421 const char *mntpoint
,
422 const char *pathname
,
423 NTSTATUS (*fn
)(const char *mntpoint
, struct file_info
*f
,
424 const char *mask
, void *private_data
),
427 struct tevent_req
*req
, *subreq
;
428 struct cli_smb2_listdir_state
*state
;
430 req
= tevent_req_create(mem_ctx
, &state
,
431 struct cli_smb2_listdir_state
);
437 state
->timeout_msec
= timeout_msec
;
438 state
->session
= session
;
440 state
->level
= level
;
441 state
->flags
= flags
;
442 state
->file_index
= file_index
;
443 state
->fid_persistent
= fid_persistent
;
444 state
->fid_volatile
= fid_volatile
;
446 state
->outbuf_len
= outbuf_len
;
447 state
->attribute
= attribute
;
448 state
->mntpoint
= mntpoint
;
449 state
->pathname
= pathname
;
451 state
->private_data
= private_data
;
453 subreq
= smb2cli_query_directory_send(
454 state
, state
->ev
, state
->conn
, state
->timeout_msec
,
455 state
->session
, state
->tcon
, state
->level
,
456 state
->flags
, state
->file_index
,
457 state
->fid_persistent
, state
->fid_volatile
,
458 state
->mask
, state
->outbuf_len
);
459 if (tevent_req_nomem(subreq
, req
)) {
460 return tevent_req_post(req
, ev
);
462 tevent_req_set_callback(subreq
, cli_smb2_listdir_done
, req
);
466 static NTSTATUS
parse_finfo_id_both_directory_info(uint8_t *dir_data
,
467 uint32_t dir_data_length
,
468 struct file_info
*finfo
,
469 uint32_t *next_offset
)
475 if (dir_data_length
< 4) {
476 return NT_STATUS_INFO_LENGTH_MISMATCH
;
479 *next_offset
= IVAL(dir_data
, 0);
481 if (*next_offset
> dir_data_length
) {
482 return NT_STATUS_INFO_LENGTH_MISMATCH
;
485 if (*next_offset
!= 0) {
486 /* Ensure we only read what in this record. */
487 dir_data_length
= *next_offset
;
490 if (dir_data_length
< 105) {
491 return NT_STATUS_INFO_LENGTH_MISMATCH
;
494 finfo
->btime_ts
= interpret_long_date((const char *)dir_data
+ 8);
495 finfo
->atime_ts
= interpret_long_date((const char *)dir_data
+ 16);
496 finfo
->mtime_ts
= interpret_long_date((const char *)dir_data
+ 24);
497 finfo
->ctime_ts
= interpret_long_date((const char *)dir_data
+ 32);
498 finfo
->size
= IVAL2_TO_SMB_BIG_UINT(dir_data
+ 40, 0);
499 finfo
->attr
= IVAL(dir_data
+ 56, 0);
500 namelen
= IVAL(dir_data
+ 60,0);
501 if (namelen
> (dir_data_length
- 104)) {
502 return NT_STATUS_INFO_LENGTH_MISMATCH
;
504 slen
= CVAL(dir_data
+ 68, 0);
506 return NT_STATUS_INFO_LENGTH_MISMATCH
;
508 ret
= pull_string_talloc(finfo
,
510 FLAGS2_UNICODE_STRINGS
,
515 if (ret
== (size_t)-1) {
516 /* Bad conversion. */
517 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
520 ret
= pull_string_talloc(finfo
,
522 FLAGS2_UNICODE_STRINGS
,
527 if (ret
== (size_t)-1) {
528 /* Bad conversion. */
529 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
534 static void cli_smb2_listdir_done(struct tevent_req
*subreq
)
536 struct tevent_req
*req
= tevent_req_callback_data(
537 subreq
, struct tevent_req
);
538 struct cli_smb2_listdir_state
*state
= tevent_req_data(
539 req
, struct cli_smb2_listdir_state
);
542 uint32_t next_offset
= 0;
545 status
= smb2cli_query_directory_recv(subreq
, state
, &data
,
548 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
)) {
549 tevent_req_done(req
);
552 if (tevent_req_nterror(req
, status
)) {
557 struct file_info
*finfo
;
560 finfo
= talloc_zero(state
, struct file_info
);
561 if (tevent_req_nomem(finfo
, req
)) {
565 status
= parse_finfo_id_both_directory_info(
566 data
, data_len
, finfo
, &next_offset
);
568 DEBUG(10, ("%s: parse_finfo_id_both_directory_info returned "
569 "%s\n", __func__
, nt_errstr(status
)));
571 if (tevent_req_nterror(req
, status
)) {
575 ok
= dir_check_ftype(finfo
->attr
, state
->attribute
);
577 DEBUG(10, ("%s: dir_check_ftype(%u,%u) returned %u\n",
578 __func__
, (unsigned)finfo
->attr
,
579 (unsigned)state
->attribute
, (unsigned)ok
));
583 * Only process if attributes match. On SMB1 server
584 * does this, so on SMB2 we need to emulate in the
587 * https://bugzilla.samba.org/show_bug.cgi?id=10260
589 state
->processed_file
= true;
591 status
= state
->fn(state
->mntpoint
, finfo
,
593 state
->private_data
);
594 if (tevent_req_nterror(req
, status
)) {
601 if (next_offset
!= 0) {
603 data_len
-= next_offset
;
605 } while (next_offset
!= 0);
607 subreq
= smb2cli_query_directory_send(
608 state
, state
->ev
, state
->conn
, state
->timeout_msec
,
609 state
->session
, state
->tcon
, state
->level
,
610 state
->flags
, state
->file_index
,
611 state
->fid_persistent
, state
->fid_volatile
,
612 state
->mask
, state
->outbuf_len
);
613 if (tevent_req_nomem(subreq
, req
)) {
616 tevent_req_set_callback(subreq
, cli_smb2_listdir_done
, req
);
619 static NTSTATUS
cli_smb2_listdir_recv(struct tevent_req
*req
)
621 struct cli_smb2_listdir_state
*state
= tevent_req_data(
622 req
, struct cli_smb2_listdir_state
);
625 if (tevent_req_is_nterror(req
, &status
)) {
629 if (!state
->processed_file
) {
631 * In SMB1 findfirst returns NT_STATUS_NO_SUCH_FILE
632 * if no files match. Emulate this in the client.
634 return NT_STATUS_NO_SUCH_FILE
;
640 struct ll_lookup_state
{
641 struct mount_state
*mstate
;
646 static void cli_ll_lookup_done(struct tevent_req
*req
);
648 static void cli_ll_lookup(fuse_req_t freq
, fuse_ino_t parent_ino
,
651 struct mount_state
*mstate
= talloc_get_type_abort(
652 fuse_req_userdata(freq
), struct mount_state
);
653 struct ll_lookup_state
*state
;
654 struct tevent_req
*req
;
655 struct inode_state
*parent
;
657 DBG_DEBUG("parent_ino=%ju, name=%s\n", (uintmax_t)parent_ino
, name
);
659 parent
= idr_find(mstate
->ino_ctx
, parent_ino
);
660 if (parent
== NULL
) {
661 DBG_WARNING("could not find parent\n");
662 fuse_reply_err(freq
, ENOENT
);
666 state
= talloc(mstate
, struct ll_lookup_state
);
668 DBG_WARNING("talloc failed\n");
669 fuse_reply_err(freq
, ENOMEM
);
672 state
->mstate
= mstate
;
675 state
->path
= talloc_asprintf(state
, "%s%s%s", parent
->path
,
676 strlen(parent
->path
) ? "\\": "",
678 if (state
->path
== NULL
) {
680 fuse_reply_err(freq
, ENOMEM
);
684 req
= cli_get_unixattr_send(state
, mstate
->ev
, mstate
->cli
,
688 fuse_reply_err(freq
, ENOMEM
);
691 tevent_req_set_callback(req
, cli_ll_lookup_done
, state
);
694 static void cli_ll_lookup_done(struct tevent_req
*req
)
696 struct ll_lookup_state
*state
= tevent_req_callback_data(
697 req
, struct ll_lookup_state
);
698 struct stat sbuf
= {0};
699 struct fuse_entry_param e
;
700 struct inode_state
*ino
;
703 status
= cli_get_unixattr_recv(req
, &sbuf
);
705 if (!NT_STATUS_IS_OK(status
)) {
706 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
710 ino
= inode_state_new(state
->mstate
, state
->path
);
712 fuse_reply_err(state
->freq
, ENOMEM
);
716 e
= (struct fuse_entry_param
) {
719 .generation
= 1, /* FIXME */
724 fuse_reply_entry(state
->freq
, &e
);
728 struct ll_getattr_state
{
729 struct mount_state
*mstate
;
731 struct fuse_file_info fi
;
734 static void cli_ll_getattr_done(struct tevent_req
*req
);
736 static void cli_ll_getattr(fuse_req_t freq
, fuse_ino_t ino
,
737 struct fuse_file_info
*fi
)
739 struct mount_state
*mstate
= talloc_get_type_abort(
740 fuse_req_userdata(freq
), struct mount_state
);
741 struct ll_getattr_state
*state
;
742 struct inode_state
*istate
;
743 struct tevent_req
*req
;
745 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino
);
747 istate
= idr_find(mstate
->ino_ctx
, ino
);
748 if (istate
== NULL
) {
749 fuse_reply_err(freq
, ENOENT
);
753 state
= talloc(mstate
, struct ll_getattr_state
);
755 fuse_reply_err(freq
, ENOMEM
);
758 state
->mstate
= mstate
;
761 req
= cli_get_unixattr_send(state
, mstate
->ev
, mstate
->cli
,
765 fuse_reply_err(freq
, ENOMEM
);
768 tevent_req_set_callback(req
, cli_ll_getattr_done
, state
);
771 static void cli_ll_getattr_done(struct tevent_req
*req
)
773 struct ll_getattr_state
*state
= tevent_req_callback_data(
774 req
, struct ll_getattr_state
);
779 status
= cli_get_unixattr_recv(req
, &st
);
781 if (!NT_STATUS_IS_OK(status
)) {
782 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
786 ret
= fuse_reply_attr(state
->freq
, &st
, 1);
788 DBG_NOTICE("fuse_reply_attr failed: %s\n",
794 struct ll_open_state
{
795 struct mount_state
*mstate
;
797 struct fuse_file_info fi
;
800 static void cli_ll_open_done(struct tevent_req
*req
);
802 static void cli_ll_open(fuse_req_t freq
, fuse_ino_t ino
,
803 struct fuse_file_info
*fi
)
805 struct mount_state
*mstate
= talloc_get_type_abort(
806 fuse_req_userdata(freq
), struct mount_state
);
807 struct ll_open_state
*state
;
808 struct inode_state
*istate
;
809 struct tevent_req
*req
;
812 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino
);
814 istate
= idr_find(mstate
->ino_ctx
, ino
);
815 if (istate
== NULL
) {
816 fuse_reply_err(freq
, ENOENT
);
820 state
= talloc(mstate
, struct ll_open_state
);
822 fuse_reply_err(freq
, ENOMEM
);
825 state
->mstate
= mstate
;
829 switch (fi
->flags
& O_ACCMODE
) {
831 acc
= FILE_GENERIC_READ
;
834 acc
= FILE_GENERIC_WRITE
;
837 acc
= FILE_GENERIC_READ
|FILE_GENERIC_WRITE
;
840 fuse_reply_err(freq
, EACCES
);
844 req
= cli_smb2_create_fnum_send(
850 SMB2_IMPERSONATION_IMPERSONATION
,
852 FILE_ATTRIBUTE_NORMAL
,
853 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
855 FILE_NON_DIRECTORY_FILE
,
859 fuse_reply_err(freq
, ENOMEM
);
862 tevent_req_set_callback(req
, cli_ll_open_done
, state
);
865 static void cli_ll_open_done(struct tevent_req
*req
)
867 struct ll_open_state
*state
= tevent_req_callback_data(
868 req
, struct ll_open_state
);
872 status
= cli_smb2_create_fnum_recv(req
, &fnum
, NULL
, NULL
, NULL
);
874 if (!NT_STATUS_IS_OK(status
)) {
875 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
880 state
->fi
.direct_io
= 0;
881 state
->fi
.keep_cache
= 0;
883 fuse_reply_open(state
->freq
, &state
->fi
);
888 struct ll_release_state
{
889 struct mount_state
*mstate
;
894 static void cli_ll_release_done(struct tevent_req
*req
);
896 static void cli_ll_release(fuse_req_t freq
, fuse_ino_t ino
,
897 struct fuse_file_info
*fi
)
899 struct mount_state
*mstate
= talloc_get_type_abort(
900 fuse_req_userdata(freq
), struct mount_state
);
901 struct ll_release_state
*state
;
902 struct inode_state
*istate
;
903 struct tevent_req
*req
;
906 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino
);
908 istate
= idr_find(mstate
->ino_ctx
, ino
);
909 if (istate
== NULL
) {
910 fuse_reply_err(freq
, ENOENT
);
914 state
= talloc(mstate
, struct ll_release_state
);
916 fuse_reply_err(freq
, ENOMEM
);
919 state
->mstate
= mstate
;
925 req
= cli_smb2_close_fnum_send(state
, mstate
->ev
, mstate
->cli
, fnum
);
928 fuse_reply_err(freq
, ENOMEM
);
931 tevent_req_set_callback(req
, cli_ll_release_done
, state
);
934 static void cli_ll_release_done(struct tevent_req
*req
)
936 struct ll_release_state
*state
= tevent_req_callback_data(
937 req
, struct ll_release_state
);
938 struct inode_state
*istate
;
941 status
= cli_smb2_close_fnum_recv(req
);
943 if (!NT_STATUS_IS_OK(status
)) {
944 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
948 istate
= idr_find(state
->mstate
->ino_ctx
, state
->ino
);
949 if (istate
== NULL
) {
950 DEBUG(1, ("%s: inode %ju vanished!\n", __func__
,
951 (uintmax_t)state
->ino
));
955 fuse_reply_err(state
->freq
, 0);
959 struct ll_read_state
{
960 struct mount_state
*mstate
;
964 static void cli_ll_read_done(struct tevent_req
*req
);
966 static void cli_ll_read(fuse_req_t freq
, fuse_ino_t ino
,
967 size_t size
, off_t off
,
968 struct fuse_file_info
*fi
)
970 struct mount_state
*mstate
= talloc_get_type_abort(
971 fuse_req_userdata(freq
), struct mount_state
);
972 struct ll_read_state
*state
;
973 struct tevent_req
*req
;
976 DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino
,
977 size
, (uintmax_t)off
);
979 state
= talloc(mstate
, struct ll_read_state
);
981 fuse_reply_err(freq
, ENOMEM
);
984 state
->mstate
= mstate
;
989 req
= cli_smb2_read_send(state
, mstate
->ev
, mstate
->cli
,
993 fuse_reply_err(freq
, ENOMEM
);
996 tevent_req_set_callback(req
, cli_ll_read_done
, state
);
999 static void cli_ll_read_done(struct tevent_req
*req
)
1001 struct ll_read_state
*state
= tevent_req_callback_data(
1002 req
, struct ll_read_state
);
1007 status
= cli_smb2_read_recv(req
, &received
, &rcvbuf
);
1008 /* no talloc_free here yet */
1010 if (NT_STATUS_EQUAL(status
, NT_STATUS_END_OF_FILE
)) {
1013 status
= NT_STATUS_OK
;
1016 if (!NT_STATUS_IS_OK(status
)) {
1017 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
1020 fuse_reply_buf(state
->freq
, (char *)rcvbuf
, received
);
1024 struct ll_write_state
{
1025 struct mount_state
*mstate
;
1029 static void cli_ll_write_done(struct tevent_req
*req
);
1031 static void cli_ll_write(fuse_req_t freq
, fuse_ino_t ino
, const char *buf
,
1032 size_t size
, off_t off
, struct fuse_file_info
*fi
)
1034 struct mount_state
*mstate
= talloc_get_type_abort(
1035 fuse_req_userdata(freq
), struct mount_state
);
1036 struct ll_write_state
*state
;
1037 struct tevent_req
*req
;
1040 DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino
,
1041 size
, (uintmax_t)off
);
1043 state
= talloc(mstate
, struct ll_write_state
);
1044 if (state
== NULL
) {
1045 fuse_reply_err(freq
, ENOMEM
);
1048 state
->mstate
= mstate
;
1053 req
= cli_smb2_write_send(state
, mstate
->ev
, mstate
->cli
, fnum
, 0,
1054 (const uint8_t *)buf
, off
, size
);
1057 fuse_reply_err(freq
, ENOMEM
);
1060 tevent_req_set_callback(req
, cli_ll_write_done
, state
);
1063 static void cli_ll_write_done(struct tevent_req
*req
)
1065 struct ll_write_state
*state
= tevent_req_callback_data(
1066 req
, struct ll_write_state
);
1070 status
= cli_smb2_write_recv(req
, &written
);
1071 /* no talloc_free here yet */
1072 if (!NT_STATUS_IS_OK(status
)) {
1073 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
1076 fuse_reply_write(state
->freq
, written
);
1081 struct ll_dir_state
{
1082 uint64_t fid_persistent
;
1083 uint64_t fid_volatile
;
1085 struct file_info
*finfos
;
1086 unsigned num_finfos
, num_sent
;
1089 static bool ll_dir_state_add(struct ll_dir_state
*dir_state
,
1092 struct file_info
*tmp
, *finfo
;
1094 tmp
= talloc_realloc(dir_state
, dir_state
->finfos
,
1095 struct file_info
, dir_state
->num_finfos
+1);
1099 dir_state
->finfos
= tmp
;
1100 finfo
= &dir_state
->finfos
[dir_state
->num_finfos
];
1102 ZERO_STRUCTP(finfo
);
1104 finfo
->name
= talloc_strdup(dir_state
->finfos
, name
);
1105 if (finfo
->name
== NULL
) {
1108 dir_state
->num_finfos
+= 1;
1113 struct ll_opendir_state
{
1114 struct mount_state
*mstate
;
1116 struct fuse_file_info fi
;
1117 struct ll_dir_state
*dir_state
;
1120 static void cli_ll_opendir_done(struct tevent_req
*req
);
1122 static void cli_ll_opendir(fuse_req_t freq
, fuse_ino_t ino
,
1123 struct fuse_file_info
*fi
)
1125 struct mount_state
*mstate
= talloc_get_type_abort(
1126 fuse_req_userdata(freq
), struct mount_state
);
1127 struct cli_state
*cli
= mstate
->cli
;
1128 struct ll_opendir_state
*state
;
1129 struct inode_state
*istate
;
1130 struct tevent_req
*req
;
1132 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino
);
1134 istate
= idr_find(mstate
->ino_ctx
, ino
);
1135 if (istate
== NULL
) {
1136 fuse_reply_err(freq
, ENOENT
);
1140 state
= talloc(mstate
, struct ll_opendir_state
);
1141 if (state
== NULL
) {
1142 fuse_reply_err(freq
, ENOMEM
);
1145 state
->mstate
= mstate
;
1149 state
->dir_state
= talloc_zero(state
, struct ll_dir_state
);
1150 if (state
->dir_state
== NULL
) {
1152 fuse_reply_err(freq
, ENOMEM
);
1156 req
= smb2cli_create_send(
1157 state
, mstate
->ev
, cli
->conn
, cli
->timeout
,
1158 cli
->smb2
.session
, cli
->smb2
.tcon
, istate
->path
,
1159 0, SMB2_IMPERSONATION_IMPERSONATION
,
1160 FILE_GENERIC_READ
, FILE_ATTRIBUTE_DIRECTORY
,
1161 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
1162 FILE_OPEN
, FILE_DIRECTORY_FILE
, NULL
);
1165 fuse_reply_err(freq
, ENOMEM
);
1168 tevent_req_set_callback(req
, cli_ll_opendir_done
, state
);
1171 static void cli_ll_opendir_done(struct tevent_req
*req
)
1173 struct ll_opendir_state
*state
= tevent_req_callback_data(
1174 req
, struct ll_opendir_state
);
1177 status
= smb2cli_create_recv(req
,
1178 &state
->dir_state
->fid_persistent
,
1179 &state
->dir_state
->fid_volatile
,
1183 DEBUG(10, ("%s: smbcli_create_recv returned %s\n", __func__
,
1184 nt_errstr(status
)));
1186 if (!NT_STATUS_IS_OK(status
)) {
1187 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
1191 state
->fi
.fh
= (uint64_t)talloc_move(state
->mstate
, &state
->dir_state
);
1192 state
->fi
.direct_io
= 0;
1193 state
->fi
.keep_cache
= 0;
1195 fuse_reply_open(state
->freq
, &state
->fi
);
1200 struct ll_readdir_state
{
1202 struct ll_dir_state
*dir_state
;
1205 static void cli_ll_readdir_done(struct tevent_req
*subreq
);
1206 static NTSTATUS
cli_ll_readdir_one(const char *mnt
, struct file_info
*finfo
,
1207 const char *path
, void *private_data
);
1208 static void cli_ll_readdir_reply_one(fuse_req_t freq
,
1209 struct ll_dir_state
*dir_state
);
1211 static void cli_ll_readdir(fuse_req_t freq
, fuse_ino_t ino
, size_t size
,
1212 off_t off
, struct fuse_file_info
*fi
)
1214 struct mount_state
*mstate
= talloc_get_type_abort(
1215 fuse_req_userdata(freq
), struct mount_state
);
1216 struct cli_state
*cli
= mstate
->cli
;
1217 struct ll_dir_state
*dir_state
;
1218 struct ll_readdir_state
*state
;
1219 struct tevent_req
*req
;
1221 DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino
, size
,
1224 dir_state
= talloc_get_type_abort((void *)fi
->fh
, struct ll_dir_state
);
1226 if (dir_state
->finfos
!= NULL
) {
1227 DBG_DEBUG("finfos=%p\n", dir_state
->finfos
);
1228 cli_ll_readdir_reply_one(freq
, dir_state
);
1232 if (!ll_dir_state_add(dir_state
, ".") ||
1233 !ll_dir_state_add(dir_state
, "..")) {
1234 fuse_reply_err(freq
, ENOMEM
);
1238 state
= talloc(mstate
, struct ll_readdir_state
);
1239 if (state
== NULL
) {
1240 fuse_reply_err(freq
, ENOMEM
);
1244 state
->dir_state
= dir_state
;
1246 req
= cli_smb2_listdir_send(
1247 state
, mstate
->ev
, cli
->conn
, cli
->timeout
,
1248 cli
->smb2
.session
, cli
->smb2
.tcon
,
1249 SMB2_FIND_ID_BOTH_DIRECTORY_INFO
, 0, 0,
1250 dir_state
->fid_persistent
, dir_state
->fid_volatile
,
1252 FILE_ATTRIBUTE_DIRECTORY
| FILE_ATTRIBUTE_SYSTEM
|
1253 FILE_ATTRIBUTE_HIDDEN
,
1254 NULL
, NULL
, cli_ll_readdir_one
, dir_state
);
1257 fuse_reply_err(freq
, ENOMEM
);
1260 tevent_req_set_callback(req
, cli_ll_readdir_done
, state
);
1263 static void cli_ll_readdir_reply_one(fuse_req_t freq
,
1264 struct ll_dir_state
*dir_state
)
1266 struct file_info
*finfo
;
1268 struct stat sbuf
= {};
1271 if (dir_state
->num_finfos
== dir_state
->num_sent
) {
1272 DEBUG(10, ("%s: Done\n", __func__
));
1273 fuse_reply_buf(freq
, NULL
, 0);
1277 sbuf
.st_mode
= S_IFREG
| 0755;
1278 sbuf
.st_ino
= random(); /* FIXME :-) */
1279 finfo
= &dir_state
->finfos
[dir_state
->num_sent
];
1281 DBG_DEBUG("Adding %s\n", finfo
->name
);
1283 buflen
= fuse_add_direntry(freq
, buf
, sizeof(buf
),
1284 finfo
->name
, &sbuf
, 0);
1285 fuse_reply_buf(freq
, buf
, buflen
);
1286 dir_state
->num_sent
+= 1;
1290 static NTSTATUS
cli_ll_readdir_one(const char *mnt
, struct file_info
*finfo
,
1291 const char *path
, void *private_data
)
1293 struct ll_dir_state
*dir_state
= talloc_get_type_abort(
1294 private_data
, struct ll_dir_state
);
1296 if (ISDOT(finfo
->name
) || ISDOTDOT(finfo
->name
)) {
1297 DEBUG(10, ("%s: Ignoring %s\n", __func__
, finfo
->name
));
1298 return NT_STATUS_OK
;
1301 DBG_DEBUG("Got entry %s\n", finfo
->name
);
1303 if (!ll_dir_state_add(dir_state
, finfo
->name
)) {
1304 return NT_STATUS_NO_MEMORY
;
1306 return NT_STATUS_OK
;
1309 static void cli_ll_readdir_done(struct tevent_req
*req
)
1311 struct ll_readdir_state
*state
= tevent_req_callback_data(
1312 req
, struct ll_readdir_state
);
1315 status
= cli_smb2_listdir_recv(req
);
1317 if (!NT_STATUS_IS_OK(status
)) {
1318 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
1321 cli_ll_readdir_reply_one(state
->freq
, state
->dir_state
);
1326 struct ll_releasedir_state
{
1327 struct mount_state
*mstate
;
1329 struct ll_dir_state
*dir_state
;
1332 static void cli_ll_releasedir_done(struct tevent_req
*req
);
1334 static void cli_ll_releasedir(fuse_req_t freq
, fuse_ino_t ino
,
1335 struct fuse_file_info
*fi
)
1337 struct mount_state
*mstate
= talloc_get_type_abort(
1338 fuse_req_userdata(freq
), struct mount_state
);
1339 struct cli_state
*cli
= mstate
->cli
;
1340 struct ll_releasedir_state
*state
;
1341 struct tevent_req
*req
;
1343 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino
);
1345 state
= talloc(mstate
, struct ll_releasedir_state
);
1346 if (state
== NULL
) {
1347 fuse_reply_err(freq
, ENOMEM
);
1350 state
->mstate
= mstate
;
1353 state
->dir_state
= talloc_get_type_abort(
1354 (void *)fi
->fh
, struct ll_dir_state
);
1356 req
= smb2cli_close_send(state
, mstate
->ev
, cli
->conn
, cli
->timeout
,
1357 cli
->smb2
.session
, cli
->smb2
.tcon
, 0,
1358 state
->dir_state
->fid_persistent
,
1359 state
->dir_state
->fid_volatile
);
1362 fuse_reply_err(freq
, ENOMEM
);
1365 tevent_req_set_callback(req
, cli_ll_releasedir_done
, state
);
1368 static void cli_ll_releasedir_done(struct tevent_req
*req
)
1370 struct ll_releasedir_state
*state
= tevent_req_callback_data(
1371 req
, struct ll_releasedir_state
);
1374 status
= smb2cli_close_recv(req
);
1376 if (!NT_STATUS_IS_OK(status
)) {
1377 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
1380 TALLOC_FREE(state
->dir_state
);
1381 fuse_reply_err(state
->freq
, 0);
1385 static struct fuse_lowlevel_ops cli_ll_ops
= {
1386 .lookup
= cli_ll_lookup
,
1387 .getattr
= cli_ll_getattr
,
1388 .open
= cli_ll_open
,
1389 .create
= cli_ll_create
,
1390 .release
= cli_ll_release
,
1391 .read
= cli_ll_read
,
1392 .write
= cli_ll_write
,
1393 .opendir
= cli_ll_opendir
,
1394 .readdir
= cli_ll_readdir
,
1395 .releasedir
= cli_ll_releasedir
,
1398 static void fuse_chan_fd_handler(struct tevent_context
*ev
,
1399 struct tevent_fd
*fde
,
1401 void *private_data
);
1402 static void fuse_chan_signal_handler(struct tevent_context
*ev
,
1403 struct tevent_signal
*se
,
1407 void *private_data
);
1409 static int mount_state_destructor(struct mount_state
*s
);
1411 int do_mount(struct cli_state
*cli
, const char *mountpoint
)
1413 struct mount_state
*state
;
1414 struct inode_state
*ino
;
1415 struct fuse_args args
= { 0 };
1419 state
= talloc_zero(talloc_tos(), struct mount_state
);
1420 if (state
== NULL
) {
1421 fprintf(stderr
, "talloc failed\n");
1425 state
->ev
= tevent_context_init(state
);
1426 if (state
->ev
== NULL
) {
1427 fprintf(stderr
, "tevent_context_init failed\n");
1432 state
->ino_ctx
= idr_init(state
);
1433 if (state
->ino_ctx
== NULL
) {
1434 fprintf(stderr
, "idr_init failed\n");
1439 state
->ino_parent
= talloc_new(state
);
1440 if (state
->ino_parent
== NULL
) {
1441 fprintf(stderr
, "talloc_new failed\n");
1446 talloc_set_destructor(state
, mount_state_destructor
);
1448 ino
= inode_state_new(state
, "");
1450 fprintf(stderr
, "inode_state_new failed\n");
1454 if (ino
->ino
!= FUSE_ROOT_ID
) {
1455 fprintf(stderr
, "first inode gave %d, expected %d\n",
1456 (int)ino
->ino
, FUSE_ROOT_ID
);
1463 state
->ch
= fuse_mount(mountpoint
, &args
);
1464 if (state
->ch
== NULL
) {
1465 perror("fuse_mount failed");
1469 state
->bufsize
= fuse_chan_bufsize(state
->ch
);
1470 state
->buf
= talloc_array(state
, char, state
->bufsize
);
1471 if (state
->buf
== NULL
) {
1472 fprintf(stderr
, "talloc failed\n");
1476 fd
= fuse_chan_fd(state
->ch
);
1478 state
->fde
= tevent_add_fd(state
->ev
, state
, fd
, TEVENT_FD_READ
,
1479 fuse_chan_fd_handler
, state
);
1480 if (state
->fde
== NULL
) {
1481 fprintf(stderr
, "tevent_add_fd failed\n");
1485 state
->signal_ev
= tevent_add_signal(state
->ev
, state
, SIGINT
, 0,
1486 fuse_chan_signal_handler
, state
);
1487 if (state
->signal_ev
== NULL
) {
1488 fprintf(stderr
, "tevent_add_signal failed\n");
1492 state
->se
= fuse_lowlevel_new(&args
, &cli_ll_ops
, sizeof(cli_ll_ops
),
1494 if (state
->se
== NULL
) {
1495 perror("fuse_lowlevel_new failed");
1499 fuse_session_add_chan(state
->se
, state
->ch
);
1501 while (!state
->done
) {
1502 ret
= tevent_loop_once(state
->ev
);
1504 perror("tevent_loop_once failed");
1509 fuse_session_remove_chan(state
->ch
);
1510 fuse_session_destroy(state
->se
);
1512 fuse_unmount(mountpoint
, state
->ch
);
1518 static int mount_state_destructor(struct mount_state
*s
)
1520 TALLOC_FREE(s
->ino_parent
);
1525 static void fuse_chan_fd_handler(struct tevent_context
*ev
,
1526 struct tevent_fd
*fde
,
1530 struct mount_state
*state
= talloc_get_type_abort(
1531 private_data
, struct mount_state
);
1534 if ((flags
& TEVENT_FD_READ
) == 0) {
1538 recvd
= fuse_chan_recv(&state
->ch
, state
->buf
, state
->bufsize
);
1543 fuse_session_process(state
->se
, state
->buf
, recvd
, state
->ch
);
1546 static void fuse_chan_signal_handler(struct tevent_context
*ev
,
1547 struct tevent_signal
*se
,
1553 struct mount_state
*state
= talloc_get_type_abort(
1554 private_data
, struct mount_state
);