1 /*
2  * Copyright (C) 2016 Jakub Kruszona-Zawadzki, Core Technology Sp. z o.o.
3  *
4  * This file is part of MooseFS.
5  *
6  * MooseFS is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, version 2 (only).
9  *
10  * MooseFS is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with MooseFS; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA
18  * or visit http://www.gnu.org/licenses/gpl-2.0.html
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #if defined(HAVE_MLOCKALL) && defined(RLIMIT_MEMLOCK) && defined(MCL_CURRENT) && defined(MCL_FUTURE)
26 #  define MFS_USE_MEMLOCK 1
27 #endif
28 
29 #if defined(HAVE_MALLOC_H)
30 #  include <malloc.h>
31 #endif
32 #if defined(M_ARENA_MAX) && defined(M_ARENA_TEST) && defined(HAVE_MALLOPT)
33 #  define MFS_USE_MALLOPT 1
34 #endif
35 
36 #include <fuse.h>
37 #include <fuse_opt.h>
38 #include <fuse_lowlevel.h>
39 #include <sys/time.h>
40 #include <sys/resource.h>
41 #ifdef MFS_USE_MEMLOCK
42 #  include <sys/mman.h>
43 #endif
44 #include <unistd.h>
45 #include <fcntl.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <strings.h>
50 #include <stddef.h>
51 #include <unistd.h>
52 #include <syslog.h>
53 #include <signal.h>
54 #include <errno.h>
55 #include <pthread.h>
56 
57 #include "mfs_fuse.h"
58 #include "mfs_meta_fuse.h"
59 
60 #include "MFSCommunication.h"
61 #include "clocks.h"
62 #include "massert.h"
63 #include "portable.h"
64 #include "md5.h"
65 #include "mastercomm.h"
66 #include "masterproxy.h"
67 #include "chunkloccache.h"
68 #include "sustained_inodes.h"
69 #include "sustained_stats.h"
70 #include "symlinkcache.h"
71 #include "negentrycache.h"
72 //#include "dircache.h"
73 #include "conncache.h"
74 #include "readdata.h"
75 #include "writedata.h"
76 #include "csdb.h"
77 #include "stats.h"
78 #include "strerr.h"
79 #include "crc.h"
80 
81 #define STR_AUX(x) #x
82 #define STR(x) STR_AUX(x)
83 const char id[]="@(#) version: " VERSSTR ", written by Jakub Kruszona-Zawadzki";
84 
85 #if defined(__APPLE__)
86 #define DEFAULT_OPTIONS "allow_other,daemon_timeout=600,novncache"
87 // #define DEFAULT_OPTIONS "allow_other,default_permissions,daemon_timeout=600,iosize=65536,novncache"
88 #else
89 #define DEFAULT_OPTIONS "allow_other"
90 #endif
91 
92 static void mfs_fsinit (void *userdata, struct fuse_conn_info *conn);
93 
94 static struct fuse_lowlevel_ops mfs_meta_oper = {
95 	.init           = mfs_fsinit,
96 	.statfs         = mfs_meta_statfs,
97 	.lookup         = mfs_meta_lookup,
98 	.getattr        = mfs_meta_getattr,
99 	.setattr        = mfs_meta_setattr,
100 	.unlink         = mfs_meta_unlink,
101 	.rename         = mfs_meta_rename,
102 	.opendir        = mfs_meta_opendir,
103 	.readdir        = mfs_meta_readdir,
104 	.releasedir     = mfs_meta_releasedir,
105 	.open           = mfs_meta_open,
106 	.release        = mfs_meta_release,
107 	.read           = mfs_meta_read,
108 	.write          = mfs_meta_write,
109 //	.access         = mfs_meta_access,
110 };
111 
112 static struct fuse_lowlevel_ops mfs_oper = {
113 	.init           = mfs_fsinit,
114 	.statfs         = mfs_statfs,
115 	.lookup         = mfs_lookup,
116 	.getattr        = mfs_getattr,
117 	.setattr        = mfs_setattr,
118 	.mknod          = mfs_mknod,
119 	.unlink         = mfs_unlink,
120 	.mkdir          = mfs_mkdir,
121 	.rmdir          = mfs_rmdir,
122 	.symlink        = mfs_symlink,
123 	.readlink       = mfs_readlink,
124 	.rename         = mfs_rename,
125 	.link           = mfs_link,
126 	.opendir        = mfs_opendir,
127 	.readdir        = mfs_readdir,
128 	.releasedir     = mfs_releasedir,
129 	.create         = mfs_create,
130 	.open           = mfs_open,
131 	.release        = mfs_release,
132 	.flush          = mfs_flush,
133 	.fsync          = mfs_fsync,
134 	.read           = mfs_read,
135 	.write          = mfs_write,
136 	.access         = mfs_access,
137 	.getxattr       = mfs_getxattr,
138 	.setxattr       = mfs_setxattr,
139 	.listxattr      = mfs_listxattr,
140 	.removexattr    = mfs_removexattr,
141 };
142 
143 struct mfsopts {
144 	char *masterhost;
145 	char *masterport;
146 	char *bindhost;
147 	char *proxyhost;
148 	char *subfolder;
149 	char *password;
150 	char *md5pass;
151 	unsigned nofile;
152 	signed nice;
153 #ifdef MFS_USE_MEMLOCK
154 	int memlock;
155 #endif
156 #ifdef MFS_USE_MALLOPT
157 	int limitarenas;
158 #endif
159 	int nostdmountoptions;
160 	int meta;
161 	int debug;
162 	int delayedinit;
163 	int mkdircopysgid;
164 	char *sugidclearmodestr;
165 	int sugidclearmode;
166 	char *cachemode;
167 	int cachefiles;
168 	int keepcache;
169 	int passwordask;
170 	int donotrememberpassword;
171 //	int xattraclsupport;
172 	unsigned writecachesize;
173 	unsigned readaheadsize;
174 	unsigned readaheadleng;
175 	unsigned readaheadtrigger;
176 	unsigned ioretries;
177 	double attrcacheto;
178 	double xattrcacheto;
179 	double entrycacheto;
180 	double direntrycacheto;
181 	double negentrycacheto;
182 	double groupscacheto;
183 };
184 
185 static struct mfsopts mfsopts;
186 static char *defaultmountpoint = NULL;
187 
188 static int custom_cfg;
189 
190 enum {
191 	KEY_CFGFILE,
192 	KEY_META,
193 	KEY_HOST,
194 	KEY_PORT,
195 	KEY_BIND,
196 	KEY_PROXY,
197 	KEY_PATH,
198 	KEY_PASSWORDASK,
199 	KEY_NOSTDMOUNTOPTIONS,
200 	KEY_HELP,
201 	KEY_VERSION,
202 };
203 
204 #define MFS_OPT(t, p, v) { t, offsetof(struct mfsopts, p), v }
205 
206 static struct fuse_opt mfs_opts_stage1[] = {
207 	FUSE_OPT_KEY("mfscfgfile=",    KEY_CFGFILE),
208 	FUSE_OPT_KEY("-c ",            KEY_CFGFILE),
209 	FUSE_OPT_END
210 };
211 
212 static struct fuse_opt mfs_opts_stage2[] = {
213 	MFS_OPT("mfsmaster=%s", masterhost, 0),
214 	MFS_OPT("mfsport=%s", masterport, 0),
215 	MFS_OPT("mfsbind=%s", bindhost, 0),
216 	MFS_OPT("mfsproxy=%s", proxyhost, 0),
217 	MFS_OPT("mfssubfolder=%s", subfolder, 0),
218 	MFS_OPT("mfspassword=%s", password, 0),
219 	MFS_OPT("mfsmd5pass=%s", md5pass, 0),
220 	MFS_OPT("mfsrlimitnofile=%u", nofile, 0),
221 	MFS_OPT("mfsnice=%d", nice, 0),
222 #ifdef MFS_USE_MEMLOCK
223 	MFS_OPT("mfsmemlock", memlock, 1),
224 #endif
225 #ifdef MFS_USE_MALLOPT
226 	MFS_OPT("mfslimitarenas=%u", limitarenas, 0),
227 #endif
228 	MFS_OPT("mfswritecachesize=%u", writecachesize, 0),
229 	MFS_OPT("mfsreadaheadsize=%u", readaheadsize, 0),
230 	MFS_OPT("mfsreadaheadleng=%u", readaheadleng, 0),
231 	MFS_OPT("mfsreadaheadtrigger=%u", readaheadtrigger, 0),
232 	MFS_OPT("mfsioretries=%u", ioretries, 0),
233 	MFS_OPT("mfsdebug", debug, 1),
234 	MFS_OPT("mfsmeta", meta, 1),
235 	MFS_OPT("mfsdelayedinit", delayedinit, 1),
236 	MFS_OPT("mfsdonotrememberpassword", donotrememberpassword, 1),
237 	MFS_OPT("mfscachefiles", cachefiles, 1),
238 	MFS_OPT("mfscachemode=%s", cachemode, 0),
239 	MFS_OPT("mfsmkdircopysgid=%u", mkdircopysgid, 0),
240 	MFS_OPT("mfssugidclearmode=%s", sugidclearmodestr, 0),
241 	MFS_OPT("mfsattrcacheto=%lf", attrcacheto, 0),
242 	MFS_OPT("mfsxattrcacheto=%lf", xattrcacheto, 0),
243 	MFS_OPT("mfsentrycacheto=%lf", entrycacheto, 0),
244 	MFS_OPT("mfsdirentrycacheto=%lf", direntrycacheto, 0),
245 	MFS_OPT("mfsnegentrycacheto=%lf", negentrycacheto, 0),
246 	MFS_OPT("mfsgroupscacheto=%lf", groupscacheto, 0),
247 //	MFS_OPT("mfsaclsupport", xattraclsupport, 1),
248 
249 	FUSE_OPT_KEY("-m",             KEY_META),
250 	FUSE_OPT_KEY("--meta",         KEY_META),
251 	FUSE_OPT_KEY("-H ",            KEY_HOST),
252 	FUSE_OPT_KEY("-P ",            KEY_PORT),
253 	FUSE_OPT_KEY("-B ",            KEY_BIND),
254 	FUSE_OPT_KEY("-L ",            KEY_PROXY),
255 	FUSE_OPT_KEY("-S ",            KEY_PATH),
256 	FUSE_OPT_KEY("-p",             KEY_PASSWORDASK),
257 	FUSE_OPT_KEY("--password",     KEY_PASSWORDASK),
258 	FUSE_OPT_KEY("-n",             KEY_NOSTDMOUNTOPTIONS),
259 	FUSE_OPT_KEY("--nostdopts",    KEY_NOSTDMOUNTOPTIONS),
260 	FUSE_OPT_KEY("-V",             KEY_VERSION),
261 	FUSE_OPT_KEY("--version",      KEY_VERSION),
262 	FUSE_OPT_KEY("-h",             KEY_HELP),
263 	FUSE_OPT_KEY("--help",         KEY_HELP),
264 	FUSE_OPT_END
265 };
266 
usage(const char * progname)267 static void usage(const char *progname) {
268 	fprintf(stderr,
269 "usage: %s mountpoint [options]\n"
270 "\n", progname);
271 	fprintf(stderr,
272 "general options:\n"
273 "    -o opt,[opt...]         mount options\n"
274 "    -h   --help             print help\n"
275 "    -V   --version          print version\n"
276 "\n");
277 	fprintf(stderr,
278 "MFS options:\n"
279 "    -c CFGFILE                  equivalent to '-o mfscfgfile=CFGFILE'\n"
280 "    -m   --meta                 equivalent to '-o mfsmeta'\n"
281 "    -H HOST                     equivalent to '-o mfsmaster=HOST'\n"
282 "    -P PORT                     equivalent to '-o mfsport=PORT'\n"
283 "    -B IP                       equivalent to '-o mfsbind=IP'\n"
284 "    -L IP                       equivalent to '-o mfsproxy=IP'\n"
285 "    -S PATH                     equivalent to '-o mfssubfolder=PATH'\n"
286 "    -p   --password             similar to '-o mfspassword=PASSWORD', but show prompt and ask user for password\n"
287 "    -n   --nostdopts            do not add standard MFS mount options: '-o " DEFAULT_OPTIONS ",fsname=MFS'\n"
288 "    -o mfscfgfile=CFGFILE       load some mount options from external file (if not specified then use default file: " ETC_PATH "/mfs/mfsmount.cfg or " ETC_PATH "/mfsmount.cfg)\n"
289 "    -o mfsdebug                 print some debugging information\n"
290 "    -o mfsmeta                  mount meta filesystem (trash etc.)\n"
291 "    -o mfsdelayedinit           connection with master is done in background - with this option mount can be run without network (good for being run from fstab / init scripts etc.)\n"
292 #ifdef __linux__
293 "    -o mfsmkdircopysgid=N       sgid bit should be copied during mkdir operation (default: 1)\n"
294 #else
295 "    -o mfsmkdircopysgid=N       sgid bit should be copied during mkdir operation (default: 0)\n"
296 #endif
297 #if defined(DEFAULT_SUGID_CLEAR_MODE_EXT)
298 "    -o mfssugidclearmode=SMODE  set sugid clear mode (see below ; default: EXT)\n"
299 #elif defined(DEFAULT_SUGID_CLEAR_MODE_BSD)
300 "    -o mfssugidclearmode=SMODE  set sugid clear mode (see below ; default: BSD)\n"
301 #elif defined(DEFAULT_SUGID_CLEAR_MODE_OSX)
302 "    -o mfssugidclearmode=SMODE  set sugid clear mode (see below ; default: OSX)\n"
303 #else
304 "    -o mfssugidclearmode=SMODE  set sugid clear mode (see below ; default: NEVER)\n"
305 #endif
306 "    -o mfscachemode=CMODE       set cache mode (see below ; default: AUTO)\n"
307 "    -o mfscachefiles            (deprecated) equivalent to '-o mfscachemode=YES'\n"
308 // "    -o mfscachefiles            allow files data to be kept in cache (dangerous in network environment)\n"
309 "    -o mfsattrcacheto=SEC       set attributes cache timeout in seconds (default: 1.0)\n"
310 "    -o mfsxattrcacheto=SEC      set extended attributes (xattr) cache timeout in seconds (default: 30.0)\n"
311 "    -o mfsentrycacheto=SEC      set file entry cache timeout in seconds (default: 0.0)\n"
312 "    -o mfsdirentrycacheto=SEC   set directory entry cache timeout in seconds (default: 1.0)\n"
313 "    -o mfsnegentrycacheto=SEC   set negative entry cache timeout in seconds (default: 1.0)\n"
314 "    -o mfsgroupscacheto=SEC     set supplementary groups cache timeout in seconds (default: 300.0)\n"
315 "    -o mfsrlimitnofile=N        on startup mfsmount tries to change number of descriptors it can simultaneously open (default: 100000)\n"
316 "    -o mfsnice=N                on startup mfsmount tries to change his 'nice' value (default: -19)\n"
317 #ifdef MFS_USE_MEMLOCK
318 "    -o mfsmemlock               try to lock memory\n"
319 #endif
320 #ifdef MFS_USE_MALLOPT
321 "    -o mfslimitarenas=N         if N>0 then limit glibc malloc arenas (default: 8)\n"
322 #endif
323 "    -o mfswritecachesize=N      define size of write cache in MiB (default: 128)\n"
324 "    -o mfsreadaheadsize=N       define size of all read ahead buffers in MiB (default: 128)\n"
325 "    -o mfsreadaheadleng=N       define amount of bytes to be additionally read (default: 1048576)\n"
326 "    -o mfsreadaheadtrigger=N    define amount of bytes read sequentially that turns on read ahead (default: 10 * mfsreadaheadleng)\n"
327 "    -o mfsioretries=N           define number of retries before I/O error is returned (default: 30)\n"
328 "    -o mfsmaster=HOST           define mfsmaster location (default: " DEFAULT_MASTERNAME ")\n"
329 "    -o mfsport=PORT             define mfsmaster port number (default: " DEFAULT_MASTER_CLIENT_PORT ")\n"
330 "    -o mfsbind=IP               define source ip address for connections (default: NOT DEFINED - chosen automatically by OS)\n"
331 "    -o mfsproxy=IP              define listen ip address of local master proxy for communication with tools (default: 127.0.0.1)\n"
332 "    -o mfssubfolder=PATH        define subfolder to mount as root (default: /)\n"
333 "    -o mfspassword=PASSWORD     authenticate to mfsmaster with password\n"
334 "    -o mfsmd5pass=MD5           authenticate to mfsmaster using directly given md5 (only if mfspassword is not defined)\n"
335 "    -o mfsdonotrememberpassword do not remember password in memory - more secure, but when session is lost then new session is created without password\n"
336 "\n");
337 	fprintf(stderr,
338 "CMODE can be set to:\n"
339 "    NO,NONE or NEVER            never allow files data to be kept in cache (safest but can reduce efficiency)\n"
340 "    YES or ALWAYS               always allow files data to be kept in cache (dangerous)\n"
341 "    AUTO                        file cache is managed by mfsmaster automatically (should be very safe and efficient)\n"
342 "\n");
343 	fprintf(stderr,
344 "SMODE can be set to:\n"
345 "    NEVER                       MFS will not change suid and sgid bit on chown\n"
346 "    ALWAYS                      clear suid and sgid on every chown - safest operation\n"
347 "    OSX                         standard behavior in OS X and Solaris (chown made by unprivileged user clear suid and sgid)\n"
348 "    BSD                         standard behavior in *BSD systems (like in OSX, but only when something is really changed)\n"
349 "    EXT                         standard behavior in most file systems on Linux (directories not changed, others: suid cleared always, sgid only when group exec bit is set)\n"
350 "    XFS                         standard behavior in XFS on Linux (like EXT but directories are changed by unprivileged users)\n"
351 "SMODE extra info:\n"
352 "    btrfs,ext2,ext3,ext4,hfs[+],jfs,ntfs and reiserfs on Linux work as 'EXT'.\n"
353 "    Only xfs on Linux works a little different. Beware that there is a strange\n"
354 "    operation - chown(-1,-1) which is usually converted by a kernel into something\n"
355 "    like 'chmod ug-s', and therefore can't be controlled by MFS as 'chown'\n"
356 "\n");
357 }
358 
mfs_opt_parse_cfg_file(const char * filename,int optional,struct fuse_args * outargs)359 static void mfs_opt_parse_cfg_file(const char *filename,int optional,struct fuse_args *outargs) {
360 	FILE *fd;
361 	char lbuff[1000],*p;
362 
363 	fd = fopen(filename,"r");
364 	if (fd==NULL) {
365 		if (optional==0) {
366 			fprintf(stderr,"can't open cfg file: %s\n",filename);
367 			abort();
368 		}
369 		return;
370 	}
371 	custom_cfg = 1;
372 	while (fgets(lbuff,999,fd)) {
373 		if (lbuff[0]!='#' && lbuff[0]!=';') {
374 			lbuff[999]=0;
375 			for (p = lbuff ; *p ; p++) {
376 				if (*p=='\r' || *p=='\n') {
377 					*p=0;
378 					break;
379 				}
380 			}
381 			p--;
382 			while (p>=lbuff && (*p==' ' || *p=='\t')) {
383 				*p=0;
384 				p--;
385 			}
386 			p = lbuff;
387 			while (*p==' ' || *p=='\t') {
388 				p++;
389 			}
390 			if (*p) {
391 //				printf("add option: %s\n",p);
392 				if (*p=='-') {
393 					fuse_opt_add_arg(outargs,p);
394 				} else if (*p=='/') {
395 					if (defaultmountpoint) {
396 						free(defaultmountpoint);
397 					}
398 					defaultmountpoint = strdup(p);
399 				} else {
400 					fuse_opt_add_arg(outargs,"-o");
401 					fuse_opt_add_arg(outargs,p);
402 				}
403 			}
404 		}
405 	}
406 	fclose(fd);
407 }
408 
mfs_opt_proc_stage1(void * data,const char * arg,int key,struct fuse_args * outargs)409 static int mfs_opt_proc_stage1(void *data, const char *arg, int key, struct fuse_args *outargs) {
410 	struct fuse_args *defargs = (struct fuse_args*)data;
411 	(void)outargs;
412 
413 	if (key==KEY_CFGFILE) {
414 		if (memcmp(arg,"mfscfgfile=",11)==0) {
415 			mfs_opt_parse_cfg_file(arg+11,0,defargs);
416 		} else if (arg[0]=='-' && arg[1]=='c') {
417 			mfs_opt_parse_cfg_file(arg+2,0,defargs);
418 		}
419 		return 0;
420 	}
421 	return 1;
422 }
423 
424 // return value:
425 //   0 - discard this arg
426 //   1 - keep this arg for future processing
mfs_opt_proc_stage2(void * data,const char * arg,int key,struct fuse_args * outargs)427 static int mfs_opt_proc_stage2(void *data, const char *arg, int key, struct fuse_args *outargs) {
428 	(void)data;
429 
430 	switch (key) {
431 	case FUSE_OPT_KEY_OPT:
432 		return 1;
433 	case FUSE_OPT_KEY_NONOPT:
434 		return 1;
435 	case KEY_HOST:
436 		if (mfsopts.masterhost!=NULL) {
437 			free(mfsopts.masterhost);
438 		}
439 		mfsopts.masterhost = strdup(arg+2);
440 		return 0;
441 	case KEY_PORT:
442 		if (mfsopts.masterport!=NULL) {
443 			free(mfsopts.masterport);
444 		}
445 		mfsopts.masterport = strdup(arg+2);
446 		return 0;
447 	case KEY_BIND:
448 		if (mfsopts.bindhost!=NULL) {
449 			free(mfsopts.bindhost);
450 		}
451 		mfsopts.bindhost = strdup(arg+2);
452 		return 0;
453 	case KEY_PROXY:
454 		if (mfsopts.proxyhost!=NULL) {
455 			free(mfsopts.proxyhost);
456 		}
457 		mfsopts.proxyhost = strdup(arg+2);
458 		return 0;
459 	case KEY_PATH:
460 		if (mfsopts.subfolder!=NULL) {
461 			free(mfsopts.subfolder);
462 		}
463 		mfsopts.subfolder = strdup(arg+2);
464 		return 0;
465 	case KEY_PASSWORDASK:
466 		mfsopts.passwordask = 1;
467 		return 0;
468 	case KEY_META:
469 		mfsopts.meta = 1;
470 		return 0;
471 	case KEY_NOSTDMOUNTOPTIONS:
472 		mfsopts.nostdmountoptions = 1;
473 		return 0;
474 	case KEY_VERSION:
475 		fprintf(stderr, "MFS version %s\n",VERSSTR);
476 		{
477 			struct fuse_args helpargs = FUSE_ARGS_INIT(0, NULL);
478 
479 			fuse_opt_add_arg(&helpargs,outargs->argv[0]);
480 			fuse_opt_add_arg(&helpargs,"--version");
481 			fuse_parse_cmdline(&helpargs,NULL,NULL,NULL);
482 			fuse_mount(NULL,&helpargs);
483 		}
484 		exit(0);
485 	case KEY_HELP:
486 		usage(outargs->argv[0]);
487 		{
488 			struct fuse_args helpargs = FUSE_ARGS_INIT(0, NULL);
489 
490 			fuse_opt_add_arg(&helpargs,outargs->argv[0]);
491 			fuse_opt_add_arg(&helpargs,"-ho");
492 			fuse_parse_cmdline(&helpargs,NULL,NULL,NULL);
493 			fuse_mount("",&helpargs);
494 		}
495 		exit(1);
496 	default:
497 		fprintf(stderr, "internal error\n");
498 		abort();
499 	}
500 }
501 
mfs_fsinit(void * userdata,struct fuse_conn_info * conn)502 static void mfs_fsinit (void *userdata, struct fuse_conn_info *conn) {
503 	int *piped = (int*)userdata;
504 	char s;
505 	conn->max_write = 131072;
506 	conn->max_readahead = 131072;
507 #if defined(FUSE_CAP_BIG_WRITES) || defined(FUSE_CAP_DONT_MASK)
508 	conn->want = 0;
509 #endif
510 #ifdef FUSE_CAP_BIG_WRITES
511 	conn->want |= FUSE_CAP_BIG_WRITES;
512 #endif
513 #ifdef FUSE_CAP_DONT_MASK
514 	conn->want |= FUSE_CAP_DONT_MASK;
515 #endif
516 	if (piped[1]>=0) {
517 		s=0;
518 		if (write(piped[1],&s,1)!=1) {
519 			syslog(LOG_ERR,"pipe write error: %s",strerr(errno));
520 		}
521 		close(piped[1]);
522 	}
523 }
524 
main_thread_create(pthread_t * th,const pthread_attr_t * attr,void * (* fn)(void *),void * arg)525 int main_thread_create(pthread_t *th,const pthread_attr_t *attr,void *(*fn)(void *),void *arg) {
526 	sigset_t oldset;
527 	sigset_t newset;
528 	int res;
529 
530 	sigemptyset(&newset);
531 	sigaddset(&newset, SIGTERM);
532 	sigaddset(&newset, SIGINT);
533 	sigaddset(&newset, SIGHUP);
534 	sigaddset(&newset, SIGQUIT);
535 	pthread_sigmask(SIG_BLOCK, &newset, &oldset);
536 	res = pthread_create(th,attr,fn,arg);
537 	pthread_sigmask(SIG_SETMASK, &oldset, NULL);
538 	return res;
539 }
540 
main_minthread_create(pthread_t * th,uint8_t detached,void * (* fn)(void *),void * arg)541 int main_minthread_create(pthread_t *th,uint8_t detached,void *(*fn)(void *),void *arg) {
542 	static pthread_attr_t *thattr = NULL;
543 	static uint8_t thattr_detached;
544 	if (thattr == NULL) {
545 		size_t mystacksize;
546 		thattr = malloc(sizeof(pthread_attr_t));
547 		passert(thattr);
548 		zassert(pthread_attr_init(thattr));
549 #ifdef PTHREAD_STACK_MIN
550 		mystacksize = PTHREAD_STACK_MIN;
551 		if (mystacksize < 0x100000) {
552 			mystacksize = 0x100000;
553 		}
554 #else
555 		mystacksize = 0x100000;
556 #endif
557 		zassert(pthread_attr_setstacksize(thattr,mystacksize));
558 		thattr_detached = detached + 1; // make it different
559 	}
560 	if (detached != thattr_detached) {
561 		if (detached) {
562 			zassert(pthread_attr_setdetachstate(thattr,PTHREAD_CREATE_DETACHED));
563 		} else {
564 			zassert(pthread_attr_setdetachstate(thattr,PTHREAD_CREATE_JOINABLE));
565 		}
566 		thattr_detached = detached;
567 	}
568 	return main_thread_create(th,thattr,fn,arg);
569 }
570 
mainloop(struct fuse_args * args,const char * mp,int mt,int fg)571 int mainloop(struct fuse_args *args,const char* mp,int mt,int fg) {
572 	struct fuse_session *se;
573 	struct fuse_chan *ch;
574 	struct rlimit rls;
575 	int piped[2];
576 	char s;
577 	int err;
578 	int i;
579 	md5ctx ctx;
580 	uint8_t md5pass[16];
581 
582 	if (mfsopts.passwordask && mfsopts.password==NULL && mfsopts.md5pass==NULL) {
583 		mfsopts.password = getpass("MFS Password:");
584 	}
585 	if (mfsopts.password) {
586 		md5_init(&ctx);
587 		md5_update(&ctx,(uint8_t*)(mfsopts.password),strlen(mfsopts.password));
588 		md5_final(md5pass,&ctx);
589 		memset(mfsopts.password,0,strlen(mfsopts.password));
590 	} else if (mfsopts.md5pass) {
591 		uint8_t *p = (uint8_t*)(mfsopts.md5pass);
592 		for (i=0 ; i<16 ; i++) {
593 			if (*p>='0' && *p<='9') {
594 				md5pass[i]=(*p-'0')<<4;
595 			} else if (*p>='a' && *p<='f') {
596 				md5pass[i]=(*p-'a'+10)<<4;
597 			} else if (*p>='A' && *p<='F') {
598 				md5pass[i]=(*p-'A'+10)<<4;
599 			} else {
600 				fprintf(stderr,"bad md5 definition (md5 should be given as 32 hex digits)\n");
601 				return 1;
602 			}
603 			p++;
604 			if (*p>='0' && *p<='9') {
605 				md5pass[i]+=(*p-'0');
606 			} else if (*p>='a' && *p<='f') {
607 				md5pass[i]+=(*p-'a'+10);
608 			} else if (*p>='A' && *p<='F') {
609 				md5pass[i]+=(*p-'A'+10);
610 			} else {
611 				fprintf(stderr,"bad md5 definition (md5 should be given as 32 hex digits)\n");
612 				return 1;
613 			}
614 			p++;
615 		}
616 		if (*p) {
617 			fprintf(stderr,"bad md5 definition (md5 should be given as 32 hex digits)\n");
618 			return 1;
619 		}
620 		memset(mfsopts.md5pass,0,strlen(mfsopts.md5pass));
621 	}
622 
623 	if (mfsopts.delayedinit) {
624 		fs_init_master_connection(mfsopts.bindhost,mfsopts.masterhost,mfsopts.masterport,mfsopts.meta,mp,mfsopts.subfolder,(mfsopts.password||mfsopts.md5pass)?md5pass:NULL,mfsopts.donotrememberpassword,1);
625 	} else {
626 		if (fs_init_master_connection(mfsopts.bindhost,mfsopts.masterhost,mfsopts.masterport,mfsopts.meta,mp,mfsopts.subfolder,(mfsopts.password||mfsopts.md5pass)?md5pass:NULL,mfsopts.donotrememberpassword,0)<0) {
627 			return 1;
628 		}
629 	}
630 	memset(md5pass,0,16);
631 
632 	if (fg==0) {
633 		openlog(STR(APPNAME), LOG_PID | LOG_NDELAY , LOG_DAEMON);
634 	} else {
635 #if defined(LOG_PERROR)
636 		openlog(STR(APPNAME), LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_USER);
637 #else
638 		openlog(STR(APPNAME), LOG_PID | LOG_NDELAY, LOG_USER);
639 #endif
640 	}
641 
642 	i = mfsopts.nofile;
643 	while (1) {
644 		rls.rlim_cur = i;
645 		rls.rlim_max = i;
646 		if (setrlimit(RLIMIT_NOFILE,&rls)<0) {
647 			i /= 2;
648 			if (i<1000) {
649 				break;
650 			}
651 		} else {
652 			break;
653 		}
654 	}
655 	if (i != (int)(mfsopts.nofile)) {
656 		fprintf(stderr,"can't set open file limit to %d\n",mfsopts.nofile);
657 		if (i>=1000) {
658 			fprintf(stderr,"open file limit set to: %d\n",i);
659 		}
660 	}
661 
662 	setpriority(PRIO_PROCESS,getpid(),mfsopts.nice);
663 #ifdef MFS_USE_MEMLOCK
664 	if (mfsopts.memlock) {
665 		rls.rlim_cur = RLIM_INFINITY;
666 		rls.rlim_max = RLIM_INFINITY;
667 		if (setrlimit(RLIMIT_MEMLOCK,&rls)<0) {
668 			mfsopts.memlock=0;
669 		}
670 	}
671 #endif
672 
673 	piped[0] = piped[1] = -1;
674 	if (fg==0) {
675 		if (pipe(piped)<0) {
676 			fprintf(stderr,"pipe error\n");
677 			return 1;
678 		}
679 		err = fork();
680 		if (err<0) {
681 			fprintf(stderr,"fork error\n");
682 			return 1;
683 		} else if (err>0) {
684 			close(piped[1]);
685 			err = read(piped[0],&s,1);
686 			if (err==0) {
687 				s=1;
688 			}
689 			return s;
690 		}
691 		close(piped[0]);
692 		s=1;
693 	}
694 
695 
696 #ifdef MFS_USE_MEMLOCK
697 	if (mfsopts.memlock) {
698 		if (mlockall(MCL_CURRENT|MCL_FUTURE)==0) {
699 			syslog(LOG_NOTICE,"process memory was successfully locked in RAM");
700 		}
701 	}
702 #endif
703 
704 /* glibc malloc tuning */
705 #ifdef MFS_USE_MALLOPT
706 	if (mfsopts.limitarenas) {
707 		if (!getenv("MALLOC_ARENA_MAX")) {
708 			syslog(LOG_NOTICE,"setting glibc malloc arena max to 8");
709 			mallopt(M_ARENA_MAX, mfsopts.limitarenas);
710 		}
711 		if (!getenv("MALLOC_ARENA_TEST")) {
712 			syslog(LOG_NOTICE,"setting glibc malloc arena test to 1");
713 			mallopt(M_ARENA_TEST, 1);
714 		}
715 	} else {
716 		syslog(LOG_NOTICE,"setting glibc malloc arenas turned off");
717 	}
718 #endif /* glibc malloc tuning */
719 
720 	syslog(LOG_NOTICE,"monotonic clock function: %s",monotonic_method());
721 	syslog(LOG_NOTICE,"monotonic clock speed: %"PRIu32" ops / 10 mili seconds",monotonic_speed());
722 
723 	conncache_init(200);
724 	chunkloc_cache_init();
725 	symlink_cache_init();
726 	negentry_cache_init(mfsopts.negentrycacheto);
727 //	dir_cache_init();
728 	fs_init_threads(mfsopts.ioretries);
729 	if (masterproxy_init(mfsopts.proxyhost)<0) {
730 		fs_term();
731 //		dir_cache_term();
732 		negentry_cache_term();
733 		symlink_cache_term();
734 		chunkloc_cache_term();
735 		return 1;
736 	}
737 
738 //	fs_term();
739 //	negentry_cache_term();
740 //	symlink_cache_term();
741 //	chunkloc_cache_term();
742 //	return 1;
743 
744 	if (mfsopts.meta==0) {
745 		csdb_init();
746 		read_data_init(mfsopts.readaheadsize*1024*1024,mfsopts.readaheadleng,mfsopts.readaheadtrigger,mfsopts.ioretries);
747 		write_data_init(mfsopts.writecachesize*1024*1024,mfsopts.ioretries);
748 	}
749 
750  	ch = fuse_mount(mp, args);
751 	if (ch==NULL) {
752 		fprintf(stderr,"error in fuse_mount\n");
753 		if (piped[1]>=0) {
754 			if (write(piped[1],&s,1)!=1) {
755 				fprintf(stderr,"pipe write error\n");
756 			}
757 			close(piped[1]);
758 		}
759 		if (mfsopts.meta==0) {
760 			write_data_term();
761 			read_data_term();
762 			csdb_term();
763 		}
764 		masterproxy_term();
765 		fs_term();
766 //		dir_cache_term();
767 		negentry_cache_term();
768 		symlink_cache_term();
769 		chunkloc_cache_term();
770 		return 1;
771 	}
772 
773 	if (mfsopts.meta) {
774 		mfs_meta_init(mfsopts.debug,mfsopts.entrycacheto,mfsopts.attrcacheto);
775 		se = fuse_lowlevel_new(args, &mfs_meta_oper, sizeof(mfs_meta_oper), (void*)piped);
776 	} else {
777 		mfs_init(mfsopts.debug,mfsopts.keepcache,mfsopts.direntrycacheto,mfsopts.entrycacheto,mfsopts.attrcacheto,mfsopts.xattrcacheto,mfsopts.groupscacheto,mfsopts.mkdircopysgid,mfsopts.sugidclearmode,1); //mfsopts.xattraclsupport);
778 		se = fuse_lowlevel_new(args, &mfs_oper, sizeof(mfs_oper), (void*)piped);
779 	}
780 	if (se==NULL) {
781 		fuse_unmount(mp,ch);
782 		fprintf(stderr,"error in fuse_lowlevel_new\n");
783 		portable_usleep(100000);	// time for print other error messages by FUSE
784 		if (piped[1]>=0) {
785 			if (write(piped[1],&s,1)!=1) {
786 				fprintf(stderr,"pipe write error\n");
787 			}
788 			close(piped[1]);
789 		}
790 		if (mfsopts.meta==0) {
791 			write_data_term();
792 			read_data_term();
793 			csdb_term();
794 		}
795 		masterproxy_term();
796 		fs_term();
797 //		dir_cache_term();
798 		negentry_cache_term();
799 		symlink_cache_term();
800 		chunkloc_cache_term();
801 		return 1;
802 	}
803 
804 //	fprintf(stderr,"check\n");
805 	fuse_session_add_chan(se, ch);
806 
807 	if (fuse_set_signal_handlers(se)<0) {
808 		fprintf(stderr,"error in fuse_set_signal_handlers\n");
809 		fuse_session_remove_chan(ch);
810 		fuse_session_destroy(se);
811 		fuse_unmount(mp,ch);
812 		if (piped[1]>=0) {
813 			if (write(piped[1],&s,1)!=1) {
814 				fprintf(stderr,"pipe write error\n");
815 			}
816 			close(piped[1]);
817 		}
818 		if (mfsopts.meta==0) {
819 			write_data_term();
820 			read_data_term();
821 			csdb_term();
822 		}
823 		masterproxy_term();
824 		fs_term();
825 //		dir_cache_term();
826 		negentry_cache_term();
827 		symlink_cache_term();
828 		chunkloc_cache_term();
829 		return 1;
830 	}
831 
832 	if (mfsopts.debug==0 && fg==0) {
833 		setsid();
834 		setpgid(0,getpid());
835 		if ((i = open("/dev/null", O_RDWR, 0)) != -1) {
836 			(void)dup2(i, STDIN_FILENO);
837 			(void)dup2(i, STDOUT_FILENO);
838 			(void)dup2(i, STDERR_FILENO);
839 			if (i>2) close (i);
840 		}
841 	}
842 
843 	sinodes_init(mp);
844 	sstats_init();
845 
846 	if (mt) {
847 		err = fuse_session_loop_mt(se);
848 	} else {
849 		err = fuse_session_loop(se);
850 	}
851 	if (err) {
852 		if (piped[1]>=0) {
853 			if (write(piped[1],&s,1)!=1) {
854 				syslog(LOG_ERR,"pipe write error: %s",strerr(errno));
855 			}
856 			close(piped[1]);
857 		}
858 	}
859 
860 	sstats_term();
861 	sinodes_term();
862 
863 	fuse_remove_signal_handlers(se);
864 	fuse_session_remove_chan(ch);
865 	fuse_session_destroy(se);
866 	fuse_unmount(mp,ch);
867 	if (mfsopts.meta==0) {
868 		write_data_term();
869 		read_data_term();
870 		csdb_term();
871 	}
872 	masterproxy_term();
873 	fs_term();
874 //	dir_cache_term();
875 	negentry_cache_term();
876 	symlink_cache_term();
877 	chunkloc_cache_term();
878 	return err ? 1 : 0;
879 }
880 
881 #if FUSE_VERSION == 25
fuse_opt_insert_arg(struct fuse_args * args,int pos,const char * arg)882 static int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) {
883 	assert(pos <= args->argc);
884 	if (fuse_opt_add_arg(args, arg) == -1) {
885 		return -1;
886 	}
887 	if (pos != args->argc - 1) {
888 		char *newarg = args->argv[args->argc - 1];
889 		memmove(&args->argv[pos + 1], &args->argv[pos], sizeof(char *) * (args->argc - pos - 1));
890 		args->argv[pos] = newarg;
891 	}
892 	return 0;
893 }
894 #endif
895 
strncpy_remove_commas(char * dstbuff,unsigned int dstsize,char * src)896 static unsigned int strncpy_remove_commas(char *dstbuff, unsigned int dstsize,char *src) {
897 	char c;
898 	unsigned int l;
899 	l=0;
900 	while ((c=*src++) && l+1<dstsize) {
901 		if (c!=',') {
902 			*dstbuff++ = c;
903 			l++;
904 		}
905 	}
906 	*dstbuff=0;
907 	return l;
908 }
909 
910 #if HAVE_FUSE_VERSION
strncpy_escape_commas(char * dstbuff,unsigned int dstsize,char * src)911 static unsigned int strncpy_escape_commas(char *dstbuff, unsigned int dstsize,char *src) {
912 	char c;
913 	unsigned int l;
914 	l=0;
915 	while ((c=*src++) && l+1<dstsize) {
916 		if (c!=',' && c!='\\') {
917 			*dstbuff++ = c;
918 			l++;
919 		} else {
920 			if (l+2<dstsize) {
921 				*dstbuff++ = '\\';
922 				*dstbuff++ = c;
923 				l+=2;
924 			} else {
925 				*dstbuff=0;
926 				return l;
927 			}
928 		}
929 	}
930 	*dstbuff=0;
931 	return l;
932 }
933 #endif
934 
remove_mfsmount_magic(struct fuse_args * args)935 void remove_mfsmount_magic(struct fuse_args *args) {
936 	int i;
937 	for (i=1 ; i<args->argc ; i++) {
938 		if (strcmp(args->argv[i],"mfsmount_magic")==0) {
939 			if (i+1 < args->argc) {
940 				memmove(&args->argv[i],&args->argv[i+1],sizeof(char *)*(args->argc - i - 1));
941 			}
942 			args->argc--;
943 			return;
944 		}
945 	}
946 }
947 
make_fsname(struct fuse_args * args)948 void make_fsname(struct fuse_args *args) {
949 	char fsnamearg[256];
950 	unsigned int l;
951 #if HAVE_FUSE_VERSION
952 	int libver;
953 	libver = fuse_version();
954 	if (libver >= 27) {
955 		l = snprintf(fsnamearg,256,"-osubtype=mfs%s,fsname=",(mfsopts.meta)?"meta":"");
956 		if (libver >= 28) {
957 			l += strncpy_escape_commas(fsnamearg+l,256-l,mfsopts.masterhost);
958 			if (l<255) {
959 				fsnamearg[l++]=':';
960 			}
961 			l += strncpy_escape_commas(fsnamearg+l,256-l,mfsopts.masterport);
962 			if (mfsopts.subfolder[0]!='/') {
963 				if (l<255) {
964 					fsnamearg[l++]='/';
965 				}
966 			}
967 			if (mfsopts.subfolder[0]!='/' && mfsopts.subfolder[1]!=0) {
968 				l += strncpy_escape_commas(fsnamearg+l,256-l,mfsopts.subfolder);
969 			}
970 			if (l>255) {
971 				l=255;
972 			}
973 			fsnamearg[l]=0;
974 		} else {
975 			l += strncpy_remove_commas(fsnamearg+l,256-l,mfsopts.masterhost);
976 			if (l<255) {
977 				fsnamearg[l++]=':';
978 			}
979 			l += strncpy_remove_commas(fsnamearg+l,256-l,mfsopts.masterport);
980 			if (mfsopts.subfolder[0]!='/') {
981 				if (l<255) {
982 					fsnamearg[l++]='/';
983 				}
984 			}
985 			if (mfsopts.subfolder[0]!='/' && mfsopts.subfolder[1]!=0) {
986 				l += strncpy_remove_commas(fsnamearg+l,256-l,mfsopts.subfolder);
987 			}
988 			if (l>255) {
989 				l=255;
990 			}
991 			fsnamearg[l]=0;
992 		}
993 	} else {
994 #else
995 		l = snprintf(fsnamearg,256,"-ofsname=mfs%s#",(mfsopts.meta)?"meta":"");
996 		l += strncpy_remove_commas(fsnamearg+l,256-l,mfsopts.masterhost);
997 		if (l<255) {
998 			fsnamearg[l++]=':';
999 		}
1000 		l += strncpy_remove_commas(fsnamearg+l,256-l,mfsopts.masterport);
1001 		if (mfsopts.subfolder[0]!='/') {
1002 			if (l<255) {
1003 				fsnamearg[l++]='/';
1004 			}
1005 		}
1006 		if (mfsopts.subfolder[0]!='/' && mfsopts.subfolder[1]!=0) {
1007 			l += strncpy_remove_commas(fsnamearg+l,256-l,mfsopts.subfolder);
1008 		}
1009 		if (l>255) {
1010 			l=255;
1011 		}
1012 		fsnamearg[l]=0;
1013 #endif
1014 #if HAVE_FUSE_VERSION
1015 	}
1016 #endif
1017 	fuse_opt_insert_arg(args, 1, fsnamearg);
1018 }
1019 
1020 /*
1021 void dump_args(const char *prfx,struct fuse_args *args) {
1022 	int i;
1023 	for (i=0 ; i<args->argc ; i++) {
1024 		printf("%s [%d]: %s\n",prfx,i,args->argv[i]);
1025 	}
1026 }
1027 */
1028 
main(int argc,char * argv[])1029 int main(int argc, char *argv[]) {
1030 	int res;
1031 	int mt,fg;
1032 	int i;
1033 	char *mountpoint;
1034 	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1035 	struct fuse_args defaultargs = FUSE_ARGS_INIT(0, NULL);
1036 
1037 #if defined(SIGPIPE) && defined(SIG_IGN)
1038 	signal(SIGPIPE,SIG_IGN);
1039 #endif
1040 	strerr_init();
1041 	mycrc32_init();
1042 
1043 	mfsopts.masterhost = NULL;
1044 	mfsopts.masterport = NULL;
1045 	mfsopts.bindhost = NULL;
1046 	mfsopts.proxyhost = NULL;
1047 	mfsopts.subfolder = NULL;
1048 	mfsopts.password = NULL;
1049 	mfsopts.md5pass = NULL;
1050 	mfsopts.nofile = 0;
1051 	mfsopts.nice = -19;
1052 #ifdef MFS_USE_MEMLOCK
1053 	mfsopts.memlock = 0;
1054 #endif
1055 #ifdef MFS_USE_MALLOPT
1056 	mfsopts.limitarenas = 8;
1057 #endif
1058 	mfsopts.nostdmountoptions = 0;
1059 	mfsopts.meta = 0;
1060 	mfsopts.debug = 0;
1061 	mfsopts.delayedinit = 0;
1062 #ifdef __linux__
1063 	mfsopts.mkdircopysgid = 1;
1064 #else
1065 	mfsopts.mkdircopysgid = 0;
1066 #endif
1067 	mfsopts.sugidclearmodestr = NULL;
1068 	mfsopts.donotrememberpassword = 0;
1069 //	mfsopts.xattraclsupport = 0;
1070 	mfsopts.cachefiles = 0;
1071 	mfsopts.cachemode = NULL;
1072 	mfsopts.writecachesize = 0;
1073 	mfsopts.readaheadsize = 0;
1074 	mfsopts.readaheadleng = 0;
1075 	mfsopts.readaheadtrigger = 0;
1076 	mfsopts.ioretries = 30;
1077 	mfsopts.passwordask = 0;
1078 	mfsopts.attrcacheto = 1.0;
1079 	mfsopts.xattrcacheto = 30.0;
1080 	mfsopts.entrycacheto = 0.0;
1081 	mfsopts.direntrycacheto = 1.0;
1082 	mfsopts.negentrycacheto = 1.0;
1083 	mfsopts.groupscacheto = 300.0;
1084 
1085 	custom_cfg = 0;
1086 
1087 //	dump_args("input_args",&args);
1088 
1089 	if (fuse_opt_parse(&args, &defaultargs, mfs_opts_stage1, mfs_opt_proc_stage1)<0) {
1090 		exit(1);
1091 	}
1092 
1093 	if (custom_cfg==0) {
1094 		int cfgfd;
1095 		char *cfgfile;
1096 
1097 		cfgfile=strdup(ETC_PATH "/mfs/mfsmount.cfg");
1098 		if ((cfgfd = open(cfgfile,O_RDONLY))<0 && errno==ENOENT) {
1099 			free(cfgfile);
1100 			cfgfile=strdup(ETC_PATH "/mfsmount.cfg");
1101 			if ((cfgfd = open(cfgfile,O_RDONLY))>=0) {
1102 				fprintf(stderr,"default sysconf path has changed - please move mfsmount.cfg from "ETC_PATH"/ to "ETC_PATH"/mfs/\n");
1103 			}
1104 		}
1105 		if (cfgfd>=0) {
1106 			close(cfgfd);
1107 		}
1108 		mfs_opt_parse_cfg_file(cfgfile,1,&defaultargs);
1109 		free(cfgfile);
1110 	}
1111 
1112 //	dump_args("parsed_defaults",&defaultargs);
1113 //	dump_args("changed_args",&args);
1114 
1115 	for (i=0 ; i<defaultargs.argc ; i++) {
1116 		fuse_opt_add_arg(&args,defaultargs.argv[i]);
1117 	}
1118 
1119 //	dump_args("combined_args",&args);
1120 
1121 	if (fuse_opt_parse(&args, &mfsopts, mfs_opts_stage2, mfs_opt_proc_stage2)<0) {
1122 		exit(1);
1123 	}
1124 
1125 //	dump_args("combined_args_after_parse",&args);
1126 
1127 	if (mfsopts.cachemode!=NULL && mfsopts.cachefiles) {
1128 		fprintf(stderr,"mfscachemode and mfscachefiles options are exclusive - use only mfscachemode\nsee: %s -h for help\n",argv[0]);
1129 		return 1;
1130 	}
1131 
1132 	if (mfsopts.cachemode==NULL) {
1133 		mfsopts.keepcache=(mfsopts.cachefiles)?1:0;
1134 	} else if (strcasecmp(mfsopts.cachemode,"AUTO")==0) {
1135 		mfsopts.keepcache=0;
1136 	} else if (strcasecmp(mfsopts.cachemode,"YES")==0 || strcasecmp(mfsopts.cachemode,"ALWAYS")==0) {
1137 		mfsopts.keepcache=1;
1138 	} else if (strcasecmp(mfsopts.cachemode,"NO")==0 || strcasecmp(mfsopts.cachemode,"NONE")==0 || strcasecmp(mfsopts.cachemode,"NEVER")==0) {
1139 		mfsopts.keepcache=2;
1140 	} else {
1141 		fprintf(stderr,"unrecognized cachemode option\nsee: %s -h for help\n",argv[0]);
1142 		return 1;
1143 	}
1144 	if (mfsopts.sugidclearmodestr==NULL) {
1145 #if defined(DEFAULT_SUGID_CLEAR_MODE_EXT)
1146 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_EXT;
1147 #elif defined(DEFAULT_SUGID_CLEAR_MODE_BSD)
1148 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_BSD;
1149 #elif defined(DEFAULT_SUGID_CLEAR_MODE_OSX)
1150 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_OSX;
1151 #else
1152 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_NEVER;
1153 #endif
1154 	} else if (strcasecmp(mfsopts.sugidclearmodestr,"NEVER")==0) {
1155 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_NEVER;
1156 	} else if (strcasecmp(mfsopts.sugidclearmodestr,"ALWAYS")==0) {
1157 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_ALWAYS;
1158 	} else if (strcasecmp(mfsopts.sugidclearmodestr,"OSX")==0) {
1159 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_OSX;
1160 	} else if (strcasecmp(mfsopts.sugidclearmodestr,"BSD")==0) {
1161 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_BSD;
1162 	} else if (strcasecmp(mfsopts.sugidclearmodestr,"EXT")==0) {
1163 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_EXT;
1164 	} else if (strcasecmp(mfsopts.sugidclearmodestr,"XFS")==0) {
1165 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_XFS;
1166 	} else {
1167 		fprintf(stderr,"unrecognized sugidclearmode option\nsee: %s -h for help\n",argv[0]);
1168 		return 1;
1169 	}
1170 	if (mfsopts.masterhost==NULL) {
1171 		mfsopts.masterhost = strdup(DEFAULT_MASTERNAME);
1172 	}
1173 	if (mfsopts.masterport==NULL) {
1174 		mfsopts.masterport = strdup(DEFAULT_MASTER_CLIENT_PORT);
1175 	}
1176 	if (mfsopts.proxyhost==NULL) {
1177 		mfsopts.proxyhost = strdup("127.0.0.1");
1178 	}
1179 	if (mfsopts.subfolder==NULL) {
1180 		mfsopts.subfolder = strdup("/");
1181 	}
1182 	if (mfsopts.nofile==0) {
1183 		mfsopts.nofile=100000;
1184 	}
1185 	if (mfsopts.writecachesize==0) {
1186 		mfsopts.writecachesize=128;
1187 	}
1188 	if (mfsopts.writecachesize<16) {
1189 		fprintf(stderr,"write cache size too low (%u MiB) - increased to 16 MiB\n",mfsopts.writecachesize);
1190 		mfsopts.writecachesize=16;
1191 	}
1192 	if (mfsopts.writecachesize>2048) {
1193 		fprintf(stderr,"write cache size too big (%u MiB) - decresed to 2048 MiB\n",mfsopts.writecachesize);
1194 		mfsopts.writecachesize=2048;
1195 	}
1196 	if (mfsopts.readaheadsize==0) {
1197 		mfsopts.readaheadsize=128;
1198 	}
1199 	if (mfsopts.readaheadsize<16) {
1200 		fprintf(stderr,"read ahead size too low (%u MiB) - increased to 16 MiB\n",mfsopts.readaheadsize);
1201 		mfsopts.readaheadsize=16;
1202 	}
1203 	if (mfsopts.readaheadsize>2048) {
1204 		fprintf(stderr,"read ahead size too big (%u MiB) - decresed to 2048 MiB\n",mfsopts.readaheadsize);
1205 		mfsopts.readaheadsize=2048;
1206 	}
1207 	if (mfsopts.readaheadleng==0) {
1208 		mfsopts.readaheadleng=0x100000;
1209 	}
1210 	if (mfsopts.readaheadleng<0x20000) {
1211 		fprintf(stderr,"read ahead length too low (%u B) - increased to 128 KiB\n",mfsopts.readaheadleng);
1212 		mfsopts.readaheadleng=0x20000;
1213 	}
1214 	if (mfsopts.readaheadleng>0x1000000) {
1215 		fprintf(stderr,"read ahead length too big (%u B) - decresed to 16 MiB\n",mfsopts.readaheadleng);
1216 		mfsopts.readaheadleng=0x1000000;
1217 	}
1218 	if (mfsopts.readaheadtrigger==0) {
1219 		mfsopts.readaheadtrigger=mfsopts.readaheadleng*10;
1220 	}
1221 
1222 	if (mfsopts.nostdmountoptions==0) {
1223 		fuse_opt_add_arg(&args, "-o" DEFAULT_OPTIONS);
1224 	}
1225 
1226 
1227 	make_fsname(&args);
1228 	remove_mfsmount_magic(&args);
1229 
1230 //	dump_args("combined_args_before_fuse_parse_cmdline",&args);
1231 
1232 	if (fuse_parse_cmdline(&args,&mountpoint,&mt,&fg)<0) {
1233 		fprintf(stderr,"see: %s -h for help\n",argv[0]);
1234 		return 1;
1235 	}
1236 
1237 	if (!mountpoint) {
1238 		if (defaultmountpoint) {
1239 			mountpoint = defaultmountpoint;
1240 		} else {
1241 			fprintf(stderr,"no mount point\nsee: %s -h for help\n",argv[0]);
1242 			return 1;
1243 		}
1244 	}
1245 
1246 	res = mainloop(&args,mountpoint,mt,fg);
1247 	fuse_opt_free_args(&defaultargs);
1248 	fuse_opt_free_args(&args);
1249 	free(mfsopts.masterhost);
1250 	free(mfsopts.masterport);
1251 	if (mfsopts.bindhost) {
1252 		free(mfsopts.bindhost);
1253 	}
1254 	if (mfsopts.proxyhost) {
1255 		free(mfsopts.proxyhost);
1256 	}
1257 	free(mfsopts.subfolder);
1258 	if (defaultmountpoint) {
1259 		free(defaultmountpoint);
1260 	}
1261 	stats_term();
1262 	strerr_term();
1263 	return res;
1264 }
1265