changed n_link to 1, so 'find' will work (we have no enless subdirs by default anymore)
[k8muffin.git] / src / fusedrv / fusedrv.c
blob777e6bb5c1423872cbdb65407c778a84c3f0b305
1 /* coded by Ketmar // Vampire Avalon (psyc://ketmar.no-ip.org/~Ketmar)
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #define FUSE_USE_VERSION 26
19 #ifndef _BSD_SOURCE
20 # define _BSD_SOURCE
21 #endif
22 #ifndef _GNU_SOURCE
23 # define _GNU_SOURCE
24 #endif
26 #include <ctype.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <fuse.h>
31 #include <iconv.h>
32 #include <libgen.h>
33 #include <limits.h>
34 #include <stdint.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <time.h>
39 #include <unistd.h>
41 #include <sys/stat.h>
42 #include <sys/types.h>
43 #include <sys/xattr.h>
45 #include "../libdbg/dbglog.h"
46 #include "../uthash.h"
47 #include "../translit.h"
48 #include "../libk8clock/k8clock.h"
51 ////////////////////////////////////////////////////////////////////////////////
52 #define NO_SEARCH_DBGLOG
53 //#define FUSEDRV_LOG_CALLS
56 ////////////////////////////////////////////////////////////////////////////////
57 static int opt_log_calls = 0;
59 # define fd_call_dlogf(...) do { \
60 if (opt_log_calls) dlogf(__VA_ARGS__); \
61 } while (0)
64 ////////////////////////////////////////////////////////////////////////////////
65 #include "../tagload.c"
66 #include "../search.c"
69 ////////////////////////////////////////////////////////////////////////////////
70 static char *tagfilename = NULL;
71 static int tfs_dirs_opened = 0; // # of opened dirs
72 static int tfs_need_reload = 0; // !0: reload when last dir closed
73 static uint64_t cur_dir_time = 1;
76 ////////////////////////////////////////////////////////////////////////////////
77 static inline void tagdb_check_and_reload (void) {
78 if (tfs_need_reload) {
79 if (tfs_dirs_opened == 0) {
80 dlogf("* reloading tagdb from '%s'...", tagfilename);
81 tfs_need_reload = 0;
82 clear_hashes();
83 dlogf("* hashes cleared");
84 tagdb_unload();
85 dlogf("* tagdb unloaded");
86 if (tagdb_load(tagfilename) != 0) {
87 dlogf("FATAL: can't load tagfile: '%s'!\n", tagfilename);
88 abort();
90 dlogf("* tagdb loaded");
91 build_hashes();
92 dlogf("* hashes built");
93 ++cur_dir_time;
99 ////////////////////////////////////////////////////////////////////////////////
100 static char *get_real_path (const char *fn, int add_slash) {
101 if (fn != NULL && fn[0]) {
102 char *pp = realpath(fn, NULL);
103 if (pp != NULL) {
104 if (pp[0]) {
105 if (add_slash && pp[strlen(pp)-1] != '/') {
106 char *t = malloc(strlen(pp)+2);
107 sprintf(t, "%s/", pp);
108 free(pp);
109 pp = t;
111 return pp;
113 free(pp);
116 return NULL;
120 ////////////////////////////////////////////////////////////////////////////////
121 //#define CTR ((my_data_struct_t *)(fuse_get_context()->private_data))
124 // report errors to logfile and give -errno to caller
126 static int tfs_error (const char *str) {
127 int ret = -errno;
128 fd_call_dlogf(" ERROR %s: %s", str, strerror(errno));
129 return ret;
134 static const fl_tagvalue_t *find_by_tag_name (const char *name) {
135 const char *t = strrchr(name, '/');
136 if (t == NULL) t = name; else ++t;
137 return get_tag_by_name(name, &tval_hash);
141 // short names are unique
142 static fl_fileinfo_t *find_by_short_name (const char *path) {
143 filename_t *sf;
144 const char *t = strrchr(path, '/');
145 if (t == NULL) t = path; else ++t;
146 HASH_FIND_STR(sname_hash, t, sf);
147 return (sf != NULL ? sf->fi : NULL);
151 ////////////////////////////////////////////////////////////////////////////////
152 typedef struct {
153 int fd; // <0: internal file
154 int space_eater;
155 int size;
156 char *data;
157 //UT_hash_handle hh;
158 } filehandle_t;
161 //static filehandle_t *fhandles = NULL;
164 static filehandle_t *colon_create_info_file (const char *path) {
165 filehandle_t *fh = NULL;
166 fd_call_dlogf("colon_create_info_file: [%s]\n", path);
167 if (strcmp(path, "/:/info") == 0) {
168 fh = calloc(1, sizeof(*fh));
169 fh->fd = -1;
170 fh->data = calloc(8192, 1);
171 //snprintf(fh->data, 8192, "files: %d; otags: %d; ntags: %d\n", HASH_COUNT(file_name_hash), HASH_COUNT(tagv_hash_o), HASH_COUNT(tagv_hash_t));
172 snprintf(fh->data, 8192, "files: %d; tags: %d; opened dirs: %d; reload deferred: %d\n", HASH_COUNT(sname_hash), HASH_COUNT(tval_hash),
173 tfs_dirs_opened, tfs_need_reload
175 fh->size = strlen(fh->data);
177 return fh;
181 static filehandle_t *fht_open (const char *realpath) {
182 int fd = open(realpath, O_RDONLY);
183 if (fd >= 0) {
184 filehandle_t *fh = calloc(1, sizeof(*fh));
185 fh->fd = fd;
186 return fh;
188 return NULL;
192 static filehandle_t *fht_create_eater (const char *realpath) {
193 filehandle_t *fh = calloc(1, sizeof(*fh));
194 fh->fd = -1;
195 fh->space_eater = 1;
196 return fh;
200 static void fht_close (filehandle_t *fh) {
201 if (fh != NULL) {
202 if (fh->fd >= 0) close(fh->fd);
203 if (fh->data != NULL) free(fh->data);
204 free(fh);
209 static int fht_read (const char *path, char *buf, size_t size, off_t offset, filehandle_t *fh) {
210 if (fh != NULL) {
211 if (fh->fd >= 0) {
212 ssize_t rd;
213 fd_call_dlogf("tfs_read: path=[%s], buf=0x%p, size=%d, offset=%lld", path, buf, size, offset);
214 if (fh->fd < 0) return -EPERM;
215 if (offset >= 0x7fffffff || size >= 0x7fffffff) return 0;/*-EPERM;*/ //???
216 if (lseek(fh->fd, offset, SEEK_SET) == (off_t)-1) return -EPERM;
217 rd = read(fh->fd, buf, size);
218 if (rd < 0) return -EPERM;
219 return rd;
221 if (fh->data != NULL) {
222 if (offset >= fh->size) return 0;
223 if ((uint64_t)offset+size > fh->size) size = fh->size-offset;
224 if (size == 0) return 0;
225 memcpy(buf, fh->data+offset, size);
226 return size;
229 return -EPERM;
233 static int fht_write (const char *path, const char *buf, size_t size, off_t offset, filehandle_t *fh) {
234 if (fh != NULL) {
235 if (fh->fd < 0) {
236 if (fh->space_eater) return size; // eat 'em alive!
239 return -EPERM;
243 ////////////////////////////////////////////////////////////////////////////////
244 // this is used in getattr() and readdir()
245 // note that readdir() will call this with 'filename.ext' and getattr() will call this with full path
246 static const char *spxnames[] = {
247 ":otags",
248 ":ttags",
249 ":files",
250 ":or",
251 ":and",
254 static int do_stat (const char *path, struct stat *statbuf) {
255 fl_fileinfo_t *nfo;
256 const char *name;
257 memset(statbuf, 0, sizeof(*statbuf));
259 fd_call_dlogf("do_stat: [%s]\n", path);
260 //FIXME!
261 statbuf->st_uid = fuse_get_context()->uid;
262 statbuf->st_gid = fuse_get_context()->gid;
264 //TODO: set to 2? do we rally want 'find' to work on muffin?
265 statbuf->st_nlink = 1; // was '2'; actually, this should be set to 'subdir_count+2' (or 1), else 'find' will fail
266 statbuf->st_mode = S_IFDIR|0555;
267 //statbuf->st_dev = get_fi(0)->devid;
268 //statbuf->st_ino = get_fi(0)->inode;
269 statbuf->st_blksize = 4096; // i/o block size
270 //statbuf->st_blocks = 1;
271 //statbuf->st_size = 1;
272 statbuf->st_atime = statbuf->st_ctime = statbuf->st_mtime = cur_dir_time;
274 //stat(get_str(get_fi(0)->realname), statbuf);
275 //stat("/home/ketmar", statbuf);
276 statbuf->st_mode = S_IFDIR|0555;
278 if (strcmp(path, "/") == 0) {
279 return 0;
281 if (strcmp(path, "/:") == 0) {
282 statbuf->st_mode = S_IFDIR|0755; // writeable
283 return 0;
285 if (strncmp(path, "/:/", 3) == 0) {
286 // all files are regular ones here
287 statbuf->st_mode = S_IFREG|0444;
288 if (strcmp(path, "/:/reload") == 0) {
289 if (tfs_need_reload) {
290 fd_call_dlogf(" stat: eater is here\n");
291 statbuf->st_mode = S_IFREG|0777;
292 return 0;
294 return -ENOENT;
296 statbuf->st_size = 4096; // arbitrary, but must be bigger than the actual 'info' files
297 return 0;
300 name = strrchr(path, '/');
301 if (name == NULL) name = path; else ++name;
302 fd_call_dlogf(" do_stat_name: [%s]\n", name);
304 if (name[0] == ':') {
305 fd_call_dlogf(" do_spec_stat: [%s]\n", name);
306 for (size_t f = 0; f < ARRAYLEN(spxnames); ++f) if (strcmp(name, spxnames[f]) == 0) {
307 fd_call_dlogf(" stat: SPECIAL\n");
308 return 0;
310 for (size_t f = 0; f < ARRAYLEN(spec_dirs); ++f) if (strcmp(spec_dirs[f].name, name) == 0) {
311 fd_call_dlogf(" stat: SPECIAL\n");
312 return 0;
314 dlogf("baddir: [%s]\n", path);
315 return -ENOENT;
318 if ((nfo = find_by_short_name(name)) != NULL) {
319 // normal file
321 if (do_real_stat) {
322 if (stat(nfo->fullname, statbuf) == 0) return 0;
323 return -ENOENT; //TODO: return better error here
326 fd_call_dlogf(" stat: file\n");
327 #ifdef USE_DEVID_INODE
328 statbuf->st_dev = nfo->devid;
329 statbuf->st_ino = nfo->inode;
330 #else
331 statbuf->st_dev = 666;
332 statbuf->st_ino = (uintptr_t)nfo;
333 #endif
334 statbuf->st_mode = S_IFREG|0444;
335 statbuf->st_size = nfo->size;
336 statbuf->st_blksize = 4096; // i/o block size
337 statbuf->st_blocks = (nfo->size/512)+(nfo->size%512 ? 1 : 0);
338 statbuf->st_atime = statbuf->st_ctime = statbuf->st_mtime = nfo->mtime;
339 return 0;
342 //dlogf("not-a-file: [%s]\n", path);
343 if (find_by_tag_name(name) != NULL) {
344 // this is dirs too
345 fd_call_dlogf(" stat: dir\n");
346 return 0;
349 dlogf(" stat: ***not-found: [%s]\n", path);
350 return -ENOENT;
354 /** Get file attributes.
356 * Similar to stat(). The 'st_dev' and 'st_blksize' fields are
357 * ignored. The 'st_ino' field is ignored except if the 'use_ino'
358 * mount option is given.
360 static int tfs_getattr (const char *path, struct stat *statbuf) {
361 fd_call_dlogf("tfs_getattr: path=[%s]", path);
362 return do_stat(path, statbuf);
366 /** Read the target of a symbolic link
368 * The buffer should be filled with a null terminated string. The
369 * buffer size argument includes the space for the terminating
370 * null character. If the linkname is too long to fit in the
371 * buffer, it should be truncated. The return value should be 0
372 * for success.
374 // Note the system readlink() will truncate and lose the terminating
375 // null. So, the size passed to to the system readlink() must be one
376 // less than the size passed to tfs_readlink()
377 static int tfs_readlink (const char *path, char *link, size_t size) {
378 fd_call_dlogf("tfs_readlink: path=[%s]", path);
379 return -ENOENT; // there are no symlinks
383 /** Create a file node
385 * This is called for creation of all non-directory, non-symlink
386 * nodes. If the filesystem defines a create() method, then for
387 * regular files that will be called instead.
389 static int tfs_mknod (const char *path, mode_t mode, dev_t dev) {
390 fd_call_dlogf("tfs_mknod: path=[%s], mode=0%03o, dev=%lld", path, mode, dev);
392 //if (!S_ISREG(mode)) return -EINVAL; // only regular files can be created
393 // no new files can be created here
394 //return (exists ? EEXIST : EFAULT);
395 return -EFAULT;
399 /** Create a directory
401 * Note that the mode argument may not have the type specification
402 * bits set, i.e. S_ISDIR(mode) can be false. To obtain the
403 * correct directory type bits use mode|S_IFDIR
405 static int tfs_mkdir (const char *path, mode_t mode) {
406 fd_call_dlogf("tfs_mkdir: path=[%s], mode=0%03o", path, mode);
407 // no new directories can be created here
408 return -EFAULT;
412 /** Remove a file */
413 static int tfs_unlink (const char *path) {
414 fd_call_dlogf("tfs_unlink: path=[%s]", path);
415 return -EFAULT;
419 /** Remove a directory */
420 static int tfs_rmdir (const char *path) {
421 fd_call_dlogf("tfs_rmdir: path=[%s]", path);
422 return -EFAULT;
426 /** Create a symbolic link */
427 // The parameters here are a little bit confusing, but do correspond
428 // to the symlink() system call. The 'path' is where the link points,
429 // while the 'link' is the link itself. So we need to leave the path
430 // unaltered, but insert the link into the mounted directory.
431 static int tfs_symlink (const char *path, const char *link) {
432 fd_call_dlogf("tfs_symlink: path=[%s]; link=[%s]", path, link);
433 return -EFAULT;
437 /** Rename a file */
438 // both path and newpath are fs-relative
439 static int tfs_rename (const char *path, const char *newpath) {
440 fd_call_dlogf("tfs_rename: path=[%s]; newpath=[%s]", path, newpath);
441 return -EFAULT;
445 /** Create a hard link to a file */
446 static int tfs_link (const char *path, const char *newpath) {
447 fd_call_dlogf("tfs_link: path=[%s]; newpath=[%s]", path, newpath);
448 return -EFAULT;
452 /** Change the permission bits of a file */
453 static int tfs_chmod (const char *path, mode_t mode) {
454 fd_call_dlogf("tfs_chmod: path=[%s], mode=0%03o", path, mode);
455 return 0;
459 /** Change the owner and group of a file */
460 static int tfs_chown (const char *path, uid_t uid, gid_t gid) {
461 fd_call_dlogf("tfs_chown: path=[%s], uid=%d, gid=%d", path, uid, gid);
462 return 0;
466 /** Change the size of a file */
467 static int tfs_truncate (const char *path, off_t newsize) {
468 fd_call_dlogf("tfs_truncate: path=[%s], newsize=%lld", path, newsize);
469 return -EFAULT;
473 /** Change the access and/or modification times of a file
475 * Deprecated, use utimens() instead.
478 static int tfs_utime (const char *path, struct utimbuf *ubuf) {
479 return 0;
484 /** File open operation
486 * No creation (O_CREAT, O_EXCL) and by default also no
487 * truncation (O_TRUNC) flags will be passed to open(). If an
488 * application specifies O_TRUNC, fuse first calls truncate()
489 * and then open(). Only if 'atomic_o_trunc' has been
490 * specified and kernel version is 2.6.24 or later, O_TRUNC is
491 * passed on to open.
493 * Unless the 'default_permissions' mount option is given,
494 * open should check if the operation is permitted for the
495 * given flags. Optionally open may also return an arbitrary
496 * filehandle in the fuse_file_info structure, which will be
497 * passed to all file operations.
499 * Changed in version 2.2
501 static int tfs_open (const char *path, struct fuse_file_info *fi) {
502 fl_fileinfo_t *nfo;
503 filehandle_t *fh = NULL;
504 fd_call_dlogf("tfs_open: path=[%s], fi=0x%p; append=%d; trunc=%d\n", path, fi, (fi->flags&O_APPEND ? 1 : 0), (fi->flags&O_TRUNC ? 1 : 0));
505 if ((fi->flags&O_APPEND) || (fi->flags&O_TRUNC)) {
506 fd_call_dlogf(" bad flags!\n");
507 return -EPERM;
509 switch (fi->flags&(O_RDONLY|O_WRONLY|O_RDWR)) {
510 case O_RDONLY: break;
511 case O_WRONLY:
512 case O_RDWR:
513 fd_call_dlogf(" bad access mode!\n");
514 return -EPERM;
517 if (strncmp(path, "/:/", 3) == 0) {
518 fh = colon_create_info_file(path);
519 } else {
520 if ((nfo = find_by_short_name(path)) != NULL) {
521 fd_call_dlogf(" found: [%s]\n", get_str(nfo->realname));
522 fh = fht_open(get_str(nfo->realname));
525 if (fh != NULL) {
526 fi->fh = (uintptr_t)fh;
527 return 0;
529 fd_call_dlogf(" shit!\n");
530 return -ENOENT;
534 /** Read data from an open file
536 * Read should return exactly the number of bytes requested except
537 * on EOF or error, otherwise the rest of the data will be
538 * substituted with zeroes. An exception to this is when the
539 * 'direct_io' mount option is specified, in which case the return
540 * value of the read system call will reflect the return value of
541 * this operation.
543 * Changed in version 2.2
545 static int tfs_read (const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
546 filehandle_t *fh = (filehandle_t *)(uintptr_t)(fi->fh);
547 return fht_read(path, buf, size, offset, fh);
551 /** Write data to an open file
553 * Write should return exactly the number of bytes requested
554 * except on error. An exception to this is when the 'direct_io'
555 * mount option is specified (see read operation).
557 * Changed in version 2.2
559 static int tfs_write (const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
560 fd_call_dlogf("tfs_write: path=[%s], buf=0x%p, size=%d, offset=%lld, fi=0x%p", path, buf, size, offset, fi);
561 filehandle_t *fh = (filehandle_t *)(uintptr_t)(fi->fh);
562 return fht_write(path, buf, size, offset, fh);
566 /** Get file system statistics
568 * The 'f_frsize', 'f_favail', 'f_fsid' and 'f_flag' fields are ignored
570 * Replaced 'struct statfs' parameter with 'struct statvfs' in
571 * version 2.5
573 static int tfs_statfs (const char *path, struct statvfs *statv) {
574 return -EPERM;
576 //statvfs
577 //memset(statv, 0, sizeof(*statv));
578 //statv->f_bsize = 8192; // fixme
579 fd_call_dlogf("tfs_statfs: path=[%s], statv=0x%p", path, statv);
580 //return retstat;
584 /** Possibly flush cached data
586 * BIG NOTE: This is not equivalent to fsync(). It's not a
587 * request to sync dirty data.
589 * Flush is called on each close() of a file descriptor. So if a
590 * filesystem wants to return write errors in close() and the file
591 * has cached dirty data, this is a good place to write back data
592 * and return any errors. Since many applications ignore close()
593 * errors this is not always useful.
595 * NOTE: The flush() method may be called more than once for each
596 * open(). This happens if more than one file descriptor refers
597 * to an opened file due to dup(), dup2() or fork() calls. It is
598 * not possible to determine if a flush is final, so each flush
599 * should be treated equally. Multiple write-flush sequences are
600 * relatively rare, so this shouldn't be a problem.
602 * Filesystems shouldn't assume that flush will always be called
603 * after some writes, or that if will be called at all.
605 * Changed in version 2.2
607 static int tfs_flush (const char *path, struct fuse_file_info *fi) {
608 fd_call_dlogf("tfs_flush: path=[%s], fi=0x%p", path, fi);
609 return 0;
613 /** Release an open file
615 * Release is called when there are no more references to an open
616 * file: all file descriptors are closed and all memory mappings
617 * are unmapped.
619 * For every open() call there will be exactly one release() call
620 * with the same flags and file descriptor. It is possible to
621 * have a file opened more than once, in which case only the last
622 * release will mean, that no more reads/writes will happen on the
623 * file. The return value of release is ignored.
625 * Changed in version 2.2
627 static int tfs_release (const char *path, struct fuse_file_info *fi) {
628 filehandle_t *fh = (filehandle_t *)(uintptr_t)(fi->fh);
629 int is_eater = (fh != NULL ? fh->space_eater : 0);
630 fd_call_dlogf("tfs_release: path=[%s]; fh=%p; eater=%d\n", path, fh, is_eater);
631 fht_close(fh);
632 fi->fh = 0;
633 if (is_eater) tagdb_check_and_reload();
634 return 0;
638 /** Synchronize file contents
640 * If the datasync parameter is non-zero, then only the user data
641 * should be flushed, not the meta data.
643 * Changed in version 2.2
645 static int tfs_fsync (const char *path, int datasync, struct fuse_file_info *fi) {
646 fd_call_dlogf("tfs_fsync: path=[%s], datasync=%d, fi=0x%p", path, datasync, fi);
647 return 0;
651 /** Set extended attributes */
652 static int tfs_setxattr (const char *path, const char *name, const char *value, size_t size, int flags) {
653 fd_call_dlogf("tfs_setxattr: path=[%s], name=[%s], value=[%s], size=%d, flags=0x%08x", path, name, value, size, (unsigned int)flags);
654 return -EPERM;
658 static const struct {
659 const char *name;
660 int tflidx;
661 int hidden;
662 } xattr_tags[] = {
663 {.name="user.oartist", .tflidx=TFL_ARTIST_O, .hidden=1},
664 {.name="user.artist", .tflidx=TFL_ARTIST_O, .hidden=0},
665 {.name="user._artist", .tflidx=TFL_ARTIST_T, .hidden=0},
666 {.name="user.tartist", .tflidx=TFL_ARTIST_T, .hidden=1},
667 {.name="user.oalbum", .tflidx=TFL_ALBUM_O, .hidden=1},
668 {.name="user.album", .tflidx=TFL_ALBUM_O, .hidden=0},
669 {.name="user._album", .tflidx=TFL_ALBUM_T, .hidden=0},
670 {.name="user.talbum", .tflidx=TFL_ALBUM_T, .hidden=1},
671 {.name="user.otitle", .tflidx=TFL_TITLE_O, .hidden=1},
672 {.name="user.title", .tflidx=TFL_TITLE_O, .hidden=0},
673 {.name="user._title", .tflidx=TFL_TITLE_T, .hidden=0},
674 {.name="user.ttitle", .tflidx=TFL_TITLE_T, .hidden=1},
675 {.name="user.ogenre", .tflidx=TFL_GENRE_O, .hidden=1},
676 {.name="user.genre", .tflidx=TFL_GENRE_O, .hidden=0},
677 {.name="user._genre", .tflidx=TFL_GENRE_T, .hidden=0},
678 {.name="user.tgenre", .tflidx=TFL_GENRE_T, .hidden=1},
679 {.name="user.year", .tflidx=TFL_YEAR, .hidden=0},
683 /** Get extended attributes */
684 // If the given node described by path has the named extended attribute, and the vlen is zero,
685 // return the number of bytes required to provide the value of the extended attribute.
686 // The next call will have a buffer that is long enough to hold the value. Copy the value into
687 // the buffer and return the number of bytes that the value occupies.
688 static int tfs_getxattr (const char *path, const char *name, char *value, size_t size) {
689 fd_call_dlogf("tfs_getxattr: path=[%s], name=[%s], value=0x%p, size=%d", path, name, value, size);
690 if (name != NULL && name[0]) {
691 fl_fileinfo_t *nfo;
692 const char *val = NULL;
693 int len;
694 if ((nfo = find_by_short_name(path)) == NULL) {
695 // satisfy 'ls -l'
697 struct stat sb;
698 if (do_stat(path, &sb) != 0) return -ENOENT;
700 // just return 'no data' for any attribute of any path; it's harmful (i think)
701 return 0;
703 if (strcmp(name, "user.realname") == 0) {
704 val = get_str(nfo->realname);
705 } else {
706 for (size_t f = 0; f < ARRAYLEN(xattr_tags); ++f) {
707 if (strcmp(name, xattr_tags[f].name) == 0) {
708 val = get_str(get_tv(nfo->tags[xattr_tags[f].tflidx])->value);
709 break;
713 if (val != NULL) {
714 len = strlen(val);
715 if (size > 0 && len > 0) memcpy(value, val, len);
716 return len;
718 return 0;
720 //return -EPERM;
721 return 0;
725 #define STRXCPY(str) do { \
726 const char *s = (str); \
727 if (s != NULL && s[0]) { \
728 int xlen = strlen(s)+1; \
729 if (list != NULL) { memcpy(list, s, xlen); list += xlen; } \
730 tlen += xlen; \
732 } while (0)
735 /** List extended attributes */
736 // size: the same note as for getxattr applies here.
737 // fill 'list' with asciiz-strings, one just after another.
738 // note that 'getfattr -d' lists only 'user.' attributes
739 static int tfs_listxattr (const char *path, char *list, size_t size) {
740 fl_fileinfo_t *nfo;
741 int tlen = 0;
742 fd_call_dlogf("tfs_listxattr: path=[%s], list=0x%p, size=%d", path, list, size);
743 if ((nfo = find_by_short_name(path)) == NULL) return -ENOENT;
744 STRXCPY("user.realname");
746 for (size_t f = 0; f < ARRAYLEN(xattr_tags); ++f) if (!xattr_tags[f].hidden) STRXCPY(xattr_tags[f].name);
748 return tlen;
749 //return -EPERM;
753 /** Remove extended attributes */
754 static int tfs_removexattr (const char *path, const char *name) {
755 fd_call_dlogf("tfs_removexattr: path=[%s], name=[%s]", path, name);
756 return -EPERM;
760 /** Open directory
762 * Unless the 'default_permissions' mount option is given,
763 * this method should check if opendir is permitted for this
764 * directory. Optionally opendir may also return an arbitrary
765 * filehandle in the fuse_file_info structure, which will be
766 * passed to readdir, closedir and fsyncdir.
768 * Introduced in version 2.3
770 static int tfs_opendir (const char *path, struct fuse_file_info *fi) {
771 dirinfo_t *odir;
772 tagdb_check_and_reload();
773 odir = list_dir(path);
774 if (odir == NULL) {
775 fd_call_dlogf("tfs_opendir: BAD [%s]\n", path);
776 return -EPERM;
778 ++tfs_dirs_opened;
779 fd_call_dlogf("tfs_opendir: path=[%s], %d entries", path, odir->fcount);
780 fi->fh = (uintptr_t)odir; // fh is 64 bit, so this is safe
781 return 0;
785 /** Read directory
787 * This supersedes the old getdir() interface. New applications
788 * should use this.
790 * The filesystem may choose between two modes of operation:
792 * 1) The readdir implementation ignores the offset parameter, and
793 * passes zero to the filler function's offset. The filler
794 * function will not return '1' (unless an error happens), so the
795 * whole directory is read in a single readdir operation. This
796 * works just like the old getdir() method.
798 * 2) The readdir implementation keeps track of the offsets of the
799 * directory entries. It uses the offset parameter and always
800 * passes non-zero offset to the filler function. When the buffer
801 * is full (or an error happens) the filler function will return
802 * '1'.
804 * Introduced in version 2.3
807 * typedef int (*fuse_fill_dir_t) (void *buf, const char *name, const struct stat *stbuf, off_t off);
809 static int tfs_readdir (const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
810 dirinfo_t *odir = (dirinfo_t *)(uintptr_t)fi->fh;
811 struct stat stbuf, *starg = &stbuf;
812 fd_call_dlogf("tfs_readdir: path=[%s], buf=0x%p, filler=0x%p, offset=%lld, fi=0x%p", path, buf, filler, offset, fi);
813 if (odir == NULL) return -EPERM;
814 //dlogf("tfs_readdir: path=[%s], %d entries", path, odir->fcount);
815 if (offset < 0 || offset >= odir->fcount) return 0;
816 // we can build 'struct stat' here, so readdir() will return some useful flags
817 // but this is not really necessary, and we can just pass NULL
818 if (do_stat(odir->items[offset].name, starg) != 0) starg = NULL; // don't pass anything on error
819 //starg = NULL;
820 if (filler(buf, odir->items[offset].name, starg, offset+1)) {
821 fd_call_dlogf("tfs_readdir: path=[%s], filler failed!", path);
822 return -ENOMEM;
824 //dlogf("tfs_readdir: path=[%s], name=[%s]", path, odir->items[offset].name);
825 return 0;
829 /** Release directory
831 * Introduced in version 2.3
833 static int tfs_releasedir (const char *path, struct fuse_file_info *fi) {
834 dirinfo_t *odir = (dirinfo_t *)(uintptr_t)fi->fh;
835 fd_call_dlogf("tfs_releasedir: path=[%s], fi=0x%p", path, fi);
836 if (odir == NULL) {
837 tagdb_check_and_reload();
838 return -EPERM;
840 if (odir->items != NULL) free(odir->items);
841 free(odir);
842 fi->fh = 0;
843 --tfs_dirs_opened;
844 tagdb_check_and_reload();
845 return 0;
849 /** Synchronize directory contents
851 * If the datasync parameter is non-zero, then only the user data
852 * should be flushed, not the meta data
854 * Introduced in version 2.3
856 // when exactly is this called? when a user calls fsync and it
857 // happens to be a directory? ???
858 static int tfs_fsyncdir (const char *path, int datasync, struct fuse_file_info *fi) {
859 fd_call_dlogf("tfs_fsyncdir: path=[%s], datasync=%d, fi=0x%p", path, datasync, fi);
860 return 0;
865 * Initialize filesystem
867 * The return value will passed in the private_data field of
868 * fuse_context to all file operations and as a parameter to the
869 * destroy() method.
871 * Introduced in version 2.3
872 * Changed in version 2.6
874 // Undocumented but extraordinarily useful fact: the fuse_context is
875 // set up before this function is called, and
876 // fuse_get_context()->private_data returns the user_data passed to
877 // fuse_main(). Really seems like either it should be a third
878 // parameter coming in here, or else the fact should be documented
879 // (and this might as well return void, as it did in older versions of
880 // FUSE).
881 static void *tfs_init (struct fuse_conn_info *conn) {
882 char tmpbuf[4096];
883 dlogf("tfs_init: protocol v%u.%u", conn->proto_major, conn->proto_minor);
884 // dump caps
885 if (conn->capable) {
886 tmpbuf[0] = 0;
887 if (conn->capable&FUSE_CAP_ASYNC_READ) strcat(tmpbuf, " FUSE_CAP_ASYNC_READ");
888 if (conn->capable&FUSE_CAP_POSIX_LOCKS) strcat(tmpbuf, " FUSE_CAP_POSIX_LOCKS"); // filesystem supports "remote" locking
889 if (conn->capable&FUSE_CAP_ATOMIC_O_TRUNC) strcat(tmpbuf, " FUSE_CAP_ATOMIC_O_TRUNC"); // filesystem handles the O_TRUNC open flag
890 if (conn->capable&FUSE_CAP_EXPORT_SUPPORT) strcat(tmpbuf, " FUSE_CAP_EXPORT_SUPPORT"); // filesystem handles lookups of "." and ".."
891 if (conn->capable&FUSE_CAP_BIG_WRITES) strcat(tmpbuf, " FUSE_CAP_BIG_WRITES"); // filesystem can handle write size larger than 4kB
892 if (conn->capable&FUSE_CAP_DONT_MASK) strcat(tmpbuf, " FUSE_CAP_DONT_MASK"); // don't apply umask to file mode on create operations
893 dlogf("kernel caps:%s\n", tmpbuf);
895 // set our params
896 conn->want = 0; // the only flag we want is FUSE_CAP_BIG_WRITES, but we don't support writing
897 conn->async_read = 0; // we don't support async reads
898 return fuse_get_context()->private_data;
903 * Clean up filesystem
905 * Called on filesystem exit.
907 * Introduced in version 2.3
909 static void tfs_destroy (void *userdata) {
910 dlogf("tfs_destroy: userdata=0x%p", userdata);
915 * Check file access permissions
917 * This will be called for the access() system call. If the
918 * 'default_permissions' mount option is given, this method is not
919 * called.
921 * This method is not called under Linux kernel versions 2.4.x
923 * Introduced in version 2.5
925 //k8: don't know if mode can be combination of flags, and what to do then
926 static int tfs_access (const char *path, int mode) {
927 struct stat st;
928 int res;
929 //dlogf("tfs_access: path=[%s], mode=0x%02x", path, mode);
930 if (mode&W_OK) {
931 //dlogf(" W_OK: [%s]\n", path);
932 return -EPERM; // can't write
934 res = do_stat(path, &st);
935 if (res == 0) {
936 if (S_ISDIR(st.st_mode)) return 0;
937 if (mode&X_OK) return -EPERM; // can't execute
938 return 0;
940 return res;
945 * Create and open a file
947 * If the file does not exist, first create it with the specified
948 * mode, and then open it.
950 * If this method is not implemented or under Linux kernel
951 * versions earlier than 2.6.15, the mknod() and open() methods
952 * will be called instead.
954 * Introduced in version 2.5
956 static int tfs_create (const char *path, mode_t mode, struct fuse_file_info *fi) {
957 fd_call_dlogf("tfs_create: path=[%s], mode=0%03o, fi=0x%p", path, mode, fi);
958 if (strcmp(path, "/:/reload") == 0) {
959 // special action: reload tagdb
960 filehandle_t *fh;
961 tfs_need_reload = 1;
962 // defer reloading, 'cause FUSE will call tfs_getattr() just after creating the eater
963 //tagdb_check_and_reload();
964 if ((fh = fht_create_eater(path)) != NULL) {
965 fi->fh = (uintptr_t)fh;
966 return 0;
968 return -EPERM;
970 //fi->fh = fd;
971 return -EPERM;
976 * Change the size of an open file
978 * This method is called instead of the truncate() method if the
979 * truncation was invoked from an ftruncate() system call.
981 * If this method is not implemented or under Linux kernel
982 * versions earlier than 2.6.15, the truncate() method will be
983 * called instead.
985 * Introduced in version 2.5
987 static int tfs_ftruncate (const char *path, off_t offset, struct fuse_file_info *fi) {
988 //dlogf("tfs_ftruncate: path=[%s], offset=%lld, fi=0x%p", path, offset, fi);
989 return -EPERM;
994 * Get attributes from an open file
996 * This method is called instead of the getattr() method if the
997 * file information is available.
999 * Currently this is only called after the create() method if that
1000 * is implemented (see above). Later it may be called for
1001 * invocations of fstat() too.
1003 * Introduced in version 2.5
1005 static int tfs_fgetattr (const char *path, struct stat *statbuf, struct fuse_file_info *fi) {
1006 //dlogf("tfs_fgetattr: path=[%s], statbuf=0x%p, fi=0x%p", path, statbuf, fi);
1007 return tfs_getattr(path, statbuf); // just pass it up
1012 * Perform POSIX file locking operation
1014 * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW.
1016 * For the meaning of fields in 'struct flock' see the man page
1017 * for fcntl(2). The l_whence field will always be set to
1018 * SEEK_SET.
1020 * For checking lock ownership, the 'fuse_file_info->owner'
1021 * argument must be used.
1023 * For F_GETLK operation, the library will first check currently
1024 * held locks, and if a conflicting lock is found it will return
1025 * information without calling this method. This ensures, that
1026 * for local locks the l_pid field is correctly filled in. The
1027 * results may not be accurate in case of race conditions and in
1028 * the presence of hard links, but it's unlikly that an
1029 * application would rely on accurate GETLK results in these
1030 * cases. If a conflicting lock is not found, this method will be
1031 * called, and the filesystem may fill out l_pid by a meaningful
1032 * value, or it may leave this field zero.
1034 * For F_SETLK and F_SETLKW the l_pid field will be set to the pid
1035 * of the process performing the locking operation.
1037 * Note: if this method is not implemented, the kernel will still
1038 * allow file locking to work locally. Hence it is only
1039 * interesting for network filesystems and similar.
1041 * Introduced in version 2.6
1044 static int tfs_lock (const char *path, struct fuse_file_info *fi, int cmd, struct flock *) {
1045 return -EPERM;
1051 * Change the access and modification times of a file with
1052 * nanosecond resolution
1054 * Introduced in version 2.6
1056 static int tfs_utimens (const char *path, const struct timespec tv[2]) {
1057 return 0; // pretend that we done it
1062 * Map block index within file to block index within device
1064 * Note: This makes sense only for block device backed filesystems
1065 * mounted with the 'blkdev' option
1067 * Introduced in version 2.6
1070 static int tfs_bmap (const char *path, size_t blocksize, uint64_t *idx) {
1071 return -EPERM;
1077 * Ioctl
1079 * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in
1080 * 64bit environment. The size and direction of data is
1081 * determined by _IOC_*() decoding of cmd. For _IOC_NONE,
1082 * data will be NULL, for _IOC_WRITE data is out area, for
1083 * _IOC_READ in area and if both are set in/out area. In all
1084 * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes.
1086 * Introduced in version 2.8
1089 static int tfs_ioctl (const char *path, int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void *data) {
1090 return -EPERM;
1096 * Poll for IO readiness events
1098 * Note: If ph is non-NULL, the client should notify
1099 * when IO readiness events occur by calling
1100 * fuse_notify_poll() with the specified ph.
1102 * Regardless of the number of times poll with a non-NULL ph
1103 * is received, single notification is enough to clear all.
1104 * Notifying more times incurs overhead but doesn't harm
1105 * correctness.
1107 * The callee is responsible for destroying ph with
1108 * fuse_pollhandle_destroy() when no longer in use.
1110 * Introduced in version 2.8
1113 static int tfs_poll (const char *path, struct fuse_file_info *fi, struct fuse_pollhandle *ph, unsigned *reventsp) {
1114 return -EPERM;
1119 ////////////////////////////////////////////////////////////////////////////////
1120 static const struct fuse_operations tfs_oper = {
1121 .getattr = tfs_getattr,
1122 .readlink = tfs_readlink,
1123 .getdir = NULL, // deprecated
1124 .mknod = tfs_mknod,
1125 .mkdir = tfs_mkdir,
1126 .unlink = tfs_unlink,
1127 .rmdir = tfs_rmdir,
1128 .symlink = tfs_symlink,
1129 .rename = tfs_rename,
1130 .link = tfs_link,
1131 .chmod = tfs_chmod,
1132 .chown = tfs_chown,
1133 .truncate = tfs_truncate,
1134 .utime = NULL, // deprecated
1135 .open = tfs_open,
1136 .read = tfs_read,
1137 .write = tfs_write,
1138 .statfs = tfs_statfs,
1139 .flush = tfs_flush,
1140 .release = tfs_release,
1141 .fsync = tfs_fsync,
1142 .setxattr = tfs_setxattr,
1143 .getxattr = tfs_getxattr,
1144 .listxattr = tfs_listxattr,
1145 .removexattr = tfs_removexattr,
1146 .opendir = tfs_opendir,
1147 .readdir = tfs_readdir,
1148 .releasedir = tfs_releasedir,
1149 .fsyncdir = tfs_fsyncdir,
1150 .init = tfs_init,
1151 .destroy = tfs_destroy,
1152 .access = tfs_access,
1153 .create = tfs_create,
1154 .ftruncate = tfs_ftruncate,
1155 .fgetattr = tfs_fgetattr,
1156 //.lock = tfs_lock,
1157 .utimens = tfs_utimens,
1158 //.bmap = tfs_bmap,
1161 * Flag indicating, that the filesystem can accept a NULL path
1162 * as the first argument for the following operations:
1164 * read, write, flush, release, fsync, readdir, releasedir,
1165 * fsyncdir, ftruncate, fgetattr and lock
1167 //unsigned int flag_nullpath_ok : 1;
1168 .flag_nullpath_ok = 0,
1169 // 2.8 API
1170 //.ioctl = tfs_ioctl,
1171 //.poll = tfs_poll,
1175 static void tfs_usage (void) {
1176 fprintf(stderr,
1177 "usage: fusedrv [mount options] tags.dat mountpoint\n"
1178 "special options:\n"
1179 " --debug write some debug logs\n"
1180 " --files show files not only in /:files\n"
1182 exit(1);
1186 static int tagfile_loaded = 0;
1187 static const char *argv00 = "./me";
1190 // return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept
1191 static int opt_processor (void *data, const char *arg, int key, struct fuse_args *outargs) {
1192 //fprintf(stderr, "key=%d; arg=%s\n", key, arg);
1194 if (key == FUSE_OPT_KEY_OPT) {
1195 // this is not FUSE option, handle it
1196 // question: how to handle cases like '-x fuck'?
1197 if (strcmp(arg, "--debug") == 0) {
1198 char *me = strdup(argv00), *logname;
1199 if (strrchr(me, '/')) strrchr(me, '/')[0] = 0;
1200 logname = get_real_path(me, 1);
1201 free(me);
1202 if (logname != NULL) {
1203 char *ln = malloc(strlen(logname)+128);
1204 sprintf(ln, "%smuffin.log", logname);
1205 dbglog_set_filename(ln);
1206 dbglog_set_screenout(0);
1207 dbglog_set_fileout(1);
1208 free(ln);
1209 free(logname);
1211 return 0;
1214 if (strcmp(arg, "--no-debug") == 0) {
1215 dbglog_set_screenout(0);
1216 dbglog_set_fileout(0);
1217 return 0;
1220 if (strcmp(arg, "--debug-calls") == 0) {
1221 opt_log_calls = 1;
1222 return 0;
1225 if (strcmp(arg, "--files") == 0) {
1226 opt_files_only_in_files = 0;
1227 return 0;
1230 if (strcmp(arg, "--no-files") == 0) {
1231 opt_files_only_in_files = 1;
1232 return 0;
1235 if (strcmp(arg, "--help") == 0) {
1236 tfs_usage();
1237 return 0;
1240 if (strcmp(arg, "-h") == 0) { tfs_usage(); exit(0); }
1241 //fprintf(stderr, "FATAL: unknown option '%s'!\n", arg);
1242 //exit(1);
1243 return 1;
1246 if (key == FUSE_OPT_KEY_NONOPT) {
1247 // not an option; first non-option arg is tagdb file name
1248 if (!tagfile_loaded) {
1249 uint64_t stt, ste;
1250 tagfilename = get_real_path(arg, 0);
1251 if (tagfilename == NULL) { fprintf(stderr, "FATAL: can't determine tag file name!\n"); return -1; }
1252 printf("loading database...\n");
1253 stt = k8clock();
1254 if (tagdb_load(tagfilename) != 0) { fprintf(stderr, "FATAL: can't load tagfile: '%s'!\n", tagfilename); return -1; }
1255 ste = k8clock();
1256 printf("database load time: %.15g seconds\n", (double)(ste-stt)/1000.0);
1257 printf("building hashes...\n");
1258 stt = k8clock();
1259 build_hashes();
1260 ste = k8clock();
1261 printf("hash build time: %.15g seconds\n", (double)(ste-stt)/1000.0);
1262 //if (file_info_count == 0) { fprintf(stderr, "FATAL: no valid files in tagfile: '%s'!\n", arg); exit(1); }
1263 tagfile_loaded = 1;
1264 return 0;
1267 return 1;
1270 return 1;
1274 int main (int argc, char *argv[]) {
1275 int fuse_stat;
1276 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1278 if (argc > 0 && argv[0] != NULL) argv00 = argv[0];
1280 dbglog_set_screenout(0);
1281 dbglog_set_fileout(0);
1283 if (getuid() == 0 || geteuid() == 0) {
1284 fprintf(stderr, "FATAL: running fusedrv as root is inappropriate!\n");
1285 return 1;
1287 //dlogf("=== starting fusedrv ===");
1289 fuse_opt_parse(&args, NULL, NULL, opt_processor);
1290 if (!tagfile_loaded) tfs_usage();
1291 //fuse_opt_add_arg(&args, "-omodules=subdir,subdir=/foo");
1292 fuse_opt_add_arg(&args, "-s"); // single-threaded
1294 //for (int f = 0; f < args.argc; ++f) fprintf(stderr, "arg#%d: [%s]\n", f, args.argv[f]);
1296 //fprintf(stderr, "about to call fuse_main\n");
1297 fuse_stat = fuse_main(args.argc, args.argv, &tfs_oper, (void *)666);
1298 //fprintf(stderr, "fuse_main returned %d\n", fuse_stat);
1300 clear_hashes();
1301 tagdb_unload();
1302 fuse_opt_free_args(&args);
1303 return fuse_stat;