mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-03-07 17:28:15 +00:00
virfile: Adjust error path for virFileOpenForked
Rather than have a dummy waitpid loop and return of the failure status from recvfd, adjust the logic to save the recvfd error & fd and then in priority order: - if waitpid failed, use that errno value - waitpid succeeded, but if the child exited abnormally, report failure (use EACCES to report as return failure, since either EACCES or EPERM is what caused us to fall into the fork+setuid path) - waitpid succeeded, but if the child reported non-zero status, report failure (use the errno value that the child encoded into exit status) - waitpid succeeded, but if recvfd failed, report recvfd_errno - waitpid and recvfd succeeded, use the fd NOTE: Original logic to retry the open and force owner mode was "documented" as only being attempted if we had already tried opening with the fork+setuid, but checked flags vs. VIR_FILE_OPEN_NOFORK which is counter to how we would get to that point. So that code was removed.
This commit is contained in:
parent
45853b5289
commit
92d9114eac
@ -2034,6 +2034,7 @@ virFileOpenForked(const char *path, int openflags, mode_t mode,
|
|||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int waitret, status, ret = 0;
|
int waitret, status, ret = 0;
|
||||||
|
int recvfd_errno = 0;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
int pair[2] = { -1, -1 };
|
int pair[2] = { -1, -1 };
|
||||||
gid_t *groups;
|
gid_t *groups;
|
||||||
@ -2124,15 +2125,12 @@ virFileOpenForked(const char *path, int openflags, mode_t mode,
|
|||||||
fd = recvfd(pair[0], 0);
|
fd = recvfd(pair[0], 0);
|
||||||
} while (fd < 0 && errno == EINTR);
|
} while (fd < 0 && errno == EINTR);
|
||||||
VIR_FORCE_CLOSE(pair[0]); /* NB: this preserves errno */
|
VIR_FORCE_CLOSE(pair[0]); /* NB: this preserves errno */
|
||||||
|
if (fd < 0)
|
||||||
|
recvfd_errno = errno;
|
||||||
|
|
||||||
/* gnulib will return ENOTCONN in certain instances */
|
/* wait for child to complete, and retrieve its exit code
|
||||||
if (fd < 0 && !(errno == EACCES || errno == ENOTCONN)) {
|
* if waitpid fails, use that status
|
||||||
ret = -errno;
|
*/
|
||||||
while (waitpid(pid, NULL, 0) == -1 && errno == EINTR);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* wait for child to complete, and retrieve its exit code */
|
|
||||||
while ((waitret = waitpid(pid, &status, 0)) == -1 && errno == EINTR);
|
while ((waitret = waitpid(pid, &status, 0)) == -1 && errno == EINTR);
|
||||||
if (waitret == -1) {
|
if (waitret == -1) {
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
@ -2142,28 +2140,41 @@ virFileOpenForked(const char *path, int openflags, mode_t mode,
|
|||||||
VIR_FORCE_CLOSE(fd);
|
VIR_FORCE_CLOSE(fd);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (!WIFEXITED(status) || (ret = -WEXITSTATUS(status)) == -EACCES ||
|
|
||||||
fd == -1) {
|
/*
|
||||||
/* fall back to the simpler method, which works better in
|
* If waitpid succeeded, but if the child exited abnormally or
|
||||||
* some cases */
|
* reported non-zero status, report failure.
|
||||||
VIR_FORCE_CLOSE(fd);
|
|
||||||
if (flags & VIR_FILE_OPEN_NOFORK) {
|
|
||||||
/* If we had already tried opening w/o fork+setuid and
|
|
||||||
* failed, no sense trying again. Just set return the
|
|
||||||
* original errno that we got at that time (by
|
|
||||||
* definition, always either EACCES or EPERM - EACCES
|
|
||||||
* is close enough).
|
|
||||||
*/
|
*/
|
||||||
return -EACCES;
|
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||||
}
|
char *msg = virProcessTranslateStatus(status);
|
||||||
if ((fd = open(path, openflags, mode)) < 0)
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||||
return -errno;
|
_("child failed to create '%s': %s"),
|
||||||
ret = virFileOpenForceOwnerMode(path, fd, mode, uid, gid, flags);
|
path, msg);
|
||||||
if (ret < 0) {
|
|
||||||
VIR_FORCE_CLOSE(fd);
|
VIR_FORCE_CLOSE(fd);
|
||||||
|
VIR_FREE(msg);
|
||||||
|
/* Use child exit status if possible; otherwise,
|
||||||
|
* just use -EACCES, since by our original failure in
|
||||||
|
* the non fork+setuid path would have been EACCES or
|
||||||
|
* EPERM by definition (see qemuOpenFileAs after the
|
||||||
|
* first virFileOpenAs failure), but EACCES is close enough.
|
||||||
|
* Besides -EPERM is like returning fd == -1.
|
||||||
|
*/
|
||||||
|
if (WIFEXITED(status))
|
||||||
|
ret = -WEXITSTATUS(status);
|
||||||
|
else
|
||||||
|
ret = -EACCES;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if waitpid succeeded, but recvfd failed, report recvfd_errno */
|
||||||
|
if (recvfd_errno != 0) {
|
||||||
|
virReportSystemError(recvfd_errno,
|
||||||
|
_("failed recvfd for child creating '%s'"),
|
||||||
|
path);
|
||||||
|
return -recvfd_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* otherwise, waitpid and recvfd succeeded, return the fd */
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user