func (vfs FuseVfs) Mkdir(name string, mode uint32, context *fuse.Context) fuse.Status { log.Infof(2, "BEGIN Mkdir(%v)", name) defer log.Infof(2, "END Mkdir(%v)", name) path := vfs.splitPath(name) if len(path) != 2 { return fuse.EPERM } switch path[0] { case tagsDir: name := path[1] if _, err := vfs.store.AddTag(name); err != nil { log.Fatalf("could not create tag '%v': %v", name, err) } if err := vfs.store.Commit(); err != nil { log.Fatalf("could not commit transaction: %v", err) } return fuse.OK case queriesDir: return fuse.EINVAL } return fuse.ENOSYS }
func (vfs FuseVfs) openTaggedEntryDir(path []string) ([]fuse.DirEntry, fuse.Status) { log.Infof(2, "BEGIN openTaggedEntryDir(%v)", path) defer log.Infof(2, "END openTaggedEntryDir(%v)", path) expression := query.HasAll(path) files, err := vfs.store.QueryFiles(expression) if err != nil { log.Fatalf("could not query files: %v", err) } tagNames := make(map[string]interface{}, len(path)) for _, tagName := range path { tagNames[tagName] = nil } furtherTagNames := make([]string, 0, 10) for _, file := range files { fileTags, err := vfs.store.FileTagsByFileId(file.Id) if err != nil { log.Fatalf("could not retrieve file-tags for file '%v': %v", file.Id, err) } tagIds := make([]uint, len(fileTags)) for index, fileTag := range fileTags { tagIds[index] = fileTag.TagId } tags, err := vfs.store.TagsByIds(tagIds) if err != nil { log.Fatalf("could not retrieve tags: %v", err) } for _, tag := range tags { _, has := tagNames[tag.Name] if !has { if !containsName(furtherTagNames, tag.Name) { furtherTagNames = append(furtherTagNames, tag.Name) } } } } entries := make([]fuse.DirEntry, 0, len(files)+len(furtherTagNames)) for _, tagName := range furtherTagNames { entries = append(entries, fuse.DirEntry{Name: tagName, Mode: fuse.S_IFDIR | 0755}) } for _, file := range files { linkName := vfs.getLinkName(file) entries = append(entries, fuse.DirEntry{Name: linkName, Mode: fuse.S_IFLNK}) } return entries, fuse.OK }
func (vfs FuseVfs) Unlink(name string, context *fuse.Context) fuse.Status { log.Infof(2, "BEGIN Unlink(%v)", name) defer log.Infof(2, "END Unlink(%v)", name) fileId := vfs.parseFileId(name) if fileId == 0 { // can only unlink file symbolic links return fuse.EPERM } file, err := vfs.store.File(fileId) if err != nil { log.Fatal("could not retrieve file '%v': %v", fileId, err) } if file == nil { // reply ok if file doesn't exist otherwise recursive deletes fail return fuse.OK } path := vfs.splitPath(name) switch path[0] { case tagsDir: tagName := path[len(path)-2] //TODO value name tag, err := vfs.store.TagByName(tagName) if err != nil { log.Fatal(err) } if tag == nil { log.Fatalf("could not retrieve tag '%v'.", tagName) } if err = vfs.store.DeleteFileTag(fileId, tag.Id, 0); err != nil { log.Fatal(err) } if err := vfs.store.Commit(); err != nil { log.Fatalf("could not commit transaction: %v", err) } return fuse.OK case queriesDir: return fuse.EPERM } return fuse.ENOSYS }
func (vfs FuseVfs) openQueryEntryDir(path []string) ([]fuse.DirEntry, fuse.Status) { log.Infof(2, "BEGIN openQueryEntryDir(%v)", path) defer log.Infof(2, "END openQueryEntryDir(%v)", path) queryText := path[0] expression, err := query.Parse(queryText) if err != nil { return nil, fuse.ENOENT } tagNames := query.TagNames(expression) tags, err := vfs.store.TagsByNames(tagNames) for _, tagName := range tagNames { if !containsTag(tags, tagName) { return nil, fuse.ENOENT } } files, err := vfs.store.QueryFiles(expression) if err != nil { log.Fatalf("could not query files: %v", err) } entries := make([]fuse.DirEntry, 0, len(files)) for _, file := range files { linkName := vfs.getLinkName(file) entries = append(entries, fuse.DirEntry{Name: linkName, Mode: fuse.S_IFLNK}) } return entries, fuse.OK }
func (vfs FuseVfs) getTagsAttr() (*fuse.Attr, fuse.Status) { log.Infof(2, "BEGIN getTagsAttr") defer log.Infof(2, "END getTagsAttr") tagCount, err := vfs.store.TagCount() if err != nil { log.Fatalf("could not get tag count: %v", err) } now := time.Now() return &fuse.Attr{Mode: fuse.S_IFDIR | 0755, Nlink: 2, Size: uint64(tagCount), Mtime: uint64(now.Unix()), Mtimensec: uint32(now.Nanosecond())}, fuse.OK }
func (vfs FuseVfs) Rmdir(name string, context *fuse.Context) fuse.Status { log.Infof(2, "BEGIN Rmdir(%v)", name) defer log.Infof(2, "END Rmdir(%v)", name) path := vfs.splitPath(name) switch path[0] { case tagsDir: if len(path) != 2 { // can only remove top-level tag directories return fuse.EPERM } tagName := path[1] tag, err := vfs.store.TagByName(tagName) if err != nil { log.Fatalf("could not retrieve tag '%v': %v", tagName, err) } if tag == nil { return fuse.ENOENT } count, err := vfs.store.FileTagCountByTagId(tag.Id) if err != nil { log.Fatalf("could not retrieve file-tag count for tag '%v': %v", tagName, err) } if count > 0 { return fuse.Status(syscall.ENOTEMPTY) } if err := vfs.store.DeleteTag(tag.Id); err != nil { log.Fatalf("could not delete tag '%v': %v", tagName, err) } if err := vfs.store.Commit(); err != nil { log.Fatalf("could not commit transaction: %v", err) } return fuse.OK case queriesDir: if len(path) != 2 { // can only remove top-level queries directories return fuse.EPERM } text := path[1] if err := vfs.store.DeleteQuery(text); err != nil { log.Fatalf("could not remove tag '%v': %v", name, err) } if err := vfs.store.Commit(); err != nil { log.Fatalf("could not commit transaction: %v", err) } return fuse.OK } return fuse.ENOSYS }
func init() { if path := os.Getenv("TMSU_DB"); path != "" { log.Info(3, "TMSU_DB=", path) Path = path } else { u, err := user.Current() if err != nil { log.Fatalf("could not identify current user: %v", err) } Path = filepath.Join(u.HomeDir, ".tmsu", "default.db") } }
func (vfs FuseVfs) getQueryEntryAttr(path []string) (*fuse.Attr, fuse.Status) { log.Infof(2, "BEGIN getQueryEntryAttr(%v)", path) defer log.Infof(2, "END getQueryEntryAttr(%v)", path) pathLength := len(path) name := path[pathLength-1] if len(path) == 1 && path[0] == queryHelpFilename { now := time.Now() return &fuse.Attr{Mode: fuse.S_IFREG | 0444, Nlink: 1, Size: uint64(len(queryDirHelp)), Mtime: uint64(now.Unix()), Mtimensec: uint32(now.Nanosecond())}, fuse.OK } if len(path) > 1 { fileId := vfs.parseFileId(name) if fileId != 0 { return vfs.getFileEntryAttr(fileId) } return nil, fuse.ENOENT } queryText := path[0] if queryText[len(queryText)-1] == ' ' { // prevent multiple entries for same query when typing path in a GUI return nil, fuse.ENOENT } expression, err := query.Parse(queryText) if err != nil { return nil, fuse.ENOENT } tagNames := query.TagNames(expression) tags, err := vfs.store.TagsByNames(tagNames) for _, tagName := range tagNames { if !containsTag(tags, tagName) { return nil, fuse.ENOENT } } _, _ = vfs.store.AddQuery(queryText) if err := vfs.store.Commit(); err != nil { log.Fatalf("could not commit transaction: %v", err) } now := time.Now() return &fuse.Attr{Mode: fuse.S_IFDIR | 0755, Nlink: 2, Size: uint64(0), Mtime: uint64(now.Unix()), Mtimensec: uint32(now.Nanosecond())}, fuse.OK }
func (vfs FuseVfs) Rename(oldName string, newName string, context *fuse.Context) fuse.Status { log.Infof(2, "BEGIN Rename(%v, %v)", oldName, newName) defer log.Infof(2, "END Rename(%v, %v)", oldName, newName) oldPath := vfs.splitPath(oldName) newPath := vfs.splitPath(newName) if len(oldPath) != 2 || len(newPath) != 2 { return fuse.EPERM } if oldPath[0] != tagsDir || newPath[0] != tagsDir { return fuse.EPERM } oldTagName := oldPath[1] newTagName := newPath[1] tag, err := vfs.store.TagByName(oldTagName) if err != nil { log.Fatalf("could not retrieve tag '%v': %v", oldTagName, err) } if tag == nil { return fuse.ENOENT } if _, err := vfs.store.RenameTag(tag.Id, newTagName); err != nil { log.Fatalf("could not rename tag '%v' to '%v': %v", oldTagName, newTagName, err) } if err := vfs.store.Commit(); err != nil { log.Fatalf("could not commit transaction: %v", err) } return fuse.OK }
func (vfs FuseVfs) tagDirectories() ([]fuse.DirEntry, fuse.Status) { log.Infof(2, "BEGIN tagDirectories") defer log.Infof(2, "END tagDirectories") tags, err := vfs.store.Tags() if err != nil { log.Fatalf("Could not retrieve tags: %v", err) } entries := make([]fuse.DirEntry, len(tags)) for index, tag := range tags { entries[index] = fuse.DirEntry{Name: tag.Name, Mode: fuse.S_IFDIR} } return entries, fuse.OK }
func (vfs FuseVfs) readTaggedEntryLink(path []string) (string, fuse.Status) { log.Infof(2, "BEGIN readTaggedEntryLink(%v)", path) defer log.Infof(2, "END readTaggedEntryLink(%v)", path) name := path[len(path)-1] fileId := vfs.parseFileId(name) if fileId == 0 { return "", fuse.ENOENT } file, err := vfs.store.File(fileId) if err != nil { log.Fatalf("could not find file %v in database.", fileId) } return file.Path(), fuse.OK }
func (vfs FuseVfs) queriesDirectories() ([]fuse.DirEntry, fuse.Status) { log.Infof(2, "BEGIN queriesDirectories") defer log.Infof(2, "END queriesDirectories") queries, err := vfs.store.Queries() if err != nil { log.Fatalf("could not retrieve queries: %v", err) } if len(queries) == 0 { return []fuse.DirEntry{fuse.DirEntry{Name: queryHelpFilename, Mode: fuse.S_IFREG}}, fuse.OK } entries := make([]fuse.DirEntry, len(queries)) for index, query := range queries { entries[index] = fuse.DirEntry{Name: query.Text, Mode: fuse.S_IFDIR} } return entries, fuse.OK }
func (vfs FuseVfs) getFileEntryAttr(fileId uint) (*fuse.Attr, fuse.Status) { file, err := vfs.store.File(fileId) if err != nil { log.Fatalf("could not retrieve file #%v: %v", fileId, err) } if file == nil { return &fuse.Attr{Mode: fuse.S_IFREG}, fuse.ENOENT } fileInfo, err := os.Stat(file.Path()) var size int64 var modTime time.Time if err == nil { size = fileInfo.Size() modTime = fileInfo.ModTime() } else { size = 0 modTime = time.Time{} } return &fuse.Attr{Mode: fuse.S_IFLNK | 0755, Size: uint64(size), Mtime: uint64(modTime.Unix()), Mtimensec: uint32(modTime.Nanosecond())}, fuse.OK }
func Run() { helpCommands = commands parser := NewOptionParser(globalOptions, commands) commandName, options, arguments, err := parser.Parse(os.Args[1:]) if err != nil { log.Fatal(err) } switch { case options.HasOption("--version"): commandName = "version" case options.HasOption("--help"), commandName == "": commandName = "help" } log.Verbosity = options.Count("--verbose") + 1 if dbOption := options.Get("--database"); dbOption != nil && dbOption.Argument != "" { database.Path = dbOption.Argument } command := commands[commandName] if command == nil { log.Fatalf("Invalid command '%v'.", commandName) } err = command.Exec(options, arguments) if err != nil { if err != blankError { log.Warn(err.Error()) } os.Exit(1) } }
func (vfs FuseVfs) getTaggedEntryAttr(path []string) (*fuse.Attr, fuse.Status) { log.Infof(2, "BEGIN getTaggedEntryAttr(%v)", path) defer log.Infof(2, "END getTaggedEntryAttr(%v)", path) pathLength := len(path) name := path[pathLength-1] fileId := vfs.parseFileId(name) if fileId != 0 { return vfs.getFileEntryAttr(fileId) } // tag directory tagIds, err := vfs.tagNamesToIds(path) if err != nil { log.Fatalf("could not lookup tag IDs: %v.", err) } if tagIds == nil { return nil, fuse.ENOENT } now := time.Now() return &fuse.Attr{Mode: fuse.S_IFDIR | 0755, Nlink: 2, Size: uint64(0), Mtime: uint64(now.Unix()), Mtimensec: uint32(now.Nanosecond())}, fuse.OK }