]> git.proxmox.com Git - ceph.git/blob - ceph/fusetrace/fusetrace_ll.cc
import 15.2.4
[ceph.git] / ceph / fusetrace / fusetrace_ll.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:4; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=4 smarttab
3
4 /*
5 FUSE: Filesystem in Userspace
6 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
7
8 This program can be distributed under the terms of the GNU GPL.
9 See the file COPYING.
10
11 gcc -Wall `pkg-config fuse --cflags --libs` -lulockmgr fusexmp_fh.c -o fusexmp_fh
12 */
13
14 #ifdef HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17
18
19 #include <fuse/fuse_lowlevel.h>
20 #include <ulockmgr.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <dirent.h>
26 #include <errno.h>
27 #include <sys/time.h>
28 #ifdef HAVE_SETXATTR
29 #include <sys/xattr.h>
30 #endif
31 #include <time.h>
32
33
34 #include "include/unordered_map.h"
35 #include "include/hash_namespace.h"
36
37 #ifndef __LP64__
38 CEPH_HASH_NAMESPACE_START
39 template<> struct hash<uint64_t> {
40 size_t operator()(uint64_t __x) const {
41 static hash<uint32_t> H;
42 return H((__x >> 32) ^ (__x & 0xffffffff));
43 }
44 };
45 CEPH_HASH_NAMESPACE_END
46 #endif
47
48
49 #include <iostream>
50 #include <fstream>
51 #include <map>
52 #include <set>
53 using namespace std;
54
55 #include "common/ceph_mutex.h"
56
57 ceph::mutex trace_lock;
58 ofstream tracefile;
59
60 #define traceout (tracefile.is_open() ? tracefile : cout)
61
62 char *basedir = 0;
63 int debug = 0;
64 bool do_timestamps = true;
65
66 #define dout if (debug) cout
67
68 ceph::mutex lock;
69
70 struct Inode {
71 struct stat stbuf;
72 int ref;
73 set<int> fds;
74
75 map<pair<string,ino_t>,Inode*> parents;
76
77 // if dir,
78 map<string,Inode*> dentries;
79
80 Inode() : ref(0) {}
81
82 Inode *lookup(const string& dname) {
83 if (dentries.count(dname))
84 return dentries[dname];
85 return 0;
86 }
87 };
88
89 Inode *root = 0;
90 ceph::unordered_map<ino_t, Inode*> inode_map;
91
92 bool make_inode_path(string &buf, Inode *in)
93 {
94 if (!in->parents.empty()) {
95 if (!make_inode_path(buf, in->parents.begin()->second))
96 return false;
97 buf += "/";
98 buf += in->parents.begin()->first.first;
99 } else {
100 if (in != root) return false;
101 assert(in->stbuf.st_ino == 1);
102 buf = basedir;
103 buf += "/";
104 }
105 return true;
106 //dout << "path: " << in->stbuf.st_ino << " -> " << buf << endl;
107 }
108
109 bool make_inode_path(string &buf, Inode *in, const char *name)
110 {
111 if (!make_inode_path(buf, in)) return false;
112 buf += "/";
113 buf += name;
114 return true;
115 }
116
117 bool make_ino_path(string &buf, ino_t ino)
118 {
119 Inode *in = inode_map[ino];
120 assert(in);
121 return make_inode_path(buf, in);
122 }
123
124 bool make_ino_path(string &buf, ino_t ino, const char *name)
125 {
126 Inode *in = inode_map[ino];
127 assert(in);
128 if (!make_inode_path(buf, in))
129 return false;
130 buf += "/";
131 buf += name;
132 return true;
133 }
134
135 void remove_dentry(Inode *pin, const string& dname)
136 {
137 dout << "remove_dentry " << pin->stbuf.st_ino << " " << dname << endl;
138
139 Inode *in = pin->lookup(dname);
140 assert(in);
141 pin->dentries.erase(dname);
142 in->parents.erase(pair<string,ino_t>(dname,pin->stbuf.st_ino));
143
144 dout << "remove_dentry " << pin->stbuf.st_ino << " " << dname
145 << " ... inode " << in->stbuf.st_ino << " ref " << in->ref
146 << endl;
147 }
148
149 void add_dentry(Inode *parent, const string& dname, Inode *in)
150 {
151 dout << "add_dentry " << parent->stbuf.st_ino << " " << dname << " to " << in->stbuf.st_ino << endl;
152
153 if (parent->dentries.count(dname))
154 remove_dentry(parent, dname); // e.g., when renaming over another file..
155
156 parent->dentries[dname] = in;
157 in->parents[pair<string,ino_t>(dname,parent->stbuf.st_ino)] = parent;
158 }
159
160 void unlink_inode(Inode *in)
161 {
162 dout << "unlink_inode " << in->stbuf.st_ino << " ref " << in->ref << endl;
163
164 // remove parent links
165 while (!in->parents.empty()) {
166 Inode *parent = in->parents.begin()->second;
167 string dname = in->parents.begin()->first.first;
168 remove_dentry(parent, dname);
169 }
170
171 // remove children
172 while (!in->dentries.empty())
173 remove_dentry(in, in->dentries.begin()->first);
174
175 while (!in->fds.empty()) {
176 int fd = *in->fds.begin();
177 ::close(fd);
178 in->fds.erase(in->fds.begin());
179 dout << "remove_inode closeing stray fd " << fd << endl;
180 }
181 }
182
183 void remove_inode(Inode *in)
184 {
185 dout << "remove_inode " << in->stbuf.st_ino << " ref " << in->ref << endl;
186
187 unlink_inode(in);
188
189 inode_map.erase(in->stbuf.st_ino);
190 dout << "remove_inode " << in->stbuf.st_ino << " done" << endl;
191 delete in;
192 }
193
194 Inode *add_inode(Inode *parent, const char *name, struct stat *attr)
195 {
196 dout << "add_inode " << parent->stbuf.st_ino << " " << name << " " << attr->st_ino << endl;
197
198 Inode *in;
199 if (inode_map.count(attr->st_ino)) {
200 // reuse inode
201 in = inode_map[attr->st_ino];
202 unlink_inode(in); // hrm.. should this close open fds? probably.
203 dout << "** REUSING INODE **" << endl;
204 } else {
205 inode_map[attr->st_ino] = in = new Inode;
206 }
207 memcpy(&in->stbuf, attr, sizeof(*attr));
208
209 string dname(name);
210 add_dentry(parent, dname, in);
211
212 return in;
213 }
214
215
216 void print_time()
217 {
218 if (do_timestamps) {
219 struct timeval tv;
220 gettimeofday(&tv, 0);
221 traceout << "@" << endl
222 << tv.tv_sec << endl
223 << tv.tv_usec << endl;
224 }
225 }
226
227
228 bool has_perm(int mask, Inode *in, int uid, int gid)
229 {
230 dout << "hash_perm " << uid << "." << gid << " " << oct << mask << " in " << in->stbuf.st_mode
231 << " " << in->stbuf.st_uid << "." << in->stbuf.st_gid << endl;
232 if (in->stbuf.st_mode & mask) return true;
233 if (in->stbuf.st_gid == gid && in->stbuf.st_mode & (mask << 3)) return true;
234 if (in->stbuf.st_uid == uid && in->stbuf.st_mode & (mask << 6)) return true;
235 return false;
236 }
237
238
239 static void ft_ll_lookup(fuse_req_t req, fuse_ino_t pino, const char *name)
240 {
241 int res = 0;
242
243 //dout << "lookup " << pino << " " << name << endl;
244
245 struct fuse_entry_param fe;
246 memset(&fe, 0, sizeof(fe));
247
248 lock.lock();
249 Inode *parent = inode_map[pino];
250 assert(parent);
251
252 // check permissions
253
254 string dname(name);
255 string path;
256 Inode *in = 0;
257 if (!has_perm(0001, parent, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid)) {
258 res = EPERM;
259 }
260 else if (!make_inode_path(path, parent, name)) {
261 res = ENOENT;
262 } else {
263 in = parent->lookup(dname);
264 if (in && res == 0) {
265 // re-stat, for good measure
266 res = ::lstat(path.c_str(), &in->stbuf);
267
268 // hrm!
269 if (res != 0) {
270 dout << "** WEIRD ** lookup on " << pino << " " << name << " inode went away!" << endl;
271 in = 0;
272 res = errno;
273 }
274
275 //dout << "have " << in->stbuf.st_ino << endl;
276 } else {
277 in = new Inode;
278 res = ::lstat(path.c_str(), &in->stbuf);
279 //dout << "stat " << path << " res = " << res << endl;
280 if (res == 0) {
281 inode_map[in->stbuf.st_ino] = in;
282 add_dentry(parent, dname, in);
283 } else {
284 delete in;
285 in = 0;
286 res = errno;
287 }
288 }
289 if (in) {
290 in->ref++;
291 fe.ino = in->stbuf.st_ino;
292 memcpy(&fe.attr, &in->stbuf, sizeof(in->stbuf));
293 }
294 }
295 lock.unlock();
296
297 trace_lock.lock();
298 print_time();
299 traceout << "ll_lookup" << endl << pino << endl << name << endl << fe.attr.st_ino << endl;
300 trace_lock.unlock();
301
302 if (in)
303 fuse_reply_entry(req, &fe);
304 else
305 fuse_reply_err(req, res);
306 }
307
308 static void ft_ll_forget(fuse_req_t req, fuse_ino_t ino, long unsigned nlookup)
309 {
310 if (ino != 1) {
311 std::scoped_lock l{lock};
312 Inode *in = inode_map[ino];
313 if (in) {
314 dout << "forget on " << ino << " ref " << in->ref << ", forget " << nlookup << endl;
315 if (in->ref < nlookup)
316 dout << "**** BAD **** forget on " << ino << " ref " << in->ref << ", forget " << nlookup << endl;
317
318 in->ref -= nlookup;
319 if (in->ref <= 0)
320 remove_inode(in);
321 } else {
322 dout << "**** BAD **** forget " << nlookup << " on nonexistent inode " << ino << endl;
323 }
324 }
325
326 {
327 std::scoped_lock l{trace_lock};
328 print_time();
329 traceout << "ll_forget" << endl << ino << endl << nlookup << endl;
330 }
331
332 fuse_reply_none(req);
333 }
334
335 static void ft_ll_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
336 {
337 int res = 0;
338 string path;
339 int fd = 0;
340
341 Inode *in = 0;
342 struct stat attr;
343
344 lock.lock();
345 in = inode_map[ino];
346 if (in->fds.empty()) {
347 if (!make_inode_path(path, in))
348 res = ENOENT;
349 } else
350 fd = *in->fds.begin();
351 lock.unlock();
352
353 if (fd > 0) {
354 res = ::fstat(fd, &attr);
355 dout << "getattr fstat on fd " << fd << " res " << res << endl;
356 } else if (res == 0) {
357 res = ::lstat(path.c_str(), &attr);
358 dout << "getattr lstat on " << path << " res " << res << endl;
359 }
360 if (res < 0) res = errno;
361 if (ino == 1) attr.st_ino = 1;
362
363 trace_lock.lock();
364 print_time();
365 traceout << "ll_getattr" << endl << ino << endl;
366 trace_lock.unlock();
367
368 if (res == 0) {
369 lock.lock();
370 memcpy(&in->stbuf, &attr, sizeof(attr));
371 lock.unlock();
372 fuse_reply_attr(req, &attr, 0);
373 } else
374 fuse_reply_err(req, res);
375 }
376
377 static void ft_ll_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
378 int to_set, struct fuse_file_info *fi)
379 {
380 string path;
381 Inode *in = 0;
382 int fd = 0;
383 int res = 0;
384
385 lock.lock();
386 in = inode_map[ino];
387 if (in->fds.empty() || (to_set & FUSE_SET_ATTR_MTIME)) {
388 if (!make_inode_path(path, in))
389 res = ENOENT;
390 } else
391 fd = *in->fds.begin();
392 lock.unlock();
393
394 trace_lock.lock();
395 print_time();
396 traceout << "ll_setattr" << endl << ino << endl;
397 traceout << attr->st_mode << endl;
398 traceout << attr->st_uid << endl << attr->st_gid << endl;
399 traceout << attr->st_size << endl;
400 traceout << attr->st_mtime << endl;
401 traceout << attr->st_atime << endl;
402 traceout << to_set << endl;
403 trace_lock.unlock();
404
405 if (res == 0 && !has_perm(0010, in, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid)) {
406 res = EPERM;
407 } else if (res == 0) {
408 if (to_set & FUSE_SET_ATTR_MODE) {
409 if (fd > 0)
410 res = ::fchmod(fd, attr->st_mode);
411 else
412 res = ::chmod(path.c_str(), attr->st_mode);
413 }
414 if (!res && to_set & FUSE_SET_ATTR_UID) {
415 if (fd > 0)
416 res = ::fchown(fd, attr->st_uid, attr->st_gid);
417 else
418 res = ::chown(path.c_str(), attr->st_uid, attr->st_gid);
419 }
420 if (!res && to_set & FUSE_SET_ATTR_SIZE) {
421 if (fd > 0)
422 res = ::ftruncate(fd, attr->st_size);
423 else
424 res = ::truncate(path.c_str(), attr->st_size);
425 }
426 if (!res && to_set & FUSE_SET_ATTR_MTIME) {
427 struct utimbuf ut;
428 ut.actime = attr->st_atime;
429 ut.modtime = attr->st_mtime;
430 res = ::utime(path.c_str(), &ut);
431 }
432 if (res < 0) res = errno;
433 }
434
435 if (res == 0) {
436 lock.lock();
437 ::lstat(path.c_str(), &in->stbuf);
438 if (ino == 1) in->stbuf.st_ino = 1;
439 memcpy(attr, &in->stbuf, sizeof(*attr));
440 lock.unlock();
441 fuse_reply_attr(req, attr, 0);
442 } else
443 fuse_reply_err(req, res);
444 }
445
446
447 static void ft_ll_readlink(fuse_req_t req, fuse_ino_t ino)
448 {
449 string path;
450 int res = 0;
451
452 lock.lock();
453 if (!make_ino_path(path, ino))
454 res = ENOENT;
455 lock.unlock();
456
457 trace_lock.lock();
458 print_time();
459 traceout << "ll_readlink" << endl << ino << endl;
460 trace_lock.unlock();
461
462 char buf[256];
463 if (res == 0) res = readlink(path.c_str(), buf, 255);
464 if (res < 0) res = errno;
465
466 if (res >= 0) {
467 buf[res] = 0;
468 fuse_reply_readlink(req, buf);
469 } else {
470 fuse_reply_err(req, res);
471 }
472 }
473
474
475 static void ft_ll_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
476 {
477 string path;
478 int res = 0;
479 lock.lock();
480 Inode *in = inode_map[ino];
481 if (!make_inode_path(path, in))
482 res = ENOENT;
483 lock.unlock();
484
485 DIR *dir = 0;
486 if (res == 0 && !has_perm(0100, in, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid))
487 res = EPERM;
488 else if (res == 0) dir = opendir(path.c_str());
489 if (res < 0) res = errno;
490
491 trace_lock.lock();
492 print_time();
493 traceout << "ll_opendir" << endl << ino << endl << (unsigned long)dir << endl;
494 trace_lock.unlock();
495
496 if (dir) {
497 fi->fh = (long)dir;
498 fuse_reply_open(req, fi);
499 } else
500 fuse_reply_err(req, res);
501 }
502
503 static void ft_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
504 off_t off, struct fuse_file_info *fi)
505 {
506 struct dirent *de;
507 DIR *dp = (DIR*)fi->fh;
508
509 // buffer
510 char *buf;
511 size_t pos = 0;
512
513 buf = new char[size];
514 if (!buf) {
515 fuse_reply_err(req, ENOMEM);
516 return;
517 }
518
519 seekdir(dp, off);
520 while ((de = readdir(dp)) != NULL) {
521 struct stat st;
522 memset(&st, 0, sizeof(st));
523 st.st_ino = de->d_ino;
524 st.st_mode = de->d_type << 12;
525
526 size_t entrysize = fuse_add_direntry(req, buf + pos, size - pos,
527 de->d_name, &st, telldir(dp));
528 if (entrysize > size - pos)
529 break; // didn't fit, done for now.
530 pos += entrysize;
531 }
532
533 fuse_reply_buf(req, buf, pos);
534 delete[] buf;
535 }
536
537 static void ft_ll_releasedir(fuse_req_t req, fuse_ino_t ino,
538 struct fuse_file_info *fi)
539 {
540 DIR *dir = (DIR*)fi->fh;
541
542 trace_lock.lock();
543 print_time();
544 traceout << "ll_releasedir" << endl << (unsigned long)dir << endl;
545 trace_lock.unlock();
546
547 closedir(dir);
548 fuse_reply_err(req, 0);
549 }
550
551
552
553 static void ft_ll_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
554 mode_t mode, dev_t rdev)
555 {
556 string path;
557 Inode *pin = 0;
558 int res = 0;
559 lock.lock();
560 pin = inode_map[parent];
561 if (!make_inode_path(path, pin, name))
562 res = ENOENT;
563 lock.unlock();
564
565 dout << "mknod " << path << endl;
566 if (res == 0 && !has_perm(0010, pin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid))
567 res = EPERM;
568 else if (res == 0) res = ::mknod(path.c_str(), mode, rdev);
569 if (res < 0)
570 res = errno;
571 else
572 ::chown(path.c_str(), fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid);
573
574 struct fuse_entry_param fe;
575 if (res == 0) {
576 memset(&fe, 0, sizeof(fe));
577 ::lstat(path.c_str(), &fe.attr);
578 fe.ino = fe.attr.st_ino;
579 lock.lock();
580 Inode *in = add_inode(pin, name, &fe.attr);
581 in->ref++;
582 lock.unlock();
583 }
584
585 trace_lock.lock();
586 print_time();
587 traceout << "ll_mknod" << endl << parent << endl << name << endl << mode << endl << rdev << endl;
588 traceout << (res == 0 ? fe.ino:0) << endl;
589 trace_lock.unlock();
590
591 if (res == 0)
592 fuse_reply_entry(req, &fe);
593 else
594 fuse_reply_err(req, res);
595 }
596
597 static void ft_ll_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
598 mode_t mode)
599 {
600 string path;
601 Inode *pin = 0;
602 int res = 0;
603 lock.lock();
604 pin = inode_map[parent];
605 if (!make_inode_path(path, pin, name))
606 res = ENOENT;
607 lock.unlock();
608
609 if (res == 0 && !has_perm(0010, pin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid))
610 res = EPERM;
611 else if (res == 0) res = ::mkdir(path.c_str(), mode);
612 if (res < 0)
613 res = errno;
614 else
615 ::chown(path.c_str(), fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid);
616
617 struct fuse_entry_param fe;
618 if (res == 0) {
619 memset(&fe, 0, sizeof(fe));
620 ::lstat(path.c_str(), &fe.attr);
621 fe.ino = fe.attr.st_ino;
622 lock.lock();
623 Inode *in = add_inode(pin, name, &fe.attr);
624 in->ref++;
625 lock.unlock();
626 }
627
628 trace_lock.lock();
629 print_time();
630 traceout << "ll_mkdir" << endl << parent << endl << name << endl << mode << endl;
631 traceout << (res == 0 ? fe.ino:0) << endl;
632 trace_lock.unlock();
633
634 if (res == 0)
635 fuse_reply_entry(req, &fe);
636 else
637 fuse_reply_err(req, res);
638 }
639
640 static void ft_ll_symlink(fuse_req_t req, const char *value, fuse_ino_t parent, const char *name)
641 {
642 string path;
643 Inode *pin = 0;
644 int res = 0;
645
646 lock.lock();
647 pin = inode_map[parent];
648 if (!make_inode_path(path, pin, name))
649 res = ENOENT;
650 lock.unlock();
651
652 if (res == 0 && !has_perm(0010, pin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid))
653 res = EPERM;
654 else if (res == 0) res = ::symlink(value, path.c_str());
655 if (res < 0)
656 res = errno;
657 else
658 ::chown(path.c_str(), fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid);
659
660 struct fuse_entry_param fe;
661 if (res == 0) {
662 memset(&fe, 0, sizeof(fe));
663 ::lstat(path.c_str(), &fe.attr);
664 fe.ino = fe.attr.st_ino;
665 lock.lock();
666 Inode *in = add_inode(pin, name, &fe.attr);
667 in->ref++;
668 lock.unlock();
669 }
670
671 trace_lock.lock();
672 print_time();
673 traceout << "ll_symlink" << endl << parent << endl << name << endl << value << endl;
674 traceout << (res == 0 ? fe.ino:0) << endl;
675 trace_lock.unlock();
676
677 if (res == 0)
678 fuse_reply_entry(req, &fe);
679 else
680 fuse_reply_err(req, res);
681 }
682
683 static void ft_ll_create(fuse_req_t req, fuse_ino_t parent, const char *name,
684 mode_t mode, struct fuse_file_info *fi)
685 {
686 string path;
687 Inode *pin = 0;
688 int res = 0;
689
690 lock.lock();
691 pin = inode_map[parent];
692 if (!make_inode_path(path, pin, name))
693 res = ENOENT;
694 lock.unlock();
695
696 dout << "create " << path << endl;
697 int fd = 0;
698 if (res == 0 && !has_perm(0010, pin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid))
699 res = EPERM;
700 else if (res == 0) {
701 fd = ::open(path.c_str(), fi->flags|O_CREAT, mode);
702 if (fd < 0) {
703 res = errno;
704 } else {
705 ::fchown(fd, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid);
706 }
707 }
708
709 struct fuse_entry_param fe;
710 memset(&fe, 0, sizeof(fe));
711 if (res == 0) {
712 ::lstat(path.c_str(), &fe.attr);
713 fe.ino = fe.attr.st_ino;
714 lock.lock();
715 Inode *in = add_inode(pin, name, &fe.attr);
716 in->ref++;
717 in->fds.insert(fd);
718 lock.unlock();
719 fi->fh = fd;
720 }
721
722 trace_lock.lock();
723 print_time();
724 traceout << "ll_create" << endl
725 << parent << endl
726 << name << endl
727 << mode << endl
728 << fi->flags << endl
729 << (res == 0 ? fd:0) << endl
730 << fe.ino << endl;
731 trace_lock.unlock();
732
733 if (res == 0)
734 fuse_reply_create(req, &fe, fi);
735 else
736 fuse_reply_err(req, res);
737
738 }
739
740
741 static void ft_ll_statfs(fuse_req_t req, fuse_ino_t ino)
742 {
743 string path;
744 int res = 0;
745 if (ino) {
746 lock.lock();
747 if (!make_ino_path(path, ino))
748 res = ENOENT;
749 lock.unlock();
750 } else {
751 path = basedir;
752 }
753
754 trace_lock.lock();
755 print_time();
756 traceout << "ll_statfs" << endl << ino << endl;
757 trace_lock.unlock();
758
759 struct statvfs stbuf;
760 if (res == 0) res = statvfs(path.c_str(), &stbuf);
761 if (res < 0) res = errno;
762
763 if (res == 0)
764 fuse_reply_statfs(req, &stbuf);
765 else
766 fuse_reply_err(req, res);
767 }
768
769 static void ft_ll_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
770 {
771 string path;
772 Inode *pin = 0;
773 Inode *in = 0;
774 string dname(name);
775 int res = 0;
776 lock.lock();
777 pin = inode_map[parent];
778 in = pin->lookup(dname);
779 if (!make_inode_path(path, pin, name))
780 res = ENOENT;
781 lock.unlock();
782
783 trace_lock.lock();
784 print_time();
785 traceout << "ll_unlink" << endl << parent << endl << name << endl;
786 trace_lock.unlock();
787
788 if (res == 0 && !has_perm(0010, pin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid))
789 res = EPERM;
790 else if (res == 0) {
791 if (in && in->fds.empty()) {
792 int fd = ::open(path.c_str(), O_RDWR);
793 if (fd > 0)
794 in->fds.insert(fd); // for slow getattrs.. wtf
795 dout << "unlink opening paranoia fd " << fd << endl;
796 }
797 res = ::unlink(path.c_str());
798 if (res < 0) res = errno;
799 }
800
801 if (res == 0) {
802 // remove from out cache
803 lock.lock();
804 string dname(name);
805 if (pin->lookup(dname))
806 remove_dentry(pin, dname);
807 lock.unlock();
808 fuse_reply_err(req, 0);
809 } else
810 fuse_reply_err(req, res);
811 }
812
813 static void ft_ll_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
814 {
815 string path;
816 Inode *pin = 0;
817 int res = 0;
818
819 lock.lock();
820 pin = inode_map[parent];
821 if (!make_inode_path(path, pin, name))
822 res = ENOENT;
823 lock.unlock();
824
825 trace_lock.lock();
826 print_time();
827 traceout << "ll_rmdir" << endl << parent << endl << name << endl;
828 trace_lock.unlock();
829
830 if (res == 0 && !has_perm(0010, pin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid))
831 res = EPERM;
832 else if (res == 0) res = ::rmdir(path.c_str());
833 if (res < 0) res = errno;
834
835 if (res == 0) {
836 // remove from out cache
837 lock.lock();
838 string dname(name);
839 if (pin->lookup(dname))
840 remove_dentry(pin, dname);
841 lock.unlock();
842 fuse_reply_err(req, 0);
843 } else
844 fuse_reply_err(req, res);
845 }
846
847
848 static void ft_ll_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
849 fuse_ino_t newparent, const char *newname)
850 {
851 string path;
852 string newpath;
853 Inode *pin = 0;
854 Inode *newpin = 0;
855 int res = 0;
856 lock.lock();
857 pin = inode_map[parent];
858 if (!make_inode_path(path, pin, name))
859 res = ENOENT;
860 newpin = inode_map[newparent];
861 if (!make_inode_path(newpath, newpin, newname))
862 res = ENOENT;
863 lock.unlock();
864
865 trace_lock.lock();
866 print_time();
867 traceout << "ll_rename" << endl
868 << parent << endl
869 << name << endl
870 << newparent << endl
871 << newname << endl;
872 trace_lock.unlock();
873
874 if (res == 0 && (!has_perm(0010, pin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid) ||
875 !has_perm(0010, newpin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid)))
876 res = EPERM;
877 else if (res == 0) res = ::rename(path.c_str(), newpath.c_str());
878 if (res < 0) res = errno;
879
880 if (res == 0) {
881 string dname(name);
882 string newdname(newname);
883 lock.lock();
884 Inode *in = pin->lookup(dname);
885 if (in) {
886 add_dentry(newpin, newdname, in);
887 remove_dentry(pin, dname);
888 } else {
889 dout << "hrm, rename didn't have renamed inode.. " << path << " to " << newpath << endl;
890 }
891 lock.unlock();
892 fuse_reply_err(req, 0);
893 } else
894 fuse_reply_err(req, res);
895 }
896
897 static void ft_ll_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
898 const char *newname)
899 {
900 string path;
901 string newpath;
902 Inode *in = 0;
903 Inode *newpin = 0;
904 int res = 0;
905 lock.lock();
906 in = inode_map[ino];
907 if (!make_inode_path(path, in))
908 res = ENOENT;
909
910 newpin = inode_map[newparent];
911 if (!make_inode_path(newpath, newpin, newname))
912 res = ENOENT;
913 lock.unlock();
914
915 trace_lock.lock();
916 print_time();
917 traceout << "ll_link" << endl
918 << ino << endl
919 << newparent << endl
920 << newname << endl;
921 trace_lock.unlock();
922
923 //cout << "link " << path << " newpath " << newpath << endl;
924 if (res == 0 && (!has_perm(0010, in, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid) ||
925 !has_perm(0010, newpin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid)))
926 res = EPERM;
927 else if (res == 0) res = ::link(path.c_str(), newpath.c_str());
928 if (res < 0) res = errno;
929
930 if (res == 0) {
931 struct fuse_entry_param fe;
932 memset(&fe, 0, sizeof(fe));
933 ::lstat(newpath.c_str(), &fe.attr);
934
935 lock.lock();
936 string newdname(newname);
937 add_dentry(newpin, newdname, in);
938 in->ref++;
939 memcpy(&in->stbuf, &fe.attr, sizeof(fe.attr)); // re-read, bc we changed the link count
940 lock.unlock();
941
942 fe.ino = fe.attr.st_ino;
943 fuse_reply_entry(req, &fe);
944 } else
945 fuse_reply_err(req, res);
946 }
947
948 static void ft_ll_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
949 {
950 string path;
951 Inode *in = 0;
952 int res = 0;
953
954 lock.lock();
955 in = inode_map[ino];
956 if (!make_inode_path(path, in))
957 res = ENOENT;
958 lock.unlock();
959
960 int want = 0100;
961 if (fi->flags & O_RDWR) want |= 0010;
962 if (fi->flags == O_WRONLY) want = 0010;
963
964 int fd = 0;
965 if (res == 0 && !has_perm(want, in, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid))
966 res = EPERM;
967 else if (res == 0) {
968 fd = ::open(path.c_str(), fi->flags);
969 if (fd <= 0) res = errno;
970 }
971
972 trace_lock.lock();
973 print_time();
974 traceout << "ll_open" << endl
975 << ino << endl
976 << fi->flags << endl
977 << (fd > 0 ? fd:0) << endl;
978 trace_lock.unlock();
979
980 if (res == 0) {
981 lock.lock();
982 in->fds.insert(fd);
983 lock.unlock();
984 fi->fh = fd;
985 fuse_reply_open(req, fi);
986 } else
987 fuse_reply_err(req, res);
988 }
989
990 static void ft_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
991 struct fuse_file_info *fi)
992 {
993
994 char *buf = new char[size];
995 int res = ::pread(fi->fh, buf, size, off);
996
997 //cout << "read " << path << " " << off << "~" << size << endl;
998 trace_lock.lock();
999 print_time();
1000 traceout << "ll_read" << endl
1001 << fi->fh << endl
1002 << off << endl
1003 << size << endl;
1004 trace_lock.unlock();
1005
1006 if (res >= 0)
1007 fuse_reply_buf(req, buf, res);
1008 else
1009 fuse_reply_err(req, errno);
1010 delete[] buf;
1011 }
1012
1013 static void ft_ll_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
1014 size_t size, off_t off, struct fuse_file_info *fi)
1015 {
1016 int res = ::pwrite(fi->fh, buf, size, off);
1017
1018 trace_lock.lock();
1019 print_time();
1020 traceout << "ll_write" << endl
1021 << fi->fh << endl
1022 << off << endl
1023 << size << endl;
1024 trace_lock.unlock();
1025
1026 if (res >= 0)
1027 fuse_reply_write(req, res);
1028 else
1029 fuse_reply_err(req, errno);
1030 }
1031
1032 static void ft_ll_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
1033 {
1034 trace_lock.lock();
1035 print_time();
1036 traceout << "ll_flush" << endl << fi->fh << endl;
1037 trace_lock.unlock();
1038
1039 int res = ::fdatasync(fi->fh);
1040 //int res = ::close(dup(fi->fh));
1041 if (res >= 0)
1042 fuse_reply_err(req, 0);
1043 else
1044 fuse_reply_err(req, errno);
1045 }
1046
1047 static void ft_ll_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
1048 {
1049 trace_lock.lock();
1050 print_time();
1051 traceout << "ll_release" << endl << fi->fh << endl;
1052 trace_lock.unlock();
1053
1054 lock.lock();
1055 Inode *in = inode_map[ino];
1056 in->fds.erase(fi->fh);
1057 lock.unlock();
1058
1059 int res = ::close(fi->fh);
1060 if (res >= 0)
1061 fuse_reply_err(req, 0);
1062 else
1063 fuse_reply_err(req, errno);
1064 }
1065
1066 static void ft_ll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
1067 struct fuse_file_info *fi)
1068 {
1069 trace_lock.lock();
1070 print_time();
1071 traceout << "ll_fsync" << endl << fi->fh << endl;
1072 trace_lock.unlock();
1073
1074 int res = ::fsync(fi->fh);
1075 if (res >= 0)
1076 fuse_reply_err(req, 0);
1077 else
1078 fuse_reply_err(req, errno);
1079 }
1080
1081 static struct fuse_lowlevel_ops ft_ll_oper = {
1082 init: 0,
1083 destroy: 0,
1084 lookup: ft_ll_lookup,
1085 forget: ft_ll_forget,
1086 getattr: ft_ll_getattr,
1087 setattr: ft_ll_setattr,
1088 readlink: ft_ll_readlink,
1089 mknod: ft_ll_mknod,
1090 mkdir: ft_ll_mkdir,
1091 unlink: ft_ll_unlink,
1092 rmdir: ft_ll_rmdir,
1093 symlink: ft_ll_symlink,
1094 rename: ft_ll_rename,
1095 link: ft_ll_link,
1096 open: ft_ll_open,
1097 read: ft_ll_read,
1098 write: ft_ll_write,
1099 flush: ft_ll_flush,
1100 release: ft_ll_release,
1101 fsync: ft_ll_fsync,
1102 opendir: ft_ll_opendir,
1103 readdir: ft_ll_readdir,
1104 releasedir: ft_ll_releasedir,
1105 fsyncdir: 0,
1106 statfs: ft_ll_statfs,
1107 setxattr: 0,
1108 getxattr: 0,
1109 listxattr: 0,
1110 removexattr: 0,
1111 access: 0,
1112 create: ft_ll_create,
1113 getlk: 0,
1114 setlk: 0,
1115 bmap: 0
1116 };
1117
1118 int main(int argc, char *argv[])
1119 {
1120 // open trace
1121
1122 // figure base dir
1123 char *newargv[100];
1124 int newargc = 0;
1125 for (int i=0; i<argc; i++) {
1126 if (strcmp(argv[i], "--basedir") == 0) {
1127 basedir = argv[++i];
1128 } else if (strcmp(argv[i], "--timestamps") == 0) {
1129 do_timestamps = atoi(argv[++i]);
1130 } else if (strcmp(argv[i], "--trace") == 0) {
1131 tracefile.open(argv[++i], ios::out|ios::trunc);
1132 if (!tracefile.is_open())
1133 cerr << "** couldn't open trace file " << argv[i] << endl;
1134 } else if (strcmp(argv[i], "--debug") == 0) {
1135 debug = 1;
1136 } else {
1137 cout << "arg: " << newargc << " " << argv[i] << endl;
1138 newargv[newargc++] = argv[i];
1139 }
1140 }
1141 newargv[newargc++] = "-o";
1142 newargv[newargc++] = "allow_other";
1143 // newargv[newargc++] = "-o";
1144 // newargv[newargc++] = "default_permissions";
1145 if (!basedir) return 1;
1146 cout << "basedir is " << basedir << endl;
1147
1148 // create root ino
1149 root = new Inode;
1150 ::lstat(basedir, &root->stbuf);
1151 root->stbuf.st_ino = 1;
1152 inode_map[1] = root;
1153 root->ref++;
1154
1155 umask(0);
1156
1157 // go go gadget fuse
1158 struct fuse_args args = FUSE_ARGS_INIT(newargc, newargv);
1159 struct fuse_chan *ch;
1160 char *mountpoint;
1161
1162 if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) != -1 &&
1163 (ch = fuse_mount(mountpoint, &args)) != NULL) {
1164 struct fuse_session *se;
1165
1166 // init fuse
1167 se = fuse_lowlevel_new(&args, &ft_ll_oper, sizeof(ft_ll_oper),
1168 NULL);
1169 if (se != NULL) {
1170 if (fuse_set_signal_handlers(se) != -1) {
1171 fuse_session_add_chan(se, ch);
1172 if (fuse_session_loop(se) <= -1) {
1173 cout << "Failed fuse_session_loop() call." << endl;
1174 return 1;
1175 }
1176 fuse_remove_signal_handlers(se);
1177 fuse_session_remove_chan(ch);
1178 }
1179 fuse_session_destroy(se);
1180 }
1181 fuse_unmount(mountpoint, ch);
1182 }
1183 fuse_opt_free_args(&args);
1184 }