blob: 7eb55b5e66e5aa4ff52df4cfc746953c1f13bd9a [file] [log] [blame]
/* ----------------------------------------------------------------------- *
*
* Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* ----------------------------------------------------------------------- */
/*
* initramfs_file.c
*
* Utility functions to add arbitrary files including cpio header
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <syslinux/linux.h>
#define CPIO_MAGIC "070701"
struct cpio_header {
char c_magic[6]; /* 070701 */
char c_ino[8]; /* Inode number */
char c_mode[8]; /* File mode and permissions */
char c_uid[8]; /* uid */
char c_gid[8]; /* gid */
char c_nlink[8]; /* Number of links */
char c_mtime[8]; /* Modification time */
char c_filesize[8]; /* Size of data field */
char c_maj[8]; /* File device major number */
char c_min[8]; /* File device minor number */
char c_rmaj[8]; /* Device node reference major number */
char c_rmin[8]; /* Device node reference minor number */
char c_namesize[8]; /* Length of filename including final \0 */
char c_chksum[8]; /* Checksum if c_magic ends in 2 */
};
static uint32_t next_ino = 1;
/* Create cpio headers for the directory entries leading up to a file.
Returns the number of bytes; doesn't touch the buffer if too small. */
static size_t initramfs_mkdirs(const char *filename, void *buffer,
size_t buflen)
{
const char *p = filename;
char *bp = buffer;
int len;
size_t bytes = 0, hdr_sz;
int pad;
while ((p = strchr(p, '/'))) {
if (p != filename && p[-1] != '/') {
len = p - filename;
bytes += ((sizeof(struct cpio_header) + len + 1) + 3) & ~3;
}
p++;
}
if (buflen >= bytes) {
p = filename;
while ((p = strchr(p, '/'))) {
if (p != filename && p[-1] != '/') {
len = p - filename;
hdr_sz = ((sizeof(struct cpio_header) + len + 1) + 3) & ~3;
bp += sprintf(bp, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x"
"%08x%08x%08x%08x", next_ino++, S_IFDIR | 0755,
0, 0, 1, 0, 0, 0, 1, 0, 1, len + 1, 0);
memcpy(bp, filename, len);
bp += len;
pad = hdr_sz - (sizeof(struct cpio_header) + len);
memset(bp, 0, pad);
bp += pad;
}
p++;
}
}
return bytes;
}
/*
* Create a file header (with optional parent directory entries)
* and add it to an initramfs chain
*/
int initramfs_mknod(struct initramfs *ihead, const char *filename,
int do_mkdir,
uint16_t mode, size_t len, uint32_t major, uint32_t minor)
{
size_t bytes, hdr_sz;
int namelen = strlen(filename);
int pad;
char *buffer, *bp;
if (do_mkdir)
bytes = initramfs_mkdirs(filename, NULL, 0);
else
bytes = 0;
hdr_sz = ((sizeof(struct cpio_header) + namelen + 1) + 3) & ~3;
bytes += hdr_sz;
bp = buffer = malloc(bytes);
if (!buffer)
return -1;
if (do_mkdir)
bp += initramfs_mkdirs(filename, bp, bytes);
bp += sprintf(bp, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x"
"%08x%08x%08x%08x", next_ino++, mode,
0, 0, 1, 0, len, 0, 1, major, minor, namelen + 1, 0);
memcpy(bp, filename, namelen);
bp += namelen;
pad = hdr_sz - (sizeof(struct cpio_header) + namelen);
memset(bp, 0, pad);
if (initramfs_add_data(ihead, buffer, bytes, bytes, 4)) {
free(buffer);
return -1;
}
return 0;
}
/*
* Add a file given data in memory to an initramfs chain. This
* can be used to create nonfiles like symlinks by specifying an
* appropriate mode.
*/
int initramfs_add_file(struct initramfs *ihead, const void *data,
size_t data_len, size_t len,
const char *filename, int do_mkdir, uint32_t mode)
{
if (initramfs_mknod(ihead, filename, do_mkdir,
(mode & S_IFMT) ? mode : mode | S_IFREG, len, 0, 1))
return -1;
return initramfs_add_data(ihead, data, data_len, len, 4);
}
int initramfs_add_trailer(struct initramfs *ihead)
{
return initramfs_mknod(ihead, "TRAILER!!!", 0, 0, 0, 0, 0);
}