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
42 #include <sys/types.h>
43 #include <sys/xattr.h>
45 #include "../libdbg/dbglog.h"
46 #include "../uthash.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__); \
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
);
82 dlogf("* hashes cleared");
84 dlogf("* tagdb unloaded");
85 if (tagdb_load(tagfilename
) != 0) {
86 dlogf("FATAL: can't load tagfile: '%s'!\n", tagfilename
);
89 dlogf("* tagdb loaded");
91 dlogf("* hashes built");
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
);
104 if (add_slash
&& pp
[strlen(pp
)-1] != '/') {
105 char *t
= malloc(strlen(pp
)+2);
106 sprintf(t
, "%s/", pp
);
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) {
127 fd_call_dlogf(" ERROR %s: %s", str, strerror(errno));
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
) {
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 ////////////////////////////////////////////////////////////////////////////////
152 int fd
; // <0: internal file
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
));
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
);
180 static filehandle_t
*fht_open (const char *realpath
) {
181 int fd
= open(realpath
, O_RDONLY
);
183 filehandle_t
*fh
= calloc(1, sizeof(*fh
));
191 static filehandle_t
*fht_create_eater (const char *realpath
) {
192 filehandle_t
*fh
= calloc(1, sizeof(*fh
));
199 static void fht_close (filehandle_t
*fh
) {
201 if (fh
->fd
>= 0) close(fh
->fd
);
202 if (fh
->data
!= NULL
) free(fh
->data
);
208 static int fht_read (const char *path
, char *buf
, size_t size
, off_t offset
, filehandle_t
*fh
) {
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
;
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
);
232 static int fht_write (const char *path
, const char *buf
, size_t size
, off_t offset
, filehandle_t
*fh
) {
235 if (fh
->space_eater
) return size
; // eat 'em alive!
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
[] = {
253 static int do_stat (const char *path
, struct stat
*statbuf
) {
256 memset(statbuf
, 0, sizeof(*statbuf
));
258 fd_call_dlogf("do_stat: [%s]\n", path
);
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) {
280 if (strcmp(path
, "/:") == 0) {
281 statbuf
->st_mode
= S_IFDIR
|0755; // writeable
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;
295 statbuf
->st_size
= 4096; // arbitrary, but must be bigger than the actual 'info' files
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");
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");
313 dlogf("baddir: [%s]\n", path
);
317 if ((nfo
= find_by_short_name(name
)) != NULL
) {
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
;
336 //dlogf("not-a-file: [%s]\n", path);
337 if (find_by_tag_name(name
) != NULL
) {
339 fd_call_dlogf(" stat: dir\n");
343 dlogf(" stat: ***not-found: [%s]\n", path
);
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
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);
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
407 static int tfs_unlink (const char *path
) {
408 fd_call_dlogf("tfs_unlink: path=[%s]", path
);
413 /** Remove a directory */
414 static int tfs_rmdir (const char *path
) {
415 fd_call_dlogf("tfs_rmdir: path=[%s]", path
);
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
);
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
);
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
);
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
);
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
);
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
);
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) {
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
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
) {
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");
503 switch (fi
->flags
&(O_RDONLY
|O_WRONLY
|O_RDWR
)) {
504 case O_RDONLY
: break;
507 fd_call_dlogf(" bad access mode!\n");
511 if (strncmp(path
, "/:/", 3) == 0) {
512 fh
= colon_create_info_file(path
);
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
));
520 fi
->fh
= (uintptr_t)fh
;
523 fd_call_dlogf(" shit!\n");
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
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
567 static int tfs_statfs (const char *path
, struct statvfs
*statv
) {
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
);
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
);
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
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
);
627 if (is_eater
) tagdb_check_and_reload();
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
);
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
);
652 static const struct {
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]) {
686 const char *val
= NULL
;
688 if ((nfo
= find_by_short_name(path
)) == NULL
) {
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)
697 if (strcmp(name
, "user.realname") == 0) {
698 val
= get_str(nfo
->realname
);
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
);
709 if (size
> 0 && len
> 0) memcpy(value
, val
, len
);
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; } \
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
) {
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
);
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
);
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
) {
766 tagdb_check_and_reload();
767 odir
= list_dir(path
);
769 fd_call_dlogf("tfs_opendir: BAD [%s]\n", path
);
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
781 * This supersedes the old getdir() interface. New applications
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
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
814 if (filler(buf
, odir
->names
[offset
], starg
, offset
+1)) {
815 fd_call_dlogf("tfs_readdir: path=[%s], filler failed!", path
);
818 //dlogf("tfs_readdir: path=[%s], name=[%s]", path, odir->names[offset]);
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
);
831 tagdb_check_and_reload();
834 if (odir
->names
!= NULL
) free(odir
->names
);
838 tagdb_check_and_reload();
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
);
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
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
875 static void *tfs_init (struct fuse_conn_info
*conn
) {
877 dlogf("tfs_init: protocol v%u.%u", conn
->proto_major
, conn
->proto_minor
);
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
);
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
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
) {
923 //dlogf("tfs_access: path=[%s], mode=0x%02x", path, mode);
925 //dlogf(" W_OK: [%s]\n", path);
926 return -EPERM
; // can't write
928 res
= do_stat(path
, &st
);
930 if (S_ISDIR(st
.st_mode
)) return 0;
931 if (mode
&X_OK
) return -EPERM
; // can't execute
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
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
;
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
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);
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
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 *) {
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) {
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) {
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
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) {
1113 ////////////////////////////////////////////////////////////////////////////////
1114 static const struct fuse_operations tfs_oper
= {
1115 .getattr
= tfs_getattr
,
1116 .readlink
= tfs_readlink
,
1117 .getdir
= NULL
, // deprecated
1120 .unlink
= tfs_unlink
,
1122 .symlink
= tfs_symlink
,
1123 .rename
= tfs_rename
,
1127 .truncate
= tfs_truncate
,
1128 .utime
= NULL
, // deprecated
1132 .statfs
= tfs_statfs
,
1134 .release
= tfs_release
,
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
,
1145 .destroy
= tfs_destroy
,
1146 .access
= tfs_access
,
1147 .create
= tfs_create
,
1148 .ftruncate
= tfs_ftruncate
,
1149 .fgetattr
= tfs_fgetattr
,
1151 .utimens
= tfs_utimens
,
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,
1164 //.ioctl = tfs_ioctl,
1169 static void tfs_usage (void) {
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"
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);
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);
1208 if (strcmp(arg
, "--no-debug") == 0) {
1209 dbglog_set_screenout(0);
1210 dbglog_set_fileout(0);
1214 if (strcmp(arg
, "--debug-calls") == 0) {
1219 if (strcmp(arg
, "--files") == 0) {
1220 opt_files_only_in_files
= 0;
1224 if (strcmp(arg
, "--no-files") == 0) {
1225 opt_files_only_in_files
= 1;
1229 if (strcmp(arg
, "--help") == 0) {
1234 if (strcmp(arg
, "-h") == 0) { tfs_usage(); exit(0); }
1235 //fprintf(stderr, "FATAL: unknown option '%s'!\n", arg);
1240 if (key
== FUSE_OPT_KEY_NONOPT
) {
1241 // not an option; first non-option arg is tagdb file name
1242 if (!tagfile_loaded
) {
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");
1248 if (tagdb_load(tagfilename
) != 0) { fprintf(stderr
, "FATAL: can't load tagfile: '%s'!\n", tagfilename
); return -1; }
1250 printf("database load time: %.15g seconds\n", (double)(ste
-stt
)/(double)CLOCKS_PER_SEC
);
1251 printf("building hashes...\n");
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); }
1268 int main (int argc
, char *argv
[]) {
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");
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);
1296 fuse_opt_free_args(&args
);