blob: 95882ed7661d6636711f12e9bdcbcf5b6ac43b32 [file] [log] [blame]
Miklos Szeredi12744942005-07-11 12:32:31 +00001/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPL.
6 See the file COPYING.LIB
7*/
8
Miklos Szeredibd10a7b2005-07-15 09:59:59 +00009#include <config.h>
Miklos Szeredia1482422005-08-14 23:00:27 +000010#include "fuse_lowlevel.h"
Miklos Szeredi12744942005-07-11 12:32:31 +000011#include "fuse_kernel.h"
Miklos Szeredi12744942005-07-11 12:32:31 +000012
13#include <stdio.h>
14#include <string.h>
15#include <stdlib.h>
16#include <unistd.h>
17#include <limits.h>
18#include <errno.h>
Miklos Szeredi12744942005-07-11 12:32:31 +000019#include <stdint.h>
Miklos Szeredi12744942005-07-11 12:32:31 +000020
Miklos Szeredi2482fdb2005-07-12 13:23:53 +000021#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
Miklos Szeredi12744942005-07-11 12:32:31 +000022
Miklos Szeredia1482422005-08-14 23:00:27 +000023struct fuse_ll {
24 unsigned int debug : 1;
25 unsigned int allow_root : 1;
26 struct fuse_lowlevel_ops op;
27 int got_init;
28 void *userdata;
29 int major;
30 int minor;
31 uid_t owner;
Miklos Szeredi12744942005-07-11 12:32:31 +000032};
33
Miklos Szeredi76c17522005-07-13 14:08:19 +000034struct fuse_req {
35 struct fuse_ll *f;
36 uint64_t unique;
Miklos Szeredibd10a7b2005-07-15 09:59:59 +000037 struct fuse_ctx ctx;
Miklos Szeredia1482422005-08-14 23:00:27 +000038 struct fuse_chan *ch;
Miklos Szeredi76c17522005-07-13 14:08:19 +000039};
40
Miklos Szeredi12744942005-07-11 12:32:31 +000041static const char *opname(enum fuse_opcode opcode)
42{
43 switch (opcode) {
44 case FUSE_LOOKUP: return "LOOKUP";
45 case FUSE_FORGET: return "FORGET";
46 case FUSE_GETATTR: return "GETATTR";
47 case FUSE_SETATTR: return "SETATTR";
48 case FUSE_READLINK: return "READLINK";
49 case FUSE_SYMLINK: return "SYMLINK";
50 case FUSE_MKNOD: return "MKNOD";
51 case FUSE_MKDIR: return "MKDIR";
52 case FUSE_UNLINK: return "UNLINK";
53 case FUSE_RMDIR: return "RMDIR";
54 case FUSE_RENAME: return "RENAME";
55 case FUSE_LINK: return "LINK";
56 case FUSE_OPEN: return "OPEN";
57 case FUSE_READ: return "READ";
58 case FUSE_WRITE: return "WRITE";
59 case FUSE_STATFS: return "STATFS";
60 case FUSE_FLUSH: return "FLUSH";
61 case FUSE_RELEASE: return "RELEASE";
62 case FUSE_FSYNC: return "FSYNC";
63 case FUSE_SETXATTR: return "SETXATTR";
64 case FUSE_GETXATTR: return "GETXATTR";
65 case FUSE_LISTXATTR: return "LISTXATTR";
66 case FUSE_REMOVEXATTR: return "REMOVEXATTR";
67 case FUSE_INIT: return "INIT";
68 case FUSE_OPENDIR: return "OPENDIR";
69 case FUSE_READDIR: return "READDIR";
70 case FUSE_RELEASEDIR: return "RELEASEDIR";
71 case FUSE_FSYNCDIR: return "FSYNCDIR";
72 default: return "???";
73 }
74}
75
Miklos Szeredi76c17522005-07-13 14:08:19 +000076static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
Miklos Szeredi12744942005-07-11 12:32:31 +000077{
Miklos Szeredi2482fdb2005-07-12 13:23:53 +000078 attr->ino = stbuf->st_ino;
Miklos Szeredi12744942005-07-11 12:32:31 +000079 attr->mode = stbuf->st_mode;
Miklos Szeredi12744942005-07-11 12:32:31 +000080 attr->nlink = stbuf->st_nlink;
Miklos Szeredi2482fdb2005-07-12 13:23:53 +000081 attr->uid = stbuf->st_uid;
82 attr->gid = stbuf->st_gid;
Miklos Szeredi12744942005-07-11 12:32:31 +000083 attr->rdev = stbuf->st_rdev;
84 attr->size = stbuf->st_size;
85 attr->blocks = stbuf->st_blocks;
86 attr->atime = stbuf->st_atime;
87 attr->mtime = stbuf->st_mtime;
88 attr->ctime = stbuf->st_ctime;
89#ifdef HAVE_STRUCT_STAT_ST_ATIM
90 attr->atimensec = stbuf->st_atim.tv_nsec;
91 attr->mtimensec = stbuf->st_mtim.tv_nsec;
92 attr->ctimensec = stbuf->st_ctim.tv_nsec;
93#endif
94}
95
Miklos Szeredi76c17522005-07-13 14:08:19 +000096static void convert_attr(const struct fuse_attr *attr, struct stat *stbuf)
97{
98 stbuf->st_mode = attr->mode;
99 stbuf->st_uid = attr->uid;
100 stbuf->st_gid = attr->gid;
101 stbuf->st_size = attr->size;
102 stbuf->st_atime = attr->atime;
103 stbuf->st_mtime = attr->mtime;
104 stbuf->st_ctime = attr->ctime;
105#ifdef HAVE_STRUCT_STAT_ST_ATIM
106 stbuf->st_atim.tv_nsec = attr->atimensec;
107 stbuf->st_mtim.tv_nsec = attr->mtimensec;
108 stbuf->st_ctim.tv_nsec = attr->ctimensec;
109#endif
110}
111
Miklos Szeredi12744942005-07-11 12:32:31 +0000112static size_t iov_length(const struct iovec *iov, size_t count)
113{
114 size_t seg;
115 size_t ret = 0;
116
117 for (seg = 0; seg < count; seg++)
118 ret += iov[seg].iov_len;
119 return ret;
120}
121
Miklos Szeredia1482422005-08-14 23:00:27 +0000122static void free_req(fuse_req_t req)
Miklos Szeredi12744942005-07-11 12:32:31 +0000123{
Miklos Szeredia1482422005-08-14 23:00:27 +0000124 free(req);
Miklos Szeredi12744942005-07-11 12:32:31 +0000125}
126
Miklos Szeredia1482422005-08-14 23:00:27 +0000127static int send_reply(fuse_req_t req, int error, const void *arg,
128 size_t argsize)
Miklos Szeredi12744942005-07-11 12:32:31 +0000129{
130 struct fuse_out_header out;
131 struct iovec iov[2];
132 size_t count;
Miklos Szeredia1482422005-08-14 23:00:27 +0000133 int res;
Miklos Szeredi12744942005-07-11 12:32:31 +0000134
135 if (error <= -1000 || error > 0) {
136 fprintf(stderr, "fuse: bad error value: %i\n", error);
137 error = -ERANGE;
138 }
139
Miklos Szeredia1482422005-08-14 23:00:27 +0000140 out.unique = req->unique;
Miklos Szeredi12744942005-07-11 12:32:31 +0000141 out.error = error;
142 count = 1;
143 iov[0].iov_base = &out;
144 iov[0].iov_len = sizeof(struct fuse_out_header);
145 if (argsize && !error) {
146 count++;
Miklos Szeredi76c17522005-07-13 14:08:19 +0000147 iov[1].iov_base = (void *) arg;
Miklos Szeredi12744942005-07-11 12:32:31 +0000148 iov[1].iov_len = argsize;
149 }
Miklos Szeredia1482422005-08-14 23:00:27 +0000150 out.len = iov_length(iov, count);
151
152 if (req->f->debug) {
153 printf(" unique: %llu, error: %i (%s), outsize: %i\n",
154 out.unique, out.error, strerror(-out.error), out.len);
155 fflush(stdout);
156 }
157 res = fuse_chan_send(req->ch, iov, count);
158 free_req(req);
159
160 return res;
Miklos Szeredi12744942005-07-11 12:32:31 +0000161}
162
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000163size_t fuse_dirent_size(size_t namelen)
Miklos Szeredi12744942005-07-11 12:32:31 +0000164{
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000165 return FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + namelen);
Miklos Szeredi12744942005-07-11 12:32:31 +0000166}
167
Miklos Szeredif6e0ec62005-08-03 09:11:06 +0000168char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf,
Miklos Szeredi76c17522005-07-13 14:08:19 +0000169 off_t off)
Miklos Szeredi12744942005-07-11 12:32:31 +0000170{
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000171 unsigned namelen = strlen(name);
172 unsigned entlen = FUSE_NAME_OFFSET + namelen;
173 unsigned entsize = fuse_dirent_size(namelen);
174 unsigned padlen = entsize - entlen;
175 struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
Miklos Szeredi12744942005-07-11 12:32:31 +0000176
Miklos Szeredif6e0ec62005-08-03 09:11:06 +0000177 dirent->ino = stbuf->st_ino;
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000178 dirent->off = off;
179 dirent->namelen = namelen;
Miklos Szeredif6e0ec62005-08-03 09:11:06 +0000180 dirent->type = (stbuf->st_mode & 0170000) >> 12;
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000181 strncpy(dirent->name, name, namelen);
182 if (padlen)
183 memset(buf + entlen, 0, padlen);
Miklos Szeredi12744942005-07-11 12:32:31 +0000184
Miklos Szeredi76c17522005-07-13 14:08:19 +0000185 return buf + entsize;
Miklos Szeredi12744942005-07-11 12:32:31 +0000186}
187
Miklos Szeredif6e0ec62005-08-03 09:11:06 +0000188static void convert_statfs(const struct statfs *stbuf,
Miklos Szeredi76c17522005-07-13 14:08:19 +0000189 struct fuse_kstatfs *kstatfs)
Miklos Szeredi12744942005-07-11 12:32:31 +0000190{
Miklos Szeredif6e0ec62005-08-03 09:11:06 +0000191 kstatfs->bsize = stbuf->f_bsize;
192 kstatfs->blocks = stbuf->f_blocks;
193 kstatfs->bfree = stbuf->f_bfree;
194 kstatfs->bavail = stbuf->f_bavail;
195 kstatfs->files = stbuf->f_files;
196 kstatfs->ffree = stbuf->f_ffree;
197 kstatfs->namelen = stbuf->f_namelen;
Miklos Szeredi12744942005-07-11 12:32:31 +0000198}
199
Miklos Szeredia1482422005-08-14 23:00:27 +0000200static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
Miklos Szeredi76c17522005-07-13 14:08:19 +0000201{
Miklos Szeredia1482422005-08-14 23:00:27 +0000202 return send_reply(req, 0, arg, argsize);
Miklos Szeredi76c17522005-07-13 14:08:19 +0000203}
204
205int fuse_reply_err(fuse_req_t req, int err)
206{
Miklos Szeredia1482422005-08-14 23:00:27 +0000207 return send_reply(req, -err, NULL, 0);
Miklos Szeredi76c17522005-07-13 14:08:19 +0000208}
209
Miklos Szeredi836ab712005-10-03 14:11:59 +0000210void fuse_reply_none(fuse_req_t req)
Miklos Szeredi76c17522005-07-13 14:08:19 +0000211{
212 free_req(req);
Miklos Szeredi76c17522005-07-13 14:08:19 +0000213}
214
215static unsigned long calc_timeout_sec(double t)
216{
217 if (t > (double) ULONG_MAX)
218 return ULONG_MAX;
219 else if (t < 0.0)
220 return 0;
221 else
222 return (unsigned long) t;
223}
224
225static unsigned int calc_timeout_nsec(double t)
226{
227 double f = t - (double) calc_timeout_sec(t);
228 if (f < 0.0)
229 return 0;
230 else if (f >= 0.999999999)
231 return 999999999;
232 else
233 return (unsigned int) (f * 1.0e9);
234}
235
Miklos Szeredib0c52c52005-08-23 15:39:43 +0000236static void fill_entry(struct fuse_entry_out *arg,
237 const struct fuse_entry_param *e)
238{
239 arg->nodeid = e->ino;
240 arg->generation = e->generation;
241 arg->entry_valid = calc_timeout_sec(e->entry_timeout);
242 arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
243 arg->attr_valid = calc_timeout_sec(e->attr_timeout);
244 arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
245 convert_stat(&e->attr, &arg->attr);
246}
247
248static void fill_open(struct fuse_open_out *arg,
249 const struct fuse_file_info *f)
250{
251 arg->fh = f->fh;
252 if (f->direct_io)
253 arg->open_flags |= FOPEN_DIRECT_IO;
254 if (f->keep_cache)
255 arg->open_flags |= FOPEN_KEEP_CACHE;
256}
257
Miklos Szeredi76c17522005-07-13 14:08:19 +0000258int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
259{
260 struct fuse_entry_out arg;
261
262 memset(&arg, 0, sizeof(arg));
Miklos Szeredib0c52c52005-08-23 15:39:43 +0000263 fill_entry(&arg, e);
264 return send_reply_ok(req, &arg, sizeof(arg));
265}
Miklos Szeredi76c17522005-07-13 14:08:19 +0000266
Miklos Szeredi76c17522005-07-13 14:08:19 +0000267int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
268 double attr_timeout)
269{
270 struct fuse_attr_out arg;
271
272 memset(&arg, 0, sizeof(arg));
273 arg.attr_valid = calc_timeout_sec(attr_timeout);
274 arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
275 convert_stat(attr, &arg.attr);
276
Miklos Szeredia1482422005-08-14 23:00:27 +0000277 return send_reply_ok(req, &arg, sizeof(arg));
Miklos Szeredi76c17522005-07-13 14:08:19 +0000278}
279
Miklos Szeredif6e0ec62005-08-03 09:11:06 +0000280int fuse_reply_readlink(fuse_req_t req, const char *linkname)
Miklos Szeredi76c17522005-07-13 14:08:19 +0000281{
Miklos Szeredia1482422005-08-14 23:00:27 +0000282 return send_reply_ok(req, linkname, strlen(linkname));
Miklos Szeredi76c17522005-07-13 14:08:19 +0000283}
284
285int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
286{
287 struct fuse_open_out arg;
288
289 memset(&arg, 0, sizeof(arg));
Miklos Szeredib0c52c52005-08-23 15:39:43 +0000290 fill_open(&arg, f);
Miklos Szeredia1482422005-08-14 23:00:27 +0000291 return send_reply_ok(req, &arg, sizeof(arg));
Miklos Szeredi76c17522005-07-13 14:08:19 +0000292}
293
294int fuse_reply_write(fuse_req_t req, size_t count)
295{
296 struct fuse_write_out arg;
297
298 memset(&arg, 0, sizeof(arg));
299 arg.size = count;
Miklos Szeredi9b813af2005-07-21 07:59:37 +0000300
Miklos Szeredia1482422005-08-14 23:00:27 +0000301 return send_reply_ok(req, &arg, sizeof(arg));
Miklos Szeredi76c17522005-07-13 14:08:19 +0000302}
303
304int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
305{
Miklos Szeredia1482422005-08-14 23:00:27 +0000306 return send_reply_ok(req, buf, size);
Miklos Szeredi76c17522005-07-13 14:08:19 +0000307}
308
Miklos Szeredif6e0ec62005-08-03 09:11:06 +0000309int fuse_reply_statfs(fuse_req_t req, const struct statfs *stbuf)
Miklos Szeredi76c17522005-07-13 14:08:19 +0000310{
311 struct fuse_statfs_out arg;
312
313 memset(&arg, 0, sizeof(arg));
Miklos Szeredif6e0ec62005-08-03 09:11:06 +0000314 convert_statfs(stbuf, &arg.st);
Miklos Szeredi9b813af2005-07-21 07:59:37 +0000315
Miklos Szeredia1482422005-08-14 23:00:27 +0000316 return send_reply_ok(req, &arg, sizeof(arg));
Miklos Szeredi76c17522005-07-13 14:08:19 +0000317}
318
319int fuse_reply_xattr(fuse_req_t req, size_t count)
320{
321 struct fuse_getxattr_out arg;
322
323 memset(&arg, 0, sizeof(arg));
324 arg.size = count;
Miklos Szeredi9b813af2005-07-21 07:59:37 +0000325
Miklos Szeredia1482422005-08-14 23:00:27 +0000326 return send_reply_ok(req, &arg, sizeof(arg));
Miklos Szeredi76c17522005-07-13 14:08:19 +0000327}
328
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000329static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, char *name)
Miklos Szeredi12744942005-07-11 12:32:31 +0000330{
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000331 if (req->f->op.lookup)
332 req->f->op.lookup(req, nodeid, name);
Miklos Szeredi12744942005-07-11 12:32:31 +0000333 else
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000334 fuse_reply_err(req, ENOSYS);
Miklos Szeredi12744942005-07-11 12:32:31 +0000335}
336
Miklos Szeredi76c17522005-07-13 14:08:19 +0000337static void do_forget(fuse_req_t req, fuse_ino_t nodeid,
338 struct fuse_forget_in *arg)
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000339{
340 if (req->f->op.forget)
Miklos Szeredi76c17522005-07-13 14:08:19 +0000341 req->f->op.forget(req, nodeid, arg->nlookup);
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000342}
343
344static void do_getattr(fuse_req_t req, fuse_ino_t nodeid)
345{
346 if (req->f->op.getattr)
Miklos Szerediecce1bf2005-08-25 15:19:06 +0000347 req->f->op.getattr(req, nodeid, NULL);
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000348 else
349 fuse_reply_err(req, ENOSYS);
350}
351
352static void do_setattr(fuse_req_t req, fuse_ino_t nodeid,
Miklos Szerediecce1bf2005-08-25 15:19:06 +0000353 struct fuse_setattr_in *arg, struct fuse_file_info *fi)
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000354{
Miklos Szeredi76c17522005-07-13 14:08:19 +0000355 if (req->f->op.setattr) {
356 struct stat stbuf;
Miklos Szeredie3b83092005-07-22 17:24:30 +0000357 memset(&stbuf, 0, sizeof(stbuf));
Miklos Szeredi76c17522005-07-13 14:08:19 +0000358 convert_attr(&arg->attr, &stbuf);
Miklos Szerediecce1bf2005-08-25 15:19:06 +0000359 req->f->op.setattr(req, nodeid, &stbuf, arg->valid, fi);
Miklos Szeredi76c17522005-07-13 14:08:19 +0000360 } else
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000361 fuse_reply_err(req, ENOSYS);
362}
363
364static void do_readlink(fuse_req_t req, fuse_ino_t nodeid)
365{
366 if (req->f->op.readlink)
367 req->f->op.readlink(req, nodeid);
368 else
369 fuse_reply_err(req, ENOSYS);
370}
371
372static void do_mknod(fuse_req_t req, fuse_ino_t nodeid,
373 struct fuse_mknod_in *arg)
374{
375 if (req->f->op.mknod)
376 req->f->op.mknod(req, nodeid, PARAM(arg), arg->mode, arg->rdev);
377 else
378 fuse_reply_err(req, ENOSYS);
379}
380
381static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid,
382 struct fuse_mkdir_in *arg)
383{
384 if (req->f->op.mkdir)
385 req->f->op.mkdir(req, nodeid, PARAM(arg), arg->mode);
386 else
387 fuse_reply_err(req, ENOSYS);
388}
389
390static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, char *name)
391{
392 if (req->f->op.unlink)
393 req->f->op.unlink(req, nodeid, name);
394 else
395 fuse_reply_err(req, ENOSYS);
396}
397
398static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, char *name)
399{
400 if (req->f->op.rmdir)
401 req->f->op.rmdir(req, nodeid, name);
402 else
403 fuse_reply_err(req, ENOSYS);
404}
405
406static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, char *name,
Miklos Szeredif6e0ec62005-08-03 09:11:06 +0000407 char *linkname)
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000408{
409 if (req->f->op.symlink)
Miklos Szeredif6e0ec62005-08-03 09:11:06 +0000410 req->f->op.symlink(req, linkname, nodeid, name);
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000411 else
412 fuse_reply_err(req, ENOSYS);
413}
414
415static void do_rename(fuse_req_t req, fuse_ino_t nodeid,
416 struct fuse_rename_in *arg)
417{
418 char *oldname = PARAM(arg);
419 char *newname = oldname + strlen(oldname) + 1;
420
421 if (req->f->op.rename)
422 req->f->op.rename(req, nodeid, oldname, arg->newdir, newname);
423 else
424 fuse_reply_err(req, ENOSYS);
425}
426
427static void do_link(fuse_req_t req, fuse_ino_t nodeid,
428 struct fuse_link_in *arg)
429{
430 if (req->f->op.link)
431 req->f->op.link(req, arg->oldnodeid, nodeid, PARAM(arg));
432 else
433 fuse_reply_err(req, ENOSYS);
434}
435
436static void do_open(fuse_req_t req, fuse_ino_t nodeid,
437 struct fuse_open_in *arg)
438{
439 struct fuse_file_info fi;
Miklos Szeredi9b813af2005-07-21 07:59:37 +0000440
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000441 memset(&fi, 0, sizeof(fi));
442 fi.flags = arg->flags;
443
444 if (req->f->op.open)
445 req->f->op.open(req, nodeid, &fi);
446 else
Miklos Szeredi76c17522005-07-13 14:08:19 +0000447 fuse_reply_open(req, &fi);
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000448}
449
450static void do_read(fuse_req_t req, fuse_ino_t nodeid,
451 struct fuse_read_in *arg)
452{
Miklos Szeredi76c17522005-07-13 14:08:19 +0000453 if (req->f->op.read) {
454 struct fuse_file_info fi;
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000455
Miklos Szeredi76c17522005-07-13 14:08:19 +0000456 memset(&fi, 0, sizeof(fi));
457 fi.fh = arg->fh;
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000458 req->f->op.read(req, nodeid, arg->size, arg->offset, &fi);
Miklos Szeredi76c17522005-07-13 14:08:19 +0000459 } else
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000460 fuse_reply_err(req, ENOSYS);
461}
462
463static void do_write(fuse_req_t req, fuse_ino_t nodeid,
464 struct fuse_write_in *arg)
465{
466 struct fuse_file_info fi;
467
468 memset(&fi, 0, sizeof(fi));
469 fi.fh = arg->fh;
470 fi.writepage = arg->write_flags & 1;
471
472 if (req->f->op.write)
Miklos Szeredi76c17522005-07-13 14:08:19 +0000473 req->f->op.write(req, nodeid, PARAM(arg), arg->size,
474 arg->offset, &fi);
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000475 else
476 fuse_reply_err(req, ENOSYS);
477}
478
479static void do_flush(fuse_req_t req, fuse_ino_t nodeid,
480 struct fuse_flush_in *arg)
481{
482 struct fuse_file_info fi;
483
484 memset(&fi, 0, sizeof(fi));
485 fi.fh = arg->fh;
486
487 if (req->f->op.flush)
488 req->f->op.flush(req, nodeid, &fi);
489 else
490 fuse_reply_err(req, ENOSYS);
491}
492
493static void do_release(fuse_req_t req, fuse_ino_t nodeid,
494 struct fuse_release_in *arg)
495{
496 struct fuse_file_info fi;
497
498 memset(&fi, 0, sizeof(fi));
499 fi.flags = arg->flags;
500 fi.fh = arg->fh;
501
502 if (req->f->op.release)
503 req->f->op.release(req, nodeid, &fi);
504 else
Miklos Szeredi76c17522005-07-13 14:08:19 +0000505 fuse_reply_err(req, 0);
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000506}
507
508static void do_fsync(fuse_req_t req, fuse_ino_t nodeid,
Miklos Szeredi12744942005-07-11 12:32:31 +0000509 struct fuse_fsync_in *inarg)
510{
Miklos Szeredi12744942005-07-11 12:32:31 +0000511 struct fuse_file_info fi;
512
513 memset(&fi, 0, sizeof(fi));
514 fi.fh = inarg->fh;
515
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000516 if (req->f->op.fsync)
517 req->f->op.fsync(req, nodeid, inarg->fsync_flags & 1, &fi);
518 else
519 fuse_reply_err(req, ENOSYS);
520}
521
522static void do_opendir(fuse_req_t req, fuse_ino_t nodeid,
523 struct fuse_open_in *arg)
524{
525 struct fuse_file_info fi;
526
527 memset(&fi, 0, sizeof(fi));
528 fi.flags = arg->flags;
529
530 if (req->f->op.opendir)
531 req->f->op.opendir(req, nodeid, &fi);
532 else
Miklos Szeredi76c17522005-07-13 14:08:19 +0000533 fuse_reply_open(req, &fi);
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000534}
535
536static void do_readdir(fuse_req_t req, fuse_ino_t nodeid,
537 struct fuse_read_in *arg)
538{
539 struct fuse_file_info fi;
540
541 memset(&fi, 0, sizeof(fi));
542 fi.fh = arg->fh;
543
544 if (req->f->op.readdir)
545 req->f->op.readdir(req, nodeid, arg->size, arg->offset, &fi);
546 else
547 fuse_reply_err(req, ENOSYS);
548}
549
550static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid,
551 struct fuse_release_in *arg)
552{
553 struct fuse_file_info fi;
554
555 memset(&fi, 0, sizeof(fi));
556 fi.flags = arg->flags;
557 fi.fh = arg->fh;
558
559 if (req->f->op.releasedir)
560 req->f->op.releasedir(req, nodeid, &fi);
561 else
Miklos Szeredi76c17522005-07-13 14:08:19 +0000562 fuse_reply_err(req, 0);
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000563}
564
565static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid,
566 struct fuse_fsync_in *inarg)
567{
568 struct fuse_file_info fi;
569
570 memset(&fi, 0, sizeof(fi));
571 fi.fh = inarg->fh;
572
573 if (req->f->op.fsyncdir)
574 req->f->op.fsyncdir(req, nodeid, inarg->fsync_flags & 1, &fi);
575 else
576 fuse_reply_err(req, ENOSYS);
577}
578
Miklos Szeredibd10a7b2005-07-15 09:59:59 +0000579static void do_statfs(fuse_req_t req)
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000580{
581 if (req->f->op.statfs)
Miklos Szeredibd10a7b2005-07-15 09:59:59 +0000582 req->f->op.statfs(req);
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000583 else
584 fuse_reply_err(req, ENOSYS);
Miklos Szeredi12744942005-07-11 12:32:31 +0000585}
586
Miklos Szeredi4331a272005-07-12 14:51:04 +0000587static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid,
Miklos Szeredi12744942005-07-11 12:32:31 +0000588 struct fuse_setxattr_in *arg)
589{
Miklos Szeredi12744942005-07-11 12:32:31 +0000590 char *name = PARAM(arg);
Miklos Szeredi1b188022005-07-28 11:07:29 +0000591 char *value = name + strlen(name) + 1;
Miklos Szeredi12744942005-07-11 12:32:31 +0000592
Miklos Szeredi4331a272005-07-12 14:51:04 +0000593 if (req->f->op.setxattr)
Miklos Szeredi76c17522005-07-13 14:08:19 +0000594 req->f->op.setxattr(req, nodeid, name, value, arg->size,
595 arg->flags);
Miklos Szeredi4331a272005-07-12 14:51:04 +0000596 else
597 fuse_reply_err(req, ENOSYS);
Miklos Szeredi12744942005-07-11 12:32:31 +0000598}
599
Miklos Szeredi4331a272005-07-12 14:51:04 +0000600static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid,
Miklos Szeredi12744942005-07-11 12:32:31 +0000601 struct fuse_getxattr_in *arg)
602{
Miklos Szeredi4331a272005-07-12 14:51:04 +0000603 if (req->f->op.getxattr)
604 req->f->op.getxattr(req, nodeid, PARAM(arg), arg->size);
Miklos Szeredi12744942005-07-11 12:32:31 +0000605 else
Miklos Szeredi4331a272005-07-12 14:51:04 +0000606 fuse_reply_err(req, ENOSYS);
Miklos Szeredi12744942005-07-11 12:32:31 +0000607}
608
Miklos Szeredi4331a272005-07-12 14:51:04 +0000609static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid,
Miklos Szeredi12744942005-07-11 12:32:31 +0000610 struct fuse_getxattr_in *arg)
611{
Miklos Szeredi4331a272005-07-12 14:51:04 +0000612 if (req->f->op.listxattr)
613 req->f->op.listxattr(req, nodeid, arg->size);
Miklos Szeredi12744942005-07-11 12:32:31 +0000614 else
Miklos Szeredi4331a272005-07-12 14:51:04 +0000615 fuse_reply_err(req, ENOSYS);
Miklos Szeredi12744942005-07-11 12:32:31 +0000616}
617
Miklos Szeredi4331a272005-07-12 14:51:04 +0000618static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, char *name)
Miklos Szeredi12744942005-07-11 12:32:31 +0000619{
Miklos Szeredi4331a272005-07-12 14:51:04 +0000620 if (req->f->op.removexattr)
621 req->f->op.removexattr(req, nodeid, name);
622 else
623 fuse_reply_err(req, ENOSYS);
Miklos Szeredi12744942005-07-11 12:32:31 +0000624}
625
Miklos Szeredia1482422005-08-14 23:00:27 +0000626static void do_init(fuse_req_t req, struct fuse_init_in_out *arg)
Miklos Szeredi12744942005-07-11 12:32:31 +0000627{
628 struct fuse_init_in_out outarg;
Miklos Szeredia1482422005-08-14 23:00:27 +0000629 struct fuse_ll *f = req->f;
Miklos Szeredi12744942005-07-11 12:32:31 +0000630
Miklos Szeredi76c17522005-07-13 14:08:19 +0000631 if (f->debug) {
Miklos Szeredi12744942005-07-11 12:32:31 +0000632 printf("INIT: %u.%u\n", arg->major, arg->minor);
633 fflush(stdout);
634 }
635 f->got_init = 1;
636 if (f->op.init)
Miklos Szeredia1482422005-08-14 23:00:27 +0000637 f->op.init(f->userdata);
Miklos Szeredi12744942005-07-11 12:32:31 +0000638
Miklos Szeredi76c17522005-07-13 14:08:19 +0000639 f->major = FUSE_KERNEL_VERSION;
640 f->minor = FUSE_KERNEL_MINOR_VERSION;
641
Miklos Szeredi12744942005-07-11 12:32:31 +0000642 memset(&outarg, 0, sizeof(outarg));
643 outarg.major = f->major;
644 outarg.minor = f->minor;
645
Miklos Szeredi76c17522005-07-13 14:08:19 +0000646 if (f->debug) {
Miklos Szeredi12744942005-07-11 12:32:31 +0000647 printf(" INIT: %u.%u\n", outarg.major, outarg.minor);
648 fflush(stdout);
649 }
650
Miklos Szeredia1482422005-08-14 23:00:27 +0000651 send_reply_ok(req, &outarg, sizeof(outarg));
Miklos Szeredi12744942005-07-11 12:32:31 +0000652}
653
Miklos Szeredibd10a7b2005-07-15 09:59:59 +0000654void *fuse_req_userdata(fuse_req_t req)
655{
656 return req->f->userdata;
657}
658
659const struct fuse_ctx *fuse_req_ctx(fuse_req_t req)
660{
661 return &req->ctx;
662}
663
Miklos Szeredia1482422005-08-14 23:00:27 +0000664static void fuse_ll_process(void *data, const char *buf, size_t len,
665 struct fuse_chan *ch)
Miklos Szeredi12744942005-07-11 12:32:31 +0000666{
Miklos Szeredia1482422005-08-14 23:00:27 +0000667 struct fuse_ll *f = (struct fuse_ll *) data;
668 struct fuse_in_header *in = (struct fuse_in_header *) buf;
669 const void *inarg = buf + sizeof(struct fuse_in_header);
Miklos Szeredi76c17522005-07-13 14:08:19 +0000670 struct fuse_req *req;
Miklos Szeredi12744942005-07-11 12:32:31 +0000671
Miklos Szeredi76c17522005-07-13 14:08:19 +0000672 if (f->debug) {
Miklos Szeredi12744942005-07-11 12:32:31 +0000673 printf("unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %i\n",
Miklos Szeredif6e0ec62005-08-03 09:11:06 +0000674 in->unique, opname((enum fuse_opcode) in->opcode), in->opcode,
Miklos Szeredia1482422005-08-14 23:00:27 +0000675 (unsigned long) in->nodeid, len);
Miklos Szeredi12744942005-07-11 12:32:31 +0000676 fflush(stdout);
677 }
678
Miklos Szeredi76c17522005-07-13 14:08:19 +0000679 req = (struct fuse_req *) malloc(sizeof(struct fuse_req));
680 if (req == NULL) {
681 fprintf(stderr, "fuse: failed to allocate request\n");
Miklos Szeredia1482422005-08-14 23:00:27 +0000682 return;
Miklos Szeredi76c17522005-07-13 14:08:19 +0000683 }
Miklos Szeredi9b813af2005-07-21 07:59:37 +0000684
Miklos Szeredi76c17522005-07-13 14:08:19 +0000685 req->f = f;
686 req->unique = in->unique;
Miklos Szeredibd10a7b2005-07-15 09:59:59 +0000687 req->ctx.uid = in->uid;
688 req->ctx.gid = in->gid;
689 req->ctx.pid = in->pid;
Miklos Szeredia1482422005-08-14 23:00:27 +0000690 req->ch = ch;
Miklos Szeredi12744942005-07-11 12:32:31 +0000691
Miklos Szeredia1482422005-08-14 23:00:27 +0000692 if (!f->got_init && in->opcode != FUSE_INIT)
693 fuse_reply_err(req, EPROTO);
694 else if (f->allow_root && in->uid != f->owner && in->uid != 0 &&
695 in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
696 in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
697 in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
698 in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR) {
699 fuse_reply_err(req, EACCES);
700 } else switch (in->opcode) {
701 case FUSE_INIT:
702 do_init(req, (struct fuse_init_in_out *) inarg);
703 break;
Miklos Szeredi2bb750e2005-10-03 14:54:24 +0000704
Miklos Szeredi12744942005-07-11 12:32:31 +0000705 case FUSE_LOOKUP:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000706 do_lookup(req, in->nodeid, (char *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000707 break;
708
Miklos Szeredi76c17522005-07-13 14:08:19 +0000709 case FUSE_FORGET:
710 do_forget(req, in->nodeid, (struct fuse_forget_in *) inarg);
Miklos Szeredi2482fdb2005-07-12 13:23:53 +0000711 break;
Miklos Szeredi76c17522005-07-13 14:08:19 +0000712
Miklos Szeredi12744942005-07-11 12:32:31 +0000713 case FUSE_GETATTR:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000714 do_getattr(req, in->nodeid);
Miklos Szeredi12744942005-07-11 12:32:31 +0000715 break;
716
717 case FUSE_SETATTR:
Miklos Szerediecce1bf2005-08-25 15:19:06 +0000718 do_setattr(req, in->nodeid, (struct fuse_setattr_in *) inarg, NULL);
719 break;
720
Miklos Szeredi12744942005-07-11 12:32:31 +0000721 case FUSE_READLINK:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000722 do_readlink(req, in->nodeid);
Miklos Szeredi12744942005-07-11 12:32:31 +0000723 break;
724
725 case FUSE_MKNOD:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000726 do_mknod(req, in->nodeid, (struct fuse_mknod_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000727 break;
728
729 case FUSE_MKDIR:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000730 do_mkdir(req, in->nodeid, (struct fuse_mkdir_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000731 break;
732
733 case FUSE_UNLINK:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000734 do_unlink(req, in->nodeid, (char *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000735 break;
736
737 case FUSE_RMDIR:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000738 do_rmdir(req, in->nodeid, (char *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000739 break;
740
741 case FUSE_SYMLINK:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000742 do_symlink(req, in->nodeid, (char *) inarg,
Miklos Szeredi12744942005-07-11 12:32:31 +0000743 ((char *) inarg) + strlen((char *) inarg) + 1);
744 break;
745
746 case FUSE_RENAME:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000747 do_rename(req, in->nodeid, (struct fuse_rename_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000748 break;
749
750 case FUSE_LINK:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000751 do_link(req, in->nodeid, (struct fuse_link_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000752 break;
753
754 case FUSE_OPEN:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000755 do_open(req, in->nodeid, (struct fuse_open_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000756 break;
757
758 case FUSE_FLUSH:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000759 do_flush(req, in->nodeid, (struct fuse_flush_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000760 break;
761
762 case FUSE_RELEASE:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000763 do_release(req, in->nodeid, (struct fuse_release_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000764 break;
765
766 case FUSE_READ:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000767 do_read(req, in->nodeid, (struct fuse_read_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000768 break;
769
770 case FUSE_WRITE:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000771 do_write(req, in->nodeid, (struct fuse_write_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000772 break;
773
774 case FUSE_STATFS:
Miklos Szeredibd10a7b2005-07-15 09:59:59 +0000775 do_statfs(req);
Miklos Szeredi12744942005-07-11 12:32:31 +0000776 break;
777
778 case FUSE_FSYNC:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000779 do_fsync(req, in->nodeid, (struct fuse_fsync_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000780 break;
781
782 case FUSE_SETXATTR:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000783 do_setxattr(req, in->nodeid, (struct fuse_setxattr_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000784 break;
785
786 case FUSE_GETXATTR:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000787 do_getxattr(req, in->nodeid, (struct fuse_getxattr_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000788 break;
789
790 case FUSE_LISTXATTR:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000791 do_listxattr(req, in->nodeid, (struct fuse_getxattr_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000792 break;
793
794 case FUSE_REMOVEXATTR:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000795 do_removexattr(req, in->nodeid, (char *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000796 break;
797
798 case FUSE_OPENDIR:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000799 do_opendir(req, in->nodeid, (struct fuse_open_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000800 break;
801
802 case FUSE_READDIR:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000803 do_readdir(req, in->nodeid, (struct fuse_read_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000804 break;
805
806 case FUSE_RELEASEDIR:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000807 do_releasedir(req, in->nodeid, (struct fuse_release_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000808 break;
809
810 case FUSE_FSYNCDIR:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000811 do_fsyncdir(req, in->nodeid, (struct fuse_fsync_in *) inarg);
Miklos Szeredi12744942005-07-11 12:32:31 +0000812 break;
813
814 default:
Miklos Szeredi76c17522005-07-13 14:08:19 +0000815 fuse_reply_err(req, ENOSYS);
Miklos Szeredi12744942005-07-11 12:32:31 +0000816 }
Miklos Szeredi12744942005-07-11 12:32:31 +0000817}
818
Miklos Szeredia1482422005-08-14 23:00:27 +0000819int fuse_lowlevel_is_lib_option(const char *opt)
Miklos Szeredi12744942005-07-11 12:32:31 +0000820{
821 if (strcmp(opt, "debug") == 0 ||
Miklos Szeredi76c17522005-07-13 14:08:19 +0000822 strcmp(opt, "allow_root") == 0)
Miklos Szeredi12744942005-07-11 12:32:31 +0000823 return 1;
824 else
825 return 0;
826}
827
Miklos Szeredi76c17522005-07-13 14:08:19 +0000828static int parse_ll_opts(struct fuse_ll *f, const char *opts)
Miklos Szeredi12744942005-07-11 12:32:31 +0000829{
830 if (opts) {
831 char *xopts = strdup(opts);
832 char *s = xopts;
833 char *opt;
834
835 if (xopts == NULL) {
836 fprintf(stderr, "fuse: memory allocation failed\n");
837 return -1;
838 }
839
840 while((opt = strsep(&s, ","))) {
841 if (strcmp(opt, "debug") == 0)
Miklos Szeredi76c17522005-07-13 14:08:19 +0000842 f->debug = 1;
Miklos Szeredi12744942005-07-11 12:32:31 +0000843 else if (strcmp(opt, "allow_root") == 0)
Miklos Szeredi76c17522005-07-13 14:08:19 +0000844 f->allow_root = 1;
Miklos Szeredi12744942005-07-11 12:32:31 +0000845 else
846 fprintf(stderr, "fuse: warning: unknown option `%s'\n", opt);
847 }
848 free(xopts);
849 }
850 return 0;
851}
852
Miklos Szeredia1482422005-08-14 23:00:27 +0000853static void fuse_ll_destroy(void *data)
854{
855 struct fuse_ll *f = (struct fuse_ll *) data;
856
857 if (f->op.destroy)
858 f->op.destroy(f->userdata);
859
860 free(f);
861}
862
863
Miklos Szeredi2bb750e2005-10-03 14:54:24 +0000864struct fuse_session *fuse_lowlevel_new(const char *opts,
Miklos Szeredia1482422005-08-14 23:00:27 +0000865 const struct fuse_lowlevel_ops *op,
866 size_t op_size, void *userdata)
Miklos Szeredi12744942005-07-11 12:32:31 +0000867{
868 struct fuse_ll *f;
Miklos Szeredia1482422005-08-14 23:00:27 +0000869 struct fuse_session *se;
870 struct fuse_session_ops sop = {
871 .process = fuse_ll_process,
872 .destroy = fuse_ll_destroy,
873 };
Miklos Szeredi12744942005-07-11 12:32:31 +0000874
Miklos Szeredia1482422005-08-14 23:00:27 +0000875 if (sizeof(struct fuse_lowlevel_ops) < op_size) {
Miklos Szeredib75d4b92005-10-11 10:12:08 +0000876 fprintf(stderr, "fuse: warning: library too old, some operations may not work\n");
Miklos Szeredia1482422005-08-14 23:00:27 +0000877 op_size = sizeof(struct fuse_lowlevel_ops);
Miklos Szeredi12744942005-07-11 12:32:31 +0000878 }
879
880 f = (struct fuse_ll *) calloc(1, sizeof(struct fuse_ll));
881 if (f == NULL) {
882 fprintf(stderr, "fuse: failed to allocate fuse object\n");
883 goto out;
884 }
885
Miklos Szeredi76c17522005-07-13 14:08:19 +0000886 if (parse_ll_opts(f, opts) == -1)
Miklos Szeredi12744942005-07-11 12:32:31 +0000887 goto out_free;
888
Miklos Szeredi12744942005-07-11 12:32:31 +0000889 memcpy(&f->op, op, op_size);
Miklos Szeredi12744942005-07-11 12:32:31 +0000890 f->owner = getuid();
Miklos Szeredibd10a7b2005-07-15 09:59:59 +0000891 f->userdata = userdata;
Miklos Szeredi12744942005-07-11 12:32:31 +0000892
Miklos Szeredia1482422005-08-14 23:00:27 +0000893 se = fuse_session_new(&sop, f);
894 if (!se)
895 goto out_free;
896
897 return se;
Miklos Szeredi12744942005-07-11 12:32:31 +0000898
899 out_free:
900 free(f);
901 out:
902 return NULL;
903}