Files
kopia/fs/dir_json.go
Jarek Kowalski bdd1f9e886 dir api cleanup
2016-04-01 20:02:21 -07:00

151 lines
2.8 KiB
Go

package fs
import (
"bufio"
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"strconv"
"time"
"github.com/kopia/kopia/cas"
)
var (
header = []byte("DIRECTORY:v1")
newLine = []byte("\n")
)
type writer struct {
lastEntryType EntryType
objectWriter io.Writer
}
type serializedDirectoryEntryV1 struct {
Name string `json:"name"`
Type string `json:"type"`
FileSize *int64 `json:"size,omitempty,string"`
Mode string `json:"mode"`
ModTime time.Time `json:"modified,omitempty"`
UserID uint32 `json:"uid,omitempty"`
GroupID uint32 `json:"gid,omitempty"`
ObjectID string `json:"objectID"`
}
func serializeManifestEntry(e *Entry) []byte {
s := serializedDirectoryEntryV1{
Name: e.Name,
Type: string(e.Type),
Mode: strconv.FormatInt(int64(e.Mode), 8),
ObjectID: string(e.ObjectID),
UserID: e.UserID,
GroupID: e.GroupID,
}
s.ModTime = e.ModTime.UTC()
if e.Type == EntryTypeFile {
fs := e.Size
s.FileSize = &fs
}
jsonBytes, _ := json.Marshal(s)
return jsonBytes
}
func (w *writer) WriteEntry(e *Entry) error {
if e.Type == "" {
return errors.New("missing entry type")
}
if w.lastEntryType == "" {
if _, err := w.objectWriter.Write(header); err != nil {
return err
}
if _, err := w.objectWriter.Write(newLine); err != nil {
return err
}
} else {
if w.lastEntryType != e.Type && e.Type == EntryTypeDirectory {
return errors.New("directories must be added before non-directories")
}
}
w.lastEntryType = e.Type
s := serializeManifestEntry(e)
if _, err := w.objectWriter.Write(s); err != nil {
return err
}
if _, err := w.objectWriter.Write(newLine); err != nil {
return err
}
return nil
}
func (dir Directory) writeJSON(w io.Writer) error {
dw := &writer{
objectWriter: w,
}
for _, d := range dir.Entries {
if err := dw.WriteEntry(d); err != nil {
return err
}
}
return nil
}
func (dir *Directory) readJSON(r io.Reader) error {
var err error
s := bufio.NewScanner(r)
if !s.Scan() {
return fmt.Errorf("empty file")
}
if !bytes.Equal(s.Bytes(), header) {
return fmt.Errorf("invalid header: expected '%v' got '%v'", header, s.Bytes())
}
var entries []*Entry
for s.Scan() {
line := s.Bytes()
var v serializedDirectoryEntryV1
if err := json.Unmarshal(line, &v); err != nil {
return nil
}
e := &Entry{}
e.Name = v.Name
e.UserID = v.UserID
e.GroupID = v.GroupID
e.ObjectID, err = cas.ParseObjectID(v.ObjectID)
if err != nil {
return nil
}
m, err := strconv.ParseInt(v.Mode, 8, 16)
if err != nil {
return nil
}
e.Mode = int16(m)
e.ModTime = v.ModTime
e.Type = EntryType(v.Type)
if e.Type == EntryTypeFile {
if v.FileSize == nil {
return fmt.Errorf("missing file size")
}
e.Size = *v.FileSize
}
entries = append(entries, e)
}
dir.Entries = entries
return nil
}