Skip to content

Commit

Permalink
refactor and add test
Browse files Browse the repository at this point in the history
  • Loading branch information
zhijian-pro committed Sep 21, 2022
1 parent e9a5c6d commit 9cd1dcc
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 30 deletions.
39 changes: 9 additions & 30 deletions pkg/meta/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -1045,7 +1045,7 @@ func (m *baseMeta) GetPaths(ctx Context, inode Ino) []string {
}
return paths
}
func (m *baseMeta) reCountDirNlink(ctx Context, inode Ino) (uint32, syscall.Errno) {
func (m *baseMeta) countDirNlink(ctx Context, inode Ino) (uint32, syscall.Errno) {
var entries []*Entry
if st := m.en.doReaddir(ctx, inode, 0, &entries, -1); st != 0 {
logger.Errorf("readdir inode %d: %s", inode, st)
Expand Down Expand Up @@ -1100,8 +1100,8 @@ func (m *baseMeta) walk(ctx Context, inode Ino, path string, attr *Attr, walkFn

func (m *baseMeta) Check(ctx Context, fpath string, repair bool, recursive bool) (st syscall.Errno) {
var attr Attr
var inode Ino = 1
var parent Ino = 1
var inode = RootInode
var parent = RootInode
attr.Typ = TypeDirectory
ps := strings.FieldsFunc(fpath, func(r rune) bool {
return r == '/'
Expand All @@ -1122,33 +1122,12 @@ func (m *baseMeta) Check(ctx Context, fpath string, repair bool, recursive bool)
// missing attribute
p := "/" + path.Join(ps[:i+1]...)
if attr.Typ != TypeDirectory { // TODO: determine file size?
logger.Errorf("Path %s (inode %d type %d) cannot be auto-repaired, you have to repair it manually or remove it", p, inode, attr.Typ)
} else if !repair {
logger.Warnf("Path %s (inode %d) can be repaired, please re-check with 'repair' enabled", p, inode)
} else { // repair directory inode
now := time.Now().Unix()
attr.Mode = 0644
attr.Uid = ctx.Uid()
attr.Gid = ctx.Gid()
attr.Atime = now
attr.Mtime = now
attr.Ctime = now
attr.Length = 4 << 10
attr.Parent = parent
if attr.Nlink, st = m.reCountDirNlink(ctx, inode); st != 0 {
logger.Errorf("Recount nlink for inode %d: %s", inode, st)
return
}
if st = m.en.doRepair(ctx, inode, &attr); st == 0 {
logger.Infof("Path %s (inode %d) is successfully repaired", p, inode)
} else {
logger.Errorf("Repair path %s inode %d: %s", p, inode, st)
}
}
if !recursive {
return // handle one missing inode at a time
logger.Warnf("Path %s (inode %d type %d) attribute is missing and cannot be auto-repaired, you have to repair it manually or remove it", p, inode, attr.Typ)
} else {
logger.Warnf("Path %s (inode %d) attribute is missing but can be repaired, please re-run with '--path %s --repair' to fix it", p, inode, p)
}
}

if st = m.walk(ctx, inode, fpath, &attr, func(ctx Context, inode1 Ino, path string, attr *Attr, st1 syscall.Errno) syscall.Errno {
if st1 != 0 {
logger.Errorf("Walk path %s inode %d: %s", path, inode1, st1)
Expand All @@ -1162,9 +1141,9 @@ func (m *baseMeta) Check(ctx Context, fpath string, repair bool, recursive bool)
logger.Errorf("GetAttr inode %d: %s", inode1, st1)
return st1
}
nlink, st1 := m.reCountDirNlink(ctx, inode1)
nlink, st1 := m.countDirNlink(ctx, inode1)
if st1 != 0 {
logger.Errorf("Recount nlink for inode %d: %s", inode1, st1)
logger.Errorf("Count nlink for inode %d: %s", inode1, st1)
return st1
}
if attr.Full && attr.Nlink == nlink {
Expand Down
159 changes: 159 additions & 0 deletions pkg/meta/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ import (
"testing"
"time"

"github.com/go-redis/redis/v8"
"github.com/juicedata/juicefs/pkg/utils"
"xorm.io/xorm"
)

func TestRedisClient(t *testing.T) {
Expand Down Expand Up @@ -78,6 +80,8 @@ func testMeta(t *testing.T, m Meta) {
testCaseIncensi(t, m)
base.conf.ReadOnly = true
testReadOnly(t, m)
base.conf.ReadOnly = false
testCheckAndRepair(t, m)
}

func testMetaClient(t *testing.T, m Meta) {
Expand Down Expand Up @@ -1584,3 +1588,158 @@ func testAttrFlags(t *testing.T, m Meta) {
t.Fatalf("copy_file_range f: %s", st)
}
}

func setAttr(t *testing.T, m Meta, inode Ino, attr *Attr) {
var err error
switch m := m.(type) {
case *redisMeta:
err = m.txn(Background, func(tx *redis.Tx) error {
return tx.Set(Background, m.inodeKey(inode), m.marshal(attr), 0).Err()
}, m.inodeKey(inode))
case *dbMeta:
err = m.txn(func(s *xorm.Session) error {
_, err = s.ID(inode).AllCols().Update(&node{
Inode: inode,
Type: attr.Typ,
Flags: attr.Flags,
Mode: attr.Mode,
Uid: attr.Uid,
Gid: attr.Gid,
Mtime: attr.Mtime * 1e6,
Ctime: attr.Ctime * 1e6,
Atime: attr.Atime * 1e6,
Nlink: attr.Nlink,
Length: attr.Length,
Rdev: attr.Rdev,
Parent: attr.Parent,
})
return err
})
case *kvMeta:
err = m.txn(func(tx kvTxn) error {
tx.set(m.inodeKey(inode), m.marshal(attr))
return nil
})
}
if err != nil {
t.Fatalf("setAttr: %v", err)
}
}

func testCheckAndRepair(t *testing.T, m Meta) {
if err := m.NewSession(); err != nil {
t.Fatalf("new session: %s", err)
}
defer m.CloseSession()
var checkInode, d1Inode, d2Inode, d3Inode, d4Inode Ino
dirAttr := &Attr{Mode: 0644, Full: true, Typ: TypeDirectory, Nlink: 3}
//dirAttr.Parent = RootInode
if st := m.Mkdir(Background, RootInode, "check", 0640, 022, 0, &checkInode, dirAttr); st != 0 {
t.Fatalf("mkdir: %s", st)
}
dirAttr.Parent = checkInode
if st := m.Mkdir(Background, checkInode, "d1", 0640, 022, 0, &d1Inode, dirAttr); st != 0 {
t.Fatalf("mkdir: %s", st)
}
dirAttr.Parent = d1Inode
if st := m.Mkdir(Background, d1Inode, "d2", 0640, 022, 0, &d2Inode, dirAttr); st != 0 {
t.Fatalf("mkdir: %s", st)
}
dirAttr.Parent = d2Inode
if st := m.Mkdir(Background, d2Inode, "d3", 0640, 022, 0, &d3Inode, dirAttr); st != 0 {
t.Fatalf("mkdir: %s", st)
}
dirAttr.Parent = d3Inode
dirAttr.Nlink = 2
if st := m.Mkdir(Background, d3Inode, "d4", 0640, 022, 0, &d4Inode, dirAttr); st != 0 {
t.Fatalf("mkdir: %s", st)
}

if st := m.GetAttr(Background, checkInode, dirAttr); st != 0 {
t.Fatalf("getattr: %s", st)
}
dirAttr.Nlink = 0
setAttr(t, m, checkInode, dirAttr)

if st := m.GetAttr(Background, d1Inode, dirAttr); st != 0 {
t.Fatalf("getattr: %s", st)
}
dirAttr.Nlink = 0
setAttr(t, m, d1Inode, dirAttr)

if st := m.GetAttr(Background, d2Inode, dirAttr); st != 0 {
t.Fatalf("getattr: %s", st)
}
dirAttr.Nlink = 0
setAttr(t, m, d2Inode, dirAttr)

if st := m.GetAttr(Background, d3Inode, dirAttr); st != 0 {
t.Fatalf("getattr: %s", st)
}
dirAttr.Nlink = 0
setAttr(t, m, d3Inode, dirAttr)

if st := m.GetAttr(Background, d4Inode, dirAttr); st != 0 {
t.Fatalf("getattr: %s", st)
}
dirAttr.Full = false
dirAttr.Nlink = 0
setAttr(t, m, d4Inode, dirAttr)

if st := m.Check(Background, "/check", false, false); st != 0 {
t.Fatalf("check: %s", st)
}
if st := m.GetAttr(Background, checkInode, dirAttr); st != 0 {
t.Fatalf("getattr: %s", st)
}
if dirAttr.Nlink != 0 {
t.Fatalf("checkInode nlink should is 0 now: %d", dirAttr.Nlink)
}

if st := m.Check(Background, "/check", true, false); st != 0 {
t.Fatalf("check: %s", st)
}
if st := m.GetAttr(Background, checkInode, dirAttr); st != 0 {
t.Fatalf("getattr: %s", st)
}
if dirAttr.Nlink != 3 {
t.Fatalf("checkInode nlink should is 3 now: %d", dirAttr.Nlink)
}

if st := m.Check(Background, "/check/d1/d2", true, false); st != 0 {
t.Fatalf("check: %s", st)
}
if st := m.GetAttr(Background, d2Inode, dirAttr); st != 0 {
t.Fatalf("getattr: %s", st)
}
if dirAttr.Nlink != 3 {
t.Fatalf("d2Inode nlink should is 3 now: %d", dirAttr.Nlink)
}
if st := m.GetAttr(Background, d1Inode, dirAttr); st != 0 {
t.Fatalf("getattr: %s", st)
}
if dirAttr.Nlink != 0 {
t.Fatalf("d1Inode nlink should is now: %d", dirAttr.Nlink)
}

if st := m.Check(Background, "/", true, true); st != 0 {
t.Fatalf("check: %s", st)
}
for _, ino := range []Ino{checkInode, d1Inode, d2Inode, d3Inode} {
if st := m.GetAttr(Background, ino, dirAttr); st != 0 {
t.Fatalf("getattr: %s", st)
}
if dirAttr.Nlink != 3 {
t.Fatalf("nlink should is 3 now: %d", dirAttr.Nlink)
}
}
if st := m.GetAttr(Background, d4Inode, dirAttr); st != 0 {
t.Fatalf("getattr: %s", st)
}
if !dirAttr.Full {
t.Fatalf("d4Inode attr should not be missing: %v", dirAttr.Full)
}
if dirAttr.Nlink != 2 {
t.Fatalf("d4Inode nlink should is 2 now: %d", dirAttr.Nlink)
}
}

0 comments on commit 9cd1dcc

Please sign in to comment.