diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23dae51..81a6c46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,5 +40,9 @@ jobs: - name: Build working-directory: src/github.com/containerd/btrfs run: | - sudo apt-get update && sudo apt-get install -y libbtrfs-dev make vet binaries + + - name: Build with an old version of kernel headers + working-directory: src/github.com/containerd/btrfs + run: | + DOCKER_BUILDKIT=1 docker build -f hack/Dockerfile.compilation-test . diff --git a/README.md b/README.md index 505f39b..abdac2a 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,21 @@ Native Go bindings for btrfs. These are in the early stages. We will try to maintain stability, but please vendor if you are relying on these directly. +# Dependencies + +## v2.x + +Headers from kernel 4.12 or newer. +The package name is `linux-libc-dev` on Debian/Ubuntu, `kernel-headers` on Fedora and RHEL-like distros. + +The headers are only required on compilation time, not on run time. + +## v1.x + +libbtrfs headers. +The package name is `libbtrfs-dev` on Debian/Ubuntu, `btrfs-progs-devel` on Fedora and CentOS 7. +The package is not available for Rocky Linux and AlmaLinux. + # Contribute This package may not cover all the use cases for btrfs. If something you need diff --git a/btrfs.c b/btrfs.c index f0da012..fe7607c 100644 --- a/btrfs.c +++ b/btrfs.c @@ -14,20 +14,16 @@ limitations under the License. */ -#include -#include -#include -#include - +#include #include "btrfs.h" void unpack_root_item(struct gosafe_btrfs_root_item* dst, struct btrfs_root_item* src) { memcpy(dst->uuid, src->uuid, BTRFS_UUID_SIZE); memcpy(dst->parent_uuid, src->parent_uuid, BTRFS_UUID_SIZE); memcpy(dst->received_uuid, src->received_uuid, BTRFS_UUID_SIZE); - dst->gen = btrfs_root_generation(src); - dst->ogen = btrfs_root_otransid(src); - dst->flags = btrfs_root_flags(src); + dst->generation = src->generation; + dst->otransid = src->otransid; + dst->flags = src->flags; } /* unpack_root_ref(struct gosafe_btrfs_root_ref* dst, struct btrfs_root_ref* src) { */ diff --git a/btrfs.go b/btrfs.go index c60ab56..471dd34 100644 --- a/btrfs.go +++ b/btrfs.go @@ -17,8 +17,7 @@ package btrfs /* -#include -#include +#include #include "btrfs.h" static char* get_name_btrfs_ioctl_vol_args_v2(struct btrfs_ioctl_vol_args_v2* btrfs_struct) { @@ -154,13 +153,13 @@ func subvolMap(path string) (map[uint64]*Info, error) { // get an entry of the objectid, with name, but the parent is // the offset. - nname := C.btrfs_stack_root_ref_name_len(&rr) + nname := le16ToNative(rr.name_len) name := string(buf[C.sizeof_struct_btrfs_root_ref : C.sizeof_struct_btrfs_root_ref+uintptr(nname)]) info.ID = uint64(sh.objectid) info.ParentID = uint64(sh.offset) info.Name = name - info.DirID = uint64(C.btrfs_stack_root_ref_dirid(&rr)) + info.DirID = le64ToNative(rr.dirid) subvolsByID[uint64(sh.objectid)] = info } else if sh._type == C.BTRFS_ROOT_ITEM_KEY && @@ -185,8 +184,8 @@ func subvolMap(path string) (map[uint64]*Info, error) { info.ParentUUID = uuidString(&gri.parent_uuid) info.ReceivedUUID = uuidString(&gri.received_uuid) - info.Generation = uint64(gri.gen) - info.OriginalGeneration = uint64(gri.ogen) + info.Generation = le64ToNative(gri.generation) + info.OriginalGeneration = le64ToNative(gri.otransid) subvolsByID[uint64(sh.objectid)] = info } diff --git a/btrfs.h b/btrfs.h index 1ec451e..bbf0d7a 100644 --- a/btrfs.h +++ b/btrfs.h @@ -14,23 +14,25 @@ limitations under the License. */ -#include -#include -#include -#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,12,0) +#error "Headers from kernel >= 4.12 are required on compilation time (not on run time)" +#endif +#include +#include // unfortunately, we need to define "alignment safe" C structs to populate for // packed structs that aren't handled by cgo. Fields will be added here, as // needed. struct gosafe_btrfs_root_item { - u8 uuid[BTRFS_UUID_SIZE]; - u8 parent_uuid[BTRFS_UUID_SIZE]; - u8 received_uuid[BTRFS_UUID_SIZE]; + __u8 uuid[BTRFS_UUID_SIZE]; + __u8 parent_uuid[BTRFS_UUID_SIZE]; + __u8 received_uuid[BTRFS_UUID_SIZE]; - u64 gen; - u64 ogen; - u64 flags; + __le64 generation; + __le64 otransid; + __le64 flags; }; void unpack_root_item(struct gosafe_btrfs_root_item* dst, struct btrfs_root_item* src); diff --git a/cmd/btrfs-test/main.go b/cmd/btrfs-test/main.go index cc56eb0..e07623e 100644 --- a/cmd/btrfs-test/main.go +++ b/cmd/btrfs-test/main.go @@ -23,7 +23,7 @@ import ( "os" "text/tabwriter" - "github.com/containerd/btrfs" + "github.com/containerd/btrfs/v2" ) var ( diff --git a/go.mod b/go.mod index 346a825..71bc75b 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ -module github.com/containerd/btrfs +module github.com/containerd/btrfs/v2 go 1.19 + +require golang.org/x/sys v0.5.0 diff --git a/go.sum b/go.sum index e69de29..ba21016 100644 --- a/go.sum +++ b/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/hack/Dockerfile.compilation-test b/hack/Dockerfile.compilation-test new file mode 100644 index 0000000..23b14fc --- /dev/null +++ b/hack/Dockerfile.compilation-test @@ -0,0 +1,18 @@ +# Dockerfile for testing compilation with an old version of the kernel headers. + +ARG GOLANG_VERSION=1.19 +# LINUX_VERSION must be >= 4.12 (https://github.com/torvalds/linux/commit/fcc8487d477a3452a1d0ccbdd4c5e0e1e3cb8bed) +ARG LINUX_VERSION=4.12 + +FROM golang:${GOLANG_VERSION} +ARG LINUX_VERSION +RUN curl -sSL -O "https://mirrors.edge.kernel.org/pub/linux/kernel/v$(echo ${LINUX_VERSION} | cut -d. -f1).x/linux-${LINUX_VERSION}.tar.gz" +RUN tar Cxzf / "linux-${LINUX_VERSION}.tar.gz" && \ + cd "/linux-${LINUX_VERSION}" && \ + make headers_install INSTALL_HDR_PATH=/usr2 && \ + for f in /usr2/include/*; do rm -rf "/usr/include/$(basename ${f})"; done && \ + cp -a /usr2/include /usr + +COPY . /go/src/github.com/containerd/btrfs +WORKDIR /go/src/github.com/containerd/btrfs +RUN make binaries diff --git a/helpers.go b/helpers.go index 4f0c8f1..e4c4894 100644 --- a/helpers.go +++ b/helpers.go @@ -17,19 +17,20 @@ package btrfs /* -#include -#include -#include +#include "btrfs.h" */ import "C" import ( "bufio" "bytes" + "encoding/binary" "fmt" "os" "strings" "unsafe" + + "golang.org/x/sys/cpu" ) func subvolID(fd uintptr) (uint64, error) { @@ -48,7 +49,7 @@ var ( zeros = zeroArray[:] ) -func uuidString(uuid *[C.BTRFS_UUID_SIZE]C.u8) string { +func uuidString(uuid *[C.BTRFS_UUID_SIZE]C.__u8) string { b := (*[maxByteSliceSize]byte)(unsafe.Pointer(uuid))[:C.BTRFS_UUID_SIZE] if bytes.Equal(b, zeros) { @@ -58,6 +59,24 @@ func uuidString(uuid *[C.BTRFS_UUID_SIZE]C.u8) string { return fmt.Sprintf("%x-%x-%x-%x-%x", b[:4], b[4:4+2], b[6:6+2], b[8:8+2], b[10:16]) } +func le16ToNative(le16 C.__le16) uint16 { + if cpu.IsBigEndian { + b := make([]byte, 2) + binary.LittleEndian.PutUint16(b, uint16(le16)) + return binary.BigEndian.Uint16(b) + } + return uint16(le16) +} + +func le64ToNative(le64 C.__le64) uint64 { + if cpu.IsBigEndian { + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, uint64(le64)) + return binary.BigEndian.Uint64(b) + } + return uint64(le64) +} + func findMountPoint(path string) (string, error) { fp, err := os.Open("/proc/self/mounts") if err != nil {