Home | History | Annotate | Download | only in libinstaller
      1 /* ----------------------------------------------------------------------- *
      2  *
      3  *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
      4  *   Copyright 2010 Intel Corporation; author: H. Peter Anvin
      5  *
      6  *   This program is free software; you can redistribute it and/or modify
      7  *   it under the terms of the GNU General Public License as published by
      8  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
      9  *   Boston MA 02111-1307, USA; either version 2 of the License, or
     10  *   (at your option) any later version; incorporated herein by reference.
     11  *
     12  * ----------------------------------------------------------------------- */
     13 
     14 /*
     15  * advio.c
     16  *
     17  * Linux ADV I/O
     18  *
     19  * Return 0 on success, -1 on error, and set errno.
     20  *
     21  */
     22 #define  _GNU_SOURCE
     23 
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <stddef.h>
     27 #include <stdint.h>
     28 #include <string.h>
     29 #include <unistd.h>
     30 #include <errno.h>
     31 #include <fcntl.h>
     32 #include <sys/stat.h>
     33 #include <sys/types.h>
     34 #include "syslxint.h"
     35 #include "syslxcom.h"
     36 
     37 /*
     38  * Read the ADV from an existing instance, or initialize if invalid.
     39  * Returns -1 on fatal errors, 0 if ADV is okay, 1 if the ADV is
     40  * invalid, and 2 if the file does not exist.
     41  */
     42 int read_adv(const char *path, const char *cfg)
     43 {
     44     char *file;
     45     int fd = -1;
     46     struct stat st;
     47     int err = 0;
     48     int rv;
     49 
     50     rv = asprintf(&file, "%s%s%s", path,
     51 		  path[0] && path[strlen(path) - 1] == '/' ? "" : "/", cfg);
     52 
     53     if (rv < 0 || !file) {
     54 	perror(program);
     55 	return -1;
     56     }
     57 
     58     fd = open(file, O_RDONLY);
     59     if (fd < 0) {
     60 	if (errno != ENOENT) {
     61 	    err = -1;
     62 	} else {
     63 	    syslinux_reset_adv(syslinux_adv);
     64 	    err = 2;		/* Nonexistence is not a fatal error */
     65 	}
     66     } else if (fstat(fd, &st)) {
     67 	err = -1;
     68     } else if (st.st_size < 2 * ADV_SIZE) {
     69 	/* Too small to be useful */
     70 	syslinux_reset_adv(syslinux_adv);
     71 	err = 0;		/* Nothing to read... */
     72     } else if (xpread(fd, syslinux_adv, 2 * ADV_SIZE,
     73 		      st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
     74 	err = -1;
     75     } else {
     76 	/* We got it... maybe? */
     77 	err = syslinux_validate_adv(syslinux_adv) ? 1 : 0;
     78     }
     79 
     80     if (err < 0)
     81 	perror(file);
     82 
     83     if (fd >= 0)
     84 	close(fd);
     85 
     86     free(file);
     87 
     88     return err;
     89 }
     90 
     91 /*
     92  * Update the ADV in an existing installation.
     93  */
     94 int write_adv(const char *path, const char *cfg)
     95 {
     96     unsigned char advtmp[2 * ADV_SIZE];
     97     char *file;
     98     int fd = -1;
     99     struct stat st, xst;
    100     int err = 0;
    101     int rv;
    102 
    103     rv = asprintf(&file, "%s%s%s", path,
    104 		  path[0] && path[strlen(path) - 1] == '/' ? "" : "/", cfg);
    105 
    106     if (rv < 0 || !file) {
    107 	perror(program);
    108 	return -1;
    109     }
    110 
    111     fd = open(file, O_RDONLY);
    112     if (fd < 0) {
    113 	err = -1;
    114     } else if (fstat(fd, &st)) {
    115 	err = -1;
    116     } else if (st.st_size < 2 * ADV_SIZE) {
    117 	/* Too small to be useful */
    118 	err = -2;
    119     } else if (xpread(fd, advtmp, 2 * ADV_SIZE,
    120 		      st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
    121 	err = -1;
    122     } else {
    123 	/* We got it... maybe? */
    124 	err = syslinux_validate_adv(advtmp) ? -2 : 0;
    125 	if (!err) {
    126 	    /* Got a good one, write our own ADV here */
    127 	    clear_attributes(fd);
    128 
    129 	    /* Need to re-open read-write */
    130 	    close(fd);
    131 	    fd = open(file, O_RDWR | O_SYNC);
    132 	    if (fd < 0) {
    133 		err = -1;
    134 	    } else if (fstat(fd, &xst) || xst.st_ino != st.st_ino ||
    135 		       xst.st_dev != st.st_dev || xst.st_size != st.st_size) {
    136 		fprintf(stderr, "%s: race condition on write\n", file);
    137 		err = -2;
    138 	    }
    139 	    /* Write our own version ... */
    140 	    if (xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
    141 			st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
    142 		err = -1;
    143 	    }
    144 
    145 	    sync();
    146 	    set_attributes(fd);
    147 	}
    148     }
    149 
    150     if (err == -2)
    151 	fprintf(stderr, "%s: cannot write auxilliary data (need --update)?\n",
    152 		file);
    153     else if (err == -1)
    154 	perror(file);
    155 
    156     if (fd >= 0)
    157 	close(fd);
    158     if (file)
    159 	free(file);
    160 
    161     return err;
    162 }
    163