159
package main
import (
"fmt"
bolt "go.etcd.io/bbolt"
"path/filepath"
"strings"
)
// var db *bolt.DB
//
// func init() {
// var err error
// db, err = bolt.Open("build/my.db", 0600, nil)
// if err != nil {
// log.Fatalln(err)
// }
// }
// FileSystem represents a file system in a database
type FileSystem struct {
db *bolt.DB
}
// OpenDB opens or creates the database
func OpenDB(dbPath string) (*FileSystem, error) {
db, err := bolt.Open(dbPath, 0600, nil)
if err != nil {
return nil, err
}
err = db.Update(func(tx *bolt.Tx) error {
_, err = tx.CreateBucketIfNotExists([]byte("root"))
return err
})
return &FileSystem{db: db}, err
}
type DirEntry struct {
Name string
Size int
IsDir bool
}
// GetEntry retrieves the content of a file or the list of items in a directory
func (fs *FileSystem) GetEntry(path string) ([]byte, []DirEntry, error) {
path = strings.Trim(path, "/")
paths := strings.Split(filepath.Dir(path), "/")
path = filepath.Base(path)
var items []DirEntry
var item DirEntry
var data []byte
return data, items, fs.db.View(func(tx *bolt.Tx) error {
currentBucket := tx.Bucket([]byte("root"))
if currentBucket == nil {
return fmt.Errorf("root bucket does not exist")
}
for _, dir := range paths {
if dir == "" || dir == "." {
continue
}
currentBucket = currentBucket.Bucket([]byte(dir))
if currentBucket == nil {
return fmt.Errorf("directory %s does not exist", dir)
}
}
if path != "" && path != "." {
data = currentBucket.Get([]byte(path))
if data != nil {
return nil
}
currentBucket = currentBucket.Bucket([]byte(path))
}
if currentBucket != nil {
return currentBucket.ForEach(func(k, v []byte) error {
item = DirEntry{
Name: string(k),
Size: len(v),
IsDir: false,
}
if item.Size == 0 && currentBucket.Bucket(k) != nil {
item.Name += "/"
item.IsDir = true
}
items = append(items, item)
return nil
})
}
return fmt.Errorf("entry %s does not exist", path)
})
}
// PutFile stores the content of a file, creating directories as needed
func (fs *FileSystem) PutFile(path string, data []byte) error {
path = strings.Trim(path, "/")
paths := strings.Split(filepath.Dir(path), "/")
path = filepath.Base(path)
if len(path) < 1 || strings.HasPrefix(path, ".") {
return fmt.Errorf("invalid file name")
}
var err error
return fs.db.Update(func(tx *bolt.Tx) error {
currentBucket := tx.Bucket([]byte("root"))
if currentBucket == nil {
return fmt.Errorf("root bucket does not exist")
}
for _, dir := range paths {
if dir == "" || dir == "." {
continue
}
if currentBucket.Get([]byte(dir)) != nil {
return fmt.Errorf("directory %s is a file", dir)
}
currentBucket, err = currentBucket.CreateBucketIfNotExists([]byte(dir))
if err != nil || currentBucket == nil {
return fmt.Errorf("directory %s does not exist", dir)
}
}
if currentBucket.Bucket([]byte(path)) != nil {
return fmt.Errorf("file %s is a directory", path)
}
return currentBucket.Put([]byte(path), data)
})
}
// Close the database
func (fs *FileSystem) Close() error {
return fs.db.Close()
}
func main() {
fs, err := OpenDB("build/my.db")
if err != nil {
fmt.Println(err)
return
}
defer fs.Close()
// err = fs.PutFile("index.txt", []byte("Hello"))
// if err != nil {
// fmt.Println(err)
// return
// }
data, items, err := fs.GetEntry("/")
if err != nil {
fmt.Println(err)
return
}
if data != nil {
fmt.Println(string(data))
return
}
for _, item := range items {
fmt.Println(item)
}
}