setadv.c 3.9 KB

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