Git backed by object storage because you can't stop me
git
object-storage
kefka
1package s3fs
2
3import (
4 "fmt"
5 "path"
6 "strings"
7 "sync"
8
9 "github.com/go-git/go-billy/v6"
10 "github.com/tigrisdata/storage-go"
11)
12
13const (
14 DefaultSeparator = "/"
15)
16
17type S3FS struct {
18 client *storage.Client
19 bucket string
20 root string
21 separator string
22
23 // temps holds TempFile-backed buffers keyed by canonical S3 key, so a
24 // subsequent Open of the same path returns a reader over the same bytes
25 // the writer is still appending to. See tempfs.go.
26 tempMu sync.Mutex
27 temps map[string]*tempBuffer
28}
29
30// NewS3FS creates a new S3FS Filesystem.
31func NewS3FS(client *storage.Client, bucket string) (billy.Filesystem, error) {
32 // Check for a non-nil client
33 if client == nil {
34 return nil, fmt.Errorf("s3 client cannot be nil")
35 }
36 return &S3FS{
37 client: client,
38 bucket: bucket,
39 root: "",
40 separator: DefaultSeparator,
41 temps: make(map[string]*tempBuffer),
42 }, nil
43}
44
45// Capabilities returns the filesystem capabilities.
46func (fs3 *S3FS) Capabilities() billy.Capability {
47 return billy.ReadCapability | billy.WriteCapability
48}
49
50func (fs3 *S3FS) cleanPath(p ...string) string {
51 // Join the path elements
52 j := path.Join(p...)
53
54 // Clean the path before joining to root
55 c := path.Clean(j)
56
57 // Join the root and cleaned path
58 f := path.Join(fs3.root, c)
59
60 // Return the full path
61 return path.Clean(f)
62}
63
64// key turns a root-relative billy path into the canonical S3 object key:
65// root-joined, cleaned, and stripped of the leading slash that S3 keys never
66// carry. All S3 operations must funnel through here so reads and writes agree
67// on the same key regardless of chroot depth.
68func (fs3 *S3FS) key(name string) string {
69 return strings.TrimPrefix(fs3.cleanPath(name), "/")
70}