blob: 9853a726edd755bba3b9ed05acbd538e85577806 [file] [log] [blame]
/* ----------------------------------------------------------------------- *
*
* Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
*
* 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, Inc., 53 Temple Place Ste 330,
* Boston MA 02111-1307, USA; either version 2 of the License, or
* (at your option) any later version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
/*
* fatchain.c
*
* Follow a FAT chain
*/
#include "libfatint.h"
#include "ulint.h"
/*
* Convert a cluster number (or 0 for the root directory) to a
* sector number. Return -1 on failure.
*/
libfat_sector_t libfat_clustertosector(const struct libfat_filesystem *fs,
int32_t cluster)
{
if (cluster == 0)
cluster = fs->rootcluster;
if (cluster == 0)
return fs->rootdir;
else if (cluster < 2 || cluster >= fs->endcluster)
return -1;
else
return fs->data + ((libfat_sector_t) (cluster - 2) << fs->clustshift);
}
/*
* Get the next sector of either the root directory or a FAT chain.
* Returns 0 on end of file and -1 on error.
*/
libfat_sector_t libfat_nextsector(struct libfat_filesystem * fs,
libfat_sector_t s)
{
int32_t cluster, nextcluster;
uint32_t fatoffset;
libfat_sector_t fatsect;
uint8_t *fsdata;
uint32_t clustmask = fs->clustsize - 1;
libfat_sector_t rs;
if (s < fs->data) {
if (s < fs->rootdir)
return -1;
/* Root directory */
s++;
return (s < fs->data) ? s : 0;
}
rs = s - fs->data;
if (~rs & clustmask)
return s + 1; /* Next sector in cluster */
cluster = 2 + (rs >> fs->clustshift);
if (cluster >= fs->endcluster)
return -1;
switch (fs->fat_type) {
case FAT12:
/* Get first byte */
fatoffset = cluster + (cluster >> 1);
fatsect = fs->fat + (fatoffset >> LIBFAT_SECTOR_SHIFT);
fsdata = libfat_get_sector(fs, fatsect);
if (!fsdata)
return -1;
nextcluster = fsdata[fatoffset & LIBFAT_SECTOR_MASK];
/* Get second byte */
fatoffset++;
fatsect = fs->fat + (fatoffset >> LIBFAT_SECTOR_SHIFT);
fsdata = libfat_get_sector(fs, fatsect);
if (!fsdata)
return -1;
nextcluster |= fsdata[fatoffset & LIBFAT_SECTOR_MASK] << 8;
/* Extract the FAT entry */
if (cluster & 1)
nextcluster >>= 4;
else
nextcluster &= 0x0FFF;
if (nextcluster >= 0x0FF8)
return 0;
break;
case FAT16:
fatoffset = cluster << 1;
fatsect = fs->fat + (fatoffset >> LIBFAT_SECTOR_SHIFT);
fsdata = libfat_get_sector(fs, fatsect);
if (!fsdata)
return -1;
nextcluster =
read16((le16_t *) & fsdata[fatoffset & LIBFAT_SECTOR_MASK]);
if (nextcluster >= 0x0FFF8)
return 0;
break;
case FAT28:
fatoffset = cluster << 2;
fatsect = fs->fat + (fatoffset >> LIBFAT_SECTOR_SHIFT);
fsdata = libfat_get_sector(fs, fatsect);
if (!fsdata)
return -1;
nextcluster =
read32((le32_t *) & fsdata[fatoffset & LIBFAT_SECTOR_MASK]);
nextcluster &= 0x0FFFFFFF;
if (nextcluster >= 0x0FFFFFF8)
return 0;
break;
default:
return -1; /* WTF? */
}
return libfat_clustertosector(fs, nextcluster);
}