Add test target for figfs_test.
[figfs.git] / fuse / Fuse_util.c
blob75154247bd0a51331b11ba72dbe9ba94a8b5e5ff
1 /*
2 This file is part of the "OCamlFuse" library.
4 OCamlFuse is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation (version 2 of the License).
8 OCamlFuse is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with OCamlFuse. See the file LICENSE. If you haven't received
15 a copy of the GNU General Public License, write to:
17 Free Software Foundation, Inc.,
18 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA
21 Vincenzo Ciancia
23 applejack@users.sf.net
24 vincenzo_ml@yahoo.it
27 #define UNKNOWN_ERR 127
29 #include <caml/mlvalues.h>
30 #include <caml/memory.h>
31 #include <caml/alloc.h>
32 #include <caml/fail.h>
33 #include <caml/callback.h>
34 #include <caml/custom.h>
35 #include <caml/bigarray.h>
36 #include <caml/camlidlruntime.h>
37 #include <caml/mlvalues.h>
38 #include <caml/callback.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <errno.h>
43 #include <string.h>
44 #include <math.h>
45 #include <sys/types.h>
46 #include <dirent.h>
47 #include <sys/stat.h>
48 #include <fcntl.h>
50 #define FUSE_USE_VERSION 22
51 #include <fuse.h>
52 #include <sys/types.h>
53 #include <sys/xattr.h>
55 #include "Fuse_bindings.h"
57 #define min(a,b) (a<b?a:b)
59 value callback4(value closure,value arg1,value arg2,value arg3,value arg4)
61 value args[4]={arg1,arg2,arg3,arg4};
62 return callbackN(closure,4,args);
65 value c2ml_setxattr_flags(int flags)
67 CAMLparam0 ();
68 CAMLlocal1(res);
70 if (flags==XATTR_CREATE)
72 res=Val_int(1);
74 else if (flags==XATTR_REPLACE)
76 res=Val_int(2);
78 else res=Val_int(0);
80 CAMLreturn(res);
83 /* This part shamelessly copied from mlfuse */
84 #define ADDFLAGT(T, X) \
85 num_ml_constr--;\
86 if (T) {\
87 tmp = alloc_small (2, 0);\
88 Field (tmp, 0) = Val_int (num_ml_constr);\
89 Field (tmp, 1) = res;\
90 res = tmp;\
93 #define ADDFLAG(X) ADDFLAGT(flags & X, X)
94 #define ADDBASEFLAG(X) ADDFLAGT((flags & 3) == X, X)
96 value c_flags_to_open_flag_list (int flags) {
97 CAMLparam0 ();
98 CAMLlocal2 (res, tmp);
99 int num_ml_constr = 8;
101 res = Val_int (0);
103 ADDFLAG (O_EXCL);
104 ADDFLAG (O_TRUNC);
105 ADDFLAG (O_CREAT);
106 ADDFLAG (O_APPEND);
107 ADDFLAG (O_NONBLOCK);
109 ADDBASEFLAG (O_RDWR);
110 ADDBASEFLAG (O_WRONLY);
111 ADDBASEFLAG (O_RDONLY);
113 CAMLreturn (res);
115 /* End of shame */
117 static int ml2c_unix_error_vect[] =
119 E2BIG,
120 EACCES,
121 EAGAIN,
122 EBADF,
123 EBUSY,
124 ECHILD,
125 EDEADLK,
126 EDOM ,
127 EEXIST,
128 EFAULT,
129 EFBIG,
130 EINTR,
131 EINVAL,
132 EIO,
133 EISDIR,
134 EMFILE,
135 EMLINK,
136 ENAMETOOLONG,
137 ENFILE,
138 ENODEV,
139 ENOENT,
140 ENOEXEC,
141 ENOLCK,
142 ENOMEM,
143 ENOSPC,
144 ENOSYS,
145 ENOTDIR,
146 ENOTEMPTY,
147 ENOTTY,
148 ENXIO,
149 EPERM,
150 EPIPE,
151 ERANGE,
152 EROFS,
153 ESPIPE,
154 ESRCH,
155 EXDEV,
156 EWOULDBLOCK,
157 EINPROGRESS,
158 EALREADY,
159 ENOTSOCK,
160 EDESTADDRREQ,
161 EMSGSIZE,
162 EPROTOTYPE,
163 ENOPROTOOPT,
164 EPROTONOSUPPORT ,
165 ESOCKTNOSUPPORT ,
166 EOPNOTSUPP,
167 EPFNOSUPPORT,
168 EAFNOSUPPORT,
169 EADDRINUSE,
170 EADDRNOTAVAIL,
171 ENETDOWN,
172 ENETUNREACH,
173 ENETRESET,
174 ECONNABORTED,
175 ECONNRESET,
176 ENOBUFS,
177 EISCONN,
178 ENOTCONN,
179 ESHUTDOWN,
180 ETOOMANYREFS,
181 ETIMEDOUT,
182 ECONNREFUSED,
183 EHOSTDOWN,
184 EHOSTUNREACH,
185 ELOOP,
186 EOVERFLOW,
187 0 /* Terminator for the inverse function */
190 static int ml2c_unix_error_vect_dim=0;
192 int ml2c_unix_error(int ocaml_err)
194 if (ocaml_err < ml2c_unix_error_vect_dim)
195 return ml2c_unix_error_vect[ocaml_err];
196 return UNKNOWN_ERR; /* TODO: find an appropriate value */
199 /* TODO: This sucks */
201 int * invert_array(int * src,int * indim,int * outdim)
203 /* Find dimensions */
204 int dim=0;
205 int i=0;
206 int srcdim = 0;
208 while (src[srcdim]!=0)
210 if (src[srcdim]>=dim) dim=src[srcdim]+1;
211 srcdim++;
214 /* Create the result */
215 int * res = malloc(dim * sizeof(int));
216 for (i = 0;i < dim;i++) res[i]=UNKNOWN_ERR; /* TODO: find a meaningful value */
218 /* Invert the array */
219 for (i = 0;i < srcdim;i++) res[src[i]]=i;
221 *indim=srcdim;
222 *outdim=dim;
223 return res;
226 static int c2ml_unix_error_vect_dim=0;
227 static int * c2ml_unix_error_vect;
229 int c2ml_unix_error(int c_err)
231 if (c_err < c2ml_unix_error_vect_dim)
232 return c2ml_unix_error_vect[c_err];
233 return UNKNOWN_ERR; /* TODO: find a meaningful value (also search for UNKNOWN_ERR in this file */
236 /* end "Thisk sucks" part */
238 int ml2c_unix_file_kind[] =
240 S_IFREG,
241 S_IFDIR,
242 S_IFCHR,
243 S_IFBLK,
244 S_IFLNK,
245 S_IFIFO,
246 S_IFSOCK
249 void ml2c_Unix_stats_struct_stat(value v,struct stat * s)
251 memset(s,0,sizeof(*s));
252 s->st_dev=Int_val(Field(v,0));
253 s->st_ino=Int_val(Field(v,1));
254 s->st_mode=0 | Int_val(Field(v,3)) | ml2c_unix_file_kind[Int_val(Field(v,2))];
255 s->st_nlink=Int_val(Field(v,4));
256 s->st_uid=Int_val(Field(v,5));
257 s->st_gid=Int_val(Field(v,6));
258 s->st_rdev=Int_val(Field(v,7));
259 s->st_size=Int64_val(Field(v,8));
260 s->st_blksize=512; /* TODO: STUB, at least use the one from statfs */
261 s->st_blocks=ceil(((double)s->st_size)/((double)s->st_blksize)); /* TODO: STUB! */
262 s->st_atime=Double_val(Field(v,9));
263 s->st_mtime=Double_val(Field(v,10));
264 s->st_ctime=Double_val(Field(v,11));
267 void ml2c_Unix_struct_statfs(value v,struct statfs * st)
269 memset(st,0,sizeof(*st));
270 st->f_bsize = Int64_val(Field(v,0));
271 st->f_blocks = Int64_val(Field(v,1));
272 st->f_bfree = Int64_val(Field(v,2));
273 st->f_bavail = Int64_val(Field(v,3));
274 st->f_files = Int64_val(Field(v,4));
275 st->f_ffree = Int64_val(Field(v,5));
276 st->f_namelen = Int64_val(Field(v,6));
279 #define FOR_ALL_OPS(MACRO) \
280 MACRO(getattr) \
281 MACRO(readlink) \
282 MACRO(readdir) \
283 MACRO(opendir) \
284 MACRO(releasedir) \
285 MACRO(fsyncdir) \
286 MACRO(mknod) \
287 MACRO(mkdir) \
288 MACRO(symlink) \
289 MACRO(unlink) \
290 MACRO(rmdir) \
291 MACRO(rename) \
292 MACRO(link) \
293 MACRO(chmod) \
294 MACRO(chown) \
295 MACRO(truncate) \
296 MACRO(utime) \
297 MACRO(open) \
298 MACRO(read) \
299 MACRO(write) \
300 MACRO(statfs) \
301 MACRO(release) \
302 MACRO(flush) \
303 MACRO(fsync) \
304 MACRO(setxattr) \
305 MACRO(getxattr) \
306 MACRO(listxattr) \
307 MACRO(removexattr)
309 #define SET_NULL_OP(OPNAME) .OPNAME = NULL,
311 static struct fuse_operations ops = {
312 FOR_ALL_OPS(SET_NULL_OP)
315 static value * ocaml_list_length=NULL;
317 #define DECLARE_OP_CLOSURE(OPNAME) static value * OPNAME##_closure=NULL;
318 FOR_ALL_OPS(DECLARE_OP_CLOSURE)
320 #define getattr_ARGS (const char* path, struct stat * buf)
321 #define getattr_CB vres=callback(*getattr_closure,vpath);
322 #define getattr_RES \
323 ml2c_Unix_stats_struct_stat(Field(vres,0),buf);
325 /* TODO: allow ocaml to use the offset and the stat argument of the filler */
326 #define readdir_ARGS (const char * path, void * buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info * info)
327 #define readdir_CB vres=callback2(*readdir_closure,vpath,Val_int(info->fh));
328 #define readdir_RES \
329 vtmp=Field(vres,0); \
330 while (Is_block(vtmp)) \
332 if (filler(buf,String_val(Field(vtmp,0)),NULL,0)) break; \
333 if (res != 0) break; \
334 vtmp=Field(vtmp,1); \
337 #define mknod_ARGS (const char *path, mode_t mode, dev_t rdev)
338 #define mknod_CB vres=callback2(*mknod_closure,vpath,Val_int(mode));
339 #define mknod_RES
341 #define mkdir_ARGS (const char *path, mode_t mode)
342 #define mkdir_CB vres=callback2(*mkdir_closure,vpath,Val_int(mode));
343 #define mkdir_RES
345 #define unlink_ARGS (const char *path)
346 #define unlink_CB vres=callback(*unlink_closure,vpath);
347 #define unlink_RES
349 #define rmdir_ARGS (const char *path)
350 #define rmdir_CB vres=callback(*rmdir_closure,vpath);
351 #define rmdir_RES
353 #define readlink_ARGS (const char *path, char *buf, size_t size)
354 #define readlink_CB vres=callback(*readlink_closure,vpath);
355 #define readlink_RES strncpy(buf,String_val(Field(vres,0)),size-1);
357 #define symlink_ARGS (const char *path, const char *dest)
358 #define symlink_CB \
359 vtmp = copy_string(dest); \
360 vres=callback2(*symlink_closure,vpath,vtmp);
361 #define symlink_RES
363 #define rename_ARGS (const char *path, const char *dest)
364 #define rename_CB \
365 vtmp = copy_string(dest); \
366 vres=callback2(*rename_closure,vpath,vtmp);
367 #define rename_RES
369 #define link_ARGS (const char *path, const char *dest)
370 #define link_CB \
371 vtmp = copy_string(dest); \
372 vres=callback2(*link_closure,vpath,vtmp);
373 #define link_RES
375 #define chmod_ARGS (const char *path, mode_t mode)
376 #define chmod_CB vres=callback2(*chmod_closure,vpath,Val_int(mode));
377 #define chmod_RES
379 #define chown_ARGS (const char *path, uid_t uid, gid_t gid)
380 #define chown_CB vres=callback3(*chown_closure,vpath,Val_int(uid),Val_int(gid));
381 #define chown_RES
383 #define truncate_ARGS (const char *path, off_t size)
384 #define truncate_CB vres=callback2(*truncate_closure,vpath,copy_int64(size));
385 #define truncate_RES
387 #define utime_ARGS (const char *path, struct utimbuf *buf)
388 #define utime_CB vres=callback3(*utime_closure,vpath,copy_double(buf->actime),copy_double(buf->modtime));
389 #define utime_RES
391 #define open_ARGS (const char *path, struct fuse_file_info *fi)
392 #define open_CB vres=callback2(*open_closure,vpath,c_flags_to_open_flag_list(fi->flags));
393 #define open_RES if (Field(vres,0) != Val_int(0)) fi->fh = Int_val(Field(Field(vres,0),0));
395 #define opendir_ARGS (const char *path, struct fuse_file_info *fi)
396 #define opendir_CB vres=callback2(*opendir_closure,vpath,c_flags_to_open_flag_list(fi->flags));
397 #define opendir_RES if (Field(vres,0) != Val_int(0)) fi->fh = Int_val(Field(Field(vres,0),0));
399 #define read_ARGS (const char *path, char *buf, size_t size, off_t offset,struct fuse_file_info * fi)
400 #define read_CB \
401 vres=callback4(*read_closure,vpath,alloc_bigarray_dims(BIGARRAY_UINT8|BIGARRAY_C_LAYOUT,1,buf,size),copy_int64(offset),Val_int(fi->fh));
402 #define read_RES res=Int_val(Field(vres,0));
404 #define write_ARGS (const char *path, const char *buf, size_t size,off_t offset,struct fuse_file_info * fi) /* TODO: check usage of the writepages field of fi */
405 #define write_CB \
406 vres=callback4(*write_closure,vpath,alloc_bigarray_dims(BIGARRAY_UINT8|BIGARRAY_C_LAYOUT,1,(char *)buf,size),copy_int64(offset),Val_int(fi->fh));
407 #define write_RES res=Int_val(Field(vres,0));
409 #define release_ARGS (const char *path, struct fuse_file_info * fi)
410 #define release_CB vres=callback3(*release_closure,vpath,c_flags_to_open_flag_list(fi->flags),Val_int(fi->fh));
411 #define release_RES
413 #define releasedir_ARGS (const char *path, struct fuse_file_info * fi)
414 #define releasedir_CB vres=callback3(*releasedir_closure,vpath,c_flags_to_open_flag_list(fi->flags),Val_int(fi->fh));
415 #define releasedir_RES
417 #define flush_ARGS (const char *path,struct fuse_file_info * fi)
418 #define flush_CB vres=callback2(*flush_closure,vpath,Val_int(fi->fh));
419 #define flush_RES
421 #define statfs_ARGS (const char *path, struct statfs *stbuf)
422 #define statfs_CB vres=callback(*statfs_closure,vpath);
423 #define statfs_RES ml2c_Unix_struct_statfs(Field(vres,0),stbuf);
425 #define fsync_ARGS (const char *path, int isdatasync,struct fuse_file_info * fi)
426 #define fsync_CB vres=callback3(*fsync_closure,vpath,Val_bool(isdatasync),Val_int(fi->fh));
427 #define fsync_RES
429 #define fsyncdir_ARGS (const char *path, int isdatasync,struct fuse_file_info * fi)
430 #define fsyncdir_CB vres=callback3(*fsync_closure,vpath,Val_bool(isdatasync),Val_int(fi->fh));
431 #define fsyncdir_RES
433 #define setxattr_ARGS (const char *path, const char *name, const char *val,size_t size,int flags)
434 #define setxattr_CB \
435 vstring = alloc_string(size); \
436 memcpy(String_val(vstring),val,size); \
437 vres=callback4(*setxattr_closure,vpath,copy_string(name),vstring,c2ml_setxattr_flags(flags));
438 #define setxattr_RES
440 #define getxattr_ARGS (const char *path, const char *name, char *val,size_t size)
441 #define getxattr_CB \
442 vres=callback2(*getxattr_closure,vpath,copy_string(name));
443 #define getxattr_RES \
444 res=string_length(Field(vres,0)); \
445 if (size > 0) \
446 if (string_length(Field(vres,0))>=size) \
448 res = -UNKNOWN_ERR; \
450 else \
452 memcpy(val,String_val(Field(vres,0)),string_length(Field(vres,0))); \
455 #define listxattr_ARGS (const char *path, char *list, size_t size)
456 #define listxattr_CB vres=callback(*listxattr_closure,vpath);
457 #define listxattr_RES \
458 vtmp=Field(Field(vres,0),0);\
459 int len; \
460 char * dest=list; \
461 int rem=size; \
462 if (size==0) \
464 res = Int_val(Field(Field(vres,0),1)); \
466 else \
468 while (Is_block(vtmp)) \
470 len = string_length(Field(vtmp,0))+1; \
471 if (rem>=len) \
473 memcpy(dest,String_val(Field(vtmp,0)),len); \
474 vtmp=Field(vtmp,1);\
475 dest+=len; \
476 rem=rem-len; \
478 else \
480 res = -ERANGE; \
481 break; \
484 res=size-rem; \
487 #define removexattr_ARGS (const char *path, const char *name)
488 #define removexattr_CB vres=callback2(*removexattr_closure,vpath,copy_string(name));
489 #define removexattr_RES
491 #define CALLBACK(OPNAME) \
492 static int ops_##OPNAME OPNAME##_ARGS \
494 leave_blocking_section(); \
495 value vstring; \
496 value vpath; \
497 value vres; \
498 value vtmp; \
499 int res=-1; \
500 vpath = copy_string(path); \
501 OPNAME##_CB \
502 if (Tag_val(vres)==1) /* Result is not Bad */ \
504 res=0; \
505 OPNAME##_RES /* res can be changed here */ \
507 else \
509 if (Is_block(Field(vres,0))) /* This is EUNKNOWNERR of int in ocaml */ \
510 res=-Int_val(Field(Field(vres,0),0)); \
511 else res=-ml2c_unix_error(Int_val(Field(vres,0))); \
513 enter_blocking_section(); \
514 return res; \
517 FOR_ALL_OPS(CALLBACK)
519 #define SET_OPERATION(OPNAME) \
520 if (op->OPNAME==NULL) ops.OPNAME=NULL; \
521 else \
523 OPNAME##_closure=caml_named_value(op->OPNAME); \
524 ops.OPNAME=ops_##OPNAME; \
527 void set_fuse_operations(struct fuse_operation_names const *op)
529 FOR_ALL_OPS(SET_OPERATION)
532 struct fuse_operations * get_fuse_operations()
534 return &ops;
537 value * ocaml_fuse_loop_closure;
539 int mainloop(struct fuse * f,int multithreaded)
541 if (f == NULL)
542 return(-1);
544 value _fuse=alloc_small(1, Abstract_tag);
545 Field(_fuse, 0) = (value) f;
547 return callback2(*ocaml_fuse_loop_closure,_fuse,Val_bool(multithreaded));
550 void ml_fuse_init()
552 c2ml_unix_error_vect = invert_array(ml2c_unix_error_vect,&ml2c_unix_error_vect_dim,&c2ml_unix_error_vect_dim); /* TODO: this sucks together with the one at the beginning of file */
555 void ml_fuse_main(int argc,str * argv,struct fuse_operations const * op)
557 ocaml_fuse_loop_closure = caml_named_value("ocaml_fuse_loop");
558 ocaml_list_length = caml_named_value("ocaml_list_length");
560 char * mountpoint;
561 int multithreaded;
562 int fd;
564 struct fuse * fuse = fuse_setup(argc,argv,op,sizeof(struct fuse_operations),&mountpoint,&multithreaded,&fd);
566 if (fuse!=NULL)
568 mainloop(fuse,multithreaded);
569 fuse_teardown(fuse,fd,mountpoint);
573 value ocaml_fuse_is_null(value v) /* For Com.opaque values */
575 return Val_bool(0==Field(v,0)); // Is this the right way to check for null?