On 10/21/2011 07:12 AM, Michal Privoznik wrote:
This function checks if a given path is accessible under
given uid and gid.
---
src/util/util.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/util/util.h | 3 ++
2 files changed, 86 insertions(+), 0 deletions(-)
diff --git a/src/util/util.c b/src/util/util.c
index af27344..7343485 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -671,6 +671,77 @@ virFileIsExecutable(const char *file)
}
#ifndef WIN32
+/* Check that a file is accessible under certain
+ * user& gid.
+ * @mode can be F_OK, or a bitwise combination of R_OK, W_OK, and X_OK.
+ * see 'man access' for more details.
+ * Returns 0 on success, -errno on fail,
+ *>0 on signaled child.
That's awkward. How about just stating:
Returns 0 on success, -1 on fail with errno set.
+ */
+int
+virFileAccessibleAs(const char *path, int mode,
+ uid_t uid, gid_t gid)
+{
+ pid_t pid = 0;
+ int waitret, status, ret = 0;
+ int forkRet = 0;
+ bool do_fork = (uid != getuid() || gid != getgid());
+
+ if (do_fork)
+ forkRet = virFork(&pid);
The control flow is a bit hard to follow. While it looks correct, I
think it would be easier to read as follows (it also matches my proposal
to return -1 with errno set, when encountering error):
if (uid == getuid() && gid == getgid())
return access(path, mode);
forkRet = virFork(&pid);
... code without reference to do_fork ...
+ if (pid) { /* parent */
+ do {
+ if ((waitret = waitpid(pid,&status, 0))< 0) {
+ ret = -errno;
+ virKillProcess(pid, SIGKILL);
+ return ret;
+ }
+ } while (!WIFEXITED(status)&& !WIFSIGNALED(status));
Use virPidWait() (from command.h), not this hand-rolled do/while loop.
In particular, your loop will only ever execute exactly once, since
status will always satisfy either WIFEXITED or WIFSIGNALED if waitpid()
was successful; and you fail to handle EINTR as a reason to retry waitpid.
Returning a positive value for a signaled child is awkward; I would just
treat that as failure, by setting errno to EIO and returning -1. The
likelihood of the child dying by signal is super-slim, not to mention
that I don't see how the client can behave any better by knowing that
the child process died than if it just reacted as if access were denied
in the first place.
@@ -993,6 +1064,18 @@ childerror:
#else /* WIN32 */
+int
+virFileAccessibleAs(const char *path ATTRIBUTE_UNUSED,
+ int mode ATTRIBUTE_UNUSED,
+ uid_t uid ATTRIBUTE_UNUSED,
+ git_t gid ATTRIBUTE_UNUSED)
+{
+ virUtilError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("virFileAccessibleAs is not implemented for
WIN32"));
+
+ return -ENOSYS;
Rather than failing with -ENOSYS, can we at least try access(path,
mode), ignoring uid and gid? It may have false negatives (fail in cases
where a different uid would succeed), but is more likely to be useful
than just blindly declaring failure.
--
Eric Blake eblake(a)redhat.com +1-801-349-2682
Libvirt virtualization library
http://libvirt.org