mirror of
https://github.com/golang/go
synced 2024-09-30 10:28:33 -06:00
os: allow case only renames on case-insensitive filesystems
Fixes #35222
This commit is contained in:
parent
15bff20829
commit
954a016c9b
@ -27,13 +27,17 @@ func rename(oldname, newname string) error {
|
|||||||
// At this point we've determined the newname is bad.
|
// At this point we've determined the newname is bad.
|
||||||
// But just in case oldname is also bad, prioritize returning
|
// But just in case oldname is also bad, prioritize returning
|
||||||
// the oldname error because that's what we did historically.
|
// the oldname error because that's what we did historically.
|
||||||
if _, err := Lstat(oldname); err != nil {
|
// However, if the old name and new name are not the same, yet
|
||||||
|
// they refer to the same file, it implies a case-only
|
||||||
|
// rename on a case-insensitive filesystem, which is ok.
|
||||||
|
if ofi, err := Lstat(oldname); err != nil {
|
||||||
if pe, ok := err.(*PathError); ok {
|
if pe, ok := err.(*PathError); ok {
|
||||||
err = pe.Err
|
err = pe.Err
|
||||||
}
|
}
|
||||||
return &LinkError{"rename", oldname, newname, err}
|
return &LinkError{"rename", oldname, newname, err}
|
||||||
|
} else if newname == oldname || !SameFile(fi, ofi) {
|
||||||
|
return &LinkError{"rename", oldname, newname, syscall.EEXIST}
|
||||||
}
|
}
|
||||||
return &LinkError{"rename", oldname, newname, syscall.EEXIST}
|
|
||||||
}
|
}
|
||||||
err = syscall.Rename(oldname, newname)
|
err = syscall.Rename(oldname, newname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -973,6 +973,67 @@ func TestRenameToDirFailed(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRenameCaseDifference(pt *testing.T) {
|
||||||
|
from, to := "renameFROM", "RENAMEfrom"
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
create func() error
|
||||||
|
}{
|
||||||
|
{"dir", func() error {
|
||||||
|
return Mkdir(from, 0777)
|
||||||
|
}},
|
||||||
|
{"file", func() error {
|
||||||
|
fd, err := Create(from)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return fd.Close()
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
pt.Run(test.name, func(t *testing.T) {
|
||||||
|
defer chtmpdir(t)()
|
||||||
|
|
||||||
|
if err := test.create(); err != nil {
|
||||||
|
t.Fatalf("failed to create test file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := Stat(to); err != nil {
|
||||||
|
// Sanity check that the underlying filesystem is not case sensitive.
|
||||||
|
if IsNotExist(err) {
|
||||||
|
t.Skipf("case sensitive filesystem")
|
||||||
|
}
|
||||||
|
t.Fatalf("stat %q, got: %q", to, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := Rename(from, to); err != nil {
|
||||||
|
t.Fatalf("unexpected error when renaming from %q to %q: %s", from, to, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := Open(".")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Open .: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat does not return the real case of the file (it returns what the called asked for)
|
||||||
|
// So we have to use readdir to get the real name of the file.
|
||||||
|
dirNames, err := fd.Readdirnames(-1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("readdirnames: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dirNamesLen := len(dirNames); dirNamesLen != 1 {
|
||||||
|
t.Fatalf("unexpected dirNames len, got %q, want %q", dirNamesLen, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dirNames[0] != to {
|
||||||
|
t.Errorf("unexpected name, got %q, want %q", dirNames[0], to)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func exec(t *testing.T, dir, cmd string, args []string, expect string) {
|
func exec(t *testing.T, dir, cmd string, args []string, expect string) {
|
||||||
r, w, err := Pipe()
|
r, w, err := Pipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user