| /* | |
| * (C) Copyright 2002 | |
| * Stäubli Faverges - <www.staubli.com> | |
| * Pierre AUBERT p.aubert@staubli.com | |
| * | |
| * See file CREDITS for list of people who contributed to this | |
| * project. | |
| * | |
| * This program is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU General Public License as | |
| * published by the Free Software Foundation; either version 2 of | |
| * the License, or (at your option) any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
| * MA 02111-1307 USA | |
| */ | |
| #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); | |
| } |