/* ----------------------------------------------------------------------- * * * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved * Copyright 2010 Intel Corporation; author: H. Peter Anvin * * 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, Inc., 53 Temple Place Ste 330, * Boston MA 02111-1307, USA; either version 2 of the License, or * (at your option) any later version; incorporated herein by reference. * * ----------------------------------------------------------------------- */ /* * advio.c * * Linux ADV I/O * * Return 0 on success, -1 on error, and set errno. * */ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <stdint.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include "syslxint.h" #include "syslxcom.h" /* * Read the ADV from an existing instance, or initialize if invalid. * Returns -1 on fatal errors, 0 if ADV is okay, 1 if the ADV is * invalid, and 2 if the file does not exist. */ int read_adv(const char *path, const char *cfg) { char *file; int fd = -1; struct stat st; int err = 0; int rv; rv = asprintf(&file, "%s%s%s", path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/", cfg); if (rv < 0 || !file) { perror(program); return -1; } fd = open(file, O_RDONLY); if (fd < 0) { if (errno != ENOENT) { err = -1; } else { syslinux_reset_adv(syslinux_adv); err = 2; /* Nonexistence is not a fatal error */ } } else if (fstat(fd, &st)) { err = -1; } else if (st.st_size < 2 * ADV_SIZE) { /* Too small to be useful */ syslinux_reset_adv(syslinux_adv); err = 0; /* Nothing to read... */ } else if (xpread(fd, syslinux_adv, 2 * ADV_SIZE, st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) { err = -1; } else { /* We got it... maybe? */ err = syslinux_validate_adv(syslinux_adv) ? 1 : 0; } if (err < 0) perror(file); if (fd >= 0) close(fd); free(file); return err; } /* * Update the ADV in an existing installation. */ int write_adv(const char *path, const char *cfg) { unsigned char advtmp[2 * ADV_SIZE]; char *file; int fd = -1; struct stat st, xst; int err = 0; int rv; rv = asprintf(&file, "%s%s%s", path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/", cfg); if (rv < 0 || !file) { perror(program); return -1; } fd = open(file, O_RDONLY); if (fd < 0) { err = -1; } else if (fstat(fd, &st)) { err = -1; } else if (st.st_size < 2 * ADV_SIZE) { /* Too small to be useful */ err = -2; } else if (xpread(fd, advtmp, 2 * ADV_SIZE, st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) { err = -1; } else { /* We got it... maybe? */ err = syslinux_validate_adv(advtmp) ? -2 : 0; if (!err) { /* Got a good one, write our own ADV here */ clear_attributes(fd); /* Need to re-open read-write */ close(fd); fd = open(file, O_RDWR | O_SYNC); if (fd < 0) { err = -1; } else if (fstat(fd, &xst) || xst.st_ino != st.st_ino || xst.st_dev != st.st_dev || xst.st_size != st.st_size) { fprintf(stderr, "%s: race condition on write\n", file); err = -2; } /* Write our own version ... */ if (xpwrite(fd, syslinux_adv, 2 * ADV_SIZE, st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) { err = -1; } sync(); set_attributes(fd); } } if (err == -2) fprintf(stderr, "%s: cannot write auxilliary data (need --update)?\n", file); else if (err == -1) perror(file); if (fd >= 0) close(fd); if (file) free(file); return err; }