Home | History | Annotate | Download | only in ldlinux
      1 /* ----------------------------------------------------------------------- *
      2  *
      3  *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
      4  *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
      5  *
      6  *   Permission is hereby granted, free of charge, to any person
      7  *   obtaining a copy of this software and associated documentation
      8  *   files (the "Software"), to deal in the Software without
      9  *   restriction, including without limitation the rights to use,
     10  *   copy, modify, merge, publish, distribute, sublicense, and/or
     11  *   sell copies of the Software, and to permit persons to whom
     12  *   the Software is furnished to do so, subject to the following
     13  *   conditions:
     14  *
     15  *   The above copyright notice and this permission notice shall
     16  *   be included in all copies or substantial portions of the Software.
     17  *
     18  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     19  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
     20  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     21  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
     22  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     23  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     24  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     25  *   OTHER DEALINGS IN THE SOFTWARE.
     26  *
     27  * ----------------------------------------------------------------------- */
     28 
     29 /*
     30  * syslinux/setadv.c
     31  *
     32  * (Over)write a data item in the auxilliary data vector.  To
     33  * delete an item, set its length to zero.
     34  *
     35  * Return 0 on success, -1 on error, and set errno.
     36  *
     37  * NOTE: Data is not written to disk unless
     38  * syslinux_adv_write() is called.
     39  */
     40 
     41 #include <syslinux/adv.h>
     42 #include <klibc/compiler.h>
     43 #include <inttypes.h>
     44 #include <string.h>
     45 #include <errno.h>
     46 #include <alloca.h>
     47 
     48 __export int syslinux_setadv(int tag, size_t size, const void *data)
     49 {
     50     uint8_t *p, *advtmp;
     51     size_t rleft, left;
     52 
     53     if ((unsigned)tag - 1 > 254) {
     54 	errno = EINVAL;
     55 	return -1;		/* Impossible tag value */
     56     }
     57 
     58     if (size > 255) {
     59 	errno = ENOSPC;		/* Max 255 bytes for a data item */
     60 	return -1;
     61     }
     62 
     63     rleft = left = syslinux_adv_size();
     64     p = advtmp = alloca(left);
     65     memcpy(p, syslinux_adv_ptr(), left);	/* Make working copy */
     66 
     67     while (rleft >= 2) {
     68 	uint8_t ptag = p[0];
     69 	size_t plen = p[1] + 2;
     70 
     71 	if (ptag == ADV_END)
     72 	    break;
     73 
     74 	if (ptag == tag) {
     75 	    /* Found our tag.  Delete it. */
     76 
     77 	    if (plen >= rleft) {
     78 		/* Entire remainder is our tag */
     79 		break;
     80 	    }
     81 	    memmove(p, p + plen, rleft - plen);
     82 	    rleft -= plen;	/* Fewer bytes to read, but not to write */
     83 	} else {
     84 	    /* Not our tag */
     85 	    if (plen > rleft)
     86 		break;		/* Corrupt tag (overrun) - overwrite it */
     87 
     88 	    left -= plen;
     89 	    rleft -= plen;
     90 	    p += plen;
     91 	}
     92     }
     93 
     94     /* Now (p, left) reflects the position to write in and how much space
     95        we have for our data. */
     96 
     97     if (size) {
     98 	if (left < size + 2) {
     99 	    errno = ENOSPC;	/* Not enough space for data */
    100 	    return -1;
    101 	}
    102 
    103 	*p++ = tag;
    104 	*p++ = size;
    105 	memcpy(p, data, size);
    106 	p += size;
    107 	left -= size + 2;
    108     }
    109 
    110     memset(p, 0, left);
    111 
    112     /* If we got here, everything went OK, commit the write to low memory */
    113     memcpy(syslinux_adv_ptr(), advtmp, syslinux_adv_size());
    114 
    115     return 0;
    116 }
    117