[U-Boot] [PATCH v2 2/8] fs/fat: introduce new director iterators

Łukasz Majewski lukma at denx.de
Sun Sep 3 15:08:05 UTC 2017


On 09/02/2017 06:37 PM, Rob Clark wrote:
> Untangle directory traversal into a simple iterator, to replace the
> existing multi-purpose do_fat_read_at() + get_dentfromdir().
> 
> Signed-off-by: Rob Clark <robdclark at gmail.com>

Reviewed-by: Łukasz Majewski <lukma at denx.de>

> ---
>   fs/fat/fat.c | 326 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 326 insertions(+)
> 
> diff --git a/fs/fat/fat.c b/fs/fat/fat.c
> index e1c0a15dc7..c72d6ca931 100644
> --- a/fs/fat/fat.c
> +++ b/fs/fat/fat.c
> @@ -1245,6 +1245,332 @@ exit:
>   	return ret;
>   }
>   
> +
> +/*
> + * Directory iterator, to simplify filesystem traversal
> + */
> +
> +typedef struct {
> +	fsdata    *fsdata;
> +	unsigned   cursect;
> +	dir_entry *dent;
> +	int        remaining;     /* remaining dent's in current cluster */
> +	int        last_cluster;
> +	int        is_root;
> +
> +	/* current iterator position values: */
> +	char       l_name[VFAT_MAXLEN_BYTES];
> +	char       s_name[14];
> +	char      *name;          /* l_name if there is one, else s_name */
> +
> +	u8         block[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN);
> +} fat_itr;
> +
> +static int fat_itr_isdir(fat_itr *itr);
> +
> +/**
> + * fat_itr_root() - initialize an iterator to start at the root
> + * directory
> + *
> + * @itr: iterator to initialize
> + * @fsdata: filesystem data for the partition
> + * @return 0 on success, else -errno
> + */
> +static int fat_itr_root(fat_itr *itr, fsdata *fsdata)
> +{
> +	if (get_fs_info(fsdata))
> +		return -ENXIO;
> +
> +	itr->fsdata = fsdata;
> +	itr->cursect = fsdata->rootdir_sect;
> +	itr->dent = NULL;
> +	itr->remaining = 0;
> +	itr->last_cluster = 0;
> +	itr->is_root = 1;
> +
> +	return 0;
> +}
> +
> +/**
> + * fat_itr_child() - initialize an iterator to descend into a sub-
> + * directory
> + *
> + * Initializes 'itr' to iterate the contents of the directory at
> + * the current cursor position of 'parent'.  It is an error to
> + * call this if the current cursor of 'parent' is pointing at a
> + * regular file.
> + *
> + * Note that 'itr' and 'parent' can be the same pointer if you do
> + * not need to preserve 'parent' after this call, which is useful
> + * for traversing directory structure to resolve a file/directory.
> + *
> + * @itr: iterator to initialize
> + * @parent: the iterator pointing at a directory entry in the
> + *    parent directory of the directory to iterate
> + */
> +static void fat_itr_child(fat_itr *itr, fat_itr *parent)
> +{
> +	fsdata *mydata = parent->fsdata;  /* for silly macros */
> +	unsigned clustnum = START(parent->dent);
> +
> +	assert(fat_itr_isdir(parent));
> +
> +	itr->fsdata = parent->fsdata;
> +	if (clustnum > 0) {
> +		itr->cursect = itr->fsdata->data_begin +
> +			(clustnum * itr->fsdata->clust_size);
> +	} else {
> +		itr->cursect = parent->fsdata->rootdir_sect;
> +	}
> +	itr->dent = NULL;
> +	itr->remaining = 0;
> +	itr->last_cluster = 0;
> +	itr->is_root = 0;
> +}
> +
> +static void *next_cluster(fat_itr *itr)
> +{
> +	fsdata *mydata = itr->fsdata;  /* for silly macros */
> +	int ret;
> +
> +	/* have we reached the end? */
> +	if (itr->last_cluster)
> +		return NULL;
> +
> +	debug("FAT read(sect=%d), clust_size=%d, DIRENTSPERBLOCK=%zd\n",
> +	      itr->cursect, itr->fsdata->clust_size, DIRENTSPERBLOCK);
> +
> +	/*
> +	 * NOTE: do_fat_read_at() had complicated logic to deal w/
> +	 * vfat names that span multiple clusters in the fat16 case,
> +	 * which get_dentfromdir() probably also needed (and was
> +	 * missing).  And not entirely sure what fat32 didn't have
> +	 * the same issue..  We solve that by only caring about one
> +	 * dent at a time and iteratively constructing the vfat long
> +	 * name.
> +	 */
> +	ret = disk_read(itr->cursect, itr->fsdata->clust_size,
> +			itr->block);
> +	if (ret < 0) {
> +		debug("Error: reading block\n");
> +		return NULL;
> +	}
> +
> +	if (itr->is_root && itr->fsdata->fatsize != 32) {
> +		itr->cursect++;
> +		if (itr->cursect - itr->fsdata->rootdir_sect >=
> +		    itr->fsdata->rootdir_size) {
> +			debug("cursect: 0x%x\n", itr->cursect);
> +			itr->last_cluster = 1;
> +		}
> +	} else {
> +		itr->cursect = get_fatent(itr->fsdata, itr->cursect);
> +		if (CHECK_CLUST(itr->cursect, itr->fsdata->fatsize)) {
> +			debug("cursect: 0x%x\n", itr->cursect);
> +			itr->last_cluster = 1;
> +		}
> +	}
> +
> +	return itr->block;
> +}
> +
> +static dir_entry *next_dent(fat_itr *itr)
> +{
> +	if (itr->remaining == 0) {
> +		struct dir_entry *dent = next_cluster(itr);
> +
> +		/* have we reached the last cluster? */
> +		if (!dent)
> +			return NULL;
> +
> +		itr->remaining = itr->fsdata->sect_size / sizeof(dir_entry) - 1;
> +		itr->dent = dent;
> +	} else {
> +		itr->remaining--;
> +		itr->dent++;
> +	}
> +
> +	/* have we reached the last valid entry? */
> +	if (itr->dent->name[0] == 0)
> +		return NULL;
> +
> +	return itr->dent;
> +}
> +
> +static dir_entry *extract_vfat_name(fat_itr *itr)
> +{
> +	struct dir_entry *dent = itr->dent;
> +	int seqn = itr->dent->name[0] & ~LAST_LONG_ENTRY_MASK;
> +	u8 chksum, alias_checksum = ((dir_slot *)dent)->alias_checksum;
> +	int n = 0;
> +
> +	while (seqn--) {
> +		char buf[13];
> +		int idx = 0;
> +
> +		slot2str((dir_slot *)dent, buf, &idx);
> +
> +		/* shift accumulated long-name up and copy new part in: */
> +		memmove(itr->l_name + idx, itr->l_name, n);
> +		memcpy(itr->l_name, buf, idx);
> +		n += idx;
> +
> +		dent = next_dent(itr);
> +		if (!dent)
> +			return NULL;
> +	}
> +
> +	itr->l_name[n] = '\0';
> +
> +	chksum = mkcksum(dent->name, dent->ext);
> +
> +	/* checksum mismatch could mean deleted file, etc.. skip it: */
> +	if (chksum != alias_checksum) {
> +		debug("** chksum=%x, alias_checksum=%x, l_name=%s, s_name=%8s.%3s\n",
> +		      chksum, alias_checksum, itr->l_name, dent->name, dent->ext);
> +		return NULL;
> +	}
> +
> +	return dent;
> +}
> +
> +/**
> + * fat_itr_next() - step to the next entry in a directory
> + *
> + * Must be called once on a new iterator before the cursor is valid.
> + *
> + * @itr: the iterator to iterate
> + * @return boolean, 1 if success or 0 if no more entries in the
> + *    current directory
> + */
> +static int fat_itr_next(fat_itr *itr)
> +{
> +	dir_entry *dent;
> +
> +	itr->name = NULL;
> +
> +	while (1) {
> +		dent = next_dent(itr);
> +		if (!dent)
> +			return 0;
> +
> +		if (dent->name[0] == DELETED_FLAG ||
> +		    dent->name[0] == aRING)
> +			continue;
> +
> +		if (dent->attr & ATTR_VOLUME) {
> +			if (vfat_enabled &&
> +			    (dent->attr & ATTR_VFAT) == ATTR_VFAT &&
> +			    (dent->name[0] & LAST_LONG_ENTRY_MASK)) {
> +				dent = extract_vfat_name(itr);
> +				if (!dent)
> +					continue;
> +				itr->name = itr->l_name;
> +				break;
> +			} else {
> +				/* Volume label or VFAT entry, skip */
> +				continue;
> +			}
> +		}
> +
> +		break;
> +	}
> +
> +	get_name(dent, itr->s_name);
> +	if (!itr->name)
> +		itr->name = itr->s_name;
> +
> +	return 1;
> +}
> +
> +/**
> + * fat_itr_isdir() - is current cursor position pointing to a directory
> + *
> + * @itr: the iterator
> + * @return true if cursor is at a directory
> + */
> +static int fat_itr_isdir(fat_itr *itr)
> +{
> +	return !!(itr->dent->attr & ATTR_DIR);
> +}
> +
> +/*
> + * Helpers:
> + */
> +
> +#define TYPE_FILE 0x1
> +#define TYPE_DIR  0x2
> +#define TYPE_ANY  (TYPE_FILE | TYPE_DIR)
> +
> +/**
> + * fat_itr_resolve() - traverse directory structure to resolve the
> + * requested path.
> + *
> + * Traverse directory structure to the requested path.  If the specified
> + * path is to a directory, this will descend into the directory and
> + * leave it iterator at the start of the directory.  If the path is to a
> + * file, it will leave the iterator in the parent directory with current
> + * cursor at file's entry in the directory.
> + *
> + * @itr: iterator initialized to root
> + * @path: the requested path
> + * @type: bitmask of allowable file types
> + * @return 0 on success or -errno
> + */
> +static int fat_itr_resolve(fat_itr *itr, const char *path, unsigned type)
> +{
> +	const char *next;
> +
> +	/* chomp any extra leading slashes: */
> +	while (path[0] && ISDIRDELIM(path[0]))
> +		path++;
> +
> +	/* are we at the end? */
> +	if (strlen(path) == 0) {
> +		if (!(type & TYPE_DIR))
> +			return -ENOENT;
> +		return 0;
> +	}
> +
> +	/* find length of next path entry: */
> +	next = path;
> +	while (next[0] && !ISDIRDELIM(next[0]))
> +		next++;
> +
> +	while (fat_itr_next(itr)) {
> +		int match = 0;
> +
> +		/* check both long and short name: */
> +		if (!strncasecmp(path, itr->name, next - path))
> +			match = 1;
> +		else if (itr->name != itr->s_name &&
> +			 !strncasecmp(path, itr->s_name, (next - path)))
> +			match = 1;
> +
> +		if (!match)
> +			continue;
> +
> +		if (fat_itr_isdir(itr)) {
> +			/* recurse into directory: */
> +			fat_itr_child(itr, itr);
> +			return fat_itr_resolve(itr, next, type);
> +		} else if (next[0]) {
> +			/*
> +			 * If next is not empty then we have a case
> +			 * like: /path/to/realfile/nonsense
> +			 */
> +			debug("bad trailing path: %s\n", next);
> +			return -ENOENT;
> +		} else if (!(type & TYPE_FILE)) {
> +			return -ENOTDIR;
> +		} else {
> +			return 0;
> +		}
> +	}
> +
> +	return -ENOENT;
> +}
> +
>   int do_fat_read(const char *filename, void *buffer, loff_t maxsize, int dols,
>   		loff_t *actread)
>   {
> 


-- 
Best regards,

Lukasz Majewski

--

DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de


More information about the U-Boot mailing list