github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/filesystem/permissions_posix.go (about) 1 //go:build !windows 2 3 package filesystem 4 5 import ( 6 "errors" 7 "fmt" 8 "os" 9 userpkg "os/user" 10 "strconv" 11 ) 12 13 // OwnershipSpecification is an opaque type that encodes specification of file 14 // and/or directory ownership. 15 type OwnershipSpecification struct { 16 // ownerID encodes the POSIX user ID associated with the ownership 17 // specification. A value of -1 indicates the absence of specification. The 18 // availability of -1 as a sentinel value for omission is guaranteed by the 19 // POSIX definition of chmod. 20 ownerID int 21 // groupID encodes the POSIX user ID associated with the ownership 22 // specification. A value of -1 indicates the absence of specification. The 23 // availability of -1 as a sentinel value for omission is guaranteed by the 24 // POSIX definition of chmod. 25 groupID int 26 } 27 28 // NewOwnershipSpecification parsers owner and group specifications and resolves 29 // their system-level identifiers. 30 func NewOwnershipSpecification(owner, group string) (*OwnershipSpecification, error) { 31 // Attempt to parse and look up owner user, if specified. 32 ownerID := -1 33 if owner != "" { 34 switch kind, identifier := ParseOwnershipIdentifier(owner); kind { 35 case OwnershipIdentifierKindInvalid: 36 return nil, errors.New("invalid user specification") 37 case OwnershipIdentifierKindPOSIXID: 38 if u, err := strconv.Atoi(identifier); err != nil { 39 return nil, fmt.Errorf("unable to convert user ID to numeric value: %w", err) 40 } else if u < 0 { 41 return nil, errors.New("negative user ID") 42 } else { 43 ownerID = u 44 } 45 case OwnershipIdentifierKindWindowsSID: 46 return nil, errors.New("Windows SIDs not supported on POSIX systems") 47 case OwnershipIdentifierKindName: 48 if userObject, err := userpkg.Lookup(identifier); err != nil { 49 return nil, fmt.Errorf("unable to lookup user by ID: %w", err) 50 } else if u, err := strconv.Atoi(userObject.Uid); err != nil { 51 return nil, fmt.Errorf("unable to convert user ID to numeric value: %w", err) 52 } else if u < 0 { 53 return nil, errors.New("negative user ID retrieved") 54 } else { 55 ownerID = u 56 } 57 default: 58 panic("unhandled ownership identifier kind") 59 } 60 } 61 62 // Attempt to parse and look up group, if specified. 63 groupID := -1 64 if group != "" { 65 switch kind, identifier := ParseOwnershipIdentifier(group); kind { 66 case OwnershipIdentifierKindInvalid: 67 return nil, errors.New("invalid group specification") 68 case OwnershipIdentifierKindPOSIXID: 69 if g, err := strconv.Atoi(identifier); err != nil { 70 return nil, fmt.Errorf("unable to convert group ID to numeric value: %w", err) 71 } else if g < 0 { 72 return nil, errors.New("negative group ID") 73 } else { 74 groupID = g 75 } 76 case OwnershipIdentifierKindWindowsSID: 77 return nil, errors.New("Windows SIDs not supported on POSIX systems") 78 case OwnershipIdentifierKindName: 79 if groupObject, err := userpkg.LookupGroup(identifier); err != nil { 80 return nil, fmt.Errorf("unable to lookup group by ID: %w", err) 81 } else if g, err := strconv.Atoi(groupObject.Gid); err != nil { 82 return nil, fmt.Errorf("unable to convert group ID to numeric value: %w", err) 83 } else if g < 0 { 84 return nil, errors.New("negative group ID retrieved") 85 } else { 86 groupID = g 87 } 88 default: 89 panic("unhandled ownership identifier kind") 90 } 91 } 92 93 // Success. 94 return &OwnershipSpecification{ 95 ownerID: ownerID, 96 groupID: groupID, 97 }, nil 98 } 99 100 // SetPermissionsByPath sets the permissions on the content at the specified 101 // path. Ownership information is set first, followed by permissions extracted 102 // from the mode using ModePermissionsMask. Ownership setting can be skipped 103 // completely by providing a nil OwnershipSpecification or a specification with 104 // both components unset. An OwnershipSpecification may also include only 105 // certain components, in which case only those components will be set. 106 // Permission setting can be skipped by providing a mode value that yields 0 107 // after permission bit masking. 108 func SetPermissionsByPath(path string, ownership *OwnershipSpecification, mode Mode) error { 109 // Set ownership information, if specified. 110 if ownership != nil && (ownership.ownerID != -1 || ownership.groupID != -1) { 111 if err := os.Chown(path, ownership.ownerID, ownership.groupID); err != nil { 112 return fmt.Errorf("unable to set ownership information: %w", err) 113 } 114 } 115 116 // Set permissions, if specified. 117 mode = mode & ModePermissionsMask 118 if mode != 0 { 119 if err := os.Chmod(path, os.FileMode(mode)); err != nil { 120 return fmt.Errorf("unable to set permission bits: %w", err) 121 } 122 } 123 124 // Success. 125 return nil 126 }