aboutsummaryrefslogtreecommitdiff
path: root/backup/restore.go
diff options
context:
space:
mode:
Diffstat (limited to 'backup/restore.go')
-rw-r--r--backup/restore.go134
1 files changed, 134 insertions, 0 deletions
diff --git a/backup/restore.go b/backup/restore.go
new file mode 100644
index 0000000..6a03200
--- /dev/null
+++ b/backup/restore.go
@@ -0,0 +1,134 @@
+package backup
+
+import (
+ "code.laria.me/petrific/acl"
+ "code.laria.me/petrific/fs"
+ "code.laria.me/petrific/objects"
+ "code.laria.me/petrific/storage"
+ "fmt"
+ "io"
+ "os"
+)
+
+func RestoreFile(s storage.Storage, id objects.ObjectId, w io.Writer) error {
+ file, err := storage.GetObjectOfType(s, id, objects.OTFile)
+ if err != nil {
+ return err
+ }
+
+ for i, fragment := range *file.(*objects.File) {
+ blob_obj, err := storage.GetObjectOfType(s, fragment.Blob, objects.OTBlob)
+ if err != nil {
+ return err
+ }
+ blob := *blob_obj.(*objects.Blob)
+
+ if uint64(len(blob)) != fragment.Size {
+ return fmt.Errorf("RestoreFile: blob size of %s doesn't match size in fragment %d of file %s", fragment.Blob, i, id)
+ }
+
+ if _, err := w.Write(blob); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func execBitFromACL(a acl.ACL) bool {
+ return a.ToUnixPerms()&0100 != 0
+}
+
+func RestoreDir(s storage.Storage, id objects.ObjectId, root fs.Dir) error {
+ tree_obj, err := storage.GetObjectOfType(s, id, objects.OTTree)
+ tree := tree_obj.(objects.Tree)
+
+ seen := make(map[string]struct{})
+
+ for name, file_info := range tree {
+ switch file_info.Type() {
+ case objects.TETFile:
+ tmpname := fmt.Sprintf(".petrific-%d-%s", os.Getpid(), id)
+ new_file, err := root.CreateChildFile(tmpname, execBitFromACL(file_info.ACL()))
+ if err != nil {
+ return err
+ }
+ rwc, err := new_file.Open()
+ if err != nil {
+ return err
+ }
+
+ if err := RestoreFile(s, file_info.(objects.TreeEntryFile).Ref, rwc); err != nil {
+ rwc.Close()
+ return err
+ }
+ rwc.Close()
+
+ if err := root.RenameChild(tmpname, name); err != nil {
+ return err
+ }
+ case objects.TETDir:
+ var subdir fs.Dir
+
+ // Try to use existing directory
+ child, err := root.GetChild(name)
+ if err == nil {
+ if child.Type() == fs.FDir {
+ subdir = child.(fs.Dir)
+ } else {
+ if err := child.Delete(); err != nil {
+ return err
+ }
+ }
+ } else if err != os.ErrNotExist {
+ return err
+ }
+
+ // Create directory, if it doesn't exist
+ if subdir == nil {
+ subdir, err = root.CreateChildDir(name)
+ if err != nil {
+ return err
+ }
+ }
+
+ if err := RestoreDir(s, file_info.(objects.TreeEntryDir).Ref, subdir); err != nil {
+ return err
+ }
+ case objects.TETSymlink:
+ // Is there already a child of that name? If yes, delete it
+ child, err := root.GetChild(name)
+ if err == nil {
+ if err := child.Delete(); err != nil {
+ return err
+ }
+ } else if err != os.ErrNotExist {
+ return err
+ }
+
+ if _, err := root.CreateChildSymlink(name, file_info.(objects.TreeEntrySymlink).Target); err != nil {
+ return err
+ }
+ default:
+ return fmt.Errorf("child '%s' of %s has unknown tree entry type %s", name, id, file_info.Type())
+ }
+
+ seen[name] = struct{}{}
+ }
+
+ // We now restored all children, we now need to remove the children of root, that shouldn't be there accoring to the backup
+ children, err := root.Readdir()
+ if err != nil {
+ return err
+ }
+ for _, c := range children {
+ _, ok := seen[c.Name()]
+ if !ok {
+ if err := c.Delete(); err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}