@@ -8,15 +8,31 @@ import (
88 "regexp"
99 "strings"
1010 "syscall"
11+ "unsafe"
12+
13+ winio "github.com/Microsoft/go-winio"
1114)
1215
16+ // MkdirAllWithACL is a wrapper for MkdirAll that creates a directory
17+ // ACL'd for Builtin Administrators and Local System.
18+ func MkdirAllWithACL (path string , perm os.FileMode ) error {
19+ return mkdirall (path , true )
20+ }
21+
1322// MkdirAll implementation that is volume path aware for Windows.
14- func MkdirAll (path string , perm os.FileMode ) error {
23+ func MkdirAll (path string , _ os.FileMode ) error {
24+ return mkdirall (path , false )
25+ }
26+
27+ // mkdirall is a custom version of os.MkdirAll modified for use on Windows
28+ // so that it is both volume path aware, and can create a directory with
29+ // a DACL.
30+ func mkdirall (path string , adminAndLocalSystem bool ) error {
1531 if re := regexp .MustCompile (`^\\\\\?\\Volume{[a-z0-9-]+}$` ); re .MatchString (path ) {
1632 return nil
1733 }
1834
19- // The rest of this method is copied from os.MkdirAll and should be kept
35+ // The rest of this method is largely copied from os.MkdirAll and should be kept
2036 // as-is to ensure compatibility.
2137
2238 // Fast path: if we can tell whether path is a directory or file, stop with success or error.
@@ -45,14 +61,19 @@ func MkdirAll(path string, perm os.FileMode) error {
4561
4662 if j > 1 {
4763 // Create parent
48- err = MkdirAll (path [0 :j - 1 ], perm )
64+ err = mkdirall (path [0 :j - 1 ], false )
4965 if err != nil {
5066 return err
5167 }
5268 }
5369
54- // Parent now exists; invoke Mkdir and use its result.
55- err = os .Mkdir (path , perm )
70+ // Parent now exists; invoke os.Mkdir or mkdirWithACL and use its result.
71+ if adminAndLocalSystem {
72+ err = mkdirWithACL (path )
73+ } else {
74+ err = os .Mkdir (path , 0 )
75+ }
76+
5677 if err != nil {
5778 // Handle arguments like "foo/." by
5879 // double-checking that directory doesn't exist.
@@ -65,6 +86,36 @@ func MkdirAll(path string, perm os.FileMode) error {
6586 return nil
6687}
6788
89+ // mkdirWithACL creates a new directory. If there is an error, it will be of
90+ // type *PathError. .
91+ //
92+ // This is a modified and combined version of os.Mkdir and syscall.Mkdir
93+ // in golang to cater for creating a directory am ACL permitting full
94+ // access, with inheritance, to any subfolder/file for Built-in Administrators
95+ // and Local System.
96+ func mkdirWithACL (name string ) error {
97+ sa := syscall.SecurityAttributes {Length : 0 }
98+ sddl := "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)"
99+ sd , err := winio .SddlToSecurityDescriptor (sddl )
100+ if err != nil {
101+ return & os.PathError {"mkdir" , name , err }
102+ }
103+ sa .Length = uint32 (unsafe .Sizeof (sa ))
104+ sa .InheritHandle = 1
105+ sa .SecurityDescriptor = uintptr (unsafe .Pointer (& sd [0 ]))
106+
107+ namep , err := syscall .UTF16PtrFromString (name )
108+ if err != nil {
109+ return & os.PathError {"mkdir" , name , err }
110+ }
111+
112+ e := syscall .CreateDirectory (namep , & sa )
113+ if e != nil {
114+ return & os.PathError {"mkdir" , name , e }
115+ }
116+ return nil
117+ }
118+
68119// IsAbs is a platform-specific wrapper for filepath.IsAbs. On Windows,
69120// golang filepath.IsAbs does not consider a path \windows\system32 as absolute
70121// as it doesn't start with a drive-letter/colon combination. However, in
0 commit comments