Message ID | 20220324120034.1570408-1-plautrba@redhat.com (mailing list archive) |
---|---|
State | Accepted |
Commit | c7a3b93e31df |
Headers | show |
Series | [v3] libsemanage: Fall back to semanage_copy_dir when rename() fails | expand |
On Thu, Mar 24, 2022 at 1:01 PM Petr Lautrbach <plautrba@redhat.com> wrote: > In some circumstances, like semanage-store being on overlayfs, rename() > could fail with EXDEV - Invalid cross-device link. This is due to the > fact that overlays doesn't support rename() if source and target are not > on the same layer, e.g. in containers built from several layers. Even > though it's not atomic operation, it's better to try to copy files from > src to dst on our own in this case. Next rebuild will probably not fail > as the new directories will be on the same layer. > > Fixes: https://github.com/SELinuxProject/selinux/issues/343 > > Reproducer: > > $ cd selinux1 > > $ cat Dockerfile > FROM fedora:35 > RUN dnf install -y selinux-policy selinux-policy-targeted > > $ podman build -t localhost/selinux . --no-cache > > $ cd ../selinux2 > > $ cat Dockerfile > FROM localhost/selinux > RUN semodule -B > > $ podman build -t localhost/selinux2 . --no-cache > STEP 2/2: RUN semodule -B > libsemanage.semanage_commit_sandbox: Error while renaming /var/lib/selinux/targeted/active to /var/lib/selinux/targeted/previous. (Invalid cross-device link). > semodule: Failed! > Error: error building at STEP "RUN semodule -B": error while running runtime: exit status 1 > > With the fix: > > $ podman build -t localhost/selinux2 . --no-cache > STEP 2/2: RUN semodule -B > libsemanage.semanage_rename: Warning: rename(/var/lib/selinux/targeted/active, /var/lib/selinux/targeted/previous) failed: Invalid cross-device link, fall back to non-atomic semanage_copy_dir_flags() > > COMMIT localhost/selinux2 > --> d2cfcebc1a1 > Successfully tagged localhost/selinux2:latest > d2cfcebc1a1b34f1c2cd661ac18292b0612c3e5fa71d6fa1441be244da91b1af > > Reported-by: Joseph Marrero Corchado <jmarrero@redhat.com> > Signed-off-by: Petr Lautrbach <plautrba@redhat.com> > --- > > v2 > - improve the commit message > - use WARN() instead of fprintf(stderr, > > v3 > - WARN without \n at the end > - split long line Acked-by: Ondrej Mosnacek <omosnace@redhat.com> Note that I didn't give the logic a thorough review, so I'd prefer someone else to give it a final look and merge it.
Ondrej Mosnacek <omosnace@redhat.com> writes: > On Thu, Mar 24, 2022 at 1:01 PM Petr Lautrbach <plautrba@redhat.com> wrote: >> In some circumstances, like semanage-store being on overlayfs, rename() >> could fail with EXDEV - Invalid cross-device link. This is due to the >> fact that overlays doesn't support rename() if source and target are not >> on the same layer, e.g. in containers built from several layers. Even >> though it's not atomic operation, it's better to try to copy files from >> src to dst on our own in this case. Next rebuild will probably not fail >> as the new directories will be on the same layer. >> >> Fixes: https://github.com/SELinuxProject/selinux/issues/343 >> >> Reproducer: >> >> $ cd selinux1 >> >> $ cat Dockerfile >> FROM fedora:35 >> RUN dnf install -y selinux-policy selinux-policy-targeted >> >> $ podman build -t localhost/selinux . --no-cache >> >> $ cd ../selinux2 >> >> $ cat Dockerfile >> FROM localhost/selinux >> RUN semodule -B >> >> $ podman build -t localhost/selinux2 . --no-cache >> STEP 2/2: RUN semodule -B >> libsemanage.semanage_commit_sandbox: Error while renaming /var/lib/selinux/targeted/active to /var/lib/selinux/targeted/previous. (Invalid cross-device link). >> semodule: Failed! >> Error: error building at STEP "RUN semodule -B": error while running runtime: exit status 1 >> >> With the fix: >> >> $ podman build -t localhost/selinux2 . --no-cache >> STEP 2/2: RUN semodule -B >> libsemanage.semanage_rename: Warning: rename(/var/lib/selinux/targeted/active, /var/lib/selinux/targeted/previous) failed: Invalid cross-device link, fall back to non-atomic semanage_copy_dir_flags() >> >> COMMIT localhost/selinux2 >> --> d2cfcebc1a1 >> Successfully tagged localhost/selinux2:latest >> d2cfcebc1a1b34f1c2cd661ac18292b0612c3e5fa71d6fa1441be244da91b1af >> >> Reported-by: Joseph Marrero Corchado <jmarrero@redhat.com> >> Signed-off-by: Petr Lautrbach <plautrba@redhat.com> >> --- >> >> v2 >> - improve the commit message >> - use WARN() instead of fprintf(stderr, >> >> v3 >> - WARN without \n at the end >> - split long line > > Acked-by: Ondrej Mosnacek <omosnace@redhat.com> > > Note that I didn't give the logic a thorough review, so I'd prefer > someone else to give it a final look and merge it. Merged. > -- > Ondrej Mosnacek > Software Engineer, Linux Security - SELinux kernel > Red Hat, Inc.
diff --git a/libsemanage/src/semanage_store.c b/libsemanage/src/semanage_store.c index 767f05cb2853..c6d2c5e72709 100644 --- a/libsemanage/src/semanage_store.c +++ b/libsemanage/src/semanage_store.c @@ -697,6 +697,10 @@ int semanage_store_access_check(void) /********************* other I/O functions *********************/ +static int semanage_rename(semanage_handle_t * sh, const char *tmp, const char *dst); +int semanage_remove_directory(const char *path); +static int semanage_copy_dir_flags(const char *src, const char *dst, int flag); + /* Callback used by scandir() to select files. */ static int semanage_filename_select(const struct dirent *d) { @@ -768,7 +772,21 @@ out: return retval; } -static int semanage_copy_dir_flags(const char *src, const char *dst, int flag); +static int semanage_rename(semanage_handle_t * sh, const char *src, const char *dst) { + int retval; + + retval = rename(src, dst); + if (retval == 0 || errno != EXDEV) + return retval; + + /* we can't use rename() due to filesystem limitation, lets try to copy files manually */ + WARN(sh, "WARNING: rename(%s, %s) failed: %s, fall back to non-atomic semanage_copy_dir_flags()", + src, dst, strerror(errno)); + if (semanage_copy_dir_flags(src, dst, 1) == -1) { + return -1; + } + return semanage_remove_directory(src); +} /* Copies all of the files from src to dst, recursing into * subdirectories. Returns 0 on success, -1 on error. */ @@ -1770,7 +1788,7 @@ static int semanage_commit_sandbox(semanage_handle_t * sh) goto cleanup; } - if (rename(active, backup) == -1) { + if (semanage_rename(sh, active, backup) == -1) { ERR(sh, "Error while renaming %s to %s.", active, backup); retval = -1; goto cleanup; @@ -1779,12 +1797,12 @@ static int semanage_commit_sandbox(semanage_handle_t * sh) /* clean up some files from the sandbox before install */ /* remove homedir_template from sandbox */ - if (rename(sandbox, active) == -1) { + if (semanage_rename(sh, sandbox, active) == -1) { ERR(sh, "Error while renaming %s to %s.", sandbox, active); /* note that if an error occurs during the next * function then the store will be left in an * inconsistent state */ - if (rename(backup, active) < 0) + if (semanage_rename(sh, backup, active) < 0) ERR(sh, "Error while renaming %s back to %s.", backup, active); retval = -1; @@ -1795,10 +1813,10 @@ static int semanage_commit_sandbox(semanage_handle_t * sh) * function then the store will be left in an * inconsistent state */ int errsv = errno; - if (rename(active, sandbox) < 0) + if (semanage_rename(sh, active, sandbox) < 0) ERR(sh, "Error while renaming %s back to %s.", active, sandbox); - else if (rename(backup, active) < 0) + else if (semanage_rename(sh, backup, active) < 0) ERR(sh, "Error while renaming %s back to %s.", backup, active); else
In some circumstances, like semanage-store being on overlayfs, rename() could fail with EXDEV - Invalid cross-device link. This is due to the fact that overlays doesn't support rename() if source and target are not on the same layer, e.g. in containers built from several layers. Even though it's not atomic operation, it's better to try to copy files from src to dst on our own in this case. Next rebuild will probably not fail as the new directories will be on the same layer. Fixes: https://github.com/SELinuxProject/selinux/issues/343 Reproducer: $ cd selinux1 $ cat Dockerfile FROM fedora:35 RUN dnf install -y selinux-policy selinux-policy-targeted $ podman build -t localhost/selinux . --no-cache $ cd ../selinux2 $ cat Dockerfile FROM localhost/selinux RUN semodule -B $ podman build -t localhost/selinux2 . --no-cache STEP 2/2: RUN semodule -B libsemanage.semanage_commit_sandbox: Error while renaming /var/lib/selinux/targeted/active to /var/lib/selinux/targeted/previous. (Invalid cross-device link). semodule: Failed! Error: error building at STEP "RUN semodule -B": error while running runtime: exit status 1 With the fix: $ podman build -t localhost/selinux2 . --no-cache STEP 2/2: RUN semodule -B libsemanage.semanage_rename: Warning: rename(/var/lib/selinux/targeted/active, /var/lib/selinux/targeted/previous) failed: Invalid cross-device link, fall back to non-atomic semanage_copy_dir_flags() COMMIT localhost/selinux2 --> d2cfcebc1a1 Successfully tagged localhost/selinux2:latest d2cfcebc1a1b34f1c2cd661ac18292b0612c3e5fa71d6fa1441be244da91b1af Reported-by: Joseph Marrero Corchado <jmarrero@redhat.com> Signed-off-by: Petr Lautrbach <plautrba@redhat.com> --- v2 - improve the commit message - use WARN() instead of fprintf(stderr, v3 - WARN without \n at the end - split long line libsemanage/src/semanage_store.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-)