
Thanks for the quick response! On 02/23/2010 03:32 PM, Eric Blake wrote:
According to Laine Stump on 2/23/2010 12:54 PM:
Minor nits (I have not validated the overall patch in context; this is just from a quick high-level review)
If qemudDomainRestore fails to open the domain save file, create a pipe, then fork a process that does setuid(qemu_user) and opens the file, then reads this file and stuffs it into the pipe. the parnet
s/. the parnet/. The parent/
libvirtd process will use the other end of the pipe as its fd, then reap the child process after its done reading.
s/its/it's/
Ah, yes. Those terrible typos that never seem worth the effort to fix after they're already checked in. Thanks for nipping it in the bud!
This makes domain restore work on a root-squash NFS share that is only visible to the qemu user. --- src/qemu/qemu_driver.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 155 insertions(+), 3 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ac6ef6a..f909a59 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4659,6 +4659,135 @@ cleanup: return ret; }
+/* qemudOpenAsUID() - pipe/fork/setuid/open a file, and return the + pipe fd to caller, so that it can read from the file. Also return + the pid of the child process, so the caller can wait for it to exit + after it's finished reading (to avoid a zombie, if nothing + else). */ + +static int qemudOpenAsUID(const char *path, uid_t uid, pid_t *childpid) { + int pipefd[2] = {-1, -1};
Why the explicit initialization...
Mostly because I was following the example of util.c:__virExec(), which does the same thing. Following examples seems to get me into nearly as much trouble as ignoring them ;-)
+ int fd = -1; + + *childpid = -1; + + if (pipe(pipefd)< 0) {
...since I see nothing in POSIX that guarantees that pipe() will preserve your initialized values across failure. I think it's better to explicitly set things to -1 on the failure path, rather than initializing that way.
Interesting point. If we can't rely on that, then __virExec() should be changed too. Of course this makes maintenance a bit more error prone, since we would have to make sure that *every* jump to the cleanup that happened prior to creating the pipe would set the pipe fds to -1 (or just leave the initialization in, and re-set them to -1 when pipe failed; that's probably the safest thing to do).
+ virReportSystemError(errno, + _("failed to create pipe to read '%s'"), + path); + goto parentcleanup; + } + + int forkRet = virFork(childpid);
Unrelated to your patch, but we should probably start using pipe2 and atomically marking pipes as CLOEXEC, so that use of libvirt in a multithreaded environment does not have a race window where unrelated threads can leak libvirt's fds across an exec (well, for new enough kernels anyway; at least gnulib has a pipe2 fallback for older kernels).
...
I think I just heard someone volunteering! ;-)
+ /* child */ + + /* setuid to the qemu user, then open the file, read it, + and stuff it into the pipe for the parent process to + read */ + int exit_code; + char *buf = NULL; + int bufsize = 1024 * 1024; + int bytesread; + + /* child doesn't need the read side of the pipe */ + close(pipefd[0]); + + if (forkRet< 0) { + exit_code = errno; + virReportSystemError(errno, + _("failed in child after forking to read '%s'"), + path); + goto childcleanup; + } + + if (setuid(uid) != 0) { + exit_code = errno; + virReportSystemError(errno, + _("cannot setuid(%d) to read '%s'"), + getuid(), path);
Is that the right uid to be reporting in the error message?
What's the proper emoticon for blushing in embarrassment? Bonehead typo ;-) Thanks for pointing it out.