diff --git a/.github/scripts/hypo/fs_sdk_test.py b/.github/scripts/hypo/fs_sdk_test.py index 7977905dbb6b..d10b8f4b047f 100644 --- a/.github/scripts/hypo/fs_sdk_test.py +++ b/.github/scripts/hypo/fs_sdk_test.py @@ -132,14 +132,14 @@ def test_issue_1424(self): state.readline(file=v2, mode='r', offset=1708, user='root', whence=0) state.teardown() - # def test_issue_1425(self): - # # SEE: https://github.com/juicedata/jfs/issues/1425 - # state = JuicefsMachine() - # v1 = state.init_folders() - # v2 = state.create_file(content=b'a', file_name='a', parent=v1, umask=18, user='root') - # v3 = state.mkdir(mode=0, parent=v1, subdir='b', umask=18, user='root') - # state.rename_dir(entry=v3, new_entry_name=v2, parent=v1, umask=18, user='root') - # state.teardown() + def test_issue_1425(self): + # SEE: https://github.com/juicedata/jfs/issues/1425 + state = JuicefsMachine() + v1 = state.init_folders() + v2 = state.create_file(content=b'a', file_name='a', parent=v1, umask=18, user='root') + v3 = state.mkdir(mode=0, parent=v1, subdir='b', umask=18, user='root') + state.rename_dir(entry=v3, new_entry_name=v2, parent=v1, umask=18, user='root') + state.teardown() def test_issue_1442(self): # SEE: https://github.com/juicedata/jfs/issues/1442 @@ -149,14 +149,14 @@ def test_issue_1442(self): state.set_xattr(file=v2, flag=0, name='user.0', user='root', value=b'\x01\x01\x00\x01') state.teardown() - # def test_issue_1443(self): - # # SEE: https://github.com/juicedata/jfs/issues/1443 - # state = JuicefsMachine() - # v1 = state.init_folders() - # v2 = state.create_file(content=b'bcb', file_name='bcba', parent=v1, umask=18, user='root') - # v3 = state.hardlink(src_file=v2, link_file_name='a', parent=v1, umask=18, user='root') - # state.rename_file(entry=v2, new_entry_name=v3, parent=v1, umask=18, user='root') - # state.teardown() + def test_issue_1443(self): + # SEE: https://github.com/juicedata/jfs/issues/1443 + state = JuicefsMachine() + v1 = state.init_folders() + v2 = state.create_file(content=b'bcb', file_name='bcba', parent=v1, umask=18, user='root') + v3 = state.hardlink(src_file=v2, link_file_name='a', parent=v1, umask=18, user='root') + state.rename_file(entry=v2, new_entry_name=v3, parent=v1, umask=18, user='root') + state.teardown() def test_issue_1449(self): # SEE: https://github.com/juicedata/jfs/issues/1449 diff --git a/pkg/meta/redis.go b/pkg/meta/redis.go index 3563ceae555d..1dc82165477b 100644 --- a/pkg/meta/redis.go +++ b/pkg/meta/redis.go @@ -1745,6 +1745,16 @@ func (m *redisMeta) doRename(ctx Context, parentSrc Ino, nameSrc string, parentD if err := tx.Watch(ctx, keys...).Err(); err != nil { return err } + if dino > 0 { + if ino == dino { + return errno(nil) + } + if typ == TypeDirectory && dtyp != TypeDirectory { + return syscall.ENOTDIR + } else if typ != TypeDirectory && dtyp == TypeDirectory { + return syscall.EISDIR + } + } keys = []string{m.inodeKey(parentSrc), m.inodeKey(parentDst), m.inodeKey(ino)} if dino > 0 { diff --git a/pkg/meta/sql.go b/pkg/meta/sql.go index 2a1267d06a8f..0026f4ff1dbe 100644 --- a/pkg/meta/sql.go +++ b/pkg/meta/sql.go @@ -290,8 +290,8 @@ func retriveUrlConnsOptions(murl string) (string, int, int, int, int) { var vOpenConns int = 0 var vIdleConns int = runtime.GOMAXPROCS(-1) * 2 - var vIdleTime int = 300 - var vLifeTime int = 0 + var vIdleTime int = 300 + var vLifeTime int = 0 if optIndex != -1 { baseurl := murl[:optIndex] @@ -307,11 +307,11 @@ func retriveUrlConnsOptions(murl string) (string, int, int, int, int) { } if vals.Has("max_idle_time") { vIdleTime, _ = strconv.Atoi(vals.Get("max_idle_time")) - vals.Del("max_idle_time"); + vals.Del("max_idle_time") } if vals.Has("max_life_time") { vLifeTime, _ = strconv.Atoi(vals.Get("max_life_time")) - vals.Del("max_life_time"); + vals.Del("max_life_time") } optsurl = vals.Encode() } @@ -1245,17 +1245,17 @@ func (m *dbMeta) upsertSlice(s *xorm.Session, inode Ino, indx uint32, buf []byte driver := m.Name() if driver == "sqlite3" || driver == "postgres" { _, err = s.Exec(` - INSERT INTO jfs_chunk (inode, indx, slices) - VALUES (?, ?, ?) - ON CONFLICT (inode, indx) - DO UPDATE SET slices=jfs_chunk.slices || ?`, inode, indx, buf, buf) + INSERT INTO jfs_chunk (inode, indx, slices) + VALUES (?, ?, ?) + ON CONFLICT (inode, indx) + DO UPDATE SET slices=jfs_chunk.slices || ?`, inode, indx, buf, buf) } else { var r sql.Result r, err = s.Exec(` - INSERT INTO jfs_chunk (inode, indx, slices) - VALUES (?, ?, ?) - ON DUPLICATE KEY UPDATE - slices=concat(slices, ?)`, inode, indx, buf, buf) + INSERT INTO jfs_chunk (inode, indx, slices) + VALUES (?, ?, ?) + ON DUPLICATE KEY UPDATE + slices=concat(slices, ?)`, inode, indx, buf, buf) n, _ := r.RowsAffected() *insert = n == 1 // https://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html } @@ -2052,31 +2052,36 @@ func (m *dbMeta) doRename(ctx Context, parentSrc Ino, nameSrc string, parentDst dn.Parent = parentSrc } } + } else if de.Inode == se.Inode { + return nil + } else if se.Type == TypeDirectory && de.Type != TypeDirectory { + return syscall.ENOTDIR + } else if de.Type == TypeDirectory { + if se.Type != TypeDirectory { + return syscall.EISDIR + } + exist, err := s.Exist(&edge{Parent: de.Inode}) + if err != nil { + return err + } + if exist { + return syscall.ENOTEMPTY + } + dpn.Nlink-- + dstnlink-- + dupdate = true + if trash > 0 { + dn.Parent = trash + } } else { - if de.Type == TypeDirectory { - exist, err := s.Exist(&edge{Parent: de.Inode}) - if err != nil { - return err - } - if exist { - return syscall.ENOTEMPTY - } - dpn.Nlink-- - dstnlink-- - dupdate = true - if trash > 0 { - dn.Parent = trash - } - } else { - if trash == 0 { - dn.Nlink-- - if de.Type == TypeFile && dn.Nlink == 0 { - opened = m.of.IsOpen(dn.Inode) - } - defer func() { m.of.InvalidateChunk(dino, invalidateAttrOnly) }() - } else if dn.Parent > 0 { - dn.Parent = trash + if trash == 0 { + dn.Nlink-- + if de.Type == TypeFile && dn.Nlink == 0 { + opened = m.of.IsOpen(dn.Inode) } + defer func() { m.of.InvalidateChunk(dino, invalidateAttrOnly) }() + } else if dn.Parent > 0 { + dn.Parent = trash } } if ctx.Uid() != 0 && dpn.Mode&01000 != 0 && ctx.Uid() != dpn.Uid && ctx.Uid() != dn.Uid { diff --git a/pkg/meta/tkv.go b/pkg/meta/tkv.go index 010b7cf298ef..47bb156630b5 100644 --- a/pkg/meta/tkv.go +++ b/pkg/meta/tkv.go @@ -1592,6 +1592,12 @@ func (m *kvMeta) doRename(ctx Context, parentSrc Ino, nameSrc string, parentDst tattr.Parent = parentSrc } } + } else if dino == ino { + return nil + } else if typ == TypeDirectory && dtyp != TypeDirectory { + return syscall.ENOTDIR + } else if typ != TypeDirectory && dtyp == TypeDirectory { + return syscall.EISDIR } else { if dtyp == TypeDirectory { if tx.exist(m.entryKey(dino, "")) { diff --git a/sdk/java/libjfs/main.go b/sdk/java/libjfs/main.go index 08698e6aac74..f50d1f2328ac 100644 --- a/sdk/java/libjfs/main.go +++ b/sdk/java/libjfs/main.go @@ -870,11 +870,16 @@ func jfs_rmr(pid int64, h int64, cpath *C.char) int32 { //export jfs_rename func jfs_rename(pid int64, h int64, oldpath *C.char, newpath *C.char) int32 { + return jfs_rename0(pid, h, oldpath, newpath, meta.RenameNoReplace) +} + +//export jfs_rename0 +func jfs_rename0(pid int64, h int64, oldpath *C.char, newpath *C.char, flags uint32) int32 { w := F(h) if w == nil { return EINVAL } - return errno(w.Rename(w.withPid(pid), C.GoString(oldpath), C.GoString(newpath), meta.RenameNoReplace)) + return errno(w.Rename(w.withPid(pid), C.GoString(oldpath), C.GoString(newpath), flags)) } //export jfs_truncate diff --git a/sdk/python/juicefs/juicefs/juicefs.py b/sdk/python/juicefs/juicefs/juicefs.py index beb6203e4471..4b63a1315da2 100644 --- a/sdk/python/juicefs/juicefs/juicefs.py +++ b/sdk/python/juicefs/juicefs/juicefs.py @@ -233,7 +233,7 @@ def rmdir(self, path): def rename(self, old, new): """Rename the file or directory old to new.""" - self.lib.jfs_rename(c_int64(_tid()), c_int64(self.h), _bin(old), _bin(new), c_uint32(0)) + self.lib.jfs_rename0(c_int64(_tid()), c_int64(self.h), _bin(old), _bin(new), c_uint32(0)) def listdir(self, path, detail=False): """Return a list containing the names of the entries in the directory given by path."""