1 /*
2  * Copyright (C) 2021 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)
26 #  if defined(HAVE_SYS_MMAN_H)
27 #    include <sys/mman.h>
28 #  endif
29 #  if defined(HAVE_SYS_RESOURCE_H)
30 #    include <sys/resource.h>
31 #  endif
32 #  if defined(RLIMIT_MEMLOCK) && defined(MCL_CURRENT) && defined(MCL_FUTURE)
33 #    define MFS_USE_MEMLOCK 1
34 #  endif
35 #endif
36 
37 #if defined(HAVE_MALLOC_H)
38 #  include <malloc.h>
39 #endif
40 #if defined(M_ARENA_MAX) && defined(M_ARENA_TEST) && defined(HAVE_MALLOPT)
41 #  define MFS_USE_MALLOPT 1
42 #endif
43 
44 #if defined(HAVE_LINUX_OOM_H)
45 #  include <linux/oom.h>
46 #  if defined(OOM_DISABLE) || defined(OOM_SCORE_ADJ_MIN)
47 #    define OOM_ADJUSTABLE 1
48 #  endif
49 #endif
50 
51 #if defined(HAVE_SYS_UTSNAME_H)
52 #  include <sys/utsname.h>
53 #  define MFS_GET_KERNEL_VERSION 1
54 #endif
55 
56 #include "fusecommon.h"
57 
58 #include <fuse.h>
59 #include <fuse_opt.h>
60 #include <sys/time.h>
61 #include <sys/resource.h>
62 #include <sys/stat.h>
63 #include <dirent.h>
64 #include <unistd.h>
65 #include <fcntl.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <strings.h>
70 #include <stddef.h>
71 #include <unistd.h>
72 #include <syslog.h>
73 #include <signal.h>
74 #include <errno.h>
75 #include <pthread.h>
76 
77 #include "mfs_fuse.h"
78 #include "mfs_meta_fuse.h"
79 
80 #include "MFSCommunication.h"
81 #include "clocks.h"
82 #include "massert.h"
83 #include "portable.h"
84 #include "md5.h"
85 #include "mastercomm.h"
86 #include "masterproxy.h"
87 #include "csorder.h"
88 #include "sustained_parents.h"
89 #include "sustained_inodes.h"
90 #include "sustained_stats.h"
91 #include "symlinkcache.h"
92 #include "negentrycache.h"
93 //#include "dircache.h"
94 #include "chunksdatacache.h"
95 #include "inoleng.h"
96 #include "conncache.h"
97 #include "chunkrwlock.h"
98 #include "readdata.h"
99 #include "writedata.h"
100 #include "delayrun.h"
101 #include "csdb.h"
102 #include "stats.h"
103 #include "strerr.h"
104 #include "crc.h"
105 #include "processname.h"
106 #include "idstr.h"
107 
108 #define STR_AUX(x) #x
109 #define STR(x) STR_AUX(x)
110 
111 #if defined(__APPLE__)
112 #define DEFAULT_OPTIONS "allow_other,daemon_timeout=600,novncache"
113 // #define DEFAULT_OPTIONS "allow_other,default_permissions,daemon_timeout=600,iosize=65536,novncache"
114 #else
115 #define DEFAULT_OPTIONS "allow_other"
116 #endif
117 
118 #if FUSE_VERSION >= 30 || defined(__APPLE__) || defined(__FreeBSD__)
119 #define FUSE_ALLOWS_NOT_EMPTY_DIRS 1
120 #endif
121 
122 static void mfs_fsinit (void *userdata, struct fuse_conn_info *conn);
123 
124 static struct fuse_lowlevel_ops mfs_meta_oper = {
125 	.init           = mfs_fsinit,
126 	.statfs         = mfs_meta_statfs,
127 	.lookup         = mfs_meta_lookup,
128 	.getattr        = mfs_meta_getattr,
129 	.setattr        = mfs_meta_setattr,
130 	.unlink         = mfs_meta_unlink,
131 	.rename         = mfs_meta_rename,
132 	.opendir        = mfs_meta_opendir,
133 	.readdir        = mfs_meta_readdir,
134 	.releasedir     = mfs_meta_releasedir,
135 	.open           = mfs_meta_open,
136 	.release        = mfs_meta_release,
137 	.read           = mfs_meta_read,
138 	.write          = mfs_meta_write,
139 //	.access         = mfs_meta_access,
140 };
141 
142 static struct fuse_lowlevel_ops mfs_oper = {
143 	.init           = mfs_fsinit,
144 	.statfs         = mfs_statfs,
145 	.lookup         = mfs_lookup,
146 	.getattr        = mfs_getattr,
147 	.setattr        = mfs_setattr,
148 	.mknod          = mfs_mknod,
149 	.unlink         = mfs_unlink,
150 	.mkdir          = mfs_mkdir,
151 	.rmdir          = mfs_rmdir,
152 	.symlink        = mfs_symlink,
153 	.readlink       = mfs_readlink,
154 	.rename         = mfs_rename,
155 	.link           = mfs_link,
156 	.opendir        = mfs_opendir,
157 	.readdir        = mfs_readdir,
158 	.releasedir     = mfs_releasedir,
159 	.create         = mfs_create,
160 	.open           = mfs_open,
161 	.release        = mfs_release,
162 	.flush          = mfs_flush,
163 	.fsync          = mfs_fsync,
164 	.read           = mfs_read,
165 	.write          = mfs_write,
166 	.access         = mfs_access,
167 	.getxattr       = mfs_getxattr,
168 	.setxattr       = mfs_setxattr,
169 	.listxattr      = mfs_listxattr,
170 	.removexattr    = mfs_removexattr,
171 #if FUSE_VERSION >= 26
172 	.getlk		= mfs_getlk,
173 	.setlk		= mfs_setlk,
174 #endif
175 #if FUSE_VERSION >= 29
176 	.flock		= mfs_flock,
177 #endif
178 #if FUSE_VERSION >= 30
179 	.readdirplus	= mfs_readdirplus,
180 #endif
181 };
182 
183 struct mfsopts {
184 	char *masterhost;
185 	char *masterport;
186 	char *bindhost;
187 	char *proxyhost;
188 	char *subfolder;
189 	char *password;
190 	char *passfile;
191 	char *md5pass;
192 	char *preferedlabels;
193 	unsigned nofile;
194 	signed nice;
195 	int mfssuid;
196 	int mfsdev;
197 #ifdef MFS_USE_MEMLOCK
198 	int memlock;
199 #endif
200 #ifdef MFS_USE_MALLOPT
201 	int limitarenas;
202 #endif
203 #if defined(__linux__) && defined(OOM_ADJUSTABLE)
204 	int allowoomkiller;
205 #endif
206 #ifdef FUSE_ALLOWS_NOT_EMPTY_DIRS
207 	int nonempty;
208 #endif
209 	int nostdmountoptions;
210 	int meta;
211 	int debug;
212 	int flattrash;
213 	int delayedinit;
214 	unsigned int mkdircopysgid;
215 	char *sugidclearmodestr;
216 	int sugidclearmode;
217 	char *cachemode;
218 	int cachefiles;
219 	int keepcache;
220 	int passwordask;
221 	int noxattrs;
222 	int noposixlocks;
223 	int nobsdlocks;
224 	int donotrememberpassword;
225 //	int xattraclsupport;
226 	unsigned writecachesize;
227 	unsigned readaheadsize;
228 	unsigned readaheadleng;
229 	unsigned readaheadtrigger;
230 	int erroronlostchunk;
231 	int erroronnospace;
232 	unsigned ioretries;
233 	unsigned timeout;
234 	unsigned logretry;
235 	double attrcacheto;
236 	double xattrcacheto;
237 	double entrycacheto;
238 	double direntrycacheto;
239 	double negentrycacheto;
240 	double symlinkcacheto;
241 	double groupscacheto;
242 	double fsyncmintime;
243 	int fsyncbeforeclose;
244 	int netdev; // only for ignoring '_netdev' option
245 };
246 
247 static struct mfsopts mfsopts;
248 static char *defaultmountpoint = NULL;
249 
250 static int custom_cfg;
251 
252 enum {
253 	KEY_CFGFILE,
254 	KEY_META,
255 	KEY_HOST,
256 	KEY_PORT,
257 	KEY_BIND,
258 	KEY_PROXY,
259 	KEY_PATH,
260 	KEY_PASSWORDASK,
261 	KEY_NOSTDMOUNTOPTIONS,
262 	KEY_HELP,
263 	KEY_VERSION,
264 };
265 
266 #define MFS_OPT(t, p, v) { t, offsetof(struct mfsopts, p), v }
267 
268 static struct fuse_opt mfs_opts_stage1[] = {
269 	FUSE_OPT_KEY("mfscfgfile=",    KEY_CFGFILE),
270 	FUSE_OPT_KEY("-c ",            KEY_CFGFILE),
271 	FUSE_OPT_END
272 };
273 
274 static struct fuse_opt mfs_opts_stage2[] = {
275 	MFS_OPT("mfsmaster=%s", masterhost, 0),
276 	MFS_OPT("mfsport=%s", masterport, 0),
277 	MFS_OPT("mfsbind=%s", bindhost, 0),
278 	MFS_OPT("mfsproxy=%s", proxyhost, 0),
279 	MFS_OPT("mfssubfolder=%s", subfolder, 0),
280 	MFS_OPT("mfspassword=%s", password, 0),
281 	MFS_OPT("mfspassfile=%s", passfile, 0),
282 	MFS_OPT("mfsmd5pass=%s", md5pass, 0),
283 	MFS_OPT("mfspreflabels=%s", preferedlabels, 0),
284 	MFS_OPT("mfsrlimitnofile=%u", nofile, 0),
285 	MFS_OPT("mfsnice=%d", nice, 0),
286 	MFS_OPT("mfssuid", mfssuid, 1),
287 	MFS_OPT("mfsdev", mfsdev, 1),
288 #ifdef MFS_USE_MEMLOCK
289 	MFS_OPT("mfsmemlock", memlock, 1),
290 #endif
291 #ifdef MFS_USE_MALLOPT
292 	MFS_OPT("mfslimitarenas=%u", limitarenas, 0),
293 #endif
294 #if defined(__linux__) && defined(OOM_ADJUSTABLE)
295 	MFS_OPT("mfsallowoomkiller", allowoomkiller, 1),
296 #endif
297 #ifdef FUSE_ALLOWS_NOT_EMPTY_DIRS
298 	MFS_OPT("nonempty", nonempty, 1),
299 #endif
300 	MFS_OPT("mfswritecachesize=%u", writecachesize, 0),
301 	MFS_OPT("mfsreadaheadsize=%u", readaheadsize, 0),
302 	MFS_OPT("mfsreadaheadleng=%u", readaheadleng, 0),
303 	MFS_OPT("mfsreadaheadtrigger=%u", readaheadtrigger, 0),
304 	MFS_OPT("mfserroronlostchunk", erroronlostchunk, 1),
305 	MFS_OPT("mfserroronnospace", erroronnospace, 1),
306 	MFS_OPT("mfsioretries=%u", ioretries, 0),
307 	MFS_OPT("mfstimeout=%u", timeout, 0),
308 	MFS_OPT("mfslogretry=%u", logretry, 0),
309 	MFS_OPT("mfsdebug", debug, 1),
310 	MFS_OPT("mfsmeta", meta, 1),
311 	MFS_OPT("mfsflattrash", flattrash, 1),
312 	MFS_OPT("mfsdelayedinit", delayedinit, 1),
313 	MFS_OPT("mfsdonotrememberpassword", donotrememberpassword, 1),
314 	MFS_OPT("mfscachefiles", cachefiles, 1),
315 	MFS_OPT("mfsnoxattr", noxattrs, 1),
316 	MFS_OPT("mfsnoxattrs", noxattrs, 1),
317 	MFS_OPT("mfsnoposixlock", noposixlocks, 1),
318 	MFS_OPT("mfsnoposixlocks", noposixlocks, 1),
319 	MFS_OPT("mfsnobsdlock", nobsdlocks, 1),
320 	MFS_OPT("mfsnobsdlocks", nobsdlocks, 1),
321 	MFS_OPT("mfscachemode=%s", cachemode, 0),
322 	MFS_OPT("mfsmkdircopysgid=%u", mkdircopysgid, 0),
323 	MFS_OPT("mfssugidclearmode=%s", sugidclearmodestr, 0),
324 	MFS_OPT("mfsattrcacheto=%lf", attrcacheto, 0),
325 	MFS_OPT("mfsxattrcacheto=%lf", xattrcacheto, 0),
326 	MFS_OPT("mfsentrycacheto=%lf", entrycacheto, 0),
327 	MFS_OPT("mfsdirentrycacheto=%lf", direntrycacheto, 0),
328 	MFS_OPT("mfsnegentrycacheto=%lf", negentrycacheto, 0),
329 	MFS_OPT("mfssymlinkcacheto=%lf", symlinkcacheto, 0),
330 	MFS_OPT("mfsgroupscacheto=%lf", groupscacheto, 0),
331 //	MFS_OPT("mfsaclsupport", xattraclsupport, 1),
332 	MFS_OPT("mfsfsyncmintime=%lf", fsyncmintime, 0),
333 	MFS_OPT("mfsfsyncbeforeclose", fsyncbeforeclose, 1),
334 	MFS_OPT("_netdev", netdev, 1),
335 
336 	FUSE_OPT_KEY("-m",             KEY_META),
337 	FUSE_OPT_KEY("--meta",         KEY_META),
338 	FUSE_OPT_KEY("-H ",            KEY_HOST),
339 	FUSE_OPT_KEY("-P ",            KEY_PORT),
340 	FUSE_OPT_KEY("-B ",            KEY_BIND),
341 	FUSE_OPT_KEY("-L ",            KEY_PROXY),
342 	FUSE_OPT_KEY("-S ",            KEY_PATH),
343 	FUSE_OPT_KEY("-p",             KEY_PASSWORDASK),
344 	FUSE_OPT_KEY("--password",     KEY_PASSWORDASK),
345 	FUSE_OPT_KEY("-n",             KEY_NOSTDMOUNTOPTIONS),
346 	FUSE_OPT_KEY("--nostdopts",    KEY_NOSTDMOUNTOPTIONS),
347 	FUSE_OPT_KEY("-V",             KEY_VERSION),
348 	FUSE_OPT_KEY("--version",      KEY_VERSION),
349 	FUSE_OPT_KEY("-h",             KEY_HELP),
350 	FUSE_OPT_KEY("--help",         KEY_HELP),
351 	FUSE_OPT_END
352 };
353 
usage(const char * progname)354 static void usage(const char *progname) {
355 	FILE *fd;
356 
357 #if FUSE_VERSION >= 30
358 	fd = stdout;
359 #else /* FUSE2 */
360 	fd = stderr;
361 #endif
362 
363 	fprintf(fd,"usage: %s [HOST[:PORT]:[PATH]] [options] mountpoint\n",progname);
364 	fprintf(fd,"\n");
365 	fprintf(fd,"general options:\n");
366 	fprintf(fd,"    -o opt,[opt...]         mount options\n");
367 #if FUSE_VERSION >= 30
368 	fuse_cmdline_help();
369 #else /* FUSE2 */
370 	fprintf(fd,"    -h   --help             print help\n");
371 	fprintf(fd,"    -V   --version          print version\n");
372 #endif
373 	fprintf(fd,"\n");
374 	fprintf(fd,"MFS options:\n");
375 	fprintf(fd,"    -c CFGFILE                  equivalent to '-o mfscfgfile=CFGFILE'\n");
376 	fprintf(fd,"    -m   --meta                 equivalent to '-o mfsmeta'\n");
377 	fprintf(fd,"    -H HOST                     equivalent to '-o mfsmaster=HOST'\n");
378 	fprintf(fd,"    -P PORT                     equivalent to '-o mfsport=PORT'\n");
379 	fprintf(fd,"    -B IP                       equivalent to '-o mfsbind=IP'\n");
380 	fprintf(fd,"    -L IP                       equivalent to '-o mfsproxy=IP'\n");
381 	fprintf(fd,"    -S PATH                     equivalent to '-o mfssubfolder=PATH'\n");
382 	fprintf(fd,"    -p   --password             similar to '-o mfspassword=PASSWORD', but show prompt and ask user for password\n");
383 	fprintf(fd,"    -n   --nostdopts            do not add standard MFS mount options: '-o " DEFAULT_OPTIONS ",fsname=MFS'\n");
384 #ifdef FUSE_ALLOWS_NOT_EMPTY_DIRS
385 	fprintf(fd,"    -o nonempty                 allow to mount MFS in nonempty directory\n");
386 #endif
387 	fprintf(fd,"    -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");
388 	fprintf(fd,"    -o mfsdebug                 print some debugging information\n");
389 	fprintf(fd,"    -o mfsmeta                  mount meta filesystem (trash etc.)\n");
390 	fprintf(fd,"    -o mfsflattrash             use flat trash structure in meta\n");
391 	fprintf(fd,"    -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");
392 #if defined(__linux__)
393 	fprintf(fd,"    -o mfsmkdircopysgid=N       sgid bit should be copied during mkdir operation (default: 1)\n");
394 #else
395 	fprintf(fd,"    -o mfsmkdircopysgid=N       sgid bit should be copied during mkdir operation (default: 0)\n");
396 #endif
397 #if defined(DEFAULT_SUGID_CLEAR_MODE_EXT)
398 	fprintf(fd,"    -o mfssugidclearmode=SMODE  set sugid clear mode (see below ; default: EXT)\n");
399 #elif defined(DEFAULT_SUGID_CLEAR_MODE_BSD)
400 	fprintf(fd,"    -o mfssugidclearmode=SMODE  set sugid clear mode (see below ; default: BSD)\n");
401 #elif defined(DEFAULT_SUGID_CLEAR_MODE_OSX)
402 	fprintf(fd,"    -o mfssugidclearmode=SMODE  set sugid clear mode (see below ; default: OSX)\n");
403 #else
404 	fprintf(fd,"    -o mfssugidclearmode=SMODE  set sugid clear mode (see below ; default: NEVER)\n");
405 #endif
406 #if defined(__FreeBSD__)
407 	fprintf(fd,"    -o mfscachemode=CMODE       set cache mode (see below ; default: FBSDAUTO)\n");
408 #else
409 	fprintf(fd,"    -o mfscachemode=CMODE       set cache mode (see below ; default: AUTO)\n");
410 #endif
411 	fprintf(fd,"    -o mfscachefiles            (deprecated) equivalent to '-o mfscachemode=YES'\n");
412 	fprintf(fd,"    -o mfsattrcacheto=SEC       set attributes cache timeout in seconds (default: 1.0)\n");
413 	fprintf(fd,"    -o mfsxattrcacheto=SEC      set extended attributes (xattr) cache timeout in seconds (default: 30.0)\n");
414 	fprintf(fd,"    -o mfsentrycacheto=SEC      set file entry cache timeout in seconds (default: 0.0)\n");
415 	fprintf(fd,"    -o mfsdirentrycacheto=SEC   set directory entry cache timeout in seconds (default: 1.0)\n");
416 	fprintf(fd,"    -o mfsnegentrycacheto=SEC   set negative entry cache timeout in seconds (default: 0.0)\n");
417 	fprintf(fd,"    -o mfssymlinkcacheto=SEC    set symbolic link cache timeout in seconds (default: 300.0)\n");
418 	fprintf(fd,"    -o mfsgroupscacheto=SEC     set supplementary groups cache timeout in seconds (default: 300.0)\n");
419 	fprintf(fd,"    -o mfsrlimitnofile=N        on startup mfsmount tries to change number of descriptors it can simultaneously open (default: 100000)\n");
420 	fprintf(fd,"    -o mfsnice=N                on startup mfsmount tries to change his 'nice' value (default: -19)\n");
421 #ifdef MFS_USE_MEMLOCK
422 	fprintf(fd,"    -o mfsmemlock               try to lock memory\n");
423 #endif
424 #ifdef MFS_USE_MALLOPT
425 	fprintf(fd,"    -o mfslimitarenas=N         if N>0 then limit glibc malloc arenas (default: 4)\n");
426 #endif
427 #if defined(__linux__) && defined(OOM_ADJUSTABLE)
428 	fprintf(fd,"    -o mfsallowoomkiller        do not disable out of memory killer\n");
429 #endif
430 	fprintf(fd,"    -o mfsfsyncmintime=SEC      force fsync before last file close when file was opened/created at least SEC seconds earlier (default: 0.0 - always do fsync before close)\n");
431 	fprintf(fd,"    -o mfswritecachesize=N      define size of write cache in MiB (default: 256)\n");
432 	fprintf(fd,"    -o mfsreadaheadsize=N       define size of all read ahead buffers in MiB (default: 256)\n");
433 	fprintf(fd,"    -o mfsreadaheadleng=N       define amount of bytes to be additionally read (default: 1048576)\n");
434 	fprintf(fd,"    -o mfsreadaheadtrigger=N    define amount of bytes read sequentially that turns on read ahead (default: 10 * mfsreadaheadleng)\n");
435 	fprintf(fd,"    -o mfserroronlostchunk      when all known chunkservers are connected to the master and the required chunk is missing then immediately finish I/O and return an error\n");
436 	fprintf(fd,"    -o mfserroronnospace        when all known chunkservers are connected to the master and there is no free space then immediately finish I/O and return an error\n");
437 	fprintf(fd,"    -o mfsioretries=N           define number of retries before I/O error is returned (default: 30)\n");
438 	fprintf(fd,"    -o mfstimeout=N             define maximum timeout in seconds before I/O error is returned (default: 0 - which means no timeout)\n");
439 	fprintf(fd,"    -o mfslogretry=N            define minimal retry counter on which system will start log I/O messages (default: 5)\n");
440 	fprintf(fd,"    -o mfsmaster=HOST           define mfsmaster location (default: " DEFAULT_MASTERNAME ")\n");
441 	fprintf(fd,"    -o mfsport=PORT             define mfsmaster port number (default: " DEFAULT_MASTER_CLIENT_PORT ")\n");
442 	fprintf(fd,"    -o mfsbind=IP               define source ip address for connections (default: NOT DEFINED - chosen automatically by OS)\n");
443 	fprintf(fd,"    -o mfsproxy=IP              define listen ip address of local master proxy for communication with tools (default: 127.0.0.1)\n");
444 	fprintf(fd,"    -o mfssubfolder=PATH        define subfolder to mount as root (default: /)\n");
445 	fprintf(fd,"    -o mfspassword=PASSWORD     authenticate to mfsmaster with given password\n");
446 	fprintf(fd,"    -o mfspassfile=FILENAME     authenticate to mfsmaster with password from given file\n");
447 	fprintf(fd,"    -o mfsmd5pass=MD5           authenticate to mfsmaster using directly given md5 (only if mfspassword is not defined)\n");
448 	fprintf(fd,"    -o mfsdonotrememberpassword do not remember password in memory - more secure, but when session is lost then new session is created without password\n");
449 	fprintf(fd,"    -o mfspreflabels=LABELEXPR  specify preferred labels for choosing chunkservers during I/O\n");
450 	fprintf(fd,"    -o mfsnoxattrs              turn off xattr support\n");
451 #if FUSE_VERSION >= 26
452 	fprintf(fd,"    -o mfsnoposixlocks          turn off support for global posix locks (lockf + ioctl) - locks will work locally\n");
453 #endif
454 #if FUSE_VERSION >= 29
455 	fprintf(fd,"    -o mfsnobsdlocks            turn off support for global BSD locks (flock) - locks will work locally\n");
456 #endif
457 	fprintf(fd,"\n");
458 	fprintf(fd,"CMODE can be set to:\n");
459 	fprintf(fd,"    DIRECT                      forces direct io (bypasses cache)\n");
460 	fprintf(fd,"    NO,NONE or NEVER            never allow files data to be kept in cache (safest but can reduce efficiency)\n");
461 	fprintf(fd,"    YES or ALWAYS               always allow files data to be kept in cache (dangerous)\n");
462 	fprintf(fd,"    AUTO                        file cache is managed by mfsmaster automatically (should be very safe and efficient)\n");
463 	fprintf(fd,"\n");
464 	fprintf(fd,"SMODE can be set to:\n");
465 	fprintf(fd,"    NEVER                       MFS will not change suid and sgid bit on chown\n");
466 	fprintf(fd,"    ALWAYS                      clear suid and sgid on every chown - safest operation\n");
467 	fprintf(fd,"    OSX                         standard behavior in OS X and Solaris (chown made by unprivileged user clear suid and sgid)\n");
468 	fprintf(fd,"    BSD                         standard behavior in *BSD systems (like in OSX, but only when something is really changed)\n");
469 	fprintf(fd,"    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");
470 	fprintf(fd,"    XFS                         standard behavior in XFS on Linux (like EXT but directories are changed by unprivileged users)\n");
471 	fprintf(fd,"SMODE extra info:\n");
472 	fprintf(fd,"    btrfs,ext2,ext3,ext4,hfs[+],jfs,ntfs and reiserfs on Linux work as 'EXT'.\n");
473 	fprintf(fd,"    Only xfs on Linux works a little different. Beware that there is a strange\n");
474 	fprintf(fd,"    operation - chown(-1,-1) which is usually converted by a kernel into something\n");
475 	fprintf(fd,"    like 'chmod ug-s', and therefore can't be controlled by MFS as 'chown'\n");
476 	fprintf(fd,"\n");
477 	fprintf(fd,"LABELEXPR grammar:\n");
478 	fprintf(fd,"    LABELEXPR -> S ';' LABELEXPR | S\n");
479 	fprintf(fd,"    S -> S '+' M | M\n");
480 	fprintf(fd,"    M -> M L | L\n");
481 	fprintf(fd,"    L -> 'a' .. 'z' | 'A' .. 'Z' | '(' S ')' | '[' S ']'\n");
482 	fprintf(fd,"\n");
483 	fprintf(fd,"    Subexpressions should be placed in priority order.\n");
484 	fprintf(fd,"    Up to nine subexpressions (priorities) can be specified.\n");
485 	fprintf(fd,"\n");
486 }
487 
mfs_opt_parse_cfg_file(const char * filename,int optional,struct fuse_args * outargs)488 static void mfs_opt_parse_cfg_file(const char *filename,int optional,struct fuse_args *outargs) {
489 	FILE *fd;
490 	char lbuff[1000],*p;
491 
492 	fd = fopen(filename,"r");
493 	if (fd==NULL) {
494 		if (optional==0) {
495 			fprintf(stderr,"can't open cfg file: %s\n",filename);
496 			abort();
497 		}
498 		return;
499 	}
500 	custom_cfg = 1;
501 	while (fgets(lbuff,999,fd)) {
502 		if (lbuff[0]!='#' && lbuff[0]!=';') {
503 			lbuff[999]=0;
504 			for (p = lbuff ; *p ; p++) {
505 				if (*p=='\r' || *p=='\n') {
506 					*p=0;
507 					break;
508 				}
509 			}
510 			p--;
511 			while (p>=lbuff && (*p==' ' || *p=='\t')) {
512 				*p=0;
513 				p--;
514 			}
515 			p = lbuff;
516 			while (*p==' ' || *p=='\t') {
517 				p++;
518 			}
519 			if (*p) {
520 //				printf("add option: %s\n",p);
521 				if (*p=='-') {
522 					fuse_opt_add_arg(outargs,p);
523 				} else if (*p=='/') {
524 					if (defaultmountpoint) {
525 						free(defaultmountpoint);
526 					}
527 					defaultmountpoint = strdup(p);
528 				} else {
529 					fuse_opt_add_arg(outargs,"-o");
530 					fuse_opt_add_arg(outargs,p);
531 				}
532 			}
533 		}
534 	}
535 	fclose(fd);
536 }
537 
mfs_opt_proc_stage1(void * data,const char * arg,int key,struct fuse_args * outargs)538 static int mfs_opt_proc_stage1(void *data, const char *arg, int key, struct fuse_args *outargs) {
539 	struct fuse_args *defargs = (struct fuse_args*)data;
540 	(void)outargs;
541 
542 	if (key==KEY_CFGFILE) {
543 		if (memcmp(arg,"mfscfgfile=",11)==0) {
544 			mfs_opt_parse_cfg_file(arg+11,0,defargs);
545 		} else if (arg[0]=='-' && arg[1]=='c') {
546 			mfs_opt_parse_cfg_file(arg+2,0,defargs);
547 		}
548 		return 0;
549 	}
550 	return 1;
551 }
552 
553 // return value:
554 //   0 - discard this arg
555 //   1 - keep this arg for future processing
mfs_opt_proc_stage2(void * data,const char * arg,int key,struct fuse_args * outargs)556 static int mfs_opt_proc_stage2(void *data, const char *arg, int key, struct fuse_args *outargs) {
557 	(void)data;
558 
559 	switch (key) {
560 	case FUSE_OPT_KEY_OPT:
561 		return 1;
562 	case FUSE_OPT_KEY_NONOPT:
563 		return 1;
564 	case KEY_HOST:
565 		if (mfsopts.masterhost!=NULL) {
566 			free(mfsopts.masterhost);
567 		}
568 		mfsopts.masterhost = strdup(arg+2);
569 		return 0;
570 	case KEY_PORT:
571 		if (mfsopts.masterport!=NULL) {
572 			free(mfsopts.masterport);
573 		}
574 		mfsopts.masterport = strdup(arg+2);
575 		return 0;
576 	case KEY_BIND:
577 		if (mfsopts.bindhost!=NULL) {
578 			free(mfsopts.bindhost);
579 		}
580 		mfsopts.bindhost = strdup(arg+2);
581 		return 0;
582 	case KEY_PROXY:
583 		if (mfsopts.proxyhost!=NULL) {
584 			free(mfsopts.proxyhost);
585 		}
586 		mfsopts.proxyhost = strdup(arg+2);
587 		return 0;
588 	case KEY_PATH:
589 		if (mfsopts.subfolder!=NULL) {
590 			free(mfsopts.subfolder);
591 		}
592 		mfsopts.subfolder = strdup(arg+2);
593 		return 0;
594 	case KEY_PASSWORDASK:
595 		mfsopts.passwordask = 1;
596 		return 0;
597 	case KEY_META:
598 		mfsopts.meta = 1;
599 		return 0;
600 	case KEY_NOSTDMOUNTOPTIONS:
601 		mfsopts.nostdmountoptions = 1;
602 		return 0;
603 	case KEY_VERSION:
604 		fprintf(stderr, "MFS version %s\n",VERSSTR);
605 #if FUSE_VERSION >= 30
606 		fuse_lowlevel_version();
607 #else
608 		{
609 			struct fuse_args helpargs = FUSE_ARGS_INIT(0, NULL);
610 
611 			fuse_opt_add_arg(&helpargs,outargs->argv[0]);
612 			fuse_opt_add_arg(&helpargs,"--version");
613 			fuse_parse_cmdline(&helpargs,NULL,NULL,NULL);
614 			fuse_mount(NULL,&helpargs);
615 		}
616 #endif
617 		exit(0);
618 	case KEY_HELP:
619 		usage(outargs->argv[0]);
620 #if FUSE_VERSION >= 30
621 		printf("fuse lowlevel options:\n");
622 		fuse_lowlevel_help();
623 #else
624 		{
625 			struct fuse_args helpargs = FUSE_ARGS_INIT(0, NULL);
626 
627 			fuse_opt_add_arg(&helpargs,outargs->argv[0]);
628 			fuse_opt_add_arg(&helpargs,"-ho");
629 			fuse_parse_cmdline(&helpargs,NULL,NULL,NULL);
630 			fuse_mount("",&helpargs);
631 		}
632 #endif
633 		exit(1);
634 	default:
635 		fprintf(stderr, "internal error\n");
636 		abort();
637 	}
638 }
639 
640 static uint8_t fuse_init_set = 0;
641 static uint32_t fuse_proto_major = 0;
642 static uint32_t fuse_proto_minor = 0;
643 #if FUSE_VERSION >= 28
644 static uint32_t fuse_capable = 0;
645 static uint32_t fuse_defaults = 0;
646 static uint32_t fuse_want = 0;
647 #endif
648 
mfs_fsinit(void * userdata,struct fuse_conn_info * conn)649 static void mfs_fsinit (void *userdata, struct fuse_conn_info *conn) {
650 	int *piped = (int*)userdata;
651 	char s;
652 
653 #if FUSE_VERSION >= 28
654 	fuse_defaults = conn->want;
655 #endif
656 #if FUSE_VERSION >= 30
657 
658 //	conn->max_write - default should be set to maximum value, so we don't want to decrease it
659 	conn->max_read = 0; // we do not have limit
660 //	conn->max_readahead - same as with max_write
661 
662 // FUSE_CAP_ASYNC_READ (on)
663 // FUSE_CAP_POSIX_LOCKS (on)
664 // FUSE_CAP_ATOMIC_O_TRUNC (on)
665 // FUSE_CAP_EXPORT_SUPPORT (off)
666 // FUSE_CAP_DONT_MASK (off)
667 // FUSE_CAP_SPLICE_WRITE (off)
668 // FUSE_CAP_SPLICE_MOVE (off)
669 // FUSE_CAP_SPLICE_READ (on)
670 // FUSE_CAP_FLOCK_LOCKS (on)
671 // FUSE_CAP_IOCTL_DIR (on)
672 // FUSE_CAP_AUTO_INVAL_DATA (on)
673 // FUSE_CAP_READDIRPLUS (on)
674 // FUSE_CAP_READDIRPLUS_AUTO (on)
675 // FUSE_CAP_ASYNC_DIO (on)
676 // FUSE_CAP_WRITEBACK_CACHE (off)
677 // FUSE_CAP_NO_OPEN_SUPPORT (doesn't matter)
678 // FUSE_CAP_PARALLEL_DIROPS (on)
679 // FUSE_CAP_POSIX_ACL (off)
680 // FUSE_CAP_HANDLE_KILLPRIV (on)
681 
682 // ensure that FUSE_CAP_ASYNC_READ is on
683 #ifdef FUSE_CAP_ASYNC_READ
684 	conn->want |= FUSE_CAP_ASYNC_READ;
685 #endif
686 // turn on FUSE_CAP_ATOMIC_O_TRUNC
687 #ifdef FUSE_CAP_ATOMIC_O_TRUNC
688 	conn->want |= FUSE_CAP_ATOMIC_O_TRUNC;
689 #endif
690 // turn on FUSE_CAP_EXPORT_SUPPORT (we do support lookups of "." and "..")
691 #ifdef FUSE_CAP_EXPORT_SUPPORT
692 	conn->want |= FUSE_CAP_EXPORT_SUPPORT;
693 #endif
694 // turn on CAP_DONT_MASK
695 #ifdef FUSE_CAP_DONT_MASK
696 	conn->want |= FUSE_CAP_DONT_MASK;
697 #endif
698 // turn off SPLICE flags - as for now we don't support them
699 #ifdef FUSE_CAP_SPLICE_WRITE
700 	conn->want &= ~FUSE_CAP_SPLICE_WRITE;
701 #endif
702 #ifdef FUSE_CAP_SPLICE_MOVE
703 	conn->want &= ~FUSE_CAP_SPLICE_MOVE;
704 #endif
705 #ifdef FUSE_CAP_SPLICE_READ
706 	conn->want &= ~FUSE_CAP_SPLICE_READ;
707 #endif
708 // ignore FUSE_CAP_IOCTL_DIR - we do not use ioctl's, so leave default
709 // turn off FUSE_CAP_AUTO_INVAL_DATA - we have to check it later, but likely it will highly decrease efficiency
710 #ifdef FUSE_CAP_AUTO_INVAL_DATA
711 	conn->want &= ~FUSE_CAP_AUTO_INVAL_DATA;
712 #endif
713 // turn on FUSE_CAP_READDIRPLUS and FUSE_CAP_READDIRPLUS_AUTO, but not for "meta" filesystem
714 	if (mfsopts.meta) {
715 #ifdef FUSE_CAP_READDIRPLUS
716 		conn->want &= ~FUSE_CAP_READDIRPLUS;
717 #endif
718 #ifdef FUSE_CAP_READDIRPLUS_AUTO
719 		conn->want &= ~FUSE_CAP_READDIRPLUS_AUTO;
720 #endif
721 	} else {
722 #ifdef FUSE_CAP_READDIRPLUS
723 		conn->want |= FUSE_CAP_READDIRPLUS;
724 #endif
725 #ifdef FUSE_CAP_READDIRPLUS_AUTO
726 		conn->want |= FUSE_CAP_READDIRPLUS_AUTO;
727 #endif
728 	}
729 // turn on async direct requests
730 #ifdef FUSE_CAP_ASYNC_DIO
731 	conn->want |= FUSE_CAP_ASYNC_DIO;
732 #endif
733 // turn off write back cache - we do that on our side, and it introduces posix incompatibilities in the kernel
734 #ifdef FUSE_CAP_WRITEBACK_CACHE
735 	conn->want &= ~FUSE_CAP_WRITEBACK_CACHE;
736 #endif
737 // turn on parallel directory ops
738 #ifdef FUSE_CAP_PARALLEL_DIROPS
739 	conn->want |= FUSE_CAP_PARALLEL_DIROPS;
740 #endif
741 // turn off FUSE_CAP_POSIX_ACL - we are doing all checks on our side
742 #ifdef FUSE_CAP_POSIX_ACL
743 	conn->want &= ~FUSE_CAP_POSIX_ACL;
744 #endif
745 // turn off FUSE_CAP_HANDLE_KILLPRIV - we have to implement it first
746 #ifdef FUSE_CAP_HANDLE_KILLPRIV
747 	conn->want &= ~FUSE_CAP_HANDLE_KILLPRIV;
748 #endif
749 // locks
750 #ifdef FUSE_CAP_FLOCK_LOCKS
751 	if (mfsopts.nobsdlocks==0) {
752 		conn->want |= FUSE_CAP_FLOCK_LOCKS;
753 	} else {
754 		conn->want &= ~FUSE_CAP_FLOCK_LOCKS;
755 	}
756 #endif
757 #ifdef FUSE_CAP_POSIX_LOCKS
758 	if (mfsopts.noposixlocks==0) {
759 		conn->want |= FUSE_CAP_POSIX_LOCKS;
760 	} else {
761 		conn->want &= ~FUSE_CAP_POSIX_LOCKS;
762 	}
763 #endif
764 	conn->want &= conn->capable; // we don't want to request things that kernel can't do
765 //	conn->max_background - leave default
766 //	conn->congestion_threshold - leave default
767 	conn->time_gran = 1000000000;
768 
769 #else /* FUSE2 */
770 
771 	conn->max_write = 131072;
772 	conn->max_readahead = 131072;
773 #if defined(FUSE_CAP_BIG_WRITES) || defined(FUSE_CAP_DONT_MASK) || defined(FUSE_CAP_FLOCK_LOCKS) || defined(FUSE_CAP_POSIX_LOCKS)
774 	conn->want = 0;
775 #endif
776 #ifdef FUSE_CAP_BIG_WRITES
777 	conn->want |= FUSE_CAP_BIG_WRITES;
778 #endif
779 #ifdef FUSE_CAP_DONT_MASK
780 	conn->want |= FUSE_CAP_DONT_MASK;
781 #endif
782 #ifdef FUSE_CAP_FLOCK_LOCKS
783 	if (mfsopts.nobsdlocks==0) {
784 		conn->want |= FUSE_CAP_FLOCK_LOCKS;
785 	}
786 #endif
787 #ifdef FUSE_CAP_POSIX_LOCKS
788 	if (mfsopts.noposixlocks==0) {
789 		conn->want |= FUSE_CAP_POSIX_LOCKS;
790 	}
791 #endif
792 #endif /* FUSE2/3 */
793 
794 #if defined(__FreeBSD__)
795 	if (conn->proto_major>7 || (conn->proto_major==7 && conn->proto_minor>=23)) { // This is "New" Fuse introduced in FBSD 12.1 with many fixes - we want to change our default behaviour
796 		mfs_freebsd_workarounds(0);
797 	} else {
798 		mfs_freebsd_workarounds(1);
799 	}
800 #endif
801 	fuse_proto_major = conn->proto_major;
802 	fuse_proto_minor = conn->proto_minor;
803 #if FUSE_VERSION >= 28
804 	fuse_capable = conn->capable;
805 	fuse_want = conn->want;
806 #endif
807 	fuse_init_set = 1;
808 	if (piped[1]>=0) {
809 		s=0;
810 		if (write(piped[1],&s,1)!=1) {
811 			syslog(LOG_ERR,"pipe write error: %s",strerr(errno));
812 		}
813 		close(piped[1]);
814 	}
815 }
816 
817 static uint8_t params_sesflags = 0;
818 static uint16_t params_umaskval = 0;
819 static uint32_t params_maprootuid = 0;
820 static uint32_t params_maprootgid = 0;
821 static uint32_t params_mapalluid = 0;
822 static uint32_t params_mapallgid = 0;
823 static uint8_t params_mingoal = 0;
824 static uint8_t params_maxgoal = 0;
825 static uint32_t params_mintrashtime = 0;
826 static uint32_t params_maxtrashtime = 0;
827 static uint32_t params_disables = 0;
828 
main_setparams(uint8_t sesflags,uint16_t umaskval,uint32_t maprootuid,uint32_t maprootgid,uint32_t mapalluid,uint32_t mapallgid,uint8_t mingoal,uint8_t maxgoal,uint32_t mintrashtime,uint32_t maxtrashtime,uint32_t disables)829 void main_setparams(uint8_t sesflags,uint16_t umaskval,uint32_t maprootuid,uint32_t maprootgid,uint32_t mapalluid,uint32_t mapallgid,uint8_t mingoal,uint8_t maxgoal,uint32_t mintrashtime,uint32_t maxtrashtime,uint32_t disables) {
830 	params_sesflags = sesflags;
831 	params_umaskval = umaskval;
832 	params_maprootuid = maprootuid;
833 	params_maprootgid = maprootgid;
834 	params_mapalluid = mapalluid;
835 	params_mapallgid = mapallgid;
836 	params_mingoal = mingoal;
837 	params_maxgoal = maxgoal;
838 	params_mintrashtime = mintrashtime;
839 	params_maxtrashtime = maxtrashtime;
840 	params_disables = disables;
841 }
842 
main_snprint_parameters(char * buff,uint32_t size)843 uint32_t main_snprint_parameters(char *buff,uint32_t size) {
844 	uint32_t leng = 0;
845 
846 #define bprintf(...) { if (leng<size) leng+=snprintf(buff+leng,size-leng,__VA_ARGS__); }
847 #define NOTNULL(a) (((a)==NULL)?"(not defined)":(a))
848 #define DIRECTOPT(name,string) bprintf(name ": %s\n",string);
849 #define BOOLOPT(name,opt) bprintf(name ": %s\n",(mfsopts.opt)?"(defined)":"(not defined)");
850 #define STROPT(name,opt) bprintf(name ": %s\n",NOTNULL(mfsopts.opt));
851 #define NUMOPT(name,format,opt) bprintf(name ": %" format "\n",(mfsopts.opt));
852 	STROPT("mfsmaster",masterhost);
853 	STROPT("mfsport",masterport);
854 	STROPT("mfsbind",bindhost);
855 	STROPT("mfsproxy",proxyhost);
856 	STROPT("mfssubfolder",subfolder);
857 	STROPT("mfspassword",password);
858 	STROPT("mfspassfile",passfile);
859 	STROPT("mfsmd5pass",md5pass);
860 	STROPT("mfspreflabels",preferedlabels);
861 	NUMOPT("mfsrlimitnofile","u",nofile);
862 	NUMOPT("mfsnice","d",nice);
863 	BOOLOPT("mfssuid",mfssuid);
864 	BOOLOPT("mfsdev",mfsdev);
865 #ifdef MFS_USE_MEMLOCK
866 	BOOLOPT("mfsmemlock",memlock);
867 #endif
868 #ifdef MFS_USE_MALLOPT
869 	NUMOPT("mfslimitarenas","u",limitarenas);
870 #endif
871 #if defined(__linux__) && defined(OOM_ADJUSTABLE)
872 	BOOLOPT("mfsallowoomkiller",allowoomkiller);
873 #endif
874 #ifdef FUSE_ALLOWS_NOT_EMPTY_DIRS
875 	BOOLOPT("nonempty",nonempty);
876 #endif
877 	NUMOPT("mfswritecachesize","u",writecachesize);
878 	NUMOPT("mfsreadaheadsize","u",readaheadsize);
879 	NUMOPT("mfsreadaheadleng","u",readaheadleng);
880 	NUMOPT("mfsreadaheadtrigger","u",readaheadtrigger);
881 	BOOLOPT("mfserroronlostchunk",erroronlostchunk);
882 	BOOLOPT("mfserroronnospace",erroronnospace);
883 	NUMOPT("mfsioretries","u",ioretries);
884 	NUMOPT("mfstimeout","u",timeout);
885 	NUMOPT("mfslogretry","u",logretry);
886 	BOOLOPT("mfsdebug",debug);
887 	BOOLOPT("mfsmeta",meta);
888 	BOOLOPT("mfsflattrash",flattrash);
889 	BOOLOPT("mfsdelayedinit",delayedinit);
890 	BOOLOPT("mfsdonotrememberpassword",donotrememberpassword);
891 	BOOLOPT("mfscachefiles",cachefiles);
892 	BOOLOPT("mfsnoxattrs",noxattrs);
893 	BOOLOPT("mfsnoposixlocks",noposixlocks);
894 	BOOLOPT("mfsnobsdlocks",nobsdlocks);
895 	STROPT("mfscachemode",cachemode);
896 	NUMOPT("mfsmkdircopysgid","u",mkdircopysgid);
897 	STROPT("mfssugidclearmode",sugidclearmodestr);
898 	NUMOPT("mfsattrcacheto",".3lf",attrcacheto);
899 	NUMOPT("mfsxattrcacheto",".3lf",xattrcacheto);
900 	NUMOPT("mfsentrycacheto",".3lf",entrycacheto);
901 	NUMOPT("mfsdirentrycacheto",".3lf",direntrycacheto);
902 	NUMOPT("mfsnegentrycacheto",".3lf",negentrycacheto);
903 	NUMOPT("mfssymlinkcacheto",".3lf",symlinkcacheto);
904 	NUMOPT("mfsgroupscacheto",".3lf",groupscacheto);
905 	NUMOPT("mfsfsyncmintime",".3lf",fsyncmintime);
906 	BOOLOPT("mfsfsyncbeforeclose",fsyncbeforeclose);
907 	DIRECTOPT("working_keep_cache_mode",
908 			(mfsopts.keepcache==0)?"AUTO":
909 			(mfsopts.keepcache==1)?"YES":
910 			(mfsopts.keepcache==2)?"NO":
911 			(mfsopts.keepcache==3)?"DIRECT":
912 			(mfsopts.keepcache==4)?"FBSDAUTO":
913 			"(unknown value)");
914 	DIRECTOPT("working_sugid_clear_mode",
915 			(mfsopts.sugidclearmode==SUGID_CLEAR_MODE_NEVER)?"NEVER":
916 			(mfsopts.sugidclearmode==SUGID_CLEAR_MODE_ALWAYS)?"ALWAYS":
917 			(mfsopts.sugidclearmode==SUGID_CLEAR_MODE_OSX)?"OSX":
918 			(mfsopts.sugidclearmode==SUGID_CLEAR_MODE_BSD)?"BSD":
919 			(mfsopts.sugidclearmode==SUGID_CLEAR_MODE_EXT)?"EXT":
920 			(mfsopts.sugidclearmode==SUGID_CLEAR_MODE_XFS)?"XFS":
921 			"(unknown value)");
922 	DIRECTOPT("no_std_mount_options",(mfsopts.nostdmountoptions)?"(defined)":"(not defined)");
923 	bprintf("master_sesflags: %"PRIu8"\n",params_sesflags);
924 	bprintf("master_umaskval: 0%03"PRIo16"\n",params_umaskval);
925 	bprintf("master_maproot: %"PRIu32":%"PRIu32"\n",params_maprootuid,params_maprootgid);
926 	bprintf("master_mapall: %"PRIu32":%"PRIu32"\n",params_mapalluid,params_mapallgid);
927 	bprintf("master_goallimit: %"PRIu8":%"PRIu8"\n",params_mingoal,params_maxgoal);
928 	bprintf("master_trashlimit: %"PRIu32":%"PRIu32"\n",params_mintrashtime,params_maxtrashtime);
929 	bprintf("master_disables: 0x%"PRIX32"\n",params_disables);
930 	{
931 		uint32_t mver = master_version();
932 		if (mver>0) {
933 			bprintf("moosefs_master_version: %u.%u.%u%s\n",(mver>>16),(mver>>8)&0xFF,(mver>>1)&0x7F,(mver&1)?"-pro":"");
934 		}
935 	}
936 	bprintf("moosefs_mount_version: %s\n",VERSSTR);
937 	bprintf("moosefs_mount_build: %u\n",BUILDNO);
938 	bprintf("compiled_with_fuse: %u.%u\n",((FUSE_VERSION)/10),((FUSE_VERSION)%10));
939 #if HAVE_FUSE_VERSION
940 	{
941 		int libver = fuse_version();
942 		bprintf("fuse_library_version: %"PRId32".%"PRId32"\n",libver/10,libver%10);
943 	}
944 #endif
945 	if (fuse_init_set) {
946 		bprintf("kernel_fuse_protocol: %"PRIu32".%"PRIu32"\n",fuse_proto_major,fuse_proto_minor);
947 #if FUSE_VERSION >= 28
948 		bprintf("kernel_capability_mask: 0x%"PRIX32"\n",fuse_capable);
949 		bprintf("kernel_defaults_mask: 0x%"PRIX32"\n",fuse_defaults);
950 		bprintf("kernel_working_mask: 0x%"PRIX32"\n",fuse_want);
951 #endif
952 	}
953 	return leng;
954 }
955 
main_kernelversion(void)956 uint32_t main_kernelversion(void) {
957 #ifdef MFS_GET_KERNEL_VERSION
958 	uint32_t maj,min;
959 	const char *r;
960 	struct utsname utsn;
961 
962 	maj = 0;
963 	min = 0;
964 	if (uname(&utsn)<0) {
965 		fprintf(stderr,"uname error: %s\n",strerr(errno));
966 		return 0;
967 	}
968 	r = utsn.release;
969 	if (r==NULL) {
970 		fprintf(stderr,"uname error: (release is NULL)\n");
971 		return 0;
972 	}
973 	while (*r>='0' && *r<='9') {
974 		maj *= 10;
975 		maj += (*r)-'0';
976 		r++;
977 	}
978 	if (*r=='.') {
979 		r++;
980 		while (*r>='0' && *r<='9') {
981 			min *= 10;
982 			min += (*r)-'0';
983 			r++;
984 		}
985 	}
986 	if (maj>0xFFFF) {
987 		maj = 0xFFFF;
988 	}
989 	if (min>0xFFFF) {
990 		min = 0xFFFF;
991 	}
992 	return maj * 0x10000 + min;
993 #else
994 	return 0;
995 #endif
996 }
997 
998 #if FUSE_VERSION >= 30
mainloop(struct fuse_args * args,struct fuse_cmdline_opts * cmdopts)999 int mainloop(struct fuse_args *args,struct fuse_cmdline_opts *cmdopts) {
1000 #else
1001 int mainloop(struct fuse_args *args,const char* mp,int mt,int fg) {
1002 #endif
1003 	struct fuse_session *se;
1004 #if FUSE_VERSION < 30
1005 	/* FUSE2 */
1006 	struct fuse_chan *ch;
1007 #endif
1008 	struct rlimit rls;
1009 	int piped[2];
1010 	char s;
1011 	int err;
1012 	int i;
1013 	md5ctx ctx;
1014 	uint8_t md5pass[16];
1015 
1016 	if (mfsopts.passwordask && mfsopts.password==NULL && mfsopts.md5pass==NULL) {
1017 		mfsopts.password = getpass("MFS Password:");
1018 	}
1019 	if (mfsopts.password) {
1020 		md5_init(&ctx);
1021 		md5_update(&ctx,(uint8_t*)(mfsopts.password),strlen(mfsopts.password));
1022 		md5_final(md5pass,&ctx);
1023 		memset(mfsopts.password,0,strlen(mfsopts.password));
1024 	} else if (mfsopts.md5pass) {
1025 		uint8_t *p = (uint8_t*)(mfsopts.md5pass);
1026 		for (i=0 ; i<16 ; i++) {
1027 			if (*p>='0' && *p<='9') {
1028 				md5pass[i]=(*p-'0')<<4;
1029 			} else if (*p>='a' && *p<='f') {
1030 				md5pass[i]=(*p-'a'+10)<<4;
1031 			} else if (*p>='A' && *p<='F') {
1032 				md5pass[i]=(*p-'A'+10)<<4;
1033 			} else {
1034 				fprintf(stderr,"bad md5 definition (md5 should be given as 32 hex digits)\n");
1035 				return 1;
1036 			}
1037 			p++;
1038 			if (*p>='0' && *p<='9') {
1039 				md5pass[i]+=(*p-'0');
1040 			} else if (*p>='a' && *p<='f') {
1041 				md5pass[i]+=(*p-'a'+10);
1042 			} else if (*p>='A' && *p<='F') {
1043 				md5pass[i]+=(*p-'A'+10);
1044 			} else {
1045 				fprintf(stderr,"bad md5 definition (md5 should be given as 32 hex digits)\n");
1046 				return 1;
1047 			}
1048 			p++;
1049 		}
1050 		if (*p) {
1051 			fprintf(stderr,"bad md5 definition (md5 should be given as 32 hex digits)\n");
1052 			return 1;
1053 		}
1054 		memset(mfsopts.md5pass,0,strlen(mfsopts.md5pass));
1055 	}
1056 
1057 	if (mfsopts.delayedinit) {
1058 #if FUSE_VERSION >= 30
1059 		fs_init_master_connection(mfsopts.bindhost,mfsopts.masterhost,mfsopts.masterport,mfsopts.meta,cmdopts->mountpoint,mfsopts.subfolder,(mfsopts.password||mfsopts.md5pass)?md5pass:NULL,mfsopts.donotrememberpassword,1);
1060 #else /* FUSE2 */
1061 		fs_init_master_connection(mfsopts.bindhost,mfsopts.masterhost,mfsopts.masterport,mfsopts.meta,mp,mfsopts.subfolder,(mfsopts.password||mfsopts.md5pass)?md5pass:NULL,mfsopts.donotrememberpassword,1);
1062 #endif
1063 	} else {
1064 #if FUSE_VERSION >= 30
1065 		if (fs_init_master_connection(mfsopts.bindhost,mfsopts.masterhost,mfsopts.masterport,mfsopts.meta,cmdopts->mountpoint,mfsopts.subfolder,(mfsopts.password||mfsopts.md5pass)?md5pass:NULL,mfsopts.donotrememberpassword,0)<0) {
1066 #else /* FUSE2 */
1067 		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) {
1068 #endif
1069 			return 1;
1070 		}
1071 	}
1072 	memset(md5pass,0,16);
1073 
1074 #if FUSE_VERSION >= 30
1075 	if (cmdopts->foreground==0) {
1076 #else /* FUSE2 */
1077 	if (fg==0) {
1078 #endif
1079 		openlog(STR(APPNAME), LOG_PID | LOG_NDELAY , LOG_DAEMON);
1080 	} else {
1081 #if defined(LOG_PERROR)
1082 		openlog(STR(APPNAME), LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_USER);
1083 #else
1084 		openlog(STR(APPNAME), LOG_PID | LOG_NDELAY, LOG_USER);
1085 #endif
1086 	}
1087 
1088 	i = mfsopts.nofile;
1089 	while (1) {
1090 		rls.rlim_cur = i;
1091 		rls.rlim_max = i;
1092 		if (setrlimit(RLIMIT_NOFILE,&rls)<0) {
1093 			i /= 2;
1094 			if (i<1000) {
1095 				break;
1096 			}
1097 		} else {
1098 			break;
1099 		}
1100 	}
1101 	if (i != (int)(mfsopts.nofile)) {
1102 		fprintf(stderr,"can't set open file limit to %u\n",mfsopts.nofile);
1103 		if (i>=1000) {
1104 			fprintf(stderr,"open file limit set to: %d\n",i);
1105 		}
1106 	}
1107 
1108 	setpriority(PRIO_PROCESS,getpid(),mfsopts.nice);
1109 #ifdef MFS_USE_MEMLOCK
1110 	if (mfsopts.memlock) {
1111 		rls.rlim_cur = RLIM_INFINITY;
1112 		rls.rlim_max = RLIM_INFINITY;
1113 		if (setrlimit(RLIMIT_MEMLOCK,&rls)<0) {
1114 			mfsopts.memlock=0;
1115 		}
1116 	}
1117 #endif
1118 
1119 	piped[0] = piped[1] = -1;
1120 #if FUSE_VERSION >= 30
1121 	if (cmdopts->foreground==0) {
1122 #else /* FUSE2 */
1123 	if (fg==0) {
1124 #endif
1125 		if (pipe(piped)<0) {
1126 			fprintf(stderr,"pipe error\n");
1127 			return 1;
1128 		}
1129 		err = fork();
1130 		if (err<0) {
1131 			fprintf(stderr,"fork error\n");
1132 			return 1;
1133 		} else if (err>0) {
1134 			close(piped[1]);
1135 			err = read(piped[0],&s,1);
1136 			if (err==0) {
1137 				s=1;
1138 			}
1139 			return s;
1140 		}
1141 		close(piped[0]);
1142 		s=1;
1143 	}
1144 
1145 
1146 #ifdef MFS_USE_MEMLOCK
1147 	if (mfsopts.memlock) {
1148 		if (mlockall(MCL_CURRENT|MCL_FUTURE)==0) {
1149 			syslog(LOG_NOTICE,"process memory was successfully locked in RAM");
1150 		}
1151 	}
1152 #endif
1153 
1154 /* glibc malloc tuning */
1155 #ifdef MFS_USE_MALLOPT
1156 	if (mfsopts.limitarenas) {
1157 		if (!getenv("MALLOC_ARENA_MAX")) {
1158 			syslog(LOG_NOTICE,"setting glibc malloc arena max to %u",mfsopts.limitarenas);
1159 			mallopt(M_ARENA_MAX, mfsopts.limitarenas);
1160 		}
1161 		if (!getenv("MALLOC_ARENA_TEST")) {
1162 			syslog(LOG_NOTICE,"setting glibc malloc arena test to %u",mfsopts.limitarenas);
1163 			mallopt(M_ARENA_TEST, mfsopts.limitarenas);
1164 		}
1165 	} else {
1166 		syslog(LOG_NOTICE,"setting glibc malloc arenas turned off");
1167 	}
1168 #endif /* glibc malloc tuning */
1169 
1170 #if defined(__linux__) && defined(OOM_ADJUSTABLE)
1171 	if (mfsopts.allowoomkiller==0) {
1172 		FILE *fd;
1173 		int dis;
1174 		dis = 0;
1175 #  if defined(OOM_SCORE_ADJ_MIN)
1176 		fd = fopen("/proc/self/oom_score_adj","w");
1177 		if (fd!=NULL) {
1178 			fprintf(fd,"%d\n",OOM_SCORE_ADJ_MIN);
1179 			fclose(fd);
1180 			dis = 1;
1181 #    if defined(OOM_DISABLE)
1182 		} else {
1183 			fd = fopen("/proc/self/oom_adj","w");
1184 			if (fd!=NULL) {
1185 				fprintf(fd,"%d\n",OOM_DISABLE);
1186 				fclose(fd);
1187 				dis = 1;
1188 			}
1189 #    endif
1190 		}
1191 #  elif defined(OOM_DISABLE)
1192 		fd = fopen("/proc/self/oom_adj","w");
1193 		if (fd!=NULL) {
1194 			fprintf(fd,"%d\n",OOM_DISABLE);
1195 			fclose(fd);
1196 			dis = 1;
1197 		}
1198 #  endif
1199 		if (dis) {
1200 			syslog(LOG_NOTICE,"out of memory killer disabled");
1201 		} else {
1202 			syslog(LOG_NOTICE,"can't disable out of memory killer");
1203 		}
1204 	}
1205 #endif
1206 
1207 	syslog(LOG_NOTICE,"monotonic clock function: %s",monotonic_method());
1208 	syslog(LOG_NOTICE,"monotonic clock speed: %"PRIu32" ops / 10 mili seconds",monotonic_speed());
1209 
1210 	inoleng_init();
1211 	conncache_init(200);
1212 	chunkrwlock_init();
1213 	chunksdatacache_init();
1214 	symlink_cache_init(mfsopts.symlinkcacheto);
1215 	negentry_cache_init(mfsopts.negentrycacheto);
1216 //	dir_cache_init();
1217 	fs_init_threads(mfsopts.ioretries,mfsopts.timeout);
1218 	if (masterproxy_init(mfsopts.proxyhost)<0) {
1219 		err = 1;
1220 		goto exit2;
1221 	}
1222 
1223 #if FUSE_VERSION < 30
1224 	/* FUSE2 */
1225  	ch = fuse_mount(mp, args);
1226 	if (ch==NULL) {
1227 		fprintf(stderr,"error in fuse_mount\n");
1228 		if (piped[1]>=0) {
1229 			if (write(piped[1],&s,1)!=1) {
1230 				fprintf(stderr,"pipe write error\n");
1231 			}
1232 			close(piped[1]);
1233 		}
1234 		err = 1;
1235 		goto exit2;
1236 	}
1237 #endif
1238 	if (mfsopts.meta) {
1239 		mfs_meta_init(mfsopts.debug,mfsopts.entrycacheto,mfsopts.attrcacheto,mfsopts.flattrash);
1240 #if FUSE_VERSION >= 30
1241 		se = fuse_session_new(args, &mfs_meta_oper, sizeof(mfs_meta_oper), (void*)piped);
1242 #else /* FUSE2 */
1243 		se = fuse_lowlevel_new(args, &mfs_meta_oper, sizeof(mfs_meta_oper), (void*)piped);
1244 #endif
1245 	} else {
1246 		csdb_init();
1247 		delay_init();
1248 		read_data_init(mfsopts.readaheadsize*1024*1024,mfsopts.readaheadleng,mfsopts.readaheadtrigger,mfsopts.ioretries,mfsopts.timeout,mfsopts.logretry,mfsopts.erroronlostchunk,mfsopts.erroronnospace);
1249 		write_data_init(mfsopts.writecachesize*1024*1024,mfsopts.ioretries,mfsopts.timeout,mfsopts.logretry,mfsopts.erroronlostchunk,mfsopts.erroronnospace);
1250 #if FUSE_VERSION >= 30
1251 		mfs_init(mfsopts.debug,mfsopts.keepcache,mfsopts.direntrycacheto,mfsopts.entrycacheto,mfsopts.attrcacheto,mfsopts.xattrcacheto,mfsopts.groupscacheto,mfsopts.mkdircopysgid,mfsopts.sugidclearmode,1,mfsopts.fsyncmintime,mfsopts.noxattrs,mfsopts.noposixlocks,mfsopts.nobsdlocks); //mfsopts.xattraclsupport);
1252 		se = fuse_session_new(args, &mfs_oper, sizeof(mfs_oper), (void*)piped);
1253 		mfs_setsession(se);
1254 #else /* FUSE2 */
1255 		mfs_init(mfsopts.debug,mfsopts.keepcache,mfsopts.direntrycacheto,mfsopts.entrycacheto,mfsopts.attrcacheto,mfsopts.xattrcacheto,mfsopts.groupscacheto,mfsopts.mkdircopysgid,mfsopts.sugidclearmode,1,mfsopts.fsyncmintime,mfsopts.noxattrs,mfsopts.noposixlocks,mfsopts.nobsdlocks); //mfsopts.xattraclsupport);
1256 		se = fuse_lowlevel_new(args, &mfs_oper, sizeof(mfs_oper), (void*)piped);
1257 #endif
1258 	}
1259 	if (se==NULL) {
1260 #if FUSE_VERSION >= 30
1261 		fprintf(stderr,"error in fuse_session_new\n");
1262 #else /* FUSE2 */
1263 		fuse_unmount(mp,ch);
1264 		fprintf(stderr,"error in fuse_lowlevel_new\n");
1265 #endif
1266 		portable_usleep(100000);	// time for print other error messages by FUSE
1267 		if (piped[1]>=0) {
1268 			if (write(piped[1],&s,1)!=1) {
1269 				fprintf(stderr,"pipe write error\n");
1270 			}
1271 			close(piped[1]);
1272 		}
1273 		err = 1;
1274 		goto exit1;
1275 	}
1276 
1277 //	fprintf(stderr,"check\n");
1278 
1279 	if (fuse_set_signal_handlers(se)<0) {
1280 		fprintf(stderr,"error in fuse_set_signal_handlers\n");
1281 #if FUSE_VERSION >= 30
1282 		fuse_session_destroy(se);
1283 #else
1284 		fuse_session_remove_chan(ch);
1285 		fuse_session_destroy(se);
1286 		fuse_unmount(mp,ch);
1287 #endif
1288 		if (piped[1]>=0) {
1289 			if (write(piped[1],&s,1)!=1) {
1290 				fprintf(stderr,"pipe write error\n");
1291 			}
1292 			close(piped[1]);
1293 		}
1294 		err = 1;
1295 		goto exit1;
1296 	}
1297 
1298 #if FUSE_VERSION >= 30
1299 	if (fuse_session_mount(se,cmdopts->mountpoint)<0) {
1300 		fprintf(stderr,"error in fuse_session_mount\n");
1301 		fuse_session_destroy(se);
1302 		if (piped[1]>=0) {
1303 			if (write(piped[1],&s,1)!=1) {
1304 				fprintf(stderr,"pipe write error\n");
1305 			}
1306 			close(piped[1]);
1307 		}
1308 		err = 1;
1309 		goto exit1;
1310 	}
1311 #else /* FUSE2 */
1312 	fuse_session_add_chan(se, ch);
1313 	mfs_setchan(ch);
1314 #endif
1315 
1316 #if FUSE_VERSION >= 30
1317 	if (mfsopts.debug==0 && cmdopts->foreground==0) {
1318 #else
1319 	if (mfsopts.debug==0 && fg==0) {
1320 #endif
1321 		setsid();
1322 		setpgid(0,getpid());
1323 		if ((i = open("/dev/null", O_RDWR, 0)) != -1) {
1324 			(void)dup2(i, STDIN_FILENO);
1325 			(void)dup2(i, STDOUT_FILENO);
1326 			(void)dup2(i, STDERR_FILENO);
1327 			if (i>2) close (i);
1328 		}
1329 	}
1330 
1331 	sparents_init();
1332 #if FUSE_VERSION >= 30
1333 	sinodes_init(cmdopts->mountpoint);
1334 #else /* FUSE2 */
1335 	sinodes_init(mp);
1336 #endif
1337 	sstats_init();
1338 
1339 	{
1340 		char pname[256];
1341 #if FUSE_VERSION >= 30
1342 		snprintf(pname,256,"mfsmount (mounted on: %s)",cmdopts->mountpoint);
1343 #else /* FUSE2 */
1344 		snprintf(pname,256,"mfsmount (mounted on: %s)",mp);
1345 #endif
1346 		pname[255] = 0;
1347 		processname_set(pname);
1348 	}
1349 
1350 #if FUSE_VERSION >= 30
1351 	if (cmdopts->singlethread==0) {
1352 		struct fuse_loop_config lopts;
1353 		memset(&lopts,0,sizeof(lopts));
1354 		lopts.clone_fd = cmdopts->clone_fd;
1355 		lopts.max_idle_threads = cmdopts->max_idle_threads;
1356 		err = fuse_session_loop_mt(se,&lopts);
1357 #else
1358 	if (mt) {
1359 		err = fuse_session_loop_mt(se);
1360 #endif
1361 	} else {
1362 		err = fuse_session_loop(se);
1363 	}
1364 	if (err) {
1365 		if (piped[1]>=0) {
1366 			if (write(piped[1],&s,1)!=1) {
1367 				syslog(LOG_ERR,"pipe write error: %s",strerr(errno));
1368 			}
1369 			close(piped[1]);
1370 		}
1371 	}
1372 
1373 	sstats_term();
1374 	sinodes_term();
1375 	sparents_term();
1376 
1377 	fuse_remove_signal_handlers(se);
1378 #if FUSE_VERSION >= 30
1379 	fuse_session_unmount(se);
1380 	fuse_session_destroy(se);
1381 #else /* FUSE2 */
1382 	fuse_session_remove_chan(ch);
1383 	fuse_session_destroy(se);
1384 	fuse_unmount(mp,ch);
1385 #endif
1386 exit1:
1387 	if (mfsopts.meta==0) {
1388 		mfs_term();
1389 		write_data_term();
1390 		read_data_term();
1391 		delay_term();
1392 		csdb_term();
1393 	}
1394 	masterproxy_term();
1395 exit2:
1396 	fs_term();
1397 //	dir_cache_term();
1398 	negentry_cache_term();
1399 	symlink_cache_term();
1400 	chunksdatacache_term();
1401 	chunkrwlock_term();
1402 	conncache_term();
1403 	inoleng_term();
1404 	return err ? 1 : 0;
1405 }
1406 
1407 #if FUSE_VERSION == 25
1408 static int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) {
1409 	assert(pos <= args->argc);
1410 	if (fuse_opt_add_arg(args, arg) == -1) {
1411 		return -1;
1412 	}
1413 	if (pos != args->argc - 1) {
1414 		char *newarg = args->argv[args->argc - 1];
1415 		memmove(&args->argv[pos + 1], &args->argv[pos], sizeof(char *) * (args->argc - pos - 1));
1416 		args->argv[pos] = newarg;
1417 	}
1418 	return 0;
1419 }
1420 #endif
1421 
1422 static unsigned int strncpy_remove_commas(char *dstbuff, unsigned int dstsize,char *src) {
1423 	char c;
1424 	unsigned int l;
1425 	l=0;
1426 	while ((c=*src++) && l+1<dstsize) {
1427 		if (c!=',') {
1428 			*dstbuff++ = c;
1429 			l++;
1430 		}
1431 	}
1432 	*dstbuff=0;
1433 	return l;
1434 }
1435 
1436 #if HAVE_FUSE_VERSION
1437 static unsigned int strncpy_escape_commas(char *dstbuff, unsigned int dstsize,char *src) {
1438 	char c;
1439 	unsigned int l;
1440 	l=0;
1441 	while ((c=*src++) && l+1<dstsize) {
1442 		if (c!=',' && c!='\\') {
1443 			*dstbuff++ = c;
1444 			l++;
1445 		} else {
1446 			if (l+2<dstsize) {
1447 				*dstbuff++ = '\\';
1448 				*dstbuff++ = c;
1449 				l+=2;
1450 			} else {
1451 				*dstbuff=0;
1452 				return l;
1453 			}
1454 		}
1455 	}
1456 	*dstbuff=0;
1457 	return l;
1458 }
1459 #endif
1460 
1461 void remove_mfsmount_magic(struct fuse_args *args) {
1462 	int i;
1463 	for (i=1 ; i<args->argc ; i++) {
1464 		if (strcmp(args->argv[i],"mfsmount_magic")==0) {
1465 			if (i+1 < args->argc) {
1466 				memmove(&args->argv[i],&args->argv[i+1],sizeof(char *)*(args->argc - i - 1));
1467 			}
1468 			args->argc--;
1469 			return;
1470 		}
1471 	}
1472 }
1473 
1474 void make_fsname(struct fuse_args *args) {
1475 	char fsnamearg[256];
1476 	unsigned int l;
1477 #if HAVE_FUSE_VERSION
1478 	int libver;
1479 	libver = fuse_version();
1480 	if (libver >= 27) {
1481 		l = snprintf(fsnamearg,256,"-osubtype=mfs%s,fsname=",(mfsopts.meta)?"meta":"");
1482 		if (libver >= 28) {
1483 			l += strncpy_escape_commas(fsnamearg+l,256-l,mfsopts.masterhost);
1484 			if (l<255) {
1485 				fsnamearg[l++]=':';
1486 			}
1487 			l += strncpy_escape_commas(fsnamearg+l,256-l,mfsopts.masterport);
1488 			if (mfsopts.subfolder[0]!='/') {
1489 				if (l<255) {
1490 					fsnamearg[l++]='/';
1491 				}
1492 			}
1493 			if (mfsopts.subfolder[0]!='/' && mfsopts.subfolder[1]!=0) {
1494 				l += strncpy_escape_commas(fsnamearg+l,256-l,mfsopts.subfolder);
1495 			}
1496 			if (l>255) {
1497 				l=255;
1498 			}
1499 			fsnamearg[l]=0;
1500 		} else {
1501 			l += strncpy_remove_commas(fsnamearg+l,256-l,mfsopts.masterhost);
1502 			if (l<255) {
1503 				fsnamearg[l++]=':';
1504 			}
1505 			l += strncpy_remove_commas(fsnamearg+l,256-l,mfsopts.masterport);
1506 			if (mfsopts.subfolder[0]!='/') {
1507 				if (l<255) {
1508 					fsnamearg[l++]='/';
1509 				}
1510 			}
1511 			if (mfsopts.subfolder[0]!='/' && mfsopts.subfolder[1]!=0) {
1512 				l += strncpy_remove_commas(fsnamearg+l,256-l,mfsopts.subfolder);
1513 			}
1514 			if (l>255) {
1515 				l=255;
1516 			}
1517 			fsnamearg[l]=0;
1518 		}
1519 	} else {
1520 #else
1521 		l = snprintf(fsnamearg,256,"-ofsname=mfs%s#",(mfsopts.meta)?"meta":"");
1522 		l += strncpy_remove_commas(fsnamearg+l,256-l,mfsopts.masterhost);
1523 		if (l<255) {
1524 			fsnamearg[l++]=':';
1525 		}
1526 		l += strncpy_remove_commas(fsnamearg+l,256-l,mfsopts.masterport);
1527 		if (mfsopts.subfolder[0]!='/') {
1528 			if (l<255) {
1529 				fsnamearg[l++]='/';
1530 			}
1531 		}
1532 		if (mfsopts.subfolder[0]!='/' && mfsopts.subfolder[1]!=0) {
1533 			l += strncpy_remove_commas(fsnamearg+l,256-l,mfsopts.subfolder);
1534 		}
1535 		if (l>255) {
1536 			l=255;
1537 		}
1538 		fsnamearg[l]=0;
1539 #endif
1540 #if HAVE_FUSE_VERSION
1541 	}
1542 #endif
1543 	fuse_opt_insert_arg(args, 1, fsnamearg);
1544 }
1545 
1546 /* debug function
1547 void dump_args(const char *prfx,struct fuse_args *args) {
1548 	int i;
1549 	for (i=0 ; i<args->argc ; i++) {
1550 		printf("%s [%d]: %s\n",prfx,i,args->argv[i]);
1551 	}
1552 }
1553 */
1554 
1555 char* password_read(const char *filename) {
1556 	FILE *fd;
1557 	char passwordbuff[1024];
1558 	char *ret;
1559 	int i;
1560 
1561 	fd = fopen(filename,"r");
1562 	if (fd==NULL) {
1563 		fprintf(stderr,"error opening password file: %s\n",filename);
1564 		return NULL;
1565 	}
1566 	if (fgets(passwordbuff,1024,fd)==NULL) {
1567 		fprintf(stderr,"password file (%s) is empty\n",filename);
1568 		fclose(fd);
1569 		return NULL;
1570 	}
1571 	fclose(fd);
1572 	passwordbuff[1023]=0;
1573 	i = strlen(passwordbuff);
1574 	while (i>0) {
1575 		i--;
1576 		if (passwordbuff[i]=='\n' || passwordbuff[i]=='\r') {
1577 			passwordbuff[i]=0;
1578 		} else {
1579 			break;
1580 		}
1581 	}
1582 	if (i==0) {
1583 		fprintf(stderr,"first line in password file (%s) is empty\n",filename);
1584 		return NULL;
1585 	}
1586 	ret = malloc(i+1);
1587 	passert(ret);
1588 	memcpy(ret,passwordbuff,i);
1589 	memset(passwordbuff,0,1024);
1590 	ret[i] = 0;
1591 	return ret;
1592 }
1593 
1594 #ifdef FUSE_ALLOWS_NOT_EMPTY_DIRS
1595 int check_if_dir_is_empty(const char *mp) {
1596 	DIR *dd = NULL;
1597 	struct stat st;
1598 	struct dirent *de;
1599 #if !defined(__GLIBC__) || (__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 23))
1600 	struct dirent *destorage = NULL;
1601 #endif
1602 	int res = 0;
1603 
1604 	if (stat(mp,&st)<0) {
1605 		fprintf(stderr,"stat mountpoint '%s': %s\n",mp,strerr(errno));
1606 		goto err;
1607 	}
1608 	if ((st.st_mode & S_IFMT) != S_IFDIR) {
1609 		fprintf(stderr,"given mountpoint '%s' is not a directory\n",mp);
1610 		goto err;
1611 	}
1612 
1613 #if !defined(__GLIBC__) || (__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 23))
1614 	destorage = (struct dirent*)malloc(sizeof(struct dirent)+pathconf(mp,_PC_NAME_MAX)+1);
1615 	passert(destorage);
1616 #endif
1617 
1618 	dd = opendir(mp);
1619 	if (dd==NULL) {
1620 		fprintf(stderr,"error opening '%s': %s\n",mp,strerr(errno));
1621 		goto err;
1622 	}
1623 #if !defined(__GLIBC__) || (__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 23))
1624 	while (readdir_r(dd,destorage,&de)==0 && de!=NULL) {
1625 #else
1626 	while ((de = readdir(dd)) != NULL) {
1627 #endif
1628 		if (de->d_name[0]=='.') {
1629 			if (de->d_name[1]==0) {
1630 				continue;
1631 			}
1632 			if (de->d_name[1]=='.' && de->d_name[2]==0) {
1633 				continue;
1634 			}
1635 		}
1636 		fprintf(stderr,"mountpoint '%s' is not empty\n",mp);
1637 		goto err;
1638 	}
1639 	res = 1;
1640 err:
1641 	if (dd!=NULL) {
1642 		closedir(dd);
1643 	}
1644 #if !defined(__GLIBC__) || (__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 23))
1645 	if (destorage!=NULL) {
1646 		free(destorage);
1647 	}
1648 #endif
1649 	return res;
1650 }
1651 #endif
1652 
1653 int main(int argc, char *argv[]) {
1654 	int res;
1655 	int i;
1656 #if FUSE_VERSION >= 30
1657 	struct fuse_cmdline_opts cmdopts;
1658 #else
1659 	int mt,fg;
1660 	char *mountpoint;
1661 #endif
1662 	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1663 	struct fuse_args defaultargs = FUSE_ARGS_INIT(0, NULL);
1664 
1665 	processname_init(argc,argv);
1666 
1667 #if defined(SIGPIPE) && defined(SIG_IGN)
1668 	signal(SIGPIPE,SIG_IGN);
1669 #endif
1670 	strerr_init();
1671 	mycrc32_init();
1672 
1673 	setenv("FUSE_THREAD_STACK","524288",0); // works good with 262144 but not 131072, so for safety we will use 524288
1674 
1675 	memset(&mfsopts,0,sizeof(mfsopts));
1676 	mfsopts.masterhost = NULL;
1677 	mfsopts.masterport = NULL;
1678 	mfsopts.bindhost = NULL;
1679 	mfsopts.proxyhost = NULL;
1680 	mfsopts.subfolder = NULL;
1681 	mfsopts.password = NULL;
1682 	mfsopts.passfile = NULL;
1683 	mfsopts.md5pass = NULL;
1684 	mfsopts.preferedlabels = NULL;
1685 	mfsopts.nofile = 0;
1686 	mfsopts.nice = -19;
1687 	mfsopts.mfssuid = 0;
1688 	mfsopts.mfsdev = 0;
1689 #ifdef MFS_USE_MEMLOCK
1690 	mfsopts.memlock = 0;
1691 #endif
1692 #ifdef MFS_USE_MALLOPT
1693 	mfsopts.limitarenas = 4;
1694 #endif
1695 #if defined(__linux__) && defined(OOM_DISABLE)
1696 	mfsopts.allowoomkiller = 0;
1697 #endif
1698 	mfsopts.nostdmountoptions = 0;
1699 	mfsopts.meta = 0;
1700 	mfsopts.flattrash = 0;
1701 	mfsopts.debug = 0;
1702 	mfsopts.delayedinit = 0;
1703 #ifdef __linux__
1704 	mfsopts.mkdircopysgid = 1;
1705 #else
1706 	mfsopts.mkdircopysgid = 0;
1707 #endif
1708 	mfsopts.sugidclearmodestr = NULL;
1709 	mfsopts.donotrememberpassword = 0;
1710 //	mfsopts.xattraclsupport = 0;
1711 	mfsopts.cachefiles = 0;
1712 	mfsopts.noxattrs = 0;
1713 	mfsopts.noposixlocks = 0;
1714 	mfsopts.nobsdlocks = 0;
1715 	mfsopts.cachemode = NULL;
1716 	mfsopts.writecachesize = 0;
1717 	mfsopts.readaheadsize = 0;
1718 	mfsopts.readaheadleng = 0;
1719 	mfsopts.readaheadtrigger = 0;
1720 	mfsopts.erroronlostchunk = 0;
1721 	mfsopts.erroronnospace = 0;
1722 	mfsopts.ioretries = 30;
1723 	mfsopts.timeout = 0;
1724 	mfsopts.logretry = 5;
1725 	mfsopts.passwordask = 0;
1726 	mfsopts.attrcacheto = 1.0;
1727 	mfsopts.xattrcacheto = 30.0;
1728 	mfsopts.entrycacheto = 0.0;
1729 	mfsopts.direntrycacheto = 1.0;
1730 	mfsopts.negentrycacheto = 0.0;
1731 	mfsopts.symlinkcacheto = 300.0;
1732 	mfsopts.groupscacheto = 300.0;
1733 	mfsopts.fsyncbeforeclose = 0;
1734 	mfsopts.fsyncmintime = 0.0;
1735 
1736 	custom_cfg = 0;
1737 
1738 //	dump_args("input_args",&args);
1739 
1740 	if (args.argc>1) {
1741 		uint32_t hostlen,portlen,colons;
1742 		char *c,*portbegin;
1743 		int optpos;
1744 		// skip options in format '-o XXXX' and '-oXXXX'
1745 		optpos = 1;
1746 		while (optpos<args.argc) {
1747 			c = args.argv[optpos];
1748 			if (c[0]=='-' && c[1]=='o') {
1749 				if (c[2]) {
1750 					optpos++;
1751 				} else {
1752 					optpos+=2;
1753 				}
1754 			} else {
1755 				break;
1756 			}
1757 		}
1758 		if (optpos<args.argc) {
1759 			// check if next arg matches to HOST[:PORT]:[PATH]
1760 			c = args.argv[optpos];
1761 			colons = 0;
1762 			for (i=0 ; c[i] ; i++) {
1763 				if (c[i]==':') {
1764 					colons++;
1765 				}
1766 			}
1767 			if (colons>0) {
1768 				hostlen = 0;
1769 				portlen = 0;
1770 				portbegin = NULL;
1771 				while (((*c)>='a' && (*c)<='z') || ((*c)>='A' && (*c)<='Z') || ((*c)>='0' && (*c)<='9') || (*c)=='-' || (*c)=='.') { // DNS chars
1772 					c++;
1773 					hostlen++;
1774 				}
1775 				if (hostlen>0) {
1776 					if ((*c)==':' && colons>1) {
1777 						c++;
1778 						portbegin = c;
1779 						while ((*c)>='0' && ((*c)<='9')) {
1780 							c++;
1781 							portlen++;
1782 						}
1783 					}
1784 					if ((*c)==':') { // match found
1785 						c++;
1786 						if (*c) {
1787 							mfsopts.subfolder = strdup(c);
1788 						}
1789 						mfsopts.masterhost = malloc(hostlen+1);
1790 						memcpy(mfsopts.masterhost,args.argv[optpos],hostlen);
1791 						mfsopts.masterhost[hostlen]=0;
1792 						if (portbegin!=NULL && portlen>0) {
1793 							mfsopts.masterport = malloc(portlen+1);
1794 							memcpy(mfsopts.masterport,portbegin,portlen);
1795 							mfsopts.masterport[portlen]=0;
1796 						}
1797 						for (i=optpos+1 ; i<args.argc ; i++) {
1798 							args.argv[i-1] = args.argv[i];
1799 						}
1800 						args.argc--;
1801 					}
1802 				}
1803 			}
1804 		}
1805 	}
1806 
1807 //	dump_args("after_first_filter",&args);
1808 
1809 	if (fuse_opt_parse(&args, &defaultargs, mfs_opts_stage1, mfs_opt_proc_stage1)<0) {
1810 		exit(1);
1811 	}
1812 
1813 	if (custom_cfg==0) {
1814 		int cfgfd;
1815 		char *cfgfile;
1816 
1817 		cfgfile=strdup(ETC_PATH "/mfs/mfsmount.cfg");
1818 		if ((cfgfd = open(cfgfile,O_RDONLY))<0 && errno==ENOENT) {
1819 			free(cfgfile);
1820 			cfgfile=strdup(ETC_PATH "/mfsmount.cfg");
1821 			if ((cfgfd = open(cfgfile,O_RDONLY))>=0) {
1822 				fprintf(stderr,"default sysconf path has changed - please move mfsmount.cfg from "ETC_PATH"/ to "ETC_PATH"/mfs/\n");
1823 			}
1824 		}
1825 		if (cfgfd>=0) {
1826 			close(cfgfd);
1827 		}
1828 		mfs_opt_parse_cfg_file(cfgfile,1,&defaultargs);
1829 		free(cfgfile);
1830 	}
1831 
1832 //	dump_args("parsed_defaults",&defaultargs);
1833 //	dump_args("changed_args",&args);
1834 
1835 	for (i=0 ; i<defaultargs.argc ; i++) {
1836 		fuse_opt_add_arg(&args,defaultargs.argv[i]);
1837 	}
1838 
1839 //	dump_args("combined_args",&args);
1840 
1841 	if (fuse_opt_parse(&args, &mfsopts, mfs_opts_stage2, mfs_opt_proc_stage2)<0) {
1842 		exit(1);
1843 	}
1844 
1845 //	dump_args("combined_args_after_parse",&args);
1846 
1847 	if (mfsopts.cachemode!=NULL && mfsopts.cachefiles) {
1848 		fprintf(stderr,"mfscachemode and mfscachefiles options are exclusive - use only mfscachemode\nsee: %s -h for help\n",argv[0]);
1849 		return 1;
1850 	}
1851 
1852 	if (mfsopts.cachemode==NULL) {
1853 #if defined(__FreeBSD__)
1854 		mfsopts.keepcache = 4;
1855 #else
1856 		mfsopts.keepcache = (mfsopts.cachefiles)?1:0;
1857 #endif
1858 	} else if (strcasecmp(mfsopts.cachemode,"AUTO")==0) {
1859 		mfsopts.keepcache=0;
1860 	} else if (strcasecmp(mfsopts.cachemode,"YES")==0 || strcasecmp(mfsopts.cachemode,"ALWAYS")==0) {
1861 		mfsopts.keepcache=1;
1862 	} else if (strcasecmp(mfsopts.cachemode,"NO")==0 || strcasecmp(mfsopts.cachemode,"NONE")==0 || strcasecmp(mfsopts.cachemode,"NEVER")==0) {
1863 		mfsopts.keepcache=2;
1864 	} else if (strcasecmp(mfsopts.cachemode,"DIRECT")==0) {
1865 		mfsopts.keepcache=3;
1866 #if defined(__FreeBSD__)
1867 	} else if (strcasecmp(mfsopts.cachemode,"FBSDAUTO")==0) {
1868 		mfsopts.keepcache=4;
1869 #endif
1870 	} else {
1871 		fprintf(stderr,"unrecognized cachemode option\nsee: %s -h for help\n",argv[0]);
1872 		return 1;
1873 	}
1874 	if (mfsopts.sugidclearmodestr==NULL) {
1875 #if defined(DEFAULT_SUGID_CLEAR_MODE_EXT)
1876 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_EXT;
1877 #elif defined(DEFAULT_SUGID_CLEAR_MODE_BSD)
1878 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_BSD;
1879 #elif defined(DEFAULT_SUGID_CLEAR_MODE_OSX)
1880 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_OSX;
1881 #else
1882 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_NEVER;
1883 #endif
1884 	} else if (strcasecmp(mfsopts.sugidclearmodestr,"NEVER")==0) {
1885 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_NEVER;
1886 	} else if (strcasecmp(mfsopts.sugidclearmodestr,"ALWAYS")==0) {
1887 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_ALWAYS;
1888 	} else if (strcasecmp(mfsopts.sugidclearmodestr,"OSX")==0) {
1889 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_OSX;
1890 	} else if (strcasecmp(mfsopts.sugidclearmodestr,"BSD")==0) {
1891 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_BSD;
1892 	} else if (strcasecmp(mfsopts.sugidclearmodestr,"EXT")==0) {
1893 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_EXT;
1894 	} else if (strcasecmp(mfsopts.sugidclearmodestr,"XFS")==0) {
1895 		mfsopts.sugidclearmode = SUGID_CLEAR_MODE_XFS;
1896 	} else {
1897 		fprintf(stderr,"unrecognized sugidclearmode option\nsee: %s -h for help\n",argv[0]);
1898 		return 1;
1899 	}
1900 	if (mfsopts.masterhost==NULL) {
1901 		mfsopts.masterhost = strdup(DEFAULT_MASTERNAME);
1902 	}
1903 	if (mfsopts.masterport==NULL) {
1904 		mfsopts.masterport = strdup(DEFAULT_MASTER_CLIENT_PORT);
1905 	}
1906 	if (mfsopts.proxyhost==NULL) {
1907 		mfsopts.proxyhost = strdup("127.0.0.1");
1908 	}
1909 	if (mfsopts.subfolder==NULL) {
1910 		mfsopts.subfolder = strdup("/");
1911 	}
1912 	if (mfsopts.passfile!=NULL) {
1913 		if (mfsopts.password!=NULL||mfsopts.md5pass!=NULL) {
1914 			fprintf(stderr,"mfspassfile option is mutually exclusive with mfspassword and mfsmd5pass\nsee: %s -h for help\n",argv[0]);
1915 			return 1;
1916 		}
1917 		mfsopts.password = password_read(mfsopts.passfile);
1918 		if (mfsopts.password==NULL) {
1919 			return 1;
1920 		}
1921 	}
1922 	if (mfsopts.nofile==0) {
1923 		mfsopts.nofile=100000;
1924 	}
1925 	if (mfsopts.writecachesize==0) {
1926 		mfsopts.writecachesize=256;
1927 	}
1928 	if (mfsopts.writecachesize<16) {
1929 		fprintf(stderr,"write cache size too low (%u MiB) - increased to 16 MiB\n",mfsopts.writecachesize);
1930 		mfsopts.writecachesize=16;
1931 	}
1932 	if (mfsopts.writecachesize>2048) {
1933 		fprintf(stderr,"write cache size too big (%u MiB) - decresed to 2048 MiB\n",mfsopts.writecachesize);
1934 		mfsopts.writecachesize=2048;
1935 	}
1936 	if (mfsopts.readaheadsize==0) {
1937 		mfsopts.readaheadsize=256;
1938 	}
1939 	if (mfsopts.readaheadsize<16) {
1940 		fprintf(stderr,"read ahead size too low (%u MiB) - increased to 16 MiB\n",mfsopts.readaheadsize);
1941 		mfsopts.readaheadsize=16;
1942 	}
1943 	if (mfsopts.readaheadsize>2048) {
1944 		fprintf(stderr,"read ahead size too big (%u MiB) - decresed to 2048 MiB\n",mfsopts.readaheadsize);
1945 		mfsopts.readaheadsize=2048;
1946 	}
1947 	if (mfsopts.readaheadleng==0) {
1948 		mfsopts.readaheadleng=0x100000;
1949 	}
1950 	if (mfsopts.readaheadleng<0x20000) {
1951 		fprintf(stderr,"read ahead length too low (%u B) - increased to 128 KiB\n",mfsopts.readaheadleng);
1952 		mfsopts.readaheadleng=0x20000;
1953 	}
1954 	if (mfsopts.readaheadleng>0x200000) {
1955 		fprintf(stderr,"read ahead length too big (%u B) - decresed to 2 MiB\n",mfsopts.readaheadleng);
1956 		mfsopts.readaheadleng=0x200000;
1957 	}
1958 	if (mfsopts.readaheadtrigger==0) {
1959 		mfsopts.readaheadtrigger=mfsopts.readaheadleng*10;
1960 	}
1961 
1962 	if (mfsopts.nostdmountoptions==0) {
1963 		fuse_opt_add_arg(&args, "-o" DEFAULT_OPTIONS);
1964 	}
1965 
1966 	if (mfsopts.fsyncbeforeclose) {
1967 		mfsopts.fsyncmintime=0.0;
1968 	}
1969 
1970 #define TIMEOUT_CLAMP(option,name,max) \
1971 	if ((option) > (max)) { \
1972 		fprintf(stderr,name " cache timeout too big (%.2lf) - decreased to %.2lf\n",(option),(max)); \
1973 		(option) = (max); \
1974 	} \
1975 	if ((option) < 0.0) { \
1976 		fprintf(stderr,"negative value of " name " cache timeout - set to 0\n"); \
1977 		(option) = 0.0; \
1978 	}
1979 
1980 	TIMEOUT_CLAMP(mfsopts.attrcacheto,"attribute",86400.0);
1981 	TIMEOUT_CLAMP(mfsopts.xattrcacheto,"xattr",86400.0);
1982 	TIMEOUT_CLAMP(mfsopts.entrycacheto,"entry",86400.0);
1983 	TIMEOUT_CLAMP(mfsopts.direntrycacheto,"directory entry",86400.0);
1984 	TIMEOUT_CLAMP(mfsopts.negentrycacheto,"non existing entry",86400.0);
1985 	TIMEOUT_CLAMP(mfsopts.symlinkcacheto,"symbolic link",86400.0);
1986 	TIMEOUT_CLAMP(mfsopts.groupscacheto,"auxiliary groups",86400.0);
1987 
1988 	if (csorder_init(mfsopts.preferedlabels)<0) {
1989 		fprintf(stderr,"error parsing preferred labels expression\nsee: %s -h for help\n",argv[0]);
1990 		return 1;
1991 	}
1992 
1993 	make_fsname(&args);
1994 	if (mfsopts.mfssuid) {
1995 		fuse_opt_insert_arg(&args, 1, "-osuid");
1996 	}
1997 	if (mfsopts.mfsdev) {
1998 		fuse_opt_insert_arg(&args, 1, "-odev");
1999 	}
2000 	remove_mfsmount_magic(&args);
2001 
2002 //	dump_args("combined_args_before_fuse_parse_cmdline",&args);
2003 
2004 #if FUSE_VERSION >= 30
2005 	if (fuse_parse_cmdline(&args,&cmdopts)<0) {
2006 #else
2007 	if (fuse_parse_cmdline(&args,&mountpoint,&mt,&fg)<0) {
2008 #endif
2009 		fprintf(stderr,"see: %s -h for help\n",argv[0]);
2010 		return 1;
2011 	}
2012 
2013 #if FUSE_VERSION >= 30
2014 	if (!cmdopts.mountpoint) {
2015 		if (defaultmountpoint) {
2016 			cmdopts.mountpoint = defaultmountpoint;
2017 #else
2018 	if (!mountpoint) {
2019 		if (defaultmountpoint) {
2020 			mountpoint = defaultmountpoint;
2021 #endif
2022 		} else {
2023 			fprintf(stderr,"no mount point\nsee: %s -h for help\n",argv[0]);
2024 			return 1;
2025 		}
2026 	}
2027 
2028 #ifdef FUSE_ALLOWS_NOT_EMPTY_DIRS
2029 	if (mfsopts.nonempty==0) {
2030 #if FUSE_VERSION >= 30
2031 		if (check_if_dir_is_empty(cmdopts.mountpoint)==0) {
2032 #else
2033 		if (check_if_dir_is_empty(mountpoint)==0) {
2034 #endif
2035 			return 1;
2036 		}
2037 	}
2038 #endif
2039 
2040 #if FUSE_VERSION >= 30
2041 	res = mainloop(&args,&cmdopts);
2042 #else
2043 	res = mainloop(&args,mountpoint,mt,fg);
2044 #endif
2045 	fuse_opt_free_args(&defaultargs);
2046 	fuse_opt_free_args(&args);
2047 	free(mfsopts.masterhost);
2048 	free(mfsopts.masterport);
2049 	if (mfsopts.bindhost) {
2050 		free(mfsopts.bindhost);
2051 	}
2052 	if (mfsopts.proxyhost) {
2053 		free(mfsopts.proxyhost);
2054 	}
2055 	free(mfsopts.subfolder);
2056 	if (defaultmountpoint) {
2057 		free(defaultmountpoint);
2058 	}
2059 	stats_term();
2060 	strerr_term();
2061 	return res;
2062 }
2063