syslinux-3.08-2 sources from FC4
[bootcd.git] / syslinux / libfat / fatchain.c
1 #ident "$Id: fatchain.c,v 1.1 2004/12/15 10:14:39 hpa Exp $"
2 /* ----------------------------------------------------------------------- *
3  *   
4  *   Copyright 2004 H. Peter Anvin - All Rights Reserved
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9  *   Boston MA 02111-1307, USA; either version 2 of the License, or
10  *   (at your option) any later version; incorporated herein by reference.
11  *
12  * ----------------------------------------------------------------------- */
13
14 /*
15  * fatchain.c
16  *
17  * Follow a FAT chain
18  */
19
20 #include "libfatint.h"
21 #include "ulint.h"
22
23 /*
24  * Convert a cluster number (or 0 for the root directory) to a
25  * sector number.  Return -1 on failure.
26  */
27 libfat_sector_t libfat_clustertosector(const struct libfat_filesystem *fs,
28                                        int32_t cluster)
29 {
30   if ( cluster == 0 )
31     cluster = fs->rootcluster;
32
33   if ( cluster == 0 )
34     return fs->rootdir;
35   else if ( cluster < 2 || cluster >= fs->endcluster )
36     return -1;
37   else
38     return fs->data + ((libfat_sector_t)(cluster-2) << fs->clustshift);
39 }
40
41 /*
42  * Get the next sector of either the root directory or a FAT chain.
43  * Returns 0 on end of file and -1 on error.
44  */
45
46 libfat_sector_t libfat_nextsector(struct libfat_filesystem *fs,
47                                   libfat_sector_t s)
48 {
49   int32_t cluster, nextcluster;
50   uint32_t fatoffset;
51   libfat_sector_t fatsect;
52   uint8_t *fsdata;
53   uint32_t clustmask = fs->clustsize - 1;
54   libfat_sector_t rs;
55
56   if ( s < fs->data ) {
57     if ( s < fs->rootdir )
58       return -1;
59
60     /* Root directory */
61     s++;
62     return ( s < fs->data ) ? s : 0;
63   }
64
65   rs = s - fs->data;
66
67   if ( ~rs & clustmask )
68     return s+1;                 /* Next sector in cluster */
69
70   cluster = 2 + (rs >> fs->clustshift);
71
72   if ( cluster >= fs->endcluster )
73     return -1;
74
75   switch ( fs->fat_type ) {
76   case FAT12:
77     /* Get first byte */
78     fatoffset = cluster + (cluster >> 1);
79     fatsect = fs->fat + (fatoffset >> LIBFAT_SECTOR_SHIFT);
80     fsdata = libfat_get_sector(fs, fatsect);
81     if ( !fsdata )
82       return -1;
83     nextcluster = fsdata[fatoffset & LIBFAT_SECTOR_MASK];
84
85     /* Get second byte */
86     fatoffset++;
87     fatsect = fs->fat + (fatoffset >> LIBFAT_SECTOR_SHIFT);
88     fsdata = libfat_get_sector(fs, fatsect);
89     if ( !fsdata )
90       return -1;
91     nextcluster |= fsdata[fatoffset & LIBFAT_SECTOR_MASK] << 8;
92
93     /* Extract the FAT entry */
94     if ( cluster & 1 )
95       nextcluster >>= 4;
96     else
97       nextcluster &= 0x0FFF;
98
99     if ( nextcluster >= 0x0FF8 )
100       return 0;
101     break;
102
103   case FAT16:
104     fatoffset = cluster << 1;
105     fatsect = fs->fat + (fatoffset >> LIBFAT_SECTOR_SHIFT);
106     fsdata = libfat_get_sector(fs, fatsect);
107     if ( !fsdata )
108       return -1;
109     nextcluster = read16((le16_t *)&fsdata[fatoffset & LIBFAT_SECTOR_MASK]);
110     
111     if ( nextcluster >= 0x0FFF8 )
112       return 0;
113     break;
114
115   case FAT28:
116     fatoffset = cluster << 2;
117     fatsect = fs->fat + (fatoffset >> LIBFAT_SECTOR_SHIFT);
118     fsdata = libfat_get_sector(fs, fatsect);
119     if ( !fsdata )
120       return -1;
121     nextcluster = read32((le32_t *)&fsdata[fatoffset & LIBFAT_SECTOR_MASK]);
122     nextcluster &= 0x0FFFFFFF;
123     
124     if ( nextcluster >= 0x0FFFFFF8 )
125       return 0;
126     break;
127     
128   default:
129     return -1;                  /* WTF? */
130   }
131
132   return libfat_clustertosector(fs, nextcluster);
133 }
134
135     
136