lib: Avoid memcpy in debug_systemd_log()
[Samba.git] / examples / fuse / clifuse.c
blob59af1602980764a50302f80ba0ebf6c5d6360cdc
1 /*
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"
25 #include "client.h"
26 #include "trans2.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"
33 #include "clifuse.h"
34 #include "lib/util/idtree.h"
36 struct mount_state {
37 struct tevent_context *ev;
38 struct cli_state *cli;
39 bool done;
41 struct tevent_fd *fde;
42 struct tevent_signal *signal_ev;
44 struct fuse_chan *ch;
45 struct fuse_session *se;
47 size_t bufsize;
48 char *buf;
50 struct idr_context *ino_ctx;
51 TALLOC_CTX *ino_parent;
54 struct inode_state {
55 struct idr_context *ino_ctx;
56 fuse_ino_t ino;
57 char path[];
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,
64 const char *path)
66 struct inode_state *state;
67 size_t pathlen;
68 int ino;
70 pathlen = strlen(path);
71 state = talloc_size(
72 mem_ctx, offsetof(struct inode_state, path) + pathlen + 1);
73 if (state == NULL) {
74 return NULL;
76 talloc_set_name_const(state, "struct inode_state");
78 ino = idr_get_new_above(ino_ctx, state, 1, INT32_MAX);
79 if (ino == -1) {
80 TALLOC_FREE(state);
81 return NULL;
84 state->ino = ino;
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);
92 return state;
95 static struct inode_state *inode_state_new(struct mount_state *mstate,
96 const char *path)
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);
105 return 0;
108 struct ll_create_state {
109 struct mount_state *mstate;
110 fuse_req_t freq;
111 struct fuse_file_info fi;
112 char *path;
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);
132 return;
135 state = talloc(mstate, struct ll_create_state);
136 if (state == NULL) {
137 fuse_reply_err(freq, ENOMEM);
138 return;
140 state->mstate = mstate;
141 state->freq = freq;
142 state->fi = *fi;
144 state->path = talloc_asprintf(state, "%s%s%s", istate->path,
145 strlen(istate->path) ? "\\": "",
146 name);
147 if (state->path == NULL) {
148 TALLOC_FREE(state);
149 fuse_reply_err(freq, ENOMEM);
150 return;
153 req = cli_smb2_create_fnum_send(
154 state,
155 mstate->ev,
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,
162 FILE_CREATE,
163 FILE_NON_DIRECTORY_FILE,
164 NULL);
165 if (req == NULL) {
166 TALLOC_FREE(state);
167 fuse_reply_err(freq, ENOMEM);
168 return;
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;
179 uint16_t fnum;
180 NTSTATUS status;
182 status = cli_smb2_create_fnum_recv(req, &fnum, NULL, NULL, NULL, NULL);
183 TALLOC_FREE(req);
184 if (!NT_STATUS_IS_OK(status)) {
185 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
186 return;
189 state->fi.fh = fnum;
190 state->fi.direct_io = 0;
191 state->fi.keep_cache = 0;
193 ino = inode_state_new(state->mstate, state->path);
194 if (ino == NULL) {
195 fuse_reply_err(state->freq, ENOMEM);
196 return;
199 e = (struct fuse_entry_param) {
200 .ino = ino->ino,
201 .generation = 1, /* FIXME */
202 .attr_timeout = 1.0,
203 .entry_timeout = 1.0
206 fuse_reply_create(state->freq, &e, &state->fi);
208 TALLOC_FREE(state);
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;
221 uint32_t mode;
222 uint64_t ino;
223 uint64_t size;
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,
234 const char *path)
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);
241 if (req == NULL) {
242 return NULL;
244 state->ev = ev;
245 state->cli = cli;
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,
253 FILE_OPEN, 0, NULL);
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);
259 return 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;
269 NTSTATUS status;
271 status = smb2cli_create_recv(
272 subreq,
273 &state->fid_persistent,
274 &state->fid_volatile,
275 NULL,
276 NULL,
277 NULL,
278 NULL);
279 TALLOC_FREE(subreq);
280 if (tevent_req_nterror(req, status)) {
281 DBG_DEBUG("smb2cli_create_recv returned %s\n",
282 nt_errstr(status));
283 return;
286 subreq = smb2cli_query_info_send(
287 state,
288 state->ev,
289 cli->conn,
291 cli->smb2.session,
292 cli->smb2.tcon,
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 */
298 0, /* in_flags */
299 state->fid_persistent,
300 state->fid_volatile);
301 if (tevent_req_nomem(subreq, req)) {
302 return;
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;
314 NTSTATUS status;
315 DATA_BLOB outbuf;
317 status = smb2cli_query_info_recv(subreq, state, &outbuf);
318 TALLOC_FREE(subreq);
319 if (tevent_req_nterror(req, status)) {
320 DBG_DEBUG("smb2cli_query_info_recv returned %s\n",
321 nt_errstr(status));
322 return;
325 if (outbuf.length < 0x60) {
326 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
327 return;
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)) {
343 return;
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);
352 NTSTATUS status;
354 status = smb2cli_close_recv(subreq);
355 TALLOC_FREE(subreq);
356 if (tevent_req_nterror(req, status)) {
357 return;
359 tevent_req_done(req);
362 static NTSTATUS cli_get_unixattr_recv(struct tevent_req *req,
363 struct stat *st)
365 struct cli_get_unixattr_state *state = tevent_req_data(
366 req, struct cli_get_unixattr_state);
367 NTSTATUS status;
369 if (tevent_req_is_nterror(req, &status)) {
370 return status;
373 if (state->mode & FILE_ATTRIBUTE_DIRECTORY) {
374 st->st_mode = (S_IFDIR | 0555);
375 st->st_nlink = 2;
376 } else {
377 st->st_mode = (S_IFREG | 0444);
378 st->st_nlink = 1;
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);
389 return NT_STATUS_OK;
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;
398 uint8_t level;
399 uint8_t flags;
400 uint32_t file_index;
401 uint64_t fid_persistent;
402 uint64_t fid_volatile;
403 const char *mask;
404 uint32_t outbuf_len;
406 uint16_t attribute;
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);
411 void *private_data;
412 bool processed_file;
415 static void cli_smb2_listdir_done(struct tevent_req *subreq);
417 static struct tevent_req *cli_smb2_listdir_send(
418 TALLOC_CTX *mem_ctx,
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,
424 uint8_t level,
425 uint8_t flags,
426 uint32_t file_index,
427 uint64_t fid_persistent,
428 uint64_t fid_volatile,
429 const char *mask,
430 uint32_t outbuf_len,
431 uint16_t attribute,
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),
436 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);
443 if (req == NULL) {
444 return NULL;
446 state->ev = ev;
447 state->conn = conn;
448 state->timeout_msec = timeout_msec;
449 state->session = session;
450 state->tcon = tcon;
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;
456 state->mask = mask;
457 state->outbuf_len = outbuf_len;
458 state->attribute = attribute;
459 state->mntpoint = mntpoint;
460 state->pathname = pathname;
461 state->fn = fn;
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);
474 return 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)
482 size_t namelen = 0;
483 size_t slen = 0;
484 size_t ret = 0;
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);
516 if (slen > 24) {
517 return NT_STATUS_INFO_LENGTH_MISMATCH;
519 ret = pull_string_talloc(finfo,
520 dir_data,
521 FLAGS2_UNICODE_STRINGS,
522 &finfo->short_name,
523 dir_data + 70,
524 slen,
525 STR_UNICODE);
526 if (ret == (size_t)-1) {
527 /* Bad conversion. */
528 return NT_STATUS_INVALID_NETWORK_RESPONSE;
531 ret = pull_string_talloc(finfo,
532 dir_data,
533 FLAGS2_UNICODE_STRINGS,
534 &finfo->name,
535 dir_data + 104,
536 namelen,
537 STR_UNICODE);
538 if (ret == (size_t)-1) {
539 /* Bad conversion. */
540 return NT_STATUS_INVALID_NETWORK_RESPONSE;
542 return NT_STATUS_OK;
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);
551 uint8_t *data;
552 uint32_t data_len;
553 uint32_t next_offset = 0;
554 NTSTATUS status;
556 status = smb2cli_query_directory_recv(subreq, state, &data,
557 &data_len);
558 TALLOC_FREE(subreq);
559 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
560 tevent_req_done(req);
561 return;
563 if (tevent_req_nterror(req, status)) {
564 return;
567 do {
568 struct file_info *finfo;
569 bool ok;
571 finfo = talloc_zero(state, struct file_info);
572 if (tevent_req_nomem(finfo, req)) {
573 return;
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)) {
583 return;
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));
592 if (ok) {
594 * Only process if attributes match. On SMB1 server
595 * does this, so on SMB2 we need to emulate in the
596 * client.
598 * https://bugzilla.samba.org/show_bug.cgi?id=10260
600 state->processed_file = true;
602 status = state->fn(state->mntpoint, finfo,
603 state->pathname,
604 state->private_data);
605 if (tevent_req_nterror(req, status)) {
606 return;
610 TALLOC_FREE(finfo);
612 if (next_offset != 0) {
613 data += next_offset;
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)) {
625 return;
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);
634 NTSTATUS status;
636 if (tevent_req_is_nterror(req, &status)) {
637 return 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;
648 return NT_STATUS_OK;
651 struct ll_lookup_state {
652 struct mount_state *mstate;
653 fuse_req_t freq;
654 char *path;
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,
660 const char *name)
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);
674 return;
677 state = talloc(mstate, struct ll_lookup_state);
678 if (state == NULL) {
679 DBG_WARNING("talloc failed\n");
680 fuse_reply_err(freq, ENOMEM);
681 return;
683 state->mstate = mstate;
684 state->freq = freq;
686 state->path = talloc_asprintf(state, "%s%s%s", parent->path,
687 strlen(parent->path) ? "\\": "",
688 name);
689 if (state->path == NULL) {
690 TALLOC_FREE(state);
691 fuse_reply_err(freq, ENOMEM);
692 return;
695 req = cli_get_unixattr_send(state, mstate->ev, mstate->cli,
696 state->path);
697 if (req == NULL) {
698 TALLOC_FREE(state);
699 fuse_reply_err(freq, ENOMEM);
700 return;
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;
712 NTSTATUS status;
714 status = cli_get_unixattr_recv(req, &sbuf);
715 TALLOC_FREE(req);
716 if (!NT_STATUS_IS_OK(status)) {
717 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
718 return;
721 ino = inode_state_new(state->mstate, state->path);
722 if (ino == NULL) {
723 fuse_reply_err(state->freq, ENOMEM);
724 return;
727 e = (struct fuse_entry_param) {
728 .ino = ino->ino,
729 .attr = sbuf,
730 .generation = 1, /* FIXME */
731 .attr_timeout = 1.0,
732 .entry_timeout = 1.0
735 fuse_reply_entry(state->freq, &e);
736 TALLOC_FREE(state);
739 static void
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),
744 struct mount_state);
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);
752 return;
754 TALLOC_FREE(istate);
755 fuse_reply_none(freq);
758 struct ll_getattr_state {
759 struct mount_state *mstate;
760 fuse_req_t freq;
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);
780 return;
783 state = talloc(mstate, struct ll_getattr_state);
784 if (state == NULL) {
785 fuse_reply_err(freq, ENOMEM);
786 return;
788 state->mstate = mstate;
789 state->freq = freq;
791 req = cli_get_unixattr_send(state, mstate->ev, mstate->cli,
792 istate->path);
793 if (req == NULL) {
794 TALLOC_FREE(state);
795 fuse_reply_err(freq, ENOMEM);
796 return;
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);
805 struct stat st;
806 NTSTATUS status;
807 int ret;
809 status = cli_get_unixattr_recv(req, &st);
810 TALLOC_FREE(req);
811 if (!NT_STATUS_IS_OK(status)) {
812 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
813 return;
816 ret = fuse_reply_attr(state->freq, &st, 1);
817 if (ret != 0) {
818 DBG_NOTICE("fuse_reply_attr failed: %s\n",
819 strerror(-errno));
824 struct ll_open_state {
825 struct mount_state *mstate;
826 fuse_req_t freq;
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;
840 uint32_t acc;
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);
847 return;
850 state = talloc(mstate, struct ll_open_state);
851 if (state == NULL) {
852 fuse_reply_err(freq, ENOMEM);
853 return;
855 state->mstate = mstate;
856 state->freq = freq;
857 state->fi = *fi;
859 switch (fi->flags & O_ACCMODE) {
860 case O_RDONLY:
861 acc = FILE_GENERIC_READ;
862 break;
863 case O_WRONLY:
864 acc = FILE_GENERIC_WRITE;
865 break;
866 case O_RDWR:
867 acc = FILE_GENERIC_READ|FILE_GENERIC_WRITE;
868 break;
869 default:
870 fuse_reply_err(freq, EACCES);
871 return;
874 req = cli_smb2_create_fnum_send(
875 state,
876 mstate->ev,
877 mstate->cli,
878 istate->path,
879 (struct cli_smb2_create_flags){0},
880 SMB2_IMPERSONATION_IMPERSONATION,
881 acc,
882 FILE_ATTRIBUTE_NORMAL,
883 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
884 FILE_OPEN,
885 FILE_NON_DIRECTORY_FILE,
886 NULL);
887 if (req == NULL) {
888 TALLOC_FREE(state);
889 fuse_reply_err(freq, ENOMEM);
890 return;
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);
899 uint16_t fnum;
900 NTSTATUS status;
902 status = cli_smb2_create_fnum_recv(req, &fnum, NULL, NULL, NULL, NULL);
903 TALLOC_FREE(req);
904 if (!NT_STATUS_IS_OK(status)) {
905 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
906 return;
909 state->fi.fh = fnum;
910 state->fi.direct_io = 0;
911 state->fi.keep_cache = 0;
913 fuse_reply_open(state->freq, &state->fi);
915 TALLOC_FREE(state);
918 struct ll_release_state {
919 struct mount_state *mstate;
920 fuse_req_t freq;
921 fuse_ino_t ino;
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;
934 uint16_t fnum;
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);
941 return;
944 state = talloc(mstate, struct ll_release_state);
945 if (state == NULL) {
946 fuse_reply_err(freq, ENOMEM);
947 return;
949 state->mstate = mstate;
950 state->freq = freq;
951 state->ino = ino;
953 fnum = fi->fh;
955 req = cli_smb2_close_fnum_send(state, mstate->ev, mstate->cli, fnum, 0);
956 if (req == NULL) {
957 TALLOC_FREE(state);
958 fuse_reply_err(freq, ENOMEM);
959 return;
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;
969 NTSTATUS status;
971 status = cli_smb2_close_fnum_recv(req);
972 TALLOC_FREE(req);
973 if (!NT_STATUS_IS_OK(status)) {
974 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
975 return;
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));
983 TALLOC_FREE(istate);
985 fuse_reply_err(state->freq, 0);
986 TALLOC_FREE(state);
989 struct ll_read_state {
990 struct mount_state *mstate;
991 fuse_req_t freq;
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;
1004 uint16_t fnum;
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);
1012 return;
1014 state->mstate = mstate;
1015 state->freq = freq;
1017 fnum = fi->fh;
1019 req = cli_smb2_read_send(state, mstate->ev, mstate->cli,
1020 fnum, off, size);
1021 if (req == NULL) {
1022 TALLOC_FREE(state);
1023 fuse_reply_err(freq, ENOMEM);
1024 return;
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);
1033 ssize_t received;
1034 uint8_t *rcvbuf;
1035 NTSTATUS status;
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)) {
1041 received = 0;
1042 rcvbuf = NULL;
1043 status = NT_STATUS_OK;
1046 if (!NT_STATUS_IS_OK(status)) {
1047 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
1048 return;
1050 fuse_reply_buf(state->freq, (char *)rcvbuf, received);
1051 TALLOC_FREE(state);
1054 struct ll_write_state {
1055 struct mount_state *mstate;
1056 fuse_req_t freq;
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;
1068 uint16_t fnum;
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);
1076 return;
1078 state->mstate = mstate;
1079 state->freq = freq;
1081 fnum = fi->fh;
1083 req = cli_smb2_write_send(state, mstate->ev, mstate->cli, fnum, 0,
1084 (const uint8_t *)buf, off, size);
1085 if (req == NULL) {
1086 TALLOC_FREE(state);
1087 fuse_reply_err(freq, ENOMEM);
1088 return;
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);
1097 size_t written;
1098 NTSTATUS status;
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));
1104 return;
1106 fuse_reply_write(state->freq, written);
1107 TALLOC_FREE(state);
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,
1120 const char *name)
1122 struct file_info *tmp, *finfo;
1124 tmp = talloc_realloc(dir_state, dir_state->finfos,
1125 struct file_info, dir_state->num_finfos+1);
1126 if (tmp == NULL) {
1127 return false;
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) {
1136 return false;
1138 dir_state->num_finfos += 1;
1140 return true;
1143 struct ll_opendir_state {
1144 struct mount_state *mstate;
1145 fuse_req_t freq;
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);
1167 return;
1170 state = talloc(mstate, struct ll_opendir_state);
1171 if (state == NULL) {
1172 fuse_reply_err(freq, ENOMEM);
1173 return;
1175 state->mstate = mstate;
1176 state->freq = freq;
1177 state->fi = *fi;
1179 state->dir_state = talloc_zero(state, struct ll_dir_state);
1180 if (state->dir_state == NULL) {
1181 TALLOC_FREE(state);
1182 fuse_reply_err(freq, ENOMEM);
1183 return;
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);
1193 if (req == NULL) {
1194 TALLOC_FREE(state);
1195 fuse_reply_err(freq, ENOMEM);
1196 return;
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);
1205 NTSTATUS status;
1207 status = smb2cli_create_recv(
1208 req,
1209 &state->dir_state->fid_persistent,
1210 &state->dir_state->fid_volatile,
1211 NULL,
1212 NULL,
1213 NULL,
1214 NULL);
1215 TALLOC_FREE(req);
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));
1222 return;
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);
1231 TALLOC_FREE(state);
1234 struct ll_readdir_state {
1235 fuse_req_t freq;
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,
1256 (uintmax_t)off);
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);
1263 return;
1266 if (!ll_dir_state_add(dir_state, ".") ||
1267 !ll_dir_state_add(dir_state, "..")) {
1268 fuse_reply_err(freq, ENOMEM);
1269 return;
1272 state = talloc(mstate, struct ll_readdir_state);
1273 if (state == NULL) {
1274 fuse_reply_err(freq, ENOMEM);
1275 return;
1277 state->freq = freq;
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,
1285 "*", 0xffff,
1286 FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM |
1287 FILE_ATTRIBUTE_HIDDEN,
1288 NULL, NULL, cli_ll_readdir_one, dir_state);
1289 if (req == NULL) {
1290 TALLOC_FREE(state);
1291 fuse_reply_err(freq, ENOMEM);
1292 return;
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;
1301 char buf[1024];
1302 struct stat sbuf = {};
1303 size_t buflen;
1305 if (dir_state->num_finfos == dir_state->num_sent) {
1306 DEBUG(10, ("%s: Done\n", __func__));
1307 fuse_reply_buf(freq, NULL, 0);
1308 return;
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;
1321 return;
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);
1347 NTSTATUS status;
1349 status = cli_smb2_listdir_recv(req);
1350 TALLOC_FREE(req);
1351 if (!NT_STATUS_IS_OK(status)) {
1352 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
1353 return;
1355 cli_ll_readdir_reply_one(state->freq, state->dir_state);
1356 TALLOC_FREE(state);
1360 struct ll_releasedir_state {
1361 struct mount_state *mstate;
1362 fuse_req_t freq;
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);
1382 return;
1384 state->mstate = mstate;
1385 state->freq = freq;
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);
1394 if (req == NULL) {
1395 TALLOC_FREE(state);
1396 fuse_reply_err(freq, ENOMEM);
1397 return;
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);
1406 NTSTATUS status;
1408 status = smb2cli_close_recv(req);
1409 TALLOC_FREE(req);
1410 if (!NT_STATUS_IS_OK(status)) {
1411 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
1412 return;
1414 TALLOC_FREE(state->dir_state);
1415 fuse_reply_err(state->freq, 0);
1416 TALLOC_FREE(state);
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,
1435 uint16_t flags,
1436 void *private_data);
1437 static void fuse_chan_signal_handler(struct tevent_context *ev,
1438 struct tevent_signal *se,
1439 int signum,
1440 int count,
1441 void *siginfo,
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 };
1451 int fd;
1452 int ret = 1;
1454 state = talloc_zero(talloc_tos(), struct mount_state);
1455 if (state == NULL) {
1456 fprintf(stderr, "talloc failed\n");
1457 return 1;
1460 state->ev = tevent_context_init(state);
1461 if (state->ev == NULL) {
1462 fprintf(stderr, "tevent_context_init failed\n");
1463 TALLOC_FREE(state);
1464 return 1;
1467 state->ino_ctx = idr_init(state);
1468 if (state->ino_ctx == NULL) {
1469 fprintf(stderr, "idr_init failed\n");
1470 TALLOC_FREE(state);
1471 return 1;
1474 state->ino_parent = talloc_new(state);
1475 if (state->ino_parent == NULL) {
1476 fprintf(stderr, "talloc_new failed\n");
1477 TALLOC_FREE(state);
1478 return 1;
1481 talloc_set_destructor(state, mount_state_destructor);
1483 ino = inode_state_new(state, "");
1484 if (ino == NULL) {
1485 fprintf(stderr, "inode_state_new failed\n");
1486 TALLOC_FREE(state);
1487 return 1;
1489 if (ino->ino != FUSE_ROOT_ID) {
1490 fprintf(stderr, "first inode gave %d, expected %d\n",
1491 (int)ino->ino, FUSE_ROOT_ID);
1492 TALLOC_FREE(state);
1493 return 1;
1496 state->cli = cli;
1498 state->ch = fuse_mount(mountpoint, &args);
1499 if (state->ch == NULL) {
1500 perror("fuse_mount failed");
1501 goto fail_free;
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");
1508 goto fail_unmount;
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");
1517 goto fail_unmount;
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");
1524 goto fail_unmount;
1527 state->se = fuse_lowlevel_new(&args, &cli_ll_ops, sizeof(cli_ll_ops),
1528 state);
1529 if (state->se == NULL) {
1530 perror("fuse_lowlevel_new failed");
1531 goto fail_unmount;
1534 fuse_session_add_chan(state->se, state->ch);
1536 while (!state->done) {
1537 ret = tevent_loop_once(state->ev);
1538 if (ret == -1) {
1539 perror("tevent_loop_once failed");
1540 break;
1544 fuse_session_remove_chan(state->ch);
1545 fuse_session_destroy(state->se);
1546 fail_unmount:
1547 fuse_unmount(mountpoint, state->ch);
1548 fail_free:
1549 TALLOC_FREE(state);
1550 return ret;
1553 static int mount_state_destructor(struct mount_state *s)
1555 TALLOC_FREE(s->ino_parent);
1556 return 0;
1560 static void fuse_chan_fd_handler(struct tevent_context *ev,
1561 struct tevent_fd *fde,
1562 uint16_t flags,
1563 void *private_data)
1565 struct mount_state *state = talloc_get_type_abort(
1566 private_data, struct mount_state);
1567 int recvd;
1569 if ((flags & TEVENT_FD_READ) == 0) {
1570 return;
1573 recvd = fuse_chan_recv(&state->ch, state->buf, state->bufsize);
1574 if (recvd <= 0) {
1575 state->done = true;
1576 return;
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,
1583 int signum,
1584 int count,
1585 void *siginfo,
1586 void *private_data)
1588 struct mount_state *state = talloc_get_type_abort(
1589 private_data, struct mount_state);
1590 state->done = true;