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

Alistair Popple alistair at popple.id.au
Mon Jun 22 19:24:15 AEST 2015


I'm assuming the only changes here were to address my comments from last time,
which it looks like you have done so:

Reviewed-By: Alistair Popple <alistair at popple.id.au>

On Tue, 16 Jun 2015 19:01:07 Cyril Bur wrote:
> 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 */
> 



More information about the Skiboot mailing list