|  | /* | 
|  | * (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> | 
|  |  | 
|  | #if (CONFIG_COMMANDS & CFG_CMD_FDOS) | 
|  |  | 
|  | #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); | 
|  | } | 
|  | #endif |