TL;DR
Attempting to format a block device on my FUSE file system fails with EPERM
at the open
syscall. Permissions are set to 777 and the necessary ioctl
s are stubbed, but no logs are printed from within the FUSE handler.
Background
I'm writing a program to create virtual disk images. One of my criteria is that it must be able to run with zero superuser access, meaning I can't mount loopback devices, change owners of files or even edit /etc/fuse.conf. For this reason, my approach ends up being fairly long-winded. Specifically, in order to format the various partitions on the disk, I would like to be able to use system tools, because that gives me a far greater range of possible file systems. This involves exposing the various partitions on the VDisk as block devices to the system. However, all the possible methods I've found have required either nbd
s or loopback devices. Both of which require superuser access.
Implementing FUSE myself
However, implementing block devices in FUSE is not only possible, but supported. Unfortunately, I wasn't able to find much documentation on the matter and since I'm doing all this in Rust, the documentation world for this is even more scarce.
I've implemented the following FUSE methods:
-
init
-
lookup
-
getattr
-
open
-
read
-
write
-
readdir
-
ioctl
BLKGETSIZE
BLKFLSBUF
BLKSSZGET
I can list the contents of the file system and get directory/file information. I'm deliberately ignoring methods which create or modify resources, as this is done through the build process.
The error
As mentioned, I get permission denied (EPERM
) error. strace
ing the mkfs
call shows that it's the open
call to the block device that fails on the kernel side. Full strace
result.
execve("/usr/sbin/mkfs.fat", ["mkfs.fat", "out/partitions/EFI"], 0x7ffd42f64ab8 /* 76 vars */) = 0
--- snip ---
openat(AT_FDCWD, "out/partitions/EFI", O_RDWR|O_EXCL) = -1 EACCES (Permission denied)
write(2, "mkfs.fat: unable to open out/par"..., 63mkfs.fat: unable to open out/partitions/EFI: Permission denied
) = 63
exit_group(1) = ?
For clarity, my directory structure looks like this:
out
├── minimal.qcow2 [raw disk image] (shadows minimal.qcow2 [qcow2 file] with qemu-storage-daemon)
├── partitions
│ ├── EFI [Block device]
│ └── System [Block device]
└── qemu-monitor.sock [UNIX domain socket]
Of course, there are logging functions tracing every method. I do see logs when listing out the partitions, but not when formatting.
As I mentioned, I've found very little documentation on what could actually be causing this error.
Further insights
Thanks to the insights from @orenkishon, I've found some more details that just baffle me.
-
I found some options in
fuser
which were interesting:MountOption::Dev
Enable special character and block devicesMountOption::DefaultPermission
Enable permission checking in the kernelMountOption::RW
Read-write filesystem (apparently not a default option)
Unfortunately, no combination of which resolved my issue.
-
Log functions aren't called immediately. They seem to be tied to some sort of flushing operation. I can run the
mkfs.fat
command, see one or two logs, switch back to my IDE and see a page worth of logs appear.This may be due to the fact that the directory I'm generating the files is within the project's directory, so it is visible to the IDE, but it strikes me as very unusual.
-
The log in the
access
function is never visible, but in thestatfs
function is, but only ifmkfs
is called from outside theout
directory and is the first of anymkfs
calls.project > cd ./out project/out > mkfs.fat partitions/EFI mkfs.fat 4.2 (2021-01-31) mkfs.fat: unable to open partitions/EFI: Permission denied # No logs project > mkfs.fat out/partitions/EFI mkfs.fat 4.2 (2021-01-31) mkfs.fat: unable to open out/partitions/EFI: Permission denied # No logs project > cargo run ... project > mkfs.fat out/partitions mkfs.fat 4.2 (2021-01-31) mkfs.fat: unable to open out/partitions/EFI: Permission denied # Logs appear after switching to IDE
Answers
It sounds like you're encountering issues with permission when trying to format a block device on your FUSE file system. Here are a few things you can check and consider:
-
Ensure Correct Permissions: You mentioned that the permissions are set to 777, but double-check that the permissions on the block device file itself (
partitions/EFI
) allow the user running themkfs
command to access it. Also, ensure that the directory permissions ofout/partitions
allow the user to create and modify files within it. -
Check SELinux/AppArmor: If you're running SELinux or AppArmor, they might be restricting the access to the block device. Check the security policies and logs related to them to see if they're blocking any operations.
-
Filesystem Type Compatibility: Ensure that the filesystem type you're trying to format (
mkfs.fat
) is compatible with the block device you're using. Some filesystem types might require specific features or settings on the underlying device. -
Logging: Since you mentioned that logging functions are not immediately called but seem to be tied to some flushing operation, ensure that your logging mechanism is properly synchronized and flushing logs when necessary. It's possible that the logs are buffered and not immediately visible due to this behavior.
-
FUSE Mount Options: Experiment with different FUSE mount options. For example, try enabling special character and block devices (
MountOption::Dev
) and check if it affects the behavior. -
Test with Different Paths: Test formatting the block device using absolute paths (
/path/to/your/device
) instead of relative paths (out/partitions/EFI
). This can help rule out any issues related to the current working directory. -
Check Kernel Logs: Look into the kernel logs (
dmesg
or/var/log/messages
) for any relevant error messages or warnings that might shed light on the permission denied issue. -
Consider Alternative Approaches: If all else fails, consider alternative approaches such as using privileged helper programs or different filesystem types that don't require formatting via
mkfs
.
By systematically checking these points and experimenting with different configurations, you should be able to narrow down the cause of the permission denied error and find a resolution.