added /:/info pseudofile (just a sample)
[k8muffin.git] / src / fusedrv / fusedrv.c
blob2cc00d243d498be2bd790d58537980f5b09fd0cd
1 #define FUSE_USE_VERSION 26
3 #ifndef _BSD_SOURCE
4 # define _BSD_SOURCE
5 #endif
6 #ifndef _GNU_SOURCE
7 # define _GNU_SOURCE
8 #endif
10 #include <ctype.h>
11 #include <dirent.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <fuse.h>
15 #include <iconv.h>
16 #include <libgen.h>
17 #include <limits.h>
18 #include <stdint.h>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <time.h>
23 #include <unistd.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <sys/xattr.h>
29 #include "../libdbg/dbglog.h"
30 #include "../uthash.h"
31 #include "translit.h"
34 ////////////////////////////////////////////////////////////////////////////////
35 #include "../tagload.c"
36 #include "../search.c"
39 ////////////////////////////////////////////////////////////////////////////////
40 //#define CTR ((my_data_struct_t *)(fuse_get_context()->private_data))
43 // report errors to logfile and give -errno to caller
45 static int tfs_error (const char *str) {
46 int ret = -errno;
47 dlogf(" ERROR %s: %s", str, strerror(errno));
48 return ret;
53 static tagvalue_t *find_by_tag_name (const char *name, tagvalue_t **hash) {
54 tagvalue_t *tv;
55 const char *t = strrchr(name, '/');
56 if (t == NULL) t = name; else ++t;
57 HASH_FIND_STR(*hash, t, tv);
58 return tv;
63 // short names are unique, so we can skip queries
64 static file_info_t *find_by_short_name (const char *path) {
65 file_sname_t *sf;
66 const char *t = strrchr(path, '/');
67 if (t == NULL) t = path; else ++t;
68 HASH_FIND_STR(file_sname_hash, t, sf);
69 return (sf != NULL ? sf->fi : NULL);
73 ////////////////////////////////////////////////////////////////////////////////
74 typedef struct {
75 int fd; // <0: internal file
76 int size;
77 char *data;
78 //UT_hash_handle hh;
79 } filehandle_t;
82 //static filehandle_t *fhandles = NULL;
85 static filehandle_t *colon_create_info_file (const char *path) {
86 filehandle_t *fh = NULL;
87 dlogf("colon_create_info_file: [%s]\n", path);
88 if (strcmp(path, "/:/info") == 0) {
89 fh = calloc(1, sizeof(*fh));
90 fh->fd = -1;
91 fh->data = calloc(8192, 1);
92 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));
93 fh->size = strlen(fh->data);
95 return fh;
99 static filehandle_t *fht_open (const char *realpath) {
100 int fd = open(realpath, O_RDONLY);
101 if (fd >= 0) {
102 filehandle_t *fh = calloc(1, sizeof(*fh));
103 fh->fd = fd;
104 return fh;
106 return NULL;
110 static void fht_close (filehandle_t *fh) {
111 if (fh != NULL) {
112 if (fh->fd >= 0) close(fh->fd);
113 if (fh->data != NULL) free(fh->data);
114 free(fh);
119 static int fht_read (const char *path, char *buf, size_t size, off_t offset, filehandle_t *fh) {
120 if (fh != NULL) {
121 if (fh->fd >= 0) {
122 ssize_t rd;
123 //dlogf("tfs_read: path=[%s], buf=0x%p, size=%d, offset=%lld, fi=0x%p", path, buf, size, offset, fi);
124 if (fh->fd < 0) return -EPERM;
125 if (offset >= 0x7fffffff || size >= 0x7fffffff) return 0;/*-EPERM;*/ //???
126 if (lseek(fh->fd, offset, SEEK_SET) == (off_t)-1) return -EPERM;
127 rd = read(fh->fd, buf, size);
128 if (rd < 0) return -EPERM;
129 return rd;
131 if (fh->data != NULL) {
132 if (offset >= fh->size) return 0;
133 if ((uint64_t)offset+size > fh->size) size = fh->size-offset;
134 if (size == 0) return 0;
135 memcpy(buf, fh->data+offset, size);
136 return size;
139 return -EPERM;
143 ////////////////////////////////////////////////////////////////////////////////
144 // this is used in getattr() and readdir()
145 // note that readdir() will call this with 'filename.ext' and getattr() will call this with full path
146 static int do_stat (const char *path, struct stat *statbuf, int do_real_stat) {
147 file_info_t *nfo;
148 const char *name;
149 memset(statbuf, 0, sizeof(*statbuf));
151 //FIXME!
152 statbuf->st_uid = fuse_get_context()->uid;
153 statbuf->st_gid = fuse_get_context()->gid;
155 statbuf->st_mode = S_IFDIR|0555;
156 //TODO: set to 2? do we rally want 'find' to work on muffin?
157 statbuf->st_nlink = 1; // actually, this should be set to 'subdir_count+2' (or 1), else 'find' will fail
159 if (strcmp(path, "/") == 0 || strcmp(path, "/:otags") == 0 || strcmp(path, "/:") == 0) return 0;
160 if (strncmp(path, "/:/", 3) == 0) {
161 // all files are regular ones here
162 statbuf->st_mode = S_IFREG|0444;
163 statbuf->st_size = 4096; // arbitrary, but must be bigger than the actual 'info' files
164 return 0;
167 name = strrchr(path, '/');
168 if (name == NULL) name = path; else ++name;
170 if (name[0] == ':') {
171 for (size_t f = 0; f < ARRAYLEN(specdirs); ++f) if (strcmp(specdirs[f], name) == 0) return 0;
172 if (strcmp(name, ":files") == 0) return 0;
173 return -ENOENT;
176 if ((nfo = find_by_short_name(name)) != NULL) {
177 // normal file
178 if (do_real_stat) {
179 if (stat(nfo->fullname, statbuf) == 0) return 0;
180 return -ENOENT; //TODO: return better error here
182 statbuf->st_mode = S_IFREG|0444;
183 return 0;
186 if (find_by_tag_name(name, &tagv_hash_t) != NULL || find_by_tag_name(name, &tagv_hash_o) != NULL) return 0; // this is dirs too
188 return -ENOENT;
192 /** Get file attributes.
194 * Similar to stat(). The 'st_dev' and 'st_blksize' fields are
195 * ignored. The 'st_ino' field is ignored except if the 'use_ino'
196 * mount option is given.
198 static int tfs_getattr (const char *path, struct stat *statbuf) {
199 //dlogf("tfs_getattr: path=[%s]", path);
200 return do_stat(path, statbuf, 1);
204 /** Read the target of a symbolic link
206 * The buffer should be filled with a null terminated string. The
207 * buffer size argument includes the space for the terminating
208 * null character. If the linkname is too long to fit in the
209 * buffer, it should be truncated. The return value should be 0
210 * for success.
212 // Note the system readlink() will truncate and lose the terminating
213 // null. So, the size passed to to the system readlink() must be one
214 // less than the size passed to tfs_readlink()
215 static int tfs_readlink (const char *path, char *link, size_t size) {
216 //dlogf("tfs_readlink: path=[%s]", path);
217 return -ENOENT; // there are no symlinks
221 /** Create a file node
223 * This is called for creation of all non-directory, non-symlink
224 * nodes. If the filesystem defines a create() method, then for
225 * regular files that will be called instead.
227 static int tfs_mknod (const char *path, mode_t mode, dev_t dev) {
228 //dlogf("tfs_mknod: path=[%s], mode=0%03o, dev=%lld", path, mode, dev);
230 //if (!S_ISREG(mode)) return -EINVAL; // only regular files can be created
231 // no new files can be created here
232 //return (exists ? EEXIST : EFAULT);
233 return -EFAULT;
237 /** Create a directory
239 * Note that the mode argument may not have the type specification
240 * bits set, i.e. S_ISDIR(mode) can be false. To obtain the
241 * correct directory type bits use mode|S_IFDIR
243 static int tfs_mkdir (const char *path, mode_t mode) {
244 //dlogf("tfs_mkdir: path=[%s], mode=0%03o", path, mode);
245 // no new directories can be created here
246 return -EFAULT;
250 /** Remove a file */
251 static int tfs_unlink (const char *path) {
252 //dlogf("tfs_unlink: path=[%s]", path);
253 return -EFAULT;
257 /** Remove a directory */
258 static int tfs_rmdir (const char *path) {
259 //dlogf("tfs_rmdir: path=[%s]", path);
260 return -EFAULT;
264 /** Create a symbolic link */
265 // The parameters here are a little bit confusing, but do correspond
266 // to the symlink() system call. The 'path' is where the link points,
267 // while the 'link' is the link itself. So we need to leave the path
268 // unaltered, but insert the link into the mounted directory.
269 static int tfs_symlink (const char *path, const char *link) {
270 //dlogf("tfs_symlink: path=[%s]; link=[%s]", path, link);
271 return -EFAULT;
275 /** Rename a file */
276 // both path and newpath are fs-relative
277 static int tfs_rename (const char *path, const char *newpath) {
278 //dlogf("tfs_rename: path=[%s]; newpath=[%s]", path, newpath);
279 return -EFAULT;
283 /** Create a hard link to a file */
284 static int tfs_link (const char *path, const char *newpath) {
285 //dlogf("tfs_link: path=[%s]; newpath=[%s]", path, newpath);
286 return -EFAULT;
290 /** Change the permission bits of a file */
291 static int tfs_chmod (const char *path, mode_t mode) {
292 //dlogf("tfs_chmod: path=[%s], mode=0%03o", path, mode);
293 return 0;
297 /** Change the owner and group of a file */
298 static int tfs_chown (const char *path, uid_t uid, gid_t gid) {
299 //dlogf("tfs_chown: path=[%s], uid=%d, gid=%d", path, uid, gid);
300 return 0;
304 /** Change the size of a file */
305 static int tfs_truncate (const char *path, off_t newsize) {
306 //dlogf("tfs_truncate: path=[%s], newsize=%lld", path, newsize);
307 return -EFAULT;
311 /** Change the access and/or modification times of a file
313 * Deprecated, use utimens() instead.
316 static int tfs_utime (const char *path, struct utimbuf *ubuf) {
317 return 0;
322 /** File open operation
324 * No creation (O_CREAT, O_EXCL) and by default also no
325 * truncation (O_TRUNC) flags will be passed to open(). If an
326 * application specifies O_TRUNC, fuse first calls truncate()
327 * and then open(). Only if 'atomic_o_trunc' has been
328 * specified and kernel version is 2.6.24 or later, O_TRUNC is
329 * passed on to open.
331 * Unless the 'default_permissions' mount option is given,
332 * open should check if the operation is permitted for the
333 * given flags. Optionally open may also return an arbitrary
334 * filehandle in the fuse_file_info structure, which will be
335 * passed to all file operations.
337 * Changed in version 2.2
339 static int tfs_open (const char *path, struct fuse_file_info *fi) {
340 file_info_t *nfo;
341 filehandle_t *fh = NULL;
342 //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));
343 if ((fi->flags&O_APPEND) || (fi->flags&O_TRUNC)) {
344 //dlogf(" bad flags!\n");
345 return -EPERM;
347 switch (fi->flags&(O_RDONLY|O_WRONLY|O_RDWR)) {
348 case O_RDONLY: break;
349 case O_WRONLY:
350 case O_RDWR:
351 //dlogf(" bad access mode!\n");
352 return -EPERM;
355 if (strncmp(path, "/:/", 3) == 0) {
356 fh = colon_create_info_file(path);
357 } else {
358 if ((nfo = find_by_short_name(path)) != NULL) {
359 //dlogf(" found: [%s]\n", nfo->fullname);
360 fh = fht_open(nfo->fullname);
363 if (fh != NULL) {
364 fi->fh = (uintptr_t)fh;
365 return 0;
367 //dlogf(" shit!\n");
368 return -ENOENT;
372 /** Read data from an open file
374 * Read should return exactly the number of bytes requested except
375 * on EOF or error, otherwise the rest of the data will be
376 * substituted with zeroes. An exception to this is when the
377 * 'direct_io' mount option is specified, in which case the return
378 * value of the read system call will reflect the return value of
379 * this operation.
381 * Changed in version 2.2
383 static int tfs_read (const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
384 filehandle_t *fh = (filehandle_t *)(uintptr_t)(fi->fh);
385 return fht_read(path, buf, size, offset, fh);
389 /** Write data to an open file
391 * Write should return exactly the number of bytes requested
392 * except on error. An exception to this is when the 'direct_io'
393 * mount option is specified (see read operation).
395 * Changed in version 2.2
397 static int tfs_write (const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
398 //dlogf("tfs_write: path=[%s], buf=0x%p, size=%d, offset=%lld, fi=0x%p", path, buf, size, offset, fi);
399 return -EPERM;
403 /** Get file system statistics
405 * The 'f_frsize', 'f_favail', 'f_fsid' and 'f_flag' fields are ignored
407 * Replaced 'struct statfs' parameter with 'struct statvfs' in
408 * version 2.5
410 static int tfs_statfs (const char *path, struct statvfs *statv) {
411 return -EPERM;
413 //statvfs
414 //memset(statv, 0, sizeof(*statv));
415 //statv->f_bsize = 8192; // fixme
416 //dlogf("tfs_statfs: path=[%s], statv=0x%p", path, statv);
417 //return retstat;
421 /** Possibly flush cached data
423 * BIG NOTE: This is not equivalent to fsync(). It's not a
424 * request to sync dirty data.
426 * Flush is called on each close() of a file descriptor. So if a
427 * filesystem wants to return write errors in close() and the file
428 * has cached dirty data, this is a good place to write back data
429 * and return any errors. Since many applications ignore close()
430 * errors this is not always useful.
432 * NOTE: The flush() method may be called more than once for each
433 * open(). This happens if more than one file descriptor refers
434 * to an opened file due to dup(), dup2() or fork() calls. It is
435 * not possible to determine if a flush is final, so each flush
436 * should be treated equally. Multiple write-flush sequences are
437 * relatively rare, so this shouldn't be a problem.
439 * Filesystems shouldn't assume that flush will always be called
440 * after some writes, or that if will be called at all.
442 * Changed in version 2.2
444 static int tfs_flush (const char *path, struct fuse_file_info *fi) {
445 //dlogf("tfs_flush: path=[%s], fi=0x%p", path, fi);
446 return 0;
450 /** Release an open file
452 * Release is called when there are no more references to an open
453 * file: all file descriptors are closed and all memory mappings
454 * are unmapped.
456 * For every open() call there will be exactly one release() call
457 * with the same flags and file descriptor. It is possible to
458 * have a file opened more than once, in which case only the last
459 * release will mean, that no more reads/writes will happen on the
460 * file. The return value of release is ignored.
462 * Changed in version 2.2
464 static int tfs_release (const char *path, struct fuse_file_info *fi) {
465 filehandle_t *fh = (filehandle_t *)(uintptr_t)(fi->fh);
466 fht_close(fh);
467 fi->fh = 0;
468 return 0;
472 /** Synchronize file contents
474 * If the datasync parameter is non-zero, then only the user data
475 * should be flushed, not the meta data.
477 * Changed in version 2.2
479 static int tfs_fsync (const char *path, int datasync, struct fuse_file_info *fi) {
480 //dlogf("tfs_fsync: path=[%s], datasync=%d, fi=0x%p", path, datasync, fi);
481 return 0;
485 /** Set extended attributes */
486 static int tfs_setxattr (const char *path, const char *name, const char *value, size_t size, int flags) {
487 //dlogf("tfs_setxattr: path=[%s], name=[%s], value=[%s], size=%d, flags=0x%08x", path, name, value, size, (unsigned int)flags);
488 return -EPERM;
492 /** Get extended attributes */
493 // If the given node described by path has the named extended attribute, and the vlen is zero,
494 // return the number of bytes required to provide the value of the extended attribute.
495 // The next call will have a buffer that is long enough to hold the value. Copy the value into
496 // the buffer and return the number of bytes that the value occupies.
497 static int tfs_getxattr (const char *path, const char *name, char *value, size_t size) {
498 //dlogf("tfs_getxattr: path=[%s], name=[%s], value=0x%p, size=%d", path, name, value, size);
499 if (name != NULL && name[0]) {
500 file_info_t *nfo;
501 const char *val = NULL;
502 int len;
503 if ((nfo = find_by_short_name(path)) == NULL) return -ENOENT;
504 if (strcmp(name, "user.realname") == 0) {
505 val = nfo->fullname;
506 } else if (strcmp(name, "user.oartist") == 0 || strcmp(name, "user.artist") == 0) {
507 if (nfo->artist_o != NULL) val = nfo->artist_o->s;
508 } else if (strcmp(name, "user.oalbum") == 0 || strcmp(name, "user.album") == 0) {
509 if (nfo->album_o != NULL) val = nfo->album_o->s;
510 } else if (strcmp(name, "user.otitle") == 0 || strcmp(name, "user.title") == 0) {
511 if (nfo->title_o != NULL) val = nfo->title_o->s;
512 } else if (strcmp(name, "user.ogenre") == 0 || strcmp(name, "user.genre") == 0) {
513 if (nfo->genre_o != NULL) val = nfo->genre_o->s;
514 } else if (strcmp(name, "user.oyear") == 0 || strcmp(name, "user._year") == 0 || strcmp(name, "user.year") == 0) {
515 if (nfo->year_o != NULL) val = nfo->year_o->s;
516 } else if (strcmp(name, "user._artist") == 0) {
517 if (nfo->artist_t != NULL) val = nfo->artist_t->s;
518 } else if (strcmp(name, "user._album") == 0) {
519 if (nfo->album_t != NULL) val = nfo->album_t->s;
520 } else if (strcmp(name, "user._title") == 0) {
521 if (nfo->title_t != NULL) val = nfo->title_t->s;
522 } else if (strcmp(name, "user._genre") == 0) {
523 if (nfo->genre_t != NULL) val = nfo->genre_t->s;
525 if (val != NULL) {
526 len = strlen(val);
527 if (size > 0 && len > 0) memcpy(value, val, len);
528 return len;
530 return 0;
532 //return -EPERM;
533 return 0;
537 #define STRXCPY(str) do { \
538 const char *s = (str); \
539 if (s != NULL && s[0]) { \
540 int xlen = strlen(s)+1; \
541 if (list != NULL) { memcpy(list, s, xlen); list += xlen; } \
542 tlen += xlen; \
544 } while (0)
547 static inline int ok_tv (const tagvalue_t *tv, const char *unk) {
548 if (tv != NULL) {
549 if (unk != NULL && strcmp(unk, tv->s) == 0) return 0;
550 if (tv->s[0] == 0) return 0;
551 return 1;
553 return 0;
557 /** List extended attributes */
558 // size: the same note as for getxattr applies here.
559 // fill 'list' with asciiz-strings, one just after another.
560 // note that 'getfattr -d' lists only 'user.' attributes
561 static int tfs_listxattr (const char *path, char *list, size_t size) {
562 file_info_t *nfo;
563 int tlen = 0;
564 //dlogf("tfs_listxattr: path=[%s], list=0x%p, size=%d", path, list, size);
565 if ((nfo = find_by_short_name(path)) == NULL) return -ENOENT;
566 STRXCPY("user.realname");
568 if (ok_tv(nfo->artist_o, "unknown artist")) STRXCPY("user.artist");
569 if (ok_tv(nfo->album_o, "unknown album")) STRXCPY("user.album");
570 if (ok_tv(nfo->title_o, "unknown title")) STRXCPY("user.title");
571 if (ok_tv(nfo->genre_o, "unknown genre")) STRXCPY("user.genre");
572 if (ok_tv(nfo->year_o, NULL)) STRXCPY("user.year");
574 if (ok_tv(nfo->artist_t, "unknown artist")) STRXCPY("user._artist");
575 if (ok_tv(nfo->album_t, "unknown album")) STRXCPY("user._album");
576 if (ok_tv(nfo->title_t, "unknown title")) STRXCPY("user._title");
577 if (ok_tv(nfo->genre_t, "unknown genre")) STRXCPY("user._genre");
579 return tlen;
580 //return -EPERM;
584 /** Remove extended attributes */
585 static int tfs_removexattr (const char *path, const char *name) {
586 //dlogf("tfs_removexattr: path=[%s], name=[%s]", path, name);
587 return -EPERM;
591 /** Open directory
593 * Unless the 'default_permissions' mount option is given,
594 * this method should check if opendir is permitted for this
595 * directory. Optionally opendir may also return an arbitrary
596 * filehandle in the fuse_file_info structure, which will be
597 * passed to readdir, closedir and fsyncdir.
599 * Introduced in version 2.3
601 static int tfs_opendir (const char *path, struct fuse_file_info *fi) {
602 dirinfo_t *odir = list_dir(path);
603 if (odir == NULL) return -EPERM;
604 //dlogf("tfs_opendir: path=[%s], %d entries", path, odir->fcount);
605 fi->fh = (uintptr_t)odir; // fh is 64 bit, so this is safe
606 return 0;
610 /** Read directory
612 * This supersedes the old getdir() interface. New applications
613 * should use this.
615 * The filesystem may choose between two modes of operation:
617 * 1) The readdir implementation ignores the offset parameter, and
618 * passes zero to the filler function's offset. The filler
619 * function will not return '1' (unless an error happens), so the
620 * whole directory is read in a single readdir operation. This
621 * works just like the old getdir() method.
623 * 2) The readdir implementation keeps track of the offsets of the
624 * directory entries. It uses the offset parameter and always
625 * passes non-zero offset to the filler function. When the buffer
626 * is full (or an error happens) the filler function will return
627 * '1'.
629 * Introduced in version 2.3
632 * typedef int (*fuse_fill_dir_t) (void *buf, const char *name, const struct stat *stbuf, off_t off);
634 static int tfs_readdir (const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
635 dirinfo_t *odir = (dirinfo_t *)(uintptr_t)fi->fh;
636 struct stat stbuf, *starg = &stbuf;
637 //dlogf("tfs_readdir: path=[%s], buf=0x%p, filler=0x%p, offset=%lld, fi=0x%p", path, buf, filler, offset, fi);
638 if (odir == NULL) return -EPERM;
639 //dlogf("tfs_readdir: path=[%s], %d entries", path, odir->fcount);
640 if (offset < 0 || offset >= odir->fcount) return 0;
641 // we can build 'struct stat' here, so readdir() will return some useful flags
642 // but this is not really necessary, and we can just pass NULL
643 if (do_stat(odir->names[offset], starg, 0) != 0) starg = NULL; // don't pass anything on error
644 if (filler(buf, odir->names[offset], starg, offset+1)) {
645 //dlogf("tfs_readdir: path=[%s], filler failed!", path);
646 return -ENOMEM;
648 //dlogf("tfs_readdir: path=[%s], name=[%s]", path, odir->names[offset]);
649 return 0;
653 /** Release directory
655 * Introduced in version 2.3
657 static int tfs_releasedir (const char *path, struct fuse_file_info *fi) {
658 dirinfo_t *odir = (dirinfo_t *)(uintptr_t)fi->fh;
659 //dlogf("tfs_releasedir: path=[%s], fi=0x%p", path, fi);
660 if (odir == NULL) return -EPERM;
661 if (odir->names != NULL) free(odir->names);
662 free(odir);
663 fi->fh = 0;
664 return 0;
668 /** Synchronize directory contents
670 * If the datasync parameter is non-zero, then only the user data
671 * should be flushed, not the meta data
673 * Introduced in version 2.3
675 // when exactly is this called? when a user calls fsync and it
676 // happens to be a directory? ???
677 static int tfs_fsyncdir (const char *path, int datasync, struct fuse_file_info *fi) {
678 //dlogf("tfs_fsyncdir: path=[%s], datasync=%d, fi=0x%p", path, datasync, fi);
679 return 0;
684 * Initialize filesystem
686 * The return value will passed in the private_data field of
687 * fuse_context to all file operations and as a parameter to the
688 * destroy() method.
690 * Introduced in version 2.3
691 * Changed in version 2.6
693 // Undocumented but extraordinarily useful fact: the fuse_context is
694 // set up before this function is called, and
695 // fuse_get_context()->private_data returns the user_data passed to
696 // fuse_main(). Really seems like either it should be a third
697 // parameter coming in here, or else the fact should be documented
698 // (and this might as well return void, as it did in older versions of
699 // FUSE).
700 static void *tfs_init (struct fuse_conn_info *conn) {
701 char tmpbuf[4096];
702 dlogf("tfs_init: protocol v%u.%u", conn->proto_major, conn->proto_minor);
703 // dump caps
704 if (conn->capable) {
705 tmpbuf[0] = 0;
706 if (conn->capable&FUSE_CAP_ASYNC_READ) strcat(tmpbuf, " FUSE_CAP_ASYNC_READ");
707 if (conn->capable&FUSE_CAP_POSIX_LOCKS) strcat(tmpbuf, " FUSE_CAP_POSIX_LOCKS"); // filesystem supports "remote" locking
708 if (conn->capable&FUSE_CAP_ATOMIC_O_TRUNC) strcat(tmpbuf, " FUSE_CAP_ATOMIC_O_TRUNC"); // filesystem handles the O_TRUNC open flag
709 if (conn->capable&FUSE_CAP_EXPORT_SUPPORT) strcat(tmpbuf, " FUSE_CAP_EXPORT_SUPPORT"); // filesystem handles lookups of "." and ".."
710 if (conn->capable&FUSE_CAP_BIG_WRITES) strcat(tmpbuf, " FUSE_CAP_BIG_WRITES"); // filesystem can handle write size larger than 4kB
711 if (conn->capable&FUSE_CAP_DONT_MASK) strcat(tmpbuf, " FUSE_CAP_DONT_MASK"); // don't apply umask to file mode on create operations
712 dlogf("kernel caps:%s\n", tmpbuf);
714 // set our params
715 conn->want = 0; // the only flag we want is FUSE_CAP_BIG_WRITES, but we don't support writing
716 conn->async_read = 0; // we don't support async reads
717 return fuse_get_context()->private_data;
722 * Clean up filesystem
724 * Called on filesystem exit.
726 * Introduced in version 2.3
728 static void tfs_destroy (void *userdata) {
729 dlogf("tfs_destroy: userdata=0x%p", userdata);
734 * Check file access permissions
736 * This will be called for the access() system call. If the
737 * 'default_permissions' mount option is given, this method is not
738 * called.
740 * This method is not called under Linux kernel versions 2.4.x
742 * Introduced in version 2.5
744 //k8: don't know if mode can be combination of flags, and what to do then
745 static int tfs_access (const char *path, int mode) {
746 const char *name;
747 //dlogf("tfs_access: path=[%s], mode=0%d", path, mode);
748 if (mode&W_OK) return -EPERM; // can't write
749 if (strcmp(path, "/") == 0 || strcmp(path, "/:otags") == 0) return 0; // all perms
750 name = strrchr(path, '/')+1;
751 if (name[0] == ':') {
752 for (size_t f = 0; f < ARRAYLEN(specdirs); ++f) if (strcmp(specdirs[f], name) == 0) return 0;
753 if (strcmp(name, ":files") == 0) return 0;
754 return -ENOENT;
756 if (find_by_tag_name(name, &tagv_hash_t) != NULL) return 0;
757 if (find_by_tag_name(name, &tagv_hash_o) != NULL) return 0;
758 if (mode&X_OK) return -EPERM; // can't execute
759 if (find_by_short_name(name) != NULL) return 0;
760 return -EPERM; // no such file
765 * Create and open a file
767 * If the file does not exist, first create it with the specified
768 * mode, and then open it.
770 * If this method is not implemented or under Linux kernel
771 * versions earlier than 2.6.15, the mknod() and open() methods
772 * will be called instead.
774 * Introduced in version 2.5
776 static int tfs_create (const char *path, mode_t mode, struct fuse_file_info *fi) {
777 //dlogf("tfs_create: path=[%s], mode=0%03o, fi=0x%p", path, mode, fi);
778 //fi->fh = fd;
779 return -EPERM;
784 * Change the size of an open file
786 * This method is called instead of the truncate() method if the
787 * truncation was invoked from an ftruncate() system call.
789 * If this method is not implemented or under Linux kernel
790 * versions earlier than 2.6.15, the truncate() method will be
791 * called instead.
793 * Introduced in version 2.5
795 static int tfs_ftruncate (const char *path, off_t offset, struct fuse_file_info *fi) {
796 //dlogf("tfs_ftruncate: path=[%s], offset=%lld, fi=0x%p", path, offset, fi);
797 return -EPERM;
802 * Get attributes from an open file
804 * This method is called instead of the getattr() method if the
805 * file information is available.
807 * Currently this is only called after the create() method if that
808 * is implemented (see above). Later it may be called for
809 * invocations of fstat() too.
811 * Introduced in version 2.5
813 static int tfs_fgetattr (const char *path, struct stat *statbuf, struct fuse_file_info *fi) {
814 //dlogf("tfs_fgetattr: path=[%s], statbuf=0x%p, fi=0x%p", path, statbuf, fi);
815 return tfs_getattr(path, statbuf); // just pass it up
820 * Perform POSIX file locking operation
822 * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW.
824 * For the meaning of fields in 'struct flock' see the man page
825 * for fcntl(2). The l_whence field will always be set to
826 * SEEK_SET.
828 * For checking lock ownership, the 'fuse_file_info->owner'
829 * argument must be used.
831 * For F_GETLK operation, the library will first check currently
832 * held locks, and if a conflicting lock is found it will return
833 * information without calling this method. This ensures, that
834 * for local locks the l_pid field is correctly filled in. The
835 * results may not be accurate in case of race conditions and in
836 * the presence of hard links, but it's unlikly that an
837 * application would rely on accurate GETLK results in these
838 * cases. If a conflicting lock is not found, this method will be
839 * called, and the filesystem may fill out l_pid by a meaningful
840 * value, or it may leave this field zero.
842 * For F_SETLK and F_SETLKW the l_pid field will be set to the pid
843 * of the process performing the locking operation.
845 * Note: if this method is not implemented, the kernel will still
846 * allow file locking to work locally. Hence it is only
847 * interesting for network filesystems and similar.
849 * Introduced in version 2.6
852 static int tfs_lock (const char *path, struct fuse_file_info *fi, int cmd, struct flock *) {
853 return -EPERM;
859 * Change the access and modification times of a file with
860 * nanosecond resolution
862 * Introduced in version 2.6
864 static int tfs_utimens (const char *path, const struct timespec tv[2]) {
865 return 0; // pretend that we done it
870 * Map block index within file to block index within device
872 * Note: This makes sense only for block device backed filesystems
873 * mounted with the 'blkdev' option
875 * Introduced in version 2.6
878 static int tfs_bmap (const char *path, size_t blocksize, uint64_t *idx) {
879 return -EPERM;
885 * Ioctl
887 * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in
888 * 64bit environment. The size and direction of data is
889 * determined by _IOC_*() decoding of cmd. For _IOC_NONE,
890 * data will be NULL, for _IOC_WRITE data is out area, for
891 * _IOC_READ in area and if both are set in/out area. In all
892 * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes.
894 * Introduced in version 2.8
897 static int tfs_ioctl (const char *path, int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void *data) {
898 return -EPERM;
904 * Poll for IO readiness events
906 * Note: If ph is non-NULL, the client should notify
907 * when IO readiness events occur by calling
908 * fuse_notify_poll() with the specified ph.
910 * Regardless of the number of times poll with a non-NULL ph
911 * is received, single notification is enough to clear all.
912 * Notifying more times incurs overhead but doesn't harm
913 * correctness.
915 * The callee is responsible for destroying ph with
916 * fuse_pollhandle_destroy() when no longer in use.
918 * Introduced in version 2.8
921 static int tfs_poll (const char *path, struct fuse_file_info *fi, struct fuse_pollhandle *ph, unsigned *reventsp) {
922 return -EPERM;
927 ////////////////////////////////////////////////////////////////////////////////
928 static const struct fuse_operations tfs_oper = {
929 .getattr = tfs_getattr,
930 .readlink = tfs_readlink,
931 .getdir = NULL, // deprecated
932 .mknod = tfs_mknod,
933 .mkdir = tfs_mkdir,
934 .unlink = tfs_unlink,
935 .rmdir = tfs_rmdir,
936 .symlink = tfs_symlink,
937 .rename = tfs_rename,
938 .link = tfs_link,
939 .chmod = tfs_chmod,
940 .chown = tfs_chown,
941 .truncate = tfs_truncate,
942 .utime = NULL, // deprecated
943 .open = tfs_open,
944 .read = tfs_read,
945 .write = tfs_write,
946 .statfs = tfs_statfs,
947 .flush = tfs_flush,
948 .release = tfs_release,
949 .fsync = tfs_fsync,
950 .setxattr = tfs_setxattr,
951 .getxattr = tfs_getxattr,
952 .listxattr = tfs_listxattr,
953 .removexattr = tfs_removexattr,
954 .opendir = tfs_opendir,
955 .readdir = tfs_readdir,
956 .releasedir = tfs_releasedir,
957 .fsyncdir = tfs_fsyncdir,
958 .init = tfs_init,
959 .destroy = tfs_destroy,
960 .access = tfs_access,
961 .create = tfs_create,
962 .ftruncate = tfs_ftruncate,
963 .fgetattr = tfs_fgetattr,
964 //.lock = tfs_lock,
965 .utimens = tfs_utimens,
966 //.bmap = tfs_bmap,
969 * Flag indicating, that the filesystem can accept a NULL path
970 * as the first argument for the following operations:
972 * read, write, flush, release, fsync, readdir, releasedir,
973 * fsyncdir, ftruncate, fgetattr and lock
975 //unsigned int flag_nullpath_ok : 1;
976 .flag_nullpath_ok = 0,
977 // 2.8 API
978 //.ioctl = tfs_ioctl,
979 //.poll = tfs_poll,
983 static void tfs_usage (void) {
984 fprintf(stderr,
985 "usage: fusedrv [mount options] tags.dat mountpoint\n"
986 "special options:\n"
987 " --debug write some debug logs\n"
988 " --nokoi don't use koi8 (you will loose some nice features)\n"
989 " --stat do stat() on startup to skip invalid files\n"
991 exit(1);
995 static int tagfile_loaded = 0;
996 static const char *argv00 = "./me";
998 // return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept
999 static int opt_processor (void *data, const char *arg, int key, struct fuse_args *outargs) {
1000 //fprintf(stderr, "key=%d; arg=%s\n", key, arg);
1002 if (key == FUSE_OPT_KEY_OPT) {
1003 // this is not FUSE option, handle it
1004 // question: how to handle cases like '-x fuck'?
1005 if (strcmp(arg, "--debug") == 0) {
1006 static char logname[8192], rn[8192];
1007 strcpy(logname, argv00);
1008 if (strrchr(logname, '/')) strrchr(logname, '/')[0] = 0;
1009 char *pp = realpath(logname, rn);
1010 if (pp == NULL || pp[0] != '/') {
1011 fprintf(stderr, "FATAL: realpath(\"%s\") fucked! [%s]\n", logname, pp);
1012 abort();
1014 if (pp[strlen(pp)-1] != '/') strcat(pp, "/");
1015 strcat(pp, "fusedrv.log");
1016 dbglog_set_filename(pp);
1017 dbglog_set_screenout(0);
1018 dbglog_set_fileout(1);
1019 return 0;
1022 if (strcmp(arg, "--help") == 0) {
1023 tfs_usage();
1024 return 0;
1027 if (strcmp(arg, "--nodebug") == 0) {
1028 dbglog_set_screenout(0);
1029 dbglog_set_fileout(0);
1030 return 0;
1033 if (strcmp(arg, "--nokoi") == 0) {
1034 opt_tagload_koi8 = 0;
1035 return 0;
1038 if (strcmp(arg, "--koi") == 0) {
1039 opt_tagload_koi8 = 1;
1040 return 0;
1043 if (strcmp(arg, "--stat") == 0) {
1044 opt_tagload_dostat = 1;
1045 return 0;
1048 if (strcmp(arg, "--nostat") == 0) {
1049 opt_tagload_dostat = 0;
1050 return 0;
1053 if (strcmp(arg, "-h") == 0) { tfs_usage(); exit(0); }
1054 //fprintf(stderr, "FATAL: unknown option '%s'!\n", arg);
1055 //exit(1);
1056 return 1;
1059 if (key == FUSE_OPT_KEY_NONOPT) {
1060 // not an option; first non-option arg is tagdb file name
1061 if (!tagfile_loaded) {
1062 clock_t stt, ste;
1063 stt = clock();
1064 if (load_tagfile(arg) != 0) { fprintf(stderr, "FATAL: can't open tagfile: '%s'!\n", arg); exit(1); }
1065 ste = clock();
1066 printf("database load time: %.15g seconds\n", (double)(ste-stt)/(double)CLOCKS_PER_SEC);
1067 if (file_info_count == 0) { fprintf(stderr, "FATAL: no valid files in tagfile: '%s'!\n", arg); exit(1); }
1068 tagfile_loaded = 1;
1069 return 0;
1072 return 1;
1075 return 1;
1079 int main (int argc, char *argv[]) {
1080 int fuse_stat;
1081 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1083 if (argc > 0 && argv[0] != NULL) argv00 = argv[0];
1085 dbglog_set_screenout(0);
1086 dbglog_set_fileout(0);
1088 if (getuid() == 0 || geteuid() == 0) {
1089 fprintf(stderr, "FATAL: running fusedrv as root is inappropriate!\n");
1090 return 1;
1092 //dlogf("=== starting fusedrv ===");
1094 fuse_opt_parse(&args, NULL, NULL, opt_processor);
1095 if (!tagfile_loaded) tfs_usage();
1096 //fuse_opt_add_arg(&args, "-omodules=subdir,subdir=/foo");
1097 fuse_opt_add_arg(&args, "-s"); // single-threaded
1099 //for (int f = 0; f < args.argc; ++f) fprintf(stderr, "arg#%d: [%s]\n", f, args.argv[f]);
1101 //fprintf(stderr, "about to call fuse_main\n");
1102 fuse_stat = fuse_main(args.argc, args.argv, &tfs_oper, (void *)666);
1103 //fprintf(stderr, "fuse_main returned %d\n", fuse_stat);
1105 fuse_opt_free_args(&args);
1106 return fuse_stat;