1 /*
2  * Copyright (C) 2016 Jakub Kruszona-Zawadzki, Core Technology Sp. z o.o.
3  *
4  * This file is part of MooseFS.
5  *
6  * MooseFS is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, version 2 (only).
9  *
10  * MooseFS is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with MooseFS; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA
18  * or visit http://www.gnu.org/licenses/gpl-2.0.html
19  */
20 
21 #if defined(__APPLE__)
22 # if ! defined(__DARWIN_64_BIT_INO_T) && ! defined(_DARWIN_USE_64_BIT_INODE)
23 #  define __DARWIN_64_BIT_INO_T 0
24 # endif
25 #endif
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #include <fuse_lowlevel.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <assert.h>
39 #include <syslog.h>
40 #include <inttypes.h>
41 #include <pthread.h>
42 
43 #include "stats.h"
44 #include "oplog.h"
45 #include "datapack.h"
46 #include "clocks.h"
47 #include "mastercomm.h"
48 #include "masterproxy.h"
49 #include "getgroups.h"
50 #include "readdata.h"
51 #include "writedata.h"
52 #include "massert.h"
53 #include "strerr.h"
54 #include "MFSCommunication.h"
55 
56 #include "sustained_stats.h"
57 #include "dirattrcache.h"
58 #include "symlinkcache.h"
59 #include "negentrycache.h"
60 #include "xattrcache.h"
61 // #include "dircache.h"
62 
63 #if MFS_ROOT_ID != FUSE_ROOT_ID
64 #error FUSE_ROOT_ID is not equal to MFS_ROOT_ID
65 #endif
66 
67 #if defined(__FreeBSD__)
68 // workaround for bug in FreeBSD Fuse version (kernel part)
69 #  define FREEBSD_FALSE_TRUNCATE_WORKAROUND 1
70 #  define FREEBSD_EARLY_RELEASE_BUG_WORKAROUND 1
71 #  define FREEBSD_EARLY_RELEASE_DELAY 10.0
72 #endif
73 
74 #define READDIR_BUFFSIZE 50000
75 
76 #define MAX_FILE_SIZE (int64_t)(MFS_MAX_FILE_SIZE)
77 
78 #define PKGVERSION ((VERSMAJ)*1000000+(VERSMID)*10000+((VERSMIN)>>1)*100+(RELEASE))
79 
80 #define MASTERINFO_WITH_VERSION 1
81 // #define MASTER_NAME ".master"
82 // #define MASTER_INODE 0x7FFFFFFF
83 // 0x01b6 == 0666
84 // static uint8_t masterattr[35]={'f', 0x01,0xB6, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0,0,0,0,0};
85 
86 #define MASTERINFO_NAME ".masterinfo"
87 #define MASTERINFO_INODE 0x7FFFFFFF
88 // 0x0124 == 0b100100100 == 0444
89 #ifdef MASTERINFO_WITH_VERSION
90 static uint8_t masterinfoattr[35]={'f', 0x01,0x24, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0,0,0,0,14};
91 #else
92 static uint8_t masterinfoattr[35]={'f', 0x01,0x24, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0,0,0,0,10};
93 #endif
94 
95 #define STATS_NAME ".stats"
96 #define STATS_INODE 0x7FFFFFF0
97 // 0x01A4 == 0b110100100 == 0644
98 static uint8_t statsattr[35]={'f', 0x01,0xA4, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0,0,0,0,0};
99 
100 #define OPLOG_NAME ".oplog"
101 #define OPLOG_INODE 0x7FFFFFF1
102 #define OPHISTORY_NAME ".ophistory"
103 #define OPHISTORY_INODE 0x7FFFFFF2
104 // 0x0100 == 0b100000000 == 0400
105 static uint8_t oplogattr[35]={'f', 0x01,0x00, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0,0,0,0,0};
106 
107 #define MOOSE_NAME ".mooseart"
108 #define MOOSE_INODE 0x7FFFFFF3
109 // 0x01A4 == 0b110100100 == 0644
110 static uint8_t mooseattr[35]={'f', 0x01,0xA4, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0,0,0,0,0};
111 
112 /* DIRCACHE
113 #define ATTRCACHE_NAME ".attrcache"
114 #define ATTRCACHE_INODE 0x7FFFFFF3
115 // 0x0180 == 0b110000000 == 0600
116 static uint8_t attrcacheattr[35]={'f', 0x01,0x80, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0,0,0,0,0};
117 */
118 
119 #define MIN_SPECIAL_INODE 0x7FFFFFF0
120 #define IS_SPECIAL_INODE(ino) ((ino)>=MIN_SPECIAL_INODE)
121 #define IS_SPECIAL_NAME(name) ((name)[0]=='.' && (strcmp(STATS_NAME,(name))==0 || strcmp(MASTERINFO_NAME,(name))==0 || strcmp(OPLOG_NAME,(name))==0 || strcmp(OPHISTORY_NAME,(name))==0 || strcmp(MOOSE_NAME,(name))==0/* || strcmp(ATTRCACHE_NAME,(name))==0*/))
122 
123 typedef struct _sinfo {
124 	char *buff;
125 	uint32_t leng;
126 	uint8_t reset;
127 	pthread_mutex_t lock;
128 } sinfo;
129 
130 typedef struct _dirbuf {
131 	int wasread;
132 	int dataformat;
133 	uid_t uid;
134 	gid_t gid;
135 	const uint8_t *p;
136 	size_t size;
137 	void *dcache;
138 	pthread_mutex_t lock;
139 } dirbuf;
140 
141 enum {IO_NONE,IO_READ,IO_WRITE,IO_READONLY,IO_WRITEONLY};
142 
143 typedef struct _finfo {
144 	uint8_t mode;
145 	void *data;
146 	pthread_mutex_t lock;
147 #ifdef FREEBSD_EARLY_RELEASE_BUG_WORKAROUND
148 	uint32_t ops_in_progress;
149 	double lastuse;
150 	struct _finfo *next;
151 #endif
152 } finfo;
153 
154 #ifdef FREEBSD_EARLY_RELEASE_BUG_WORKAROUND
155 static finfo *finfo_head = NULL;
156 static pthread_mutex_t finfo_list_lock = PTHREAD_MUTEX_INITIALIZER;
157 #endif
158 
159 static int debug_mode = 0;
160 static int usedircache = 1;
161 static int keep_cache = 0;
162 static double direntry_cache_timeout = 0.1;
163 static double entry_cache_timeout = 0.0;
164 static double attr_cache_timeout = 0.1;
165 static int mkdir_copy_sgid = 0;
166 static int sugid_clear_mode = 0;
167 static int xattr_cache_on = 0;
168 static int xattr_acl_support = 0;
169 static int full_permissions = 0;
170 
171 //static int local_mode = 0;
172 //static int no_attr_cache = 0;
173 
174 enum {
175 	OP_STATFS = 0,
176 	OP_ACCESS,
177 	OP_LOOKUP,
178 	OP_ERRLOOKUP,
179 	OP_POSLOOKUP,
180 	OP_NEGLOOKUP,
181 	OP_LOOKUP_INTERNAL,
182 	OP_DIRCACHE_LOOKUP,
183 	OP_NEGCACHE_LOOKUP,
184 //	OP_DIRCACHE_LOOKUP_POSITIVE,
185 //	OP_DIRCACHE_LOOKUP_NEGATIVE,
186 //	OP_DIRCACHE_LOOKUP_NOATTR,
187 	OP_GETATTR,
188 	OP_DIRCACHE_GETATTR,
189 	OP_SETATTR,
190 	OP_MKNOD,
191 	OP_UNLINK,
192 	OP_MKDIR,
193 	OP_RMDIR,
194 	OP_SYMLINK,
195 	OP_READLINK_MASTER,
196 	OP_READLINK_CACHED,
197 	OP_RENAME,
198 	OP_LINK,
199 	OP_OPENDIR,
200 	OP_READDIR,
201 	OP_RELEASEDIR,
202 	OP_CREATE,
203 	OP_OPEN,
204 	OP_RELEASE,
205 	OP_READ,
206 	OP_WRITE,
207 	OP_FLUSH,
208 	OP_FSYNC,
209 	OP_SETXATTR,
210 	OP_GETXATTR,
211 	OP_LISTXATTR,
212 	OP_REMOVEXATTR,
213 //	OP_GETDIR_CACHED,
214 	OP_GETDIR_FULL,
215 	OP_GETDIR_SMALL,
216 	STATNODES
217 };
218 
219 static void *statsptr[STATNODES];
220 
mfs_statsptr_init(void)221 void mfs_statsptr_init(void) {
222 	void *s;
223 	s = stats_get_subnode(NULL,"fuse_ops",0,1);
224 	statsptr[OP_SETXATTR] = stats_get_subnode(s,"setxattr",0,1);
225 	statsptr[OP_GETXATTR] = stats_get_subnode(s,"getxattr",0,1);
226 	statsptr[OP_LISTXATTR] = stats_get_subnode(s,"listxattr",0,1);
227 	statsptr[OP_REMOVEXATTR] = stats_get_subnode(s,"removexattr",0,1);
228 	statsptr[OP_FSYNC] = stats_get_subnode(s,"fsync",0,1);
229 	statsptr[OP_FLUSH] = stats_get_subnode(s,"flush",0,1);
230 	statsptr[OP_WRITE] = stats_get_subnode(s,"write",0,1);
231 	statsptr[OP_READ] = stats_get_subnode(s,"read",0,1);
232 	statsptr[OP_RELEASE] = stats_get_subnode(s,"release",0,1);
233 	statsptr[OP_OPEN] = stats_get_subnode(s,"open",0,1);
234 	statsptr[OP_CREATE] = stats_get_subnode(s,"create",0,1);
235 	statsptr[OP_RELEASEDIR] = stats_get_subnode(s,"releasedir",0,1);
236 	statsptr[OP_READDIR] = stats_get_subnode(s,"readdir",0,1);
237 	statsptr[OP_OPENDIR] = stats_get_subnode(s,"opendir",0,1);
238 	statsptr[OP_LINK] = stats_get_subnode(s,"link",0,1);
239 	statsptr[OP_RENAME] = stats_get_subnode(s,"rename",0,1);
240 	{
241 		void *rl;
242 		rl = stats_get_subnode(s,"readlink",0,1);
243 		statsptr[OP_READLINK_MASTER] = stats_get_subnode(rl,"master",0,1);
244 		statsptr[OP_READLINK_CACHED] = stats_get_subnode(rl,"cached",0,1);
245 	}
246 	statsptr[OP_SYMLINK] = stats_get_subnode(s,"symlink",0,1);
247 	statsptr[OP_RMDIR] = stats_get_subnode(s,"rmdir",0,1);
248 	statsptr[OP_MKDIR] = stats_get_subnode(s,"mkdir",0,1);
249 	statsptr[OP_UNLINK] = stats_get_subnode(s,"unlink",0,1);
250 	statsptr[OP_MKNOD] = stats_get_subnode(s,"mknod",0,1);
251 	statsptr[OP_SETATTR] = stats_get_subnode(s,"setattr",0,1);
252 	statsptr[OP_GETATTR] = stats_get_subnode(s,"getattr",0,1);
253 	statsptr[OP_DIRCACHE_GETATTR] = stats_get_subnode(s,"getattr-cached",0,1);
254 	{
255 		void *l,*cl,*nl;
256 		l = stats_get_subnode(s,"lookup",0,1);
257 		cl = stats_get_subnode(l,"cached",0,1);
258 		nl = stats_get_subnode(l,"master",0,1);
259 		statsptr[OP_LOOKUP_INTERNAL] = stats_get_subnode(l,"internal",0,1);
260 		statsptr[OP_POSLOOKUP] = stats_get_subnode(nl,"positive",0,1);
261 		statsptr[OP_NEGLOOKUP] = stats_get_subnode(nl,"negative",0,1);
262 		statsptr[OP_ERRLOOKUP] = stats_get_subnode(nl,"error",0,1);
263 		if (usedircache) {
264 			statsptr[OP_DIRCACHE_LOOKUP] = stats_get_subnode(cl,"readdir",0,1);
265 		}
266 		statsptr[OP_NEGCACHE_LOOKUP] = stats_get_subnode(cl,"negative",0,1);
267 	}
268 	statsptr[OP_ACCESS] = stats_get_subnode(s,"access",0,1);
269 	statsptr[OP_STATFS] = stats_get_subnode(s,"statfs",0,1);
270 	{
271 		void *rd;
272 		rd = stats_get_subnode(s,"readdir",0,1);
273 		if (usedircache) {
274 			statsptr[OP_GETDIR_FULL] = stats_get_subnode(rd,"with_attrs",0,1);
275 		}
276 		statsptr[OP_GETDIR_SMALL] = stats_get_subnode(rd,"without_attrs",0,1);
277 	}
278 }
279 
mfs_stats_inc(uint8_t id)280 void mfs_stats_inc(uint8_t id) {
281 	if (id<STATNODES) {
282 		stats_counter_inc(statsptr[id]);
283 	}
284 }
285 
286 static pthread_key_t aclstorage;
287 
mfs_aclstorage_free(void * ptr)288 void mfs_aclstorage_free(void *ptr) {
289 	if (ptr!=NULL) {
290 		free(ptr);
291 	}
292 }
293 
mfs_aclstorage_init(void)294 void mfs_aclstorage_init(void) {
295 	zassert(pthread_key_create(&aclstorage,mfs_aclstorage_free));
296 	zassert(pthread_setspecific(aclstorage,NULL));
297 }
298 
mfs_aclstorage_get(uint32_t size)299 void* mfs_aclstorage_get(uint32_t size) {
300 	uint8_t *buff,*p;
301 	const uint8_t *cp;
302 	uint32_t s;
303 	buff = pthread_getspecific(aclstorage);
304 	if (buff!=NULL) {
305 		cp = p = buff;
306 		s = get32bit(&cp);
307 		if (size<=s) {
308 			return (buff+4);
309 		}
310 		free(buff);
311 	}
312 	buff = malloc(size+4);
313 	passert(buff);
314 	p = buff;
315 	put32bit(&p,size);
316 	zassert(pthread_setspecific(aclstorage,buff));
317 	return p;
318 }
319 
320 #ifndef EDQUOT
321 # define EDQUOT ENOSPC
322 #endif
323 #ifndef ENOATTR
324 # ifdef ENODATA
325 #  define ENOATTR ENODATA
326 # else
327 #  define ENOATTR ENOENT
328 # endif
329 #endif
330 
mfs_errorconv(int status)331 static int mfs_errorconv(int status) {
332 	int ret;
333 	switch (status) {
334 	case STATUS_OK:
335 		ret=0;
336 		break;
337 	case ERROR_EPERM:
338 		ret=EPERM;
339 		break;
340 	case ERROR_ENOTDIR:
341 		ret=ENOTDIR;
342 		break;
343 	case ERROR_ENOENT:
344 		ret=ENOENT;
345 		break;
346 	case ERROR_EACCES:
347 		ret=EACCES;
348 		break;
349 	case ERROR_EEXIST:
350 		ret=EEXIST;
351 		break;
352 	case ERROR_EINVAL:
353 		ret=EINVAL;
354 		break;
355 	case ERROR_ENOTEMPTY:
356 		ret=ENOTEMPTY;
357 		break;
358 	case ERROR_IO:
359 		ret=EIO;
360 		break;
361 	case ERROR_EROFS:
362 		ret=EROFS;
363 		break;
364 	case ERROR_QUOTA:
365 		ret=EDQUOT;
366 		break;
367 	case ERROR_ENOATTR:
368 		ret=ENOATTR;
369 		break;
370 	case ERROR_ENOTSUP:
371 		ret=ENOTSUP;
372 		break;
373 	case ERROR_ERANGE:
374 		ret=ERANGE;
375 		break;
376 	case ERROR_NOSPACE:
377 		ret=ENOSPC;
378 		break;
379 	case ERROR_CHUNKLOST:
380 		ret=ENXIO;
381 		break;
382 	case ERROR_NOCHUNKSERVERS:
383 		ret=ENOSPC;
384 		break;
385 	case ERROR_CSNOTPRESENT:
386 		ret=ENXIO;
387 		break;
388 	default:
389 		ret=EINVAL;
390 		break;
391 	}
392 	if (debug_mode && ret!=0) {
393 #ifdef HAVE_STRERROR_R
394 		char errorbuff[500];
395 # ifdef STRERROR_R_CHAR_P
396 		fprintf(stderr,"status: %s\n",strerror_r(ret,errorbuff,500));
397 # else
398 		strerror_r(ret,errorbuff,500);
399 		fprintf(stderr,"status: %s\n",errorbuff);
400 # endif
401 #else
402 # ifdef HAVE_PERROR
403 		errno=ret;
404 		perror("status: ");
405 # else
406 		fprintf(stderr,"status: %d\n",ret);
407 # endif
408 #endif
409 	}
410 	return ret;
411 }
412 
fsnodes_type_convert(uint8_t type)413 static inline uint8_t fsnodes_type_convert(uint8_t type) {
414 	switch (type) {
415 		case DISP_TYPE_FILE:
416 			return TYPE_FILE;
417 		case DISP_TYPE_DIRECTORY:
418 			return TYPE_DIRECTORY;
419 		case DISP_TYPE_SYMLINK:
420 			return TYPE_SYMLINK;
421 		case DISP_TYPE_FIFO:
422 			return TYPE_FIFO;
423 		case DISP_TYPE_BLOCKDEV:
424 			return TYPE_BLOCKDEV;
425 		case DISP_TYPE_CHARDEV:
426 			return TYPE_CHARDEV;
427 		case DISP_TYPE_SOCKET:
428 			return TYPE_SOCKET;
429 		case DISP_TYPE_TRASH:
430 			return TYPE_TRASH;
431 		case DISP_TYPE_SUSTAINED:
432 			return TYPE_SUSTAINED;
433 	}
434 	return 0;
435 }
436 
mfs_type_to_stat(uint32_t inode,uint8_t type,struct stat * stbuf)437 static void mfs_type_to_stat(uint32_t inode,uint8_t type, struct stat *stbuf) {
438 	memset(stbuf,0,sizeof(struct stat));
439 	stbuf->st_ino = inode;
440 	switch (type&0x7F) {
441 	case DISP_TYPE_DIRECTORY:
442 	case TYPE_DIRECTORY:
443 		stbuf->st_mode = S_IFDIR;
444 		break;
445 	case DISP_TYPE_SYMLINK:
446 	case TYPE_SYMLINK:
447 		stbuf->st_mode = S_IFLNK;
448 		break;
449 	case DISP_TYPE_FILE:
450 	case TYPE_FILE:
451 		stbuf->st_mode = S_IFREG;
452 		break;
453 	case DISP_TYPE_FIFO:
454 	case TYPE_FIFO:
455 		stbuf->st_mode = S_IFIFO;
456 		break;
457 	case DISP_TYPE_SOCKET:
458 	case TYPE_SOCKET:
459 		stbuf->st_mode = S_IFSOCK;
460 		break;
461 	case DISP_TYPE_BLOCKDEV:
462 	case TYPE_BLOCKDEV:
463 		stbuf->st_mode = S_IFBLK;
464 		break;
465 	case DISP_TYPE_CHARDEV:
466 	case TYPE_CHARDEV:
467 		stbuf->st_mode = S_IFCHR;
468 		break;
469 	default:
470 		stbuf->st_mode = 0;
471 	}
472 }
473 
mfs_attr_get_type(const uint8_t attr[35])474 static inline uint8_t mfs_attr_get_type(const uint8_t attr[35]) {
475 	if (attr[0]<64) { // 1.7.29 and up
476 		return (attr[1]>>4);
477 	} else {
478 		return fsnodes_type_convert(attr[0]&0x7F);
479 	}
480 }
481 
mfs_attr_get_mattr(const uint8_t attr[35])482 static inline uint8_t mfs_attr_get_mattr(const uint8_t attr[35]) {
483 	if (attr[0]<64) { // 1.7.29 and up
484 		return attr[0];
485 	} else {
486 		return (attr[1]>>4);
487 	}
488 }
489 
mfs_attr_modify(uint32_t to_set,uint8_t attr[35],struct stat * stbuf)490 static void mfs_attr_modify(uint32_t to_set,uint8_t attr[35],struct stat *stbuf) {
491 	uint8_t mattr;
492 	uint16_t attrmode;
493 	uint8_t attrtype;
494 	uint32_t attruid,attrgid,attratime,attrmtime,attrctime;
495 	const uint8_t *ptr;
496 	uint8_t *wptr;
497 	ptr = attr;
498 	if (attr[0]<64) { // 1.7.29 and up
499 		mattr = get8bit(&ptr);
500 		attrmode = get16bit(&ptr);
501 		attrtype = (attrmode>>12);
502 	} else {
503 		attrtype = get8bit(&ptr);
504 		attrtype = fsnodes_type_convert(attrtype&0x7F);
505 		attrmode = get16bit(&ptr);
506 		mattr = attrmode >> 12;
507 	}
508 	attrmode &= 0x0FFF;
509 	attruid = get32bit(&ptr);
510 	attrgid = get32bit(&ptr);
511 	attratime = get32bit(&ptr);
512 	attrmtime = get32bit(&ptr);
513 	attrctime = get32bit(&ptr);
514 	if (to_set & FUSE_SET_ATTR_MODE) {
515 		attrmode = stbuf->st_mode & 07777;
516 	}
517 	if (to_set & FUSE_SET_ATTR_UID) {
518 		attruid = stbuf->st_uid;
519 	}
520 	if (to_set & FUSE_SET_ATTR_GID) {
521 		attrgid = stbuf->st_gid;
522 	}
523 	if (to_set & FUSE_SET_ATTR_ATIME) {
524 		attratime = stbuf->st_atime;
525 	}
526 	if (to_set & FUSE_SET_ATTR_MTIME) {
527 		attratime = stbuf->st_mtime;
528 	}
529 	attrctime = time(NULL);
530 	wptr = attr;
531 	put8bit(&wptr,mattr);
532 	attrmode |= ((uint16_t)attrtype)<<12;
533 	put16bit(&wptr,attrmode);
534 	put32bit(&wptr,attruid);
535 	put32bit(&wptr,attrgid);
536 	put32bit(&wptr,attratime);
537 	put32bit(&wptr,attrmtime);
538 	put32bit(&wptr,attrctime);
539 }
540 
mfs_attr_to_stat(uint32_t inode,const uint8_t attr[35],struct stat * stbuf)541 static void mfs_attr_to_stat(uint32_t inode,const uint8_t attr[35], struct stat *stbuf) {
542 	uint16_t attrmode;
543 	uint8_t attrtype;
544 	uint32_t attruid,attrgid,attratime,attrmtime,attrctime,attrnlink,attrrdev;
545 	uint64_t attrlength;
546 	const uint8_t *ptr;
547 	ptr = attr;
548 	if (attr[0]<64) { // 1.7.29 and up
549 		ptr++;
550 		attrmode = get16bit(&ptr);
551 		attrtype = (attrmode>>12);
552 	} else {
553 		attrtype = get8bit(&ptr);
554 		attrtype = fsnodes_type_convert(attrtype&0x7F);
555 		attrmode = get16bit(&ptr);
556 	}
557 	attrmode &= 0x0FFF;
558 	attruid = get32bit(&ptr);
559 	attrgid = get32bit(&ptr);
560 	attratime = get32bit(&ptr);
561 	attrmtime = get32bit(&ptr);
562 	attrctime = get32bit(&ptr);
563 	attrnlink = get32bit(&ptr);
564 	stbuf->st_ino = inode;
565 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
566 	stbuf->st_blksize = MFSBLOCKSIZE;
567 #endif
568 	switch (attrtype & 0x7F) {
569 	case TYPE_DIRECTORY:
570 		stbuf->st_mode = S_IFDIR | attrmode;
571 		attrlength = get64bit(&ptr);
572 		stbuf->st_size = attrlength;
573 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
574 		stbuf->st_blocks = (attrlength+511)/512;
575 #endif
576 		break;
577 	case TYPE_SYMLINK:
578 		stbuf->st_mode = S_IFLNK | attrmode;
579 		attrlength = get64bit(&ptr);
580 		stbuf->st_size = attrlength;
581 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
582 		stbuf->st_blocks = (attrlength+511)/512;
583 #endif
584 		break;
585 	case TYPE_FILE:
586 		stbuf->st_mode = S_IFREG | attrmode;
587 		attrlength = get64bit(&ptr);
588 		stbuf->st_size = attrlength;
589 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
590 		stbuf->st_blocks = (attrlength+511)/512;
591 #endif
592 		break;
593 	case TYPE_FIFO:
594 		stbuf->st_mode = S_IFIFO | attrmode;
595 		stbuf->st_size = 0;
596 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
597 		stbuf->st_blocks = 0;
598 #endif
599 		break;
600 	case TYPE_SOCKET:
601 		stbuf->st_mode = S_IFSOCK | attrmode;
602 		stbuf->st_size = 0;
603 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
604 		stbuf->st_blocks = 0;
605 #endif
606 		break;
607 	case TYPE_BLOCKDEV:
608 		stbuf->st_mode = S_IFBLK | attrmode;
609 		attrrdev = get32bit(&ptr);
610 #ifdef HAVE_STRUCT_STAT_ST_RDEV
611 		stbuf->st_rdev = attrrdev;
612 #endif
613 		stbuf->st_size = 0;
614 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
615 		stbuf->st_blocks = 0;
616 #endif
617 		break;
618 	case TYPE_CHARDEV:
619 		stbuf->st_mode = S_IFCHR | attrmode;
620 		attrrdev = get32bit(&ptr);
621 #ifdef HAVE_STRUCT_STAT_ST_RDEV
622 		stbuf->st_rdev = attrrdev;
623 #endif
624 		stbuf->st_size = 0;
625 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
626 		stbuf->st_blocks = 0;
627 #endif
628 		break;
629 	default:
630 		stbuf->st_mode = 0;
631 	}
632 	stbuf->st_uid = attruid;
633 	stbuf->st_gid = attrgid;
634 	stbuf->st_atime = attratime;
635 	stbuf->st_mtime = attrmtime;
636 	stbuf->st_ctime = attrctime;
637 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
638 	stbuf->st_birthtime = attrctime;	// for future use
639 #endif
640 	stbuf->st_nlink = attrnlink;
641 }
642 
mfs_makemodestr(char modestr[11],uint16_t mode)643 static inline void mfs_makemodestr(char modestr[11],uint16_t mode) {
644 	uint32_t i;
645 	strcpy(modestr,"?rwxrwxrwx");
646 	switch (mode & S_IFMT) {
647 	case S_IFSOCK:
648 		modestr[0] = 's';
649 		break;
650 	case S_IFLNK:
651 		modestr[0] = 'l';
652 		break;
653 	case S_IFREG:
654 		modestr[0] = '-';
655 		break;
656 	case S_IFBLK:
657 		modestr[0] = 'b';
658 		break;
659 	case S_IFDIR:
660 		modestr[0] = 'd';
661 		break;
662 	case S_IFCHR:
663 		modestr[0] = 'c';
664 		break;
665 	case S_IFIFO:
666 		modestr[0] = 'f';
667 		break;
668 	}
669 	if (mode & S_ISUID) {
670 		modestr[3] = 's';
671 	}
672 	if (mode & S_ISGID) {
673 		modestr[6] = 's';
674 	}
675 	if (mode & S_ISVTX) {
676 		modestr[9] = 't';
677 	}
678 	for (i=0 ; i<9 ; i++) {
679 		if ((mode & (1<<i))==0) {
680 			if (modestr[9-i]=='s' || modestr[9-i]=='t') {
681 				modestr[9-i]&=0xDF;
682 			} else {
683 				modestr[9-i]='-';
684 			}
685 		}
686 	}
687 }
688 
mfs_makeattrstr(char * buff,uint32_t size,struct stat * stbuf)689 static void mfs_makeattrstr(char *buff,uint32_t size,struct stat *stbuf) {
690 	char modestr[11];
691 	mfs_makemodestr(modestr,stbuf->st_mode);
692 #ifdef HAVE_STRUCT_STAT_ST_RDEV
693 	if (modestr[0]=='b' || modestr[0]=='c') {
694 		snprintf(buff,size,"[%s:0%06o,%u,%ld,%ld,%lu,%lu,%lu,%llu,%08lX]",modestr,(unsigned int)(stbuf->st_mode),(unsigned int)(stbuf->st_nlink),(long int)stbuf->st_uid,(long int)stbuf->st_gid,(unsigned long int)(stbuf->st_atime),(unsigned long int)(stbuf->st_mtime),(unsigned long int)(stbuf->st_ctime),(unsigned long long int)(stbuf->st_size),(unsigned long int)(stbuf->st_rdev));
695 	} else {
696 		snprintf(buff,size,"[%s:0%06o,%u,%ld,%ld,%lu,%lu,%lu,%llu]",modestr,(unsigned int)(stbuf->st_mode),(unsigned int)(stbuf->st_nlink),(long int)stbuf->st_uid,(long int)stbuf->st_gid,(unsigned long int)(stbuf->st_atime),(unsigned long int)(stbuf->st_mtime),(unsigned long int)(stbuf->st_ctime),(unsigned long long int)(stbuf->st_size));
697 	}
698 #else
699 	snprintf(buff,size,"[%s:0%06o,%u,%ld,%ld,%lu,%lu,%lu,%llu]",modestr,(unsigned int)(stbuf->st_mode),(unsigned int)(stbuf->st_nlink),(long int)stbuf->st_uid,(long int)stbuf->st_gid,(unsigned long int)(stbuf->st_atime),(unsigned long int)(stbuf->st_mtime),(unsigned long int)(stbuf->st_ctime),(unsigned long long int)(stbuf->st_size));
700 #endif
701 }
702 
703 #if FUSE_USE_VERSION >= 26
mfs_statfs(fuse_req_t req,fuse_ino_t ino)704 void mfs_statfs(fuse_req_t req,fuse_ino_t ino) {
705 #else
706 void mfs_statfs(fuse_req_t req) {
707 #endif
708 	uint64_t totalspace,availspace,trashspace,sustainedspace;
709 	uint32_t inodes;
710 	uint32_t bsize;
711 	struct statvfs stfsbuf;
712 	memset(&stfsbuf,0,sizeof(stfsbuf));
713 	struct fuse_ctx ctx;
714 
715 	ctx = *(fuse_req_ctx(req));
716 	mfs_stats_inc(OP_STATFS);
717 	if (debug_mode) {
718 #if FUSE_USE_VERSION >= 26
719 		oplog_printf(&ctx,"statfs (%lu)",(unsigned long int)ino);
720 #else
721 		oplog_printf(&ctx,"statfs ()");
722 #endif
723 	}
724 #if FUSE_USE_VERSION >= 26
725 	(void)ino;
726 #endif
727 	fs_statfs(&totalspace,&availspace,&trashspace,&sustainedspace,&inodes);
728 
729 #if defined(__APPLE__)
730 	if (totalspace>0x0001000000000000ULL) {
731 		bsize = 0x20000;
732 	} else {
733 		bsize = 0x10000;
734 	}
735 #else
736 	bsize = 0x10000;
737 #endif
738 
739 	stfsbuf.f_namemax = MFS_NAME_MAX;
740 	stfsbuf.f_frsize = bsize;
741 	stfsbuf.f_bsize = bsize;
742 #if defined(__APPLE__)
743 	// FUSE on apple (or other parts of kernel) expects 32-bit values, so it's better to saturate this values than let being cut on 32-bit
744 	// can't change bsize also because 64k seems to be the biggest acceptable value for bsize
745 
746 	if (totalspace/bsize>0xFFFFFFFFU) {
747 		stfsbuf.f_blocks = 0xFFFFFFFFU;
748 	} else {
749 		stfsbuf.f_blocks = totalspace/bsize;
750 	}
751 	if (availspace/bsize>0xFFFFFFFFU) {
752 		stfsbuf.f_bfree = 0xFFFFFFFFU;
753 		stfsbuf.f_bavail = 0xFFFFFFFFU;
754 	} else {
755 		stfsbuf.f_bfree = availspace/bsize;
756 		stfsbuf.f_bavail = availspace/bsize;
757 	}
758 #else
759 	stfsbuf.f_blocks = totalspace/bsize;
760 	stfsbuf.f_bfree = availspace/bsize;
761 	stfsbuf.f_bavail = availspace/bsize;
762 #endif
763 	stfsbuf.f_files = 1000000000+PKGVERSION+inodes;
764 	stfsbuf.f_ffree = 1000000000+PKGVERSION;
765 	stfsbuf.f_favail = 1000000000+PKGVERSION;
766 	//stfsbuf.f_flag = ST_RDONLY;
767 #if FUSE_USE_VERSION >= 26
768 	oplog_printf(&ctx,"statfs (%lu): OK (%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu32")",(unsigned long int)ino,totalspace,availspace,trashspace,sustainedspace,inodes);
769 #else
770 	oplog_printf(&ctx,"statfs (): OK (%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu32")",totalspace,availspace,trashspace,sustainedspace,inodes);
771 #endif
772 	fuse_reply_statfs(req,&stfsbuf);
773 }
774 
775 /*
776 static int mfs_node_access(uint8_t attr[32],uint32_t uid,uint32_t gid,int mask) {
777 	uint32_t emode,mmode;
778 	uint32_t attruid,attrgid;
779 	uint16_t attrmode;
780 	uint8_t *ptr;
781 	if (uid == 0) {
782 		return 1;
783 	}
784 	ptr = attr+2;
785 	attrmode = get16bit(&ptr);
786 	attruid = get32bit(&ptr);
787 	attrgid = get32bit(&ptr);
788 	if (uid == attruid) {
789 		emode = (attrmode & 0700) >> 6;
790 	} else if (gid == attrgid) {
791 		emode = (attrmode & 0070) >> 3;
792 	} else {
793 		emode = attrmode & 0007;
794 	}
795 	mmode = 0;
796 	if (mask & R_OK) {
797 		mmode |= 4;
798 	}
799 	if (mask & W_OK) {
800 		mmode |= 2;
801 	}
802 	if (mask & X_OK) {
803 		mmode |= 1;
804 	}
805 	if ((emode & mmode) == mmode) {
806 		return 1;
807 	}
808 	return 0;
809 }
810 */
811 
812 // simple access test for deleted cwd nodes - no ACL's
813 int mfs_access_test(const uint8_t attr[35],int mmode,uint32_t uid,uint32_t gidcnt,uint32_t *gidtab) {
814 	uint8_t modebits,gok;
815 	uint16_t attrmode;
816 	uint32_t attruid,attrgid;
817 	const uint8_t *ptr;
818 
819 	if (uid==0) {
820 		return 0;
821 	}
822 	ptr = attr+1;
823 	attrmode = get16bit(&ptr);
824 	attruid = get32bit(&ptr);
825 	attrgid = get32bit(&ptr);
826 
827 	if (uid == attruid) {
828 		modebits = (attrmode >> 6) & 7;
829 	} else {
830 		gok = 0;
831 		while (gidcnt>0) {
832 			gidcnt--;
833 			if (gidtab[gidcnt] == attrgid) {
834 				modebits = (attrmode >> 3) & 7;
835 				gok = 1;
836 				break;
837 			}
838 		}
839 		if (gok==0) {
840 			modebits = attrmode & 7;
841 		}
842 	}
843 	if ((mmode & modebits) == mmode) {
844 		return 0;
845 	}
846 	return EACCES;
847 }
848 
849 void mfs_access(fuse_req_t req, fuse_ino_t ino, int mask) {
850 	int status;
851 	struct fuse_ctx ctx;
852 	uint8_t attr[35];
853 	groups *gids;
854 	int mmode;
855 	int force_mode;
856 
857 	ctx = *(fuse_req_ctx(req));
858 	if (debug_mode) {
859 		oplog_printf(&ctx,"access (%lu,0x%X) ...",(unsigned long int)ino,mask);
860 		fprintf(stderr,"access (%lu,0x%X)\n",(unsigned long int)ino,mask);
861 	}
862 	mfs_stats_inc(OP_ACCESS);
863 #if (R_OK==MODE_MASK_R) && (W_OK==MODE_MASK_W) && (X_OK==MODE_MASK_X)
864 	mmode = mask;
865 #else
866 	mmode = 0;
867 	if (mask & R_OK) {
868 		mmode |= MODE_MASK_R;
869 	}
870 	if (mask & W_OK) {
871 		mmode |= MODE_MASK_W;
872 	}
873 	if (mask & X_OK) {
874 		mmode |= MODE_MASK_X;
875 	}
876 #endif
877 	if (IS_SPECIAL_INODE(ino)) {
878 		if (mask & (W_OK | X_OK)) {
879 			fuse_reply_err(req,EACCES);
880 		} else {
881 			fuse_reply_err(req,0);
882 		}
883 		return;
884 	}
885 
886 	if (full_permissions) {
887 		gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
888 		status = fs_access(ino,ctx.uid,gids->gidcnt,gids->gidtab,mmode);
889 		groups_rel(gids);
890 	} else {
891 		uint32_t gidtmp = ctx.gid;
892 		status = fs_access(ino,ctx.uid,1,&gidtmp,mmode);
893 	}
894 	force_mode = 0;
895 	if (status==ERROR_ENOENT) {
896 		if (ctx.pid == getpid()) {
897 			force_mode = 1;
898 		}
899 		if (sstats_get(ino,attr,force_mode)==STATUS_OK) {
900 			if (force_mode==0) {
901 				force_mode = 2;
902 			}
903 		}
904 	}
905 	if (force_mode) {
906 		if (force_mode == 1) {
907 			if (debug_mode) {
908 				fprintf(stderr,"special case: internal access (%lu,0x%X) - positive answer\n",(unsigned long int)ino,mask);
909 			}
910 			oplog_printf(&ctx,"special case: internal access (%lu,0x%X): OK",(unsigned long int)ino,mask);
911 			status = 0;
912 		} else {
913 			if (debug_mode) {
914 				fprintf(stderr,"special case: sustained access (%lu,0x%X) - using stored data\n",(unsigned long int)ino,mask);
915 			}
916 			if (full_permissions) {
917 				gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
918 				status = mfs_access_test(attr,mmode,ctx.uid,gids->gidcnt,gids->gidtab);
919 				groups_rel(gids);
920 			} else {
921 				uint32_t gidtmp = ctx.gid;
922 				status = mfs_access_test(attr,mmode,ctx.uid,1,&gidtmp);
923 			}
924 		}
925 	} else {
926 		status = mfs_errorconv(status);
927 		if (status!=0) {
928 			oplog_printf(&ctx,"access (%lu,0x%X): %s",(unsigned long int)ino,mask,strerr(status));
929 		} else {
930 			oplog_printf(&ctx,"access (%lu,0x%X): OK",(unsigned long int)ino,mask);
931 		}
932 	}
933 	fuse_reply_err(req,status);
934 }
935 
936 void mfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) {
937 	struct fuse_entry_param e;
938 	uint64_t maxfleng;
939 	uint32_t inode;
940 	uint32_t nleng;
941 	uint8_t attr[35];
942 	char attrstr[256];
943 	uint8_t mattr,type;
944 	uint8_t icacheflag;
945 	int status;
946 	struct fuse_ctx ctx;
947 	groups *gids;
948 
949 	ctx = *(fuse_req_ctx(req));
950 	if (debug_mode) {
951 		oplog_printf(&ctx,"lookup (%lu,%s) ...",(unsigned long int)parent,name);
952 		fprintf(stderr,"lookup (%lu,%s)\n",(unsigned long int)parent,name);
953 	}
954 	nleng = strlen(name);
955 	if (nleng>MFS_NAME_MAX) {
956 		mfs_stats_inc(OP_ERRLOOKUP);
957 		oplog_printf(&ctx,"lookup (%lu,%s): %s",(unsigned long int)parent,name,strerr(ENAMETOOLONG));
958 		fuse_reply_err(req, ENAMETOOLONG);
959 		return;
960 	}
961 	if (parent==FUSE_ROOT_ID) {
962 		if (nleng==2 && name[0]=='.' && name[1]=='.') {
963 			nleng=1;
964 		}
965 		if (strcmp(name,MASTERINFO_NAME)==0) {
966 			memset(&e, 0, sizeof(e));
967 			e.ino = MASTERINFO_INODE;
968 			e.generation = 1;
969 			e.attr_timeout = 3600.0;
970 			e.entry_timeout = 3600.0;
971 			mfs_attr_to_stat(MASTERINFO_INODE,masterinfoattr,&e.attr);
972 			mfs_stats_inc(OP_LOOKUP_INTERNAL);
973 			mfs_makeattrstr(attrstr,256,&e.attr);
974 			oplog_printf(&ctx,"lookup (%lu,%s) (internal node: MASTERINFO): OK (%.1lf,%lu,%.1lf,%s)",(unsigned long int)parent,name,e.entry_timeout,(unsigned long int)e.ino,e.attr_timeout,attrstr);
975 			fuse_reply_entry(req, &e);
976 			return ;
977 		}
978 		if (strcmp(name,STATS_NAME)==0) {
979 			memset(&e, 0, sizeof(e));
980 			e.ino = STATS_INODE;
981 			e.generation = 1;
982 			e.attr_timeout = 3600.0;
983 			e.entry_timeout = 3600.0;
984 			mfs_attr_to_stat(STATS_INODE,statsattr,&e.attr);
985 			mfs_stats_inc(OP_LOOKUP_INTERNAL);
986 			mfs_makeattrstr(attrstr,256,&e.attr);
987 			oplog_printf(&ctx,"lookup (%lu,%s) (internal node: STATS): OK (%.1lf,%lu,%.1lf,%s)",(unsigned long int)parent,name,e.entry_timeout,(unsigned long int)e.ino,e.attr_timeout,attrstr);
988 			fuse_reply_entry(req, &e);
989 			return ;
990 		}
991 		if (strcmp(name,MOOSE_NAME)==0) {
992 			memset(&e, 0, sizeof(e));
993 			e.ino = MOOSE_INODE;
994 			e.generation = 1;
995 			e.attr_timeout = 3600.0;
996 			e.entry_timeout = 3600.0;
997 			mfs_attr_to_stat(MOOSE_INODE,mooseattr,&e.attr);
998 			mfs_stats_inc(OP_LOOKUP_INTERNAL);
999 			mfs_makeattrstr(attrstr,256,&e.attr);
1000 			oplog_printf(&ctx,"lookup (%lu,%s) (internal node: MOOSE): OK (%.1lf,%lu,%.1lf,%s)",(unsigned long int)parent,name,e.entry_timeout,(unsigned long int)e.ino,e.attr_timeout,attrstr);
1001 			fuse_reply_entry(req, &e);
1002 			return ;
1003 		}
1004 		if (strcmp(name,OPLOG_NAME)==0) {
1005 			memset(&e, 0, sizeof(e));
1006 			e.ino = OPLOG_INODE;
1007 			e.generation = 1;
1008 			e.attr_timeout = 3600.0;
1009 			e.entry_timeout = 3600.0;
1010 			mfs_attr_to_stat(OPLOG_INODE,oplogattr,&e.attr);
1011 			mfs_stats_inc(OP_LOOKUP_INTERNAL);
1012 			mfs_makeattrstr(attrstr,256,&e.attr);
1013 			oplog_printf(&ctx,"lookup (%lu,%s) (internal node: OPLOG): OK (%.1lf,%lu,%.1lf,%s)",(unsigned long int)parent,name,e.entry_timeout,(unsigned long int)e.ino,e.attr_timeout,attrstr);
1014 			fuse_reply_entry(req, &e);
1015 			return ;
1016 		}
1017 		if (strcmp(name,OPHISTORY_NAME)==0) {
1018 			memset(&e, 0, sizeof(e));
1019 			e.ino = OPHISTORY_INODE;
1020 			e.generation = 1;
1021 			e.attr_timeout = 3600.0;
1022 			e.entry_timeout = 3600.0;
1023 			mfs_attr_to_stat(OPHISTORY_INODE,oplogattr,&e.attr);
1024 			mfs_stats_inc(OP_LOOKUP_INTERNAL);
1025 			mfs_makeattrstr(attrstr,256,&e.attr);
1026 			oplog_printf(&ctx,"lookup (%lu,%s) (internal node: OPHISTORY): OK (%.1lf,%lu,%.1lf,%s)",(unsigned long int)parent,name,e.entry_timeout,(unsigned long int)e.ino,e.attr_timeout,attrstr);
1027 			fuse_reply_entry(req, &e);
1028 			return ;
1029 		}
1030 /*
1031 		if (strcmp(name,ATTRCACHE_NAME)==0) {
1032 			memset(&e, 0, sizeof(e));
1033 			e.ino = ATTRCACHE_INODE;
1034 			e.generation = 1;
1035 			e.attr_timeout = 3600.0;
1036 			e.entry_timeout = 3600.0;
1037 			mfs_attr_to_stat(ATTRCACHE_INODE,attrcacheattr,&e.attr);
1038 			mfs_stats_inc(OP_LOOKUP_INTERNAL);
1039 			oplog_printf(&ctx,"lookup (%lu,%s) (internal node: ATTRCACHE)",(unsigned long int)parent,name);
1040 			fuse_reply_entry(req, &e);
1041 			return ;
1042 		}
1043 */
1044 	}
1045 /*
1046 	if (newdircache) {
1047 		const uint8_t *dbuff;
1048 		uint32_t dsize;
1049 		switch (dir_cache_lookup(parent,nleng,(const uint8_t*)name,&inode,attr)) {
1050 			case -1:
1051 				mfs_stats_inc(OP_DIRCACHE_LOOKUP_NEGATIVE);
1052 				oplog_printf(&ctx,"lookup (%lu,%s) (cached answer: %s)",(unsigned long int)parent,name,strerr(ENOENT));
1053 				fuse_reply_err(req,ENOENT);
1054 				return;
1055 			case 1:
1056 				mfs_stats_inc(OP_DIRCACHE_LOOKUP_POSITIVE);
1057 				status = 0;
1058 				oplog_printf(&ctx,"lookup (%lu,%s) (cached answer: %lu)",(unsigned long int)parent,name,(unsigned long int)inode);
1059 				break;
1060 			case -2:
1061 				mfs_stats_inc(OP_LOOKUP);
1062 				status = fs_lookup(parent,nleng,(const uint8_t*)name,ctx.uid,ctx.gid,&inode,attr);
1063 				status = mfs_errorconv(status);
1064 				if (status!=0) {
1065 					oplog_printf(&ctx,"lookup (%lu,%s) (lookup forced by cache: %s)",(unsigned long int)parent,name,strerr(status));
1066 				} else {
1067 					oplog_printf(&ctx,"lookup (%lu,%s) (lookup forced by cache: %lu)",(unsigned long int)parent,name,(unsigned long int)inode);
1068 				}
1069 				break;
1070 			case -3:
1071 				mfs_stats_inc(OP_DIRCACHE_LOOKUP_NOATTR);
1072 				status = fs_getattr(inode,ctx.uid,ctx.gid,attr);
1073 				status = mfs_errorconv(status);
1074 				if (status!=0) {
1075 					oplog_printf(&ctx,"lookup (%lu,%s) (getattr forced by cache: %s)",(unsigned long int)parent,name,strerr(status));
1076 				} else {
1077 					oplog_printf(&ctx,"lookup (%lu,%s) (getattr forced by cache: %lu)",(unsigned long int)parent,name,(unsigned long int)inode);
1078 				}
1079 				break;
1080 			default:
1081 				status = fs_getdir_plus(parent,ctx.uid,ctx.gid,1,&dbuff,&dsize);
1082 				status = mfs_errorconv(status);
1083 				if (status!=0) {
1084 					oplog_printf(&ctx,"lookup (%lu,%s) (readdir: %s)",(unsigned long int)parent,name,strerr(status));
1085 					fuse_reply_err(req, status);
1086 					return;
1087 				}
1088 				mfs_stats_inc(OP_GETDIR_FULL);
1089 				dir_cache_newdirdata(parent,dsize,dbuff);
1090 				switch (dir_cache_lookup(parent,nleng,(const uint8_t*)name,&inode,attr)) {
1091 					case -1:
1092 						mfs_stats_inc(OP_DIRCACHE_LOOKUP_NEGATIVE);
1093 						oplog_printf(&ctx,"lookup (%lu,%s) (after readdir cached answer: %s)",(unsigned long int)parent,name,strerr(ENOENT));
1094 						fuse_reply_err(req,ENOENT);
1095 						return;
1096 					case 1:
1097 						mfs_stats_inc(OP_DIRCACHE_LOOKUP_POSITIVE);
1098 						oplog_printf(&ctx,"lookup (%lu,%s) (after readdir cached answer: %lu)",(unsigned long int)parent,name,(unsigned long int)inode);
1099 						break;
1100 					default:
1101 						mfs_stats_inc(OP_LOOKUP);
1102 						status = fs_lookup(parent,nleng,(const uint8_t*)name,ctx.uid,ctx.gid,&inode,attr);
1103 						status = mfs_errorconv(status);
1104 						if (status!=0) {
1105 							oplog_printf(&ctx,"lookup (%lu,%s) (after readdir lookup forced by cache: %s)",(unsigned long int)parent,name,strerr(status));
1106 						} else {
1107 							oplog_printf(&ctx,"lookup (%lu,%s) (after readdir lookup forced by cache: %lu)",(unsigned long int)parent,name,(unsigned long int)inode);
1108 						}
1109 				}
1110 		}
1111 	} else
1112 */
1113 	if (usedircache && dcache_lookup(&ctx,parent,nleng,(const uint8_t*)name,&inode,attr)) {
1114 		if (debug_mode) {
1115 			fprintf(stderr,"lookup: sending data from dircache\n");
1116 		}
1117 		mfs_stats_inc(OP_DIRCACHE_LOOKUP);
1118 		status = 0;
1119 		icacheflag = 1;
1120 //		oplog_printf(&ctx,"lookup (%lu,%s) (using open dir cache): OK (%lu)",(unsigned long int)parent,name,(unsigned long int)inode);
1121 	} else {
1122 		if (negentry_cache_search(parent,nleng,(const uint8_t*)name)) {
1123 			if (debug_mode) {
1124 				fprintf(stderr,"lookup: sending data from negcache\n");
1125 			}
1126 			oplog_printf(&ctx,"lookup (%lu,%s) (using negative entry cache): %s",(unsigned long int)parent,name,strerr(ENOENT));
1127 			mfs_stats_inc(OP_NEGCACHE_LOOKUP);
1128 			fuse_reply_err(req,ENOENT);
1129 			return;
1130 		}
1131 		if (full_permissions) {
1132 			gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
1133 			status = fs_lookup(parent,nleng,(const uint8_t*)name,ctx.uid,gids->gidcnt,gids->gidtab,&inode,attr);
1134 			groups_rel(gids);
1135 		} else {
1136 			uint32_t gidtmp = ctx.gid;
1137 			status = fs_lookup(parent,nleng,(const uint8_t*)name,ctx.uid,1,&gidtmp,&inode,attr);
1138 		}
1139 		status = mfs_errorconv(status);
1140 		icacheflag = 0;
1141 		if (status==0) {
1142 			mfs_stats_inc(OP_POSLOOKUP);
1143 		} else if (status==ENOENT) {
1144 			if (strcmp(name,MASTERINFO_NAME)==0) {
1145 				memset(&e, 0, sizeof(e));
1146 				e.ino = MASTERINFO_INODE;
1147 				e.generation = 1;
1148 				e.attr_timeout = 3600.0;
1149 				e.entry_timeout = 3600.0;
1150 				mfs_attr_to_stat(MASTERINFO_INODE,masterinfoattr,&e.attr);
1151 				mfs_stats_inc(OP_LOOKUP_INTERNAL);
1152 				mfs_makeattrstr(attrstr,256,&e.attr);
1153 				oplog_printf(&ctx,"lookup (%lu,%s) (internal node: MASTERINFO): OK (%.1lf,%lu,%.1lf,%s)",(unsigned long int)parent,name,e.entry_timeout,(unsigned long int)e.ino,e.attr_timeout,attrstr);
1154 				fuse_reply_entry(req, &e);
1155 				return ;
1156 			} else {
1157 				mfs_stats_inc(OP_NEGLOOKUP);
1158 				negentry_cache_insert(parent,nleng,(const uint8_t*)name);
1159 			}
1160 		} else {
1161 			mfs_stats_inc(OP_ERRLOOKUP);
1162 		}
1163 	}
1164 	if (status!=0) {
1165 		oplog_printf(&ctx,"lookup (%lu,%s): %s",(unsigned long int)parent,name,strerr(status));
1166 		fuse_reply_err(req, status);
1167 		return;
1168 	}
1169 	type = mfs_attr_get_type(attr);
1170 	if (type==TYPE_FILE) {
1171 		maxfleng = write_data_getmaxfleng(inode);
1172 	} else {
1173 		maxfleng = 0;
1174 	}
1175 	if (type==TYPE_DIRECTORY) {
1176 		sstats_set(inode,attr,1);
1177 	}
1178 	memset(&e, 0, sizeof(e));
1179 	e.ino = inode;
1180 	e.generation = 1;
1181 	mattr = mfs_attr_get_mattr(attr);
1182 	e.attr_timeout = (mattr&MATTR_NOACACHE)?0.0:attr_cache_timeout;
1183 	e.entry_timeout = (mattr&MATTR_NOECACHE)?0.0:((type==TYPE_DIRECTORY)?direntry_cache_timeout:entry_cache_timeout);
1184 	mfs_attr_to_stat(inode,attr,&e.attr);
1185 	if (maxfleng>(uint64_t)(e.attr.st_size)) {
1186 		e.attr.st_size=maxfleng;
1187 	}
1188 	if (mfs_attr_get_type(attr)==TYPE_FILE) {
1189 		read_inode_set_length(inode,e.attr.st_size,0);
1190 	}
1191 //	if (type==TYPE_FILE && debug_mode) {
1192 //		fprintf(stderr,"lookup inode %lu - file size: %llu\n",(unsigned long int)inode,(unsigned long long int)e.attr.st_size);
1193 //	}
1194 	mfs_makeattrstr(attrstr,256,&e.attr);
1195 	oplog_printf(&ctx,"lookup (%lu,%s)%s: OK (%.1lf,%lu,%.1lf,%s)",(unsigned long int)parent,name,icacheflag?" (using open dir cache)":"",e.entry_timeout,(unsigned long int)e.ino,e.attr_timeout,attrstr);
1196 	fuse_reply_entry(req, &e);
1197 	if (debug_mode) {
1198 		fprintf(stderr,"lookup: positive answer timeouts (attr:%.3lf,entry:%.3lf)\n",e.attr_timeout,e.entry_timeout);
1199 	}
1200 }
1201 
1202 void mfs_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
1203 	uint64_t maxfleng;
1204 	double attr_timeout;
1205 	struct stat o_stbuf;
1206 	uint8_t attr[35];
1207 	uint8_t type;
1208 	char attrstr[256];
1209 	int status;
1210 	uint8_t icacheflag;
1211 	struct fuse_ctx ctx;
1212 	int force_mode;
1213 	(void)fi;
1214 
1215 	ctx = *(fuse_req_ctx(req));
1216 //	mfs_stats_inc(OP_GETATTR);
1217 	if (debug_mode) {
1218 		oplog_printf(&ctx,"getattr (%lu) ...",(unsigned long int)ino);
1219 		fprintf(stderr,"getattr (%lu)\n",(unsigned long int)ino);
1220 	}
1221 //	if (ino==MASTER_INODE) {
1222 //		memset(&o_stbuf, 0, sizeof(struct stat));
1223 //		mfs_attr_to_stat(ino,masterattr,&o_stbuf);
1224 //		fuse_reply_attr(req, &o_stbuf, 3600.0);
1225 //		mfs_stats_inc(OP_GETATTR);
1226 //		return;
1227 //	}
1228 	if (ino==MASTERINFO_INODE) {
1229 		memset(&o_stbuf, 0, sizeof(struct stat));
1230 		mfs_attr_to_stat(ino,masterinfoattr,&o_stbuf);
1231 		mfs_stats_inc(OP_GETATTR);
1232 		mfs_makeattrstr(attrstr,256,&o_stbuf);
1233 		oplog_printf(&ctx,"getattr (%lu) (internal node: MASTERINFO): OK (3600,%s)",(unsigned long int)ino,attrstr);
1234 		fuse_reply_attr(req, &o_stbuf, 3600.0);
1235 		return;
1236 	}
1237 	if (ino==STATS_INODE) {
1238 		memset(&o_stbuf, 0, sizeof(struct stat));
1239 		mfs_attr_to_stat(ino,statsattr,&o_stbuf);
1240 		mfs_stats_inc(OP_GETATTR);
1241 		mfs_makeattrstr(attrstr,256,&o_stbuf);
1242 		oplog_printf(&ctx,"getattr (%lu) (internal node: STATS): OK (3600,%s)",(unsigned long int)ino,attrstr);
1243 		fuse_reply_attr(req, &o_stbuf, 3600.0);
1244 		return;
1245 	}
1246 	if (ino==MOOSE_INODE) {
1247 		memset(&o_stbuf, 0, sizeof(struct stat));
1248 		mfs_attr_to_stat(ino,mooseattr,&o_stbuf);
1249 		mfs_stats_inc(OP_GETATTR);
1250 		mfs_makeattrstr(attrstr,256,&o_stbuf);
1251 		oplog_printf(&ctx,"getattr (%lu) (internal node: MOOSE): OK (3600,%s)",(unsigned long int)ino,attrstr);
1252 		fuse_reply_attr(req, &o_stbuf, 3600.0);
1253 		return;
1254 	}
1255 	if (ino==OPLOG_INODE || ino==OPHISTORY_INODE) {
1256 		memset(&o_stbuf, 0, sizeof(struct stat));
1257 		mfs_attr_to_stat(ino,oplogattr,&o_stbuf);
1258 //		if (fi && fi->fh) {
1259 //			uint64_t *posptr = (uint64_t*)(unsigned long)(fi->fh);
1260 //			o_stbuf.st_size = (*posptr)+oplog_getpos();
1261 //		}
1262 		mfs_stats_inc(OP_GETATTR);
1263 		mfs_makeattrstr(attrstr,256,&o_stbuf);
1264 		oplog_printf(&ctx,"getattr (%lu) (internal node: %s): OK (3600,%s)",(unsigned long int)ino,(ino==OPLOG_INODE)?"OPLOG":"OPHISTORY",attrstr);
1265 		fuse_reply_attr(req, &o_stbuf, 3600.0);
1266 		return;
1267 	}
1268 /*
1269 	if (ino==ATTRCACHE_INODE) {
1270 		memset(&o_stbuf, 0, sizeof(struct stat));
1271 		mfs_attr_to_stat(ino,attrcacheattr,&o_stbuf);
1272 		mfs_stats_inc(OP_GETATTR);
1273 		oplog_printf(&ctx,"getattr (%lu) (internal node ATTRCACHE)",(unsigned long int)ino);
1274 		fuse_reply_attr(req, &o_stbuf, 3600.0);
1275 		return;
1276 	}
1277 */
1278 //	if (write_data_flush_inode(ino)) {
1279 //		mfs_stats_inc(OP_GETATTR);
1280 //		status = fs_getattr(ino,ctx.uid,ctx.gid,attr);
1281 //		status = mfs_errorconv(status);
1282 /*
1283 	if (newdircache) {
1284 		if (dir_cache_getattr(ino,attr)) {
1285 			mfs_stats_inc(OP_DIRCACHE_GETATTR);
1286 			status = 0;
1287 			oplog_printf(&ctx,"getattr (%lu) (data found in cache)",(unsigned long int)ino);
1288 		} else {
1289 			mfs_stats_inc(OP_GETATTR);
1290 			status = fs_getattr(ino,ctx.uid,ctx.gid,attr);
1291 			status = mfs_errorconv(status);
1292 			if (status!=0) {
1293 				oplog_printf(&ctx,"getattr (%lu) (data not found in cache: %s)",(unsigned long int)ino,strerr(status));
1294 			} else {
1295 				oplog_printf(&ctx,"getattr (%lu) (data not found in cache)",(unsigned long int)ino);
1296 			}
1297 		}
1298 	} else
1299 */
1300 	force_mode = 0;
1301 	if (usedircache && dcache_getattr(&ctx,ino,attr)) {
1302 		if (debug_mode) {
1303 			fprintf(stderr,"getattr: sending data from dircache\n");
1304 		}
1305 		mfs_stats_inc(OP_DIRCACHE_GETATTR);
1306 		status = 0;
1307 		icacheflag = 1;
1308 	} else {
1309 		mfs_stats_inc(OP_GETATTR);
1310 		status = fs_getattr(ino,(fi!=NULL)?1:0,ctx.uid,ctx.gid,attr);
1311 		if (status==ERROR_ENOENT) {
1312 			if (ctx.pid==getpid()) {
1313 				force_mode = 1;
1314 			}
1315 			status = sstats_get(ino,attr,force_mode);
1316 			if (status==STATUS_OK && force_mode==0) {
1317 				force_mode = 2;
1318 			}
1319 		}
1320 		status = mfs_errorconv(status);
1321 		icacheflag = 0;
1322 	}
1323 	if (status!=0) {
1324 		oplog_printf(&ctx,"getattr (%lu): %s",(unsigned long int)ino,strerr(status));
1325 		fuse_reply_err(req, status);
1326 		return;
1327 	}
1328 	type = mfs_attr_get_type(attr);
1329 	if (type==TYPE_FILE) {
1330 		maxfleng = write_data_getmaxfleng(ino);
1331 	} else {
1332 		maxfleng = 0;
1333 	}
1334 	if (type==TYPE_DIRECTORY && force_mode==0) {
1335 		sstats_set(ino,attr,1);
1336 	}
1337 	memset(&o_stbuf, 0, sizeof(struct stat));
1338 	mfs_attr_to_stat(ino,attr,&o_stbuf);
1339 	if (maxfleng>(uint64_t)(o_stbuf.st_size)) {
1340 		o_stbuf.st_size=maxfleng;
1341 	}
1342 	if (type==TYPE_FILE) {
1343 		read_inode_set_length(ino,o_stbuf.st_size,0);
1344 	}
1345 	attr_timeout = ((mfs_attr_get_mattr(attr)&MATTR_NOACACHE) || force_mode)?0.0:attr_cache_timeout;
1346 	mfs_makeattrstr(attrstr,256,&o_stbuf);
1347 	oplog_printf(&ctx,"getattr (%lu)%s: OK (%.1lf,%s)",(unsigned long int)ino,icacheflag?" (using open dir cache)":(force_mode==1)?" (internal getattr)":(force_mode==2)?" (sustained nodes)":"",attr_timeout,attrstr);
1348 	fuse_reply_attr(req, &o_stbuf, attr_timeout);
1349 }
1350 
1351 void mfs_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *stbuf, int to_set, struct fuse_file_info *fi) {
1352 	struct stat o_stbuf;
1353 	uint64_t maxfleng;
1354 	uint8_t attr[35];
1355 	char modestr[11];
1356 	char attrstr[256];
1357 	double attr_timeout;
1358 	int status;
1359 	struct fuse_ctx ctx;
1360 	groups *gids;
1361 	uint8_t setmask = 0;
1362 
1363 	ctx = *(fuse_req_ctx(req));
1364 	mfs_makemodestr(modestr,stbuf->st_mode);
1365 	mfs_stats_inc(OP_SETATTR);
1366 	if (debug_mode) {
1367 		oplog_printf(&ctx,"setattr (%lu,0x%X,[%s:0%04o,%ld,%ld,%lu,%lu,%llu]) ...",(unsigned long int)ino,to_set,modestr+1,(unsigned int)(stbuf->st_mode & 07777),(long int)stbuf->st_uid,(long int)stbuf->st_gid,(unsigned long int)(stbuf->st_atime),(unsigned long int)(stbuf->st_mtime),(unsigned long long int)(stbuf->st_size));
1368 		fprintf(stderr,"setattr (%lu,0x%X,[%s:0%04o,%ld,%ld,%lu,%lu,%llu])\n",(unsigned long int)ino,to_set,modestr+1,(unsigned int)(stbuf->st_mode & 07777),(long int)stbuf->st_uid,(long int)stbuf->st_gid,(unsigned long int)(stbuf->st_atime),(unsigned long int)(stbuf->st_mtime),(unsigned long long int)(stbuf->st_size));
1369 	}
1370 	if (ino==MASTERINFO_INODE) {
1371 		oplog_printf(&ctx,"setattr (%lu,0x%X,[%s:0%04o,%ld,%ld,%lu,%lu,%llu]): %s",(unsigned long int)ino,to_set,modestr+1,(unsigned int)(stbuf->st_mode & 07777),(long int)stbuf->st_uid,(long int)stbuf->st_gid,(unsigned long int)(stbuf->st_atime),(unsigned long int)(stbuf->st_mtime),(unsigned long long int)(stbuf->st_size),strerr(EPERM));
1372 		fuse_reply_err(req, EPERM);
1373 		return;
1374 	}
1375 	if (ino==STATS_INODE) {
1376 		memset(&o_stbuf, 0, sizeof(struct stat));
1377 		mfs_attr_to_stat(ino,statsattr,&o_stbuf);
1378 		mfs_makeattrstr(attrstr,256,&o_stbuf);
1379 		oplog_printf(&ctx,"setattr (%lu,0x%X,[%s:0%04o,%ld,%ld,%lu,%lu,%llu]) (internal node: STATS): OK (3600,%s)",(unsigned long int)ino,to_set,modestr+1,(unsigned int)(stbuf->st_mode & 07777),(long int)stbuf->st_uid,(long int)stbuf->st_gid,(unsigned long int)(stbuf->st_atime),(unsigned long int)(stbuf->st_mtime),(unsigned long long int)(stbuf->st_size),attrstr);
1380 		fuse_reply_attr(req, &o_stbuf, 3600.0);
1381 		return;
1382 	}
1383 	if (ino==MOOSE_INODE) {
1384 		memset(&o_stbuf, 0, sizeof(struct stat));
1385 		mfs_attr_to_stat(ino,mooseattr,&o_stbuf);
1386 		mfs_makeattrstr(attrstr,256,&o_stbuf);
1387 		oplog_printf(&ctx,"setattr (%lu,0x%X,[%s:0%04o,%ld,%ld,%lu,%lu,%llu]) (internal node: MOOSE): OK (3600,%s)",(unsigned long int)ino,to_set,modestr+1,(unsigned int)(stbuf->st_mode & 07777),(long int)stbuf->st_uid,(long int)stbuf->st_gid,(unsigned long int)(stbuf->st_atime),(unsigned long int)(stbuf->st_mtime),(unsigned long long int)(stbuf->st_size),attrstr);
1388 		fuse_reply_attr(req, &o_stbuf, 3600.0);
1389 		return;
1390 	}
1391 	if (ino==OPLOG_INODE || ino==OPHISTORY_INODE) {
1392 		memset(&o_stbuf, 0, sizeof(struct stat));
1393 		mfs_attr_to_stat(ino,oplogattr,&o_stbuf);
1394 //		if (fi && fi->fh) {
1395 //			uint64_t *posptr = (uint64_t*)(unsigned long)(fi->fh);
1396 //			o_stbuf.st_size = (*posptr)+oplog_getpos();
1397 //		}
1398 		mfs_makeattrstr(attrstr,256,&o_stbuf);
1399 		oplog_printf(&ctx,"setattr (%lu,0x%X,[%s:0%04o,%ld,%ld,%lu,%lu,%llu]) (internal node: %s): OK (3600,%s)",(unsigned long int)ino,to_set,modestr+1,(unsigned int)(stbuf->st_mode & 07777),(long int)stbuf->st_uid,(long int)stbuf->st_gid,(unsigned long int)(stbuf->st_atime),(unsigned long int)(stbuf->st_mtime),(unsigned long long int)(stbuf->st_size),(ino==OPLOG_INODE)?"OPLOG":"OPHISTORY",attrstr);
1400 		fuse_reply_attr(req, &o_stbuf, 3600.0);
1401 		return;
1402 	}
1403 /*
1404 	if (ino==ATTRCACHE_INODE) {
1405 		memset(&o_stbuf, 0, sizeof(struct stat));
1406 		mfs_attr_to_stat(ino,attrcacheattr,&o_stbuf);
1407 		fuse_reply_attr(req, &o_stbuf, 3600.0);
1408 		return;
1409 	}
1410 */
1411 	status = EINVAL;
1412 	if ((to_set & (FUSE_SET_ATTR_MODE|FUSE_SET_ATTR_UID|FUSE_SET_ATTR_GID|FUSE_SET_ATTR_ATIME|FUSE_SET_ATTR_MTIME|FUSE_SET_ATTR_SIZE)) == 0) {	// change other flags or change nothing
1413 //		status = fs_getattr(ino,ctx.uid,ctx.gid,attr);
1414 		// ext3 compatibility - change ctime during this operation (usually chown(-1,-1))
1415 		if (full_permissions) {
1416 			gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
1417 			status = fs_setattr(ino,(fi!=NULL)?1:0,ctx.uid,gids->gidcnt,gids->gidtab,0,0,0,0,0,0,0,attr);
1418 			groups_rel(gids);
1419 		} else {
1420 			uint32_t gidtmp = ctx.gid;
1421 			status = fs_setattr(ino,(fi!=NULL)?1:0,ctx.uid,1,&gidtmp,0,0,0,0,0,0,0,attr);
1422 		}
1423 		if (status==ERROR_ENOENT) {
1424 			status = sstats_get(ino,attr,0);
1425 			if (status==STATUS_OK) {
1426 				mfs_attr_modify(to_set,attr,stbuf);
1427 			}
1428 		}
1429 		if (status==STATUS_OK) {
1430 			sstats_set(ino,attr,0);
1431 		}
1432 		status = mfs_errorconv(status);
1433 		if (status!=0) {
1434 			oplog_printf(&ctx,"setattr (%lu,0x%X,[%s:0%04o,%ld,%ld,%lu,%lu,%llu]): %s",(unsigned long int)ino,to_set,modestr+1,(unsigned int)(stbuf->st_mode & 07777),(long int)stbuf->st_uid,(long int)stbuf->st_gid,(unsigned long int)(stbuf->st_atime),(unsigned long int)(stbuf->st_mtime),(unsigned long long int)(stbuf->st_size),strerr(status));
1435 			fuse_reply_err(req, status);
1436 			return;
1437 		}
1438 	}
1439 	if (to_set & (FUSE_SET_ATTR_MODE|FUSE_SET_ATTR_UID|FUSE_SET_ATTR_GID|FUSE_SET_ATTR_ATIME|FUSE_SET_ATTR_MTIME)) {
1440 		setmask = 0;
1441 		if (to_set & FUSE_SET_ATTR_MODE) {
1442 			setmask |= SET_MODE_FLAG;
1443 			if (xattr_cache_on) {
1444 				xattr_cache_del(ino,6+1+5+1+3+1+6,(const uint8_t*)"system.posix_acl_access");
1445 			}
1446 		}
1447 		if (to_set & FUSE_SET_ATTR_UID) {
1448 			setmask |= SET_UID_FLAG;
1449 		}
1450 		if (to_set & FUSE_SET_ATTR_GID) {
1451 			setmask |= SET_GID_FLAG;
1452 		}
1453 		if (to_set & FUSE_SET_ATTR_ATIME) {
1454 			setmask |= SET_ATIME_FLAG;
1455 		}
1456 		if (to_set & FUSE_SET_ATTR_MTIME) {
1457 			setmask |= SET_MTIME_FLAG;
1458 			write_data_flush_inode(ino);	// in this case we want flush all pending writes because they could overwrite mtime
1459 		}
1460 		if (full_permissions) {
1461 			gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
1462 			status = fs_setattr(ino,(fi!=NULL)?1:0,ctx.uid,gids->gidcnt,gids->gidtab,setmask,stbuf->st_mode&07777,stbuf->st_uid,stbuf->st_gid,stbuf->st_atime,stbuf->st_mtime,sugid_clear_mode,attr);
1463 			groups_rel(gids);
1464 		} else {
1465 			uint32_t gidtmp = ctx.gid;
1466 			status = fs_setattr(ino,(fi!=NULL)?1:0,ctx.uid,1,&gidtmp,setmask,stbuf->st_mode&07777,stbuf->st_uid,stbuf->st_gid,stbuf->st_atime,stbuf->st_mtime,sugid_clear_mode,attr);
1467 		}
1468 		if (status==ERROR_ENOENT) {
1469 			status = sstats_get(ino,attr,0);
1470 			if (status==STATUS_OK) {
1471 				mfs_attr_modify(to_set,attr,stbuf);
1472 			}
1473 		}
1474 		if (status==STATUS_OK) {
1475 			sstats_set(ino,attr,0);
1476 		}
1477 		status = mfs_errorconv(status);
1478 		if (status!=0) {
1479 			oplog_printf(&ctx,"setattr (%lu,0x%X,[%s:0%04o,%ld,%ld,%lu,%lu,%llu]): %s",(unsigned long int)ino,to_set,modestr+1,(unsigned int)(stbuf->st_mode & 07777),(long int)stbuf->st_uid,(long int)stbuf->st_gid,(unsigned long int)(stbuf->st_atime),(unsigned long int)(stbuf->st_mtime),(unsigned long long int)(stbuf->st_size),strerr(status));
1480 			fuse_reply_err(req, status);
1481 			return;
1482 		}
1483 	}
1484 	if (to_set & FUSE_SET_ATTR_SIZE) {
1485 		if (stbuf->st_size<0) {
1486 			oplog_printf(&ctx,"setattr (%lu,0x%X,[%s:0%04o,%ld,%ld,%lu,%lu,%llu]): %s",(unsigned long int)ino,to_set,modestr+1,(unsigned int)(stbuf->st_mode & 07777),(long int)stbuf->st_uid,(long int)stbuf->st_gid,(unsigned long int)(stbuf->st_atime),(unsigned long int)(stbuf->st_mtime),(unsigned long long int)(stbuf->st_size),strerr(EINVAL));
1487 			fuse_reply_err(req, EINVAL);
1488 			return;
1489 		}
1490 		if (stbuf->st_size>=MAX_FILE_SIZE) {
1491 			oplog_printf(&ctx,"setattr (%lu,0x%X,[%s:0%04o,%ld,%ld,%lu,%lu,%llu]): %s",(unsigned long int)ino,to_set,modestr+1,(unsigned int)(stbuf->st_mode & 07777),(long int)stbuf->st_uid,(long int)stbuf->st_gid,(unsigned long int)(stbuf->st_atime),(unsigned long int)(stbuf->st_mtime),(unsigned long long int)(stbuf->st_size),strerr(EFBIG));
1492 			fuse_reply_err(req, EFBIG);
1493 			return;
1494 		}
1495 		write_data_flush_inode(ino);
1496 		if (full_permissions) {
1497 			uint32_t trycnt;
1498 			gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
1499 			trycnt = 0;
1500 			while (1) {
1501 #ifdef FREEBSD_FALSE_TRUNCATE_WORKAROUND
1502 				status = fs_truncate(ino,(fi!=NULL)?(TRUNCATE_FLAG_OPENED|TRUNCATE_FLAG_TIMEFIX):TRUNCATE_FLAG_TIMEFIX,ctx.uid,gids->gidcnt,gids->gidtab,stbuf->st_size,attr);
1503 #else
1504 				status = fs_truncate(ino,(fi!=NULL)?TRUNCATE_FLAG_OPENED:0,ctx.uid,gids->gidcnt,gids->gidtab,stbuf->st_size,attr);
1505 #endif
1506 				if (status==STATUS_OK || status==ERROR_EROFS || status==ERROR_EACCES || status==ERROR_EPERM || status==ERROR_ENOENT || status==ERROR_QUOTA || status==ERROR_NOSPACE || status==ERROR_CHUNKLOST) {
1507 					break;
1508 				} else if (status!=ERROR_LOCKED) {
1509 					trycnt++;
1510 					if (trycnt>=30) {
1511 						break;
1512 					}
1513 				}
1514 				sleep(1+((trycnt<30)?(trycnt/3):10));
1515 			}
1516 			groups_rel(gids);
1517 		} else {
1518 			uint32_t gidtmp = ctx.gid;
1519 			uint32_t trycnt;
1520 			trycnt = 0;
1521 			while (1) {
1522 #ifdef FREEBSD_FALSE_TRUNCATE_WORKAROUND
1523 				status = fs_truncate(ino,(fi!=NULL)?(TRUNCATE_FLAG_OPENED|TRUNCATE_FLAG_TIMEFIX):TRUNCATE_FLAG_TIMEFIX,ctx.uid,1,&gidtmp,stbuf->st_size,attr);
1524 #else
1525 				status = fs_truncate(ino,(fi!=NULL)?TRUNCATE_FLAG_OPENED:0,ctx.uid,1,&gidtmp,stbuf->st_size,attr);
1526 #endif
1527 				if (status==STATUS_OK || status==ERROR_EROFS || status==ERROR_EACCES || status==ERROR_EPERM || status==ERROR_ENOENT || status==ERROR_QUOTA || status==ERROR_NOSPACE || status==ERROR_CHUNKLOST) {
1528 					break;
1529 				} else if (status!=ERROR_LOCKED) {
1530 					trycnt++;
1531 					if (trycnt>=30) {
1532 						break;
1533 					}
1534 				}
1535 				sleep(1+((trycnt<30)?(trycnt/3):10));
1536 			}
1537 		}
1538 		status = mfs_errorconv(status);
1539 		// read_inode_ops(ino);
1540 		write_data_setmaxfleng(ino,stbuf->st_size);
1541 		read_inode_set_length(ino,stbuf->st_size,1);
1542 		if (status!=0) {
1543 			oplog_printf(&ctx,"setattr (%lu,0x%X,[%s:0%04o,%ld,%ld,%lu,%lu,%llu]): %s",(unsigned long int)ino,to_set,modestr+1,(unsigned int)(stbuf->st_mode & 07777),(long int)stbuf->st_uid,(long int)stbuf->st_gid,(unsigned long int)(stbuf->st_atime),(unsigned long int)(stbuf->st_mtime),(unsigned long long int)(stbuf->st_size),strerr(status));
1544 			fuse_reply_err(req, status);
1545 			return;
1546 		}
1547 	}
1548 	if (status!=0) {	// should never happened but better check than sorry
1549 		oplog_printf(&ctx,"setattr (%lu,0x%X,[%s:0%04o,%ld,%ld,%lu,%lu,%llu]): %s",(unsigned long int)ino,to_set,modestr+1,(unsigned int)(stbuf->st_mode & 07777),(long int)stbuf->st_uid,(long int)stbuf->st_gid,(unsigned long int)(stbuf->st_atime),(unsigned long int)(stbuf->st_mtime),(unsigned long long int)(stbuf->st_size),strerr(status));
1550 		fuse_reply_err(req, status);
1551 		return;
1552 	}
1553 	dcache_setattr(ino,attr);
1554 	if (mfs_attr_get_type(attr)==TYPE_FILE) {
1555 		maxfleng = write_data_getmaxfleng(ino);
1556 	} else {
1557 		maxfleng = 0;
1558 	}
1559 	memset(&o_stbuf, 0, sizeof(struct stat));
1560 	mfs_attr_to_stat(ino,attr,&o_stbuf);
1561 	if (maxfleng>(uint64_t)(o_stbuf.st_size)) {
1562 		o_stbuf.st_size=maxfleng;
1563 	}
1564 	attr_timeout = (mfs_attr_get_mattr(attr)&MATTR_NOACACHE)?0.0:attr_cache_timeout;
1565 	mfs_makeattrstr(attrstr,256,&o_stbuf);
1566 	oplog_printf(&ctx,"setattr (%lu,0x%X,[%s:0%04o,%ld,%ld,%lu,%lu,%llu]): OK (%.1lf,%s)",(unsigned long int)ino,to_set,modestr+1,(unsigned int)(stbuf->st_mode & 07777),(long int)stbuf->st_uid,(long int)stbuf->st_gid,(unsigned long int)(stbuf->st_atime),(unsigned long int)(stbuf->st_mtime),(unsigned long long int)(stbuf->st_size),attr_timeout,attrstr);
1567 	fuse_reply_attr(req, &o_stbuf, attr_timeout);
1568 }
1569 
1570 void mfs_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev) {
1571 	struct fuse_entry_param e;
1572 	uint32_t inode;
1573 	uint8_t attr[35];
1574 	char modestr[11];
1575 	char attrstr[256];
1576 	uint8_t mattr;
1577 	uint32_t nleng;
1578 	uint16_t cumask;
1579 	int status;
1580 	uint8_t type;
1581 	struct fuse_ctx ctx;
1582 	groups *gids;
1583 
1584 	ctx = *(fuse_req_ctx(req));
1585 	mfs_makemodestr(modestr,mode);
1586 	mfs_stats_inc(OP_MKNOD);
1587 	if (debug_mode) {
1588 #ifdef FUSE_CAP_DONT_MASK
1589 		char umaskstr[11];
1590 		mfs_makemodestr(umaskstr,ctx.umask);
1591 		oplog_printf(&ctx,"mknod (%lu,%s,%s:0%04o/%s:0%04o,0x%08lX) ...",(unsigned long int)parent,name,modestr,(unsigned int)mode,umaskstr+1,(unsigned int)(ctx.umask),(unsigned long int)rdev);
1592 		fprintf(stderr,"mknod (%lu,%s,%s:0%04o/%s:0%04o,0x%08lX)\n",(unsigned long int)parent,name,modestr,(unsigned int)mode,umaskstr+1,(unsigned int)(ctx.umask),(unsigned long int)rdev);
1593 #else
1594 		oplog_printf(&ctx,"mknod (%lu,%s,%s:0%04o,0x%08lX) ...",(unsigned long int)parent,name,modestr,(unsigned int)mode,(unsigned long int)rdev);
1595 		fprintf(stderr,"mknod (%lu,%s,%s:0%04o,0x%08lX)\n",(unsigned long int)parent,name,modestr,(unsigned int)mode,(unsigned long int)rdev);
1596 #endif
1597 	}
1598 	nleng = strlen(name);
1599 	if (nleng>MFS_NAME_MAX) {
1600 		oplog_printf(&ctx,"mknod (%lu,%s,%s:0%04o,0x%08lX): %s",(unsigned long int)parent,name,modestr,(unsigned int)mode,(unsigned long int)rdev,strerr(ENAMETOOLONG));
1601 		fuse_reply_err(req, ENAMETOOLONG);
1602 		return;
1603 	}
1604 	if (S_ISFIFO(mode)) {
1605 		type = TYPE_FIFO;
1606 	} else if (S_ISCHR(mode)) {
1607 		type = TYPE_CHARDEV;
1608 	} else if (S_ISBLK(mode)) {
1609 		type = TYPE_BLOCKDEV;
1610 	} else if (S_ISSOCK(mode)) {
1611 		type = TYPE_SOCKET;
1612 	} else if (S_ISREG(mode) || (mode&0170000)==0) {
1613 		type = TYPE_FILE;
1614 	} else {
1615 		oplog_printf(&ctx,"mknod (%lu,%s,%s:0%04o,0x%08lX): %s",(unsigned long int)parent,name,modestr,(unsigned int)mode,(unsigned long int)rdev,strerr(EPERM));
1616 		fuse_reply_err(req, EPERM);
1617 		return;
1618 	}
1619 
1620 	if (parent==FUSE_ROOT_ID) {
1621 		if (IS_SPECIAL_NAME(name)) {
1622 			oplog_printf(&ctx,"mknod (%lu,%s,%s:0%04o,0x%08lX): %s",(unsigned long int)parent,name,modestr,(unsigned int)mode,(unsigned long int)rdev,strerr(EACCES));
1623 			fuse_reply_err(req, EACCES);
1624 			return;
1625 		}
1626 	}
1627 
1628 #ifdef FUSE_CAP_DONT_MASK
1629 	cumask = ctx.umask;
1630 #else
1631 	cumask = 0;
1632 #endif
1633 	if (full_permissions) {
1634 		gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
1635 		status = fs_mknod(parent,nleng,(const uint8_t*)name,type,mode&07777,cumask,ctx.uid,gids->gidcnt,gids->gidtab,rdev,&inode,attr);
1636 		groups_rel(gids);
1637 	} else {
1638 		uint32_t gidtmp = ctx.gid;
1639 		status = fs_mknod(parent,nleng,(const uint8_t*)name,type,mode&07777,cumask,ctx.uid,1,&gidtmp,rdev,&inode,attr);
1640 	}
1641 	status = mfs_errorconv(status);
1642 	if (status!=0) {
1643 		oplog_printf(&ctx,"mknod (%lu,%s,%s:0%04o,0x%08lX): %s",(unsigned long int)parent,name,modestr,(unsigned int)mode,(unsigned long int)rdev,strerr(status));
1644 		fuse_reply_err(req, status);
1645 	} else {
1646 		negentry_cache_remove(parent,nleng,(const uint8_t*)name);
1647 //		if (newdircache) {
1648 //			dir_cache_link(parent,nleng,(const uint8_t*)name,inode,attr);
1649 //		}
1650 		dcache_invalidate_attr(parent);
1651 		memset(&e, 0, sizeof(e));
1652 		e.ino = inode;
1653 		e.generation = 1;
1654 		mattr = mfs_attr_get_mattr(attr);
1655 		e.attr_timeout = (mattr&MATTR_NOACACHE)?0.0:attr_cache_timeout;
1656 		e.entry_timeout = (mattr&MATTR_NOECACHE)?0.0:entry_cache_timeout;
1657 		mfs_attr_to_stat(inode,attr,&e.attr);
1658 		mfs_makeattrstr(attrstr,256,&e.attr);
1659 		oplog_printf(&ctx,"mknod (%lu,%s,%s:0%04o,0x%08lX): OK (%.1lf,%lu,%.1lf,%s)",(unsigned long int)parent,name,modestr,(unsigned int)mode,(unsigned long int)rdev,e.entry_timeout,(unsigned long int)e.ino,e.attr_timeout,attrstr);
1660 		fuse_reply_entry(req, &e);
1661 	}
1662 }
1663 
1664 void mfs_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) {
1665 	uint32_t nleng;
1666 	int status;
1667 	struct fuse_ctx ctx;
1668 	groups *gids;
1669 
1670 	ctx = *(fuse_req_ctx(req));
1671 	mfs_stats_inc(OP_UNLINK);
1672 	if (debug_mode) {
1673 		oplog_printf(&ctx,"unlink (%lu,%s) ...",(unsigned long int)parent,name);
1674 		fprintf(stderr,"unlink (%lu,%s)\n",(unsigned long int)parent,name);
1675 	}
1676 	if (parent==FUSE_ROOT_ID) {
1677 		if (IS_SPECIAL_NAME(name)) {
1678 			oplog_printf(&ctx,"unlink (%lu,%s): %s",(unsigned long int)parent,name,strerr(EACCES));
1679 			fuse_reply_err(req, EACCES);
1680 			return;
1681 		}
1682 	}
1683 
1684 	nleng = strlen(name);
1685 	if (nleng>MFS_NAME_MAX) {
1686 		oplog_printf(&ctx,"unlink (%lu,%s): %s",(unsigned long int)parent,name,strerr(ENAMETOOLONG));
1687 		fuse_reply_err(req, ENAMETOOLONG);
1688 		return;
1689 	}
1690 
1691 	if (full_permissions) {
1692 		gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
1693 		status = fs_unlink(parent,nleng,(const uint8_t*)name,ctx.uid,gids->gidcnt,gids->gidtab);
1694 		groups_rel(gids);
1695 	} else {
1696 		uint32_t gidtmp = ctx.gid;
1697 		status = fs_unlink(parent,nleng,(const uint8_t*)name,ctx.uid,1,&gidtmp);
1698 	}
1699 	status = mfs_errorconv(status);
1700 	if (status!=0) {
1701 		oplog_printf(&ctx,"unlink (%lu,%s): %s",(unsigned long int)parent,name,strerr(status));
1702 		fuse_reply_err(req, status);
1703 	} else {
1704 		negentry_cache_insert(parent,nleng,(const uint8_t*)name);
1705 //		if (newdircache) {
1706 //			dir_cache_unlink(parent,nleng,(const uint8_t*)name);
1707 //		}
1708 		dcache_invalidate_attr(parent);
1709 		dcache_invalidate_name(&ctx,parent,nleng,(const uint8_t*)name);
1710 		oplog_printf(&ctx,"unlink (%lu,%s): OK",(unsigned long int)parent,name);
1711 		fuse_reply_err(req, 0);
1712 	}
1713 }
1714 
1715 void mfs_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode) {
1716 	struct fuse_entry_param e;
1717 	uint32_t inode;
1718 	uint8_t attr[35];
1719 	char modestr[11];
1720 	char attrstr[256];
1721 	uint8_t mattr;
1722 	uint32_t nleng;
1723 	uint16_t cumask;
1724 	int status;
1725 	struct fuse_ctx ctx;
1726 	groups *gids;
1727 
1728 	ctx = *(fuse_req_ctx(req));
1729 	mfs_makemodestr(modestr,mode);
1730 	mfs_stats_inc(OP_MKDIR);
1731 	if (debug_mode) {
1732 #ifdef FUSE_CAP_DONT_MASK
1733 		char umaskstr[11];
1734 		mfs_makemodestr(umaskstr,ctx.umask);
1735 		oplog_printf(&ctx,"mkdir (%lu,%s,d%s:0%04o/%s:0%04o) ...",(unsigned long int)parent,name,modestr+1,(unsigned int)mode,umaskstr+1,(unsigned int)(ctx.umask));
1736 		fprintf(stderr,"mkdir (%lu,%s,d%s:0%04o/%s:0%04o)\n",(unsigned long int)parent,name,modestr+1,(unsigned int)mode,umaskstr+1,(unsigned int)(ctx.umask));
1737 #else
1738 		oplog_printf(&ctx,"mkdir (%lu,%s,d%s:0%04o) ...",(unsigned long int)parent,name,modestr+1,(unsigned int)mode);
1739 		fprintf(stderr,"mkdir (%lu,%s,d%s:0%04o)\n",(unsigned long int)parent,name,modestr+1,(unsigned int)mode);
1740 #endif
1741 	}
1742 	if (parent==FUSE_ROOT_ID) {
1743 		if (IS_SPECIAL_NAME(name)) {
1744 			oplog_printf(&ctx,"mkdir (%lu,%s,d%s:0%04o): %s",(unsigned long int)parent,name,modestr+1,(unsigned int)mode,strerr(EACCES));
1745 			fuse_reply_err(req, EACCES);
1746 			return;
1747 		}
1748 	}
1749 	nleng = strlen(name);
1750 	if (nleng>MFS_NAME_MAX) {
1751 		oplog_printf(&ctx,"mkdir (%lu,%s,d%s:0%04o): %s",(unsigned long int)parent,name,modestr+1,(unsigned int)mode,strerr(ENAMETOOLONG));
1752 		fuse_reply_err(req, ENAMETOOLONG);
1753 		return;
1754 	}
1755 
1756 #ifdef FUSE_CAP_DONT_MASK
1757 	cumask = ctx.umask;
1758 #else
1759 	cumask = 0;
1760 #endif
1761 	if (full_permissions) {
1762 		gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
1763 		status = fs_mkdir(parent,nleng,(const uint8_t*)name,mode,cumask,ctx.uid,gids->gidcnt,gids->gidtab,mkdir_copy_sgid,&inode,attr);
1764 		groups_rel(gids);
1765 	} else {
1766 		uint32_t gidtmp = ctx.gid;
1767 		status = fs_mkdir(parent,nleng,(const uint8_t*)name,mode,cumask,ctx.uid,1,&gidtmp,mkdir_copy_sgid,&inode,attr);
1768 	}
1769 	status = mfs_errorconv(status);
1770 	if (status!=0) {
1771 		oplog_printf(&ctx,"mkdir (%lu,%s,d%s:0%04o): %s",(unsigned long int)parent,name,modestr+1,(unsigned int)mode,strerr(status));
1772 		fuse_reply_err(req, status);
1773 	} else {
1774 		sstats_set(inode,attr,1);
1775 		negentry_cache_remove(parent,nleng,(const uint8_t*)name);
1776 //		if (newdircache) {
1777 //			dir_cache_link(parent,nleng,(const uint8_t*)name,inode,attr);
1778 //		}
1779 		dcache_invalidate_attr(parent);
1780 		memset(&e, 0, sizeof(e));
1781 		e.ino = inode;
1782 		e.generation = 1;
1783 		mattr = mfs_attr_get_mattr(attr);
1784 		e.attr_timeout = (mattr&MATTR_NOACACHE)?0.0:attr_cache_timeout;
1785 		e.entry_timeout = (mattr&MATTR_NOECACHE)?0.0:direntry_cache_timeout;
1786 		mfs_attr_to_stat(inode,attr,&e.attr);
1787 		mfs_makeattrstr(attrstr,256,&e.attr);
1788 		oplog_printf(&ctx,"mkdir (%lu,%s,d%s:0%04o): OK (%.1lf,%lu,%.1lf,%s)",(unsigned long int)parent,name,modestr+1,(unsigned int)mode,e.entry_timeout,(unsigned long int)e.ino,e.attr_timeout,attrstr);
1789 		fuse_reply_entry(req, &e);
1790 	}
1791 }
1792 
1793 void mfs_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) {
1794 	uint32_t nleng;
1795 	int status;
1796 	struct fuse_ctx ctx;
1797 	groups *gids;
1798 
1799 	ctx = *(fuse_req_ctx(req));
1800 	mfs_stats_inc(OP_RMDIR);
1801 	if (debug_mode) {
1802 		oplog_printf(&ctx,"rmdir (%lu,%s) ...",(unsigned long int)parent,name);
1803 		fprintf(stderr,"rmdir (%lu,%s)\n",(unsigned long int)parent,name);
1804 	}
1805 	if (parent==FUSE_ROOT_ID) {
1806 		if (IS_SPECIAL_NAME(name)) {
1807 			oplog_printf(&ctx,"rmdir (%lu,%s): %s",(unsigned long int)parent,name,strerr(EACCES));
1808 			fuse_reply_err(req, EACCES);
1809 			return;
1810 		}
1811 	}
1812 	nleng = strlen(name);
1813 	if (nleng>MFS_NAME_MAX) {
1814 		oplog_printf(&ctx,"rmdir (%lu,%s): %s",(unsigned long int)parent,name,strerr(ENAMETOOLONG));
1815 		fuse_reply_err(req, ENAMETOOLONG);
1816 		return;
1817 	}
1818 
1819 	if (full_permissions) {
1820 		gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
1821 		status = fs_rmdir(parent,nleng,(const uint8_t*)name,ctx.uid,gids->gidcnt,gids->gidtab);
1822 		groups_rel(gids);
1823 	} else {
1824 		uint32_t gidtmp = ctx.gid;
1825 		status = fs_rmdir(parent,nleng,(const uint8_t*)name,ctx.uid,1,&gidtmp);
1826 	}
1827 	status = mfs_errorconv(status);
1828 	if (status!=0) {
1829 		oplog_printf(&ctx,"rmdir (%lu,%s): %s",(unsigned long int)parent,name,strerr(status));
1830 		fuse_reply_err(req, status);
1831 	} else {
1832 		negentry_cache_insert(parent,nleng,(const uint8_t*)name);
1833 //		if (newdircache) {
1834 //			dir_cache_unlink(parent,nleng,(const uint8_t*)name);
1835 //		}
1836 		dcache_invalidate_attr(parent);
1837 		dcache_invalidate_name(&ctx,parent,nleng,(const uint8_t*)name);
1838 		oplog_printf(&ctx,"rmdir (%lu,%s): OK",(unsigned long int)parent,name);
1839 		fuse_reply_err(req, 0);
1840 	}
1841 }
1842 
1843 void mfs_symlink(fuse_req_t req, const char *path, fuse_ino_t parent, const char *name) {
1844 	struct fuse_entry_param e;
1845 	uint32_t inode;
1846 	uint8_t attr[35];
1847 	char attrstr[256];
1848 	uint8_t mattr;
1849 	uint32_t nleng;
1850 	int status;
1851 	struct fuse_ctx ctx;
1852 	groups *gids;
1853 
1854 	ctx = *(fuse_req_ctx(req));
1855 	mfs_stats_inc(OP_SYMLINK);
1856 	if (debug_mode) {
1857 		oplog_printf(&ctx,"symlink (%s,%lu,%s) ...",path,(unsigned long int)parent,name);
1858 		fprintf(stderr,"symlink (%s,%lu,%s)\n",path,(unsigned long int)parent,name);
1859 	}
1860 	if (parent==FUSE_ROOT_ID) {
1861 		if (IS_SPECIAL_NAME(name)) {
1862 			oplog_printf(&ctx,"symlink (%s,%lu,%s): %s",path,(unsigned long int)parent,name,strerr(EACCES));
1863 			fuse_reply_err(req, EACCES);
1864 			return;
1865 		}
1866 	}
1867 	nleng = strlen(name);
1868 	if (nleng>MFS_NAME_MAX || (strlen(path)+1)>MFS_SYMLINK_MAX) {
1869 		oplog_printf(&ctx,"symlink (%s,%lu,%s): %s",path,(unsigned long int)parent,name,strerr(ENAMETOOLONG));
1870 		fuse_reply_err(req, ENAMETOOLONG);
1871 		return;
1872 	}
1873 
1874 	if (full_permissions) {
1875 		gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
1876 		status = fs_symlink(parent,nleng,(const uint8_t*)name,(const uint8_t*)path,ctx.uid,gids->gidcnt,gids->gidtab,&inode,attr);
1877 		groups_rel(gids);
1878 	} else {
1879 		uint32_t gidtmp = ctx.gid;
1880 		status = fs_symlink(parent,nleng,(const uint8_t*)name,(const uint8_t*)path,ctx.uid,1,&gidtmp,&inode,attr);
1881 	}
1882 	status = mfs_errorconv(status);
1883 	if (status!=0) {
1884 		oplog_printf(&ctx,"symlink (%s,%lu,%s): %s",path,(unsigned long int)parent,name,strerr(status));
1885 		fuse_reply_err(req, status);
1886 	} else {
1887 		negentry_cache_remove(parent,nleng,(const uint8_t*)name);
1888 //		if (newdircache) {
1889 //			dir_cache_link(parent,nleng,(const uint8_t*)name,inode,attr);
1890 //		}
1891 		dcache_invalidate_attr(parent);
1892 		memset(&e, 0, sizeof(e));
1893 		e.ino = inode;
1894 		e.generation = 1;
1895 		mattr = mfs_attr_get_mattr(attr);
1896 		e.attr_timeout = (mattr&MATTR_NOACACHE)?0.0:attr_cache_timeout;
1897 		e.entry_timeout = (mattr&MATTR_NOECACHE)?0.0:entry_cache_timeout;
1898 		mfs_attr_to_stat(inode,attr,&e.attr);
1899 		mfs_makeattrstr(attrstr,256,&e.attr);
1900 		oplog_printf(&ctx,"symlink (%s,%lu,%s): OK (%.1lf,%lu,%.1lf,%s)",path,(unsigned long int)parent,name,e.entry_timeout,(unsigned long int)e.ino,e.attr_timeout,attrstr);
1901 		fuse_reply_entry(req, &e);
1902 	}
1903 }
1904 
1905 void mfs_readlink(fuse_req_t req, fuse_ino_t ino) {
1906 	int status;
1907 	const uint8_t *path;
1908 	struct fuse_ctx ctx;
1909 
1910 	ctx = *(fuse_req_ctx(req));
1911 	if (debug_mode) {
1912 		oplog_printf(&ctx,"readlink (%lu) ...",(unsigned long int)ino);
1913 		fprintf(stderr,"readlink (%lu)\n",(unsigned long int)ino);
1914 	}
1915 	if (symlink_cache_search(ino,&path)) {
1916 		mfs_stats_inc(OP_READLINK_CACHED);
1917 		oplog_printf(&ctx,"readlink (%lu) (using cache): OK (%s)",(unsigned long int)ino,(char*)path);
1918 		fuse_reply_readlink(req, (char*)path);
1919 		return;
1920 	}
1921 	mfs_stats_inc(OP_READLINK_MASTER);
1922 	status = fs_readlink(ino,&path);
1923 	status = mfs_errorconv(status);
1924 	if (status!=0) {
1925 		oplog_printf(&ctx,"readlink (%lu): %s",(unsigned long int)ino,strerr(status));
1926 		fuse_reply_err(req, status);
1927 	} else {
1928 		dcache_invalidate_attr(ino);
1929 		symlink_cache_insert(ino,path);
1930 		oplog_printf(&ctx,"readlink (%lu): OK (%s)",(unsigned long int)ino,(char*)path);
1931 		fuse_reply_readlink(req, (char*)path);
1932 	}
1933 }
1934 
1935 void mfs_rename(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname) {
1936 	uint32_t nleng,newnleng;
1937 	int status;
1938 	uint32_t inode;
1939 	uint8_t attr[35];
1940 	struct fuse_ctx ctx;
1941 	groups *gids;
1942 
1943 	ctx = *(fuse_req_ctx(req));
1944 	mfs_stats_inc(OP_RENAME);
1945 	if (debug_mode) {
1946 		oplog_printf(&ctx,"rename (%lu,%s,%lu,%s) ...",(unsigned long int)parent,name,(unsigned long int)newparent,newname);
1947 		fprintf(stderr,"rename (%lu,%s,%lu,%s)\n",(unsigned long int)parent,name,(unsigned long int)newparent,newname);
1948 	}
1949 	if (parent==FUSE_ROOT_ID) {
1950 		if (IS_SPECIAL_NAME(name)) {
1951 			oplog_printf(&ctx,"rename (%lu,%s,%lu,%s): %s",(unsigned long int)parent,name,(unsigned long int)newparent,newname,strerr(EACCES));
1952 			fuse_reply_err(req, EACCES);
1953 			return;
1954 		}
1955 	}
1956 	if (newparent==FUSE_ROOT_ID) {
1957 		if (IS_SPECIAL_NAME(newname)) {
1958 			oplog_printf(&ctx,"rename (%lu,%s,%lu,%s): %s",(unsigned long int)parent,name,(unsigned long int)newparent,newname,strerr(EACCES));
1959 			fuse_reply_err(req, EACCES);
1960 			return;
1961 		}
1962 	}
1963 	nleng = strlen(name);
1964 	if (nleng>MFS_NAME_MAX) {
1965 		oplog_printf(&ctx,"rename (%lu,%s,%lu,%s): %s",(unsigned long int)parent,name,(unsigned long int)newparent,newname,strerr(ENAMETOOLONG));
1966 		fuse_reply_err(req, ENAMETOOLONG);
1967 		return;
1968 	}
1969 	newnleng = strlen(newname);
1970 	if (newnleng>MFS_NAME_MAX) {
1971 		oplog_printf(&ctx,"rename (%lu,%s,%lu,%s): %s",(unsigned long int)parent,name,(unsigned long int)newparent,newname,strerr(ENAMETOOLONG));
1972 		fuse_reply_err(req, ENAMETOOLONG);
1973 		return;
1974 	}
1975 
1976 	if (full_permissions) {
1977 		gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
1978 		status = fs_rename(parent,nleng,(const uint8_t*)name,newparent,newnleng,(const uint8_t*)newname,ctx.uid,gids->gidcnt,gids->gidtab,&inode,attr);
1979 		groups_rel(gids);
1980 	} else {
1981 		uint32_t gidtmp = ctx.gid;
1982 		status = fs_rename(parent,nleng,(const uint8_t*)name,newparent,newnleng,(const uint8_t*)newname,ctx.uid,1,&gidtmp,&inode,attr);
1983 	}
1984 	status = mfs_errorconv(status);
1985 	if (status!=0) {
1986 		oplog_printf(&ctx,"rename (%lu,%s,%lu,%s): %s",(unsigned long int)parent,name,(unsigned long int)newparent,newname,strerr(status));
1987 		fuse_reply_err(req, status);
1988 	} else {
1989 		negentry_cache_insert(parent,nleng,(const uint8_t*)name);
1990 		negentry_cache_remove(newparent,newnleng,(const uint8_t*)newname);
1991 //		if (newdircache) {
1992 //			dir_cache_unlink(parent,nleng,(const uint8_t*)name);
1993 //			dir_cache_link(newparent,newnleng,(const uint8_t*)newname,inode,attr);
1994 //		}
1995 		dcache_invalidate_attr(parent);
1996 		if (newparent!=parent) {
1997 			dcache_invalidate_attr(newparent);
1998 		}
1999 		dcache_invalidate_name(&ctx,parent,nleng,(const uint8_t*)name);
2000 		oplog_printf(&ctx,"rename (%lu,%s,%lu,%s): OK",(unsigned long int)parent,name,(unsigned long int)newparent,newname);
2001 		fuse_reply_err(req, 0);
2002 	}
2003 }
2004 
2005 void mfs_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname) {
2006 	uint32_t newnleng;
2007 	int status;
2008 	struct fuse_entry_param e;
2009 	uint32_t inode;
2010 	uint8_t attr[35];
2011 	char attrstr[256];
2012 	uint8_t mattr;
2013 	struct fuse_ctx ctx;
2014 	groups *gids;
2015 
2016 	ctx = *(fuse_req_ctx(req));
2017 	mfs_stats_inc(OP_LINK);
2018 	if (debug_mode) {
2019 		oplog_printf(&ctx,"link (%lu,%lu,%s) ...",(unsigned long int)ino,(unsigned long int)newparent,newname);
2020 		fprintf(stderr,"link (%lu,%lu,%s)\n",(unsigned long int)ino,(unsigned long int)newparent,newname);
2021 	}
2022 	if (IS_SPECIAL_INODE(ino)) {
2023 		oplog_printf(&ctx,"link (%lu,%lu,%s): %s",(unsigned long int)ino,(unsigned long int)newparent,newname,strerr(EACCES));
2024 		fuse_reply_err(req, EACCES);
2025 		return;
2026 	}
2027 	if (newparent==FUSE_ROOT_ID) {
2028 		if (IS_SPECIAL_NAME(newname)) {
2029 			oplog_printf(&ctx,"link (%lu,%lu,%s): %s",(unsigned long int)ino,(unsigned long int)newparent,newname,strerr(EACCES));
2030 			fuse_reply_err(req, EACCES);
2031 			return;
2032 		}
2033 	}
2034 	newnleng = strlen(newname);
2035 	if (newnleng>MFS_NAME_MAX) {
2036 		oplog_printf(&ctx,"link (%lu,%lu,%s): %s",(unsigned long int)ino,(unsigned long int)newparent,newname,strerr(ENAMETOOLONG));
2037 		fuse_reply_err(req, ENAMETOOLONG);
2038 		return;
2039 	}
2040 
2041 //	write_data_flush_inode(ino);
2042 	if (full_permissions) {
2043 		gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
2044 		status = fs_link(ino,newparent,newnleng,(const uint8_t*)newname,ctx.uid,gids->gidcnt,gids->gidtab,&inode,attr);
2045 		groups_rel(gids);
2046 	} else {
2047 		uint32_t gidtmp = ctx.gid;
2048 		status = fs_link(ino,newparent,newnleng,(const uint8_t*)newname,ctx.uid,1,&gidtmp,&inode,attr);
2049 	}
2050 	status = mfs_errorconv(status);
2051 	if (status!=0) {
2052 		oplog_printf(&ctx,"link (%lu,%lu,%s): %s",(unsigned long int)ino,(unsigned long int)newparent,newname,strerr(status));
2053 		fuse_reply_err(req, status);
2054 	} else {
2055 		negentry_cache_remove(newparent,newnleng,(const uint8_t*)newname);
2056 //		if (newdircache) {
2057 //			dir_cache_link(newparent,newnleng,(const uint8_t*)newname,inode,attr);
2058 //		}
2059 		if (ino!=inode) {
2060 			dcache_invalidate_attr(ino);
2061 		}
2062 		dcache_invalidate_attr(newparent);
2063 		dcache_setattr(inode,attr);
2064 		memset(&e, 0, sizeof(e));
2065 		e.ino = inode;
2066 		e.generation = 1;
2067 		mattr = mfs_attr_get_mattr(attr);
2068 		e.attr_timeout = (mattr&MATTR_NOACACHE)?0.0:attr_cache_timeout;
2069 		e.entry_timeout = (mattr&MATTR_NOECACHE)?0.0:entry_cache_timeout;
2070 		mfs_attr_to_stat(inode,attr,&e.attr);
2071 		mfs_makeattrstr(attrstr,256,&e.attr);
2072 		oplog_printf(&ctx,"link (%lu,%lu,%s): OK (%.1lf,%lu,%.1lf,%s)",(unsigned long int)ino,(unsigned long int)newparent,newname,e.entry_timeout,(unsigned long int)e.ino,e.attr_timeout,attrstr);
2073 		fuse_reply_entry(req, &e);
2074 	}
2075 }
2076 
2077 void mfs_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
2078 	dirbuf *dirinfo;
2079 	int status;
2080 	uint8_t attr[35];
2081 	struct fuse_ctx ctx;
2082 	groups *gids;
2083 
2084 	ctx = *(fuse_req_ctx(req));
2085 	mfs_stats_inc(OP_OPENDIR);
2086 	if (debug_mode) {
2087 		oplog_printf(&ctx,"opendir (%lu) ...",(unsigned long int)ino);
2088 		fprintf(stderr,"opendir (%lu)\n",(unsigned long int)ino);
2089 	}
2090 	if (IS_SPECIAL_INODE(ino)) {
2091 		oplog_printf(&ctx,"opendir (%lu): %s",(unsigned long int)ino,strerr(ENOTDIR));
2092 		fuse_reply_err(req, ENOTDIR);
2093 	}
2094 	if (full_permissions) {
2095 		gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
2096 		status = fs_access(ino,ctx.uid,gids->gidcnt,gids->gidtab,MODE_MASK_R);	// at least test rights
2097 		groups_rel(gids);
2098 	} else { // no acl means - we are using default permissions, so do not check supplementary groups
2099 		status = fs_access(ino,ctx.uid,1,&(ctx.gid),MODE_MASK_R);	// at least test rights
2100 	}
2101 	if (status==ERROR_ENOENT && sstats_get(ino,attr,0)==STATUS_OK) {
2102 		if (full_permissions) {
2103 			gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
2104 			status = mfs_access_test(attr,MODE_MASK_R,ctx.uid,gids->gidcnt,gids->gidtab);
2105 			groups_rel(gids);
2106 		} else {
2107 			uint32_t gidtmp = ctx.gid;
2108 			status = mfs_access_test(attr,MODE_MASK_R,ctx.uid,1,&gidtmp);
2109 		}
2110 		if (status!=STATUS_OK) {
2111 			status = mfs_errorconv(status);
2112 			oplog_printf(&ctx,"opendir (%lu): %s",(unsigned long int)ino,strerr(status));
2113 			fuse_reply_err(req, status);
2114 		} else {
2115 			dirinfo = malloc(sizeof(dirbuf));
2116 			pthread_mutex_init(&(dirinfo->lock),NULL);
2117 			dirinfo->p = NULL;
2118 			dirinfo->size = 0;
2119 			dirinfo->dcache = NULL;
2120 			dirinfo->wasread = 2;
2121 			pthread_mutex_unlock(&(dirinfo->lock));	// make valgrind happy
2122 			fi->fh = (unsigned long)dirinfo;;
2123 			oplog_printf(&ctx,"sustained opendir (%lu): forced OK with empty directory",(unsigned long int)ino);
2124 			if (fuse_reply_open(req,fi) == -ENOENT) {
2125 				fi->fh = 0;
2126 				pthread_mutex_destroy(&(dirinfo->lock));
2127 				free(dirinfo);
2128 			}
2129 		}
2130 	} else if (status!=STATUS_OK) {
2131 		status = mfs_errorconv(status);
2132 		oplog_printf(&ctx,"opendir (%lu): %s",(unsigned long int)ino,strerr(status));
2133 		fuse_reply_err(req, status);
2134 	} else {
2135 		dirinfo = malloc(sizeof(dirbuf));
2136 		pthread_mutex_init(&(dirinfo->lock),NULL);
2137 		pthread_mutex_lock(&(dirinfo->lock));	// make valgrind happy
2138 		dirinfo->p = NULL;
2139 		dirinfo->size = 0;
2140 		dirinfo->dcache = NULL;
2141 		dirinfo->wasread = 0;
2142 		pthread_mutex_unlock(&(dirinfo->lock));	// make valgrind happy
2143 		fi->fh = (unsigned long)dirinfo;
2144 		oplog_printf(&ctx,"opendir (%lu): OK",(unsigned long int)ino);
2145 		if (fuse_reply_open(req,fi) == -ENOENT) {
2146 			fi->fh = 0;
2147 			pthread_mutex_destroy(&(dirinfo->lock));
2148 			free(dirinfo);
2149 		}
2150 	}
2151 }
2152 
2153 void mfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
2154 	int status;
2155         dirbuf *dirinfo = (dirbuf *)((unsigned long)(fi->fh));
2156 	char buffer[READDIR_BUFFSIZE];
2157 	char name[MFS_NAME_MAX+1];
2158 	const uint8_t *ptr,*eptr;
2159 	uint8_t end;
2160 	size_t opos,oleng;
2161 	uint8_t nleng;
2162 	uint32_t inode;
2163 	uint8_t type;
2164 	struct stat stbuf;
2165 	struct fuse_ctx ctx;
2166 	groups *gids;
2167 
2168 	ctx = *(fuse_req_ctx(req));
2169 	mfs_stats_inc(OP_READDIR);
2170 	if (debug_mode) {
2171 		oplog_printf(&ctx,"readdir (%lu,%llu,%llu) ...",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off);
2172 		fprintf(stderr,"readdir (%lu,%llu,%llu)\n",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off);
2173 	}
2174 	if (off<0) {
2175 		oplog_printf(&ctx,"readdir (%lu,%llu,%llu): %s",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,strerr(EINVAL));
2176 		fuse_reply_err(req,EINVAL);
2177 		return;
2178 	}
2179 	pthread_mutex_lock(&(dirinfo->lock));
2180 	if (dirinfo->wasread==0 || (dirinfo->wasread==1 && off==0)) {
2181 		const uint8_t *dbuff;
2182 		uint32_t dsize;
2183 		uint8_t needscopy;
2184 /*
2185 		if (newdircache) {
2186 			status = dir_cache_getdirdata(ino,&dsize,&dbuff);
2187 			if (status==1) {	// got dir from new cache
2188 				mfs_stats_inc(OP_GETDIR_CACHED);
2189 				needscopy = 0;
2190 				dirinfo->dataformat = 0;
2191 				status = 0;
2192 			} else {
2193 				status = fs_getdir_plus(ino,ctx.uid,ctx.gid,1,&dbuff,&dsize);
2194 				if (status==0) {
2195 					mfs_stats_inc(OP_GETDIR_FULL);
2196 					dir_cache_newdirdata(ino,dsize,dbuff);
2197 				}
2198 				needscopy = 1;
2199 				dirinfo->dataformat = 1;
2200 			}
2201 		} else
2202 */
2203 		if (usedircache) {
2204 			uint8_t df;
2205 			df = 1;
2206 			if (full_permissions) {
2207 				gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
2208 				status = fs_readdir(ino,ctx.uid,gids->gidcnt,gids->gidtab,1,0,&dbuff,&dsize);
2209 				if (status==ERROR_EACCES) {
2210 					df = 0;
2211 					status = fs_readdir(ino,ctx.uid,gids->gidcnt,gids->gidtab,0,0,&dbuff,&dsize);
2212 				}
2213 				groups_rel(gids);
2214 			} else {
2215 				uint32_t gidtmp = ctx.gid;
2216 				status = fs_readdir(ino,ctx.uid,1,&gidtmp,1,0,&dbuff,&dsize);
2217 				if (status==ERROR_EACCES) {
2218 					df = 0;
2219 					status = fs_readdir(ino,ctx.uid,1,&gidtmp,0,0,&dbuff,&dsize);
2220 				}
2221 			}
2222 			if (status==0) {
2223 				if (df) {
2224 					mfs_stats_inc(OP_GETDIR_FULL);
2225 				} else {
2226 					mfs_stats_inc(OP_GETDIR_SMALL);
2227 				}
2228 			}
2229 			needscopy = 1;
2230 			dirinfo->dataformat = df;
2231 		} else {
2232 			if (full_permissions) {
2233 				gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
2234 				status = fs_readdir(ino,ctx.uid,gids->gidcnt,gids->gidtab,0,0,&dbuff,&dsize);
2235 				groups_rel(gids);
2236 			} else {
2237 				uint32_t gidtmp = ctx.gid;
2238 				status = fs_readdir(ino,ctx.uid,1,&gidtmp,0,0,&dbuff,&dsize);
2239 			}
2240 			if (status==0) {
2241 				mfs_stats_inc(OP_GETDIR_SMALL);
2242 			}
2243 			needscopy = 1;
2244 			dirinfo->dataformat = 0;
2245 		}
2246 		status = mfs_errorconv(status);
2247 		if (status!=0) {
2248 			oplog_printf(&ctx,"readdir (%lu,%llu,%llu): %s",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,strerr(status));
2249 			fuse_reply_err(req, status);
2250 			pthread_mutex_unlock(&(dirinfo->lock));
2251 			return;
2252 		}
2253 		if (dirinfo->dcache) {
2254 			dcache_release(dirinfo->dcache);
2255 			dirinfo->dcache = NULL;
2256 		}
2257 		if (dirinfo->p) {
2258 			free((uint8_t*)(dirinfo->p));
2259 			dirinfo->p = NULL;
2260 		}
2261 		if (needscopy) {
2262 			dirinfo->p = malloc(dsize);
2263 			if (dirinfo->p == NULL) {
2264 				oplog_printf(&ctx,"readdir (%lu,%llu,%llu): %s",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,strerr(EINVAL));
2265 				fuse_reply_err(req,EINVAL);
2266 				pthread_mutex_unlock(&(dirinfo->lock));
2267 				return;
2268 			}
2269 			memcpy((uint8_t*)(dirinfo->p),dbuff,dsize);
2270 		} else {
2271 			dirinfo->p = dbuff;
2272 		}
2273 		dirinfo->size = dsize;
2274 		if (usedircache && dirinfo->dataformat==1) {
2275 			dirinfo->dcache = dcache_new(&ctx,ino,dirinfo->p,dirinfo->size);
2276 		}
2277 	}
2278 	dirinfo->wasread=1;
2279 
2280 	if (off>=(off_t)(dirinfo->size)) {
2281 		oplog_printf(&ctx,"readdir (%lu,%llu,%llu): OK (no data)",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off);
2282 		fuse_reply_buf(req, NULL, 0);
2283 	} else {
2284 		if (size>READDIR_BUFFSIZE) {
2285 			size=READDIR_BUFFSIZE;
2286 		}
2287 		ptr = dirinfo->p+off;
2288 		eptr = dirinfo->p+dirinfo->size;
2289 		opos = 0;
2290 		end = 0;
2291 
2292 		while (ptr<eptr && end==0) {
2293 			nleng = ptr[0];
2294 			ptr++;
2295 			memcpy(name,ptr,nleng);
2296 			name[nleng]=0;
2297 			ptr+=nleng;
2298 			off+=nleng+((dirinfo->dataformat)?40:6);
2299 			if (ptr+5<=eptr) {
2300 				inode = get32bit(&ptr);
2301 				if (dirinfo->dataformat) {
2302 					mfs_attr_to_stat(inode,ptr,&stbuf);
2303 					ptr+=35;
2304 				} else {
2305 					type = get8bit(&ptr);
2306 					mfs_type_to_stat(inode,type,&stbuf);
2307 				}
2308 				oleng = fuse_add_direntry(req, buffer + opos, size - opos, name, &stbuf, off);
2309 				if (opos+oleng>size) {
2310 					end=1;
2311 				} else {
2312 					opos+=oleng;
2313 				}
2314 			}
2315 		}
2316 
2317 		oplog_printf(&ctx,"readdir (%lu,%llu,%llu): OK (%lu)",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,(unsigned long int)opos);
2318 		fuse_reply_buf(req,buffer,opos);
2319 	}
2320 	pthread_mutex_unlock(&(dirinfo->lock));
2321 }
2322 
2323 void mfs_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
2324 	(void)ino;
2325 	dirbuf *dirinfo = (dirbuf *)((unsigned long)(fi->fh));
2326 	struct fuse_ctx ctx;
2327 
2328 	ctx = *(fuse_req_ctx(req));
2329 	mfs_stats_inc(OP_RELEASEDIR);
2330 	if (debug_mode) {
2331 		oplog_printf(&ctx,"releasedir (%lu) ...",(unsigned long int)ino);
2332 		fprintf(stderr,"releasedir (%lu)\n",(unsigned long int)ino);
2333 	}
2334 	pthread_mutex_lock(&(dirinfo->lock));
2335 	pthread_mutex_unlock(&(dirinfo->lock));
2336 	pthread_mutex_destroy(&(dirinfo->lock));
2337 	if (dirinfo->dcache) {
2338 		dcache_release(dirinfo->dcache);
2339 	}
2340 	if (dirinfo->p) {
2341 		free((uint8_t*)(dirinfo->p));
2342 	}
2343 	free(dirinfo);
2344 	fi->fh = 0;
2345 	oplog_printf(&ctx,"releasedir (%lu): OK",(unsigned long int)ino);
2346 	fuse_reply_err(req,0);
2347 }
2348 
2349 #ifdef FREEBSD_EARLY_RELEASE_BUG_WORKAROUND
2350 static void mfs_real_removefileinfo(finfo* fileinfo) {
2351 	pthread_mutex_lock(&(fileinfo->lock));
2352 	if (fileinfo->mode == IO_READONLY || fileinfo->mode == IO_READ) {
2353 		read_data_end(fileinfo->data);
2354 	} else if (fileinfo->mode == IO_WRITEONLY || fileinfo->mode == IO_WRITE) {
2355 //		write_data_flush(fileinfo->data);
2356 		write_data_end(fileinfo->data);
2357 	}
2358 	pthread_mutex_unlock(&(fileinfo->lock));
2359 	pthread_mutex_destroy(&(fileinfo->lock));
2360 	free(fileinfo);
2361 }
2362 #endif
2363 
2364 static finfo* mfs_newfileinfo(uint8_t accmode,uint32_t inode) {
2365 	finfo *fileinfo;
2366 #ifdef FREEBSD_EARLY_RELEASE_BUG_WORKAROUND
2367 	finfo **fileinfoptr;
2368 	double now;
2369 	now = monotonic_seconds();
2370 	pthread_mutex_lock(&finfo_list_lock);
2371 	fileinfoptr = &finfo_head;
2372 	while ((fileinfo=*fileinfoptr)) {
2373 		if (fileinfo->ops_in_progress==0 && fileinfo->lastuse+FREEBSD_EARLY_RELEASE_DELAY<now) {
2374 			*fileinfoptr = fileinfo->next;
2375 			mfs_real_removefileinfo(fileinfo);
2376 		} else {
2377 			fileinfoptr = &(fileinfo->next);
2378 		}
2379 	}
2380 	pthread_mutex_unlock(&finfo_list_lock);
2381 #endif
2382 	fileinfo = malloc(sizeof(finfo));
2383 	pthread_mutex_init(&(fileinfo->lock),NULL);
2384 	pthread_mutex_lock(&(fileinfo->lock)); // make helgrind happy
2385 #ifdef __FreeBSD__
2386 	/* old FreeBSD fuse reads whole file when opening with O_WRONLY|O_APPEND,
2387 	 * so can't open it write-only */
2388 	(void)accmode;
2389 	(void)inode;
2390 	fileinfo->mode = IO_NONE;
2391 	fileinfo->data = NULL;
2392 #else
2393 	if (accmode == O_RDONLY) {
2394 		fileinfo->mode = IO_READONLY;
2395 		fileinfo->data = read_data_new(inode);
2396 	} else if (accmode == O_WRONLY) {
2397 		fileinfo->mode = IO_WRITEONLY;
2398 		fileinfo->data = write_data_new(inode);
2399 	} else {
2400 		fileinfo->mode = IO_NONE;
2401 		fileinfo->data = NULL;
2402 	}
2403 #endif
2404 #ifdef FREEBSD_EARLY_RELEASE_BUG_WORKAROUND
2405 	fileinfo->ops_in_progress = 0;
2406 	fileinfo->lastuse = now;
2407 	fileinfo->next = NULL;
2408 #endif
2409 	pthread_mutex_unlock(&(fileinfo->lock)); // make helgrind happy
2410 	return fileinfo;
2411 }
2412 
2413 static void mfs_removefileinfo(finfo* fileinfo) {
2414 #ifdef FREEBSD_EARLY_RELEASE_BUG_WORKAROUND
2415 	pthread_mutex_lock(&(fileinfo->lock));
2416 	fileinfo->lastuse = monotonic_seconds();
2417 	pthread_mutex_unlock(&(fileinfo->lock));
2418 	pthread_mutex_lock(&finfo_list_lock);
2419 	fileinfo->next = finfo_head;
2420 	finfo_head = fileinfo;
2421 	pthread_mutex_unlock(&finfo_list_lock);
2422 #else
2423 	pthread_mutex_lock(&(fileinfo->lock));
2424 	if (fileinfo->mode == IO_READONLY || fileinfo->mode == IO_READ) {
2425 		read_data_end(fileinfo->data);
2426 	} else if (fileinfo->mode == IO_WRITEONLY || fileinfo->mode == IO_WRITE) {
2427 //		write_data_flush(fileinfo->data);
2428 		write_data_end(fileinfo->data);
2429 	}
2430 	pthread_mutex_unlock(&(fileinfo->lock));
2431 	pthread_mutex_destroy(&(fileinfo->lock));
2432 	free(fileinfo);
2433 #endif
2434 }
2435 
2436 void mfs_create(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi) {
2437 	struct fuse_entry_param e;
2438 	uint32_t inode;
2439 	uint8_t attr[35];
2440 	char modestr[11];
2441 	char attrstr[256];
2442 	uint8_t mattr;
2443 	uint32_t nleng;
2444 	uint16_t cumask;
2445 	int status;
2446 	struct fuse_ctx ctx;
2447 	groups *gids;
2448 	finfo *fileinfo;
2449 
2450 	ctx = *(fuse_req_ctx(req));
2451 	mfs_makemodestr(modestr,mode);
2452 	mfs_stats_inc(OP_CREATE);
2453 	if (debug_mode) {
2454 #ifdef FUSE_CAP_DONT_MASK
2455 		char umaskstr[11];
2456 		mfs_makemodestr(umaskstr,ctx.umask);
2457 		oplog_printf(&ctx,"create (%lu,%s,-%s:0%04o/%s:0%04o)",(unsigned long int)parent,name,modestr+1,(unsigned int)mode,umaskstr+1,(unsigned int)(ctx.umask));
2458 		fprintf(stderr,"create (%lu,%s,-%s:0%04o/%s:0%04o)\n",(unsigned long int)parent,name,modestr+1,(unsigned int)mode,umaskstr+1,(unsigned int)(ctx.umask));
2459 #else
2460 		oplog_printf(&ctx,"create (%lu,%s,-%s:0%04o)",(unsigned long int)parent,name,modestr+1,(unsigned int)mode);
2461 		fprintf(stderr,"create (%lu,%s,-%s:0%04o)\n",(unsigned long int)parent,name,modestr+1,(unsigned int)mode);
2462 #endif
2463 	}
2464 	if (parent==FUSE_ROOT_ID) {
2465 		if (IS_SPECIAL_NAME(name)) {
2466 			oplog_printf(&ctx,"create (%lu,%s,-%s:0%04o): %s",(unsigned long int)parent,name,modestr+1,(unsigned int)mode,strerr(EACCES));
2467 			fuse_reply_err(req,EACCES);
2468 			return;
2469 		}
2470 	}
2471 	nleng = strlen(name);
2472 	if (nleng>MFS_NAME_MAX) {
2473 		oplog_printf(&ctx,"create (%lu,%s,-%s:0%04o): %s",(unsigned long int)parent,name,modestr+1,(unsigned int)mode,strerr(ENAMETOOLONG));
2474 		fuse_reply_err(req, ENAMETOOLONG);
2475 		return;
2476 	}
2477 
2478 #ifdef FUSE_CAP_DONT_MASK
2479 	cumask = ctx.umask;
2480 #else
2481 	cumask = 0;
2482 #endif
2483 	if (full_permissions) {
2484 		gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
2485 		status = fs_create(parent,nleng,(const uint8_t*)name,mode&07777,cumask,ctx.uid,gids->gidcnt,gids->gidtab,&inode,attr);
2486 		groups_rel(gids);
2487 	} else {
2488 		uint32_t gidtmp = ctx.gid;
2489 		status = fs_create(parent,nleng,(const uint8_t*)name,mode&07777,cumask,ctx.uid,1,&gidtmp,&inode,attr);
2490 	}
2491 	if (status!=ERROR_ENOTSUP) {
2492 #if defined(__APPLE__)
2493 		// due to bug in os x - create in deleted directory goes into infinite loop when it gets ENOENT, so we should change it to different error - we use EACCES
2494 		if (status==ERROR_ENOENT && sstats_get(parent,attr,0)==STATUS_OK) {
2495 			status=ERROR_EACCES;
2496 		}
2497 #endif
2498 		status = mfs_errorconv(status);
2499 		if (status!=0) {
2500 			oplog_printf(&ctx,"create (%lu,%s,-%s:0%04o): %s",(unsigned long int)parent,name,modestr+1,(unsigned int)mode,strerr(status));
2501 			fuse_reply_err(req, status);
2502 			return;
2503 		}
2504 		negentry_cache_remove(parent,nleng,(const uint8_t*)name);
2505 		if (xattr_cache_on) { // Linux asks for this xattr before every write, so after create we can safely assume that there is no such attribute, and set it in xattr cache (improve efficiency on small files)
2506 			xattr_cache_set(inode,ctx.uid,ctx.gid,8+1+10,(const uint8_t*)"security.capability",NULL,0,ERROR_ENOATTR);
2507 		}
2508 //		if (newdircache) {
2509 //			dir_cache_link(parent,nleng,(const uint8_t*)name,inode,attr);
2510 //		}
2511 	} else {
2512 		uint8_t oflags;
2513 		uint32_t gidtmp = ctx.gid;
2514 		oflags = AFTER_CREATE;
2515 		if ((fi->flags & O_ACCMODE) == O_RDONLY) {
2516 			oflags |= WANT_READ;
2517 		} else if ((fi->flags & O_ACCMODE) == O_WRONLY) {
2518 			oflags |= WANT_WRITE;
2519 		} else if ((fi->flags & O_ACCMODE) == O_RDWR) {
2520 			oflags |= WANT_READ | WANT_WRITE;
2521 		} else {
2522 			oplog_printf(&ctx,"create (%lu,%s,-%s:0%04o): %s",(unsigned long int)parent,name,modestr+1,(unsigned int)mode,strerr(EINVAL));
2523 			fuse_reply_err(req, EINVAL);
2524 			return;
2525 		}
2526 		status = fs_mknod(parent,nleng,(const uint8_t*)name,TYPE_FILE,mode&07777,cumask,ctx.uid,1,&gidtmp,0,&inode,attr);
2527 		status = mfs_errorconv(status);
2528 		if (status!=0) {
2529 			oplog_printf(&ctx,"create (%lu,%s,-%s:0%04o) (mknod): %s",(unsigned long int)parent,name,modestr+1,(unsigned int)mode,strerr(status));
2530 			fuse_reply_err(req, status);
2531 			return;
2532 		}
2533 		negentry_cache_remove(parent,nleng,(const uint8_t*)name);
2534 //		if (newdircache) {
2535 //			dir_cache_link(parent,nleng,(const uint8_t*)name,inode,attr);
2536 //		}
2537 		status = fs_opencheck(inode,ctx.uid,1,&gidtmp,oflags,NULL);
2538 		status = mfs_errorconv(status);
2539 		if (status!=0) {
2540 			oplog_printf(&ctx,"create (%lu,%s,-%s:0%04o) (open): %s",(unsigned long int)parent,name,modestr+1,(unsigned int)mode,strerr(status));
2541 			fuse_reply_err(req, status);
2542 			return;
2543 		}
2544 	}
2545 
2546 	mattr = mfs_attr_get_mattr(attr);
2547 	fileinfo = mfs_newfileinfo(fi->flags & O_ACCMODE,inode);
2548 	fi->fh = (unsigned long)fileinfo;
2549 #if defined(__FreeBSD__) || defined(__APPLE__)
2550 	fi->keep_cache = 0;
2551 	fi->direct_io = 1;
2552 #else
2553 	if (keep_cache==1) {
2554 		fi->keep_cache=1;
2555 	} else if (keep_cache==2) {
2556 		fi->keep_cache=0;
2557 	} else {
2558 		fi->keep_cache = (mattr&MATTR_ALLOWDATACACHE)?1:0;
2559 	}
2560 	fi->direct_io = 0;
2561 #endif
2562 	if (debug_mode) {
2563 		fprintf(stderr,"create (%lu) ok -> keep cache: %u\n",(unsigned long int)inode,(unsigned int)fi->keep_cache);
2564 	}
2565 	dcache_invalidate_attr(parent);
2566 	memset(&e, 0, sizeof(e));
2567 	e.ino = inode;
2568 	e.generation = 1;
2569 	e.attr_timeout = (mattr&MATTR_NOACACHE)?0.0:attr_cache_timeout;
2570 	e.entry_timeout = (mattr&MATTR_NOECACHE)?0.0:entry_cache_timeout;
2571 	mfs_attr_to_stat(inode,attr,&e.attr);
2572 	mfs_makeattrstr(attrstr,256,&e.attr);
2573 	oplog_printf(&ctx,"create (%lu,%s,-%s:0%04o): OK (%.1lf,%lu,%.1lf,%s,%u)",(unsigned long int)parent,name,modestr+1,(unsigned int)mode,e.entry_timeout,(unsigned long int)e.ino,e.attr_timeout,attrstr,(unsigned int)fi->keep_cache);
2574 	if (fuse_reply_create(req, &e, fi) == -ENOENT) {
2575 		mfs_removefileinfo(fileinfo);
2576 		fi->fh = 0;
2577 	}
2578 }
2579 
2580 void mfs_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
2581 	uint8_t oflags;
2582 	uint8_t attr[35];
2583 #if !(defined(__FreeBSD__) || defined(__APPLE__))
2584 	uint8_t mattr;
2585 #endif
2586 	int status;
2587 	struct fuse_ctx ctx;
2588 	groups *gids;
2589 	finfo *fileinfo;
2590 
2591 	ctx = *(fuse_req_ctx(req));
2592 	mfs_stats_inc(OP_OPEN);
2593 	if (debug_mode) {
2594 		oplog_printf(&ctx,"open (%lu) ...",(unsigned long int)ino);
2595 		fprintf(stderr,"open (%lu)\n",(unsigned long int)ino);
2596 	}
2597 //	if (ino==MASTER_INODE) {
2598 //		minfo *masterinfo;
2599 //		status = fs_direct_connect();
2600 //		if (status<0) {
2601 //			fuse_reply_err(req,EIO);
2602 //			return;
2603 //		}
2604 //		masterinfo = malloc(sizeof(minfo));
2605 //		if (masterinfo==NULL) {
2606 //			fuse_reply_err(req,ENOMEM);
2607 //			return;
2608 //		}
2609 //		masterinfo->sd = status;
2610 //		masterinfo->sent = 0;
2611 //		fi->direct_io = 1;
2612 //		fi->fh = (unsigned long)masterinfo;
2613 //		fuse_reply_open(req, fi);
2614 //		return;
2615 //	}
2616 	if (ino==MASTERINFO_INODE) {
2617 		if ((fi->flags & O_ACCMODE) != O_RDONLY) {
2618 			oplog_printf(&ctx,"open (%lu) (internal node: MASTERINFO): %s",(unsigned long int)ino,strerr(EACCES));
2619 			fuse_reply_err(req,EACCES);
2620 			return;
2621 		}
2622 		fi->fh = 0;
2623 		fi->direct_io = 0;
2624 		fi->keep_cache = 1;
2625 		oplog_printf(&ctx,"open (%lu) (internal node: MASTERINFO): OK (0,1)",(unsigned long int)ino);
2626 		fuse_reply_open(req, fi);
2627 		return;
2628 	}
2629 
2630 	if (ino==STATS_INODE) {
2631 		sinfo *statsinfo;
2632 //		if ((fi->flags & O_ACCMODE) != O_RDONLY) {
2633 //			stats_reset_all();
2634 //			fuse_reply_err(req,EACCES);
2635 //			return;
2636 //		}
2637 		statsinfo = malloc(sizeof(sinfo));
2638 		if (statsinfo==NULL) {
2639 			oplog_printf(&ctx,"open (%lu) (internal node: STATS): %s",(unsigned long int)ino,strerr(ENOMEM));
2640 			fuse_reply_err(req,ENOMEM);
2641 			return;
2642 		}
2643 		pthread_mutex_init(&(statsinfo->lock),NULL);	// make helgrind happy
2644 		pthread_mutex_lock(&(statsinfo->lock));		// make helgrind happy
2645 		stats_show_all(&(statsinfo->buff),&(statsinfo->leng));
2646 		statsinfo->reset = 0;
2647 		pthread_mutex_unlock(&(statsinfo->lock));	// make helgrind happy
2648 		fi->fh = (unsigned long)statsinfo;
2649 		fi->direct_io = 1;
2650 		fi->keep_cache = 0;
2651 		oplog_printf(&ctx,"open (%lu) (internal node: STATS): OK (1,0)",(unsigned long int)ino);
2652 		fuse_reply_open(req, fi);
2653 		return;
2654 	}
2655 	if (ino==MOOSE_INODE) {
2656 		fi->fh = 0;
2657 		fi->direct_io = 1;
2658 		fi->keep_cache = 0;
2659 		oplog_printf(&ctx,"open (%lu) (internal node: MOOSE): OK (1,0)",(unsigned long int)ino);
2660 		fuse_reply_open(req, fi);
2661 		return;
2662 	}
2663 	if (ino==OPLOG_INODE || ino==OPHISTORY_INODE) {
2664 		if ((fi->flags & O_ACCMODE) != O_RDONLY) {
2665 			oplog_printf(&ctx,"open (%lu) (internal node: %s): %s",(unsigned long int)ino,(ino==OPLOG_INODE)?"OPLOG":"OPHISTORY",strerr(EACCES));
2666 			fuse_reply_err(req,EACCES);
2667 			return;
2668 		}
2669 		fi->fh = oplog_newhandle((ino==OPHISTORY_INODE)?1:0);
2670 		fi->direct_io = 1;
2671 		fi->keep_cache = 0;
2672 		oplog_printf(&ctx,"open (%lu) (internal node: %s): OK (1,0)",(unsigned long int)ino,(ino==OPLOG_INODE)?"OPLOG":"OPHISTORY");
2673 		fuse_reply_open(req, fi);
2674 		return;
2675 	}
2676 /*
2677 	if (ino==ATTRCACHE_INODE) {
2678 		fi->fh = 0;
2679 		fi->direct_io = 1;
2680 		fi->keep_cache = 0;
2681 		fuse_reply_open(req, fi);
2682 		return;
2683 	}
2684 */
2685 	oflags = 0;
2686 	if ((fi->flags & O_ACCMODE) == O_RDONLY) {
2687 		oflags |= WANT_READ;
2688 	} else if ((fi->flags & O_ACCMODE) == O_WRONLY) {
2689 		oflags |= WANT_WRITE;
2690 	} else if ((fi->flags & O_ACCMODE) == O_RDWR) {
2691 		oflags |= WANT_READ | WANT_WRITE;
2692 	}
2693 	// TODO: idea - only recently used inodes may have their data in cache, so if inode wasn't recently used then return ok status and force cache clear
2694 	//       fs_opencheck should always return status ok (because system should check access rights before using attributes), but even though perform opencheck
2695 	//       and if it returns error then save this error in fileinfo record, and return it on the first following I/O operation
2696 	//
2697 	//	This should significantly speed up opening process.
2698 	//
2699 	// if (usedinodes_cache_check(ino)==0 || keep_cache>0) {
2700 	//	usedinodes_cache_add(ino);
2701 	//	fileinfo = mfs_newfileinfo(fi->flags & O_ACCMODE,ino);
2702 	//	fi->fh = (unsigned long)fileinfo;
2703 	//	if (keep_cache==1) {
2704 	//		fi->keep_cache=1;
2705 	//	} else {
2706 	//		fi->keep_cache=0;
2707 	//	}
2708 	//	if (debug_mode) {
2709 	//		fprintf(stderr,"open (%lu) ok -> keep cache: %lu\n",(unsigned long int)ino,(unsigned long int)fi->keep_cache);
2710 	//	}
2711 	//	fi->direct_io = 0;
2712 	//	oplog_printf(&ctx,"open (%lu): OK (%lu,%lu)",(unsigned long int)ino,(unsigned long int)fi->direct_io,(unsigned long int)fi->keep_cache);
2713 	//	if (fuse_reply_open(req, fi) == -ENOENT) {
2714 	//		mfs_removefileinfo(fileinfo);
2715 	//	}
2716 	//	status = fs_opencheck(ino,ctx.uid,ctx.gid,oflags,attr);
2717 	//	status = mfs_errorconv(status);
2718 	//	if (status!=0) {
2719 	//		pthread_mutes_lock(&(fileinfo->lock));
2720 	//		fileinfo->status = status;
2721 	//		pthread_mutex_unlock(&(fileinfo->lock));
2722 	//	}
2723 	// } else {
2724 	if (full_permissions) {
2725 		gids = groups_get_x(ctx.pid,ctx.uid,ctx.gid,2);
2726 		status = fs_opencheck(ino,ctx.uid,gids->gidcnt,gids->gidtab,oflags,attr);
2727 		groups_rel(gids);
2728 	} else {
2729 		uint32_t gidtmp = ctx.gid;
2730 		status = fs_opencheck(ino,ctx.uid,1,&gidtmp,oflags,attr);
2731 	}
2732 	status = mfs_errorconv(status);
2733 	if (status!=0) {
2734 		oplog_printf(&ctx,"open (%lu): %s",(unsigned long int)ino,strerr(status));
2735 		fuse_reply_err(req, status);
2736 		return ;
2737 	}
2738 
2739 	fileinfo = mfs_newfileinfo(fi->flags & O_ACCMODE,ino);
2740 	fi->fh = (unsigned long)fileinfo;
2741 #if defined(__FreeBSD__) || defined(__APPLE__)
2742 	fi->keep_cache = 0;
2743 	fi->direct_io = 1;
2744 #else
2745 	mattr = mfs_attr_get_mattr(attr);
2746 	if (keep_cache==1) {
2747 		fi->keep_cache=1;
2748 	} else if (keep_cache==2) {
2749 		fi->keep_cache=0;
2750 	} else {
2751 		fi->keep_cache = (mattr&MATTR_ALLOWDATACACHE)?1:0;
2752 	}
2753 	fi->direct_io = 0;
2754 #endif
2755 	if (debug_mode) {
2756 		fprintf(stderr,"open (%lu) ok -> keep cache: %u\n",(unsigned long int)ino,(unsigned int)fi->keep_cache);
2757 	}
2758 	oplog_printf(&ctx,"open (%lu): OK (%u,%u)",(unsigned long int)ino,(unsigned int)fi->direct_io,(unsigned int)fi->keep_cache);
2759 	if (fuse_reply_open(req, fi) == -ENOENT) {
2760 		mfs_removefileinfo(fileinfo);
2761 	}
2762 	// }
2763 }
2764 
2765 void mfs_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
2766 	finfo *fileinfo = (finfo*)(unsigned long)(fi->fh);
2767 	struct fuse_ctx ctx;
2768 
2769 	ctx = *(fuse_req_ctx(req));
2770 	mfs_stats_inc(OP_RELEASE);
2771 	if (debug_mode) {
2772 		oplog_printf(&ctx,"release (%lu) ...",(unsigned long int)ino);
2773 		fprintf(stderr,"release (%lu)\n",(unsigned long int)ino);
2774 	}
2775 //	if (ino==MASTER_INODE) {
2776 //		minfo *masterinfo = (minfo*)(unsigned long)(fi->fh);
2777 //		if (masterinfo!=NULL) {
2778 //			fs_direct_close(masterinfo->sd);
2779 //			free(masterinfo);
2780 //		}
2781 //		fuse_reply_err(req,0);
2782 //		return;
2783 //	}
2784 	if (ino==MASTERINFO_INODE/* || ino==ATTRCACHE_INODE*/) {
2785 		oplog_printf(&ctx,"release (%lu) (internal node: MASTERINFO): OK",(unsigned long int)ino);
2786 		fuse_reply_err(req,0);
2787 		return;
2788 	}
2789 	if (ino==STATS_INODE) {
2790 		sinfo *statsinfo = (sinfo*)(unsigned long)(fi->fh);
2791 		if (statsinfo!=NULL) {
2792 			pthread_mutex_lock(&(statsinfo->lock));		// make helgrind happy
2793 			if (statsinfo->buff!=NULL) {
2794 				free(statsinfo->buff);
2795 			}
2796 			if (statsinfo->reset) {
2797 				stats_reset_all();
2798 			}
2799 			pthread_mutex_unlock(&(statsinfo->lock));	// make helgrind happy
2800 			pthread_mutex_destroy(&(statsinfo->lock));	// make helgrind happy
2801 			free(statsinfo);
2802 		}
2803 		oplog_printf(&ctx,"release (%lu) (internal node: STATS): OK",(unsigned long int)ino);
2804 		fuse_reply_err(req,0);
2805 		return;
2806 	}
2807 	if (ino==MOOSE_INODE) {
2808 		oplog_printf(&ctx,"release (%lu) (internal node: MOOSE): OK",(unsigned long int)ino);
2809 		fuse_reply_err(req,0);
2810 		return;
2811 	}
2812 	if (ino==OPLOG_INODE || ino==OPHISTORY_INODE) {
2813 		oplog_releasehandle(fi->fh);
2814 		oplog_printf(&ctx,"release (%lu) (internal node: %s): OK",(unsigned long int)ino,(ino==OPLOG_INODE)?"OPLOG":"OPHISTORY");
2815 		fuse_reply_err(req,0);
2816 		return;
2817 	}
2818 //	fuse_reply_err(req,0);
2819 	dcache_invalidate_attr(ino);
2820 	fs_release(ino);
2821 	oplog_printf(&ctx,"release (%lu): OK",(unsigned long int)ino);
2822 	fuse_reply_err(req,0);
2823 	if (fileinfo!=NULL) {
2824 		mfs_removefileinfo(fileinfo);
2825 	}
2826 }
2827 
2828 void mfs_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
2829 	finfo *fileinfo = (finfo*)(unsigned long)(fi->fh);
2830 	uint8_t *buff;
2831 	uint32_t ssize;
2832 	struct iovec *iov;
2833 	uint32_t iovcnt;
2834 	void *buffptr;
2835 	int err;
2836 	struct fuse_ctx ctx;
2837 
2838 	ctx = *(fuse_req_ctx(req));
2839 	mfs_stats_inc(OP_READ);
2840 	if (debug_mode) {
2841 		if (ino!=OPLOG_INODE && ino!=OPHISTORY_INODE) {
2842 			oplog_printf(&ctx,"read (%lu,%llu,%llu) ...",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off);
2843 		}
2844 		fprintf(stderr,"read from inode %lu up to %llu bytes from position %llu\n",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off);
2845 	}
2846 	if (ino==MASTERINFO_INODE) {
2847 		uint8_t masterinfo[14];
2848 		fs_getmasterlocation(masterinfo);
2849 		masterproxy_getlocation(masterinfo);
2850 #ifdef MASTERINFO_WITH_VERSION
2851 		if (off>=14) {
2852 			oplog_printf(&ctx,"read (%lu,%llu,%llu): OK (no data)",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off);
2853 			fuse_reply_buf(req,NULL,0);
2854 		} else if (off+size>14) {
2855 			oplog_printf(&ctx,"read (%lu,%llu,%llu): OK (%lu)",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,(unsigned long int)(14-off));
2856 			fuse_reply_buf(req,(char*)(masterinfo+off),14-off);
2857 #else
2858 		if (off>=10) {
2859 			oplog_printf(&ctx,"read (%lu,%llu,%llu): OK (no data)",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off);
2860 			fuse_reply_buf(req,NULL,0);
2861 		} else if (off+size>10) {
2862 			oplog_printf(&ctx,"read (%lu,%llu,%llu): OK (%lu)",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,(unsigned long int)(10-off));
2863 			fuse_reply_buf(req,(char*)(masterinfo+off),10-off);
2864 #endif
2865 		} else {
2866 			oplog_printf(&ctx,"read (%lu,%llu,%llu): OK (%lu)",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,(unsigned long int)size);
2867 			fuse_reply_buf(req,(char*)(masterinfo+off),size);
2868 		}
2869 		return;
2870 	}
2871 	if (ino==STATS_INODE) {
2872 		sinfo *statsinfo = (sinfo*)(unsigned long)(fi->fh);
2873 		if (statsinfo!=NULL) {
2874 			pthread_mutex_lock(&(statsinfo->lock));		// make helgrind happy
2875 			if (off>=statsinfo->leng) {
2876 				oplog_printf(&ctx,"read (%lu,%llu,%llu): OK (no data)",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off);
2877 				fuse_reply_buf(req,NULL,0);
2878 			} else if ((uint64_t)(off+size)>(uint64_t)(statsinfo->leng)) {
2879 				oplog_printf(&ctx,"read (%lu,%llu,%llu): OK (%lu)",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,(unsigned long int)(statsinfo->leng-off));
2880 				fuse_reply_buf(req,statsinfo->buff+off,statsinfo->leng-off);
2881 			} else {
2882 				oplog_printf(&ctx,"read (%lu,%llu,%llu): OK (%lu)",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,(unsigned long int)size);
2883 				fuse_reply_buf(req,statsinfo->buff+off,size);
2884 			}
2885 			pthread_mutex_unlock(&(statsinfo->lock));	// make helgrind happy
2886 		} else {
2887 			oplog_printf(&ctx,"read (%lu,%llu,%llu): OK (no data)",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off);
2888 			fuse_reply_buf(req,NULL,0);
2889 		}
2890 		return;
2891 	}
2892 	if (ino==MOOSE_INODE) {
2893 		static char mooseascii[175] = {
2894 			0x20, 0x5C, 0x5F, 0x5C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
2895 			0x2F, 0x5F, 0x2F, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x5C, 0x5F, 0x5C, 0x5F, 0x20, 0x20, 0x20, 0x20,
2896 			0x5F, 0x2F, 0x5F, 0x2F, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5C, 0x2D, 0x2D,
2897 			0x2F, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x40, 0x40, 0x5C, 0x5F, 0x2D,
2898 			0x2D, 0x5F, 0x5F, 0x5F, 0x5F, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x5F, 0x5F,
2899 			0x29, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
2900 			0x20, 0x20, 0x20, 0x60, 0x60, 0x5C, 0x20, 0x20, 0x20, 0x20, 0x5F, 0x5F, 0x20, 0x20, 0x7C, 0x0A,
2901 			0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0x7C, 0x2D, 0x27, 0x20,
2902 			0x20, 0x60, 0x7C, 0x7C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
2903 			0x7C, 0x7C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0x7C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
2904 			0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x22, 0x0A};
2905 		uint32_t t = monotonic_useconds()%5000000;
2906 		if (t<150000 || (t>=600000 && t<750000)) {
2907 			mooseascii[59]='O';
2908 			mooseascii[60]='O';
2909 		} else if ((t>=150000 && t<300000) || (t>=450000 && t<600000)) {
2910 			mooseascii[59]='O';
2911 			mooseascii[60]='o';
2912 		} else if (t>=300000 && t<450000) {
2913 			mooseascii[59]='O';
2914 			mooseascii[60]='-';
2915 		} else {
2916 			mooseascii[59]='O';
2917 			mooseascii[60]='O';
2918 		}
2919 		if (off>=175) {
2920 			fuse_reply_buf(req,NULL,0);
2921 		} else if ((uint64_t)(off+size)>175) {
2922 			fuse_reply_buf(req,mooseascii+off,175-off);
2923 		} else {
2924 			fuse_reply_buf(req,mooseascii+off,size);
2925 		}
2926 		return;
2927 	}
2928 	if (ino==OPLOG_INODE || ino==OPHISTORY_INODE) {
2929 		oplog_getdata(fi->fh,&buff,&ssize,size);
2930 		fuse_reply_buf(req,(char*)buff,ssize);
2931 		oplog_releasedata(fi->fh);
2932 		return;
2933 	}
2934 /*
2935 	if (ino==ATTRCACHE_INODE) {
2936 		uint8_t info[2];
2937 		info[0]=dir_cache_ison()+'0';
2938 		if (info[0]!='0' && info[0]!='1') {
2939 			info[0]='X';
2940 		}
2941 		info[1]='\n';
2942 		if (off>2) {
2943 			fuse_reply_buf(req,NULL,0);
2944 		} else if (off+size>2) {
2945 			fuse_reply_buf(req,(char*)(info+off),2-off);
2946 		} else {
2947 			fuse_reply_buf(req,(char*)(info+off),size);
2948 		}
2949 		return;
2950 	}
2951 */
2952 	if (fileinfo==NULL) {
2953 		oplog_printf(&ctx,"read (%lu,%llu,%llu): %s",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,strerr(EBADF));
2954 		fuse_reply_err(req,EBADF);
2955 		return;
2956 	}
2957 //	if (ino==MASTER_INODE) {
2958 //		minfo *masterinfo = (minfo*)(unsigned long)(fi->fh);
2959 //		if (masterinfo->sent) {
2960 //			int rsize;
2961 //			buff = malloc(size);
2962 //			rsize = fs_direct_read(masterinfo->sd,buff,size);
2963 //			fuse_reply_buf(req,(char*)buff,rsize);
2964 //			//syslog(LOG_WARNING,"master received: %d/%llu",rsize,(unsigned long long int)size);
2965 //			free(buff);
2966 //		} else {
2967 //			syslog(LOG_WARNING,"master: read before write");
2968 //			fuse_reply_buf(req,NULL,0);
2969 //		}
2970 //		return;
2971 //	}
2972 	if (off>=MAX_FILE_SIZE || off+size>=MAX_FILE_SIZE) {
2973 		oplog_printf(&ctx,"read (%lu,%llu,%llu): %s",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,strerr(EFBIG));
2974 		fuse_reply_err(req,EFBIG);
2975 		return;
2976 	}
2977 	pthread_mutex_lock(&(fileinfo->lock));
2978 	if (fileinfo->mode==IO_WRITEONLY) {
2979 		pthread_mutex_unlock(&(fileinfo->lock));
2980 		oplog_printf(&ctx,"read (%lu,%llu,%llu): %s",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,strerr(EACCES));
2981 		fuse_reply_err(req,EACCES);
2982 		return;
2983 	}
2984 #ifdef FREEBSD_EARLY_RELEASE_BUG_WORKAROUND
2985 	fileinfo->ops_in_progress++;
2986 #endif
2987 	if (fileinfo->mode==IO_WRITE) {
2988 		err = write_data_flush(fileinfo->data);
2989 		if (err!=0) {
2990 #ifdef FREEBSD_EARLY_RELEASE_BUG_WORKAROUND
2991 			fileinfo->ops_in_progress--;
2992 			fileinfo->lastuse = monotonic_seconds();
2993 #endif
2994 			pthread_mutex_unlock(&(fileinfo->lock));
2995 			if (debug_mode) {
2996 				fprintf(stderr,"IO error occurred while writing inode %lu\n",(unsigned long int)ino);
2997 			}
2998 			oplog_printf(&ctx,"read (%lu,%llu,%llu): %s",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,strerr(err));
2999 			fuse_reply_err(req,err);
3000 			return;
3001 		}
3002 		write_data_end(fileinfo->data);
3003 	}
3004 	if (fileinfo->mode==IO_WRITE || fileinfo->mode==IO_NONE) {
3005 		fileinfo->mode = IO_READ;
3006 		fileinfo->data = read_data_new(ino);
3007 	}
3008 	write_data_flush_inode(ino);
3009 /* stable version
3010 	ssize = size;
3011 	buff = NULL;	// use internal 'readdata' buffer
3012 	err = read_data(fileinfo->data,off,&ssize,&buff);
3013 */
3014 	ssize = size;
3015 	err = read_data(fileinfo->data,off,&ssize,&buffptr,&iov,&iovcnt);
3016 
3017 	if (err!=0) {
3018 		if (debug_mode) {
3019 			fprintf(stderr,"IO error occurred while reading inode %lu\n",(unsigned long int)ino);
3020 		}
3021 		oplog_printf(&ctx,"read (%lu,%llu,%llu): %s",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,strerr(err));
3022 		fuse_reply_err(req,err);
3023 	} else {
3024 		if (debug_mode) {
3025 			fprintf(stderr,"%"PRIu32" bytes have been read from inode %lu\n",ssize,(unsigned long int)ino);
3026 		}
3027 		oplog_printf(&ctx,"read (%lu,%llu,%llu): OK (%lu)",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,(unsigned long int)ssize);
3028 //		fuse_reply_buf(req,(char*)buff,ssize);
3029 		fuse_reply_iov(req,iov,iovcnt);
3030 	}
3031 //	read_data_freebuff(fileinfo->data);
3032 	read_data_free_buff(fileinfo->data,buffptr,iov);
3033 #ifdef FREEBSD_EARLY_RELEASE_BUG_WORKAROUND
3034 	fileinfo->ops_in_progress--;
3035 	fileinfo->lastuse = monotonic_seconds();
3036 #endif
3037 	pthread_mutex_unlock(&(fileinfo->lock));
3038 }
3039 
3040 void mfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi) {
3041 	finfo *fileinfo = (finfo*)(unsigned long)(fi->fh);
3042 	int err;
3043 	struct fuse_ctx ctx;
3044 
3045 	ctx = *(fuse_req_ctx(req));
3046 	mfs_stats_inc(OP_WRITE);
3047 	if (debug_mode) {
3048 		oplog_printf(&ctx,"write (%lu,%llu,%llu) ...",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off);
3049 		fprintf(stderr,"write to inode %lu %llu bytes at position %llu\n",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off);
3050 	}
3051 	if (ino==MASTERINFO_INODE || ino==OPLOG_INODE || ino==OPHISTORY_INODE || ino==MOOSE_INODE) {
3052 		oplog_printf(&ctx,"write (%lu,%llu,%llu): %s",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,strerr(EACCES));
3053 		fuse_reply_err(req,EACCES);
3054 		return;
3055 	}
3056 	if (ino==STATS_INODE) {
3057 		sinfo *statsinfo = (sinfo*)(unsigned long)(fi->fh);
3058 		if (statsinfo!=NULL) {
3059 			pthread_mutex_lock(&(statsinfo->lock));		// make helgrind happy
3060 			statsinfo->reset=1;
3061 			pthread_mutex_unlock(&(statsinfo->lock));	// make helgrind happy
3062 		}
3063 		oplog_printf(&ctx,"write (%lu,%llu,%llu): OK (%lu)",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,(unsigned long int)size);
3064 		fuse_reply_write(req,size);
3065 		return;
3066 	}
3067 /*
3068 	if (ino==ATTRCACHE_INODE) {
3069 		if (off==0 && size>0 && buf[0]>='0' && buf[0]<='1') {
3070 			dir_cache_user_switch(buf[0]-'0');
3071 			newdircache = buf[0]-'0';
3072 		}
3073 		fuse_reply_write(req,size);
3074 		return;
3075 	}
3076 */
3077 	if (fileinfo==NULL) {
3078 		oplog_printf(&ctx,"write (%lu,%llu,%llu): %s",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,strerr(EBADF));
3079 		fuse_reply_err(req,EBADF);
3080 		return;
3081 	}
3082 //	if (ino==MASTER_INODE) {
3083 //		minfo *masterinfo = (minfo*)(unsigned long)(fi->fh);
3084 //		int wsize;
3085 //		masterinfo->sent=1;
3086 //		wsize = fs_direct_write(masterinfo->sd,(const uint8_t*)buf,size);
3087 //		//syslog(LOG_WARNING,"master sent: %d/%llu",wsize,(unsigned long long int)size);
3088 //		fuse_reply_write(req,wsize);
3089 //		return;
3090 //	}
3091 	if (off>=MAX_FILE_SIZE || off+size>=MAX_FILE_SIZE) {
3092 		oplog_printf(&ctx,"write (%lu,%llu,%llu): %s",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,strerr(EFBIG));
3093 		fuse_reply_err(req, EFBIG);
3094 		return;
3095 	}
3096 	pthread_mutex_lock(&(fileinfo->lock));
3097 	if (fileinfo->mode==IO_READONLY) {
3098 		pthread_mutex_unlock(&(fileinfo->lock));
3099 		oplog_printf(&ctx,"write (%lu,%llu,%llu): %s",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,strerr(EACCES));
3100 		fuse_reply_err(req,EACCES);
3101 		return;
3102 	}
3103 #ifdef FREEBSD_EARLY_RELEASE_BUG_WORKAROUND
3104 	fileinfo->ops_in_progress++;
3105 #endif
3106 	if (fileinfo->mode==IO_READ) {
3107 		read_data_end(fileinfo->data);
3108 	}
3109 	if (fileinfo->mode==IO_READ || fileinfo->mode==IO_NONE) {
3110 		fileinfo->mode = IO_WRITE;
3111 		fileinfo->data = write_data_new(ino);
3112 	}
3113 	err = write_data(fileinfo->data,off,size,(const uint8_t*)buf);
3114 #ifdef FREEBSD_EARLY_RELEASE_BUG_WORKAROUND
3115 	fileinfo->ops_in_progress--;
3116 	fileinfo->lastuse = monotonic_seconds();
3117 #endif
3118 	if (err!=0) {
3119 		pthread_mutex_unlock(&(fileinfo->lock));
3120 		if (debug_mode) {
3121 			fprintf(stderr,"IO error occurred while writing inode %lu\n",(unsigned long int)ino);
3122 		}
3123 		oplog_printf(&ctx,"write (%lu,%llu,%llu): %s",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,strerr(err));
3124 		fuse_reply_err(req,err);
3125 	} else {
3126 		pthread_mutex_unlock(&(fileinfo->lock));
3127 		if (debug_mode) {
3128 			fprintf(stderr,"%llu bytes have been written to inode %lu\n",(unsigned long long int)size,(unsigned long int)ino);
3129 		}
3130 		oplog_printf(&ctx,"write (%lu,%llu,%llu): OK (%lu)",(unsigned long int)ino,(unsigned long long int)size,(unsigned long long int)off,(unsigned long int)size);
3131 		read_inode_dirty_region(ino,off,size,buf);
3132 		fuse_reply_write(req,size);
3133 	}
3134 }
3135 
3136 void mfs_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
3137 	finfo *fileinfo = (finfo*)(unsigned long)(fi->fh);
3138 	int err;
3139 	struct fuse_ctx ctx;
3140 
3141 	ctx = *(fuse_req_ctx(req));
3142 	mfs_stats_inc(OP_FLUSH);
3143 	if (debug_mode) {
3144 		oplog_printf(&ctx,"flush (%lu) ...",(unsigned long int)ino);
3145 		fprintf(stderr,"flush (%lu)\n",(unsigned long int)ino);
3146 	}
3147 	if (IS_SPECIAL_INODE(ino)) {
3148 		oplog_printf(&ctx,"flush (%lu): OK",(unsigned long int)ino);
3149 		fuse_reply_err(req,0);
3150 		return;
3151 	}
3152 	if (fileinfo==NULL) {
3153 		oplog_printf(&ctx,"flush (%lu): %s",(unsigned long int)ino,strerr(EBADF));
3154 		fuse_reply_err(req,EBADF);
3155 		return;
3156 	}
3157 //	syslog(LOG_NOTICE,"remove_locks inode:%lu owner:%llu",(unsigned long int)ino,(unsigned long long int)fi->lock_owner);
3158 	err = 0;
3159 //	fuse_reply_err(req,err);
3160 	pthread_mutex_lock(&(fileinfo->lock));
3161 #ifdef FREEBSD_EARLY_RELEASE_BUG_WORKAROUND
3162 	fileinfo->ops_in_progress++;
3163 #endif
3164 	if (fileinfo->mode==IO_WRITE || fileinfo->mode==IO_WRITEONLY) {
3165 		err = write_data_flush(fileinfo->data);
3166 	}
3167 #ifdef FREEBSD_EARLY_RELEASE_BUG_WORKAROUND
3168 	fileinfo->ops_in_progress--;
3169 	fileinfo->lastuse = monotonic_seconds();
3170 #endif
3171 	pthread_mutex_unlock(&(fileinfo->lock));
3172 	if (err!=0) {
3173 		oplog_printf(&ctx,"flush (%lu): %s",(unsigned long int)ino,strerr(err));
3174 	} else {
3175 		dcache_invalidate_attr(ino);
3176 		oplog_printf(&ctx,"flush (%lu): OK",(unsigned long int)ino);
3177 	}
3178 	fuse_reply_err(req,err);
3179 }
3180 
3181 void mfs_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi) {
3182 	finfo *fileinfo = (finfo*)(unsigned long)(fi->fh);
3183 	int err;
3184 	struct fuse_ctx ctx;
3185 
3186 	ctx = *(fuse_req_ctx(req));
3187 	mfs_stats_inc(OP_FSYNC);
3188 	if (debug_mode) {
3189 		oplog_printf(&ctx,"fsync (%lu,%d) ...",(unsigned long int)ino,datasync);
3190 		fprintf(stderr,"fsync (%lu,%d)\n",(unsigned long int)ino,datasync);
3191 	}
3192 	if (IS_SPECIAL_INODE(ino)) {
3193 		oplog_printf(&ctx,"fsync (%lu,%d): OK",(unsigned long int)ino,datasync);
3194 		fuse_reply_err(req,0);
3195 		return;
3196 	}
3197 	if (fileinfo==NULL) {
3198 		oplog_printf(&ctx,"fsync (%lu,%d): %s",(unsigned long int)ino,datasync,strerr(EBADF));
3199 		fuse_reply_err(req,EBADF);
3200 		return;
3201 	}
3202 	err = 0;
3203 	pthread_mutex_lock(&(fileinfo->lock));
3204 #ifdef FREEBSD_EARLY_RELEASE_BUG_WORKAROUND
3205 	fileinfo->ops_in_progress++;
3206 #endif
3207 	if (fileinfo->mode==IO_WRITE || fileinfo->mode==IO_WRITEONLY) {
3208 		err = write_data_flush(fileinfo->data);
3209 	}
3210 #ifdef FREEBSD_EARLY_RELEASE_BUG_WORKAROUND
3211 	fileinfo->ops_in_progress--;
3212 	fileinfo->lastuse = monotonic_seconds();
3213 #endif
3214 	pthread_mutex_unlock(&(fileinfo->lock));
3215 	if (err!=0) {
3216 		oplog_printf(&ctx,"fsync (%lu,%d): %s",(unsigned long int)ino,datasync,strerr(err));
3217 	} else {
3218 		dcache_invalidate_attr(ino);
3219 		oplog_printf(&ctx,"fsync (%lu,%d): OK",(unsigned long int)ino,datasync);
3220 	}
3221 	fuse_reply_err(req,err);
3222 }
3223 
3224 // Linux ACL format:
3225 //   version:8 (2)
3226 //   flags:8 (0)
3227 //   filler:16
3228 //   N * [ tag:16 perm:16 id:32 ]
3229 //   tag:
3230 //     01 - user
3231 //     02 - named user
3232 //     04 - group
3233 //     08 - named group
3234 //     10 - mask
3235 //     20 - other
3236 
3237 int mfs_getacl(fuse_req_t req, fuse_ino_t ino, uint8_t opened,uint32_t uid,uint32_t gids,uint32_t *gid,uint8_t aclxattr,const uint8_t **buff,uint32_t *leng) {
3238 	uint16_t userperm;
3239 	uint16_t groupperm;
3240 	uint16_t otherperm;
3241 	uint16_t maskperm;
3242 	uint16_t namedusers;
3243 	uint16_t namedgroups;
3244 	const uint8_t *namedacls;
3245 	uint8_t *b;
3246 	uint32_t namedaclssize;
3247 	const uint8_t *p;
3248 	uint32_t i;
3249 	int status;
3250 
3251 	(void)req;
3252 	*buff = NULL;
3253 	*leng = 0;
3254 	status = fs_getacl(ino,opened,uid,gids,gid,aclxattr,&userperm,&groupperm,&otherperm,&maskperm,&namedusers,&namedgroups,&namedacls,&namedaclssize);
3255 
3256 	if (status!=STATUS_OK) {
3257 		return status;
3258 	}
3259 
3260 	if (((namedusers+namedgroups)*6U) != namedaclssize) {
3261 		return ERROR_EINVAL;
3262 	}
3263 
3264 	*leng = 4+32+(namedusers+namedgroups)*8;
3265 	b = mfs_aclstorage_get(4+32+(namedusers+namedgroups)*8);
3266 //	fprintf(stderr,"getacl buff ptr: %p (size: %u)\n",(void*)b,4+32+(namedusers+namedgroups)*8);
3267 	*buff = b;
3268 	p = namedacls;
3269 	b[0] = 2;
3270 	b[1] = 0;
3271 	b[2] = 0;
3272 	b[3] = 0;
3273 	b+=4;
3274 	*(uint16_t*)(b) = 1;
3275 	*(uint16_t*)(b+2) = userperm;
3276 	*(uint32_t*)(b+4) = UINT32_C(0xFFFFFFFF);
3277 	b+=8;
3278 	for (i=0 ; i<namedusers ; i++) {
3279 		*(uint32_t*)(b+4) = get32bit(&p);
3280 		*(uint16_t*)(b) = 2;
3281 		*(uint16_t*)(b+2) = get16bit(&p);
3282 		b+=8;
3283 	}
3284 	*(uint16_t*)(b) = 4;
3285 	*(uint16_t*)(b+2) = groupperm;
3286 	*(uint32_t*)(b+4) = UINT32_C(0xFFFFFFFF);
3287 	b+=8;
3288 	for (i=0 ; i<namedgroups ; i++) {
3289 		*(uint32_t*)(b+4) = get32bit(&p);
3290 		*(uint16_t*)(b) = 8;
3291 		*(uint16_t*)(b+2) = get16bit(&p);
3292 		b+=8;
3293 	}
3294 	*(uint16_t*)(b) = 16;
3295 	*(uint16_t*)(b+2) = maskperm;
3296 	*(uint32_t*)(b+4) = UINT32_C(0xFFFFFFFF);
3297 	b+=8;
3298 	*(uint16_t*)(b) = 32;
3299 	*(uint16_t*)(b+2) = otherperm;
3300 	*(uint32_t*)(b+4) = UINT32_C(0xFFFFFFFF);
3301 	b+=8;
3302 //	fprintf(stderr,"getacl buff end ptr: %p\n",(void*)b);
3303 	return STATUS_OK;
3304 }
3305 
3306 int mfs_setacl(fuse_req_t req,fuse_ino_t ino,uint32_t uid,uint8_t aclxattr,const char *buff,uint32_t leng) {
3307 	uint16_t userperm;
3308 	uint16_t groupperm;
3309 	uint16_t otherperm;
3310 	uint16_t maskperm;
3311 	uint16_t namedusers;
3312 	uint16_t namedgroups;
3313 	uint16_t acls;
3314 	uint8_t *p,*namedacls;
3315 	uint32_t i;
3316 	uint16_t tag;
3317 
3318 	(void)req;
3319 	if (leng<4 || ((leng % 8) != 4) ) {
3320 		return ERROR_EINVAL;
3321 	}
3322 
3323 	if (buff[0]!=2) {
3324 		return ERROR_EINVAL;
3325 	}
3326 
3327 	acls = (leng - 4) / 8;
3328 	userperm = 0xFFFF; // means empty
3329 	groupperm = 0xFFFF; // means empty
3330 	otherperm = 0xFFFF; // means empty
3331 	maskperm = 0xFFFF; // means no mask
3332 	namedusers = 0;
3333 	namedgroups = 0;
3334 
3335 	for (i=0 ; i<acls ; i++) {
3336 		tag = *(const uint16_t*)(buff+4+i*8);
3337 		if (tag & 1) {
3338 			if (userperm!=0xFFFF) {
3339 				return ERROR_EINVAL;
3340 			}
3341 			userperm = *(const uint16_t*)(buff+6+i*8);
3342 		}
3343 		if (tag & 2) {
3344 			namedusers++;
3345 		}
3346 		if (tag & 4) {
3347 			if (groupperm!=0xFFFF) {
3348 				return ERROR_EINVAL;
3349 			}
3350 			groupperm = *(const uint16_t*)(buff+6+i*8);
3351 		}
3352 		if (tag & 8) {
3353 			namedgroups++;
3354 		}
3355 		if (tag & 16) {
3356 			if (maskperm!=0xFFFF) {
3357 				return ERROR_EINVAL;
3358 			}
3359 			maskperm = *(const uint16_t*)(buff+6+i*8);
3360 		}
3361 		if (tag & 32) {
3362 			if (otherperm!=0xFFFF) {
3363 				return ERROR_EINVAL;
3364 			}
3365 			otherperm = *(const uint16_t*)(buff+6+i*8);
3366 		}
3367 	}
3368 	if (maskperm==0xFFFF && (namedusers|namedgroups)>0) {
3369 		return ERROR_EINVAL;
3370 	}
3371 
3372 	namedacls = mfs_aclstorage_get((namedusers+namedgroups)*6);
3373 //	fprintf(stderr,"namedacls ptr: %p (size: %u)\n",(void*)namedacls,(namedusers+namedgroups)*6);
3374 	p = namedacls;
3375 	for (i=0 ; i<acls ; i++) {
3376 		tag = *(const uint16_t*)(buff+4+i*8);
3377 		if (tag & 2) {
3378 			put32bit(&p,*(const uint32_t*)(buff+8+i*8));
3379 			put16bit(&p,*(const uint16_t*)(buff+6+i*8));
3380 		}
3381 	}
3382 	for (i=0 ; i<acls ; i++) {
3383 		tag = *(const uint16_t*)(buff+4+i*8);
3384 		if (tag & 8) {
3385 			put32bit(&p,*(const uint32_t*)(buff+8+i*8));
3386 			put16bit(&p,*(const uint16_t*)(buff+6+i*8));
3387 		}
3388 	}
3389 //	fprintf(stderr,"namedacls end ptr: %p\n",(void*)p);
3390 	return fs_setacl(ino,uid,aclxattr,userperm,groupperm,otherperm,maskperm,namedusers,namedgroups,namedacls,(namedusers+namedgroups)*6);
3391 }
3392 
3393 #if defined(__APPLE__)
3394 void mfs_setxattr (fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags, uint32_t position) {
3395 #else
3396 void mfs_setxattr (fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags) {
3397 	uint32_t position=0;
3398 #endif
3399 	uint32_t nleng;
3400 	int status;
3401 	uint8_t mode;
3402 	struct fuse_ctx ctx;
3403 	groups *gids;
3404 	uint8_t aclxattr;
3405 
3406 	ctx = *(fuse_req_ctx(req));
3407 	mfs_stats_inc(OP_SETXATTR);
3408 	if (debug_mode) {
3409 		oplog_printf(&ctx,"setxattr (%lu,%s,%llu,%d) ...",(unsigned long int)ino,name,(unsigned long long int)size,flags);
3410 		fprintf(stderr,"setxattr (%lu,%s,%llu,%d)\n",(unsigned long int)ino,name,(unsigned long long int)size,flags);
3411 	}
3412 	if (IS_SPECIAL_INODE(ino)) {
3413 		oplog_printf(&ctx,"setxattr (%lu,%s,%llu,%d): %s",(unsigned long int)ino,name,(unsigned long long int)size,flags,strerr(EPERM));
3414 		fuse_reply_err(req,EPERM);
3415 		return;
3416 	}
3417 	if (size>MFS_XATTR_SIZE_MAX) {
3418 #if defined(__APPLE__)
3419 		// Mac OS X returns E2BIG here
3420 		oplog_printf(&ctx,"setxattr (%lu,%s,%llu,%d): %s",(unsigned long int)ino,name,(unsigned long long int)size,flags,strerr(E2BIG));
3421 		fuse_reply_err(req,E2BIG);
3422 #else
3423 		oplog_printf(&ctx,"setxattr (%lu,%s,%llu,%d): %s",(unsigned long int)ino,name,(unsigned long long int)size,flags,strerr(ERANGE));
3424 		fuse_reply_err(req,ERANGE);
3425 #endif
3426 		return;
3427 	}
3428 	nleng = strlen(name);
3429 	if (nleng>MFS_XATTR_NAME_MAX) {
3430 #if defined(__APPLE__)
3431 		// Mac OS X returns EPERM here
3432 		oplog_printf(&ctx,"setxattr (%lu,%s,%llu,%d): %s",(unsigned long int)ino,name,(unsigned long long int)size,flags,strerr(EPERM));
3433 		fuse_reply_err(req,EPERM);
3434 #else
3435 		oplog_printf(&ctx,"setxattr (%lu,%s,%llu,%d): %s",(unsigned long int)ino,name,(unsigned long long int)size,flags,strerr(ERANGE));
3436 		fuse_reply_err(req,ERANGE);
3437 #endif
3438 		return;
3439 	}
3440 	if (nleng==0) {
3441 		oplog_printf(&ctx,"setxattr (%lu,%s,%llu,%d): %s",(unsigned long int)ino,name,(unsigned long long int)size,flags,strerr(EINVAL));
3442 		fuse_reply_err(req,EINVAL);
3443 		return;
3444 	}
3445 #if defined(XATTR_CREATE) && defined(XATTR_REPLACE)
3446 	if ((flags&XATTR_CREATE) && (flags&XATTR_REPLACE)) {
3447 		oplog_printf(&ctx,"setxattr (%lu,%s,%llu,%d): %s",(unsigned long int)ino,name,(unsigned long long int)size,flags,strerr(EINVAL));
3448 		fuse_reply_err(req,EINVAL);
3449 		return;
3450 	}
3451 	mode = (flags==XATTR_CREATE)?MFS_XATTR_CREATE_ONLY:(flags==XATTR_REPLACE)?MFS_XATTR_REPLACE_ONLY:MFS_XATTR_CREATE_OR_REPLACE;
3452 #else
3453 	mode = 0;
3454 #endif
3455 	aclxattr = 0;
3456 	if (strcmp(name,"system.posix_acl_access")==0) {
3457 		aclxattr=1;
3458 	} else if (strcmp(name,"system.posix_acl_default")==0) {
3459 		aclxattr=2;
3460 	}
3461 	(void)position;
3462 	if (xattr_cache_on) {
3463 		xattr_cache_del(ino,nleng,(const uint8_t*)name);
3464 	}
3465 	if (aclxattr && xattr_acl_support==0) {
3466 		oplog_printf(&ctx,"setxattr (%lu,%s,%llu,%d): %s",(unsigned long int)ino,name,(unsigned long long int)size,flags,strerr(ENOTSUP));
3467 		fuse_reply_err(req,ENOTSUP);
3468 		return;
3469 	}
3470 	if (aclxattr) {
3471 		status = mfs_setacl(req,ino,ctx.uid,aclxattr,value,size);
3472 	} else {
3473 		if (full_permissions) {
3474 			gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
3475 			status = fs_setxattr(ino,0,ctx.uid,gids->gidcnt,gids->gidtab,nleng,(const uint8_t*)name,(uint32_t)size,(const uint8_t*)value,mode);
3476 			groups_rel(gids);
3477 		} else {
3478 			uint32_t gidtmp = ctx.gid;
3479 			status = fs_setxattr(ino,0,ctx.uid,1,&gidtmp,nleng,(const uint8_t*)name,(uint32_t)size,(const uint8_t*)value,mode);
3480 		}
3481 	}
3482 	status = mfs_errorconv(status);
3483 	if (status!=0) {
3484 		oplog_printf(&ctx,"setxattr (%lu,%s,%llu,%d): %s",(unsigned long int)ino,name,(unsigned long long int)size,flags,strerr(status));
3485 		fuse_reply_err(req,status);
3486 		return;
3487 	}
3488 	oplog_printf(&ctx,"setxattr (%lu,%s,%llu,%d): OK",(unsigned long int)ino,name,(unsigned long long int)size,flags);
3489 	fuse_reply_err(req,0);
3490 }
3491 
3492 #if defined(__APPLE__)
3493 void mfs_getxattr (fuse_req_t req, fuse_ino_t ino, const char *name, size_t size, uint32_t position) {
3494 #else
3495 void mfs_getxattr (fuse_req_t req, fuse_ino_t ino, const char *name, size_t size) {
3496 	uint32_t position=0;
3497 #endif /* __APPLE__ */
3498 	uint32_t nleng;
3499 	uint8_t attr[35];
3500 	int status;
3501 	uint8_t mode;
3502 	const uint8_t *buff;
3503 	uint32_t leng;
3504 	struct fuse_ctx ctx;
3505 	groups *gids;
3506 	void *xattr_value_release;
3507 	uint8_t aclxattr;
3508 
3509 	ctx = *(fuse_req_ctx(req));
3510 	mfs_stats_inc(OP_GETXATTR);
3511 	if (debug_mode) {
3512 		oplog_printf(&ctx,"getxattr (%lu,%s,%llu) ...",(unsigned long int)ino,name,(unsigned long long int)size);
3513 		fprintf(stderr,"getxattr (%lu,%s,%llu)\n",(unsigned long int)ino,name,(unsigned long long int)size);
3514 	}
3515 	if (IS_SPECIAL_INODE(ino)) {
3516 		oplog_printf(&ctx,"getxattr (%lu,%s,%llu): %s",(unsigned long int)ino,name,(unsigned long long int)size,strerr(EPERM));
3517 		fuse_reply_err(req,EPERM);
3518 		return;
3519 	}
3520 //	if (xattr_acl_support==0 && (strcmp(name,"system.posix_acl_default")==0 || strcmp(name,"system.posix_acl_access")==0)) {
3521 //		oplog_printf(&ctx,"getxattr (%lu,%s,%llu): %s",(unsigned long int)ino,name,(unsigned long long int)size,strerr(ENOTSUP));
3522 //		fuse_reply_err(req,ENOTSUP);
3523 //		return;
3524 //	}
3525 	nleng = strlen(name);
3526 	if (nleng>MFS_XATTR_NAME_MAX) {
3527 #if defined(__APPLE__)
3528 		// Mac OS X returns EPERM here
3529 		oplog_printf(&ctx,"getxattr (%lu,%s,%llu): %s",(unsigned long int)ino,name,(unsigned long long int)size,strerr(EPERM));
3530 		fuse_reply_err(req,EPERM);
3531 #else
3532 		oplog_printf(&ctx,"getxattr (%lu,%s,%llu): %s",(unsigned long int)ino,name,(unsigned long long int)size,strerr(ERANGE));
3533 		fuse_reply_err(req,ERANGE);
3534 #endif
3535 		return;
3536 	}
3537 	if (nleng==0) {
3538 		oplog_printf(&ctx,"getxattr (%lu,%s,%llu): %s",(unsigned long int)ino,name,(unsigned long long int)size,strerr(EINVAL));
3539 		fuse_reply_err(req,EINVAL);
3540 		return;
3541 	}
3542 	if (size==0) {
3543 		mode = MFS_XATTR_LENGTH_ONLY;
3544 	} else {
3545 		mode = MFS_XATTR_GETA_DATA;
3546 	}
3547 	aclxattr = 0;
3548 	if (strcmp(name,"system.posix_acl_access")==0) {
3549 		aclxattr=1;
3550 	} else if (strcmp(name,"system.posix_acl_default")==0) {
3551 		aclxattr=2;
3552 	}
3553 	if (aclxattr && xattr_acl_support==0) {
3554 		oplog_printf(&ctx,"getxattr (%lu,%s,%llu): %s",(unsigned long int)ino,name,(unsigned long long int)size,strerr(ENOTSUP));
3555 		fuse_reply_err(req,ENOTSUP);
3556 		return;
3557 	}
3558 	(void)position;
3559 	gids = NULL; // make gcc happy
3560 	if (full_permissions) {
3561 		if (strcmp(name,"com.apple.quarantine")==0) {
3562 			gids = groups_get_x(ctx.pid,ctx.uid,ctx.gid,1);
3563 		} else {
3564 			gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
3565 		}
3566 	}
3567 	xattr_value_release = NULL;
3568 	if (xattr_cache_on) {
3569 		xattr_value_release = xattr_cache_get(ino,ctx.uid,ctx.gid,nleng,(const uint8_t*)name,&buff,&leng,&status);
3570 		if (xattr_value_release==NULL) {
3571 			if (usedircache && dcache_getattr(&ctx,ino,attr) && (mfs_attr_get_mattr(attr)&MATTR_NOXATTR)) { // no xattr
3572 				status = ERROR_ENOATTR;
3573 				buff = NULL;
3574 				leng = 0;
3575 			} else {
3576 				if (aclxattr) {
3577 					if (full_permissions) {
3578 						status = mfs_getacl(req,ino,0,ctx.uid,gids->gidcnt,gids->gidtab,aclxattr,&buff,&leng);
3579 					} else {
3580 						uint32_t gidtmp = ctx.gid;
3581 						status = mfs_getacl(req,ino,0,ctx.uid,1,&gidtmp,aclxattr,&buff,&leng);
3582 					}
3583 				} else {
3584 					if (full_permissions) {
3585 						status = fs_getxattr(ino,0,ctx.uid,gids->gidcnt,gids->gidtab,nleng,(const uint8_t*)name,MFS_XATTR_GETA_DATA,&buff,&leng);
3586 					} else {
3587 						uint32_t gidtmp = ctx.gid;
3588 						status = fs_getxattr(ino,0,ctx.uid,1,&gidtmp,nleng,(const uint8_t*)name,MFS_XATTR_GETA_DATA,&buff,&leng);
3589 					}
3590 				}
3591 			}
3592 			xattr_cache_set(ino,ctx.uid,ctx.gid,nleng,(const uint8_t*)name,buff,leng,status);
3593 		} else if (debug_mode) {
3594 			fprintf(stderr,"getxattr: sending data from cache\n");
3595 		}
3596 	} else {
3597 		if (usedircache && dcache_getattr(&ctx,ino,attr) && (mfs_attr_get_mattr(attr)&MATTR_NOXATTR)) { // no xattr
3598 			status = ERROR_ENOATTR;
3599 			buff = NULL;
3600 			leng = 0;
3601 		} else {
3602 			if (aclxattr) {
3603 				if (full_permissions) {
3604 					status = mfs_getacl(req,ino,0,ctx.uid,gids->gidcnt,gids->gidtab,aclxattr,&buff,&leng);
3605 				} else {
3606 					uint32_t gidtmp = ctx.gid;
3607 					status = mfs_getacl(req,ino,0,ctx.uid,1,&gidtmp,aclxattr,&buff,&leng);
3608 				}
3609 			} else {
3610 				if (full_permissions) {
3611 					status = fs_getxattr(ino,0,ctx.uid,gids->gidcnt,gids->gidtab,nleng,(const uint8_t*)name,mode,&buff,&leng);
3612 				} else {
3613 					uint32_t gidtmp = ctx.gid;
3614 					status = fs_getxattr(ino,0,ctx.uid,1,&gidtmp,nleng,(const uint8_t*)name,mode,&buff,&leng);
3615 				}
3616 			}
3617 		}
3618 	}
3619 	if (full_permissions) {
3620 		groups_rel(gids);
3621 	}
3622 	status = mfs_errorconv(status);
3623 	if (status!=0) {
3624 		oplog_printf(&ctx,"getxattr (%lu,%s,%llu)%s: %s",(unsigned long int)ino,name,(unsigned long long int)size,(xattr_value_release==NULL)?"":" (using cache)",strerr(status));
3625 		fuse_reply_err(req,status);
3626 		if (xattr_value_release!=NULL) {
3627 			xattr_cache_rel(xattr_value_release);
3628 		}
3629 		return;
3630 	}
3631 	if (size==0) {
3632 		oplog_printf(&ctx,"getxattr (%lu,%s,%llu)%s: OK (%"PRIu32")",(unsigned long int)ino,name,(unsigned long long int)size,(xattr_value_release==NULL)?"":" (using cache)",leng);
3633 		fuse_reply_xattr(req,leng);
3634 	} else {
3635 		if (leng>size) {
3636 			oplog_printf(&ctx,"getxattr (%lu,%s,%llu)%s: %s",(unsigned long int)ino,name,(unsigned long long int)size,(xattr_value_release==NULL)?"":" (using cache)",strerr(ERANGE));
3637 			fuse_reply_err(req,ERANGE);
3638 		} else {
3639 			oplog_printf(&ctx,"getxattr (%lu,%s,%llu)%s: OK (%"PRIu32")",(unsigned long int)ino,name,(unsigned long long int)size,(xattr_value_release==NULL)?"":" (using cache)",leng);
3640 			fuse_reply_buf(req,(const char*)buff,leng);
3641 		}
3642 	}
3643 	if (xattr_value_release!=NULL) {
3644 		xattr_cache_rel(xattr_value_release);
3645 	}
3646 }
3647 
3648 void mfs_listxattr (fuse_req_t req, fuse_ino_t ino, size_t size) {
3649 	const uint8_t *buff;
3650 	uint32_t leng;
3651 	uint8_t attr[35];
3652 	int status;
3653 	uint8_t mode;
3654 	struct fuse_ctx ctx;
3655 	groups *gids;
3656 
3657 	ctx = *(fuse_req_ctx(req));
3658 	mfs_stats_inc(OP_LISTXATTR);
3659 	if (debug_mode) {
3660 		oplog_printf(&ctx,"listxattr (%lu,%llu) ...",(unsigned long int)ino,(unsigned long long int)size);
3661 		fprintf(stderr,"listxattr (%lu,%llu)\n",(unsigned long int)ino,(unsigned long long int)size);
3662 	}
3663 	if (IS_SPECIAL_INODE(ino)) {
3664 		oplog_printf(&ctx,"listxattr (%lu,%llu): %s",(unsigned long int)ino,(unsigned long long int)size,strerr(EPERM));
3665 		fuse_reply_err(req,EPERM);
3666 		return;
3667 	}
3668 	if (size==0) {
3669 		mode = MFS_XATTR_LENGTH_ONLY;
3670 	} else {
3671 		mode = MFS_XATTR_GETA_DATA;
3672 	}
3673 	if (usedircache && dcache_getattr(&ctx,ino,attr) && (mfs_attr_get_mattr(attr)&MATTR_NOXATTR)) { // no xattr
3674 		status = STATUS_OK;
3675 		buff = NULL;
3676 		leng = 0;
3677 	} else {
3678 		if (full_permissions) {
3679 			gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
3680 			status = fs_listxattr(ino,0,ctx.uid,gids->gidcnt,gids->gidtab,mode,&buff,&leng);
3681 			groups_rel(gids);
3682 		} else {
3683 			uint32_t gidtmp = ctx.gid;
3684 			status = fs_listxattr(ino,0,ctx.uid,1,&gidtmp,mode,&buff,&leng);
3685 		}
3686 	}
3687 	status = mfs_errorconv(status);
3688 	if (status!=0) {
3689 		oplog_printf(&ctx,"listxattr (%lu,%llu): %s",(unsigned long int)ino,(unsigned long long int)size,strerr(status));
3690 		fuse_reply_err(req,status);
3691 		return;
3692 	}
3693 	if (size==0) {
3694 		oplog_printf(&ctx,"listxattr (%lu,%llu): OK (%"PRIu32")",(unsigned long int)ino,(unsigned long long int)size,leng);
3695 		fuse_reply_xattr(req,leng);
3696 	} else {
3697 		if (leng>size) {
3698 			oplog_printf(&ctx,"listxattr (%lu,%llu): %s",(unsigned long int)ino,(unsigned long long int)size,strerr(ERANGE));
3699 			fuse_reply_err(req,ERANGE);
3700 		} else {
3701 			oplog_printf(&ctx,"listxattr (%lu,%llu): OK (%"PRIu32")",(unsigned long int)ino,(unsigned long long int)size,leng);
3702 			fuse_reply_buf(req,(const char*)buff,leng);
3703 		}
3704 	}
3705 }
3706 
3707 void mfs_removexattr (fuse_req_t req, fuse_ino_t ino, const char *name) {
3708 	uint32_t nleng;
3709 	int status;
3710 	struct fuse_ctx ctx;
3711 	groups *gids;
3712 	uint8_t aclxattr;
3713 
3714 	ctx = *(fuse_req_ctx(req));
3715 	mfs_stats_inc(OP_REMOVEXATTR);
3716 	if (debug_mode) {
3717 		oplog_printf(&ctx,"removexattr (%lu,%s) ...",(unsigned long int)ino,name);
3718 		fprintf(stderr,"removexattr (%lu,%s)\n",(unsigned long int)ino,name);
3719 	}
3720 	if (IS_SPECIAL_INODE(ino)) {
3721 		oplog_printf(&ctx,"removexattr (%lu,%s): %s",(unsigned long int)ino,name,strerr(EPERM));
3722 		fuse_reply_err(req,EPERM);
3723 		return;
3724 	}
3725 	aclxattr = 0;
3726 	if (strcmp(name,"system.posix_acl_access")==0) {
3727 		aclxattr=1;
3728 	} else if (strcmp(name,"system.posix_acl_default")==0) {
3729 		aclxattr=2;
3730 	}
3731 	if (aclxattr && xattr_acl_support==0) {
3732 		oplog_printf(&ctx,"removexattr (%lu,%s): %s",(unsigned long int)ino,name,strerr(ENOTSUP));
3733 		fuse_reply_err(req,ENOTSUP);
3734 		return;
3735 	}
3736 	nleng = strlen(name);
3737 	if (nleng>MFS_XATTR_NAME_MAX) {
3738 #if defined(__APPLE__)
3739 		// Mac OS X returns EPERM here
3740 		oplog_printf(&ctx,"removexattr (%lu,%s): %s",(unsigned long int)ino,name,strerr(EPERM));
3741 		fuse_reply_err(req,EPERM);
3742 #else
3743 		oplog_printf(&ctx,"removexattr (%lu,%s): %s",(unsigned long int)ino,name,strerr(ERANGE));
3744 		fuse_reply_err(req,ERANGE);
3745 #endif
3746 		return;
3747 	}
3748 	if (nleng==0) {
3749 		oplog_printf(&ctx,"removexattr (%lu,%s): %s",(unsigned long int)ino,name,strerr(EINVAL));
3750 		fuse_reply_err(req,EINVAL);
3751 		return;
3752 	}
3753 	if (xattr_cache_on) {
3754 		xattr_cache_del(ino,nleng,(const uint8_t*)name);
3755 	}
3756 	if (aclxattr) {
3757 		status = fs_setacl(ino,ctx.uid,aclxattr,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0,0,NULL,0);
3758 	} else {
3759 		if (full_permissions) {
3760 			gids = groups_get(ctx.pid,ctx.uid,ctx.gid);
3761 			status = fs_removexattr(ino,0,ctx.uid,gids->gidcnt,gids->gidtab,nleng,(const uint8_t*)name);
3762 			groups_rel(gids);
3763 		} else {
3764 			uint32_t gidtmp = ctx.gid;
3765 			status = fs_removexattr(ino,0,ctx.uid,1,&gidtmp,nleng,(const uint8_t*)name);
3766 		}
3767 	}
3768 	status = mfs_errorconv(status);
3769 	if (status!=0) {
3770 		oplog_printf(&ctx,"removexattr (%lu,%s): %s",(unsigned long int)ino,name,strerr(status));
3771 		fuse_reply_err(req,status);
3772 	} else {
3773 		oplog_printf(&ctx,"removexattr (%lu,%s): OK",(unsigned long int)ino,name);
3774 		fuse_reply_err(req,0);
3775 	}
3776 }
3777 
3778 void mfs_init(int debug_mode_in,int keep_cache_in,double direntry_cache_timeout_in,double entry_cache_timeout_in,double attr_cache_timeout_in,double xattr_cache_timeout_in,double groups_cache_timeout,int mkdir_copy_sgid_in,int sugid_clear_mode_in,int xattr_acl_support_in) {
3779 	const char* sugid_clear_mode_strings[] = {SUGID_CLEAR_MODE_STRINGS};
3780 	debug_mode = debug_mode_in;
3781 	keep_cache = keep_cache_in;
3782 	direntry_cache_timeout = direntry_cache_timeout_in;
3783 	entry_cache_timeout = entry_cache_timeout_in;
3784 	attr_cache_timeout = attr_cache_timeout_in;
3785 	mkdir_copy_sgid = mkdir_copy_sgid_in;
3786 	sugid_clear_mode = sugid_clear_mode_in;
3787 	xattr_cache_init(xattr_cache_timeout_in);
3788 	xattr_cache_on = (xattr_cache_timeout_in>0.0)?1:0;
3789 	xattr_acl_support = xattr_acl_support_in;
3790 	if (groups_cache_timeout>0.0) {
3791 		groups_init(groups_cache_timeout,debug_mode);
3792 		full_permissions = 1;
3793 	} else {
3794 		full_permissions = 0;
3795 	}
3796 	mfs_aclstorage_init();
3797 	if (debug_mode) {
3798 		fprintf(stderr,"cache parameters: file_keep_cache=%s direntry_cache_timeout=%.2lf entry_cache_timeout=%.2lf attr_cache_timeout=%.2lf xattr_cache_timeout_in=%.2lf (%s)\n",(keep_cache==1)?"always":(keep_cache==2)?"never":"auto",direntry_cache_timeout,entry_cache_timeout,attr_cache_timeout,xattr_cache_timeout_in,xattr_cache_on?"on":"off");
3799 		fprintf(stderr,"mkdir copy sgid=%d\nsugid clear mode=%s\n",mkdir_copy_sgid_in,(sugid_clear_mode_in<SUGID_CLEAR_MODE_OPTIONS)?sugid_clear_mode_strings[sugid_clear_mode_in]:"???");
3800 	}
3801 	mfs_statsptr_init();
3802 }
3803