From 80713e3f73e48856221c667f32b94b0a023ebecc Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Mon, 25 Aug 2008 17:13:37 -1000 Subject: Renamed loader2 subdirectory to loader (hooray for git) --- loader/method.c | 540 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 540 insertions(+) create mode 100644 loader/method.c (limited to 'loader/method.c') diff --git a/loader/method.c b/loader/method.c new file mode 100644 index 000000000..26dc2c3fa --- /dev/null +++ b/loader/method.c @@ -0,0 +1,540 @@ +/* + * method.c - generic install method setup functions + * + * Copyright (C) 2002 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 . + * + * Author(s): Erik Troan + * Matt Wilson + * Michael Fulbright + * Jeremy Katz + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "copy.h" +#include "loader.h" +#include "loadermisc.h" +#include "log.h" +#include "lang.h" +#include "mediacheck.h" +#include "method.h" + +#include "../isys/imount.h" +#include "../isys/isys.h" +#include "../isys/cpio.h" + +#include "devt.h" + +#include "nfsinstall.h" +#include "hdinstall.h" +#include "urlinstall.h" + +/* boot flags */ +extern uint64_t flags; + +int umountLoopback(char * mntpoint, char * device) { + int loopfd; + + umount(mntpoint); + + logMessage(INFO, "umounting loopback %s %s", mntpoint, device); + + loopfd = open(device, O_RDONLY); + + if (ioctl(loopfd, LOOP_CLR_FD, 0) == -1) + logMessage(ERROR, "LOOP_CLR_FD failed for %s %s: %m", mntpoint, device); + + close(loopfd); + + return 0; +} + +int mountLoopback(char *fsystem, char *mntpoint, char *device) { + char *opts; + + if (device == NULL) { + logMessage(ERROR, "no loopback device given"); + return LOADER_ERROR; + } + + if (access(fsystem, F_OK) != 0) { + logMessage(ERROR, "file %s is not accessible", fsystem); + return LOADER_ERROR; + } + + if (asprintf(&opts, "ro,loop=%s", device) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (doPwMount(fsystem, mntpoint, "iso9660", opts, NULL)) { + if (doPwMount(fsystem, mntpoint, "ext2", opts, NULL)) { + if (doPwMount(fsystem, mntpoint, "squashfs", opts, NULL)) { + if (doPwMount(fsystem, mntpoint, "cramfs", opts, NULL)) { + if (doPwMount(fsystem, mntpoint, "vfat", opts, NULL)) { + logMessage(ERROR, "failed to mount loopback device %s on %s as %s: %m", + device, mntpoint, fsystem); + return LOADER_ERROR; + } + } + } + } + } + + logMessage(INFO, "mounted loopback device %s on %s as %s", mntpoint, device, fsystem); + + return 0; +} + +/* returns the *absolute* path (malloced) to the #1 iso image */ +/* get timestamp and description of ISO image from stamp file */ +/* returns 0 on success, -1 otherwise */ +int readStampFileFromIso(char *file, char **timestamp, char **releasedescr) { + DIR * dir; + FILE *f; + struct dirent * ent; + struct stat sb; + char *stampfile; + char *descr, *tstamp; + char tmpstr[1024]; + int filetype; + int rc; + + lstat(file, &sb); + if (S_ISBLK(sb.st_mode)) { + filetype = 1; + if (doPwMount(file, "/tmp/testmnt", "iso9660", "ro", NULL)) { + logMessage(ERROR, "Failed to mount device %s to get description", + file); + return -1; + } + } else if (S_ISREG(sb.st_mode)) { + filetype = 2; + if (mountLoopback(file, "/tmp/testmnt", "/dev/loop6")) { + logMessage(ERROR, "Failed to mount iso %s to get description", + file); + return -1; + } + } else { + logMessage(ERROR, "Unknown type of file %s to get description", + file); + return -1; + } + + if (!(dir = opendir("/tmp/testmnt"))) { + umount("/tmp/testmnt"); + if (filetype == 2) + umountLoopback("/tmp/testmnt", "/dev/loop6"); + return -1; + } + + errno = 0; + stampfile = NULL; + while ((ent = readdir(dir))) { + if (!strncmp(ent->d_name, ".discinfo", 9)) { + stampfile = strdup(".discinfo"); + break; + } + } + + closedir(dir); + descr = NULL; + tstamp = NULL; + if (stampfile) { + snprintf(tmpstr, sizeof(tmpstr), "/tmp/testmnt/%s", stampfile); + f = fopen(tmpstr, "r"); + if (f) { + char *tmpptr; + + /* readtime stamp line */ + tmpptr = fgets(tmpstr, sizeof(tmpstr), f); + + if (tmpptr) + tstamp = strdup(tmpstr); + + /* now read OS description line */ + if (tmpptr) + tmpptr = fgets(tmpstr, sizeof(tmpstr), f); + + if (tmpptr) + descr = strdup(tmpstr); + + /* skip over arch */ + if (tmpptr) + tmpptr = fgets(tmpstr, sizeof(tmpstr), f); + + /* now get the CD number */ + if (tmpptr) { + unsigned int len; + char *p, *newstr; + + tmpptr = fgets(tmpstr, sizeof(tmpstr), f); + + /* nuke newline from end of descr, stick number on end*/ + for (p=descr+strlen(descr); p != descr && !isspace(*p); p--); + + *p = '\0'; + len = strlen(descr) + strlen(tmpstr) + 10; + newstr = malloc(len); + strncpy(newstr, descr, len-1); + strncat(newstr, " ", len-1); + + /* is this a DVD or not? If disc id has commas, like */ + /* "1,2,3", its a DVD */ + if (strchr(tmpstr, ',')) + strncat(newstr, "DVD\n", len-1); + else { + strncat(newstr, "disc ", len-1); + strncat(newstr, tmpstr, len-1); + } + + free(descr); + descr = newstr; + } + + fclose(f); + } + } + + free(stampfile); + + umount("/tmp/testmnt"); + if (filetype == 2) + umountLoopback("/tmp/testmnt", "/dev/loop6"); + + if (descr != NULL && tstamp != NULL) { + descr[strlen(descr)-1] = '\0'; + *releasedescr = descr; + + tstamp[strlen(tstamp)-1] = '\0'; + *timestamp = tstamp; + + rc = 0; + } else { + rc = 1; + } + + return rc; +} + +/* XXX this ignores "location", which should be fixed + * + * Given a starting isoFile, will offer choice to mediacheck it and + * all other ISO images in the same directory with the same stamp + */ +void queryIsoMediaCheck(char *isoFile) { + DIR * dir; + struct dirent * ent; + char *isoDir; + char isoImage[1024]; + char tmpmessage[1024]; + char *master_timestamp; + char *tmpstr; + int rc, first; + + /* dont bother to test in automated installs */ + if (FL_KICKSTART(flags) && !FL_MEDIACHECK(flags)) + return; + + /* if they did not specify to mediacheck explicitely then return */ + if (!FL_MEDIACHECK(flags)) + return; + + /* check that file is actually an iso */ + if (!fileIsIso(isoFile)) + return; + + /* get stamp of isoFile, free descr since we dont care */ + readStampFileFromIso(isoFile, &master_timestamp, &tmpstr); + free(tmpstr); + + /* get base path from isoFile */ + tmpstr = strdup(isoFile); + isoDir = strdup(dirname(tmpstr)); + free(tmpstr); + + logMessage(DEBUGLVL, "isoFile = %s", isoFile); + logMessage(DEBUGLVL, "isoDir = %s", isoDir); + logMessage(DEBUGLVL, "Master Timestemp = %s", master_timestamp); + + if (!(dir = opendir(isoDir))) { + newtWinMessage(_("Error"), _("OK"), + _("Failed to read directory %s: %m"), + isoDir); + free(isoDir); + free(master_timestamp); + return; + } + + /* Walk through the directories looking for a CD images. */ + errno = 0; + first = 0; + while (1) { + char *nextname; + char *tdescr, *tstamp; + + if (first) { + first = 1; + nextname = isoFile; + } else { + ent = readdir(dir); + if (!ent) + break; + + nextname = ent->d_name; + } + + /* synthesize name of iso from isoDir and file entry */ + snprintf(isoImage, sizeof(isoImage), "%s/%s", isoDir, nextname); + + /* see if this is an iso image */ + if (!fileIsIso(isoImage)) { + errno = 0; + continue; + } + + /* see if its part of the current CD set */ + readStampFileFromIso(isoImage, &tstamp, &tdescr); + if (strcmp(tstamp, master_timestamp)) { + errno = 0; + continue; + } + + /* found a valid candidate, proceed */ + snprintf(tmpmessage, sizeof(tmpmessage), + _("Would you like to perform a checksum " + "test of the ISO image:\n\n %s?"), isoImage); + + rc = newtWinChoice(_("Checksum Test"), _("Test"), _("Skip"), + tmpmessage); + + if (rc == 2) { + logMessage(INFO, "mediacheck: skipped checking of %s", isoImage); + if (tdescr) + free(tdescr); + continue; + } else { + doMediaCheck(isoImage, tdescr); + if (tdescr) + free(tdescr); + + continue; + } + } + + free(isoDir); + free(master_timestamp); + closedir(dir); +} + +static void copyWarnFn (char *msg) { + logMessage(WARNING, msg); +} + +static void copyErrorFn (char *msg) { + newtWinMessage(_("Error"), _("OK"), _(msg)); +} + +/* + * unpack a gzipped cpio ball into a tree rooted at rootDir + * returns 0 on success, 1 on failure + */ +int unpackCpioBall(char * ballPath, char * rootDir) { + gzFile fd; + char *buf, *cwd; + int rc = 1; + + if (access(ballPath, R_OK)) + return 1; + + if (access(rootDir, R_OK)) + mkdirChain(rootDir); + + buf = (char *)malloc(PATH_MAX); + cwd = getcwd(buf, PATH_MAX); + if ((rc = chdir(rootDir)) == 0) { + fd = gunzip_open(ballPath); + if (fd) { + if (!installCpioFile(fd, NULL, NULL, 0)) { + logMessage(INFO, "copied contents of %s into %s", ballPath, + rootDir); + rc = chdir(cwd); + return 0; + } + gunzip_close(fd); + } + rc = chdir(cwd); + } + + return 1; +} + +void copyUpdatesImg(char * path) { + if (!access(path, R_OK)) { + if (!mountLoopback(path, "/tmp/update-disk", "/dev/loop7")) { + copyDirectory("/tmp/update-disk", "/tmp/updates", copyWarnFn, + copyErrorFn); + umountLoopback("/tmp/update-disk", "/dev/loop7"); + unlink("/tmp/update-disk"); + } else { + unpackCpioBall(path, "/tmp/updates"); + } + } +} + +void copyProductImg(char * path) { + if (!access(path, R_OK)) { + if (!mountLoopback(path, "/tmp/product-disk", "/dev/loop7")) { + copyDirectory("/tmp/product-disk", "/tmp/product", copyWarnFn, + copyErrorFn); + umountLoopback("/tmp/product-disk", "/dev/loop7"); + unlink("/tmp/product-disk"); + } + } +} + +/* unmount a second stage, if mounted. Used for CDs and mediacheck mostly, + so we can eject CDs. */ +void umountStage2(void) { + umountLoopback("/mnt/runtime", "/dev/loop0"); +} + +/* mount a second stage, verify the stamp file, copy updates + * Returns 0 on success, 1 on failure to mount, -1 on bad stamp */ +int mountStage2(char *stage2path) { + if (access(stage2path, R_OK)) { + return 1; + } + + if (mountLoopback(stage2path, "/mnt/runtime", "/dev/loop0")) { + return 1; + } + + return 0; +} + + +/* copies a second stage from fd to dest and mounts on mntpoint */ +int copyFileAndLoopbackMount(int fd, char * dest, + char * device, char * mntpoint) { + int rc; + struct stat sb; + + rc = copyFileFd(fd, dest); + stat(dest, &sb); + logMessage(DEBUGLVL, "copied %" PRId64 " bytes to %s (%s)", sb.st_size, dest, + ((rc) ? " incomplete" : "complete")); + + if (rc) { + /* just to make sure */ + unlink(dest); + return 1; + } + + if (mountLoopback(dest, mntpoint, device)) { + /* JKFIXME: this used to be fatal, but that seems unfriendly */ + logMessage(ERROR, "Error mounting %s on %s: %m", device, mntpoint); + return 1; + } + + return 0; +} + +/* given a device name (w/o '/dev' on it), try to get a file */ +/* Error codes: + 1 - could not create device node + 2 - could not mount device as ext2, vfat, or iso9660 + 3 - file named path not there +*/ +int getFileFromBlockDevice(char *device, char *path, char * dest) { + int rc; + char file[4096]; + + logMessage(INFO, "getFileFromBlockDevice(%s, %s)", device, path); + + if (doPwMount(device, "/tmp/mnt", "vfat", "ro", NULL) && + doPwMount(device, "/tmp/mnt", "ext2", "ro", NULL) && + doPwMount(device, "/tmp/mnt", "iso9660", "ro", NULL)) { + logMessage(ERROR, "failed to mount /dev/%s: %m", device); + return 2; + } + + snprintf(file, sizeof(file), "/tmp/mnt/%s", path); + logMessage(INFO, "Searching for file on path %s", file); + + if (access(file, R_OK)) { + rc = 3; + } else { + copyFile(file, dest); + rc = 0; + logMessage(INFO, "file copied to %s", dest); + } + + umount("/tmp/mnt"); + unlink("/tmp/mnt"); + return rc; +} + +void setStage2LocFromCmdline(char * arg, struct loaderData_s * ld) { + char * c, * dup; + + dup = strdup(arg); + c = dup; + /* : will let us delimit real information on the method */ + if ((c = strtok(c, ":"))) { + c = strtok(NULL, ":"); + + if (!strncmp(arg, "nfs:", 4)) { + ld->method = METHOD_NFS; + ld->stage2Data = calloc(sizeof(struct nfsInstallData *), 1); + + ((struct nfsInstallData *)ld->stage2Data)->mountOpts = NULL; + ((struct nfsInstallData *)ld->stage2Data)->host = strdup(c); + if ((c = strtok(NULL, ":"))) { + ((struct nfsInstallData *)ld->stage2Data)->directory = strdup(c); + } + } else if (!strncmp(arg, "ftp:", 4) || + !strncmp(arg, "http:", 5)) { + ld->method = METHOD_URL; + ld->stage2Data = calloc(sizeof(struct urlInstallData *), 1); + ((struct urlInstallData *)ld->stage2Data)->url = strdup(arg); + } else if (!strncmp(arg, "cdrom:", 6)) { + ld->method = METHOD_CDROM; + } else if (!strncmp(arg, "harddrive:", 10) || + !strncmp(arg, "hd:", 3)) { + ld->method = METHOD_HD; + ld->stage2Data = calloc(sizeof(struct hdInstallData *), 1); + ((struct hdInstallData *)ld->stage2Data)->partition = strdup(c); + if ((c = strtok(NULL, ":"))) { + ((struct hdInstallData *)ld->stage2Data)->directory = strdup(c); + } + } + } + free(dup); +} -- cgit