| /* |
| * (C) Copyright 2002 |
| * Stäubli Faverges - <www.staubli.com> |
| * Pierre AUBERT p.aubert@staubli.com |
| * |
| * SPDX-License-Identifier: GPL-2.0+ |
| */ |
| |
| #include <common.h> |
| #include <config.h> |
| #include <malloc.h> |
| |
| #include "dos.h" |
| #include "fdos.h" |
| |
| static int cache_sect; |
| static unsigned char cache [SZ_STD_SECTOR]; |
| |
| |
| #define min(x,y) ((x)<(y)?(x):(y)) |
| |
| static int descend (Slot_t *parent, |
| Fs_t *fs, |
| char *path); |
| |
| /*----------------------------------------------------------------------------- |
| * init_subdir -- |
| *----------------------------------------------------------------------------- |
| */ |
| void init_subdir (void) |
| { |
| cache_sect = -1; |
| } |
| /*----------------------------------------------------------------------------- |
| * basename -- |
| *----------------------------------------------------------------------------- |
| */ |
| char *basename (char *name) |
| { |
| register char *cptr; |
| |
| if (!name || !*name) { |
| return (""); |
| } |
| |
| for (cptr= name; *cptr++; ); |
| while (--cptr >= name) { |
| if (*cptr == '/') { |
| return (cptr + 1); |
| } |
| } |
| return(name); |
| } |
| /*----------------------------------------------------------------------------- |
| * root_map -- |
| *----------------------------------------------------------------------------- |
| */ |
| static int root_map (Fs_t *fs, Slot_t *file, int where, int *len) |
| { |
| *len = min (*len, fs -> dir_len * SZ_STD_SECTOR - where); |
| if (*len < 0 ) { |
| *len = 0; |
| return (-1); |
| } |
| return fs -> dir_start * SZ_STD_SECTOR + where; |
| } |
| /*----------------------------------------------------------------------------- |
| * normal_map -- |
| *----------------------------------------------------------------------------- |
| */ |
| static int normal_map (Fs_t *fs, Slot_t *file, int where, int *len) |
| { |
| int offset; |
| int NrClu; |
| unsigned short RelCluNr; |
| unsigned short CurCluNr; |
| unsigned short NewCluNr; |
| unsigned short AbsCluNr; |
| int clus_size; |
| |
| clus_size = fs -> cluster_size * SZ_STD_SECTOR; |
| offset = where % clus_size; |
| |
| *len = min (*len, file -> FileSize - where); |
| |
| if (*len < 0 ) { |
| *len = 0; |
| return (0); |
| } |
| |
| if (file -> FirstAbsCluNr < 2){ |
| *len = 0; |
| return (0); |
| } |
| |
| RelCluNr = where / clus_size; |
| |
| if (RelCluNr >= file -> PreviousRelCluNr){ |
| CurCluNr = file -> PreviousRelCluNr; |
| AbsCluNr = file -> PreviousAbsCluNr; |
| } else { |
| CurCluNr = 0; |
| AbsCluNr = file -> FirstAbsCluNr; |
| } |
| |
| |
| NrClu = (offset + *len - 1) / clus_size; |
| while (CurCluNr <= RelCluNr + NrClu) { |
| if (CurCluNr == RelCluNr){ |
| /* we have reached the beginning of our zone. Save |
| * coordinates */ |
| file -> PreviousRelCluNr = RelCluNr; |
| file -> PreviousAbsCluNr = AbsCluNr; |
| } |
| NewCluNr = fat_decode (fs, AbsCluNr); |
| if (NewCluNr == 1 || NewCluNr == 0) { |
| PRINTF("Fat problem while decoding %d %x\n", |
| AbsCluNr, NewCluNr); |
| return (-1); |
| } |
| if (CurCluNr == RelCluNr + NrClu) { |
| break; |
| } |
| |
| if (CurCluNr < RelCluNr && NewCluNr == FAT12_END) { |
| *len = 0; |
| return 0; |
| } |
| |
| if (CurCluNr >= RelCluNr && NewCluNr != AbsCluNr + 1) |
| break; |
| CurCluNr++; |
| AbsCluNr = NewCluNr; |
| } |
| |
| *len = min (*len, (1 + CurCluNr - RelCluNr) * clus_size - offset); |
| |
| return (((file -> PreviousAbsCluNr - 2) * fs -> cluster_size + |
| fs -> dir_start + fs -> dir_len) * |
| SZ_STD_SECTOR + offset); |
| } |
| /*----------------------------------------------------------------------------- |
| * open_subdir -- open the subdir containing the file |
| *----------------------------------------------------------------------------- |
| */ |
| int open_subdir (File_t *desc) |
| { |
| char *pathname; |
| char *tmp, *s, *path; |
| char terminator; |
| |
| if ((pathname = (char *)malloc (MAX_PATH)) == NULL) { |
| return (-1); |
| } |
| |
| strcpy (pathname, desc -> name); |
| |
| /* Suppress file name */ |
| tmp = basename (pathname); |
| *tmp = '\0'; |
| |
| /* root directory init */ |
| desc -> subdir.FirstAbsCluNr = 0; |
| desc -> subdir.FileSize = -1; |
| desc -> subdir.map = root_map; |
| desc -> subdir.dir.attr = ATTR_DIRECTORY; |
| |
| tmp = pathname; |
| for (s = tmp; ; ++s) { |
| if (*s == '/' || *s == '\0') { |
| path = tmp; |
| terminator = *s; |
| *s = '\0'; |
| if (s != tmp && strcmp (path,".")) { |
| if (descend (&desc -> subdir, desc -> fs, path) < 0) { |
| free (pathname); |
| return (-1); |
| } |
| } |
| if (terminator == 0) { |
| break; |
| } |
| tmp = s + 1; |
| } |
| } |
| free (pathname); |
| return (0); |
| } |
| /*----------------------------------------------------------------------------- |
| * descend -- |
| *----------------------------------------------------------------------------- |
| */ |
| static int descend (Slot_t *parent, |
| Fs_t *fs, |
| char *path) |
| { |
| int entry; |
| Slot_t SubDir; |
| |
| if(path[0] == '\0' || strcmp (path, ".") == 0) { |
| return (0); |
| } |
| |
| |
| entry = 0; |
| if (vfat_lookup (parent, |
| fs, |
| &(SubDir.dir), |
| &entry, |
| 0, |
| path, |
| ACCEPT_DIR | SINGLE | DO_OPEN, |
| 0, |
| &SubDir) == 0) { |
| *parent = SubDir; |
| return (0); |
| } |
| |
| if (strcmp(path, "..") == 0) { |
| parent -> FileSize = -1; |
| parent -> FirstAbsCluNr = 0; |
| parent -> map = root_map; |
| return (0); |
| } |
| return (-1); |
| } |
| /*----------------------------------------------------------------------------- |
| * open_file -- |
| *----------------------------------------------------------------------------- |
| */ |
| int open_file (Slot_t *file, Directory_t *dir) |
| { |
| int first; |
| unsigned long size; |
| |
| first = __le16_to_cpu (dir -> start); |
| |
| if(first == 0 && |
| (dir -> attr & ATTR_DIRECTORY) != 0) { |
| file -> FirstAbsCluNr = 0; |
| file -> FileSize = -1; |
| file -> map = root_map; |
| return (0); |
| } |
| |
| if ((dir -> attr & ATTR_DIRECTORY) != 0) { |
| size = (1UL << 31) - 1; |
| } |
| else { |
| size = __le32_to_cpu (dir -> size); |
| } |
| |
| file -> map = normal_map; |
| file -> FirstAbsCluNr = first; |
| file -> PreviousRelCluNr = 0xffff; |
| file -> FileSize = size; |
| return (0); |
| } |
| /*----------------------------------------------------------------------------- |
| * read_file -- |
| *----------------------------------------------------------------------------- |
| */ |
| int read_file (Fs_t *fs, |
| Slot_t *file, |
| char *buf, |
| int where, |
| int len) |
| { |
| int pos; |
| int read, nb, sect, offset; |
| |
| pos = file -> map (fs, file, where, &len); |
| if (pos < 0) { |
| return -1; |
| } |
| if (len == 0) { |
| return (0); |
| } |
| |
| /* Compute sector number */ |
| sect = pos / SZ_STD_SECTOR; |
| offset = pos % SZ_STD_SECTOR; |
| read = 0; |
| |
| if (offset) { |
| /* Read doesn't start at the sector beginning. We need to use our */ |
| /* cache */ |
| if (sect != cache_sect) { |
| if (dev_read (cache, sect, 1) < 0) { |
| return (-1); |
| } |
| cache_sect = sect; |
| } |
| nb = min (len, SZ_STD_SECTOR - offset); |
| |
| memcpy (buf, cache + offset, nb); |
| read += nb; |
| len -= nb; |
| sect += 1; |
| } |
| |
| if (len > SZ_STD_SECTOR) { |
| nb = (len - 1) / SZ_STD_SECTOR; |
| if (dev_read (buf + read, sect, nb) < 0) { |
| return ((read) ? read : -1); |
| } |
| /* update sector position */ |
| sect += nb; |
| |
| /* Update byte position */ |
| nb *= SZ_STD_SECTOR; |
| read += nb; |
| len -= nb; |
| } |
| |
| if (len) { |
| if (sect != cache_sect) { |
| if (dev_read (cache, sect, 1) < 0) { |
| return ((read) ? read : -1); |
| cache_sect = -1; |
| } |
| cache_sect = sect; |
| } |
| |
| memcpy (buf + read, cache, len); |
| read += len; |
| } |
| return (read); |
| } |