github.com/rumpl/bof@v23.0.0-rc.2+incompatible/pkg/system/filesys_windows.go (about)

     1  package system // import "github.com/docker/docker/pkg/system"
     2  
     3  import (
     4  	"os"
     5  	"regexp"
     6  	"syscall"
     7  	"unsafe"
     8  
     9  	"golang.org/x/sys/windows"
    10  )
    11  
    12  const (
    13  	// SddlAdministratorsLocalSystem is local administrators plus NT AUTHORITY\System
    14  	SddlAdministratorsLocalSystem = "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)"
    15  )
    16  
    17  // MkdirAllWithACL is a wrapper for MkdirAll that creates a directory
    18  // with an appropriate SDDL defined ACL.
    19  func MkdirAllWithACL(path string, perm os.FileMode, sddl string) error {
    20  	return mkdirall(path, true, sddl)
    21  }
    22  
    23  // MkdirAll implementation that is volume path aware for Windows. It can be used
    24  // as a drop-in replacement for os.MkdirAll()
    25  func MkdirAll(path string, _ os.FileMode) error {
    26  	return mkdirall(path, false, "")
    27  }
    28  
    29  // mkdirall is a custom version of os.MkdirAll modified for use on Windows
    30  // so that it is both volume path aware, and can create a directory with
    31  // a DACL.
    32  func mkdirall(path string, applyACL bool, sddl string) error {
    33  	if re := regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}$`); re.MatchString(path) {
    34  		return nil
    35  	}
    36  
    37  	// The rest of this method is largely copied from os.MkdirAll and should be kept
    38  	// as-is to ensure compatibility.
    39  
    40  	// Fast path: if we can tell whether path is a directory or file, stop with success or error.
    41  	dir, err := os.Stat(path)
    42  	if err == nil {
    43  		if dir.IsDir() {
    44  			return nil
    45  		}
    46  		return &os.PathError{
    47  			Op:   "mkdir",
    48  			Path: path,
    49  			Err:  syscall.ENOTDIR,
    50  		}
    51  	}
    52  
    53  	// Slow path: make sure parent exists and then call Mkdir for path.
    54  	i := len(path)
    55  	for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator.
    56  		i--
    57  	}
    58  
    59  	j := i
    60  	for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element.
    61  		j--
    62  	}
    63  
    64  	if j > 1 {
    65  		// Create parent
    66  		err = mkdirall(path[0:j-1], false, sddl)
    67  		if err != nil {
    68  			return err
    69  		}
    70  	}
    71  
    72  	// Parent now exists; invoke os.Mkdir or mkdirWithACL and use its result.
    73  	if applyACL {
    74  		err = mkdirWithACL(path, sddl)
    75  	} else {
    76  		err = os.Mkdir(path, 0)
    77  	}
    78  
    79  	if err != nil {
    80  		// Handle arguments like "foo/." by
    81  		// double-checking that directory doesn't exist.
    82  		dir, err1 := os.Lstat(path)
    83  		if err1 == nil && dir.IsDir() {
    84  			return nil
    85  		}
    86  		return err
    87  	}
    88  	return nil
    89  }
    90  
    91  // mkdirWithACL creates a new directory. If there is an error, it will be of
    92  // type *PathError. .
    93  //
    94  // This is a modified and combined version of os.Mkdir and windows.Mkdir
    95  // in golang to cater for creating a directory am ACL permitting full
    96  // access, with inheritance, to any subfolder/file for Built-in Administrators
    97  // and Local System.
    98  func mkdirWithACL(name string, sddl string) error {
    99  	sa := windows.SecurityAttributes{Length: 0}
   100  	sd, err := windows.SecurityDescriptorFromString(sddl)
   101  	if err != nil {
   102  		return &os.PathError{Op: "mkdir", Path: name, Err: err}
   103  	}
   104  	sa.Length = uint32(unsafe.Sizeof(sa))
   105  	sa.InheritHandle = 1
   106  	sa.SecurityDescriptor = sd
   107  
   108  	namep, err := windows.UTF16PtrFromString(name)
   109  	if err != nil {
   110  		return &os.PathError{Op: "mkdir", Path: name, Err: err}
   111  	}
   112  
   113  	e := windows.CreateDirectory(namep, &sa)
   114  	if e != nil {
   115  		return &os.PathError{Op: "mkdir", Path: name, Err: e}
   116  	}
   117  	return nil
   118  }