mirror of
https://github.com/restic/restic.git
synced 2025-03-09 00:00:02 +01:00
Add ADS related properties to node
This commit is contained in:
parent
e188d281d0
commit
0cff89de41
3 changed files with 146 additions and 29 deletions
|
@ -187,29 +187,33 @@ func nodeRestoreGenericAttributes(node *restic.Node, path string, warn func(msg
|
|||
if len(node.GenericAttributes) == 0 {
|
||||
return nil
|
||||
}
|
||||
var errs []error
|
||||
windowsAttributes, unknownAttribs, err := genericAttributesToWindowsAttrs(node.GenericAttributes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing generic attribute for: %s : %v", path, err)
|
||||
}
|
||||
if windowsAttributes.CreationTime != nil {
|
||||
if err := restoreCreationTime(path, windowsAttributes.CreationTime); err != nil {
|
||||
errs = append(errs, fmt.Errorf("error restoring creation time for: %s : %v", path, err))
|
||||
if node.IsMainFile() {
|
||||
var errs []error
|
||||
windowsAttributes, unknownAttribs, err := genericAttributesToWindowsAttrs(node.GenericAttributes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing generic attribute for: %s : %v", path, err)
|
||||
}
|
||||
}
|
||||
if windowsAttributes.FileAttributes != nil {
|
||||
if err := restoreFileAttributes(path, windowsAttributes.FileAttributes); err != nil {
|
||||
errs = append(errs, fmt.Errorf("error restoring file attributes for: %s : %v", path, err))
|
||||
if windowsAttributes.CreationTime != nil {
|
||||
if err := restoreCreationTime(path, windowsAttributes.CreationTime); err != nil {
|
||||
errs = append(errs, fmt.Errorf("error restoring creation time for: %s : %v", path, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
if windowsAttributes.SecurityDescriptor != nil {
|
||||
if err := setSecurityDescriptor(path, windowsAttributes.SecurityDescriptor); err != nil {
|
||||
errs = append(errs, fmt.Errorf("error restoring security descriptor for: %s : %v", path, err))
|
||||
if windowsAttributes.FileAttributes != nil {
|
||||
if err := restoreFileAttributes(path, windowsAttributes.FileAttributes); err != nil {
|
||||
errs = append(errs, fmt.Errorf("error restoring file attributes for: %s : %v", path, err))
|
||||
}
|
||||
}
|
||||
if windowsAttributes.SecurityDescriptor != nil {
|
||||
if err := setSecurityDescriptor(path, windowsAttributes.SecurityDescriptor); err != nil {
|
||||
errs = append(errs, fmt.Errorf("error restoring security descriptor for: %s : %v", path, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
restic.HandleUnknownGenericAttributesFound(unknownAttribs, warn)
|
||||
return errors.Join(errs...)
|
||||
node.RemoveExtraStreams(path)
|
||||
restic.HandleUnknownGenericAttributesFound(unknownAttribs, warn)
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// genericAttributesToWindowsAttrs converts the generic attributes map to a WindowsAttributes and also returns a string of unknown attributes that it could not convert.
|
||||
|
@ -336,10 +340,20 @@ func decryptFile(pathPointer *uint16) error {
|
|||
|
||||
// nodeFillGenericAttributes fills in the generic attributes for windows like File Attributes,
|
||||
// Created time and Security Descriptors.
|
||||
func nodeFillGenericAttributes(node *restic.Node, path string, stat *ExtendedFileInfo) error {
|
||||
if strings.Contains(filepath.Base(path), ":") {
|
||||
// Do not process for Alternate Data Streams in Windows
|
||||
return nil
|
||||
// It also checks if the volume supports extended attributes and stores the result in a map
|
||||
// so that it does not have to be checked again for subsequent calls for paths in the same volume.
|
||||
func nodeFillGenericAttributes(node *restic.Node, path string, stat *ExtendedFileInfo) (err error) {
|
||||
isAds := restic.IsAds(path)
|
||||
var attrs restic.WindowsAttributes
|
||||
if isAds {
|
||||
attrs, err = getWindowsAttributesForAds(stat, &isAds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
node.GenericAttributes, err = restic.WindowsAttrsToGenericAttributes(attrs)
|
||||
// Do not process remaining generic attributes for Alternate Data Streams in Windows
|
||||
// Also do not allow to process extended attributes for ADS.
|
||||
return err
|
||||
}
|
||||
|
||||
isVolume, err := isVolumePath(path)
|
||||
|
@ -361,15 +375,49 @@ func nodeFillGenericAttributes(node *restic.Node, path string, stat *ExtendedFil
|
|||
}
|
||||
}
|
||||
|
||||
winFI := stat.sys.(*syscall.Win32FileAttributeData)
|
||||
attrs, err = getWindowsAttributes(stat, sd, path, isAds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
node.GenericAttributes, err = restic.WindowsAttrsToGenericAttributes(attrs)
|
||||
return err
|
||||
}
|
||||
|
||||
// Add Windows attributes
|
||||
node.GenericAttributes, err = restic.WindowsAttrsToGenericAttributes(restic.WindowsAttributes{
|
||||
func getWindowsAttributesForAds(stat *ExtendedFileInfo, isAds *bool) (restic.WindowsAttributes, error) {
|
||||
winFI := stat.sys.(*syscall.Win32FileAttributeData)
|
||||
return restic.WindowsAttributes{
|
||||
CreationTime: &winFI.CreationTime,
|
||||
FileAttributes: &winFI.FileAttributes,
|
||||
IsADS: isAds,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getWindowsAttributes(stat *ExtendedFileInfo, sd *[]byte, path string, isAds bool) (restic.WindowsAttributes, error) {
|
||||
winFI := stat.sys.(*syscall.Win32FileAttributeData)
|
||||
attrs := restic.WindowsAttributes{
|
||||
CreationTime: &winFI.CreationTime,
|
||||
FileAttributes: &winFI.FileAttributes,
|
||||
SecurityDescriptor: sd,
|
||||
})
|
||||
return err
|
||||
}
|
||||
if isAds {
|
||||
attrs.IsADS = &isAds
|
||||
} else {
|
||||
hasAds := getHasAds(path)
|
||||
if len(hasAds) > 0 {
|
||||
attrs.HasADS = &hasAds
|
||||
}
|
||||
}
|
||||
return attrs, nil
|
||||
}
|
||||
|
||||
func getHasAds(path string) (hasAds []string) {
|
||||
s, names, err := restic.GetADStreamNames(path)
|
||||
if s {
|
||||
hasAds = names
|
||||
} else if err != nil {
|
||||
debug.Log("Could not fetch ads information for %v %v.", path, err)
|
||||
}
|
||||
return hasAds
|
||||
}
|
||||
|
||||
// checkAndStoreEASupport checks if the volume of the path supports extended attributes and stores the result in a map
|
||||
|
|
|
@ -46,13 +46,19 @@ const (
|
|||
TypeFileAttributes GenericAttributeType = "windows.file_attributes"
|
||||
// TypeSecurityDescriptor is the GenericAttributeType used for storing security descriptors including owner, group, discretionary access control list (DACL), system access control list (SACL)) for windows files within the generic attributes map.
|
||||
TypeSecurityDescriptor GenericAttributeType = "windows.security_descriptor"
|
||||
// TypeHasADS is the GenericAttributeType used for to indicate that a file has Alternate Data Streams attached to it.
|
||||
// The value will have a | separate list of the ADS attached to the file. Those files will have a generic attribute TypeIsADS.
|
||||
TypeHasADS GenericAttributeType = "windows.has_ads"
|
||||
// TypeIsADS is the GenericAttributeType used for to indicate that a file represents an Alternate Data Streams and is attached to (child of) a file in the value.
|
||||
// The file in the value will be a file which has a generic attribute TypeHasADS.
|
||||
TypeIsADS GenericAttributeType = "windows.is_ads"
|
||||
|
||||
// Generic Attributes for other OS types should be defined here.
|
||||
)
|
||||
|
||||
// init is called when the package is initialized. Any new GenericAttributeTypes being created must be added here as well.
|
||||
func init() {
|
||||
storeGenericAttributeType(TypeCreationTime, TypeFileAttributes, TypeSecurityDescriptor)
|
||||
storeGenericAttributeType(TypeCreationTime, TypeFileAttributes, TypeSecurityDescriptor, TypeHasADS, TypeIsADS)
|
||||
}
|
||||
|
||||
// genericAttributesForOS maintains a map of known genericAttributesForOS to the OSType
|
||||
|
|
|
@ -2,11 +2,16 @@ package restic
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/restic/restic/internal/debug"
|
||||
)
|
||||
|
||||
const AdsSeparator = "|"
|
||||
|
||||
// WindowsAttributes are the genericAttributes for Windows OS
|
||||
type WindowsAttributes struct {
|
||||
// CreationTime is used for storing creation time for windows files.
|
||||
|
@ -16,6 +21,11 @@ type WindowsAttributes struct {
|
|||
// SecurityDescriptor is used for storing security descriptors which includes
|
||||
// owner, group, discretionary access control list (DACL), system access control list (SACL)
|
||||
SecurityDescriptor *[]byte `generic:"security_descriptor"`
|
||||
// HasADS is used for storing if a file has an Alternate Data Stream attached to it.
|
||||
HasADS *[]string `generic:"has_ads"`
|
||||
// IsADS is used for storing if a file is an Alternate Data Stream and is attached to (child of) a file in the value.
|
||||
// The file in the value will be a file which has a generic attribute TypeHasADS.
|
||||
IsADS *bool `generic:"is_ads"`
|
||||
}
|
||||
|
||||
// windowsAttrsToGenericAttributes converts the WindowsAttributes to a generic attributes map using reflection
|
||||
|
@ -24,3 +34,56 @@ func WindowsAttrsToGenericAttributes(windowsAttributes WindowsAttributes) (attrs
|
|||
windowsAttributesValue := reflect.ValueOf(windowsAttributes)
|
||||
return OSAttrsToGenericAttributes(reflect.TypeOf(windowsAttributes), &windowsAttributesValue, runtime.GOOS)
|
||||
}
|
||||
|
||||
// IsMainFile indicates if this is the main file and not a secondary file like an ads stream.
|
||||
// This is used for functionalities we want to skip for secondary (ads) files.
|
||||
// Eg. For Windows we do not want to count the secondary files
|
||||
func (node Node) IsMainFile() bool {
|
||||
return string(node.GenericAttributes[TypeIsADS]) != "true"
|
||||
}
|
||||
|
||||
// RemoveExtraStreams removes any extra streams on the file which are not present in the
|
||||
// backed up state in the generic attribute TypeHasAds.
|
||||
func (node Node) RemoveExtraStreams(path string) {
|
||||
success, existingStreams, _ := GetADStreamNames(path)
|
||||
if success {
|
||||
var adsValues []string
|
||||
|
||||
hasAdsBytes := node.GenericAttributes[TypeHasADS]
|
||||
if hasAdsBytes != nil {
|
||||
var adsArray []string
|
||||
err := json.Unmarshal(hasAdsBytes, &adsArray)
|
||||
if err == nil {
|
||||
adsValues = adsArray
|
||||
}
|
||||
}
|
||||
|
||||
extraStreams := filterItems(adsValues, existingStreams)
|
||||
for _, extraStream := range extraStreams {
|
||||
streamToRemove := path + extraStream
|
||||
err := os.Remove(streamToRemove)
|
||||
if err != nil {
|
||||
debug.Log("Error removing stream: %s : %s", streamToRemove, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// filterItems filters out which items are in evalArray which are not in referenceArray.
|
||||
func filterItems(referenceArray, evalArray []string) (result []string) {
|
||||
// Create a map to store elements of referenceArray for fast lookup
|
||||
referenceArrayMap := make(map[string]bool)
|
||||
for _, item := range referenceArray {
|
||||
referenceArrayMap[item] = true
|
||||
}
|
||||
|
||||
// Iterate through elements of evalArray
|
||||
for _, item := range evalArray {
|
||||
// Check if the item is not in referenceArray
|
||||
if !referenceArrayMap[item] {
|
||||
// Append to the result array
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue