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 }
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 }
func (vfs FuseVfs) getQueryAttr() (*fuse.Attr, fuse.Status) { log.Infof(2, "BEGIN getQueryAttr") defer log.Infof(2, "END getQueryAttr") now := time.Now() return &fuse.Attr{Mode: fuse.S_IFDIR | 0755, Nlink: 2, Size: 0, Mtime: uint64(now.Unix()), Mtimensec: uint32(now.Nanosecond())}, fuse.OK }
func statusCheckFile(absPath string, file *entities.File, report *StatusReport) error { log.Infof(2, "%v: checking file status.", absPath) stat, err := os.Stat(file.Path()) if err != nil { switch { case os.IsNotExist(err): log.Infof(2, "%v: file is missing.", absPath) report.AddRow(Row{absPath, MISSING}) return nil case os.IsPermission(err): log.Warnf("%v: permission denied.", absPath) case strings.Contains(err.Error(), "not a directory"): //TODO improve report.AddRow(Row{file.Path(), MISSING}) return nil default: return fmt.Errorf("%v: could not stat: %v", file.Path(), err) } } else { if stat.Size() != file.Size || !stat.ModTime().UTC().Equal(file.ModTime) { log.Infof(2, "%v: file is modified.", absPath) report.AddRow(Row{absPath, MODIFIED}) } else { log.Infof(2, "%v: file is unchanged.", absPath) report.AddRow(Row{absPath, TAGGED}) } } return nil }
func determineStatuses(dbFiles entities.Files) (unmodified, modified, missing entities.Files) { log.Infof(2, "determining file statuses") unmodified = make(entities.Files, 0, 10) modified = make(entities.Files, 0, 10) missing = make(entities.Files, 0, 10) for _, dbFile := range dbFiles { stat, err := os.Stat(dbFile.Path()) if err != nil { switch { case os.IsPermission(err): //TODO return as warning log.Warnf("%v: permission denied", dbFile.Path()) continue case os.IsNotExist(err): //TODO return as warning log.Infof(2, "%v: missing", dbFile.Path()) missing = append(missing, dbFile) continue } } if dbFile.ModTime.Equal(stat.ModTime().UTC()) && dbFile.Size == stat.Size() { log.Infof(2, "%v: unmodified", dbFile.Path()) unmodified = append(unmodified, dbFile) } else { log.Infof(2, "%v: modified", dbFile.Path()) modified = append(modified, dbFile) } } return }
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 }
func (vfs FuseVfs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) { log.Infof(2, "BEGIN GetAttr(%v)", name) defer log.Infof(2, "END GetAttr(%v)", name) switch name { case databaseFilename: return vfs.getDatabaseFileAttr() case "": fallthrough case tagsDir: return vfs.getTagsAttr() case queriesDir: return vfs.getQueryAttr() } path := vfs.splitPath(name) switch path[0] { case tagsDir: return vfs.getTaggedEntryAttr(path[1:]) case queriesDir: return vfs.getQueryEntryAttr(path[1:]) } return nil, fuse.ENOENT }
func repairMoved(store *storage.Storage, tx *storage.Tx, missing entities.Files, searchPaths []string, pretend bool, settings entities.Settings) error { log.Infof(2, "repairing moved files") if len(missing) == 0 || len(searchPaths) == 0 { // don't bother enumerating filesystem if nothing to do return nil } pathsBySize, err := buildPathBySizeMap(searchPaths) if err != nil { return err } for index, dbFile := range missing { log.Infof(2, "%v: searching for new location", dbFile.Path()) pathsOfSize := pathsBySize[dbFile.Size] log.Infof(2, "%v: file is of size %v, identified %v files of this size", dbFile.Path(), dbFile.Size, len(pathsOfSize)) for _, candidatePath := range pathsOfSize { candidateFile, err := store.FileByPath(tx, candidatePath) if err != nil { return err } if candidateFile != nil { // file is already tagged continue } stat, err := os.Stat(candidatePath) if err != nil { return fmt.Errorf("%v: could not stat file: %v", candidatePath, err) } fingerprint, err := fingerprint.Create(candidatePath, settings.FileFingerprintAlgorithm(), settings.DirectoryFingerprintAlgorithm(), settings.SymlinkFingerprintAlgorithm()) if err != nil { return fmt.Errorf("%v: could not create fingerprint: %v", candidatePath, err) } if fingerprint == dbFile.Fingerprint { if !pretend { _, err := store.UpdateFile(tx, dbFile.Id, candidatePath, dbFile.Fingerprint, stat.ModTime(), dbFile.Size, dbFile.IsDir) if err != nil { return fmt.Errorf("%v: could not update file in database: %v", dbFile.Path(), err) } } fmt.Printf("%v: updated path to %v\n", dbFile.Path(), candidatePath) missing[index] = nil break } } } return nil }
func untagPathsAll(store *storage.Storage, tx *storage.Tx, paths []string, recursive, followSymlinks bool) (error, warnings) { warnings := make(warnings, 0, 10) for _, path := range paths { absPath, err := filepath.Abs(path) if err != nil { return fmt.Errorf("%v: could not get absolute path: %v", path, err), warnings } log.Infof(2, "%v: resolving path", path) stat, err := os.Lstat(absPath) if err != nil { switch { case os.IsNotExist(err), os.IsPermission(err): // ignore default: return err, nil } } else if stat.Mode()&os.ModeSymlink != 0 && followSymlinks { absPath, err = _path.Dereference(absPath) if err != nil { return err, nil } } file, err := store.FileByPath(tx, absPath) if err != nil { return fmt.Errorf("%v: could not retrieve file: %v", path, err), warnings } if file == nil { warnings = append(warnings, fmt.Sprintf("%v: file is not tagged.", path)) continue } log.Infof(2, "%v: removing all tags.", path) if err := store.DeleteFileTagsByFileId(tx, file.Id); err != nil { return fmt.Errorf("%v: could not remove file's tags: %v", path, err), warnings } if recursive { childFiles, err := store.FilesByDirectory(tx, file.Path()) if err != nil { return fmt.Errorf("%v: could not retrieve files for directory: %v", path, err), warnings } for _, childFile := range childFiles { if err := store.DeleteFileTagsByFileId(tx, childFile.Id); err != nil { return fmt.Errorf("%v: could not remove file's tags: %v", childFile.Path(), err), warnings } } } } return nil, warnings }
func (vfs FuseVfs) topFiles() ([]fuse.DirEntry, fuse.Status) { log.Infof(2, "BEGIN topFiles") defer log.Infof(2, "END topFiles") entries := []fuse.DirEntry{ fuse.DirEntry{Name: databaseFilename, Mode: fuse.S_IFLNK}, fuse.DirEntry{Name: tagsDir, Mode: fuse.S_IFDIR}, fuse.DirEntry{Name: queriesDir, Mode: fuse.S_IFDIR}} return entries, fuse.OK }
func upgrade(tx *sql.Tx) error { version := currentSchemaVersion(tx) log.Infof(2, "database schema has version %v, latest schema version is %v", version, latestSchemaVersion) if version == latestSchemaVersion { log.Infof(2, "schema is up to date") return nil } noVersion := schemaVersion{} if version == noVersion { log.Infof(2, "creating schema") if err := createSchema(tx); err != nil { return err } // still need to run upgrade as per 0.5.0 database did not store a version } log.Infof(2, "upgrading database") if version.LessThan(schemaVersion{common.Version{0, 5, 0}, 0}) { log.Infof(2, "renaming fingerprint algorithm setting") if err := renameFingerprintAlgorithmSetting(tx); err != nil { return err } } if version.LessThan(schemaVersion{common.Version{0, 6, 0}, 0}) { log.Infof(2, "recreating implication table") if err := recreateImplicationTable(tx); err != nil { return err } } if version.LessThan(schemaVersion{common.Version{0, 7, 0}, 0}) { log.Infof(2, "updating fingerprint algorithms") if err := updateFingerprintAlgorithms(tx); err != nil { return err } } if version.LessThan(schemaVersion{common.Version{0, 7, 0}, 1}) { log.Infof(2, "recreating version table") if err := recreateVersionTable(tx); err != nil { return err } } log.Infof(2, "updating schema version") if err := updateSchemaVersion(tx, latestSchemaVersion); err != nil { return err } return nil }
func deleteImplications(store *storage.Storage, tx *storage.Tx, tagArgs []string) (error, warnings) { log.Infof(2, "loading settings") implyingTagArg := tagArgs[0] impliedTagArgs := tagArgs[1:] implyingTagName, implyingValueName := parseTagEqValueName(implyingTagArg) implyingTag, err := store.TagByName(tx, implyingTagName) if err != nil { return err, nil } if implyingTag == nil { return NoSuchTagError{implyingTagName}, nil } implyingValue, err := store.ValueByName(tx, implyingValueName) if err != nil { return err, nil } if implyingValue == nil { return NoSuchValueError{implyingValueName}, nil } warnings := make(warnings, 0, 10) for _, impliedTagArg := range impliedTagArgs { log.Infof(2, "removing tag implication %v -> %v.", implyingTagArg, impliedTagArg) impliedTagName, impliedValueName := parseTagEqValueName(impliedTagArg) impliedTag, err := store.TagByName(tx, impliedTagName) if err != nil { return err, warnings } if impliedTag == nil { warnings = append(warnings, fmt.Sprintf("no such tag '%v'", impliedTagName)) } impliedValue, err := store.ValueByName(tx, impliedValueName) if err != nil { return err, warnings } if impliedValue == nil { warnings = append(warnings, fmt.Sprintf("no such value '%v'", impliedValueName)) } if err := store.DeleteImplication(tx, entities.TagIdValueIdPair{implyingTag.Id, implyingValue.Id}, entities.TagIdValueIdPair{impliedTag.Id, impliedValue.Id}); err != nil { return fmt.Errorf("could not delete tag implication of %v to %v: %v", implyingTagArg, impliedTagArg, err), warnings } } return nil, warnings }
func (vfs FuseVfs) Open(name string, flags uint32, context *fuse.Context) (nodefs.File, fuse.Status) { log.Infof(2, "BEGIN Open(%v)", name) defer log.Infof(2, "END Open(%v)", name) switch name { case filepath.Join(queriesDir, helpFilename): return nodefs.NewDataFile([]byte(queryDirHelp)), fuse.OK case filepath.Join(tagsDir, helpFilename): return nodefs.NewDataFile([]byte(tagsDirHelp)), fuse.OK } return nil, fuse.ENOSYS }
func mergeValues(store *storage.Storage, tx *storage.Tx, sourceValueNames []string, destValueName string) (error, warnings) { destValue, err := store.ValueByName(tx, destValueName) if err != nil { return fmt.Errorf("could not retrieve value '%v': %v", destValueName, err), nil } if destValue == nil { return fmt.Errorf("no such value '%v'", destValueName), nil } warnings := make(warnings, 0, 10) for _, sourceValueName := range sourceValueNames { if sourceValueName == destValueName { warnings = append(warnings, fmt.Sprintf("cannot merge value '%v' into itself", sourceValueName)) continue } sourceValue, err := store.ValueByName(tx, sourceValueName) if err != nil { return fmt.Errorf("could not retrieve value '%v': %v", sourceValueName, err), warnings } if sourceValue == nil { warnings = append(warnings, fmt.Sprintf("no such value '%v'", sourceValueName)) continue } log.Infof(2, "finding files tagged with value '%v'.", sourceValueName) fileTags, err := store.FileTagsByValueId(tx, sourceValue.Id) if err != nil { return fmt.Errorf("could not retrieve files for value '%v': %v", sourceValueName, err), warnings } log.Infof(2, "applying value '%v' to these files.", destValueName) for _, fileTag := range fileTags { if _, err = store.AddFileTag(tx, fileTag.FileId, fileTag.TagId, destValue.Id); err != nil { return fmt.Errorf("could not apply value '%v' to file #%v: %v", destValueName, fileTag.FileId, err), warnings } } log.Infof(2, "deleting value '%v'.", sourceValueName) if err = store.DeleteValue(tx, sourceValue.Id); err != nil { return fmt.Errorf("could not delete value '%v': %v", sourceValueName, err), warnings } } return nil, warnings }
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 }
func buildPathBySizeMapRecursive(path string, pathBySizeMap map[int64][]string) error { absPath, err := filepath.Abs(path) if err != nil { return fmt.Errorf("%v: could not get absolute path", path) } stat, err := os.Stat(absPath) if err != nil { switch { case os.IsPermission(err): log.Warnf("%v: permission denied", path) default: return err } } if stat.IsDir() { log.Infof(3, "%v: examining directory contents", absPath) dir, err := os.Open(absPath) if err != nil { return fmt.Errorf("%v: could not open directory: %v", path, err) } names, err := dir.Readdirnames(0) dir.Close() if err != nil { return fmt.Errorf("%v: could not read directory entries: %v", path, err) } for _, name := range names { childPath := filepath.Join(path, name) if err := buildPathBySizeMapRecursive(childPath, pathBySizeMap); err != nil { return err } } } else { log.Infof(3, "%v: file is of size %v", absPath, stat.Size()) filesOfSize, ok := pathBySizeMap[stat.Size()] if ok { pathBySizeMap[stat.Size()] = append(filesOfSize, absPath) } else { pathBySizeMap[stat.Size()] = []string{absPath} } } return nil }
func manualRepair(store *storage.Storage, tx *storage.Tx, fromPath, toPath string, pretend bool) error { absFromPath, err := filepath.Abs(fromPath) if err != nil { return fmt.Errorf("%v: could not determine absolute path", err) } absToPath, err := filepath.Abs(toPath) if err != nil { return fmt.Errorf("%v: could not determine absolute path", err) } log.Infof(2, "retrieving files under '%v' from the database", fromPath) dbFile, err := store.FileByPath(tx, absFromPath) if err != nil { return fmt.Errorf("%v: could not retrieve file: %v", fromPath, err) } if dbFile != nil { log.Infof(2, "%v: updating to %v", fromPath, toPath) if !pretend { if err := manualRepairFile(store, tx, dbFile, absToPath); err != nil { return err } } } dbFiles, err := store.FilesByDirectory(tx, absFromPath) if err != nil { return fmt.Errorf("could not retrieve files from storage: %v", err) } for _, dbFile = range dbFiles { relFileFromPath := _path.Rel(dbFile.Path()) absFileToPath := strings.Replace(dbFile.Path(), absFromPath, absToPath, 1) relFileToPath := _path.Rel(absFileToPath) log.Infof(2, "%v: updating to %v", relFileFromPath, relFileToPath) if !pretend { if err := manualRepairFile(store, tx, dbFile, absFileToPath); err != nil { return err } } } return nil }
func buildPathBySizeMap(paths []string) (map[int64][]string, error) { log.Infof(2, "building map of paths by size") pathsBySize := make(map[int64][]string, 10) for _, path := range paths { if err := buildPathBySizeMapRecursive(path, pathsBySize); err != nil { return nil, err } } log.Infof(2, "path by size map has %v sizes", len(pathsBySize)) return pathsBySize, nil }
func listValuesForTag(store *storage.Storage, tx *storage.Tx, tagName string, showCount, onePerLine bool) error { tag, err := store.TagByName(tx, tagName) if err != nil { return fmt.Errorf("could not retrieve tag '%v': %v", tagName, err) } if tag == nil { return fmt.Errorf("no such tag, '%v'", tagName) } log.Infof(2, "retrieving values for tag '%v'.", tagName) values, err := store.ValuesByTag(tx, tag.Id) if err != nil { return fmt.Errorf("could not retrieve values for tag '%v': %v", tagName, err) } if showCount { fmt.Println(len(values)) } else { if onePerLine { for _, value := range values { fmt.Println(escape(value.Name, '=', ' ')) } } else { valueNames := make([]string, len(values)) for index, value := range values { valueNames[index] = escape(value.Name, '=', ' ') } terminal.PrintColumns(valueNames) } } return nil }
func findDuplicatesInDb(store *storage.Storage, tx *storage.Tx) error { log.Info(2, "identifying duplicate files.") fileSets, err := store.DuplicateFiles(tx) if err != nil { return fmt.Errorf("could not identify duplicate files: %v", err) } log.Infof(2, "found %v sets of duplicate files.", len(fileSets)) for index, fileSet := range fileSets { if index > 0 { fmt.Println() } fmt.Printf("Set of %v duplicates:\n", len(fileSet)) for _, file := range fileSet { relPath := _path.Rel(file.Path()) fmt.Printf(" %v\n", relPath) } } return nil }
func OpenAt(path string) (*Database, error) { log.Infof(2, "opening database at '%v'.", path) _, err := os.Stat(path) if err != nil { switch { case os.IsNotExist(err): return nil, DatabaseNotFoundError{path} default: return nil, DatabaseAccessError{path, err} } } db, err := sql.Open("sqlite3", path) if err != nil { return nil, DatabaseAccessError{path, err} } tx, err := db.Begin() if err != nil { return nil, DatabaseTransactionError{path, err} } if err := upgrade(tx); err != nil { return nil, err } if err := tx.Commit(); err != nil { return nil, DatabaseTransactionError{path, err} } return &Database{db}, nil }
func repairModified(store *storage.Storage, tx *storage.Tx, modified entities.Files, pretend bool, settings entities.Settings) error { log.Infof(2, "repairing modified files") for _, dbFile := range modified { stat, err := os.Stat(dbFile.Path()) if err != nil { return err } fingerprint, err := fingerprint.Create(dbFile.Path(), settings.FileFingerprintAlgorithm(), settings.DirectoryFingerprintAlgorithm(), settings.SymlinkFingerprintAlgorithm()) if err != nil { log.Warnf("%v: could not create fingerprint: %v", dbFile.Path(), err) continue } if !pretend { _, err := store.UpdateFile(tx, dbFile.Id, dbFile.Path(), fingerprint, stat.ModTime(), stat.Size(), stat.IsDir()) if err != nil { return fmt.Errorf("%v: could not update file in database: %v", dbFile.Path(), err) } } fmt.Printf("%v: updated fingerprint\n", dbFile.Path()) } return nil }
func (vfs FuseVfs) tagNamesForFiles(tx *storage.Tx, files entities.Files) ([]string, error) { tagNames := make([]string, 0, 10) 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) } tagIds := make(entities.TagIds, len(fileTags)) for index, fileTag := range fileTags { tagIds[index] = fileTag.TagId } tags, err := vfs.store.TagsByIds(tx, tagIds) if err != nil { return nil, fmt.Errorf("could not retrieve tags: %v", err) } for _, tag := range tags { if strings.ContainsAny(tag.Name, "/\\") { log.Infof(2, "tag '%v' omitted as it contains slashes") continue } if !containsString(tagNames, tag.Name) { tagNames = append(tagNames, tag.Name) } } } return tagNames, nil }
func unmount(path string) error { log.Info(2, "searching path for fusermount.") fusermountPath, err := exec.LookPath("fusermount") if err != nil { return fmt.Errorf("could not find 'fusermount': ensure fuse is installed: %v", err) } log.Infof(2, "running: %v -u %v.", fusermountPath, path) process, err := os.StartProcess(fusermountPath, []string{fusermountPath, "-u", path}, &os.ProcAttr{}) if err != nil { return fmt.Errorf("could not start 'fusermount': %v", err) } log.Info(2, "waiting for process to exit.") processState, err := process.Wait() if err != nil { return fmt.Errorf("error waiting for process to exit: %v", err) } if !processState.Success() { return fmt.Errorf("could not unmount virtual filesystem") } return nil }
func listImplications(store *storage.Storage, tx *storage.Tx, colour bool) error { log.Infof(2, "retrieving tag implications.") implications, err := store.Implications(tx) if err != nil { return fmt.Errorf("could not retrieve implications: %v", err) } width := 0 for _, implication := range implications { length := len(implication.ImplyingTag.Name) if implication.ImplyingValue.Id != 0 { length += 1 + len(implication.ImplyingValue.Name) } if length > width { width = length } } if len(implications) > 0 { for _, implication := range implications { implying := formatTagValueName(implication.ImplyingTag.Name, implication.ImplyingValue.Name, colour, false, true) implied := formatTagValueName(implication.ImpliedTag.Name, implication.ImpliedValue.Name, colour, true, false) fmt.Printf("%*v -> %v\n", width, implying, implied) } } return nil }
func renameTag(store *storage.Storage, tx *storage.Tx, currentName, newName string) error { sourceTag, err := store.TagByName(tx, currentName) if err != nil { return fmt.Errorf("could not retrieve tag '%v': %v", currentName, err) } if sourceTag == nil { return fmt.Errorf("no such tag '%v'", currentName) } destTag, err := store.TagByName(tx, newName) if err != nil { return fmt.Errorf("could not retrieve tag '%v': %v", newName, err) } if destTag != nil { return fmt.Errorf("tag '%v' already exists", newName) } log.Infof(2, "renaming tag '%v' to '%v'.", currentName, newName) _, err = store.RenameTag(tx, sourceTag.Id, newName) if err != nil { return fmt.Errorf("could not rename tag '%v' to '%v': %v", currentName, newName, err) } return nil }
func renameValue(store *storage.Storage, tx *storage.Tx, currentName, newName string) error { sourceValue, err := store.ValueByName(tx, currentName) if err != nil { return fmt.Errorf("could not retrieve value '%v': %v", currentName, err) } if sourceValue == nil { return fmt.Errorf("no such value '%v'", currentName) } destValue, err := store.ValueByName(tx, newName) if err != nil { return fmt.Errorf("could not retrieve value '%v': %v", newName, err) } if destValue != nil { return fmt.Errorf("value '%v' already exists", newName) } log.Infof(2, "renaming value '%v' to '%v'.", currentName, newName) _, err = store.RenameValue(tx, sourceValue.Id, newName) if err != nil { return fmt.Errorf("could not rename value '%v' to '%v': %v", currentName, newName, err) } return nil }
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 }
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 }
func deleteUntaggedFiles(store *storage.Storage, tx *storage.Tx, files entities.Files) error { log.Infof(2, "purging untagged files") fileIds := make([]entities.FileId, len(files)) for index, file := range files { fileIds[index] = file.Id } return store.DeleteUntaggedFiles(tx, fileIds) }