2 * Copyright (c) 1996, 1998 Robert Nordier
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
21 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
25 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
32 * Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems,
36 #include <sys/types.h>
45 static int dos_open(const char *path
, struct open_file
*fd
);
46 static int dos_close(struct open_file
*fd
);
47 static int dos_read(struct open_file
*fd
, void *buf
, size_t size
, size_t *resid
);
48 static off_t
dos_seek(struct open_file
*fd
, off_t offset
, int whence
);
49 static int dos_stat(struct open_file
*fd
, struct stat
*sb
);
50 static int dos_readdir(struct open_file
*fd
, struct dirent
*d
);
52 struct fs_ops dosfs_fsops
= {
63 #define SECSIZ 512 /* sector size */
64 #define SSHIFT 9 /* SECSIZ shift */
65 #define DEPSEC 16 /* directory entries per sector */
66 #define DSHIFT 4 /* DEPSEC shift */
67 #define LOCLUS 2 /* lowest cluster number */
69 /* DOS "BIOS Parameter Block" */
71 u_char secsiz
[2]; /* sector size */
72 u_char spc
; /* sectors per cluster */
73 u_char ressec
[2]; /* reserved sectors */
74 u_char fats
; /* FATs */
75 u_char dirents
[2]; /* root directory entries */
76 u_char secs
[2]; /* total sectors */
77 u_char media
; /* media descriptor */
78 u_char spf
[2]; /* sectors per FAT */
79 u_char spt
[2]; /* sectors per track */
80 u_char heads
[2]; /* drive heads */
81 u_char hidsec
[4]; /* hidden sectors */
82 u_char lsecs
[4]; /* huge sectors */
83 u_char lspf
[4]; /* huge sectors per FAT */
84 u_char xflg
[2]; /* flags */
85 u_char vers
[2]; /* filesystem version */
86 u_char rdcl
[4]; /* root directory start cluster */
87 u_char infs
[2]; /* filesystem info sector */
88 u_char bkbs
[2]; /* backup boot sector */
91 /* Initial portion of DOS boot sector */
93 u_char jmp
[3]; /* usually 80x86 'jmp' opcode */
94 u_char oem
[8]; /* OEM name and version */
95 DOS_BPB bpb
; /* BPB */
98 /* Supply missing "." and ".." root directory entries */
99 static const char *const dotstr
[2] = {".", ".."};
100 static DOS_DE dot
[2] = {
101 {". ", " ", FA_DIR
, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
102 {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}},
103 {".. ", " ", FA_DIR
, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
104 {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}
107 /* The usual conversion macros to avoid multiplication and division */
108 #define bytsec(n) ((n) >> SSHIFT)
109 #define secbyt(s) ((s) << SSHIFT)
110 #define entsec(e) ((e) >> DSHIFT)
111 #define bytblk(fs, n) ((n) >> (fs)->bshift)
112 #define blkbyt(fs, b) ((b) << (fs)->bshift)
113 #define secblk(fs, s) ((s) >> ((fs)->bshift - SSHIFT))
114 #define blksec(fs, b) ((b) << ((fs)->bshift - SSHIFT))
116 /* Convert cluster number to offset within filesystem */
117 #define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS))
119 /* Convert cluster number to logical sector number */
120 #define blklsn(fs, b) ((fs)->lsndta + blksec(fs, (b) - LOCLUS))
122 /* Convert cluster number to offset within FAT */
123 #define fatoff(sz, c) ((sz) == 12 ? (c) + ((c) >> 1) : \
124 (sz) == 16 ? (c) << 1 : \
127 /* Does cluster number reference a valid data cluster? */
128 #define okclus(fs, c) ((c) >= LOCLUS && (c) <= (fs)->xclus)
130 /* Get start cluster from directory entry */
131 #define stclus(sz, de) ((sz) != 32 ? cv2((de)->clus) : \
132 ((u_int)cv2((de)->dex.h_clus) << 16) | \
139 int unit
; /* disk unit number */
140 int size
; /* buffer (and fat) size in sectors */
144 static struct fatcache fat
;
146 static int dosunmount(DOS_FS
*);
147 static int parsebs(DOS_FS
*, DOS_BS
*);
148 static int namede(DOS_FS
*, const char *, DOS_DE
**);
149 static int lookup(DOS_FS
*, u_int
, const char *, DOS_DE
**);
150 static void cp_xdnm(u_char
*, DOS_XDE
*);
151 static void cp_sfn(u_char
*, DOS_DE
*);
152 static off_t
fsize(DOS_FS
*, DOS_DE
*);
153 static int fatcnt(DOS_FS
*, u_int
);
154 static int fatget(DOS_FS
*, u_int
*);
155 static int fatend(u_int
, u_int
);
156 static int ioread(DOS_FS
*, u_int
, void *, u_int
);
157 static int ioget(struct open_file
*, daddr_t
, size_t, void *, u_int
);
160 dos_read_fat(DOS_FS
*fs
, struct open_file
*fd
)
162 struct devdesc
*dd
= fd
->f_devdata
;
164 if (fat
.buf
!= NULL
) { /* can we reuse old buffer? */
165 if (fat
.size
!= fs
->spf
) {
166 free(fat
.buf
); /* no, free old buffer */
172 fat
.buf
= malloc(secbyt(fs
->spf
));
174 if (fat
.buf
!= NULL
) {
175 if (ioget(fd
, fs
->lsnfat
, 0, fat
.buf
, secbyt(fs
->spf
)) == 0) {
177 fat
.unit
= dd
->d_unit
;
181 if (fat
.buf
!= NULL
) /* got IO error */
184 fat
.unit
= -1; /* impossible unit */
189 * Mount DOS filesystem
192 dos_mount(DOS_FS
*fs
, struct open_file
*fd
)
195 struct devdesc
*dd
= fd
->f_devdata
;
198 bzero(fs
, sizeof(DOS_FS
));
201 if ((err
= !(buf
= malloc(secbyt(1))) ? errno
: 0) ||
202 (err
= ioget(fs
->fd
, 0, 0, buf
, secbyt(1))) ||
203 (err
= parsebs(fs
, (DOS_BS
*)buf
))) {
206 (void)dosunmount(fs
);
211 if (fat
.buf
== NULL
|| fat
.unit
!= dd
->d_unit
)
212 dos_read_fat(fs
, fd
);
215 fs
->root
.name
[0] = ' ';
216 if (fs
->fatsz
== 32) {
217 fs
->root
.clus
[0] = fs
->rdcl
& 0xff;
218 fs
->root
.clus
[1] = (fs
->rdcl
>> 8) & 0xff;
219 fs
->root
.dex
.h_clus
[0] = (fs
->rdcl
>> 16) & 0xff;
220 fs
->root
.dex
.h_clus
[1] = (fs
->rdcl
>> 24) & 0xff;
226 * Unmount mounted filesystem
229 dos_unmount(DOS_FS
*fs
)
235 if ((err
= dosunmount(fs
)))
241 * Common code shared by dos_mount() and dos_unmount()
244 dosunmount(DOS_FS
*fs
)
254 dos_open(const char *path
, struct open_file
*fd
)
262 /* Allocate mount structure, associate with open */
263 fs
= malloc(sizeof(DOS_FS
));
265 if ((err
= dos_mount(fs
, fd
)))
268 if ((err
= namede(fs
, path
, &de
)))
271 clus
= stclus(fs
->fatsz
, de
);
272 size
= cv4(de
->size
);
274 if ((!(de
->attr
& FA_DIR
) && (!clus
!= !size
)) ||
275 ((de
->attr
& FA_DIR
) && size
) ||
276 (clus
&& !okclus(fs
, clus
))) {
280 f
= malloc(sizeof(DOS_FILE
));
281 bzero(f
, sizeof(DOS_FILE
));
285 fd
->f_fsdata
= (void *)f
;
295 dos_read(struct open_file
*fd
, void *buf
, size_t nbyte
, size_t *resid
)
298 u_int nb
, off
, clus
, c
, cnt
, n
;
299 DOS_FILE
*f
= (DOS_FILE
*)fd
->f_fsdata
;
303 * as ioget() can be called *a lot*, use twiddle here.
304 * also 4 seems to be good value not to slow loading down too much:
305 * with 270MB file (~540k ioget() calls, twiddle can easily waste 4-5sec.
309 if ((size
= fsize(f
->fs
, &f
->de
)) == -1)
311 if (nb
> (n
= size
- f
->offset
))
314 if ((clus
= stclus(f
->fs
->fatsz
, &f
->de
)))
315 off
&= f
->fs
->bsize
- 1;
322 n
= bytblk(f
->fs
, f
->offset
);
326 if ((err
= fatget(f
->fs
, &c
)))
328 if (!okclus(f
->fs
, c
)) {
333 if (!clus
|| (n
= f
->fs
->bsize
- off
) > cnt
)
335 if ((err
= ioread(f
->fs
, (c
? blkoff(f
->fs
, c
) :
336 secbyt(f
->fs
->lsndir
)) + off
, buf
, n
)))
341 buf
= (char *)buf
+ n
;
346 *resid
= nbyte
- nb
+ cnt
;
351 * Reposition within file
354 dos_seek(struct open_file
*fd
, off_t offset
, int whence
)
358 DOS_FILE
*f
= (DOS_FILE
*)fd
->f_fsdata
;
360 size
= cv4(f
->de
.size
);
376 if (off
< 0 || off
> size
) {
380 f
->offset
= (u_int
)off
;
389 dos_close(struct open_file
*fd
)
391 DOS_FILE
*f
= (DOS_FILE
*)fd
->f_fsdata
;
401 * Return some stat information on a file.
404 dos_stat(struct open_file
*fd
, struct stat
*sb
)
406 DOS_FILE
*f
= (DOS_FILE
*)fd
->f_fsdata
;
408 /* only important stuff */
409 sb
->st_mode
= f
->de
.attr
& FA_DIR
? S_IFDIR
| 0555 : S_IFREG
| 0444;
413 if ((sb
->st_size
= fsize(f
->fs
, &f
->de
)) == -1)
419 dos_checksum(char *name
, char *ext
)
425 bcopy(ext
, buf
+8, 3);
427 for (i
= 0; i
< 11; i
++) {
428 x
= ((x
& 1) << 7) | (x
>> 1);
436 dos_readdir(struct open_file
*fd
, struct dirent
*d
)
438 /* DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; */
449 err
= dos_read(fd
, &dd
, sizeof(dd
), &res
);
452 if (res
== sizeof(dd
))
454 if (dd
.de
.name
[0] == 0)
457 /* Skip deleted entries */
458 if (dd
.de
.name
[0] == 0xe5)
461 /* Check if directory entry is volume label */
462 if (dd
.de
.attr
& FA_LABEL
) {
464 * If volume label set, check if the current entry is
465 * extended entry (FA_XDE) for long file names.
467 if ((dd
.de
.attr
& FA_MASK
) == FA_XDE
) {
469 * Read through all following extended entries
470 * to get the long file name. 0x40 marks the
471 * last entry containing part of long file name.
473 if (dd
.xde
.seq
& 0x40)
475 else if (dd
.xde
.seq
!= xdn
- 1 || dd
.xde
.chk
!= chk
)
477 x
= dd
.xde
.seq
& ~0x40;
478 if (x
< 1 || x
> 20) {
482 cp_xdnm(fn
, &dd
.xde
);
484 /* skip only volume label entries */
489 x
= dos_checksum(dd
.de
.name
, dd
.de
.ext
);
500 d
->d_fileno
= (dd
.de
.clus
[1] << 8) + dd
.de
.clus
[0];
501 d
->d_reclen
= sizeof(*d
);
502 d
->d_type
= (dd
.de
.attr
& FA_DIR
) ? DT_DIR
: DT_REG
;
503 memcpy(d
->d_name
, fn
, sizeof(d
->d_name
));
508 * Parse DOS boot sector
511 parsebs(DOS_FS
*fs
, DOS_BS
*bs
)
515 if ((bs
->jmp
[0] != 0x69 &&
516 bs
->jmp
[0] != 0xe9 &&
517 (bs
->jmp
[0] != 0xeb || bs
->jmp
[2] != 0x90)) ||
518 bs
->bpb
.media
< 0xf0)
520 if (cv2(bs
->bpb
.secsiz
) != SECSIZ
)
522 if (!(fs
->spc
= bs
->bpb
.spc
) || fs
->spc
& (fs
->spc
- 1))
524 fs
->bsize
= secbyt(fs
->spc
);
525 fs
->bshift
= ffs(fs
->bsize
) - 1;
526 if ((fs
->spf
= cv2(bs
->bpb
.spf
))) {
527 if (bs
->bpb
.fats
!= 2)
529 if (!(fs
->dirents
= cv2(bs
->bpb
.dirents
)))
532 if (!(fs
->spf
= cv4(bs
->bpb
.lspf
)))
534 if (!bs
->bpb
.fats
|| bs
->bpb
.fats
> 16)
536 if ((fs
->rdcl
= cv4(bs
->bpb
.rdcl
)) < LOCLUS
)
539 if (!(fs
->lsnfat
= cv2(bs
->bpb
.ressec
)))
541 fs
->lsndir
= fs
->lsnfat
+ fs
->spf
* bs
->bpb
.fats
;
542 fs
->lsndta
= fs
->lsndir
+ entsec(fs
->dirents
);
543 if (!(sc
= cv2(bs
->bpb
.secs
)) && !(sc
= cv4(bs
->bpb
.lsecs
)))
547 if ((fs
->xclus
= secblk(fs
, sc
- fs
->lsndta
) + 1) < LOCLUS
)
549 fs
->fatsz
= fs
->dirents
? fs
->xclus
< 0xff6 ? 12 : 16 : 32;
550 sc
= (secbyt(fs
->spf
) << 1) / (fs
->fatsz
>> 2) - 1;
557 * Return directory entry from path
560 namede(DOS_FS
*fs
, const char *path
, DOS_DE
**dep
)
575 if (!(s
= strchr(path
, '/')))
577 if ((n
= s
- path
) > 255)
579 memcpy(name
, path
, n
);
582 if (!(de
->attr
& FA_DIR
))
584 if ((err
= lookup(fs
, stclus(fs
->fatsz
, de
), name
, &de
)))
592 * Lookup path segment
595 lookup(DOS_FS
*fs
, u_int clus
, const char *name
, DOS_DE
**dep
)
597 static DOS_DIR dir
[DEPSEC
];
600 u_int nsec
, lsec
, xdn
, chk
, sec
, ent
, x
;
604 for (ent
= 0; ent
< 2; ent
++)
605 if (!strcasecmp(name
, dotstr
[ent
])) {
609 if (!clus
&& fs
->fatsz
== 32)
611 nsec
= !clus
? entsec(fs
->dirents
) : fs
->spc
;
617 else if (okclus(fs
, clus
))
618 lsec
= blklsn(fs
, clus
);
621 for (sec
= 0; sec
< nsec
; sec
++) {
622 if ((err
= ioget(fs
->fd
, lsec
+ sec
, 0, dir
, secbyt(1))))
624 for (ent
= 0; ent
< DEPSEC
; ent
++) {
625 if (!*dir
[ent
].de
.name
)
627 if (*dir
[ent
].de
.name
!= 0xe5) {
628 if ((dir
[ent
].de
.attr
& FA_MASK
) == FA_XDE
) {
629 x
= dir
[ent
].xde
.seq
;
630 if (x
& 0x40 || (x
+ 1 == xdn
&&
631 dir
[ent
].xde
.chk
== chk
)) {
633 chk
= dir
[ent
].xde
.chk
;
636 if (x
>= 1 && x
<= 20) {
637 cp_xdnm(lfn
, &dir
[ent
].xde
);
642 } else if (!(dir
[ent
].de
.attr
& FA_LABEL
)) {
643 if ((ok
= xdn
== 1)) {
644 x
= dos_checksum(dir
[ent
].de
.name
, dir
[ent
].de
.ext
);
646 !strcasecmp(name
, (const char *)lfn
);
649 cp_sfn(sfn
, &dir
[ent
].de
);
650 ok
= !strcasecmp(name
, (const char *)sfn
);
663 if ((err
= fatget(fs
, &clus
)))
665 if (fatend(fs
->fatsz
, clus
))
672 * Copy name from extended directory entry
675 cp_xdnm(u_char
*lfn
, DOS_XDE
*xde
)
681 {offsetof(DOS_XDE
, name1
), sizeof(xde
->name1
) / 2},
682 {offsetof(DOS_XDE
, name2
), sizeof(xde
->name2
) / 2},
683 {offsetof(DOS_XDE
, name3
), sizeof(xde
->name3
) / 2}
688 lfn
+= 13 * ((xde
->seq
& ~0x40) - 1);
689 for (n
= 0; n
< 3; n
++)
690 for (p
= (u_char
*)xde
+ ix
[n
].off
, x
= ix
[n
].dim
; x
;
692 if ((c
= cv2(p
)) && (c
< 32 || c
> 127))
702 * Copy short filename
705 cp_sfn(u_char
*sfn
, DOS_DE
*de
)
711 if (*de
->name
!= ' ') {
712 for (j
= 7; de
->name
[j
] == ' '; j
--);
713 for (i
= 0; i
<= j
; i
++)
715 if (*de
->ext
!= ' ') {
717 for (j
= 2; de
->ext
[j
] == ' '; j
--);
718 for (i
= 0; i
<= j
; i
++)
728 * Return size of file in bytes
731 fsize(DOS_FS
*fs
, DOS_DE
*de
)
737 if (!(size
= cv4(de
->size
)) && de
->attr
& FA_DIR
) {
738 if (!(c
= cv2(de
->clus
)))
739 size
= fs
->dirents
* sizeof(DOS_DE
);
741 if ((n
= fatcnt(fs
, c
)) == -1)
743 size
= blkbyt(fs
, n
);
750 * Count number of clusters in chain
753 fatcnt(DOS_FS
*fs
, u_int c
)
757 for (n
= 0; okclus(fs
, c
); n
++)
760 return fatend(fs
->fatsz
, c
) ? n
: -1;
764 * Get next cluster in cluster chain. Use in core fat cache unless another
765 * device replaced it.
768 fatget(DOS_FS
*fs
, u_int
*c
)
772 u_int x
, offset
, off
, n
, nbyte
, lsec
;
773 struct devdesc
*dd
= fs
->fd
->f_devdata
;
776 if (fat
.unit
!= dd
->d_unit
) {
777 /* fat cache was changed to another device, don't use it */
778 err
= ioread(fs
, secbyt(fs
->lsnfat
) + fatoff(fs
->fatsz
, *c
), buf
,
779 fs
->fatsz
!= 32 ? 2 : 4);
783 offset
= fatoff(fs
->fatsz
, *c
);
784 nbyte
= fs
->fatsz
!= 32 ? 2 : 4;
787 if ((off
= offset
& (SECSIZ
- 1))) {
789 lsec
= bytsec(offset
);
791 if ((n
= SECSIZ
- off
) > nbyte
)
793 memcpy(s
, fat
.buf
+ secbyt(lsec
) + off
, n
);
797 n
= nbyte
& (SECSIZ
- 1);
799 memcpy(s
, fat
.buf
+ secbyt(bytsec(offset
)), nbyte
);
804 memcpy(s
, fat
.buf
+ secbyt(bytsec(offset
)), n
);
807 x
= fs
->fatsz
!= 32 ? cv2(buf
) : cv4(buf
);
808 *c
= fs
->fatsz
== 12 ? *c
& 1 ? x
>> 4 : x
& 0xfff : x
;
813 * Is cluster an end-of-chain marker?
816 fatend(u_int sz
, u_int c
)
818 return c
> (sz
== 12 ? 0xff7U
: sz
== 16 ? 0xfff7U
: 0xffffff7);
822 * Offset-based I/O primitive
825 ioread(DOS_FS
*fs
, u_int offset
, void *buf
, u_int nbyte
)
832 if ((off
= offset
& (SECSIZ
- 1))) {
834 if ((n
= SECSIZ
- off
) > nbyte
)
836 if ((err
= ioget(fs
->fd
, bytsec(offset
), off
, s
, n
)))
842 n
= nbyte
& (SECSIZ
- 1);
844 if ((err
= ioget(fs
->fd
, bytsec(offset
), 0, s
, nbyte
)))
850 if ((err
= ioget(fs
->fd
, bytsec(offset
), 0, s
, n
)))
857 * Sector-based I/O primitive
860 ioget(struct open_file
*fd
, daddr_t lsec
, size_t offset
, void *buf
, u_int size
)
862 return ((fd
->f_dev
->dv_strategy
)(fd
->f_devdata
, F_READ
, lsec
, offset
,