s4:torture: add a test-suite for VSS
[bbaumbach/samba-autobuild/.git] / examples / fuse / clifuse.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * fusermount smb2 client
4  * Copyright (C) Volker Lendecke 2016
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 #define FUSE_USE_VERSION 26
21 #define _FILE_OFFSET_BITS 64
22 #include "fuse/fuse_lowlevel.h"
23
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
35 struct mount_state {
36         struct tevent_context *ev;
37         struct cli_state *cli;
38         bool done;
39
40         struct tevent_fd *fde;
41         struct tevent_signal *signal_ev;
42
43         struct fuse_chan *ch;
44         struct fuse_session *se;
45
46         size_t bufsize;
47         char *buf;
48
49         struct idr_context *ino_ctx;
50         TALLOC_CTX *ino_parent;
51 };
52
53 struct inode_state {
54         struct idr_context *ino_ctx;
55         fuse_ino_t ino;
56         char path[1];
57 };
58
59 static int inode_state_destructor(struct inode_state *s);
60
61 static struct inode_state *inode_state_init(TALLOC_CTX *mem_ctx,
62                                             struct idr_context *ino_ctx,
63                                             const char *path)
64 {
65         struct inode_state *state;
66         size_t pathlen;
67         int ino;
68
69         pathlen = strlen(path);
70         state = talloc_size(
71                 mem_ctx, offsetof(struct inode_state, path) + pathlen + 1);
72         if (state == NULL) {
73                 return NULL;
74         }
75         talloc_set_name_const(state, "struct inode_state");
76
77         ino = idr_get_new_above(ino_ctx, state, 1, INT32_MAX);
78         if (ino == -1) {
79                 TALLOC_FREE(state);
80                 return NULL;
81         }
82
83         state->ino = ino;
84         state->ino_ctx = ino_ctx;
85         memcpy(state->path, path, pathlen + 1);
86
87         DBG_DEBUG("Creating ino %d for path %s\n", ino, path);
88
89         talloc_set_destructor(state, inode_state_destructor);
90
91         return state;
92 }
93
94 static struct inode_state *inode_state_new(struct mount_state *mstate,
95                                            const char *path)
96 {
97         return inode_state_init(mstate->ino_parent, mstate->ino_ctx, path);
98 }
99
100 static int inode_state_destructor(struct inode_state *s)
101 {
102         DBG_DEBUG("destroying inode %ju\n", (uintmax_t)s->ino);
103         idr_remove(s->ino_ctx, s->ino);
104         return 0;
105 }
106
107 struct ll_create_state {
108         struct mount_state *mstate;
109         fuse_req_t freq;
110         struct fuse_file_info fi;
111         char *path;
112 };
113
114 static void cli_ll_create_done(struct tevent_req *req);
115
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)
118 {
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;
124
125         DBG_DEBUG("parent=%ju, name=%s, mode=%x\n", (uintmax_t)parent,
126                   name, (unsigned)mode);
127
128         istate = idr_find(mstate->ino_ctx, parent);
129         if (istate == NULL) {
130                 fuse_reply_err(freq, ENOENT);
131                 return;
132         }
133
134         state = talloc(mstate, struct ll_create_state);
135         if (state == NULL) {
136                 fuse_reply_err(freq, ENOMEM);
137                 return;
138         }
139         state->mstate = mstate;
140         state->freq = freq;
141         state->fi = *fi;
142
143         state->path = talloc_asprintf(state, "%s%s%s", istate->path,
144                                       strlen(istate->path) ? "\\": "",
145                                       name);
146         if (state->path == NULL) {
147                 TALLOC_FREE(state);
148                 fuse_reply_err(freq, ENOMEM);
149                 return;
150         }
151
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);
157         if (req == NULL) {
158                 TALLOC_FREE(state);
159                 fuse_reply_err(freq, ENOMEM);
160                 return;
161         }
162         tevent_req_set_callback(req, cli_ll_create_done, state);
163 }
164
165 static void cli_ll_create_done(struct tevent_req *req)
166 {
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;
171         uint16_t fnum;
172         NTSTATUS status;
173
174         status = cli_smb2_create_fnum_recv(req, &fnum, NULL);
175         TALLOC_FREE(req);
176         if (!NT_STATUS_IS_OK(status)) {
177                 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
178                 return;
179         }
180
181         state->fi.fh = fnum;
182         state->fi.direct_io = 0;
183         state->fi.keep_cache = 0;
184
185         ino = inode_state_new(state->mstate, state->path);
186         if (ino == NULL) {
187                 fuse_reply_err(state->freq, ENOMEM);
188                 return;
189         }
190
191         e = (struct fuse_entry_param) {
192                 .ino = ino->ino,
193                 .generation = 1, /* FIXME */
194                 .attr_timeout = 1.0,
195                 .entry_timeout = 1.0
196         };
197
198         fuse_reply_create(state->freq, &e, &state->fi);
199
200         TALLOC_FREE(state);
201 }
202
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;
208
209         struct timespec create_time;
210         struct timespec access_time;
211         struct timespec write_time;
212         struct timespec change_time;
213         uint32_t mode;
214         uint64_t ino;
215         uint64_t size;
216 };
217
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);
221
222
223 static struct tevent_req *cli_get_unixattr_send(TALLOC_CTX *mem_ctx,
224                                                 struct tevent_context *ev,
225                                                 struct cli_state *cli,
226                                                 const char *path)
227 {
228         struct tevent_req *req, *subreq;
229         struct cli_get_unixattr_state *state;
230
231         req = tevent_req_create(mem_ctx, &state,
232                                 struct cli_get_unixattr_state);
233         if (req == NULL) {
234                 return NULL;
235         }
236         state->ev = ev;
237         state->cli = cli;
238
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,
245                 FILE_OPEN, 0, NULL);
246         if (tevent_req_nomem(subreq, req)) {
247                 return tevent_req_post(req, ev);
248         }
249         tevent_req_set_callback(subreq, cli_get_unixattr_opened, req);
250
251         return req;
252 }
253
254 static void cli_get_unixattr_opened(struct tevent_req *subreq)
255 {
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;
261         NTSTATUS status;
262
263         status = smb2cli_create_recv(subreq, &state->fid_persistent,
264                                      &state->fid_volatile, NULL, NULL, NULL);
265         TALLOC_FREE(subreq);
266         if (tevent_req_nterror(req, status)) {
267                 DBG_DEBUG("smb2cli_create_recv returned %s\n",
268                           nt_errstr(status));
269                 return;
270         }
271
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 */
280                 0, /* in_flags */
281                 state->fid_persistent,
282                 state->fid_volatile);
283         if (tevent_req_nomem(subreq, req)) {
284                 return;
285         }
286         tevent_req_set_callback(subreq, cli_get_unixattr_gotinfo, req);
287 }
288
289 static void cli_get_unixattr_gotinfo(struct tevent_req *subreq)
290 {
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;
296         NTSTATUS status;
297         DATA_BLOB outbuf;
298
299         status = smb2cli_query_info_recv(subreq, state, &outbuf);
300         TALLOC_FREE(subreq);
301         if (tevent_req_nterror(req, status)) {
302                 DBG_DEBUG("smb2cli_query_info_recv returned %s\n",
303                           nt_errstr(status));
304                 return;
305         }
306
307         if (outbuf.length < 0x60) {
308                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
309                 return;
310         }
311
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);
319
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)) {
325                 return;
326         }
327         tevent_req_set_callback(subreq, cli_get_unixattr_closed, req);
328 }
329
330 static void cli_get_unixattr_closed(struct tevent_req *subreq)
331 {
332         struct tevent_req *req = tevent_req_callback_data(
333                 subreq, struct tevent_req);
334         NTSTATUS status;
335
336         status = smb2cli_close_recv(subreq);
337         TALLOC_FREE(subreq);
338         if (tevent_req_nterror(req, status)) {
339                 return;
340         }
341         tevent_req_done(req);
342 }
343
344 static NTSTATUS cli_get_unixattr_recv(struct tevent_req *req,
345                                       struct stat *st)
346 {
347         struct cli_get_unixattr_state *state = tevent_req_data(
348                 req, struct cli_get_unixattr_state);
349         NTSTATUS status;
350
351         if (tevent_req_is_nterror(req, &status)) {
352                 return status;
353         }
354
355         if (IS_DOS_DIR(state->mode)) {
356                 st->st_mode = (S_IFDIR | 0555);
357                 st->st_nlink = 2;
358         } else {
359                 st->st_mode = (S_IFREG | 0444);
360                 st->st_nlink = 1;
361         }
362
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);
370
371         return NT_STATUS_OK;
372 }
373
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;
380         uint8_t level;
381         uint8_t flags;
382         uint32_t file_index;
383         uint64_t fid_persistent;
384         uint64_t fid_volatile;
385         const char *mask;
386         uint32_t outbuf_len;
387
388         uint16_t attribute;
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);
393         void *private_data;
394         bool processed_file;
395 };
396
397 static void cli_smb2_listdir_done(struct tevent_req *subreq);
398
399 static struct tevent_req *cli_smb2_listdir_send(
400         TALLOC_CTX *mem_ctx,
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,
406         uint8_t level,
407         uint8_t flags,
408         uint32_t file_index,
409         uint64_t fid_persistent,
410         uint64_t fid_volatile,
411         const char *mask,
412         uint32_t outbuf_len,
413         uint16_t attribute,
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),
418         void *private_data)
419 {
420         struct tevent_req *req, *subreq;
421         struct cli_smb2_listdir_state *state;
422
423         req = tevent_req_create(mem_ctx, &state,
424                                 struct cli_smb2_listdir_state);
425         if (req == NULL) {
426                 return NULL;
427         }
428         state->ev = ev;
429         state->conn = conn;
430         state->timeout_msec = timeout_msec;
431         state->session = session;
432         state->tcon = tcon;
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;
438         state->mask = mask;
439         state->outbuf_len = outbuf_len;
440         state->attribute = attribute;
441         state->mntpoint = mntpoint;
442         state->pathname = pathname;
443         state->fn = fn;
444         state->private_data = private_data;
445
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);
454         }
455         tevent_req_set_callback(subreq, cli_smb2_listdir_done, req);
456         return req;
457 }
458
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)
463 {
464         size_t namelen = 0;
465         size_t slen = 0;
466         size_t ret = 0;
467
468         if (dir_data_length < 4) {
469                 return NT_STATUS_INFO_LENGTH_MISMATCH;
470         }
471
472         *next_offset = IVAL(dir_data, 0);
473
474         if (*next_offset > dir_data_length) {
475                 return NT_STATUS_INFO_LENGTH_MISMATCH;
476         }
477
478         if (*next_offset != 0) {
479                 /* Ensure we only read what in this record. */
480                 dir_data_length = *next_offset;
481         }
482
483         if (dir_data_length < 105) {
484                 return NT_STATUS_INFO_LENGTH_MISMATCH;
485         }
486
487         finfo->btime_ts = interpret_long_date((const char *)dir_data + 8);
488         finfo->atime_ts = interpret_long_date((const char *)dir_data + 16);
489         finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24);
490         finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32);
491         finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
492         finfo->mode = CVAL(dir_data + 56, 0);
493         namelen = IVAL(dir_data + 60,0);
494         if (namelen > (dir_data_length - 104)) {
495                 return NT_STATUS_INFO_LENGTH_MISMATCH;
496         }
497         slen = CVAL(dir_data + 68, 0);
498         if (slen > 24) {
499                 return NT_STATUS_INFO_LENGTH_MISMATCH;
500         }
501         ret = pull_string_talloc(finfo,
502                                 dir_data,
503                                 FLAGS2_UNICODE_STRINGS,
504                                 &finfo->short_name,
505                                 dir_data + 70,
506                                 slen,
507                                 STR_UNICODE);
508         if (ret == (size_t)-1) {
509                 /* Bad conversion. */
510                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
511         }
512
513         ret = pull_string_talloc(finfo,
514                                 dir_data,
515                                 FLAGS2_UNICODE_STRINGS,
516                                 &finfo->name,
517                                 dir_data + 104,
518                                 namelen,
519                                 STR_UNICODE);
520         if (ret == (size_t)-1) {
521                 /* Bad conversion. */
522                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
523         }
524         return NT_STATUS_OK;
525 }
526
527 static void cli_smb2_listdir_done(struct tevent_req *subreq)
528 {
529         struct tevent_req *req = tevent_req_callback_data(
530                 subreq, struct tevent_req);
531         struct cli_smb2_listdir_state *state = tevent_req_data(
532                 req, struct cli_smb2_listdir_state);
533         uint8_t *data;
534         uint32_t data_len;
535         uint32_t next_offset = 0;
536         NTSTATUS status;
537
538         status = smb2cli_query_directory_recv(subreq, state, &data,
539                                               &data_len);
540         TALLOC_FREE(subreq);
541         if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
542                 tevent_req_done(req);
543                 return;
544         }
545         if (tevent_req_nterror(req, status)) {
546                 return;
547         }
548
549         do {
550                 struct file_info *finfo;
551                 bool ok;
552
553                 finfo = talloc_zero(state, struct file_info);
554                 if (tevent_req_nomem(finfo, req)) {
555                         return;
556                 }
557
558                 status = parse_finfo_id_both_directory_info(
559                         data, data_len, finfo, &next_offset);
560
561                 DEBUG(10, ("%s: parse_finfo_id_both_directory_info returned "
562                            "%s\n", __func__, nt_errstr(status)));
563
564                 if (tevent_req_nterror(req, status)) {
565                         return;
566                 }
567
568                 ok = dir_check_ftype(finfo->mode, state->attribute);
569
570                 DEBUG(10, ("%s: dir_check_ftype(%u,%u) returned %u\n",
571                            __func__, (unsigned)finfo->mode,
572                            (unsigned)state->attribute, (unsigned)ok));
573
574                 if (ok) {
575                         /*
576                          * Only process if attributes match. On SMB1 server
577                          * does this, so on SMB2 we need to emulate in the
578                          * client.
579                          *
580                          * https://bugzilla.samba.org/show_bug.cgi?id=10260
581                          */
582                         state->processed_file = true;
583
584                         status = state->fn(state->mntpoint, finfo,
585                                            state->pathname,
586                                            state->private_data);
587                         if (tevent_req_nterror(req, status)) {
588                                 return;
589                         }
590                 }
591
592                 TALLOC_FREE(finfo);
593
594                 if (next_offset != 0) {
595                         data += next_offset;
596                         data_len -= next_offset;
597                 }
598         } while (next_offset != 0);
599
600         subreq = smb2cli_query_directory_send(
601                 state, state->ev, state->conn, state->timeout_msec,
602                 state->session, state->tcon, state->level,
603                 state->flags, state->file_index,
604                 state->fid_persistent, state->fid_volatile,
605                 state->mask, state->outbuf_len);
606         if (tevent_req_nomem(subreq, req)) {
607                 return;
608         }
609         tevent_req_set_callback(subreq, cli_smb2_listdir_done, req);
610 }
611
612 static NTSTATUS cli_smb2_listdir_recv(struct tevent_req *req)
613 {
614         struct cli_smb2_listdir_state *state = tevent_req_data(
615                 req, struct cli_smb2_listdir_state);
616         NTSTATUS status;
617
618         if (tevent_req_is_nterror(req, &status)) {
619                 return status;
620         }
621
622         if (!state->processed_file) {
623                 /*
624                  * In SMB1 findfirst returns NT_STATUS_NO_SUCH_FILE
625                  * if no files match. Emulate this in the client.
626                  */
627                 return NT_STATUS_NO_SUCH_FILE;
628         }
629
630         return NT_STATUS_OK;
631 }
632
633 struct ll_lookup_state {
634         struct mount_state *mstate;
635         fuse_req_t freq;
636         char *path;
637 };
638
639 static void cli_ll_lookup_done(struct tevent_req *req);
640
641 static void cli_ll_lookup(fuse_req_t freq, fuse_ino_t parent_ino,
642                           const char *name)
643 {
644         struct mount_state *mstate = talloc_get_type_abort(
645                 fuse_req_userdata(freq), struct mount_state);
646         struct ll_lookup_state *state;
647         struct tevent_req *req;
648         struct inode_state *parent;
649
650         DBG_DEBUG("parent_ino=%ju, name=%s\n", (uintmax_t)parent_ino, name);
651
652         parent = idr_find(mstate->ino_ctx, parent_ino);
653         if (parent == NULL) {
654                 DBG_WARNING("could not find parent\n");
655                 fuse_reply_err(freq, ENOENT);
656                 return;
657         }
658
659         state = talloc(mstate, struct ll_lookup_state);
660         if (state == NULL) {
661                 DBG_WARNING("talloc failed\n");
662                 fuse_reply_err(freq, ENOMEM);
663                 return;
664         }
665         state->mstate = mstate;
666         state->freq = freq;
667
668         state->path = talloc_asprintf(state, "%s%s%s", parent->path,
669                                       strlen(parent->path) ? "\\": "",
670                                       name);
671         if (state->path == NULL) {
672                 TALLOC_FREE(state);
673                 fuse_reply_err(freq, ENOMEM);
674                 return;
675         }
676
677         req = cli_get_unixattr_send(state, mstate->ev, mstate->cli,
678                                     state->path);
679         if (req == NULL) {
680                 TALLOC_FREE(state);
681                 fuse_reply_err(freq, ENOMEM);
682                 return;
683         }
684         tevent_req_set_callback(req, cli_ll_lookup_done, state);
685 }
686
687 static void cli_ll_lookup_done(struct tevent_req *req)
688 {
689         struct ll_lookup_state *state = tevent_req_callback_data(
690                 req, struct ll_lookup_state);
691         struct stat sbuf = {0};
692         struct fuse_entry_param e;
693         struct inode_state *ino;
694         NTSTATUS status;
695
696         status = cli_get_unixattr_recv(req, &sbuf);
697         TALLOC_FREE(req);
698         if (!NT_STATUS_IS_OK(status)) {
699                 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
700                 return;
701         }
702
703         ino = inode_state_new(state->mstate, state->path);
704         if (ino == NULL) {
705                 fuse_reply_err(state->freq, ENOMEM);
706                 return;
707         }
708
709         e = (struct fuse_entry_param) {
710                 .ino = ino->ino,
711                 .attr = sbuf,
712                 .generation = 1, /* FIXME */
713                 .attr_timeout = 1.0,
714                 .entry_timeout = 1.0
715         };
716
717         fuse_reply_entry(state->freq, &e);
718         TALLOC_FREE(state);
719 }
720
721 struct ll_getattr_state {
722         struct mount_state *mstate;
723         fuse_req_t freq;
724         struct fuse_file_info fi;
725 };
726
727 static void cli_ll_getattr_done(struct tevent_req *req);
728
729 static void cli_ll_getattr(fuse_req_t freq, fuse_ino_t ino,
730                            struct fuse_file_info *fi)
731 {
732         struct mount_state *mstate = talloc_get_type_abort(
733                 fuse_req_userdata(freq), struct mount_state);
734         struct ll_getattr_state *state;
735         struct inode_state *istate;
736         struct tevent_req *req;
737
738         DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
739
740         istate = idr_find(mstate->ino_ctx, ino);
741         if (istate == NULL) {
742                 fuse_reply_err(freq, ENOENT);
743                 return;
744         }
745
746         state = talloc(mstate, struct ll_getattr_state);
747         if (state == NULL) {
748                 fuse_reply_err(freq, ENOMEM);
749                 return;
750         }
751         state->mstate = mstate;
752         state->freq = freq;
753
754         req = cli_get_unixattr_send(state, mstate->ev, mstate->cli,
755                                     istate->path);
756         if (req == NULL) {
757                 TALLOC_FREE(state);
758                 fuse_reply_err(freq, ENOMEM);
759                 return;
760         }
761         tevent_req_set_callback(req, cli_ll_getattr_done, state);
762 }
763
764 static void cli_ll_getattr_done(struct tevent_req *req)
765 {
766         struct ll_getattr_state *state = tevent_req_callback_data(
767                 req, struct ll_getattr_state);
768         struct stat st;
769         NTSTATUS status;
770         int ret;
771
772         status = cli_get_unixattr_recv(req, &st);
773         TALLOC_FREE(req);
774         if (!NT_STATUS_IS_OK(status)) {
775                 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
776                 return;
777         }
778
779         ret = fuse_reply_attr(state->freq, &st, 1);
780         if (ret != 0) {
781                 DBG_NOTICE("fuse_reply_attr failed: %s\n",
782                            strerror(-errno));
783         }
784 }
785
786
787 struct ll_open_state {
788         struct mount_state *mstate;
789         fuse_req_t freq;
790         struct fuse_file_info fi;
791 };
792
793 static void cli_ll_open_done(struct tevent_req *req);
794
795 static void cli_ll_open(fuse_req_t freq, fuse_ino_t ino,
796                         struct fuse_file_info *fi)
797 {
798         struct mount_state *mstate = talloc_get_type_abort(
799                 fuse_req_userdata(freq), struct mount_state);
800         struct ll_open_state *state;
801         struct inode_state *istate;
802         struct tevent_req *req;
803         uint32_t acc;
804
805         DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
806
807         istate = idr_find(mstate->ino_ctx, ino);
808         if (istate == NULL) {
809                 fuse_reply_err(freq, ENOENT);
810                 return;
811         }
812
813         state = talloc(mstate, struct ll_open_state);
814         if (state == NULL) {
815                 fuse_reply_err(freq, ENOMEM);
816                 return;
817         }
818         state->mstate = mstate;
819         state->freq = freq;
820         state->fi = *fi;
821
822         switch (fi->flags & O_ACCMODE) {
823         case O_RDONLY:
824                 acc = FILE_GENERIC_READ;
825                 break;
826         case O_WRONLY:
827                 acc = FILE_GENERIC_WRITE;
828                 break;
829         case O_RDWR:
830                 acc = FILE_GENERIC_READ|FILE_GENERIC_WRITE;
831                 break;
832         default:
833                 fuse_reply_err(freq, EACCES);
834                 return;
835         }
836
837         req = cli_smb2_create_fnum_send(
838                 state, mstate->ev, mstate->cli, istate->path,
839                 0, acc, FILE_ATTRIBUTE_NORMAL,
840                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
841                 FILE_OPEN, FILE_NON_DIRECTORY_FILE);
842         if (req == NULL) {
843                 TALLOC_FREE(state);
844                 fuse_reply_err(freq, ENOMEM);
845                 return;
846         }
847         tevent_req_set_callback(req, cli_ll_open_done, state);
848 }
849
850 static void cli_ll_open_done(struct tevent_req *req)
851 {
852         struct ll_open_state *state = tevent_req_callback_data(
853                 req, struct ll_open_state);
854         uint16_t fnum;
855         NTSTATUS status;
856
857         status = cli_smb2_create_fnum_recv(req, &fnum, NULL);
858         TALLOC_FREE(req);
859         if (!NT_STATUS_IS_OK(status)) {
860                 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
861                 return;
862         }
863
864         state->fi.fh = fnum;
865         state->fi.direct_io = 0;
866         state->fi.keep_cache = 0;
867
868         fuse_reply_open(state->freq, &state->fi);
869
870         TALLOC_FREE(state);
871 }
872
873 struct ll_release_state {
874         struct mount_state *mstate;
875         fuse_req_t freq;
876         fuse_ino_t ino;
877 };
878
879 static void cli_ll_release_done(struct tevent_req *req);
880
881 static void cli_ll_release(fuse_req_t freq, fuse_ino_t ino,
882                            struct fuse_file_info *fi)
883 {
884         struct mount_state *mstate = talloc_get_type_abort(
885                 fuse_req_userdata(freq), struct mount_state);
886         struct ll_release_state *state;
887         struct inode_state *istate;
888         struct tevent_req *req;
889         uint16_t fnum;
890
891         DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
892
893         istate = idr_find(mstate->ino_ctx, ino);
894         if (istate == NULL) {
895                 fuse_reply_err(freq, ENOENT);
896                 return;
897         }
898
899         state = talloc(mstate, struct ll_release_state);
900         if (state == NULL) {
901                 fuse_reply_err(freq, ENOMEM);
902                 return;
903         }
904         state->mstate = mstate;
905         state->freq = freq;
906         state->ino = ino;
907
908         fnum = fi->fh;
909
910         req = cli_smb2_close_fnum_send(state, mstate->ev, mstate->cli, fnum);
911         if (req == NULL) {
912                 TALLOC_FREE(state);
913                 fuse_reply_err(freq, ENOMEM);
914                 return;
915         }
916         tevent_req_set_callback(req, cli_ll_release_done, state);
917 }
918
919 static void cli_ll_release_done(struct tevent_req *req)
920 {
921         struct ll_release_state *state = tevent_req_callback_data(
922                 req, struct ll_release_state);
923         struct inode_state *istate;
924         NTSTATUS status;
925
926         status = cli_smb2_close_fnum_recv(req);
927         TALLOC_FREE(req);
928         if (!NT_STATUS_IS_OK(status)) {
929                 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
930                 return;
931         }
932
933         istate = idr_find(state->mstate->ino_ctx, state->ino);
934         if (istate == NULL) {
935                 DEBUG(1, ("%s: inode %ju vanished!\n", __func__,
936                           (uintmax_t)state->ino));
937         }
938         TALLOC_FREE(istate);
939
940         fuse_reply_err(state->freq, 0);
941         TALLOC_FREE(state);
942 }
943
944 struct ll_read_state {
945         struct mount_state *mstate;
946         fuse_req_t freq;
947 };
948
949 static void cli_ll_read_done(struct tevent_req *req);
950
951 static void cli_ll_read(fuse_req_t freq, fuse_ino_t ino,
952                         size_t size, off_t off,
953                         struct fuse_file_info *fi)
954 {
955         struct mount_state *mstate = talloc_get_type_abort(
956                 fuse_req_userdata(freq), struct mount_state);
957         struct ll_read_state *state;
958         struct tevent_req *req;
959         uint16_t fnum;
960
961         DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino,
962                   size, (uintmax_t)off);
963
964         state = talloc(mstate, struct ll_read_state);
965         if (state == NULL) {
966                 fuse_reply_err(freq, ENOMEM);
967                 return;
968         }
969         state->mstate = mstate;
970         state->freq = freq;
971
972         fnum = fi->fh;
973
974         req = cli_smb2_read_send(state, mstate->ev, mstate->cli,
975                                  fnum, off, size);
976         if (req == NULL) {
977                 TALLOC_FREE(state);
978                 fuse_reply_err(freq, ENOMEM);
979                 return;
980         }
981         tevent_req_set_callback(req, cli_ll_read_done, state);
982 }
983
984 static void cli_ll_read_done(struct tevent_req *req)
985 {
986         struct ll_read_state *state = tevent_req_callback_data(
987                 req, struct ll_read_state);
988         ssize_t received;
989         uint8_t *rcvbuf;
990         NTSTATUS status;
991
992         status = cli_smb2_read_recv(req, &received, &rcvbuf);
993         /* no talloc_free here yet */
994
995         if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
996                 received = 0;
997                 rcvbuf = NULL;
998                 status = NT_STATUS_OK;
999         }
1000
1001         if (!NT_STATUS_IS_OK(status)) {
1002                 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
1003                 return;
1004         }
1005         fuse_reply_buf(state->freq, (char *)rcvbuf, received);
1006         TALLOC_FREE(state);
1007 }
1008
1009 struct ll_write_state {
1010         struct mount_state *mstate;
1011         fuse_req_t freq;
1012 };
1013
1014 static void cli_ll_write_done(struct tevent_req *req);
1015
1016 static void cli_ll_write(fuse_req_t freq, fuse_ino_t ino, const char *buf,
1017                          size_t size, off_t off, struct fuse_file_info *fi)
1018 {
1019         struct mount_state *mstate = talloc_get_type_abort(
1020                 fuse_req_userdata(freq), struct mount_state);
1021         struct ll_write_state *state;
1022         struct tevent_req *req;
1023         uint16_t fnum;
1024
1025         DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino,
1026                   size, (uintmax_t)off);
1027
1028         state = talloc(mstate, struct ll_write_state);
1029         if (state == NULL) {
1030                 fuse_reply_err(freq, ENOMEM);
1031                 return;
1032         }
1033         state->mstate = mstate;
1034         state->freq = freq;
1035
1036         fnum = fi->fh;
1037
1038         req = cli_smb2_write_send(state, mstate->ev, mstate->cli, fnum, 0,
1039                                   (const uint8_t *)buf, off, size);
1040         if (req == NULL) {
1041                 TALLOC_FREE(state);
1042                 fuse_reply_err(freq, ENOMEM);
1043                 return;
1044         }
1045         tevent_req_set_callback(req, cli_ll_write_done, state);
1046 }
1047
1048 static void cli_ll_write_done(struct tevent_req *req)
1049 {
1050         struct ll_write_state *state = tevent_req_callback_data(
1051                 req, struct ll_write_state);
1052         size_t written;
1053         NTSTATUS status;
1054
1055         status = cli_smb2_write_recv(req, &written);
1056         /* no talloc_free here yet */
1057         if (!NT_STATUS_IS_OK(status)) {
1058                 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
1059                 return;
1060         }
1061         fuse_reply_write(state->freq, written);
1062         TALLOC_FREE(state);
1063 }
1064
1065
1066 struct ll_dir_state {
1067         uint64_t fid_persistent;
1068         uint64_t fid_volatile;
1069
1070         struct file_info *finfos;
1071         unsigned num_finfos, num_sent;
1072 };
1073
1074 static bool ll_dir_state_add(struct ll_dir_state *dir_state,
1075                              const char *name)
1076 {
1077         struct file_info *tmp, *finfo;
1078
1079         tmp = talloc_realloc(dir_state, dir_state->finfos,
1080                              struct file_info, dir_state->num_finfos+1);
1081         if (tmp == NULL) {
1082                 return false;
1083         }
1084         dir_state->finfos = tmp;
1085         finfo = &dir_state->finfos[dir_state->num_finfos];
1086
1087         ZERO_STRUCTP(finfo);
1088
1089         finfo->name = talloc_strdup(dir_state->finfos, name);
1090         if (finfo->name == NULL) {
1091                 return false;
1092         }
1093         dir_state->num_finfos += 1;
1094
1095         return true;
1096 }
1097
1098 struct ll_opendir_state {
1099         struct mount_state *mstate;
1100         fuse_req_t freq;
1101         struct fuse_file_info fi;
1102         struct ll_dir_state *dir_state;
1103 };
1104
1105 static void cli_ll_opendir_done(struct tevent_req *req);
1106
1107 static void cli_ll_opendir(fuse_req_t freq, fuse_ino_t ino,
1108                            struct fuse_file_info *fi)
1109 {
1110         struct mount_state *mstate = talloc_get_type_abort(
1111                 fuse_req_userdata(freq), struct mount_state);
1112         struct cli_state *cli = mstate->cli;
1113         struct ll_opendir_state *state;
1114         struct inode_state *istate;
1115         struct tevent_req *req;
1116
1117         DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
1118
1119         istate = idr_find(mstate->ino_ctx, ino);
1120         if (istate == NULL) {
1121                 fuse_reply_err(freq, ENOENT);
1122                 return;
1123         }
1124
1125         state = talloc(mstate, struct ll_opendir_state);
1126         if (state == NULL) {
1127                 fuse_reply_err(freq, ENOMEM);
1128                 return;
1129         }
1130         state->mstate = mstate;
1131         state->freq = freq;
1132         state->fi = *fi;
1133
1134         state->dir_state = talloc_zero(state, struct ll_dir_state);
1135         if (state->dir_state == NULL) {
1136                 TALLOC_FREE(state);
1137                 fuse_reply_err(freq, ENOMEM);
1138                 return;
1139         }
1140
1141         req = smb2cli_create_send(
1142                 state, mstate->ev, cli->conn, cli->timeout,
1143                 cli->smb2.session, cli->smb2.tcon, istate->path,
1144                 0, SMB2_IMPERSONATION_IMPERSONATION,
1145                 FILE_GENERIC_READ, FILE_ATTRIBUTE_DIRECTORY,
1146                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
1147                 FILE_OPEN, FILE_DIRECTORY_FILE, NULL);
1148         if (req == NULL) {
1149                 TALLOC_FREE(state);
1150                 fuse_reply_err(freq, ENOMEM);
1151                 return;
1152         }
1153         tevent_req_set_callback(req, cli_ll_opendir_done, state);
1154 }
1155
1156 static void cli_ll_opendir_done(struct tevent_req *req)
1157 {
1158         struct ll_opendir_state *state = tevent_req_callback_data(
1159                 req, struct ll_opendir_state);
1160         NTSTATUS status;
1161
1162         status = smb2cli_create_recv(req,
1163                                      &state->dir_state->fid_persistent,
1164                                      &state->dir_state->fid_volatile,
1165                                      NULL, NULL, NULL);
1166         TALLOC_FREE(req);
1167
1168         DEBUG(10, ("%s: smbcli_create_recv returned %s\n", __func__,
1169                    nt_errstr(status)));
1170
1171         if (!NT_STATUS_IS_OK(status)) {
1172                 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
1173                 return;
1174         }
1175
1176         state->fi.fh = (uint64_t)talloc_move(state->mstate, &state->dir_state);
1177         state->fi.direct_io = 0;
1178         state->fi.keep_cache = 0;
1179
1180         fuse_reply_open(state->freq, &state->fi);
1181
1182         TALLOC_FREE(state);
1183 }
1184
1185 struct ll_readdir_state {
1186         fuse_req_t freq;
1187         struct ll_dir_state *dir_state;
1188 };
1189
1190 static void cli_ll_readdir_done(struct tevent_req *subreq);
1191 static NTSTATUS cli_ll_readdir_one(const char *mnt, struct file_info *finfo,
1192                                    const char *path, void *private_data);
1193 static void cli_ll_readdir_reply_one(fuse_req_t freq,
1194                                      struct ll_dir_state *dir_state);
1195
1196 static void cli_ll_readdir(fuse_req_t freq, fuse_ino_t ino, size_t size,
1197                            off_t off, struct fuse_file_info *fi)
1198 {
1199         struct mount_state *mstate = talloc_get_type_abort(
1200                 fuse_req_userdata(freq), struct mount_state);
1201         struct cli_state *cli = mstate->cli;
1202         struct ll_dir_state *dir_state;
1203         struct ll_readdir_state *state;
1204         struct tevent_req *req;
1205
1206         DBG_DEBUG("ino=%ju, size=%zu, off=%ju\n", (uintmax_t)ino, size,
1207                   (uintmax_t)off);
1208
1209         dir_state = talloc_get_type_abort((void *)fi->fh, struct ll_dir_state);
1210
1211         if (dir_state->finfos != NULL) {
1212                 DBG_DEBUG("finfos=%p\n", dir_state->finfos);
1213                 cli_ll_readdir_reply_one(freq, dir_state);
1214                 return;
1215         }
1216
1217         if (!ll_dir_state_add(dir_state, ".") ||
1218             !ll_dir_state_add(dir_state, "..")) {
1219                 fuse_reply_err(freq, ENOMEM);
1220                 return;
1221         }
1222
1223         state = talloc(mstate, struct ll_readdir_state);
1224         if (state == NULL) {
1225                 fuse_reply_err(freq, ENOMEM);
1226                 return;
1227         }
1228         state->freq = freq;
1229         state->dir_state = dir_state;
1230
1231         req = cli_smb2_listdir_send(
1232                 state, mstate->ev, cli->conn, cli->timeout,
1233                 cli->smb2.session, cli->smb2.tcon,
1234                 SMB2_FIND_ID_BOTH_DIRECTORY_INFO, 0, 0,
1235                 dir_state->fid_persistent, dir_state->fid_volatile,
1236                 "*", 0xffff,
1237                 FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM |
1238                 FILE_ATTRIBUTE_HIDDEN,
1239                 NULL, NULL, cli_ll_readdir_one, dir_state);
1240         if (req == NULL) {
1241                 TALLOC_FREE(state);
1242                 fuse_reply_err(freq, ENOMEM);
1243                 return;
1244         }
1245         tevent_req_set_callback(req, cli_ll_readdir_done, state);
1246 }
1247
1248 static void cli_ll_readdir_reply_one(fuse_req_t freq,
1249                                      struct ll_dir_state *dir_state)
1250 {
1251         struct file_info *finfo;
1252         char buf[1024];
1253         struct stat sbuf = {};
1254         size_t buflen;
1255
1256         if (dir_state->num_finfos == dir_state->num_sent) {
1257                 DEBUG(10, ("%s: Done\n", __func__));
1258                 fuse_reply_buf(freq, NULL, 0);
1259                 return;
1260         }
1261
1262         sbuf.st_mode = S_IFREG | 0755;
1263         sbuf.st_ino = random(); /* FIXME :-) */
1264         finfo = &dir_state->finfos[dir_state->num_sent];
1265
1266         DBG_DEBUG("Adding %s\n", finfo->name);
1267
1268         buflen = fuse_add_direntry(freq, buf, sizeof(buf),
1269                                    finfo->name, &sbuf, 0);
1270         fuse_reply_buf(freq, buf, buflen);
1271         dir_state->num_sent += 1;
1272         return;
1273 }
1274
1275 static NTSTATUS cli_ll_readdir_one(const char *mnt, struct file_info *finfo,
1276                                    const char *path, void *private_data)
1277 {
1278         struct ll_dir_state *dir_state = talloc_get_type_abort(
1279                 private_data, struct ll_dir_state);
1280
1281         if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
1282                 DEBUG(10, ("%s: Ignoring %s\n", __func__, finfo->name));
1283                 return NT_STATUS_OK;
1284         }
1285
1286         DBG_DEBUG("Got entry %s\n", finfo->name);
1287
1288         if (!ll_dir_state_add(dir_state, finfo->name)) {
1289                 return NT_STATUS_NO_MEMORY;
1290         }
1291         return NT_STATUS_OK;
1292 }
1293
1294 static void cli_ll_readdir_done(struct tevent_req *req)
1295 {
1296         struct ll_readdir_state *state = tevent_req_callback_data(
1297                 req, struct ll_readdir_state);
1298         NTSTATUS status;
1299
1300         status = cli_smb2_listdir_recv(req);
1301         TALLOC_FREE(req);
1302         if (!NT_STATUS_IS_OK(status)) {
1303                 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
1304                 return;
1305         }
1306         cli_ll_readdir_reply_one(state->freq, state->dir_state);
1307         TALLOC_FREE(state);
1308 }
1309
1310
1311 struct ll_releasedir_state {
1312         struct mount_state *mstate;
1313         fuse_req_t freq;
1314         struct ll_dir_state *dir_state;
1315 };
1316
1317 static void cli_ll_releasedir_done(struct tevent_req *req);
1318
1319 static void cli_ll_releasedir(fuse_req_t freq, fuse_ino_t ino,
1320                               struct fuse_file_info *fi)
1321 {
1322         struct mount_state *mstate = talloc_get_type_abort(
1323                 fuse_req_userdata(freq), struct mount_state);
1324         struct cli_state *cli = mstate->cli;
1325         struct ll_releasedir_state *state;
1326         struct tevent_req *req;
1327
1328         DBG_DEBUG("ino=%ju\n", (uintmax_t)ino);
1329
1330         state = talloc(mstate, struct ll_releasedir_state);
1331         if (state == NULL) {
1332                 fuse_reply_err(freq, ENOMEM);
1333                 return;
1334         }
1335         state->mstate = mstate;
1336         state->freq = freq;
1337
1338         state->dir_state = talloc_get_type_abort(
1339                 (void *)fi->fh, struct ll_dir_state);
1340
1341         req = smb2cli_close_send(state, mstate->ev, cli->conn, cli->timeout,
1342                                  cli->smb2.session, cli->smb2.tcon, 0,
1343                                  state->dir_state->fid_persistent,
1344                                  state->dir_state->fid_volatile);
1345         if (req == NULL) {
1346                 TALLOC_FREE(state);
1347                 fuse_reply_err(freq, ENOMEM);
1348                 return;
1349         }
1350         tevent_req_set_callback(req, cli_ll_releasedir_done, state);
1351 }
1352
1353 static void cli_ll_releasedir_done(struct tevent_req *req)
1354 {
1355         struct ll_releasedir_state *state = tevent_req_callback_data(
1356                 req, struct ll_releasedir_state);
1357         NTSTATUS status;
1358
1359         status = smb2cli_close_recv(req);
1360         TALLOC_FREE(req);
1361         if (!NT_STATUS_IS_OK(status)) {
1362                 fuse_reply_err(state->freq, map_errno_from_nt_status(status));
1363                 return;
1364         }
1365         TALLOC_FREE(state->dir_state);
1366         fuse_reply_err(state->freq, 0);
1367         TALLOC_FREE(state);
1368 }
1369
1370 static struct fuse_lowlevel_ops cli_ll_ops = {
1371         .lookup = cli_ll_lookup,
1372         .getattr = cli_ll_getattr,
1373         .open = cli_ll_open,
1374         .create = cli_ll_create,
1375         .release = cli_ll_release,
1376         .read = cli_ll_read,
1377         .write = cli_ll_write,
1378         .opendir = cli_ll_opendir,
1379         .readdir = cli_ll_readdir,
1380         .releasedir = cli_ll_releasedir,
1381 };
1382
1383 static void fuse_chan_fd_handler(struct tevent_context *ev,
1384                                  struct tevent_fd *fde,
1385                                  uint16_t flags,
1386                                  void *private_data);
1387 static void fuse_chan_signal_handler(struct tevent_context *ev,
1388                                      struct tevent_signal *se,
1389                                      int signum,
1390                                      int count,
1391                                      void *siginfo,
1392                                      void *private_data);
1393
1394 static int mount_state_destructor(struct mount_state *s);
1395
1396 int do_mount(struct cli_state *cli, const char *mountpoint)
1397 {
1398         struct mount_state *state;
1399         struct inode_state *ino;
1400         struct fuse_args args = { 0 };
1401         int fd;
1402         int ret = 1;
1403
1404         state = talloc_zero(talloc_tos(), struct mount_state);
1405         if (state == NULL) {
1406                 fprintf(stderr, "talloc failed\n");
1407                 return 1;
1408         }
1409
1410         state->ev = tevent_context_init(state);
1411         if (state->ev == NULL) {
1412                 fprintf(stderr, "tevent_context_init failed\n");
1413                 TALLOC_FREE(state);
1414                 return 1;
1415         }
1416
1417         state->ino_ctx = idr_init(state);
1418         if (state->ino_ctx == NULL) {
1419                 fprintf(stderr, "idr_init failed\n");
1420                 TALLOC_FREE(state);
1421                 return 1;
1422         }
1423
1424         state->ino_parent = talloc_new(state);
1425         if (state->ino_parent == NULL) {
1426                 fprintf(stderr, "talloc_new failed\n");
1427                 TALLOC_FREE(state);
1428                 return 1;
1429         }
1430
1431         talloc_set_destructor(state, mount_state_destructor);
1432
1433         ino = inode_state_new(state, "");
1434         if (ino == NULL) {
1435                 fprintf(stderr, "inode_state_new failed\n");
1436                 TALLOC_FREE(state);
1437                 return 1;
1438         }
1439         if (ino->ino != FUSE_ROOT_ID) {
1440                 fprintf(stderr, "first inode gave %d, expected %d\n",
1441                         (int)ino->ino, FUSE_ROOT_ID);
1442                 TALLOC_FREE(state);
1443                 return 1;
1444         }
1445
1446         state->cli = cli;
1447
1448         state->ch = fuse_mount(mountpoint, &args);
1449         if (state->ch == NULL) {
1450                 perror("fuse_mount failed");
1451                 goto fail_free;
1452         }
1453
1454         state->bufsize = fuse_chan_bufsize(state->ch);
1455         state->buf = talloc_array(state, char, state->bufsize);
1456         if (state->buf == NULL) {
1457                 fprintf(stderr, "talloc failed\n");
1458                 goto fail_unmount;
1459         }
1460
1461         fd = fuse_chan_fd(state->ch);
1462
1463         state->fde = tevent_add_fd(state->ev, state, fd, TEVENT_FD_READ,
1464                                    fuse_chan_fd_handler, state);
1465         if (state->fde == NULL) {
1466                 fprintf(stderr, "tevent_add_fd failed\n");
1467                 goto fail_unmount;
1468         }
1469
1470         state->signal_ev = tevent_add_signal(state->ev, state, SIGINT, 0,
1471                                              fuse_chan_signal_handler, state);
1472         if (state->signal_ev == NULL) {
1473                 fprintf(stderr, "tevent_add_signal failed\n");
1474                 goto fail_unmount;
1475         }
1476
1477         state->se = fuse_lowlevel_new(&args, &cli_ll_ops, sizeof(cli_ll_ops),
1478                                       state);
1479         if (state->se == NULL) {
1480                 perror("fuse_lowlevel_new failed");
1481                 goto fail_unmount;
1482         }
1483
1484         fuse_session_add_chan(state->se, state->ch);
1485
1486         while (!state->done) {
1487                 ret = tevent_loop_once(state->ev);
1488                 if (ret == -1) {
1489                         perror("tevent_loop_once failed");
1490                         break;
1491                 }
1492         }
1493
1494         fuse_session_remove_chan(state->ch);
1495         fuse_session_destroy(state->se);
1496 fail_unmount:
1497         fuse_unmount(mountpoint, state->ch);
1498 fail_free:
1499         TALLOC_FREE(state);
1500         return ret;
1501 }
1502
1503 static int mount_state_destructor(struct mount_state *s)
1504 {
1505         TALLOC_FREE(s->ino_parent);
1506         return 0;
1507 }
1508
1509
1510 static void fuse_chan_fd_handler(struct tevent_context *ev,
1511                                  struct tevent_fd *fde,
1512                                  uint16_t flags,
1513                                  void *private_data)
1514 {
1515         struct mount_state *state = talloc_get_type_abort(
1516                 private_data, struct mount_state);
1517         int recvd;
1518
1519         if ((flags & TEVENT_FD_READ) == 0) {
1520                 return;
1521         }
1522
1523         recvd = fuse_chan_recv(&state->ch, state->buf, state->bufsize);
1524         if (recvd <= 0) {
1525                 state->done = true;
1526                 return;
1527         }
1528         fuse_session_process(state->se, state->buf, recvd, state->ch);
1529 }
1530
1531 static void fuse_chan_signal_handler(struct tevent_context *ev,
1532                                      struct tevent_signal *se,
1533                                      int signum,
1534                                      int count,
1535                                      void *siginfo,
1536                                      void *private_data)
1537 {
1538         struct mount_state *state = talloc_get_type_abort(
1539                 private_data, struct mount_state);
1540         state->done = true;
1541 }