/* $Id$ * client FUSE interface for libcaptive * Copyright (C) 2005 Jan Kratochvil * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; exactly version 2 of June 1991 is required * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "main.h" /* self */ #include "op_statfs.h" #include "op_fsync.h" #include "op_fsyncdir.h" #include "op_opendir.h" #include "op_readdir.h" #include "op_releasedir.h" #include "op_open.h" #include "op_read.h" #include "op_release.h" #include "op_getattr.h" #include "op_mknod.h" #include "op_unlink.h" #include "op_mkdir.h" #include "op_rmdir.h" #include "op_chmod.h" #include "op_truncate.h" #include "op_write.h" #include "op_rename.h" #include "op_utime.h" /* Config: */ /* FIXME: Dupe with libcaptive/client/options.c */ #define DEFAULT_SYSLOG_FACILITY LOG_DAEMON /* Each element must be preceded by a comma (',')! */ #define LIBFUSE_ADDONS ",default_permissions,allow_other,kernel_cache" CaptiveVfsObject *capfuse_captive_vfs_object; static const struct poptOption popt_table[]={ CAPTIVE_POPT_INCLUDE, POPT_AUTOHELP POPT_TABLEEND }; static const struct fuse_operations capfuse_operations={ statfs: op_statfs, fsync: op_fsync, fsyncdir: op_fsyncdir, opendir: op_opendir, readdir: op_readdir, releasedir: op_releasedir, open: op_open, read: op_read, release: op_release, getattr: op_getattr, mknod: op_mknod, unlink: op_unlink, mkdir: op_mkdir, rmdir: op_rmdir, chmod: op_chmod, truncate: op_truncate, write: op_write, rename: op_rename, utime: op_utime, }; /* argv[0] expected as the program name. */ /* Only options and mountpoint expected here. */ static void capfuse_run(int argc,const char **argv) { char *capfuse_mountpoint; int capfuse_multithreaded,capfuse_fd; struct fuse *capfuse_fuse; if (!(capfuse_fuse=fuse_setup( argc, /* argc */ (/*de-const; broken fuset_setup()*/char **)argv, /* argv */ &capfuse_operations, /* op */ sizeof(capfuse_operations), /* op_size */ &capfuse_mountpoint, /* mountpoint */ &capfuse_multithreaded, /* multithreaded */ &capfuse_fd))) /* fd */ g_error(_( "FUSE fuse_setup() failed!\n" "You may need Linux kernel 2.6.14 or higher with its 'fuse.ko' enabled.\n" "You may also need to install or upgrade 'fuse' package to your system.")); if (fuse_loop(capfuse_fuse)) { /* Do not: g_error(_("FUSE fuse_loop() error")); * as it is caused on each umount(8). * FIXME: Why? */ } fuse_teardown(capfuse_fuse,capfuse_fd,capfuse_mountpoint); } int main(int argc,char **argv) { poptContext context=NULL; /* Valid if: opt_o; '= NULL' to shut up GCC. */ int rest_argc; const char **rest_argv,**csp; struct captive_options options; int capfuse_argc; const char **capfuse_argv; const char *program_name=argv[0]; const char *sandbox_server_argv0=G_STRINGIFY(LIBEXECDIR) "/captive-sandbox-server"; const char *image_filename=NULL; gboolean opt_n=FALSE; gboolean opt_v=FALSE; const char *opt_o=NULL; const char *mountpoint=NULL; char **argv_sp; int opt_o_argc; /* Valid if: opt_o */ const char **opt_o_argv=NULL; /* Valid if: opt_o */ #if 0 { char **sp; for (sp=argv;*sp;sp++) fprintf(stderr,"argv: \"%s\"\n",*sp); } #endif /* Do not set g_log_set_always_fatal() here as we would not be able * to restart failed children due to communication-failure alarms. */ captive_standalone_init(); /* poptGetNextOpt() below requires valid: captive_options */ captive_options_init(&options); captive_options=&options; /* for parsing by 'CAPTIVE_POPT_INCLUDE' */ argv_sp=argv+1; if (*argv_sp && (*argv_sp)[0]!='-') image_filename=*argv_sp++; if (*argv_sp && (*argv_sp)[0]!='-') mountpoint=*argv_sp++; if (*argv_sp && !strcmp(*argv_sp,"-n")) { opt_n=TRUE; argv_sp++; } if (*argv_sp && !strcmp(*argv_sp,"-v")) { opt_v=TRUE; argv_sp++; } if (argv_sp[0] && argv_sp[1] && !strcmp(argv_sp[0],"-o")) { argv_sp++; opt_o=*argv_sp++; } if (*argv_sp) { /* Lethal path but still give chance for "--help" etc. */ /* Do not: POPT_CONTEXT_POSIXMEHARDER * as mount(8) puts there first un-pre-dashed "ro"/"rw" etc. */ /* poptGetNextOpt() requires valid: captive_options */ if ((context=poptGetContext( PACKAGE, /* name */ argc,(/*en-const*/const char **)argv, /* argc,argv */ popt_table, /* options */ 0))) /* flags; && !POPT_CONTEXT_KEEP_FIRST */ while (0<=poptGetNextOpt(context)); g_error(_("Excessive argument: %s"),*argv_sp); } if (opt_o) { char *opt_o_spaced=(char */* de-const; length is not changed */)captive_strdup_alloca(opt_o); char *s,quote; quote=0; for (s=opt_o_spaced;*s;s++) { /* Be compatible with: poptParseArgvString() * which also does not differentiate '"' and "'" */ if (*s=='\\' && s[1]) { s++; continue; } if (quote) { if (*s==quote) quote=0; continue; } switch (*s) { case '"': case '\'': quote=*s; continue; case ',': *s=' '; continue; } } if (poptParseArgvString(opt_o_spaced,&opt_o_argc,&opt_o_argv)) g_error(_("Error splitting arguments of the space-reparsed '-o': %s"),opt_o_spaced); /* Adjust the options for all the subsystems eating the same string vector. */ for (csp=opt_o_argv;*csp;csp++) { /* Unsupported mount(8) options not valid for fusermount(8) and causing: * fusermount: mount failed: Invalid argument */ if (0 || !strcmp(*csp,"defaults") || !strcmp(*csp, "auto") || !strcmp(*csp,"noauto") || !strcmp(*csp,"user")) { memmove(csp,csp+1,(opt_o_argv+opt_o_argc+1-(csp+1))*sizeof(*csp)); opt_o_argc--; csp--; continue; } /* Pre-dash "ro"/"rw" to let libcaptive to be aware of the mode. * We will put the final "ro"/"rw" there again from 'captive_options.rwmode' later. */ if (!strcmp(*csp,"ro")) *csp="--ro"; if (!strcmp(*csp,"rw")) *csp="--rw"; /* LUFS "uid" and "gid" options should map to FUSE well, I hope. */ #define STRNCMP_CONST(mem,string) strncmp((mem),(string),strlen((string))) if (0 || !STRNCMP_CONST(*csp,"fmask=") || !STRNCMP_CONST(*csp,"dmask=") || !STRNCMP_CONST(*csp,"channels=") || !STRNCMP_CONST(*csp,"root=") || !strcmp(*csp,"own_fs") || !strcmp(*csp,"quiet") || !STRNCMP_CONST(*csp,"dir_cache_ttl=") ) g_error(_("Seen obsolete LUFS option \"%s\" - please update your \"/etc/fstab\" for FUSE options instead"), *csp); #undef STRNCMP_CONST } } g_assert(!options.sandbox_server_argv); g_assert(!options.sandbox_server_ior); /* captive_options_free(&options) will: g_free(options.sandbox_server_argv); */ /* Allocation is so terrible to be compatible with: captive_options_copy() */ options.sandbox_server_argv=g_malloc(2*sizeof(*options.sandbox_server_argv)+strlen(sandbox_server_argv0)+1); options.sandbox_server_argv[0]=(char *)(options.sandbox_server_argv+2); options.sandbox_server_argv[1]=NULL; strcpy(options.sandbox_server_argv[0],sandbox_server_argv0); options.sandbox=TRUE; g_assert(!options.bug_pathname); options.bug_pathname=g_strdup(G_STRINGIFY(VARLIBCAPTIVEDIR) "/bug-%FT%T.captivebug.xml.gz"); options.syslog_facility=DEFAULT_SYSLOG_FACILITY; if (opt_o) { /* Do not: POPT_CONTEXT_POSIXMEHARDER * as mount(8) puts there first un-pre-dashed "ro"/"rw" etc. */ if (!(context=poptGetContext( PACKAGE, /* name */ opt_o_argc,opt_o_argv, /* argc,argv */ popt_table, /* options */ POPT_CONTEXT_KEEP_FIRST))) /* flags */ g_error(_("Error parsing command-line arguments from pre-parsed '-o': %s"),opt_o); if (poptReadDefaultConfig(context, TRUE)) /* useEnv */ g_warning(_("Error reading default popt configuration")); while (0<=poptGetNextOpt(context)); rest_argv=poptGetArgs(context); } else rest_argv=NULL; rest_argc=0; for (csp=rest_argv;csp && *csp;csp++) rest_argc++; if (!image_filename || !mountpoint) g_error(_("File/device disk image pathname and mountpoint command-line arguments required")); /* image_iochannel */ g_assert(options.image_iochannel==NULL); if (!(options.image_iochannel=g_io_channel_new_file( image_filename, /* filename */ (options.rwmode==CAPTIVE_OPTION_RWMODE_RW ? "r+" : "r"), /* mode */ NULL))) { /* error */ g_error(_("image_iochannel failed open of: %s"),image_filename); return EXIT_FAILURE; } if (!options.captivemodid) options.captivemodid=captive_captivemodid_load_default(FALSE); if (options.filesystem.type==CAPTIVE_OPTIONS_MODULE_TYPE_EMPTY) { const char *self_prefix="mount.captive-"; size_t self_prefix_len=strlen(self_prefix); const char *fsname; if ((fsname=strrchr(program_name,'/'))) fsname++; else fsname=program_name; if (strncmp(fsname,self_prefix,self_prefix_len)) g_error(_("Cannot detect default filesystem name from my basename: %s"),fsname); fsname+=self_prefix_len; if (!captive_options_module_load(&options.filesystem, captive_printf_alloca("%s/%s.sys",G_STRINGIFY(VARLIBCAPTIVEDIR),fsname))) g_error(_("'--filesystem' option requires valid pathname ('ntfs.sys' suggested)")); g_assert(options.filesystem.type!=CAPTIVE_OPTIONS_MODULE_TYPE_EMPTY); } if (!options.load_module) { struct captive_options_module *options_module; captive_new(options_module); if (!captive_options_module_load(options_module,G_STRINGIFY(VARLIBCAPTIVEDIR) "/ntoskrnl.exe")) g_error(_("'--load-module' option requires valid pathname ('ntoskrnl.exe' suggested)")); options.load_module=g_list_append(options.load_module,options_module); } /* It is still required for: captive_options_module_load() */ captive_options=NULL; /* already parsed by 'CAPTIVE_POPT_INCLUDE' */ if (GNOME_VFS_OK!=captive_vfs_new(&capfuse_captive_vfs_object,&options)) { g_error(_("captive_vfs_new() failed")); return EXIT_FAILURE; } captive_options_free(&options); /* Simulate argv[0] there as it got cut by popt. */ capfuse_argc=4+2*rest_argc; captive_newn_alloca(capfuse_argv,capfuse_argc+1); csp=capfuse_argv; *csp++=argv[0]; *csp++=mountpoint; *csp++="-o"; *csp++=captive_printf_alloca("fsname=%s" LIBFUSE_ADDONS ",%s", image_filename,(options.rwmode==CAPTIVE_OPTION_RWMODE_RO ? "ro" : "rw")); if (rest_argv) while (*rest_argv) { *csp++="-o"; *csp++=*rest_argv++; } *csp=NULL; #if 0 { const char **csp; for (csp=capfuse_argv;*csp;csp++) fprintf(stderr,"capfuse_argv: \"%s\"\n",*csp); } #endif /* FIXFUSE: fuse_main()/fuse_main_real() would be enough for Captive fuse but * the public interface of fuse_main() is too broken. */ capfuse_run(capfuse_argc,capfuse_argv); /* 'rest_argv' gets cleared by 'poptFreeContext(context);' below */ if (opt_o) poptFreeContext(context); if (capfuse_captive_vfs_object) { g_object_unref(capfuse_captive_vfs_object); capfuse_captive_vfs_object=NULL; } return EXIT_SUCCESS; }