From 1e2412e50214cf37b8b5e689784c3a36f9f2eaf9 Mon Sep 17 00:00:00 2001 From: shiftraodd <1572197779@qq.com> Date: Tue, 23 Jun 2026 16:31:14 +0800 Subject: [PATCH] fix: enforce XATTR_REPLACE semantics in setxattr (#10059) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 修复weedfs_xattr.go 中 XATTR_REPLACE 语义缺失 * mount: fix XATTR_CREATE/XATTR_REPLACE flag semantics in setxattr XATTR_CREATE fell through into the XATTR_REPLACE branch: creating a new attribute hit the empty-oldData guard and returned ENODATA instead of creating it, while creating over an existing attribute silently succeeded without the EEXIST that setxattr(2) requires. Drop the fallthrough chain so CREATE returns EEXIST when the attribute already exists, REPLACE returns ENODATA when it is missing, and otherwise the value is written. Test existence via the map lookup so an attribute with an empty value is still treated as present. --------- Co-authored-by: 王郁文 Co-authored-by: Chris Lu --- weed/mount/weedfs_xattr.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/weed/mount/weedfs_xattr.go b/weed/mount/weedfs_xattr.go index e85b9d854..8ae6eb6d5 100644 --- a/weed/mount/weedfs_xattr.go +++ b/weed/mount/weedfs_xattr.go @@ -121,22 +121,23 @@ func (wfs *WFS) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, attr st if entry.Extended == nil { entry.Extended = make(map[string][]byte) } - oldData, _ := entry.Extended[XATTR_PREFIX+attr] + _, exists := entry.Extended[XATTR_PREFIX+attr] switch input.Flags { case sys.XATTR_CREATE: - if len(oldData) > 0 { - break + if exists { + return fuse.Status(syscall.EEXIST) } - fallthrough case sys.XATTR_REPLACE: - fallthrough - default: - // data aliases the FUSE request's pooled input buffer, which is - // recycled once this handler returns. Copy before storing so a - // later request reusing the buffer cannot corrupt the value. - entry.Extended[XATTR_PREFIX+attr] = append([]byte(nil), data...) + if !exists { + return fuse.ENODATA + } } + // data aliases the FUSE request's pooled input buffer, which is + // recycled once this handler returns. Copy before storing so a + // later request reusing the buffer cannot corrupt the value. + entry.Extended[XATTR_PREFIX+attr] = append([]byte(nil), data...) + if fh != nil { fh.dirtyMetadata = true return fuse.OK