Initial commit

This commit is contained in:
Quentin 2021-08-19 22:12:12 +02:00
commit d69640894d
3 changed files with 462 additions and 0 deletions

9
go.mod Normal file
View File

@ -0,0 +1,9 @@
module git.deuxfleurs.fr/Deuxfleurs/bagage
go 1.16
require (
github.com/go-ldap/ldap/v3 v3.4.1
github.com/minio/minio-go/v7 v7.0.12
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d
)

88
go.sum Normal file
View File

@ -0,0 +1,88 @@
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap/v3 v3.4.1 h1:fU/0xli6HY02ocbMuozHAYsaHLcnkLjvho2r5a34BUU=
github.com/go-ldap/ldap/v3 v3.4.1/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s=
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4=
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
github.com/minio/minio-go/v7 v7.0.12 h1:/4pxUdwn9w0QEryNkrrWaodIESPRX+NxpO0Q6hVdaAA=
github.com/minio/minio-go/v7 v7.0.12/go.mod h1:S23iSP5/gbMwtxeY5FM71R+TkAYyzEdoNEDDwpt8yWs=
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f h1:aZp0e2vLN4MToVqnjNEYEtrEA8RH8U8FN1CU7JgqsPU=
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

365
main.go Normal file
View File

@ -0,0 +1,365 @@
package main
import (
"context"
"log"
"fmt"
"os"
"io/fs"
"time"
"errors"
"net/http"
"strings"
"path"
"golang.org/x/net/webdav"
"github.com/go-ldap/ldap/v3"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
)
type bagageCtxKey string
const garageEntry = bagageCtxKey("garage")
type garageCtx struct {
MC *minio.Client
StatCache map[string]*GarageStat
}
func main() {
pathPrefix := "/webdav"
UserBaseDN := "ou=users,dc=deuxfleurs,dc=fr"
UserNameAttr := "cn"
Endpoint := "garage.deuxfleurs.fr"
UseSSL := true
srv := &webdav.Handler{
Prefix: pathPrefix,
FileSystem: NewGarageFS(),
LockSystem: webdav.NewMemLS(),
Logger: func(r *http.Request, err error) {
log.Printf("WEBDAV: %#s, ERROR: %v", r, err)
},
}
//http.Handle("/", srv)
http.HandleFunc(pathPrefix + "/", func(w http.ResponseWriter, r *http.Request) {
username, password, ok := r.BasicAuth()
if !ok {
NotAuthorized(w,r)
return
}
ldapSock, err := ldap.Dial("tcp", "127.0.0.1:1389")
if err != nil {
log.Println(err)
InternalError(w,r)
return
}
defer ldapSock.Close()
// Check credential
userDn := fmt.Sprintf("%s=%s,%s", UserNameAttr, username, UserBaseDN)
err = ldapSock.Bind(userDn, password)
if err != nil {
log.Println(err)
NotAuthorized(w,r)
return
}
// Get S3 creds garage_s3_access_key garage_s3_secret_key
searchRequest := ldap.NewSearchRequest(
userDn,
ldap.ScopeBaseObject,
ldap.NeverDerefAliases,
0,
0,
false,
"(objectClass=*)",
[]string{"garage_s3_access_key", "garage_s3_secret_key"},
nil)
sr, err := ldapSock.Search(searchRequest)
if err != nil {
log.Println(err)
InternalError(w,r)
return
}
if len(sr.Entries) != 1 {
log.Println("Wrong number of LDAP entries, expected 1, got", len(sr.Entries))
InternalError(w,r)
return
}
access_key := sr.Entries[0].GetAttributeValue("garage_s3_access_key")
secret_key := sr.Entries[0].GetAttributeValue("garage_s3_secret_key")
if access_key == "" || secret_key == "" {
log.Println("Either access key or secret key is missing in LDAP for ", userDn)
InternalError(w,r)
return
}
mc, err := minio.New(Endpoint, &minio.Options{
Creds: credentials.NewStaticV4(access_key, secret_key, ""),
Secure: UseSSL,
})
if err != nil {
log.Println(err)
InternalError(w,r)
return
}
nctx := context.WithValue(r.Context(), garageEntry, garageCtx{MC: mc, StatCache: make(map[string]*GarageStat)})
srv.ServeHTTP(w, r.WithContext(nctx))
return
})
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatalf("Error with WebDAV server: %v", err)
}
}
func NotAuthorized(w http.ResponseWriter, r *http.Request) {
w.Header().Set("WWW-Authenticate", `Basic realm="Pour accéder à Bagage, veuillez entrer vos identifiants Deuxfleurs"`)
w.WriteHeader(401)
w.Write([]byte("401 Unauthorized\n"))
}
func InternalError(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(500)
w.Write([]byte("500 Internal Server Error\n"))
}
/*
/////// Select Action
If no slash or one trailing slash
return ListBuckets
Else
obj := ListObjects
If obj.Length == 1
return GetObject
Else
return obj
*/
type GarageFS struct {}
func NewGarageFS() *GarageFS {
grg := new(GarageFS)
return grg
}
func (s *GarageFS) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
return errors.New("Not implemented Mkdir")
}
func (s *GarageFS) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
return NewGarageFile(ctx, name)
}
func (s *GarageFS) RemoveAll(ctx context.Context, name string) error {
return errors.New("Not implemented RemoveAll")
}
func (s *GarageFS) Rename(ctx context.Context, oldName, newName string) error {
return errors.New("Not implemented Rename")
}
func (s *GarageFS) Stat(ctx context.Context, name string) (os.FileInfo, error) {
return NewGarageStat(ctx, name)
}
type GarageFile struct {
ctx context.Context
mc *minio.Client
path string
}
func NewGarageFile(ctx context.Context, path string) (webdav.File, error) {
gf := new(GarageFile)
gf.ctx = ctx
gf.mc = ctx.Value(garageEntry).(garageCtx).MC
gf.path = path
return gf, nil
}
func (gf *GarageFile) Close() error {
return errors.New("not implemented Close")
}
func (gf *GarageFile) Read(p []byte) (n int, err error) {
return 0, errors.New("not implemented Read")
}
func (gf *GarageFile) Write(p []byte) (n int, err error) {
return 0, errors.New("not implemented Write")
}
func (gf *GarageFile) Seek(offset int64, whence int) (int64, error) {
return 0, errors.New("not implemented Seek")
}
/*
ReadDir reads the contents of the directory associated with the file f and returns a slice of DirEntry values in directory order. Subsequent calls on the same file will yield later DirEntry records in the directory.
If n > 0, ReadDir returns at most n DirEntry records. In this case, if ReadDir returns an empty slice, it will return an error explaining why. At the end of a directory, the error is io.EOF.
If n <= 0, ReadDir returns all the DirEntry records remaining in the directory. When it succeeds, it returns a nil error (not io.EOF).
*/
func (gf *GarageFile) Readdir(count int) ([]fs.FileInfo, error) {
log.Println("Call Readdir with count", count)
if gf.path == "/" {
return gf.readDirRoot(count)
} else {
exploded_path := strings.SplitN(gf.path, "/", 3)
return gf.readDirChild(count, exploded_path[1], exploded_path[2])
}
}
func (gf *GarageFile) readDirRoot(count int) ([]fs.FileInfo, error) {
buckets, err := gf.mc.ListBuckets(gf.ctx)
if err != nil {
return nil, err
}
entries := make([]fs.FileInfo, 0, len(buckets))
for _, bucket := range buckets {
ngf, err := NewGarageStat(gf.ctx, "/"+bucket.Name)
if err != nil {
return nil, err
}
entries = append(entries, ngf)
}
return entries, nil
}
func (gf *GarageFile) readDirChild(count int, bucket, prefix string) ([]fs.FileInfo, error) {
log.Println("call ListObjects with", bucket, prefix)
objs_info := gf.mc.ListObjects(gf.ctx, bucket, minio.ListObjectsOptions{
Prefix: prefix,
Recursive: false,
})
entries := make([]fs.FileInfo,0)
for object := range objs_info {
if object.Err != nil {
return nil, object.Err
}
ngf, err := NewGarageStatFromObjectInfo(gf.ctx, bucket, object)
if err != nil {
return nil, err
}
entries = append(entries, ngf)
}
return entries, nil
}
func (gf *GarageFile) Stat() (fs.FileInfo, error) {
return NewGarageStat(gf.ctx, gf.path)
}
/* Implements */
// StatObject???
type GarageStat struct {
obj minio.ObjectInfo
bucket string
}
func NewGarageStat(ctx context.Context, path string) (*GarageStat, error) {
cache := ctx.Value(garageEntry).(garageCtx).StatCache
if entry, ok := cache[path]; ok {
return entry, nil
}
gs, err := newGarageStatFresh(ctx, path)
if err != nil {
return nil, err
}
cache[path] = gs
return gs, nil
}
func newGarageStatFresh(ctx context.Context, path string) (*GarageStat, error) {
mc := ctx.Value(garageEntry).(garageCtx).MC
gs := new(GarageStat)
gs.bucket = "/"
gs.obj = minio.ObjectInfo{}
exploded_path := strings.SplitN(path, "/", 3)
// Check if we can extract the bucket name
if len(exploded_path) < 2 {
return gs, nil
}
gs.bucket = exploded_path[1]
// Check if we can extract the prefix
if len(exploded_path) < 3 || exploded_path[2] == "" {
return gs, nil
}
gs.obj.Key = exploded_path[2]
// Check if this is a file or a folder
log.Println("call StatObject with", gs.bucket, gs.obj.Key)
obj, err := mc.StatObject(ctx, gs.bucket, gs.obj.Key, minio.StatObjectOptions{})
if e, ok := err.(minio.ErrorResponse); ok && e.Code == "NoSuchKey" {
return gs, nil
}
if err != nil {
return nil, err
}
// If it is a file, assign its data
gs.obj = obj
return gs, nil
}
func NewGarageStatFromObjectInfo(ctx context.Context, bucket string, obj minio.ObjectInfo) (*GarageStat, error) {
gs := new(GarageStat)
gs.bucket = bucket
gs.obj = obj
cache := ctx.Value(garageEntry).(garageCtx).StatCache
cache[path.Join("/", bucket, obj.Key)] = gs
return gs, nil
}
func (gs *GarageStat) Name() string {
if gs.obj.Key != "" {
return path.Base(gs.obj.Key)
} else {
return gs.bucket
}
}
func (gs *GarageStat) Size() int64 {
return gs.obj.Size
}
func (gs *GarageStat) Mode() fs.FileMode {
if gs.obj.ETag == "" {
return fs.ModeDir | fs.ModePerm
} else {
return fs.ModePerm
}
}
func (gs *GarageStat) ModTime() time.Time {
return gs.obj.LastModified
}
func (gs *GarageStat) IsDir() bool {
return gs.Mode().IsDir()
}
func (gs *GarageStat) Sys() interface{} {
return nil
}