Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Restore whole folder to sdtout as tar
With this change it is possible to dump a folder to stdout as a tar. The It can be used just like the normal dump command: `./restic dump fa97e6e1 "/data/test/" > test.tar` Where `/data/test/` is a a folder instead of a file.
- Loading branch information
Showing
7 changed files
with
393 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
Enhancement: Ability to dump folders to tar via stdout | ||
|
||
We've added the ability to dump whole folders to stdout via the `dump` command. | ||
Restic now requires at least Go 1.10 due to a limitation of the standard | ||
library for Go <= 1.9. | ||
|
||
https://github.com/restic/restic/pull/2124 | ||
https://github.com/restic/restic/issues/2123 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package main | ||
|
||
// Adapted from https://github.com/maxymania/go-system/blob/master/posix_acl/posix_acl.go | ||
|
||
import ( | ||
"bytes" | ||
"encoding/binary" | ||
"fmt" | ||
) | ||
|
||
const ( | ||
aclUserOwner = 0x0001 | ||
aclUser = 0x0002 | ||
aclGroupOwner = 0x0004 | ||
aclGroup = 0x0008 | ||
aclMask = 0x0010 | ||
aclOthers = 0x0020 | ||
) | ||
|
||
type aclSID uint64 | ||
|
||
type aclElem struct { | ||
Tag uint16 | ||
Perm uint16 | ||
ID uint32 | ||
} | ||
|
||
type acl struct { | ||
Version uint32 | ||
List []aclElement | ||
} | ||
|
||
type aclElement struct { | ||
aclSID | ||
Perm uint16 | ||
} | ||
|
||
func (a *aclSID) setUID(uid uint32) { | ||
*a = aclSID(uid) | (aclUser << 32) | ||
} | ||
func (a *aclSID) setGID(gid uint32) { | ||
*a = aclSID(gid) | (aclGroup << 32) | ||
} | ||
|
||
func (a *aclSID) setType(tp int) { | ||
*a = aclSID(tp) << 32 | ||
} | ||
|
||
func (a aclSID) getType() int { | ||
return int(a >> 32) | ||
} | ||
func (a aclSID) getID() uint32 { | ||
return uint32(a & 0xffffffff) | ||
} | ||
func (a aclSID) String() string { | ||
switch a >> 32 { | ||
case aclUserOwner: | ||
return "user::" | ||
case aclUser: | ||
return fmt.Sprintf("user:%v:", a.getID()) | ||
case aclGroupOwner: | ||
return "group::" | ||
case aclGroup: | ||
return fmt.Sprintf("group:%v:", a.getID()) | ||
case aclMask: | ||
return "mask::" | ||
case aclOthers: | ||
return "other::" | ||
} | ||
return "?:" | ||
} | ||
|
||
func (a aclElement) String() string { | ||
str := "" | ||
if (a.Perm & 4) != 0 { | ||
str += "r" | ||
} else { | ||
str += "-" | ||
} | ||
if (a.Perm & 2) != 0 { | ||
str += "w" | ||
} else { | ||
str += "-" | ||
} | ||
if (a.Perm & 1) != 0 { | ||
str += "x" | ||
} else { | ||
str += "-" | ||
} | ||
return fmt.Sprintf("%v%v", a.aclSID, str) | ||
} | ||
|
||
func (a *acl) decode(xattr []byte) { | ||
var elem aclElement | ||
ae := new(aclElem) | ||
nr := bytes.NewReader(xattr) | ||
e := binary.Read(nr, binary.LittleEndian, &a.Version) | ||
if e != nil { | ||
a.Version = 0 | ||
return | ||
} | ||
if len(a.List) > 0 { | ||
a.List = a.List[:0] | ||
} | ||
for binary.Read(nr, binary.LittleEndian, ae) == nil { | ||
elem.aclSID = (aclSID(ae.Tag) << 32) | aclSID(ae.ID) | ||
elem.Perm = ae.Perm | ||
a.List = append(a.List, elem) | ||
} | ||
} | ||
|
||
func (a *acl) encode() []byte { | ||
buf := new(bytes.Buffer) | ||
ae := new(aclElem) | ||
binary.Write(buf, binary.LittleEndian, &a.Version) | ||
for _, elem := range a.List { | ||
ae.Tag = uint16(elem.getType()) | ||
ae.Perm = elem.Perm | ||
ae.ID = elem.getID() | ||
binary.Write(buf, binary.LittleEndian, ae) | ||
} | ||
return buf.Bytes() | ||
} | ||
|
||
func (a *acl) String() string { | ||
var finalacl string | ||
for _, acl := range a.List { | ||
finalacl += acl.String() + "\n" | ||
} | ||
return finalacl | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package main | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
func Test_acl_decode(t *testing.T) { | ||
type args struct { | ||
xattr []byte | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
want string | ||
}{ | ||
{ | ||
name: "decode string", | ||
args: args{ | ||
xattr: []byte{2, 0, 0, 0, 1, 0, 6, 0, 255, 255, 255, 255, 2, 0, 7, 0, 0, 0, 0, 0, 2, 0, 7, 0, 254, 255, 0, 0, 4, 0, 7, 0, 255, 255, 255, 255, 16, 0, 7, 0, 255, 255, 255, 255, 32, 0, 4, 0, 255, 255, 255, 255}, | ||
}, | ||
want: "user::rw-\nuser:0:rwx\nuser:65534:rwx\ngroup::rwx\nmask::rwx\nother::r--\n", | ||
}, | ||
{ | ||
name: "decode fail", | ||
args: args{ | ||
xattr: []byte("abctest"), | ||
}, | ||
want: "", | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
a := &acl{} | ||
a.decode(tt.args.xattr) | ||
if tt.want != a.String() { | ||
t.Errorf("acl.decode() = %v, want: %v", a.String(), tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func Test_acl_encode(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
want []byte | ||
args []aclElement | ||
}{ | ||
{ | ||
name: "encode values", | ||
want: []byte{2, 0, 0, 0, 1, 0, 6, 0, 255, 255, 255, 255, 2, 0, 7, 0, 0, 0, 0, 0, 2, 0, 7, 0, 254, 255, 0, 0, 4, 0, 7, 0, 255, 255, 255, 255, 16, 0, 7, 0, 255, 255, 255, 255, 32, 0, 4, 0, 255, 255, 255, 255}, | ||
args: []aclElement{ | ||
{ | ||
aclSID: 8589934591, | ||
Perm: 6, | ||
}, | ||
{ | ||
aclSID: 8589934592, | ||
Perm: 7, | ||
}, | ||
{ | ||
aclSID: 8590000126, | ||
Perm: 7, | ||
}, | ||
{ | ||
aclSID: 21474836479, | ||
Perm: 7, | ||
}, | ||
{ | ||
aclSID: 73014444031, | ||
Perm: 7, | ||
}, | ||
{ | ||
aclSID: 141733920767, | ||
Perm: 4, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "encode fail", | ||
want: []byte{2, 0, 0, 0}, | ||
args: []aclElement{}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
a := &acl{ | ||
Version: 2, | ||
List: tt.args, | ||
} | ||
if got := a.encode(); !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("acl.encode() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.