+
+static
+int run_as(int (*cmd)(const char *path, mode_t mode, uid_t uid, gid_t gid),
+ const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+ int ret = 0;
+ pid_t pid;
+
+ /*
+ * If we are non-root, we can only deal with our own uid.
+ */
+ if (geteuid() != 0) {
+ if (uid != geteuid()) {
+ ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
+ uid, geteuid());
+ return -EPERM;
+ }
+ return (*cmd)(path, mode, uid, gid);
+ }
+
+ pid = fork();
+ if (pid > 0) {
+ int status;
+
+ /*
+ * Parent: wait for child to return, in which case the
+ * shared memory map will have been created.
+ */
+ pid = wait(&status);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ ret = -1;
+ goto end;
+ }
+ goto end;
+ } else if (pid == 0) {
+ /* Child */
+ setegid(gid);
+ if (ret < 0) {
+ perror("setegid");
+ exit(EXIT_FAILURE);
+ }
+ ret = seteuid(uid);
+ if (ret < 0) {
+ perror("seteuid");
+ exit(EXIT_FAILURE);
+ }
+ ret = (*cmd)(path, mode, uid, gid);
+ if (!ret)
+ exit(EXIT_SUCCESS);
+ else
+ exit(EXIT_FAILURE);
+ } else {
+ return -1;
+ }
+end:
+ return ret;
+}
+
+int mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+ DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
+ path, mode, uid, gid);
+ return run_as(_mkdir_recursive, path, mode, uid, gid);
+}