summaryrefslogtreecommitdiffstats
path: root/pyanaconda/isys/imount.c
diff options
context:
space:
mode:
Diffstat (limited to 'pyanaconda/isys/imount.c')
-rw-r--r--pyanaconda/isys/imount.c328
1 files changed, 328 insertions, 0 deletions
diff --git a/pyanaconda/isys/imount.c b/pyanaconda/isys/imount.c
new file mode 100644
index 000000000..ed0f5a762
--- /dev/null
+++ b/pyanaconda/isys/imount.c
@@ -0,0 +1,328 @@
+/*
+ * imount.c
+ *
+ * Copyright (C) 2007, 2008, 2009 Red Hat, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "imount.h"
+#include "log.h"
+
+#define _(foo) foo
+
+static int mkdirIfNone(char * directory);
+
+static int readFD(int fd, char **buf) {
+ char *p;
+ size_t size = 4096;
+ int s, filesize = 0;
+
+ *buf = calloc(4096, sizeof (char));
+ if (*buf == NULL)
+ abort();
+
+ do {
+ p = &(*buf)[filesize];
+ s = read(fd, p, 4096);
+ if (s < 0)
+ break;
+
+ filesize += s;
+ if (s == 0)
+ break;
+
+ size += s;
+ *buf = realloc(*buf, size);
+ if (*buf == NULL)
+ abort();
+ } while (1);
+
+ if (filesize == 0 && s < 0) {
+ free(*buf);
+ *buf = NULL;
+ return -1;
+ }
+
+ return filesize;
+}
+
+static size_t rstrip(char *str) {
+ size_t len = strlen(str);
+ if (len > 0 && str[len-1] == '\n') {
+ str[len-1] = '\0';
+ --len;
+ }
+ return len;
+}
+
+int mountCommandWrapper(int mode, char *dev, char *where, char *fs,
+ char *options, char **err) {
+ int rc, child, status;
+ int stdout_pipe[2], stderr_pipe[2];
+ char *opts = NULL, *device = NULL, *cmd = NULL;
+
+ switch (mode) {
+ case IMOUNT_MODE_MOUNT:
+ case IMOUNT_MODE_BIND:
+ cmd = "/bin/mount";
+ if (mkdirChain(where))
+ return IMOUNT_ERR_ERRNO;
+ break;
+ case IMOUNT_MODE_UMOUNT:
+ cmd = "/bin/umount";
+ break;
+ default:
+ return IMOUNT_ERR_MODE;
+ }
+
+ if (mode == IMOUNT_MODE_MOUNT) {
+ if (strstr(fs, "nfs")) {
+ if (options) {
+ if (asprintf(&opts, "%s,nolock", options) == -1) {
+ fprintf(stderr, "%s: %d: %s\n", __func__, __LINE__,
+ strerror(errno));
+ fflush(stderr);
+ abort();
+ }
+ } else {
+ opts = strdup("nolock");
+ }
+
+ device = strdup(dev);
+ } else {
+ if ((options && strstr(options, "bind") == NULL) &&
+ strncmp(dev, "LABEL=", 6) && strncmp(dev, "UUID=", 5) &&
+ *dev != '/') {
+ if (asprintf(&device, "/dev/%s", dev) == -1) {
+ fprintf(stderr, "%s: %d: %s\n", __func__, __LINE__,
+ strerror(errno));
+ fflush(stderr);
+ abort();
+ }
+ } else {
+ device = strdup(dev);
+ }
+
+ if (options)
+ opts = strdup(options);
+ }
+ }
+
+ if (pipe(stdout_pipe))
+ return IMOUNT_ERR_ERRNO;
+ if (pipe(stderr_pipe))
+ return IMOUNT_ERR_ERRNO;
+
+ if (!(child = fork())) {
+ int tty_fd;
+
+ close(stdout_pipe[0]);
+ close(stderr_pipe[0]);
+
+ /* Pull stdin from /dev/tty5 and redirect stdout and stderr to the pipes
+ * so we can log the output and put error messages into exceptions.
+ * We'll only use these messages should mount also return an error
+ * code.
+ */
+ tty_fd = open("/dev/tty5", O_RDONLY);
+ close(STDIN_FILENO);
+ dup2(tty_fd, STDIN_FILENO);
+ close(tty_fd);
+
+ close(STDOUT_FILENO);
+ dup2(stdout_pipe[1], STDOUT_FILENO);
+ close(STDERR_FILENO);
+ dup2(stderr_pipe[1], STDERR_FILENO);
+
+ if (mode == IMOUNT_MODE_MOUNT) {
+ if (opts) {
+ logProgramMessage(INFO, "Running... %s -n -t %s -o %s %s %s",
+ cmd, fs, opts, device, where);
+ rc = execl(cmd, cmd,
+ "-n", "-t", fs, "-o", opts, device, where, NULL);
+ exit(1);
+ } else {
+ logProgramMessage(INFO, "Running... %s -n -t %s %s %s",
+ cmd, fs, device, where);
+ rc = execl(cmd, cmd, "-n", "-t", fs, device, where, NULL);
+ exit(1);
+ }
+ } else if (mode == IMOUNT_MODE_BIND) {
+ logProgramMessage(INFO, "Running... %s --bind %s %s",
+ cmd, dev, where);
+ rc = execl(cmd, cmd, "--bind", dev, where, NULL);
+ exit(1);
+ } else if (mode == IMOUNT_MODE_UMOUNT) {
+ logProgramMessage(INFO, "Running... %s %s", cmd, where);
+ rc = execl(cmd, cmd, where, NULL);
+ exit(1);
+ } else {
+ logProgramMessage(ERROR, "Running... Unknown imount mode: %d\n", mode);
+ exit(1);
+ }
+ }
+
+ close(stdout_pipe[1]);
+ close(stderr_pipe[1]);
+
+ char *buffer = NULL;
+ /* In case when when the stderr pipe gets enough data to fill the kernel
+ * buffer we might see a deadlock as this will block the mount program on
+ * its next write(). The buffer size is 65kB though so we should be safe.
+ */
+ rc = readFD(stdout_pipe[0], &buffer);
+ if (rc > 0) {
+ rstrip(buffer);
+ logProgramMessage(INFO, buffer);
+ free(buffer);
+ buffer = NULL;
+ }
+ rc = readFD(stderr_pipe[0], &buffer);
+ if (rc > 0) {
+ rstrip(buffer);
+ logProgramMessage(ERROR, buffer);
+ if (err != NULL)
+ *err = buffer;
+ else
+ free(buffer);
+ }
+ close(stdout_pipe[0]);
+ close(stderr_pipe[0]);
+
+ waitpid(child, &status, 0);
+
+ if (opts) {
+ free(opts);
+ }
+
+ if (device) {
+ free(device);
+ }
+
+ if (!WIFEXITED(status))
+ return IMOUNT_ERR_OTHER;
+ else if ( (rc = WEXITSTATUS(status)) ) {
+ /* Refer to 'man mount' for the meaning of the error codes. */
+ switch (rc) {
+ case 1:
+ return IMOUNT_ERR_PERMISSIONS;
+ case 2:
+ return IMOUNT_ERR_SYSTEM;
+ case 4:
+ return IMOUNT_ERR_MOUNTINTERNAL;
+ case 8:
+ return IMOUNT_ERR_USERINTERRUPT;
+ case 16:
+ return IMOUNT_ERR_MTAB;
+ case 32:
+ return IMOUNT_ERR_MOUNTFAILURE;
+ case 64:
+ return IMOUNT_ERR_PARTIALSUCC;
+ default:
+ return IMOUNT_ERR_OTHER;
+ }
+ }
+
+ return 0;
+}
+
+int doBindMount(char* path, char *where, char **err) {
+ return mountCommandWrapper(IMOUNT_MODE_BIND,
+ path, where, NULL, NULL, err);
+}
+
+int doPwMount(char *dev, char *where, char *fs, char *options, char **err) {
+ return mountCommandWrapper(IMOUNT_MODE_MOUNT,
+ dev, where, fs, options, err);
+}
+
+int doPwUmount(char *where, char **err) {
+ return mountCommandWrapper(IMOUNT_MODE_UMOUNT,
+ NULL, where, NULL, NULL, err);
+}
+
+int mkdirChain(char * origChain) {
+ char * chain;
+ char * chptr;
+
+ chain = alloca(strlen(origChain) + 1);
+ strcpy(chain, origChain);
+ chptr = chain;
+
+ while ((chptr = strchr(chptr, '/'))) {
+ *chptr = '\0';
+ if (mkdirIfNone(chain)) {
+ *chptr = '/';
+ return IMOUNT_ERR_ERRNO;
+ }
+
+ *chptr = '/';
+ chptr++;
+ }
+
+ if (mkdirIfNone(chain))
+ return IMOUNT_ERR_ERRNO;
+
+ return 0;
+}
+
+/* Returns true iff it is possible that the mount command that have returned
+ * 'errno' might succeed at a later time (think e.g. not yet initialized USB
+ * device, etc.) */
+int mountMightSucceedLater(int mountRc)
+{
+ int rc;
+ switch (mountRc) {
+ case IMOUNT_ERR_MOUNTFAILURE:
+ rc = 1;
+ break;
+ default:
+ rc = 0;
+ }
+ return rc;
+}
+
+static int mkdirIfNone(char * directory) {
+ int rc, mkerr;
+ char * chptr;
+
+ /* If the file exists it *better* be a directory -- I'm not going to
+ actually check or anything */
+ if (!access(directory, X_OK)) return 0;
+
+ /* if the path is '/' we get ENOFILE not found" from mkdir, rather
+ then EEXIST which is weird */
+ for (chptr = directory; *chptr; chptr++)
+ if (*chptr != '/') break;
+ if (!*chptr) return 0;
+
+ rc = mkdir(directory, 0755);
+ mkerr = errno;
+
+ if (!rc || mkerr == EEXIST) return 0;
+
+ return IMOUNT_ERR_ERRNO;
+}