Skip to content

Commit 4a09a9b

Browse files
AudriusButkeviciusianlancetaylor
authored andcommitted
os: allow case only renames on case-insensitive filesystems
Fixes #35222 Change-Id: I8be45092ac4079d21ff54661637a3aa8ec4eb9bc GitHub-Last-Rev: 954a016 GitHub-Pull-Request: #35298 Reviewed-on: https://go-review.googlesource.com/c/go/+/204601 Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent ce49f95 commit 4a09a9b

File tree

2 files changed

+67
-2
lines changed

2 files changed

+67
-2
lines changed

src/os/file_unix.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,17 @@ func rename(oldname, newname string) error {
2727
// At this point we've determined the newname is bad.
2828
// But just in case oldname is also bad, prioritize returning
2929
// the oldname error because that's what we did historically.
30-
if _, err := Lstat(oldname); err != nil {
30+
// However, if the old name and new name are not the same, yet
31+
// they refer to the same file, it implies a case-only
32+
// rename on a case-insensitive filesystem, which is ok.
33+
if ofi, err := Lstat(oldname); err != nil {
3134
if pe, ok := err.(*PathError); ok {
3235
err = pe.Err
3336
}
3437
return &LinkError{"rename", oldname, newname, err}
38+
} else if newname == oldname || !SameFile(fi, ofi) {
39+
return &LinkError{"rename", oldname, newname, syscall.EEXIST}
3540
}
36-
return &LinkError{"rename", oldname, newname, syscall.EEXIST}
3741
}
3842
err = syscall.Rename(oldname, newname)
3943
if err != nil {

src/os/os_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,67 @@ func TestRenameToDirFailed(t *testing.T) {
973973
}
974974
}
975975

976+
func TestRenameCaseDifference(pt *testing.T) {
977+
from, to := "renameFROM", "RENAMEfrom"
978+
tests := []struct {
979+
name string
980+
create func() error
981+
}{
982+
{"dir", func() error {
983+
return Mkdir(from, 0777)
984+
}},
985+
{"file", func() error {
986+
fd, err := Create(from)
987+
if err != nil {
988+
return err
989+
}
990+
return fd.Close()
991+
}},
992+
}
993+
994+
for _, test := range tests {
995+
pt.Run(test.name, func(t *testing.T) {
996+
defer chtmpdir(t)()
997+
998+
if err := test.create(); err != nil {
999+
t.Fatalf("failed to create test file: %s", err)
1000+
}
1001+
1002+
if _, err := Stat(to); err != nil {
1003+
// Sanity check that the underlying filesystem is not case sensitive.
1004+
if IsNotExist(err) {
1005+
t.Skipf("case sensitive filesystem")
1006+
}
1007+
t.Fatalf("stat %q, got: %q", to, err)
1008+
}
1009+
1010+
if err := Rename(from, to); err != nil {
1011+
t.Fatalf("unexpected error when renaming from %q to %q: %s", from, to, err)
1012+
}
1013+
1014+
fd, err := Open(".")
1015+
if err != nil {
1016+
t.Fatalf("Open .: %s", err)
1017+
}
1018+
1019+
// Stat does not return the real case of the file (it returns what the called asked for)
1020+
// So we have to use readdir to get the real name of the file.
1021+
dirNames, err := fd.Readdirnames(-1)
1022+
if err != nil {
1023+
t.Fatalf("readdirnames: %s", err)
1024+
}
1025+
1026+
if dirNamesLen := len(dirNames); dirNamesLen != 1 {
1027+
t.Fatalf("unexpected dirNames len, got %q, want %q", dirNamesLen, 1)
1028+
}
1029+
1030+
if dirNames[0] != to {
1031+
t.Errorf("unexpected name, got %q, want %q", dirNames[0], to)
1032+
}
1033+
})
1034+
}
1035+
}
1036+
9761037
func exec(t *testing.T, dir, cmd string, args []string, expect string) {
9771038
r, w, err := Pipe()
9781039
if err != nil {

0 commit comments

Comments
 (0)