fs: unexport a several windows functions
This commit is contained in:
parent
cf051e777a
commit
6d3a5260d3
14 changed files with 119 additions and 117 deletions
|
@ -56,14 +56,14 @@ var (
|
||||||
errEaValueTooLarge = errors.New("extended attribute value too large")
|
errEaValueTooLarge = errors.New("extended attribute value too large")
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExtendedAttribute represents a single Windows EA.
|
// extendedAttribute represents a single Windows EA.
|
||||||
type ExtendedAttribute struct {
|
type extendedAttribute struct {
|
||||||
Name string
|
Name string
|
||||||
Value []byte
|
Value []byte
|
||||||
Flags uint8
|
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
|
var info fileFullEaInformation
|
||||||
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
|
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -90,9 +90,9 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
|
||||||
return ea, nb, err
|
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.
|
// 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 {
|
for len(b) != 0 {
|
||||||
ea, nb, err := parseEa(b)
|
ea, nb, err := parseEa(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -105,7 +105,7 @@ func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
|
||||||
return eas, err
|
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) {
|
if int(uint8(len(ea.Name))) != len(ea.Name) {
|
||||||
return errEaNameTooLarge
|
return errEaNameTooLarge
|
||||||
}
|
}
|
||||||
|
@ -153,9 +153,9 @@ func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
|
||||||
return nil
|
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.
|
// buffer for use with BackupWrite, ZwSetEaFile, etc.
|
||||||
func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
|
func encodeExtendedAttributes(eas []extendedAttribute) ([]byte, error) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
for i := range eas {
|
for i := range eas {
|
||||||
last := false
|
last := false
|
||||||
|
@ -217,11 +217,11 @@ const (
|
||||||
STATUS_NO_EAS_ON_FILE = -1073741742
|
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).
|
// `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 extended file attribute names in windows are case-insensitive and when fetching
|
||||||
// the attributes the names are generally returned in UPPER case.
|
// 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
|
// default buffer size to start with
|
||||||
bufLen := 1024
|
bufLen := 1024
|
||||||
buf := make([]byte, bufLen)
|
buf := make([]byte, bufLen)
|
||||||
|
@ -246,13 +246,13 @@ func GetFileEA(handle windows.Handle) ([]ExtendedAttribute, error) {
|
||||||
}
|
}
|
||||||
break
|
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).
|
// handle must have been opened with the file access flag FILE_WRITE_EA(0x10).
|
||||||
func SetFileEA(handle windows.Handle, attrs []ExtendedAttribute) error {
|
func fsetEA(handle windows.Handle, attrs []extendedAttribute) error {
|
||||||
encodedEA, err := EncodeExtendedAttributes(attrs)
|
encodedEA, err := encodeExtendedAttributes(attrs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to encoded extended attributes: %w", err)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// PathSupportsExtendedAttributes returns true if the path supports extended attributes.
|
// pathSupportsExtendedAttributes returns true if the path supports extended attributes.
|
||||||
func PathSupportsExtendedAttributes(path string) (supported bool, err error) {
|
func pathSupportsExtendedAttributes(path string) (supported bool, err error) {
|
||||||
var fileSystemFlags uint32
|
var fileSystemFlags uint32
|
||||||
utf16Path, err := windows.UTF16PtrFromString(path)
|
utf16Path, err := windows.UTF16PtrFromString(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -300,8 +300,8 @@ func PathSupportsExtendedAttributes(path string) (supported bool, err error) {
|
||||||
return supported, nil
|
return supported, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVolumePathName returns the volume path name for the given path.
|
// getVolumePathName returns the volume path name for the given path.
|
||||||
func GetVolumePathName(path string) (volumeName string, err error) {
|
func getVolumePathName(path string) (volumeName string, err error) {
|
||||||
utf16Path, err := windows.UTF16PtrFromString(path)
|
utf16Path, err := windows.UTF16PtrFromString(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
|
@ -46,7 +46,7 @@ import (
|
||||||
// under MIT license.
|
// under MIT license.
|
||||||
|
|
||||||
var (
|
var (
|
||||||
testEas = []ExtendedAttribute{
|
testEas = []extendedAttribute{
|
||||||
{Name: "foo", Value: []byte("bar")},
|
{Name: "foo", Value: []byte("bar")},
|
||||||
{Name: "fizz", Value: []byte("buzz")},
|
{Name: "fizz", Value: []byte("buzz")},
|
||||||
}
|
}
|
||||||
|
@ -58,14 +58,14 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRoundTripEas(t *testing.T) {
|
func TestRoundTripEas(t *testing.T) {
|
||||||
b, err := EncodeExtendedAttributes(testEas)
|
b, err := encodeExtendedAttributes(testEas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(testEasEncoded, b) {
|
if !reflect.DeepEqual(testEasEncoded, b) {
|
||||||
t.Fatalf("Encoded mismatch %v %v", testEasEncoded, b)
|
t.Fatalf("Encoded mismatch %v %v", testEasEncoded, b)
|
||||||
}
|
}
|
||||||
eas, err := DecodeExtendedAttributes(b)
|
eas, err := decodeExtendedAttributes(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ func TestRoundTripEas(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEasDontNeedPaddingAtEnd(t *testing.T) {
|
func TestEasDontNeedPaddingAtEnd(t *testing.T) {
|
||||||
eas, err := DecodeExtendedAttributes(testEasNotPadded)
|
eas, err := decodeExtendedAttributes(testEasNotPadded)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -85,21 +85,21 @@ func TestEasDontNeedPaddingAtEnd(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTruncatedEasFailCorrectly(t *testing.T) {
|
func TestTruncatedEasFailCorrectly(t *testing.T) {
|
||||||
_, err := DecodeExtendedAttributes(testEasTruncated)
|
_, err := decodeExtendedAttributes(testEasTruncated)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected error")
|
t.Fatal("expected error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNilEasEncodeAndDecodeAsNil(t *testing.T) {
|
func TestNilEasEncodeAndDecodeAsNil(t *testing.T) {
|
||||||
b, err := EncodeExtendedAttributes(nil)
|
b, err := encodeExtendedAttributes(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if len(b) != 0 {
|
if len(b) != 0 {
|
||||||
t.Fatal("expected empty")
|
t.Fatal("expected empty")
|
||||||
}
|
}
|
||||||
eas, err := DecodeExtendedAttributes(nil)
|
eas, err := decodeExtendedAttributes(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -178,8 +178,8 @@ func setupTestFolder(t *testing.T) string {
|
||||||
return testfolderPath
|
return testfolderPath
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateTestEAs(t *testing.T, nAttrs int, path string) []ExtendedAttribute {
|
func generateTestEAs(t *testing.T, nAttrs int, path string) []extendedAttribute {
|
||||||
testEAs := make([]ExtendedAttribute, nAttrs)
|
testEAs := make([]extendedAttribute, nAttrs)
|
||||||
for i := 0; i < nAttrs; i++ {
|
for i := 0; i < nAttrs; i++ {
|
||||||
testEAs[i].Name = fmt.Sprintf("TESTEA%d", i+1)
|
testEAs[i].Name = fmt.Sprintf("TESTEA%d", i+1)
|
||||||
testEAs[i].Value = make([]byte, getRandomInt())
|
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) {
|
func testSetGetEA(t *testing.T, path string, handle windows.Handle, testEAs []extendedAttribute) {
|
||||||
if err := SetFileEA(handle, testEAs); err != nil {
|
if err := fsetEA(handle, testEAs); err != nil {
|
||||||
t.Fatalf("set EA for path %s failed: %s", path, err)
|
t.Fatalf("set EA for path %s failed: %s", path, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
readEAs, err := GetFileEA(handle)
|
readEAs, err := fgetEA(handle)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("get EA for path %s failed: %s", path, err)
|
t.Fatalf("get EA for path %s failed: %s", path, err)
|
||||||
}
|
}
|
||||||
|
@ -262,7 +262,7 @@ func TestPathSupportsExtendedAttributes(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
supported, err := PathSupportsExtendedAttributes(tc.path)
|
supported, err := pathSupportsExtendedAttributes(tc.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -273,7 +273,7 @@ func TestPathSupportsExtendedAttributes(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test with an invalid path
|
// Test with an invalid path
|
||||||
_, err := PathSupportsExtendedAttributes("Z:\\NonExistentPath-UAS664da5s4dyu56das45f5as")
|
_, err := pathSupportsExtendedAttributes("Z:\\NonExistentPath-UAS664da5s4dyu56das45f5as")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Expected an error for non-existent path, but got 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 {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
volumeName, err := GetVolumePathName(tc.path)
|
volumeName, err := getVolumePathName(tc.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -316,7 +316,7 @@ func TestGetVolumePathName(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test with an invalid path
|
// Test with an invalid path
|
||||||
_, err := GetVolumePathName("Z:\\NonExistentPath")
|
_, err := getVolumePathName("Z:\\NonExistentPath")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Expected an error for non-existent path, but got nil")
|
t.Error("Expected an error for non-existent path, but got nil")
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,8 +37,8 @@ func isNotSupported(err error) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chmod changes the mode of the named file to mode.
|
// 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 {
|
||||||
err := os.Chmod(fixpath(name), mode)
|
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)
|
// ignore the error if the FS does not support setting this mode (e.g. CIFS with gvfs on Linux)
|
||||||
|
|
|
@ -75,17 +75,17 @@ func TempFile(dir, prefix string) (f *os.File, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chmod changes the mode of the named file to mode.
|
// 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)
|
return os.Chmod(fixpath(name), mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearSystem removes the system attribute from the file.
|
// clearSystem removes the system attribute from the file.
|
||||||
func ClearSystem(path string) error {
|
func clearSystem(path string) error {
|
||||||
return ClearAttribute(path, windows.FILE_ATTRIBUTE_SYSTEM)
|
return clearAttribute(path, windows.FILE_ATTRIBUTE_SYSTEM)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearAttribute removes the specified attribute from the file.
|
// clearAttribute removes the specified attribute from the file.
|
||||||
func ClearAttribute(path string, attribute uint32) error {
|
func clearAttribute(path string, attribute uint32) error {
|
||||||
ptr, err := windows.UTF16PtrFromString(fixpath(path))
|
ptr, err := windows.UTF16PtrFromString(fixpath(path))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -105,8 +105,8 @@ func ClearAttribute(path string, attribute uint32) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenHandleForEA return a file handle for file or dir for setting/getting EAs
|
// 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) {
|
func openHandleForEA(nodeType restic.NodeType, path string, writeAccess bool) (handle windows.Handle, err error) {
|
||||||
path = fixpath(path)
|
path = fixpath(path)
|
||||||
fileAccess := windows.FILE_READ_EA
|
fileAccess := windows.FILE_READ_EA
|
||||||
if writeAccess {
|
if writeAccess {
|
||||||
|
|
|
@ -79,7 +79,7 @@ func parseMountPoints(list string, msgError ErrorHandler) (volumes map[string]st
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, s := range strings.Split(list, ";") {
|
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))
|
msgError(s, errors.Errorf("failed to parse vss.exclude-volumes [%s]: %s", s, err))
|
||||||
} else {
|
} else {
|
||||||
if volumes == nil {
|
if volumes == nil {
|
||||||
|
@ -146,7 +146,7 @@ func (fs *LocalVss) isMountPointIncluded(mountPoint string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
volume, err := GetVolumeNameForVolumeMountPoint(mountPoint)
|
volume, err := getVolumeNameForVolumeMountPoint(mountPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fs.msgError(mountPoint, errors.Errorf("failed to get volume from mount point [%s]: %s", mountPoint, err))
|
fs.msgError(mountPoint, errors.Errorf("failed to get volume from mount point [%s]: %s", mountPoint, err))
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
@ -120,10 +121,10 @@ func TestVSSConfig(t *testing.T) {
|
||||||
func TestParseMountPoints(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}\}\\$`)
|
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
|
// It's not a good idea to test functions based on getVolumeNameForVolumeMountPoint by calling
|
||||||
// GetVolumeNameForVolumeMountPoint itself, but we have restricted test environment:
|
// getVolumeNameForVolumeMountPoint itself, but we have restricted test environment:
|
||||||
// cannot manage volumes and can only be sure that the mount point C:\ exists
|
// 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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
// calling Chmod below will no longer allow any modifications to be made on the file and the
|
||||||
// calls above would fail.
|
// calls above would fail.
|
||||||
if node.Type != restic.NodeTypeSymlink {
|
if node.Type != restic.NodeTypeSymlink {
|
||||||
if err := Chmod(path, node.Mode); err != nil {
|
if err := chmod(path, node.Mode); err != nil {
|
||||||
if firsterr == nil {
|
if firsterr == nil {
|
||||||
firsterr = errors.WithStack(err)
|
firsterr = errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,9 +85,9 @@ func nodeRestoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error
|
||||||
func nodeRestoreExtendedAttributes(node *restic.Node, path string) (err error) {
|
func nodeRestoreExtendedAttributes(node *restic.Node, path string) (err error) {
|
||||||
count := len(node.ExtendedAttributes)
|
count := len(node.ExtendedAttributes)
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
eas := make([]ExtendedAttribute, count)
|
eas := make([]extendedAttribute, count)
|
||||||
for i, attr := range node.ExtendedAttributes {
|
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 {
|
if errExt := restoreExtendedAttributes(node.Type, path, eas); errExt != nil {
|
||||||
return errExt
|
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.
|
// fill extended attributes in the node. This also includes the Generic attributes for windows.
|
||||||
func nodeFillExtendedAttributes(node *restic.Node, path string, _ bool) (err error) {
|
func nodeFillExtendedAttributes(node *restic.Node, path string, _ bool) (err error) {
|
||||||
var fileHandle windows.Handle
|
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
|
return nil
|
||||||
}
|
}
|
||||||
if err != 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
|
defer closeFileHandle(fileHandle, path) // Replaced inline defer with named function call
|
||||||
//Get the windows Extended Attributes using the file handle
|
//Get the windows Extended Attributes using the file handle
|
||||||
var extAtts []ExtendedAttribute
|
var extAtts []extendedAttribute
|
||||||
extAtts, err = GetFileEA(fileHandle)
|
extAtts, err = fgetEA(fileHandle)
|
||||||
debug.Log("fillExtendedAttributes(%v) %v", path, extAtts)
|
debug.Log("fillExtendedAttributes(%v) %v", path, extAtts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Errorf("get EA failed for path %v, with: %v", path, err)
|
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.
|
// 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.
|
// 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
|
var fileHandle windows.Handle
|
||||||
if fileHandle, err = OpenHandleForEA(nodeType, path, true); fileHandle == 0 {
|
if fileHandle, err = openHandleForEA(nodeType, path, true); fileHandle == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err != 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
|
defer closeFileHandle(fileHandle, path) // Replaced inline defer with named function call
|
||||||
|
|
||||||
// clear old unexpected xattrs by setting them to an empty value
|
// clear old unexpected xattrs by setting them to an empty value
|
||||||
oldEAs, err := GetFileEA(fileHandle)
|
oldEAs, err := fgetEA(fileHandle)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -165,11 +165,11 @@ func restoreExtendedAttributes(nodeType restic.NodeType, path string, eas []Exte
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
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 errors.Errorf("set EA failed for path %v, with: %v", path, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -230,7 +230,7 @@ func nodeRestoreGenericAttributes(node *restic.Node, path string, warn func(msg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if windowsAttributes.SecurityDescriptor != nil {
|
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))
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to encrypt file: failed to reset permissions: %s : %v", path, err)
|
return fmt.Errorf("failed to encrypt file: failed to reset permissions: %s : %v", path, err)
|
||||||
}
|
}
|
||||||
err = ClearSystem(path)
|
err = clearSystem(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to encrypt file: failed to clear system flag: %s : %v", path, err)
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to encrypt file: failed to reset permissions: %s : %v", path, err)
|
return fmt.Errorf("failed to encrypt file: failed to reset permissions: %s : %v", path, err)
|
||||||
}
|
}
|
||||||
err = ClearSystem(path)
|
err = clearSystem(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to decrypt file: failed to clear system flag: %s : %v", path, err)
|
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 {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if sd, err = GetSecurityDescriptor(path); err != nil {
|
if sd, err = getSecurityDescriptor(path); err != nil {
|
||||||
return allowExtended, err
|
return allowExtended, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -422,7 +422,7 @@ func checkAndStoreEASupport(path string) (isEASupportedVolume bool, err error) {
|
||||||
return eaSupportedValue.(bool), nil
|
return eaSupportedValue.(bool), nil
|
||||||
}
|
}
|
||||||
// If not found, check if EA is supported with manually prepared volume name
|
// 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 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) {
|
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)
|
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
|
return false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If an entry is not found, get the actual volume name using the GetVolumePathName function
|
// If an entry is not found, get the actual volume name
|
||||||
volumeNameActual, err := GetVolumePathName(path)
|
volumeNameActual, err := getVolumePathName(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("Error getting actual volume name %s for path %s: %v", volumeName, path, err)
|
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.
|
// 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
|
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
|
// 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
|
// Debug log for cases where the prepared volume name is not valid
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("Error checking if extended attributes are supported for actual volume name %s: %v", volumeNameActual, err)
|
debug.Log("Error checking if extended attributes are supported for actual volume name %s: %v", volumeNameActual, err)
|
||||||
|
|
|
@ -23,10 +23,10 @@ import (
|
||||||
func TestRestoreSecurityDescriptors(t *testing.T) {
|
func TestRestoreSecurityDescriptors(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
tempDir := t.TempDir()
|
tempDir := t.TempDir()
|
||||||
for i, sd := range TestFileSDs {
|
for i, sd := range testFileSDs {
|
||||||
testRestoreSecurityDescriptor(t, sd, tempDir, restic.NodeTypeFile, fmt.Sprintf("testfile%d", i))
|
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))
|
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
|
sdByteFromRestoredNode := getWindowsAttr(t, testPath, node).SecurityDescriptor
|
||||||
|
|
||||||
// Get the security descriptor for the test path after the restore.
|
// 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))
|
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.
|
// 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.
|
// 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 {
|
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))
|
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.OK(t, errors.Wrapf(err, "Error getting extended attributes for: %s", testPath))
|
||||||
test.Equals(t, len(node.ExtendedAttributes), len(extAttr))
|
test.Equals(t, len(node.ExtendedAttributes), len(extAttr))
|
||||||
|
|
||||||
for _, expectedExtAttr := range node.ExtendedAttributes {
|
for _, expectedExtAttr := range node.ExtendedAttributes {
|
||||||
var foundExtAttr *ExtendedAttribute
|
var foundExtAttr *extendedAttribute
|
||||||
for _, ea := range extAttr {
|
for _, ea := range extAttr {
|
||||||
if strings.EqualFold(ea.Name, expectedExtAttr.Name) {
|
if strings.EqualFold(ea.Name, expectedExtAttr.Name) {
|
||||||
foundExtAttr = &ea
|
foundExtAttr = &ea
|
||||||
|
@ -491,13 +491,13 @@ func TestPrepareVolumeName(t *testing.T) {
|
||||||
test.Equals(t, tc.expectedVolume, volume)
|
test.Equals(t, tc.expectedVolume, volume)
|
||||||
|
|
||||||
if tc.isRealPath {
|
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.
|
// If the prepared volume name is not valid, we will next fetch the actual volume name.
|
||||||
test.OK(t, err)
|
test.OK(t, err)
|
||||||
|
|
||||||
test.Equals(t, tc.expectedEASupported, isEASupportedVolume)
|
test.Equals(t, tc.expectedEASupported, isEASupportedVolume)
|
||||||
|
|
||||||
actualVolume, err := GetVolumePathName(tc.path)
|
actualVolume, err := getVolumePathName(tc.path)
|
||||||
test.OK(t, err)
|
test.OK(t, err)
|
||||||
test.Equals(t, tc.expectedVolume, actualVolume)
|
test.Equals(t, tc.expectedVolume, actualVolume)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,14 +19,14 @@ var (
|
||||||
onceBackup sync.Once
|
onceBackup sync.Once
|
||||||
onceRestore sync.Once
|
onceRestore sync.Once
|
||||||
|
|
||||||
// SeBackupPrivilege allows the application to bypass file and directory ACLs to back up files and directories.
|
// seBackupPrivilege allows the application to bypass file and directory ACLs to back up files and directories.
|
||||||
SeBackupPrivilege = "SeBackupPrivilege"
|
seBackupPrivilege = "SeBackupPrivilege"
|
||||||
// SeRestorePrivilege allows the application to bypass file and directory ACLs to restore files and directories.
|
// seRestorePrivilege allows the application to bypass file and directory ACLs to restore files and directories.
|
||||||
SeRestorePrivilege = "SeRestorePrivilege"
|
seRestorePrivilege = "SeRestorePrivilege"
|
||||||
// SeSecurityPrivilege allows read and write access to all SACLs.
|
// seSecurityPrivilege allows read and write access to all SACLs.
|
||||||
SeSecurityPrivilege = "SeSecurityPrivilege"
|
seSecurityPrivilege = "SeSecurityPrivilege"
|
||||||
// SeTakeOwnershipPrivilege allows the application to take ownership of files and directories, regardless of the permissions set on them.
|
// seTakeOwnershipPrivilege allows the application to take ownership of files and directories, regardless of the permissions set on them.
|
||||||
SeTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege"
|
seTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege"
|
||||||
|
|
||||||
lowerPrivileges atomic.Bool
|
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.
|
// 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
|
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.
|
// 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.
|
// 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)
|
onceBackup.Do(enableBackupPrivilege)
|
||||||
|
|
||||||
var sd *windows.SECURITY_DESCRIPTOR
|
var sd *windows.SECURITY_DESCRIPTOR
|
||||||
|
@ -59,7 +59,7 @@ func GetSecurityDescriptor(filePath string) (securityDescriptor *[]byte, err err
|
||||||
if !useLowerPrivileges && isHandlePrivilegeNotHeldError(err) {
|
if !useLowerPrivileges && isHandlePrivilegeNotHeldError(err) {
|
||||||
// If ERROR_PRIVILEGE_NOT_HELD is encountered, fallback to backups/restores using lower non-admin privileges.
|
// If ERROR_PRIVILEGE_NOT_HELD is encountered, fallback to backups/restores using lower non-admin privileges.
|
||||||
lowerPrivileges.Store(true)
|
lowerPrivileges.Store(true)
|
||||||
return GetSecurityDescriptor(filePath)
|
return getSecurityDescriptor(filePath)
|
||||||
} else if errors.Is(err, windows.ERROR_NOT_SUPPORTED) {
|
} else if errors.Is(err, windows.ERROR_NOT_SUPPORTED) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
} else {
|
} else {
|
||||||
|
@ -74,15 +74,15 @@ func GetSecurityDescriptor(filePath string) (securityDescriptor *[]byte, err err
|
||||||
return &sdBytes, nil
|
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
|
// This needs admin permissions or SeRestorePrivilege, SeSecurityPrivilege and SeTakeOwnershipPrivilege
|
||||||
// for setting the full SD.
|
// for setting the full SD.
|
||||||
// If there are no admin permissions/required privileges, only the DACL from the SD can be set and
|
// 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.
|
// 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)
|
onceRestore.Do(enableRestorePrivilege)
|
||||||
// Set the security descriptor on the file
|
// Set the security descriptor on the file
|
||||||
sd, err := SecurityDescriptorBytesToStruct(*securityDescriptor)
|
sd, err := securityDescriptorBytesToStruct(*securityDescriptor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error converting bytes to security descriptor: %w", err)
|
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 !useLowerPrivileges && isHandlePrivilegeNotHeldError(err) {
|
||||||
// If ERROR_PRIVILEGE_NOT_HELD is encountered, fallback to backups/restores using lower non-admin privileges.
|
// If ERROR_PRIVILEGE_NOT_HELD is encountered, fallback to backups/restores using lower non-admin privileges.
|
||||||
lowerPrivileges.Store(true)
|
lowerPrivileges.Store(true)
|
||||||
return SetSecurityDescriptor(filePath, securityDescriptor)
|
return setSecurityDescriptor(filePath, securityDescriptor)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("set named security info failed with: %w", err)
|
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
|
// enableBackupPrivilege enables privilege for backing up security descriptors
|
||||||
func enableBackupPrivilege() {
|
func enableBackupPrivilege() {
|
||||||
err := enableProcessPrivileges([]string{SeBackupPrivilege})
|
err := enableProcessPrivileges([]string{seBackupPrivilege})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("error enabling backup privilege: %v", err)
|
debug.Log("error enabling backup privilege: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,7 @@ func enableBackupPrivilege() {
|
||||||
|
|
||||||
// enableBackupPrivilege enables privilege for restoring security descriptors
|
// enableBackupPrivilege enables privilege for restoring security descriptors
|
||||||
func enableRestorePrivilege() {
|
func enableRestorePrivilege() {
|
||||||
err := enableProcessPrivileges([]string{SeRestorePrivilege, SeSecurityPrivilege, SeTakeOwnershipPrivilege})
|
err := enableProcessPrivileges([]string{seRestorePrivilege, seSecurityPrivilege, seTakeOwnershipPrivilege})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("error enabling restore/security privilege: %v", err)
|
debug.Log("error enabling restore/security privilege: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -174,9 +174,9 @@ func isHandlePrivilegeNotHeldError(err error) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecurityDescriptorBytesToStruct converts the security descriptor bytes representation
|
// securityDescriptorBytesToStruct converts the security descriptor bytes representation
|
||||||
// into a pointer to windows SECURITY_DESCRIPTOR.
|
// 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 {
|
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)
|
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
|
privNameMutex sync.Mutex
|
||||||
)
|
)
|
||||||
|
|
||||||
// PrivilegeError represents an error enabling privileges.
|
// privilegeError represents an error enabling privileges.
|
||||||
type PrivilegeError struct {
|
type privilegeError struct {
|
||||||
privileges []uint64
|
privileges []uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error returns the string message for the error.
|
// Error returns the string message for the error.
|
||||||
func (e *PrivilegeError) Error() string {
|
func (e *privilegeError) Error() string {
|
||||||
s := "Could not enable privilege "
|
s := "Could not enable privilege "
|
||||||
if len(e.privileges) > 1 {
|
if len(e.privileges) > 1 {
|
||||||
s = "Could not enable privileges "
|
s = "Could not enable privileges "
|
||||||
|
|
|
@ -28,7 +28,7 @@ func TestSetGetFileSecurityDescriptors(t *testing.T) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
testSecurityDescriptors(t, TestFileSDs, testfilePath)
|
testSecurityDescriptors(t, testFileSDs, testfilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetGetFolderSecurityDescriptors(t *testing.T) {
|
func TestSetGetFolderSecurityDescriptors(t *testing.T) {
|
||||||
|
@ -40,7 +40,7 @@ func TestSetGetFolderSecurityDescriptors(t *testing.T) {
|
||||||
t.Fatalf("failed to create temporary file: %s", err)
|
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) {
|
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)
|
sdInputBytes, err := base64.StdEncoding.DecodeString(testSD)
|
||||||
test.OK(t, errors.Wrapf(err, "Error decoding SD: %s", testPath))
|
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))
|
test.OK(t, errors.Wrapf(err, "Error setting file security descriptor for: %s", testPath))
|
||||||
|
|
||||||
var sdOutputBytes *[]byte
|
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))
|
test.OK(t, errors.Wrapf(err, "Error getting file security descriptor for: %s", testPath))
|
||||||
|
|
||||||
CompareSecurityDescriptors(t, testPath, sdInputBytes, *sdOutputBytes)
|
compareSecurityDescriptors(t, testPath, sdInputBytes, *sdOutputBytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,18 +13,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TestFileSDs = []string{"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAfAAEAAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABAUAP8BHwABAQAAAAAABRIAAAAAEBgA/wEfAAECAAAAAAAFIAAAACACAAAAECQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAA=",
|
testFileSDs = []string{"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAfAAEAAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABAUAP8BHwABAQAAAAAABRIAAAAAEBgA/wEfAAECAAAAAAAFIAAAACACAAAAECQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAA=",
|
||||||
"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAyAAHAAAAAAAUAKkAEgABAQAAAAAABQcAAAAAABQAiQASAAEBAAAAAAAFBwAAAAAAJACpABIAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSar7QMAAAAAJAC/ARMAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSar6gMAAAAAFAD/AR8AAQEAAAAAAAUSAAAAAAAYAP8BHwABAgAAAAAABSAAAAAgAgAAAAAkAP8BHwABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAA",
|
"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAyAAHAAAAAAAUAKkAEgABAQAAAAAABQcAAAAAABQAiQASAAEBAAAAAAAFBwAAAAAAJACpABIAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSar7QMAAAAAJAC/ARMAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSar6gMAAAAAFAD/AR8AAQEAAAAAAAUSAAAAAAAYAP8BHwABAgAAAAAABSAAAAAgAgAAAAAkAP8BHwABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAA",
|
||||||
"AQAUvBQAAAAwAAAA7AAAAEwAAAABBQAAAAAABRUAAAAvr7t03PyHGk2FokNHCAAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAoAAFAAAAAAAkAP8BHwABBQAAAAAABRUAAAAvr7t03PyHGk2FokNHCAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABAUAP8BHwABAQAAAAAABRIAAAAAEBgA/wEfAAECAAAAAAAFIAAAACACAAAAECQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAACAHQAAwAAAAKAJAC/AQIAAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDtgQAAALAJAC/AQMAAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDPgkAAAJAJAD/AQ8AAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDtQQAAA==",
|
"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=",
|
"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIA3AAIAAAAAAIUAKkAEgABAQAAAAAABQcAAAAAAxQAiQASAAEBAAAAAAAFBwAAAAAAJACpABIAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSar7QMAAAAAJAC/ARMAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSar6gMAAAALFAC/ARMAAQEAAAAAAAMAAAAAABMUAP8BHwABAQAAAAAABRIAAAAAExgA/wEfAAECAAAAAAAFIAAAACACAAAAEyQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAA=",
|
||||||
"AQAUvBQAAAAwAAAA7AAAAEwAAAABBQAAAAAABRUAAAAvr7t03PyHGk2FokNHCAAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAoAAFAAAAAAAkAP8BHwABBQAAAAAABRUAAAAvr7t03PyHGk2FokNHCAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABMUAP8BHwABAQAAAAAABRIAAAAAExgA/wEfAAECAAAAAAAFIAAAACACAAAAEyQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAACAHQAAwAAAAKAJAC/AQIAAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDtgQAAALAJAC/AQMAAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDPgkAAAJAJAD/AQ8AAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDtQQAAA==",
|
"AQAUvBQAAAAwAAAA7AAAAEwAAAABBQAAAAAABRUAAAAvr7t03PyHGk2FokNHCAAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAoAAFAAAAAAAkAP8BHwABBQAAAAAABRUAAAAvr7t03PyHGk2FokNHCAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABMUAP8BHwABAQAAAAAABRIAAAAAExgA/wEfAAECAAAAAAAFIAAAACACAAAAEyQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAACAHQAAwAAAAKAJAC/AQIAAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDtgQAAALAJAC/AQMAAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDPgkAAAJAJAD/AQ8AAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDtQQAAA==",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsAdmin checks if current user is an administrator.
|
// isAdmin checks if current user is an administrator.
|
||||||
func IsAdmin() (isAdmin bool, err error) {
|
func isAdmin() (isAdmin bool, err error) {
|
||||||
var sid *windows.SID
|
var sid *windows.SID
|
||||||
err = windows.AllocateAndInitializeSid(&windows.SECURITY_NT_AUTHORITY, 2, windows.SECURITY_BUILTIN_DOMAIN_RID, windows.DOMAIN_ALIAS_RID_ADMINS,
|
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)
|
0, 0, 0, 0, 0, 0, &sid)
|
||||||
|
@ -40,15 +40,15 @@ func IsAdmin() (isAdmin bool, err error) {
|
||||||
return member, nil
|
return member, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompareSecurityDescriptors runs tests for comparing 2 security descriptors in []byte format.
|
// compareSecurityDescriptors runs tests for comparing 2 security descriptors in []byte format.
|
||||||
func CompareSecurityDescriptors(t *testing.T, testPath string, sdInputBytes, sdOutputBytes []byte) {
|
func compareSecurityDescriptors(t *testing.T, testPath string, sdInputBytes, sdOutputBytes []byte) {
|
||||||
sdInput, err := SecurityDescriptorBytesToStruct(sdInputBytes)
|
sdInput, err := securityDescriptorBytesToStruct(sdInputBytes)
|
||||||
test.OK(t, errors.Wrapf(err, "Error converting SD to struct for: %s", testPath))
|
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))
|
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))
|
test.OK(t, errors.Wrapf(err, "Error checking if user is admin: %s", testPath))
|
||||||
|
|
||||||
var ownerExpected *windows.SID
|
var ownerExpected *windows.SID
|
||||||
|
|
|
@ -33,9 +33,9 @@ func HasSufficientPrivilegesForVSS() error {
|
||||||
return errors.New("VSS snapshots are only supported on windows")
|
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.
|
// and calls the equivalent windows api.
|
||||||
func GetVolumeNameForVolumeMountPoint(mountPoint string) (string, error) {
|
func getVolumeNameForVolumeMountPoint(mountPoint string) (string, error) {
|
||||||
return mountPoint, nil
|
return mountPoint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
type HRESULT uint
|
type HRESULT uint
|
||||||
|
|
||||||
// HRESULT constant values necessary for using VSS api.
|
// HRESULT constant values necessary for using VSS api.
|
||||||
|
//
|
||||||
//nolint:golint
|
//nolint:golint
|
||||||
const (
|
const (
|
||||||
S_OK HRESULT = 0x00000000
|
S_OK HRESULT = 0x00000000
|
||||||
|
@ -830,9 +831,9 @@ func HasSufficientPrivilegesForVSS() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVolumeNameForVolumeMountPoint add trailing backslash to input parameter
|
// getVolumeNameForVolumeMountPoint add trailing backslash to input parameter
|
||||||
// and calls the equivalent windows api.
|
// 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 {
|
if mountPoint != "" && mountPoint[len(mountPoint)-1] != filepath.Separator {
|
||||||
mountPoint += string(filepath.Separator)
|
mountPoint += string(filepath.Separator)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue