1 /* $NetBSD: dosfs.c,v 1.20 2014/03/20 03:13:18 christos Exp $ */
4 * Copyright (c) 1996, 1998 Robert Nordier
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 * Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems,
36 * XXX DOES NOT SUPPORT:
38 * LIBSA_FS_SINGLECOMPONENT
41 #include <sys/param.h>
43 #include <fs/msdosfs/bpb.h>
44 #include <fs/msdosfs/direntry.h>
47 #include <lib/libkern/libkern.h>
56 #define SECSIZ 512 /* sector size */
57 #define SSHIFT 9 /* SECSIZ shift */
58 #define DEPSEC 16 /* directory entries per sector */
59 #define DSHIFT 4 /* DEPSEC shift */
60 #define LOCLUS 2 /* lowest cluster number */
63 struct direntry de
; /* standard directory entry */
64 struct winentry xde
; /* extended directory entry */
68 struct open_file
*fd
; /* file descriptor */
69 u_char
*buf
; /* buffer */
70 u_int bufsec
; /* buffered sector */
71 u_int links
; /* active links to structure */
72 u_int spc
; /* sectors per cluster */
73 u_int bsize
; /* cluster size in bytes */
74 u_int bshift
; /* cluster conversion shift */
75 u_int dirents
; /* root directory entries */
76 u_int spf
; /* sectors per fat */
77 u_int rdcl
; /* root directory start cluster */
78 u_int lsnfat
; /* start of fat */
79 u_int lsndir
; /* start of root dir */
80 u_int lsndta
; /* start of data area */
81 u_int fatsz
; /* FAT entry size */
82 u_int xclus
; /* maximum cluster number */
86 DOS_FS
*fs
; /* associated filesystem */
87 struct direntry de
; /* directory entry */
88 u_int offset
; /* current offset */
89 u_int c
; /* last cluster read */
92 /* Initial portion of DOS boot sector */
94 u_char jmp
[3]; /* usually 80x86 'jmp' opcode */
95 u_char oem
[8]; /* OEM name and version */
96 struct byte_bpb710 bpb
; /* BPB */
99 /* Supply missing "." and ".." root directory entries */
100 static const char *const dotstr
[2] = {".", ".."};
101 static const struct direntry dot
[2] = {
102 {". ", " ", ATTR_DIRECTORY
,
103 0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0},
104 {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}},
106 {".. ", " ", ATTR_DIRECTORY
,
107 0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0},
108 {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}
111 /* The usual conversion macros to avoid multiplication and division */
112 #define bytsec(n) ((n) >> SSHIFT)
113 #define secbyt(s) ((s) << SSHIFT)
114 #define entsec(e) ((e) >> DSHIFT)
115 #define bytblk(fs, n) ((n) >> (fs)->bshift)
116 #define blkbyt(fs, b) ((b) << (fs)->bshift)
117 #define secblk(fs, s) ((s) >> ((fs)->bshift - SSHIFT))
118 #define blksec(fs, b) ((b) << ((fs)->bshift - SSHIFT))
120 /* Convert cluster number to offset within filesystem */
121 #define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS))
123 /* Convert cluster number to logical sector number */
124 #define blklsn(fs, b) ((fs)->lsndta + blksec(fs, (b) - LOCLUS))
126 /* Convert cluster number to offset within FAT */
127 #define fatoff(sz, c) ((sz) == 12 ? (c) + ((c) >> 1) : \
128 (sz) == 16 ? (c) << 1 : \
131 /* Does cluster number reference a valid data cluster? */
132 #define okclus(fs, c) ((c) >= LOCLUS && (c) <= (fs)->xclus)
134 /* Get start cluster from directory entry */
135 #define stclus(sz, de) ((sz) != 32 ? (u_int)getushort((de)->deStartCluster) : \
136 ((u_int)getushort((de)->deHighClust) << 16) | \
137 (u_int)getushort((de)->deStartCluster))
139 static int dosunmount(DOS_FS
*);
140 static int parsebs(DOS_FS
*, DOS_BS
*);
141 static int namede(DOS_FS
*, const char *, const struct direntry
**);
142 static int lookup(DOS_FS
*, u_int
, const char *, const struct direntry
**);
143 static void cp_xdnm(u_char
*, struct winentry
*);
144 static void cp_sfn(u_char
*, struct direntry
*);
145 static off_t
fsize(DOS_FS
*, struct direntry
*);
146 static int fatcnt(DOS_FS
*, u_int
);
147 static int fatget(DOS_FS
*, u_int
*);
148 static int fatend(u_int
, u_int
);
149 static int ioread(DOS_FS
*, u_int
, void *, u_int
);
150 static int iobuf(DOS_FS
*, u_int
);
151 static int ioget(struct open_file
*, u_int
, void *, u_int
);
153 #define strcasecmp(s1, s2) dos_strcasecmp(s1, s2)
155 strcasecmp(const char *s1
, const char *s2
)
158 #define TO_UPPER(c) ((c) >= 'a' && (c) <= 'z' ? (c) - ('a' - 'A') : (c))
162 if (TO_UPPER(c1
) != TO_UPPER(c2
))
171 * Mount DOS filesystem
174 dos_mount(DOS_FS
*fs
, struct open_file
*fd
)
178 (void)memset(fs
, 0, sizeof(DOS_FS
));
180 if ((err
= !(fs
->buf
= alloc(SECSIZ
)) ? errno
: 0) ||
181 (err
= ioget(fs
->fd
, 0, fs
->buf
, 1)) ||
182 (err
= parsebs(fs
, (DOS_BS
*)fs
->buf
))) {
183 (void) dosunmount(fs
);
189 #ifndef LIBSA_NO_FS_CLOSE
191 * Unmount mounted filesystem
194 dos_unmount(DOS_FS
*fs
)
200 if ((err
= dosunmount(fs
)))
207 * Common code shared by dos_mount() and dos_unmount()
210 dosunmount(DOS_FS
*fs
)
213 dealloc(fs
->buf
, SECSIZ
);
214 dealloc(fs
, sizeof(DOS_FS
));
222 dosfs_open(const char *path
, struct open_file
*fd
)
224 const struct direntry
*de
;
230 /* Allocate mount structure, associate with open */
231 fs
= alloc(sizeof(DOS_FS
));
233 if ((err
= dos_mount(fs
, fd
)))
236 if ((err
= namede(fs
, path
, &de
)))
239 clus
= stclus(fs
->fatsz
, de
);
240 size
= getulong(de
->deFileSize
);
242 if ((!(de
->deAttributes
& ATTR_DIRECTORY
) && (!clus
!= !size
)) ||
243 ((de
->deAttributes
& ATTR_DIRECTORY
) && size
) ||
244 (clus
&& !okclus(fs
, clus
))) {
249 f
= alloc(sizeof(DOS_FILE
));
251 /* due to __internal_memset_ causing all sorts of register spillage
252 (and being completely unoptimized for zeroing small amounts of
253 memory), if we hand-initialize the remaining members of f to zero,
254 the code size drops 68 bytes. This makes no sense, admittedly. */
258 (void)memset(f
, 0, sizeof(DOS_FILE
));
263 fd
->f_fsdata
= (void *)f
;
274 dosfs_read(struct open_file
*fd
, void *vbuf
, size_t nbyte
, size_t *resid
)
277 u_int8_t
*buf
= vbuf
;
278 u_int nb
, off
, clus
, c
, cnt
, n
;
279 DOS_FILE
*f
= (DOS_FILE
*)fd
->f_fsdata
;
283 if ((size
= fsize(f
->fs
, &f
->de
)) == -1)
285 if (nb
> (n
= size
- f
->offset
))
288 if ((clus
= stclus(f
->fs
->fatsz
, &f
->de
)))
289 off
&= f
->fs
->bsize
- 1;
296 n
= bytblk(f
->fs
, f
->offset
);
301 if ((err
= fatget(f
->fs
, &c
)))
303 if (!okclus(f
->fs
, c
)) {
308 if (!clus
|| (n
= f
->fs
->bsize
- off
) > cnt
)
310 if ((err
= ioread(f
->fs
, (c
? blkoff(f
->fs
, c
) :
311 secbyt(f
->fs
->lsndir
)) + off
,
322 *resid
= nbyte
- nb
+ cnt
;
326 #ifndef LIBSA_NO_FS_WRITE
331 dosfs_write(struct open_file
*fd
, void *start
, size_t size
, size_t *resid
)
336 #endif /* !LIBSA_NO_FS_WRITE */
338 #ifndef LIBSA_NO_FS_SEEK
340 * Reposition within file
343 dosfs_seek(struct open_file
*fd
, off_t offset
, int whence
)
347 DOS_FILE
*f
= (DOS_FILE
*)fd
->f_fsdata
;
349 size
= getulong(f
->de
.deFileSize
);
364 if (off
< 0 || off
> size
)
366 f
->offset
= (u_int
) off
;
370 #endif /* !LIBSA_NO_FS_SEEK */
372 #ifndef LIBSA_NO_FS_CLOSE
377 dosfs_close(struct open_file
*fd
)
379 DOS_FILE
*f
= (DOS_FILE
*)fd
->f_fsdata
;
383 dealloc(f
, sizeof(DOS_FILE
));
387 #endif /* !LIBSA_NO_FS_CLOSE */
390 * Return some stat information on a file.
393 dosfs_stat(struct open_file
*fd
, struct stat
*sb
)
395 DOS_FILE
*f
= (DOS_FILE
*)fd
->f_fsdata
;
397 /* only important stuff */
398 sb
->st_mode
= (f
->de
.deAttributes
& ATTR_DIRECTORY
) ?
399 (S_IFDIR
| 0555) : (S_IFREG
| 0444);
403 if ((sb
->st_size
= fsize(f
->fs
, &f
->de
)) == -1)
408 #if defined(LIBSA_ENABLE_LS_OP)
411 dosfs_ls(struct open_file
*f
, const char *pattern
)
416 #if defined(__minix) && defined(LIBSA_ENABLE_LOAD_MODS_OP)
418 dosfs_load_mods(struct open_file
*f
, const char *pattern
,
419 void (*funcp
)(char *), char *path
)
421 load_modsunsup("dosfs");
423 #endif /* defined(__minix) && defined(LIBSA_ENABLE_LOAD_MODS_OP) */
427 * Parse DOS boot sector
430 parsebs(DOS_FS
*fs
, DOS_BS
*bs
)
434 if ((bs
->jmp
[0] != 0x69 &&
435 bs
->jmp
[0] != 0xe9 &&
436 (bs
->jmp
[0] != 0xeb || bs
->jmp
[2] != 0x90)) ||
437 bs
->bpb
.bpbMedia
< 0xf0)
439 if (getushort(bs
->bpb
.bpbBytesPerSec
) != SECSIZ
)
441 if (!(fs
->spc
= bs
->bpb
.bpbSecPerClust
) || fs
->spc
& (fs
->spc
- 1))
443 fs
->bsize
= secbyt(fs
->spc
);
444 fs
->bshift
= ffs(fs
->bsize
) - 1;
445 if ((fs
->spf
= getushort(bs
->bpb
.bpbFATsecs
))) {
446 if (bs
->bpb
.bpbFATs
!= 2)
448 if (!(fs
->dirents
= getushort(bs
->bpb
.bpbRootDirEnts
)))
451 if (!(fs
->spf
= getulong(bs
->bpb
.bpbBigFATsecs
)))
453 if (!bs
->bpb
.bpbFATs
|| bs
->bpb
.bpbFATs
> 16)
455 if ((fs
->rdcl
= getulong(bs
->bpb
.bpbRootClust
)) < LOCLUS
)
458 if (!(fs
->lsnfat
= getushort(bs
->bpb
.bpbResSectors
)))
460 fs
->lsndir
= fs
->lsnfat
+ fs
->spf
* bs
->bpb
.bpbFATs
;
461 fs
->lsndta
= fs
->lsndir
+ entsec(fs
->dirents
);
462 if (!(sc
= getushort(bs
->bpb
.bpbSectors
)) &&
463 !(sc
= getulong(bs
->bpb
.bpbHugeSectors
)))
467 if ((fs
->xclus
= secblk(fs
, sc
- fs
->lsndta
) + 1) < LOCLUS
)
469 fs
->fatsz
= fs
->dirents
? fs
->xclus
< 0xff6 ? 12 : 16 : 32;
470 sc
= (secbyt(fs
->spf
) << 1) / (fs
->fatsz
>> 2) - 1;
477 * Return directory entry from path
480 namede(DOS_FS
*fs
, const char *path
, const struct direntry
**dep
)
483 const struct direntry
*de
;
493 if (!(s
= strchr(path
, '/')))
495 if ((n
= s
- path
) > 255)
497 memcpy(name
, path
, n
);
500 if (!(de
->deAttributes
& ATTR_DIRECTORY
))
502 if ((err
= lookup(fs
, stclus(fs
->fatsz
, de
), name
, &de
)))
512 * Lookup path segment
515 lookup(DOS_FS
*fs
, u_int clus
, const char *name
, const struct direntry
**dep
)
517 static DOS_DIR
*dir
= NULL
;
520 u_int nsec
, lsec
, xdn
, chk
, sec
, ent
, x
;
524 for (ent
= 0; ent
< 2; ent
++)
525 if (!strcasecmp(name
, dotstr
[ent
])) {
531 dir
= alloc(sizeof(DOS_DIR
) * DEPSEC
);
536 if (!clus
&& fs
->fatsz
== 32)
538 nsec
= !clus
? entsec(fs
->dirents
) : fs
->spc
;
544 else if (okclus(fs
, clus
))
545 lsec
= blklsn(fs
, clus
);
550 for (sec
= 0; sec
< nsec
; sec
++) {
551 if ((err
= ioget(fs
->fd
, lsec
+ sec
, dir
, 1)))
553 for (ent
= 0; ent
< DEPSEC
; ent
++) {
554 if (!*dir
[ent
].de
.deName
) {
558 if (*dir
[ent
].de
.deName
!= 0xe5) {
559 if (dir
[ent
].de
.deAttributes
==
561 x
= dir
[ent
].xde
.weCnt
;
564 dir
[ent
].xde
.weChksum
==
567 chk
= dir
[ent
].xde
.weChksum
;
570 if (x
>= 1 && x
<= 20) {
571 cp_xdnm(lfn
, &dir
[ent
].xde
);
576 } else if (!(dir
[ent
].de
.deAttributes
&
578 if ((ok
= xdn
== 1)) {
581 x
= ((((x
& 1) << 7) | (x
>> 1)) +
582 msdos_dirchar(&dir
[ent
].de
,i
)) & 0xff;
584 !strcasecmp(name
, (const char *)lfn
);
587 cp_sfn(sfn
, &dir
[ent
].de
);
588 ok
= !strcasecmp(name
, (const char *)sfn
);
601 if ((err
= fatget(fs
, &clus
)))
603 if (fatend(fs
->fatsz
, clus
))
608 dealloc(dir
, sizeof(DOS_DIR
) * DEPSEC
);
615 * Copy name from extended directory entry
618 cp_xdnm(u_char
*lfn
, struct winentry
*xde
)
620 static const struct {
624 { offsetof(struct winentry
, wePart1
),
625 sizeof(xde
->wePart1
) / 2 },
626 { offsetof(struct winentry
, wePart2
),
627 sizeof(xde
->wePart2
) / 2 },
628 { offsetof(struct winentry
, wePart3
),
629 sizeof(xde
->wePart3
) / 2 }
634 lfn
+= 13 * ((xde
->weCnt
& WIN_CNT
) - 1);
635 for (n
= 0; n
< 3; n
++)
636 for (p
= (u_char
*)xde
+ ix
[n
].off
, x
= ix
[n
].dim
; x
;
638 if ((c
= getushort(p
)) && (c
< 32 || c
> 127))
643 if (xde
->weCnt
& WIN_LAST
)
648 * Copy short filename
651 cp_sfn(u_char
*sfn
, struct direntry
*de
)
657 if (*de
->deName
!= ' ') {
658 for (j
= 7; de
->deName
[j
] == ' '; j
--);
659 for (i
= 0; i
<= j
; i
++)
660 *p
++ = de
->deName
[i
];
661 if (*de
->deExtension
!= ' ') {
663 for (j
= 2; de
->deExtension
[j
] == ' '; j
--);
664 for (i
= 0; i
<= j
; i
++)
665 *p
++ = de
->deExtension
[i
];
674 * Return size of file in bytes
677 fsize(DOS_FS
*fs
, struct direntry
*de
)
683 if (!(size
= getulong(de
->deFileSize
)) &&
684 de
->deAttributes
& ATTR_DIRECTORY
) {
685 if (!(c
= getushort(de
->deStartCluster
))) {
686 size
= fs
->dirents
* sizeof(struct direntry
);
688 if ((n
= fatcnt(fs
, c
)) == -1)
690 size
= blkbyt(fs
, n
);
697 * Count number of clusters in chain
700 fatcnt(DOS_FS
*fs
, u_int c
)
704 for (n
= 0; okclus(fs
, c
); n
++)
707 return fatend(fs
->fatsz
, c
) ? n
: -1;
711 * Get next cluster in cluster chain
714 fatget(DOS_FS
*fs
, u_int
*c
)
720 err
= ioread(fs
, secbyt(fs
->lsnfat
) + fatoff(fs
->fatsz
, *c
), buf
,
721 fs
->fatsz
!= 32 ? 2 : 4);
724 x
= fs
->fatsz
!= 32 ? getushort(buf
) : getulong(buf
);
725 *c
= fs
->fatsz
== 12 ? *c
& 1 ? x
>> 4 : x
& 0xfff : x
;
730 * Is cluster an end-of-chain marker?
733 fatend(u_int sz
, u_int c
)
735 return c
> (sz
== 12 ? 0xff7U
: sz
== 16 ? 0xfff7U
: 0xffffff7);
739 * Offset-based I/O primitive
742 ioread(DOS_FS
*fs
, u_int offset
, void *buf
, u_int nbyte
)
749 if ((off
= offset
& (SECSIZ
- 1))) {
751 if ((err
= iobuf(fs
, bytsec(offset
))))
754 if ((n
= SECSIZ
- off
) > nbyte
)
756 memcpy(s
, fs
->buf
+ off
, n
);
760 n
= nbyte
& (SECSIZ
- 1);
762 if ((err
= ioget(fs
->fd
, bytsec(offset
), s
, bytsec(nbyte
))))
768 if ((err
= iobuf(fs
, bytsec(offset
))))
770 memcpy(s
, fs
->buf
, n
);
776 * Buffered sector-based I/O primitive
779 iobuf(DOS_FS
*fs
, u_int lsec
)
783 if (fs
->bufsec
!= lsec
) {
784 if ((err
= ioget(fs
->fd
, lsec
, fs
->buf
, 1)))
792 * Sector-based I/O primitive
795 ioget(struct open_file
*fd
, u_int lsec
, void *buf
, u_int nsec
)
800 #ifndef LIBSA_NO_TWIDDLE
803 err
= DEV_STRATEGY(fd
->f_dev
)(fd
->f_devdata
, F_READ
, lsec
,
804 secbyt(nsec
), buf
, &rsize
);