Merge pull request #7531 from thaJeztah/sys_windows
sys: optimize and refactor MkdirAllWithACL()
This commit is contained in:
		@@ -25,28 +25,36 @@ import (
 | 
				
			|||||||
	"golang.org/x/sys/windows"
 | 
						"golang.org/x/sys/windows"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					// SddlAdministratorsLocalSystem is local administrators plus NT AUTHORITY\System.
 | 
				
			||||||
	// SddlAdministratorsLocalSystem is local administrators plus NT AUTHORITY\System
 | 
					const SddlAdministratorsLocalSystem = "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)"
 | 
				
			||||||
	SddlAdministratorsLocalSystem = "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MkdirAllWithACL is a wrapper for MkdirAll that creates a directory
 | 
					// volumePath is a regular expression to check if a path is a Windows
 | 
				
			||||||
// ACL'd for Builtin Administrators and Local System.
 | 
					// volume path (e.g., "\\?\Volume{4c1b02c1-d990-11dc-99ae-806e6f6e6963}"
 | 
				
			||||||
func MkdirAllWithACL(path string, perm os.FileMode) error {
 | 
					// or "\\?\Volume{4c1b02c1-d990-11dc-99ae-806e6f6e6963}\").
 | 
				
			||||||
	return mkdirall(path, true)
 | 
					var volumePath = regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}\\?$`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MkdirAllWithACL is a custom version of os.MkdirAll modified for use on Windows
 | 
				
			||||||
 | 
					// so that it is both volume path aware, and to create a directory
 | 
				
			||||||
 | 
					// an appropriate SDDL defined ACL for Builtin Administrators and Local System.
 | 
				
			||||||
 | 
					func MkdirAllWithACL(path string, _ os.FileMode) error {
 | 
				
			||||||
 | 
						sa, err := makeSecurityAttributes(SddlAdministratorsLocalSystem)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return &os.PathError{Op: "mkdirall", Path: path, Err: err}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return mkdirall(path, sa)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MkdirAll implementation that is volume path aware for Windows. It can be used
 | 
					// MkdirAll is a custom version of os.MkdirAll that is volume path aware for
 | 
				
			||||||
// as a drop-in replacement for os.MkdirAll()
 | 
					// Windows. It can be used as a drop-in replacement for os.MkdirAll.
 | 
				
			||||||
func MkdirAll(path string, _ os.FileMode) error {
 | 
					func MkdirAll(path string, _ os.FileMode) error {
 | 
				
			||||||
	return mkdirall(path, false)
 | 
						return mkdirall(path, nil)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// mkdirall is a custom version of os.MkdirAll modified for use on Windows
 | 
					// mkdirall is a custom version of os.MkdirAll modified for use on Windows
 | 
				
			||||||
// so that it is both volume path aware, and can create a directory with
 | 
					// so that it is both volume path aware, and can create a directory with
 | 
				
			||||||
// a DACL.
 | 
					// a DACL.
 | 
				
			||||||
func mkdirall(path string, adminAndLocalSystem bool) error {
 | 
					func mkdirall(path string, perm *windows.SecurityAttributes) error {
 | 
				
			||||||
	if re := regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}$`); re.MatchString(path) {
 | 
						if volumePath.MatchString(path) {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -59,11 +67,7 @@ func mkdirall(path string, adminAndLocalSystem bool) error {
 | 
				
			|||||||
		if dir.IsDir() {
 | 
							if dir.IsDir() {
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return &os.PathError{
 | 
							return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR}
 | 
				
			||||||
			Op:   "mkdir",
 | 
					 | 
				
			||||||
			Path: path,
 | 
					 | 
				
			||||||
			Err:  syscall.ENOTDIR,
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Slow path: make sure parent exists and then call Mkdir for path.
 | 
						// Slow path: make sure parent exists and then call Mkdir for path.
 | 
				
			||||||
@@ -78,20 +82,15 @@ func mkdirall(path string, adminAndLocalSystem bool) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if j > 1 {
 | 
						if j > 1 {
 | 
				
			||||||
		// Create parent
 | 
							// Create parent.
 | 
				
			||||||
		err = mkdirall(path[0:j-1], adminAndLocalSystem)
 | 
							err = mkdirall(fixRootDirectory(path[:j-1]), perm)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Parent now exists; invoke os.Mkdir or mkdirWithACL and use its result.
 | 
						// Parent now exists; invoke Mkdir and use its result.
 | 
				
			||||||
	if adminAndLocalSystem {
 | 
						err = mkdirWithACL(path, perm)
 | 
				
			||||||
		err = mkdirWithACL(path)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		err = os.Mkdir(path, 0)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		// Handle arguments like "foo/." by
 | 
							// Handle arguments like "foo/." by
 | 
				
			||||||
		// double-checking that directory doesn't exist.
 | 
							// double-checking that directory doesn't exist.
 | 
				
			||||||
@@ -111,24 +110,42 @@ func mkdirall(path string, adminAndLocalSystem bool) error {
 | 
				
			|||||||
// in golang to cater for creating a directory am ACL permitting full
 | 
					// in golang to cater for creating a directory am ACL permitting full
 | 
				
			||||||
// access, with inheritance, to any subfolder/file for Built-in Administrators
 | 
					// access, with inheritance, to any subfolder/file for Built-in Administrators
 | 
				
			||||||
// and Local System.
 | 
					// and Local System.
 | 
				
			||||||
func mkdirWithACL(name string) error {
 | 
					func mkdirWithACL(name string, sa *windows.SecurityAttributes) error {
 | 
				
			||||||
	sa := windows.SecurityAttributes{Length: 0}
 | 
						if sa == nil {
 | 
				
			||||||
	sd, err := windows.SecurityDescriptorFromString(SddlAdministratorsLocalSystem)
 | 
							return os.Mkdir(name, 0)
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return &os.PathError{Op: "mkdir", Path: name, Err: err}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	sa.Length = uint32(unsafe.Sizeof(sa))
 | 
					 | 
				
			||||||
	sa.InheritHandle = 1
 | 
					 | 
				
			||||||
	sa.SecurityDescriptor = sd
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	namep, err := windows.UTF16PtrFromString(name)
 | 
						namep, err := windows.UTF16PtrFromString(name)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return &os.PathError{Op: "mkdir", Path: name, Err: err}
 | 
							return &os.PathError{Op: "mkdir", Path: name, Err: err}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	e := windows.CreateDirectory(namep, &sa)
 | 
						err = windows.CreateDirectory(namep, sa)
 | 
				
			||||||
	if e != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return &os.PathError{Op: "mkdir", Path: name, Err: e}
 | 
							return &os.PathError{Op: "mkdir", Path: name, Err: err}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// fixRootDirectory fixes a reference to a drive's root directory to
 | 
				
			||||||
 | 
					// have the required trailing slash.
 | 
				
			||||||
 | 
					func fixRootDirectory(p string) string {
 | 
				
			||||||
 | 
						if len(p) == len(`\\?\c:`) {
 | 
				
			||||||
 | 
							if os.IsPathSeparator(p[0]) && os.IsPathSeparator(p[1]) && p[2] == '?' && os.IsPathSeparator(p[3]) && p[5] == ':' {
 | 
				
			||||||
 | 
								return p + `\`
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return p
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func makeSecurityAttributes(sddl string) (*windows.SecurityAttributes, error) {
 | 
				
			||||||
 | 
						var sa windows.SecurityAttributes
 | 
				
			||||||
 | 
						sa.Length = uint32(unsafe.Sizeof(sa))
 | 
				
			||||||
 | 
						sa.InheritHandle = 1
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						sa.SecurityDescriptor, err = windows.SecurityDescriptorFromString(sddl)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &sa, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user