2
2
mirror of https://github.com/octoleo/restic.git synced 2024-11-23 05:12:10 +00:00

fs: unexport a several windows functions

This commit is contained in:
Michael Eischer 2024-07-21 16:00:47 +02:00
parent cf051e777a
commit 6d3a5260d3
14 changed files with 119 additions and 117 deletions

View File

@ -56,14 +56,14 @@ var (
errEaValueTooLarge = errors.New("extended attribute value too large")
)
// ExtendedAttribute represents a single Windows EA.
type ExtendedAttribute struct {
// extendedAttribute represents a single Windows EA.
type extendedAttribute struct {
Name string
Value []byte
Flags uint8
}
func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
func parseEa(b []byte) (ea extendedAttribute, nb []byte, err error) {
var info fileFullEaInformation
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
if err != nil {
@ -90,9 +90,9 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
return ea, nb, err
}
// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
// decodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
func decodeExtendedAttributes(b []byte) (eas []extendedAttribute, err error) {
for len(b) != 0 {
ea, nb, err := parseEa(b)
if err != nil {
@ -105,7 +105,7 @@ func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
return eas, err
}
func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
func writeEa(buf *bytes.Buffer, ea *extendedAttribute, last bool) error {
if int(uint8(len(ea.Name))) != len(ea.Name) {
return errEaNameTooLarge
}
@ -153,9 +153,9 @@ func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
return nil
}
// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
// encodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
// buffer for use with BackupWrite, ZwSetEaFile, etc.
func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
func encodeExtendedAttributes(eas []extendedAttribute) ([]byte, error) {
var buf bytes.Buffer
for i := range eas {
last := false
@ -217,11 +217,11 @@ const (
STATUS_NO_EAS_ON_FILE = -1073741742
)
// GetFileEA retrieves the extended attributes for the file represented by `handle`. The
// fgetEA retrieves the extended attributes for the file represented by `handle`. The
// `handle` must have been opened with file access flag FILE_READ_EA (0x8).
// The extended file attribute names in windows are case-insensitive and when fetching
// the attributes the names are generally returned in UPPER case.
func GetFileEA(handle windows.Handle) ([]ExtendedAttribute, error) {
func fgetEA(handle windows.Handle) ([]extendedAttribute, error) {
// default buffer size to start with
bufLen := 1024
buf := make([]byte, bufLen)
@ -246,13 +246,13 @@ func GetFileEA(handle windows.Handle) ([]ExtendedAttribute, error) {
}
break
}
return DecodeExtendedAttributes(buf)
return decodeExtendedAttributes(buf)
}
// SetFileEA sets the extended attributes for the file represented by `handle`. The
// fsetEA sets the extended attributes for the file represented by `handle`. The
// handle must have been opened with the file access flag FILE_WRITE_EA(0x10).
func SetFileEA(handle windows.Handle, attrs []ExtendedAttribute) error {
encodedEA, err := EncodeExtendedAttributes(attrs)
func fsetEA(handle windows.Handle, attrs []extendedAttribute) error {
encodedEA, err := encodeExtendedAttributes(attrs)
if err != nil {
return fmt.Errorf("failed to encoded extended attributes: %w", err)
}
@ -285,8 +285,8 @@ func setFileEA(handle windows.Handle, iosb *ioStatusBlock, buf *uint8, bufLen ui
return
}
// PathSupportsExtendedAttributes returns true if the path supports extended attributes.
func PathSupportsExtendedAttributes(path string) (supported bool, err error) {
// pathSupportsExtendedAttributes returns true if the path supports extended attributes.
func pathSupportsExtendedAttributes(path string) (supported bool, err error) {
var fileSystemFlags uint32
utf16Path, err := windows.UTF16PtrFromString(path)
if err != nil {
@ -300,8 +300,8 @@ func PathSupportsExtendedAttributes(path string) (supported bool, err error) {
return supported, nil
}
// GetVolumePathName returns the volume path name for the given path.
func GetVolumePathName(path string) (volumeName string, err error) {
// getVolumePathName returns the volume path name for the given path.
func getVolumePathName(path string) (volumeName string, err error) {
utf16Path, err := windows.UTF16PtrFromString(path)
if err != nil {
return "", err

View File

@ -46,7 +46,7 @@ import (
// under MIT license.
var (
testEas = []ExtendedAttribute{
testEas = []extendedAttribute{
{Name: "foo", Value: []byte("bar")},
{Name: "fizz", Value: []byte("buzz")},
}
@ -58,14 +58,14 @@ var (
)
func TestRoundTripEas(t *testing.T) {
b, err := EncodeExtendedAttributes(testEas)
b, err := encodeExtendedAttributes(testEas)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(testEasEncoded, b) {
t.Fatalf("Encoded mismatch %v %v", testEasEncoded, b)
}
eas, err := DecodeExtendedAttributes(b)
eas, err := decodeExtendedAttributes(b)
if err != nil {
t.Fatal(err)
}
@ -75,7 +75,7 @@ func TestRoundTripEas(t *testing.T) {
}
func TestEasDontNeedPaddingAtEnd(t *testing.T) {
eas, err := DecodeExtendedAttributes(testEasNotPadded)
eas, err := decodeExtendedAttributes(testEasNotPadded)
if err != nil {
t.Fatal(err)
}
@ -85,21 +85,21 @@ func TestEasDontNeedPaddingAtEnd(t *testing.T) {
}
func TestTruncatedEasFailCorrectly(t *testing.T) {
_, err := DecodeExtendedAttributes(testEasTruncated)
_, err := decodeExtendedAttributes(testEasTruncated)
if err == nil {
t.Fatal("expected error")
}
}
func TestNilEasEncodeAndDecodeAsNil(t *testing.T) {
b, err := EncodeExtendedAttributes(nil)
b, err := encodeExtendedAttributes(nil)
if err != nil {
t.Fatal(err)
}
if len(b) != 0 {
t.Fatal("expected empty")
}
eas, err := DecodeExtendedAttributes(nil)
eas, err := decodeExtendedAttributes(nil)
if err != nil {
t.Fatal(err)
}
@ -178,8 +178,8 @@ func setupTestFolder(t *testing.T) string {
return testfolderPath
}
func generateTestEAs(t *testing.T, nAttrs int, path string) []ExtendedAttribute {
testEAs := make([]ExtendedAttribute, nAttrs)
func generateTestEAs(t *testing.T, nAttrs int, path string) []extendedAttribute {
testEAs := make([]extendedAttribute, nAttrs)
for i := 0; i < nAttrs; i++ {
testEAs[i].Name = fmt.Sprintf("TESTEA%d", i+1)
testEAs[i].Value = make([]byte, getRandomInt())
@ -231,12 +231,12 @@ func cleanupTestFile(t *testing.T, path string) {
}
}
func testSetGetEA(t *testing.T, path string, handle windows.Handle, testEAs []ExtendedAttribute) {
if err := SetFileEA(handle, testEAs); err != nil {
func testSetGetEA(t *testing.T, path string, handle windows.Handle, testEAs []extendedAttribute) {
if err := fsetEA(handle, testEAs); err != nil {
t.Fatalf("set EA for path %s failed: %s", path, err)
}
readEAs, err := GetFileEA(handle)
readEAs, err := fgetEA(handle)
if err != nil {
t.Fatalf("get EA for path %s failed: %s", path, err)
}
@ -262,7 +262,7 @@ func TestPathSupportsExtendedAttributes(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
supported, err := PathSupportsExtendedAttributes(tc.path)
supported, err := pathSupportsExtendedAttributes(tc.path)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
@ -273,7 +273,7 @@ func TestPathSupportsExtendedAttributes(t *testing.T) {
}
// Test with an invalid path
_, err := PathSupportsExtendedAttributes("Z:\\NonExistentPath-UAS664da5s4dyu56das45f5as")
_, err := pathSupportsExtendedAttributes("Z:\\NonExistentPath-UAS664da5s4dyu56das45f5as")
if err == nil {
t.Error("Expected an error for non-existent path, but got nil")
}
@ -305,7 +305,7 @@ func TestGetVolumePathName(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
volumeName, err := GetVolumePathName(tc.path)
volumeName, err := getVolumePathName(tc.path)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
@ -316,7 +316,7 @@ func TestGetVolumePathName(t *testing.T) {
}
// Test with an invalid path
_, err := GetVolumePathName("Z:\\NonExistentPath")
_, err := getVolumePathName("Z:\\NonExistentPath")
if err == nil {
t.Error("Expected an error for non-existent path, but got nil")
}

View File

@ -37,8 +37,8 @@ func isNotSupported(err error) bool {
return false
}
// Chmod changes the mode of the named file to mode.
func Chmod(name string, mode os.FileMode) error {
// chmod changes the mode of the named file to mode.
func chmod(name string, mode os.FileMode) error {
err := os.Chmod(fixpath(name), mode)
// ignore the error if the FS does not support setting this mode (e.g. CIFS with gvfs on Linux)

View File

@ -75,17 +75,17 @@ func TempFile(dir, prefix string) (f *os.File, err error) {
}
// Chmod changes the mode of the named file to mode.
func Chmod(name string, mode os.FileMode) error {
func chmod(name string, mode os.FileMode) error {
return os.Chmod(fixpath(name), mode)
}
// ClearSystem removes the system attribute from the file.
func ClearSystem(path string) error {
return ClearAttribute(path, windows.FILE_ATTRIBUTE_SYSTEM)
// clearSystem removes the system attribute from the file.
func clearSystem(path string) error {
return clearAttribute(path, windows.FILE_ATTRIBUTE_SYSTEM)
}
// ClearAttribute removes the specified attribute from the file.
func ClearAttribute(path string, attribute uint32) error {
// clearAttribute removes the specified attribute from the file.
func clearAttribute(path string, attribute uint32) error {
ptr, err := windows.UTF16PtrFromString(fixpath(path))
if err != nil {
return err
@ -105,8 +105,8 @@ func ClearAttribute(path string, attribute uint32) error {
return nil
}
// OpenHandleForEA return a file handle for file or dir for setting/getting EAs
func OpenHandleForEA(nodeType restic.NodeType, path string, writeAccess bool) (handle windows.Handle, err error) {
// openHandleForEA return a file handle for file or dir for setting/getting EAs
func openHandleForEA(nodeType restic.NodeType, path string, writeAccess bool) (handle windows.Handle, err error) {
path = fixpath(path)
fileAccess := windows.FILE_READ_EA
if writeAccess {

View File

@ -79,7 +79,7 @@ func parseMountPoints(list string, msgError ErrorHandler) (volumes map[string]st
return
}
for _, s := range strings.Split(list, ";") {
if v, err := GetVolumeNameForVolumeMountPoint(s); err != nil {
if v, err := getVolumeNameForVolumeMountPoint(s); err != nil {
msgError(s, errors.Errorf("failed to parse vss.exclude-volumes [%s]: %s", s, err))
} else {
if volumes == nil {
@ -146,7 +146,7 @@ func (fs *LocalVss) isMountPointIncluded(mountPoint string) bool {
return true
}
volume, err := GetVolumeNameForVolumeMountPoint(mountPoint)
volume, err := getVolumeNameForVolumeMountPoint(mountPoint)
if err != nil {
fs.msgError(mountPoint, errors.Errorf("failed to get volume from mount point [%s]: %s", mountPoint, err))
return true

View File

@ -1,3 +1,4 @@
//go:build windows
// +build windows
package fs
@ -120,10 +121,10 @@ func TestVSSConfig(t *testing.T) {
func TestParseMountPoints(t *testing.T) {
volumeMatch := regexp.MustCompile(`^\\\\\?\\Volume\{[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}\}\\$`)
// It's not a good idea to test functions based on GetVolumeNameForVolumeMountPoint by calling
// GetVolumeNameForVolumeMountPoint itself, but we have restricted test environment:
// It's not a good idea to test functions based on getVolumeNameForVolumeMountPoint by calling
// getVolumeNameForVolumeMountPoint itself, but we have restricted test environment:
// cannot manage volumes and can only be sure that the mount point C:\ exists
sysVolume, err := GetVolumeNameForVolumeMountPoint("C:")
sysVolume, err := getVolumeNameForVolumeMountPoint("C:")
if err != nil {
t.Fatal(err)
}

View File

@ -306,7 +306,7 @@ func nodeRestoreMetadata(node *restic.Node, path string, warn func(msg string))
// calling Chmod below will no longer allow any modifications to be made on the file and the
// calls above would fail.
if node.Type != restic.NodeTypeSymlink {
if err := Chmod(path, node.Mode); err != nil {
if err := chmod(path, node.Mode); err != nil {
if firsterr == nil {
firsterr = errors.WithStack(err)
}

View File

@ -85,9 +85,9 @@ func nodeRestoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error
func nodeRestoreExtendedAttributes(node *restic.Node, path string) (err error) {
count := len(node.ExtendedAttributes)
if count > 0 {
eas := make([]ExtendedAttribute, count)
eas := make([]extendedAttribute, count)
for i, attr := range node.ExtendedAttributes {
eas[i] = ExtendedAttribute{Name: attr.Name, Value: attr.Value}
eas[i] = extendedAttribute{Name: attr.Name, Value: attr.Value}
}
if errExt := restoreExtendedAttributes(node.Type, path, eas); errExt != nil {
return errExt
@ -99,7 +99,7 @@ func nodeRestoreExtendedAttributes(node *restic.Node, path string) (err error) {
// fill extended attributes in the node. This also includes the Generic attributes for windows.
func nodeFillExtendedAttributes(node *restic.Node, path string, _ bool) (err error) {
var fileHandle windows.Handle
if fileHandle, err = OpenHandleForEA(node.Type, path, false); fileHandle == 0 {
if fileHandle, err = openHandleForEA(node.Type, path, false); fileHandle == 0 {
return nil
}
if err != nil {
@ -107,8 +107,8 @@ func nodeFillExtendedAttributes(node *restic.Node, path string, _ bool) (err err
}
defer closeFileHandle(fileHandle, path) // Replaced inline defer with named function call
//Get the windows Extended Attributes using the file handle
var extAtts []ExtendedAttribute
extAtts, err = GetFileEA(fileHandle)
var extAtts []extendedAttribute
extAtts, err = fgetEA(fileHandle)
debug.Log("fillExtendedAttributes(%v) %v", path, extAtts)
if err != nil {
return errors.Errorf("get EA failed for path %v, with: %v", path, err)
@ -139,9 +139,9 @@ func closeFileHandle(fileHandle windows.Handle, path string) {
// restoreExtendedAttributes handles restore of the Windows Extended Attributes to the specified path.
// The Windows API requires setting of all the Extended Attributes in one call.
func restoreExtendedAttributes(nodeType restic.NodeType, path string, eas []ExtendedAttribute) (err error) {
func restoreExtendedAttributes(nodeType restic.NodeType, path string, eas []extendedAttribute) (err error) {
var fileHandle windows.Handle
if fileHandle, err = OpenHandleForEA(nodeType, path, true); fileHandle == 0 {
if fileHandle, err = openHandleForEA(nodeType, path, true); fileHandle == 0 {
return nil
}
if err != nil {
@ -150,7 +150,7 @@ func restoreExtendedAttributes(nodeType restic.NodeType, path string, eas []Exte
defer closeFileHandle(fileHandle, path) // Replaced inline defer with named function call
// clear old unexpected xattrs by setting them to an empty value
oldEAs, err := GetFileEA(fileHandle)
oldEAs, err := fgetEA(fileHandle)
if err != nil {
return err
}
@ -165,11 +165,11 @@ func restoreExtendedAttributes(nodeType restic.NodeType, path string, eas []Exte
}
if !found {
eas = append(eas, ExtendedAttribute{Name: oldEA.Name, Value: nil})
eas = append(eas, extendedAttribute{Name: oldEA.Name, Value: nil})
}
}
if err = SetFileEA(fileHandle, eas); err != nil {
if err = fsetEA(fileHandle, eas); err != nil {
return errors.Errorf("set EA failed for path %v, with: %v", path, err)
}
return nil
@ -230,7 +230,7 @@ func nodeRestoreGenericAttributes(node *restic.Node, path string, warn func(msg
}
}
if windowsAttributes.SecurityDescriptor != nil {
if err := SetSecurityDescriptor(path, windowsAttributes.SecurityDescriptor); err != nil {
if err := setSecurityDescriptor(path, windowsAttributes.SecurityDescriptor); err != nil {
errs = append(errs, fmt.Errorf("error restoring security descriptor for: %s : %v", path, err))
}
}
@ -296,7 +296,7 @@ func fixEncryptionAttribute(path string, attrs *uint32, pathPointer *uint16) (er
if err != nil {
return fmt.Errorf("failed to encrypt file: failed to reset permissions: %s : %v", path, err)
}
err = ClearSystem(path)
err = clearSystem(path)
if err != nil {
return fmt.Errorf("failed to encrypt file: failed to clear system flag: %s : %v", path, err)
}
@ -324,7 +324,7 @@ func fixEncryptionAttribute(path string, attrs *uint32, pathPointer *uint16) (er
if err != nil {
return fmt.Errorf("failed to encrypt file: failed to reset permissions: %s : %v", path, err)
}
err = ClearSystem(path)
err = clearSystem(path)
if err != nil {
return fmt.Errorf("failed to decrypt file: failed to clear system flag: %s : %v", path, err)
}
@ -392,7 +392,7 @@ func nodeFillGenericAttributes(node *restic.Node, path string, fi os.FileInfo, s
if err != nil {
return false, err
}
if sd, err = GetSecurityDescriptor(path); err != nil {
if sd, err = getSecurityDescriptor(path); err != nil {
return allowExtended, err
}
}
@ -422,7 +422,7 @@ func checkAndStoreEASupport(path string) (isEASupportedVolume bool, err error) {
return eaSupportedValue.(bool), nil
}
// If not found, check if EA is supported with manually prepared volume name
isEASupportedVolume, err = PathSupportsExtendedAttributes(volumeName + `\`)
isEASupportedVolume, err = pathSupportsExtendedAttributes(volumeName + `\`)
// If the prepared volume name is not valid, we will fetch the actual volume name next.
if err != nil && !errors.Is(err, windows.DNS_ERROR_INVALID_NAME) {
debug.Log("Error checking if extended attributes are supported for prepared volume name %s: %v", volumeName, err)
@ -431,8 +431,8 @@ func checkAndStoreEASupport(path string) (isEASupportedVolume bool, err error) {
return false, nil
}
}
// If an entry is not found, get the actual volume name using the GetVolumePathName function
volumeNameActual, err := GetVolumePathName(path)
// If an entry is not found, get the actual volume name
volumeNameActual, err := getVolumePathName(path)
if err != nil {
debug.Log("Error getting actual volume name %s for path %s: %v", volumeName, path, err)
// There can be multiple errors like path does not exist, bad network path, etc.
@ -447,7 +447,7 @@ func checkAndStoreEASupport(path string) (isEASupportedVolume bool, err error) {
return eaSupportedValue.(bool), nil
}
// If the actual volume name is different and is not in the map, again check if the new volume supports extended attributes with the actual volume name
isEASupportedVolume, err = PathSupportsExtendedAttributes(volumeNameActual + `\`)
isEASupportedVolume, err = pathSupportsExtendedAttributes(volumeNameActual + `\`)
// Debug log for cases where the prepared volume name is not valid
if err != nil {
debug.Log("Error checking if extended attributes are supported for actual volume name %s: %v", volumeNameActual, err)

View File

@ -23,10 +23,10 @@ import (
func TestRestoreSecurityDescriptors(t *testing.T) {
t.Parallel()
tempDir := t.TempDir()
for i, sd := range TestFileSDs {
for i, sd := range testFileSDs {
testRestoreSecurityDescriptor(t, sd, tempDir, restic.NodeTypeFile, fmt.Sprintf("testfile%d", i))
}
for i, sd := range TestDirSDs {
for i, sd := range testDirSDs {
testRestoreSecurityDescriptor(t, sd, tempDir, restic.NodeTypeDir, fmt.Sprintf("testdir%d", i))
}
}
@ -47,13 +47,13 @@ func testRestoreSecurityDescriptor(t *testing.T, sd string, tempDir string, file
sdByteFromRestoredNode := getWindowsAttr(t, testPath, node).SecurityDescriptor
// Get the security descriptor for the test path after the restore.
sdBytesFromRestoredPath, err := GetSecurityDescriptor(testPath)
sdBytesFromRestoredPath, err := getSecurityDescriptor(testPath)
test.OK(t, errors.Wrapf(err, "Error while getting the security descriptor for: %s", testPath))
// Compare the input SD and the SD got from the restored file.
CompareSecurityDescriptors(t, testPath, sdInputBytes, *sdBytesFromRestoredPath)
compareSecurityDescriptors(t, testPath, sdInputBytes, *sdBytesFromRestoredPath)
// Compare the SD got from node constructed from the restored file info and the SD got directly from the restored file.
CompareSecurityDescriptors(t, testPath, *sdByteFromRestoredNode, *sdBytesFromRestoredPath)
compareSecurityDescriptors(t, testPath, *sdByteFromRestoredNode, *sdBytesFromRestoredPath)
}
func getNode(name string, fileType restic.NodeType, genericAttributes map[restic.GenericAttributeType]json.RawMessage) restic.Node {
@ -312,12 +312,12 @@ func TestRestoreExtendedAttributes(t *testing.T) {
test.OK(t, errors.Wrapf(err, "Error closing file for: %s", testPath))
}()
extAttr, err := GetFileEA(handle)
extAttr, err := fgetEA(handle)
test.OK(t, errors.Wrapf(err, "Error getting extended attributes for: %s", testPath))
test.Equals(t, len(node.ExtendedAttributes), len(extAttr))
for _, expectedExtAttr := range node.ExtendedAttributes {
var foundExtAttr *ExtendedAttribute
var foundExtAttr *extendedAttribute
for _, ea := range extAttr {
if strings.EqualFold(ea.Name, expectedExtAttr.Name) {
foundExtAttr = &ea
@ -491,13 +491,13 @@ func TestPrepareVolumeName(t *testing.T) {
test.Equals(t, tc.expectedVolume, volume)
if tc.isRealPath {
isEASupportedVolume, err := PathSupportsExtendedAttributes(volume + `\`)
isEASupportedVolume, err := pathSupportsExtendedAttributes(volume + `\`)
// If the prepared volume name is not valid, we will next fetch the actual volume name.
test.OK(t, err)
test.Equals(t, tc.expectedEASupported, isEASupportedVolume)
actualVolume, err := GetVolumePathName(tc.path)
actualVolume, err := getVolumePathName(tc.path)
test.OK(t, err)
test.Equals(t, tc.expectedVolume, actualVolume)
}

View File

@ -19,14 +19,14 @@ var (
onceBackup sync.Once
onceRestore sync.Once
// SeBackupPrivilege allows the application to bypass file and directory ACLs to back up files and directories.
SeBackupPrivilege = "SeBackupPrivilege"
// SeRestorePrivilege allows the application to bypass file and directory ACLs to restore files and directories.
SeRestorePrivilege = "SeRestorePrivilege"
// SeSecurityPrivilege allows read and write access to all SACLs.
SeSecurityPrivilege = "SeSecurityPrivilege"
// SeTakeOwnershipPrivilege allows the application to take ownership of files and directories, regardless of the permissions set on them.
SeTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege"
// seBackupPrivilege allows the application to bypass file and directory ACLs to back up files and directories.
seBackupPrivilege = "SeBackupPrivilege"
// seRestorePrivilege allows the application to bypass file and directory ACLs to restore files and directories.
seRestorePrivilege = "SeRestorePrivilege"
// seSecurityPrivilege allows read and write access to all SACLs.
seSecurityPrivilege = "SeSecurityPrivilege"
// seTakeOwnershipPrivilege allows the application to take ownership of files and directories, regardless of the permissions set on them.
seTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege"
lowerPrivileges atomic.Bool
)
@ -40,10 +40,10 @@ var lowBackupSecurityFlags windows.SECURITY_INFORMATION = windows.OWNER_SECURITY
// Flags for restore without admin permissions. If there are no admin permissions, only the DACL from the SD can be restored and owner and group will be set based on the current user.
var lowRestoreSecurityFlags windows.SECURITY_INFORMATION = windows.DACL_SECURITY_INFORMATION | windows.ATTRIBUTE_SECURITY_INFORMATION | windows.PROTECTED_DACL_SECURITY_INFORMATION
// GetSecurityDescriptor takes the path of the file and returns the SecurityDescriptor for the file.
// getSecurityDescriptor takes the path of the file and returns the SecurityDescriptor for the file.
// This needs admin permissions or SeBackupPrivilege for getting the full SD.
// If there are no admin permissions, only the current user's owner, group and DACL will be got.
func GetSecurityDescriptor(filePath string) (securityDescriptor *[]byte, err error) {
func getSecurityDescriptor(filePath string) (securityDescriptor *[]byte, err error) {
onceBackup.Do(enableBackupPrivilege)
var sd *windows.SECURITY_DESCRIPTOR
@ -59,7 +59,7 @@ func GetSecurityDescriptor(filePath string) (securityDescriptor *[]byte, err err
if !useLowerPrivileges && isHandlePrivilegeNotHeldError(err) {
// If ERROR_PRIVILEGE_NOT_HELD is encountered, fallback to backups/restores using lower non-admin privileges.
lowerPrivileges.Store(true)
return GetSecurityDescriptor(filePath)
return getSecurityDescriptor(filePath)
} else if errors.Is(err, windows.ERROR_NOT_SUPPORTED) {
return nil, nil
} else {
@ -74,15 +74,15 @@ func GetSecurityDescriptor(filePath string) (securityDescriptor *[]byte, err err
return &sdBytes, nil
}
// SetSecurityDescriptor sets the SecurityDescriptor for the file at the specified path.
// setSecurityDescriptor sets the SecurityDescriptor for the file at the specified path.
// This needs admin permissions or SeRestorePrivilege, SeSecurityPrivilege and SeTakeOwnershipPrivilege
// for setting the full SD.
// If there are no admin permissions/required privileges, only the DACL from the SD can be set and
// owner and group will be set based on the current user.
func SetSecurityDescriptor(filePath string, securityDescriptor *[]byte) error {
func setSecurityDescriptor(filePath string, securityDescriptor *[]byte) error {
onceRestore.Do(enableRestorePrivilege)
// Set the security descriptor on the file
sd, err := SecurityDescriptorBytesToStruct(*securityDescriptor)
sd, err := securityDescriptorBytesToStruct(*securityDescriptor)
if err != nil {
return fmt.Errorf("error converting bytes to security descriptor: %w", err)
}
@ -120,7 +120,7 @@ func SetSecurityDescriptor(filePath string, securityDescriptor *[]byte) error {
if !useLowerPrivileges && isHandlePrivilegeNotHeldError(err) {
// If ERROR_PRIVILEGE_NOT_HELD is encountered, fallback to backups/restores using lower non-admin privileges.
lowerPrivileges.Store(true)
return SetSecurityDescriptor(filePath, securityDescriptor)
return setSecurityDescriptor(filePath, securityDescriptor)
} else {
return fmt.Errorf("set named security info failed with: %w", err)
}
@ -150,7 +150,7 @@ func setNamedSecurityInfoLow(filePath string, dacl *windows.ACL) error {
// enableBackupPrivilege enables privilege for backing up security descriptors
func enableBackupPrivilege() {
err := enableProcessPrivileges([]string{SeBackupPrivilege})
err := enableProcessPrivileges([]string{seBackupPrivilege})
if err != nil {
debug.Log("error enabling backup privilege: %v", err)
}
@ -158,7 +158,7 @@ func enableBackupPrivilege() {
// enableBackupPrivilege enables privilege for restoring security descriptors
func enableRestorePrivilege() {
err := enableProcessPrivileges([]string{SeRestorePrivilege, SeSecurityPrivilege, SeTakeOwnershipPrivilege})
err := enableProcessPrivileges([]string{seRestorePrivilege, seSecurityPrivilege, seTakeOwnershipPrivilege})
if err != nil {
debug.Log("error enabling restore/security privilege: %v", err)
}
@ -174,9 +174,9 @@ func isHandlePrivilegeNotHeldError(err error) bool {
return false
}
// SecurityDescriptorBytesToStruct converts the security descriptor bytes representation
// securityDescriptorBytesToStruct converts the security descriptor bytes representation
// into a pointer to windows SECURITY_DESCRIPTOR.
func SecurityDescriptorBytesToStruct(sd []byte) (*windows.SECURITY_DESCRIPTOR, error) {
func securityDescriptorBytesToStruct(sd []byte) (*windows.SECURITY_DESCRIPTOR, error) {
if l := int(unsafe.Sizeof(windows.SECURITY_DESCRIPTOR{})); len(sd) < l {
return nil, fmt.Errorf("securityDescriptor (%d) smaller than expected (%d): %w", len(sd), l, windows.ERROR_INCORRECT_SIZE)
}
@ -245,13 +245,13 @@ var (
privNameMutex sync.Mutex
)
// PrivilegeError represents an error enabling privileges.
type PrivilegeError struct {
// privilegeError represents an error enabling privileges.
type privilegeError struct {
privileges []uint64
}
// Error returns the string message for the error.
func (e *PrivilegeError) Error() string {
func (e *privilegeError) Error() string {
s := "Could not enable privilege "
if len(e.privileges) > 1 {
s = "Could not enable privileges "

View File

@ -28,7 +28,7 @@ func TestSetGetFileSecurityDescriptors(t *testing.T) {
}
}()
testSecurityDescriptors(t, TestFileSDs, testfilePath)
testSecurityDescriptors(t, testFileSDs, testfilePath)
}
func TestSetGetFolderSecurityDescriptors(t *testing.T) {
@ -40,7 +40,7 @@ func TestSetGetFolderSecurityDescriptors(t *testing.T) {
t.Fatalf("failed to create temporary file: %s", err)
}
testSecurityDescriptors(t, TestDirSDs, testfolderPath)
testSecurityDescriptors(t, testDirSDs, testfolderPath)
}
func testSecurityDescriptors(t *testing.T, testSDs []string, testPath string) {
@ -48,13 +48,13 @@ func testSecurityDescriptors(t *testing.T, testSDs []string, testPath string) {
sdInputBytes, err := base64.StdEncoding.DecodeString(testSD)
test.OK(t, errors.Wrapf(err, "Error decoding SD: %s", testPath))
err = SetSecurityDescriptor(testPath, &sdInputBytes)
err = setSecurityDescriptor(testPath, &sdInputBytes)
test.OK(t, errors.Wrapf(err, "Error setting file security descriptor for: %s", testPath))
var sdOutputBytes *[]byte
sdOutputBytes, err = GetSecurityDescriptor(testPath)
sdOutputBytes, err = getSecurityDescriptor(testPath)
test.OK(t, errors.Wrapf(err, "Error getting file security descriptor for: %s", testPath))
CompareSecurityDescriptors(t, testPath, sdInputBytes, *sdOutputBytes)
compareSecurityDescriptors(t, testPath, sdInputBytes, *sdOutputBytes)
}
}

View File

@ -13,18 +13,18 @@ import (
)
var (
TestFileSDs = []string{"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAfAAEAAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABAUAP8BHwABAQAAAAAABRIAAAAAEBgA/wEfAAECAAAAAAAFIAAAACACAAAAECQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAA=",
testFileSDs = []string{"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAfAAEAAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABAUAP8BHwABAQAAAAAABRIAAAAAEBgA/wEfAAECAAAAAAAFIAAAACACAAAAECQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAA=",
"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAyAAHAAAAAAAUAKkAEgABAQAAAAAABQcAAAAAABQAiQASAAEBAAAAAAAFBwAAAAAAJACpABIAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSar7QMAAAAAJAC/ARMAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSar6gMAAAAAFAD/AR8AAQEAAAAAAAUSAAAAAAAYAP8BHwABAgAAAAAABSAAAAAgAgAAAAAkAP8BHwABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAA",
"AQAUvBQAAAAwAAAA7AAAAEwAAAABBQAAAAAABRUAAAAvr7t03PyHGk2FokNHCAAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAoAAFAAAAAAAkAP8BHwABBQAAAAAABRUAAAAvr7t03PyHGk2FokNHCAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABAUAP8BHwABAQAAAAAABRIAAAAAEBgA/wEfAAECAAAAAAAFIAAAACACAAAAECQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAACAHQAAwAAAAKAJAC/AQIAAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDtgQAAALAJAC/AQMAAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDPgkAAAJAJAD/AQ8AAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDtQQAAA==",
}
TestDirSDs = []string{"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAfAAEAAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABMUAP8BHwABAQAAAAAABRIAAAAAExgA/wEfAAECAAAAAAAFIAAAACACAAAAEyQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAA=",
testDirSDs = []string{"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAfAAEAAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABMUAP8BHwABAQAAAAAABRIAAAAAExgA/wEfAAECAAAAAAAFIAAAACACAAAAEyQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAA=",
"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIA3AAIAAAAAAIUAKkAEgABAQAAAAAABQcAAAAAAxQAiQASAAEBAAAAAAAFBwAAAAAAJACpABIAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSar7QMAAAAAJAC/ARMAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSar6gMAAAALFAC/ARMAAQEAAAAAAAMAAAAAABMUAP8BHwABAQAAAAAABRIAAAAAExgA/wEfAAECAAAAAAAFIAAAACACAAAAEyQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAA=",
"AQAUvBQAAAAwAAAA7AAAAEwAAAABBQAAAAAABRUAAAAvr7t03PyHGk2FokNHCAAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAoAAFAAAAAAAkAP8BHwABBQAAAAAABRUAAAAvr7t03PyHGk2FokNHCAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABMUAP8BHwABAQAAAAAABRIAAAAAExgA/wEfAAECAAAAAAAFIAAAACACAAAAEyQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAACAHQAAwAAAAKAJAC/AQIAAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDtgQAAALAJAC/AQMAAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDPgkAAAJAJAD/AQ8AAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDtQQAAA==",
}
)
// IsAdmin checks if current user is an administrator.
func IsAdmin() (isAdmin bool, err error) {
// isAdmin checks if current user is an administrator.
func isAdmin() (isAdmin bool, err error) {
var sid *windows.SID
err = windows.AllocateAndInitializeSid(&windows.SECURITY_NT_AUTHORITY, 2, windows.SECURITY_BUILTIN_DOMAIN_RID, windows.DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0, &sid)
@ -40,15 +40,15 @@ func IsAdmin() (isAdmin bool, err error) {
return member, nil
}
// CompareSecurityDescriptors runs tests for comparing 2 security descriptors in []byte format.
func CompareSecurityDescriptors(t *testing.T, testPath string, sdInputBytes, sdOutputBytes []byte) {
sdInput, err := SecurityDescriptorBytesToStruct(sdInputBytes)
// compareSecurityDescriptors runs tests for comparing 2 security descriptors in []byte format.
func compareSecurityDescriptors(t *testing.T, testPath string, sdInputBytes, sdOutputBytes []byte) {
sdInput, err := securityDescriptorBytesToStruct(sdInputBytes)
test.OK(t, errors.Wrapf(err, "Error converting SD to struct for: %s", testPath))
sdOutput, err := SecurityDescriptorBytesToStruct(sdOutputBytes)
sdOutput, err := securityDescriptorBytesToStruct(sdOutputBytes)
test.OK(t, errors.Wrapf(err, "Error converting SD to struct for: %s", testPath))
isAdmin, err := IsAdmin()
isAdmin, err := isAdmin()
test.OK(t, errors.Wrapf(err, "Error checking if user is admin: %s", testPath))
var ownerExpected *windows.SID

View File

@ -33,9 +33,9 @@ func HasSufficientPrivilegesForVSS() error {
return errors.New("VSS snapshots are only supported on windows")
}
// GetVolumeNameForVolumeMountPoint add trailing backslash to input parameter
// getVolumeNameForVolumeMountPoint add trailing backslash to input parameter
// and calls the equivalent windows api.
func GetVolumeNameForVolumeMountPoint(mountPoint string) (string, error) {
func getVolumeNameForVolumeMountPoint(mountPoint string) (string, error) {
return mountPoint, nil
}

View File

@ -22,6 +22,7 @@ import (
type HRESULT uint
// HRESULT constant values necessary for using VSS api.
//
//nolint:golint
const (
S_OK HRESULT = 0x00000000
@ -830,9 +831,9 @@ func HasSufficientPrivilegesForVSS() error {
return err
}
// GetVolumeNameForVolumeMountPoint add trailing backslash to input parameter
// getVolumeNameForVolumeMountPoint add trailing backslash to input parameter
// and calls the equivalent windows api.
func GetVolumeNameForVolumeMountPoint(mountPoint string) (string, error) {
func getVolumeNameForVolumeMountPoint(mountPoint string) (string, error) {
if mountPoint != "" && mountPoint[len(mountPoint)-1] != filepath.Separator {
mountPoint += string(filepath.Separator)
}