github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/filesystem/permissions_windows.go (about) 1 package filesystem 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 userpkg "os/user" 8 9 "golang.org/x/sys/windows" 10 11 aclapi "github.com/hectane/go-acl/api" 12 ) 13 14 // OwnershipSpecification is an opaque type that encodes specification of file 15 // and/or directory ownership. 16 type OwnershipSpecification struct { 17 // ownerSID encodes the Windows owner SID associated with the ownership 18 // specification. It may represent either a user SID or a group SID. A nil 19 // value indicates the absence of specification. 20 ownerSID *windows.SID 21 // groupSid encodes the Windows group SID associated with the ownership 22 // specification. A nil value indicates the absence of specification. 23 groupSID *windows.SID 24 } 25 26 // NewOwnershipSpecification parsers owner and group specifications and resolves 27 // their system-level identifiers. 28 func NewOwnershipSpecification(owner, group string) (*OwnershipSpecification, error) { 29 // Attempt to parse and look up owner, if specified. On Windows, an owner 30 // can be either a user or a group. 31 var ownerSID *windows.SID 32 if owner != "" { 33 switch kind, identifier := ParseOwnershipIdentifier(owner); kind { 34 case OwnershipIdentifierKindInvalid: 35 return nil, errors.New("invalid owner specification") 36 case OwnershipIdentifierKindPOSIXID: 37 return nil, errors.New("POSIX IDs not supported on Windows systems") 38 case OwnershipIdentifierKindWindowsSID: 39 // Verify that this SID represents either a user or a group. 40 var retrievedSID string 41 if userObject, err := userpkg.LookupId(identifier); err == nil { 42 retrievedSID = userObject.Uid 43 } else if groupObject, err := userpkg.LookupGroupId(identifier); err == nil { 44 retrievedSID = groupObject.Gid 45 } else { 46 return nil, errors.New("unable to find user or group with specified owner SID") 47 } 48 49 // Convert the retrieved SID to a string. 50 if s, err := windows.StringToSid(retrievedSID); err != nil { 51 return nil, fmt.Errorf("unable to convert SID string to object: %w", err) 52 } else { 53 ownerSID = s 54 } 55 case OwnershipIdentifierKindName: 56 // Verify that this name represents either a user or a group and 57 // retrieve the associated SID. 58 var retrievedSID string 59 if userObject, err := userpkg.Lookup(identifier); err == nil { 60 retrievedSID = userObject.Uid 61 } else if groupObject, err := userpkg.LookupGroup(identifier); err == nil { 62 retrievedSID = groupObject.Gid 63 } else { 64 return nil, errors.New("unable to find user or group with specified owner name") 65 } 66 67 // Convert the retrieved SID to a string. 68 if s, err := windows.StringToSid(retrievedSID); err != nil { 69 return nil, fmt.Errorf("unable to convert SID string to object: %w", err) 70 } else { 71 ownerSID = s 72 } 73 default: 74 panic("unhandled ownership identifier kind") 75 } 76 } 77 78 // Attempt to parse and look up group, if specified. 79 var groupSID *windows.SID 80 if group != "" { 81 switch kind, identifier := ParseOwnershipIdentifier(group); kind { 82 case OwnershipIdentifierKindInvalid: 83 return nil, errors.New("invalid group specification") 84 case OwnershipIdentifierKindPOSIXID: 85 return nil, errors.New("POSIX IDs not supported on Windows systems") 86 case OwnershipIdentifierKindWindowsSID: 87 if groupObject, err := userpkg.LookupGroupId(identifier); err != nil { 88 return nil, fmt.Errorf("unable to lookup group by ID: %w", err) 89 } else if g, err := windows.StringToSid(groupObject.Gid); err != nil { 90 return nil, fmt.Errorf("unable to convert SID string to object: %w", err) 91 } else { 92 groupSID = g 93 } 94 case OwnershipIdentifierKindName: 95 if groupObject, err := userpkg.LookupGroup(identifier); err != nil { 96 return nil, fmt.Errorf("unable to lookup group by ID: %w", err) 97 } else if g, err := windows.StringToSid(groupObject.Gid); err != nil { 98 return nil, fmt.Errorf("unable to convert SID string to object: %w", err) 99 } else { 100 groupSID = g 101 } 102 default: 103 panic("unhandled ownership identifier kind") 104 } 105 } 106 107 // Success. 108 return &OwnershipSpecification{ 109 ownerSID: ownerSID, 110 groupSID: groupSID, 111 }, nil 112 } 113 114 // SetPermissionsByPath sets the permissions on the content at the specified 115 // path. Ownership information is set first, followed by permissions extracted 116 // from the mode using ModePermissionsMask. Ownership setting can be skipped 117 // completely by providing a nil OwnershipSpecification or a specification with 118 // both components unset. An OwnershipSpecification may also include only 119 // certain components, in which case only those components will be set. 120 // Permission setting can be skipped by providing a mode value that yields 0 121 // after permission bit masking. 122 func SetPermissionsByPath(path string, ownership *OwnershipSpecification, mode Mode) error { 123 // Set ownership information, if specified. 124 if ownership != nil && (ownership.ownerSID != nil || ownership.groupSID != nil) { 125 // Compute the information that we're going to set. 126 var information uint32 127 if ownership.ownerSID != nil { 128 information |= aclapi.OWNER_SECURITY_INFORMATION 129 } 130 if ownership.groupSID != nil { 131 information |= aclapi.GROUP_SECURITY_INFORMATION 132 } 133 134 // Set the information. 135 if err := aclapi.SetNamedSecurityInfo( 136 path, 137 aclapi.SE_FILE_OBJECT, 138 information, 139 ownership.ownerSID, 140 ownership.groupSID, 141 0, 142 0, 143 ); err != nil { 144 return fmt.Errorf("unable to set ownership information: %w", err) 145 } 146 } 147 148 // Set permissions, if specified. 149 mode = mode & ModePermissionsMask 150 if mode != 0 { 151 if err := os.Chmod(path, os.FileMode(mode)); err != nil { 152 return fmt.Errorf("unable to set permission bits: %w", err) 153 } 154 } 155 156 // Success. 157 return nil 158 }