[U-Boot] [PATCH 2/8] fs/fat: introduce new director iterators
Rob Clark
robdclark at gmail.com
Sun Aug 13 10:45:22 UTC 2017
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>
---
fs/fat/fat.c | 326 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 326 insertions(+)
diff --git a/fs/fat/fat.c b/fs/fat/fat.c
index eb3792d4c1..69fa7f4cab 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 = 0;
+ }
+ 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.0
More information about the U-Boot
mailing list