Skip to content

Commit 4472e9b

Browse files
committed
pkg/system: deprecate MkdirAll and remove custom volume GUID handling
commit 86d1223 introduced a custom version of `os.MkdirAll` for Windows to account for situations where the path to create would start with a Windows volume name (GUID path), for example, `"\\?\Volume{4c1b02c1-d990-11dc-99ae-806e6f6e6963}\`. At the time that patch was added we were using [go1.4.2], which did not have special handling for Windows in [MkdirAll], therefore would recognize such paths as regular paths, trying to create them, which would fail. This code was later updated in 46ec4c1 to provide ACL (DACL) support on Windows. Further updates were made in cfef1b1 and 55ceb50 to allow for an early return when detecting a volume GUID path, and the code was re-aligned with the latest (go1.19.2) implementation in f058afc, which brought in the platform-specific [fixRootDirectory] handling introduced in go1.11. While that enhancement detected UNC volume-paths (`\\?c\`, `//?/c:`), it did not yet support volume GUID paths. go1.22, through [golang.org/cl/86295] added support for this, and `os.MkdirAll` now natively detects volume GUID paths, making our own implementation for this redundant. This patch: - Deprecates pkg/system.MkdirAll in favor of os.MkdirAll, which now provides the same functionality on go1.22 and up. - Renames the (non-exported) `mkdirall` function to `mkdirAllWithACL`, and synchronises `it` with the [implementation in go1.23.4], bringing in the changes from [golang.org/cl/86295] and [golang.org/cl/582499]. - Adds a fast path to `MkdirAllWithACL` if no ACL / SDDL is provided. It's worth noting that we currently still support go1.22, and that the implementation changed in go1.23; those changes ([golang.org/cl/581517] and [golang.org/cl/566556]) were lateral moves, therefore should be identical to the implementation in go1.22, and we can safely use the implementation provided by [filepath.VolumeName] on either go1.22 or go1.23. [go1.4.2]: https://github.com/moby/moby/blob/86d1223a29907ffc6afba557b5138cfad7816bb4/Dockerfile#L77 [MkdirAll]: https://github.com/golang/go/blob/go1.4.2/src/os/path.go#L19-L60 [fixRootDirectory]: golang/go@b86e766 [golang.org/cl/86295]: golang/go@cd589c8 [golang.org/cl/582499]: golang/go@5616ab6 [golang.org/cl/581517]: golang/go@ad22356 [golang.org/cl/566556]: golang/go@ceef063 [1]: https://github.com/golang/go/blob/go1.23.4/src/os/path.go#L12-L66 [filepath.VolumeName]: https://pkg.go.dev/path/filepath#VolumeName Signed-off-by: Sebastiaan van Stijn <[email protected]>
1 parent b5d5fef commit 4472e9b

3 files changed

Lines changed: 39 additions & 50 deletions

File tree

pkg/system/filesys.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,11 @@ import (
1717
func IsAbs(path string) bool {
1818
return filepath.IsAbs(path) || strings.HasPrefix(path, string(os.PathSeparator))
1919
}
20+
21+
// MkdirAll creates a directory named path along with any necessary parents,
22+
// with permission specified by attribute perm for all dir created.
23+
//
24+
// Deprecated: [os.MkdirAll] now natively supports Windows GUID volume paths, and should be used instead. This alias will be removed in the next release.
25+
func MkdirAll(path string, perm os.FileMode) error {
26+
return os.MkdirAll(path, perm)
27+
}

pkg/system/filesys_unix.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,6 @@ package system // import "github.com/docker/docker/pkg/system"
55
import "os"
66

77
// MkdirAllWithACL is a wrapper for os.MkdirAll on unix systems.
8-
func MkdirAllWithACL(path string, perm os.FileMode, sddl string) error {
9-
return os.MkdirAll(path, perm)
10-
}
11-
12-
// MkdirAll creates a directory named path along with any necessary parents,
13-
// with permission specified by attribute perm for all dir created.
14-
func MkdirAll(path string, perm os.FileMode) error {
8+
func MkdirAllWithACL(path string, perm os.FileMode, _ string) error {
159
return os.MkdirAll(path, perm)
1610
}

pkg/system/filesys_windows.go

Lines changed: 30 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package system // import "github.com/docker/docker/pkg/system"
22

33
import (
44
"os"
5-
"regexp"
5+
"path/filepath"
66
"syscall"
77
"unsafe"
88

@@ -12,11 +12,6 @@ import (
1212
// SddlAdministratorsLocalSystem is local administrators plus NT AUTHORITY\System.
1313
const SddlAdministratorsLocalSystem = "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)"
1414

15-
// volumePath is a regular expression to check if a path is a Windows
16-
// volume path (e.g., "\\?\Volume{4c1b02c1-d990-11dc-99ae-806e6f6e6963}"
17-
// or "\\?\Volume{4c1b02c1-d990-11dc-99ae-806e6f6e6963}\").
18-
var volumePath = regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}\\?$`)
19-
2015
// MkdirAllWithACL is a custom version of os.MkdirAll modified for use on Windows
2116
// so that it is both volume path aware, and can create a directory with
2217
// an appropriate SDDL defined ACL.
@@ -25,26 +20,23 @@ func MkdirAllWithACL(path string, _ os.FileMode, sddl string) error {
2520
if err != nil {
2621
return &os.PathError{Op: "mkdirall", Path: path, Err: err}
2722
}
28-
return mkdirall(path, sa)
29-
}
30-
31-
// MkdirAll is a custom version of os.MkdirAll that is volume path aware for
32-
// Windows. It can be used as a drop-in replacement for os.MkdirAll.
33-
func MkdirAll(path string, _ os.FileMode) error {
34-
return mkdirall(path, nil)
23+
return mkdirAllWithACL(path, sa)
3524
}
3625

37-
// mkdirall is a custom version of os.MkdirAll modified for use on Windows
38-
// so that it is both volume path aware, and can create a directory with
39-
// a DACL.
40-
func mkdirall(path string, perm *windows.SecurityAttributes) error {
41-
if volumePath.MatchString(path) {
42-
return nil
26+
// mkdirAllWithACL is a custom version of os.MkdirAll with DACL support on Windows.
27+
// It is fully identical to [os.MkdirAll] if no DACL is provided.
28+
//
29+
// Code in this function is based on the implementation in [go1.23.4].
30+
//
31+
// Copyright 2009 The Go Authors. All rights reserved.
32+
// Use of this source code is governed by a BSD-style
33+
// license that can be found in the LICENSE file.
34+
//
35+
// [go1.23.4]: https://github.com/golang/go/blob/go1.23.4/src/os/path.go#L12-L66
36+
func mkdirAllWithACL(path string, perm *windows.SecurityAttributes) error {
37+
if perm == nil {
38+
return os.MkdirAll(path, 0)
4339
}
44-
45-
// The rest of this method is largely copied from os.MkdirAll and should be kept
46-
// as-is to ensure compatibility.
47-
4840
// Fast path: if we can tell whether path is a directory or file, stop with success or error.
4941
dir, err := os.Stat(path)
5042
if err == nil {
@@ -55,19 +47,25 @@ func mkdirall(path string, perm *windows.SecurityAttributes) error {
5547
}
5648

5749
// Slow path: make sure parent exists and then call Mkdir for path.
58-
i := len(path)
59-
for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator.
50+
51+
// Extract the parent folder from path by first removing any trailing
52+
// path separator and then scanning backward until finding a path
53+
// separator or reaching the beginning of the string.
54+
i := len(path) - 1
55+
for i >= 0 && os.IsPathSeparator(path[i]) {
6056
i--
6157
}
62-
63-
j := i
64-
for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element.
65-
j--
58+
for i >= 0 && !os.IsPathSeparator(path[i]) {
59+
i--
60+
}
61+
if i < 0 {
62+
i = 0
6663
}
6764

68-
if j > 1 {
69-
// Create parent.
70-
err = mkdirall(fixRootDirectory(path[:j-1]), perm)
65+
// If there is a parent directory, and it is not the volume name,
66+
// recurse to ensure parent directory exists.
67+
if parent := path[:i]; len(parent) > len(filepath.VolumeName(path)) {
68+
err = mkdirAllWithACL(parent, perm)
7169
if err != nil {
7270
return err
7371
}
@@ -111,17 +109,6 @@ func mkdirWithACL(name string, sa *windows.SecurityAttributes) error {
111109
return nil
112110
}
113111

114-
// fixRootDirectory fixes a reference to a drive's root directory to
115-
// have the required trailing slash.
116-
func fixRootDirectory(p string) string {
117-
if len(p) == len(`\\?\c:`) {
118-
if os.IsPathSeparator(p[0]) && os.IsPathSeparator(p[1]) && p[2] == '?' && os.IsPathSeparator(p[3]) && p[5] == ':' {
119-
return p + `\`
120-
}
121-
}
122-
return p
123-
}
124-
125112
func makeSecurityAttributes(sddl string) (*windows.SecurityAttributes, error) {
126113
var sa windows.SecurityAttributes
127114
sa.Length = uint32(unsafe.Sizeof(sa))

0 commit comments

Comments
 (0)