]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net> | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
15 | #include <sys/file.h> | |
16 | #include <sys/types.h> | |
17 | #include <sys/wait.h> | |
18 | #include <signal.h> | |
19 | #include <stdio.h> | |
20 | #include <stdlib.h> | |
21 | #include <string.h> | |
22 | #include <errno.h> | |
23 | #include <fcntl.h> | |
24 | #include <unistd.h> | |
25 | ||
26 | // ceph | |
27 | #include "common/errno.h" | |
28 | #include "common/safe_io.h" | |
29 | #include "include/types.h" | |
30 | #include "Client.h" | |
31 | #include "Fh.h" | |
32 | #include "ioctl.h" | |
33 | #include "common/config.h" | |
34 | #include "include/assert.h" | |
35 | #include "include/cephfs/ceph_statx.h" | |
36 | ||
37 | #include "fuse_ll.h" | |
38 | #include <fuse.h> | |
39 | #include <fuse_lowlevel.h> | |
40 | ||
41 | #define dout_context g_ceph_context | |
42 | ||
43 | #define FINO_INO(x) ((x) & ((1ull<<48)-1ull)) | |
44 | #define FINO_STAG(x) ((x) >> 48) | |
45 | #define MAKE_FINO(i,s) ((i) | ((s) << 48)) | |
46 | ||
47 | #define MINORBITS 20 | |
48 | #define MINORMASK ((1U << MINORBITS) - 1) | |
49 | ||
50 | #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) | |
51 | #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) | |
52 | #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) | |
53 | ||
54 | static uint32_t new_encode_dev(dev_t dev) | |
55 | { | |
56 | unsigned major = MAJOR(dev); | |
57 | unsigned minor = MINOR(dev); | |
58 | return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12); | |
59 | } | |
60 | ||
61 | static dev_t new_decode_dev(uint32_t dev) | |
62 | { | |
63 | unsigned major = (dev & 0xfff00) >> 8; | |
64 | unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00); | |
65 | return MKDEV(major, minor); | |
66 | } | |
67 | ||
68 | class CephFuse::Handle { | |
69 | public: | |
70 | Handle(Client *c, int fd); | |
71 | ~Handle(); | |
72 | ||
73 | int init(int argc, const char *argv[]); | |
74 | int start(); | |
75 | int loop(); | |
76 | void finalize(); | |
77 | ||
78 | uint64_t fino_snap(uint64_t fino); | |
79 | uint64_t make_fake_ino(inodeno_t ino, snapid_t snapid); | |
80 | Inode * iget(fuse_ino_t fino); | |
81 | void iput(Inode *in); | |
82 | ||
83 | int fd_on_success; | |
84 | Client *client; | |
85 | ||
86 | struct fuse_chan *ch; | |
87 | struct fuse_session *se; | |
88 | char *mountpoint; | |
89 | ||
90 | Mutex stag_lock; | |
91 | int last_stag; | |
92 | ||
93 | ceph::unordered_map<uint64_t,int> snap_stag_map; | |
94 | ceph::unordered_map<int,uint64_t> stag_snap_map; | |
95 | ||
96 | pthread_key_t fuse_req_key; | |
97 | void set_fuse_req(fuse_req_t); | |
98 | fuse_req_t get_fuse_req(); | |
99 | ||
100 | struct fuse_args args; | |
101 | }; | |
102 | ||
103 | static int getgroups(fuse_req_t req, gid_t **sgids) | |
104 | { | |
105 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) | |
106 | assert(sgids); | |
107 | int c = fuse_req_getgroups(req, 0, NULL); | |
108 | if (c < 0) { | |
109 | return c; | |
110 | } | |
111 | if (c == 0) { | |
112 | return 0; | |
113 | } | |
114 | ||
b5b8bbf5 FG |
115 | gid_t *gids = new (std::nothrow) gid_t[c]; |
116 | if (!gids) { | |
7c673cae FG |
117 | return -ENOMEM; |
118 | } | |
b5b8bbf5 | 119 | c = fuse_req_getgroups(req, c, gids); |
7c673cae | 120 | if (c < 0) { |
b5b8bbf5 FG |
121 | delete gids; |
122 | } else { | |
123 | *sgids = gids; | |
7c673cae FG |
124 | } |
125 | return c; | |
126 | #endif | |
127 | return -ENOSYS; | |
128 | } | |
129 | ||
28e407b8 | 130 | static void get_fuse_groups(UserPerm& perms, fuse_req_t req) |
7c673cae | 131 | { |
28e407b8 AA |
132 | if (g_conf->get_val<bool>("fuse_set_user_groups")) { |
133 | gid_t *gids = NULL; | |
134 | int count = getgroups(req, &gids); | |
135 | ||
136 | if (count > 0) { | |
137 | perms.init_gids(gids, count); | |
138 | } else if (count < 0) { | |
139 | derr << __func__ << ": getgroups failed: " << cpp_strerror(-count) | |
140 | << dendl; | |
141 | } | |
142 | } | |
7c673cae FG |
143 | } |
144 | ||
7c673cae FG |
145 | |
146 | static CephFuse::Handle *fuse_ll_req_prepare(fuse_req_t req) | |
147 | { | |
148 | CephFuse::Handle *cfuse = (CephFuse::Handle *)fuse_req_userdata(req); | |
149 | cfuse->set_fuse_req(req); | |
150 | return cfuse; | |
151 | } | |
152 | ||
153 | static void fuse_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) | |
154 | { | |
155 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
156 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
157 | struct fuse_entry_param fe; | |
158 | Inode *i2, *i1 = cfuse->iget(parent); // see below | |
159 | int r; | |
160 | UserPerm perms(ctx->uid, ctx->gid); | |
28e407b8 | 161 | get_fuse_groups(perms, req); |
7c673cae FG |
162 | |
163 | memset(&fe, 0, sizeof(fe)); | |
164 | r = cfuse->client->ll_lookup(i1, name, &fe.attr, &i2, perms); | |
165 | if (r >= 0) { | |
166 | fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev); | |
167 | fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev); | |
168 | fuse_reply_entry(req, &fe); | |
169 | } else { | |
170 | fuse_reply_err(req, -r); | |
171 | } | |
172 | ||
173 | // XXX NB, we dont iput(i2) because FUSE will do so in a matching | |
174 | // fuse_ll_forget() | |
175 | cfuse->iput(i1); | |
176 | } | |
177 | ||
178 | static void fuse_ll_forget(fuse_req_t req, fuse_ino_t ino, | |
179 | long unsigned nlookup) | |
180 | { | |
181 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
182 | cfuse->client->ll_forget(cfuse->iget(ino), nlookup+1); | |
183 | fuse_reply_none(req); | |
184 | } | |
185 | ||
186 | static void fuse_ll_getattr(fuse_req_t req, fuse_ino_t ino, | |
187 | struct fuse_file_info *fi) | |
188 | { | |
189 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
190 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
191 | Inode *in = cfuse->iget(ino); | |
192 | struct stat stbuf; | |
193 | UserPerm perms(ctx->uid, ctx->gid); | |
28e407b8 | 194 | get_fuse_groups(perms, req); |
7c673cae FG |
195 | |
196 | (void) fi; // XXX | |
197 | ||
198 | if (cfuse->client->ll_getattr(in, &stbuf, perms) | |
199 | == 0) { | |
200 | stbuf.st_ino = cfuse->make_fake_ino(stbuf.st_ino, stbuf.st_dev); | |
201 | stbuf.st_rdev = new_encode_dev(stbuf.st_rdev); | |
202 | fuse_reply_attr(req, &stbuf, 0); | |
203 | } else | |
204 | fuse_reply_err(req, ENOENT); | |
205 | ||
206 | cfuse->iput(in); // iput required | |
207 | } | |
208 | ||
209 | static void fuse_ll_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, | |
210 | int to_set, struct fuse_file_info *fi) | |
211 | { | |
212 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
213 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
214 | Inode *in = cfuse->iget(ino); | |
215 | UserPerm perms(ctx->uid, ctx->gid); | |
28e407b8 | 216 | get_fuse_groups(perms, req); |
7c673cae FG |
217 | |
218 | int mask = 0; | |
219 | if (to_set & FUSE_SET_ATTR_MODE) mask |= CEPH_SETATTR_MODE; | |
220 | if (to_set & FUSE_SET_ATTR_UID) mask |= CEPH_SETATTR_UID; | |
221 | if (to_set & FUSE_SET_ATTR_GID) mask |= CEPH_SETATTR_GID; | |
222 | if (to_set & FUSE_SET_ATTR_MTIME) mask |= CEPH_SETATTR_MTIME; | |
223 | if (to_set & FUSE_SET_ATTR_ATIME) mask |= CEPH_SETATTR_ATIME; | |
224 | if (to_set & FUSE_SET_ATTR_SIZE) mask |= CEPH_SETATTR_SIZE; | |
225 | #if !defined(DARWIN) | |
226 | if (to_set & FUSE_SET_ATTR_MTIME_NOW) mask |= CEPH_SETATTR_MTIME_NOW; | |
227 | if (to_set & FUSE_SET_ATTR_ATIME_NOW) mask |= CEPH_SETATTR_ATIME_NOW; | |
228 | #endif | |
229 | ||
230 | int r = cfuse->client->ll_setattr(in, attr, mask, perms); | |
231 | if (r == 0) | |
232 | fuse_reply_attr(req, attr, 0); | |
233 | else | |
234 | fuse_reply_err(req, -r); | |
235 | ||
236 | cfuse->iput(in); // iput required | |
237 | } | |
238 | ||
239 | // XATTRS | |
240 | ||
241 | static void fuse_ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, | |
242 | const char *value, size_t size, | |
243 | int flags | |
244 | #if defined(DARWIN) | |
245 | ,uint32_t pos | |
246 | #endif | |
247 | ) | |
248 | { | |
249 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
250 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
251 | Inode *in = cfuse->iget(ino); | |
252 | UserPerm perms(ctx->uid, ctx->gid); | |
28e407b8 | 253 | get_fuse_groups(perms, req); |
7c673cae FG |
254 | |
255 | int r = cfuse->client->ll_setxattr(in, name, value, size, flags, perms); | |
256 | fuse_reply_err(req, -r); | |
257 | ||
258 | cfuse->iput(in); // iput required | |
259 | } | |
260 | ||
261 | static void fuse_ll_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) | |
262 | { | |
263 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
264 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
265 | Inode *in = cfuse->iget(ino); | |
266 | char buf[size]; | |
267 | UserPerm perms(ctx->uid, ctx->gid); | |
28e407b8 | 268 | get_fuse_groups(perms, req); |
7c673cae FG |
269 | |
270 | int r = cfuse->client->ll_listxattr(in, buf, size, perms); | |
271 | if (size == 0 && r >= 0) | |
272 | fuse_reply_xattr(req, r); | |
273 | else if (r >= 0) | |
274 | fuse_reply_buf(req, buf, r); | |
275 | else | |
276 | fuse_reply_err(req, -r); | |
277 | ||
278 | cfuse->iput(in); // iput required | |
279 | } | |
280 | ||
281 | static void fuse_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, | |
282 | size_t size | |
283 | #if defined(DARWIN) | |
284 | ,uint32_t position | |
285 | #endif | |
286 | ) | |
287 | { | |
288 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
289 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
290 | Inode *in = cfuse->iget(ino); | |
291 | char buf[size]; | |
292 | UserPerm perms(ctx->uid, ctx->gid); | |
28e407b8 | 293 | get_fuse_groups(perms, req); |
7c673cae FG |
294 | |
295 | int r = cfuse->client->ll_getxattr(in, name, buf, size, perms); | |
296 | if (size == 0 && r >= 0) | |
297 | fuse_reply_xattr(req, r); | |
298 | else if (r >= 0) | |
299 | fuse_reply_buf(req, buf, r); | |
300 | else | |
301 | fuse_reply_err(req, -r); | |
302 | ||
303 | cfuse->iput(in); // iput required | |
304 | } | |
305 | ||
306 | static void fuse_ll_removexattr(fuse_req_t req, fuse_ino_t ino, | |
307 | const char *name) | |
308 | { | |
309 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
310 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
311 | Inode *in = cfuse->iget(ino); | |
312 | UserPerm perms(ctx->uid, ctx->gid); | |
28e407b8 | 313 | get_fuse_groups(perms, req); |
7c673cae FG |
314 | |
315 | int r = cfuse->client->ll_removexattr(in, name, perms); | |
316 | fuse_reply_err(req, -r); | |
317 | ||
318 | cfuse->iput(in); // iput required | |
319 | } | |
320 | ||
321 | static void fuse_ll_opendir(fuse_req_t req, fuse_ino_t ino, | |
322 | struct fuse_file_info *fi) | |
323 | { | |
324 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
325 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
326 | Inode *in = cfuse->iget(ino); | |
327 | void *dirp; | |
328 | ||
329 | UserPerm perms(ctx->uid, ctx->gid); | |
28e407b8 | 330 | get_fuse_groups(perms, req); |
7c673cae FG |
331 | |
332 | int r = cfuse->client->ll_opendir(in, fi->flags, (dir_result_t **)&dirp, | |
333 | perms); | |
334 | if (r >= 0) { | |
335 | fi->fh = (uint64_t)dirp; | |
336 | fuse_reply_open(req, fi); | |
337 | } else { | |
338 | fuse_reply_err(req, -r); | |
339 | } | |
340 | ||
341 | cfuse->iput(in); // iput required | |
342 | } | |
343 | ||
344 | static void fuse_ll_readlink(fuse_req_t req, fuse_ino_t ino) | |
345 | { | |
346 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
347 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
348 | Inode *in = cfuse->iget(ino); | |
349 | char buf[PATH_MAX + 1]; // leave room for a null terminator | |
350 | UserPerm perms(ctx->uid, ctx->gid); | |
28e407b8 | 351 | get_fuse_groups(perms, req); |
7c673cae FG |
352 | |
353 | int r = cfuse->client->ll_readlink(in, buf, sizeof(buf) - 1, perms); | |
354 | if (r >= 0) { | |
355 | buf[r] = '\0'; | |
356 | fuse_reply_readlink(req, buf); | |
357 | } else { | |
358 | fuse_reply_err(req, -r); | |
359 | } | |
360 | ||
361 | cfuse->iput(in); // iput required | |
362 | } | |
363 | ||
364 | static void fuse_ll_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, | |
365 | mode_t mode, dev_t rdev) | |
366 | { | |
367 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
368 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
369 | Inode *i2, *i1 = cfuse->iget(parent); | |
370 | struct fuse_entry_param fe; | |
371 | UserPerm perms(ctx->uid, ctx->gid); | |
28e407b8 | 372 | get_fuse_groups(perms, req); |
7c673cae FG |
373 | |
374 | memset(&fe, 0, sizeof(fe)); | |
375 | ||
376 | int r = cfuse->client->ll_mknod(i1, name, mode, new_decode_dev(rdev), | |
377 | &fe.attr, &i2, perms); | |
378 | if (r == 0) { | |
379 | fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev); | |
380 | fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev); | |
381 | fuse_reply_entry(req, &fe); | |
382 | } else { | |
383 | fuse_reply_err(req, -r); | |
384 | } | |
385 | ||
386 | // XXX NB, we dont iput(i2) because FUSE will do so in a matching | |
387 | // fuse_ll_forget() | |
388 | cfuse->iput(i1); // iput required | |
389 | } | |
390 | ||
391 | static void fuse_ll_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, | |
392 | mode_t mode) | |
393 | { | |
394 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
395 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
396 | Inode *i2, *i1; | |
397 | struct fuse_entry_param fe; | |
398 | ||
399 | memset(&fe, 0, sizeof(fe)); | |
400 | UserPerm perm(ctx->uid, ctx->gid); | |
28e407b8 | 401 | get_fuse_groups(perm, req); |
7c673cae FG |
402 | #ifdef HAVE_SYS_SYNCFS |
403 | if (cfuse->fino_snap(parent) == CEPH_SNAPDIR && | |
404 | cfuse->client->cct->_conf->fuse_multithreaded && | |
405 | cfuse->client->cct->_conf->fuse_syncfs_on_mksnap) { | |
406 | int err = 0; | |
91327a77 | 407 | int fd = ::open(cfuse->mountpoint, O_RDONLY | O_DIRECTORY | O_CLOEXEC); |
7c673cae FG |
408 | if (fd < 0) { |
409 | err = errno; | |
410 | } else { | |
411 | int r = ::syncfs(fd); | |
412 | if (r < 0) | |
413 | err = errno; | |
414 | ::close(fd); | |
415 | } | |
416 | if (err) { | |
417 | fuse_reply_err(req, err); | |
418 | return; | |
419 | } | |
420 | } | |
421 | #endif | |
422 | ||
423 | i1 = cfuse->iget(parent); | |
424 | int r = cfuse->client->ll_mkdir(i1, name, mode, &fe.attr, &i2, perm); | |
425 | if (r == 0) { | |
426 | fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev); | |
427 | fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev); | |
428 | fuse_reply_entry(req, &fe); | |
429 | } else { | |
430 | fuse_reply_err(req, -r); | |
431 | } | |
432 | ||
433 | // XXX NB, we dont iput(i2) because FUSE will do so in a matching | |
434 | // fuse_ll_forget() | |
435 | cfuse->iput(i1); // iput required | |
436 | } | |
437 | ||
438 | static void fuse_ll_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) | |
439 | { | |
440 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
441 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
442 | Inode *in = cfuse->iget(parent); | |
443 | UserPerm perm(ctx->uid, ctx->gid); | |
28e407b8 | 444 | get_fuse_groups(perm, req); |
7c673cae FG |
445 | |
446 | int r = cfuse->client->ll_unlink(in, name, perm); | |
447 | fuse_reply_err(req, -r); | |
448 | ||
449 | cfuse->iput(in); // iput required | |
450 | } | |
451 | ||
452 | static void fuse_ll_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) | |
453 | { | |
454 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
455 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
456 | Inode *in = cfuse->iget(parent); | |
457 | UserPerm perms(ctx->uid, ctx->gid); | |
28e407b8 | 458 | get_fuse_groups(perms, req); |
7c673cae FG |
459 | |
460 | int r = cfuse->client->ll_rmdir(in, name, perms); | |
461 | fuse_reply_err(req, -r); | |
462 | ||
463 | cfuse->iput(in); // iput required | |
464 | } | |
465 | ||
466 | static void fuse_ll_symlink(fuse_req_t req, const char *existing, | |
467 | fuse_ino_t parent, const char *name) | |
468 | { | |
469 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
470 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
471 | Inode *i2, *i1 = cfuse->iget(parent); | |
472 | struct fuse_entry_param fe; | |
473 | UserPerm perms(ctx->uid, ctx->gid); | |
28e407b8 | 474 | get_fuse_groups(perms, req); |
7c673cae FG |
475 | |
476 | memset(&fe, 0, sizeof(fe)); | |
477 | ||
478 | int r = cfuse->client->ll_symlink(i1, name, existing, &fe.attr, &i2, perms); | |
479 | if (r == 0) { | |
480 | fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev); | |
481 | fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev); | |
482 | fuse_reply_entry(req, &fe); | |
483 | } else { | |
484 | fuse_reply_err(req, -r); | |
485 | } | |
486 | ||
487 | // XXX NB, we dont iput(i2) because FUSE will do so in a matching | |
488 | // fuse_ll_forget() | |
489 | cfuse->iput(i1); // iput required | |
490 | } | |
491 | ||
492 | static void fuse_ll_rename(fuse_req_t req, fuse_ino_t parent, const char *name, | |
493 | fuse_ino_t newparent, const char *newname) | |
494 | { | |
495 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
496 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
497 | Inode *in = cfuse->iget(parent); | |
498 | Inode *nin = cfuse->iget(newparent); | |
499 | UserPerm perm(ctx->uid, ctx->gid); | |
28e407b8 | 500 | get_fuse_groups(perm, req); |
7c673cae FG |
501 | |
502 | int r = cfuse->client->ll_rename(in, name, nin, newname, perm); | |
503 | fuse_reply_err(req, -r); | |
504 | ||
505 | cfuse->iput(in); // iputs required | |
506 | cfuse->iput(nin); | |
507 | } | |
508 | ||
509 | static void fuse_ll_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, | |
510 | const char *newname) | |
511 | { | |
512 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
513 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
514 | Inode *in = cfuse->iget(ino); | |
515 | Inode *nin = cfuse->iget(newparent); | |
516 | struct fuse_entry_param fe; | |
517 | ||
518 | memset(&fe, 0, sizeof(fe)); | |
519 | UserPerm perm(ctx->uid, ctx->gid); | |
28e407b8 | 520 | get_fuse_groups(perm, req); |
7c673cae FG |
521 | |
522 | /* | |
523 | * Note that we could successfully link, but then fail the subsequent | |
524 | * getattr and return an error. Perhaps we should ignore getattr errors, | |
525 | * but then how do we tell FUSE that the attrs are bogus? | |
526 | */ | |
527 | int r = cfuse->client->ll_link(in, nin, newname, perm); | |
528 | if (r == 0) { | |
529 | r = cfuse->client->ll_getattr(in, &fe.attr, perm); | |
530 | if (r == 0) { | |
531 | fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev); | |
532 | fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev); | |
533 | fuse_reply_entry(req, &fe); | |
534 | } | |
535 | } | |
536 | ||
537 | if (r != 0) { | |
538 | /* | |
539 | * Many ll operations in libcephfs return an extra inode reference, but | |
540 | * ll_link currently does not. Still, FUSE needs one for the new dentry, | |
541 | * so we commandeer the reference taken earlier when ll_link is successful. | |
542 | * On error however, we must put that reference. | |
543 | */ | |
544 | cfuse->iput(in); | |
545 | fuse_reply_err(req, -r); | |
546 | } | |
547 | ||
548 | cfuse->iput(nin); | |
549 | } | |
550 | ||
551 | static void fuse_ll_open(fuse_req_t req, fuse_ino_t ino, | |
552 | struct fuse_file_info *fi) | |
553 | { | |
554 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
555 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
556 | Inode *in = cfuse->iget(ino); | |
557 | Fh *fh = NULL; | |
558 | UserPerm perms(ctx->uid, ctx->gid); | |
28e407b8 | 559 | get_fuse_groups(perms, req); |
7c673cae FG |
560 | |
561 | int r = cfuse->client->ll_open(in, fi->flags, &fh, perms); | |
562 | if (r == 0) { | |
563 | fi->fh = (uint64_t)fh; | |
564 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) | |
565 | if (cfuse->client->cct->_conf->fuse_disable_pagecache) | |
566 | fi->direct_io = 1; | |
567 | else if (cfuse->client->cct->_conf->fuse_use_invalidate_cb) | |
568 | fi->keep_cache = 1; | |
569 | #endif | |
570 | fuse_reply_open(req, fi); | |
571 | } else { | |
572 | fuse_reply_err(req, -r); | |
573 | } | |
574 | ||
575 | cfuse->iput(in); // iput required | |
576 | } | |
577 | ||
578 | static void fuse_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, | |
579 | struct fuse_file_info *fi) | |
580 | { | |
581 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
582 | Fh *fh = reinterpret_cast<Fh*>(fi->fh); | |
583 | bufferlist bl; | |
584 | int r = cfuse->client->ll_read(fh, off, size, &bl); | |
585 | if (r >= 0) | |
586 | fuse_reply_buf(req, bl.c_str(), bl.length()); | |
587 | else | |
588 | fuse_reply_err(req, -r); | |
589 | } | |
590 | ||
591 | static void fuse_ll_write(fuse_req_t req, fuse_ino_t ino, const char *buf, | |
592 | size_t size, off_t off, struct fuse_file_info *fi) | |
593 | { | |
594 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
595 | Fh *fh = reinterpret_cast<Fh*>(fi->fh); | |
596 | int r = cfuse->client->ll_write(fh, off, size, buf); | |
597 | if (r >= 0) | |
598 | fuse_reply_write(req, r); | |
599 | else | |
600 | fuse_reply_err(req, -r); | |
601 | } | |
602 | ||
603 | static void fuse_ll_flush(fuse_req_t req, fuse_ino_t ino, | |
604 | struct fuse_file_info *fi) | |
605 | { | |
606 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
607 | Fh *fh = reinterpret_cast<Fh*>(fi->fh); | |
608 | int r = cfuse->client->ll_flush(fh); | |
609 | fuse_reply_err(req, -r); | |
610 | } | |
611 | ||
612 | #ifdef FUSE_IOCTL_COMPAT | |
613 | static void fuse_ll_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg, struct fuse_file_info *fi, | |
614 | unsigned flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz) | |
615 | { | |
616 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
617 | ||
618 | if (flags & FUSE_IOCTL_COMPAT) { | |
619 | fuse_reply_err(req, ENOSYS); | |
620 | return; | |
621 | } | |
622 | ||
623 | switch (static_cast<unsigned>(cmd)) { | |
624 | case CEPH_IOC_GET_LAYOUT: { | |
625 | file_layout_t layout; | |
626 | struct ceph_ioctl_layout l; | |
627 | Fh *fh = (Fh*)fi->fh; | |
628 | cfuse->client->ll_file_layout(fh, &layout); | |
629 | l.stripe_unit = layout.stripe_unit; | |
630 | l.stripe_count = layout.stripe_count; | |
631 | l.object_size = layout.object_size; | |
632 | l.data_pool = layout.pool_id; | |
633 | fuse_reply_ioctl(req, 0, &l, sizeof(struct ceph_ioctl_layout)); | |
634 | } | |
635 | break; | |
636 | default: | |
637 | fuse_reply_err(req, EINVAL); | |
638 | } | |
639 | } | |
640 | #endif | |
641 | ||
642 | #if FUSE_VERSION > FUSE_MAKE_VERSION(2, 9) | |
643 | ||
644 | static void fuse_ll_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, | |
645 | off_t offset, off_t length, | |
646 | struct fuse_file_info *fi) | |
647 | { | |
648 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
649 | Fh *fh = (Fh*)fi->fh; | |
650 | int r = cfuse->client->ll_fallocate(fh, mode, offset, length); | |
651 | fuse_reply_err(req, -r); | |
652 | } | |
653 | ||
654 | #endif | |
655 | ||
656 | static void fuse_ll_release(fuse_req_t req, fuse_ino_t ino, | |
657 | struct fuse_file_info *fi) | |
658 | { | |
659 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
660 | Fh *fh = reinterpret_cast<Fh*>(fi->fh); | |
661 | int r = cfuse->client->ll_release(fh); | |
662 | fuse_reply_err(req, -r); | |
663 | } | |
664 | ||
665 | static void fuse_ll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, | |
666 | struct fuse_file_info *fi) | |
667 | { | |
668 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
669 | Fh *fh = reinterpret_cast<Fh*>(fi->fh); | |
670 | int r = cfuse->client->ll_fsync(fh, datasync); | |
671 | fuse_reply_err(req, -r); | |
672 | } | |
673 | ||
674 | struct readdir_context { | |
675 | fuse_req_t req; | |
676 | char *buf; | |
677 | size_t size; | |
678 | size_t pos; /* in buf */ | |
679 | uint64_t snap; | |
680 | }; | |
681 | ||
682 | /* | |
683 | * return 0 on success, -1 if out of space | |
684 | */ | |
685 | static int fuse_ll_add_dirent(void *p, struct dirent *de, | |
686 | struct ceph_statx *stx, off_t next_off, | |
687 | Inode *in) | |
688 | { | |
689 | struct readdir_context *c = (struct readdir_context *)p; | |
690 | CephFuse::Handle *cfuse = (CephFuse::Handle *)fuse_req_userdata(c->req); | |
691 | ||
692 | struct stat st; | |
693 | st.st_ino = cfuse->make_fake_ino(stx->stx_ino, c->snap); | |
694 | st.st_mode = stx->stx_mode; | |
695 | st.st_rdev = new_encode_dev(stx->stx_rdev); | |
696 | ||
697 | size_t room = c->size - c->pos; | |
698 | size_t entrysize = fuse_add_direntry(c->req, c->buf + c->pos, room, | |
699 | de->d_name, &st, next_off); | |
700 | if (entrysize > room) | |
701 | return -ENOSPC; | |
702 | ||
703 | /* success */ | |
704 | c->pos += entrysize; | |
705 | return 0; | |
706 | } | |
707 | ||
708 | static void fuse_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, | |
709 | off_t off, struct fuse_file_info *fi) | |
710 | { | |
711 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
712 | ||
713 | dir_result_t *dirp = reinterpret_cast<dir_result_t*>(fi->fh); | |
714 | cfuse->client->seekdir(dirp, off); | |
715 | ||
716 | struct readdir_context rc; | |
717 | rc.req = req; | |
718 | rc.buf = new char[size]; | |
719 | rc.size = size; | |
720 | rc.pos = 0; | |
721 | rc.snap = cfuse->fino_snap(ino); | |
722 | ||
723 | int r = cfuse->client->readdir_r_cb(dirp, fuse_ll_add_dirent, &rc); | |
724 | if (r == 0 || r == -ENOSPC) /* ignore ENOSPC from our callback */ | |
725 | fuse_reply_buf(req, rc.buf, rc.pos); | |
726 | else | |
727 | fuse_reply_err(req, -r); | |
728 | delete[] rc.buf; | |
729 | } | |
730 | ||
731 | static void fuse_ll_releasedir(fuse_req_t req, fuse_ino_t ino, | |
732 | struct fuse_file_info *fi) | |
733 | { | |
734 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
735 | dir_result_t *dirp = reinterpret_cast<dir_result_t*>(fi->fh); | |
736 | cfuse->client->ll_releasedir(dirp); | |
737 | fuse_reply_err(req, 0); | |
738 | } | |
739 | ||
740 | static void fuse_ll_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, | |
741 | struct fuse_file_info *fi) | |
742 | { | |
743 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
744 | dir_result_t *dirp = reinterpret_cast<dir_result_t*>(fi->fh); | |
745 | int r = cfuse->client->ll_fsyncdir(dirp); | |
746 | fuse_reply_err(req, -r); | |
747 | } | |
748 | ||
749 | static void fuse_ll_access(fuse_req_t req, fuse_ino_t ino, int mask) | |
750 | { | |
28e407b8 AA |
751 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); |
752 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
753 | Inode *in = cfuse->iget(ino); | |
754 | UserPerm perms(ctx->uid, ctx->gid); | |
755 | get_fuse_groups(perms, req); | |
756 | ||
757 | int r = cfuse->client->inode_permission(in, perms, mask); | |
758 | fuse_reply_err(req, -r); | |
759 | cfuse->iput(in); | |
7c673cae FG |
760 | } |
761 | ||
762 | static void fuse_ll_create(fuse_req_t req, fuse_ino_t parent, const char *name, | |
763 | mode_t mode, struct fuse_file_info *fi) | |
764 | { | |
765 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
766 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
767 | Inode *i1 = cfuse->iget(parent), *i2; | |
768 | struct fuse_entry_param fe; | |
769 | Fh *fh = NULL; | |
770 | UserPerm perms(ctx->uid, ctx->gid); | |
28e407b8 | 771 | get_fuse_groups(perms, req); |
7c673cae FG |
772 | |
773 | memset(&fe, 0, sizeof(fe)); | |
774 | ||
775 | // pass &i2 for the created inode so that ll_create takes an initial ll_ref | |
776 | int r = cfuse->client->ll_create(i1, name, mode, fi->flags, &fe.attr, &i2, | |
777 | &fh, perms); | |
778 | if (r == 0) { | |
779 | fi->fh = (uint64_t)fh; | |
780 | fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev); | |
781 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) | |
782 | if (cfuse->client->cct->_conf->fuse_disable_pagecache) | |
783 | fi->direct_io = 1; | |
784 | else if (cfuse->client->cct->_conf->fuse_use_invalidate_cb) | |
785 | fi->keep_cache = 1; | |
786 | #endif | |
787 | fuse_reply_create(req, &fe, fi); | |
788 | } else | |
789 | fuse_reply_err(req, -r); | |
790 | // XXX NB, we dont iput(i2) because FUSE will do so in a matching | |
791 | // fuse_ll_forget() | |
792 | cfuse->iput(i1); // iput required | |
793 | } | |
794 | ||
795 | static void fuse_ll_statfs(fuse_req_t req, fuse_ino_t ino) | |
796 | { | |
797 | struct statvfs stbuf; | |
798 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
799 | Inode *in = cfuse->iget(ino); | |
800 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
801 | UserPerm perms(ctx->uid, ctx->gid); | |
28e407b8 | 802 | get_fuse_groups(perms, req); |
7c673cae FG |
803 | |
804 | int r = cfuse->client->ll_statfs(in, &stbuf, perms); | |
805 | if (r == 0) | |
806 | fuse_reply_statfs(req, &stbuf); | |
807 | else | |
808 | fuse_reply_err(req, -r); | |
809 | ||
810 | cfuse->iput(in); // iput required | |
811 | } | |
812 | ||
813 | static void fuse_ll_getlk(fuse_req_t req, fuse_ino_t ino, | |
814 | struct fuse_file_info *fi, struct flock *lock) | |
815 | { | |
816 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
817 | Fh *fh = reinterpret_cast<Fh*>(fi->fh); | |
818 | ||
819 | int r = cfuse->client->ll_getlk(fh, lock, fi->lock_owner); | |
820 | if (r == 0) | |
821 | fuse_reply_lock(req, lock); | |
822 | else | |
823 | fuse_reply_err(req, -r); | |
824 | } | |
825 | ||
826 | static void fuse_ll_setlk(fuse_req_t req, fuse_ino_t ino, | |
827 | struct fuse_file_info *fi, struct flock *lock, int sleep) | |
828 | { | |
829 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
830 | Fh *fh = reinterpret_cast<Fh*>(fi->fh); | |
831 | ||
832 | // must use multithread if operation may block | |
833 | if (!cfuse->client->cct->_conf->fuse_multithreaded && | |
834 | sleep && lock->l_type != F_UNLCK) { | |
835 | fuse_reply_err(req, EDEADLK); | |
836 | return; | |
837 | } | |
838 | ||
839 | int r = cfuse->client->ll_setlk(fh, lock, fi->lock_owner, sleep); | |
840 | fuse_reply_err(req, -r); | |
841 | } | |
842 | ||
843 | static void fuse_ll_interrupt(fuse_req_t req, void* data) | |
844 | { | |
845 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
846 | cfuse->client->ll_interrupt(data); | |
847 | } | |
848 | ||
849 | static void switch_interrupt_cb(void *handle, void* data) | |
850 | { | |
851 | CephFuse::Handle *cfuse = (CephFuse::Handle *)handle; | |
852 | fuse_req_t req = cfuse->get_fuse_req(); | |
853 | ||
854 | if (data) | |
855 | fuse_req_interrupt_func(req, fuse_ll_interrupt, data); | |
856 | else | |
857 | fuse_req_interrupt_func(req, NULL, NULL); | |
858 | } | |
859 | ||
860 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9) | |
861 | static void fuse_ll_flock(fuse_req_t req, fuse_ino_t ino, | |
862 | struct fuse_file_info *fi, int cmd) | |
863 | { | |
864 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
865 | Fh *fh = (Fh*)fi->fh; | |
866 | ||
867 | // must use multithread if operation may block | |
868 | if (!cfuse->client->cct->_conf->fuse_multithreaded && | |
869 | !(cmd & (LOCK_NB | LOCK_UN))) { | |
870 | fuse_reply_err(req, EDEADLK); | |
871 | return; | |
872 | } | |
873 | ||
874 | int r = cfuse->client->ll_flock(fh, cmd, fi->lock_owner); | |
875 | fuse_reply_err(req, -r); | |
876 | } | |
877 | #endif | |
878 | ||
879 | #if !defined(DARWIN) | |
880 | static mode_t umask_cb(void *handle) | |
881 | { | |
882 | CephFuse::Handle *cfuse = (CephFuse::Handle *)handle; | |
883 | fuse_req_t req = cfuse->get_fuse_req(); | |
884 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
885 | return ctx->umask; | |
886 | } | |
887 | #endif | |
888 | ||
889 | static void ino_invalidate_cb(void *handle, vinodeno_t vino, int64_t off, | |
890 | int64_t len) | |
891 | { | |
892 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) | |
893 | CephFuse::Handle *cfuse = (CephFuse::Handle *)handle; | |
894 | fuse_ino_t fino = cfuse->make_fake_ino(vino.ino, vino.snapid); | |
895 | fuse_lowlevel_notify_inval_inode(cfuse->ch, fino, off, len); | |
896 | #endif | |
897 | } | |
898 | ||
899 | static void dentry_invalidate_cb(void *handle, vinodeno_t dirino, | |
900 | vinodeno_t ino, string& name) | |
901 | { | |
902 | CephFuse::Handle *cfuse = (CephFuse::Handle *)handle; | |
903 | fuse_ino_t fdirino = cfuse->make_fake_ino(dirino.ino, dirino.snapid); | |
904 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9) | |
905 | fuse_ino_t fino = 0; | |
906 | if (ino.ino != inodeno_t()) | |
907 | fino = cfuse->make_fake_ino(ino.ino, ino.snapid); | |
908 | fuse_lowlevel_notify_delete(cfuse->ch, fdirino, fino, name.c_str(), name.length()); | |
909 | #elif FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) | |
910 | fuse_lowlevel_notify_inval_entry(cfuse->ch, fdirino, name.c_str(), name.length()); | |
911 | #endif | |
912 | } | |
913 | ||
914 | static int remount_cb(void *handle) | |
915 | { | |
916 | // used for trimming kernel dcache. when remounting a file system, linux kernel | |
917 | // trims all unused dentries in the file system | |
918 | char cmd[1024]; | |
919 | CephFuse::Handle *cfuse = (CephFuse::Handle *)handle; | |
920 | snprintf(cmd, sizeof(cmd), "mount -i -o remount %s", cfuse->mountpoint); | |
921 | int r = system(cmd); | |
922 | if (r != 0 && r != -1) { | |
923 | r = WEXITSTATUS(r); | |
924 | } | |
925 | ||
926 | return r; | |
927 | } | |
928 | ||
929 | static void do_init(void *data, fuse_conn_info *conn) | |
930 | { | |
931 | CephFuse::Handle *cfuse = (CephFuse::Handle *)data; | |
932 | Client *client = cfuse->client; | |
933 | ||
934 | #if !defined(DARWIN) | |
935 | if (!client->cct->_conf->fuse_default_permissions && | |
936 | client->ll_handle_umask()) { | |
937 | // apply umask in userspace if posix acl is enabled | |
938 | if(conn->capable & FUSE_CAP_DONT_MASK) | |
939 | conn->want |= FUSE_CAP_DONT_MASK; | |
940 | } | |
941 | #endif | |
942 | ||
943 | if (cfuse->fd_on_success) { | |
944 | //cout << "fuse init signaling on fd " << fd_on_success << std::endl; | |
945 | // see Preforker::daemonize(), ceph-fuse's parent process expects a `-1` | |
946 | // from a daemonized child process. | |
947 | uint32_t r = -1; | |
948 | int err = safe_write(cfuse->fd_on_success, &r, sizeof(r)); | |
949 | if (err) { | |
950 | derr << "fuse_ll: do_init: safe_write failed with error " | |
951 | << cpp_strerror(err) << dendl; | |
952 | ceph_abort(); | |
953 | } | |
954 | //cout << "fuse init done signaling on fd " << fd_on_success << std::endl; | |
955 | ||
956 | // close stdout, etc. | |
957 | ::close(0); | |
958 | ::close(1); | |
959 | ::close(2); | |
960 | } | |
961 | } | |
962 | ||
963 | const static struct fuse_lowlevel_ops fuse_ll_oper = { | |
964 | init: do_init, | |
965 | destroy: 0, | |
966 | lookup: fuse_ll_lookup, | |
967 | forget: fuse_ll_forget, | |
968 | getattr: fuse_ll_getattr, | |
969 | setattr: fuse_ll_setattr, | |
970 | readlink: fuse_ll_readlink, | |
971 | mknod: fuse_ll_mknod, | |
972 | mkdir: fuse_ll_mkdir, | |
973 | unlink: fuse_ll_unlink, | |
974 | rmdir: fuse_ll_rmdir, | |
975 | symlink: fuse_ll_symlink, | |
976 | rename: fuse_ll_rename, | |
977 | link: fuse_ll_link, | |
978 | open: fuse_ll_open, | |
979 | read: fuse_ll_read, | |
980 | write: fuse_ll_write, | |
981 | flush: fuse_ll_flush, | |
982 | release: fuse_ll_release, | |
983 | fsync: fuse_ll_fsync, | |
984 | opendir: fuse_ll_opendir, | |
985 | readdir: fuse_ll_readdir, | |
986 | releasedir: fuse_ll_releasedir, | |
987 | fsyncdir: fuse_ll_fsyncdir, | |
988 | statfs: fuse_ll_statfs, | |
989 | setxattr: fuse_ll_setxattr, | |
990 | getxattr: fuse_ll_getxattr, | |
991 | listxattr: fuse_ll_listxattr, | |
992 | removexattr: fuse_ll_removexattr, | |
993 | access: fuse_ll_access, | |
994 | create: fuse_ll_create, | |
995 | getlk: fuse_ll_getlk, | |
996 | setlk: fuse_ll_setlk, | |
997 | bmap: 0, | |
998 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) | |
999 | #ifdef FUSE_IOCTL_COMPAT | |
1000 | ioctl: fuse_ll_ioctl, | |
1001 | #else | |
1002 | ioctl: 0, | |
1003 | #endif | |
1004 | poll: 0, | |
1005 | #endif | |
1006 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9) | |
1007 | write_buf: 0, | |
1008 | retrieve_reply: 0, | |
1009 | forget_multi: 0, | |
1010 | flock: fuse_ll_flock, | |
1011 | #endif | |
1012 | #if FUSE_VERSION > FUSE_MAKE_VERSION(2, 9) | |
1013 | fallocate: fuse_ll_fallocate | |
1014 | #endif | |
1015 | }; | |
1016 | ||
1017 | ||
1018 | CephFuse::Handle::Handle(Client *c, int fd) : | |
1019 | fd_on_success(fd), | |
1020 | client(c), | |
1021 | ch(NULL), | |
1022 | se(NULL), | |
1023 | mountpoint(NULL), | |
1024 | stag_lock("fuse_ll.cc stag_lock"), | |
1025 | last_stag(0) | |
1026 | { | |
1027 | snap_stag_map[CEPH_NOSNAP] = 0; | |
1028 | stag_snap_map[0] = CEPH_NOSNAP; | |
1029 | memset(&args, 0, sizeof(args)); | |
1030 | } | |
1031 | ||
1032 | CephFuse::Handle::~Handle() | |
1033 | { | |
1034 | fuse_opt_free_args(&args); | |
1035 | } | |
1036 | ||
1037 | void CephFuse::Handle::finalize() | |
1038 | { | |
1039 | if (se) | |
1040 | fuse_remove_signal_handlers(se); | |
1041 | if (ch) | |
1042 | fuse_session_remove_chan(ch); | |
1043 | if (se) | |
1044 | fuse_session_destroy(se); | |
1045 | if (ch) | |
1046 | fuse_unmount(mountpoint, ch); | |
1047 | ||
1048 | pthread_key_delete(fuse_req_key); | |
1049 | } | |
1050 | ||
1051 | int CephFuse::Handle::init(int argc, const char *argv[]) | |
1052 | { | |
1053 | ||
1054 | int r = pthread_key_create(&fuse_req_key, NULL); | |
1055 | if (r) { | |
1056 | derr << "pthread_key_create failed." << dendl; | |
1057 | return r; | |
1058 | } | |
1059 | ||
1060 | // set up fuse argc/argv | |
1061 | int newargc = 0; | |
1062 | const char **newargv = (const char **) malloc((argc + 10) * sizeof(char *)); | |
1063 | if(!newargv) | |
1064 | return ENOMEM; | |
1065 | ||
1066 | newargv[newargc++] = argv[0]; | |
1067 | newargv[newargc++] = "-f"; // stay in foreground | |
1068 | ||
1069 | if (client->cct->_conf->fuse_allow_other) { | |
1070 | newargv[newargc++] = "-o"; | |
1071 | newargv[newargc++] = "allow_other"; | |
1072 | } | |
1073 | if (client->cct->_conf->fuse_default_permissions) { | |
1074 | newargv[newargc++] = "-o"; | |
1075 | newargv[newargc++] = "default_permissions"; | |
1076 | } | |
1077 | #if defined(__linux__) | |
1078 | if (client->cct->_conf->fuse_big_writes) { | |
1079 | newargv[newargc++] = "-o"; | |
1080 | newargv[newargc++] = "big_writes"; | |
1081 | } | |
1082 | if (client->cct->_conf->fuse_atomic_o_trunc) { | |
1083 | newargv[newargc++] = "-o"; | |
1084 | newargv[newargc++] = "atomic_o_trunc"; | |
1085 | } | |
1086 | #endif | |
1087 | if (client->cct->_conf->fuse_debug) | |
1088 | newargv[newargc++] = "-d"; | |
1089 | ||
1090 | for (int argctr = 1; argctr < argc; argctr++) | |
1091 | newargv[newargc++] = argv[argctr]; | |
1092 | ||
1093 | derr << "init, newargv = " << newargv << " newargc=" << newargc << dendl; | |
1094 | struct fuse_args a = FUSE_ARGS_INIT(newargc, (char**)newargv); | |
1095 | args = a; // Roundabout construction b/c FUSE_ARGS_INIT is for initialization not assignment | |
1096 | ||
1097 | if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) == -1) { | |
1098 | derr << "fuse_parse_cmdline failed." << dendl; | |
1099 | fuse_opt_free_args(&args); | |
1100 | free(newargv); | |
1101 | return EINVAL; | |
1102 | } | |
1103 | ||
1104 | assert(args.allocated); // Checking fuse has realloc'd args so we can free newargv | |
1105 | free(newargv); | |
1106 | return 0; | |
1107 | } | |
1108 | ||
1109 | int CephFuse::Handle::start() | |
1110 | { | |
1111 | ch = fuse_mount(mountpoint, &args); | |
1112 | if (!ch) { | |
1113 | derr << "fuse_mount(mountpoint=" << mountpoint << ") failed." << dendl; | |
1114 | return EIO; | |
1115 | } | |
1116 | ||
1117 | se = fuse_lowlevel_new(&args, &fuse_ll_oper, sizeof(fuse_ll_oper), this); | |
1118 | if (!se) { | |
1119 | derr << "fuse_lowlevel_new failed" << dendl; | |
1120 | return EDOM; | |
1121 | } | |
1122 | ||
1123 | signal(SIGTERM, SIG_DFL); | |
1124 | signal(SIGINT, SIG_DFL); | |
1125 | if (fuse_set_signal_handlers(se) == -1) { | |
1126 | derr << "fuse_set_signal_handlers failed" << dendl; | |
1127 | return ENOSYS; | |
1128 | } | |
1129 | ||
1130 | fuse_session_add_chan(se, ch); | |
1131 | ||
1132 | ||
1133 | struct client_callback_args args = { | |
1134 | handle: this, | |
1135 | ino_cb: client->cct->_conf->fuse_use_invalidate_cb ? ino_invalidate_cb : NULL, | |
1136 | dentry_cb: dentry_invalidate_cb, | |
1137 | switch_intr_cb: switch_interrupt_cb, | |
1138 | #if defined(__linux__) | |
1139 | remount_cb: remount_cb, | |
1140 | #endif | |
7c673cae FG |
1141 | #if !defined(DARWIN) |
1142 | umask_cb: umask_cb, | |
1143 | #endif | |
1144 | }; | |
1145 | client->ll_register_callbacks(&args); | |
1146 | ||
1147 | return 0; | |
1148 | } | |
1149 | ||
1150 | int CephFuse::Handle::loop() | |
1151 | { | |
1152 | if (client->cct->_conf->fuse_multithreaded) { | |
1153 | return fuse_session_loop_mt(se); | |
1154 | } else { | |
1155 | return fuse_session_loop(se); | |
1156 | } | |
1157 | } | |
1158 | ||
1159 | uint64_t CephFuse::Handle::fino_snap(uint64_t fino) | |
1160 | { | |
1161 | if (fino == FUSE_ROOT_ID) | |
1162 | return CEPH_NOSNAP; | |
1163 | ||
1164 | if (client->use_faked_inos()) { | |
1165 | vinodeno_t vino = client->map_faked_ino(fino); | |
1166 | return vino.snapid; | |
1167 | } else { | |
1168 | Mutex::Locker l(stag_lock); | |
1169 | uint64_t stag = FINO_STAG(fino); | |
1170 | assert(stag_snap_map.count(stag)); | |
1171 | return stag_snap_map[stag]; | |
1172 | } | |
1173 | } | |
1174 | ||
1175 | Inode * CephFuse::Handle::iget(fuse_ino_t fino) | |
1176 | { | |
1177 | if (fino == FUSE_ROOT_ID) | |
1178 | return client->get_root(); | |
1179 | ||
1180 | if (client->use_faked_inos()) { | |
1181 | return client->ll_get_inode((ino_t)fino); | |
1182 | } else { | |
1183 | vinodeno_t vino(FINO_INO(fino), fino_snap(fino)); | |
1184 | return client->ll_get_inode(vino); | |
1185 | } | |
1186 | } | |
1187 | ||
1188 | void CephFuse::Handle::iput(Inode *in) | |
1189 | { | |
1190 | client->ll_put(in); | |
1191 | } | |
1192 | ||
1193 | uint64_t CephFuse::Handle::make_fake_ino(inodeno_t ino, snapid_t snapid) | |
1194 | { | |
1195 | if (client->use_faked_inos()) { | |
1196 | // already faked by libcephfs | |
1197 | if (ino == client->get_root_ino()) | |
1198 | return FUSE_ROOT_ID; | |
1199 | ||
1200 | return ino; | |
1201 | } else { | |
1202 | if (snapid == CEPH_NOSNAP && ino == client->get_root_ino()) | |
1203 | return FUSE_ROOT_ID; | |
1204 | ||
1205 | Mutex::Locker l(stag_lock); | |
1206 | uint64_t stag; | |
1207 | if (snap_stag_map.count(snapid) == 0) { | |
1208 | stag = ++last_stag; | |
1209 | snap_stag_map[snapid] = stag; | |
1210 | stag_snap_map[stag] = snapid; | |
1211 | } else | |
1212 | stag = snap_stag_map[snapid]; | |
1213 | inodeno_t fino = MAKE_FINO(ino, stag); | |
1214 | //cout << "make_fake_ino " << ino << "." << snapid << " -> " << fino << std::endl; | |
1215 | return fino; | |
1216 | } | |
1217 | } | |
1218 | ||
1219 | void CephFuse::Handle::set_fuse_req(fuse_req_t req) | |
1220 | { | |
1221 | pthread_setspecific(fuse_req_key, (void*)req); | |
1222 | } | |
1223 | ||
1224 | fuse_req_t CephFuse::Handle::get_fuse_req() | |
1225 | { | |
1226 | return (fuse_req_t) pthread_getspecific(fuse_req_key); | |
1227 | } | |
1228 | ||
1229 | ||
1230 | CephFuse::CephFuse(Client *c, int fd) : _handle(new CephFuse::Handle(c, fd)) | |
1231 | { | |
1232 | } | |
1233 | ||
1234 | CephFuse::~CephFuse() | |
1235 | { | |
1236 | delete _handle; | |
1237 | } | |
1238 | ||
1239 | int CephFuse::init(int argc, const char *argv[]) | |
1240 | { | |
1241 | return _handle->init(argc, argv); | |
1242 | } | |
1243 | ||
1244 | int CephFuse::start() | |
1245 | { | |
1246 | return _handle->start(); | |
1247 | } | |
1248 | ||
1249 | int CephFuse::loop() | |
1250 | { | |
1251 | return _handle->loop(); | |
1252 | } | |
1253 | ||
1254 | void CephFuse::finalize() | |
1255 | { | |
1256 | return _handle->finalize(); | |
1257 | } | |
1258 | ||
1259 | std::string CephFuse::get_mount_point() const | |
1260 | { | |
1261 | if (_handle->mountpoint) { | |
1262 | return _handle->mountpoint; | |
1263 | } else { | |
1264 | return ""; | |
1265 | } | |
1266 | } |