1 /*
2    Copyright 2005-2010 Jakub Kruszona-Zawadzki, Gemius SA, 2013-2014 EditShare, 2013-2015 Skytechnology sp. z o.o..
3 
4    This file was part of MooseFS and is part of LizardFS.
5 
6    LizardFS 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 3.
9 
10    LizardFS 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 LizardFS  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "common/platform.h"
20 
21 #include <errno.h>
22 #include <fstream>
23 #include <fuse/fuse.h>
24 
25 #include "common/crc.h"
26 #include "common/md5.h"
27 #include "protocol/MFSCommunication.h"
28 #include "common/mfserr.h"
29 #include "common/sockets.h"
30 #include "mount/fuse/mfs_fuse.h"
31 #include "mount/fuse/mfs_meta_fuse.h"
32 #include "mount/fuse/mount_config.h"
33 #include "mount/g_io_limiters.h"
34 #include "mount/mastercomm.h"
35 #include "mount/masterproxy.h"
36 #include "mount/readdata.h"
37 #include "mount/stats.h"
38 #include "mount/symlinkcache.h"
39 #include "mount/writedata.h"
40 
41 #define STR_AUX(x) #x
42 #define STR(x) STR_AUX(x)
43 
44 static void mfs_fsinit (void *userdata, struct fuse_conn_info *conn);
45 
46 static struct fuse_lowlevel_ops mfs_meta_oper;
47 
48 static struct fuse_lowlevel_ops mfs_oper;
49 
init_fuse_lowlevel_ops()50 static void init_fuse_lowlevel_ops() {
51 	mfs_meta_oper.init = mfs_fsinit;
52 	mfs_meta_oper.statfs = mfs_meta_statfs;
53 	mfs_meta_oper.lookup = mfs_meta_lookup;
54 	mfs_meta_oper.getattr = mfs_meta_getattr;
55 	mfs_meta_oper.setattr = mfs_meta_setattr;
56 	mfs_meta_oper.unlink = mfs_meta_unlink;
57 	mfs_meta_oper.rename = mfs_meta_rename;
58 	mfs_meta_oper.opendir = mfs_meta_opendir;
59 	mfs_meta_oper.readdir = mfs_meta_readdir;
60 	mfs_meta_oper.releasedir = mfs_meta_releasedir;
61 	mfs_meta_oper.open = mfs_meta_open;
62 	mfs_meta_oper.release = mfs_meta_release;
63 	mfs_meta_oper.read = mfs_meta_read;
64 	mfs_meta_oper.write = mfs_meta_write;
65 
66 	mfs_oper.init = mfs_fsinit;
67 	mfs_oper.statfs = mfs_statfs;
68 	mfs_oper.lookup = mfs_lookup;
69 	mfs_oper.getattr = mfs_getattr;
70 	mfs_oper.setattr = mfs_setattr;
71 	mfs_oper.mknod = mfs_mknod;
72 	mfs_oper.unlink = mfs_unlink;
73 	mfs_oper.mkdir = mfs_mkdir;
74 	mfs_oper.rmdir = mfs_rmdir;
75 	mfs_oper.symlink = mfs_symlink;
76 	mfs_oper.readlink = mfs_readlink;
77 	mfs_oper.rename = mfs_rename;
78 	mfs_oper.link = mfs_link;
79 	mfs_oper.opendir = mfs_opendir;
80 	mfs_oper.readdir = mfs_readdir;
81 	mfs_oper.releasedir = mfs_releasedir;
82 	mfs_oper.create = mfs_create;
83 	mfs_oper.open = mfs_open;
84 	mfs_oper.release = mfs_release;
85 	mfs_oper.flush = mfs_flush;
86 	mfs_oper.fsync = mfs_fsync;
87 	mfs_oper.read = mfs_read;
88 	mfs_oper.write = mfs_write;
89 	mfs_oper.access = mfs_access;
90 	mfs_oper.getxattr = mfs_getxattr;
91 	mfs_oper.setxattr = mfs_setxattr;
92 	mfs_oper.listxattr = mfs_listxattr;
93 	mfs_oper.removexattr = mfs_removexattr;
94 #if FUSE_VERSION >= 26
95 	if (gMountOptions.filelocks) {
96 		mfs_oper.getlk = lzfs_getlk;
97 		mfs_oper.setlk = lzfs_setlk;
98 	}
99 #endif
100 #if FUSE_VERSION >= 29
101 	if (gMountOptions.filelocks) {
102 		mfs_oper.flock = lzfs_flock;
103 	}
104 #endif
105 }
106 
mfs_fsinit(void * userdata,struct fuse_conn_info * conn)107 static void mfs_fsinit (void *userdata, struct fuse_conn_info *conn) {
108 #if (FUSE_VERSION >= 28)
109 	conn->want |= FUSE_CAP_DONT_MASK;
110 #else
111 		(void)conn;
112 #endif
113 
114 	int *piped = (int*)userdata;
115 	if (piped[1]>=0) {
116 		char s = 0;
117 		if (write(piped[1],&s,1)!=1) {
118 			lzfs_pretty_syslog(LOG_ERR,"pipe write error: %s",strerr(errno));
119 		}
120 		close(piped[1]);
121 	}
122 }
123 
mainloop(struct fuse_args * args,const char * mp,int mt,int fg)124 int mainloop(struct fuse_args *args,const char* mp,int mt,int fg) {
125 	struct fuse_session *se;
126 	struct fuse_chan *ch;
127 	struct rlimit rls;
128 	int piped[2];
129 	char s;
130 	int err;
131 	int i;
132 	md5ctx ctx;
133 	std::vector<uint8_t> md5pass;
134 
135 	if (gMountOptions.passwordask && gMountOptions.password==NULL
136 			&& gMountOptions.md5pass==NULL) {
137 		gMountOptions.password = getpass("MFS Password:");
138 	}
139 	if (gMountOptions.password) {
140 		md5pass.resize(16);
141 		md5_init(&ctx);
142 		md5_update(&ctx,(uint8_t*)(gMountOptions.password),
143 				strlen(gMountOptions.password));
144 		md5_final(md5pass.data(),&ctx);
145 		memset(gMountOptions.password,0,strlen(gMountOptions.password));
146 	} else if (gMountOptions.md5pass) {
147 		int ret = md5_parse(md5pass, gMountOptions.md5pass);
148 		if (ret < 0) {
149 			fprintf(stderr,"bad md5 definition (md5 should be given as 32 hex digits)\n");
150 			return 1;
151 		}
152 		memset(gMountOptions.md5pass,0,strlen(gMountOptions.md5pass));
153 	}
154 
155 	if (fg==0) {
156 		openlog(STR(APPNAME), LOG_PID | LOG_NDELAY , LOG_DAEMON);
157 	} else {
158 #if defined(LOG_PERROR)
159 		openlog(STR(APPNAME), LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_USER);
160 #else
161 		openlog(STR(APPNAME), LOG_PID | LOG_NDELAY, LOG_USER);
162 #endif
163 	}
164 	lzfs::add_log_syslog();
165 
166 	rls.rlim_cur = gMountOptions.nofile;
167 	rls.rlim_max = gMountOptions.nofile;
168 	setrlimit(RLIMIT_NOFILE,&rls);
169 
170 	setpriority(PRIO_PROCESS,getpid(),gMountOptions.nice);
171 #ifdef MFS_USE_MEMLOCK
172 	if (gMountOptions.memlock) {
173 		rls.rlim_cur = RLIM_INFINITY;
174 		rls.rlim_max = RLIM_INFINITY;
175 		if (setrlimit(RLIMIT_MEMLOCK,&rls)<0) {
176 			gMountOptions.memlock=0;
177 		}
178 	}
179 #endif
180 
181 	piped[0] = piped[1] = -1;
182 	if (fg==0) {
183 		if (pipe(piped)<0) {
184 			fprintf(stderr,"pipe error\n");
185 			return 1;
186 		}
187 		err = fork();
188 		if (err<0) {
189 			fprintf(stderr,"fork error\n");
190 			return 1;
191 		} else if (err > 0) {
192 			std::fill(md5pass.begin(), md5pass.end(), 0);
193 			close(piped[1]);
194 			err = read(piped[0],&s,1);
195 			if (err==0) {
196 				s=1;
197 			}
198 			return s;
199 		}
200 		close(piped[0]);
201 		s=1;
202 	} else {
203 		lzfs::add_log_stderr(lzfs::log_level::debug);
204 	}
205 
206 
207 #ifdef MFS_USE_MEMLOCK
208 	if (gMountOptions.memlock) {
209 		if (mlockall(MCL_CURRENT|MCL_FUTURE)==0) {
210 			lzfs_pretty_syslog(LOG_NOTICE,"process memory was successfully locked in RAM");
211 		}
212 	}
213 #endif
214 
215 	LizardClient::FsInitParams params(
216 		gMountOptions.bindhost ? gMountOptions.bindhost : "",
217 		gMountOptions.masterhost, gMountOptions.masterport, mp);
218 	params.verbose = true;
219 	params.meta = gMountOptions.meta;
220 	params.subfolder = gMountOptions.subfolder;
221 	params.password_digest = std::move(md5pass);
222 	params.do_not_remember_password = gMountOptions.donotrememberpassword;
223 	params.delayed_init = gMountOptions.delayedinit;
224 	params.report_reserved_period = gMountOptions.reportreservedperiod;
225 	params.io_retries = gMountOptions.ioretries;
226 	params.io_limits_config_file = gMountOptions.iolimits ? gMountOptions.iolimits : "";
227 	params.bandwidth_overuse = gMountOptions.bandwidthoveruse;
228 
229 	params.chunkserver_round_time_ms = gMountOptions.chunkserverrtt;
230 	params.chunkserver_connect_timeout_ms = gMountOptions.chunkserverconnectreadto;
231 	params.chunkserver_wave_read_timeout_ms = gMountOptions.chunkserverwavereadto;
232 	params.total_read_timeout_ms = gMountOptions.chunkservertotalreadto;
233 	params.cache_expiration_time_ms = gMountOptions.cacheexpirationtime;
234 	params.readahead_max_window_size_kB = gMountOptions.readaheadmaxwindowsize;
235 	params.prefetch_xor_stripes = gMountOptions.prefetchxorstripes;
236 	params.bandwidth_overuse = gMountOptions.bandwidthoveruse;
237 	params.write_cache_size = gMountOptions.writecachesize;
238 	params.write_workers = gMountOptions.writeworkers;
239 	params.write_window_size = gMountOptions.writewindowsize;
240 	params.chunkserver_write_timeout_ms = gMountOptions.chunkserverwriteto;
241 	params.cache_per_inode_percentage = gMountOptions.cachePerInodePercentage;
242 
243 	params.keep_cache = gMountOptions.keepcache;
244 	params.direntry_cache_timeout = gMountOptions.direntrycacheto;
245 	params.direntry_cache_size = gMountOptions.direntrycachesize;
246 	params.entry_cache_timeout = gMountOptions.entrycacheto;
247 	params.attr_cache_timeout = gMountOptions.attrcacheto;
248 	params.mkdir_copy_sgid = gMountOptions.mkdircopysgid;
249 	params.sugid_clear_mode = gMountOptions.sugidclearmode;
250 	params.use_rw_lock = gMountOptions.rwlock;
251 	params.acl_cache_timeout = gMountOptions.aclcacheto;
252 	params.acl_cache_size = gMountOptions.aclcachesize;
253 
254 	params.debug_mode = gMountOptions.debug;
255 	if (gMountOptions.meta == 0) {
256 		try {
257 			LizardClient::fs_init(params);
258 		} catch (...) {
259 			if (piped[1] >= 0) {
260 				if (write(piped[1], &s, 1) != 1) {
261 					fprintf(stderr,"pipe write error\n");
262 				}
263 				close(piped[1]);
264 			}
265 			return 1;
266 		}
267 	} else {
268 		masterproxy_init();
269 		symlink_cache_init();
270 		if (gMountOptions.delayedinit) {
271 			fs_init_master_connection(params);
272 		} else {
273 			if (fs_init_master_connection(params) < 0) {
274 				if (piped[1] >= 0) {
275 					if (write(piped[1], &s, 1) != 1) {
276 						fprintf(stderr,"pipe write error\n");
277 					}
278 					close(piped[1]);
279 				}
280 				return 1;
281 			}
282 		}
283 		fs_init_threads(params.io_retries);
284 	}
285 
286 	ch = fuse_mount(mp, args);
287 	if (ch == NULL) {
288 		fprintf(stderr,"error in fuse_mount\n");
289 		if (piped[1]>=0) {
290 			if (write(piped[1], &s, 1) != 1) {
291 				fprintf(stderr,"pipe write error\n");
292 			}
293 			close(piped[1]);
294 		}
295 		if (gMountOptions.meta == 0) {
296 			LizardClient::fs_term();
297 		} else {
298 			masterproxy_term();
299 			fs_term();
300 			symlink_cache_term();
301 		}
302 		return 1;
303 	}
304 
305 	if (gMountOptions.meta) {
306 		mfs_meta_init(gMountOptions.debug,
307 				gMountOptions.entrycacheto,
308 				gMountOptions.attrcacheto);
309 		se = fuse_lowlevel_new(args, &mfs_meta_oper, sizeof(mfs_meta_oper), (void*)piped);
310 	} else {
311 		se = fuse_lowlevel_new(args, &mfs_oper, sizeof(mfs_oper), (void*)piped);
312 	}
313 	if (se == NULL) {
314 		fuse_unmount(mp,ch);
315 		fprintf(stderr,"error in fuse_lowlevel_new\n");
316 		usleep(100000); // time for print other error messages by FUSE
317 		if (piped[1]>=0) {
318 			if (write(piped[1],&s,1)!=1) {
319 				fprintf(stderr,"pipe write error\n");
320 			}
321 			close(piped[1]);
322 		}
323 		if (gMountOptions.meta == 0) {
324 			LizardClient::fs_term();
325 		} else {
326 			masterproxy_term();
327 			fs_term();
328 			symlink_cache_term();
329 		}
330 		return 1;
331 	}
332 
333 	fuse_session_add_chan(se, ch);
334 
335 	if (fuse_set_signal_handlers(se)<0) {
336 		fprintf(stderr,"error in fuse_set_signal_handlers\n");
337 		fuse_session_remove_chan(ch);
338 		fuse_session_destroy(se);
339 		fuse_unmount(mp,ch);
340 		if (piped[1]>=0) {
341 			if (write(piped[1],&s,1)!=1) {
342 				fprintf(stderr,"pipe write error\n");
343 			}
344 			close(piped[1]);
345 		}
346 		if (gMountOptions.meta == 0) {
347 			LizardClient::fs_term();
348 		} else {
349 			masterproxy_term();
350 			fs_term();
351 			symlink_cache_term();
352 		}
353 		return 1;
354 	}
355 
356 	if (gMountOptions.debug==0 && fg==0) {
357 		setsid();
358 		setpgid(0,getpid());
359 		if ((i = open("/dev/null", O_RDWR, 0)) != -1) {
360 			(void)dup2(i, STDIN_FILENO);
361 			(void)dup2(i, STDOUT_FILENO);
362 			(void)dup2(i, STDERR_FILENO);
363 			if (i>2) close (i);
364 		}
365 	}
366 
367 	if (mt) {
368 		err = fuse_session_loop_mt(se);
369 	} else {
370 		err = fuse_session_loop(se);
371 	}
372 	if (err) {
373 		if (piped[1]>=0) {
374 			if (write(piped[1],&s,1)!=1) {
375 				lzfs_pretty_syslog(LOG_ERR,"pipe write error: %s",strerr(errno));
376 			}
377 			close(piped[1]);
378 		}
379 	}
380 	fuse_remove_signal_handlers(se);
381 	fuse_session_remove_chan(ch);
382 	fuse_session_destroy(se);
383 	fuse_unmount(mp,ch);
384 	if (gMountOptions.meta == 0) {
385 		LizardClient::fs_term();
386 	} else {
387 		masterproxy_term();
388 		fs_term();
389 		symlink_cache_term();
390 	}
391 	return err ? 1 : 0;
392 }
393 
394 #if FUSE_VERSION == 25
fuse_opt_insert_arg(struct fuse_args * args,int pos,const char * arg)395 static int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) {
396 	assert(pos <= args->argc);
397 	if (fuse_opt_add_arg(args, arg) == -1) {
398 		return -1;
399 	}
400 	if (pos != args->argc - 1) {
401 		char *newarg = args->argv[args->argc - 1];
402 		memmove(&args->argv[pos + 1], &args->argv[pos], sizeof(char *) * (args->argc - pos - 1));
403 		args->argv[pos] = newarg;
404 	}
405 	return 0;
406 }
407 #endif
408 
strncpy_remove_commas(char * dstbuff,unsigned int dstsize,char * src)409 static unsigned int strncpy_remove_commas(char *dstbuff, unsigned int dstsize,char *src) {
410 	char c;
411 	unsigned int l;
412 	l=0;
413 	while ((c=*src++) && l+1<dstsize) {
414 		if (c!=',') {
415 			*dstbuff++ = c;
416 			l++;
417 		}
418 	}
419 	*dstbuff=0;
420 	return l;
421 }
422 
423 #if LIZARDFS_HAVE_FUSE_VERSION
strncpy_escape_commas(char * dstbuff,unsigned int dstsize,char * src)424 static unsigned int strncpy_escape_commas(char *dstbuff, unsigned int dstsize,char *src) {
425 	char c;
426 	unsigned int l;
427 	l=0;
428 	while ((c=*src++) && l+1<dstsize) {
429 		if (c!=',' && c!='\\') {
430 			*dstbuff++ = c;
431 			l++;
432 		} else {
433 			if (l+2<dstsize) {
434 				*dstbuff++ = '\\';
435 				*dstbuff++ = c;
436 				l+=2;
437 			} else {
438 				*dstbuff=0;
439 				return l;
440 			}
441 		}
442 	}
443 	*dstbuff=0;
444 	return l;
445 }
446 #endif
447 
make_fsname(struct fuse_args * args)448 void make_fsname(struct fuse_args *args) {
449 	char fsnamearg[256];
450 	unsigned int l;
451 #if LIZARDFS_HAVE_FUSE_VERSION
452 	int libver;
453 	libver = fuse_version();
454 	if (libver >= 27) {
455 		l = snprintf(fsnamearg,256,"-osubtype=mfs%s,fsname=",(gMountOptions.meta)?"meta":"");
456 		if (libver >= 28) {
457 			l += strncpy_escape_commas(fsnamearg+l,256-l,gMountOptions.masterhost);
458 			if (l<255) {
459 				fsnamearg[l++]=':';
460 			}
461 			l += strncpy_escape_commas(fsnamearg+l,256-l,gMountOptions.masterport);
462 			if (gMountOptions.subfolder[0]!='/') {
463 				if (l<255) {
464 					fsnamearg[l++]='/';
465 				}
466 			}
467 			if (gMountOptions.subfolder[0]!='/' && gMountOptions.subfolder[1]!=0) {
468 				l += strncpy_escape_commas(fsnamearg+l,256-l,gMountOptions.subfolder);
469 			}
470 			if (l>255) {
471 				l=255;
472 			}
473 			fsnamearg[l]=0;
474 		} else {
475 			l += strncpy_remove_commas(fsnamearg+l,256-l,gMountOptions.masterhost);
476 			if (l<255) {
477 				fsnamearg[l++]=':';
478 			}
479 			l += strncpy_remove_commas(fsnamearg+l,256-l,gMountOptions.masterport);
480 			if (gMountOptions.subfolder[0]!='/') {
481 				if (l<255) {
482 					fsnamearg[l++]='/';
483 				}
484 			}
485 			if (gMountOptions.subfolder[0]!='/' && gMountOptions.subfolder[1]!=0) {
486 				l += strncpy_remove_commas(fsnamearg+l,256-l,gMountOptions.subfolder);
487 			}
488 			if (l>255) {
489 				l=255;
490 			}
491 			fsnamearg[l]=0;
492 		}
493 	} else {
494 #else
495 		l = snprintf(fsnamearg,256,"-ofsname=mfs%s#",(gMountOptions.meta)?"meta":"");
496 		l += strncpy_remove_commas(fsnamearg+l,256-l,gMountOptions.masterhost);
497 		if (l<255) {
498 			fsnamearg[l++]=':';
499 		}
500 		l += strncpy_remove_commas(fsnamearg+l,256-l,gMountOptions.masterport);
501 		if (gMountOptions.subfolder[0]!='/') {
502 			if (l<255) {
503 				fsnamearg[l++]='/';
504 			}
505 		}
506 		if (gMountOptions.subfolder[0]!='/' && gMountOptions.subfolder[1]!=0) {
507 			l += strncpy_remove_commas(fsnamearg+l,256-l,gMountOptions.subfolder);
508 		}
509 		if (l>255) {
510 			l=255;
511 		}
512 		fsnamearg[l]=0;
513 #endif
514 #if LIZARDFS_HAVE_FUSE_VERSION
515 	}
516 #endif
517 	fuse_opt_insert_arg(args, 1, fsnamearg);
518 }
519 
main(int argc,char * argv[])520 int main(int argc, char *argv[]) try {
521 	int res;
522 	int mt,fg;
523 	char *mountpoint;
524 	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
525 	struct fuse_args defaultargs = FUSE_ARGS_INIT(0, NULL);
526 
527 	fuse_opt_add_arg(&defaultargs,"fakeappname");
528 
529 	if (fuse_opt_parse(&args, &defaultargs, gMfsOptsStage1, mfs_opt_proc_stage1)<0) {
530 		exit(1);
531 	}
532 
533 	if (gCustomCfg==0) {
534 		mfs_opt_parse_cfg_file(ETC_PATH "/mfsmount.cfg",1,&defaultargs);
535 	}
536 
537 	if (fuse_opt_parse(&defaultargs, &gMountOptions, gMfsOptsStage2, mfs_opt_proc_stage2)<0) {
538 		exit(1);
539 	}
540 
541 	if (fuse_opt_parse(&args, &gMountOptions, gMfsOptsStage2, mfs_opt_proc_stage2)<0) {
542 		exit(1);
543 	}
544 
545 	init_fuse_lowlevel_ops();
546 
547 	if (gMountOptions.cachemode!=NULL && gMountOptions.cachefiles) {
548 		fprintf(stderr,"mfscachemode and mfscachefiles options are exclusive - use only mfscachemode\nsee: %s -h for help\n",argv[0]);
549 		return 1;
550 	}
551 
552 	if (gMountOptions.cachemode==NULL) {
553 		gMountOptions.keepcache=(gMountOptions.cachefiles)?1:0;
554 	} else if (strcasecmp(gMountOptions.cachemode,"AUTO")==0) {
555 		gMountOptions.keepcache=0;
556 	} else if (strcasecmp(gMountOptions.cachemode,"YES")==0 || strcasecmp(gMountOptions.cachemode,"ALWAYS")==0) {
557 		gMountOptions.keepcache=1;
558 	} else if (strcasecmp(gMountOptions.cachemode,"NO")==0
559 			|| strcasecmp(gMountOptions.cachemode,"NONE")==0
560 			|| strcasecmp(gMountOptions.cachemode,"NEVER")==0) {
561 		gMountOptions.keepcache=2;
562 		gMountOptions.cacheexpirationtime=0;
563 	} else {
564 		fprintf(stderr,"unrecognized cachemode option\nsee: %s -h for help\n",argv[0]);
565 		return 1;
566 	}
567 	if (gMountOptions.sugidclearmodestr==NULL) {
568 		gMountOptions.sugidclearmode = LizardClient::FsInitParams::kDefaultSugidClearMode;
569 	} else if (strcasecmp(gMountOptions.sugidclearmodestr,"NEVER")==0) {
570 		gMountOptions.sugidclearmode = SugidClearMode::kNever;
571 	} else if (strcasecmp(gMountOptions.sugidclearmodestr,"ALWAYS")==0) {
572 		gMountOptions.sugidclearmode = SugidClearMode::kAlways;
573 	} else if (strcasecmp(gMountOptions.sugidclearmodestr,"OSX")==0) {
574 		gMountOptions.sugidclearmode = SugidClearMode::kOsx;
575 	} else if (strcasecmp(gMountOptions.sugidclearmodestr,"BSD")==0) {
576 		gMountOptions.sugidclearmode = SugidClearMode::kBsd;
577 	} else if (strcasecmp(gMountOptions.sugidclearmodestr,"EXT")==0) {
578 		gMountOptions.sugidclearmode = SugidClearMode::kExt;
579 	} else if (strcasecmp(gMountOptions.sugidclearmodestr,"XFS")==0) {
580 		gMountOptions.sugidclearmode = SugidClearMode::kXfs;
581 	} else {
582 		fprintf(stderr,"unrecognized sugidclearmode option\nsee: %s -h for help\n",argv[0]);
583 		return 1;
584 	}
585 	if (gMountOptions.masterhost==NULL) {
586 		gMountOptions.masterhost = strdup("mfsmaster");
587 	}
588 	if (gMountOptions.masterport==NULL) {
589 		gMountOptions.masterport = strdup("9421");
590 	}
591 	if (gMountOptions.subfolder==NULL) {
592 		gMountOptions.subfolder = strdup("/");
593 	}
594 	if (gMountOptions.nofile==0) {
595 		gMountOptions.nofile=100000;
596 	}
597 	if (gMountOptions.writecachesize==0) {
598 		gMountOptions.writecachesize=128;
599 	}
600 	if (gMountOptions.cachePerInodePercentage < 1) {
601 		fprintf(stderr, "cache per inode percentage too low (%u %%) - increased to 1%%\n",
602 				gMountOptions.cachePerInodePercentage);
603 		gMountOptions.cachePerInodePercentage = 1;
604 	}
605 	if (gMountOptions.cachePerInodePercentage > 100) {
606 		fprintf(stderr, "cache per inode percentage too big (%u %%) - decreased to 100%%\n",
607 				gMountOptions.cachePerInodePercentage);
608 		gMountOptions.cachePerInodePercentage = 100;
609 	}
610 	if (gMountOptions.writecachesize<16) {
611 		fprintf(stderr,"write cache size too low (%u MiB) - increased to 16 MiB\n",
612 				gMountOptions.writecachesize);
613 		gMountOptions.writecachesize=16;
614 	}
615 	if (gMountOptions.writecachesize>1024*1024) {
616 		fprintf(stderr,"write cache size too big (%u MiB) - decreased to 1 TiB\n",
617 				gMountOptions.writecachesize);
618 		gMountOptions.writecachesize=1024*1024;
619 	}
620 	if (gMountOptions.writeworkers<1) {
621 		fprintf(stderr,"no write workers - increasing number of workers to 1\n");
622 		gMountOptions.writeworkers=1;
623 	}
624 	if (gMountOptions.writewindowsize < 1) {
625 		fprintf(stderr,"write window size is 0 - increasing to 1\n");
626 		gMountOptions.writewindowsize = 1;
627 	}
628 	if (gMountOptions.nostdmountoptions==0) {
629 		fuse_opt_add_arg(&args, "-o" DEFAULT_OPTIONS);
630 	}
631 	if (gMountOptions.aclcachesize > 1000 * 1000) {
632 		fprintf(stderr,"acl cache size too big (%u) - decreased to 1000000\n",
633 				gMountOptions.aclcachesize);
634 		gMountOptions.aclcachesize = 1000 * 1000;
635 	}
636 	if (gMountOptions.direntrycachesize > 10000000) {
637 		fprintf(stderr,"directory entry cache size too big (%u) - decreased to 10000000\n",
638 				gMountOptions.direntrycachesize);
639 		gMountOptions.direntrycachesize = 10000000;
640 	}
641 
642 	make_fsname(&args);
643 
644 	if (fuse_parse_cmdline(&args,&mountpoint,&mt,&fg)<0) {
645 		fprintf(stderr,"see: %s -h for help\n",argv[0]);
646 		return 1;
647 	}
648 
649 	if (!mountpoint) {
650 		if (gDefaultMountpoint) {
651 			mountpoint = gDefaultMountpoint;
652 		} else {
653 			fprintf(stderr,"no mount point\nsee: %s -h for help\n",argv[0]);
654 			return 1;
655 		}
656 	}
657 
658 	res = mainloop(&args,mountpoint,mt,fg);
659 	fuse_opt_free_args(&args);
660 	fuse_opt_free_args(&defaultargs);
661 	free(gMountOptions.masterhost);
662 	free(gMountOptions.masterport);
663 	if (gMountOptions.bindhost) {
664 		free(gMountOptions.bindhost);
665 	}
666 	free(gMountOptions.subfolder);
667 	if (gDefaultMountpoint && gDefaultMountpoint != mountpoint) {
668 		free(gDefaultMountpoint);
669 	}
670 	if (gMountOptions.iolimits) {
671 		free(gMountOptions.iolimits);
672 	}
673 	free(mountpoint);
674 	stats_term();
675 	return res;
676 } catch (std::bad_alloc ex) {
677 	mabort("run out of memory");
678 }
679