// Copyright (C) 2021 Marius Schellenberger package fs import ( "errors" "git.giftfish.de/ston1th/docstore/pkg/core" "git.giftfish.de/ston1th/docstore/pkg/log" "os" "path/filepath" "sort" "strings" "sync" ) const ( DirMode = 0o750 FileMode = 0o640 ) // Directory represents a directory and its contents type Directory struct { Dirs core.Paths Files core.Paths } type Filesystem struct { Base string // protects scan m sync.RWMutex scan map[string]struct{} } func NewFilesystem(base string) (fs *Filesystem, err error) { if !filepath.IsAbs(base) { err = errors.New("base dir '" + base + "' is not an absolute path") return } fi, err := os.Stat(base) if err != nil { err = os.Mkdir(base, DirMode) if err != nil { return } log.Printf("fs: created data directory '%s'", base) fi, err = os.Stat(base) if err != nil { return } } if !fi.IsDir() { err = errors.New("base dir '" + base + "' is not a directory") return } fs = &Filesystem{Base: base, scan: make(map[string]struct{})} return } func (fs *Filesystem) ReadDir(path string) (d *Directory) { path = Clean(path) combined := filepath.Join(fs.Base, path) f, err := os.Open(combined) if err != nil { return } names, err := f.Readdirnames(0) if err != nil { return } d = new(Directory) for _, n := range names { fi, err := os.Stat(filepath.Join(combined, n)) if err != nil { continue } m := fi.Mode() if m.IsDir() { d.Dirs = append(d.Dirs, core.Path{Name: n, Abs: filepath.Join(path, n)}) continue } if m.IsRegular() { abs := filepath.Join(path, n) d.Files = append(d.Files, core.Path{Name: n, Abs: abs, Scan: fs.CheckScan(abs)}) } } sort.Sort(d.Dirs) sort.Sort(d.Files) return } func (fs *Filesystem) Recursive(p string) (paths core.Paths) { p, _ = filepath.Split(Clean(p)) if len(p) >= 2 { p = p[:len(p)-1] } filepath.Walk(fs.Base, func(path string, info os.FileInfo, err error) error { if err != nil { return nil } if info.IsDir() { a := strings.TrimPrefix(path, fs.Base) if a == "" { a = "/" } paths = append(paths, core.Path{Abs: a, Flag: a == p}) } return nil }) sort.Sort(paths) return } func (fs *Filesystem) RecursiveFiles(p string) (files []string) { filepath.Walk(filepath.Join(fs.Base, Clean(p)), func(path string, info os.FileInfo, err error) error { if err != nil { return nil } if info.Mode().IsRegular() { files = append(files, strings.TrimPrefix(path, fs.Base)) } return nil }) return } func Paths(path string) (p core.Paths) { s := strings.Split(Clean(path), string(filepath.Separator)) for i, v := range s[1:] { if v == "" { continue } if i == 0 { p = append(p, core.Path{Name: v, Abs: "/" + v}) continue } p = append(p, core.Path{Name: v, Abs: filepath.Join(p[i-1].Abs, v)}) } if len(p) != 0 { p[len(p)-1].Flag = true } return }