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
23 applejack@users.sf.net
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>
45 #include <sys/types.h>
50 #define FUSE_USE_VERSION 22
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
)
70 if (flags
==XATTR_CREATE
)
74 else if (flags
==XATTR_REPLACE
)
83 /* This part shamelessly copied from mlfuse */
84 #define ADDFLAGT(T, X) \
87 tmp = alloc_small (2, 0);\
88 Field (tmp, 0) = Val_int (num_ml_constr);\
89 Field (tmp, 1) = res;\
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
) {
98 CAMLlocal2 (res
, tmp
);
99 int num_ml_constr
= 8;
107 ADDFLAG (O_NONBLOCK
);
109 ADDBASEFLAG (O_RDWR
);
110 ADDBASEFLAG (O_WRONLY
);
111 ADDBASEFLAG (O_RDONLY
);
117 static int ml2c_unix_error_vect
[] =
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 */
208 while (src
[srcdim
]!=0)
210 if (src
[srcdim
]>=dim
) dim
=src
[srcdim
]+1;
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
;
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
[] =
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) \
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));
341 #define mkdir_ARGS (const char *path, mode_t mode)
342 #define mkdir_CB vres=callback2(*mkdir_closure,vpath,Val_int(mode));
345 #define unlink_ARGS (const char *path)
346 #define unlink_CB vres=callback(*unlink_closure,vpath);
349 #define rmdir_ARGS (const char *path)
350 #define rmdir_CB vres=callback(*rmdir_closure,vpath);
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)
359 vtmp = copy_string(dest); \
360 vres=callback2(*symlink_closure,vpath,vtmp);
363 #define rename_ARGS (const char *path, const char *dest)
365 vtmp = copy_string(dest); \
366 vres=callback2(*rename_closure,vpath,vtmp);
369 #define link_ARGS (const char *path, const char *dest)
371 vtmp = copy_string(dest); \
372 vres=callback2(*link_closure,vpath,vtmp);
375 #define chmod_ARGS (const char *path, mode_t mode)
376 #define chmod_CB vres=callback2(*chmod_closure,vpath,Val_int(mode));
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));
383 #define truncate_ARGS (const char *path, off_t size)
384 #define truncate_CB vres=callback2(*truncate_closure,vpath,copy_int64(size));
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));
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)
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 */
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));
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));
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));
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));
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));
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)); \
446 if (string_length(Field(vres,0))>=size) \
448 res = -UNKNOWN_ERR; \
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);\
464 res = Int_val(Field(Field(vres,0),1)); \
468 while (Is_block(vtmp)) \
470 len = string_length(Field(vtmp,0))+1; \
473 memcpy(dest,String_val(Field(vtmp,0)),len); \
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(); \
500 vpath = copy_string(path); \
502 if (Tag_val(vres)==1) /* Result is not Bad */ \
505 OPNAME##_RES /* res can be changed here */ \
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(); \
517 FOR_ALL_OPS(CALLBACK
)
519 #define SET_OPERATION(OPNAME) \
520 if (op->OPNAME==NULL) ops.OPNAME=NULL; \
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()
537 value
* ocaml_fuse_loop_closure
;
539 int mainloop(struct fuse
* f
,int multithreaded
)
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
));
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");
564 struct fuse
* fuse
= fuse_setup(argc
,argv
,op
,sizeof(struct fuse_operations
),&mountpoint
,&multithreaded
,&fd
);
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?