2 * Unix SMB/CIFS implementation.
3 * fusermount smb2 client
4 * Copyright (C) Volker Lendecke 2016
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #define FUSE_USE_VERSION 26
21 #define _FILE_OFFSET_BITS 64
22 #include "fuse/fuse_lowlevel.h"
24 #include "source3/include/includes.h"
27 #include "libsmb/proto.h"
28 #include "libsmb/clirap.h"
29 #include "libsmb/cli_smb2_fnum.h"
30 #include "lib/util/tevent_ntstatus.h"
31 #include "libcli/smb/smbXcli_base.h"
32 #include "libcli/security/security.h"
36 struct tevent_context
*ev
;
37 struct cli_state
*cli
;
40 struct tevent_fd
*fde
;
41 struct tevent_signal
*signal_ev
;
44 struct fuse_session
*se
;
49 struct idr_context
*ino_ctx
;
50 TALLOC_CTX
*ino_parent
;
54 struct idr_context
*ino_ctx
;
59 static int inode_state_destructor(struct inode_state
*s
);
61 static struct inode_state
*inode_state_init(TALLOC_CTX
*mem_ctx
,
62 struct idr_context
*ino_ctx
,
65 struct inode_state
*state
;
69 pathlen
= strlen(path
);
71 mem_ctx
, offsetof(struct inode_state
, path
) + pathlen
+ 1);
75 talloc_set_name_const(state
, "struct inode_state");
77 ino
= idr_get_new_above(ino_ctx
, state
, 1, INT32_MAX
);
84 state
->ino_ctx
= ino_ctx
;
85 memcpy(state
->path
, path
, pathlen
+ 1);
87 DBG_DEBUG("Creating ino %d for path %s\n", ino
, path
);
89 talloc_set_destructor(state
, inode_state_destructor
);
94 static struct inode_state
*inode_state_new(struct mount_state
*mstate
,
97 return inode_state_init(mstate
->ino_parent
, mstate
->ino_ctx
, path
);
100 static int inode_state_destructor(struct inode_state
*s
)
102 DBG_DEBUG("destroying inode %ju\n", (uintmax_t)s
->ino
);
103 idr_remove(s
->ino_ctx
, s
->ino
);
107 struct ll_create_state
{
108 struct mount_state
*mstate
;
110 struct fuse_file_info fi
;
114 static void cli_ll_create_done(struct tevent_req
*req
);
116 static void cli_ll_create(fuse_req_t freq
, fuse_ino_t parent
, const char *name
,
117 mode_t mode
, struct fuse_file_info
*fi
)
119 struct mount_state
*mstate
= talloc_get_type_abort(
120 fuse_req_userdata(freq
), struct mount_state
);
121 struct ll_create_state
*state
;
122 struct inode_state
*istate
;
123 struct tevent_req
*req
;
125 DBG_DEBUG("parent=%ju, name=%s, mode=%x\n", (uintmax_t)parent
,
126 name
, (unsigned)mode
);
128 istate
= idr_find(mstate
->ino_ctx
, parent
);
129 if (istate
== NULL
) {
130 fuse_reply_err(freq
, ENOENT
);
134 state
= talloc(mstate
, struct ll_create_state
);
136 fuse_reply_err(freq
, ENOMEM
);
139 state
->mstate
= mstate
;
143 state
->path
= talloc_asprintf(state
, "%s%s%s", istate
->path
,
144 strlen(istate
->path
) ? "\\": "",
146 if (state
->path
== NULL
) {
148 fuse_reply_err(freq
, ENOMEM
);
152 req
= cli_smb2_create_fnum_send(
153 state
, mstate
->ev
, mstate
->cli
, state
->path
,
154 0, FILE_GENERIC_READ
|FILE_GENERIC_WRITE
, FILE_ATTRIBUTE_NORMAL
,
155 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
156 FILE_CREATE
, FILE_NON_DIRECTORY_FILE
);
159 fuse_reply_err(freq
, ENOMEM
);
162 tevent_req_set_callback(req
, cli_ll_create_done
, state
);
165 static void cli_ll_create_done(struct tevent_req
*req
)
167 struct ll_create_state
*state
= tevent_req_callback_data(
168 req
, struct ll_create_state
);
169 struct fuse_entry_param e
;
170 struct inode_state
*ino
;
174 status
= cli_smb2_create_fnum_recv(req
, &fnum
, NULL
);
176 if (!NT_STATUS_IS_OK(status
)) {
177 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
182 state
->fi
.direct_io
= 0;
183 state
->fi
.keep_cache
= 0;
185 ino
= inode_state_new(state
->mstate
, state
->path
);
187 fuse_reply_err(state
->freq
, ENOMEM
);
191 e
= (struct fuse_entry_param
) {
193 .generation
= 1, /* FIXME */
198 fuse_reply_create(state
->freq
, &e
, &state
->fi
);
203 struct cli_get_unixattr_state
{
204 struct tevent_context
*ev
;
205 struct cli_state
*cli
;
206 uint64_t fid_persistent
;
207 uint64_t fid_volatile
;
209 struct timespec create_time
;
210 struct timespec access_time
;
211 struct timespec write_time
;
212 struct timespec change_time
;
218 static void cli_get_unixattr_opened(struct tevent_req
*subreq
);
219 static void cli_get_unixattr_gotinfo(struct tevent_req
*subreq
);
220 static void cli_get_unixattr_closed(struct tevent_req
*subreq
);
223 static struct tevent_req
*cli_get_unixattr_send(TALLOC_CTX
*mem_ctx
,
224 struct tevent_context
*ev
,
225 struct cli_state
*cli
,
228 struct tevent_req
*req
, *subreq
;
229 struct cli_get_unixattr_state
*state
;
231 req
= tevent_req_create(mem_ctx
, &state
,
232 struct cli_get_unixattr_state
);
239 subreq
= smb2cli_create_send(
240 state
, ev
, cli
->conn
, cli
->timeout
, cli
->smb2
.session
,
241 cli
->smb2
.tcon
, path
, SMB2_OPLOCK_LEVEL_NONE
,
242 SMB2_IMPERSONATION_IMPERSONATION
,
243 SYNCHRONIZE_ACCESS
|FILE_READ_ATTRIBUTES
, 0,
244 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
246 if (tevent_req_nomem(subreq
, req
)) {
247 return tevent_req_post(req
, ev
);
249 tevent_req_set_callback(subreq
, cli_get_unixattr_opened
, req
);
254 static void cli_get_unixattr_opened(struct tevent_req
*subreq
)
256 struct tevent_req
*req
= tevent_req_callback_data(
257 subreq
, struct tevent_req
);
258 struct cli_get_unixattr_state
*state
= tevent_req_data(
259 req
, struct cli_get_unixattr_state
);
260 struct cli_state
*cli
= state
->cli
;
263 status
= smb2cli_create_recv(subreq
, &state
->fid_persistent
,
264 &state
->fid_volatile
, NULL
, NULL
, NULL
);
266 if (tevent_req_nterror(req
, status
)) {
267 DBG_DEBUG("smb2cli_create_recv returned %s\n",
272 subreq
= smb2cli_query_info_send(
273 state
, state
->ev
, cli
->conn
, 0,
274 cli
->smb2
.session
, cli
->smb2
.tcon
,
275 1, /* in_info_type */
276 (SMB_FILE_ALL_INFORMATION
- 1000), /* in_file_info_class */
277 0xFFFF, /* in_max_output_length */
278 NULL
, /* in_input_buffer */
279 0, /* in_additional_info */
281 state
->fid_persistent
,
282 state
->fid_volatile
);
283 if (tevent_req_nomem(subreq
, req
)) {
286 tevent_req_set_callback(subreq
, cli_get_unixattr_gotinfo
, req
);
289 static void cli_get_unixattr_gotinfo(struct tevent_req
*subreq
)
291 struct tevent_req
*req
= tevent_req_callback_data(
292 subreq
, struct tevent_req
);
293 struct cli_get_unixattr_state
*state
= tevent_req_data(
294 req
, struct cli_get_unixattr_state
);
295 struct cli_state
*cli
= state
->cli
;
299 status
= smb2cli_query_info_recv(subreq
, state
, &outbuf
);
301 if (tevent_req_nterror(req
, status
)) {
302 DBG_DEBUG("smb2cli_query_info_recv returned %s\n",
307 if (outbuf
.length
< 0x60) {
308 tevent_req_nterror(req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
312 state
->create_time
= interpret_long_date((char *)outbuf
.data
+ 0x0);
313 state
->access_time
= interpret_long_date((char *)outbuf
.data
+ 0x8);
314 state
->write_time
= interpret_long_date((char *)outbuf
.data
+ 0x10);
315 state
->change_time
= interpret_long_date((char *)outbuf
.data
+ 0x18);
316 state
->mode
= IVAL(outbuf
.data
, 0x20);
317 state
->size
= BVAL(outbuf
.data
, 0x30);
318 state
->ino
= BVAL(outbuf
.data
, 0x40);
320 subreq
= smb2cli_close_send(state
, state
->ev
, cli
->conn
, 0,
321 cli
->smb2
.session
, cli
->smb2
.tcon
, 0,
322 state
->fid_persistent
,
323 state
->fid_volatile
);
324 if (tevent_req_nomem(subreq
, req
)) {
327 tevent_req_set_callback(subreq
, cli_get_unixattr_closed
, req
);
330 static void cli_get_unixattr_closed(struct tevent_req
*subreq
)
332 struct tevent_req
*req
= tevent_req_callback_data(
333 subreq
, struct tevent_req
);
336 status
= smb2cli_close_recv(subreq
);
338 if (tevent_req_nterror(req
, status
)) {
341 tevent_req_done(req
);
344 static NTSTATUS
cli_get_unixattr_recv(struct tevent_req
*req
,
347 struct cli_get_unixattr_state
*state
= tevent_req_data(
348 req
, struct cli_get_unixattr_state
);
351 if (tevent_req_is_nterror(req
, &status
)) {
355 if (IS_DOS_DIR(state
->mode
)) {
356 st
->st_mode
= (S_IFDIR
| 0555);
359 st
->st_mode
= (S_IFREG
| 0444);
363 st
->st_size
= state
->size
;
364 st
->st_uid
= getuid();
365 st
->st_gid
= getgid();
366 st
->st_ino
= state
->ino
;
367 st
->st_atime
= convert_timespec_to_time_t(state
->access_time
);
368 st
->st_ctime
= convert_timespec_to_time_t(state
->change_time
);
369 st
->st_mtime
= convert_timespec_to_time_t(state
->write_time
);
374 struct cli_smb2_listdir_state
{
375 struct tevent_context
*ev
;
376 struct smbXcli_conn
*conn
;
377 uint32_t timeout_msec
;
378 struct smbXcli_session
*session
;
379 struct smbXcli_tcon
*tcon
;
383 uint64_t fid_persistent
;
384 uint64_t fid_volatile
;
389 const char *mntpoint
;
390 const char *pathname
;
391 NTSTATUS (*fn
)(const char *mntpoint
, struct file_info
*f
,
392 const char *mask
, void *private_data
);
397 static void cli_smb2_listdir_done(struct tevent_req
*subreq
);
399 static struct tevent_req
*cli_smb2_listdir_send(
401 struct tevent_context
*ev
,
402 struct smbXcli_conn
*conn
,
403 uint32_t timeout_msec
,
404 struct smbXcli_session
*session
,
405 struct smbXcli_tcon
*tcon
,
409 uint64_t fid_persistent
,
410 uint64_t fid_volatile
,
414 const char *mntpoint
,
415 const char *pathname
,
416 NTSTATUS (*fn
)(const char *mntpoint
, struct file_info
*f
,
417 const char *mask
, void *private_data
),
420 struct tevent_req
*req
, *subreq
;
421 struct cli_smb2_listdir_state
*state
;
423 req
= tevent_req_create(mem_ctx
, &state
,
424 struct cli_smb2_listdir_state
);
430 state
->timeout_msec
= timeout_msec
;
431 state
->session
= session
;
433 state
->level
= level
;
434 state
->flags
= flags
;
435 state
->file_index
= file_index
;
436 state
->fid_persistent
= fid_persistent
;
437 state
->fid_volatile
= fid_volatile
;
439 state
->outbuf_len
= outbuf_len
;
440 state
->attribute
= attribute
;
441 state
->mntpoint
= mntpoint
;
442 state
->pathname
= pathname
;
444 state
->private_data
= private_data
;
446 subreq
= smb2cli_query_directory_send(
447 state
, state
->ev
, state
->conn
, state
->timeout_msec
,
448 state
->session
, state
->tcon
, state
->level
,
449 state
->flags
, state
->file_index
,
450 state
->fid_persistent
, state
->fid_volatile
,
451 state
->mask
, state
->outbuf_len
);
452 if (tevent_req_nomem(subreq
, req
)) {
453 return tevent_req_post(req
, ev
);
455 tevent_req_set_callback(subreq
, cli_smb2_listdir_done
, req
);
459 static NTSTATUS
parse_finfo_id_both_directory_info(uint8_t *dir_data
,
460 uint32_t dir_data_length
,
461 struct file_info
*finfo
,
462 uint32_t *next_offset
)
468 if (dir_data_length
< 4) {
469 return NT_STATUS_INFO_LENGTH_MISMATCH
;
472 *next_offset
= IVAL(dir_data
, 0);
474 if (*next_offset
> dir_data_length
) {
475 return NT_STATUS_INFO_LENGTH_MISMATCH
;
478 if (*next_offset
!= 0) {
479 /* Ensure we only read what in this record. */
480 dir_data_length
= *next_offset
;
483 if (dir_data_length
< 105) {
484 return NT_STATUS_INFO_LENGTH_MISMATCH
;
487 finfo
->atime_ts
= interpret_long_date((const char *)dir_data
+ 16);
488 finfo
->mtime_ts
= interpret_long_date((const char *)dir_data
+ 24);
489 finfo
->ctime_ts
= interpret_long_date((const char *)dir_data
+ 32);
490 finfo
->size
= IVAL2_TO_SMB_BIG_UINT(dir_data
+ 40, 0);
491 finfo
->mode
= CVAL(dir_data
+ 56, 0);
492 namelen
= IVAL(dir_data
+ 60,0);
493 if (namelen
> (dir_data_length
- 104)) {
494 return NT_STATUS_INFO_LENGTH_MISMATCH
;
496 slen
= CVAL(dir_data
+ 68, 0);
498 return NT_STATUS_INFO_LENGTH_MISMATCH
;
500 ret
= pull_string_talloc(finfo
,
502 FLAGS2_UNICODE_STRINGS
,
507 if (ret
== (size_t)-1) {
508 /* Bad conversion. */
509 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
512 ret
= pull_string_talloc(finfo
,
514 FLAGS2_UNICODE_STRINGS
,
519 if (ret
== (size_t)-1) {
520 /* Bad conversion. */
521 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
526 static void cli_smb2_listdir_done(struct tevent_req
*subreq
)
528 struct tevent_req
*req
= tevent_req_callback_data(
529 subreq
, struct tevent_req
);
530 struct cli_smb2_listdir_state
*state
= tevent_req_data(
531 req
, struct cli_smb2_listdir_state
);
534 uint32_t next_offset
= 0;
537 status
= smb2cli_query_directory_recv(subreq
, state
, &data
,
540 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
)) {
541 tevent_req_done(req
);
544 if (tevent_req_nterror(req
, status
)) {
549 struct file_info
*finfo
;
552 finfo
= talloc_zero(state
, struct file_info
);
553 if (tevent_req_nomem(finfo
, req
)) {
557 status
= parse_finfo_id_both_directory_info(
558 data
, data_len
, finfo
, &next_offset
);
560 DEBUG(10, ("%s: parse_finfo_id_both_directory_info returned "
561 "%s\n", __func__
, nt_errstr(status
)));
563 if (tevent_req_nterror(req
, status
)) {
567 ok
= dir_check_ftype(finfo
->mode
, state
->attribute
);
569 DEBUG(10, ("%s: dir_check_ftype(%u,%u) returned %u\n",
570 __func__
, (unsigned)finfo
->mode
,
571 (unsigned)state
->attribute
, (unsigned)ok
));
575 * Only process if attributes match. On SMB1 server
576 * does this, so on SMB2 we need to emulate in the
579 * https://bugzilla.samba.org/show_bug.cgi?id=10260
581 state
->processed_file
= true;
583 status
= state
->fn(state
->mntpoint
, finfo
,
585 state
->private_data
);
586 if (tevent_req_nterror(req
, status
)) {
593 if (next_offset
!= 0) {
595 data_len
-= next_offset
;
597 } while (next_offset
!= 0);
599 subreq
= smb2cli_query_directory_send(
600 state
, state
->ev
, state
->conn
, state
->timeout_msec
,
601 state
->session
, state
->tcon
, state
->level
,
602 state
->flags
, state
->file_index
,
603 state
->fid_persistent
, state
->fid_volatile
,
604 state
->mask
, state
->outbuf_len
);
605 if (tevent_req_nomem(subreq
, req
)) {
608 tevent_req_set_callback(subreq
, cli_smb2_listdir_done
, req
);
611 static NTSTATUS
cli_smb2_listdir_recv(struct tevent_req
*req
)
613 struct cli_smb2_listdir_state
*state
= tevent_req_data(
614 req
, struct cli_smb2_listdir_state
);
617 if (tevent_req_is_nterror(req
, &status
)) {
621 if (!state
->processed_file
) {
623 * In SMB1 findfirst returns NT_STATUS_NO_SUCH_FILE
624 * if no files match. Emulate this in the client.
626 return NT_STATUS_NO_SUCH_FILE
;
632 struct ll_lookup_state
{
633 struct mount_state
*mstate
;
638 static void cli_ll_lookup_done(struct tevent_req
*req
);
640 static void cli_ll_lookup(fuse_req_t freq
, fuse_ino_t parent_ino
,
643 struct mount_state
*mstate
= talloc_get_type_abort(
644 fuse_req_userdata(freq
), struct mount_state
);
645 struct ll_lookup_state
*state
;
646 struct tevent_req
*req
;
647 struct inode_state
*parent
;
649 DBG_DEBUG("parent_ino=%ju, name=%s\n", (uintmax_t)parent_ino
, name
);
651 parent
= idr_find(mstate
->ino_ctx
, parent_ino
);
652 if (parent
== NULL
) {
653 DBG_WARNING("could not find parent\n");
654 fuse_reply_err(freq
, ENOENT
);
658 state
= talloc(mstate
, struct ll_lookup_state
);
660 DBG_WARNING("talloc failed\n");
661 fuse_reply_err(freq
, ENOMEM
);
664 state
->mstate
= mstate
;
667 state
->path
= talloc_asprintf(state
, "%s%s%s", parent
->path
,
668 strlen(parent
->path
) ? "\\": "",
670 if (state
->path
== NULL
) {
672 fuse_reply_err(freq
, ENOMEM
);
676 req
= cli_get_unixattr_send(state
, mstate
->ev
, mstate
->cli
,
680 fuse_reply_err(freq
, ENOMEM
);
683 tevent_req_set_callback(req
, cli_ll_lookup_done
, state
);
686 static void cli_ll_lookup_done(struct tevent_req
*req
)
688 struct ll_lookup_state
*state
= tevent_req_callback_data(
689 req
, struct ll_lookup_state
);
690 struct stat sbuf
= {0};
691 struct fuse_entry_param e
;
692 struct inode_state
*ino
;
695 status
= cli_get_unixattr_recv(req
, &sbuf
);
697 if (!NT_STATUS_IS_OK(status
)) {
698 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
702 ino
= inode_state_new(state
->mstate
, state
->path
);
704 fuse_reply_err(state
->freq
, ENOMEM
);
708 e
= (struct fuse_entry_param
) {
711 .generation
= 1, /* FIXME */
716 fuse_reply_entry(state
->freq
, &e
);
720 struct ll_getattr_state
{
721 struct mount_state
*mstate
;
723 struct fuse_file_info fi
;
726 static void cli_ll_getattr_done(struct tevent_req
*req
);
728 static void cli_ll_getattr(fuse_req_t freq
, fuse_ino_t ino
,
729 struct fuse_file_info
*fi
)
731 struct mount_state
*mstate
= talloc_get_type_abort(
732 fuse_req_userdata(freq
), struct mount_state
);
733 struct ll_getattr_state
*state
;
734 struct inode_state
*istate
;
735 struct tevent_req
*req
;
737 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino
);
739 istate
= idr_find(mstate
->ino_ctx
, ino
);
740 if (istate
== NULL
) {
741 fuse_reply_err(freq
, ENOENT
);
745 state
= talloc(mstate
, struct ll_getattr_state
);
747 fuse_reply_err(freq
, ENOMEM
);
750 state
->mstate
= mstate
;
753 req
= cli_get_unixattr_send(state
, mstate
->ev
, mstate
->cli
,
757 fuse_reply_err(freq
, ENOMEM
);
760 tevent_req_set_callback(req
, cli_ll_getattr_done
, state
);
763 static void cli_ll_getattr_done(struct tevent_req
*req
)
765 struct ll_getattr_state
*state
= tevent_req_callback_data(
766 req
, struct ll_getattr_state
);
771 status
= cli_get_unixattr_recv(req
, &st
);
773 if (!NT_STATUS_IS_OK(status
)) {
774 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
778 ret
= fuse_reply_attr(state
->freq
, &st
, 1);
780 DBG_NOTICE("fuse_reply_attr failed: %s\n",
786 struct ll_open_state
{
787 struct mount_state
*mstate
;
789 struct fuse_file_info fi
;
792 static void cli_ll_open_done(struct tevent_req
*req
);
794 static void cli_ll_open(fuse_req_t freq
, fuse_ino_t ino
,
795 struct fuse_file_info
*fi
)
797 struct mount_state
*mstate
= talloc_get_type_abort(
798 fuse_req_userdata(freq
), struct mount_state
);
799 struct ll_open_state
*state
;
800 struct inode_state
*istate
;
801 struct tevent_req
*req
;
804 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino
);
806 istate
= idr_find(mstate
->ino_ctx
, ino
);
807 if (istate
== NULL
) {
808 fuse_reply_err(freq
, ENOENT
);
812 state
= talloc(mstate
, struct ll_open_state
);
814 fuse_reply_err(freq
, ENOMEM
);
817 state
->mstate
= mstate
;
821 switch (fi
->flags
& O_ACCMODE
) {
823 acc
= FILE_GENERIC_READ
;
826 acc
= FILE_GENERIC_WRITE
;
829 acc
= FILE_GENERIC_READ
|FILE_GENERIC_WRITE
;
832 fuse_reply_err(freq
, EACCES
);
836 req
= cli_smb2_create_fnum_send(
837 state
, mstate
->ev
, mstate
->cli
, istate
->path
,
838 0, acc
, FILE_ATTRIBUTE_NORMAL
,
839 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
840 FILE_OPEN
, FILE_NON_DIRECTORY_FILE
);
843 fuse_reply_err(freq
, ENOMEM
);
846 tevent_req_set_callback(req
, cli_ll_open_done
, state
);
849 static void cli_ll_open_done(struct tevent_req
*req
)
851 struct ll_open_state
*state
= tevent_req_callback_data(
852 req
, struct ll_open_state
);
856 status
= cli_smb2_create_fnum_recv(req
, &fnum
, NULL
);
858 if (!NT_STATUS_IS_OK(status
)) {
859 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
864 state
->fi
.direct_io
= 0;
865 state
->fi
.keep_cache
= 0;
867 fuse_reply_open(state
->freq
, &state
->fi
);
872 struct ll_release_state
{
873 struct mount_state
*mstate
;
878 static void cli_ll_release_done(struct tevent_req
*req
);
880 static void cli_ll_release(fuse_req_t freq
, fuse_ino_t ino
,
881 struct fuse_file_info
*fi
)
883 struct mount_state
*mstate
= talloc_get_type_abort(
884 fuse_req_userdata(freq
), struct mount_state
);
885 struct ll_release_state
*state
;
886 struct inode_state
*istate
;
887 struct tevent_req
*req
;
890 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino
);
892 istate
= idr_find(mstate
->ino_ctx
, ino
);
893 if (istate
== NULL
) {
894 fuse_reply_err(freq
, ENOENT
);
898 state
= talloc(mstate
, struct ll_release_state
);
900 fuse_reply_err(freq
, ENOMEM
);
903 state
->mstate
= mstate
;
909 req
= cli_smb2_close_fnum_send(state
, mstate
->ev
, mstate
->cli
, fnum
);
912 fuse_reply_err(freq
, ENOMEM
);
915 tevent_req_set_callback(req
, cli_ll_release_done
, state
);
918 static void cli_ll_release_done(struct tevent_req
*req
)
920 struct ll_release_state
*state
= tevent_req_callback_data(
921 req
, struct ll_release_state
);
922 struct inode_state
*istate
;
925 status
= cli_smb2_close_fnum_recv(req
);
927 if (!NT_STATUS_IS_OK(status
)) {
928 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
932 istate
= idr_find(state
->mstate
->ino_ctx
, state
->ino
);
933 if (istate
== NULL
) {
934 DEBUG(1, ("%s: inode %ju vanished!\n", __func__
,
935 (uintmax_t)state
->ino
));
939 fuse_reply_err(state
->freq
, 0);
943 struct ll_read_state
{
944 struct mount_state
*mstate
;
948 static void cli_ll_read_done(struct tevent_req
*req
);
950 static void cli_ll_read(fuse_req_t freq
, fuse_ino_t ino
,
951 size_t size
, off_t off
,
952 struct fuse_file_info
*fi
)
954 struct mount_state
*mstate
= talloc_get_type_abort(
955 fuse_req_userdata(freq
), struct mount_state
);
956 struct ll_read_state
*state
;
957 struct tevent_req
*req
;
960 DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino
,
961 size
, (uintmax_t)off
);
963 state
= talloc(mstate
, struct ll_read_state
);
965 fuse_reply_err(freq
, ENOMEM
);
968 state
->mstate
= mstate
;
973 req
= cli_smb2_read_send(state
, mstate
->ev
, mstate
->cli
,
977 fuse_reply_err(freq
, ENOMEM
);
980 tevent_req_set_callback(req
, cli_ll_read_done
, state
);
983 static void cli_ll_read_done(struct tevent_req
*req
)
985 struct ll_read_state
*state
= tevent_req_callback_data(
986 req
, struct ll_read_state
);
991 status
= cli_smb2_read_recv(req
, &received
, &rcvbuf
);
992 /* no talloc_free here yet */
994 if (NT_STATUS_EQUAL(status
, NT_STATUS_END_OF_FILE
)) {
997 status
= NT_STATUS_OK
;
1000 if (!NT_STATUS_IS_OK(status
)) {
1001 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
1004 fuse_reply_buf(state
->freq
, (char *)rcvbuf
, received
);
1008 struct ll_write_state
{
1009 struct mount_state
*mstate
;
1013 static void cli_ll_write_done(struct tevent_req
*req
);
1015 static void cli_ll_write(fuse_req_t freq
, fuse_ino_t ino
, const char *buf
,
1016 size_t size
, off_t off
, struct fuse_file_info
*fi
)
1018 struct mount_state
*mstate
= talloc_get_type_abort(
1019 fuse_req_userdata(freq
), struct mount_state
);
1020 struct ll_write_state
*state
;
1021 struct tevent_req
*req
;
1024 DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino
,
1025 size
, (uintmax_t)off
);
1027 state
= talloc(mstate
, struct ll_write_state
);
1028 if (state
== NULL
) {
1029 fuse_reply_err(freq
, ENOMEM
);
1032 state
->mstate
= mstate
;
1037 req
= cli_smb2_write_send(state
, mstate
->ev
, mstate
->cli
, fnum
, 0,
1038 (const uint8_t *)buf
, off
, size
);
1041 fuse_reply_err(freq
, ENOMEM
);
1044 tevent_req_set_callback(req
, cli_ll_write_done
, state
);
1047 static void cli_ll_write_done(struct tevent_req
*req
)
1049 struct ll_write_state
*state
= tevent_req_callback_data(
1050 req
, struct ll_write_state
);
1054 status
= cli_smb2_write_recv(req
, &written
);
1055 /* no talloc_free here yet */
1056 if (!NT_STATUS_IS_OK(status
)) {
1057 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
1060 fuse_reply_write(state
->freq
, written
);
1065 struct ll_dir_state
{
1066 uint64_t fid_persistent
;
1067 uint64_t fid_volatile
;
1069 struct file_info
*finfos
;
1070 unsigned num_finfos
, num_sent
;
1073 static bool ll_dir_state_add(struct ll_dir_state
*dir_state
,
1076 struct file_info
*tmp
, *finfo
;
1078 tmp
= talloc_realloc(dir_state
, dir_state
->finfos
,
1079 struct file_info
, dir_state
->num_finfos
+1);
1083 dir_state
->finfos
= tmp
;
1084 finfo
= &dir_state
->finfos
[dir_state
->num_finfos
];
1086 ZERO_STRUCTP(finfo
);
1088 finfo
->name
= talloc_strdup(dir_state
->finfos
, name
);
1089 if (finfo
->name
== NULL
) {
1092 dir_state
->num_finfos
+= 1;
1097 struct ll_opendir_state
{
1098 struct mount_state
*mstate
;
1100 struct fuse_file_info fi
;
1101 struct ll_dir_state
*dir_state
;
1104 static void cli_ll_opendir_done(struct tevent_req
*req
);
1106 static void cli_ll_opendir(fuse_req_t freq
, fuse_ino_t ino
,
1107 struct fuse_file_info
*fi
)
1109 struct mount_state
*mstate
= talloc_get_type_abort(
1110 fuse_req_userdata(freq
), struct mount_state
);
1111 struct cli_state
*cli
= mstate
->cli
;
1112 struct ll_opendir_state
*state
;
1113 struct inode_state
*istate
;
1114 struct tevent_req
*req
;
1116 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino
);
1118 istate
= idr_find(mstate
->ino_ctx
, ino
);
1119 if (istate
== NULL
) {
1120 fuse_reply_err(freq
, ENOENT
);
1124 state
= talloc(mstate
, struct ll_opendir_state
);
1125 if (state
== NULL
) {
1126 fuse_reply_err(freq
, ENOMEM
);
1129 state
->mstate
= mstate
;
1133 state
->dir_state
= talloc_zero(state
, struct ll_dir_state
);
1134 if (state
->dir_state
== NULL
) {
1136 fuse_reply_err(freq
, ENOMEM
);
1140 req
= smb2cli_create_send(
1141 state
, mstate
->ev
, cli
->conn
, cli
->timeout
,
1142 cli
->smb2
.session
, cli
->smb2
.tcon
, istate
->path
,
1143 0, SMB2_IMPERSONATION_IMPERSONATION
,
1144 FILE_GENERIC_READ
, FILE_ATTRIBUTE_DIRECTORY
,
1145 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
1146 FILE_OPEN
, FILE_DIRECTORY_FILE
, NULL
);
1149 fuse_reply_err(freq
, ENOMEM
);
1152 tevent_req_set_callback(req
, cli_ll_opendir_done
, state
);
1155 static void cli_ll_opendir_done(struct tevent_req
*req
)
1157 struct ll_opendir_state
*state
= tevent_req_callback_data(
1158 req
, struct ll_opendir_state
);
1161 status
= smb2cli_create_recv(req
,
1162 &state
->dir_state
->fid_persistent
,
1163 &state
->dir_state
->fid_volatile
,
1167 DEBUG(10, ("%s: smbcli_create_recv returned %s\n", __func__
,
1168 nt_errstr(status
)));
1170 if (!NT_STATUS_IS_OK(status
)) {
1171 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
1175 state
->fi
.fh
= (uint64_t)talloc_move(state
->mstate
, &state
->dir_state
);
1176 state
->fi
.direct_io
= 0;
1177 state
->fi
.keep_cache
= 0;
1179 fuse_reply_open(state
->freq
, &state
->fi
);
1184 struct ll_readdir_state
{
1186 struct ll_dir_state
*dir_state
;
1189 static void cli_ll_readdir_done(struct tevent_req
*subreq
);
1190 static NTSTATUS
cli_ll_readdir_one(const char *mnt
, struct file_info
*finfo
,
1191 const char *path
, void *private_data
);
1192 static void cli_ll_readdir_reply_one(fuse_req_t freq
,
1193 struct ll_dir_state
*dir_state
);
1195 static void cli_ll_readdir(fuse_req_t freq
, fuse_ino_t ino
, size_t size
,
1196 off_t off
, struct fuse_file_info
*fi
)
1198 struct mount_state
*mstate
= talloc_get_type_abort(
1199 fuse_req_userdata(freq
), struct mount_state
);
1200 struct cli_state
*cli
= mstate
->cli
;
1201 struct ll_dir_state
*dir_state
;
1202 struct ll_readdir_state
*state
;
1203 struct tevent_req
*req
;
1205 DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino
, size
,
1208 dir_state
= talloc_get_type_abort((void *)fi
->fh
, struct ll_dir_state
);
1210 if (dir_state
->finfos
!= NULL
) {
1211 DBG_DEBUG("finfos=%p\n", dir_state
->finfos
);
1212 cli_ll_readdir_reply_one(freq
, dir_state
);
1216 if (!ll_dir_state_add(dir_state
, ".") ||
1217 !ll_dir_state_add(dir_state
, "..")) {
1218 fuse_reply_err(freq
, ENOMEM
);
1222 state
= talloc(mstate
, struct ll_readdir_state
);
1223 if (state
== NULL
) {
1224 fuse_reply_err(freq
, ENOMEM
);
1228 state
->dir_state
= dir_state
;
1230 req
= cli_smb2_listdir_send(
1231 state
, mstate
->ev
, cli
->conn
, cli
->timeout
,
1232 cli
->smb2
.session
, cli
->smb2
.tcon
,
1233 SMB2_FIND_ID_BOTH_DIRECTORY_INFO
, 0, 0,
1234 dir_state
->fid_persistent
, dir_state
->fid_volatile
,
1236 FILE_ATTRIBUTE_DIRECTORY
| FILE_ATTRIBUTE_SYSTEM
|
1237 FILE_ATTRIBUTE_HIDDEN
,
1238 NULL
, NULL
, cli_ll_readdir_one
, dir_state
);
1241 fuse_reply_err(freq
, ENOMEM
);
1244 tevent_req_set_callback(req
, cli_ll_readdir_done
, state
);
1247 static void cli_ll_readdir_reply_one(fuse_req_t freq
,
1248 struct ll_dir_state
*dir_state
)
1250 struct file_info
*finfo
;
1252 struct stat sbuf
= {};
1255 if (dir_state
->num_finfos
== dir_state
->num_sent
) {
1256 DEBUG(10, ("%s: Done\n", __func__
));
1257 fuse_reply_buf(freq
, NULL
, 0);
1261 sbuf
.st_mode
= S_IFREG
| 0755;
1262 sbuf
.st_ino
= random(); /* FIXME :-) */
1263 finfo
= &dir_state
->finfos
[dir_state
->num_sent
];
1265 DBG_DEBUG("Adding %s\n", finfo
->name
);
1267 buflen
= fuse_add_direntry(freq
, buf
, sizeof(buf
),
1268 finfo
->name
, &sbuf
, 0);
1269 fuse_reply_buf(freq
, buf
, buflen
);
1270 dir_state
->num_sent
+= 1;
1274 static NTSTATUS
cli_ll_readdir_one(const char *mnt
, struct file_info
*finfo
,
1275 const char *path
, void *private_data
)
1277 struct ll_dir_state
*dir_state
= talloc_get_type_abort(
1278 private_data
, struct ll_dir_state
);
1280 if (ISDOT(finfo
->name
) || ISDOTDOT(finfo
->name
)) {
1281 DEBUG(10, ("%s: Ignoring %s\n", __func__
, finfo
->name
));
1282 return NT_STATUS_OK
;
1285 DBG_DEBUG("Got entry %s\n", finfo
->name
);
1287 if (!ll_dir_state_add(dir_state
, finfo
->name
)) {
1288 return NT_STATUS_NO_MEMORY
;
1290 return NT_STATUS_OK
;
1293 static void cli_ll_readdir_done(struct tevent_req
*req
)
1295 struct ll_readdir_state
*state
= tevent_req_callback_data(
1296 req
, struct ll_readdir_state
);
1299 status
= cli_smb2_listdir_recv(req
);
1301 if (!NT_STATUS_IS_OK(status
)) {
1302 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
1305 cli_ll_readdir_reply_one(state
->freq
, state
->dir_state
);
1310 struct ll_releasedir_state
{
1311 struct mount_state
*mstate
;
1313 struct ll_dir_state
*dir_state
;
1316 static void cli_ll_releasedir_done(struct tevent_req
*req
);
1318 static void cli_ll_releasedir(fuse_req_t freq
, fuse_ino_t ino
,
1319 struct fuse_file_info
*fi
)
1321 struct mount_state
*mstate
= talloc_get_type_abort(
1322 fuse_req_userdata(freq
), struct mount_state
);
1323 struct cli_state
*cli
= mstate
->cli
;
1324 struct ll_releasedir_state
*state
;
1325 struct tevent_req
*req
;
1327 DBG_DEBUG("ino=%ju\n", (uintmax_t)ino
);
1329 state
= talloc(mstate
, struct ll_releasedir_state
);
1330 if (state
== NULL
) {
1331 fuse_reply_err(freq
, ENOMEM
);
1334 state
->mstate
= mstate
;
1337 state
->dir_state
= talloc_get_type_abort(
1338 (void *)fi
->fh
, struct ll_dir_state
);
1340 req
= smb2cli_close_send(state
, mstate
->ev
, cli
->conn
, cli
->timeout
,
1341 cli
->smb2
.session
, cli
->smb2
.tcon
, 0,
1342 state
->dir_state
->fid_persistent
,
1343 state
->dir_state
->fid_volatile
);
1346 fuse_reply_err(freq
, ENOMEM
);
1349 tevent_req_set_callback(req
, cli_ll_releasedir_done
, state
);
1352 static void cli_ll_releasedir_done(struct tevent_req
*req
)
1354 struct ll_releasedir_state
*state
= tevent_req_callback_data(
1355 req
, struct ll_releasedir_state
);
1358 status
= smb2cli_close_recv(req
);
1360 if (!NT_STATUS_IS_OK(status
)) {
1361 fuse_reply_err(state
->freq
, map_errno_from_nt_status(status
));
1364 TALLOC_FREE(state
->dir_state
);
1365 fuse_reply_err(state
->freq
, 0);
1369 static struct fuse_lowlevel_ops cli_ll_ops
= {
1370 .lookup
= cli_ll_lookup
,
1371 .getattr
= cli_ll_getattr
,
1372 .open
= cli_ll_open
,
1373 .create
= cli_ll_create
,
1374 .release
= cli_ll_release
,
1375 .read
= cli_ll_read
,
1376 .write
= cli_ll_write
,
1377 .opendir
= cli_ll_opendir
,
1378 .readdir
= cli_ll_readdir
,
1379 .releasedir
= cli_ll_releasedir
,
1382 static void fuse_chan_fd_handler(struct tevent_context
*ev
,
1383 struct tevent_fd
*fde
,
1385 void *private_data
);
1386 static void fuse_chan_signal_handler(struct tevent_context
*ev
,
1387 struct tevent_signal
*se
,
1391 void *private_data
);
1393 static int mount_state_destructor(struct mount_state
*s
);
1395 int do_mount(struct cli_state
*cli
, const char *mountpoint
)
1397 struct mount_state
*state
;
1398 struct inode_state
*ino
;
1399 struct fuse_args args
= { 0 };
1403 state
= talloc_zero(talloc_tos(), struct mount_state
);
1404 if (state
== NULL
) {
1405 fprintf(stderr
, "talloc failed\n");
1409 state
->ev
= tevent_context_init(state
);
1410 if (state
->ev
== NULL
) {
1411 fprintf(stderr
, "tevent_context_init failed\n");
1416 state
->ino_ctx
= idr_init(state
);
1417 if (state
->ino_ctx
== NULL
) {
1418 fprintf(stderr
, "idr_init failed\n");
1423 state
->ino_parent
= talloc_new(state
);
1424 if (state
->ino_parent
== NULL
) {
1425 fprintf(stderr
, "talloc_new failed\n");
1430 talloc_set_destructor(state
, mount_state_destructor
);
1432 ino
= inode_state_new(state
, "");
1434 fprintf(stderr
, "inode_state_new failed\n");
1438 if (ino
->ino
!= FUSE_ROOT_ID
) {
1439 fprintf(stderr
, "first inode gave %d, expected %d\n",
1440 (int)ino
->ino
, FUSE_ROOT_ID
);
1447 state
->ch
= fuse_mount(mountpoint
, &args
);
1448 if (state
->ch
== NULL
) {
1449 perror("fuse_mount failed");
1453 state
->bufsize
= fuse_chan_bufsize(state
->ch
);
1454 state
->buf
= talloc_array(state
, char, state
->bufsize
);
1455 if (state
->buf
== NULL
) {
1456 fprintf(stderr
, "talloc failed\n");
1460 fd
= fuse_chan_fd(state
->ch
);
1462 state
->fde
= tevent_add_fd(state
->ev
, state
, fd
, TEVENT_FD_READ
,
1463 fuse_chan_fd_handler
, state
);
1464 if (state
->fde
== NULL
) {
1465 fprintf(stderr
, "tevent_add_fd failed\n");
1469 state
->signal_ev
= tevent_add_signal(state
->ev
, state
, SIGINT
, 0,
1470 fuse_chan_signal_handler
, state
);
1471 if (state
->signal_ev
== NULL
) {
1472 fprintf(stderr
, "tevent_add_signal failed\n");
1476 state
->se
= fuse_lowlevel_new(&args
, &cli_ll_ops
, sizeof(cli_ll_ops
),
1478 if (state
->se
== NULL
) {
1479 perror("fuse_lowlevel_new failed");
1483 fuse_session_add_chan(state
->se
, state
->ch
);
1485 while (!state
->done
) {
1486 ret
= tevent_loop_once(state
->ev
);
1488 perror("tevent_loop_once failed");
1493 fuse_session_remove_chan(state
->ch
);
1494 fuse_session_destroy(state
->se
);
1496 fuse_unmount(mountpoint
, state
->ch
);
1502 static int mount_state_destructor(struct mount_state
*s
)
1504 TALLOC_FREE(s
->ino_parent
);
1509 static void fuse_chan_fd_handler(struct tevent_context
*ev
,
1510 struct tevent_fd
*fde
,
1514 struct mount_state
*state
= talloc_get_type_abort(
1515 private_data
, struct mount_state
);
1518 if ((flags
& TEVENT_FD_READ
) == 0) {
1522 recvd
= fuse_chan_recv(&state
->ch
, state
->buf
, state
->bufsize
);
1527 fuse_session_process(state
->se
, state
->buf
, recvd
, state
->ch
);
1530 static void fuse_chan_signal_handler(struct tevent_context
*ev
,
1531 struct tevent_signal
*se
,
1537 struct mount_state
*state
= talloc_get_type_abort(
1538 private_data
, struct mount_state
);