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