Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / 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 }