From mboxrd@z Thu Jan 1 00:00:00 1970 Return-path: Received: from 6.mo2.mail-out.ovh.net ([87.98.165.38] helo=mo2.mail-out.ovh.net) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1VRfiI-0005kB-2Z for barebox@lists.infradead.org; Thu, 03 Oct 2013 10:01:06 +0000 Received: from mail616.ha.ovh.net (b9.ovh.net [213.186.33.59]) by mo2.mail-out.ovh.net (Postfix) with SMTP id 8BC11DC22D4 for ; Thu, 3 Oct 2013 12:00:35 +0200 (CEST) From: Jean-Christophe PLAGNIOL-VILLARD Date: Thu, 3 Oct 2013 12:01:53 +0200 Message-Id: <1380794513-7100-2-git-send-email-plagnioj@jcrosoft.com> In-Reply-To: <1380794513-7100-1-git-send-email-plagnioj@jcrosoft.com> References: <20131003095957.GQ32444@ns203013.ovh.net> <1380794513-7100-1-git-send-email-plagnioj@jcrosoft.com> List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "barebox" Errors-To: barebox-bounces+u.kleine-koenig=pengutronix.de@lists.infradead.org Subject: [PATCH 2/2] fs: add BPKFS support To: barebox@lists.infradead.org Cc: Fargier Sylvain Simple update file format developed for Somfy, tools and library are available under LGPLv2 (https://www.gitorious.org/libbpk). This format in the v1.0 allow you to store 6 types a binary stream for a unique hardware id: - bootloader - bootloader_version - description.gz - kernel - rootfs - firmware_version and you can easly add more binary stream type. The fs will display you in a directory per hw id and if a binary stream type is unknown will be display as unknown_%08x # mount image.bpk /tmp # ls -l /tmp/hw_id_0/ -rwxrwxrwx 10 firmware_version -rwxrwxrwx 8 firmware_version.crc -rwxrwxrwx 1845968 kernel -rwxrwxrwx 8 kernel.crc -rwxrwxrwx 5062656 rootfs -rwxrwxrwx 8 rootfs.crc -rwxrwxrwx 248 bootloader -rwxrwxrwx 8 bootloader.crc -rwxrwxrwx 248925 description.gz -rwxrwxrwx 8 description.gz.crc -rwxrwxrwx 4 bootloader_version -rwxrwxrwx 8 bootloader_version.crc -rwxrwxrwx 4 unknown_1234567g -rwxrwxrwx 8 unknown_1234567g.crc Why BPK and not CPIO or uImage 1) CPIO cpio does not handle > 4GiB image and does not have any crc checksum 2) uImage uImage only provide one crc32 for the all data part and only a list of binary stream with no information about what is what (in multi-image format) 3) BPK BPK provide a crc32 for the header part and one crc32 per binary stream so if you does not care of some data you are not force to check them And you known exactly the binary stream type and for which hw to use it. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Fargier Sylvain --- fs/Kconfig | 5 + fs/Makefile | 1 + fs/bpkfs.c | 514 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/bpkfs.h | 68 ++++++++ 4 files changed, 588 insertions(+) create mode 100644 fs/bpkfs.c create mode 100644 include/bpkfs.h diff --git a/fs/Kconfig b/fs/Kconfig index d11431d..a914af9 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -48,6 +48,11 @@ config FS_UIMAGEFS bool prompt "uImage FS support" +config FS_BPKFS + bool + prompt "BPKFS support" + help + config PARTITION_NEED_MTD bool diff --git a/fs/Makefile b/fs/Makefile index 0bc9116..395514d 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_FS_TFTP) += tftp.o obj-$(CONFIG_FS_OMAP4_USBBOOT) += omap4_usbbootfs.o obj-$(CONFIG_FS_NFS) += nfs.o obj-$(CONFIG_FS_UIMAGEFS) += uimagefs.o +obj-$(CONFIG_FS_BPKFS) += bpkfs.o diff --git a/fs/bpkfs.c b/fs/bpkfs.c new file mode 100644 index 0000000..b3b45be --- /dev/null +++ b/fs/bpkfs.c @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2013 Jean-Christophe PLAGNIOL-VILLARD + * + * Simple update file format developed for Somfy, tools and library are + * available under LGPLv2 (https://www.gitorious.org/libbpk). + * + * under GPLv2 ONLY + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool bpkfs_is_crc_file(struct bpkfs_handle_data *d) +{ + return d->type & (1 << 31); +} + +const char* bpkfs_type_to_str(uint32_t type) +{ + switch (type) { + case BPKFS_TYPE_BL: + return "bootloader"; + case BPKFS_TYPE_BLV: + return "bootloader_version"; + case BPKFS_TYPE_DSC: + return "description.gz"; + case BPKFS_TYPE_KER: + return "kernel"; + case BPKFS_TYPE_RFS: + return "rootfs"; + case BPKFS_TYPE_FMV: + return "firmware_version"; + } + + return NULL; +} + +static struct bpkfs_handle_hw *bpkfs_get_by_hw_id( + struct bpkfs_handle *handle, uint32_t hw_id) +{ + struct bpkfs_handle_hw *h; + + list_for_each_entry(h, &handle->list, list_hw_id) { + if (h->hw_id == hw_id) + return h; + } + + return NULL; +} + +static struct bpkfs_handle_hw *bpkfs_hw_id_get_by_name( + struct bpkfs_handle *handle, const char *name) +{ + struct bpkfs_handle_hw *h; + + if (!name) + return NULL; + + list_for_each_entry(h, &handle->list, list_hw_id) { + if (strcmp(h->name, name) == 0) + return h; + } + + return NULL; +} + +static struct bpkfs_handle_data *bpkfs_data_get_by_name( + struct bpkfs_handle_hw *h, const char *name) +{ + struct bpkfs_handle_data *d; + + if (!name) + return NULL; + + list_for_each_entry(d, &h->list_data, list) { + if (strcmp(d->name, name) == 0) + return d; + } + + return NULL; +} + +static struct bpkfs_handle_hw *bpkfs_get_or_add_hw_id( + struct bpkfs_handle *handle, uint32_t hw_id) +{ + struct bpkfs_handle_hw *h; + + h = bpkfs_get_by_hw_id(handle, hw_id); + if (h) + return h; + + h = xzalloc(sizeof(*h)); + + INIT_LIST_HEAD(&h->list_data); + h->hw_id = hw_id; + h->name = asprintf("hw_id_%x", hw_id); + list_add_tail(&h->list_hw_id, &handle->list); + + return h; +} + +static struct bpkfs_handle_data *bpkfs_get_by_type( + struct bpkfs_handle *handle, uint32_t hw_id, uint32_t type) +{ + struct bpkfs_handle_data *d; + struct bpkfs_handle_hw *h; + + h = bpkfs_get_by_hw_id(handle, hw_id); + if (!h) + return NULL; + + list_for_each_entry(d, &h->list_data, list) { + if (d->type == type) + return d; + } + + return NULL; +} + +static int bpkfs_open(struct device_d *dev, FILE *f, const char *filename) +{ + struct bpkfs_handle *priv = dev->priv; + struct bpkfs_handle_data *d; + struct bpkfs_handle_hw *h; + char *dir, *file; + int ret = -EINVAL; + char *tmp = xstrdup(filename); + char *tmp2 = xstrdup(filename); + + dir = dirname(tmp); + + if (dir[0] == '/') + dir++; + + h = bpkfs_hw_id_get_by_name(priv, dir); + if (!h) + goto out; + + file = basename(tmp2); + d = bpkfs_data_get_by_name(h, file); + if (!d) + goto out; + + if (!bpkfs_is_crc_file(d)) { + d->fd = open(priv->filename, O_RDONLY); + if (d->fd < 0) { + ret = d->fd; + goto out; + } + + lseek(d->fd, d->offset, SEEK_SET); + } + + f->size = d->size; + f->inode = d; + ret = 0; + +out: + free(tmp); + free(tmp2); + return ret; +} + +static int bpkfs_close(struct device_d *dev, FILE *file) +{ + struct bpkfs_handle_data *d = file->inode; + + close(d->fd); + + return 0; +} + +static int bpkfs_read(struct device_d *dev, FILE *file, void *buf, size_t insize) +{ + struct bpkfs_handle_data *d = file->inode; + + if (bpkfs_is_crc_file(d)) { + memcpy(buf, &d->data[d->pos], insize); + return insize; + } else { + return read(d->fd, buf, insize); + } +} + +static loff_t bpkfs_lseek(struct device_d *dev, FILE *file, loff_t pos) +{ + struct bpkfs_handle_data *d = file->inode; + + if (!bpkfs_is_crc_file(d)) + lseek(d->fd, d->offset + pos, SEEK_SET); + + d->pos = pos; + + return pos; +} + +struct somfy_readdir { + struct bpkfs_handle_hw *h; + struct bpkfs_handle_data *d; + + DIR dir; +}; + +static DIR *bpkfs_opendir(struct device_d *dev, const char *pathname) +{ + struct bpkfs_handle *priv = dev->priv; + struct somfy_readdir *sdir; + DIR *dir; + + sdir = xzalloc(sizeof(*sdir)); + dir = &sdir->dir; + dir->priv = sdir; + + if (pathname[0] == '/') + pathname++; + + if (!strlen(pathname)) { + if (list_empty(&priv->list)) + return dir; + + sdir->h = list_first_entry(&priv->list, + struct bpkfs_handle_hw, list_hw_id); + } else { + sdir->h = bpkfs_hw_id_get_by_name(priv, pathname); + if (!sdir->h || list_empty(&sdir->h->list_data)) + return dir; + + sdir->d = list_first_entry(&sdir->h->list_data, + struct bpkfs_handle_data, list); + } + + return dir; +} + +static struct dirent *bpkfs_readdir(struct device_d *dev, DIR *dir) +{ + struct bpkfs_handle *priv = dev->priv; + struct somfy_readdir *sdir = dir->priv; + struct bpkfs_handle_hw *h = sdir->h; + struct bpkfs_handle_data *d = sdir->d; + + if (!h) + return NULL; + + if (!d) { + if (&h->list_hw_id == &priv->list) + return NULL; + + strcpy(dir->d.d_name, h->name); + sdir->h = list_entry(h->list_hw_id.next, struct bpkfs_handle_hw, list_hw_id); + } else { + if (&d->list == &h->list_data) + return NULL; + + strcpy(dir->d.d_name, d->name); + sdir->d = list_entry(d->list.next, struct bpkfs_handle_data, list); + } + + return &dir->d; +} + +static int bpkfs_closedir(struct device_d *dev, DIR *dir) +{ + struct somfy_readdir *sdir = dir->priv; + + free(sdir); + return 0; +} + +static int bpkfs_stat(struct device_d *dev, const char *filename, struct stat *s) +{ + struct bpkfs_handle *priv = dev->priv; + struct bpkfs_handle_data *d; + struct bpkfs_handle_hw *h; + char *dir, *file; + int ret = -EINVAL; + char *tmp = xstrdup(filename); + char *tmp2 = xstrdup(filename); + + dir = dirname(tmp); + + if (filename[0] == '/') + filename++; + + if (dir[0] == '/') + dir++; + + if (!strlen(dir)) { + h = bpkfs_hw_id_get_by_name(priv, filename); + if (!h) + goto out; + + s->st_size = strlen(filename); + s->st_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO; + ret = 0; + goto out; + } + h = bpkfs_hw_id_get_by_name(priv, dir); + if (!h) + goto out; + + file = basename(tmp2); + d = bpkfs_data_get_by_name(h, file); + if (!d) + goto out; + + s->st_size = d->size; + s->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO; + + ret = 0; + +out: + free(tmp); + free(tmp2); + return ret; +} + +static void bpkfs_remove_data(struct bpkfs_handle_hw *h) +{ + struct bpkfs_handle_data *d, *tmp; + + list_for_each_entry_safe(d, tmp, &h->list_data, list) { + free(d->name); + free(d); + } +} + +static void bpkfs_remove(struct device_d *dev) +{ + struct bpkfs_handle *priv = dev->priv; + struct bpkfs_handle_hw *h, *tmp; + + list_for_each_entry_safe(h, tmp, &priv->list, list_hw_id) { + bpkfs_remove_data(h); + free(h->name); + free(h); + } + + free(priv); +} + +static int bpkfs_probe(struct device_d *dev) +{ + struct fs_device_d *fsdev = dev_to_fs_device(dev); + struct bpkfs_handle *priv; + struct bpkfs_header *header; + struct bpkfs_data_header data_header; + int ret = 0; + uint32_t checksum, crc; + uint64_t size; + int i; + size_t offset = 0; + char *buf; + int fd; + + priv = xzalloc(sizeof(struct bpkfs_handle)); + INIT_LIST_HEAD(&priv->list); + buf = xmalloc(2048); + dev->priv = priv; + + priv->filename = fsdev->backingstore; + dev_dbg(dev, "mount: %s\n", fsdev->backingstore); + + fd = open(fsdev->backingstore, O_RDONLY); + if (fd < 0) { + ret = fd; + goto err; + } + + header = &priv->header; + + ret = read(fd, header, sizeof(*header)); + if (ret < 0) { + dev_err(dev, "could not read: %s (ret = %d)\n", errno_str(), ret); + goto err; + } + + dev_dbg(dev, "header.magic = 0x%x\n", be32_to_cpu(header->magic)); + dev_dbg(dev, "header.version = 0x%x\n", be32_to_cpu(header->version)); + dev_dbg(dev, "header.crc = 0x%x\n", be32_to_cpu(header->crc)); + dev_dbg(dev, "header.size = %llu\n", be64_to_cpu(header->size)); + dev_dbg(dev, "header.spare = %llu\n", be64_to_cpu(header->spare)); + + size = be64_to_cpu(header->size); + offset += sizeof(*header); + size -= sizeof(*header); + + checksum = be32_to_cpu(header->crc); + header->crc = 0; + + crc = crc32(0, header, sizeof(*header)); + + for (i = 0; size; i++) { + struct bpkfs_handle_data *d; + struct bpkfs_handle_hw *h; + const char *type; + + ret = read(fd, &data_header, sizeof(data_header)); + if (ret < 0) { + dev_err(dev, "could not read: %s\n", errno_str()); + goto err; + } else if (ret == 0) { + dev_err(dev, "EOF: to_read %llu\n", size); + goto err; + } + + d = xzalloc(sizeof(*d)); + + crc = crc32(crc, &data_header, sizeof(data_header)); + offset += sizeof(data_header); + size -= sizeof(data_header); + + d->type = be32_to_cpu(data_header.type); + d->hw_id = be32_to_cpu(data_header.hw_id); + d->size = be64_to_cpu(data_header.size); + d->offset = offset; + d->crc = be32_to_cpu(data_header.crc); + type = bpkfs_type_to_str(d->type); + + h = bpkfs_get_or_add_hw_id(priv, d->hw_id); + + if (!type) { + type = "unknown"; + d->name = asprintf("%s_%08x", type, d->type); + } else { + d->name = xstrdup(type); + } + + dev_dbg(dev, "%d: type = 0x%x => %s\n", i, d->type, d->name); + dev_dbg(dev, "%d: size = %llu\n", i, d->size); + dev_dbg(dev, "%d: offset = %d\n", i, d->offset); + + dev_dbg(dev, "%d: hw_id = 0x%x => %s\n", i, h->hw_id, h->name); + + offset += d->size; + size -= d->size; + + if (bpkfs_get_by_type(priv, d->hw_id, d->type)) { + dev_info(dev, "ignore data %d type %s already present, ignored\n", + i, type); + free(d); + continue; + } + + list_add_tail(&d->list, &h->list_data); + priv->nb_data_entries++; + + ret = lseek(fd, d->size, SEEK_CUR); + if (ret < 0) { + dev_err(dev, "could not seek: %s\n", errno_str()); + goto err; + } + + type = d->name; + d = xzalloc(sizeof(*d)); + d->type = be32_to_cpu(data_header.type); + d->name = asprintf("%s.crc", type); + d->type |= (1 << 31); + d->size = 8; + sprintf(d->data, "%08x", be32_to_cpu(data_header.crc)); + list_add_tail(&d->list, &h->list_data); + } + + if (crc != checksum) { + dev_err(dev, "invalid crc (0x%x != 0x%x)\n", checksum, crc); + goto err; + } + + close(fd); + free(buf); + + return 0; + +err: + close(fd); + free(buf); + bpkfs_remove(dev); + + return ret; +} + +static struct fs_driver_d bpkfs_driver = { + .open = bpkfs_open, + .close = bpkfs_close, + .read = bpkfs_read, + .lseek = bpkfs_lseek, + .opendir = bpkfs_opendir, + .readdir = bpkfs_readdir, + .closedir = bpkfs_closedir, + .stat = bpkfs_stat, + .flags = 0, + .type = filetype_bpk, + .drv = { + .probe = bpkfs_probe, + .remove = bpkfs_remove, + .name = "bpkfs", + } +}; + +static int bpkfs_init(void) +{ + return register_fs_driver(&bpkfs_driver); +} +coredevice_initcall(bpkfs_init); diff --git a/include/bpkfs.h b/include/bpkfs.h new file mode 100644 index 0000000..ccb56c1 --- /dev/null +++ b/include/bpkfs.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013 Jean-Christophe PLAGNIOL-VILLARD + * + * under GPLv2 only + */ + +#ifndef __BPKFS_H__ +#define __BPKFS_H__ + +#include +#include + +#define BPKFS_TYPE_BL 0x50424c00 +#define BPKFS_TYPE_BLV 0x50424c56 +#define BPKFS_TYPE_DSC 0x44456343 +#define BPKFS_TYPE_KER 0x504b4552 +#define BPKFS_TYPE_RFS 0x50524653 +#define BPKFS_TYPE_FMV 0x46575600 + +struct bpkfs_header { + uint32_t magic; + uint32_t version; + uint64_t size; + uint32_t crc; + uint64_t spare; +} __attribute__ ((packed)) ; + +struct bpkfs_data_header { + uint32_t type; + uint64_t size; + uint32_t crc; + uint32_t hw_id; + uint32_t spare; +} __attribute__ ((packed)) ; + +struct bpkfs_handle_hw { + char *name; + uint32_t hw_id; + + struct list_head list_data; + struct list_head list_hw_id; +}; + +struct bpkfs_handle_data { + char *name; + uint32_t type; + uint32_t hw_id; + uint64_t size; + + int fd; + size_t offset; /* offset in the image */ + size_t pos; /* pos in the data */ + uint32_t crc; + + char data[8]; + + struct list_head list; +}; + +struct bpkfs_handle { + struct bpkfs_header header; + int nb_data_entries; + char *filename; + + struct list_head list; +}; + +#endif /* __BPKFS_H__ */ -- 1.8.4.rc3 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox