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

Alistair Popple alistair at popple.id.au
Fri Jun 12 12:39:01 AEST 2015


Cyril,

Comments below.

Regards,

Alistair

On Fri, 5 Jun 2015 14:11:28 Cyril Bur wrote:

<snip>

> +
> +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;
> +}
> +
> +static int file_erase(struct blocklevel_device *bl, uint32_t dst, uint32_t len)

Probably worth a comment here on what it actually means to "erase" a file.

> +{
> +	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;
> +
> +	path = strdup("/proc/self/fd/");
> +	if (!path)
> +		return FLASH_ERR_MALLOC_FAILED;
> +
> +	/* strlen() + 1 to count the \0 */
> +	len = snprintf(path, strlen(path) + 1, "%s%d", path, file_data->fd);
> +	while (len > 0 && len > strlen(path)) {
> +		len++; /* count the /0 */
> +		path = realloc(path, len);
> +		if (!path)
> +			return FLASH_ERR_MALLOC_FAILED;
> +		len = snprintf(path, len, "%s%d", path, file_data->fd);
> +	}

What am I missing here? As this won't be used in skiboot couldn't you just do
asprintf(path, len, "/proc/self/fd/%d", path, file_data->fd)?

> +
> +	if (len <= 0) {
> +		free(path);
> +		return FLASH_ERR_VERIFY_FAILURE;
> +	}
> +
> +	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;
> +
> +	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..1ad9129
> --- /dev/null
> +++ b/libflash/file.h
> @@ -0,0 +1,43 @@
> +/* 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
> +
> +/*
> + * 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