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