aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libflash/file.c294
-rw-r--r--libflash/file.h45
2 files changed, 339 insertions, 0 deletions
diff --git a/libflash/file.c b/libflash/file.c
new file mode 100644
index 0000000..df11271
--- /dev/null
+++ b/libflash/file.c
@@ -0,0 +1,294 @@
+/* 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.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.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 */