From 4024ab566a0d86b7bdb3eeec4aa751ef31a7f883 Mon Sep 17 00:00:00 2001
From: Alessandro Rubini <rubini@gnudd.com>
Date: Mon, 8 Apr 2013 16:17:10 +0200
Subject: [PATCH] sdbfs/userspace (and doc): added sdb-extract

Signed-off-by: Alessandro Rubini <rubini@gnudd.com>
---
 sdbfs/doc/sdbfs.in            |  42 ++++++++
 sdbfs/userspace/.gitignore    |   1 +
 sdbfs/userspace/Makefile      |   2 +-
 sdbfs/userspace/sdb-extract.c | 192 ++++++++++++++++++++++++++++++++++
 4 files changed, 236 insertions(+), 1 deletion(-)
 create mode 100644 sdbfs/userspace/sdb-extract.c

diff --git a/sdbfs/doc/sdbfs.in b/sdbfs/doc/sdbfs.in
index 667ffa5..2d9af27 100644
--- a/sdbfs/doc/sdbfs.in
+++ b/sdbfs/doc/sdbfs.in
@@ -582,6 +582,48 @@ and @i{gensdbfs.c} has been over-allocated as a 64kB area.
    e38de09fe2bd0dab3ff7ebcab300977e  -
 @end smallexample
 
+@c ==========================================================================
+@node sdb-extract
+@section sdb-extract
+
+The @i{sdb-extract} tool is a program that opens an SDBFS file and creates
+a directory with the contents of the filesystem and a the associated
+configuration file.  Running  @i{gensdbfs} for this output directory would
+create the exact same SDBFS image you started from.
+
+The tool is meant to help retrieving information from already-built
+EEPROM images, and possibly change them before rebuilding an image file.
+
+The program refuses to change existing directories and extracts the
+SDB filesystem to a newly-created directory.  It receives two
+arguments: the output directory and the input SDB file (i.e., the same
+arguments you would pass to @i{gensdbfs}).
+
+The optional @t{-f} command line option forces @i{sdb-extract} to add
+files to an existing directory. In this case the program creates a
+configuration file (called @t{--SDB-CONFIG--}) only if it doesn't exist
+in the output directory.
+
+This is an example run of the program, using the @i{doc} directory
+of this package as data set:
+
+@example
+   % ./gensdbfs ../doc /tmp/doc.sdb
+   % ./sdb-extract /tmp/doc-extracted /tmp/doc.sdb
+   % ./gensdbfs /tmp/doc-extracted /tmp/doc2.sdb
+   % diff -ur ../doc /tmp/doc-extracted/
+   Only in /tmp/doc-extracted/: --SDB-CONFIG--
+@end example
+
+In general, if you re-run @i{gensdbfs} on the extracted directory, the
+generated SDBFS image will be different, because the order of the
+files is unpredictable.
+
+Another point to keep in mind is the use of @t{maxsize =} directive
+for @i{gensdbfs}.  SDB has no concept of ``current'' and ``max'' size,
+so a file allocated with @t{maxsize =} will be extracted at its maximum
+size, and no @t{maxsize =} is generated in the output @t{--SDB-CONFIG--}.
+
 @c ##########################################################################
 @node Kernel Support
 @chapter Kernel Support
diff --git a/sdbfs/userspace/.gitignore b/sdbfs/userspace/.gitignore
index 74bfd5b..4cfaf59 100644
--- a/sdbfs/userspace/.gitignore
+++ b/sdbfs/userspace/.gitignore
@@ -1,2 +1,3 @@
 gensdbfs
 sdb-read
+sdb-extract
diff --git a/sdbfs/userspace/Makefile b/sdbfs/userspace/Makefile
index 98e4676..4a367a7 100644
--- a/sdbfs/userspace/Makefile
+++ b/sdbfs/userspace/Makefile
@@ -13,7 +13,7 @@ CFLAGS = -Wall -ggdb
 CFLAGS += -I../lib -I../include -I../include/linux
 LDFLAGS = -L../lib -lsdbfs
 
-PROG = gensdbfs sdb-read
+PROG = gensdbfs sdb-read sdb-extract
 
 all: $(PROG)
 
diff --git a/sdbfs/userspace/sdb-extract.c b/sdbfs/userspace/sdb-extract.c
new file mode 100644
index 0000000..0bb15de
--- /dev/null
+++ b/sdbfs/userspace/sdb-extract.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2013 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ *
+ * This work is part of the White Rabbit project, a research effort led
+ * by CERN, the European Institute for Nuclear Research.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include "libsdbfs.h"
+#define CFG_NAME "--SDB-CONFIG--"
+
+/*
+ * This is similar to ./sdb-read, so some code duplication is there,
+ * but I'd better keep the tools separate and simple
+ */
+
+char *prgname;
+
+static int opt_force, opt_entry;
+
+static int create_file(struct sdbfs *fs, struct sdb_device *d, FILE *cfgf)
+{
+	FILE *f;
+	struct sdb_product *p;
+	struct sdb_component *c;
+	char name[32];
+	int mode = 0444;
+
+	c = &d->sdb_component;
+	p = &c->product;
+
+	/* Remove trailing spaces from the name */
+	strncpy(name, (char *)p->name, sizeof(p->name));
+	name[sizeof(p->name)] = '\0';
+	while (name[strlen(name) - 1] == ' ')
+		name[strlen(name) - 1] = '\0';
+
+	/* Print cfgfile information */
+	fprintf(cfgf, "%s\n" "\tvendor = 0x%016llx\n" "\tdevice = 0x%08x\n",
+		name, ntohll(p->vendor_id), ntohl(p->device_id));
+	fprintf(cfgf, "\tposition = 0x%llx\n", ntohll(c->addr_first));
+	if (ntohl(d->bus_specific) & SDB_DATA_WRITE) {
+		fprintf(cfgf, "\twrite = 1\n");
+		mode |= 0222;
+	}
+	if (ntohl(d->bus_specific) & SDB_DATA_EXEC)
+		mode |= 0111;
+	fprintf(cfgf, "\n");
+
+	/* Create the actual file unless it is the root directory */
+	if (!strcmp(name, "."))
+		return 0;
+	f = fopen(name, "w");
+	if (!f) {
+		fprintf(stderr, "%s: open(%s): %s\n", prgname, name,
+			strerror(errno));
+		return -1;
+	}
+	fwrite(fs->data + ntohll(c->addr_first), 1,
+	       ntohll(c->addr_last) + 1 - ntohll(c->addr_first), f);
+	fclose(f);
+	chmod(name, mode);
+	return 0;
+}
+
+
+/* As promised, here's the user-interface glue (and initialization, I admit) */
+int main(int argc, char **argv)
+{
+	int n, new, c, err, cfgfd;
+	FILE *f, *cfgf;
+	struct sdbfs _fs;
+	struct sdbfs *fs = &_fs; /* I like to type "fs->" */
+	struct sdb_device *d;
+	struct stat stbuf;
+	void *mapaddr;
+	char *fsname, *dirname;
+	struct dirent **namelist;
+	int pagesize = getpagesize();
+
+	prgname = argv[0];
+
+	while ( (c = getopt(argc, argv, "e:f")) != -1) {
+		switch (c) {
+		case 'f':
+			opt_force = 1;
+			break;
+		case 'e':
+			if (sscanf(optarg, "%i", &opt_entry) != 1) {
+				fprintf(stderr, "%s: not a number \"%s\"\n",
+					prgname, optarg);
+				exit(1);
+			}
+		}
+	}
+	if (optind < argc - 2) {
+		fprintf(stderr, "%s: Use: \"%s [-f] [-e <entry>] "
+			"<output-dir> <sdb-file>\n", prgname, prgname);
+		exit(1);
+	}
+	fsname = argv[optind + 1];
+	dirname = argv[optind];
+
+	if ( !(f = fopen(fsname, "r")) || fstat(fileno(f), &stbuf) < 0) {
+		fprintf(stderr, "%s: %s: %s\n", prgname, fsname,
+			strerror(errno));
+		exit(1);
+	}
+
+	stbuf.st_size += pagesize - 1;
+	stbuf.st_size &= ~(pagesize - 1);
+	mapaddr = mmap(0, stbuf.st_size, PROT_READ, MAP_PRIVATE, fileno(f), 0);
+	if (mapaddr == MAP_FAILED) {
+		fprintf(stderr, "%s: mmap(%s): %s\n", prgname, fsname,
+			strerror(errno));
+		exit(1);
+	}
+
+	/* Check output dir is empty, open config file */
+
+	/* Open the filesystem */
+	memset(fs, 0, sizeof(*fs));
+
+	fs->name = fsname; /* not mandatory */
+	fs->blocksize = 256; /* only used for writing, actually */
+	fs->entrypoint = opt_entry;
+	fs->data = mapaddr;
+
+	err = sdbfs_dev_create(fs, 0);
+	if (err) {
+		fprintf(stderr, "%s: sdbfs_dev_create(): %s\n", prgname,
+			strerror(-err));
+		fprintf(stderr, "\t(wrong entry point 0x%08lx?)\n",
+			fs->entrypoint);
+		exit(1);
+	}
+
+	/* We are sure the fs is good: create output dir and cfgfile */
+	if (mkdir(dirname, 0777) < 0 && errno != EEXIST) {
+		fprintf(stderr, "%s: %s: %s\n", prgname, dirname,
+			strerror(errno));
+		exit(1);
+	}
+	if (chdir(dirname) < 0) {
+		fprintf(stderr, "%s: %s: %s\n", prgname, dirname,
+			strerror(errno));
+		exit(1);
+
+	}
+        n = scandir(".", &namelist, 0, 0);
+	if (!opt_force && n != 2) {
+		fprintf(stderr, "%s: %s: not empty\n", prgname, dirname);
+		exit(1);
+	}
+	cfgfd = open(CFG_NAME, O_RDWR | O_CREAT | O_EXCL, 0666);
+	if (cfgfd < 0) {
+		fprintf(stderr, "%s: Warning: %s/%s: %s\n", prgname, dirname,
+			CFG_NAME, strerror(errno));
+		cfgf = fopen("/dev/null", "w");
+	} else {
+		cfgf = fdopen(cfgfd, "w");
+	}
+
+	/* Save the header */
+	fprintf(cfgf, "# Configuration file generated by %s, reading %s\n\n",
+		prgname, fsname);
+
+	/* The root directory is a file like the other ones */
+	while ( (d = sdbfs_scan(fs, new)) != NULL) {
+		create_file(fs, d, cfgf);
+		new = 0;
+	}
+	sdbfs_dev_destroy(fs);
+	return 0;
+}
+
-- 
GitLab