All of lore.kernel.org
 help / color / mirror / Atom feed
From: Rob Clark <robdclark@gmail.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH v2 2/8] fs/fat: introduce new director iterators
Date: Sat,  2 Sep 2017 12:37:57 -0400	[thread overview]
Message-ID: <20170902163806.27265-3-robdclark@gmail.com> (raw)
In-Reply-To: <20170902163806.27265-1-robdclark@gmail.com>

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@gmail.com>
---
 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)
 {
-- 
2.13.5

  parent reply	other threads:[~2017-09-02 16:37 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-09-02 16:37 [U-Boot] [PATCH v2 0/8] fs/fat: cleanups + readdir implementation Rob Clark
2017-09-02 16:37 ` [U-Boot] [PATCH v2 1/8] fs/fat: split out helper to init fsdata Rob Clark
2017-09-03 14:52   ` Łukasz Majewski
2017-09-03 15:47     ` Rob Clark
2017-09-03 16:16       ` Łukasz Majewski
2017-09-05  8:55   ` Simon Glass
2017-09-02 16:37 ` Rob Clark [this message]
2017-09-03 15:08   ` [U-Boot] [PATCH v2 2/8] fs/fat: introduce new director iterators Łukasz Majewski
2017-09-05  8:56   ` Simon Glass
2017-09-05  9:54     ` Rob Clark
2017-09-09  4:55       ` Simon Glass
2017-09-09 10:34         ` Rob Clark
2017-09-02 16:37 ` [U-Boot] [PATCH v2 3/8] fat/fs: convert to directory iterators Rob Clark
2017-09-03 15:08   ` Łukasz Majewski
2017-09-05  8:56     ` Simon Glass
2017-09-06  2:18       ` Rob Clark
2017-09-02 16:37 ` [U-Boot] [PATCH v2 4/8] fs: add fs_readdir() Rob Clark
2017-09-03 15:16   ` Łukasz Majewski
2017-09-05  8:56     ` Simon Glass
2017-09-05 10:48       ` Rob Clark
2017-09-02 16:38 ` [U-Boot] [PATCH v2 5/8] fs/fat: implement opendir/readdir/closedir Rob Clark
2017-09-03 15:17   ` Łukasz Majewski
2017-09-05  8:56   ` Simon Glass
2017-09-02 16:38 ` [U-Boot] [PATCH v2 6/8] fat/fs: remove a bunch of dead code Rob Clark
2017-09-05  8:56   ` Simon Glass
2017-09-02 16:38 ` [U-Boot] [PATCH v2 7/8] fat/fs: move ls to generic implementation Rob Clark
2017-09-03 15:19   ` Łukasz Majewski
2017-09-05  8:56   ` Simon Glass
2017-09-06  2:12     ` Rob Clark
2017-09-02 16:38 ` [U-Boot] [PATCH v2 8/8] fs/fat: fix case for FAT shortnames Rob Clark
2017-09-03 15:22   ` Łukasz Majewski
2017-09-05  8:56     ` Simon Glass
  -- strict thread matches above, loose matches on Subject: below --
2017-08-14 13:16 [U-Boot] [PATCH v2 0/8] fs/fat: cleanups + readdir implementation Rob Clark
2017-08-14 13:16 ` [U-Boot] [PATCH v2 2/8] fs/fat: introduce new director iterators Rob Clark

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170902163806.27265-3-robdclark@gmail.com \
    --to=robdclark@gmail.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.