Make it explicit that Windows need -swap, even XP :(
[syslinux.git] / libinstaller / setadv.c
blobc768d1b133533a89293337443806384acbc3ad2b
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 * Boston MA 02111-1307, USA; either version 2 of the License, or
9 * (at your option) any later version; incorporated herein by reference.
11 * ----------------------------------------------------------------------- */
14 * setadv.c
16 * (Over)write a data item in the auxilliary data vector. To
17 * delete an item, set its length to zero.
19 * Return 0 on success, -1 on error, and set errno.
23 #include <string.h>
24 #include <errno.h>
25 #include "syslxint.h"
27 unsigned char syslinux_adv[2*ADV_SIZE];
29 #define ADV_MAGIC1 0x5a2d2fa5 /* Head signature */
30 #define ADV_MAGIC2 0xa3041767 /* Total checksum */
31 #define ADV_MAGIC3 0xdd28bf64 /* Tail signature */
33 static void cleanup_adv(unsigned char *advbuf)
35 int i;
36 uint32_t csum;
38 /* Make sure both copies agree, and update the checksum */
39 set_32(advbuf, ADV_MAGIC1);
41 csum = ADV_MAGIC2;
42 for (i = 8; i < ADV_SIZE-4; i += 4)
43 csum -= get_32(advbuf+i);
45 set_32(advbuf+4, csum);
46 set_32(advbuf+ADV_SIZE-4, ADV_MAGIC3);
48 memcpy(advbuf+ADV_SIZE, advbuf, ADV_SIZE);
51 int syslinux_setadv(int tag, size_t size, const void *data)
53 uint8_t *p;
54 size_t left;
55 uint8_t advtmp[ADV_SIZE];
57 if ((unsigned)tag-1 > 254) {
58 errno = EINVAL;
59 return -1; /* Impossible tag value */
62 if (size > 255) {
63 errno = ENOSPC; /* Max 255 bytes for a data item */
64 return -1;
67 left = ADV_LEN;
68 p = advtmp;
69 memcpy(p, syslinux_adv+2*4, left); /* Make working copy */
71 while (left >= 2) {
72 uint8_t ptag = p[0];
73 size_t plen = p[1]+2;
75 if (ptag == ADV_END)
76 break;
78 if (ptag == tag) {
79 /* Found our tag. Delete it. */
81 if (plen >= left) {
82 /* Entire remainder is our tag */
83 break;
85 memmove(p, p+plen, left-plen);
86 } else {
87 /* Not our tag */
88 if (plen > left)
89 break; /* Corrupt tag (overrun) - overwrite it */
91 left -= plen;
92 p += plen;
96 /* Now (p, left) reflects the position to write in and how much space
97 we have for our data. */
99 if (size) {
100 if (left < size+2) {
101 errno = ENOSPC; /* Not enough space for data */
102 return -1;
105 *p++ = tag;
106 *p++ = size;
107 memcpy(p, data, size);
108 p += size;
109 left -= size+2;
112 memset(p, 0, left);
114 /* If we got here, everything went OK, commit the write */
115 memcpy(syslinux_adv+2*4, advtmp, ADV_LEN);
116 cleanup_adv(syslinux_adv);
118 return 0;
121 void syslinux_reset_adv(unsigned char *advbuf)
123 /* Create an all-zero ADV */
124 memset(advbuf+2*4, 0, ADV_LEN);
125 cleanup_adv(advbuf);
128 static int adv_consistent(const unsigned char *p)
130 int i;
131 uint32_t csum;
133 if (get_32(p) != ADV_MAGIC1 || get_32(p+ADV_SIZE-4) != ADV_MAGIC3)
134 return 0;
136 csum = 0;
137 for (i = 4; i < ADV_SIZE-4; i += 4)
138 csum += get_32(p+i);
140 return csum == ADV_MAGIC2;
144 * Verify that an in-memory ADV is consistent, making the copies consistent.
145 * If neither copy is OK, return -1 and call syslinux_reset_adv().
147 int syslinux_validate_adv(unsigned char *advbuf)
149 if (adv_consistent(advbuf+0*ADV_SIZE)) {
150 memcpy(advbuf+ADV_SIZE, advbuf, ADV_SIZE);
151 return 0;
152 } else if (adv_consistent(advbuf+1*ADV_SIZE)) {
153 memcpy(advbuf, advbuf+ADV_SIZE, ADV_SIZE);
154 return 0;
155 } else {
156 syslinux_reset_adv(advbuf);
157 return -1;