Initial commit on Forgejo

Fresh repository history for elektrine/magpie hosted at
https://git.elektrine.com/elektrine/magpie.
This commit is contained in:
Maxfield Luke 2026-07-29 04:56:34 -04:00
commit ddbd19f153
23 changed files with 5636 additions and 0 deletions

181
backup.go Normal file
View file

@ -0,0 +1,181 @@
package main
import (
"archive/tar"
"compress/gzip"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
func BackupDir(src string, dst string) error {
out, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o600)
if err != nil {
return err
}
defer out.Close()
gz := gzip.NewWriter(out)
defer gz.Close()
tw := tar.NewWriter(gz)
defer tw.Close()
root, err := filepath.Abs(src)
if err != nil {
return err
}
return filepath.WalkDir(root, func(path string, entry os.DirEntry, err error) error {
if err != nil || entry.IsDir() {
return err
}
if entry.Type()&os.ModeSymlink != 0 {
return fmt.Errorf("refusing to back up symlink %q", path)
}
rel, err := filepath.Rel(root, path)
if err != nil {
return err
}
info, err := entry.Info()
if err != nil {
return err
}
header, err := tar.FileInfoHeader(info, "")
if err != nil {
return err
}
header.Name = filepath.ToSlash(rel)
if err := tw.WriteHeader(header); err != nil {
return err
}
file, err := os.Open(path)
if err != nil {
return err
}
fileInfo, statErr := file.Stat()
currentInfo, lstatErr := os.Lstat(path)
if statErr != nil || lstatErr != nil {
_ = file.Close()
if statErr != nil {
return statErr
}
return lstatErr
}
if currentInfo.Mode()&os.ModeSymlink != 0 || !os.SameFile(currentInfo, fileInfo) {
_ = file.Close()
return fmt.Errorf("refusing to back up replaced or symlinked file %q", path)
}
_, copyErr := io.Copy(tw, file)
closeErr := file.Close()
if copyErr != nil {
return copyErr
}
return closeErr
})
}
func RestoreDir(src string, dst string) error {
return RestoreDirWithLimit(src, dst, defaultMaxRestoreBytes)
}
func RestoreDirWithLimit(src string, dst string, maxBytes int64) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
gz, err := gzip.NewReader(in)
if err != nil {
return err
}
defer gz.Close()
tr := tar.NewReader(gz)
root, err := filepath.Abs(dst)
if err != nil {
return err
}
if err := os.MkdirAll(root, 0o700); err != nil {
return err
}
var restoredBytes int64
for {
header, err := tr.Next()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
if strings.Contains(header.Name, "..") || strings.HasPrefix(header.Name, "/") {
return fmt.Errorf("unsafe archive path %q", header.Name)
}
if header.Typeflag != tar.TypeReg && header.Typeflag != tar.TypeRegA && header.Typeflag != tar.TypeDir {
return fmt.Errorf("unsupported archive entry %q", header.Name)
}
if header.Size < 0 {
return fmt.Errorf("invalid archive size for %q", header.Name)
}
if header.Typeflag == tar.TypeReg || header.Typeflag == tar.TypeRegA {
restoredBytes += header.Size
if maxBytes > 0 && restoredBytes > maxBytes {
return fmt.Errorf("restore exceeds maximum size")
}
}
path := filepath.Join(root, filepath.FromSlash(header.Name))
if !strings.HasPrefix(path, root+string(os.PathSeparator)) && path != root {
return fmt.Errorf("unsafe archive path %q", header.Name)
}
if err := rejectSymlinkParents(root, path); err != nil {
return err
}
if header.FileInfo().IsDir() {
if err := os.MkdirAll(path, 0o700); err != nil {
return err
}
continue
}
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
return err
}
if info, err := os.Lstat(path); err == nil && info.Mode()&os.ModeSymlink != 0 {
return fmt.Errorf("refusing to restore through symlink %q", header.Name)
}
out, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o600)
if err != nil {
return err
}
_, copyErr := io.CopyN(out, tr, header.Size)
closeErr := out.Close()
if copyErr != nil {
return copyErr
}
if closeErr != nil {
return closeErr
}
}
}
func rejectSymlinkParents(root string, path string) error {
current := root
rel, err := filepath.Rel(root, filepath.Dir(path))
if err != nil {
return err
}
if rel == "." {
return nil
}
for _, part := range strings.Split(rel, string(os.PathSeparator)) {
current = filepath.Join(current, part)
info, err := os.Lstat(current)
if errors.Is(err, os.ErrNotExist) {
return nil
}
if err != nil {
return err
}
if info.Mode()&os.ModeSymlink != 0 {
return fmt.Errorf("refusing to restore through symlink parent %q", current)
}
}
return nil
}