summaryrefslogtreecommitdiffstats
path: root/fs/pstore/fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/pstore/fs.c')
-rw-r--r--fs/pstore/fs.c280
1 files changed, 280 insertions, 0 deletions
diff --git a/fs/pstore/fs.c b/fs/pstore/fs.c
new file mode 100644
index 0000000000..0e05d48ea0
--- /dev/null
+++ b/fs/pstore/fs.c
@@ -0,0 +1,280 @@
+/*
+ * Persistent Storage Barebox filesystem layer
+ * Copyright © 2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <fs.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fs.h>
+#include <malloc.h>
+#include <init.h>
+#include <linux/stat.h>
+#include <linux/err.h>
+#include <linux/pstore.h>
+#include <libbb.h>
+#include <rtc.h>
+#include <libfile.h>
+#include <linux/pstore.h>
+#include "internal.h"
+
+struct list_head allpstore = LIST_HEAD_INIT(allpstore);
+
+struct pstore_private {
+ char name[PSTORE_NAMELEN];
+ struct list_head list;
+ struct pstore_info *psi;
+ enum pstore_type_id type;
+ u64 id;
+ int count;
+ ssize_t size;
+ ssize_t pos;
+ char data[];
+};
+
+/*
+ * Make a regular file in the root directory of our file system.
+ * Load it up with "size" bytes of data from "buf".
+ * Set the mtime & ctime to the date that this record was originally stored.
+ */
+int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
+ char *data, bool compressed, size_t size,
+ struct pstore_info *psi)
+{
+ struct pstore_private *private, *pos;
+
+ list_for_each_entry(pos, &allpstore, list) {
+ if (pos->type == type && pos->id == id && pos->psi == psi)
+ return -EEXIST;
+ }
+
+ private = xzalloc(sizeof(*private) + size);
+ if (!private)
+ return -ENOMEM;
+ private->type = type;
+ private->id = id;
+ private->count = count;
+ private->psi = psi;
+
+ switch (type) {
+ case PSTORE_TYPE_DMESG:
+ scnprintf(private->name, sizeof(private->name),
+ "dmesg-%s-%lld%s", psname, id,
+ compressed ? ".enc.z" : "");
+ break;
+ case PSTORE_TYPE_CONSOLE:
+ scnprintf(private->name, sizeof(private->name),
+ "console-%s-%lld", psname, id);
+ break;
+ case PSTORE_TYPE_FTRACE:
+ scnprintf(private->name, sizeof(private->name),
+ "ftrace-%s-%lld", psname, id);
+ break;
+ case PSTORE_TYPE_MCE:
+ scnprintf(private->name, sizeof(private->name),
+ "mce-%s-%lld", psname, id);
+ break;
+ case PSTORE_TYPE_PPC_RTAS:
+ scnprintf(private->name, sizeof(private->name),
+ "rtas-%s-%lld", psname, id);
+ break;
+ case PSTORE_TYPE_PPC_OF:
+ scnprintf(private->name, sizeof(private->name),
+ "powerpc-ofw-%s-%lld", psname, id);
+ break;
+ case PSTORE_TYPE_PPC_COMMON:
+ scnprintf(private->name, sizeof(private->name),
+ "powerpc-common-%s-%lld", psname, id);
+ break;
+ case PSTORE_TYPE_PMSG:
+ scnprintf(private->name, sizeof(private->name),
+ "pmsg-%s-%lld", psname, id);
+ break;
+ case PSTORE_TYPE_UNKNOWN:
+ scnprintf(private->name, sizeof(private->name),
+ "unknown-%s-%lld", psname, id);
+ break;
+ default:
+ scnprintf(private->name, sizeof(private->name),
+ "type%d-%s-%lld", type, psname, id);
+ break;
+ }
+
+ memcpy(private->data, data, size);
+ private->size = size;
+
+ list_add(&private->list, &allpstore);
+
+ return 0;
+}
+
+static struct pstore_private *pstore_get_by_name(struct list_head *head,
+ const char *name)
+{
+ struct pstore_private *d;
+
+ if (!name)
+ return NULL;
+
+ list_for_each_entry(d, head, list) {
+ if (strcmp(d->name, name) == 0)
+ return d;
+ }
+
+ return NULL;
+}
+
+static int pstore_open(struct device_d *dev, FILE *file, const char *filename)
+{
+ struct list_head *head = dev->priv;
+ struct pstore_private *d;
+
+ if (filename[0] == '/')
+ filename++;
+
+ d = pstore_get_by_name(head, filename);
+ if (!d)
+ return -EINVAL;
+
+ file->size = d->size;
+ file->priv = d;
+ d->pos = 0;
+
+ return 0;
+}
+
+static int pstore_close(struct device_d *dev, FILE *file)
+{
+ return 0;
+}
+
+static int pstore_read(struct device_d *dev, FILE *file, void *buf,
+ size_t insize)
+{
+ struct pstore_private *d = file->priv;
+
+ memcpy(buf, &d->data[d->pos], insize);
+ d->pos += insize;
+
+ return insize;
+}
+
+static loff_t pstore_lseek(struct device_d *dev, FILE *file, loff_t pos)
+{
+ struct pstore_private *d = file->priv;
+
+ d->pos = pos;
+
+ return pos;
+}
+
+static DIR *pstore_opendir(struct device_d *dev, const char *pathname)
+{
+ DIR *dir;
+
+ dir = xzalloc(sizeof(DIR));
+
+ if (list_empty(&allpstore))
+ return dir;
+
+ dir->priv = list_first_entry(&allpstore, struct pstore_private, list);
+
+ return dir;
+}
+
+static struct dirent *pstore_readdir(struct device_d *dev, DIR *dir)
+{
+ struct pstore_private *d = dir->priv;
+
+ if (!d || &d->list == &allpstore)
+ return NULL;
+
+ strcpy(dir->d.d_name, d->name);
+ dir->priv = list_entry(d->list.next, struct pstore_private, list);
+
+ return &dir->d;
+}
+
+static int pstore_closedir(struct device_d *dev, DIR *dir)
+{
+ free(dir);
+
+ return 0;
+}
+
+static int pstore_stat(struct device_d *dev, const char *filename,
+ struct stat *s)
+{
+ struct pstore_private *d;
+
+ if (filename[0] == '/')
+ filename++;
+
+ d = pstore_get_by_name(&allpstore, filename);
+ if (!d)
+ return -EINVAL;
+
+ s->st_size = d->size;
+ s->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
+
+ return 0;
+}
+
+static void pstore_remove(struct device_d *dev)
+{
+ struct pstore_private *d, *tmp;
+
+ list_for_each_entry_safe(d, tmp, &allpstore, list) {
+ free(d);
+ }
+}
+
+static int pstore_probe(struct device_d *dev)
+{
+ struct list_head *priv = &allpstore;
+
+ dev->priv = priv;
+
+ dev_dbg(dev, "mounted pstore\n");
+
+ return 0;
+}
+
+static struct fs_driver_d pstore_driver = {
+ .open = pstore_open,
+ .close = pstore_close,
+ .read = pstore_read,
+ .lseek = pstore_lseek,
+ .opendir = pstore_opendir,
+ .readdir = pstore_readdir,
+ .closedir = pstore_closedir,
+ .stat = pstore_stat,
+ .flags = FS_DRIVER_NO_DEV,
+ .type = filetype_uimage,
+ .drv = {
+ .probe = pstore_probe,
+ .remove = pstore_remove,
+ .name = "pstore",
+ }
+};
+
+static int pstore_init(void)
+{
+ return register_fs_driver(&pstore_driver);
+}
+coredevice_initcall(pstore_init);