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