fusedrv: more info in "/:/info"
[k8muffin.git] / src / fusedrv / fusedrv.c
blobf3281a8f12b3b9cf89fec0319398124a05c5cf3f
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"
50 ////////////////////////////////////////////////////////////////////////////////
51 #define NO_SEARCH_DBGLOG
52 //#define FUSEDRV_LOG_CALLS
55 ////////////////////////////////////////////////////////////////////////////////
56 static int opt_log_calls = 0;
58 # define fd_call_dlogf(...) do { \
59 if (opt_log_calls) dlogf(__VA_ARGS__); \
60 } while (0)
63 ////////////////////////////////////////////////////////////////////////////////
64 #include "../tagload.c"
65 #include "../search.c"
68 ////////////////////////////////////////////////////////////////////////////////
69 static char *tagfilename = NULL;
70 static int tfs_dirs_opened = 0; // # of opened dirs
71 static int tfs_need_reload = 0; // !0: reload when last dir closed
72 static uint64_t cur_dir_time = 1;
75 ////////////////////////////////////////////////////////////////////////////////
76 static inline void tagdb_check_and_reload (void) {
77 if (tfs_need_reload) {
78 if (tfs_dirs_opened == 0) {
79 dlogf("* reloading tagdb from '%s'...", tagfilename);
80 tfs_need_reload = 0;
81 clear_hashes();
82 dlogf("* hashes cleared");
83 tagdb_unload();
84 dlogf("* tagdb unloaded");
85 if (tagdb_load(tagfilename) != 0) {
86 dlogf("FATAL: can't load tagfile: '%s'!\n", tagfilename);
87 abort();
89 dlogf("* tagdb loaded");
90 build_hashes();
91 dlogf("* hashes built");
92 ++cur_dir_time;
98 ////////////////////////////////////////////////////////////////////////////////
99 static char *get_real_path (const char *fn, int add_slash) {
100 if (fn != NULL && fn[0]) {
101 char *pp = realpath(fn, NULL);
102 if (pp != NULL) {
103 if (pp[0]) {
104 if (add_slash && pp[strlen(pp)-1] != '/') {
105 char *t = malloc(strlen(pp)+2);
106 sprintf(t, "%s/", pp);
107 free(pp);
108 pp = t;
110 return pp;
112 free(pp);
115 return NULL;
119 ////////////////////////////////////////////////////////////////////////////////
120 //#define CTR ((my_data_struct_t *)(fuse_get_context()->private_data))
123 // report errors to logfile and give -errno to caller
125 static int tfs_error (const char *str) {
126 int ret = -errno;
127 fd_call_dlogf(" ERROR %s: %s", str, strerror(errno));
128 return ret;
133 static const fl_tagvalue_t *find_by_tag_name (const char *name) {
134 const char *t = strrchr(name, '/');
135 if (t == NULL) t = name; else ++t;
136 return get_tag_by_name(name, &tval_hash);
140 // short names are unique
141 static fl_fileinfo_t *find_by_short_name (const char *path) {
142 filename_t *sf;
143 const char *t = strrchr(path, '/');
144 if (t == NULL) t = path; else ++t;
145 HASH_FIND_STR(sname_hash, t, sf);
146 return (sf != NULL ? sf->fi : NULL);
150 ////////////////////////////////////////////////////////////////////////////////
151 typedef struct {
152 int fd; // <0: internal file
153 int space_eater;
154 int size;
155 char *data;
156 //UT_hash_handle hh;
157 } filehandle_t;
160 //static filehandle_t *fhandles = NULL;
163 static filehandle_t *colon_create_info_file (const char *path) {
164 filehandle_t *fh = NULL;
165 fd_call_dlogf("colon_create_info_file: [%s]\n", path);
166 if (strcmp(path, "/:/info") == 0) {
167 fh = calloc(1, sizeof(*fh));
168 fh->fd = -1;
169 fh->data = calloc(8192, 1);
170 //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));
171 snprintf(fh->data, 8192, "files: %d; tags: %d; opened dirs: %d; reload deferred: %d\n", HASH_COUNT(sname_hash), HASH_COUNT(tval_hash),
172 tfs_dirs_opened, tfs_need_reload
174 fh->size = strlen(fh->data);
176 return fh;
180 static filehandle_t *fht_open (const char *realpath) {
181 int fd = open(realpath, O_RDONLY);
182 if (fd >= 0) {
183 filehandle_t *fh = calloc(1, sizeof(*fh));
184 fh->fd = fd;
185 return fh;
187 return NULL;
191 static filehandle_t *fht_create_eater (const char *realpath) {
192 filehandle_t *fh = calloc(1, sizeof(*fh));
193 fh->fd = -1;
194 fh->space_eater = 1;
195 return fh;
199 static void fht_close (filehandle_t *fh) {
200 if (fh != NULL) {
201 if (fh->fd >= 0) close(fh->fd);
202 if (fh->data != NULL) free(fh->data);
203 free(fh);
208 static int fht_read (const char *path, char *buf, size_t size, off_t offset, filehandle_t *fh) {
209 if (fh != NULL) {
210 if (fh->fd >= 0) {
211 ssize_t rd;
212 fd_call_dlogf("tfs_read: path=[%s], buf=0x%p, size=%d, offset=%lld", path, buf, size, offset);
213 if (fh->fd < 0) return -EPERM;
214 if (offset >= 0x7fffffff || size >= 0x7fffffff) return 0;/*-EPERM;*/ //???
215 if (lseek(fh->fd, offset, SEEK_SET) == (off_t)-1) return -EPERM;
216 rd = read(fh->fd, buf, size);
217 if (rd < 0) return -EPERM;
218 return rd;
220 if (fh->data != NULL) {
221 if (offset >= fh->size) return 0;
222 if ((uint64_t)offset+size > fh->size) size = fh->size-offset;
223 if (size == 0) return 0;
224 memcpy(buf, fh->data+offset, size);
225 return size;
228 return -EPERM;
232 static int fht_write (const char *path, const char *buf, size_t size, off_t offset, filehandle_t *fh) {
233 if (fh != NULL) {
234 if (fh->fd < 0) {
235 if (fh->space_eater) return size; // eat 'em alive!
238 return -EPERM;
242 ////////////////////////////////////////////////////////////////////////////////
243 // this is used in getattr() and readdir()
244 // note that readdir() will call this with 'filename.ext' and getattr() will call this with full path
245 static const char *spxnames[] = {
246 ":otags",
247 ":ttags",
248 ":files",
249 ":or",
250 ":and",
253 static int do_stat (const char *path, struct stat *statbuf) {
254 fl_fileinfo_t *nfo;
255 const char *name;
256 memset(statbuf, 0, sizeof(*statbuf));
258 fd_call_dlogf("do_stat: [%s]\n", path);
259 //FIXME!
260 statbuf->st_uid = fuse_get_context()->uid;
261 statbuf->st_gid = fuse_get_context()->gid;
263 //TODO: set to 2? do we rally want 'find' to work on muffin?
264 statbuf->st_nlink = 2; // actually, this should be set to 'subdir_count+2' (or 1), else 'find' will fail
265 statbuf->st_mode = S_IFDIR|0555;
266 //statbuf->st_dev = get_fi(0)->devid;
267 //statbuf->st_ino = get_fi(0)->inode;
268 statbuf->st_blksize = 4096; // i/o block size
269 //statbuf->st_blocks = 1;
270 //statbuf->st_size = 1;
271 statbuf->st_atime = statbuf->st_ctime = statbuf->st_mtime = cur_dir_time;
273 //stat(get_str(get_fi(0)->realname), statbuf);
274 //stat("/home/ketmar", statbuf);
275 statbuf->st_mode = S_IFDIR|0555;
277 if (strcmp(path, "/") == 0) {
278 return 0;
280 if (strcmp(path, "/:") == 0) {
281 statbuf->st_mode = S_IFDIR|0755; // writeable
282 return 0;
284 if (strncmp(path, "/:/", 3) == 0) {
285 // all files are regular ones here
286 statbuf->st_mode = S_IFREG|0444;
287 if (strcmp(path, "/:/reload") == 0) {
288 if (tfs_need_reload) {
289 fd_call_dlogf(" stat: eater is here\n");
290 statbuf->st_mode = S_IFREG|0777;
291 return 0;
293 return -ENOENT;
295 statbuf->st_size = 4096; // arbitrary, but must be bigger than the actual 'info' files
296 return 0;
299 name = strrchr(path, '/');
300 if (name == NULL) name = path; else ++name;
301 fd_call_dlogf(" do_stat_name: [%s]\n", name);
303 if (name[0] == ':') {
304 fd_call_dlogf(" do_spec_stat: [%s]\n", name);
305 for (size_t f = 0; f < ARRAYLEN(spxnames); ++f) if (strcmp(name, spxnames[f]) == 0) {
306 fd_call_dlogf(" stat: SPECIAL\n");
307 return 0;
309 for (size_t f = 0; f < ARRAYLEN(spec_dirs); ++f) if (strcmp(spec_dirs[f].name, name) == 0) {
310 fd_call_dlogf(" stat: SPECIAL\n");
311 return 0;
313 dlogf("baddir: [%s]\n", path);
314 return -ENOENT;
317 if ((nfo = find_by_short_name(name)) != NULL) {
318 // normal file
320 if (do_real_stat) {
321 if (stat(nfo->fullname, statbuf) == 0) return 0;
322 return -ENOENT; //TODO: return better error here
325 fd_call_dlogf(" stat: file\n");
326 statbuf->st_dev = nfo->devid;
327 statbuf->st_ino = nfo->inode;
328 statbuf->st_mode = S_IFREG|0444;
329 statbuf->st_size = nfo->size;
330 statbuf->st_blksize = 4096; // i/o block size
331 statbuf->st_blocks = (nfo->size/512)+(nfo->size%512 ? 1 : 0);
332 statbuf->st_atime = statbuf->st_ctime = statbuf->st_mtime = nfo->mtime;
333 return 0;
336 //dlogf("not-a-file: [%s]\n", path);
337 if (find_by_tag_name(name) != NULL) {
338 // this is dirs too
339 fd_call_dlogf(" stat: dir\n");
340 return 0;
343 dlogf(" stat: ***not-found: [%s]\n", path);
344 return -ENOENT;
348 /** Get file attributes.
350 * Similar to stat(). The 'st_dev' and 'st_blksize' fields are
351 * ignored. The 'st_ino' field is ignored except if the 'use_ino'
352 * mount option is given.
354 static int tfs_getattr (const char *path, struct stat *statbuf) {
355 fd_call_dlogf("tfs_getattr: path=[%s]", path);
356 return do_stat(path, statbuf);
360 /** Read the target of a symbolic link
362 * The buffer should be filled with a null terminated string. The
363 * buffer size argument includes the space for the terminating
364 * null character. If the linkname is too long to fit in the
365 * buffer, it should be truncated. The return value should be 0
366 * for success.
368 // Note the system readlink() will truncate and lose the terminating
369 // null. So, the size passed to to the system readlink() must be one
370 // less than the size passed to tfs_readlink()
371 static int tfs_readlink (const char *path, char *link, size_t size) {
372 fd_call_dlogf("tfs_readlink: path=[%s]", path);
373 return -ENOENT; // there are no symlinks
377 /** Create a file node
379 * This is called for creation of all non-directory, non-symlink
380 * nodes. If the filesystem defines a create() method, then for
381 * regular files that will be called instead.
383 static int tfs_mknod (const char *path, mode_t mode, dev_t dev) {
384 fd_call_dlogf("tfs_mknod: path=[%s], mode=0%03o, dev=%lld", path, mode, dev);
386 //if (!S_ISREG(mode)) return -EINVAL; // only regular files can be created
387 // no new files can be created here
388 //return (exists ? EEXIST : EFAULT);
389 return -EFAULT;
393 /** Create a directory
395 * Note that the mode argument may not have the type specification
396 * bits set, i.e. S_ISDIR(mode) can be false. To obtain the
397 * correct directory type bits use mode|S_IFDIR
399 static int tfs_mkdir (const char *path, mode_t mode) {
400 fd_call_dlogf("tfs_mkdir: path=[%s], mode=0%03o", path, mode);
401 // no new directories can be created here
402 return -EFAULT;
406 /** Remove a file */
407 static int tfs_unlink (const char *path) {
408 fd_call_dlogf("tfs_unlink: path=[%s]", path);
409 return -EFAULT;
413 /** Remove a directory */
414 static int tfs_rmdir (const char *path) {
415 fd_call_dlogf("tfs_rmdir: path=[%s]", path);
416 return -EFAULT;
420 /** Create a symbolic link */
421 // The parameters here are a little bit confusing, but do correspond
422 // to the symlink() system call. The 'path' is where the link points,
423 // while the 'link' is the link itself. So we need to leave the path
424 // unaltered, but insert the link into the mounted directory.
425 static int tfs_symlink (const char *path, const char *link) {
426 fd_call_dlogf("tfs_symlink: path=[%s]; link=[%s]", path, link);
427 return -EFAULT;
431 /** Rename a file */
432 // both path and newpath are fs-relative
433 static int tfs_rename (const char *path, const char *newpath) {
434 fd_call_dlogf("tfs_rename: path=[%s]; newpath=[%s]", path, newpath);
435 return -EFAULT;
439 /** Create a hard link to a file */
440 static int tfs_link (const char *path, const char *newpath) {
441 fd_call_dlogf("tfs_link: path=[%s]; newpath=[%s]", path, newpath);
442 return -EFAULT;
446 /** Change the permission bits of a file */
447 static int tfs_chmod (const char *path, mode_t mode) {
448 fd_call_dlogf("tfs_chmod: path=[%s], mode=0%03o", path, mode);
449 return 0;
453 /** Change the owner and group of a file */
454 static int tfs_chown (const char *path, uid_t uid, gid_t gid) {
455 fd_call_dlogf("tfs_chown: path=[%s], uid=%d, gid=%d", path, uid, gid);
456 return 0;
460 /** Change the size of a file */
461 static int tfs_truncate (const char *path, off_t newsize) {
462 fd_call_dlogf("tfs_truncate: path=[%s], newsize=%lld", path, newsize);
463 return -EFAULT;
467 /** Change the access and/or modification times of a file
469 * Deprecated, use utimens() instead.
472 static int tfs_utime (const char *path, struct utimbuf *ubuf) {
473 return 0;
478 /** File open operation
480 * No creation (O_CREAT, O_EXCL) and by default also no
481 * truncation (O_TRUNC) flags will be passed to open(). If an
482 * application specifies O_TRUNC, fuse first calls truncate()
483 * and then open(). Only if 'atomic_o_trunc' has been
484 * specified and kernel version is 2.6.24 or later, O_TRUNC is
485 * passed on to open.
487 * Unless the 'default_permissions' mount option is given,
488 * open should check if the operation is permitted for the
489 * given flags. Optionally open may also return an arbitrary
490 * filehandle in the fuse_file_info structure, which will be
491 * passed to all file operations.
493 * Changed in version 2.2
495 static int tfs_open (const char *path, struct fuse_file_info *fi) {
496 fl_fileinfo_t *nfo;
497 filehandle_t *fh = NULL;
498 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));
499 if ((fi->flags&O_APPEND) || (fi->flags&O_TRUNC)) {
500 fd_call_dlogf(" bad flags!\n");
501 return -EPERM;
503 switch (fi->flags&(O_RDONLY|O_WRONLY|O_RDWR)) {
504 case O_RDONLY: break;
505 case O_WRONLY:
506 case O_RDWR:
507 fd_call_dlogf(" bad access mode!\n");
508 return -EPERM;
511 if (strncmp(path, "/:/", 3) == 0) {
512 fh = colon_create_info_file(path);
513 } else {
514 if ((nfo = find_by_short_name(path)) != NULL) {
515 fd_call_dlogf(" found: [%s]\n", get_str(nfo->realname));
516 fh = fht_open(get_str(nfo->realname));
519 if (fh != NULL) {
520 fi->fh = (uintptr_t)fh;
521 return 0;
523 fd_call_dlogf(" shit!\n");
524 return -ENOENT;
528 /** Read data from an open file
530 * Read should return exactly the number of bytes requested except
531 * on EOF or error, otherwise the rest of the data will be
532 * substituted with zeroes. An exception to this is when the
533 * 'direct_io' mount option is specified, in which case the return
534 * value of the read system call will reflect the return value of
535 * this operation.
537 * Changed in version 2.2
539 static int tfs_read (const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
540 filehandle_t *fh = (filehandle_t *)(uintptr_t)(fi->fh);
541 return fht_read(path, buf, size, offset, fh);
545 /** Write data to an open file
547 * Write should return exactly the number of bytes requested
548 * except on error. An exception to this is when the 'direct_io'
549 * mount option is specified (see read operation).
551 * Changed in version 2.2
553 static int tfs_write (const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
554 fd_call_dlogf("tfs_write: path=[%s], buf=0x%p, size=%d, offset=%lld, fi=0x%p", path, buf, size, offset, fi);
555 filehandle_t *fh = (filehandle_t *)(uintptr_t)(fi->fh);
556 return fht_write(path, buf, size, offset, fh);
560 /** Get file system statistics
562 * The 'f_frsize', 'f_favail', 'f_fsid' and 'f_flag' fields are ignored
564 * Replaced 'struct statfs' parameter with 'struct statvfs' in
565 * version 2.5
567 static int tfs_statfs (const char *path, struct statvfs *statv) {
568 return -EPERM;
570 //statvfs
571 //memset(statv, 0, sizeof(*statv));
572 //statv->f_bsize = 8192; // fixme
573 fd_call_dlogf("tfs_statfs: path=[%s], statv=0x%p", path, statv);
574 //return retstat;
578 /** Possibly flush cached data
580 * BIG NOTE: This is not equivalent to fsync(). It's not a
581 * request to sync dirty data.
583 * Flush is called on each close() of a file descriptor. So if a
584 * filesystem wants to return write errors in close() and the file
585 * has cached dirty data, this is a good place to write back data
586 * and return any errors. Since many applications ignore close()
587 * errors this is not always useful.
589 * NOTE: The flush() method may be called more than once for each
590 * open(). This happens if more than one file descriptor refers
591 * to an opened file due to dup(), dup2() or fork() calls. It is
592 * not possible to determine if a flush is final, so each flush
593 * should be treated equally. Multiple write-flush sequences are
594 * relatively rare, so this shouldn't be a problem.
596 * Filesystems shouldn't assume that flush will always be called
597 * after some writes, or that if will be called at all.
599 * Changed in version 2.2
601 static int tfs_flush (const char *path, struct fuse_file_info *fi) {
602 fd_call_dlogf("tfs_flush: path=[%s], fi=0x%p", path, fi);
603 return 0;
607 /** Release an open file
609 * Release is called when there are no more references to an open
610 * file: all file descriptors are closed and all memory mappings
611 * are unmapped.
613 * For every open() call there will be exactly one release() call
614 * with the same flags and file descriptor. It is possible to
615 * have a file opened more than once, in which case only the last
616 * release will mean, that no more reads/writes will happen on the
617 * file. The return value of release is ignored.
619 * Changed in version 2.2
621 static int tfs_release (const char *path, struct fuse_file_info *fi) {
622 filehandle_t *fh = (filehandle_t *)(uintptr_t)(fi->fh);
623 int is_eater = (fh != NULL ? fh->space_eater : 0);
624 fd_call_dlogf("tfs_release: path=[%s]; fh=%p; eater=%d\n", path, fh, is_eater);
625 fht_close(fh);
626 fi->fh = 0;
627 if (is_eater) tagdb_check_and_reload();
628 return 0;
632 /** Synchronize file contents
634 * If the datasync parameter is non-zero, then only the user data
635 * should be flushed, not the meta data.
637 * Changed in version 2.2
639 static int tfs_fsync (const char *path, int datasync, struct fuse_file_info *fi) {
640 fd_call_dlogf("tfs_fsync: path=[%s], datasync=%d, fi=0x%p", path, datasync, fi);
641 return 0;
645 /** Set extended attributes */
646 static int tfs_setxattr (const char *path, const char *name, const char *value, size_t size, int flags) {
647 fd_call_dlogf("tfs_setxattr: path=[%s], name=[%s], value=[%s], size=%d, flags=0x%08x", path, name, value, size, (unsigned int)flags);
648 return -EPERM;
652 static const struct {
653 const char *name;
654 int tflidx;
655 int hidden;
656 } xattr_tags[] = {
657 {.name="user.oartist", .tflidx=TFL_ARTIST_O, .hidden=1},
658 {.name="user.artist", .tflidx=TFL_ARTIST_O, .hidden=0},
659 {.name="user._artist", .tflidx=TFL_ARTIST_T, .hidden=0},
660 {.name="user.tartist", .tflidx=TFL_ARTIST_T, .hidden=1},
661 {.name="user.oalbum", .tflidx=TFL_ALBUM_O, .hidden=1},
662 {.name="user.album", .tflidx=TFL_ALBUM_O, .hidden=0},
663 {.name="user._album", .tflidx=TFL_ALBUM_T, .hidden=0},
664 {.name="user.talbum", .tflidx=TFL_ALBUM_T, .hidden=1},
665 {.name="user.otitle", .tflidx=TFL_TITLE_O, .hidden=1},
666 {.name="user.title", .tflidx=TFL_TITLE_O, .hidden=0},
667 {.name="user._title", .tflidx=TFL_TITLE_T, .hidden=0},
668 {.name="user.ttitle", .tflidx=TFL_TITLE_T, .hidden=1},
669 {.name="user.ogenre", .tflidx=TFL_GENRE_O, .hidden=1},
670 {.name="user.genre", .tflidx=TFL_GENRE_O, .hidden=0},
671 {.name="user._genre", .tflidx=TFL_GENRE_T, .hidden=0},
672 {.name="user.tgenre", .tflidx=TFL_GENRE_T, .hidden=1},
673 {.name="user.year", .tflidx=TFL_YEAR, .hidden=0},
677 /** Get extended attributes */
678 // If the given node described by path has the named extended attribute, and the vlen is zero,
679 // return the number of bytes required to provide the value of the extended attribute.
680 // The next call will have a buffer that is long enough to hold the value. Copy the value into
681 // the buffer and return the number of bytes that the value occupies.
682 static int tfs_getxattr (const char *path, const char *name, char *value, size_t size) {
683 fd_call_dlogf("tfs_getxattr: path=[%s], name=[%s], value=0x%p, size=%d", path, name, value, size);
684 if (name != NULL && name[0]) {
685 fl_fileinfo_t *nfo;
686 const char *val = NULL;
687 int len;
688 if ((nfo = find_by_short_name(path)) == NULL) {
689 // satisfy 'ls -l'
691 struct stat sb;
692 if (do_stat(path, &sb) != 0) return -ENOENT;
694 // just return 'no data' for any attribute of any path; it's harmful (i think)
695 return 0;
697 if (strcmp(name, "user.realname") == 0) {
698 val = get_str(nfo->realname);
699 } else {
700 for (size_t f = 0; f < ARRAYLEN(xattr_tags); ++f) {
701 if (strcmp(name, xattr_tags[f].name) == 0) {
702 val = get_str(get_tv(nfo->tags[xattr_tags[f].tflidx])->value);
703 break;
707 if (val != NULL) {
708 len = strlen(val);
709 if (size > 0 && len > 0) memcpy(value, val, len);
710 return len;
712 return 0;
714 //return -EPERM;
715 return 0;
719 #define STRXCPY(str) do { \
720 const char *s = (str); \
721 if (s != NULL && s[0]) { \
722 int xlen = strlen(s)+1; \
723 if (list != NULL) { memcpy(list, s, xlen); list += xlen; } \
724 tlen += xlen; \
726 } while (0)
729 /** List extended attributes */
730 // size: the same note as for getxattr applies here.
731 // fill 'list' with asciiz-strings, one just after another.
732 // note that 'getfattr -d' lists only 'user.' attributes
733 static int tfs_listxattr (const char *path, char *list, size_t size) {
734 fl_fileinfo_t *nfo;
735 int tlen = 0;
736 fd_call_dlogf("tfs_listxattr: path=[%s], list=0x%p, size=%d", path, list, size);
737 if ((nfo = find_by_short_name(path)) == NULL) return -ENOENT;
738 STRXCPY("user.realname");
740 for (size_t f = 0; f < ARRAYLEN(xattr_tags); ++f) if (!xattr_tags[f].hidden) STRXCPY(xattr_tags[f].name);
742 return tlen;
743 //return -EPERM;
747 /** Remove extended attributes */
748 static int tfs_removexattr (const char *path, const char *name) {
749 fd_call_dlogf("tfs_removexattr: path=[%s], name=[%s]", path, name);
750 return -EPERM;
754 /** Open directory
756 * Unless the 'default_permissions' mount option is given,
757 * this method should check if opendir is permitted for this
758 * directory. Optionally opendir may also return an arbitrary
759 * filehandle in the fuse_file_info structure, which will be
760 * passed to readdir, closedir and fsyncdir.
762 * Introduced in version 2.3
764 static int tfs_opendir (const char *path, struct fuse_file_info *fi) {
765 dirinfo_t *odir;
766 tagdb_check_and_reload();
767 odir = list_dir(path);
768 if (odir == NULL) {
769 fd_call_dlogf("tfs_opendir: BAD [%s]\n", path);
770 return -EPERM;
772 ++tfs_dirs_opened;
773 fd_call_dlogf("tfs_opendir: path=[%s], %d entries", path, odir->fcount);
774 fi->fh = (uintptr_t)odir; // fh is 64 bit, so this is safe
775 return 0;
779 /** Read directory
781 * This supersedes the old getdir() interface. New applications
782 * should use this.
784 * The filesystem may choose between two modes of operation:
786 * 1) The readdir implementation ignores the offset parameter, and
787 * passes zero to the filler function's offset. The filler
788 * function will not return '1' (unless an error happens), so the
789 * whole directory is read in a single readdir operation. This
790 * works just like the old getdir() method.
792 * 2) The readdir implementation keeps track of the offsets of the
793 * directory entries. It uses the offset parameter and always
794 * passes non-zero offset to the filler function. When the buffer
795 * is full (or an error happens) the filler function will return
796 * '1'.
798 * Introduced in version 2.3
801 * typedef int (*fuse_fill_dir_t) (void *buf, const char *name, const struct stat *stbuf, off_t off);
803 static int tfs_readdir (const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
804 dirinfo_t *odir = (dirinfo_t *)(uintptr_t)fi->fh;
805 struct stat stbuf, *starg = &stbuf;
806 fd_call_dlogf("tfs_readdir: path=[%s], buf=0x%p, filler=0x%p, offset=%lld, fi=0x%p", path, buf, filler, offset, fi);
807 if (odir == NULL) return -EPERM;
808 //dlogf("tfs_readdir: path=[%s], %d entries", path, odir->fcount);
809 if (offset < 0 || offset >= odir->fcount) return 0;
810 // we can build 'struct stat' here, so readdir() will return some useful flags
811 // but this is not really necessary, and we can just pass NULL
812 if (do_stat(odir->names[offset], starg) != 0) starg = NULL; // don't pass anything on error
813 //starg = NULL;
814 if (filler(buf, odir->names[offset], starg, offset+1)) {
815 fd_call_dlogf("tfs_readdir: path=[%s], filler failed!", path);
816 return -ENOMEM;
818 //dlogf("tfs_readdir: path=[%s], name=[%s]", path, odir->names[offset]);
819 return 0;
823 /** Release directory
825 * Introduced in version 2.3
827 static int tfs_releasedir (const char *path, struct fuse_file_info *fi) {
828 dirinfo_t *odir = (dirinfo_t *)(uintptr_t)fi->fh;
829 fd_call_dlogf("tfs_releasedir: path=[%s], fi=0x%p", path, fi);
830 if (odir == NULL) {
831 tagdb_check_and_reload();
832 return -EPERM;
834 if (odir->names != NULL) free(odir->names);
835 free(odir);
836 fi->fh = 0;
837 --tfs_dirs_opened;
838 tagdb_check_and_reload();
839 return 0;
843 /** Synchronize directory contents
845 * If the datasync parameter is non-zero, then only the user data
846 * should be flushed, not the meta data
848 * Introduced in version 2.3
850 // when exactly is this called? when a user calls fsync and it
851 // happens to be a directory? ???
852 static int tfs_fsyncdir (const char *path, int datasync, struct fuse_file_info *fi) {
853 fd_call_dlogf("tfs_fsyncdir: path=[%s], datasync=%d, fi=0x%p", path, datasync, fi);
854 return 0;
859 * Initialize filesystem
861 * The return value will passed in the private_data field of
862 * fuse_context to all file operations and as a parameter to the
863 * destroy() method.
865 * Introduced in version 2.3
866 * Changed in version 2.6
868 // Undocumented but extraordinarily useful fact: the fuse_context is
869 // set up before this function is called, and
870 // fuse_get_context()->private_data returns the user_data passed to
871 // fuse_main(). Really seems like either it should be a third
872 // parameter coming in here, or else the fact should be documented
873 // (and this might as well return void, as it did in older versions of
874 // FUSE).
875 static void *tfs_init (struct fuse_conn_info *conn) {
876 char tmpbuf[4096];
877 dlogf("tfs_init: protocol v%u.%u", conn->proto_major, conn->proto_minor);
878 // dump caps
879 if (conn->capable) {
880 tmpbuf[0] = 0;
881 if (conn->capable&FUSE_CAP_ASYNC_READ) strcat(tmpbuf, " FUSE_CAP_ASYNC_READ");
882 if (conn->capable&FUSE_CAP_POSIX_LOCKS) strcat(tmpbuf, " FUSE_CAP_POSIX_LOCKS"); // filesystem supports "remote" locking
883 if (conn->capable&FUSE_CAP_ATOMIC_O_TRUNC) strcat(tmpbuf, " FUSE_CAP_ATOMIC_O_TRUNC"); // filesystem handles the O_TRUNC open flag
884 if (conn->capable&FUSE_CAP_EXPORT_SUPPORT) strcat(tmpbuf, " FUSE_CAP_EXPORT_SUPPORT"); // filesystem handles lookups of "." and ".."
885 if (conn->capable&FUSE_CAP_BIG_WRITES) strcat(tmpbuf, " FUSE_CAP_BIG_WRITES"); // filesystem can handle write size larger than 4kB
886 if (conn->capable&FUSE_CAP_DONT_MASK) strcat(tmpbuf, " FUSE_CAP_DONT_MASK"); // don't apply umask to file mode on create operations
887 dlogf("kernel caps:%s\n", tmpbuf);
889 // set our params
890 conn->want = 0; // the only flag we want is FUSE_CAP_BIG_WRITES, but we don't support writing
891 conn->async_read = 0; // we don't support async reads
892 return fuse_get_context()->private_data;
897 * Clean up filesystem
899 * Called on filesystem exit.
901 * Introduced in version 2.3
903 static void tfs_destroy (void *userdata) {
904 dlogf("tfs_destroy: userdata=0x%p", userdata);
909 * Check file access permissions
911 * This will be called for the access() system call. If the
912 * 'default_permissions' mount option is given, this method is not
913 * called.
915 * This method is not called under Linux kernel versions 2.4.x
917 * Introduced in version 2.5
919 //k8: don't know if mode can be combination of flags, and what to do then
920 static int tfs_access (const char *path, int mode) {
921 struct stat st;
922 int res;
923 //dlogf("tfs_access: path=[%s], mode=0x%02x", path, mode);
924 if (mode&W_OK) {
925 //dlogf(" W_OK: [%s]\n", path);
926 return -EPERM; // can't write
928 res = do_stat(path, &st);
929 if (res == 0) {
930 if (S_ISDIR(st.st_mode)) return 0;
931 if (mode&X_OK) return -EPERM; // can't execute
932 return 0;
934 return res;
939 * Create and open a file
941 * If the file does not exist, first create it with the specified
942 * mode, and then open it.
944 * If this method is not implemented or under Linux kernel
945 * versions earlier than 2.6.15, the mknod() and open() methods
946 * will be called instead.
948 * Introduced in version 2.5
950 static int tfs_create (const char *path, mode_t mode, struct fuse_file_info *fi) {
951 fd_call_dlogf("tfs_create: path=[%s], mode=0%03o, fi=0x%p", path, mode, fi);
952 if (strcmp(path, "/:/reload") == 0) {
953 // special action: reload tagdb
954 filehandle_t *fh;
955 tfs_need_reload = 1;
956 // defer reloading, 'cause FUSE will call tfs_getattr() just after creating the eater
957 //tagdb_check_and_reload();
958 if ((fh = fht_create_eater(path)) != NULL) {
959 fi->fh = (uintptr_t)fh;
960 return 0;
962 return -EPERM;
964 //fi->fh = fd;
965 return -EPERM;
970 * Change the size of an open file
972 * This method is called instead of the truncate() method if the
973 * truncation was invoked from an ftruncate() system call.
975 * If this method is not implemented or under Linux kernel
976 * versions earlier than 2.6.15, the truncate() method will be
977 * called instead.
979 * Introduced in version 2.5
981 static int tfs_ftruncate (const char *path, off_t offset, struct fuse_file_info *fi) {
982 //dlogf("tfs_ftruncate: path=[%s], offset=%lld, fi=0x%p", path, offset, fi);
983 return -EPERM;
988 * Get attributes from an open file
990 * This method is called instead of the getattr() method if the
991 * file information is available.
993 * Currently this is only called after the create() method if that
994 * is implemented (see above). Later it may be called for
995 * invocations of fstat() too.
997 * Introduced in version 2.5
999 static int tfs_fgetattr (const char *path, struct stat *statbuf, struct fuse_file_info *fi) {
1000 //dlogf("tfs_fgetattr: path=[%s], statbuf=0x%p, fi=0x%p", path, statbuf, fi);
1001 return tfs_getattr(path, statbuf); // just pass it up
1006 * Perform POSIX file locking operation
1008 * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW.
1010 * For the meaning of fields in 'struct flock' see the man page
1011 * for fcntl(2). The l_whence field will always be set to
1012 * SEEK_SET.
1014 * For checking lock ownership, the 'fuse_file_info->owner'
1015 * argument must be used.
1017 * For F_GETLK operation, the library will first check currently
1018 * held locks, and if a conflicting lock is found it will return
1019 * information without calling this method. This ensures, that
1020 * for local locks the l_pid field is correctly filled in. The
1021 * results may not be accurate in case of race conditions and in
1022 * the presence of hard links, but it's unlikly that an
1023 * application would rely on accurate GETLK results in these
1024 * cases. If a conflicting lock is not found, this method will be
1025 * called, and the filesystem may fill out l_pid by a meaningful
1026 * value, or it may leave this field zero.
1028 * For F_SETLK and F_SETLKW the l_pid field will be set to the pid
1029 * of the process performing the locking operation.
1031 * Note: if this method is not implemented, the kernel will still
1032 * allow file locking to work locally. Hence it is only
1033 * interesting for network filesystems and similar.
1035 * Introduced in version 2.6
1038 static int tfs_lock (const char *path, struct fuse_file_info *fi, int cmd, struct flock *) {
1039 return -EPERM;
1045 * Change the access and modification times of a file with
1046 * nanosecond resolution
1048 * Introduced in version 2.6
1050 static int tfs_utimens (const char *path, const struct timespec tv[2]) {
1051 return 0; // pretend that we done it
1056 * Map block index within file to block index within device
1058 * Note: This makes sense only for block device backed filesystems
1059 * mounted with the 'blkdev' option
1061 * Introduced in version 2.6
1064 static int tfs_bmap (const char *path, size_t blocksize, uint64_t *idx) {
1065 return -EPERM;
1071 * Ioctl
1073 * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in
1074 * 64bit environment. The size and direction of data is
1075 * determined by _IOC_*() decoding of cmd. For _IOC_NONE,
1076 * data will be NULL, for _IOC_WRITE data is out area, for
1077 * _IOC_READ in area and if both are set in/out area. In all
1078 * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes.
1080 * Introduced in version 2.8
1083 static int tfs_ioctl (const char *path, int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void *data) {
1084 return -EPERM;
1090 * Poll for IO readiness events
1092 * Note: If ph is non-NULL, the client should notify
1093 * when IO readiness events occur by calling
1094 * fuse_notify_poll() with the specified ph.
1096 * Regardless of the number of times poll with a non-NULL ph
1097 * is received, single notification is enough to clear all.
1098 * Notifying more times incurs overhead but doesn't harm
1099 * correctness.
1101 * The callee is responsible for destroying ph with
1102 * fuse_pollhandle_destroy() when no longer in use.
1104 * Introduced in version 2.8
1107 static int tfs_poll (const char *path, struct fuse_file_info *fi, struct fuse_pollhandle *ph, unsigned *reventsp) {
1108 return -EPERM;
1113 ////////////////////////////////////////////////////////////////////////////////
1114 static const struct fuse_operations tfs_oper = {
1115 .getattr = tfs_getattr,
1116 .readlink = tfs_readlink,
1117 .getdir = NULL, // deprecated
1118 .mknod = tfs_mknod,
1119 .mkdir = tfs_mkdir,
1120 .unlink = tfs_unlink,
1121 .rmdir = tfs_rmdir,
1122 .symlink = tfs_symlink,
1123 .rename = tfs_rename,
1124 .link = tfs_link,
1125 .chmod = tfs_chmod,
1126 .chown = tfs_chown,
1127 .truncate = tfs_truncate,
1128 .utime = NULL, // deprecated
1129 .open = tfs_open,
1130 .read = tfs_read,
1131 .write = tfs_write,
1132 .statfs = tfs_statfs,
1133 .flush = tfs_flush,
1134 .release = tfs_release,
1135 .fsync = tfs_fsync,
1136 .setxattr = tfs_setxattr,
1137 .getxattr = tfs_getxattr,
1138 .listxattr = tfs_listxattr,
1139 .removexattr = tfs_removexattr,
1140 .opendir = tfs_opendir,
1141 .readdir = tfs_readdir,
1142 .releasedir = tfs_releasedir,
1143 .fsyncdir = tfs_fsyncdir,
1144 .init = tfs_init,
1145 .destroy = tfs_destroy,
1146 .access = tfs_access,
1147 .create = tfs_create,
1148 .ftruncate = tfs_ftruncate,
1149 .fgetattr = tfs_fgetattr,
1150 //.lock = tfs_lock,
1151 .utimens = tfs_utimens,
1152 //.bmap = tfs_bmap,
1155 * Flag indicating, that the filesystem can accept a NULL path
1156 * as the first argument for the following operations:
1158 * read, write, flush, release, fsync, readdir, releasedir,
1159 * fsyncdir, ftruncate, fgetattr and lock
1161 //unsigned int flag_nullpath_ok : 1;
1162 .flag_nullpath_ok = 0,
1163 // 2.8 API
1164 //.ioctl = tfs_ioctl,
1165 //.poll = tfs_poll,
1169 static void tfs_usage (void) {
1170 fprintf(stderr,
1171 "usage: fusedrv [mount options] tags.dat mountpoint\n"
1172 "special options:\n"
1173 " --debug write some debug logs\n"
1174 " --files show files not only in /:files\n"
1176 exit(1);
1180 static int tagfile_loaded = 0;
1181 static const char *argv00 = "./me";
1184 // return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept
1185 static int opt_processor (void *data, const char *arg, int key, struct fuse_args *outargs) {
1186 //fprintf(stderr, "key=%d; arg=%s\n", key, arg);
1188 if (key == FUSE_OPT_KEY_OPT) {
1189 // this is not FUSE option, handle it
1190 // question: how to handle cases like '-x fuck'?
1191 if (strcmp(arg, "--debug") == 0) {
1192 char *me = strdup(argv00), *logname;
1193 if (strrchr(me, '/')) strrchr(me, '/')[0] = 0;
1194 logname = get_real_path(me, 1);
1195 free(me);
1196 if (logname != NULL) {
1197 char *ln = malloc(strlen(logname)+128);
1198 sprintf(ln, "%smuffin.log", logname);
1199 dbglog_set_filename(ln);
1200 dbglog_set_screenout(0);
1201 dbglog_set_fileout(1);
1202 free(ln);
1203 free(logname);
1205 return 0;
1208 if (strcmp(arg, "--no-debug") == 0) {
1209 dbglog_set_screenout(0);
1210 dbglog_set_fileout(0);
1211 return 0;
1214 if (strcmp(arg, "--debug-calls") == 0) {
1215 opt_log_calls = 1;
1216 return 0;
1219 if (strcmp(arg, "--files") == 0) {
1220 opt_files_only_in_files = 0;
1221 return 0;
1224 if (strcmp(arg, "--no-files") == 0) {
1225 opt_files_only_in_files = 1;
1226 return 0;
1229 if (strcmp(arg, "--help") == 0) {
1230 tfs_usage();
1231 return 0;
1234 if (strcmp(arg, "-h") == 0) { tfs_usage(); exit(0); }
1235 //fprintf(stderr, "FATAL: unknown option '%s'!\n", arg);
1236 //exit(1);
1237 return 1;
1240 if (key == FUSE_OPT_KEY_NONOPT) {
1241 // not an option; first non-option arg is tagdb file name
1242 if (!tagfile_loaded) {
1243 clock_t stt, ste;
1244 tagfilename = get_real_path(arg, 0);
1245 if (tagfilename == NULL) { fprintf(stderr, "FATAL: can't determine tag file name!\n"); return -1; }
1246 printf("loading database...\n");
1247 stt = clock();
1248 if (tagdb_load(tagfilename) != 0) { fprintf(stderr, "FATAL: can't load tagfile: '%s'!\n", tagfilename); return -1; }
1249 ste = clock();
1250 printf("database load time: %.15g seconds\n", (double)(ste-stt)/(double)CLOCKS_PER_SEC);
1251 printf("building hashes...\n");
1252 stt = clock();
1253 build_hashes();
1254 ste = clock();
1255 printf("hash build time: %.15g seconds\n", (double)(ste-stt)/(double)CLOCKS_PER_SEC);
1256 //if (file_info_count == 0) { fprintf(stderr, "FATAL: no valid files in tagfile: '%s'!\n", arg); exit(1); }
1257 tagfile_loaded = 1;
1258 return 0;
1261 return 1;
1264 return 1;
1268 int main (int argc, char *argv[]) {
1269 int fuse_stat;
1270 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1272 if (argc > 0 && argv[0] != NULL) argv00 = argv[0];
1274 dbglog_set_screenout(0);
1275 dbglog_set_fileout(0);
1277 if (getuid() == 0 || geteuid() == 0) {
1278 fprintf(stderr, "FATAL: running fusedrv as root is inappropriate!\n");
1279 return 1;
1281 //dlogf("=== starting fusedrv ===");
1283 fuse_opt_parse(&args, NULL, NULL, opt_processor);
1284 if (!tagfile_loaded) tfs_usage();
1285 //fuse_opt_add_arg(&args, "-omodules=subdir,subdir=/foo");
1286 fuse_opt_add_arg(&args, "-s"); // single-threaded
1288 //for (int f = 0; f < args.argc; ++f) fprintf(stderr, "arg#%d: [%s]\n", f, args.argv[f]);
1290 //fprintf(stderr, "about to call fuse_main\n");
1291 fuse_stat = fuse_main(args.argc, args.argv, &tfs_oper, (void *)666);
1292 //fprintf(stderr, "fuse_main returned %d\n", fuse_stat);
1294 clear_hashes();
1295 tagdb_unload();
1296 fuse_opt_free_args(&args);
1297 return fuse_stat;