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