]> git.lizzy.rs Git - plan9front.git/blob - sys/src/boot/pc/fat.c
pc: treat EOF gracefully, allowing easier use within sam command language
[plan9front.git] / sys / src / boot / pc / fat.c
1 #include <u.h>
2 #include "fns.h"
3
4 #define GETSHORT(p) (*(ushort *)(p))
5 #define GETLONG(p) (*(uint *)(p))
6
7 enum {
8         Sectsz = 0x200,
9         Dirsz = 0x20,
10         Maxpath = 64,
11         Fat12 = 1,
12         Fat16 = 2,
13         Fat32 = 4,
14 };
15
16 typedef struct File File;
17 typedef struct Dir Dir;
18 typedef struct Pbs Pbs;
19 typedef struct Pbs32 Pbs32;
20 typedef struct Fat Fat;
21
22 struct Fat
23 {
24         ulong ver;
25         int drive;
26         ulong clustsize;
27         ulong eofmark;
28         ulong partlba;
29         ulong fatlba;
30         ulong dirstart; /* LBA for FAT16, cluster for FAT32 */
31         ulong dirents;
32         ulong datalba;
33 };
34
35 struct File
36 {
37         Fat *fat;
38         ulong lba;
39         ulong clust;
40         ulong lbaoff;
41         ulong len;
42         uchar *rp;
43         uchar *ep;
44         uchar buf[Sectsz];
45 };
46
47 struct Dir
48 {
49         char name[11];
50         uchar attr;
51         uchar reserved;
52         uchar ctime;
53         uchar ctime[2];
54         uchar cdate[2];
55         uchar adate[2];
56         uchar starthi[2];
57         uchar mtime[2];
58         uchar mdate[2];
59         uchar startlo[2];
60         uchar len[4];
61 };
62
63 struct Pbs
64 {
65         uchar magic[3];
66         uchar version[8];
67         uchar sectsize[2];
68         uchar clustsize;
69         uchar nreserv[2];
70         uchar nfats;
71         uchar rootsize[2];
72         uchar volsize[2];
73         uchar mediadesc;
74         uchar fatsize[2];
75         uchar trksize[2];
76         uchar nheads[2];
77         uchar nhidden[4];
78         uchar bigvolsize[4];
79         uchar driveno;
80         uchar reserved0;
81         uchar bootsig;
82         uchar volid[4];
83         uchar label[11];
84         uchar type[8];
85 };
86
87 struct Pbs32
88 {
89         uchar common[36];
90         uchar fatsize[4];
91         uchar flags[2];
92         uchar ver[2];
93         uchar rootclust[4];
94         uchar fsinfo[2];
95         uchar bootbak[2];
96         uchar reserved0[12];
97         uchar driveno;
98         uchar reserved1;
99         uchar bootsig;
100         uchar volid[4];
101         uchar label[11];
102         uchar type[8];
103 };
104
105 int readsect(ulong drive, ulong lba, void *buf);
106
107 void
108 unload(void)
109 {
110 }
111
112 static ulong
113 readnext(File *fp, ulong clust)
114 {
115         Fat *fat = fp->fat;
116         uchar tmp[2], *p;
117         ulong idx, lba;
118
119         if(fat->ver == Fat12)
120                 idx = (3*clust)/2;
121         else
122                 idx = clust*fat->ver;
123         lba = fat->fatlba + (idx / Sectsz);
124         if(readsect(fat->drive, lba, fp->buf))
125                 memset(fp->buf, 0xff, Sectsz);
126         p = &fp->buf[idx % Sectsz];
127         if(p == &fp->buf[Sectsz-1]){
128                 tmp[0] = *p;
129                 if(readsect(fat->drive, ++lba, fp->buf))
130                         memset(fp->buf, 0xff, Sectsz);
131                 tmp[1] = fp->buf[0];
132                 p = tmp;
133         }
134         if(fat->ver == Fat32)
135                 return GETLONG(p) & 0xfffffff;
136         idx = GETSHORT(p);
137         if(fat->ver == Fat12){
138                 if(clust & 1)
139                         idx >>= 4;
140                 idx &= 0xfff;
141         }
142         return idx;
143 }
144
145 int
146 read(void *f, void *data, int len)
147 {
148         File *fp = f;
149         Fat *fat = fp->fat;
150
151         if(fp->len > 0 && fp->rp >= fp->ep){
152                 if(fp->clust != ~0U){
153                         if(fp->lbaoff % fat->clustsize == 0){
154                                 if(fp->clust < 2 || fp->clust >= fat->eofmark)
155                                         return -1;
156                                 fp->lbaoff = (fp->clust - 2) * fat->clustsize;
157                                 fp->clust = readnext(fp, fp->clust);
158                                 fp->lba = fp->lbaoff + fat->datalba;
159                         }
160                         fp->lbaoff++;
161                 }
162                 if(readsect(fat->drive, fp->lba++, fp->rp = fp->buf))
163                         return -1;
164         }
165         if(fp->len < len)
166                 len = fp->len;
167         if(len > (fp->ep - fp->rp))
168                 len = fp->ep - fp->rp;
169         memmove(data, fp->rp, len);
170         fp->rp += len;
171         fp->len -= len;
172         return len;
173 }
174
175 void
176 close(void *)
177 {
178 }
179
180 static int
181 dirname(Dir *d, char buf[Maxpath])
182 {
183         char c, *x;
184
185         if(d->attr == 0x0F || *d->name <= 0)
186                 return -1;
187         memmove(buf, d->name, 8);
188         x = buf+8;
189         while(x > buf && x[-1] == ' ')
190                 x--;
191         if(d->name[8] != ' '){
192                 *x++ = '.';
193                 memmove(x, d->name+8, 3);
194                 x += 3;
195         }
196         while(x > buf && x[-1] == ' ')
197                 x--;
198         *x = 0;
199         x = buf;
200         while(c = *x){
201                 if(c >= 'A' && c <= 'Z'){
202                         c -= 'A';
203                         c += 'a';
204                 }
205                 *x++ = c;
206         }
207         return x - buf;
208 }
209
210 static ulong
211 dirclust(Dir *d)
212 {
213         return *((ushort*)d->starthi)<<16 | *((ushort*)d->startlo);
214 }
215
216 static void
217 fileinit(File *fp, Fat *fat, ulong lba)
218 {
219         fp->fat = fat;
220         fp->lba = lba;
221         fp->len = 0;
222         fp->lbaoff = 0;
223         fp->clust = ~0U;
224         fp->rp = fp->ep = fp->buf + Sectsz;
225 }
226
227 static int
228 fatwalk(File *fp, Fat *fat, char *path)
229 {
230         char name[Maxpath], *end;
231         int i, j;
232         Dir d;
233
234         if(fat->ver == Fat32){
235                 fileinit(fp, fat, 0);
236                 fp->clust = fat->dirstart;
237                 fp->len = ~0U;
238         }else{
239                 fileinit(fp, fat, fat->dirstart);
240                 fp->len = fat->dirents * Dirsz;
241         }
242         for(;;){
243                 if(readn(fp, &d, Dirsz) != Dirsz)
244                         break;
245                 if((i = dirname(&d, name)) <= 0)
246                         continue;
247                 while(*path == '/')
248                         path++;
249                 if((end = strchr(path, '/')) == 0)
250                         end = path + strlen(path);
251                 j = end - path;
252                 if(i == j && memcmp(name, path, j) == 0){
253                         fileinit(fp, fat, 0);
254                         fp->clust = dirclust(&d);
255                         fp->len = GETLONG(d.len);
256                         if(*end == 0)
257                                 return 0;
258                         else if(d.attr & 0x10){
259                                 fp->len = fat->clustsize * Sectsz;
260                                 path = end;
261                                 continue;
262                         }
263                         break;
264                 }
265         }
266         return -1;
267 }
268
269 static int
270 conffat(Fat *fat, void *buf)
271 {
272         Pbs *p = buf;
273         uint fatsize, volsize, datasize, reserved;
274         uint ver, dirsize, dirents, clusters;
275
276         if(GETSHORT(p->sectsize) != Sectsz)
277                 return -1;
278         if(memcmp(p->type, "FAT", 3) && memcmp(((Pbs32*)buf)->type, "FAT", 3))
279                 return -1;
280         
281         /* load values from fat */
282         ver = 0;
283         fatsize = GETSHORT(p->fatsize);
284         if(fatsize == 0){
285                 fatsize = GETLONG(((Pbs32*)buf)->fatsize);
286                 ver = Fat32;
287         }
288         volsize = GETSHORT(p->volsize);
289         if(volsize == 0)
290                 volsize = GETLONG(p->bigvolsize);
291         reserved = GETSHORT(p->nreserv);
292         dirents = GETSHORT(p->rootsize);
293         dirsize = (dirents * Dirsz + Sectsz - 1) / Sectsz;
294         datasize = volsize - (reserved + fatsize * p->nfats + dirsize);
295         clusters = datasize / p->clustsize;
296         if(ver != Fat32)
297                 if(clusters < 0xff7)
298                         ver = Fat12;
299                 else
300                         ver = Fat16;
301         
302         /* fill FAT descriptor */
303         fat->ver = ver;
304         fat->dirents = dirents;
305         fat->clustsize = p->clustsize;
306         fat->fatlba = fat->partlba + reserved;
307         fat->dirstart  = fat->fatlba + fatsize * p->nfats;
308         if(ver == Fat32){
309                 fat->datalba = fat->dirstart;
310                 fat->dirstart  = GETLONG(((Pbs32*)buf)->rootclust);
311                 fat->eofmark = 0xffffff7;
312         }else{
313                 fat->datalba = fat->dirstart + dirsize;
314                 if(ver == Fat16)
315                         fat->eofmark = 0xfff7;
316                 else
317                         fat->eofmark = 0xff7;
318         }
319         return 0;
320 }
321
322 static int
323 findfat(Fat *fat, int drive, ulong xbase, ulong lba)
324 {
325         struct {
326                 uchar status;
327                 uchar bchs[3];
328                 uchar typ;
329                 uchar echs[3];
330                 uchar lba[4];
331                 uchar len[4];
332         } p[4];
333         uchar buf[Sectsz];
334         int i;
335
336         if(xbase == 0)
337                 xbase = lba;
338         if(readsect(drive, lba, buf))
339                 return -1;
340         if(buf[0x1fe] != 0x55 || buf[0x1ff] != 0xAA)
341                 return -1;
342         if(lba == 0 && (drive & 0x80) == 0){    /* floppy */
343                 fat->drive = drive;
344                 fat->partlba = 0;
345                 if(!conffat(fat, buf))
346                         return 0;
347         }
348         memmove(p, &buf[0x1be], sizeof(p));
349         for(i=0; i<4; i++){
350                 switch(p[i].typ){
351                 case 0x05:
352                 case 0x0f:
353                 case 0x85:
354                         /* extended partitions */
355                         if(!findfat(fat, drive, xbase, xbase + GETLONG(p[i].lba)))
356                                 return 0;
357                         /* no break */
358                 case 0x00:
359                         continue;
360                 default:
361                         if(p[i].status != 0x80)
362                                 continue;
363                 case 0x39:      /* always try plan9 partition */
364                         fat->drive = drive;
365                         fat->partlba = lba + GETLONG(p[i].lba);
366                         if(readsect(drive, fat->partlba, buf))
367                                 continue;
368                         if(!conffat(fat, buf))
369                                 return 0;
370                 }
371         }
372         return -1;
373 }
374
375 void
376 start(void *sp)
377 {
378         char path[Maxpath], *kern;
379         int drive;
380         File fi;
381         Fat fat;
382         void *f;
383
384         /* drive passed in DL */
385         drive = ((ushort*)sp)[5] & 0xFF;
386
387         if(findfat(&fat, drive, 0, 0)){
388                 print("no fat\n");
389                 halt();
390         }
391         if(fatwalk(f = &fi, &fat, "plan9.ini")){
392                 print("no config\n");
393                 f = 0;
394         }
395         for(;;){
396                 kern = configure(f, path); f = 0;
397                 if(fatwalk(&fi, &fat, kern)){
398                         print("not found\n");
399                         continue;
400                 }
401                 print(bootkern(&fi));
402                 print("\n");
403         }
404 }
405