Skip to content

Commit

Permalink
gb mount
Browse files Browse the repository at this point in the history
add fuse dependency

fix flags

gbfs: implement Read()

gbfs: remove debug code

simplify how gbfs can be used

update comment

default
  • Loading branch information
babbaj committed Feb 25, 2022
1 parent be4750a commit 122a2d0
Show file tree
Hide file tree
Showing 4 changed files with 328 additions and 16 deletions.
269 changes: 269 additions & 0 deletions gbfs/gbfs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
package gbfs

import (
"bazil.org/fuse"
fuseFs "bazil.org/fuse/fs"
"context"
"fmt"
"github.com/leijurv/gb/db"
"github.com/leijurv/gb/download"
"io"
"os"
"strings"
"syscall"
"time"
)

type File struct {
path string
hash *[]byte // I love go
modifiedTime uint64
flags int32
size uint64
inode uint64 // generated
}

func (f File) name() string {
idx := strings.LastIndex(f.path, "/")
return f.path[idx+1:]
}

type Dir struct {
name string // empty for the root dir
files map[string]File
dirs map[string]*Dir
inode uint64 // generated
}

type GBFS struct {
root Dir
}

type FileHandle struct {
reader io.ReadCloser
// for sanity checking
currentOffset int64
}

func timeMillis(millis int64) time.Time {
return time.Unix(0, millis*int64(time.Millisecond))
}

func (d *Dir) Attr(ctx context.Context, attr *fuse.Attr) error {
//attr.Inode = d.inode
attr.Uid = 1000
attr.Gid = 100
attr.Mode = os.ModeDir | 0o555
attr.Nlink = 2
return nil
}

func (f *File) Attr(ctx context.Context, attr *fuse.Attr) error {
//attr.Inode = f.inode
attr.Uid = 1000
attr.Gid = 100
//attr.Mode = 0o444
mtime := timeMillis(int64(f.modifiedTime))
attr.Mtime = mtime
attr.Mode = os.FileMode(f.flags)
attr.Size = f.size
return nil
}

func (d *Dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
out := make([]fuse.Dirent, 0, len(d.dirs)+len(d.files)+2)

out = append(out, fuse.Dirent{
//Inode: d.inode,
Name: ".",
Type: fuse.DT_Dir,
})
out = append(out, fuse.Dirent{
Name: "..",
Type: fuse.DT_Dir,
})

for _, subdir := range d.dirs {
out = append(out, fuse.Dirent{
//Inode: subdir.inode,
Name: subdir.name,
Type: fuse.DT_Dir,
})
}
for _, f := range d.files {
name := f.name()
out = append(out, fuse.Dirent{
//Inode: f.inode,
Name: name,
Type: fuse.DT_File,
})
}

return out, nil
}

var _ fuseFs.Node = (*File)(nil)
var _ = fuseFs.NodeOpener(&File{})

func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fuseFs.Handle, error) {
tx, err := db.DB.Begin()
if err != nil {
panic(err)
}
defer func() {
err = tx.Commit()
if err != nil {
panic(err)
}
}()

reader := download.CatReadCloser(*f.hash, tx)
// TODO: support seeking for uncompressed file (possibly useful for videos)
resp.Flags |= fuse.OpenNonSeekable

return &FileHandle{reader, 0}, nil
}

func (fh *FileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
return fh.reader.Close()
}

var _ = fuseFs.HandleReader(&FileHandle{})

func (fh *FileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
buf := make([]byte, req.Size)
if req.Offset != fh.currentOffset {
fmt.Println("Attempt to read from wrong offset (", req.Offset, ") expected (", fh.currentOffset, ")")
return os.ErrInvalid
}
n, err := io.ReadFull(fh.reader, buf)
fh.currentOffset += int64(n)

// not sure if this makes sense but this is what the official example does
// https://github.com/bazil/zipfs/blob/master/main.go#L221
if err == io.ErrUnexpectedEOF || err == io.EOF {
err = nil
}
resp.Data = buf[:n]
return err
}

func (d *Dir) Lookup(ctx context.Context, name string) (fuseFs.Node, error) {
if subdir, ok := d.dirs[name]; ok {
return subdir, nil
}
if f, ok := d.files[name]; ok {
return &f, nil
}

return nil, syscall.ENOENT
}

func Mount(mountpoint string, path string, timestamp int64) {
root := parseDirectoryStructure(queryAllFiles(path, timestamp))
// TODO: store blob info so we don't need to query it later
//db.ShutdownDatabase()

conn, err := fuse.Mount(mountpoint,
fuse.ReadOnly(),
fuse.DefaultPermissions(),
fuse.FSName("gbfs"),
fuse.MaxReadahead(128*1024), // this is what restic uses
)
if err != nil {
panic(err)
}
defer func(conn *fuse.Conn) {
err := conn.Close()
if err != nil {
panic(err)
}
}(conn)

err = fuseFs.Serve(conn, GBFS{root})
if err != nil {
panic(err)
}
}

func (gb GBFS) Root() (fuseFs.Node, error) {
return &gb.root, nil
}

const (
QUERY = "SELECT files.path, files.hash, files.fs_modified, files.permissions, sizes.size FROM files INNER JOIN sizes ON files.hash = sizes.hash WHERE (? >= files.start AND (files.end > ? OR files.end IS NULL)) AND files.path GLOB ?"
)

func queryAllFiles(path string, timestamp int64) []File {
tx, err := db.DB.Begin()
if err != nil {
panic(err)
}
defer func() {
err = tx.Commit()
if err != nil {
panic(err)
}
}()

if !strings.HasSuffix(path, "/") {
path += "/"
}
rows, err := tx.Query(QUERY, timestamp, timestamp, path+"*")
if err != nil {
panic(err)
}
var files []File
for rows.Next() {
var file File
err = rows.Scan(&file.path, &file.hash, &file.modifiedTime, &file.flags, &file.size)
if err != nil {
panic(err)
}
files = append(files, file)
}
err = rows.Err()
if err != nil {
panic(err)
}
return files
}

func makeDir(name string, inode uint64) Dir {
return Dir{
name: name,
files: make(map[string]File),
dirs: make(map[string]*Dir),
inode: inode,
}
}

func parseDirectoryStructure(files []File) Dir {
root := makeDir("", 0)
nextInode := uint64(1)
for _, f := range files {
dir := &root
parts := strings.Split(f.path, "/")
for i := 1; i < len(parts); i++ {
element := parts[i]

if i == len(parts)-1 {
f.inode = nextInode
nextInode++
dir.files[element] = f
} else {
if val, ok := dir.dirs[element]; ok {
dir = val
} else {
cringe := makeDir(element, nextInode)
nextInode++
newDir := &cringe
dir.dirs[element] = newDir
dir = newDir
}
}
}
}

return root
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.13
require (
github.com/mattn/go-sqlite3 v1.11.0 // database locking was changed in newer versions. gb will randomly panic with "database is locked" in multithreaded situations if this dependency is updated further, apparently in disregard of the option "_busy_timeout=20000" that is set in the db connection. sorry.

bazil.org/fuse v0.0.0-20200524192727-fb710f7dfd05 // indirect
github.com/DataDog/zstd v1.4.8
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
github.com/aws/aws-sdk-go v1.41.11
Expand Down
11 changes: 11 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
bazil.org/fuse v0.0.0-20200524192727-fb710f7dfd05 h1:UrYe9YkT4Wpm6D+zByEyCJQzDqTPXqTDUI7bZ41i9VE=
bazil.org/fuse v0.0.0-20200524192727-fb710f7dfd05/go.mod h1:h0h5FBYpXThbvSfTqthw+0I4nmHnhTHkO5BoOHsBWqg=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
Expand Down Expand Up @@ -55,6 +57,7 @@ github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/DataDog/zstd v1.4.8 h1:Rpmta4xZ/MgZnriKNd24iZMhGpP5dvUcs/uqfBapKZY=
github.com/DataDog/zstd v1.4.8/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/araddon/dateparse v0.0.0-20190622164848-0fb0a474d195 h1:c4mLfegoDw6OhSJXTd2jUEQgZUQuJWtocudb97Qn9EM=
Expand Down Expand Up @@ -94,6 +97,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
Expand Down Expand Up @@ -216,6 +221,7 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
Expand All @@ -226,11 +232,14 @@ github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
github.com/tyler-smith/go-bip39 v1.0.2 h1:+t3w+KwLXO6154GNJY+qUtIxLTmFjfUmpguQT1OlOT8=
github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
Expand Down Expand Up @@ -385,6 +394,7 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191118073850-2f86c98f3482 h1:KvDjb4ikxgXnSGcAoMgJ0YUuIE44iDyT6cSRQ5vZQMs=
golang.org/x/sys v0.0.0-20191118073850-2f86c98f3482/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down Expand Up @@ -477,6 +487,7 @@ golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200423201157-2723c5de0d66/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
Expand Down
Loading

0 comments on commit 122a2d0

Please sign in to comment.