Skip to content

Commit

Permalink
feat/acl: add acl support (juicedata#4443)
Browse files Browse the repository at this point in the history
Signed-off-by: jiefeng <[email protected]>
  • Loading branch information
jiefenghuang authored Mar 12, 2024
1 parent 45cdb23 commit 61598c3
Show file tree
Hide file tree
Showing 13 changed files with 1,327 additions and 82 deletions.
32 changes: 15 additions & 17 deletions pkg/acl/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ type Cache interface {
Get(id uint32) *Rule
GetId(r *Rule) uint32
Size() int
GetMissIds(maxId uint32) []uint32
GetMissIds() []uint32
Clear()
}

func NewCache() Cache {
Expand All @@ -52,30 +53,27 @@ 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 {
func (c *cache) Clear() {
c.lock.Lock()
defer c.lock.Unlock()
c.maxId = None
c.id2Rule = make(map[uint32]*Rule)
c.cksum2Id = make(map[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
mark := make([]bool, n)
for i := uint32(1); i < n; i++ {
if _, ok := c.id2Rule[i]; ok {
mark[i] = true
}
}

n := c.maxId + 1
var ret []uint32
for i := uint32(1); i < n; i++ {
if !mark[i] {
if _, ok := c.id2Rule[i]; !ok {
ret = append(ret, i)
}
}
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 @@ -225,7 +225,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 @@ -450,6 +450,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
122 changes: 118 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,10 @@ 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
cacheACLs(ctx Context) error
}

type trashSliceScan func(ss []Slice, ts int64) (clean bool, err error)
Expand Down Expand Up @@ -162,6 +167,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 @@ -203,6 +209,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 @@ -478,6 +485,13 @@ func (m *baseMeta) newSessionInfo() []byte {

func (m *baseMeta) NewSession(record bool) error {
go m.refresh()

if m.getFormat().EnableACL {
if err := m.en.cacheACLs(Background); err != nil {
return err
}
}

if m.conf.ReadOnly {
logger.Infof("Create read-only session OK with version: %s", version.Version())
return nil
Expand Down Expand Up @@ -1171,11 +1185,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 @@ -1190,6 +1212,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 @@ -2703,7 +2729,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 @@ -2738,7 +2764,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 @@ -2800,6 +2831,89 @@ 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
}

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 setXAttrACL(xattrs *[]byte, accessACL, defaultACL uint32) {
if accessACL != aclAPI.None {
*xattrs = append(*xattrs, []byte("system.posix_acl_access")...)
*xattrs = append(*xattrs, 0)
}
if defaultACL != aclAPI.None {
*xattrs = append(*xattrs, []byte("system.posix_acl_default")...)
*xattrs = append(*xattrs, 0)
}
}

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
}

now := time.Now()
defer func() {
m.timeit("SetFacl", now)
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)
}

now := time.Now()
defer m.timeit("GetFacl", now)

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

0 comments on commit 61598c3

Please sign in to comment.