Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Back up and restore SecurityDescriptors on Windows #4708

Merged
merged 11 commits into from
May 12, 2024
2 changes: 1 addition & 1 deletion changelog/unreleased/pull-4611
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Enhancement: Back up windows created time and file attributes like hidden flag

Restic did not back up windows-specific meta-data like created time and file attributes like hidden flag.
Restic now backs up file created time and file attributes like hidden, readonly and encrypted flag when backing up files and folders on windows.
Restic now backs up file created time and file attributes like hidden, readonly and encrypted flag when backing up files and folders on Windows.

https://github.com/restic/restic/pull/4611

11 changes: 11 additions & 0 deletions changelog/unreleased/pull-4708
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Enhancement: Back up and restore SecurityDescriptors on Windows

Restic now backs up and restores SecurityDescriptors when backing up files and folders
on Windows which includes owner, group, discretionary access control list (DACL),
system access control list (SACL). This requires the user to be a member of backup
operators or the application must be run as admin.
If that is not the case, only the current user's owner, group and DACL will be backed up
and during restore only the DACL of the backed file will be restored while the current
user's owner and group will be set during the restore.

https://github.com/restic/restic/pull/4708
7 changes: 6 additions & 1 deletion doc/040_backup.rst
Original file line number Diff line number Diff line change
Expand Up @@ -514,12 +514,17 @@ written, and the next backup needs to write new metadata again. If you really
want to save the access time for files and directories, you can pass the
``--with-atime`` option to the ``backup`` command.

Backing up full security descriptors on Windows is only possible when the user
has ``SeBackupPrivilege``privilege or is running as admin. This is a restriction
of Windows not restic.
If either of these conditions are not met, only the owner, group and DACL will
be backed up.

Note that ``restic`` does not back up some metadata associated with files. Of
particular note are:

* File creation date on Unix platforms
* Inode flags on Unix platforms
* File ownership and ACLs on Windows

Reading data from a command
***************************
Expand Down
5 changes: 5 additions & 0 deletions doc/050_restore.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ Restoring symbolic links on windows is only possible when the user has
``SeCreateSymbolicLinkPrivilege`` privilege or is running as admin. This is a
restriction of windows not restic.

Restoring full security descriptors on Windows is only possible when the user has
``SeRestorePrivilege``, ``SeSecurityPrivilege`` and ``SeTakeOwnershipPrivilege``
privilege or is running as admin. This is a restriction of Windows not restic.
If either of these conditions are not met, only the DACL will be restored.

By default, restic does not restore files as sparse. Use ``restore --sparse`` to
enable the creation of sparse files if supported by the filesystem. Then restic
will restore long runs of zero bytes as holes in the corresponding files.
Expand Down
4 changes: 1 addition & 3 deletions internal/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import (
"path/filepath"
"runtime"
"strings"

"github.com/restic/restic/internal/fs"
)

var opts struct {
Expand Down Expand Up @@ -46,7 +44,7 @@ func initDebugLogger() {

fmt.Fprintf(os.Stderr, "debug log file %v\n", debugfile)

f, err := fs.OpenFile(debugfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
f, err := os.OpenFile(debugfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
if err != nil {
fmt.Fprintf(os.Stderr, "unable to open debug log file: %v\n", err)
os.Exit(2)
Expand Down
24 changes: 15 additions & 9 deletions internal/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,28 @@ func Is(x, y error) bool { return stderrors.Is(x, y) }
// unwrap errors returned by [Join].
func Unwrap(err error) error { return stderrors.Unwrap(err) }

// CombineErrors combines multiple errors into a single error.
func CombineErrors(errors ...error) error {
// CombineErrors combines multiple errors into a single error after filtering out any nil values.
// If no errors are passed, it returns nil.
// If one error is passed, it simply returns that same error.
func CombineErrors(errors ...error) (err error) {
var combinedErrorMsg string

for _, err := range errors {
if err != nil {
var multipleErrors bool
for _, errVal := range errors {
if errVal != nil {
if combinedErrorMsg != "" {
combinedErrorMsg += "; " // Separate error messages with a delimiter
multipleErrors = true
} else {
// Set the first error
err = errVal
}
combinedErrorMsg += err.Error()
combinedErrorMsg += errVal.Error()
}
}

if combinedErrorMsg == "" {
return nil // No errors, return nil
return nil // If no errors, return nil
} else if !multipleErrors {
return err // If only one error, return that first error
}

return fmt.Errorf("multiple errors occurred: [%s]", combinedErrorMsg)
}