diff options
author | wdenk <wdenk> | 2002-11-18 00:14:45 +0000 |
---|---|---|
committer | wdenk <wdenk> | 2002-11-18 00:14:45 +0000 |
commit | 2262cfeef91458b01a1bfe3812ccbbfdf8b82807 (patch) | |
tree | 3657d48c8ce9089fc96682848859d035a1e8f115 /fs/fdos/subdir.c | |
parent | 1d0350ed0b1b0f63e3fb5db6b19397b84a2ea1c7 (diff) |
* Patch by Daniel Engström, 13 Nov 2002:LABEL_2002_11_18_0115
Add support for i386 architecture and AMD SC520 board
* Patch by Pierre Aubert, 12 Nov 2002:
Add support for DOS filesystem and booting from DOS floppy disk
Diffstat (limited to 'fs/fdos/subdir.c')
-rw-r--r-- | fs/fdos/subdir.c | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/fs/fdos/subdir.c b/fs/fdos/subdir.c new file mode 100644 index 00000000000..5911f2e88f4 --- /dev/null +++ b/fs/fdos/subdir.c @@ -0,0 +1,348 @@ +/* + * (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 |