1 /*===========================================================================
2  *
3  *                            PUBLIC DOMAIN NOTICE
4  *               National Center for Biotechnology Information
5  *
6  *  This software/database is a "United States Government Work" under the
7  *  terms of the United States Copyright Act.  It was written as part of
8  *  the author's official duties as a United States Government employee and
9  *  thus cannot be copyrighted.  This software/database is freely available
10  *  to the public for use. The National Library of Medicine and the U.S.
11  *  Government have not placed any restriction on its use or reproduction.
12  *
13  *  Although all reasonable efforts have been taken to ensure the accuracy
14  *  and reliability of the software and data, the NLM and the U.S.
15  *  Government do not and cannot warrant the performance or results that
16  *  may be obtained by using this software or data. The NLM and the U.S.
17  *  Government disclaim all warranties, express or implied, including
18  *  warranties of performance, merchantability or fitness for any particular
19  *  purpose.
20  *
21  *  Please cite the author in any work or product based on this material.
22  *
23  * ===========================================================================
24  *
25  */
26 #include <kapp/main.h>
27 #include <kapp/args.h>
28 #include <klib/log.h>
29 #include <klib/out.h>
30 #include <klib/status.h>
31 #include <kfs/directory.h>
32 
33 #define FUSE_USE_VERSION 25
34 #include <fuse.h>
35 
36 #include "xml.h"
37 #include "sra-fuser.h"
38 #include "log.h"
39 
40 #include <atomic.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <sys/types.h>
47 #include <unistd.h>
48 
49 static struct stat g_mount_point_stat;
50 static struct stat g_dflt_file_stat;
51 static atomic64_t num_open_files;
52 
53 static
ConvertRC2errno(rc_t rc)54 int ConvertRC2errno(rc_t rc)
55 {
56     switch(GetRCState(rc)) {
57         case rcNoErr:
58             return 0;
59         case rcNotFound:
60             return ENOENT;
61         case rcNull:
62             return EFAULT;
63         case rcInvalid:
64             return EINVAL;
65         case rcInsufficient:
66             return ENAMETOOLONG;
67         case rcReadonly:
68             return EROFS;
69         case rcUnauthorized:
70             return EACCES;
71         case rcCorrupt:
72         default:
73             return EBADF;
74     }
75 }
76 
UX_FUSE_init(void)77 void* UX_FUSE_init(void)
78 {
79     atomic64_set(&num_open_files, 0);
80     SRA_FUSER_Init();
81     return NULL;
82 }
83 
UX_FUSE_destroy(void * x)84 void UX_FUSE_destroy(void* x)
85 {
86     uint64_t q = atomic64_read(&num_open_files);
87     if( q > 0 ) {
88         PLOGMSG(klogInfo, (klogInfo, "$(q) files still opened", PLOG_U64(q), q));
89     }
90     SRA_FUSER_Fini();
91 }
92 
93 struct UX_FUSE_readdir_callback_data {
94     const char *path;
95     void *buf;
96     fuse_fill_dir_t filler;
97 };
98 
99 static
UX_FUSE_readdir_callback(const char * name,void * data)100 rc_t CC UX_FUSE_readdir_callback( const char *name, void *data )
101 {
102     struct UX_FUSE_readdir_callback_data* d = (struct UX_FUSE_readdir_callback_data*)data;
103     int r = d->filler(d->buf, name, NULL, 0);
104     DEBUG_MSG(10, ("%s %s entry: '%s'\n", __func__, d->path, name));
105     return r != 0 ? RC(rcExe, rcDirectory, rcReading, rcBuffer, rcInsufficient) : 0;
106 }
107 
UX_FUSE_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi)108 int UX_FUSE_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
109                      off_t offset, struct fuse_file_info *fi)
110 {
111     rc_t rc = 0;
112     struct UX_FUSE_readdir_callback_data data;
113 
114     DEBUG_MSG(8, ("%s: %s\n", __func__, path));
115 
116     data.path = path;
117     data.filler = filler;
118     data.buf = buf;
119 
120     if( (rc = UX_FUSE_readdir_callback(".", &data)) == 0 &&
121         (rc = UX_FUSE_readdir_callback("..", &data)) == 0 ) {
122         rc = SRA_FUSER_GetDir(path, UX_FUSE_readdir_callback, &data);
123     }
124     if( rc != 0 ) {
125         errno = ConvertRC2errno(rc);
126         PLOGERR(klogErr, (klogErr, rc, "$(f): $(p) - $(e)", PLOG_3(PLOG_S(f),PLOG_S(p),PLOG_S(e)), __func__, path, strerror(errno)));
127         return -errno;
128     }
129     return 0;
130 }
131 
UX_FUSE_getattr(const char * path,struct stat * stbuf)132 int UX_FUSE_getattr(const char *path, struct stat *stbuf)
133 {
134     rc_t rc = 0;
135 
136     DEBUG_MSG(8, ("%s: %s\n", __func__, path));
137     if( stbuf == NULL) {
138         rc = RC(rcExe, rcFileDesc, rcClassifying, rcParam, rcNull);
139     } else if( strcmp(path, "/") == 0 ) {
140         /* root is known as mount point */
141         memmove(stbuf, &g_mount_point_stat, sizeof(g_mount_point_stat));
142     } else {
143         uint32_t type = kptBadPath, access = 0;
144         KTime_t ts = 0;
145         uint64_t file_sz = 0, block_sz = 0;
146         if( (rc = SRA_FUSER_GetAttr(path, &type, &ts, &file_sz, &access, &block_sz)) == 0 ) {
147             bool symlink = (type & kptAlias);
148             if( symlink ) {
149                 type = type & ~kptAlias;
150             }
151             if( type == kptDir ) {
152                 memmove(stbuf, &g_mount_point_stat, sizeof(g_mount_point_stat));
153                 stbuf->st_mode = S_IFDIR | (0007555 & (access == 0 ? stbuf->st_mode : access));
154             } else {
155                 memmove(stbuf, &g_dflt_file_stat, sizeof(g_dflt_file_stat));
156                 if( access == 0 ) {
157                     access = stbuf->st_mode;
158                 }
159                 stbuf->st_mode = 0007555 & (access == 0 ? stbuf->st_mode : access);
160                 if( type == kptFile ) {
161                     stbuf->st_mode |= S_IFREG;
162                 } else if( type == kptCharDev ) {
163                     stbuf->st_mode |= S_IFCHR;
164                 } else if( type == kptBlockDev ) {
165                     stbuf->st_mode |= S_IFBLK;
166                 } else if( type == kptFIFO ) {
167                     stbuf->st_mode |= S_IFIFO;
168                 } else {
169                     rc = RC(rcExe, rcFileDesc, rcClassifying, rcDirEntry, rcUnknown);
170                 }
171             }
172             if( rc == 0 ) {
173                 if( symlink ) {
174                     stbuf->st_mode = S_IFLNK | (stbuf->st_mode & 07777);
175                 }
176                 stbuf->st_size = file_sz;
177                 if( ts != 0 ) {
178                     stbuf->st_mtime = stbuf->st_atime = stbuf->st_ctime = ts;
179                 }
180                 if( block_sz > 0 ) {
181                     stbuf->st_blksize = block_sz;
182                 }
183                 DEBUG_MSG(8, ("%s: %s type: %s %lu bytes\n", __func__, path,
184                     (S_ISDIR(stbuf->st_mode) ? "dir" : (S_ISLNK(stbuf->st_mode) ? " symlink" : "file")), stbuf->st_size));
185             }
186         }
187     }
188     if( rc != 0 ) {
189         errno = ConvertRC2errno(rc);
190         PLOGERR(klogErr, (klogErr, rc, "$(f): $(p) - $(e)", PLOG_3(PLOG_S(f),PLOG_S(p),PLOG_S(e)), __func__, path, strerror(errno)));
191         return -errno;
192     }
193     return 0;
194 }
195 
UX_FUSE_readlink(const char * path,char * buf,size_t buf_sz)196 int UX_FUSE_readlink(const char *path, char *buf, size_t buf_sz)
197 {
198     rc_t rc = 0;
199 
200     DEBUG_MSG(8, ("%s: %s\n", __func__, path));
201     if( buf == NULL ) {
202         rc = RC(rcExe, rcFile, rcAliasing, rcParam, rcNull);
203     } else if( buf_sz < 1 ) {
204         rc = RC(rcExe, rcFile, rcAliasing, rcParam, rcInvalid);
205     } else {
206         rc = SRA_FUSER_ResolveLink(path, buf, buf_sz);
207     }
208     if( rc != 0 ) {
209         errno = ConvertRC2errno(rc);
210         PLOGERR(klogErr, (klogErr, rc, "$(f): $(p) - $(e)", PLOG_3(PLOG_S(f),PLOG_S(p),PLOG_S(e)), __func__, path, strerror(errno)));
211         return -errno;
212     }
213     return 0;
214 }
215 
UX_FUSE_open(const char * path,struct fuse_file_info * fi)216 int UX_FUSE_open(const char *path, struct fuse_file_info* fi)
217 {
218     rc_t rc = 0;
219     uint64_t q;
220 
221     DEBUG_MSG(8, ("%s: %s\n", __func__, path));
222     if( fi == NULL) {
223         rc = RC(rcExe, rcFile, rcOpening, rcParam, rcNull);
224     } else if( fi->flags & (O_CREAT | O_EXCL | O_TRUNC | O_APPEND)) {
225         rc = RC(rcExe, rcFile, rcOpening, rcDirEntry, rcReadonly);
226     } else {
227         const void* data = NULL;
228         if( (rc = SRA_FUSER_OpenNode(path, &data)) == 0 ) {
229             fi->fh = (uint64_t)data;
230         }
231     }
232     if( rc != 0 ) {
233         errno = ConvertRC2errno(rc);
234         PLOGERR(klogErr, (klogErr, rc, "$(f): $(p) - $(e)", PLOG_3(PLOG_S(f),PLOG_S(p),PLOG_S(e)), __func__, path, strerror(errno)));
235         return -errno;
236     }
237     q = atomic64_add_and_read(&num_open_files, 1);
238     PLOGMSG(klogInfo, (klogInfo, "opened $(n), total open $(q)", PLOG_2(PLOG_S(n),PLOG_U64(q)), path, q));
239     return 0;
240 }
241 
UX_FUSE_read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)242 int UX_FUSE_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
243 {
244     rc_t rc = 0;
245     const void* data = NULL;
246     size_t num_read = 0;
247 
248     DEBUG_MSG(8, ("%s: %s from %lu %lu bytes\n", __func__, path, offset, size));
249     if( fi == NULL || buf == NULL ) {
250         rc = RC(rcExe, rcFile, rcReading, rcParam, rcNull);
251     } else if( (data = (const void*)fi->fh) == NULL ) {
252         rc = RC(rcExe, rcFile, rcReading, rcParam, rcCorrupt);
253     } else {
254         rc = SRA_FUSER_ReadNode(path, data, buf, size, offset, &num_read);
255     }
256     if( rc != 0 ) {
257         errno = ConvertRC2errno(rc);
258         PLOGERR(klogErr, (klogErr, rc, "$(f): $(p) - $(e)", PLOG_3(PLOG_S(f),PLOG_S(p),PLOG_S(e)), __func__, path, strerror(errno)));
259         return -errno;
260     }
261     return num_read;
262 }
263 
UX_FUSE_release(const char * path,struct fuse_file_info * fi)264 int UX_FUSE_release(const char *path, struct fuse_file_info *fi)
265 {
266     rc_t rc = 0;
267     const void* data = NULL;
268     uint64_t q;
269 
270     DEBUG_MSG(8, ("%s: %s\n", __func__, path));
271     if( fi == NULL) {
272         rc = RC(rcExe, rcFile, rcReleasing, rcParam, rcNull);
273     } else if( (data = (const void*)fi->fh) == NULL ) {
274         rc = RC(rcExe, rcFile, rcReading, rcParam, rcCorrupt);
275     } else {
276         rc = SRA_FUSER_CloseNode(path, data);
277     }
278     fi->fh = 0;
279     if( rc != 0 ) {
280         errno = ConvertRC2errno(rc);
281         PLOGERR(klogErr, (klogErr, rc, "$(f): $(p) - $(e)", PLOG_3(PLOG_S(f),PLOG_S(p),PLOG_S(e)), __func__, path, strerror(errno)));
282         return -errno;
283     }
284     atomic64_dec(&num_open_files);
285     q = atomic64_read(&num_open_files);
286     PLOGMSG(klogInfo, (klogInfo, "closed $(n), total open $(q)", PLOG_2(PLOG_S(n),PLOG_U64(q)), path, q));
287     return 0;
288 }
289 
UX_FUSE_mknod(const char * path,mode_t m,dev_t d)290 int UX_FUSE_mknod(const char *path, mode_t m, dev_t d)
291 {
292     return -EROFS;
293 }
294 
UX_FUSE_mkdir(const char * path,mode_t m)295 int UX_FUSE_mkdir(const char *path, mode_t m)
296 {
297     return -EROFS;
298 }
299 
UX_FUSE_unlink(const char * path)300 int UX_FUSE_unlink(const char *path)
301 {
302     return -EROFS;
303 }
304 
UX_FUSE_rmdir(const char * path)305 int UX_FUSE_rmdir(const char *path)
306 {
307     return -EROFS;
308 }
309 
UX_FUSE_symlink(const char * path,const char * x)310 int UX_FUSE_symlink(const char *path, const char *x)
311 {
312     return -EROFS;
313 }
314 
UX_FUSE_rename(const char * path,const char * x)315 int UX_FUSE_rename(const char *path, const char *x)
316 {
317     return -EROFS;
318 }
319 
UX_FUSE_link(const char * path,const char * x)320 int UX_FUSE_link(const char *path, const char *x)
321 {
322     return -EROFS;
323 }
324 
UX_FUSE_chmod(const char * path,mode_t m)325 int UX_FUSE_chmod(const char *path, mode_t m)
326 {
327     return -EROFS;
328 }
329 
UX_FUSE_chown(const char * path,uid_t u,gid_t g)330 int UX_FUSE_chown(const char *path, uid_t u, gid_t g)
331 {
332     return -EROFS;
333 }
334 
UX_FUSE_truncate(const char * path,off_t o)335 int UX_FUSE_truncate(const char *path, off_t o)
336 {
337     return -EROFS;
338 }
339 
UX_FUSE_utime(const char * path,struct utimbuf * b)340 int UX_FUSE_utime(const char *path, struct utimbuf *b)
341 {
342     return -EROFS;
343 }
344 
UX_FUSE_write(const char * path,const char * b,size_t s,off_t o,struct fuse_file_info * fi)345 int UX_FUSE_write(const char *path, const char *b, size_t s, off_t o, struct fuse_file_info *fi)
346 {
347     return -EROFS;
348 }
349 
UX_FUSE_flush(const char * path,struct fuse_file_info * fi)350 int UX_FUSE_flush(const char *path, struct fuse_file_info *fi)
351 {
352     return 0;
353 }
354 
UX_FUSE_create(const char * path,mode_t m,struct fuse_file_info * fi)355 int UX_FUSE_create(const char *path, mode_t m, struct fuse_file_info *fi)
356 {
357     return -EROFS;
358 }
359 
UX_FUSE_ftruncate(const char * path,off_t o,struct fuse_file_info * fi)360 int UX_FUSE_ftruncate(const char *path, off_t o, struct fuse_file_info *fi)
361 {
362     return -EROFS;
363 }
364 
365 static
CoreUsage(int fd,const char * progName,bool showHelp,bool showVersion,bool fail,bool forceShowHelp)366 void CoreUsage(int fd, const char *progName, bool showHelp, bool showVersion, bool fail, bool forceShowHelp)
367 {
368     /* used only for FUSE built-in help and version printing */
369     struct fuse_operations ops;
370     struct fuse_args args;
371     memset(&args, 0, sizeof(struct fuse_args));
372     fuse_opt_add_arg(&args, progName); /* fake mount point */
373 
374 
375     if( fd != STDOUT_FILENO ) {
376         /* redirect usage to log file if it was specified */
377         dup2(fd, STDOUT_FILENO);
378     }
379     if( showHelp ) {
380         const char* p = strrchr(progName, '/');
381         if( p++ == NULL ) {
382             p = progName;
383         }
384         UsageSummary(p);
385         if( !fail || forceShowHelp ) {
386             fuse_opt_add_arg(&args, "-ho");
387             KOutMsg("\n"
388                 "    -x|--xml-dir <path>                XML file with virtual directory structure\n"
389                 "    -m|--mount-point <path>            path to a mount directory \n"
390                 "    -u|--unmount                       Unmount only and exit (only -m required)\n"
391                 );
392             KOutMsg("\nOptions:\n"
393                 "    -c|--xml-check <secs>              Check XML for update every <arg> seconds,\n"
394                 "                                       default: 0 - never.\n"
395                 "    -r|--xml-root  <path>              Base directory for a 'path' attributes in XML.\n"
396                 "                                       default: '.'\n"
397                 );
398             KOutMsg(
399                 "    -i|--xml-validate <nocheck|ignore> XML validation on load:\n"
400                 "                                       nocheck - do not check presence of dir/file in path attribute;\n"
401                 "                                       ignore - only report missing dir/file in path attribute;\n"
402                 "                                       default behaivour is to fail loading XML if dir/file is not found.\n"
403                 );
404             KOutMsg(
405                 "    --SRA-check <secs>                 Check SRA config and runs for update\n"
406                 "                                       every <arg> seconds, default: 0 - never.\n"
407                 "    --SRA-cache <path>                 Write SRA update info to a file.\n"
408                 "                                       Must have --SRA-check option value of non-zero.\n"
409                 );
410             KOutMsg(
411                 "    -L|--log-level                     Logging level as number or enum string. One\n"
412                 "                                       of (fatal|sys|int|err|warn|info) or (0-5)\n"
413                 "                                       Current/default is warn.\n"
414                 "    -l|--log-file <path>               Use log file specified by path.\n"
415                 "    -g|--log-reopen <secs>             Reopened log file every <arg> seconds\n"
416                 "                                       (external log rotation), default: 0 - never.\n"
417                 );
418             KOutMsg(
419     #if _DEBUGGING
420                 "    -+|--debug <Module[-Flag]>         Turn on debug output for module. All flags\n"
421                 "                                       if not specified.\n"
422     #endif
423                 "    -v|--verbose                       Increase the verbosity level of the program.\n"
424                 "                                       Use multiple times for more verbosity.\n"
425                 "    -V|--version                       Display the version of the program then quit.\n"
426                 "    -h|--help                          Output brief explantion for the program.\n"
427                 "\n"
428                 );
429         }
430     }
431     if( showVersion && !fail ) {
432         HelpVersion(progName, KAppVersion());
433         fuse_opt_add_arg(&args, "--version");
434     }
435     /* force help preceed fuse help */
436     fflush(stdout);
437     /* hack to force fuse lib to output to stdout */
438     dup2(fd, STDERR_FILENO);
439     if( !fail ) {
440         memset(&ops, 0, sizeof(struct fuse_operations));
441         fuse_main(args.argc, args.argv, &ops);
442     }
443     exit(fail ? rcArgv : 0);
444 }
445 
446 /*******************************************************************************
447  * KMain - defined for use with kapp library
448  *******************************************************************************/
KMain(int argc,char * argv[])449 rc_t CC KMain(int argc, char *argv[])
450 {
451     int i;
452     rc_t rc;
453 
454     bool missedArgs = argc < 2, showHelp = false, showVersion = false, unmount = false, foreground = false;
455     const char* mount_point = NULL, *xml_path = NULL, *log_file = NULL;
456     const char* sra_cache = NULL, *xml_root = ".";
457     char** fargs = (char**)calloc(argc, sizeof(char*));
458     uint32_t xml_sync = 0, log_sync = 0, sra_sync = 0;
459     EXMLValidate xml_validate = eXML_Full;
460     int log_fd = STDOUT_FILENO;
461 
462 #ifdef SRAFUSER_LOGLOCALTIME
463     KLogFmtFlagsSet(klogFmtLocalTimestamp);
464     KLogLibFmtFlagsSet(klogFmtLocalTimestamp);
465     KStsFmtFlagsSet(kstsFmtLocalTimestamp);
466     KStsLibFmtFlagsSet(kstsFmtLocalTimestamp);
467 #endif
468 
469     for(i = 1; i < argc; i++) {
470         if(!strcmp(argv[i], "-x") || !strcmp(argv[i], "--xml-dir")) {
471             xml_path = argv[++i];
472         } else if(!strcmp(argv[i], "-m") || !strcmp(argv[i], "--mount-point")) {
473             mount_point = argv[++i];
474         } else if(!strcmp(argv[i], "-xs") || !strcmp(argv[i], "-c") || !strcmp(argv[i], "--xml-check")) {
475             xml_sync = AsciiToU32(argv[++i], NULL, NULL);
476         } else if(!strcmp(argv[i], "-r") || !strcmp(argv[i], "--xml-root")) {
477             xml_root = argv[++i];
478         } else if(!strcmp(argv[i], "-i") || !strcmp(argv[i], "--xml-validate")) {
479             if( i++ == argc - 1 ) {
480                 rc = RC(rcExe, rcArgv, rcValidating, rcParam, rcInsufficient);
481                 LOGERR(klogErr, rc, "XML validation setting value");
482                 CoreUsage(log_fd, argv[0], true, false, true, false);
483             } else if( !strcmp(argv[i], "ignore") ) {
484                 xml_validate = eXML_NoFail;
485             } else if( !strcmp(argv[i], "nocheck") ) {
486                 xml_validate = eXML_NoCheck;
487             } else {
488                 rc = RC(rcExe, rcArgv, rcValidating, rcParam, rcUnrecognized);
489                 PLOGERR(klogErr, (klogErr, rc, "XML validation setting value '$(lvl)'", PLOG_S(lvl), argv[i]));
490                 CoreUsage(log_fd, argv[0], true, false, true, false);
491             }
492         } else if(!strcmp(argv[i], "-ds") || !strcmp(argv[i], "--SRA-check")) {
493             sra_sync = AsciiToU32(argv[++i], NULL, NULL);
494         } else if(!strcmp(argv[i], "-df") || !strcmp(argv[i], "--SRA-cache")) {
495             sra_cache = argv[++i];
496         } else if(!strcmp(argv[i], "-u") || !strcmp (argv[i], "--unmount")) {
497             unmount = true;
498         } else if(!strcmp(argv[i], "-L") || !strcmp (argv[i], "--log-level")) {
499             if( i == argc - 1 ) {
500                 rc = RC(rcExe, rcArgv, rcValidating, rcParam, rcInsufficient);
501                 LOGERR(klogErr, rc, "missing log level");
502                 CoreUsage(log_fd, argv[0], true, false, true, false);
503             } else if( (rc = LogLevelSet(argv[++i])) != 0 ) {
504                 PLOGERR(klogErr, (klogErr, rc, "log level $(lvl)", PLOG_S(lvl), argv[i]));
505                 CoreUsage(log_fd, argv[0], true, false, true, false);
506             }
507         } else if(!strcmp(argv[i], "-+") || !strcmp (argv[i], "--debug")) {
508 #if _DEBUGGING
509             if( i == argc - 1 ) {
510                 rc = RC(rcExe, rcArgv, rcValidating, rcParam, rcInsufficient);
511                 LOGERR(klogErr, rc, "missing debug level");
512                 CoreUsage(log_fd, argv[0], true, false, true, false);
513             } else if( (rc = KDbgSetString(argv[++i])) != 0 ) {
514                 PLOGERR(klogErr, (klogErr, rc, "debug level $(lvl)", PLOG_S(lvl), argv[i]));
515                 CoreUsage(log_fd, argv[0], true, false, true, false);
516             }
517 #else
518             i++;
519 #endif
520         } else if(!strcmp(argv[i], "-lf") || !strcmp(argv[i], "-l") || !strcmp (argv[i], "--log-file")) {
521             log_file = argv[++i];
522         } else if(!strcmp(argv[i], "-ls") || !strcmp(argv[i], "-g") || !strcmp(argv[i], "--log-reopen")) {
523             log_sync = AsciiToU32(argv[++i], NULL, NULL);
524         } else if(!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version")) {
525             showVersion = true;
526         } else if(!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) {
527             KStsLevel l = KStsLevelGet();
528             KStsLevelSet(++l);
529         } else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "-ho") || !strcmp(argv[i], "--help")) {
530             showHelp = true;
531         } else {
532             /* save arg for FUSE */
533             fargs[i] = argv[i];
534             if( !strcmp(argv[i], "-d") || !strcmp(argv[i], "-f") ||
535                 (!strcmp(argv[i], "-o") && (i > argc - 1) && !strcmp(argv[i + 1], "debug")) ) {
536                 foreground = true;
537             }
538         }
539     }
540     if( missedArgs ) {
541         CoreUsage(log_fd, argv[0], missedArgs, showVersion, true, true);
542     }
543     if( showHelp || showVersion ) {
544         CoreUsage(log_fd, argv[0], showHelp, showVersion, false, false);
545     }
546     if( (rc = LogFile_Init(log_file, log_sync, foreground, &log_fd)) != 0 ) {
547         LOGERR(klogErr, rc, log_file ? log_file : "no log");
548         CoreUsage(log_fd, argv[0], true, false, true, false);
549     }
550     if( mount_point == NULL ) {
551         LOGERR(klogErr, RC(rcExe, rcArgv, rcValidating, rcParam, rcInsufficient), "mountpoint");
552         CoreUsage(log_fd, argv[0], true, false, true, false);
553     }
554     if( unmount ) {
555         fuse_unmount(mount_point);
556         exit(0);
557     }
558     if( xml_path == NULL ) {
559         LOGERR(klogErr, RC(rcExe, rcArgv, rcValidating, rcParam, rcInsufficient), "virtual directory XML");
560         CoreUsage(log_fd, argv[0], true, false, true, false);
561     }
562     if( i != argc ) {
563         LOGERR(klogErr, RC(rcExe, rcArgv, rcValidating, rcParam, rcExcessive), argv[i]);
564         CoreUsage(log_fd, argv[0], true, false, true, false);
565     }
566     if( stat(mount_point, &g_mount_point_stat) < 0 ) {
567         PLOGMSG(klogErr, (klogErr, "$(p): $(e)", PLOG_2(PLOG_S(p),PLOG_S(e)), mount_point, strerror(errno)));
568         CoreUsage(log_fd, argv[0], true, false, true, false);
569     }
570     g_mount_point_stat.st_dev = 0;
571     g_mount_point_stat.st_ino = 0;
572     g_mount_point_stat.st_mode = S_IFDIR | 0555; /* execute read-only */
573      /* find needs more links to search in subdir */
574     g_mount_point_stat.st_nlink = 1024 * 1024 * 1024;
575 
576     if( stat(xml_path, &g_dflt_file_stat) < 0 ) {
577         PLOGMSG(klogErr, (klogErr, "$(p): $(e)", PLOG_2(PLOG_S(p),PLOG_S(e)), xml_path, strerror(errno)));
578         CoreUsage(log_fd, argv[0], true, false, true, false);
579     }
580     g_dflt_file_stat.st_dev = 0;
581     g_dflt_file_stat.st_ino = 0;
582     g_dflt_file_stat.st_mode = S_IFREG | 0444; /* read-only */
583     g_dflt_file_stat.st_nlink = 1;
584     g_dflt_file_stat.st_rdev = 0;
585     g_dflt_file_stat.st_size = 0;
586     g_dflt_file_stat.st_blksize = 0;
587     g_dflt_file_stat.st_blocks = 0;
588 
589     if( (rc = Initialize(sra_sync, xml_path, xml_sync, sra_cache, xml_root, xml_validate)) != 0 ) {
590         LOGERR(klogErr, rc, "at initialization");
591         CoreUsage(log_fd, argv[0], true, false, true, false);
592     }
593     DEBUG_MSG(8, ("Mount point set to '%s'\n", mount_point));
594 
595     {{ /* FUSE start */
596         struct fuse_operations ops;
597         struct fuse_args args;
598 
599         memset(&args, 0, sizeof(struct fuse_args));
600         fuse_opt_add_arg(&args, argv[0]);
601         /* mount point for fuse_main */
602         fuse_opt_add_arg(&args, mount_point);
603         /* save mopunt point dir and program stat */
604         for(i = 0; i < argc; i++) {
605             if( fargs[i] ) {
606                 fuse_opt_add_arg(&args, fargs[i]);
607             }
608         }
609         free(fargs);
610         memset(&ops, 0, sizeof(struct fuse_operations));
611         ops.init     = UX_FUSE_init;
612         ops.destroy  = UX_FUSE_destroy;
613         ops.getattr  = UX_FUSE_getattr;
614         ops.readdir  = UX_FUSE_readdir;
615         ops.readlink = UX_FUSE_readlink;
616         ops.open     = UX_FUSE_open;
617         ops.read     = UX_FUSE_read;
618         ops.release  = UX_FUSE_release;
619         ops.mknod = UX_FUSE_mknod;
620         ops.mkdir = UX_FUSE_mkdir;
621         ops.unlink = UX_FUSE_unlink;
622         ops.rmdir = UX_FUSE_rmdir;
623         ops.symlink = UX_FUSE_symlink;
624         ops.rename = UX_FUSE_rename;
625         ops.link = UX_FUSE_link;
626         ops.chmod = UX_FUSE_chmod;
627         ops.chown = UX_FUSE_chown;
628         ops.truncate = UX_FUSE_truncate;
629         ops.utime = UX_FUSE_utime;
630         ops.write = UX_FUSE_write;
631         ops.flush = UX_FUSE_flush;
632         ops.create = UX_FUSE_create;
633         ops.ftruncate = UX_FUSE_ftruncate;
634         rc = fuse_main(args.argc, args.argv, &ops);
635     }}
636     return rc;
637 }
638