mirror of
https://github.com/kopia/kopia.git
synced 2026-10-24 15:18:48 -04:00
fix(snapshots): Partly fix #544 by supporting setuid/setgid/sticky bits when snapshotting, restoring and fuse-mounting. (#2597)
Also modified an end-to-end test to also check that these extra mode flags work when snapshotting+restoring. Manually tested fuse-mount. Co-authored-by: Luca Citi <lciti@ieee.org>
This commit is contained in:
1 parent
0e09e1a13f
commit
37d64afc21
5 files changed
+33
-12
No files matched your search
@@ -9,6 +9,9 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ModBits is a bitmask representing the mode flags supported.
|
||||
const ModBits = os.ModePerm | os.ModeSetgid | os.ModeSetuid | os.ModeSticky
|
||||
|
||||
// ErrUnknown is returned by ErrorEntry.ErrorInfo() to indicate that type of an entry is unknown.
|
||||
var ErrUnknown = errors.Errorf("unknown or unsupported entry type")
|
||||
|
||||
|
||||
@@ -30,8 +30,26 @@ type fuseNode struct {
|
||||
entry fs.Entry
|
||||
}
|
||||
|
||||
func goModeToUnixMode(mode os.FileMode) uint32 {
|
||||
unixmode := uint32(mode.Perm())
|
||||
|
||||
if mode&os.ModeSetuid != 0 {
|
||||
unixmode |= 0o4000
|
||||
}
|
||||
|
||||
if mode&os.ModeSetgid != 0 {
|
||||
unixmode |= 0o2000
|
||||
}
|
||||
|
||||
if mode&os.ModeSticky != 0 {
|
||||
unixmode |= 0o1000
|
||||
}
|
||||
|
||||
return unixmode
|
||||
}
|
||||
|
||||
func populateAttributes(a *fuse.Attr, e fs.Entry) {
|
||||
a.Mode = uint32(e.Mode()) & uint32(os.ModePerm)
|
||||
a.Mode = goModeToUnixMode(e.Mode())
|
||||
a.Size = uint64(e.Size())
|
||||
a.Mtime = uint64(e.ModTime().Unix())
|
||||
a.Ctime = a.Mtime
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
modBits = os.ModePerm | os.ModeSetgid | os.ModeSetuid | os.ModeSticky
|
||||
outputDirMode = 0o700 // default mode to create directories in before setting their ACLs
|
||||
maxTimeDeltaToConsiderFileTheSame = 2 * time.Second
|
||||
)
|
||||
@@ -273,7 +272,7 @@ func (o *FilesystemOutput) setAttributes(targetPath string, e fs.Entry, modclear
|
||||
|
||||
// Set file permissions from e
|
||||
if o.shouldUpdatePermissions(le, e, modclear) {
|
||||
if err = o.maybeIgnorePermissionError(osChmod(targetPath, (e.Mode()&modBits)&^modclear)); err != nil {
|
||||
if err = o.maybeIgnorePermissionError(osChmod(targetPath, (e.Mode()&fs.ModBits)&^modclear)); err != nil {
|
||||
return errors.Wrap(err, "could not change permissions on "+targetPath)
|
||||
}
|
||||
}
|
||||
@@ -317,7 +316,7 @@ func (o *FilesystemOutput) shouldUpdatePermissions(local, remote fs.Entry, modcl
|
||||
return false
|
||||
}
|
||||
|
||||
return ((local.Mode() & modBits) &^ modclear) != (remote.Mode() & modBits)
|
||||
return ((local.Mode() & fs.ModBits) &^ modclear) != (remote.Mode() & fs.ModBits)
|
||||
}
|
||||
|
||||
func (o *FilesystemOutput) shouldUpdateTimes(local, remote fs.Entry) bool {
|
||||
|
||||
@@ -454,7 +454,7 @@ func newDirEntry(md fs.Entry, fname string, oid object.ID) (*snapshot.DirEntry,
|
||||
return &snapshot.DirEntry{
|
||||
Name: fname,
|
||||
Type: entryType,
|
||||
Permissions: snapshot.Permissions(md.Mode() & os.ModePerm),
|
||||
Permissions: snapshot.Permissions(md.Mode() & fs.ModBits),
|
||||
FileSize: md.Size(),
|
||||
ModTime: fs.UTCTimestampFromTime(md.ModTime()),
|
||||
UserID: md.Owner().UserID,
|
||||
|
||||
@@ -259,7 +259,7 @@ func TestSnapshotRestore(t *testing.T) {
|
||||
|
||||
// restoring single file
|
||||
e.RunAndExpectSuccess(t, "snapshot", "restore", rootID+"/single-file", filepath.Join(restoreByOIDFile, "output-file"))
|
||||
verifyFileMode(t, filepath.Join(restoreByOIDFile, "output-file"), os.FileMode(0o651))
|
||||
verifyFileMode(t, filepath.Join(restoreByOIDFile, "output-file"), overriddenFilePermissions)
|
||||
|
||||
// Restore last snapshot using the snapshot ID
|
||||
e.RunAndExpectSuccess(t, "snapshot", "restore", snapID, restoreDir)
|
||||
@@ -461,22 +461,23 @@ func TestRestoreSnapshotOfSingleFile(t *testing.T) {
|
||||
verifyFileMode(t, filepath.Join(restoreDir, "restored-2"), os.FileMode(0o653))
|
||||
|
||||
if runtime.GOOS != windowsOSName {
|
||||
overriddenFilePermissions := 0o654 | os.ModeSetuid
|
||||
|
||||
// change source file permissions and create one more snapshot
|
||||
// at this poing we will have multiple snapshot manifests with one root but different attributes.
|
||||
os.Chmod(sourceFile, 0o654)
|
||||
os.Chmod(sourceFile, overriddenFilePermissions)
|
||||
e.RunAndExpectSuccess(t, "snapshot", "create", sourceFile)
|
||||
|
||||
// when restoring by root Kopia needs to pick which manifest to apply since they are conflicting
|
||||
// We're passing --consistent-attributes which causes it to fail, since otherwise we'd restore arbitrary
|
||||
// top-level object permissions.
|
||||
e.RunAndExpectFailure(t, "snapshot", "restore", rootID, "--consistent-attributes", filepath.Join(restoreDir, "restored-3"))
|
||||
|
||||
// Without the flag kopia picks the attributes from the latest snapshot.
|
||||
e.RunAndExpectSuccess(t, "snapshot", "restore", rootID, filepath.Join(restoreDir, "restored-3"))
|
||||
verifyFileMode(t, filepath.Join(restoreDir, "restored-3"), overriddenFilePermissions)
|
||||
}
|
||||
|
||||
// Without the flag kopia picks the attributes from the latest snapshot.
|
||||
e.RunAndExpectSuccess(t, "snapshot", "restore", rootID, filepath.Join(restoreDir, "restored-3"))
|
||||
|
||||
verifyFileMode(t, filepath.Join(restoreDir, "restored-3"), os.FileMode(0o654))
|
||||
|
||||
// restoring using snapshot ID is unambiguous and always produces file with 0o653
|
||||
e.RunAndExpectSuccess(t, "snapshot", "restore", snapID, filepath.Join(restoreDir, "restored-4"))
|
||||
verifyFileMode(t, filepath.Join(restoreDir, "restored-4"), os.FileMode(0o653))
|
||||
|
||||
Reference in new issue
Block a user