1 /*
2  * Copyright (C) 2021 Jakub Kruszona-Zawadzki, Core Technology Sp. z o.o.
3  *
4  * This file is part of MooseFS.
5  *
6  * MooseFS is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, version 2 (only).
9  *
10  * MooseFS is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with MooseFS; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA
18  * or visit http://www.gnu.org/licenses/gpl-2.0.html
19  */
20 
21 // patch for old osxfuse
22 //#if defined(__APPLE__)
23 //# if ! defined(__DARWIN_64_BIT_INO_T) && ! defined(_DARWIN_USE_64_BIT_INODE)
24 //#  define __DARWIN_64_BIT_INO_T 0
25 //# endif
26 //#endif
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include "fusecommon.h"
33 
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <syslog.h>
38 #include <errno.h>
39 #include <time.h>
40 #include <pthread.h>
41 
42 #include "datapack.h"
43 #include "mastercomm.h"
44 #include "masterproxy.h"
45 #include "MFSCommunication.h"
46 
47 #define READDIR_BUFFSIZE 50000
48 
49 //typedef struct _minfo {
50 //	int sd;
51 //	int sent;
52 //} minfo;
53 typedef struct _dirbuf {
54 	int wasread;
55 	uint8_t *p;
56 	size_t size;
57 	pthread_mutex_t lock;
58 } dirbuf;
59 
60 typedef struct _pathbuf {
61 	int changed;
62 	char *p;
63 	size_t size;
64 	pthread_mutex_t lock;
65 } pathbuf;
66 
67 #define NAME_MAX 255
68 #define PATH_SIZE_LIMIT 1024
69 
70 #define META_ROOT_INODE FUSE_ROOT_ID
71 #define META_ROOT_MODE 0555
72 
73 #define META_SUBTRASH_INODE_MIN 0x7FFF0000
74 #define META_SUBTRASH_INODE_MAX ((0x7FFF0000+TRASH_BUCKETS)-1)
75 #define META_SUBTRASH_MODE 0700
76 
77 #define META_TRASH_INODE 0x7FFFFFF8
78 #define META_TRASH_MODE 0700
79 #define META_TRASH_NAME "trash"
80 #define META_UNDEL_INODE 0x7FFFFFF9
81 #define META_UNDEL_MODE 0200
82 #define META_UNDEL_NAME "undel"
83 #define META_SUSTAINED_INODE 0x7FFFFFFA
84 #define META_SUSTAINED_MODE 0500
85 #define META_SUSTAINED_NAME "sustained"
86 
87 //#define META_INODE_MIN META_ROOT_INODE
88 //#define META_INODE_MAX META_SUSTAINED_INODE
89 
90 //#define INODE_VALUE_MASK 0x1FFFFFFF
91 //#define INODE_TYPE_MASK 0x60000000
92 //#define INODE_TYPE_TRASH 0x20000000
93 //#define INODE_TYPE_SUSTAINED 0x40000000
94 //#define INODE_TYPE_SPECIAL 0x00000000
95 
96 // standard fs - inode(.master)=0x7FFFFFFF / inode(.masterinfo)=0x7FFFFFFE
97 // meta fs - inode(.master)=0x7FFFFFFE / inode(.masterinfo)=0x7FFFFFFF
98 //#define MASTER_NAME ".master"
99 //#define MASTER_INODE 0x7FFFFFFE
100 // 0x01b6 = 0666
101 //static uint8_t masterattr[ATTR_RECORD_SIZE]={'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};
102 
103 #define MASTERINFO_WITH_VERSION 1
104 
105 #define MASTERINFO_NAME ".masterinfo"
106 #define MASTERINFO_INODE 0x7FFFFFFF
107 // 0x0124 = 0444
108 #ifdef MASTERINFO_WITH_VERSION
109 static uint8_t masterinfoattr[ATTR_RECORD_SIZE]={'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};
110 #else
111 static uint8_t masterinfoattr[ATTR_RECORD_SIZE]={'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};
112 #endif
113 
114 #define MIN_SPECIAL_INODE 0x7FFF0000
115 #define IS_SPECIAL_INODE(ino) ((ino)>=MIN_SPECIAL_INODE || (ino)==META_ROOT_INODE)
116 
117 // info - todo
118 //#define META_INFO_INODE 0x7FFFFFFD
119 //#define META_INFO_NAME "info"
120 
121 #define PKGVERSION ((VERSMAJ)*1000000+(VERSMID)*1000+(VERSMIN))
122 
123 static int debug_mode = 0;
124 static int flat_trash = 0;
125 static double entry_cache_timeout = 0.0;
126 static double attr_cache_timeout = 1.0;
127 
mfs_meta_name_to_inode(const char * name)128 uint32_t mfs_meta_name_to_inode(const char *name) {
129 	uint32_t inode=0;
130 	char *end;
131 	inode = strtoul(name,&end,16);
132 	if (*end=='|' && end[1]!=0) {
133 		return inode;
134 	} else {
135 		return 0;
136 	}
137 }
138 
mfs_errorconv(int status)139 static int mfs_errorconv(int status) {
140 	switch (status) {
141 		case MFS_STATUS_OK:
142 			return 0;
143 		case MFS_ERROR_EPERM:
144 			return EPERM;
145 		case MFS_ERROR_ENOTDIR:
146 			return ENOTDIR;
147 		case MFS_ERROR_ENOENT:
148 			return ENOENT;
149 		case MFS_ERROR_EACCES:
150 			return EACCES;
151 		case MFS_ERROR_EEXIST:
152 			return EEXIST;
153 		case MFS_ERROR_EINVAL:
154 			return EINVAL;
155 		case MFS_ERROR_ENOTEMPTY:
156 			return ENOTEMPTY;
157 		case MFS_ERROR_IO:
158 			return EIO;
159 		case MFS_ERROR_EROFS:
160 			return EROFS;
161 		case MFS_ERROR_QUOTA:
162 #ifdef EDQUOT
163 			return EDQUOT;
164 #else
165 			return ENOSPC;
166 #endif
167 		default:
168 			return EINVAL;
169 	}
170 }
171 
fsnodes_type_convert(uint8_t type)172 static inline uint8_t fsnodes_type_convert(uint8_t type) {
173 	switch (type) {
174 		case DISP_TYPE_FILE:
175 			return TYPE_FILE;
176 		case DISP_TYPE_DIRECTORY:
177 			return TYPE_DIRECTORY;
178 		case DISP_TYPE_SYMLINK:
179 			return TYPE_SYMLINK;
180 		case DISP_TYPE_FIFO:
181 			return TYPE_FIFO;
182 		case DISP_TYPE_BLOCKDEV:
183 			return TYPE_BLOCKDEV;
184 		case DISP_TYPE_CHARDEV:
185 			return TYPE_CHARDEV;
186 		case DISP_TYPE_SOCKET:
187 			return TYPE_SOCKET;
188 		case DISP_TYPE_TRASH:
189 			return TYPE_TRASH;
190 		case DISP_TYPE_SUSTAINED:
191 			return TYPE_SUSTAINED;
192 	}
193 	return 0;
194 }
195 
mfs_meta_type_to_stat(uint32_t inode,uint8_t type,struct stat * stbuf)196 static void mfs_meta_type_to_stat(uint32_t inode,uint8_t type, struct stat *stbuf) {
197 	memset(stbuf,0,sizeof(struct stat));
198 	stbuf->st_ino = inode;
199 	switch (type&0x7F) {
200 	case DISP_TYPE_DIRECTORY:
201 	case TYPE_DIRECTORY:
202 		stbuf->st_mode = S_IFDIR;
203 		break;
204 	case DISP_TYPE_SYMLINK:
205 	case TYPE_SYMLINK:
206 		stbuf->st_mode = S_IFLNK;
207 		break;
208 	case DISP_TYPE_FILE:
209 	case TYPE_FILE:
210 		stbuf->st_mode = S_IFREG;
211 		break;
212 	case DISP_TYPE_FIFO:
213 	case TYPE_FIFO:
214 		stbuf->st_mode = S_IFIFO;
215 		break;
216 	case DISP_TYPE_SOCKET:
217 	case TYPE_SOCKET:
218 		stbuf->st_mode = S_IFSOCK;
219 		break;
220 	case DISP_TYPE_BLOCKDEV:
221 	case TYPE_BLOCKDEV:
222 		stbuf->st_mode = S_IFBLK;
223 		break;
224 	case DISP_TYPE_CHARDEV:
225 	case TYPE_CHARDEV:
226 		stbuf->st_mode = S_IFCHR;
227 		break;
228 	default:
229 		stbuf->st_mode = 0;
230 	}
231 }
232 
233 
mfs_meta_stat(uint32_t inode,struct stat * stbuf)234 static void mfs_meta_stat(uint32_t inode, struct stat *stbuf) {
235 	int now;
236 	stbuf->st_ino = inode;
237 	stbuf->st_size = 0;
238 //#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
239 //	stbuf->st_blocks = 0;
240 //#endif
241 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
242 	stbuf->st_blksize = MFSBLOCKSIZE;
243 #endif
244 	switch (inode) {
245 	case META_ROOT_INODE:
246 		stbuf->st_nlink = 4;
247 		stbuf->st_mode = S_IFDIR | META_ROOT_MODE ;
248 		break;
249 	case META_TRASH_INODE:
250 		stbuf->st_nlink = 3+TRASH_BUCKETS;
251 		stbuf->st_mode = S_IFDIR | META_TRASH_MODE ;
252 		break;
253 	case META_UNDEL_INODE:
254 		stbuf->st_nlink = 2+TRASH_BUCKETS;
255 		stbuf->st_mode = S_IFDIR | META_UNDEL_MODE ;
256 		break;
257 	case META_SUSTAINED_INODE:
258 		stbuf->st_nlink = 2;
259 		stbuf->st_mode = S_IFDIR | META_SUSTAINED_MODE ;
260 		break;
261 	default:
262 		if (inode>=META_SUBTRASH_INODE_MIN && inode<=META_SUBTRASH_INODE_MAX) {
263 			stbuf->st_nlink = 3;
264 			stbuf->st_mode = S_IFDIR | META_SUBTRASH_MODE ;
265 		}
266 	}
267 	stbuf->st_uid = 0;
268 	stbuf->st_gid = 0;
269 	now = time(NULL);
270 	stbuf->st_atime = now;
271 	stbuf->st_mtime = now;
272 	stbuf->st_ctime = now;
273 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
274 	stbuf->st_birthtime = now;	// for future use
275 #endif
276 }
277 
278 /*
279 static void mfs_inode_to_stat(uint32_t inode, struct stat *stbuf) {
280 	memset(stbuf,0,sizeof(struct stat));
281 	stbuf->st_ino = inode;
282 	stbuf->st_mode = S_IFREG;
283 }
284 */
285 
mfs_attr_to_stat(uint32_t inode,const uint8_t attr[ATTR_RECORD_SIZE],struct stat * stbuf)286 static void mfs_attr_to_stat(uint32_t inode,const uint8_t attr[ATTR_RECORD_SIZE], struct stat *stbuf) {
287 	uint16_t attrmode;
288 	uint8_t attrtype;
289 	uint32_t attruid,attrgid,attratime,attrmtime,attrctime,attrnlink;
290 	uint64_t attrlength;
291 	const uint8_t *ptr;
292 	ptr = attr;
293 	if (attr[0]<64) { // 1.7.29 and up
294 		ptr++;
295 		attrmode = get16bit(&ptr);
296 		attrtype = (attrmode>>12);
297 	} else {
298 		attrtype = get8bit(&ptr);
299 		attrtype = fsnodes_type_convert(attrtype&0x7F);
300 		attrmode = get16bit(&ptr);
301 	}
302 	attrmode &= 0x0FFF;
303 	attruid = get32bit(&ptr);
304 	attrgid = get32bit(&ptr);
305 	attratime = get32bit(&ptr);
306 	attrmtime = get32bit(&ptr);
307 	attrctime = get32bit(&ptr);
308 	attrnlink = get32bit(&ptr);
309 	attrlength = get64bit(&ptr);
310 	stbuf->st_ino = inode;
311 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
312 	stbuf->st_blksize = MFSBLOCKSIZE;
313 #endif
314 	if (attrtype==TYPE_FILE || attrtype==TYPE_TRASH || attrtype==TYPE_SUSTAINED) {
315 		stbuf->st_mode = S_IFREG | ( attrmode & 07777);
316 	} else {
317 		stbuf->st_mode = 0;
318 	}
319 	stbuf->st_size = attrlength;
320 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
321 	stbuf->st_blocks = (attrlength+511)/512;
322 #endif
323 	stbuf->st_uid = attruid;
324 	stbuf->st_gid = attrgid;
325 	stbuf->st_atime = attratime;
326 	stbuf->st_mtime = attrmtime;
327 	stbuf->st_ctime = attrctime;
328 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
329 	stbuf->st_birthtime = attrctime;	// for future use
330 #endif
331 	stbuf->st_nlink = attrnlink;
332 }
333 
334 #if FUSE_USE_VERSION >= 26
mfs_meta_statfs(fuse_req_t req,fuse_ino_t ino)335 void mfs_meta_statfs(fuse_req_t req, fuse_ino_t ino) {
336 #else
337 void mfs_meta_statfs(fuse_req_t req) {
338 #endif
339 	uint64_t totalspace,availspace,freespace,trashspace,sustainedspace;
340 	uint32_t inodes;
341 	struct statvfs stfsbuf;
342 	memset(&stfsbuf,0,sizeof(stfsbuf));
343 
344 #if FUSE_USE_VERSION >= 26
345 	(void)ino;
346 #endif
347 	fs_statfs(&totalspace,&availspace,&freespace,&trashspace,&sustainedspace,&inodes);
348 
349 	stfsbuf.f_namemax = NAME_MAX;
350 	stfsbuf.f_frsize = MFSBLOCKSIZE;
351 	stfsbuf.f_bsize = MFSBLOCKSIZE;
352 	stfsbuf.f_blocks = trashspace/MFSBLOCKSIZE+sustainedspace/MFSBLOCKSIZE;
353 	stfsbuf.f_bfree = sustainedspace/MFSBLOCKSIZE;
354 	stfsbuf.f_bavail = sustainedspace/MFSBLOCKSIZE;
355 	stfsbuf.f_files = 1000000000+PKGVERSION;
356 	stfsbuf.f_ffree = 1000000000+PKGVERSION;
357 	stfsbuf.f_favail = 1000000000+PKGVERSION;
358 
359 	fuse_reply_statfs(req,&stfsbuf);
360 }
361 
362 /*
363 void mfs_meta_access(fuse_req_t req, fuse_ino_t ino, int mask) {
364 	const struct fuse_ctx *ctx;
365 	ctx = fuse_req_ctx(req);
366 	switch (ino) {
367 		case FUSE_ROOT_ID:
368 			if (mask & W_OK) {
369 				fuse_reply_err(req,EACCES);
370 				return;
371 			}
372 			break;
373 		case META_TRASH_INODE:
374 			if (mask & W_OK && ctx->uid!=0) {
375 				fuse_reply_err(req,EACCES);
376 				return;
377 			}
378 			break;
379 		case META_UNDEL_INODE:
380 			if (mask & (R_OK|X_OK)) {
381 				fuse_reply_err(req,EACCES);
382 				return;
383 			}
384 			break;
385 	}
386 	fuse_reply_err(req,0);
387 }
388 */
389 
390 void mfs_meta_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) {
391 	struct fuse_entry_param e;
392 	uint32_t inode;
393 //	const struct fuse_ctx *ctx;
394 //	ctx = fuse_req_ctx(req);
395 	memset(&e, 0, sizeof(e));
396 	inode = 0;
397 	switch (parent) {
398 	case META_ROOT_INODE:
399 		if (strcmp(name,".")==0 || strcmp(name,"..")==0) {
400 			inode = META_ROOT_INODE;
401 		} else if (strcmp(name,META_TRASH_NAME)==0) {
402 			inode = META_TRASH_INODE;
403 		} else if (strcmp(name,META_SUSTAINED_NAME)==0) {
404 			inode = META_SUSTAINED_INODE;
405 //		} else if (strcmp(name,MASTER_NAME)==0) {
406 //			memset(&e, 0, sizeof(e));
407 //			e.ino = MASTER_INODE;
408 //			e.attr_timeout = 3600.0;
409 //			e.entry_timeout = 3600.0;
410 //			mfs_attr_to_stat(MASTER_INODE,masterattr,&e.attr);
411 //			fuse_reply_entry(req, &e);
412 //			return ;
413 		} else if (strcmp(name,MASTERINFO_NAME)==0) {
414 			memset(&e, 0, sizeof(e));
415 			e.ino = MASTERINFO_INODE;
416 			e.attr_timeout = 3600.0;
417 			e.entry_timeout = 3600.0;
418 			mfs_attr_to_stat(MASTERINFO_INODE,masterinfoattr,&e.attr);
419 			fuse_reply_entry(req, &e);
420 			return ;
421 		}
422 		break;
423 	case META_TRASH_INODE:
424 		if (strcmp(name,".")==0) {
425 			inode = META_TRASH_INODE;
426 		} else if (strcmp(name,"..")==0) {
427 			inode = META_ROOT_INODE;
428 		} else if (strcmp(name,META_UNDEL_NAME)==0) {
429 			inode = META_UNDEL_INODE;
430 		} else if (master_version()>=VERSION2INT(3,0,64) && flat_trash==0) { // subtrashes
431 			inode = strtoul(name,NULL,16);
432 			if (inode<TRASH_BUCKETS) {
433 				inode += META_SUBTRASH_INODE_MIN;
434 			} else {
435 				inode = 0;
436 			}
437 		} else { // flat trash
438 			inode = mfs_meta_name_to_inode(name);
439 			if (inode>0) {
440 				int status;
441 				uint8_t attr[ATTR_RECORD_SIZE];
442 				status = fs_getdetachedattr(inode,attr);
443 				status = mfs_errorconv(status);
444 				if (status!=0) {
445 					fuse_reply_err(req, status);
446 				} else {
447 					e.ino = inode;
448 					e.attr_timeout = attr_cache_timeout;
449 					e.entry_timeout = entry_cache_timeout;
450 					mfs_attr_to_stat(inode,attr,&e.attr);
451 					fuse_reply_entry(req,&e);
452 				}
453 				return;
454 			}
455 		}
456 		break;
457 	case META_UNDEL_INODE:
458 		if (strcmp(name,".")==0) {
459 			inode = META_UNDEL_INODE;
460 		} else if (strcmp(name,"..")==0) {
461 			inode = META_TRASH_INODE;
462 		}
463 		break;
464 	case META_SUSTAINED_INODE:
465 		if (strcmp(name,".")==0) {
466 			inode = META_SUSTAINED_INODE;
467 		} else if (strcmp(name,"..")==0) {
468 			inode = META_ROOT_INODE;
469 		} else {
470 			inode = mfs_meta_name_to_inode(name);
471 			if (inode>0) {
472 				int status;
473 				uint8_t attr[ATTR_RECORD_SIZE];
474 				status = fs_getdetachedattr(inode,attr);
475 				status = mfs_errorconv(status);
476 				if (status!=0) {
477 					fuse_reply_err(req, status);
478 				} else {
479 					e.ino = inode;
480 					e.attr_timeout = attr_cache_timeout;
481 					e.entry_timeout = entry_cache_timeout;
482 					mfs_attr_to_stat(inode,attr,&e.attr);
483 					fuse_reply_entry(req,&e);
484 				}
485 				return;
486 			}
487 		}
488 		break;
489 	default:
490 		if (parent>=META_SUBTRASH_INODE_MIN && parent<=META_SUBTRASH_INODE_MAX) {
491 			if (strcmp(name,".")==0) {
492 				inode = parent;
493 			} else if (strcmp(name,"..")==0) {
494 				inode = META_TRASH_INODE;
495 			} else if (strcmp(name,META_UNDEL_NAME)==0) {
496 				inode = META_UNDEL_INODE;
497 			} else {
498 				inode = mfs_meta_name_to_inode(name);
499 				if (inode>0) {
500 					int status;
501 					uint8_t attr[ATTR_RECORD_SIZE];
502 					status = fs_getdetachedattr(inode,attr);
503 					status = mfs_errorconv(status);
504 					if (status!=0) {
505 						fuse_reply_err(req, status);
506 					} else {
507 						e.ino = inode;
508 						e.attr_timeout = attr_cache_timeout;
509 						e.entry_timeout = entry_cache_timeout;
510 						mfs_attr_to_stat(inode,attr,&e.attr);
511 						fuse_reply_entry(req,&e);
512 					}
513 					return;
514 				}
515 			}
516 		}
517 	}
518 	if (inode==0) {
519 		fuse_reply_err(req,ENOENT);
520 	} else {
521 		e.ino = inode;
522 		e.attr_timeout = attr_cache_timeout;
523 		e.entry_timeout = entry_cache_timeout;
524 		mfs_meta_stat(inode,&e.attr);
525 		fuse_reply_entry(req,&e);
526 	}
527 }
528 
529 void mfs_meta_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
530 	struct stat o_stbuf;
531 	(void)fi;
532 //	if (ino==MASTER_INODE) {
533 //		memset(&o_stbuf, 0, sizeof(struct stat));
534 //		mfs_attr_to_stat(ino,masterattr,&o_stbuf);
535 //		fuse_reply_attr(req, &o_stbuf, 3600.0);
536 //	} else
537 	if (ino==MASTERINFO_INODE) {
538 		memset(&o_stbuf, 0, sizeof(struct stat));
539 		mfs_attr_to_stat(ino,masterinfoattr,&o_stbuf);
540 		fuse_reply_attr(req, &o_stbuf, 3600.0);
541 	} else if (IS_SPECIAL_INODE(ino)) {
542 		memset(&o_stbuf, 0, sizeof(struct stat));
543 		mfs_meta_stat(ino,&o_stbuf);
544 		fuse_reply_attr(req, &o_stbuf, attr_cache_timeout);
545 	} else {
546 		int status;
547 		uint8_t attr[ATTR_RECORD_SIZE];
548 		status = fs_getdetachedattr(ino,attr);
549 		status = mfs_errorconv(status);
550 		if (status!=0) {
551 			fuse_reply_err(req, status);
552 		} else {
553 			memset(&o_stbuf, 0, sizeof(struct stat));
554 			mfs_attr_to_stat(ino,attr,&o_stbuf);
555 			fuse_reply_attr(req, &o_stbuf, attr_cache_timeout);
556 		}
557 	}
558 }
559 
560 void mfs_meta_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *stbuf, int to_set, struct fuse_file_info *fi) {
561 	(void)to_set;
562 	(void)stbuf;
563 	mfs_meta_getattr(req,ino,fi);
564 }
565 
566 void mfs_meta_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) {
567 	int status;
568 	uint32_t inode;
569 	if (!(parent==META_TRASH_INODE || (parent>=META_SUBTRASH_INODE_MIN && parent<=META_SUBTRASH_INODE_MAX))) {
570 		fuse_reply_err(req,EACCES);
571 		return;
572 	}
573 	inode = mfs_meta_name_to_inode(name);
574 	if (inode==0) {
575 		fuse_reply_err(req,ENOENT);
576 		return;
577 	}
578 	status = fs_purge(inode);
579 	status = mfs_errorconv(status);
580 //	if (status!=0) {
581 	fuse_reply_err(req, status);
582 //	} else {
583 //		fuse_reply_err(req,0);
584 //	}
585 }
586 
587 #if FUSE_VERSION >= 30
588 void mfs_meta_rename(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname,unsigned int flags) {
589 #else
590 void mfs_meta_rename(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname) {
591 #endif
592 	int status;
593 	uint32_t inode;
594 	(void)newname;
595 #if FUSE_VERSION >= 30
596 	(void)flags;
597 #endif
598 	if ((!(parent==META_TRASH_INODE || (parent>=META_SUBTRASH_INODE_MIN && parent<=META_SUBTRASH_INODE_MAX))) && newparent!=META_UNDEL_INODE) {
599 		fuse_reply_err(req,EACCES);
600 		return;
601 	}
602 	inode = mfs_meta_name_to_inode(name);
603 	if (inode==0) {
604 		fuse_reply_err(req,ENOENT);
605 		return;
606 	}
607 	status = fs_undel(inode);
608 	status = mfs_errorconv(status);
609 //	if (status!=0) {
610 	fuse_reply_err(req, status);
611 //	} else {
612 //		fuse_reply_err(req,0);
613 //	}
614 }
615 
616 /*
617 static void dirbuf_add(dirbuf *b, const char *name, fuse_ino_t ino, uint8_t attr[32]) {
618 	struct stat stbuf;
619 	size_t oldsize = b->size;
620 	b->size += fuse_dirent_size(strlen(name));
621 	b->p = (char *) realloc(b->p, b->size);
622 	mfs_attr_to_stat(ino,attr,&stbuf);
623 	fuse_add_dirent(b->p + oldsize, name, &stbuf, b->size);
624 }
625 
626 static void dirbuf_meta_add(dirbuf *b, const char *name, fuse_ino_t ino) {
627 	struct stat stbuf;
628 	size_t oldsize = b->size;
629 	b->size += fuse_dirent_size(strlen(name));
630 	b->p = (char *) realloc(b->p, b->size);
631 	memset(&stbuf,0,sizeof(struct stat));
632 	mfs_meta_stat(ino,&stbuf);
633 	fuse_add_dirent(b->p + oldsize, name, &stbuf, b->size);
634 }
635 */
636 
637 static uint32_t dir_metaentries_size(uint32_t ino) {
638 	switch (ino) {
639 	case META_ROOT_INODE:
640 		return 4*6+1+2+strlen(META_TRASH_NAME)+strlen(META_SUSTAINED_NAME);
641 	case META_TRASH_INODE:
642 		if (master_version()>=VERSION2INT(3,0,64) && flat_trash==0) {
643 			return (3+TRASH_BUCKETS)*6+1+2+strlen(META_UNDEL_NAME)+(TRASH_BUCKETS*((TRASH_BUCKETS<=4096)?3:4));
644 		} else {
645 			return 3*6+1+2+strlen(META_UNDEL_NAME);
646 		}
647 	case META_UNDEL_INODE:
648 		return 2*6+1+2;
649 	case META_SUSTAINED_INODE:
650 		return 2*6+1+2;
651 	default:
652 		if (ino>=META_SUBTRASH_INODE_MIN && ino<=META_SUBTRASH_INODE_MAX) {
653 			return 3*6+1+2+strlen(META_UNDEL_NAME);
654 		}
655 	}
656 	return 0;
657 }
658 
659 static void dir_metaentries_fill(uint8_t *buff,uint32_t ino) {
660 	uint8_t l;
661 	switch (ino) {
662 	case META_ROOT_INODE:
663 		// .
664 		put8bit(&buff,1);
665 		put8bit(&buff,'.');
666 		put32bit(&buff,META_ROOT_INODE);
667 		put8bit(&buff,TYPE_DIRECTORY);
668 		// ..
669 		put8bit(&buff,2);
670 		put8bit(&buff,'.');
671 		put8bit(&buff,'.');
672 		put32bit(&buff,META_ROOT_INODE);
673 		put8bit(&buff,TYPE_DIRECTORY);
674 		// trash
675 		l = strlen(META_TRASH_NAME);
676 		put8bit(&buff,l);
677 		memcpy(buff,META_TRASH_NAME,l);
678 		buff+=l;
679 		put32bit(&buff,META_TRASH_INODE);
680 		put8bit(&buff,TYPE_DIRECTORY);
681 		// sustained
682 		l = strlen(META_SUSTAINED_NAME);
683 		put8bit(&buff,l);
684 		memcpy(buff,META_SUSTAINED_NAME,l);
685 		buff+=l;
686 		put32bit(&buff,META_SUSTAINED_INODE);
687 		put8bit(&buff,TYPE_DIRECTORY);
688 		return;
689 	case META_TRASH_INODE:
690 		// .
691 		put8bit(&buff,1);
692 		put8bit(&buff,'.');
693 		put32bit(&buff,META_TRASH_INODE);
694 		put8bit(&buff,TYPE_DIRECTORY);
695 		// ..
696 		put8bit(&buff,2);
697 		put8bit(&buff,'.');
698 		put8bit(&buff,'.');
699 		put32bit(&buff,META_ROOT_INODE);
700 		put8bit(&buff,TYPE_DIRECTORY);
701 		// undel
702 		l = strlen(META_UNDEL_NAME);
703 		put8bit(&buff,l);
704 		memcpy(buff,META_UNDEL_NAME,l);
705 		buff+=l;
706 		put32bit(&buff,META_UNDEL_INODE);
707 		put8bit(&buff,TYPE_DIRECTORY);
708 		if (master_version()>=VERSION2INT(3,0,64) && flat_trash==0) {
709 			uint32_t tid;
710 			for (tid=0 ; tid<TRASH_BUCKETS ; tid++) {
711 				if (TRASH_BUCKETS>4096) {
712 					put8bit(&buff,4);
713 					put8bit(&buff,"0123456789ABCDEF"[(tid>>12)&15]);
714 				} else {
715 					put8bit(&buff,3);
716 				}
717 				put8bit(&buff,"0123456789ABCDEF"[(tid>>8)&15]);
718 				put8bit(&buff,"0123456789ABCDEF"[(tid>>4)&15]);
719 				put8bit(&buff,"0123456789ABCDEF"[tid&15]);
720 				put32bit(&buff,tid+META_SUBTRASH_INODE_MIN);
721 				put8bit(&buff,TYPE_DIRECTORY);
722 			}
723 		}
724 		return;
725 	case META_UNDEL_INODE:
726 		// .
727 		put8bit(&buff,1);
728 		put8bit(&buff,'.');
729 		put32bit(&buff,META_UNDEL_INODE);
730 		put8bit(&buff,TYPE_DIRECTORY);
731 		// ..
732 		put8bit(&buff,2);
733 		put8bit(&buff,'.');
734 		put8bit(&buff,'.');
735 		put32bit(&buff,META_TRASH_INODE);
736 		put8bit(&buff,TYPE_DIRECTORY);
737 		return;
738 	case META_SUSTAINED_INODE:
739 		// .
740 		put8bit(&buff,1);
741 		put8bit(&buff,'.');
742 		put32bit(&buff,META_SUSTAINED_INODE);
743 		put8bit(&buff,TYPE_DIRECTORY);
744 		// ..
745 		put8bit(&buff,2);
746 		put8bit(&buff,'.');
747 		put8bit(&buff,'.');
748 		put32bit(&buff,META_ROOT_INODE);
749 		put8bit(&buff,TYPE_DIRECTORY);
750 		return;
751 	default:
752 		if (ino>=META_SUBTRASH_INODE_MIN && ino<=META_SUBTRASH_INODE_MAX) {
753 			// .
754 			put8bit(&buff,1);
755 			put8bit(&buff,'.');
756 			put32bit(&buff,META_TRASH_INODE);
757 			put8bit(&buff,TYPE_DIRECTORY);
758 			// ..
759 			put8bit(&buff,2);
760 			put8bit(&buff,'.');
761 			put8bit(&buff,'.');
762 			put32bit(&buff,META_ROOT_INODE);
763 			put8bit(&buff,TYPE_DIRECTORY);
764 			// undel
765 			l = strlen(META_UNDEL_NAME);
766 			put8bit(&buff,l);
767 			memcpy(buff,META_UNDEL_NAME,l);
768 			buff+=l;
769 			put32bit(&buff,META_UNDEL_INODE);
770 			put8bit(&buff,TYPE_DIRECTORY);
771 			return;
772 		}
773 	}
774 }
775 
776 static uint32_t dir_dataentries_size(const uint8_t *dbuff,uint32_t dsize) {
777 	uint8_t nleng;
778 	uint32_t eleng;
779 	const uint8_t *eptr;
780 	eleng=0;
781 	if (dbuff==NULL || dsize==0) {
782 		return 0;
783 	}
784 	eptr = dbuff+dsize;
785 	while (dbuff<eptr) {
786 		nleng = dbuff[0];
787 		dbuff+=5+nleng;
788 		if (nleng>255-9) {
789 			eleng+=6+255;
790 		} else {
791 			eleng+=6+nleng+9;
792 		}
793 	}
794 	return eleng;
795 }
796 
797 static void dir_dataentries_convert(uint8_t *buff,const uint8_t *dbuff,uint32_t dsize) {
798 	const char *name;
799 	uint32_t inode;
800 	uint8_t nleng;
801 	uint8_t inoleng;
802 	const uint8_t *eptr;
803 	eptr = dbuff+dsize;
804 	while (dbuff<eptr) {
805 		nleng = dbuff[0];
806 		if (dbuff+nleng+5<=eptr) {
807 			dbuff++;
808 			if (nleng>255-9) {
809 				inoleng = 255;
810 			} else {
811 				inoleng = nleng+9;
812 			}
813 			put8bit(&buff,inoleng);
814 			name = (const char*)dbuff;
815 			dbuff+=nleng;
816 			inode = get32bit(&dbuff);
817 			sprintf((char*)buff,"%08"PRIX32"|",inode);
818 			if (nleng>255-9) {
819 				memcpy(buff+9,name,255-9);
820 				buff+=255;
821 			} else {
822 				memcpy(buff+9,name,nleng);
823 				buff+=9+nleng;
824 			}
825 			put32bit(&buff,inode);
826 			put8bit(&buff,TYPE_FILE);
827 		} else {
828 			syslog(LOG_WARNING,"dir data malformed (trash)");
829 			dbuff=eptr;
830 		}
831 	}
832 }
833 
834 
835 static void dirbuf_meta_fill(dirbuf *b, uint32_t ino) {
836 	int status;
837 	uint32_t msize,dcsize;
838 	const uint8_t *dbuff;
839 	uint32_t dsize;
840 
841 	b->p = NULL;
842 	b->size = 0;
843 	msize = dir_metaentries_size(ino);
844 	if (ino==META_TRASH_INODE && (master_version()<VERSION2INT(3,0,64) || flat_trash)) {
845 		status = fs_gettrash(0xFFFFFFFF,&dbuff,&dsize);
846 		if (status!=MFS_STATUS_OK) {
847 			return;
848 		}
849 		dcsize = dir_dataentries_size(dbuff,dsize);
850 	} else if (ino==META_SUSTAINED_INODE) {
851 		status = fs_getsustained(&dbuff,&dsize);
852 		if (status!=MFS_STATUS_OK) {
853 			return;
854 		}
855 		dcsize = dir_dataentries_size(dbuff,dsize);
856 	} else if (ino>=META_SUBTRASH_INODE_MIN && ino<=META_SUBTRASH_INODE_MAX && master_version()>=VERSION2INT(3,0,64) && flat_trash==0) {
857 		status = fs_gettrash(ino-META_SUBTRASH_INODE_MIN,&dbuff,&dsize);
858 		if (status!=MFS_STATUS_OK) {
859 			return;
860 		}
861 		dcsize = dir_dataentries_size(dbuff,dsize);
862 	} else {
863 		dcsize = 0;
864 	}
865 	if (msize+dcsize==0) {
866 		return;
867 	}
868 	b->p = malloc(msize+dcsize);
869 	if (b->p==NULL) {
870 		syslog(LOG_WARNING,"out of memory");
871 		return;
872 	}
873 	if (msize>0) {
874 		dir_metaentries_fill(b->p,ino);
875 	}
876 	if (dcsize>0) {
877 		dir_dataentries_convert(b->p+msize,dbuff,dsize);
878 	}
879 	b->size = msize+dcsize;
880 }
881 
882 void mfs_meta_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
883 	dirbuf *dirinfo;
884 	if (ino==META_ROOT_INODE || ino==META_TRASH_INODE || ino==META_UNDEL_INODE || ino==META_SUSTAINED_INODE || (ino>=META_SUBTRASH_INODE_MIN && ino<=META_SUBTRASH_INODE_MAX)) {
885 		dirinfo = malloc(sizeof(dirbuf));
886 		pthread_mutex_init(&(dirinfo->lock),NULL);
887 		dirinfo->p = NULL;
888 		dirinfo->size = 0;
889 		dirinfo->wasread = 0;
890 		fi->fh = (unsigned long)dirinfo;
891 		if (fuse_reply_open(req,fi) == -ENOENT) {
892 			fi->fh = 0;
893 			pthread_mutex_destroy(&(dirinfo->lock));
894 			free(dirinfo->p);
895 			free(dirinfo);
896 		}
897 	} else {
898 		fuse_reply_err(req, ENOTDIR);
899 	}
900 }
901 
902 void mfs_meta_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
903 	dirbuf *dirinfo = (dirbuf *)((unsigned long)(fi->fh));
904 	char buffer[READDIR_BUFFSIZE];
905 	char *name,c;
906 	const uint8_t *ptr,*eptr;
907 	uint8_t end;
908 	size_t opos,oleng;
909 	uint8_t nleng;
910 	uint32_t inode;
911 	uint8_t type;
912 	struct stat stbuf;
913 
914 	if (off<0) {
915 		fuse_reply_err(req,EINVAL);
916 		return;
917 	}
918 	pthread_mutex_lock(&(dirinfo->lock));
919 	if (dirinfo->wasread==0 || (dirinfo->wasread==1 && off==0)) {
920 		if (dirinfo->p!=NULL) {
921 			free(dirinfo->p);
922 		}
923 		dirbuf_meta_fill(dirinfo,ino);
924 //		syslog(LOG_WARNING,"inode: %lu , dirinfo->p: %p , dirinfo->size: %lu",(unsigned long)ino,dirinfo->p,(unsigned long)dirinfo->size);
925 	}
926 	dirinfo->wasread=1;
927 
928 	if (off>=(off_t)(dirinfo->size)) {
929 		fuse_reply_buf(req, NULL, 0);
930 	} else {
931 		if (size>READDIR_BUFFSIZE) {
932 			size=READDIR_BUFFSIZE;
933 		}
934 		ptr = (const uint8_t*)(dirinfo->p)+off;
935 		eptr = (const uint8_t*)(dirinfo->p)+dirinfo->size;
936 		opos = 0;
937 		end = 0;
938 
939 		while (ptr<eptr && end==0) {
940 			nleng = ptr[0];
941 			ptr++;
942 			name = (char*)ptr;
943 			ptr+=nleng;
944 			off+=nleng+6;
945 			if (ptr+5<=eptr) {
946 				inode = get32bit(&ptr);
947 				type = get8bit(&ptr);
948 				mfs_meta_type_to_stat(inode,type,&stbuf);
949 				c = name[nleng];
950 				name[nleng]=0;
951 				oleng = fuse_add_direntry(req, buffer + opos, size - opos, name, &stbuf, off);
952 				name[nleng] = c;
953 				if (opos+oleng>size) {
954 					end=1;
955 				} else {
956 					opos+=oleng;
957 				}
958 			}
959 		}
960 		fuse_reply_buf(req,buffer,opos);
961 	}
962 	pthread_mutex_unlock(&(dirinfo->lock));
963 }
964 
965 void mfs_meta_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
966 	(void)ino;
967 	dirbuf *dirinfo = (dirbuf *)((unsigned long)(fi->fh));
968 	pthread_mutex_lock(&(dirinfo->lock));
969 	pthread_mutex_unlock(&(dirinfo->lock));
970 	pthread_mutex_destroy(&(dirinfo->lock));
971 	free(dirinfo->p);
972 	free(dirinfo);
973 	fi->fh = 0;
974 	fuse_reply_err(req,0);
975 }
976 
977 void mfs_meta_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
978 	pathbuf *pathinfo;
979 	const uint8_t *path;
980 	//size_t pleng;
981 	int status;
982 //	if (ino==MASTER_INODE) {
983 //		minfo *masterinfo;
984 //		status = fs_direct_connect();
985 //		if (status<0) {
986 //			fuse_reply_err(req,EIO);
987 //			return;
988 //		}
989 //		masterinfo = malloc(sizeof(minfo));
990 //		if (masterinfo==NULL) {
991 //			fuse_reply_err(req,ENOMEM);
992 //			return;
993 //		}
994 //		masterinfo->sd = status;
995 //		masterinfo->sent = 0;
996 //		fi->direct_io = 1;
997 //		fi->fh = (unsigned long)masterinfo;
998 //		fuse_reply_open(req, fi);
999 //		return;
1000 //	}
1001 	if (ino==MASTERINFO_INODE) {
1002 		fi->fh = 0;
1003 		fi->direct_io = 0;
1004 		fi->keep_cache = 1;
1005 		fuse_reply_open(req, fi);
1006 		return;
1007 	}
1008 
1009 	if (IS_SPECIAL_INODE(ino)) {
1010 		fuse_reply_err(req, EACCES);
1011 	} else {
1012 		status = fs_gettrashpath(ino,&path);
1013 		status = mfs_errorconv(status);
1014 		if (status!=0) {
1015 			fuse_reply_err(req, status);
1016 		} else {
1017 			pathinfo = malloc(sizeof(pathbuf));
1018 			pthread_mutex_init(&(pathinfo->lock),NULL);
1019 			pathinfo->changed = 0;
1020 			pathinfo->size = strlen((char*)path)+1;
1021 			pathinfo->p = malloc(pathinfo->size);
1022 			memcpy(pathinfo->p,path,pathinfo->size-1);
1023 			pathinfo->p[pathinfo->size-1]='\n';
1024 			fi->direct_io = 1;
1025 			fi->fh = (unsigned long)pathinfo;
1026 			if (fuse_reply_open(req,fi) == -ENOENT) {
1027 				fi->fh = 0;
1028 				pthread_mutex_destroy(&(pathinfo->lock));
1029 				free(pathinfo->p);
1030 				free(pathinfo);
1031 			}
1032 		}
1033 	}
1034 }
1035 
1036 void mfs_meta_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
1037 	if (ino==MASTERINFO_INODE) {
1038 		fuse_reply_err(req,0);
1039 		return;
1040 	}
1041 //	if (ino==MASTER_INODE) {
1042 //		minfo *masterinfo = (minfo*)(unsigned long)(fi->fh);
1043 //		if (masterinfo!=NULL) {
1044 //			fs_direct_close(masterinfo->sd);
1045 //			free(masterinfo);
1046 //		}
1047 //		fuse_reply_err(req,0);
1048 //		return;
1049 //	}
1050 	pathbuf *pathinfo = (pathbuf *)((unsigned long)(fi->fh));
1051 	pthread_mutex_lock(&(pathinfo->lock));
1052 	if (pathinfo->changed) {
1053 		if (pathinfo->p[pathinfo->size-1]=='\n') {
1054 			pathinfo->p[pathinfo->size-1]=0;
1055 		} else {
1056 			pathinfo->p = realloc(pathinfo->p,pathinfo->size+1);
1057 			pathinfo->p[pathinfo->size]=0;
1058 		}
1059 		fs_settrashpath(ino,(uint8_t*)pathinfo->p);
1060 	}
1061 	pthread_mutex_unlock(&(pathinfo->lock));
1062 	pthread_mutex_destroy(&(pathinfo->lock));
1063 	free(pathinfo->p);
1064 	free(pathinfo);
1065 	fi->fh = 0;
1066 	fuse_reply_err(req,0);
1067 }
1068 
1069 void mfs_meta_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
1070 	pathbuf *pathinfo = (pathbuf *)((unsigned long)(fi->fh));
1071 	if (ino==MASTERINFO_INODE) {
1072 		uint8_t masterinfo[14];
1073 		fs_getmasterlocation(masterinfo);
1074 		masterproxy_getlocation(masterinfo);
1075 #ifdef MASTERINFO_WITH_VERSION
1076 		if (off>=14) {
1077 			fuse_reply_buf(req,NULL,0);
1078 		} else if (off+size>14) {
1079 			fuse_reply_buf(req,(char*)(masterinfo+off),14-off);
1080 #else
1081 		if (off>=10) {
1082 			fuse_reply_buf(req,NULL,0);
1083 		} else if (off+size>10) {
1084 			fuse_reply_buf(req,(char*)(masterinfo+off),10-off);
1085 #endif
1086 		} else {
1087 			fuse_reply_buf(req,(char*)(masterinfo+off),size);
1088 		}
1089 		return;
1090 	}
1091 	if (pathinfo==NULL) {
1092 		fuse_reply_err(req,EBADF);
1093 		return;
1094 	}
1095 //	if (ino==MASTER_INODE) {
1096 //		minfo *masterinfo = (minfo*)(unsigned long)(fi->fh);
1097 //		if (masterinfo->sent) {
1098 //			int rsize;
1099 //			uint8_t *buff;
1100 //			buff = malloc(size);
1101 //			rsize = fs_direct_read(masterinfo->sd,buff,size);
1102 //			fuse_reply_buf(req,(char*)buff,rsize);
1103 			//syslog(LOG_WARNING,"master received: %d/%u",rsize,size);
1104 //			free(buff);
1105 //		} else {
1106 //			syslog(LOG_WARNING,"master: read before write");
1107 //			fuse_reply_buf(req,NULL,0);
1108 //		}
1109 //		return;
1110 //	}
1111 	pthread_mutex_lock(&(pathinfo->lock));
1112 	if (off<0) {
1113 		pthread_mutex_unlock(&(pathinfo->lock));
1114 		fuse_reply_err(req,EINVAL);
1115 		return;
1116 	}
1117 	if ((size_t)off>pathinfo->size) {
1118 		fuse_reply_buf(req, NULL, 0);
1119 	} else if (off + size > pathinfo->size) {
1120 		fuse_reply_buf(req, (pathinfo->p)+off,(pathinfo->size)-off);
1121 	} else {
1122 		fuse_reply_buf(req, (pathinfo->p)+off,size);
1123 	}
1124 	pthread_mutex_unlock(&(pathinfo->lock));
1125 }
1126 
1127 void mfs_meta_write(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi) {
1128 	pathbuf *pathinfo = (pathbuf *)((unsigned long)(fi->fh));
1129 	if (ino==MASTERINFO_INODE) {
1130 		fuse_reply_err(req,EACCES);
1131 		return;
1132 	}
1133 	if (pathinfo==NULL) {
1134 		fuse_reply_err(req,EBADF);
1135 		return;
1136 	}
1137 //	if (ino==MASTER_INODE) {
1138 //		minfo *masterinfo = (minfo*)(unsigned long)(fi->fh);
1139 //		int wsize;
1140 //		masterinfo->sent=1;
1141 //		wsize = fs_direct_write(masterinfo->sd,(const uint8_t*)buf,size);
1142 		//syslog(LOG_WARNING,"master sent: %d/%u",wsize,size);
1143 //		fuse_reply_write(req,wsize);
1144 //		return;
1145 //	}
1146 	if (off + size > PATH_SIZE_LIMIT) {
1147 		fuse_reply_err(req,EINVAL);
1148 		return;
1149 	}
1150 	pthread_mutex_lock(&(pathinfo->lock));
1151 	if (pathinfo->changed==0) {
1152 		pathinfo->size = 0;
1153 	}
1154 	if (off+size > pathinfo->size) {
1155 		size_t s = pathinfo->size;
1156 		pathinfo->p = realloc(pathinfo->p,off+size);
1157 		pathinfo->size = off+size;
1158 		memset(pathinfo->p+s,0,off+size-s);
1159 	}
1160 	memcpy((pathinfo->p)+off,buf,size);
1161 	pathinfo->changed = 1;
1162 	pthread_mutex_unlock(&(pathinfo->lock));
1163 	fuse_reply_write(req,size);
1164 }
1165 
1166 void mfs_meta_init(int debug_mode_in,double entry_cache_timeout_in,double attr_cache_timeout_in,int flat_trash_in) {
1167 	debug_mode = debug_mode_in;
1168 	entry_cache_timeout = entry_cache_timeout_in;
1169 	attr_cache_timeout = attr_cache_timeout_in;
1170 	flat_trash = flat_trash_in;
1171 	if (debug_mode) {
1172 		fprintf(stderr,"cache parameters: entry_cache_timeout=%.2lf attr_cache_timeout=%.2lf\n",entry_cache_timeout,attr_cache_timeout);
1173 		if (flat_trash) {
1174 			fprintf(stderr,"force using 'flat' trash\n");
1175 		}
1176 	}
1177 }
1178