aboutsummaryrefslogtreecommitdiff
// Take files given on stdin and make them into a ramfs image of our
// own, (stupid) simple format.
// In the format: for each file comes the null-terminated string
// with filename, then null-padding until a 4-aligned offset, then
// 4-byte little-endian size of the file and then the contents
// of the file and then another null-padding until a 4-aligned offset.
// Files encoded this way go one after another (so it's easy to add
// something at the beginning).
// At the and comes one null-byte (as if a file with empty name
// was there).

#include <stdint.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>
#include <sys/stat.h>
#include <string.h>

#define ANSI_FG_RED "\033[0;31m"
#define ANSI_FG_DEFAULT "\033[0;39m"

int main(int argc, char **argv)
{
  // process files in the order they are provided on the command line
  for (int i = 1; i < argc; i++)
    {
      struct stat fileinfo;
      
      if (stat(argv[i], &fileinfo))
	err(-1, "couldn't stat " ANSI_FG_RED "%s" ANSI_FG_DEFAULT,
	    argv[i]);
	
      if (!S_ISREG(fileinfo.st_mode))
	errx(-1, ANSI_FG_RED "%s" ANSI_FG_DEFAULT
	     " is not a regular file.", argv[i]);

      // don't allow files with size so big, that it can't be encoded
      // in a 4-byte unsigned int... In practice even smaller files
      // won't fit on the rpi.
      if (fileinfo.st_size > UINT32_MAX)
	errx(-1, ANSI_FG_RED "%s" ANSI_FG_DEFAULT
	     " is too big.", argv[i]);
      
      uint32_t file_size = fileinfo.st_size;
      uint32_t name_size = strlen(argv[i]) + 1; // 1 for null-byte
      
      if (fwrite(argv[i], 1, name_size, stdout) != name_size)
	errx(-1, "error writing to stdout");

      // pad with null-bytes until a 4-aligned offset
      for (uint32_t j = 0; (j + name_size) & 0b11; j++)
	if (putchar('\0'))
	  errx(-1, "error writing to stdout");	  

      // TODO convert file_size to little endian first (in case our
      // host is be).
      if (fwrite(&file_size, 4, 1, stdout) != 1)
	errx(-1, "error writing to stdout");

      // flush b4 running cat, so that stuff we've written comes
      // b4 the actual file contents in the output
      if (fflush(stdout))
	err(-1, "couldn't flush stdout");

      // we don't copy the actual file ourselves - we run cat for that
      pid_t pid;
      int wstatus;
      switch (pid = fork())
	{
	case -1:
	  err(-1, "couldn't fork");
	case 0:
	  if (execlp("cat", "cat", argv[i], NULL))
	    err(-1, "couldn't execute cat");
	default:
	  if (wait(&wstatus) == -1)
	    err(-1, "error waiting for child");

	  if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus))
	    exit(-1);
	}

      // again, pad with null-bytes until a 4-aligned offset
      for (uint32_t j = 0; (j + file_size) & 0b11; j++)
	if (putchar('\0'))
	  errx(-1, "error writing to stdout");
    }
  
  if (putchar('\0'))
    errx(-1, "error writing to stdout");
  
  return 0;
}