Ejemplo n.º 1
0
Archivo: app3.c Proyecto: abbrev/fatfs
DWORD allocate_contiguous_clusters (    /* Returns the first sector in LBA (0:error or not contiguous) */
    FIL* fp,    /* Pointer to the open file object */
    DWORD len   /* Number of bytes to allocate */
)
{
    DWORD csz, tcl, ncl, ccl, cl;


    if (f_lseek(fp, 0) || !len)     /* Check if the given parameters are valid */
        return 0;
    csz = 512UL * fp->fs->csize;    /* Cluster size in unit of byte (assuming 512 bytes/sector) */
    tcl = (len + csz - 1) / csz;    /* Total number of clusters required */
    len = tcl * csz;                /* Round-up file size to the cluster boundary */

    /* Check if the existing cluster chain is contiguous */
    if (len == fp->fsize) {
        ncl = 0; ccl = fp->sclust;
        do {
            cl = get_fat(fp->fs, ccl);  /* Get the cluster status */
            if (cl + 1 < 3) return 0;   /* Hard error? */
            if (cl != ccl + 1 && cl < fp->fs->n_fatent) break;  /* Not contiguous? */
            ccl = cl;
        } while (++ncl < tcl);
        if (ncl == tcl)             /* Is the file contiguous? */
            return clust2sect(fp->fs, fp->sclust);  /* File is contiguous. Return the start sector */
    }

    /* File is not contiguous */
#if _FS_READONLY
    return 0;								/* Exit if in read-only cfg. */
#else
    if (!(fp->flag & FA_WRITE)) return 0;   /* Exit if the file object is for read-only */

    if (f_truncate(fp)) return 0;           /* Remove the non-contiguous chain */

    /* Find a free contiguous area */
    ccl = cl = 2; ncl = 0;
    do {
        if (cl >= fp->fs->n_fatent) return 0;   /* No contiguous area is found. */
        if (get_fat(fp->fs, cl)) {  /* Encounterd a cluster in use */
            do {    /* Skip the block of used clusters */
                cl++;
                if (cl >= fp->fs->n_fatent) return 0;   /* No contiguous area is found. */
            } while (get_fat(fp->fs, cl));
            ccl = cl; ncl = 0;
        }
        cl++; ncl++;
    } while (ncl < tcl);

    /* Create a contiguous cluster chain */
    fp->fs->last_clust = ccl - 1;
    if (f_lseek(fp, len)) return 0;

    return clust2sect(fp->fs, fp->sclust);  /* Return file start sector */
#endif
}
Ejemplo n.º 2
0
FRESULT pf_write (
	const void* buff,	/* Pointer to the data to be written */
	UINT btw,			/* Number of bytes to write (0:Finalize the current write operation) */
	UINT* bw			/* Pointer to number of bytes written */
)
{
	CLUST clst;
	DWORD sect, remain;
	const BYTE *p = buff;
	BYTE cs;
	UINT wcnt;
	FATFS *fs = FatFs;


	*bw = 0;
	if (!fs) return FR_NOT_ENABLED;		/* Check file system */
	if (!(fs->flag & FA_OPENED))		/* Check if opened */
		return FR_NOT_OPENED;

	if (!btw) {		/* Finalize request */
		if ((fs->flag & FA__WIP) && disk_writep(0, 0)) ABORT(FR_DISK_ERR);
		fs->flag &= ~FA__WIP;
		return FR_OK;
	} else {		/* Write data request */
		if (!(fs->flag & FA__WIP))		/* Round-down fptr to the sector boundary */
			fs->fptr &= 0xFFFFFE00;
	}
	remain = fs->fsize - fs->fptr;
	if (btw > remain) btw = (UINT)remain;			/* Truncate btw by remaining bytes */

	while (btw)	{									/* Repeat until all data transferred */
		if ((UINT)fs->fptr % 512 == 0) {			/* On the sector boundary? */
			cs = (BYTE)(fs->fptr / 512 & (fs->csize - 1));	/* Sector offset in the cluster */
			if (!cs) {								/* On the cluster boundary? */
				if (fs->fptr == 0)					/* On the top of the file? */
					clst = fs->org_clust;
				else
					clst = get_fat(fs->curr_clust);
				if (clst <= 1) ABORT(FR_DISK_ERR);
				fs->curr_clust = clst;				/* Update current cluster */
			}
			sect = clust2sect(fs->curr_clust);		/* Get current sector */
			if (!sect) ABORT(FR_DISK_ERR);
			fs->dsect = sect + cs;
			if (disk_writep(0, fs->dsect)) ABORT(FR_DISK_ERR);	/* Initiate a sector write operation */
			fs->flag |= FA__WIP;
		}
		wcnt = 512 - (UINT)fs->fptr % 512;			/* Number of bytes to write to the sector */
		if (wcnt > btw) wcnt = btw;
		if (disk_writep(p, wcnt)) ABORT(FR_DISK_ERR);	/* Send data to the sector */
		fs->fptr += wcnt; p += wcnt;				/* Update pointers and counters */
		btw -= wcnt; *bw += wcnt;
		if ((UINT)fs->fptr % 512 == 0) {
			if (disk_writep(0, 0)) ABORT(FR_DISK_ERR);	/* Finalize the currtent secter write operation */
			fs->flag &= ~FA__WIP;
		}
	}

	return FR_OK;
}
Ejemplo n.º 3
0
static FRESULT dir_next (	/* FR_OK:Succeeded, FR_NO_FILE:End of table */
	DIR *dj			/* Pointer to directory object */
)
{
	CLUST clst;
	u16 i;
	FATFS *fs = FatFs;

	i = dj->index + 1;
	if (!i || !dj->sect)	/* Report EOT when index has reached 65535 */
		return FR_NO_FILE;

	if (!(i & (16-1))) {	/* Sector changed? */
		dj->sect++;			/* Next sector */

		if (dj->clust == 0) {	/* Static table */
			if (i >= fs->n_rootdir)	/* Report EOT when end of table */
				return FR_NO_FILE;
		}
		else {					/* Dynamic table */
			if (((i / 16) & (fs->csize-1)) == 0) {	/* Cluster changed? */
				clst = get_fat(dj->clust);		/* Get next cluster */
				if (clst <= 1) return FR_DISK_ERR;
				if (clst >= fs->max_clust)		/* When it reached end of dynamic table */
					return FR_NO_FILE;			/* Report EOT */
				dj->clust = clst;				/* Initialize data for new cluster */
				dj->sect = clust2sect(clst);
			}
		}
	}

	dj->index = i;

	return FR_OK;
}
Ejemplo n.º 4
0
FRESULT pf_write (
	const void* buff,	/* Pointer to the data to be written */
	u16 btw,			/* Number of bytes to write (0:Finalize the current write operation) */
	u16* bw			/* Pointer to number of bytes written */
)
{
	CLUST clst;
	u32 sect, remain;
	const u8 *p = buff;
	u16 wcnt;
	FATFS *fs = FatFs;

	*bw = 0;
	if (!fs) return FR_NOT_ENABLED;		/* Check file system */
	if (!(fs->flag & FA_OPENED))		/* Check if opened */
		return FR_NOT_OPENED;

	if (!btw) {		/* Finalize request */
		if ((fs->flag & FA__WIP) && disk_writep(0, 0)) goto fw_abort;
		fs->flag &= ~FA__WIP;
		return FR_OK;
	} else {		/* Write data request */
		if (!(fs->flag & FA__WIP))		/* Round down fptr to the sector boundary */
			fs->fptr &= 0xFFFFFE00;
	}
	remain = fs->fsize - fs->fptr;
	if (btw > remain) btw = (u16)remain;			/* Truncate btw by remaining bytes */
	while (btw)	{									/* Repeat until all data transferred */
		if (((u16)fs->fptr % 512) == 0) {				/* On the sector boundary? */
			if ((fs->fptr / 512 % fs->csize) == 0) {	/* On the cluster boundary? */
				clst = (fs->fptr == 0) ?			/* On the top of the file? */
					fs->org_clust : get_fat(fs->curr_clust);
				if (clst <= 1) goto fw_abort;
				fs->curr_clust = clst;				/* Update current cluster */
				fs->csect = 0;						/* Reset sector offset in the cluster */
			}
			sect = clust2sect(fs->curr_clust);		/* Get current sector */
			if (!sect) goto fw_abort;
			fs->dsect = sect + fs->csect++;
			if (disk_writep(0, fs->dsect)) goto fw_abort;	/* Initiate a sector write operation */
			fs->flag |= FA__WIP;
		}
		wcnt = 512 - ((u16)fs->fptr % 512);		/* Number of bytes to write to the sector */
		if (wcnt > btw) wcnt = btw;
		if (disk_writep(p, wcnt)) goto fw_abort;	/* Send data to the sector */
		fs->fptr += wcnt; p += wcnt;				/* Update pointers and counters */
		btw -= wcnt; *bw += wcnt;
		if (((u16)fs->fptr % 512) == 0) {
			if (disk_writep(0, 0)) goto fw_abort;	/* Finalize the currtent secter write operation */
			fs->flag &= ~FA__WIP;
		}
	}

	return FR_OK;

fw_abort:
	fs->flag = 0;
	return FR_DISK_ERR;
}
Ejemplo n.º 5
0
FRESULT pf_read (
	void* dest,		/* Pointer to the destination object */
	WORD btr,		/* Number of bytes to read (bit15:destination) */
	WORD* br		/* Pointer to number of bytes read */
)
{
	DRESULT dr;
	CLUST clst;
	DWORD sect, remain;
	WORD rcnt;
	BYTE *rbuff = dest;
	FATFS *fs = FatFs;


	*br = 0;
	if (!fs) return FR_NOT_ENABLED;		/* Check file system */
	if (!(fs->flag & FA_READ))
			return FR_INVALID_OBJECT;

	remain = fs->fsize - fs->fptr;
	if (btr > remain) btr = (UINT)remain;			/* Truncate btr by remaining bytes */

	for ( ;  btr;									/* Repeat until all data transferred */
		rbuff += rcnt, fs->fptr += rcnt, *br += rcnt, btr -= rcnt) {
		if ((fs->fptr % 512) == 0) {				/* On the sector boundary? */
			if ((fs->fptr / 512 % fs->csize) == 0) {	/* On the cluster boundary? */
				clst = (fs->fptr == 0) ?			/* On the top of the file? */
					fs->org_clust : get_fat(fs->curr_clust);
				if (clst <= 1) {
					fs->flag = 0; return FR_DISK_ERR;
				}
				fs->curr_clust = clst;				/* Update current cluster */
				fs->csect = 0;						/* Reset sector offset in the cluster */
			}
			sect = clust2sect(fs->curr_clust);		/* Get current sector */
			if (!sect) {
				fs->flag = 0; return FR_DISK_ERR;
			}
			sect += fs->csect;
			fs->dsect = sect;
			fs->csect++;							/* Next sector address in the cluster */
		}
		rcnt = 512 - ((WORD)fs->fptr % 512);		/* Get partial sector data from sector buffer */
		if (rcnt > btr) rcnt = btr;
		if (fs->flag & FA_STREAM) {
			dr = disk_readp(dest, fs->dsect, (WORD)(fs->fptr % 512), (WORD)(rcnt | 0x8000));
		} else {
			dr = disk_readp(rbuff, fs->dsect, (WORD)(fs->fptr % 512), rcnt);
		}
		if (dr) {
			fs->flag = 0;
			return (dr == RES_STRERR) ? FR_STREAM_ERR : FR_DISK_ERR;
		}
	}

	return FR_OK;
}
Ejemplo n.º 6
0
FRESULT pf_write (
	const void* buff,	/* Pointer to the data to be written */
	WORD btw,			/* Number of bytes to write (0:Finalize the current write operation) */
	WORD* bw			/* Pointer to number of bytes written */
)
{
	DRESULT dr;
	CLUST clst;
	DWORD sect, remain;
	WORD rcnt;
	BYTE cs;
	const BYTE *rbuff = buff;
	FATFS *fs = FatFs;


	*bw = 0;
	if (!fs) return FR_NOT_ENABLED;		/* Check file system */
	if (!(fs->flag & FA_OPENED))		/* Check if opened */
		return FR_NOT_OPENED;

	remain = fs->fsize - fs->fptr;
	if (btw > remain) btw = (WORD)remain;			/* Truncate btr by remaining bytes */

	while (btw)	{									/* Repeat until all data transferred */
		if ((fs->fptr % 512) == 0) {				/* On the sector boundary? */
			cs = (BYTE)(fs->fptr / 512 & (fs->csize - 1));	/* Sector offset in the cluster */
			if (!cs) {								/* On the cluster boundary? */
				clst = (fs->fptr == 0) ?			/* On the top of the file? */
					fs->org_clust : get_fat(fs->curr_clust);
				if (clst <= 1) goto fr_abort;
				fs->curr_clust = clst;				/* Update current cluster */
			}
			sect = clust2sect(fs->curr_clust);		/* Get current sector */
			if (!sect) goto fr_abort;
			fs->dsect = sect + cs;
		}
		rcnt = (WORD)(512 - (fs->fptr % 512));		/* Get partial sector data from sector buffer */
		if (rcnt > btw) rcnt = btw;
		dr = disk_writep(!buff ? 0 : rbuff, fs->dsect, (WORD)(fs->fptr % 512), rcnt);
		if (dr) goto fr_abort;
		fs->fptr += rcnt; rbuff += rcnt;			/* Update pointers and counters */
		btw -= rcnt; *bw += rcnt;
	}

	return FR_OK;

fr_abort:
	fs->flag = 0;
	return FR_DISK_ERR;
}
Ejemplo n.º 7
0
FRESULT pf_read (
	void* buff,		/* Pointer to the read buffer (NULL:Forward data to the stream)*/
	WORD btr,		/* Number of bytes to read */
	WORD* br		/* Pointer to number of bytes read */
)
{
	DRESULT dr;
	CLUST clst;
	DWORD sect, remain;
	WORD rcnt;
	BYTE cs, *rbuff = buff;
	FATFS *fs = FatFs;


	*br = 0;
	if (!fs) return FR_NOT_ENABLED;		/* Check file system */
	if (!(fs->flag & FA_OPENED))		/* Check if opened */
		return FR_NOT_OPENED;
	//led_sign_ff(5);
	remain = fs->fsize - fs->fptr;
	if (btr > remain) btr = (WORD)remain;			/* Truncate btr by remaining bytes */

	while (btr)	{									/* Repeat until all data transferred */
		if ((fs->fptr % 512) == 0) {				/* On the sector boundary? */
			cs = (BYTE)(fs->fptr / 512 & (fs->csize - 1));	/* Sector offset in the cluster */
			if (!cs) {								/* On the cluster boundary? */
				clst = (fs->fptr == 0) ?			/* On the top of the file? */
					fs->org_clust : get_fat(fs->curr_clust);
				if (clst <= 1) goto fr_abort;
				fs->curr_clust = clst;				/* Update current cluster */
			}
			sect = clust2sect(fs->curr_clust);		/* Get current sector */
			if (!sect) goto fr_abort;
			fs->dsect = sect + cs;
		}
		rcnt = (WORD)(512 - (fs->fptr % 512));		/* Get partial sector data from sector buffer */
		if (rcnt > btr) rcnt = btr;
		dr = disk_readp(!buff ? 0 : rbuff, fs->dsect, (WORD)(fs->fptr % 512), rcnt);
		if (dr) goto fr_abort;
		fs->fptr += rcnt; rbuff += rcnt;			/* Update pointers and counters */
		btr -= rcnt; *br += rcnt;
	}

	return FR_OK;

fr_abort:
	fs->flag = 0;
	return FR_DISK_ERR;
}
Ejemplo n.º 8
0
FRESULT pf_lseek (
	DWORD ofs		/* File pointer from top of file */
)
{
	CLUST clst;
	DWORD bcs, sect, ifptr;
	FATFS *fs = FatFs;


	if (!fs) return FR_NOT_ENABLED;		/* Check file system */
	if (!(fs->flag & FA_OPENED))		/* Check if opened */
			return FR_NOT_OPENED;

	if (ofs > fs->fsize) ofs = fs->fsize;	/* Clip offset with the file size */
	ifptr = fs->fptr;
	fs->fptr = 0;
	if (ofs > 0) {
		bcs = (DWORD)fs->csize * 512;	/* Cluster size (byte) */
		if (ifptr > 0 &&
			(ofs - 1) / bcs >= (ifptr - 1) / bcs) {	/* When seek to same or following cluster, */
			fs->fptr = (ifptr - 1) & ~(bcs - 1);	/* start from the current cluster */
			ofs -= fs->fptr;
			clst = fs->curr_clust;
		} else {							/* When seek to back cluster, */
			clst = fs->org_clust;			/* start from the first cluster */
			fs->curr_clust = clst;
		}
		while (ofs > bcs) {				/* Cluster following loop */
			clst = get_fat(clst);		/* Follow cluster chain */
			if (clst <= 1 || clst >= fs->max_clust) goto fe_abort;
			fs->curr_clust = clst;
			fs->fptr += bcs;
			ofs -= bcs;
		}
		fs->fptr += ofs;
		sect = clust2sect(clst);		/* Current sector */
		if (!sect) goto fe_abort;
		fs->csect = (BYTE)(ofs / 512);	/* Sector offset in the cluster */
		if (ofs % 512)
			fs->dsect = sect + fs->csect++;
	}

	return FR_OK;

fe_abort:
	fs->flag = 0;
	return FR_DISK_ERR;
}
Ejemplo n.º 9
0
FRESULT pf_read (
        FATFS* fs,          /* Filesystem descriptor */
        void* buff,		/* Pointer to the read buffer (NULL:Forward data to the stream)*/
        UINT btr,		/* Number of bytes to read */
        UINT* br		/* Pointer to number of bytes read */
        )
{
    DRESULT dr;
    CLUST clst;
    DWORD sect, remain;
    UINT rcnt;
    BYTE cs, *rbuff = buff;


    *br = 0;
    if (!fs) return FR_NOT_ENABLED;		/* Check file system */
    if (!(fs->flag & FA_OPENED))		/* Check if opened */
        return FR_NOT_OPENED;

    remain = fs->fsize - fs->fptr;
    if (btr > remain) btr = (UINT)remain;			/* Truncate btr by remaining bytes */

    while (btr)	{									/* Repeat until all data transferred */
        if ((fs->fptr % 512) == 0) {				/* On the sector boundary? */
            cs = (BYTE)(fs->fptr / 512 & (fs->csize - 1));	/* Sector offset in the cluster */
            if (!cs) {								/* On the cluster boundary? */
                if (fs->fptr == 0)					/* On the top of the file? */
                    clst = fs->org_clust;
                else
                    clst = get_fat(fs, fs->curr_clust);
                if (clst <= 1) ABORT(FR_DISK_ERR);
                fs->curr_clust = clst;				/* Update current cluster */
            }
            sect = clust2sect(fs, fs->curr_clust);		/* Get current sector */
            if (!sect) ABORT(FR_DISK_ERR);
            fs->dsect = sect + cs;
        }
        rcnt = 512 - (UINT)fs->fptr % 512;			/* Get partial sector data from sector buffer */
        if (rcnt > btr) rcnt = btr;
        dr = disk_readp(&fs->disk, !buff ? 0 : rbuff, fs->dsect, (UINT)fs->fptr % 512, rcnt);
        if (dr) ABORT(FR_DISK_ERR);
        fs->fptr += rcnt; rbuff += rcnt;			/* Update pointers and counters */
        btr -= rcnt; *br += rcnt;
    }

    return FR_OK;
}
Ejemplo n.º 10
0
static
FRESULT dir_rewind(
        FATFS* fs,      /* Filesystem descriptor */
        DIR *dj			/* Pointer to directory object */
        )
{
    CLUST clst;

    dj->index = 0;
    clst = dj->sclust;
    if (clst == 1 || clst >= fs->n_fatent)	/* Check start cluster range */
        return FR_DISK_ERR;
    if (_FS_FAT32 && !clst && (_FS_32ONLY || fs->fs_type == FS_FAT32))	/* Replace cluster# 0 with root cluster# if in FAT32 */
        clst = (CLUST)fs->dirbase;
    dj->clust = clst;						/* Current cluster */
    dj->sect = (_FS_32ONLY || clst) ? clust2sect(fs, clst) : fs->dirbase;	/* Current sector */

    return FR_OK;	/* Seek succeeded */
}
Ejemplo n.º 11
0
static FRESULT dir_rewind (
	DIR *dj			/* Pointer to directory object */
)
{
	CLUST clst;
	FATFS *fs = FatFs;

	dj->index = 0;
	clst = dj->sclust;
	if (clst == 1 || clst >= fs->max_clust)	/* Check start cluster range */
		return FR_DISK_ERR;
#if _FS_FAT32
	if (!clst && fs->fs_type == FS_FAT32)	/* Replace cluster# 0 with root cluster# if in FAT32 */
		clst = fs->dirbase;
#endif
	dj->clust = clst;						/* Current cluster */
	dj->sect = clst ? clust2sect(clst) : fs->dirbase;	/* Current sector */

	return FR_OK;	/* Seek succeeded */
}
Ejemplo n.º 12
0
int
ev_file_get_next_block(struct ev_file_status *st)
{
  uint32_t sector_offset, clust;

  /*
    Reset st->state here by default, so we can just return error code in
    all error cases without worrying about forgetting to reset state
    in some corner case.
  */
  uint8_t state = st->state;
  st->state = ST_STARTING;

  switch (state)
  {
  case ST_STARTING:
    ++st->st_get_block_done.sector;
    /* Figure out if we need to cross a cluster boundary. */
    sector_offset = st->st_get_block_done.sector - st->data_first_sector;
    if (sector_offset % st->cluster_size)
      return EV_FILE_ST_DONE;

    /* Load next cluster entry from FAT. */
    clust = st->file_cluster;

    if (clust < 2 || clust >= st->number_of_clusters + 2)
      return EV_FILE_ST_EUNSPC;
    return prep_read_fat_entry(clust, ST_LOOKUP_FILE_FAT, st);

  case ST_LOOKUP_FILE_FAT:
    st->file_cluster = st->locate_dir.cur_cluster;
    st->st_get_block_done.sector = clust2sect(st->file_cluster, st);
    return EV_FILE_ST_DONE;

  default:
    return EV_FILE_ST_EUNSPC;
  };
}
Ejemplo n.º 13
0
int
ev_file_get_first_block(const char *filename, struct ev_file_status *st)
{
  uint8_t si, di;
  char c;
  uint32_t clust;

  /*
    Reset st->state here by default, so we can just return error code in
    all error cases without worrying about forgetting to reset state
    in some corner case.
  */
  uint8_t state = st->state;
  st->state = ST_STARTING;

  switch (state)
  {
  case ST_STARTING:
    /*
      First we need to read sector 0 to look for the root of a FAT file
      system, or possibly a BIOS partition table.
      (We don't strictly need the first few bytes, but it seems cleaner to
      just read a full sector).
    */
    st->st_stream_bytes.sec = 0;
    st->st_stream_bytes.offset = 0;
    st->st_stream_bytes.len = 512;
    st->state = ST_STREAM_BLOCK_0;
    st->idx = 0;
    st->locate_fat.flags = 0;
    DEBUG(("Start reading FAT\n"));
    return EV_FILE_ST_STREAM_BYTES;

  case ST_STREAM_BLOCK_0:
    if (st->locate_fat.flags & FL_FAT_NO55AA)
      return EV_FILE_ST_EBADFS;
    /*
      We've read the first block on the device.

      Check if it is a FAT file system; if not check if it is a BIOS partition
      table and try the first partition.
    */
    if ((st->locate_fat.flags & (FL_FAT_NOFAT16|FL_FAT_NOFAT32)) ==
        (FL_FAT_NOFAT16|FL_FAT_NOFAT32))
    {
      /*
        Not a FAT file system, try find partition 0 in a BIOS
        partition table.
      */
      DEBUG(("Not FAT in sector 0, try first BIOS partion\n"));
      if (!(st->locate_fat.flags & FL_FAT_PART1))
        return EV_FILE_ST_EBADFS;
      /*
        Save partition start temporarily, as partition_start_lba will be
        overwritten during the next sector read.
      */
      st->fat_first_sector = st->locate_fat.partition_start_lba;
      st->st_stream_bytes.sec = st->locate_fat.partition_start_lba;
      st->st_stream_bytes.offset = 0;
      st->st_stream_bytes.len = 512;
      st->state = ST_STREAM_PART_BLOCK_0;
      st->idx = 0;
      st->locate_fat.flags = 0;
      return EV_FILE_ST_STREAM_BYTES;
    }
    DEBUG(("Using FAT file system in sector 0\n"));
    /* Fallthrough. */

  case ST_STREAM_PART_BLOCK_0:
    if ((st->locate_fat.flags & FL_FAT_NO55AA) ||
        (st->locate_fat.flags & (FL_FAT_NOFAT16|FL_FAT_NOFAT32)) ==
          (FL_FAT_NOFAT16|FL_FAT_NOFAT32))
      return EV_FILE_ST_EBADFS;

    /*
      We found something that has the signatures to look like a FAT file system.

      Now do some sanity checks on the read data, and load it into the permanent
      part of our structure if it looks ok.
    */
    uint32_t base_sector = (state == ST_STREAM_PART_BLOCK_0 ?
                            st->fat_first_sector : 0);
    uint32_t remain_sectors = st->locate_fat.number_of_sectors;

    if (st->locate_fat.reserved_sectors >= remain_sectors)
      return EV_FILE_ST_EBADFS;
    remain_sectors -= st->locate_fat.reserved_sectors;
    st->fat_first_sector = base_sector + st->locate_fat.reserved_sectors;

    uint32_t fat_tot_size =
      st->locate_fat.fat_sector_size * st->locate_fat.number_of_fats;
    if (fat_tot_size >= remain_sectors)
      return EV_FILE_ST_EBADFS;
    remain_sectors -= fat_tot_size;
    uint16_t num_rootdir_sectors = (st->num_rootdir_entries + 15)/16;
    if (num_rootdir_sectors >= remain_sectors)
      return EV_FILE_ST_EBADFS;
    remain_sectors -= num_rootdir_sectors;
    st->number_of_clusters = remain_sectors / st->cluster_size;

    st->data_first_sector =
      st->fat_first_sector + fat_tot_size + num_rootdir_sectors;

    if (st->number_of_clusters >= 0xfff5)
      st->flags = FL_FAT32;
    else if (st->number_of_clusters >= 0xff5)
      st->flags = FL_FAT16;
    else
      st->flags = FL_FAT12;
    DEBUG(("Found %s in sector %u\n", st->flags == FL_FAT32 ? "FAT32" :
           (st->flags == FL_FAT16 ? "FAT16" : "FAT12"), base_sector));

    /* For FAT12/FAT16, root dir is of fixed size, just after the FAT. */
    if ((st->flags & FL_TYPEMASK) != FL_FAT32)
    {
      st->root_dir_start = st->fat_first_sector + fat_tot_size;
      st->flags |= FL_FIXED_DIR;
    }
    DEBUG(("#sectors=%u #clusters=%u FAT size=%u rootdir at %u\n",
           st->locate_fat.number_of_sectors, st->number_of_clusters,
           st->locate_fat.fat_sector_size, st->root_dir_start));

    state = ST_FIND_IN_DIR;
    /* Fall through ...*/

  case ST_FIND_IN_DIR:
    /* Read the first sector of the directory, looking for the file name. */
    si = 0;
    di = 0;

    /* Copy over file name, padding and uppercasing for easy comparison. */
    for (;;)
    {
      c = filename[si++];
      if (c == '\0' || c == '.')
        break;
      if (di >= 8)
        return EV_FILE_ST_ENAME;
      if (c >= 'a' && c <= 'z')
        c = c - ('a' - 'A');
      st->locate_dir.name[di++] = c;
    }
    while (di < 8)
      st->locate_dir.name[di++] = ' ';
    if (c)
    {
      for (;;)
      {
        c = filename[si++];
        if (c == '\0')
          break;
        if (di >= 11 || c == '.')
          return EV_FILE_ST_ENAME;
      if (c >= 'a' && c <= 'z')
        c = c - ('a' - 'A');
        st->locate_dir.name[di++] = c;
      }
    }
    while (di < 11)
      st->locate_dir.name[di++] = ' ';

    st->idx = 0;
    st->locate_dir.remain_entries_in_dir = 16;
    st->locate_dir.flags = 0;
    st->locate_dir.cur_sector = 0;
    if (st->flags & FL_FIXED_DIR)
      st->st_stream_bytes.sec = st->root_dir_start;
    else
    {
      st->locate_dir.cur_cluster = st->root_dir_start;
      st->st_stream_bytes.sec = clust2sect(st->locate_dir.cur_cluster, st);
    }
    st->st_stream_bytes.offset = 0;
    st->st_stream_bytes.len = 512;
    st->state = ST_STREAM_DIR;
    return EV_FILE_ST_STREAM_BYTES;

  case ST_STREAM_DIR:
    /*
      We read and scanned a directory entry.

      Check if we found what we were looking for, else read the FAT to
      find the next directory entry, if any.
    */
    if (st->locate_dir.flags & FL_DIR_FOUND)
    {
      /* Found it! */
      st->file_cluster = st->locate_dir.start_cluster;
      st->st_get_block_done.length = st->locate_dir.file_length;
      st->st_get_block_done.sector = clust2sect(st->file_cluster, st);
      DEBUG(("File found! sector=%u len=%u cluster=%u\n",
             st->st_get_block_done.sector, st->locate_dir.file_length,
             st->locate_dir.start_cluster));
      return EV_FILE_ST_DONE;
    }

    if (st->locate_dir.flags & FL_DIR_END)
    {
      /* Reached the end without finding anything. */
      DEBUG(("Reached end of directory without finding file.\n"));
      return EV_FILE_ST_ENOENTRY;
    }

    /* We need to go scan the next sector of the directory, if any. */
    ++st->locate_dir.cur_sector;
    if (st->locate_dir.cur_sector < (st->flags & FL_FIXED_DIR ?
            st->num_rootdir_entries : st->cluster_size))
    {
      /* Scan the next sector in this cluster. */
      st->idx = 0;
      st->locate_dir.remain_entries_in_dir = 16;
      st->locate_dir.flags = 0;

      st->st_stream_bytes.sec = (st->flags & FL_FIXED_DIR ? st->root_dir_start :
        clust2sect(st->locate_dir.cur_cluster, st)) + st->locate_dir.cur_sector;
      st->st_stream_bytes.offset = 0;
      st->st_stream_bytes.len = 512;
      st->state = ST_STREAM_DIR;
      return EV_FILE_ST_STREAM_BYTES;
    }

    /* Read FAT entry to get next cluster. */
    state = ST_NEXT_CLUSTER_DIR;
    /* Fall through ... */

  case ST_NEXT_CLUSTER_DIR:
    clust = st->locate_dir.cur_cluster;

    if (clust == 0)
      return EV_FILE_ST_ENOENTRY;
    if (clust < 2 || clust >= st->number_of_clusters + 2)
      return EV_FILE_ST_EBADFS;

    /*
      Now compute the sector(s) we need to read to get the FAT entry, as
      well as the offset/length.
    */
    return prep_read_fat_entry(clust, ST_LOOKUP_DIR_FAT, st);

  case ST_LOOKUP_DIR_FAT:
    if (st->locate_dir.cur_cluster >= (uint32_t)0xfffffff8)
    {
      /* End of directory reached. */
      return EV_FILE_ST_ENOENTRY;
    }

    if (st->locate_dir.cur_cluster < 2 ||
        st->locate_dir.cur_cluster - 2 >= st->number_of_clusters)
      return EV_FILE_ST_EBADFS;

    /* Now go read the first sector of the next cluster in directory. */
    st->idx = 0;
    st->locate_dir.remain_entries_in_dir = 16;
    st->locate_dir.flags = 0;

    st->st_stream_bytes.sec = clust2sect(st->locate_dir.cur_cluster, st);
    st->st_stream_bytes.offset = 0;
    st->st_stream_bytes.len = 512;
    st->state = ST_STREAM_DIR;
    return EV_FILE_ST_STREAM_BYTES;

  default:
    return EV_FILE_ST_EUNSPC;
  };
}