1 /*-
2 * Copyright (c) 2005, 2008, 2009, 2011
3 * Thorsten Glaser <tg@mirbsd.org>
4 *
5 * Provided that these terms and disclaimer and all copyright notices
6 * are retained or reproduced in an accompanying document, permission
7 * is granted to deal in this work without restriction, including un-
8 * limited rights to use, publicly perform, distribute, sell, modify,
9 * merge, give away, or sublicence.
10 *
11 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
12 * the utmost extent permitted by applicable law, neither express nor
13 * implied; without malicious intent or gross negligence. In no event
14 * may a licensor, author or contributor be held liable for indirect,
15 * direct, other damage, loss, or other issues arising in any way out
16 * of dealing in the work, even if advised of the possibility of such
17 * damage or existence of a defect, except proven that it results out
18 * of said person's immediate fault when using the work as intended.
19 */
20
21 #include <sys/param.h>
22 #include <sys/time.h>
23 #include <sys/stat.h>
24 #include <sys/slibkern.h>
25 #include <lib/libsa/stand.h>
26 #include <lib/libsa/fat.h>
27
28 __RCSID("$MirOS: src/sys/lib/libsa/fat.c,v 1.20 2011/01/03 19:00:17 tg Exp $");
29
30 #if BYTE_ORDER != LITTLE_ENDIAN
31 #define getlew(ofs) (buf[(ofs)] + ((unsigned)buf[(ofs) + 1] << 8))
32 #define getled(ofs) (getlew(ofs) + (getlew((ofs) + 2) << 16))
33 #else
34 #define getlew(ofs) (*((uint16_t *)(&buf[(ofs)])))
35 #define getled(ofs) (*((uint32_t *)(&buf[(ofs)])))
36 #endif
37
38 enum fat_type {
39 FAT12, FAT16, FAT28
40 };
41
42 struct fat_file {
43 struct open_file *open_file; /* our "parent" structure */
44 uint32_t rootofs; /* root offset (FAT12/FAT16) */
45 uint32_t invalc; /* first invalid cluster no. */
46 uint32_t firstds; /* first data sector */
47 char *fatbuf; /* sector buffer (FAT) */
48 uint32_t fatsec; /* which sector is buffered */
49 char *databuf; /* cluster buffer (data) */
50 uint32_t datasec; /* which cluster is buffered */
51 uint32_t nodecluster; /* current node: cluster */
52 uint32_t nodeseekp; /* current node: file offset */
53 uint32_t nodesize; /* current node: file size */
54 uint16_t ress; /* BPB: reserved sectors */
55 uint16_t bpc; /* BPB: bytes per cluster */
56 uint8_t nodetype; /* 1=directory 2=file */
57 enum fat_type type; /* FAT12, FAT16 or FAT28 */
58 };
59
60 static int rd(struct open_file *, void *, daddr_t, size_t);
61 static uint32_t getfat(struct fat_file *, uint32_t);
62 static int search_dir(struct open_file *, char *);
63
64 static unsigned char fat_dirbuf[32];
65
66 #define fillbuf(ff) rd((ff)->open_file, (ff)->databuf, \
67 ((ff)->datasec - 2) * ((ff)->bpc / 512) + \
68 (ff)->firstds, (ff)->bpc)
69 #define locase(c) (((c) < 'A') || ((c) > 'Z') ? (c) : (c) - 'A' + 'a')
70
71 static int
rd(struct open_file * f,void * buf,daddr_t blk,size_t size)72 rd(struct open_file *f, void *buf, daddr_t blk, size_t size)
73 {
74 size_t buflen;
75 int rv;
76
77 twiddle();
78 if ((rv = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
79 blk, size, buf, &buflen)))
80 ;
81 else if (buflen != size)
82 rv = EIO;
83 return (rv);
84 }
85
86 static uint32_t
getfat(struct fat_file * ff,uint32_t entry)87 getfat(struct fat_file *ff, uint32_t entry)
88 {
89 uint32_t fofs, fsec, secofs, rv = 0, b;
90 char *buf;
91
92 b = (ff->type == FAT12) ? 1024 : 512;
93 if (ff->fatbuf == NULL) {
94 ff->fatbuf = alloc(b);
95 ff->fatsec = 0;
96 }
97 buf = ff->fatbuf;
98
99 fofs = (ff->type == FAT16) ? (entry << 1) :
100 ((ff->type == FAT28) ? (entry << 2) :
101 (entry + (entry / 2)));
102 fsec = ff->ress + (fofs / 512);
103 secofs = fofs % 512;
104
105 if (fsec != ff->fatsec) {
106 if ((rv = rd(ff->open_file, buf, fsec, b))) {
107 free(buf, b);
108 ff->fatbuf = NULL;
109 return (rv | 0x80000000);
110 }
111 ff->fatsec = fsec;
112 }
113
114 switch (ff->type) {
115 case FAT28:
116 rv = getled(secofs) & 0x0FFFFFFF;
117 break;
118 case FAT16:
119 rv = getlew(secofs);
120 break;
121 case FAT12:
122 rv = getlew(secofs);
123 if (entry & 1)
124 rv >>= 4;
125 else
126 rv &= 0x0FFF;
127 break;
128 }
129 return (rv);
130 }
131
132 int
fat_open(char * path,struct open_file * f)133 fat_open(char *path, struct open_file *f)
134 {
135 struct fat_file *ff;
136 unsigned char *buf;
137 int rv;
138 uint32_t spc, nfats, rootcnt, spd, spf, dblk;
139
140 /* allocate fs specific data structure */
141 ff = alloc(sizeof (struct fat_file));
142 bzero(ff, sizeof (struct fat_file));
143 f->f_fsdata = ff;
144 ff->open_file = f;
145
146 /* allocate space and read BPB */
147 buf = alloc(512);
148 if ((rv = rd(f, buf, 0, 512)))
149 goto out;
150
151 /* parse BPB */
152 rv = EINVAL;
153 if (((buf[0] != 0xEB) || (buf[2] != 0x90)) && (buf[0] != 0xE9))
154 goto out;
155 if ((buf[510] != 0x55) || (buf[511] != 0xAA))
156 goto out;
157
158 /* only 512 Bytes per sector for now */
159 if (getlew(11) != 512)
160 goto out;
161
162 spc = buf[13];
163 if (!spc || spc > 0x40 || (spc & (spc - 1)))
164 /* 0, > 0x40, or not a power of two */
165 goto out;
166 ff->bpc = spc * 512;
167 if ((ff->ress = getlew(14)) < 1)
168 goto out;
169 nfats = buf[16];
170 rootcnt = (getlew(17) * 32 + 511) / 512;
171 spd = getlew(19);
172 spf = getlew(22);
173 if (!spd)
174 spd = getled(32);
175 if (!spf)
176 spf = getled(36);
177 ff->rootofs = ff->ress + (nfats * spf);
178 ff->firstds = ff->rootofs + rootcnt;
179 dblk = (spd - ff->firstds) / spc;
180 ff->type = (dblk < 4085) ? FAT12 : ((dblk < 65525) ? FAT16 : FAT28);
181 switch (ff->type) {
182 case FAT12:
183 ff->invalc = 0x0FF7;
184 break;
185 case FAT16:
186 ff->invalc = 0xFFF7;
187 break;
188 case FAT28:
189 ff->invalc = 0x0FFFFFF7;
190 if ((ff->rootofs = getled(44)) < 2)
191 goto out;
192 break;
193 }
194
195 ff->nodetype = 1; /* 1 = directory, 2 = file */
196 ff->nodecluster = 1; /* 1 = root directory (special) */
197 ff->nodeseekp = 0;
198 if (ff->type == FAT28)
199 /* we have a real root directory file */
200 ff->nodecluster = ff->rootofs;
201 else
202 ff->nodesize = rootcnt * 512;
203
204 while (*path) {
205 char c, *cp, *npath;
206 size_t len;
207
208 /* remove extra separators */
209 while (*path == '/')
210 ++path;
211 if (*path == '\0')
212 break;
213
214 /* check that current node is a directory */
215 if (ff->nodetype != 1) {
216 rv = ENOTDIR;
217 goto out;
218 }
219
220 /* get next component of path name */
221 len = 0;
222 cp = path;
223 while (((c = *cp) != '\0') && (c != '/')) {
224 if (++len > 14) {
225 rv = ENOENT;
226 goto out;
227 }
228 cp++;
229 }
230 if (cp == path + 1 && *path == '.') {
231 path = cp;
232 continue;
233 }
234 npath = cp;
235 *cp = '\0';
236
237 /* look up component in current directory */
238 rv = search_dir(f, path);
239 *cp = c;
240 if (rv)
241 goto out;
242 path = npath;
243 }
244
245 /* found terminal component */
246 rv = 0;
247 out:
248 free(buf, 512);
249 if (rv) {
250 if (ff->fatbuf)
251 free(ff->fatbuf, (ff->type == FAT12) ? 1024 : 512);
252 if (ff->databuf)
253 free(ff->databuf, ff->bpc);
254 free(ff, sizeof (struct fat_file));
255 }
256 return (rv);
257 }
258
259 int
fat_close(struct open_file * f)260 fat_close(struct open_file *f)
261 {
262 struct fat_file *ff;
263
264 if (f == NULL)
265 return (0);
266
267 ff = f->f_fsdata;
268 if (ff) {
269 if (ff->fatbuf)
270 free(ff->fatbuf, (ff->type == FAT12) ? 1024 : 512);
271 if (ff->databuf)
272 free(ff->databuf, ff->bpc);
273 free(ff, sizeof (struct fat_file));
274 }
275 return (0);
276 }
277
278 int
fat_read(struct open_file * f,void * buf_,size_t size,size_t * resid)279 fat_read(struct open_file *f, void *buf_, size_t size, size_t *resid)
280 {
281 struct fat_file *ff = f->f_fsdata;
282 int rv = 0, otmp, isroot = 0, blksiz = ff->bpc;
283 size_t stmp;
284 char *buf = buf_;
285
286 if (!ff->databuf) {
287 ff->databuf = alloc(ff->bpc);
288 ff->datasec = 0;
289 }
290
291 if (ff->nodecluster == 1) {
292 isroot = 1;
293 blksiz = 512;
294 }
295
296 if (ff->datasec == 0) {
297 int skip = (ff->nodeseekp / ff->bpc);
298 ff->datasec = ff->nodecluster;
299 if (ff->datasec == 1) {
300 ff->datasec = ff->nodeseekp / 512 + ff->rootofs;
301 if ((rv = rd(f, ff->databuf, ff->datasec, 512))) {
302 ff->datasec = 0;
303 goto out;
304 }
305 goto filled;
306 }
307 if ((ff->datasec == 0) || (ff->datasec >= ff->invalc)) {
308 invclust:
309 ff->datasec = rv = 0;
310 goto out;
311 }
312 while (skip--) {
313 ff->datasec = getfat(ff, ff->datasec);
314 if ((ff->datasec == 0) || (ff->datasec >= ff->invalc))
315 goto invclust;
316 }
317 if ((rv = fillbuf(ff))) {
318 rderr:
319 ff->datasec = 0;
320 goto out;
321 }
322 }
323 filled:
324
325 while (size > 0) {
326 /* don't read beyond EOF */
327 if (ff->nodesize && (ff->nodeseekp >= ff->nodesize))
328 break;
329
330 otmp = ff->nodeseekp % blksiz;
331 stmp = blksiz - otmp;
332 if (ff->nodesize && (ff->nodesize - ff->nodeseekp) < stmp)
333 stmp = ff->nodesize - ff->nodeseekp;
334 if (stmp >= size)
335 stmp = size;
336 memmove(buf, ff->databuf + otmp, stmp);
337
338 size -= stmp;
339 buf += stmp;
340 ff->nodeseekp += stmp;
341
342 if (ff->nodeseekp % blksiz == 0) {
343 if (isroot) {
344 ff->datasec++;
345 if ((rv = rd(f, ff->databuf, ff->datasec, 512)))
346 goto rderr;
347 goto refilled;
348 }
349 ff->datasec = getfat(ff, ff->datasec);
350 if ((ff->datasec >= ff->invalc) || (rv = fillbuf(ff)))
351 ff->datasec = 0;
352 if (ff->datasec == 0 && size)
353 goto out;
354 refilled: ;
355 }
356 }
357 out:
358 if (resid)
359 *resid = size;
360 return (rv);
361 }
362
363 int
fat_write(struct open_file * f __unused,void * buf __unused,size_t size __unused,size_t * resid __unused)364 fat_write(struct open_file *f __unused, void *buf __unused,
365 size_t size __unused, size_t *resid __unused)
366 {
367 return (EROFS);
368 }
369
370 off_t
fat_seek(struct open_file * f,off_t offset,int where)371 fat_seek(struct open_file *f, off_t offset, int where)
372 {
373 switch (where) {
374 case SEEK_SET:
375 ((struct fat_file *)f->f_fsdata)->nodeseekp = offset;
376 break;
377 case SEEK_CUR:
378 ((struct fat_file *)f->f_fsdata)->nodeseekp += offset;
379 break;
380 case SEEK_END:
381 ((struct fat_file *)f->f_fsdata)->nodeseekp =
382 ((struct fat_file *)f->f_fsdata)->nodesize + offset;
383 break;
384 default:
385 return (-1);
386 }
387 /* invalidate file data buffer */
388 ((struct fat_file *)f->f_fsdata)->datasec = 0;
389 return (((struct fat_file *)f->f_fsdata)->nodeseekp);
390 }
391
392 int
fat_stat(struct open_file * f,struct stat * sb)393 fat_stat(struct open_file *f, struct stat *sb)
394 {
395 /* quick and dirty */
396 bzero(sb, sizeof (struct stat));
397 sb->st_mode = (((struct fat_file *)f->f_fsdata)->nodetype == 1) ?
398 040555 : 0444;
399 sb->st_size = ((struct fat_file *)f->f_fsdata)->nodesize;
400
401 return (0);
402 }
403
404 int
fat_readdir(struct open_file * f,char * name)405 fat_readdir(struct open_file *f, char *name)
406 {
407 int rv;
408 char ch, *cp;
409 size_t sr;
410
411 /* reset? */
412 if (name == NULL) {
413 fat_seek(f, 0, SEEK_SET);
414 return (0);
415 }
416
417 *name = '\0';
418 getrec:
419 if ((rv = fat_read(f, fat_dirbuf, sizeof (fat_dirbuf), &sr)))
420 return (rv);
421 if (sr)
422 return (EIO);
423 /* end of directory? */
424 if (fat_dirbuf[0] == 0)
425 return (ENOENT);
426 /* deleted file? */
427 if (fat_dirbuf[0] == 0xE5)
428 goto getrec;
429 /* part of long filename? */
430 if ((fat_dirbuf[11] & 0x3F) == 0x0F)
431 goto getrec;
432 /* volume label or FCFS symlink? */
433 if ((fat_dirbuf[11] & 0xC8))
434 goto getrec;
435 /* okay, we have a directory or regular file */
436 cp = fat_dirbuf;
437 rv = 0;
438 while (*cp != 0x20) {
439 ch = *cp++;
440 *name++ = locase(ch);
441 if (++rv == 8)
442 break;
443 }
444 cp = fat_dirbuf + 8;
445 if (*cp != 0x20)
446 *name++ = '.';
447 rv = 0;
448 while (*cp != 0x20) {
449 ch = *cp++;
450 *name++ = locase(ch);
451 if (++rv == 3)
452 break;
453 }
454 *name++ = '\0';
455
456 return (0);
457 }
458
459 static int
search_dir(struct open_file * f,char * name)460 search_dir(struct open_file *f, char *name)
461 {
462 struct fat_file *ff = f->f_fsdata;
463 int rv;
464 char fn[14];
465 char *buf = fat_dirbuf;
466
467 if ((rv = fat_readdir(f, NULL)))
468 return (rv);
469 while ((rv = fat_readdir(f, fn)) == 0)
470 if (!strcmp(fn, name)) {
471 /* found a match, follow it */
472 ff->nodecluster = getlew(20) << 16 | getlew(26);
473 ff->nodesize = getled(28);
474 if (ff->nodecluster < 2 && ff->nodesize != 0)
475 return (ENOENT);
476 ff->nodeseekp = 0;
477 ff->nodetype = (buf[11] & 0x10) ? 1 : 2;
478 /* invalidate file data buffer */
479 ff->datasec = 0;
480 return (0);
481 }
482 return (ENOENT);
483 }
484