On 02/03/2012 03:41 PM, Eric Blake wrote:
On 02/01/2012 11:36 PM, Laine Stump wrote:
> virFileOpenAs previously would only try opening a file as the current
> user, or as a different user, but wouldn't try both methods in a
> single call. This made it cumbersome to use as a replacement for
> open(2). Additionally, it had a lot of historical baggage that led to
> it being difficult to understand.
>
> This patch refactors virFileOpenAs in the following ways:
> All current consumers of virFileOpenAs should retain their present
> behavior (after a few minor changes to their setup code and
> arguments).
Yep, sure looks like it.
> ---
> src/libxl/libxl_driver.c | 4 +-
> src/qemu/qemu_driver.c | 8 +-
> src/storage/storage_backend.c | 12 +-
> src/util/util.c | 352 +++++++++++++++++++++++++++--------------
> src/util/util.h | 6 +-
> 5 files changed, 246 insertions(+), 136 deletions(-)
I think we've hit a winner for refactoring it into something that can be
further modified while investigating those modifications, making it more
powerful for new uses, and without breaking behavior in the refactor.
> +/* virFileOpenForceOwnerMode() - an internal utility function called
> + * only by virFileOpenAs(). Sets the owner and mode of the file
> + * opened as "fd" if it's not correct AND the flags say it should be
> + * forced. */
> static int
> -virFileOpenAsNoFork(const char *path, int openflags, mode_t mode,
> - uid_t uid, gid_t gid, unsigned int flags)
> +virFileOpenForceOwnerMode(const char *path, int fd, mode_t mode,
> + uid_t uid, gid_t gid, unsigned int flags)
> {
> - if ((flags& VIR_DIR_CREATE_FORCE_PERMS)
> -&& (chmod(path, mode)< 0)) {
> + if ((flags& VIR_FILE_OPEN_FORCE_MODE)&&
> + ((mode& (S_IRWXU|S_IRWXG|S_IRWXO)) !=
> + (st.st_mode& (S_IRWXU|S_IRWXG|S_IRWXO)))&&
Technically, this limits us to a mask of 00777,
> + (fchmod(fd, mode)< 0)) {
> ret = -errno;
> virReportSystemError(errno,
> _("cannot set mode of '%s' to
%04o"),
while this error message implied that we could request a mode with mask
07777. But since none of the callers are passing sticky bits, I think
we can adjust that later if we ever find a reason that we need bits from
07000 modified, and leave well enough alone in this patch.
> +/* virFileOpenForked() - an internal utility function called only by
> + * virFileOpenAs(). It forks, then the child does setuid+setgid to
> + * given uid:gid and attempts to open the file, while the parent just
> + * calls recvfd to get the open fd back from the child. returns the
> + * fd, or -errno if there is an error. */
> +static int
> +virFileOpenForked(const char *path, int openflags, mode_t mode,
> + uid_t uid, gid_t gid, unsigned int flags)
> {
> pid_t pid;
> forkRet = virFork(&pid);
> -
> - if (pid< 0) {
> - ret = -errno;
> - return ret;
> - }
> + if (pid< 0)
> + return -errno;
>
> if (pid) { /* parent */
You know, the diff for this patch is so crazy already that we might as
well make maintenance slightly easier. That is, right now, we have:
fork
if fail
return error
if parent {
wait for child
do stuff, which has several return calls
return
}
child
do stuff, which might go to childerror
childerr:
_exit
But sequentially, since the parent is waiting on the child, it might be
easier to read as:
fork
if child {
do stuff, which might go to childerror
childerror:
_exit
}
parent
if fork failed, go to cleanup
wait for child
do more stuff, which might go to cleanup
cleanup:
return
In other words, if you want to rearrange this section of code, now might
be a good time to do it, and I wouldn't even mind if you squashed it in
to this one rather than using a separate patch. But that's all
cosmetic, it wouldn't change the code you actually have.
> if (!WIFEXITED(status) || (ret = -WEXITSTATUS(status)) == -EACCES ||
> fd == -1) {
> /* fall back to the simpler method, which works better in
> * some cases */
> VIR_FORCE_CLOSE(fd);
> - flags&= ~VIR_FILE_OPEN_AS_UID;
> - return virFileOpenAsNoFork(path, openflags, mode, uid, gid, flags);
> + 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;
Fair enough. I don't think the slight loss in information will hurt;
the end result is still a reasonable failure message.
ACK.
reordered and pushed. Thanks!