Backing up lxc containers using `btrfs send`

I’m trying to backup my (unprivileged) containers using btrfs send. This works great when running all btrfs commands as root, however for production this is not desired. I would assume that running with lxc-usernsexec should work (that user should be allowed to read the subvolume I would presume), however I keep getting permission errors when sending (NB setting the subvolume to ro does work). The issue seems to be caused by the fact that btrfs is trying to read the root of the mount (for searching), and it is getting denied. In storage/btrfs.c file in lxc it seems custom searching is implemented, is this also because of the same issue? Does anyone have a workaround for running btrfs send as an unprivileged user?

For completeness here is a strace when running btrfs send:

cmd/lxc_usernsexec.c: 64: opentty - Permission denied - Failed to open tty
cmd/lxc_usernsexec.c: 64: opentty - No such file or directory - Failed to open tty
cmd/lxc_usernsexec.c: 64: opentty - Permission denied - Failed to open tty
execve("/usr/bin/btrfs", ["btrfs", "send", "-v", "-p", ".local/share/lxc/base/roo"..., ".local/share/lxc/copy/rootfs"], 0x7ffc46bcd268 /* 20 vars */) = 0
brk(NULL)                               = 0x55bdc0f60000
arch_prctl(0x3001 /* ARCH_??? */, 0x7fff9f6bb0d0) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=30702, ...}) = 0
mmap(NULL, 30702, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f76d792a000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libuuid.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200%\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=30936, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f76d7928000
mmap(NULL, 32792, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f76d791f000
mmap(0x7f76d7921000, 16384, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f76d7921000
mmap(0x7f76d7925000, 4096, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6000) = 0x7f76d7925000
mmap(0x7f76d7926000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6000) = 0x7f76d7926000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libblkid.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200\266\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=351352, ...}) = 0
mmap(NULL, 354088, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f76d78c8000
mprotect(0x7f76d78d2000, 290816, PROT_NONE) = 0
mmap(0x7f76d78d2000, 221184, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xa000) = 0x7f76d78d2000
mmap(0x7f76d7908000, 65536, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x40000) = 0x7f76d7908000
mmap(0x7f76d7919000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x50000) = 0x7f76d7919000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libz.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200\"\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=108936, ...}) = 0
mmap(NULL, 110776, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f76d78ac000
mprotect(0x7f76d78ae000, 98304, PROT_NONE) = 0
mmap(0x7f76d78ae000, 69632, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f76d78ae000
mmap(0x7f76d78bf000, 24576, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x13000) = 0x7f76d78bf000
mmap(0x7f76d78c6000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19000) = 0x7f76d78c6000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/liblzo2.so.2", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\3400\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=145480, ...}) = 0
mmap(NULL, 147472, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f76d7887000
mprotect(0x7f76d788a000, 131072, PROT_NONE) = 0
mmap(0x7f76d788a000, 114688, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7f76d788a000
mmap(0x7f76d78a6000, 12288, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1f000) = 0x7f76d78a6000
mmap(0x7f76d78aa000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f76d78aa000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libzstd.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@B\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=686160, ...}) = 0
mmap(NULL, 688192, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f76d77de000
mmap(0x7f76d77e2000, 598016, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x4000) = 0x7f76d77e2000
mmap(0x7f76d7874000, 69632, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x96000) = 0x7f76d7874000
mmap(0x7f76d7885000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xa6000) = 0x7f76d7885000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220\201\0\0\0\0\0\0"..., 832) = 832
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\345Ga\367\265T\320\374\301V)Yf]\223\337"..., 68, 824) = 68
fstat(3, {st_mode=S_IFREG|0755, st_size=157224, ...}) = 0
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\345Ga\367\265T\320\374\301V)Yf]\223\337"..., 68, 824) = 68
mmap(NULL, 140408, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f76d77bb000
mmap(0x7f76d77c2000, 69632, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x7000) = 0x7f76d77c2000
mmap(0x7f76d77d3000, 20480, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18000) = 0x7f76d77d3000
mmap(0x7f76d77d8000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c000) = 0x7f76d77d8000
mmap(0x7f76d77da000, 13432, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f76d77da000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360q\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
fstat(3, {st_mode=S_IFREG|0755, st_size=2029224, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f76d77b9000
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
mmap(NULL, 2036952, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f76d75c7000
mprotect(0x7f76d75ec000, 1847296, PROT_NONE) = 0
mmap(0x7f76d75ec000, 1540096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7f76d75ec000
mmap(0x7f76d7764000, 303104, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19d000) = 0x7f76d7764000
mmap(0x7f76d77af000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f76d77af000
mmap(0x7f76d77b5000, 13528, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f76d77b5000
close(3)                                = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f76d75c4000
arch_prctl(ARCH_SET_FS, 0x7f76d75c48c0) = 0
mprotect(0x7f76d77af000, 12288, PROT_READ) = 0
mprotect(0x7f76d77d8000, 4096, PROT_READ) = 0
mprotect(0x7f76d7885000, 4096, PROT_READ) = 0
mprotect(0x7f76d78aa000, 4096, PROT_READ) = 0
mprotect(0x7f76d78c6000, 4096, PROT_READ) = 0
mprotect(0x7f76d7919000, 20480, PROT_READ) = 0
mprotect(0x7f76d7926000, 4096, PROT_READ) = 0
mprotect(0x55bdbf5cb000, 24576, PROT_READ) = 0
mprotect(0x7f76d795f000, 4096, PROT_READ) = 0
munmap(0x7f76d792a000, 30702)           = 0
set_tid_address(0x7f76d75c4b90)         = 1565
set_robust_list(0x7f76d75c4ba0, 24)     = 0
rt_sigaction(SIGRTMIN, {sa_handler=0x7f76d77c2bf0, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x7f76d77d03c0}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {sa_handler=0x7f76d77c2c90, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART|SA_SIGINFO, sa_restorer=0x7f76d77d03c0}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
brk(NULL)                               = 0x55bdc0f60000
brk(0x55bdc0f81000)                     = 0x55bdc0f81000
getcwd("/home/user", 4096)         = 16
lstat("/home/user/.local", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/home/user/.local/share", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/home/user/.local/share/lxc", {st_mode=S_IFDIR|0777, st_size=30, ...}) = 0
lstat("/home/user/.local/share/lxc/base", {st_mode=S_IFDIR|0770, st_size=38, ...}) = 0
lstat("/home/user/.local/share/lxc/base/rootfs", {st_mode=S_IFDIR|0755, st_size=142, ...}) = 0
openat(0, "/home/user/.local/share/lxc/base/rootfs", O_RDONLY|O_NOATIME) = 3
ioctl(3, BTRFS_IOC_SUBVOL_GETFLAGS, BTRFS_SUBVOL_RDONLY) = 0
close(3)                                = 0
ioctl(1, TCGETS, 0x7fff9f6b9e80)        = -1 ENOTTY (Inappropriate ioctl for device)
getcwd("/home/user", 4096)         = 16
lstat("/home/user/.local", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/home/user/.local/share", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/home/user/.local/share/lxc", {st_mode=S_IFDIR|0777, st_size=30, ...}) = 0
lstat("/home/user/.local/share/lxc/copy", {st_mode=S_IFDIR|0770, st_size=24, ...}) = 0
lstat("/home/user/.local/share/lxc/copy/rootfs", {st_mode=S_IFDIR|0755, st_size=142, ...}) = 0
openat(AT_FDCWD, "/home/user/.local/share/lxc/copy/rootfs", O_RDONLY|O_NOATIME) = 3
close(3)                                = 0
openat(AT_FDCWD, "/proc/self/mounts", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
read(3, "/dev/root / ext4 rw,relatime,dis"..., 1024) = 1024
read(3, ",noexec,relatime,cpu,cpuacct 0 0"..., 1024) = 1024
read(3, "v,noexec,relatime 0 0\nproc /proc"..., 1024) = 642
read(3, "", 1024)                       = 0
close(3)                                = 0
lstat("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/home/user", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/home/user/.local", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/home/user/.local/share", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/home/user/.local/share/lxc", {st_mode=S_IFDIR|0777, st_size=30, ...}) = 0
openat(AT_FDCWD, "/home/user/.local/share/lxc", O_RDONLY|O_NOATIME) = -1 EPERM (Operation not permitted)
write(2, "ERROR: ", 7ERROR: )                  = 7
write(2, "cannot open '/home/user/.lo"..., 71cannot open '/home/user/.local/share/lxc': Operation not permitted) = 71
write(2, "\n", 1
)                       = 1
exit_group(1)                           = ?
+++ exited with 1 +++