Skip to content

Commit e12cec7

Browse files
committed
os: Add support for long path names on unix RemoveAll
On unix systems, long enough path names will fail when performing syscalls like `Lstat`. The current RemoveAll uses several of these syscalls, and so will fail for long paths. This can be risky, as it can let users "hide" files from the system or otherwise make long enough paths for programs to fail. By using `Unlinkat` and `Openat` syscalls instead, RemoveAll is safer on unix systems. Initially implemented for linux and darwin. Fixes #27029 Co-authored-by: Giuseppe Capizzi <[email protected]> Co-authored-by: Julia Nedialkova <[email protected]> Change-Id: I6a965569beb113c10a52967d5ea0294fcd8dd9d6
1 parent 930ce09 commit e12cec7

17 files changed

+626
-224
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package unix
6+
7+
const unlinkatTrap = uintptr(472)
8+
const openatTrap = uintptr(463)
9+
10+
const AT_REMOVEDIR = 0x80
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package unix
6+
7+
const unlinkatTrap uintptr = 301
8+
const openatTrap uintptr = 295
9+
10+
const AT_REMOVEDIR = 0x200
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package unix
6+
7+
const unlinkatTrap uintptr = 263
8+
const openatTrap uintptr = 257
9+
10+
const AT_REMOVEDIR = 0x200
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package unix
6+
7+
const unlinkatTrap = uintptr(328)
8+
const openatTrap = uintptr(322)
9+
10+
const AT_REMOVEDIR = 0x200
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package unix
6+
7+
const unlinkatTrap = uintptr(35)
8+
const openatTrap = uintptr(56)
9+
10+
const AT_REMOVEDIR = 0x200
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build mips64 mips64le
6+
7+
package unix
8+
9+
const unlinkatTrap = uintptr(5253)
10+
const openatTrap = uintptr(5247)
11+
12+
const AT_REMOVEDIR = 0x200
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build mips mipsle
6+
7+
package unix
8+
9+
const unlinkatTrap = uintptr(4294)
10+
const openatTrap = uintptr(4288)
11+
12+
const AT_REMOVEDIR = 0x200
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build ppc64 ppc64le
6+
7+
package unix
8+
9+
const unlinkatTrap = uintptr(292)
10+
const openatTrap = uintptr(286)
11+
12+
const AT_REMOVEDIR = 0x200
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package unix
6+
7+
const unlinkatTrap = uintptr(294)
8+
const openatTrap = uintptr(288)
9+
10+
const AT_REMOVEDIR = 0x200
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build linux darwin
6+
7+
package unix
8+
9+
import (
10+
"syscall"
11+
"unsafe"
12+
)
13+
14+
func Openat(fdat int, path string, flags int, perm uint32) (int, error) {
15+
var pathBytePointer *byte
16+
pathBytePointer, err := syscall.BytePtrFromString(path)
17+
if err != nil {
18+
return 0, err
19+
}
20+
21+
fdPointer, _, errNo := syscall.Syscall6(openatTrap, uintptr(fdat), uintptr(unsafe.Pointer(pathBytePointer)), uintptr(flags), uintptr(perm), 0, 0)
22+
fd := int(fdPointer)
23+
if errNo != 0 {
24+
return 0, errNo
25+
}
26+
27+
return fd, nil
28+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build linux darwin
6+
7+
package unix
8+
9+
import (
10+
"syscall"
11+
"unsafe"
12+
)
13+
14+
func Unlinkat(dirfd int, path string, flags int) error {
15+
var pathBytePointer *byte
16+
pathBytePointer, err := syscall.BytePtrFromString(path)
17+
if err != nil {
18+
return err
19+
}
20+
21+
_, _, errNo := syscall.Syscall(unlinkatTrap, uintptr(dirfd), uintptr(unsafe.Pointer(pathBytePointer)), uintptr(flags))
22+
if errNo != 0 {
23+
return errNo
24+
}
25+
26+
return nil
27+
}

src/os/path.go

Lines changed: 0 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package os
66

77
import (
8-
"io"
98
"syscall"
109
)
1110

@@ -58,101 +57,3 @@ func MkdirAll(path string, perm FileMode) error {
5857
}
5958
return nil
6059
}
61-
62-
// RemoveAll removes path and any children it contains.
63-
// It removes everything it can but returns the first error
64-
// it encounters. If the path does not exist, RemoveAll
65-
// returns nil (no error).
66-
func RemoveAll(path string) error {
67-
// Simple case: if Remove works, we're done.
68-
err := Remove(path)
69-
if err == nil || IsNotExist(err) {
70-
return nil
71-
}
72-
73-
// Otherwise, is this a directory we need to recurse into?
74-
dir, serr := Lstat(path)
75-
if serr != nil {
76-
if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) {
77-
return nil
78-
}
79-
return serr
80-
}
81-
if !dir.IsDir() {
82-
// Not a directory; return the error from Remove.
83-
return err
84-
}
85-
86-
// Remove contents & return first error.
87-
err = nil
88-
for {
89-
fd, err := Open(path)
90-
if err != nil {
91-
if IsNotExist(err) {
92-
// Already deleted by someone else.
93-
return nil
94-
}
95-
return err
96-
}
97-
98-
const request = 1024
99-
names, err1 := fd.Readdirnames(request)
100-
101-
// Removing files from the directory may have caused
102-
// the OS to reshuffle it. Simply calling Readdirnames
103-
// again may skip some entries. The only reliable way
104-
// to avoid this is to close and re-open the
105-
// directory. See issue 20841.
106-
fd.Close()
107-
108-
for _, name := range names {
109-
err1 := RemoveAll(path + string(PathSeparator) + name)
110-
if err == nil {
111-
err = err1
112-
}
113-
}
114-
115-
if err1 == io.EOF {
116-
break
117-
}
118-
// If Readdirnames returned an error, use it.
119-
if err == nil {
120-
err = err1
121-
}
122-
if len(names) == 0 {
123-
break
124-
}
125-
126-
// We don't want to re-open unnecessarily, so if we
127-
// got fewer than request names from Readdirnames, try
128-
// simply removing the directory now. If that
129-
// succeeds, we are done.
130-
if len(names) < request {
131-
err1 := Remove(path)
132-
if err1 == nil || IsNotExist(err1) {
133-
return nil
134-
}
135-
136-
if err != nil {
137-
// We got some error removing the
138-
// directory contents, and since we
139-
// read fewer names than we requested
140-
// there probably aren't more files to
141-
// remove. Don't loop around to read
142-
// the directory again. We'll probably
143-
// just get the same error.
144-
return err
145-
}
146-
}
147-
}
148-
149-
// Remove directory.
150-
err1 := Remove(path)
151-
if err1 == nil || IsNotExist(err1) {
152-
return nil
153-
}
154-
if err == nil {
155-
err = err1
156-
}
157-
return err
158-
}

0 commit comments

Comments
 (0)