Skip to content

Commit

Permalink
rofiles-fuse: Support breaking hardlinks to fs-verity files
Browse files Browse the repository at this point in the history
In case fs-verity is in used for the repo objects, and something like
"rpm-ostree apply-live" uses rofiles-fuse with --copyup, then writing
to a hard-linked file fails to copy up, like this:

echo foo > /a/rofile-mnt/a-file
/a/rofile-mnt/a-file: Operation not permitted

The reason for this is that do_write() starts by opening the file
non-truncating for writing, stat:ing it and then calling
verify_write_or_copyup(). It is expecting the the open(write) to
succeed, however, in the fs-verity case any open with write fails with
EPERM.

We fix this by delaying the EPERM failure, only reporting it when the
file descriptor needs to be used. In the case this triggered a copyup
the file descriptor will be reopened, and in this case we will not get
the EPERM anymore.

To simplify this code the fd variable now uses glnx_autofd.

This fixes coreos/rpm-ostree#4827
  • Loading branch information
alexlarsson committed Feb 14, 2024
1 parent 751ec90 commit b728531
Showing 1 changed file with 24 additions and 18 deletions.
42 changes: 24 additions & 18 deletions src/rofiles-fuse/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ callback_utimens (const char *path, const struct timespec tv[2])
static int
do_open (const char *path, mode_t mode, struct fuse_file_info *finfo)
{
int fd;
glnx_autofd int fd = -1;
struct stat stbuf;

path = ENSURE_RELPATH (path);
Expand All @@ -416,29 +416,35 @@ do_open (const char *path, mode_t mode, struct fuse_file_info *finfo)
{
/* Write */

/* We need to specially handle O_TRUNC */
/* We need to specially handle O_TRUNC.
* Also, to files using fs-verity we special case EPERM (which is
* reported when opening such files for writing), and delay reporting
* the error until after trying to copy-up.
*/
int delayed_errno = 0;
fd = openat (basefd, path, finfo->flags & ~O_TRUNC, mode);
if (fd == -1)
return -errno;

if (fstat (fd, &stbuf) == -1)
{
(void)close (fd);
return -errno;
if (errno == EPERM)
delayed_errno = errno;
else
return -errno;
}

if (fd != -1 && fstat (fd, &stbuf) == -1)
return -errno;

gboolean did_copyup;
int r = verify_write_or_copyup (path, &stbuf, &did_copyup);
int r = verify_write_or_copyup (path, fd != -1 ? &stbuf : NULL, &did_copyup);
if (r != 0)
{
(void)close (fd);
return r;
}
return r;

/* In the copyup case, we need to re-open */
if (did_copyup)
{
(void)close (fd);
if (fd != -1)
(void)close (fd);

/* Note that unlike the initial open, we will pass through
* O_TRUNC. More ideally in this copyup case we'd avoid copying
* the whole file in the first place, but eh. It's not like we're
Expand All @@ -450,21 +456,21 @@ do_open (const char *path, mode_t mode, struct fuse_file_info *finfo)
}
else
{
if (delayed_errno)
return -delayed_errno;

/* In the non-copyup case we handle O_TRUNC here, after we've verified
* the hardlink state above with verify_write_or_copyup().
*/
if (finfo->flags & O_TRUNC)
{
if (ftruncate (fd, 0) == -1)
{
(void)close (fd);
return -errno;
}
return -errno;
}
}
}

finfo->fh = fd;
finfo->fh = g_steal_fd (&fd);

return 0;
}
Expand Down

0 comments on commit b728531

Please sign in to comment.