1
0
Fork 0
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:
aneesh-n 2024-12-03 16:41:14 +05:30
parent e188d281d0
commit 0cff89de41
No known key found for this signature in database
GPG key ID: 6F5A52831C046F44
3 changed files with 146 additions and 29 deletions

View file

@ -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

View file

@ -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

View file

@ -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
}