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  * setadv.c
     16  *
     17  * (Over)write a data item in the auxilliary data vector.  To
     18  * delete an item, set its length to zero.
     19  *
     20  * Return 0 on success, -1 on error, and set errno.
     21  *
     22  */
     23 #define  _GNU_SOURCE
     24 
     25 #include <stdio.h>
     26 #include <stdlib.h>
     27 #include <stddef.h>
     28 #include <stdint.h>
     29 #include <string.h>
     30 #include <errno.h>
     31 #include "syslxint.h"
     32 #include "syslxcom.h"
     33 #include "syslxfs.h"
     34 
     35 unsigned char syslinux_adv[2 * ADV_SIZE];
     36 
     37 #define ADV_MAGIC1	0x5a2d2fa5	/* Head signature */
     38 #define ADV_MAGIC2	0xa3041767	/* Total checksum */
     39 #define ADV_MAGIC3	0xdd28bf64	/* Tail signature */
     40 
     41 static void cleanup_adv(unsigned char *advbuf)
     42 {
     43     int i;
     44     uint32_t csum;
     45 
     46     /* Make sure both copies agree, and update the checksum */
     47     set_32((uint32_t *) advbuf, ADV_MAGIC1);
     48 
     49     csum = ADV_MAGIC2;
     50     for (i = 8; i < ADV_SIZE - 4; i += 4)
     51 	csum -= get_32((uint32_t *) (advbuf + i));
     52 
     53     set_32((uint32_t *) (advbuf + 4), csum);
     54     set_32((uint32_t *) (advbuf + ADV_SIZE - 4), ADV_MAGIC3);
     55 
     56     memcpy(advbuf + ADV_SIZE, advbuf, ADV_SIZE);
     57 }
     58 
     59 int syslinux_setadv(int tag, size_t size, const void *data)
     60 {
     61     uint8_t *p;
     62     size_t left;
     63     uint8_t advtmp[ADV_SIZE];
     64 
     65     if ((unsigned)tag - 1 > 254) {
     66 	errno = EINVAL;
     67 	return -1;		/* Impossible tag value */
     68     }
     69 
     70     if (size > 255) {
     71 	errno = ENOSPC;		/* Max 255 bytes for a data item */
     72 	return -1;
     73     }
     74 
     75     left = ADV_LEN;
     76     p = advtmp;
     77     memcpy(p, syslinux_adv + 2 * 4, left);	/* Make working copy */
     78 
     79     while (left >= 2) {
     80 	uint8_t ptag = p[0];
     81 	size_t plen = p[1] + 2;
     82 
     83 	if (ptag == ADV_END)
     84 	    break;
     85 
     86 	if (ptag == tag) {
     87 	    /* Found our tag.  Delete it. */
     88 
     89 	    if (plen >= left) {
     90 		/* Entire remainder is our tag */
     91 		break;
     92 	    }
     93 	    memmove(p, p + plen, left - plen);
     94 	} else {
     95 	    /* Not our tag */
     96 	    if (plen > left)
     97 		break;		/* Corrupt tag (overrun) - overwrite it */
     98 
     99 	    left -= plen;
    100 	    p += plen;
    101 	}
    102     }
    103 
    104     /* Now (p, left) reflects the position to write in and how much space
    105        we have for our data. */
    106 
    107     if (size) {
    108 	if (left < size + 2) {
    109 	    errno = ENOSPC;	/* Not enough space for data */
    110 	    return -1;
    111 	}
    112 
    113 	*p++ = tag;
    114 	*p++ = size;
    115 	memcpy(p, data, size);
    116 	p += size;
    117 	left -= size + 2;
    118     }
    119 
    120     memset(p, 0, left);
    121 
    122     /* If we got here, everything went OK, commit the write */
    123     memcpy(syslinux_adv + 2 * 4, advtmp, ADV_LEN);
    124     cleanup_adv(syslinux_adv);
    125 
    126     return 0;
    127 }
    128 
    129 void syslinux_reset_adv(unsigned char *advbuf)
    130 {
    131     /* Create an all-zero ADV */
    132     memset(advbuf + 2 * 4, 0, ADV_LEN);
    133     cleanup_adv(advbuf);
    134 }
    135 
    136 static int adv_consistent(const unsigned char *p)
    137 {
    138     int i;
    139     uint32_t csum;
    140 
    141     if (get_32((uint32_t *) p) != ADV_MAGIC1 ||
    142 	get_32((uint32_t *) (p + ADV_SIZE - 4)) != ADV_MAGIC3)
    143 	return 0;
    144 
    145     csum = 0;
    146     for (i = 4; i < ADV_SIZE - 4; i += 4)
    147 	csum += get_32((uint32_t *) (p + i));
    148 
    149     return csum == ADV_MAGIC2;
    150 }
    151 
    152 /*
    153  * Verify that an in-memory ADV is consistent, making the copies consistent.
    154  * If neither copy is OK, return -1 and call syslinux_reset_adv().
    155  */
    156 int syslinux_validate_adv(unsigned char *advbuf)
    157 {
    158     if (adv_consistent(advbuf + 0 * ADV_SIZE)) {
    159 	memcpy(advbuf + ADV_SIZE, advbuf, ADV_SIZE);
    160 	return 0;
    161     } else if (adv_consistent(advbuf + 1 * ADV_SIZE)) {
    162 	memcpy(advbuf, advbuf + ADV_SIZE, ADV_SIZE);
    163 	return 0;
    164     } else {
    165 	syslinux_reset_adv(advbuf);
    166 	return -1;
    167     }
    168 }
    169