Loading...
/*
 * (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