aboutsummaryrefslogtreecommitdiff
path: root/weed/mount/weedfs_xattr.go
diff options
context:
space:
mode:
Diffstat (limited to 'weed/mount/weedfs_xattr.go')
-rw-r--r--weed/mount/weedfs_xattr.go173
1 files changed, 173 insertions, 0 deletions
diff --git a/weed/mount/weedfs_xattr.go b/weed/mount/weedfs_xattr.go
new file mode 100644
index 000000000..c85a1b3a1
--- /dev/null
+++ b/weed/mount/weedfs_xattr.go
@@ -0,0 +1,173 @@
+package mount
+
+import (
+ "github.com/hanwen/go-fuse/v2/fuse"
+ sys "golang.org/x/sys/unix"
+ "runtime"
+ "strings"
+ "syscall"
+)
+
+const (
+ // https://man7.org/linux/man-pages/man7/xattr.7.html#:~:text=The%20VFS%20imposes%20limitations%20that,in%20listxattr(2)).
+ MAX_XATTR_NAME_SIZE = 255
+ MAX_XATTR_VALUE_SIZE = 65536
+ XATTR_PREFIX = "xattr-" // same as filer
+)
+
+// GetXAttr reads an extended attribute, and should return the
+// number of bytes. If the buffer is too small, return ERANGE,
+// with the required buffer size.
+func (wfs *WFS) GetXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string, dest []byte) (size uint32, code fuse.Status) {
+
+ //validate attr name
+ if len(attr) > MAX_XATTR_NAME_SIZE {
+ if runtime.GOOS == "darwin" {
+ return 0, fuse.EPERM
+ } else {
+ return 0, fuse.ERANGE
+ }
+ }
+ if len(attr) == 0 {
+ return 0, fuse.EINVAL
+ }
+
+ _, _, entry, status := wfs.maybeReadEntry(header.NodeId)
+ if status != fuse.OK {
+ return 0, status
+ }
+ if entry == nil {
+ return 0, fuse.ENOENT
+ }
+ if entry.Extended == nil {
+ return 0, fuse.ENOATTR
+ }
+ data, found := entry.Extended[XATTR_PREFIX+attr]
+ if !found {
+ return 0, fuse.ENOATTR
+ }
+ if len(dest) < len(data) {
+ return uint32(len(data)), fuse.ERANGE
+ }
+ copy(dest, data)
+
+ return uint32(len(data)), fuse.OK
+}
+
+// SetXAttr writes an extended attribute.
+// https://man7.org/linux/man-pages/man2/setxattr.2.html
+// By default (i.e., flags is zero), the extended attribute will be
+// created if it does not exist, or the value will be replaced if
+// the attribute already exists. To modify these semantics, one of
+// the following values can be specified in flags:
+//
+// XATTR_CREATE
+// Perform a pure create, which fails if the named attribute
+// exists already.
+//
+// XATTR_REPLACE
+// Perform a pure replace operation, which fails if the named
+// attribute does not already exist.
+func (wfs *WFS) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, attr string, data []byte) fuse.Status {
+
+ if wfs.IsOverQuota {
+ return fuse.Status(syscall.ENOSPC)
+ }
+
+ //validate attr name
+ if len(attr) > MAX_XATTR_NAME_SIZE {
+ if runtime.GOOS == "darwin" {
+ return fuse.EPERM
+ } else {
+ return fuse.ERANGE
+ }
+ }
+ if len(attr) == 0 {
+ return fuse.EINVAL
+ }
+ //validate attr value
+ if len(data) > MAX_XATTR_VALUE_SIZE {
+ if runtime.GOOS == "darwin" {
+ return fuse.Status(syscall.E2BIG)
+ } else {
+ return fuse.ERANGE
+ }
+ }
+
+ path, _, entry, status := wfs.maybeReadEntry(input.NodeId)
+ if status != fuse.OK {
+ return status
+ }
+ if entry.Extended == nil {
+ entry.Extended = make(map[string][]byte)
+ }
+ oldData, _ := entry.Extended[XATTR_PREFIX+attr]
+ switch input.Flags {
+ case sys.XATTR_CREATE:
+ if len(oldData) > 0 {
+ break
+ }
+ fallthrough
+ case sys.XATTR_REPLACE:
+ fallthrough
+ default:
+ entry.Extended[XATTR_PREFIX+attr] = data
+ }
+
+ return wfs.saveEntry(path, entry)
+
+}
+
+// ListXAttr lists extended attributes as '\0' delimited byte
+// slice, and return the number of bytes. If the buffer is too
+// small, return ERANGE, with the required buffer size.
+func (wfs *WFS) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader, dest []byte) (n uint32, code fuse.Status) {
+ _, _, entry, status := wfs.maybeReadEntry(header.NodeId)
+ if status != fuse.OK {
+ return 0, status
+ }
+ if entry == nil {
+ return 0, fuse.ENOENT
+ }
+ if entry.Extended == nil {
+ return 0, fuse.OK
+ }
+
+ var data []byte
+ for k := range entry.Extended {
+ if strings.HasPrefix(k, XATTR_PREFIX) {
+ data = append(data, k[len(XATTR_PREFIX):]...)
+ data = append(data, 0)
+ }
+ }
+ if len(dest) < len(data) {
+ return uint32(len(data)), fuse.ERANGE
+ }
+
+ copy(dest, data)
+
+ return uint32(len(data)), fuse.OK
+}
+
+// RemoveXAttr removes an extended attribute.
+func (wfs *WFS) RemoveXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string) fuse.Status {
+ if len(attr) == 0 {
+ return fuse.EINVAL
+ }
+ path, _, entry, status := wfs.maybeReadEntry(header.NodeId)
+ if status != fuse.OK {
+ return status
+ }
+ if entry.Extended == nil {
+ return fuse.ENOATTR
+ }
+ _, found := entry.Extended[XATTR_PREFIX+attr]
+
+ if !found {
+ return fuse.ENOATTR
+ }
+
+ delete(entry.Extended, XATTR_PREFIX+attr)
+
+ return wfs.saveEntry(path, entry)
+}