Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 59 additions & 4 deletions zfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const (
)

// DestroyFlag is the options flag passed to Destroy
type DestroyFlag int
type DestroyFlag int64

// Valid destroy options
const (
Expand All @@ -79,6 +79,17 @@ const (
DestroyForceUmount = 1 << iota
)

// SendFlag is the options flags passed to SendSnapshot
type SendFlag int64

// Valid send options
const (
SendDefault SendFlag = 1 << iota
IncrementalStream = 1 << iota
IncrementalPackage = 1 << iota
ReplicationStream = 1 << iota
)

// InodeChange represents a change as reported by Diff
type InodeChange struct {
Change ChangeType
Expand Down Expand Up @@ -233,16 +244,60 @@ func ReceiveSnapshot(input io.Reader, name string) (*Dataset, error) {
return GetDataset(name)
}

// ReceiveSnapshotRollback forces a rollback of the file system to the most recent
// snapshot before the receive is initiated. After, receives a ZFS stream from the
// input io.Reader like ReceiveSnapshot does.
func ReceiveSnapshotRollback(input io.Reader, name string, overwrite bool) (*Dataset, error) {
c := command{Command: "zfs", Stdin: input}
_, err := c.Run("receive", "-F", name)
if err != nil {
return nil, err
}
return GetDataset(name)
}

// SendSnapshot sends a ZFS stream of a snapshot to the input io.Writer.
// An error will be returned if the input dataset is not of snapshot type.
func (d *Dataset) SendSnapshot(output io.Writer) error {
func (d *Dataset) SendSnapshot(output io.Writer, flags SendFlag) error {
if d.Type != DatasetSnapshot {
return errors.New("can only send snapshots")
}
c := command{Command: "zfs", Stdout: output}

// Flags for SendSnapshot
if flags&ReplicationStream !=0 {
_, err := c.Run("send", "-R", d.Name)
return err
} else {
_, err := c.Run("send", d.Name)
return err
}
}

// SendSnapshotIncremental sends a ZFS incremental stream to the input io.Writer.
// Includes options -i and -I to send an incremental stream or a stream package respectively.
func SendSnapshotIncremental(output io.Writer, d1 *Dataset, d2 *Dataset, replication bool, flags SendFlag) error {
if d1.Type != DatasetSnapshot || d2.Type != DatasetSnapshot {
return errors.New("can only send snapshots")
}

// Flags for SendSnapshot
option := ""
if flags&IncrementalStream !=0 {
option = "-i"
}
if flags&IncrementalPackage !=0 {
option = "-I"
}
c := command{Command: "zfs", Stdout: output}
_, err := c.Run("send", d.Name)
return err
if replication == true {
stream := "-R"
_, err := c.Run("send", stream, option, d1.Name, d2.Name)
return err
} else {
_, err := c.Run("send", option, d1.Name, d2.Name)
return err
}
}

// CreateVolume creates a new ZFS volume with the specified name, size, and
Expand Down
39 changes: 36 additions & 3 deletions zfs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ func TestSendSnapshot(t *testing.T) {
ok(t, err)
defer os.Remove(file.Name())

err = s.SendSnapshot(file)
err = s.SendSnapshot(file, zfs.SendDefault)
ok(t, err)

ok(t, s.Destroy(zfs.DestroyDefault))
Expand All @@ -264,6 +264,39 @@ func TestSendSnapshot(t *testing.T) {
})
}

func TestSendSnapshotIncremental(t *testing.T) {
zpoolTest(t, func() {
f, err := zfs.CreateFilesystem("test/snapshot-test", nil)
ok(t, err)

filesystems, err := zfs.Filesystems("")
ok(t, err)

for _, filesystem := range filesystems {
equals(t, zfs.DatasetFilesystem, filesystem.Type)
}

s1, err := f.Snapshot("snap1", false)
ok(t, err)
s2, err := f.Snapshot("snap2", false)
ok(t, err)

file, _ := ioutil.TempFile("/tmp/", "zfs-")
defer file.Close()
err = file.Truncate(pow2(30))
ok(t, err)
defer os.Remove(file.Name())

err = zfs.SendSnapshotIncremental(file, s1, s2, true, zfs.IncrementalStream)
ok(t, err)

ok(t, s2.Destroy(zfs.DestroyDefault))
ok(t, s1.Destroy(zfs.DestroyDefault))

ok(t, f.Destroy(zfs.DestroyDefault))
})
}

func TestChildren(t *testing.T) {
zpoolTest(t, func() {
f, err := zfs.CreateFilesystem("test/snapshot-test", nil)
Expand Down Expand Up @@ -350,7 +383,7 @@ func TestDiff(t *testing.T) {
snapshot, err := fs.Snapshot("snapshot", false)
ok(t, err)

unicodeFile, err := os.Create(filepath.Join(fs.Mountpoint, "i ❤ unicode"))
unicodeFile, err := os.Create(filepath.Join(fs.Mountpoint, "i_love_unicode"))
ok(t, err)

err = os.Rename(movedFile.Name(), movedFile.Name()+"-new")
Expand All @@ -377,7 +410,7 @@ func TestDiff(t *testing.T) {
equals(t, zfs.File, inodeChanges[2].Type)
equals(t, zfs.Renamed, inodeChanges[2].Change)

equals(t, "/test/origin/i ❤ unicode", inodeChanges[3].Path)
equals(t, "/test/origin/i_love_unicode", inodeChanges[3].Path)
equals(t, zfs.File, inodeChanges[3].Type)
equals(t, zfs.Created, inodeChanges[3].Change)

Expand Down