Skip to content

os: add support for long path names on unix RemoveAll #28494

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions src/internal/syscall/unix/at.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build linux darwin freebsd openbsd netbsd dragonfly

package unix

import (
"syscall"
"unsafe"
)

func Unlinkat(dirfd int, path string, flags int) error {
var p *byte
p, err := syscall.BytePtrFromString(path)
if err != nil {
return err
}

_, _, errno := syscall.Syscall(unlinkatTrap, uintptr(dirfd), uintptr(unsafe.Pointer(p)), uintptr(flags))
if errno != 0 {
return errno
}

return nil
}

func Openat(dirfd int, path string, flags int, perm uint32) (int, error) {
var p *byte
p, err := syscall.BytePtrFromString(path)
if err != nil {
return 0, err
}

fd, _, errno := syscall.Syscall6(openatTrap, uintptr(dirfd), uintptr(unsafe.Pointer(p)), uintptr(flags), uintptr(perm), 0, 0)
if errno != 0 {
return 0, errno
}

return int(fd), nil
}

func Fstatat(dirfd int, path string, stat *syscall.Stat_t, flags int) error {
var p *byte
p, err := syscall.BytePtrFromString(path)
if err != nil {
return err
}

_, _, errno := syscall.Syscall6(fstatatTrap, uintptr(dirfd), uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0)
if errno != 0 {
return errno
}

return nil

}
12 changes: 12 additions & 0 deletions src/internal/syscall/unix/at_sysnum_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package unix

const unlinkatTrap uintptr = 472
const openatTrap uintptr = 463
const fstatatTrap uintptr = 470

const AT_REMOVEDIR = 0x80
const AT_SYMLINK_NOFOLLOW = 0x0020
14 changes: 14 additions & 0 deletions src/internal/syscall/unix/at_sysnum_dragonfly.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package unix

import "syscall"

const unlinkatTrap uintptr = syscall.SYS_UNLINKAT
const openatTrap uintptr = syscall.SYS_OPENAT
const fstatatTrap uintptr = syscall.SYS_FSTATAT

const AT_REMOVEDIR = 0x2
const AT_SYMLINK_NOFOLLOW = 0x1
14 changes: 14 additions & 0 deletions src/internal/syscall/unix/at_sysnum_freebsd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package unix

import "syscall"

const unlinkatTrap uintptr = syscall.SYS_UNLINKAT
const openatTrap uintptr = syscall.SYS_OPENAT
const fstatatTrap uintptr = syscall.SYS_FSTATAT

const AT_REMOVEDIR = 0x800
const AT_SYMLINK_NOFOLLOW = 0x200
11 changes: 11 additions & 0 deletions src/internal/syscall/unix/at_sysnum_fstatat64_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build arm mips mipsle 386

package unix

import "syscall"

const fstatatTrap uintptr = syscall.SYS_FSTATAT64
11 changes: 11 additions & 0 deletions src/internal/syscall/unix/at_sysnum_fstatat_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build arm64

package unix

import "syscall"

const fstatatTrap uintptr = syscall.SYS_FSTATAT
13 changes: 13 additions & 0 deletions src/internal/syscall/unix/at_sysnum_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package unix

import "syscall"

const unlinkatTrap uintptr = syscall.SYS_UNLINKAT
const openatTrap uintptr = syscall.SYS_OPENAT

const AT_REMOVEDIR = 0x200
const AT_SYMLINK_NOFOLLOW = 0x100
14 changes: 14 additions & 0 deletions src/internal/syscall/unix/at_sysnum_netbsd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package unix

import "syscall"

const unlinkatTrap uintptr = syscall.SYS_UNLINKAT
const openatTrap uintptr = syscall.SYS_OPENAT
const fstatatTrap uintptr = syscall.SYS_FSTATAT

const AT_REMOVEDIR = 0x800
const AT_SYMLINK_NOFOLLOW = 0x200
11 changes: 11 additions & 0 deletions src/internal/syscall/unix/at_sysnum_newfstatat_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build amd64 mips64 mips64le ppc64 ppc64le s390x

package unix

import "syscall"

const fstatatTrap uintptr = syscall.SYS_NEWFSTATAT
14 changes: 14 additions & 0 deletions src/internal/syscall/unix/at_sysnum_openbsd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package unix

import "syscall"

const unlinkatTrap uintptr = syscall.SYS_UNLINKAT
const openatTrap uintptr = syscall.SYS_OPENAT
const fstatatTrap uintptr = syscall.SYS_FSTATAT

const AT_REMOVEDIR = 0x08
const AT_SYMLINK_NOFOLLOW = 0x02
99 changes: 0 additions & 99 deletions src/os/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
package os

import (
"io"
"syscall"
)

Expand Down Expand Up @@ -58,101 +57,3 @@ func MkdirAll(path string, perm FileMode) error {
}
return nil
}

// RemoveAll removes path and any children it contains.
// It removes everything it can but returns the first error
// it encounters. If the path does not exist, RemoveAll
// returns nil (no error).
func RemoveAll(path string) error {
// Simple case: if Remove works, we're done.
err := Remove(path)
if err == nil || IsNotExist(err) {
return nil
}

// Otherwise, is this a directory we need to recurse into?
dir, serr := Lstat(path)
if serr != nil {
if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) {
return nil
}
return serr
}
if !dir.IsDir() {
// Not a directory; return the error from Remove.
return err
}

// Remove contents & return first error.
err = nil
for {
fd, err := Open(path)
if err != nil {
if IsNotExist(err) {
// Already deleted by someone else.
return nil
}
return err
}

const request = 1024
names, err1 := fd.Readdirnames(request)

// Removing files from the directory may have caused
// the OS to reshuffle it. Simply calling Readdirnames
// again may skip some entries. The only reliable way
// to avoid this is to close and re-open the
// directory. See issue 20841.
fd.Close()

for _, name := range names {
err1 := RemoveAll(path + string(PathSeparator) + name)
if err == nil {
err = err1
}
}

if err1 == io.EOF {
break
}
// If Readdirnames returned an error, use it.
if err == nil {
err = err1
}
if len(names) == 0 {
break
}

// We don't want to re-open unnecessarily, so if we
// got fewer than request names from Readdirnames, try
// simply removing the directory now. If that
// succeeds, we are done.
if len(names) < request {
err1 := Remove(path)
if err1 == nil || IsNotExist(err1) {
return nil
}

if err != nil {
// We got some error removing the
// directory contents, and since we
// read fewer names than we requested
// there probably aren't more files to
// remove. Don't loop around to read
// the directory again. We'll probably
// just get the same error.
return err
}
}
}

// Remove directory.
err1 := Remove(path)
if err1 == nil || IsNotExist(err1) {
return nil
}
if err == nil {
err = err1
}
return err
}
Loading