1

I'm using QEMU to test Raspberry Pi before putting the image onto an SD card. I'm setting up an automated script to put some files onto the Pi, among other things, so that when I put the SD card into the Pi, it is immediately usable. I think I've run into a quirk in how permissions work, but I'm not sure.

When you run test -x, the file is supposed to be executable. Basically, the x bit is supposed to be on for your user. However, this doesn't seem to apply to files inside mounted filesystems.

The host is Ubuntu, and the guest backing image is Raspberry Pi Buster. I created the mountpoint with guestmount, because I was mounting a snapshot, not the original, and this seems to be the only/best way to do that. The basic flow was:

qemu-img convert -Oqcow2 raspberry-pi.img raspberry-pi.qcow
qemu-img create -f qcow2 snapshot.qcow -b raspberry-pi.qcow
sudo guestmount -a 'snapshot.qcow' -i 'mountpoint/'

For example, I have a file outside the repository. The file I'm testing inside the mountpoint was created by root, so I chmoded this file to root for comparison:

$ sudo ls -l --author ~/test/file
-rw-r--r-- 1 root root root 1133 Oct  8 21:43 /home/me/test/file
$ sudo test -x ~/test/file && echo 'exists' || echo 'doesn\'t exist'
doesn't exist

However, for a file inside the mountpoint, with the same permissions, the test is successful:

$ sudo ls -l --author mountpoint/home/pi/test/file
-rw-r--r-- 1 root root root 0 Oct  8 22:41 mountpoint/home/pi/test/file
$ sudo test -x ~/test/file && echo 'exists' || echo 'doesn\'t exist'
exists

Why is the file inside the mountpoint executable, whereas the one outside is not executable? Is this because the mounted filesystem is a different architecture (x86 vs. ARM)? Is it because I'm using guestmount, and the filesystem isn't the real filesysem, but an amalgamation of the snapshot & the original file? Or is this just the way mounting works? Where can I find more resources on this peculiar behavior, like other permission quirks I might encounter?

If you need any more information about the host or guest, please ask.

trysis
  • 8,086
  • 17
  • 51
  • 80

1 Answers1

2

This is a bug in libguestfs used by guestmount. You can see it here:

  /* Root user should be able to access everything, so only bother
   * with these fine-grained tests for non-root.  (RHBZ#1106548).
   */
  if (fuse->uid != 0) {
    [...]
    if (mask & X_OK)
      ok = ok &&
        (  fuse->uid == statbuf.st_uid ? statbuf.st_mode & S_IXUSR
           : fuse->gid == statbuf.st_gid ? statbuf.st_mode & S_IXGRP
           : statbuf.st_mode & S_IXOTH);
  }

The FS takes a shortcut saying that since you're root you have full access, so there's no point checking the permissions.

As you've demonstrated, this is not true. Root should only have execute permissions for directories, and for files where at least one of the execute bits is set.

I was unable to build the project to submit a patch, but you can file a bug.

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • I submitted [an issue to the GitHub repo](https://github.com/libguestfs/libguestfs/issues/54), we'll see how that goes. – trysis Oct 09 '20 at 16:41
  • This was the upstream bug that caused the workaround to be added: https://bugzilla.redhat.com/show_bug.cgi?id=1106548 I guess setting x permission should always be possible. (In the general case it doesn't make much sense to look at permissions, since if you can write over the disk image then the permissions inside are meaningless) – Rich Oct 09 '20 at 19:58
  • True, plus even for non-root you can `chmod` & rewrite the file, or any of a million other methods, some with just read. I still think `guestmount` should be consistent with the outside world, though. – trysis Oct 09 '20 at 23:26
  • Thanks for both your analyses which I agree with. I have pushed the following fix upstream: https://github.com/libguestfs/libguestfs/commit/3f4a529ab753bea43a9cef7f8aaed48a2be23e30 – Rich Oct 12 '20 at 14:23
  • @Rich This fix does not result in canonical behavior. A. The call should return true for root if *any* of the execute bits are set, regardless of owner (i.e. modes 100, 010, and 001 should all result in root having execute permissions). B. The call should return true if the entry is a directory, regardless of the entry's mode. See the [Linux kernel implementation](https://github.com/torvalds/linux/blob/bbf5c979011a099af5dc76498918ed7df445635b/fs/namei.c#L346,L385) (instead of hard coding UID 0, it uses CAP_DAC_OVERRIDE). – that other guy Oct 12 '20 at 16:55