#ident "$Id: fatchain.c,v 1.1 2004/12/15 10:14:39 hpa Exp $" /* ----------------------------------------------------------------------- * * * Copyright 2004 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); }