Skip to content

Commit

Permalink
backend/fs: fallback to file-based metadata when xattr isn't available
Browse files Browse the repository at this point in the history
Related to #894.
  • Loading branch information
fsouza committed Aug 18, 2022
1 parent 05bb573 commit c8087ad
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 72 deletions.
24 changes: 13 additions & 11 deletions internal/backend/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
type storageFS struct {
rootDir string
mtx sync.RWMutex
mh metadataHandler
}

// NewStorageFS creates an instance of the filesystem-backed storage backend.
Expand All @@ -47,17 +48,18 @@ func NewStorageFS(objects []Object, rootDir string) (Storage, error) {
return nil, err
}

// Check if rootDir supports xattr.
var mh metadataHandler = metadataFile{}
// Use xattr for metadata if rootDir supports it.
if xattr.XATTR_SUPPORTED {
_, err = readXattr(rootDir)
if err != nil {
if xerr, ok := err.(*xattr.Error); !ok || xerr.Err != xattr.ENOATTR {
return nil, fmt.Errorf("failed to determine if %q supports xattr, consider using a different storage backend or a filesystem that supports it (e.g. not tmpfs on Linux): %w", rootDir, err)
}
xattrHandler := metadataXattr{}
var xerr *xattr.Error
_, err = xattrHandler.read(rootDir)
if err == nil || (errors.As(err, &xerr) && xerr.Err == xattr.ENOATTR) {
mh = xattrHandler
}
}

s := &storageFS{rootDir: rootDir}
s := &storageFS{rootDir: rootDir, mh: mh}
for _, o := range objects {
_, err := s.CreateObject(o)
if err != nil {
Expand Down Expand Up @@ -165,7 +167,7 @@ func (s *storageFS) CreateObject(obj Object) (Object, error) {
return Object{}, err
}

if err = writeXattr(path, encoded); err != nil {
if err = s.mh.write(path, encoded); err != nil {
return Object{}, err
}

Expand All @@ -184,7 +186,7 @@ func (s *storageFS) ListObjects(bucketName string, prefix string, versions bool)
}
objects := []ObjectAttrs{}
for _, info := range infos {
if isXattrFile(info.Name()) {
if s.mh.isSpecialFile(info.Name()) {
continue
}
unescaped, err := url.PathUnescape(info.Name())
Expand Down Expand Up @@ -227,7 +229,7 @@ func (s *storageFS) GetObjectWithGeneration(bucketName, objectName string, gener
func (s *storageFS) getObject(bucketName, objectName string) (Object, error) {
path := filepath.Join(s.rootDir, url.PathEscape(bucketName), url.PathEscape(objectName))

encoded, err := readXattr(path)
encoded, err := s.mh.read(path)
if err != nil {
return Object{}, err
}
Expand Down Expand Up @@ -256,7 +258,7 @@ func (s *storageFS) DeleteObject(bucketName, objectName string) error {
return errors.New("can't delete object with empty name")
}
path := filepath.Join(s.rootDir, url.PathEscape(bucketName), url.PathEscape(objectName))
if err := removeXattrFile(path); err != nil {
if err := s.mh.remove(path); err != nil {
return err
}
return os.Remove(path)
Expand Down
12 changes: 12 additions & 0 deletions internal/backend/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright 2022 Francisco Souza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package backend

type metadataHandler interface {
write(path string, encoded []byte) error
read(path string) ([]byte, error)
remove(path string) error
isSpecialFile(path string) bool
}
30 changes: 30 additions & 0 deletions internal/backend/metadata_file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2022 Francisco Souza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package backend

import (
"os"
"strings"
)

const metadataSuffix = ".metadata"

type metadataFile struct{}

func (m metadataFile) write(path string, encoded []byte) error {
return os.WriteFile(path+metadataSuffix, encoded, 0o600)
}

func (m metadataFile) read(path string) ([]byte, error) {
return os.ReadFile(path + metadataSuffix)
}

func (m metadataFile) isSpecialFile(path string) bool {
return strings.HasSuffix(path, metadataSuffix)
}

func (m metadataFile) remove(path string) error {
return os.Remove(path + metadataSuffix)
}
29 changes: 29 additions & 0 deletions internal/backend/metadata_xattr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2022 Francisco Souza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package backend

import (
"github.com/pkg/xattr"
)

const xattrKey = "user.metadata"

type metadataXattr struct{}

func (m metadataXattr) write(path string, encoded []byte) error {
return xattr.Set(path, xattrKey, encoded)
}

func (m metadataXattr) read(path string) ([]byte, error) {
return xattr.Get(path, xattrKey)
}

func (m metadataXattr) isSpecialFile(path string) bool {
return false
}

func (m metadataXattr) remove(path string) error {
return nil
}
30 changes: 0 additions & 30 deletions internal/backend/xattr_unix.go

This file was deleted.

31 changes: 0 additions & 31 deletions internal/backend/xattr_windows.go

This file was deleted.

0 comments on commit c8087ad

Please sign in to comment.