mirror of
https://github.com/dennwc/btrfs
synced 2025-03-11 06:48:27 +00:00
implement GetCompression; add tests
This commit is contained in:
parent
b56d643295
commit
3343324613
35
btrfs.go
35
btrfs.go
@ -1,6 +1,7 @@
|
||||
package btrfs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/dennwc/btrfs/ioctl"
|
||||
"io"
|
||||
@ -228,6 +229,8 @@ const (
|
||||
ZLIB = Compression("zlib")
|
||||
)
|
||||
|
||||
const xattrCompression = xattrPrefix + "compression"
|
||||
|
||||
func SetCompression(path string, v Compression) error {
|
||||
var value []byte
|
||||
if v != CompressionNone {
|
||||
@ -237,9 +240,39 @@ func SetCompression(path string, v Compression) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := syscall.Setxattr(path, xattrPrefix+"compression", value, 0)
|
||||
err := syscall.Setxattr(path, xattrCompression, value, 0)
|
||||
if err != nil {
|
||||
return &os.PathError{Op: "setxattr", Path: path, Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetCompression(path string) (Compression, error) {
|
||||
var buf []byte
|
||||
for {
|
||||
sz, err := syscall.Getxattr(path, xattrCompression, nil)
|
||||
if err == syscall.ENODATA || sz == 0 {
|
||||
return CompressionNone, nil
|
||||
} else if err != nil {
|
||||
return CompressionNone, &os.PathError{Op: "getxattr", Path: path, Err: err}
|
||||
}
|
||||
if cap(buf) < sz {
|
||||
buf = make([]byte, sz)
|
||||
} else {
|
||||
buf = buf[:sz]
|
||||
}
|
||||
sz, err = syscall.Getxattr(path, xattrCompression, buf)
|
||||
if err == syscall.ENODATA {
|
||||
return CompressionNone, nil
|
||||
} else if err == syscall.ERANGE {
|
||||
// xattr changed by someone else, and is larger than our current buffer
|
||||
continue
|
||||
} else if err != nil {
|
||||
return CompressionNone, &os.PathError{Op: "getxattr", Path: path, Err: err}
|
||||
}
|
||||
buf = buf[:sz]
|
||||
break
|
||||
}
|
||||
buf = bytes.TrimSuffix(buf, []byte{0})
|
||||
return Compression(buf), nil
|
||||
}
|
||||
|
97
btrfs_test.go
Normal file
97
btrfs_test.go
Normal file
@ -0,0 +1,97 @@
|
||||
package btrfs
|
||||
|
||||
import (
|
||||
"github.com/dennwc/btrfs/test"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const sizeDef = 256 * 1024 * 1024
|
||||
|
||||
func TestOpen(t *testing.T) {
|
||||
dir, closer := btrfstest.New(t, sizeDef)
|
||||
defer closer()
|
||||
fs, err := Open(dir, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = fs.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSubvolume(t *testing.T) {
|
||||
dir, closer := btrfstest.New(t, sizeDef)
|
||||
defer closer()
|
||||
|
||||
isSubvol := func(path string, expect bool) {
|
||||
ok, err := IsSubVolume(path)
|
||||
if err != nil {
|
||||
t.Errorf("failed to check subvolume %v: %v", path, err)
|
||||
return
|
||||
} else if ok != expect {
|
||||
t.Errorf("unexpected result for %v", path)
|
||||
}
|
||||
}
|
||||
mkdir := func(path string) {
|
||||
path = filepath.Join(dir, path)
|
||||
if err := os.MkdirAll(path, 0755); err != nil {
|
||||
t.Fatalf("cannot create dir %v: %v", path, err)
|
||||
}
|
||||
isSubvol(path, false)
|
||||
}
|
||||
|
||||
mksub := func(path string) {
|
||||
path = filepath.Join(dir, path)
|
||||
if err := CreateSubVolume(path); err != nil {
|
||||
t.Fatalf("cannot create subvolume %v: %v", path, err)
|
||||
}
|
||||
isSubvol(path, true)
|
||||
}
|
||||
|
||||
mksub("v1")
|
||||
|
||||
mkdir("v1/d2")
|
||||
mksub("v1/v2")
|
||||
|
||||
mkdir("v1/d2/d3")
|
||||
mksub("v1/d2/v3")
|
||||
|
||||
mkdir("v1/v2/d3")
|
||||
mksub("v1/v2/v3")
|
||||
|
||||
mkdir("d1")
|
||||
|
||||
mkdir("d1/d2")
|
||||
mksub("d1/v2")
|
||||
|
||||
mkdir("d1/d2/d3")
|
||||
mksub("d1/d2/v3")
|
||||
|
||||
mkdir("d1/v2/d3")
|
||||
mksub("d1/v2/v3")
|
||||
}
|
||||
|
||||
func TestCompression(t *testing.T) {
|
||||
dir, closer := btrfstest.New(t, sizeDef)
|
||||
defer closer()
|
||||
fs, err := Open(dir, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer fs.Close()
|
||||
if err := fs.CreateSubVolume("sub"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
path := filepath.Join(dir, "sub")
|
||||
|
||||
if err := SetCompression(path, LZO); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c, err := GetCompression(path); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if c != LZO {
|
||||
t.Fatalf("unexpected compression returned: %q", string(c))
|
||||
}
|
||||
}
|
106
test/btrfstest.go
Normal file
106
test/btrfstest.go
Normal file
@ -0,0 +1,106 @@
|
||||
package btrfstest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func run(name string, args ...string) error {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
cmd := exec.Command(name, args...)
|
||||
cmd.Stdout = buf
|
||||
cmd.Stderr = buf
|
||||
err := cmd.Run()
|
||||
if err == nil {
|
||||
return nil
|
||||
} else if buf.Len() == 0 {
|
||||
return err
|
||||
}
|
||||
return errors.New("error: " + strings.TrimSpace(string(buf.Bytes())))
|
||||
}
|
||||
|
||||
func Mkfs(file string, size int64) error {
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = f.Truncate(size); err != nil {
|
||||
f.Close()
|
||||
return err
|
||||
}
|
||||
if err = f.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = run("mkfs.btrfs", file); err != nil {
|
||||
os.Remove(file)
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func Mount(mount string, file string) error {
|
||||
if err := run("mount", file, mount); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func New(t testing.TB, size int64) (string, func()) {
|
||||
f, err := ioutil.TempFile("", "btrfs_vol")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
name := f.Name()
|
||||
f.Close()
|
||||
rm := func() {
|
||||
os.Remove(name)
|
||||
}
|
||||
if err = Mkfs(name, size); err != nil {
|
||||
rm()
|
||||
}
|
||||
mount, err := ioutil.TempDir("", "btrfs_mount")
|
||||
if err != nil {
|
||||
rm()
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = Mount(mount, name); err != nil {
|
||||
rm()
|
||||
os.RemoveAll(mount)
|
||||
if txt := err.Error(); strings.Contains(txt, "permission denied") ||
|
||||
strings.Contains(txt, "only root") {
|
||||
t.Skip(err)
|
||||
} else {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
done := false
|
||||
return mount, func() {
|
||||
if done {
|
||||
return
|
||||
}
|
||||
for i := 0; i < 5; i++ {
|
||||
if err := run("umount", mount); err == nil {
|
||||
break
|
||||
} else {
|
||||
log.Println("umount failed:", err)
|
||||
if strings.Contains(err.Error(), "busy") {
|
||||
time.Sleep(time.Second)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := os.Remove(mount); err != nil {
|
||||
log.Println("cleanup failed:", err)
|
||||
}
|
||||
rm()
|
||||
done = true
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user