1 /*
2    Copyright 2005-2010 Jakub Kruszona-Zawadzki, Gemius SA, 2013-2014 EditShare, 2013-2015 Skytechnology sp. z o.o..
3 
4    This file was part of MooseFS and is part of LizardFS
5 
6    LizardFS is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation, version 3.
9 
10    LizardFS is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with LizardFS  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "common/platform.h"
20 #include "mount/fuse/mfs_fuse.h"
21 
22 #include <memory>
23 #include <string>
24 #include <type_traits>
25 #include <vector>
26 
27 #include "common/massert.h"
28 #include "common/small_vector.h"
29 #include "common/special_inode_defs.h"
30 #include "mount/fuse/lock_conversion.h"
31 #include "mount/lizard_client.h"
32 #include "mount/lizard_client_context.h"
33 #include "mount/thread_safe_map.h"
34 #include "protocol/MFSCommunication.h"
35 
36 #if SPECIAL_INODE_ROOT != FUSE_ROOT_ID
37 #error FUSE_ROOT_ID is not equal to SPECIAL_INODE_ROOT
38 #endif
39 
40 #define READDIR_BUFFSIZE 50000
41 
42 /**
43  * Function checking if types are equal, ignoring constness
44  */
45 template <class A, class B>
checkTypesEqual(const A & a,const B & b)46 void checkTypesEqual(const A& a, const B& b) {
47 	static_assert(std::is_same<decltype(a), decltype(b)>::value,
48 			"Types don't match");
49 }
50 
51 #if defined(__FreeBSD__) || defined(__DragonFly__)
52 #include <sys/user.h>
53 #endif
54 
55 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
56 #include <sys/sysctl.h>
57 
getBsdGroups(LizardClient::Context & ctx)58 void getBsdGroups(LizardClient::Context &ctx) {
59 	int mib_path[4];
60 	struct kinfo_proc kinfo;
61 	size_t len;
62 
63 	len = 4;
64 	sysctlnametomib("kern.proc.pid", mib_path, &len);
65 	mib_path[3] = ctx.pid;
66 
67 	len = sizeof(kinfo);
68 	memset(&kinfo, 0, sizeof(kinfo));
69 	if (sysctl(mib_path, 4, &kinfo, &len, nullptr, 0) == 0) {
70 #if defined(__APPLE__)
71 		ctx.gids.resize(kinfo.kp_eproc.e_ucred.cr_ngroups + 1);
72 		ctx.gids[0] = ctx.gid;
73 		for (int i = 0; i < kinfo.kp_eproc.e_ucred.cr_ngroups; ++i) {
74 			ctx.gids[i + 1] = kinfo.kp_eproc.e_ucred.cr_groups[i];
75 		}
76 #elif defined(__DragonFly__)
77 		ctx.gids.resize(kinfo.kp_ngroups + 1);
78 		ctx.gids[0] = ctx.gid;
79 		for (int i = 0; i < kinfo.kp_ngroups; ++i) {
80 			ctx.gids[i + 1] = kinfo.kp_groups[i];
81 		}
82 #else
83 		ctx.gids.resize(kinfo.ki_ngroups + 1);
84 		ctx.gids[0] = ctx.gid;
85 		for (int i = 0; i < kinfo.ki_ngroups; ++i) {
86 			ctx.gids[i + 1] = kinfo.ki_groups[i];
87 		}
88 #endif
89 	}
90 }
91 #endif
92 
updateGroupsForContext(fuse_req_t & req,LizardClient::Context & ctx)93 static void updateGroupsForContext(fuse_req_t &req, LizardClient::Context &ctx) {
94 	static_assert(sizeof(gid_t) == sizeof(LizardClient::Context::IdType),
95 	              "Invalid IdType to call fuse_req_getgroups");
96 
97 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
98 	(void)req;
99 	getBsdGroups(ctx);
100 	LizardClient::updateGroups(ctx);
101 #elif (FUSE_VERSION < 28)
102 	(void)req, (void)ctx;
103 #else
104 	static const int kMaxGroups = GroupCache::kDefaultGroupsSize - 1;
105 
106 	assert(ctx.gids.size() == 1);
107 	ctx.gids.resize(kMaxGroups + 1);
108 
109 	int getgroups_ret = fuse_req_getgroups(req, kMaxGroups, ctx.gids.data() + 1);
110 	ctx.gids.resize(std::max(1, getgroups_ret + 1));
111 	if (getgroups_ret > kMaxGroups) {
112 		getgroups_ret = fuse_req_getgroups(req, ctx.gids.size() - 1, ctx.gids.data() + 1);
113 
114 		// we include check for case when number of groups has been changed between
115 		// calls to fuse_req_getgroups
116 		ctx.gids.resize(std::max(1, std::min<int>(getgroups_ret + 1, ctx.gids.size())));
117 	}
118 	LizardClient::updateGroups(ctx);
119 #endif
120 }
121 
122 /**
123  * A function converting fuse_ctx to LizardClient::Context
124  */
get_context(fuse_req_t & req)125 LizardClient::Context get_context(fuse_req_t& req) {
126 	auto fuse_ctx = fuse_req_ctx(req);
127 #if (FUSE_VERSION >= 28)
128 	mode_t umask = fuse_ctx->umask;
129 #else
130 	mode_t umask = 0000;
131 #endif
132 	auto ret = LizardClient::Context(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, umask);
133 	if (fuse_ctx->pid > 0) {
134 		updateGroupsForContext(req, ret);
135 	}
136 	return ret;
137 }
138 
139 /**
140  * A wrapper that allows one to use fuse_file_info as if it was LizardClient::FileInfo object.
141  *  During construction, LizardClient::FileInfo object is initialized with information from the
142  *  provided fuse_file_info.
143  *  During destruction, the fuse_file_info is updated to reflect any changes made to the
144  *  LizardClient::FileInfo object.
145  */
146 class fuse_file_info_wrapper {
147 public:
fuse_file_info_wrapper(fuse_file_info * fi)148 	fuse_file_info_wrapper(fuse_file_info* fi)
149 			: fuse_fi_(fi), fs_fi_(fuse_fi_
150 					? new LizardClient::FileInfo(fi->flags, fi->direct_io, fi->keep_cache, fi->fh,
151 					fi->lock_owner)
152 					: nullptr) {
153 		if (fs_fi_) {
154 			assert(fuse_fi_);
155 		} else {
156 			assert(!fuse_fi_);
157 		}
158 	}
operator LizardClient::FileInfo*()159 	operator LizardClient::FileInfo*() {
160 		return fs_fi_.get();
161 	}
~fuse_file_info_wrapper()162 	~fuse_file_info_wrapper() {
163 		if (fs_fi_) {
164 			fuse_fi_->direct_io  = fs_fi_->direct_io;
165 			fuse_fi_->fh         = fs_fi_->fh;
166 			fuse_fi_->flags      = fs_fi_->flags;
167 			fuse_fi_->keep_cache = fs_fi_->keep_cache;
168 			checkTypesEqual(fuse_fi_->direct_io , fs_fi_->direct_io);
169 			checkTypesEqual(fuse_fi_->fh        , fs_fi_->fh);
170 			checkTypesEqual(fuse_fi_->flags     , fs_fi_->flags);
171 			checkTypesEqual(fuse_fi_->keep_cache, fs_fi_->keep_cache);
172 		}
173 	}
174 
175 private:
176 	fuse_file_info* fuse_fi_;
177 	std::unique_ptr<LizardClient::FileInfo> fs_fi_;
178 };
179 
180 /**
181  * A function converting LizardFS::EntryParam to fuse_entry_param
182  */
make_fuse_entry_param(const LizardClient::EntryParam & e)183 fuse_entry_param make_fuse_entry_param(const LizardClient::EntryParam& e) {
184 	fuse_entry_param ret;
185 	memset(&ret, 0, sizeof(ret));
186 	checkTypesEqual(ret.generation,    e.generation);
187 	checkTypesEqual(ret.attr,          e.attr);
188 	checkTypesEqual(ret.attr_timeout,  e.attr_timeout);
189 	checkTypesEqual(ret.entry_timeout, e.entry_timeout);
190 	ret.ino           = e.ino;
191 	ret.generation    = e.generation;
192 	ret.attr          = e.attr;
193 	ret.attr_timeout  = e.attr_timeout;
194 	ret.entry_timeout = e.entry_timeout;
195 	return ret;
196 }
197 
198 #ifndef EDQUOT
199 # define EDQUOT ENOSPC
200 #endif
201 #ifndef ENOATTR
202 # ifdef ENODATA
203 #  define ENOATTR ENODATA
204 # else
205 #  define ENOATTR ENOENT
206 # endif
207 #endif
208 
209 ThreadSafeMap<std::uintptr_t, lzfs_locks::InterruptData> gLockInterruptData;
210 
211 #if FUSE_USE_VERSION >= 26
mfs_statfs(fuse_req_t req,fuse_ino_t ino)212 void mfs_statfs(fuse_req_t req,fuse_ino_t ino) {
213 #else
214 void mfs_statfs(fuse_req_t req) {
215 	fuse_ino_t ino = 0;
216 #endif
217 	try {
218 		auto a = LizardClient::statfs(get_context(req), ino);
219 		fuse_reply_statfs(req, &a);
220 	} catch (LizardClient::RequestException& e) {
221 		fuse_reply_err(req, e.system_error_code);
222 	}
223 }
224 
225 void mfs_access(fuse_req_t req, fuse_ino_t ino, int mask) {
226 	try {
227 		LizardClient::access(get_context(req), ino, mask);
228 		fuse_reply_err(req, 0);
229 	} catch (LizardClient::RequestException& e) {
230 		fuse_reply_err(req, e.system_error_code);
231 	}
232 }
233 
234 void mfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) {
235 	try {
236 		auto fuseEntryParam = make_fuse_entry_param(
237 				LizardClient::lookup(get_context(req), parent, name));
238 		fuse_reply_entry(req, &fuseEntryParam);
239 	} catch (LizardClient::RequestException& e) {
240 		fuse_reply_err(req, e.system_error_code);
241 	}
242 }
243 
244 void mfs_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *) {
245 	try {
246 		// FileInfo not needed, not conducive to optimization
247 		auto a = LizardClient::getattr(get_context(req), ino);
248 		fuse_reply_attr(req, &a.attr, a.attrTimeout);
249 	} catch (LizardClient::RequestException& e) {
250 		fuse_reply_err(req, e.system_error_code);
251 	}
252 }
253 
254 void mfs_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *stbuf, int to_set,
255 		struct fuse_file_info *) {
256 	try {
257 		static_assert(LIZARDFS_SET_ATTR_MODE      == FUSE_SET_ATTR_MODE,      "incompatible");
258 		static_assert(LIZARDFS_SET_ATTR_UID       == FUSE_SET_ATTR_UID,       "incompatible");
259 		static_assert(LIZARDFS_SET_ATTR_GID       == FUSE_SET_ATTR_GID,       "incompatible");
260 		static_assert(LIZARDFS_SET_ATTR_SIZE      == FUSE_SET_ATTR_SIZE,      "incompatible");
261 		static_assert(LIZARDFS_SET_ATTR_ATIME     == FUSE_SET_ATTR_ATIME,     "incompatible");
262 		static_assert(LIZARDFS_SET_ATTR_MTIME     == FUSE_SET_ATTR_MTIME,     "incompatible");
263 #if (FUSE_VERSION >= 28)
264 		static_assert(LIZARDFS_SET_ATTR_ATIME_NOW == FUSE_SET_ATTR_ATIME_NOW, "incompatible");
265 		static_assert(LIZARDFS_SET_ATTR_MTIME_NOW == FUSE_SET_ATTR_MTIME_NOW, "incompatible");
266 #endif
267 
268 		auto a = LizardClient::setattr(get_context(req), ino, stbuf, to_set);
269 		fuse_reply_attr(req, &a.attr, a.attrTimeout);
270 	} catch (LizardClient::RequestException& e) {
271 		fuse_reply_err(req, e.system_error_code);
272 	}
273 }
274 
275 void mfs_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev) {
276 	try {
277 		auto fuseEntryParam = make_fuse_entry_param(
278 				LizardClient::mknod(get_context(req), parent, name, mode, rdev));
279 		fuse_reply_entry(req, &fuseEntryParam);
280 	} catch (LizardClient::RequestException& e) {
281 		fuse_reply_err(req, e.system_error_code);
282 	}
283 }
284 
285 void mfs_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) {
286 	try {
287 		LizardClient::unlink(get_context(req), parent, name);
288 		fuse_reply_err(req, 0);
289 	} catch (LizardClient::RequestException& e) {
290 		fuse_reply_err(req, e.system_error_code);
291 	}
292 }
293 
294 void mfs_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode) {
295 	try {
296 		auto fuseEntryParam = make_fuse_entry_param(
297 				LizardClient::mkdir(get_context(req), parent, name, mode));
298 		fuse_reply_entry(req, &fuseEntryParam);
299 	} catch (LizardClient::RequestException& e) {
300 		fuse_reply_err(req, e.system_error_code);
301 	}
302 }
303 
304 void mfs_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) {
305 	try {
306 		LizardClient::rmdir(get_context(req), parent, name);
307 		fuse_reply_err(req, 0);
308 	} catch (LizardClient::RequestException& e) {
309 		fuse_reply_err(req, e.system_error_code);
310 	}
311 }
312 
313 void mfs_symlink(fuse_req_t req, const char *path, fuse_ino_t parent, const char *name) {
314 	try {
315 		auto fuseEntryParam = make_fuse_entry_param(
316 				LizardClient::symlink(get_context(req), path, parent, name));
317 		fuse_reply_entry(req, &fuseEntryParam);
318 	} catch (LizardClient::RequestException& e) {
319 		fuse_reply_err(req, e.system_error_code);
320 	}
321 }
322 
323 void mfs_readlink(fuse_req_t req, fuse_ino_t ino) {
324 	try {
325 		fuse_reply_readlink(req,
326 				LizardClient::readlink(get_context(req), ino).c_str());
327 	} catch (LizardClient::RequestException& e) {
328 		fuse_reply_err(req, e.system_error_code);
329 	}
330 }
331 
332 void mfs_rename(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent,
333 		const char *newname) {
334 	try {
335 		LizardClient::rename(get_context(req), parent, name, newparent, newname);
336 		fuse_reply_err(req, 0);
337 	} catch (LizardClient::RequestException& e) {
338 		fuse_reply_err(req, e.system_error_code);
339 	}
340 }
341 
342 void mfs_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname) {
343 	try {
344 		auto fuseEntryParam = make_fuse_entry_param(
345 				LizardClient::link(get_context(req), ino, newparent, newname));
346 		fuse_reply_entry(req, &fuseEntryParam);
347 	} catch (LizardClient::RequestException& e) {
348 		fuse_reply_err(req, e.system_error_code);
349 	}
350 }
351 
352 void mfs_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
353 	try {
354 		LizardClient::opendir(get_context(req), ino);
355 		fuse_reply_open(req, fi);
356 	} catch (LizardClient::RequestException& e) {
357 		fuse_reply_err(req, e.system_error_code);
358 	}
359 }
360 
361 void mfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
362 		struct fuse_file_info */*fi*/) {
363 	try {
364 		char buffer[READDIR_BUFFSIZE];
365 		if (size > READDIR_BUFFSIZE) {
366 			size = READDIR_BUFFSIZE;
367 		}
368 		size_t bytesInBuffer = 0;
369 		bool end = false;
370 		while (!end) {
371 			// Calculate approximated number of entries which will fit in the buffer. If this
372 			// number is smaller than the actual value, LizardClient::readdir will be called more
373 			// than once for a single mfs_readdir (this will eg. generate more oplog entries than
374 			// one might expect). If it's bigger, the code will be slightly less optimal because
375 			// superfluous entries will be extracted by LizardClient::readdir and then discarded by
376 			// us. Using maxEntries=+inf makes the complexity of the getdents syscall O(n^2).
377 			// The expression below generates some upper bound of the actual number of entries
378 			// to be returned (because fuse adds 24 bytes of metadata to each file name in
379 			// fuse_add_direntry and aligns size up to 8 bytes), so LizardClient::readdir
380 			// should be called only once.
381 			size_t maxEntries = 1 + size / 32;
382 			// Now extract some entries and rewrite them into the buffer.
383 			auto fsDirEntries = LizardClient::readdir(get_context(req), ino, off, maxEntries);
384 			if (fsDirEntries.empty()) {
385 				break; // no more entries (we don't need to set 'end = true' here to end the loop)
386 			}
387 			for (const auto& e : fsDirEntries) {
388 				size_t entrySize = fuse_add_direntry(req,
389 						buffer + bytesInBuffer, size,
390 						e.name.c_str(), &(e.attr), e.nextEntryOffset);
391 				if (entrySize > size) {
392 					end = true; // buffer is full
393 					break;
394 				}
395 				off = e.nextEntryOffset; // update offset of the next call to LizardClient::readdir
396 				bytesInBuffer += entrySize;
397 				size -= entrySize; // decrease remaining buffer size
398 			}
399 		}
400 		fuse_reply_buf(req, buffer, bytesInBuffer);
401 	} catch (LizardClient::RequestException& e) {
402 		fuse_reply_err(req, e.system_error_code);
403 	}
404 }
405 
406 void mfs_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info */*fi*/) {
407 	try {
408 		LizardClient::releasedir(ino);
409 		fuse_reply_err(req, 0);
410 	} catch (LizardClient::RequestException& e) {
411 		fuse_reply_err(req, e.system_error_code);
412 	}
413 }
414 
415 void mfs_create(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode,
416 		struct fuse_file_info *fi) {
417 	try {
418 		auto e = make_fuse_entry_param(LizardClient::create(
419 				get_context(req), parent, name, mode, fuse_file_info_wrapper(fi)));
420 		if (fuse_reply_create(req, &e, fi) == -ENOENT) {
421 			LizardClient::remove_file_info(fuse_file_info_wrapper(fi));
422 		}
423 	} catch (LizardClient::RequestException& e) {
424 		fuse_reply_err(req, e.system_error_code);
425 	}
426 }
427 
428 void mfs_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
429 	try {
430 		LizardClient::open(get_context(req), ino, fuse_file_info_wrapper(fi));
431 		if (fuse_reply_open(req, fi) == -ENOENT) {
432 			LizardClient::remove_file_info(fuse_file_info_wrapper(fi));
433 		}
434 	} catch (LizardClient::RequestException& e) {
435 		fuse_reply_err(req, e.system_error_code);
436 	}
437 }
438 
439 void mfs_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
440 	try {
441 		LizardClient::release(ino, fuse_file_info_wrapper(fi));
442 		fuse_reply_err(req, 0);
443 	} catch (LizardClient::RequestException& e) {
444 		fuse_reply_err(req, e.system_error_code);
445 	}
446 }
447 
448 void mfs_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
449 	try {
450 		if (LizardClient::isSpecialInode(ino)) {
451 			auto ret = LizardClient::read_special_inode(
452 				   get_context(req), ino, size, off, fuse_file_info_wrapper(fi));
453 			fuse_reply_buf(req, (char*) ret.data(), ret.size());
454 		} else {
455 			ReadCache::Result ret = LizardClient::read(
456 					get_context(req), ino, size, off, fuse_file_info_wrapper(fi));
457 
458 			small_vector<struct iovec, 8> reply;
459 			ret.toIoVec(reply, off, size);
460 			fuse_reply_iov(req, reply.data(), reply.size());
461 		}
462 	} catch (LizardClient::RequestException& e) {
463 		fuse_reply_err(req, e.system_error_code);
464 	}
465 }
466 
467 void mfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off,
468 		struct fuse_file_info *fi) {
469 	try {
470 		fuse_reply_write(req, LizardClient::write(
471 				get_context(req), ino, buf, size, off, fuse_file_info_wrapper(fi)));
472 	} catch (LizardClient::RequestException& e) {
473 		fuse_reply_err(req, e.system_error_code);
474 	}
475 }
476 
477 void mfs_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
478 	try {
479 		LizardClient::flush(get_context(req), ino, fuse_file_info_wrapper(fi));
480 		fuse_reply_err(req, 0);
481 	} catch (LizardClient::RequestException& e) {
482 		fuse_reply_err(req, e.system_error_code);
483 	}
484 }
485 
486 void mfs_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi) {
487 	try {
488 		LizardClient::fsync(get_context(req), ino, datasync, fuse_file_info_wrapper(fi));
489 		fuse_reply_err(req, 0);
490 	} catch (LizardClient::RequestException& e) {
491 		fuse_reply_err(req, e.system_error_code);
492 	}
493 }
494 
495 #if defined(__APPLE__)
496 void mfs_setxattr (fuse_req_t req, fuse_ino_t ino, const char *name, const char *value,
497 		size_t size, int flags, uint32_t position) {
498 #else
499 void mfs_setxattr (fuse_req_t req, fuse_ino_t ino, const char *name, const char *value,
500 		size_t size, int flags) {
501 	uint32_t position=0;
502 #endif
503 	try {
504 		LizardClient::setxattr(get_context(req), ino, name, value, size, flags, position);
505 		fuse_reply_err(req, 0);
506 	} catch (LizardClient::RequestException& e) {
507 		fuse_reply_err(req, e.system_error_code);
508 	}
509 }
510 
511 #if defined(__APPLE__)
512 void mfs_getxattr (fuse_req_t req, fuse_ino_t ino, const char *name, size_t size,
513 		uint32_t position) {
514 #else
515 void mfs_getxattr (fuse_req_t req, fuse_ino_t ino, const char *name, size_t size) {
516 	uint32_t position=0;
517 #endif /* __APPLE__ */
518 	try {
519 		auto a = LizardClient::getxattr(get_context(req), ino, name, size, position);
520 		if (size == 0) {
521 			fuse_reply_xattr(req, a.valueLength);
522 		} else {
523 			fuse_reply_buf(req,(const char*)a.valueBuffer.data(), a.valueLength);
524 		}
525 	} catch (LizardClient::RequestException& e) {
526 		fuse_reply_err(req, e.system_error_code);
527 	}
528 }
529 
530 void mfs_listxattr (fuse_req_t req, fuse_ino_t ino, size_t size) {
531 	try {
532 		auto a = LizardClient::listxattr(get_context(req), ino, size);
533 		if (size == 0) {
534 			fuse_reply_xattr(req, a.valueLength);
535 		} else {
536 			fuse_reply_buf(req,(const char*)a.valueBuffer.data(), a.valueLength);
537 		}
538 	} catch (LizardClient::RequestException& e) {
539 		fuse_reply_err(req, e.system_error_code);
540 	}
541 }
542 
543 void mfs_removexattr (fuse_req_t req, fuse_ino_t ino, const char *name) {
544 	try {
545 		LizardClient::removexattr(get_context(req), ino, name);
546 		fuse_reply_err(req, 0);
547 	} catch (LizardClient::RequestException& e) {
548 		fuse_reply_err(req, e.system_error_code);
549 	}
550 }
551 
552 #if FUSE_VERSION >= 26
553 void lzfs_flock_interrupt(fuse_req_t req, void *data) {
554 	auto interrupt_data = gLockInterruptData.take(reinterpret_cast<std::uintptr_t>(data));
555 
556 	// if there was any data
557 	if (interrupt_data.first) {
558 		// handle interrupt
559 		LizardClient::flock_interrupt(interrupt_data.second);
560 		fuse_reply_err(req, EINTR);
561 	}
562 }
563 
564 void lzfs_setlk_interrupt(fuse_req_t req, void *data) {
565 	auto interrupt_data = gLockInterruptData.take(reinterpret_cast<std::uintptr_t>(data));
566 
567 	// if there was any data
568 	if (interrupt_data.first) {
569 		// handle interrupt
570 		LizardClient::setlk_interrupt(interrupt_data.second);
571 		fuse_reply_err(req, EINTR);
572 	}
573 }
574 
575 void lzfs_getlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock) {
576 	try {
577 		if (!lzfs_locks::posixOpValid(lock->l_type)) {
578 			fuse_reply_err(req, EINVAL);
579 			return;
580 		}
581 
582 		lzfs_locks::FlockWrapper lzfslock = lzfs_locks::convertPLock(*lock, 1);
583 		LizardClient::getlk(get_context(req), ino, fuse_file_info_wrapper(fi), lzfslock);
584 		struct flock retlock = lzfs_locks::convertToFlock(lzfslock);
585 		fuse_reply_lock(req, &retlock);
586 	} catch (LizardClient::RequestException& e) {
587 		fuse_reply_err(req, e.system_error_code);
588 	}
589 }
590 
591 void lzfs_setlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, int sleep) {
592 	std::uintptr_t interrupt_data_key = gLockInterruptData.generateKey();
593 	try {
594 		if (fuse_req_interrupted(req)) {
595 			fuse_reply_err(req, EINTR);
596 			return;
597 		}
598 
599 		if (!lzfs_locks::posixOpValid(lock->l_type)) {
600 			fuse_reply_err(req, EINVAL);
601 			return;
602 		}
603 
604 		lzfs_locks::FlockWrapper lzfslock = lzfs_locks::convertPLock(*lock, sleep);
605 		uint32_t reqid = LizardClient::setlk_send(get_context(req), ino, fuse_file_info_wrapper(fi),
606 				lzfslock);
607 
608 		// save interrupt data in static structure
609 		gLockInterruptData.put(interrupt_data_key,
610 				       lzfs_locks::InterruptData(fi->lock_owner, ino, reqid));
611 
612 		// register interrupt handle
613 		if (lzfslock.l_type == lzfs_locks::kShared || lzfslock.l_type == lzfs_locks::kExclusive) {
614 			fuse_req_interrupt_func(req, lzfs_setlk_interrupt,
615 					reinterpret_cast<void*>(interrupt_data_key));
616 		}
617 
618 		// WARNING: csetlk_recv() won't work with polonaise server,
619 		// since actual code requires setlk_send()
620 		// to be executed by the same thread.
621 		LizardClient::setlk_recv();
622 
623 		// release the memory
624 		auto interrupt_data = gLockInterruptData.take(interrupt_data_key);
625 		if (interrupt_data.first) {
626 			fuse_reply_err(req, 0);
627 		}
628 	} catch (LizardClient::RequestException& e) {
629 		// release the memory
630 		auto interrupt_data = gLockInterruptData.take(interrupt_data_key);
631 		if (interrupt_data.first) {
632 			fuse_reply_err(req, e.system_error_code);
633 		}
634 	}
635 }
636 #endif
637 #if FUSE_VERSION >= 29
638 
639 void lzfs_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, int op) {
640 	std::uintptr_t interrupt_data_key = gLockInterruptData.generateKey();
641 	try {
642 		if (fuse_req_interrupted(req)) {
643 			fuse_reply_err(req, EINTR);
644 			return;
645 		}
646 
647 		if (!lzfs_locks::flockOpValid(op)) {
648 			fuse_reply_err(req, EINVAL);
649 			return;
650 		}
651 
652 		uint32_t lzfs_op = lzfs_locks::flockOpConv(op);
653 		uint32_t reqid = LizardClient::flock_send(get_context(req), ino,
654 			fuse_file_info_wrapper(fi), lzfs_op);
655 
656 		// save interrupt data in static structure
657 		gLockInterruptData.put(interrupt_data_key,
658 				       lzfs_locks::InterruptData(fi->lock_owner, ino, reqid));
659 		// register interrupt handle
660 		if (lzfs_op == lzfs_locks::kShared || lzfs_op == lzfs_locks::kExclusive) {
661 			fuse_req_interrupt_func(req, lzfs_flock_interrupt,
662 					reinterpret_cast<void*>(interrupt_data_key));
663 		}
664 
665 		LizardClient::flock_recv();
666 
667 		// release the memory
668 		auto interrupt_data = gLockInterruptData.take(interrupt_data_key);
669 		if (interrupt_data.first) {
670 			fuse_reply_err(req, 0);
671 		}
672 	} catch (LizardClient::RequestException& e) {
673 		// release the memory
674 		auto interrupt_data = gLockInterruptData.take(interrupt_data_key);
675 		if (interrupt_data.first) {
676 			fuse_reply_err(req, e.system_error_code);
677 		}
678 	}
679 }
680 #endif
681