fs: add fs_readdir()
Needed to support efi file protocol. The fallback.efi loader wants to be able to read the contents of the /EFI directory to find an OS to boot. Modelled after POSIX opendir()/readdir()/closedir(). Unlike the other fs APIs, this is stateful (ie. state is held in the FS_DIR "directory stream"), to avoid re-traversing of the directory structure at each step. The directory stream must be released with closedir() when it is no longer needed. Signed-off-by: Rob Clark <robdclark@gmail.com> Reviewed-by: Łukasz Majewski <lukma@denx.de> Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
		
							parent
							
								
									8eafae209c
								
							
						
					
					
						commit
						4bbcc965f9
					
				
							
								
								
									
										31
									
								
								disk/part.c
								
								
								
								
							
							
						
						
									
										31
									
								
								disk/part.c
								
								
								
								
							|  | @ -331,6 +331,24 @@ int part_get_info(struct blk_desc *dev_desc, int part, | ||||||
| 	return -1; | 	return -1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | int part_get_info_whole_disk(struct blk_desc *dev_desc, disk_partition_t *info) | ||||||
|  | { | ||||||
|  | 	info->start = 0; | ||||||
|  | 	info->size = dev_desc->lba; | ||||||
|  | 	info->blksz = dev_desc->blksz; | ||||||
|  | 	info->bootable = 0; | ||||||
|  | 	strcpy((char *)info->type, BOOT_PART_TYPE); | ||||||
|  | 	strcpy((char *)info->name, "Whole Disk"); | ||||||
|  | #if CONFIG_IS_ENABLED(PARTITION_UUIDS) | ||||||
|  | 	info->uuid[0] = 0; | ||||||
|  | #endif | ||||||
|  | #ifdef CONFIG_PARTITION_TYPE_GUID | ||||||
|  | 	info->type_guid[0] = 0; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int blk_get_device_by_str(const char *ifname, const char *dev_hwpart_str, | int blk_get_device_by_str(const char *ifname, const char *dev_hwpart_str, | ||||||
| 			  struct blk_desc **dev_desc) | 			  struct blk_desc **dev_desc) | ||||||
| { | { | ||||||
|  | @ -523,18 +541,7 @@ int blk_get_device_part_str(const char *ifname, const char *dev_part_str, | ||||||
| 
 | 
 | ||||||
| 		(*dev_desc)->log2blksz = LOG2((*dev_desc)->blksz); | 		(*dev_desc)->log2blksz = LOG2((*dev_desc)->blksz); | ||||||
| 
 | 
 | ||||||
| 		info->start = 0; | 		part_get_info_whole_disk(*dev_desc, info); | ||||||
| 		info->size = (*dev_desc)->lba; |  | ||||||
| 		info->blksz = (*dev_desc)->blksz; |  | ||||||
| 		info->bootable = 0; |  | ||||||
| 		strcpy((char *)info->type, BOOT_PART_TYPE); |  | ||||||
| 		strcpy((char *)info->name, "Whole Disk"); |  | ||||||
| #if CONFIG_IS_ENABLED(PARTITION_UUIDS) |  | ||||||
| 		info->uuid[0] = 0; |  | ||||||
| #endif |  | ||||||
| #ifdef CONFIG_PARTITION_TYPE_GUID |  | ||||||
| 		info->type_guid[0] = 0; |  | ||||||
| #endif |  | ||||||
| 
 | 
 | ||||||
| 		ret = 0; | 		ret = 0; | ||||||
| 		goto cleanup; | 		goto cleanup; | ||||||
|  |  | ||||||
							
								
								
									
										104
									
								
								fs/fs.c
								
								
								
								
							
							
						
						
									
										104
									
								
								fs/fs.c
								
								
								
								
							|  | @ -21,6 +21,7 @@ | ||||||
| DECLARE_GLOBAL_DATA_PTR; | DECLARE_GLOBAL_DATA_PTR; | ||||||
| 
 | 
 | ||||||
| static struct blk_desc *fs_dev_desc; | static struct blk_desc *fs_dev_desc; | ||||||
|  | static int fs_dev_part; | ||||||
| static disk_partition_t fs_partition; | static disk_partition_t fs_partition; | ||||||
| static int fs_type = FS_TYPE_ANY; | static int fs_type = FS_TYPE_ANY; | ||||||
| 
 | 
 | ||||||
|  | @ -69,6 +70,12 @@ static inline int fs_uuid_unsupported(char *uuid_str) | ||||||
| 	return -1; | 	return -1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline int fs_opendir_unsupported(const char *filename, | ||||||
|  | 					 struct fs_dir_stream **dirs) | ||||||
|  | { | ||||||
|  | 	return -EACCES; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| struct fstype_info { | struct fstype_info { | ||||||
| 	int fstype; | 	int fstype; | ||||||
| 	char *name; | 	char *name; | ||||||
|  | @ -92,6 +99,20 @@ struct fstype_info { | ||||||
| 		     loff_t len, loff_t *actwrite); | 		     loff_t len, loff_t *actwrite); | ||||||
| 	void (*close)(void); | 	void (*close)(void); | ||||||
| 	int (*uuid)(char *uuid_str); | 	int (*uuid)(char *uuid_str); | ||||||
|  | 	/*
 | ||||||
|  | 	 * Open a directory stream.  On success return 0 and directory | ||||||
|  | 	 * stream pointer via 'dirsp'.  On error, return -errno.  See | ||||||
|  | 	 * fs_opendir(). | ||||||
|  | 	 */ | ||||||
|  | 	int (*opendir)(const char *filename, struct fs_dir_stream **dirsp); | ||||||
|  | 	/*
 | ||||||
|  | 	 * Read next entry from directory stream.  On success return 0 | ||||||
|  | 	 * and directory entry pointer via 'dentp'.  On error return | ||||||
|  | 	 * -errno.  See fs_readdir(). | ||||||
|  | 	 */ | ||||||
|  | 	int (*readdir)(struct fs_dir_stream *dirs, struct fs_dirent **dentp); | ||||||
|  | 	/* see fs_closedir() */ | ||||||
|  | 	void (*closedir)(struct fs_dir_stream *dirs); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static struct fstype_info fstypes[] = { | static struct fstype_info fstypes[] = { | ||||||
|  | @ -112,6 +133,7 @@ static struct fstype_info fstypes[] = { | ||||||
| 		.write = fs_write_unsupported, | 		.write = fs_write_unsupported, | ||||||
| #endif | #endif | ||||||
| 		.uuid = fs_uuid_unsupported, | 		.uuid = fs_uuid_unsupported, | ||||||
|  | 		.opendir = fs_opendir_unsupported, | ||||||
| 	}, | 	}, | ||||||
| #endif | #endif | ||||||
| #ifdef CONFIG_FS_EXT4 | #ifdef CONFIG_FS_EXT4 | ||||||
|  | @ -131,6 +153,7 @@ static struct fstype_info fstypes[] = { | ||||||
| 		.write = fs_write_unsupported, | 		.write = fs_write_unsupported, | ||||||
| #endif | #endif | ||||||
| 		.uuid = ext4fs_uuid, | 		.uuid = ext4fs_uuid, | ||||||
|  | 		.opendir = fs_opendir_unsupported, | ||||||
| 	}, | 	}, | ||||||
| #endif | #endif | ||||||
| #ifdef CONFIG_SANDBOX | #ifdef CONFIG_SANDBOX | ||||||
|  | @ -146,6 +169,7 @@ static struct fstype_info fstypes[] = { | ||||||
| 		.read = fs_read_sandbox, | 		.read = fs_read_sandbox, | ||||||
| 		.write = fs_write_sandbox, | 		.write = fs_write_sandbox, | ||||||
| 		.uuid = fs_uuid_unsupported, | 		.uuid = fs_uuid_unsupported, | ||||||
|  | 		.opendir = fs_opendir_unsupported, | ||||||
| 	}, | 	}, | ||||||
| #endif | #endif | ||||||
| #ifdef CONFIG_CMD_UBIFS | #ifdef CONFIG_CMD_UBIFS | ||||||
|  | @ -161,6 +185,7 @@ static struct fstype_info fstypes[] = { | ||||||
| 		.read = ubifs_read, | 		.read = ubifs_read, | ||||||
| 		.write = fs_write_unsupported, | 		.write = fs_write_unsupported, | ||||||
| 		.uuid = fs_uuid_unsupported, | 		.uuid = fs_uuid_unsupported, | ||||||
|  | 		.opendir = fs_opendir_unsupported, | ||||||
| 	}, | 	}, | ||||||
| #endif | #endif | ||||||
| 	{ | 	{ | ||||||
|  | @ -175,6 +200,7 @@ static struct fstype_info fstypes[] = { | ||||||
| 		.read = fs_read_unsupported, | 		.read = fs_read_unsupported, | ||||||
| 		.write = fs_write_unsupported, | 		.write = fs_write_unsupported, | ||||||
| 		.uuid = fs_uuid_unsupported, | 		.uuid = fs_uuid_unsupported, | ||||||
|  | 		.opendir = fs_opendir_unsupported, | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -226,6 +252,31 @@ int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype) | ||||||
| 		if (!fs_dev_desc && !info->null_dev_desc_ok) | 		if (!fs_dev_desc && !info->null_dev_desc_ok) | ||||||
| 			continue; | 			continue; | ||||||
| 
 | 
 | ||||||
|  | 		if (!info->probe(fs_dev_desc, &fs_partition)) { | ||||||
|  | 			fs_type = info->fstype; | ||||||
|  | 			fs_dev_part = part; | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* set current blk device w/ blk_desc + partition # */ | ||||||
|  | int fs_set_blk_dev_with_part(struct blk_desc *desc, int part) | ||||||
|  | { | ||||||
|  | 	struct fstype_info *info; | ||||||
|  | 	int ret, i; | ||||||
|  | 
 | ||||||
|  | 	if (part >= 1) | ||||||
|  | 		ret = part_get_info(desc, part, &fs_partition); | ||||||
|  | 	else | ||||||
|  | 		ret = part_get_info_whole_disk(desc, &fs_partition); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 	fs_dev_desc = desc; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) { | ||||||
| 		if (!info->probe(fs_dev_desc, &fs_partition)) { | 		if (!info->probe(fs_dev_desc, &fs_partition)) { | ||||||
| 			fs_type = info->fstype; | 			fs_type = info->fstype; | ||||||
| 			return 0; | 			return 0; | ||||||
|  | @ -334,6 +385,59 @@ int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len, | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | struct fs_dir_stream *fs_opendir(const char *filename) | ||||||
|  | { | ||||||
|  | 	struct fstype_info *info = fs_get_info(fs_type); | ||||||
|  | 	struct fs_dir_stream *dirs = NULL; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = info->opendir(filename, &dirs); | ||||||
|  | 	fs_close(); | ||||||
|  | 	if (ret) { | ||||||
|  | 		errno = -ret; | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	dirs->desc = fs_dev_desc; | ||||||
|  | 	dirs->part = fs_dev_part; | ||||||
|  | 
 | ||||||
|  | 	return dirs; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct fs_dirent *fs_readdir(struct fs_dir_stream *dirs) | ||||||
|  | { | ||||||
|  | 	struct fstype_info *info; | ||||||
|  | 	struct fs_dirent *dirent; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	fs_set_blk_dev_with_part(dirs->desc, dirs->part); | ||||||
|  | 	info = fs_get_info(fs_type); | ||||||
|  | 
 | ||||||
|  | 	ret = info->readdir(dirs, &dirent); | ||||||
|  | 	fs_close(); | ||||||
|  | 	if (ret) { | ||||||
|  | 		errno = -ret; | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return dirent; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void fs_closedir(struct fs_dir_stream *dirs) | ||||||
|  | { | ||||||
|  | 	struct fstype_info *info; | ||||||
|  | 
 | ||||||
|  | 	if (!dirs) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	fs_set_blk_dev_with_part(dirs->desc, dirs->part); | ||||||
|  | 	info = fs_get_info(fs_type); | ||||||
|  | 
 | ||||||
|  | 	info->closedir(dirs); | ||||||
|  | 	fs_close(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], | int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], | ||||||
| 		int fstype) | 		int fstype) | ||||||
| { | { | ||||||
|  |  | ||||||
							
								
								
									
										67
									
								
								include/fs.h
								
								
								
								
							
							
						
						
									
										67
									
								
								include/fs.h
								
								
								
								
							|  | @ -26,6 +26,17 @@ | ||||||
|  */ |  */ | ||||||
| int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype); | int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype); | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * fs_set_blk_dev_with_part - Set current block device + partition | ||||||
|  |  * | ||||||
|  |  * Similar to fs_set_blk_dev(), but useful for cases where you already | ||||||
|  |  * know the blk_desc and part number. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns non-zero if invalid partition or error accessing the disk. | ||||||
|  |  */ | ||||||
|  | int fs_set_blk_dev_with_part(struct blk_desc *desc, int part); | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Print the list of files on the partition previously set by fs_set_blk_dev(), |  * Print the list of files on the partition previously set by fs_set_blk_dev(), | ||||||
|  * in directory "dirname". |  * in directory "dirname". | ||||||
|  | @ -78,6 +89,62 @@ int fs_read(const char *filename, ulong addr, loff_t offset, loff_t len, | ||||||
| int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len, | int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len, | ||||||
| 	     loff_t *actwrite); | 	     loff_t *actwrite); | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * Directory entry types, matches the subset of DT_x in posix readdir() | ||||||
|  |  * which apply to u-boot. | ||||||
|  |  */ | ||||||
|  | #define FS_DT_DIR  4         /* directory */ | ||||||
|  | #define FS_DT_REG  8         /* regular file */ | ||||||
|  | #define FS_DT_LNK  10        /* symbolic link */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * A directory entry, returned by fs_readdir().  Returns information | ||||||
|  |  * about the file/directory at the current directory entry position. | ||||||
|  |  */ | ||||||
|  | struct fs_dirent { | ||||||
|  | 	unsigned type;       /* one of FS_DT_x (not a mask) */ | ||||||
|  | 	loff_t size;         /* size in bytes */ | ||||||
|  | 	char name[256]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* Note: fs_dir_stream should be treated as opaque to the user of fs layer */ | ||||||
|  | struct fs_dir_stream { | ||||||
|  | 	/* private to fs. layer: */ | ||||||
|  | 	struct blk_desc *desc; | ||||||
|  | 	int part; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * fs_opendir - Open a directory | ||||||
|  |  * | ||||||
|  |  * @filename: the path to directory to open | ||||||
|  |  * @return a pointer to the directory stream or NULL on error and errno | ||||||
|  |  *    set appropriately | ||||||
|  |  */ | ||||||
|  | struct fs_dir_stream *fs_opendir(const char *filename); | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * fs_readdir - Read the next directory entry in the directory stream. | ||||||
|  |  * | ||||||
|  |  * Works in an analogous way to posix readdir().  The previously returned | ||||||
|  |  * directory entry is no longer valid after calling fs_readdir() again. | ||||||
|  |  * After fs_closedir() is called, the returned directory entry is no | ||||||
|  |  * longer valid. | ||||||
|  |  * | ||||||
|  |  * @dirs: the directory stream | ||||||
|  |  * @return the next directory entry (only valid until next fs_readdir() or | ||||||
|  |  *    fs_closedir() call, do not attempt to free()) or NULL if the end of | ||||||
|  |  *    the directory is reached. | ||||||
|  |  */ | ||||||
|  | struct fs_dirent *fs_readdir(struct fs_dir_stream *dirs); | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * fs_closedir - close a directory stream | ||||||
|  |  * | ||||||
|  |  * @dirs: the directory stream | ||||||
|  |  */ | ||||||
|  | void fs_closedir(struct fs_dir_stream *dirs); | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Common implementation for various filesystem commands, optionally limited |  * Common implementation for various filesystem commands, optionally limited | ||||||
|  * to a specific filesystem type via the fstype parameter. |  * to a specific filesystem type via the fstype parameter. | ||||||
|  |  | ||||||
|  | @ -98,6 +98,12 @@ int host_get_dev_err(int dev, struct blk_desc **blk_devp); | ||||||
| 
 | 
 | ||||||
| /* disk/part.c */ | /* disk/part.c */ | ||||||
| int part_get_info(struct blk_desc *dev_desc, int part, disk_partition_t *info); | int part_get_info(struct blk_desc *dev_desc, int part, disk_partition_t *info); | ||||||
|  | /**
 | ||||||
|  |  * part_get_info_whole_disk() - get partition info for the special case of | ||||||
|  |  * a partition occupying the entire disk. | ||||||
|  |  */ | ||||||
|  | int part_get_info_whole_disk(struct blk_desc *dev_desc, disk_partition_t *info); | ||||||
|  | 
 | ||||||
| void part_print(struct blk_desc *dev_desc); | void part_print(struct blk_desc *dev_desc); | ||||||
| void part_init(struct blk_desc *dev_desc); | void part_init(struct blk_desc *dev_desc); | ||||||
| void dev_print(struct blk_desc *dev_desc); | void dev_print(struct blk_desc *dev_desc); | ||||||
|  | @ -203,6 +209,9 @@ static inline struct blk_desc *mg_disk_get_dev(int dev) { return NULL; } | ||||||
| 
 | 
 | ||||||
| static inline int part_get_info(struct blk_desc *dev_desc, int part, | static inline int part_get_info(struct blk_desc *dev_desc, int part, | ||||||
| 				disk_partition_t *info) { return -1; } | 				disk_partition_t *info) { return -1; } | ||||||
|  | static inline int part_get_info_whole_disk(struct blk_desc *dev_desc, | ||||||
|  | 					   disk_partition_t *info) | ||||||
|  | { return -1; } | ||||||
| static inline void part_print(struct blk_desc *dev_desc) {} | static inline void part_print(struct blk_desc *dev_desc) {} | ||||||
| static inline void part_init(struct blk_desc *dev_desc) {} | static inline void part_init(struct blk_desc *dev_desc) {} | ||||||
| static inline void dev_print(struct blk_desc *dev_desc) {} | static inline void dev_print(struct blk_desc *dev_desc) {} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue