Example #1
0
func (vfs FuseVfs) getFileEntryAttr(fileId entities.FileId) (*fuse.Attr, fuse.Status) {
	tx, err := vfs.store.Begin()
	if err != nil {
		log.Fatalf("could not begin transaction: %v", err)
	}
	defer tx.Commit()

	file, err := vfs.store.File(tx, 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
}
Example #2
0
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
	}

	tx, err := vfs.store.Begin()
	if err != nil {
		log.Fatalf("could not begin transaction: %v", err)
	}
	defer tx.Commit()

	switch path[0] {
	case tagsDir:
		name := path[1]

		if _, err := vfs.store.AddTag(tx, name); err != nil {
			log.Fatalf("could not create tag '%v': %v", name, err)
		}

		if err := tx.Commit(); err != nil {
			log.Fatalf("could not commit transaction: %v", err)
		}

		return fuse.OK
	case queriesDir:
		return fuse.EINVAL
	}

	return fuse.ENOSYS
}
Example #3
0
func (vfs FuseVfs) openTaggedEntryDir(tx *storage.Tx, path []string) ([]fuse.DirEntry, fuse.Status) {
	log.Infof(2, "BEGIN openTaggedEntryDir(%v)", path)
	defer log.Infof(2, "END openTaggedEntryDir(%v)", path)

	expression := pathToExpression(path)
	files, err := vfs.store.FilesForQuery(tx, expression, "", false, false, "name")
	if err != nil {
		log.Fatalf("could not query files: %v", err)
	}

	lastPathElement := path[len(path)-1]

	var valueNames []string
	if lastPathElement[0] != '=' {
		tagName := unescape(lastPathElement)

		valueNames, err = vfs.tagValueNamesForFiles(tx, tagName, files)
		if err != nil {
			log.Fatalf("could not retrieve values for '%v': %v", err)
		}
	} else {
		valueNames = []string{}
	}

	furtherTagNames, err := vfs.tagNamesForFiles(tx, files)
	if err != nil {
		log.Fatalf("could not retrieve further tags: %v", err)
	}

	entries := make([]fuse.DirEntry, 0, len(files)+len(furtherTagNames))
	for _, tagName := range furtherTagNames {
		tagName = escape(tagName)
		if !containsString(path, tagName) {
			entries = append(entries, fuse.DirEntry{Name: tagName, Mode: fuse.S_IFDIR | 0755})
		}
	}

	for _, valueName := range valueNames {
		valueName = escape(valueName)
		entries = append(entries, fuse.DirEntry{Name: "=" + valueName, 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
}
Example #4
0
func (vfs FuseVfs) tagDirectories(tx *storage.Tx) ([]fuse.DirEntry, fuse.Status) {
	log.Infof(2, "BEGIN tagDirectories")
	defer log.Infof(2, "END tagDirectories")

	tags, err := vfs.store.Tags(tx)
	if err != nil {
		log.Fatalf("Could not retrieve tags: %v", err)
	}

	entries := make([]fuse.DirEntry, 0, len(tags))
	for _, tag := range tags {
		if strings.ContainsAny(tag.Name, "/\\") {
			log.Infof(2, "Tag '%v' contains slashes so is omitted from the VFS")
			continue
		}

		entries = append(entries, fuse.DirEntry{Name: tag.Name, Mode: fuse.S_IFDIR})
	}

	// show help file until there are three tags
	if len(tags) < 3 {
		entries = append(entries, fuse.DirEntry{Name: helpFilename, Mode: fuse.S_IFREG})
	}

	return entries, fuse.OK
}
Example #5
0
func (vfs FuseVfs) OpenDir(name string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
	log.Infof(2, "BEGIN OpenDir(%v)", name)
	defer log.Infof(2, "END OpenDir(%v)", name)

	tx, err := vfs.store.Begin()
	if err != nil {
		log.Fatalf("could not begin transaction: %v", err)
	}
	defer tx.Commit()

	switch name {
	case "":
		return vfs.topFiles()
	case tagsDir:
		return vfs.tagDirectories(tx)
	case queriesDir:
		return vfs.queriesDirectories(tx)
	}

	path := vfs.splitPath(name)
	switch path[0] {
	case tagsDir:
		return vfs.openTaggedEntryDir(tx, path[1:])
	case queriesDir:
		return vfs.openQueryEntryDir(tx, path[1:])
	}

	return nil, fuse.ENOENT
}
Example #6
0
func (vfs FuseVfs) getTagsAttr() (*fuse.Attr, fuse.Status) {
	log.Infof(2, "BEGIN getTagsAttr")
	defer log.Infof(2, "END getTagsAttr")

	tx, err := vfs.store.Begin()
	if err != nil {
		log.Fatalf("could not begin transaction: %v", err)
	}
	defer tx.Commit()

	tagCount, err := vfs.store.TagCount(tx)
	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
}
Example #7
0
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)

	tx, err := vfs.store.Begin()
	if err != nil {
		log.Fatalf("could not begin transaction: %v", err)
	}
	defer tx.Commit()

	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(tx, 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(tx, tag.Id, newTagName); err != nil {
		log.Fatalf("could not rename tag '%v' to '%v': %v", oldTagName, newTagName, err)
	}

	if err := tx.Commit(); err != nil {
		log.Fatalf("could not commit transaction: %v", err)
	}

	return fuse.OK
}
Example #8
0
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)

	if len(path) == 1 && path[0] == helpFilename {
		now := time.Now()
		return &fuse.Attr{Mode: fuse.S_IFREG | 0444, Nlink: 1, Size: uint64(len(tagsDirHelp)), Mtime: uint64(now.Unix()), Mtimensec: uint32(now.Nanosecond())}, fuse.OK
	}

	name := path[len(path)-1]

	fileId := vfs.parseFileId(name)
	if fileId != 0 {
		return vfs.getFileEntryAttr(fileId)
	}

	tagNames := make([]string, 0, len(path))
	for _, pathElement := range path {
		if pathElement[0] != '=' {
			tagName := unescape(pathElement)
			tagNames = append(tagNames, tagName)
		}
	}

	tx, err := vfs.store.Begin()
	if err != nil {
		log.Fatalf("could not begin transaction: %v", err)
	}
	defer tx.Commit()

	tagIds, err := vfs.tagNamesToIds(tx, tagNames)
	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
}
Example #9
0
func (vfs FuseVfs) getDatabaseFileAttr() (*fuse.Attr, fuse.Status) {
	databasePath := vfs.store.DbPath

	fileInfo, err := os.Stat(databasePath)
	if err != nil {
		log.Fatalf("could not stat database: %v", err)
	}

	modTime := fileInfo.ModTime()

	return &fuse.Attr{Mode: fuse.S_IFLNK | 0755, Size: uint64(fileInfo.Size()), Mtime: uint64(modTime.Unix()), Mtimensec: uint32(modTime.Nanosecond())}, fuse.OK
}
Example #10
0
func (vfs FuseVfs) openQueryEntryDir(tx *storage.Tx, 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 {
		log.Fatalf("could not parse query: %v", err)
	}

	tagNames, err := query.TagNames(expression)
	if err != nil {
		log.Fatalf("could not identify tag names: %v", err)
	}

	tags, err := vfs.store.TagsByNames(tx, tagNames)
	for _, tagName := range tagNames {
		if !containsTag(tags, tagName) {
			return nil, fuse.ENOENT
		}
	}

	files, err := vfs.store.FilesForQuery(tx, expression, "", false, false, "name")
	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
}
Example #11
0
File: cli.go Project: logtcn/TMSU
func Run() {
	helpCommands = commands

	parser := NewOptionParser(globalOptions, commands)
	command, options, arguments, err := parser.Parse(os.Args[1:]...)
	if err != nil {
		log.Fatal(err)
	}

	switch {
	case options.HasOption("--version"):
		command = findCommand(commands, "version")
	case options.HasOption("--help"), command == nil:
		command = findCommand(commands, "help")
	}

	log.Verbosity = options.Count("--verbose") + 1

	var databasePath string
	switch {
	case options.HasOption("--database"):
		databasePath = options.Get("--database").Argument
	case os.Getenv("TMSU_DB") != "":
		databasePath = os.Getenv("TMSU_DB")
	default:
		databasePath, err = findDatabase()
		if err != nil {
			log.Fatalf("could not find database: %v", err)
		}
	}

	err, warnings := command.Exec(options, arguments, databasePath)

	if warnings != nil {
		for _, warning := range warnings {
			log.Warn(warning)
		}
	}

	if err != nil {
		log.Warn(err.Error())
	}

	if err != nil || (warnings != nil && len(warnings) > 0) {
		os.Exit(1)
	}
}
Example #12
0
func (vfs FuseVfs) readTaggedEntryLink(tx *storage.Tx, 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(tx, fileId)
	if err != nil {
		log.Fatalf("could not find file %v in database.", fileId)
	}

	return file.Path(), fuse.OK
}
Example #13
0
func (vfs FuseVfs) tagValueNamesForFiles(tx *storage.Tx, tagName string, files entities.Files) ([]string, error) {
	tag, err := vfs.store.TagByName(tx, tagName)
	if err != nil {
		log.Fatalf("could not look up tag '%v': %v", tagName, err)
	}
	if tag == nil {
		return []string{}, nil
	}

	valueIds := make(entities.ValueIds, 0, 10)

	predicate := func(fileTag entities.FileTag) bool {
		return fileTag.TagId == tag.Id
	}

	for _, file := range files {
		fileTags, err := vfs.store.FileTagsByFileId(tx, file.Id, false)
		if err != nil {
			return nil, fmt.Errorf("could not retrieve file-tags for file '%v': %v", file.Id, err)
		}

		for _, valueId := range fileTags.Where(predicate).ValueIds() {
			valueIds = append(valueIds, valueId)
		}
	}

	values, err := vfs.store.ValuesByIds(tx, valueIds.Uniq())
	if err != nil {
		return nil, fmt.Errorf("could not retrieve values: %v", err)
	}

	valueNames := make([]string, 0, len(values))
	for _, value := range values {
		if strings.ContainsAny(value.Name, "/\\") {
			log.Infof(2, "value '%v' omitted as it contains slashes")
			continue
		}

		valueNames = append(valueNames, value.Name)
	}

	return valueNames, nil
}
Example #14
0
func (vfs FuseVfs) queriesDirectories(tx *storage.Tx) ([]fuse.DirEntry, fuse.Status) {
	log.Infof(2, "BEGIN queriesDirectories")
	defer log.Infof(2, "END queriesDirectories")

	queries, err := vfs.store.Queries(tx)
	if err != nil {
		log.Fatalf("could not retrieve queries: %v", err)
	}

	entries := make([]fuse.DirEntry, len(queries))
	for index, query := range queries {
		entries[index] = fuse.DirEntry{Name: query.Text, Mode: fuse.S_IFDIR}
	}

	if len(queries) < 1 {
		entries = append(entries, fuse.DirEntry{Name: helpFilename, Mode: fuse.S_IFREG})
	}

	return entries, fuse.OK
}
Example #15
0
func (vfs FuseVfs) Readlink(name string, context *fuse.Context) (string, fuse.Status) {
	log.Infof(2, "BEGIN Readlink(%v)", name)
	defer log.Infof(2, "END Readlink(%v)", name)

	tx, err := vfs.store.Begin()
	if err != nil {
		log.Fatalf("could not begin transaction: %v", err)
	}
	defer tx.Commit()

	if name == ".database" {
		return vfs.readDatabaseFileLink()
	}

	path := vfs.splitPath(name)
	switch path[0] {
	case tagsDir, queriesDir:
		return vfs.readTaggedEntryLink(tx, path[1:])
	}

	return "", fuse.ENOENT
}
Example #16
0
func (vfs FuseVfs) tagDirectories(tx *storage.Tx) ([]fuse.DirEntry, fuse.Status) {
	log.Infof(2, "BEGIN tagDirectories")
	defer log.Infof(2, "END tagDirectories")

	tags, err := vfs.store.Tags(tx)
	if err != nil {
		log.Fatalf("Could not retrieve tags: %v", err)
	}

	entries := make([]fuse.DirEntry, 0, len(tags))
	for _, tag := range tags {
		tagName := escape(tag.Name)

		entries = append(entries, fuse.DirEntry{Name: tagName, Mode: fuse.S_IFDIR})
	}

	// show help file until there are three tags
	if len(tags) < 3 {
		entries = append(entries, fuse.DirEntry{Name: helpFilename, Mode: fuse.S_IFREG})
	}

	return entries, fuse.OK
}
Example #17
0
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)

	tx, err := vfs.store.Begin()
	if err != nil {
		log.Fatalf("could not begin transaction: %v", err)
	}
	defer tx.Commit()

	fileId := vfs.parseFileId(name)
	if fileId == 0 {
		// can only unlink file symbolic links
		return fuse.EPERM
	}

	file, err := vfs.store.File(tx, 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:
		dirName := path[len(path)-2]

		var tagName, valueName string
		if dirName[0] == '=' {
			tagName = path[len(path)-3]
			valueName = dirName[1:len(dirName)]
		} else {
			tagName = dirName
			valueName = ""
		}

		tag, err := vfs.store.TagByName(tx, tagName)
		if err != nil {
			log.Fatal(err)
		}
		if tag == nil {
			log.Fatalf("could not retrieve tag '%v'.", tagName)
		}

		value, err := vfs.store.ValueByName(tx, valueName)
		if err != nil {
			log.Fatal(err)
		}
		if value == nil {
			log.Fatalf("could not retrieve value '%v'.", valueName)
		}

		if err = vfs.store.DeleteFileTag(tx, fileId, tag.Id, value.Id); err != nil {
			log.Fatal(err)
		}

		if err := tx.Commit(); err != nil {
			log.Fatalf("could not commit transaction: %v", err)
		}

		return fuse.OK
	case queriesDir:
		return fuse.EPERM
	}

	return fuse.ENOSYS
}
Example #18
0
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)

	tx, err := vfs.store.Begin()
	if err != nil {
		log.Fatalf("could not begin transaction: %v", err)
	}
	defer tx.Commit()

	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(tx, 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(tx, tag.Id, false)
		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(tx, tag.Id); err != nil {
			log.Fatalf("could not delete tag '%v': %v", tagName, err)
		}

		if err := tx.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(tx, text); err != nil {
			log.Fatalf("could not remove tag '%v': %v", name, err)
		}

		if err := tx.Commit(); err != nil {
			log.Fatalf("could not commit transaction: %v", err)
		}

		return fuse.OK
	}

	return fuse.ENOSYS
}
Example #19
0
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)

	if len(path) == 1 && path[0] == helpFilename {
		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
	}

	name := path[len(path)-1]

	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
	}

	tx, err := vfs.store.Begin()
	if err != nil {
		log.Fatalf("could not begin transaction: %v", err)
	}
	defer tx.Commit()

	tagNames, err := query.TagNames(expression)
	if err != nil {
		log.Fatalf("could not identify tag names: %v", err)
	}

	tags, err := vfs.store.TagsByNames(tx, tagNames)
	for _, tagName := range tagNames {
		if !containsTag(tags, tagName) {
			return nil, fuse.ENOENT
		}
	}

	q, err := vfs.store.Query(tx, queryText)
	if err != nil {
		log.Fatalf("could not retrieve query '%v': %v", queryText, err)
	}
	if q == nil {
		_, err = vfs.store.AddQuery(tx, queryText)
		if err != nil {
			log.Fatalf("could not add query '%v': %v", queryText, 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
}