[Skiboot] [PATCH V2 4/7] libflash/file: Add a file access backend to for the blocklevel interface.

Cyril Bur cyril.bur at au1.ibm.com
Tue Jun 16 19:01:07 AEST 2015


Currently libflash reads from files by using the dodgy file_flash.c which
implements a very flash hardware themed interface but subverts it to read,
write and erase from a file descriptor.

With the introduction of this file backend, this will no longer be needed.

Signed-off-by: Cyril Bur <cyril.bur at au1.ibm.com>
---
 libflash/file.c | 293 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 libflash/file.h |  45 +++++++++
 2 files changed, 338 insertions(+)
 create mode 100644 libflash/file.c
 create mode 100644 libflash/file.h

diff --git a/libflash/file.c b/libflash/file.c
new file mode 100644
index 0000000..f4764d5
--- /dev/null
+++ b/libflash/file.c
@@ -0,0 +1,293 @@
+/* Copyright 2013-2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 	http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <ccan/container_of/container_of.h>
+
+#include <mtd/mtd-abi.h>
+
+#include "libflash.h"
+#include "blocklevel.h"
+
+struct file_data {
+	int fd;
+	char *name;
+	struct blocklevel_device bl;
+};
+
+static int file_read(struct blocklevel_device *bl, uint32_t pos, void *buf, uint32_t len)
+{
+	struct file_data *file_data = container_of(bl, struct file_data, bl);
+	int count = 0;
+	int rc;
+	rc = lseek(file_data->fd, pos, SEEK_SET);
+	/* errno should remain set */
+	if (rc != pos)
+		return FLASH_ERR_PARM_ERROR;
+
+	while (count < len) {
+		rc = read(file_data->fd, buf, len);
+		/* errno should remain set */
+		if (rc == -1)
+			return FLASH_ERR_BAD_READ;
+		count += rc;
+	}
+
+	return 0;
+}
+
+static int file_write(struct blocklevel_device *bl, uint32_t dst, const void *src,
+		uint32_t len)
+{
+	struct file_data *file_data = container_of(bl, struct file_data, bl);
+	int count = 0;
+	int rc;
+
+	rc = lseek(file_data->fd, dst, SEEK_SET);
+	/* errno should remain set */
+	if (rc == -1)
+		return FLASH_ERR_PARM_ERROR;
+
+	while (count < len) {
+		rc = write(file_data->fd, src, len);
+		/* errno should remain set */
+		if (rc == -1)
+			return FLASH_ERR_VERIFY_FAILURE;
+		count += rc;
+	}
+
+	return 0;
+}
+
+/*
+ * Due to to the fact these interfaces are ultimately supposed to deal with
+ * flash, an erase function must be implemented even when the flash images
+ * are backed by regular files.
+ * Also, erasing flash leaves all the bits set to 1. This may be expected
+ * by higher level functions so this function should also emulate that
+ */
+static int file_erase(struct blocklevel_device *bl, uint32_t dst, uint32_t len)
+{
+	unsigned long long int d = ULLONG_MAX;
+	int i = 0;
+	int rc = 0;
+
+	while (len - i > 0) {
+		rc = file_write(bl, dst + i, &d, len - i > sizeof(d) ? sizeof(d) : len - i);
+		if (rc)
+			return rc;
+		i += sizeof(d);
+	}
+
+	return 0;
+}
+
+static int mtd_erase(struct blocklevel_device *bl, uint32_t dst, uint32_t len)
+{
+	struct file_data *file_data = container_of(bl, struct file_data, bl);
+	struct erase_info_user erase_info = {
+		.start = dst,
+		.length = len
+	};
+
+	return ioctl(file_data->fd, MEMERASE, &erase_info) == -1 ? -1 : 0;
+}
+
+static int get_info_name(struct file_data *file_data, char **name)
+{
+	char *path, *lpath;
+	int len;
+	struct stat st;
+
+	if (asprintf(&path, "/proc/self/fd/%d", file_data->fd) == -1)
+		return FLASH_ERR_MALLOC_FAILED;
+
+	if (lstat(path, &st)) {
+		free(path);
+		return FLASH_ERR_PARM_ERROR;
+	}
+
+	lpath = malloc(st.st_size + 1);
+	if (!lpath) {
+		free(path);
+		return FLASH_ERR_MALLOC_FAILED;
+	}
+
+	len = readlink(path, lpath, st.st_size +1);
+	if (len == -1) {
+		free(path);
+		free(lpath);
+		return FLASH_ERR_PARM_ERROR;
+	}
+	lpath[len] = '\0';
+
+	*name = lpath;
+
+	free(path);
+	return 0;
+}
+
+
+static int mtd_get_info(struct blocklevel_device *bl, const char **name, uint32_t *total_size,
+		uint32_t *erase_granule)
+{
+	struct file_data *file_data = container_of(bl, struct file_data, bl);
+	struct mtd_info_user mtd_info;
+	int rc;
+
+	if (ioctl(file_data->fd, MEMGETINFO, &mtd_info) == -1)
+			return FLASH_ERR_BAD_READ;
+
+	if (total_size)
+		*total_size = mtd_info.size;
+
+	if (erase_granule)
+		*erase_granule = mtd_info.erasesize;
+
+	if (name) {
+		rc = get_info_name(file_data, &(file_data->name));
+		if (rc)
+			return rc;
+		*name = file_data->name;
+	}
+
+	return 0;
+}
+
+static int file_get_info(struct blocklevel_device *bl, const char **name, uint32_t *total_size,
+		uint32_t *erase_granule)
+{
+	struct file_data *file_data = container_of(bl, struct file_data, bl);
+	struct stat st;
+	int rc;
+
+	if (fstat(file_data->fd, &st))
+		return FLASH_ERR_PARM_ERROR;
+
+	if (total_size)
+		*total_size = st.st_size;
+
+	if (erase_granule)
+		*erase_granule = 1;
+
+	if (name) {
+		rc = get_info_name(file_data, &(file_data->name));
+		if (rc)
+			return rc;
+		*name = file_data->name;
+	}
+
+	return 0;
+}
+
+int file_init(int fd, struct blocklevel_device **bl)
+{
+	struct file_data *file_data;
+	struct stat sbuf;
+
+	if (!bl)
+		return FLASH_ERR_PARM_ERROR;
+
+	*bl = NULL;
+
+	file_data = malloc(sizeof(struct file_data));
+	if (!file_data)
+		return FLASH_ERR_MALLOC_FAILED;
+	memset(file_data, 0, sizeof(*file_data));
+
+	file_data->fd = fd;
+	file_data->bl.read = &file_read;
+	file_data->bl.write = &file_write;
+	file_data->bl.erase = &file_erase;
+	file_data->bl.get_info = &file_get_info;
+	file_data->bl.erase_mask = 0;
+	/*
+	 * Unfortunately not all file descriptors are created equal...
+	 * Here we check to see if the file descriptor is to an MTD device, in
+	 * which case we have to erase and get the size of it differently.
+	 */
+	if (fstat(file_data->fd, &sbuf) == -1)
+		goto out;
+
+	/* Won't be able to handle other than MTD devices for now */
+	if (S_ISCHR(sbuf.st_mode)) {
+		file_data->bl.erase = &mtd_erase;
+		file_data->bl.get_info = &mtd_get_info;
+		file_data->bl.flags = WRITE_NEED_ERASE;
+		mtd_get_info(&file_data->bl, NULL, NULL, &(file_data->bl.erase_mask));
+		file_data->bl.erase_mask--;
+	} else if (!S_ISREG(sbuf.st_mode)) {
+		/* If not a char device or a regular file something went wrong */
+		goto out;
+	}
+
+	*bl = &(file_data->bl);
+	return 0;
+out:
+	free(file_data);
+	return FLASH_ERR_PARM_ERROR;
+}
+
+int file_init_path(const char *path, int *r_fd, struct blocklevel_device **bl)
+{
+	int fd, rc;
+
+	if (!path || !bl)
+		return FLASH_ERR_PARM_ERROR;
+
+	fd = open(path, O_RDWR);
+	if (fd == -1)
+		return FLASH_ERR_PARM_ERROR;
+
+	rc = file_init(fd, bl);
+	if (rc)
+		close(fd);
+
+	if (r_fd)
+		*r_fd = fd;
+
+	return rc;
+}
+
+void file_exit(struct blocklevel_device *bl)
+{
+	struct file_data *file_data;
+	if (bl) {
+		file_data = container_of(bl, struct file_data, bl);
+		free(file_data->name);
+		free(file_data);
+	}
+}
+
+void file_exit_close(struct blocklevel_device *bl)
+{
+	struct file_data *file_data;
+	if (bl) {
+		file_data = container_of(bl, struct file_data, bl);
+		close(file_data->fd);
+		file_exit(bl);
+	}
+}
diff --git a/libflash/file.h b/libflash/file.h
new file mode 100644
index 0000000..a9a89fe
--- /dev/null
+++ b/libflash/file.h
@@ -0,0 +1,45 @@
+/* Copyright 2013-2015 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 	http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LIBFLASH_FILE_H
+#define __LIBFLASH_FILE_H
+
+#include "blocklevel.h"
+
+/*
+ * Blockevel functions created leave errno set on errors, as these calls
+ * often boil down to standard read() and write() calls, inspecting errno
+ * may prove useful
+ */
+
+int file_init(int fd, struct blocklevel_device **bl);
+void file_exit(struct blocklevel_device *bl);
+
+/*
+ * file_init_path() is a convenience wrapper which will open the path and call
+ * file_init(). The call to open happens with O_RDWR and no additional flags
+ * Because file_exit() doesn't close the file descriptor, file_init_path()
+ * makes it available.
+ */
+int file_init_path(const char *path, int *fd, struct blocklevel_device **bl);
+
+/*
+ * file_exit_close is a convenience wrapper which will close the open
+ * file descriptor and call file_exit().
+ */
+void file_exit_close(struct blocklevel_device *bl);
+
+#endif /* __LIBFLASH_FILE_H */
-- 
2.4.3



More information about the Skiboot mailing list