From 45449980f80169214633f2649a27c791d0104e9d Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sat, 9 Sep 2017 13:15:52 -0400 Subject: [PATCH 34/47] fs/fat: split out helper to init fsdata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Want to re-use this in fat dirent iterator in next patch. Signed-off-by: Rob Clark Reviewed-by: Łukasz Majewski Reviewed-by: Simon Glass --- fs/fat/fat.c | 73 +++++++++++++++++++++++++++++++++++------------------------ include/fat.h | 1 + 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/fs/fat/fat.c b/fs/fat/fat.c index 465a6875ed..e1c0a15dc7 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -808,35 +808,17 @@ exit: return ret; } -__u8 do_fat_read_at_block[MAX_CLUSTSIZE] - __aligned(ARCH_DMA_MINALIGN); - -int do_fat_read_at(const char *filename, loff_t pos, void *buffer, - loff_t maxsize, int dols, int dogetsize, loff_t *size) +static int get_fs_info(fsdata *mydata) { - char fnamecopy[2048]; boot_sector bs; volume_info volinfo; - fsdata datablock; - fsdata *mydata = &datablock; - dir_entry *dentptr = NULL; - __u16 prevcksum = 0xffff; - char *subname = ""; - __u32 cursect; - int idx, isdir = 0; - int files = 0, dirs = 0; - int ret = -1; - int firsttime; __u32 root_cluster = 0; - __u32 read_blk; - int rootdir_size = 0; - int buffer_blk_cnt; - int do_read; - __u8 *dir_ptr; + int ret; - if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) { + ret = read_bootsectandvi(&bs, &volinfo, &mydata->fatsize); + if (ret) { debug("Error: reading boot sector\n"); - return -1; + return ret; } if (mydata->fatsize == 32) { @@ -848,8 +830,7 @@ int do_fat_read_at(const char *filename, loff_t pos, void *buffer, mydata->fat_sect = bs.reserved; - cursect = mydata->rootdir_sect - = mydata->fat_sect + mydata->fatlength * bs.fats; + mydata->rootdir_sect = mydata->fat_sect + mydata->fatlength * bs.fats; mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0]; mydata->clust_size = bs.cluster_size; @@ -863,12 +844,12 @@ int do_fat_read_at(const char *filename, loff_t pos, void *buffer, mydata->data_begin = mydata->rootdir_sect - (mydata->clust_size * 2); } else { - rootdir_size = ((bs.dir_entries[1] * (int)256 + - bs.dir_entries[0]) * - sizeof(dir_entry)) / - mydata->sect_size; + mydata->rootdir_size = ((bs.dir_entries[1] * (int)256 + + bs.dir_entries[0]) * + sizeof(dir_entry)) / + mydata->sect_size; mydata->data_begin = mydata->rootdir_sect + - rootdir_size - + mydata->rootdir_size - (mydata->clust_size * 2); } @@ -893,6 +874,38 @@ int do_fat_read_at(const char *filename, loff_t pos, void *buffer, debug("Sector size: %d, cluster size: %d\n", mydata->sect_size, mydata->clust_size); + return 0; +} + +__u8 do_fat_read_at_block[MAX_CLUSTSIZE] + __aligned(ARCH_DMA_MINALIGN); + +int do_fat_read_at(const char *filename, loff_t pos, void *buffer, + loff_t maxsize, int dols, int dogetsize, loff_t *size) +{ + char fnamecopy[2048]; + fsdata datablock; + fsdata *mydata = &datablock; + dir_entry *dentptr = NULL; + __u16 prevcksum = 0xffff; + char *subname = ""; + __u32 cursect; + int idx, isdir = 0; + int files = 0, dirs = 0; + int ret = -1; + int firsttime; + __u32 root_cluster = 0; + __u32 read_blk; + int rootdir_size = 0; + int buffer_blk_cnt; + int do_read; + __u8 *dir_ptr; + + if (get_fs_info(mydata)) + return -1; + + cursect = mydata->rootdir_sect; + /* "cwd" is always the root... */ while (ISDIRDELIM(*filename)) filename++; diff --git a/include/fat.h b/include/fat.h index 71879f01ca..b671ee8f81 100644 --- a/include/fat.h +++ b/include/fat.h @@ -174,6 +174,7 @@ typedef struct { __u16 clust_size; /* Size of clusters in sectors */ int data_begin; /* The sector of the first cluster, can be negative */ int fatbufnum; /* Used by get_fatent, init to -1 */ + int rootdir_size; /* Size of root dir for non-FAT32 */ } fsdata; typedef int (file_detectfs_func)(void); -- 2.13.5 From c6e3baa565bc3b5828cf0d67ca6429dbe5f8687c Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sat, 9 Sep 2017 13:15:53 -0400 Subject: [PATCH 35/47] fs/fat: introduce new director iterators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Untangle directory traversal into a simple iterator, to replace the existing multi-purpose do_fat_read_at() + get_dentfromdir(). Signed-off-by: Rob Clark Reviewed-by: Łukasz Majewski --- fs/fat/fat.c | 356 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- include/fat.h | 7 ++ 2 files changed, 360 insertions(+), 3 deletions(-) diff --git a/fs/fat/fat.c b/fs/fat/fat.c index e1c0a15dc7..ee2bbe38f1 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -812,7 +812,6 @@ static int get_fs_info(fsdata *mydata) { boot_sector bs; volume_info volinfo; - __u32 root_cluster = 0; int ret; ret = read_bootsectandvi(&bs, &volinfo, &mydata->fatsize); @@ -822,7 +821,6 @@ static int get_fs_info(fsdata *mydata) } if (mydata->fatsize == 32) { - root_cluster = bs.root_cluster; mydata->fatlength = bs.fat32_length; } else { mydata->fatlength = bs.fat_length; @@ -843,6 +841,7 @@ static int get_fs_info(fsdata *mydata) if (mydata->fatsize == 32) { mydata->data_begin = mydata->rootdir_sect - (mydata->clust_size * 2); + mydata->root_cluster = bs.root_cluster; } else { mydata->rootdir_size = ((bs.dir_entries[1] * (int)256 + bs.dir_entries[0]) * @@ -851,6 +850,9 @@ static int get_fs_info(fsdata *mydata) mydata->data_begin = mydata->rootdir_sect + mydata->rootdir_size - (mydata->clust_size * 2); + mydata->root_cluster = (mydata->rootdir_sect - + mydata->data_begin) / + mydata->clust_size; } mydata->fatbufnum = -1; @@ -868,7 +870,7 @@ static int get_fs_info(fsdata *mydata) mydata->fatsize, mydata->fat_sect, mydata->fatlength); debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n" "Data begins at: %d\n", - root_cluster, + mydata->root_cluster, mydata->rootdir_sect, mydata->rootdir_sect * mydata->sect_size, mydata->data_begin); debug("Sector size: %d, cluster size: %d\n", mydata->sect_size, @@ -1245,6 +1247,354 @@ exit: return ret; } + +/* + * Directory iterator, to simplify filesystem traversal + * + * Implements an iterator pattern to traverse directory tables, + * transparently handling directory tables split across multiple + * clusters, and the difference between FAT12/FAT16 root directory + * (contiguous) and subdirectories + FAT32 root (chained). + * + * Rough usage: + * + * for (fat_itr_root(&itr, fsdata); fat_itr_next(&itr); ) { + * // to traverse down to a subdirectory pointed to by + * // current iterator position: + * fat_itr_child(&itr, &itr); + * } + * + * For more complete example, see fat_itr_resolve() + */ + +typedef struct { + fsdata *fsdata; /* filesystem parameters */ + unsigned clust; /* current cluster */ + int last_cluster; /* set once we've read last cluster */ + int is_root; /* is iterator at root directory */ + int remaining; /* remaining dent's in current cluster */ + + /* current iterator position values: */ + dir_entry *dent; /* current directory entry */ + char l_name[VFAT_MAXLEN_BYTES]; /* long (vfat) name */ + char s_name[14]; /* short 8.3 name */ + char *name; /* l_name if there is one, else s_name */ + + /* storage for current cluster in memory: */ + 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->clust = fsdata->root_cluster; + 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->clust = clustnum; + } else { + itr->clust = parent->fsdata->root_cluster; + } + 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; + u32 sect; + + /* have we reached the end? */ + if (itr->last_cluster) + return NULL; + + sect = clust_to_sect(itr->fsdata, itr->clust); + + debug("FAT read(sect=%d), clust_size=%d, DIRENTSPERBLOCK=%zd\n", + sect, 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(sect, 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->clust++; + sect = clust_to_sect(itr->fsdata, itr->clust); + if (sect - itr->fsdata->rootdir_sect >= + itr->fsdata->rootdir_size) { + debug("cursect: 0x%x\n", itr->clust); + itr->last_cluster = 1; + } + } else { + itr->clust = get_fatent(itr->fsdata, itr->clust); + if (CHECK_CLUST(itr->clust, itr->fsdata->fatsize)) { + debug("cursect: 0x%x\n", itr->clust); + 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); + unsigned nbytes = itr->fsdata->sect_size * + itr->fsdata->clust_size; + + /* have we reached the last cluster? */ + if (!dent) + return NULL; + + itr->remaining = nbytes / 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; + unsigned n = max(strlen(itr->name), (size_t)(next - path)); + + /* check both long and short name: */ + if (!strncasecmp(path, itr->name, n)) + match = 1; + else if (itr->name != itr->s_name && + !strncasecmp(path, itr->s_name, n)) + 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) { diff --git a/include/fat.h b/include/fat.h index b671ee8f81..21bb6666cf 100644 --- a/include/fat.h +++ b/include/fat.h @@ -175,8 +175,15 @@ typedef struct { int data_begin; /* The sector of the first cluster, can be negative */ int fatbufnum; /* Used by get_fatent, init to -1 */ int rootdir_size; /* Size of root dir for non-FAT32 */ + __u32 root_cluster; /* First cluster of root dir for FAT32 */ } fsdata; +/* TODO clean up places that are open-coding this: */ +static inline u32 clust_to_sect(fsdata *fsdata, u32 clust) +{ + return fsdata->data_begin + clust * fsdata->clust_size; +} + typedef int (file_detectfs_func)(void); typedef int (file_ls_func)(const char *dir); typedef int (file_read_func)(const char *filename, void *buffer, -- 2.13.5 From 8eafae209c35932d9a6560809c55ee4641534236 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sat, 9 Sep 2017 13:15:54 -0400 Subject: [PATCH 36/47] fat/fs: convert to directory iterators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And drop a whole lot of ugly code! Signed-off-by: Rob Clark Reviewed-by: Łukasz Majewski Reviewed-by: Simon Glass --- fs/fat/fat.c | 722 +++++++--------------------------------------------------- include/fat.h | 6 - 2 files changed, 76 insertions(+), 652 deletions(-) diff --git a/fs/fat/fat.c b/fs/fat/fat.c index ee2bbe38f1..bbba7947ee 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -119,22 +119,6 @@ int fat_register_device(struct blk_desc *dev_desc, int part_no) } /* - * Get the first occurence of a directory delimiter ('/' or '\') in a string. - * Return index into string if found, -1 otherwise. - */ -static int dirdelim(char *str) -{ - char *start = str; - - while (*str != '\0') { - if (ISDIRDELIM(*str)) - return str - start; - str++; - } - return -1; -} - -/* * Extract zero terminated short name from a directory entry. */ static void get_name(dir_entry *dirent, char *s_name) @@ -468,95 +452,6 @@ static int slot2str(dir_slot *slotptr, char *l_name, int *idx) return 0; } -/* - * Extract the full long filename starting at 'retdent' (which is really - * a slot) into 'l_name'. If successful also copy the real directory entry - * into 'retdent' - * Return 0 on success, -1 otherwise. - */ -static int -get_vfatname(fsdata *mydata, int curclust, __u8 *cluster, - dir_entry *retdent, char *l_name) -{ - dir_entry *realdent; - dir_slot *slotptr = (dir_slot *)retdent; - __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ? - PREFETCH_BLOCKS : - mydata->clust_size); - __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff; - int idx = 0; - - if (counter > VFAT_MAXSEQ) { - debug("Error: VFAT name is too long\n"); - return -1; - } - - while ((__u8 *)slotptr < buflimit) { - if (counter == 0) - break; - if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter) - return -1; - slotptr++; - counter--; - } - - if ((__u8 *)slotptr >= buflimit) { - dir_slot *slotptr2; - - if (curclust == 0) - return -1; - curclust = get_fatent(mydata, curclust); - if (CHECK_CLUST(curclust, mydata->fatsize)) { - debug("curclust: 0x%x\n", curclust); - printf("Invalid FAT entry\n"); - return -1; - } - - if (get_cluster(mydata, curclust, get_contents_vfatname_block, - mydata->clust_size * mydata->sect_size) != 0) { - debug("Error: reading directory block\n"); - return -1; - } - - slotptr2 = (dir_slot *)get_contents_vfatname_block; - while (counter > 0) { - if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK) - & 0xff) != counter) - return -1; - slotptr2++; - counter--; - } - - /* Save the real directory entry */ - realdent = (dir_entry *)slotptr2; - while ((__u8 *)slotptr2 > get_contents_vfatname_block) { - slotptr2--; - slot2str(slotptr2, l_name, &idx); - } - } else { - /* Save the real directory entry */ - realdent = (dir_entry *)slotptr; - } - - do { - slotptr--; - if (slot2str(slotptr, l_name, &idx)) - break; - } while (!(slotptr->id & LAST_LONG_ENTRY_MASK)); - - l_name[idx] = '\0'; - if (*l_name == DELETED_FLAG) - *l_name = '\0'; - else if (*l_name == aRING) - *l_name = DELETED_FLAG; - downcase(l_name); - - /* Return the real directory entry */ - memcpy(retdent, realdent, sizeof(dir_entry)); - - return 0; -} - /* Calculate short name checksum */ static __u8 mkcksum(const char name[8], const char ext[3]) { @@ -573,169 +468,13 @@ static __u8 mkcksum(const char name[8], const char ext[3]) } /* - * Get the directory entry associated with 'filename' from the directory - * starting at 'startsect' + * TODO these should go away once fat_write is reworked to use the + * directory iterator */ __u8 get_dentfromdir_block[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN); - -static dir_entry *get_dentfromdir(fsdata *mydata, int startsect, - char *filename, dir_entry *retdent, - int dols) -{ - __u16 prevcksum = 0xffff; - __u32 curclust = START(retdent); - int files = 0, dirs = 0; - - debug("get_dentfromdir: %s\n", filename); - - while (1) { - dir_entry *dentptr; - - int i; - - if (get_cluster(mydata, curclust, get_dentfromdir_block, - mydata->clust_size * mydata->sect_size) != 0) { - debug("Error: reading directory block\n"); - return NULL; - } - - dentptr = (dir_entry *)get_dentfromdir_block; - - for (i = 0; i < DIRENTSPERCLUST; i++) { - char s_name[14], l_name[VFAT_MAXLEN_BYTES]; - - l_name[0] = '\0'; - if (dentptr->name[0] == DELETED_FLAG) { - dentptr++; - continue; - } - if ((dentptr->attr & ATTR_VOLUME)) { - if (vfat_enabled && - (dentptr->attr & ATTR_VFAT) == ATTR_VFAT && - (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { - prevcksum = ((dir_slot *)dentptr)->alias_checksum; - get_vfatname(mydata, curclust, - get_dentfromdir_block, - dentptr, l_name); - if (dols) { - int isdir; - char dirc; - int doit = 0; - - isdir = (dentptr->attr & ATTR_DIR); - - if (isdir) { - dirs++; - dirc = '/'; - doit = 1; - } else { - dirc = ' '; - if (l_name[0] != 0) { - files++; - doit = 1; - } - } - if (doit) { - if (dirc == ' ') { - printf(" %8u %s%c\n", - FAT2CPU32(dentptr->size), - l_name, - dirc); - } else { - printf(" %s%c\n", - l_name, - dirc); - } - } - dentptr++; - continue; - } - debug("vfatname: |%s|\n", l_name); - } else { - /* Volume label or VFAT entry */ - dentptr++; - continue; - } - } - if (dentptr->name[0] == 0) { - if (dols) { - printf("\n%d file(s), %d dir(s)\n\n", - files, dirs); - } - debug("Dentname == NULL - %d\n", i); - return NULL; - } - if (vfat_enabled) { - __u8 csum = mkcksum(dentptr->name, dentptr->ext); - if (dols && csum == prevcksum) { - prevcksum = 0xffff; - dentptr++; - continue; - } - } - - get_name(dentptr, s_name); - if (dols) { - int isdir = (dentptr->attr & ATTR_DIR); - char dirc; - int doit = 0; - - if (isdir) { - dirs++; - dirc = '/'; - doit = 1; - } else { - dirc = ' '; - if (s_name[0] != 0) { - files++; - doit = 1; - } - } - - if (doit) { - if (dirc == ' ') { - printf(" %8u %s%c\n", - FAT2CPU32(dentptr->size), - s_name, dirc); - } else { - printf(" %s%c\n", - s_name, dirc); - } - } - - dentptr++; - continue; - } - - if (strcmp(filename, s_name) - && strcmp(filename, l_name)) { - debug("Mismatch: |%s|%s|\n", s_name, l_name); - dentptr++; - continue; - } - - memcpy(retdent, dentptr, sizeof(dir_entry)); - - debug("DentName: %s", s_name); - debug(", start: 0x%x", START(dentptr)); - debug(", size: 0x%x %s\n", - FAT2CPU32(dentptr->size), - (dentptr->attr & ATTR_DIR) ? "(DIR)" : ""); - - return retdent; - } - - curclust = get_fatent(mydata, curclust); - if (CHECK_CLUST(curclust, mydata->fatsize)) { - debug("curclust: 0x%x\n", curclust); - printf("Invalid FAT entry\n"); - return NULL; - } - } - - return NULL; -} +__u8 do_fat_read_at_block[MAX_CLUSTSIZE] + __aligned(ARCH_DMA_MINALIGN); /* * Read boot sector and volume info from a FAT filesystem @@ -879,374 +618,6 @@ static int get_fs_info(fsdata *mydata) return 0; } -__u8 do_fat_read_at_block[MAX_CLUSTSIZE] - __aligned(ARCH_DMA_MINALIGN); - -int do_fat_read_at(const char *filename, loff_t pos, void *buffer, - loff_t maxsize, int dols, int dogetsize, loff_t *size) -{ - char fnamecopy[2048]; - fsdata datablock; - fsdata *mydata = &datablock; - dir_entry *dentptr = NULL; - __u16 prevcksum = 0xffff; - char *subname = ""; - __u32 cursect; - int idx, isdir = 0; - int files = 0, dirs = 0; - int ret = -1; - int firsttime; - __u32 root_cluster = 0; - __u32 read_blk; - int rootdir_size = 0; - int buffer_blk_cnt; - int do_read; - __u8 *dir_ptr; - - if (get_fs_info(mydata)) - return -1; - - cursect = mydata->rootdir_sect; - - /* "cwd" is always the root... */ - while (ISDIRDELIM(*filename)) - filename++; - - /* Make a copy of the filename and convert it to lowercase */ - strcpy(fnamecopy, filename); - downcase(fnamecopy); - -root_reparse: - if (*fnamecopy == '\0') { - if (!dols) - goto exit; - - dols = LS_ROOT; - } else if ((idx = dirdelim(fnamecopy)) >= 0) { - isdir = 1; - fnamecopy[idx] = '\0'; - subname = fnamecopy + idx + 1; - - /* Handle multiple delimiters */ - while (ISDIRDELIM(*subname)) - subname++; - } else if (dols) { - isdir = 1; - } - - buffer_blk_cnt = 0; - firsttime = 1; - while (1) { - int i; - - if (mydata->fatsize == 32 || firsttime) { - dir_ptr = do_fat_read_at_block; - firsttime = 0; - } else { - /** - * FAT16 sector buffer modification: - * Each loop, the second buffered block is moved to - * the buffer begin, and two next sectors are read - * next to the previously moved one. So the sector - * buffer keeps always 3 sectors for fat16. - * And the current sector is the buffer second sector - * beside the "firsttime" read, when it is the first one. - * - * PREFETCH_BLOCKS is 2 for FAT16 == loop[0:1] - * n = computed root dir sector - * loop | cursect-1 | cursect | cursect+1 | - * 0 | sector n+0 | sector n+1 | none | - * 1 | none | sector n+0 | sector n+1 | - * 0 | sector n+1 | sector n+2 | sector n+3 | - * 1 | sector n+3 | ... - */ - dir_ptr = (do_fat_read_at_block + mydata->sect_size); - memcpy(do_fat_read_at_block, dir_ptr, mydata->sect_size); - } - - do_read = 1; - - if (mydata->fatsize == 32 && buffer_blk_cnt) - do_read = 0; - - if (do_read) { - read_blk = (mydata->fatsize == 32) ? - mydata->clust_size : PREFETCH_BLOCKS; - - debug("FAT read(sect=%d, cnt:%d), clust_size=%d, DIRENTSPERBLOCK=%zd\n", - cursect, read_blk, mydata->clust_size, DIRENTSPERBLOCK); - - if (disk_read(cursect, read_blk, dir_ptr) < 0) { - debug("Error: reading rootdir block\n"); - goto exit; - } - - dentptr = (dir_entry *)dir_ptr; - } - - for (i = 0; i < DIRENTSPERBLOCK; i++) { - char s_name[14], l_name[VFAT_MAXLEN_BYTES]; - __u8 csum; - - l_name[0] = '\0'; - if (dentptr->name[0] == DELETED_FLAG) { - dentptr++; - continue; - } - - if (vfat_enabled) - csum = mkcksum(dentptr->name, dentptr->ext); - - if (dentptr->attr & ATTR_VOLUME) { - if (vfat_enabled && - (dentptr->attr & ATTR_VFAT) == ATTR_VFAT && - (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { - prevcksum = - ((dir_slot *)dentptr)->alias_checksum; - - get_vfatname(mydata, - root_cluster, - dir_ptr, - dentptr, l_name); - - if (dols == LS_ROOT) { - char dirc; - int doit = 0; - int isdir = - (dentptr->attr & ATTR_DIR); - - if (isdir) { - dirs++; - dirc = '/'; - doit = 1; - } else { - dirc = ' '; - if (l_name[0] != 0) { - files++; - doit = 1; - } - } - if (doit) { - if (dirc == ' ') { - printf(" %8u %s%c\n", - FAT2CPU32(dentptr->size), - l_name, - dirc); - } else { - printf(" %s%c\n", - l_name, - dirc); - } - } - dentptr++; - continue; - } - debug("Rootvfatname: |%s|\n", - l_name); - } else { - /* Volume label or VFAT entry */ - dentptr++; - continue; - } - } else if (dentptr->name[0] == 0) { - debug("RootDentname == NULL - %d\n", i); - if (dols == LS_ROOT) { - printf("\n%d file(s), %d dir(s)\n\n", - files, dirs); - ret = 0; - } - goto exit; - } - else if (vfat_enabled && - dols == LS_ROOT && csum == prevcksum) { - prevcksum = 0xffff; - dentptr++; - continue; - } - - get_name(dentptr, s_name); - - if (dols == LS_ROOT) { - int isdir = (dentptr->attr & ATTR_DIR); - char dirc; - int doit = 0; - - if (isdir) { - dirc = '/'; - if (s_name[0] != 0) { - dirs++; - doit = 1; - } - } else { - dirc = ' '; - if (s_name[0] != 0) { - files++; - doit = 1; - } - } - if (doit) { - if (dirc == ' ') { - printf(" %8u %s%c\n", - FAT2CPU32(dentptr->size), - s_name, dirc); - } else { - printf(" %s%c\n", - s_name, dirc); - } - } - dentptr++; - continue; - } - - if (strcmp(fnamecopy, s_name) - && strcmp(fnamecopy, l_name)) { - debug("RootMismatch: |%s|%s|\n", s_name, - l_name); - dentptr++; - continue; - } - - if (isdir && !(dentptr->attr & ATTR_DIR)) - goto exit; - - debug("RootName: %s", s_name); - debug(", start: 0x%x", START(dentptr)); - debug(", size: 0x%x %s\n", - FAT2CPU32(dentptr->size), - isdir ? "(DIR)" : ""); - - goto rootdir_done; /* We got a match */ - } - debug("END LOOP: buffer_blk_cnt=%d clust_size=%d\n", buffer_blk_cnt, - mydata->clust_size); - - /* - * On FAT32 we must fetch the FAT entries for the next - * root directory clusters when a cluster has been - * completely processed. - */ - ++buffer_blk_cnt; - int rootdir_end = 0; - if (mydata->fatsize == 32) { - if (buffer_blk_cnt == mydata->clust_size) { - int nxtsect = 0; - int nxt_clust = 0; - - nxt_clust = get_fatent(mydata, root_cluster); - rootdir_end = CHECK_CLUST(nxt_clust, 32); - - nxtsect = mydata->data_begin + - (nxt_clust * mydata->clust_size); - - root_cluster = nxt_clust; - - cursect = nxtsect; - buffer_blk_cnt = 0; - } - } else { - if (buffer_blk_cnt == PREFETCH_BLOCKS) - buffer_blk_cnt = 0; - - rootdir_end = (++cursect - mydata->rootdir_sect >= - rootdir_size); - } - - /* If end of rootdir reached */ - if (rootdir_end) { - if (dols == LS_ROOT) { - printf("\n%d file(s), %d dir(s)\n\n", - files, dirs); - *size = 0; - } - goto exit; - } - } -rootdir_done: - - firsttime = 1; - - while (isdir) { - int startsect = mydata->data_begin - + START(dentptr) * mydata->clust_size; - dir_entry dent; - char *nextname = NULL; - - dent = *dentptr; - dentptr = &dent; - - idx = dirdelim(subname); - - if (idx >= 0) { - subname[idx] = '\0'; - nextname = subname + idx + 1; - /* Handle multiple delimiters */ - while (ISDIRDELIM(*nextname)) - nextname++; - if (dols && *nextname == '\0') - firsttime = 0; - } else { - if (dols && firsttime) { - firsttime = 0; - } else { - isdir = 0; - } - } - - if (get_dentfromdir(mydata, startsect, subname, dentptr, - isdir ? 0 : dols) == NULL) { - if (dols && !isdir) - *size = 0; - goto exit; - } - - if (isdir && !(dentptr->attr & ATTR_DIR)) - goto exit; - - /* - * If we are looking for a directory, and found a directory - * type entry, and the entry is for the root directory (as - * denoted by a cluster number of 0), jump back to the start - * of the function, since at least on FAT12/16, the root dir - * lives in a hard-coded location and needs special handling - * to parse, rather than simply following the cluster linked - * list in the FAT, like other directories. - */ - if (isdir && (dentptr->attr & ATTR_DIR) && !START(dentptr)) { - /* - * Modify the filename to remove the prefix that gets - * back to the root directory, so the initial root dir - * parsing code can continue from where we are without - * confusion. - */ - strcpy(fnamecopy, nextname ?: ""); - /* - * Set up state the same way as the function does when - * first started. This is required for the root dir - * parsing code operates in its expected environment. - */ - subname = ""; - cursect = mydata->rootdir_sect; - isdir = 0; - goto root_reparse; - } - - if (idx >= 0) - subname = nextname; - } - - if (dogetsize) { - *size = FAT2CPU32(dentptr->size); - ret = 0; - } else { - ret = get_contents(mydata, dentptr, pos, buffer, maxsize, size); - } - debug("Size: %u, got: %llu\n", FAT2CPU32(dentptr->size), *size); - -exit: - free(mydata->fatbuf); - return ret; -} - /* * Directory iterator, to simplify filesystem traversal @@ -1595,12 +966,6 @@ static int fat_itr_resolve(fat_itr *itr, const char *path, unsigned type) return -ENOENT; } -int do_fat_read(const char *filename, void *buffer, loff_t maxsize, int dols, - loff_t *actread) -{ - return do_fat_read_at(filename, 0, buffer, maxsize, dols, 0, actread); -} - int file_fat_detectfs(void) { boot_sector bs; @@ -1665,31 +1030,96 @@ int file_fat_detectfs(void) int file_fat_ls(const char *dir) { - loff_t size; + fsdata fsdata; + fat_itr itrblock, *itr = &itrblock; + int files = 0, dirs = 0; + int ret; + + ret = fat_itr_root(itr, &fsdata); + if (ret) + return ret; + + ret = fat_itr_resolve(itr, dir, TYPE_DIR); + if (ret) + return ret; + + while (fat_itr_next(itr)) { + if (fat_itr_isdir(itr)) { + printf(" %s/\n", itr->name); + dirs++; + } else { + printf(" %8u %s\n", + FAT2CPU32(itr->dent->size), + itr->name); + files++; + } + } - return do_fat_read(dir, NULL, 0, LS_YES, &size); + printf("\n%d file(s), %d dir(s)\n\n", files, dirs); + + return 0; } int fat_exists(const char *filename) { + fsdata fsdata; + fat_itr itrblock, *itr = &itrblock; int ret; - loff_t size; - ret = do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1, &size); + ret = fat_itr_root(itr, &fsdata); + if (ret) + return 0; + + ret = fat_itr_resolve(itr, filename, TYPE_ANY); return ret == 0; } int fat_size(const char *filename, loff_t *size) { - return do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1, size); + fsdata fsdata; + fat_itr itrblock, *itr = &itrblock; + int ret; + + ret = fat_itr_root(itr, &fsdata); + if (ret) + return ret; + + ret = fat_itr_resolve(itr, filename, TYPE_FILE); + if (ret) { + /* + * Directories don't have size, but fs_size() is not + * expected to fail if passed a directory path: + */ + fat_itr_root(itr, &fsdata); + if (!fat_itr_resolve(itr, filename, TYPE_DIR)) { + *size = 0; + return 0; + } + return ret; + } + + *size = FAT2CPU32(itr->dent->size); + + return 0; } int file_fat_read_at(const char *filename, loff_t pos, void *buffer, loff_t maxsize, loff_t *actread) { + fsdata fsdata; + fat_itr itrblock, *itr = &itrblock; + int ret; + + ret = fat_itr_root(itr, &fsdata); + if (ret) + return ret; + + ret = fat_itr_resolve(itr, filename, TYPE_FILE); + if (ret) + return ret; + printf("reading %s\n", filename); - return do_fat_read_at(filename, pos, buffer, maxsize, LS_NO, 0, - actread); + return get_contents(&fsdata, itr->dent, pos, buffer, maxsize, actread); } int file_fat_read(const char *filename, void *buffer, int maxsize) diff --git a/include/fat.h b/include/fat.h index 21bb6666cf..18d8981c48 100644 --- a/include/fat.h +++ b/include/fat.h @@ -58,12 +58,6 @@ */ #define LAST_LONG_ENTRY_MASK 0x40 -/* Flags telling whether we should read a file or list a directory */ -#define LS_NO 0 -#define LS_YES 1 -#define LS_DIR 1 -#define LS_ROOT 2 - #define ISDIRDELIM(c) ((c) == '/' || (c) == '\\') #define FSTYPE_NONE (-1) -- 2.13.5 From 4bbcc965f995564870ca02606137e60e873e0a1f Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sat, 9 Sep 2017 13:15:55 -0400 Subject: [PATCH 37/47] fs: add fs_readdir() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Łukasz Majewski Reviewed-by: Simon Glass --- disk/part.c | 31 ++++++++++------- fs/fs.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/fs.h | 67 +++++++++++++++++++++++++++++++++++++ include/part.h | 9 +++++ 4 files changed, 199 insertions(+), 12 deletions(-) diff --git a/disk/part.c b/disk/part.c index c67fdacc79..aa9183d696 100644 --- a/disk/part.c +++ b/disk/part.c @@ -331,6 +331,24 @@ int part_get_info(struct blk_desc *dev_desc, int part, 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, 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); - 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 + part_get_info_whole_disk(*dev_desc, info); ret = 0; goto cleanup; diff --git a/fs/fs.c b/fs/fs.c index 13cd3626c6..fc0c953fcb 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -21,6 +21,7 @@ DECLARE_GLOBAL_DATA_PTR; static struct blk_desc *fs_dev_desc; +static int fs_dev_part; static disk_partition_t fs_partition; static int fs_type = FS_TYPE_ANY; @@ -69,6 +70,12 @@ static inline int fs_uuid_unsupported(char *uuid_str) return -1; } +static inline int fs_opendir_unsupported(const char *filename, + struct fs_dir_stream **dirs) +{ + return -EACCES; +} + struct fstype_info { int fstype; char *name; @@ -92,6 +99,20 @@ struct fstype_info { loff_t len, loff_t *actwrite); void (*close)(void); 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[] = { @@ -112,6 +133,7 @@ static struct fstype_info fstypes[] = { .write = fs_write_unsupported, #endif .uuid = fs_uuid_unsupported, + .opendir = fs_opendir_unsupported, }, #endif #ifdef CONFIG_FS_EXT4 @@ -131,6 +153,7 @@ static struct fstype_info fstypes[] = { .write = fs_write_unsupported, #endif .uuid = ext4fs_uuid, + .opendir = fs_opendir_unsupported, }, #endif #ifdef CONFIG_SANDBOX @@ -146,6 +169,7 @@ static struct fstype_info fstypes[] = { .read = fs_read_sandbox, .write = fs_write_sandbox, .uuid = fs_uuid_unsupported, + .opendir = fs_opendir_unsupported, }, #endif #ifdef CONFIG_CMD_UBIFS @@ -161,6 +185,7 @@ static struct fstype_info fstypes[] = { .read = ubifs_read, .write = fs_write_unsupported, .uuid = fs_uuid_unsupported, + .opendir = fs_opendir_unsupported, }, #endif { @@ -175,6 +200,7 @@ static struct fstype_info fstypes[] = { .read = fs_read_unsupported, .write = fs_write_unsupported, .uuid = fs_uuid_unsupported, + .opendir = fs_opendir_unsupported, }, }; @@ -228,6 +254,31 @@ int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype) 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)) { + fs_type = info->fstype; return 0; } } @@ -334,6 +385,59 @@ int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len, 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 fstype) { diff --git a/include/fs.h b/include/fs.h index 2f2aca8378..0869ad6e80 100644 --- a/include/fs.h +++ b/include/fs.h @@ -27,6 +27,17 @@ 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(), * in directory "dirname". * @@ -79,6 +90,62 @@ int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len, 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 * to a specific filesystem type via the fstype parameter. */ diff --git a/include/part.h b/include/part.h index 0d5c99836b..86117a7ce5 100644 --- a/include/part.h +++ b/include/part.h @@ -98,6 +98,12 @@ int host_get_dev_err(int dev, struct blk_desc **blk_devp); /* disk/part.c */ 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_init(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, 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_init(struct blk_desc *dev_desc) {} static inline void dev_print(struct blk_desc *dev_desc) {} -- 2.13.5 From 1f40366b319eac7eb02f8894fff5c94fbdb47d30 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sat, 9 Sep 2017 13:15:56 -0400 Subject: [PATCH 38/47] fs/fat: implement opendir/readdir/closedir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the readdir interface using the directory iterators. Signed-off-by: Rob Clark Reviewed-by: Łukasz Majewski Reviewed-by: Simon Glass --- fs/fat/fat.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/fs/fat/fat.c b/fs/fat/fat.c index bbba7947ee..82ddb7eab1 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1146,6 +1147,66 @@ int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len, return ret; } +typedef struct { + struct fs_dir_stream parent; + struct fs_dirent dirent; + fsdata fsdata; + fat_itr itr; +} fat_dir; + +int fat_opendir(const char *filename, struct fs_dir_stream **dirsp) +{ + fat_dir *dir = malloc(sizeof(*dir)); + int ret; + + if (!dir) + return -ENOMEM; + + ret = fat_itr_root(&dir->itr, &dir->fsdata); + if (ret) + goto fail; + + ret = fat_itr_resolve(&dir->itr, filename, TYPE_DIR); + if (ret) + goto fail; + + *dirsp = (struct fs_dir_stream *)dir; + return 0; + +fail: + free(dir); + return ret; +} + +int fat_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp) +{ + fat_dir *dir = (fat_dir *)dirs; + struct fs_dirent *dent = &dir->dirent; + + if (!fat_itr_next(&dir->itr)) + return -ENOENT; + + memset(dent, 0, sizeof(*dent)); + strcpy(dent->name, dir->itr.name); + + if (fat_itr_isdir(&dir->itr)) { + dent->type = FS_DT_DIR; + } else { + dent->type = FS_DT_REG; + dent->size = FAT2CPU32(dir->itr.dent->size); + } + + *dentp = dent; + + return 0; +} + +void fat_closedir(struct fs_dir_stream *dirs) +{ + fat_dir *dir = (fat_dir *)dirs; + free(dir); +} + void fat_close(void) { } -- 2.13.5 From 41fa83d1a6cdd8ddfb3fbe332252193ff8fb8b71 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sat, 9 Sep 2017 13:15:57 -0400 Subject: [PATCH 39/47] fat/fs: remove a bunch of dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spotted by chance, when trying to remove file_fat_ls(), I noticed there were some dead users of the API. Signed-off-by: Rob Clark Acked-by: Stefan Brüns Reviewed-by: Simon Glass --- fs/fat/Makefile | 4 -- fs/fat/file.c | 183 -------------------------------------------------------- include/fat.h | 20 ------- 3 files changed, 207 deletions(-) delete mode 100644 fs/fat/file.c diff --git a/fs/fat/Makefile b/fs/fat/Makefile index b60e8486c4..3e2a6b01a8 100644 --- a/fs/fat/Makefile +++ b/fs/fat/Makefile @@ -5,7 +5,3 @@ obj-$(CONFIG_FS_FAT) := fat.o obj-$(CONFIG_FAT_WRITE):= fat_write.o - -ifndef CONFIG_SPL_BUILD -obj-$(CONFIG_FS_FAT) += file.o -endif diff --git a/fs/fat/file.c b/fs/fat/file.c deleted file mode 100644 index 89706117b9..0000000000 --- a/fs/fat/file.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * file.c - * - * Mini "VFS" by Marcus Sundberg - * - * 2002-07-28 - rjones@nexus-tech.net - ported to ppcboot v1.1.6 - * 2003-03-10 - kharris@nexus-tech.net - ported to uboot - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include -#include - -/* Supported filesystems */ -static const struct filesystem filesystems[] = { - { file_fat_detectfs, file_fat_ls, file_fat_read, "FAT" }, -}; -#define NUM_FILESYS (sizeof(filesystems)/sizeof(struct filesystem)) - -/* The filesystem which was last detected */ -static int current_filesystem = FSTYPE_NONE; - -/* The current working directory */ -#define CWD_LEN 511 -char file_cwd[CWD_LEN+1] = "/"; - -const char * -file_getfsname(int idx) -{ - if (idx < 0 || idx >= NUM_FILESYS) - return NULL; - - return filesystems[idx].name; -} - -static void -pathcpy(char *dest, const char *src) -{ - char *origdest = dest; - - do { - if (dest-file_cwd >= CWD_LEN) { - *dest = '\0'; - return; - } - *(dest) = *(src); - if (*src == '\0') { - if (dest-- != origdest && ISDIRDELIM(*dest)) { - *dest = '\0'; - } - return; - } - ++dest; - - if (ISDIRDELIM(*src)) - while (ISDIRDELIM(*src)) src++; - else - src++; - } while (1); -} - -int -file_cd(const char *path) -{ - if (ISDIRDELIM(*path)) { - while (ISDIRDELIM(*path)) path++; - strncpy(file_cwd+1, path, CWD_LEN-1); - } else { - const char *origpath = path; - char *tmpstr = file_cwd; - int back = 0; - - while (*tmpstr != '\0') tmpstr++; - do { - tmpstr--; - } while (ISDIRDELIM(*tmpstr)); - - while (*path == '.') { - path++; - while (*path == '.') { - path++; - back++; - } - if (*path != '\0' && !ISDIRDELIM(*path)) { - path = origpath; - back = 0; - break; - } - while (ISDIRDELIM(*path)) path++; - origpath = path; - } - - while (back--) { - /* Strip off path component */ - while (!ISDIRDELIM(*tmpstr)) { - tmpstr--; - } - if (tmpstr == file_cwd) { - /* Incremented again right after the loop. */ - tmpstr--; - break; - } - /* Skip delimiters */ - while (ISDIRDELIM(*tmpstr)) tmpstr--; - } - tmpstr++; - if (*path == '\0') { - if (tmpstr == file_cwd) { - *tmpstr = '/'; - tmpstr++; - } - *tmpstr = '\0'; - return 0; - } - *tmpstr = '/'; - pathcpy(tmpstr+1, path); - } - - return 0; -} - -int -file_detectfs(void) -{ - int i; - - current_filesystem = FSTYPE_NONE; - - for (i = 0; i < NUM_FILESYS; i++) { - if (filesystems[i].detect() == 0) { - strcpy(file_cwd, "/"); - current_filesystem = i; - break; - } - } - - return current_filesystem; -} - -int -file_ls(const char *dir) -{ - char fullpath[1024]; - const char *arg; - - if (current_filesystem == FSTYPE_NONE) { - printf("Can't list files without a filesystem!\n"); - return -1; - } - - if (ISDIRDELIM(*dir)) { - arg = dir; - } else { - sprintf(fullpath, "%s/%s", file_cwd, dir); - arg = fullpath; - } - return filesystems[current_filesystem].ls(arg); -} - -int file_read(const char *filename, void *buffer, int maxsize) -{ - char fullpath[1024]; - const char *arg; - - if (current_filesystem == FSTYPE_NONE) { - printf("Can't load file without a filesystem!\n"); - return -1; - } - - if (ISDIRDELIM(*filename)) { - arg = filename; - } else { - sprintf(fullpath, "%s/%s", file_cwd, filename); - arg = fullpath; - } - - return filesystems[current_filesystem].read(arg, buffer, maxsize); -} diff --git a/include/fat.h b/include/fat.h index 18d8981c48..b255ce5337 100644 --- a/include/fat.h +++ b/include/fat.h @@ -178,25 +178,6 @@ static inline u32 clust_to_sect(fsdata *fsdata, u32 clust) return fsdata->data_begin + clust * fsdata->clust_size; } -typedef int (file_detectfs_func)(void); -typedef int (file_ls_func)(const char *dir); -typedef int (file_read_func)(const char *filename, void *buffer, - int maxsize); - -struct filesystem { - file_detectfs_func *detect; - file_ls_func *ls; - file_read_func *read; - const char name[12]; -}; - -/* FAT tables */ -file_detectfs_func file_fat_detectfs; -file_ls_func file_fat_ls; -file_read_func file_fat_read; - -/* Currently this doesn't check if the dir exists or is valid... */ -int file_cd(const char *path); int file_fat_detectfs(void); int file_fat_ls(const char *dir); int fat_exists(const char *filename); @@ -204,7 +185,6 @@ int fat_size(const char *filename, loff_t *size); int file_fat_read_at(const char *filename, loff_t pos, void *buffer, loff_t maxsize, loff_t *actread); int file_fat_read(const char *filename, void *buffer, int maxsize); -const char *file_getfsname(int idx); int fat_set_blk_dev(struct blk_desc *rbdd, disk_partition_t *info); int fat_register_device(struct blk_desc *dev_desc, int part_no); -- 2.13.5 From 89191d626793490b579e1d36e7d7a4464a20f9f6 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sat, 9 Sep 2017 13:15:58 -0400 Subject: [PATCH 40/47] fat/fs: move ls to generic implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a generic implementation of 'ls' using opendir/readdir/closedir, and replace fat's custom implementation. Other filesystems should move to the generic implementation after they add opendir/readdir/closedir support. Signed-off-by: Rob Clark Reviewed-by: Łukasz Majewski Reviewed-by: Simon Glass --- fs/fat/fat.c | 32 -------------------------------- fs/fs.c | 35 +++++++++++++++++++++++++++++++++-- include/fat.h | 5 ++++- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/fs/fat/fat.c b/fs/fat/fat.c index 82ddb7eab1..c951d84f57 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -1029,38 +1029,6 @@ int file_fat_detectfs(void) return 0; } -int file_fat_ls(const char *dir) -{ - fsdata fsdata; - fat_itr itrblock, *itr = &itrblock; - int files = 0, dirs = 0; - int ret; - - ret = fat_itr_root(itr, &fsdata); - if (ret) - return ret; - - ret = fat_itr_resolve(itr, dir, TYPE_DIR); - if (ret) - return ret; - - while (fat_itr_next(itr)) { - if (fat_itr_isdir(itr)) { - printf(" %s/\n", itr->name); - dirs++; - } else { - printf(" %8u %s\n", - FAT2CPU32(itr->dent->size), - itr->name); - files++; - } - } - - printf("\n%d file(s), %d dir(s)\n\n", files, dirs); - - return 0; -} - int fat_exists(const char *filename) { fsdata fsdata; diff --git a/fs/fs.c b/fs/fs.c index fc0c953fcb..3481229aa6 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -37,6 +37,35 @@ static inline int fs_ls_unsupported(const char *dirname) return -1; } +/* generic implementation of ls in terms of opendir/readdir/closedir */ +__maybe_unused +static int fs_ls_generic(const char *dirname) +{ + struct fs_dir_stream *dirs; + struct fs_dirent *dent; + int nfiles = 0, ndirs = 0; + + dirs = fs_opendir(dirname); + if (!dirs) + return -errno; + + while ((dent = fs_readdir(dirs))) { + if (dent->type == FS_DT_DIR) { + printf(" %s/\n", dent->name); + ndirs++; + } else { + printf(" %8lld %s\n", dent->size, dent->name); + nfiles++; + } + } + + fs_closedir(dirs); + + printf("\n%d file(s), %d dir(s)\n\n", nfiles, ndirs); + + return 0; +} + static inline int fs_exists_unsupported(const char *filename) { return 0; @@ -123,7 +152,7 @@ static struct fstype_info fstypes[] = { .null_dev_desc_ok = false, .probe = fat_set_blk_dev, .close = fat_close, - .ls = file_fat_ls, + .ls = fs_ls_generic, .exists = fat_exists, .size = fat_size, .read = fat_read_file, @@ -133,7 +162,9 @@ static struct fstype_info fstypes[] = { .write = fs_write_unsupported, #endif .uuid = fs_uuid_unsupported, - .opendir = fs_opendir_unsupported, + .opendir = fat_opendir, + .readdir = fat_readdir, + .closedir = fat_closedir, }, #endif #ifdef CONFIG_FS_EXT4 diff --git a/include/fat.h b/include/fat.h index b255ce5337..83cd90017e 100644 --- a/include/fat.h +++ b/include/fat.h @@ -11,6 +11,7 @@ #define _FAT_H_ #include +#include #define CONFIG_SUPPORT_VFAT /* Maximum Long File Name length supported here is 128 UTF-16 code units */ @@ -179,7 +180,6 @@ static inline u32 clust_to_sect(fsdata *fsdata, u32 clust) } int file_fat_detectfs(void); -int file_fat_ls(const char *dir); int fat_exists(const char *filename); int fat_size(const char *filename, loff_t *size); int file_fat_read_at(const char *filename, loff_t pos, void *buffer, @@ -192,5 +192,8 @@ int file_fat_write(const char *filename, void *buf, loff_t offset, loff_t len, loff_t *actwrite); int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len, loff_t *actread); +int fat_opendir(const char *filename, struct fs_dir_stream **dirsp); +int fat_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp); +void fat_closedir(struct fs_dir_stream *dirs); void fat_close(void); #endif /* _FAT_H_ */ -- 2.13.5 From 21a24c3bf35bac83d66ce4a48eb0c7dd8a7227cb Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sat, 9 Sep 2017 13:15:59 -0400 Subject: [PATCH 41/47] fs/fat: fix case for FAT shortnames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Noticed when comparing our output to linux. There are some lcase bits which control whether filename and/or extension should be downcase'd. Signed-off-by: Rob Clark Reviewed-by: Łukasz Majewski Reviewed-by: Simon Glass --- fs/fat/fat.c | 16 ++++++++++------ fs/fat/fat_write.c | 4 ++-- include/fat.h | 6 +++++- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/fs/fat/fat.c b/fs/fat/fat.c index c951d84f57..65873a2c2a 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -29,11 +29,13 @@ static const int vfat_enabled = 0; #endif /* - * Convert a string to lowercase. + * Convert a string to lowercase. Converts at most 'len' characters, + * 'len' may be larger than the length of 'str' if 'str' is NULL + * terminated. */ -static void downcase(char *str) +static void downcase(char *str, size_t len) { - while (*str != '\0') { + while (*str != '\0' && len--) { *str = tolower(*str); str++; } @@ -131,10 +133,13 @@ static void get_name(dir_entry *dirent, char *s_name) ptr = s_name; while (*ptr && *ptr != ' ') ptr++; + if (dirent->lcase & CASE_LOWER_BASE) + downcase(s_name, (unsigned)(ptr - s_name)); if (dirent->ext[0] && dirent->ext[0] != ' ') { - *ptr = '.'; - ptr++; + *ptr++ = '.'; memcpy(ptr, dirent->ext, 3); + if (dirent->lcase & CASE_LOWER_EXT) + downcase(ptr, 3); ptr[3] = '\0'; while (*ptr && *ptr != ' ') ptr++; @@ -144,7 +149,6 @@ static void get_name(dir_entry *dirent, char *s_name) *s_name = '\0'; else if (*s_name == aRING) *s_name = DELETED_FLAG; - downcase(s_name); } static int flush_dirty_fat_buffer(fsdata *mydata); diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c index 4ca024c208..d0468baf8f 100644 --- a/fs/fat/fat_write.c +++ b/fs/fat/fat_write.c @@ -345,7 +345,7 @@ get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster, *l_name = '\0'; else if (*l_name == aRING) *l_name = DELETED_FLAG; - downcase(l_name); + downcase(l_name, INT_MAX); /* Return the real directory entry */ *retdent = realdent; @@ -981,7 +981,7 @@ static int do_fat_write(const char *filename, void *buffer, loff_t size, memcpy(l_filename, filename, name_len); l_filename[name_len] = 0; /* terminate the string */ - downcase(l_filename); + downcase(l_filename, INT_MAX); startsect = mydata->rootdir_sect; retdent = find_directory_entry(mydata, startsect, diff --git a/include/fat.h b/include/fat.h index 83cd90017e..0f58939124 100644 --- a/include/fat.h +++ b/include/fat.h @@ -128,10 +128,14 @@ typedef struct volume_info /* Boot sign comes last, 2 bytes */ } volume_info; +/* see dir_entry::lcase: */ +#define CASE_LOWER_BASE 8 /* base (name) is lower case */ +#define CASE_LOWER_EXT 16 /* extension is lower case */ + typedef struct dir_entry { char name[8],ext[3]; /* Name and extension */ __u8 attr; /* Attribute bits */ - __u8 lcase; /* Case for base and extension */ + __u8 lcase; /* Case for name and ext (CASE_LOWER_x) */ __u8 ctime_ms; /* Creation time, milliseconds */ __u16 ctime; /* Creation time */ __u16 cdate; /* Creation date */ -- 2.13.5 From 265edc03d5a19550d92cbd6e10631d5a15bdd1d5 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sat, 9 Sep 2017 13:16:00 -0400 Subject: [PATCH 42/47] fs/fat: Clean up open-coded sector <-> cluster conversions Use the clust_to_sect() helper that was introduced earlier, and add an inverse sect_to_clust(), plus update the various spots that open-coded this conversion previously. Signed-off-by: Rob Clark Reviewed-by: Simon Glass --- fs/fat/fat.c | 8 +++----- fs/fat/fat_write.c | 8 +++----- include/fat.h | 6 +++++- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/fs/fat/fat.c b/fs/fat/fat.c index 65873a2c2a..f5f74c12ff 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -257,8 +257,7 @@ get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size) int ret; if (clustnum > 0) { - startsect = mydata->data_begin + - clustnum * mydata->clust_size; + startsect = clust_to_sect(mydata, clustnum); } else { startsect = mydata->rootdir_sect; } @@ -594,9 +593,8 @@ static int get_fs_info(fsdata *mydata) mydata->data_begin = mydata->rootdir_sect + mydata->rootdir_size - (mydata->clust_size * 2); - mydata->root_cluster = (mydata->rootdir_sect - - mydata->data_begin) / - mydata->clust_size; + mydata->root_cluster = + sect_to_clust(mydata, mydata->rootdir_sect); } mydata->fatbufnum = -1; diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c index d0468baf8f..9d2e0ed74c 100644 --- a/fs/fat/fat_write.c +++ b/fs/fat/fat_write.c @@ -502,8 +502,7 @@ set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, int ret; if (clustnum > 0) - startsect = mydata->data_begin + - clustnum * mydata->clust_size; + startsect = clust_to_sect(mydata, clustnum); else startsect = mydata->rootdir_sect; @@ -751,8 +750,7 @@ static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size) __u32 startsect, sect_num, offset; if (clustnum > 0) { - startsect = mydata->data_begin + - clustnum * mydata->clust_size; + startsect = clust_to_sect(mydata, clustnum); } else { startsect = mydata->rootdir_sect; } @@ -791,7 +789,7 @@ static dir_entry *empty_dentptr; static dir_entry *find_directory_entry(fsdata *mydata, int startsect, char *filename, dir_entry *retdent, __u32 start) { - __u32 curclust = (startsect - mydata->data_begin) / mydata->clust_size; + __u32 curclust = sect_to_clust(mydata, startsect); debug("get_dentfromdir: %s\n", filename); diff --git a/include/fat.h b/include/fat.h index 0f58939124..bdeda95e6d 100644 --- a/include/fat.h +++ b/include/fat.h @@ -177,12 +177,16 @@ typedef struct { __u32 root_cluster; /* First cluster of root dir for FAT32 */ } fsdata; -/* TODO clean up places that are open-coding this: */ static inline u32 clust_to_sect(fsdata *fsdata, u32 clust) { return fsdata->data_begin + clust * fsdata->clust_size; } +static inline u32 sect_to_clust(fsdata *fsdata, u32 sect) +{ + return (sect - fsdata->data_begin) / fsdata->clust_size; +} + int file_fat_detectfs(void); int fat_exists(const char *filename); int fat_size(const char *filename, loff_t *size); -- 2.13.5 From 725ffdb5cbcc4b8a9726a68cc6ae0713266ba5a9 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 12 Sep 2017 16:40:01 -0400 Subject: [PATCH 43/47] fs/fat: fix fatbuf leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A new fatbuf was allocated by get_fs_info() (called by fat_itr_root()), but not freed, resulting in eventually running out of memory. Spotted by running 'ls -r' in a large FAT filesystem from Shell.efi. fatbuf is mainly used to cache FAT entry lookups (get_fatent()).. possibly once fat_write.c it can move into the iterator to simplify this. Signed-off-by: Rob Clark Reviewed-by: Simon Glass Reviewed-by: Łukasz Majewski --- fs/fat/fat.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/fs/fat/fat.c b/fs/fat/fat.c index f5f74c12ff..f0284398b4 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -1042,6 +1042,7 @@ int fat_exists(const char *filename) return 0; ret = fat_itr_resolve(itr, filename, TYPE_ANY); + free(fsdata.fatbuf); return ret == 0; } @@ -1061,17 +1062,19 @@ int fat_size(const char *filename, loff_t *size) * Directories don't have size, but fs_size() is not * expected to fail if passed a directory path: */ + free(fsdata.fatbuf); fat_itr_root(itr, &fsdata); if (!fat_itr_resolve(itr, filename, TYPE_DIR)) { *size = 0; - return 0; + ret = 0; } - return ret; + goto out; } *size = FAT2CPU32(itr->dent->size); - - return 0; +out: + free(fsdata.fatbuf); + return ret; } int file_fat_read_at(const char *filename, loff_t pos, void *buffer, @@ -1087,10 +1090,14 @@ int file_fat_read_at(const char *filename, loff_t pos, void *buffer, ret = fat_itr_resolve(itr, filename, TYPE_FILE); if (ret) - return ret; + goto out; printf("reading %s\n", filename); - return get_contents(&fsdata, itr->dent, pos, buffer, maxsize, actread); + ret = get_contents(&fsdata, itr->dent, pos, buffer, maxsize, actread); + +out: + free(fsdata.fatbuf); + return ret; } int file_fat_read(const char *filename, void *buffer, int maxsize) @@ -1126,7 +1133,7 @@ typedef struct { int fat_opendir(const char *filename, struct fs_dir_stream **dirsp) { - fat_dir *dir = malloc(sizeof(*dir)); + fat_dir *dir = calloc(1, sizeof(*dir)); int ret; if (!dir) @@ -1144,6 +1151,7 @@ int fat_opendir(const char *filename, struct fs_dir_stream **dirsp) return 0; fail: + free(dir->fsdata.fatbuf); free(dir); return ret; } @@ -1174,6 +1182,7 @@ int fat_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp) void fat_closedir(struct fs_dir_stream *dirs) { fat_dir *dir = (fat_dir *)dirs; + free(dir->fsdata.fatbuf); free(dir); } -- 2.13.5