Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/acl: add acl support #4443

Merged
merged 22 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ require (

replace github.com/minio/minio v0.0.0-20210206053228-97fe57bba92c => github.com/juicedata/minio v0.0.0-20231213085529-c243663574ba

replace github.com/hanwen/go-fuse/v2 v2.1.1-0.20210611132105-24a1dfe6b4f8 => github.com/juicedata/go-fuse/v2 v2.1.1-0.20230726081302-124dbfa991d7
replace github.com/hanwen/go-fuse/v2 v2.1.1-0.20210611132105-24a1dfe6b4f8 => github.com/juicedata/go-fuse/v2 v2.1.1-0.20240202080323-002ef792942e

replace github.com/dgrijalva/jwt-go v3.2.0+incompatible => github.com/golang-jwt/jwt v3.2.1+incompatible

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -622,8 +622,8 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/juicedata/cli/v2 v2.19.4-0.20230605075551-9c9c5c0dce83 h1:RyHTka3jCnTaUqfRYjlwcQlr53aasmkvHEbYLXthqr8=
github.com/juicedata/cli/v2 v2.19.4-0.20230605075551-9c9c5c0dce83/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
github.com/juicedata/go-fuse/v2 v2.1.1-0.20230726081302-124dbfa991d7 h1:4evzoVz1/AZfk9tqxWdzVYTMl2dC7VjEJHfaSFDrKS8=
github.com/juicedata/go-fuse/v2 v2.1.1-0.20230726081302-124dbfa991d7/go.mod h1:B1nGE/6RBFyBRC1RRnf23UpwCdyJ31eukw34oAKukAc=
github.com/juicedata/go-fuse/v2 v2.1.1-0.20240202080323-002ef792942e h1:Oj9V2Losi2fVgVjQ4my0zDbw4F+Ry3VOnfTRCiWwy7Q=
github.com/juicedata/go-fuse/v2 v2.1.1-0.20240202080323-002ef792942e/go.mod h1:B1nGE/6RBFyBRC1RRnf23UpwCdyJ31eukw34oAKukAc=
github.com/juicedata/go-nfs-client v0.0.0-20231018052507-dbca444fa7e8 h1:mVVipCbohnzKZPiHGzFncLKEJZpypqjpGr4End2PP48=
github.com/juicedata/go-nfs-client v0.0.0-20231018052507-dbca444fa7e8/go.mod h1:xOMqi3lOrcGe9uZLnSzgaq94Vc3oz6VPCNDLJUnXpKs=
github.com/juicedata/godaemon v0.0.0-20210629045518-3da5144a127d h1:kpQMvNZJKGY3PTt7OSoahYc4nM0HY67SvK0YyS0GLwA=
Expand Down
14 changes: 5 additions & 9 deletions pkg/acl/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type Cache interface {
Get(id uint32) *Rule
GetId(r *Rule) uint32
Size() int
GetMissIds(maxId uint32) []uint32
GetMissIds() []uint32
}

func NewCache() Cache {
Expand All @@ -52,20 +52,16 @@ type cache struct {
cksum2Id map[uint32][]uint32
}

// GetMissIds return all miss ids from 1 to max(maxId, c.maxId)
func (c *cache) GetMissIds(maxId uint32) []uint32 {
// GetMissIds return all miss ids from 1 to c.maxId
func (c *cache) GetMissIds() []uint32 {
c.lock.RLock()
defer c.lock.RUnlock()

if c.maxId == maxId && uint32(len(c.id2Rule)) == maxId {
if uint32(len(c.id2Rule)) == c.maxId {
return nil
}

if c.maxId > maxId {
maxId = c.maxId
}

n := maxId + 1
n := c.maxId + 1
mark := make([]bool, n)
for i := uint32(1); i < n; i++ {
if _, ok := c.id2Rule[i]; ok {
Expand Down
3 changes: 1 addition & 2 deletions pkg/acl/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ func TestCache(t *testing.T) {
assert.Equal(t, uint32(3), c.GetId(rule2))

c.Put(8, rule2)
assert.Equal(t, []uint32{4, 5, 6, 7, 9, 10}, c.GetMissIds(10))
assert.Equal(t, []uint32{4, 5, 6, 7}, c.GetMissIds(6))
assert.Equal(t, []uint32{4, 5, 6, 7}, c.GetMissIds())

assert.NotPanics(t, func() {
c.Put(10, nil)
Expand Down
3 changes: 2 additions & 1 deletion pkg/fuse/fuse.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ func (fs *fileSystem) RemoveXAttr(cancel <-chan struct{}, header *fuse.InHeader,
func (fs *fileSystem) Create(cancel <-chan struct{}, in *fuse.CreateIn, name string, out *fuse.CreateOut) (code fuse.Status) {
ctx := fs.newContext(cancel, &in.InHeader)
defer releaseContext(ctx)
entry, fh, err := fs.v.Create(ctx, Ino(in.NodeId), name, uint16(in.Mode), 0, in.Flags)
entry, fh, err := fs.v.Create(ctx, Ino(in.NodeId), name, uint16(in.Mode), getCreateUmask(in), in.Flags)
if err != 0 {
return fuse.Status(err)
}
Expand Down Expand Up @@ -447,6 +447,7 @@ func Serve(v *vfs.VFS, options string, xattrs, ioctl bool) error {
opt.MaxBackground = 50
opt.EnableLocks = true
opt.EnableAcl = conf.Format.EnableACL
opt.DontUmask = conf.Format.EnableACL
opt.DisableXAttrs = !xattrs
opt.EnableIoctl = ioctl
opt.MaxWrite = 1 << 20
Expand Down
4 changes: 4 additions & 0 deletions pkg/fuse/fuse_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,9 @@ func getUmask(in *fuse.MknodIn) uint16 {
return 0
}

func getCreateUmask(in *fuse.CreateIn) uint16 {
return 0
}

func setBlksize(out *fuse.Attr, size uint32) {
}
4 changes: 4 additions & 0 deletions pkg/fuse/fuse_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ import (
"github.com/hanwen/go-fuse/v2/fuse"
)

func getCreateUmask(in *fuse.CreateIn) uint16 {
return uint16(in.Umask)
}

func getUmask(in *fuse.MknodIn) uint16 {
return uint16(in.Umask)
}
Expand Down
103 changes: 99 additions & 4 deletions pkg/meta/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"time"

"github.com/dustin/go-humanize"
aclAPI "github.com/juicedata/juicefs/pkg/acl"
"github.com/juicedata/juicefs/pkg/utils"
"github.com/juicedata/juicefs/pkg/version"
"github.com/pkg/errors"
Expand Down Expand Up @@ -112,6 +113,9 @@ type engine interface {
scanPendingFiles(Context, pendingFileScan) error

GetSession(sid uint64, detail bool) (*Session, error)

doSetFacl(ctx Context, ino Ino, aclType uint8, n *aclAPI.Rule) syscall.Errno
doGetFacl(ctx Context, ino Ino, aclType uint8, n *aclAPI.Rule) syscall.Errno
}

type trashSliceScan func(ss []Slice, ts int64) (clean bool, err error)
Expand Down Expand Up @@ -162,6 +166,7 @@ type baseMeta struct {
reloadCb []func(*Format)
umounting bool
sesMu sync.Mutex
aclCache aclAPI.Cache

dirStatsLock sync.Mutex
dirStats map[Ino]dirStat
Expand Down Expand Up @@ -202,6 +207,7 @@ func newBaseMeta(addr string, conf *Config) *baseMeta {
msgCallbacks: &msgCallbacks{
callbacks: make(map[uint32]MsgCallback),
},
aclCache: aclAPI.NewCache(),

usedSpaceG: prometheus.NewGauge(prometheus.GaugeOpts{
Name: "used_space",
Expand Down Expand Up @@ -1164,11 +1170,19 @@ func (m *baseMeta) parseAttr(buf []byte, attr *Attr) {
attr.Parent = Ino(rb.Get64())
}
attr.Full = true
if rb.Left() >= 8 {
attr.AccessACL = rb.Get32()
attr.DefaultACL = rb.Get32()
}
logger.Tracef("attr: %+v -> %+v", buf, attr)
}

func (m *baseMeta) marshal(attr *Attr) []byte {
w := utils.NewBuffer(36 + 24 + 4 + 8)
size := uint32(36 + 24 + 4 + 8)
if attr.AccessACL|attr.DefaultACL != aclAPI.None {
size += 8
}
w := utils.NewBuffer(size)
w.Put8(attr.Flags)
w.Put16((uint16(attr.Typ) << 12) | (attr.Mode & 0xfff))
w.Put32(attr.Uid)
Expand All @@ -1183,6 +1197,10 @@ func (m *baseMeta) marshal(attr *Attr) []byte {
w.Put64(attr.Length)
w.Put32(attr.Rdev)
w.Put64(uint64(attr.Parent))
if attr.AccessACL+attr.DefaultACL > 0 {
w.Put32(attr.AccessACL)
w.Put32(attr.DefaultACL)
}
logger.Tracef("attr: %+v -> %+v", attr, w.Bytes())
return w.Bytes()
}
Expand Down Expand Up @@ -2696,7 +2714,7 @@ LOOP:
return eno
}

func (m *baseMeta) mergeAttr(ctx Context, inode Ino, set uint16, cur, attr *Attr, now time.Time) (*Attr, syscall.Errno) {
func (m *baseMeta) mergeAttr(ctx Context, inode Ino, set uint16, cur, attr *Attr, now time.Time, rule *aclAPI.Rule) (*Attr, syscall.Errno) {
dirtyAttr := *cur
if (set&(SetAttrUID|SetAttrGID)) != 0 && (set&SetAttrMode) != 0 {
attr.Mode |= (cur.Mode & 06000)
Expand Down Expand Up @@ -2731,7 +2749,12 @@ func (m *baseMeta) mergeAttr(ctx Context, inode Ino, set uint16, cur, attr *Attr
attr.Mode &= 05777
}
}
if attr.Mode != cur.Mode {

if rule != nil {
rule.SetMode(attr.Mode)
dirtyAttr.Mode = attr.Mode&07000 | rule.GetMode()
changed = true
} else if attr.Mode != cur.Mode {
if ctx.Uid() != 0 && ctx.Uid() != cur.Uid &&
(cur.Mode&01777 != attr.Mode&01777 || attr.Mode&02000 > cur.Mode&02000 || attr.Mode&04000 > cur.Mode&04000) {
return nil, syscall.EPERM
Expand Down Expand Up @@ -2793,6 +2816,78 @@ func (m *baseMeta) CheckSetAttr(ctx Context, inode Ino, set uint16, attr Attr) s
if st := m.en.doGetAttr(ctx, inode, &cur); st != 0 {
return st
}
_, st := m.mergeAttr(ctx, inode, set, &cur, &attr, time.Now())
_, st := m.mergeAttr(ctx, inode, set, &cur, &attr, time.Now(), nil)
return st
}

const aclCounter = "acl"
jiefenghuang marked this conversation as resolved.
Show resolved Hide resolved

var errACLNotInCache = errors.New("acl not in cache")

func (m *baseMeta) getFaclFromCache(ctx Context, ino Ino, aclType uint8, rule *aclAPI.Rule) error {
ino = m.checkRoot(ino)
cAttr := &Attr{}
if m.conf.OpenCache > 0 && m.of.Check(ino, cAttr) {
aclId := getAttrACLId(cAttr, aclType)
if aclId == aclAPI.None {
return ENOATTR
}

if cRule := m.aclCache.Get(aclId); cRule != nil {
*rule = *cRule
return nil
}
}
return errACLNotInCache
}

func setAttrACLId(attr *Attr, aclType uint8, id uint32) {
switch aclType {
case aclAPI.TypeAccess:
attr.AccessACL = id
case aclAPI.TypeDefault:
attr.DefaultACL = id
}
}

func getAttrACLId(attr *Attr, aclType uint8) uint32 {
switch aclType {
case aclAPI.TypeAccess:
return attr.AccessACL
case aclAPI.TypeDefault:
return attr.DefaultACL
}
return aclAPI.None
}

func (m *baseMeta) SetFacl(ctx Context, ino Ino, aclType uint8, rule *aclAPI.Rule) syscall.Errno {
if aclType != aclAPI.TypeAccess && aclType != aclAPI.TypeDefault {
return syscall.EINVAL
}

if !ino.IsNormal() {
return syscall.EPERM
}

defer func() {
m.timeit("SetFacl", time.Now())
jiefenghuang marked this conversation as resolved.
Show resolved Hide resolved
m.of.InvalidateChunk(ino, invalidateAttrOnly)
}()

return m.en.doSetFacl(ctx, ino, aclType, rule)
}

func (m *baseMeta) GetFacl(ctx Context, ino Ino, aclType uint8, rule *aclAPI.Rule) syscall.Errno {
var err error
if err = m.getFaclFromCache(ctx, ino, aclType, rule); err == nil {
return 0
}

if !errors.Is(err, errACLNotInCache) {
return errno(err)
}

defer m.timeit("GetFacl", time.Now())

return m.en.doGetFacl(ctx, ino, aclType, rule)
}
Loading
Loading