예제 #1
0
func (vfs FuseVfs) Unlink(name string, context *fuse.Context) fuse.Status {
	log.Infof("BEGIN Unlink(%v)", name)
	defer log.Infof("END Unlink(%v)", name)

	fileId, err := vfs.parseFileId(name)
	if err != nil {
		log.Fatal("Could not unlink: ", err)
	}

	if fileId == 0 {
		// cannot unlink tag directories
		return fuse.EPERM
	}

	path := vfs.splitPath(name)
	tagNames := path[1 : len(path)-1]

	for _, tagName := range tagNames {
		tag, err := vfs.store.Db.TagByName(tagName)
		if err != nil {
			log.Fatal(err)
		}
		if tag == nil {
			log.Fatalf("Could not retrieve tag '%v'.", tagName)
		}

		err = vfs.store.RemoveFileTag(fileId, tag.Id)
		if err != nil {
			log.Fatal(err)
		}
	}

	return fuse.OK
}
예제 #2
0
func (vfs FuseVfs) openTaggedEntryDir(path []string) ([]fuse.DirEntry, fuse.Status) {
	log.Infof("BEGIN openTaggedEntryDir(%v)", path)
	defer log.Infof("END openTaggedEntryDir(%v)", path)

	tagIds, err := vfs.tagNamesToIds(path)
	if err != nil {
		log.Fatalf("Could not lookup tag IDs: %v.", err)
	}
	if tagIds == nil {
		return nil, fuse.ENOENT
	}

	furtherTagIds, err := vfs.store.TagsForTags(tagIds)
	if err != nil {
		log.Fatalf("Could not retrieve tags for tags: %v", err)
	}

	files, err := vfs.store.FilesWithTags(tagIds, []uint{})
	if err != nil {
		log.Fatalf("Could not retrieve tagged files: %v", err)
	}

	entries := make([]fuse.DirEntry, 0, len(files)+len(furtherTagIds))
	for _, tag := range furtherTagIds {
		entries = append(entries, fuse.DirEntry{Name: tag.Name, 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
}
예제 #3
0
func (vfs FuseVfs) topDirectories() ([]fuse.DirEntry, fuse.Status) {
	log.Infof("BEGIN topDirectories")
	defer log.Infof("END topDirectories")

	entries := make([]fuse.DirEntry, 0, 1)
	entries = append(entries, fuse.DirEntry{Name: "tags", Mode: fuse.S_IFDIR})
	return entries, fuse.OK
}
예제 #4
0
func (vfs FuseVfs) getTaggedEntryAttr(path []string) (*fuse.Attr, fuse.Status) {
	log.Infof("BEGIN getTaggedEntryAttr(%v)", path)
	defer log.Infof("END getTaggedEntryAttr(%v)", path)

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

	fileId, err := vfs.parseFileId(name)
	if err != nil {
		return nil, fuse.ENOENT
	}

	if fileId == 0 {
		// 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
		}

		//TODO slow
		//		fileCount, err := vfs.store.FileCountWithTags(tagIds)
		//		if err != nil {
		//			log.Fatalf("Could not retrieve count of files with tags: %v. (%v)", path, err)
		//		}
		fileCount := 0

		now := time.Now()
		return &fuse.Attr{Mode: fuse.S_IFDIR | 0755, Nlink: 2, Size: uint64(fileCount), Mtime: uint64(now.Unix()), Mtimensec: uint32(now.Nanosecond())}, fuse.OK
	}

	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
}
예제 #5
0
func (vfs FuseVfs) getTagsAttr() (*fuse.Attr, fuse.Status) {
	log.Infof("BEGIN getTagsAttr")
	defer log.Infof("END getTagsAttr")

	tagCount, err := vfs.store.Db.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
}
예제 #6
0
파일: untag.go 프로젝트: kelvinhammond/tmsu
func (command UntagCommand) untagPath(store *storage.Storage, path string, tagIds []uint) error {
	absPath, err := filepath.Abs(path)
	if err != nil {
		return fmt.Errorf("%v: could not get absolute path: %v", path, err)
	}

	file, err := store.FileByPath(absPath)
	if err != nil {
		return fmt.Errorf("%v: could not retrieve file: %v", path, err)
	}
	if file == nil {
		return fmt.Errorf("%v: file is not tagged.", path)
	}

	for _, tagId := range tagIds {
		if command.verbose {
			log.Infof("%v: unapplying tag #%v.", file.Path(), tagId)
		}

		if err := store.RemoveFileTag(file.Id, tagId); err != nil {
			return fmt.Errorf("%v: could not remove tag #%v: %v", file.Path(), tagId, err)
		}
	}

	if err := command.removeUntaggedFile(store, file); err != nil {
		return err
	}

	if command.recursive {
		childFiles, err := store.FilesByDirectory(file.Path())
		if err != nil {
			return fmt.Errorf("%v: could not retrieve files for directory: %v", file.Path())
		}

		for _, childFile := range childFiles {
			for _, tagId := range tagIds {
				if command.verbose {
					log.Infof("%v: unapplying tag #%v.", childFile.Path(), tagId)
				}

				if err := store.RemoveFileTag(childFile.Id, tagId); err != nil {
					return fmt.Errorf("%v: could not remove tag #%v: %v", childFile.Path(), tagId, err)
				}
			}

			if err := command.removeUntaggedFile(store, childFile); err != nil {
				return err
			}
		}
	}

	return nil
}
예제 #7
0
func (vfs FuseVfs) Readlink(name string, context *fuse.Context) (string, fuse.Status) {
	log.Infof("BEGIN Readlink(%v)", name)
	defer log.Infof("END Readlink(%v)", name)

	path := vfs.splitPath(name)
	switch path[0] {
	case "tags":
		return vfs.readTaggedEntryLink(path[1:])
	}

	return "", fuse.ENOENT
}
예제 #8
0
func (command RepairCommand) repairModified(store *storage.Storage, modified fileIdAndInfoMap) error {
	if command.verbose {
		log.Info("repairing modified files")
	}

	for path, fileIdAndStat := range modified {
		fileId := fileIdAndStat.fileId
		stat := fileIdAndStat.stat

		log.Infof("%v: modified", path)

		fingerprint, err := fingerprint.Create(path)
		if err != nil {
			return fmt.Errorf("%v: could not create fingerprint: %v", path, err)
		}

		if !command.pretend {
			_, err := store.UpdateFile(fileId, path, fingerprint, stat.ModTime(), stat.Size(), stat.IsDir())
			if err != nil {
				return fmt.Errorf("%v: could not update file in database: %v", path, err)
			}
		}

	}

	return nil
}
예제 #9
0
파일: dupes.go 프로젝트: kelvinhammond/tmsu
func (command DupesCommand) findDuplicatesInDb() error {
	store, err := storage.Open()
	if err != nil {
		return fmt.Errorf("could not open storage: %v", err)
	}
	defer store.Close()

	if command.verbose {
		log.Info("identifying duplicate files.")
	}

	fileSets, err := store.DuplicateFiles()
	if err != nil {
		return fmt.Errorf("could not identify duplicate files: %v", err)
	}

	if command.verbose {
		log.Infof("found %v sets of duplicate files.", len(fileSets))
	}

	for index, fileSet := range fileSets {
		if index > 0 {
			log.Print()
		}

		log.Printf("Set of %v duplicates:", len(fileSet))

		for _, file := range fileSet {
			relPath := _path.Rel(file.Path())
			log.Printf("  %v", relPath)
		}
	}

	return nil
}
예제 #10
0
func (vfs FuseVfs) tagDirectories() ([]fuse.DirEntry, fuse.Status) {
	log.Infof("BEGIN tagDirectories")
	defer log.Infof("END tagDirectories")

	tags, err := vfs.store.Db.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
}
예제 #11
0
파일: tags.go 프로젝트: kelvinhammond/tmsu
func (command TagsCommand) listTagsForPath(store *storage.Storage, path string) error {
	if command.verbose {
		log.Infof("%v: retrieving tags.", path)
	}

	var tags, err = store.TagsForPath(path)
	if err != nil {
		return fmt.Errorf("%v: could not retrieve tags: %v", path, err)
	}

	if len(tags) == 0 {
		_, err := os.Stat(path)
		if err != nil {
			switch {
			case os.IsPermission(err):
				log.Warnf("%v: permission denied", path)
			case os.IsNotExist(err):
				return fmt.Errorf("%v: file not found", path)
			default:
				return fmt.Errorf("%v: could not stat file: %v", path, err)
			}
		}
	}

	if command.count {
		log.Print(len(tags))
	} else {
		for _, tag := range tags {
			log.Print(tag.Name)
		}
	}

	return nil
}
예제 #12
0
파일: imply.go 프로젝트: kelvinhammond/tmsu
func (command ImplyCommand) deleteImplication(store *storage.Storage, tagName, impliedTagName string) error {
	tag, err := store.Db.TagByName(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)
	}

	impliedTag, err := store.Db.TagByName(impliedTagName)
	if err != nil {
		return fmt.Errorf("could not retrieve tag '%v': %v", impliedTagName, err)
	}
	if impliedTag == nil {
		return fmt.Errorf("no such tag '%v'.", impliedTagName)
	}

	if command.verbose {
		log.Infof("removing tag implication of '%v' to '%v'.", tagName, impliedTagName)
	}

	if err = store.RemoveImplication(tag.Id, impliedTag.Id); err != nil {
		return fmt.Errorf("could not add delete tag implication of '%v' to '%v': %v", tagName, impliedTagName, err)
	}

	return nil
}
예제 #13
0
func (command UnmountCommand) unmount(path string) error {
	if command.verbose {
		log.Info("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)
	}

	if command.verbose {
		log.Infof("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)
	}

	if command.verbose {
		log.Info("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
}
예제 #14
0
파일: dupes.go 프로젝트: kelvinhammond/tmsu
func (command DupesCommand) findDuplicatesOf(paths []string) error {
	store, err := storage.Open()
	if err != nil {
		return fmt.Errorf("could not open storage: %v", err)
	}
	defer store.Close()

	first := true
	for _, path := range paths {
		if command.verbose {
			log.Infof("%v: identifying duplicate files.\n", path)
		}

		fp, err := fingerprint.Create(path)
		if err != nil {
			return fmt.Errorf("%v: could not create fingerprint: %v", path, err)
		}

		if fp == fingerprint.Fingerprint("") {
			return nil
		}

		files, err := store.FilesByFingerprint(fp)
		if err != nil {
			return fmt.Errorf("%v: could not retrieve files matching fingerprint '%v': %v", path, fp, err)
		}

		absPath, err := filepath.Abs(path)
		if err != nil {
			return fmt.Errorf("%v: could not determine absolute path: %v", path, err)
		}

		// filter out the file we're searching on
		dupes := files.Where(func(file *database.File) bool { return file.Path() != absPath })

		if len(paths) > 1 && len(dupes) > 0 {
			if first {
				first = false
			} else {
				log.Print()
			}

			log.Printf("%v duplicates of %v:", len(dupes), path)

			for _, dupe := range dupes {
				relPath := _path.Rel(dupe.Path())
				log.Printf("  %v", relPath)
			}
		} else {
			for _, dupe := range dupes {
				relPath := _path.Rel(dupe.Path())
				log.Print(relPath)
			}
		}
	}

	return nil
}
예제 #15
0
func (command RepairCommand) repairMoved(store *storage.Storage, missing databaseFileMap, untagged fileInfoMap) error {
	if command.verbose {
		log.Info("repairing moved files")
	}

	moved := make([]string, 0, 10)

	for path, dbFile := range missing {
		if command.verbose {
			log.Infof("%v: searching for new location", path)
		}

		for candidatePath, stat := range untagged {
			if stat.Size() == dbFile.Size {
				fingerprint, err := fingerprint.Create(candidatePath)
				if err != nil {
					return fmt.Errorf("%v: could not create fingerprint: %v", path, err)
				}

				if fingerprint == dbFile.Fingerprint {
					log.Infof("%v: moved to %v", path, candidatePath)

					moved = append(moved, path)

					if !command.pretend {
						_, err := store.UpdateFile(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", path, err)
						}
					}

					delete(untagged, candidatePath)

					break
				}
			}
		}
	}

	for _, path := range moved {
		delete(missing, path)
	}

	return nil
}
예제 #16
0
func (command RepairCommand) repairMissing(store *storage.Storage, missing databaseFileMap) error {
	for path, dbFile := range missing {
		if command.force && !command.pretend {
			if err := store.RemoveFileTagsByFileId(dbFile.Id); err != nil {
				return fmt.Errorf("%v: could not delete file-tags: %v", path, err)
			}

			if err := store.RemoveFile(dbFile.Id); err != nil {
				return fmt.Errorf("%v: could not delete file: %v", path, err)
			}

			log.Infof("%v: removed", path)
		} else {
			log.Infof("%v: missing", path)
		}
	}

	return nil
}
예제 #17
0
func (command *StatusCommand) checkFile(file *database.File, report *StatusReport) error {
	relPath := path.Rel(file.Path())

	if command.verbose {
		log.Infof("%v: checking file status.", file.Path())
	}

	stat, err := os.Stat(file.Path())
	if err != nil {
		switch {
		case os.IsNotExist(err):
			if command.verbose {
				log.Infof("%v: file is missing.", file.Path())
			}

			report.AddRow(Row{relPath, MISSING})
			return nil
		case os.IsPermission(err):
			log.Warnf("%v: permission denied.", file.Path())
		case strings.Contains(err.Error(), "not a directory"):
			report.AddRow(Row{relPath, MISSING})
			return nil
		default:
			return fmt.Errorf("%v: could not stat: %v", file.Path(), err)
		}
	} else {
		if stat.Size() != file.Size || stat.ModTime().UTC() != file.ModTime {
			if command.verbose {
				log.Infof("%v: file is modified.", file.Path())
			}

			report.AddRow(Row{relPath, MODIFIED})
		} else {
			if command.verbose {
				log.Infof("%v: file is unchanged.", file.Path())
			}

			report.AddRow(Row{relPath, TAGGED})
		}
	}

	return nil
}
예제 #18
0
func (vfs FuseVfs) OpenDir(name string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
	log.Infof("BEGIN OpenDir(%v)", name)
	defer log.Infof("END OpenDir(%v)", name)

	switch name {
	case "":
		return vfs.topDirectories()
	case "tags":
		return vfs.tagDirectories()
	}

	path := vfs.splitPath(name)
	switch path[0] {
	case "tags":
		return vfs.openTaggedEntryDir(path[1:])
	}

	return nil, fuse.ENOENT
}
예제 #19
0
파일: tag.go 프로젝트: kelvinhammond/tmsu
func (command TagCommand) lookupTagIds(store *storage.Storage, names []string) ([]uint, error) {
	tags, err := store.TagsByNames(names)
	if err != nil {
		return nil, fmt.Errorf("could not retrieve tags %v: %v", names, err)
	}

	for _, name := range names {
		if !tags.Any(func(tag *database.Tag) bool { return tag.Name == name }) {
			log.Infof("New tag '%v'.", name)

			tag, err := store.AddTag(name)
			if err != nil {
				return nil, fmt.Errorf("could not add tag '%v': %v", name, err)
			}

			tags = append(tags, tag)
		}
	}

	if command.verbose {
		log.Infof("retrieving tag implications")
	}

	tagIds := make([]uint, len(tags))
	for index, tag := range tags {
		tagIds[index] = tag.Id
	}

	implications, err := store.ImplicationsForTags(tagIds...)
	if err != nil {
		return nil, fmt.Errorf("could not retrieve implied tags: %v", err)
	}

	for _, implication := range implications {
		if !contains(tagIds, implication.ImpliedTag.Id) {
			log.Infof("tag '%v' is implied.", implication.ImpliedTag.Name)
			tagIds = append(tagIds, implication.ImpliedTag.Id)
		}
	}

	return tagIds, nil
}
예제 #20
0
func (vfs FuseVfs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
	log.Infof("BEGIN GetAttr(%v)", name)
	defer log.Infof("END GetAttr(%v)", name)

	switch name {
	case "":
		fallthrough
	case "tags":
		return vfs.getTagsAttr()
	}

	path := vfs.splitPath(name)

	switch path[0] {
	case "tags":
		return vfs.getTaggedEntryAttr(path[1:])
	}

	return nil, fuse.ENOENT
}
예제 #21
0
func (vfs FuseVfs) readTaggedEntryLink(path []string) (string, fuse.Status) {
	log.Infof("BEGIN readTaggedEntryLink(%v)", path)
	defer log.Infof("END readTaggedEntryLink(%v)", path)

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

	fileId, err := vfs.parseFileId(name)
	if err != nil {
		log.Fatalf("Could not parse file identifier: %v", err)
	}
	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
}
예제 #22
0
func (command *StatusCommand) findNewFiles(searchPath string, report *StatusReport) error {
	if command.verbose {
		log.Infof("%v: finding new files.", searchPath)
	}

	relPath := path.Rel(searchPath)

	if !report.ContainsRow(relPath) {
		report.AddRow(Row{relPath, UNTAGGED})
	}

	absPath, err := filepath.Abs(searchPath)
	if err != nil {
		return fmt.Errorf("%v: could not get absolute path: %v", searchPath, err)
	}

	stat, err := os.Stat(absPath)
	if err != nil {
		switch {
		case os.IsNotExist(err):
			return nil
		case os.IsPermission(err):
			log.Warnf("%v: permission denied.", searchPath)
			return nil
		default:
			return fmt.Errorf("%v: could not stat: %v", searchPath, err)
		}
	}

	if !command.directory && stat.IsDir() {
		dir, err := os.Open(absPath)
		if err != nil {
			return fmt.Errorf("%v: could not open file: %v", searchPath, err)
		}

		dirNames, err := dir.Readdirnames(0)
		if err != nil {
			return fmt.Errorf("%v: could not read directory listing: %v", searchPath, err)
		}

		for _, dirName := range dirNames {
			dirPath := filepath.Join(searchPath, dirName)
			err = command.findNewFiles(dirPath, report)
			if err != nil {
				return err
			}
		}
	}

	return nil
}
예제 #23
0
func (command StatusCommand) statusPaths(paths []string) (*StatusReport, error) {
	report := NewReport()

	store, err := storage.Open()
	if err != nil {
		return nil, fmt.Errorf("could not open storage: %v", err)
	}
	defer store.Close()

	for _, path := range paths {
		absPath, err := filepath.Abs(path)
		if err != nil {
			return nil, fmt.Errorf("%v: could not get absolute path: %v", path, err)
		}

		file, err := store.FileByPath(absPath)
		if err != nil {
			return nil, fmt.Errorf("%v: could not retrieve file: %v", path, err)
		}
		if file != nil {
			err = command.checkFile(file, report)
			if err != nil {
				return nil, err
			}
		}

		if !command.directory {
			if command.verbose {
				log.Infof("%v: retrieving files from database.", path)
			}

			files, err := store.FilesByDirectory(absPath)
			if err != nil {
				return nil, fmt.Errorf("%v: could not retrieve files for directory: %v", path, err)
			}

			err = command.checkFiles(files, report)
			if err != nil {
				return nil, err
			}
		}

		err = command.findNewFiles(path, report)
		if err != nil {
			return nil, err
		}
	}

	return report, nil
}
예제 #24
0
파일: untag.go 프로젝트: kelvinhammond/tmsu
func (command UntagCommand) removeUntaggedFile(store *storage.Storage, file *database.File) error {
	if command.verbose {
		log.Infof("%v: identifying whether file is tagged.", file.Path())
	}

	filetagCount, err := store.FileTagCountByFileId(file.Id)
	if err != nil {
		return fmt.Errorf("%v: could not get tag count: %v", file.Path(), err)
	}

	if filetagCount == 0 {
		if command.verbose {
			log.Infof("%v: removing untagged file.", file.Path())
		}

		err = store.RemoveFile(file.Id)
		if err != nil {
			return fmt.Errorf("%v: could not remove file: %v", file.Path(), err)
		}
	}

	return nil
}
예제 #25
0
func (command RenameCommand) Exec(options cli.Options, args []string) error {
	command.verbose = options.HasOption("--verbose")

	store, err := storage.Open()
	if err != nil {
		return fmt.Errorf("could not open storage: %v", err)
	}
	defer store.Close()

	if len(args) < 2 {
		return fmt.Errorf("tag to rename and new name must both be specified.")
	}

	if len(args) > 2 {
		return fmt.Errorf("too many arguments")
	}

	sourceTagName := args[0]
	destTagName := args[1]

	sourceTag, err := store.TagByName(sourceTagName)
	if err != nil {
		return fmt.Errorf("could not retrieve tag '%v': %v", sourceTagName, err)
	}
	if sourceTag == nil {
		return fmt.Errorf("no such tag '%v'.", sourceTagName)
	}

	destTag, err := store.TagByName(destTagName)
	if err != nil {
		return fmt.Errorf("could not retrieve tag '%v': %v", destTagName, err)
	}
	if destTag != nil {
		return fmt.Errorf("tag '%v' already exists.", destTagName)
	}

	if command.verbose {
		log.Infof("renaming tag '%v' to '%v'.", sourceTagName, destTagName)
	}

	_, err = store.RenameTag(sourceTag.Id, destTagName)
	if err != nil {
		return fmt.Errorf("could not rename tag '%v' to '%v': %v", sourceTagName, destTagName, err)
	}

	return nil
}
예제 #26
0
파일: untag.go 프로젝트: kelvinhammond/tmsu
func (command UntagCommand) untagPathAll(store *storage.Storage, path string) error {
	absPath, err := filepath.Abs(path)
	if err != nil {
		return fmt.Errorf("%v: could not get absolute path: %v", path, err)
	}

	file, err := store.FileByPath(absPath)
	if err != nil {
		return fmt.Errorf("%v: could not retrieve file: %v", path, err)
	}
	if file == nil {
		return fmt.Errorf("%v: file is not tagged.", path)
	}

	if command.verbose {
		log.Infof("%v: removing all tags.", file.Path())
	}

	if err := store.RemoveFileTagsByFileId(file.Id); err != nil {
		return fmt.Errorf("%v: could not remove file's tags: %v", file.Path(), err)
	}

	if err := command.removeUntaggedFile(store, file); err != nil {
		return err
	}

	if command.recursive {
		childFiles, err := store.FilesByDirectory(file.Path())
		if err != nil {
			return fmt.Errorf("%v: could not retrieve files for directory: %v", file.Path())
		}

		for _, childFile := range childFiles {
			if err := store.RemoveFileTagsByFileId(childFile.Id); err != nil {
				return fmt.Errorf("%v: could not remove file's tags: %v", childFile.Path(), err)
			}

			if err := command.removeUntaggedFile(store, childFile); err != nil {
				return err
			}

		}
	}

	return nil
}
예제 #27
0
파일: tag.go 프로젝트: kelvinhammond/tmsu
func (command *TagCommand) addFile(store *storage.Storage, path string, modTime time.Time, size uint, isDir bool) (*database.File, error) {
	if command.verbose {
		log.Infof("%v: adding file.", path)
	}

	fingerprint, err := fingerprint.Create(path)
	if err != nil {
		return nil, fmt.Errorf("%v: could not create fingerprint: %v", path, err)
	}

	file, err := store.AddFile(path, fingerprint, modTime, int64(size), isDir)
	if err != nil {
		return nil, fmt.Errorf("%v: could not add file to database: %v", path, err)
	}

	return file, nil
}
예제 #28
0
파일: tag.go 프로젝트: kelvinhammond/tmsu
func (command TagCommand) tagPath(store *storage.Storage, path string, tagIds []uint) error {
	absPath, err := filepath.Abs(path)
	if err != nil {
		return fmt.Errorf("%v: could not get absolute path: %v", path, err)
	}

	stat, err := os.Stat(path)
	if err != nil {
		switch {
		case os.IsPermission(err):
			return fmt.Errorf("%v: permisison denied", path)
		case os.IsNotExist(err):
			return fmt.Errorf("%v: no such file", path)
		default:
			return fmt.Errorf("%v: could not stat file: %v", path, err)
		}
	}

	file, err := store.FileByPath(absPath)
	if err != nil {
		return fmt.Errorf("%v: could not retrieve file: %v", path, err)
	}
	if file == nil {
		file, err = command.addFile(store, absPath, stat.ModTime(), uint(stat.Size()), stat.IsDir())
		if err != nil {
			return fmt.Errorf("%v: could not add file: %v", path, err)
		}
	}

	if command.verbose {
		log.Infof("%v: applying tags.", file.Path())
	}

	if err = store.AddFileTags(file.Id, tagIds); err != nil {
		return fmt.Errorf("%v: could not apply tags: %v", file.Path(), err)
	}

	if command.recursive && stat.IsDir() {
		if err = command.tagRecursively(store, path, tagIds); err != nil {
			return err
		}
	}

	return nil
}
예제 #29
0
파일: tags.go 프로젝트: kelvinhammond/tmsu
func (command TagsCommand) listTagsForPaths(store *storage.Storage, paths []string) error {
	for _, path := range paths {
		if command.verbose {
			log.Infof("%v: retrieving tags.", path)
		}

		var tags, err = store.TagsForPath(path)
		if err != nil {
			log.Warn(err.Error())
			continue
		}

		if command.count {
			log.Print(path + ": " + strconv.Itoa(len(tags)))
		} else {
			log.Print(path + ": " + tagLine(tags))
		}
	}

	return nil
}
예제 #30
0
func (command RepairCommand) checkFiles(store *storage.Storage, paths []string) error {
	tree := _path.NewTree()
	for _, path := range paths {
		tree.Add(path, false)
	}
	paths = tree.TopLevel().Paths()

	fsPaths, err := enumerateFileSystemPaths(paths)
	if err != nil {
		return err
	}

	dbPaths, err := enumerateDatabasePaths(store, paths)
	if err != nil {
		return err
	}

	_, untagged, modified, missing := command.determineStatuses(fsPaths, dbPaths)

	if err = command.repairModified(store, modified); err != nil {
		return err
	}

	if err = command.repairMoved(store, missing, untagged); err != nil {
		return err
	}

	if err = command.repairMissing(store, missing); err != nil {
		return err
	}

	for path, _ := range untagged {
		log.Infof("%v: untagged", path)
	}

	//TODO cleanup: any files that have no tags: remove
	//TODO cleanup: any tags that do not correspond to a file: remove

	return nil
}