1 1.23 martin /* $NetBSD: dosfs.c,v 1.23 2020/01/22 06:11:28 martin Exp $ */ 2 1.1 thorpej 3 1.1 thorpej /* 4 1.1 thorpej * Copyright (c) 1996, 1998 Robert Nordier 5 1.1 thorpej * All rights reserved. 6 1.1 thorpej * 7 1.1 thorpej * Redistribution and use in source and binary forms, with or without 8 1.1 thorpej * modification, are permitted provided that the following conditions 9 1.1 thorpej * are met: 10 1.1 thorpej * 1. Redistributions of source code must retain the above copyright 11 1.1 thorpej * notice, this list of conditions and the following disclaimer. 12 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 thorpej * notice, this list of conditions and the following disclaimer in 14 1.1 thorpej * the documentation and/or other materials provided with the 15 1.1 thorpej * distribution. 16 1.1 thorpej * 17 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS 18 1.1 thorpej * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 1.1 thorpej * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 1.1 thorpej * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY 21 1.1 thorpej * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 1.1 thorpej * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 23 1.1 thorpej * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 1.1 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 1.1 thorpej * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 26 1.1 thorpej * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 27 1.1 thorpej * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 1.1 thorpej */ 29 1.1 thorpej 30 1.1 thorpej /* 31 1.1 thorpej * Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems, 32 1.1 thorpej * also supports VFAT. 33 1.1 thorpej */ 34 1.1 thorpej 35 1.1 thorpej /* 36 1.1 thorpej * XXX DOES NOT SUPPORT: 37 1.1 thorpej * 38 1.1 thorpej * LIBSA_FS_SINGLECOMPONENT 39 1.1 thorpej */ 40 1.1 thorpej 41 1.1 thorpej #include <sys/param.h> 42 1.1 thorpej 43 1.5 veego #include <fs/msdosfs/bpb.h> 44 1.5 veego #include <fs/msdosfs/direntry.h> 45 1.1 thorpej 46 1.1 thorpej #ifdef _STANDALONE 47 1.1 thorpej #include <lib/libkern/libkern.h> 48 1.1 thorpej #else 49 1.1 thorpej #include <string.h> 50 1.1 thorpej #include <stddef.h> 51 1.1 thorpej #endif 52 1.1 thorpej 53 1.1 thorpej #include "stand.h" 54 1.1 thorpej #include "dosfs.h" 55 1.1 thorpej 56 1.1 thorpej #define SECSIZ 512 /* sector size */ 57 1.1 thorpej #define SSHIFT 9 /* SECSIZ shift */ 58 1.1 thorpej #define DEPSEC 16 /* directory entries per sector */ 59 1.1 thorpej #define DSHIFT 4 /* DEPSEC shift */ 60 1.1 thorpej #define LOCLUS 2 /* lowest cluster number */ 61 1.1 thorpej 62 1.1 thorpej typedef union { 63 1.1 thorpej struct direntry de; /* standard directory entry */ 64 1.1 thorpej struct winentry xde; /* extended directory entry */ 65 1.1 thorpej } DOS_DIR; 66 1.1 thorpej 67 1.1 thorpej typedef struct { 68 1.1 thorpej struct open_file *fd; /* file descriptor */ 69 1.1 thorpej u_char *buf; /* buffer */ 70 1.1 thorpej u_int bufsec; /* buffered sector */ 71 1.1 thorpej u_int links; /* active links to structure */ 72 1.1 thorpej u_int spc; /* sectors per cluster */ 73 1.1 thorpej u_int bsize; /* cluster size in bytes */ 74 1.1 thorpej u_int bshift; /* cluster conversion shift */ 75 1.1 thorpej u_int dirents; /* root directory entries */ 76 1.1 thorpej u_int spf; /* sectors per fat */ 77 1.1 thorpej u_int rdcl; /* root directory start cluster */ 78 1.1 thorpej u_int lsnfat; /* start of fat */ 79 1.1 thorpej u_int lsndir; /* start of root dir */ 80 1.1 thorpej u_int lsndta; /* start of data area */ 81 1.1 thorpej u_int fatsz; /* FAT entry size */ 82 1.1 thorpej u_int xclus; /* maximum cluster number */ 83 1.1 thorpej } DOS_FS; 84 1.1 thorpej 85 1.1 thorpej typedef struct { 86 1.1 thorpej DOS_FS *fs; /* associated filesystem */ 87 1.1 thorpej struct direntry de; /* directory entry */ 88 1.1 thorpej u_int offset; /* current offset */ 89 1.1 thorpej u_int c; /* last cluster read */ 90 1.1 thorpej } DOS_FILE; 91 1.1 thorpej 92 1.1 thorpej /* Initial portion of DOS boot sector */ 93 1.1 thorpej typedef struct { 94 1.1 thorpej u_char jmp[3]; /* usually 80x86 'jmp' opcode */ 95 1.1 thorpej u_char oem[8]; /* OEM name and version */ 96 1.1 thorpej struct byte_bpb710 bpb; /* BPB */ 97 1.1 thorpej } DOS_BS; 98 1.1 thorpej 99 1.1 thorpej /* Supply missing "." and ".." root directory entries */ 100 1.1 thorpej static const char *const dotstr[2] = {".", ".."}; 101 1.2 thorpej static const struct direntry dot[2] = { 102 1.1 thorpej {". ", " ", ATTR_DIRECTORY, 103 1.1 thorpej 0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, 104 1.1 thorpej {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}, 105 1.1 thorpej 106 1.1 thorpej {".. ", " ", ATTR_DIRECTORY, 107 1.1 thorpej 0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, 108 1.1 thorpej {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}} 109 1.1 thorpej }; 110 1.1 thorpej 111 1.23 martin #ifdef SA_DOSFS_NO_BIG_PART_SUPPORT 112 1.23 martin #define BYTE_OFF_T u_int 113 1.23 martin #else 114 1.23 martin #define BYTE_OFF_T uint64_t 115 1.23 martin #endif 116 1.23 martin 117 1.1 thorpej /* The usual conversion macros to avoid multiplication and division */ 118 1.1 thorpej #define bytsec(n) ((n) >> SSHIFT) 119 1.23 martin #define secbyt(s) ((BYTE_OFF_T)(s) << SSHIFT) 120 1.1 thorpej #define entsec(e) ((e) >> DSHIFT) 121 1.1 thorpej #define bytblk(fs, n) ((n) >> (fs)->bshift) 122 1.23 martin #define blkbyt(fs, b) ((BYTE_OFF_T)(b) << (fs)->bshift) 123 1.1 thorpej #define secblk(fs, s) ((s) >> ((fs)->bshift - SSHIFT)) 124 1.1 thorpej #define blksec(fs, b) ((b) << ((fs)->bshift - SSHIFT)) 125 1.1 thorpej 126 1.1 thorpej /* Convert cluster number to offset within filesystem */ 127 1.1 thorpej #define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS)) 128 1.1 thorpej 129 1.1 thorpej /* Convert cluster number to logical sector number */ 130 1.1 thorpej #define blklsn(fs, b) ((fs)->lsndta + blksec(fs, (b) - LOCLUS)) 131 1.1 thorpej 132 1.1 thorpej /* Convert cluster number to offset within FAT */ 133 1.1 thorpej #define fatoff(sz, c) ((sz) == 12 ? (c) + ((c) >> 1) : \ 134 1.1 thorpej (sz) == 16 ? (c) << 1 : \ 135 1.12 isaki (c) << 2) 136 1.1 thorpej 137 1.1 thorpej /* Does cluster number reference a valid data cluster? */ 138 1.1 thorpej #define okclus(fs, c) ((c) >= LOCLUS && (c) <= (fs)->xclus) 139 1.1 thorpej 140 1.1 thorpej /* Get start cluster from directory entry */ 141 1.8 fvdl #define stclus(sz, de) ((sz) != 32 ? (u_int)getushort((de)->deStartCluster) : \ 142 1.1 thorpej ((u_int)getushort((de)->deHighClust) << 16) | \ 143 1.12 isaki (u_int)getushort((de)->deStartCluster)) 144 1.1 thorpej 145 1.1 thorpej static int dosunmount(DOS_FS *); 146 1.1 thorpej static int parsebs(DOS_FS *, DOS_BS *); 147 1.2 thorpej static int namede(DOS_FS *, const char *, const struct direntry **); 148 1.2 thorpej static int lookup(DOS_FS *, u_int, const char *, const struct direntry **); 149 1.1 thorpej static void cp_xdnm(u_char *, struct winentry *); 150 1.1 thorpej static void cp_sfn(u_char *, struct direntry *); 151 1.1 thorpej static off_t fsize(DOS_FS *, struct direntry *); 152 1.1 thorpej static int fatcnt(DOS_FS *, u_int); 153 1.1 thorpej static int fatget(DOS_FS *, u_int *); 154 1.1 thorpej static int fatend(u_int, u_int); 155 1.23 martin static int ioread(DOS_FS *, BYTE_OFF_T, void *, u_int); 156 1.1 thorpej static int iobuf(DOS_FS *, u_int); 157 1.1 thorpej static int ioget(struct open_file *, u_int, void *, u_int); 158 1.1 thorpej 159 1.13 dsl #define strcasecmp(s1, s2) dos_strcasecmp(s1, s2) 160 1.13 dsl static int 161 1.13 dsl strcasecmp(const char *s1, const char *s2) 162 1.13 dsl { 163 1.13 dsl char c1, c2; 164 1.13 dsl #define TO_UPPER(c) ((c) >= 'a' && (c) <= 'z' ? (c) - ('a' - 'A') : (c)) 165 1.13 dsl for (;;) { 166 1.13 dsl c1 = *s1++; 167 1.13 dsl c2 = *s2++; 168 1.13 dsl if (TO_UPPER(c1) != TO_UPPER(c2)) 169 1.13 dsl return 1; 170 1.13 dsl if (c1 == 0) 171 1.13 dsl return 0; 172 1.13 dsl } 173 1.13 dsl #undef TO_UPPER 174 1.13 dsl } 175 1.13 dsl 176 1.1 thorpej /* 177 1.1 thorpej * Mount DOS filesystem 178 1.1 thorpej */ 179 1.1 thorpej static int 180 1.12 isaki dos_mount(DOS_FS *fs, struct open_file *fd) 181 1.1 thorpej { 182 1.1 thorpej int err; 183 1.1 thorpej 184 1.14 christos (void)memset(fs, 0, sizeof(DOS_FS)); 185 1.1 thorpej fs->fd = fd; 186 1.1 thorpej if ((err = !(fs->buf = alloc(SECSIZ)) ? errno : 0) || 187 1.1 thorpej (err = ioget(fs->fd, 0, fs->buf, 1)) || 188 1.12 isaki (err = parsebs(fs, (DOS_BS *)fs->buf))) { 189 1.1 thorpej (void) dosunmount(fs); 190 1.12 isaki return err; 191 1.1 thorpej } 192 1.1 thorpej return 0; 193 1.1 thorpej } 194 1.1 thorpej 195 1.4 matt #ifndef LIBSA_NO_FS_CLOSE 196 1.1 thorpej /* 197 1.1 thorpej * Unmount mounted filesystem 198 1.1 thorpej */ 199 1.1 thorpej static int 200 1.12 isaki dos_unmount(DOS_FS *fs) 201 1.1 thorpej { 202 1.1 thorpej int err; 203 1.1 thorpej 204 1.1 thorpej if (fs->links) 205 1.12 isaki return EBUSY; 206 1.1 thorpej if ((err = dosunmount(fs))) 207 1.12 isaki return err; 208 1.1 thorpej return 0; 209 1.1 thorpej } 210 1.4 matt #endif 211 1.1 thorpej 212 1.1 thorpej /* 213 1.1 thorpej * Common code shared by dos_mount() and dos_unmount() 214 1.1 thorpej */ 215 1.1 thorpej static int 216 1.12 isaki dosunmount(DOS_FS *fs) 217 1.1 thorpej { 218 1.1 thorpej if (fs->buf) 219 1.10 christos dealloc(fs->buf, SECSIZ); 220 1.10 christos dealloc(fs, sizeof(DOS_FS)); 221 1.12 isaki return 0; 222 1.1 thorpej } 223 1.1 thorpej 224 1.1 thorpej /* 225 1.1 thorpej * Open DOS file 226 1.1 thorpej */ 227 1.17 joerg __compactcall int 228 1.7 dsl dosfs_open(const char *path, struct open_file *fd) 229 1.1 thorpej { 230 1.2 thorpej const struct direntry *de; 231 1.1 thorpej DOS_FILE *f; 232 1.1 thorpej DOS_FS *fs; 233 1.1 thorpej u_int size, clus; 234 1.1 thorpej int err = 0; 235 1.1 thorpej 236 1.1 thorpej /* Allocate mount structure, associate with open */ 237 1.1 thorpej fs = alloc(sizeof(DOS_FS)); 238 1.1 thorpej 239 1.1 thorpej if ((err = dos_mount(fs, fd))) 240 1.1 thorpej goto out; 241 1.1 thorpej 242 1.1 thorpej if ((err = namede(fs, path, &de))) 243 1.1 thorpej goto out; 244 1.1 thorpej 245 1.1 thorpej clus = stclus(fs->fatsz, de); 246 1.1 thorpej size = getulong(de->deFileSize); 247 1.1 thorpej 248 1.1 thorpej if ((!(de->deAttributes & ATTR_DIRECTORY) && (!clus != !size)) || 249 1.1 thorpej ((de->deAttributes & ATTR_DIRECTORY) && size) || 250 1.1 thorpej (clus && !okclus(fs, clus))) { 251 1.1 thorpej err = EINVAL; 252 1.1 thorpej goto out; 253 1.1 thorpej } 254 1.11 dogcow 255 1.1 thorpej f = alloc(sizeof(DOS_FILE)); 256 1.11 dogcow #ifdef BOOTXX 257 1.11 dogcow /* due to __internal_memset_ causing all sorts of register spillage 258 1.11 dogcow (and being completely unoptimized for zeroing small amounts of 259 1.11 dogcow memory), if we hand-initialize the remaining members of f to zero, 260 1.11 dogcow the code size drops 68 bytes. This makes no sense, admittedly. */ 261 1.11 dogcow f->offset = 0; 262 1.11 dogcow f->c = 0; 263 1.11 dogcow #else 264 1.14 christos (void)memset(f, 0, sizeof(DOS_FILE)); 265 1.11 dogcow #endif 266 1.1 thorpej f->fs = fs; 267 1.1 thorpej fs->links++; 268 1.1 thorpej f->de = *de; 269 1.12 isaki fd->f_fsdata = (void *)f; 270 1.16 ad fsmod = "msdos"; 271 1.1 thorpej 272 1.1 thorpej out: 273 1.12 isaki return err; 274 1.1 thorpej } 275 1.1 thorpej 276 1.1 thorpej /* 277 1.1 thorpej * Read from file 278 1.1 thorpej */ 279 1.17 joerg __compactcall int 280 1.12 isaki dosfs_read(struct open_file *fd, void *vbuf, size_t nbyte, size_t *resid) 281 1.1 thorpej { 282 1.1 thorpej off_t size; 283 1.3 matt u_int8_t *buf = vbuf; 284 1.1 thorpej u_int nb, off, clus, c, cnt, n; 285 1.12 isaki DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 286 1.1 thorpej int err = 0; 287 1.1 thorpej 288 1.1 thorpej nb = (u_int) nbyte; 289 1.1 thorpej if ((size = fsize(f->fs, &f->de)) == -1) 290 1.1 thorpej return EINVAL; 291 1.22 christos n = (u_int)(size - f->offset); 292 1.22 christos if (nb > n) 293 1.1 thorpej nb = n; 294 1.1 thorpej off = f->offset; 295 1.1 thorpej if ((clus = stclus(f->fs->fatsz, &f->de))) 296 1.1 thorpej off &= f->fs->bsize - 1; 297 1.1 thorpej c = f->c; 298 1.1 thorpej cnt = nb; 299 1.1 thorpej while (cnt) { 300 1.1 thorpej n = 0; 301 1.1 thorpej if (!c) { 302 1.1 thorpej if ((c = clus)) 303 1.1 thorpej n = bytblk(f->fs, f->offset); 304 1.12 isaki } else if (!off) { 305 1.1 thorpej n++; 306 1.12 isaki } 307 1.1 thorpej while (n--) { 308 1.1 thorpej if ((err = fatget(f->fs, &c))) 309 1.1 thorpej goto out; 310 1.1 thorpej if (!okclus(f->fs, c)) { 311 1.1 thorpej err = EINVAL; 312 1.1 thorpej goto out; 313 1.1 thorpej } 314 1.1 thorpej } 315 1.1 thorpej if (!clus || (n = f->fs->bsize - off) > cnt) 316 1.1 thorpej n = cnt; 317 1.1 thorpej if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) : 318 1.1 thorpej secbyt(f->fs->lsndir)) + off, 319 1.1 thorpej buf, n))) 320 1.1 thorpej goto out; 321 1.1 thorpej f->offset += n; 322 1.1 thorpej f->c = c; 323 1.1 thorpej off = 0; 324 1.1 thorpej buf += n; 325 1.1 thorpej cnt -= n; 326 1.1 thorpej } 327 1.1 thorpej out: 328 1.1 thorpej if (resid) 329 1.1 thorpej *resid = nbyte - nb + cnt; 330 1.12 isaki return err; 331 1.1 thorpej } 332 1.1 thorpej 333 1.1 thorpej #ifndef LIBSA_NO_FS_WRITE 334 1.1 thorpej /* 335 1.1 thorpej * Not implemented. 336 1.1 thorpej */ 337 1.17 joerg __compactcall int 338 1.1 thorpej dosfs_write(struct open_file *fd, void *start, size_t size, size_t *resid) 339 1.1 thorpej { 340 1.1 thorpej 341 1.12 isaki return EROFS; 342 1.1 thorpej } 343 1.1 thorpej #endif /* !LIBSA_NO_FS_WRITE */ 344 1.1 thorpej 345 1.1 thorpej #ifndef LIBSA_NO_FS_SEEK 346 1.1 thorpej /* 347 1.1 thorpej * Reposition within file 348 1.1 thorpej */ 349 1.17 joerg __compactcall off_t 350 1.12 isaki dosfs_seek(struct open_file *fd, off_t offset, int whence) 351 1.1 thorpej { 352 1.1 thorpej off_t off; 353 1.1 thorpej u_int size; 354 1.12 isaki DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 355 1.1 thorpej 356 1.1 thorpej size = getulong(f->de.deFileSize); 357 1.1 thorpej switch (whence) { 358 1.1 thorpej case SEEK_SET: 359 1.1 thorpej off = 0; 360 1.1 thorpej break; 361 1.1 thorpej case SEEK_CUR: 362 1.1 thorpej off = f->offset; 363 1.1 thorpej break; 364 1.1 thorpej case SEEK_END: 365 1.1 thorpej off = size; 366 1.1 thorpej break; 367 1.1 thorpej default: 368 1.12 isaki return -1; 369 1.1 thorpej } 370 1.1 thorpej off += offset; 371 1.1 thorpej if (off < 0 || off > size) 372 1.12 isaki return -1; 373 1.1 thorpej f->offset = (u_int) off; 374 1.1 thorpej f->c = 0; 375 1.12 isaki return off; 376 1.1 thorpej } 377 1.1 thorpej #endif /* !LIBSA_NO_FS_SEEK */ 378 1.1 thorpej 379 1.1 thorpej #ifndef LIBSA_NO_FS_CLOSE 380 1.1 thorpej /* 381 1.1 thorpej * Close open file 382 1.1 thorpej */ 383 1.17 joerg __compactcall int 384 1.12 isaki dosfs_close(struct open_file *fd) 385 1.1 thorpej { 386 1.12 isaki DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 387 1.1 thorpej DOS_FS *fs = f->fs; 388 1.1 thorpej 389 1.1 thorpej f->fs->links--; 390 1.10 christos dealloc(f, sizeof(DOS_FILE)); 391 1.1 thorpej dos_unmount(fs); 392 1.1 thorpej return 0; 393 1.1 thorpej } 394 1.1 thorpej #endif /* !LIBSA_NO_FS_CLOSE */ 395 1.1 thorpej 396 1.1 thorpej /* 397 1.1 thorpej * Return some stat information on a file. 398 1.1 thorpej */ 399 1.17 joerg __compactcall int 400 1.12 isaki dosfs_stat(struct open_file *fd, struct stat *sb) 401 1.1 thorpej { 402 1.12 isaki DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 403 1.1 thorpej 404 1.1 thorpej /* only important stuff */ 405 1.1 thorpej sb->st_mode = (f->de.deAttributes & ATTR_DIRECTORY) ? 406 1.1 thorpej (S_IFDIR | 0555) : (S_IFREG | 0444); 407 1.1 thorpej sb->st_nlink = 1; 408 1.1 thorpej sb->st_uid = 0; 409 1.1 thorpej sb->st_gid = 0; 410 1.1 thorpej if ((sb->st_size = fsize(f->fs, &f->de)) == -1) 411 1.1 thorpej return EINVAL; 412 1.12 isaki return 0; 413 1.1 thorpej } 414 1.1 thorpej 415 1.18 tsutsui #if defined(LIBSA_ENABLE_LS_OP) 416 1.20 christos #include "ls.h" 417 1.18 tsutsui __compactcall void 418 1.18 tsutsui dosfs_ls(struct open_file *f, const char *pattern) 419 1.18 tsutsui { 420 1.20 christos lsunsup("dosfs"); 421 1.18 tsutsui } 422 1.18 tsutsui #endif 423 1.18 tsutsui 424 1.1 thorpej /* 425 1.1 thorpej * Parse DOS boot sector 426 1.1 thorpej */ 427 1.1 thorpej static int 428 1.12 isaki parsebs(DOS_FS *fs, DOS_BS *bs) 429 1.1 thorpej { 430 1.1 thorpej u_int sc; 431 1.1 thorpej 432 1.1 thorpej if ((bs->jmp[0] != 0x69 && 433 1.1 thorpej bs->jmp[0] != 0xe9 && 434 1.1 thorpej (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) || 435 1.1 thorpej bs->bpb.bpbMedia < 0xf0) 436 1.1 thorpej return EINVAL; 437 1.1 thorpej if (getushort(bs->bpb.bpbBytesPerSec) != SECSIZ) 438 1.1 thorpej return EINVAL; 439 1.1 thorpej if (!(fs->spc = bs->bpb.bpbSecPerClust) || fs->spc & (fs->spc - 1)) 440 1.1 thorpej return EINVAL; 441 1.1 thorpej fs->bsize = secbyt(fs->spc); 442 1.22 christos fs->bshift = (u_int)ffs((int)fs->bsize) - 1; 443 1.1 thorpej if ((fs->spf = getushort(bs->bpb.bpbFATsecs))) { 444 1.1 thorpej if (bs->bpb.bpbFATs != 2) 445 1.1 thorpej return EINVAL; 446 1.1 thorpej if (!(fs->dirents = getushort(bs->bpb.bpbRootDirEnts))) 447 1.1 thorpej return EINVAL; 448 1.1 thorpej } else { 449 1.1 thorpej if (!(fs->spf = getulong(bs->bpb.bpbBigFATsecs))) 450 1.1 thorpej return EINVAL; 451 1.1 thorpej if (!bs->bpb.bpbFATs || bs->bpb.bpbFATs > 16) 452 1.1 thorpej return EINVAL; 453 1.1 thorpej if ((fs->rdcl = getulong(bs->bpb.bpbRootClust)) < LOCLUS) 454 1.1 thorpej return EINVAL; 455 1.1 thorpej } 456 1.1 thorpej if (!(fs->lsnfat = getushort(bs->bpb.bpbResSectors))) 457 1.1 thorpej return EINVAL; 458 1.1 thorpej fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.bpbFATs; 459 1.1 thorpej fs->lsndta = fs->lsndir + entsec(fs->dirents); 460 1.1 thorpej if (!(sc = getushort(bs->bpb.bpbSectors)) && 461 1.1 thorpej !(sc = getulong(bs->bpb.bpbHugeSectors))) 462 1.1 thorpej return EINVAL; 463 1.1 thorpej if (fs->lsndta > sc) 464 1.1 thorpej return EINVAL; 465 1.1 thorpej if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS) 466 1.1 thorpej return EINVAL; 467 1.1 thorpej fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32; 468 1.1 thorpej sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1; 469 1.1 thorpej if (fs->xclus > sc) 470 1.1 thorpej fs->xclus = sc; 471 1.1 thorpej return 0; 472 1.1 thorpej } 473 1.1 thorpej 474 1.1 thorpej /* 475 1.1 thorpej * Return directory entry from path 476 1.1 thorpej */ 477 1.1 thorpej static int 478 1.12 isaki namede(DOS_FS *fs, const char *path, const struct direntry **dep) 479 1.1 thorpej { 480 1.1 thorpej char name[256]; 481 1.2 thorpej const struct direntry *de; 482 1.1 thorpej char *s; 483 1.1 thorpej size_t n; 484 1.1 thorpej int err; 485 1.1 thorpej 486 1.1 thorpej err = 0; 487 1.1 thorpej de = dot; 488 1.1 thorpej if (*path == '/') 489 1.1 thorpej path++; 490 1.1 thorpej while (*path) { 491 1.1 thorpej if (!(s = strchr(path, '/'))) 492 1.1 thorpej s = strchr(path, 0); 493 1.22 christos n = (size_t)(s - path); 494 1.22 christos if (n > 255) 495 1.1 thorpej return ENAMETOOLONG; 496 1.1 thorpej memcpy(name, path, n); 497 1.1 thorpej name[n] = 0; 498 1.1 thorpej path = s; 499 1.1 thorpej if (!(de->deAttributes & ATTR_DIRECTORY)) 500 1.1 thorpej return ENOTDIR; 501 1.1 thorpej if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de))) 502 1.1 thorpej return err; 503 1.1 thorpej if (*path == '/') 504 1.1 thorpej path++; 505 1.1 thorpej } 506 1.1 thorpej *dep = de; 507 1.1 thorpej return 0; 508 1.1 thorpej } 509 1.1 thorpej 510 1.1 thorpej /* 511 1.1 thorpej * Lookup path segment 512 1.1 thorpej */ 513 1.1 thorpej static int 514 1.12 isaki lookup(DOS_FS *fs, u_int clus, const char *name, const struct direntry **dep) 515 1.1 thorpej { 516 1.6 scw static DOS_DIR *dir = NULL; 517 1.1 thorpej u_char lfn[261]; 518 1.1 thorpej u_char sfn[13]; 519 1.1 thorpej u_int nsec, lsec, xdn, chk, sec, ent, x; 520 1.1 thorpej int err = 0, ok, i; 521 1.1 thorpej 522 1.1 thorpej if (!clus) 523 1.1 thorpej for (ent = 0; ent < 2; ent++) 524 1.1 thorpej if (!strcasecmp(name, dotstr[ent])) { 525 1.1 thorpej *dep = dot + ent; 526 1.1 thorpej return 0; 527 1.1 thorpej } 528 1.1 thorpej 529 1.6 scw if (dir == NULL) { 530 1.6 scw dir = alloc(sizeof(DOS_DIR) * DEPSEC); 531 1.6 scw if (dir == NULL) 532 1.12 isaki return ENOMEM; 533 1.6 scw } 534 1.1 thorpej 535 1.1 thorpej if (!clus && fs->fatsz == 32) 536 1.1 thorpej clus = fs->rdcl; 537 1.1 thorpej nsec = !clus ? entsec(fs->dirents) : fs->spc; 538 1.1 thorpej lsec = 0; 539 1.1 thorpej xdn = chk = 0; 540 1.1 thorpej for (;;) { 541 1.1 thorpej if (!clus && !lsec) 542 1.1 thorpej lsec = fs->lsndir; 543 1.1 thorpej else if (okclus(fs, clus)) 544 1.1 thorpej lsec = blklsn(fs, clus); 545 1.1 thorpej else { 546 1.1 thorpej err = EINVAL; 547 1.1 thorpej goto out; 548 1.1 thorpej } 549 1.1 thorpej for (sec = 0; sec < nsec; sec++) { 550 1.1 thorpej if ((err = ioget(fs->fd, lsec + sec, dir, 1))) 551 1.1 thorpej goto out; 552 1.1 thorpej for (ent = 0; ent < DEPSEC; ent++) { 553 1.1 thorpej if (!*dir[ent].de.deName) { 554 1.1 thorpej err = ENOENT; 555 1.1 thorpej goto out; 556 1.1 thorpej } 557 1.1 thorpej if (*dir[ent].de.deName != 0xe5) { 558 1.1 thorpej if (dir[ent].de.deAttributes == 559 1.1 thorpej ATTR_WIN95) { 560 1.1 thorpej x = dir[ent].xde.weCnt; 561 1.1 thorpej if (x & WIN_LAST || 562 1.1 thorpej (x + 1 == xdn && 563 1.1 thorpej dir[ent].xde.weChksum == 564 1.1 thorpej chk)) { 565 1.1 thorpej if (x & WIN_LAST) { 566 1.1 thorpej chk = dir[ent].xde.weChksum; 567 1.1 thorpej x &= WIN_CNT; 568 1.1 thorpej } 569 1.1 thorpej if (x >= 1 && x <= 20) { 570 1.1 thorpej cp_xdnm(lfn, &dir[ent].xde); 571 1.1 thorpej xdn = x; 572 1.1 thorpej continue; 573 1.1 thorpej } 574 1.1 thorpej } 575 1.1 thorpej } else if (!(dir[ent].de.deAttributes & 576 1.1 thorpej ATTR_VOLUME)) { 577 1.21 christos ok = xdn == 1; 578 1.21 christos if (ok) { 579 1.1 thorpej for (x = 0, i = 0; 580 1.1 thorpej i < 11; i++) 581 1.1 thorpej x = ((((x & 1) << 7) | (x >> 1)) + 582 1.22 christos (size_t)msdos_dirchar(&dir[ent].de,(size_t)i)) & 0xff; 583 1.1 thorpej ok = chk == x && 584 1.12 isaki !strcasecmp(name, (const char *)lfn); 585 1.1 thorpej } 586 1.1 thorpej if (!ok) { 587 1.1 thorpej cp_sfn(sfn, &dir[ent].de); 588 1.12 isaki ok = !strcasecmp(name, (const char *)sfn); 589 1.1 thorpej } 590 1.1 thorpej if (ok) { 591 1.1 thorpej *dep = &dir[ent].de; 592 1.6 scw goto out2; 593 1.1 thorpej } 594 1.1 thorpej } 595 1.1 thorpej } 596 1.1 thorpej xdn = 0; 597 1.1 thorpej } 598 1.1 thorpej } 599 1.1 thorpej if (!clus) 600 1.1 thorpej break; 601 1.1 thorpej if ((err = fatget(fs, &clus))) 602 1.1 thorpej goto out; 603 1.1 thorpej if (fatend(fs->fatsz, clus)) 604 1.1 thorpej break; 605 1.1 thorpej } 606 1.1 thorpej err = ENOENT; 607 1.1 thorpej out: 608 1.10 christos dealloc(dir, sizeof(DOS_DIR) * DEPSEC); 609 1.6 scw dir = NULL; 610 1.6 scw out2: 611 1.12 isaki return err; 612 1.1 thorpej } 613 1.1 thorpej 614 1.1 thorpej /* 615 1.1 thorpej * Copy name from extended directory entry 616 1.1 thorpej */ 617 1.1 thorpej static void 618 1.12 isaki cp_xdnm(u_char *lfn, struct winentry *xde) 619 1.1 thorpej { 620 1.1 thorpej static const struct { 621 1.1 thorpej u_int off; 622 1.1 thorpej u_int dim; 623 1.1 thorpej } ix[3] = { 624 1.1 thorpej { offsetof(struct winentry, wePart1), 625 1.1 thorpej sizeof(xde->wePart1) / 2 }, 626 1.1 thorpej { offsetof(struct winentry, wePart2), 627 1.1 thorpej sizeof(xde->wePart2) / 2 }, 628 1.1 thorpej { offsetof(struct winentry, wePart3), 629 1.1 thorpej sizeof(xde->wePart3) / 2 } 630 1.1 thorpej }; 631 1.1 thorpej u_char *p; 632 1.1 thorpej u_int n, x, c; 633 1.1 thorpej 634 1.1 thorpej lfn += 13 * ((xde->weCnt & WIN_CNT) - 1); 635 1.1 thorpej for (n = 0; n < 3; n++) 636 1.12 isaki for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x; 637 1.1 thorpej p += 2, x--) { 638 1.1 thorpej if ((c = getushort(p)) && (c < 32 || c > 127)) 639 1.1 thorpej c = '?'; 640 1.22 christos if (!(*lfn++ = (u_char)c)) 641 1.1 thorpej return; 642 1.1 thorpej } 643 1.1 thorpej if (xde->weCnt & WIN_LAST) 644 1.1 thorpej *lfn = 0; 645 1.1 thorpej } 646 1.1 thorpej 647 1.1 thorpej /* 648 1.1 thorpej * Copy short filename 649 1.1 thorpej */ 650 1.1 thorpej static void 651 1.12 isaki cp_sfn(u_char *sfn, struct direntry *de) 652 1.1 thorpej { 653 1.1 thorpej u_char *p; 654 1.1 thorpej int j, i; 655 1.1 thorpej 656 1.1 thorpej p = sfn; 657 1.1 thorpej if (*de->deName != ' ') { 658 1.1 thorpej for (j = 7; de->deName[j] == ' '; j--); 659 1.1 thorpej for (i = 0; i <= j; i++) 660 1.1 thorpej *p++ = de->deName[i]; 661 1.1 thorpej if (*de->deExtension != ' ') { 662 1.1 thorpej *p++ = '.'; 663 1.1 thorpej for (j = 2; de->deExtension[j] == ' '; j--); 664 1.1 thorpej for (i = 0; i <= j; i++) 665 1.1 thorpej *p++ = de->deExtension[i]; 666 1.1 thorpej } 667 1.1 thorpej } 668 1.1 thorpej *p = 0; 669 1.1 thorpej if (*sfn == 5) 670 1.1 thorpej *sfn = 0xe5; 671 1.1 thorpej } 672 1.1 thorpej 673 1.1 thorpej /* 674 1.1 thorpej * Return size of file in bytes 675 1.1 thorpej */ 676 1.1 thorpej static off_t 677 1.12 isaki fsize(DOS_FS *fs, struct direntry *de) 678 1.1 thorpej { 679 1.22 christos size_t size; 680 1.1 thorpej u_int c; 681 1.1 thorpej int n; 682 1.1 thorpej 683 1.1 thorpej if (!(size = getulong(de->deFileSize)) && 684 1.1 thorpej de->deAttributes & ATTR_DIRECTORY) { 685 1.12 isaki if (!(c = getushort(de->deStartCluster))) { 686 1.1 thorpej size = fs->dirents * sizeof(struct direntry); 687 1.12 isaki } else { 688 1.1 thorpej if ((n = fatcnt(fs, c)) == -1) 689 1.1 thorpej return n; 690 1.22 christos size = (size_t)blkbyt(fs, n); 691 1.1 thorpej } 692 1.1 thorpej } 693 1.22 christos return (off_t)size; 694 1.1 thorpej } 695 1.1 thorpej 696 1.1 thorpej /* 697 1.1 thorpej * Count number of clusters in chain 698 1.1 thorpej */ 699 1.1 thorpej static int 700 1.12 isaki fatcnt(DOS_FS *fs, u_int c) 701 1.1 thorpej { 702 1.1 thorpej int n; 703 1.1 thorpej 704 1.1 thorpej for (n = 0; okclus(fs, c); n++) 705 1.1 thorpej if (fatget(fs, &c)) 706 1.1 thorpej return -1; 707 1.1 thorpej return fatend(fs->fatsz, c) ? n : -1; 708 1.1 thorpej } 709 1.1 thorpej 710 1.1 thorpej /* 711 1.1 thorpej * Get next cluster in cluster chain 712 1.1 thorpej */ 713 1.1 thorpej static int 714 1.12 isaki fatget(DOS_FS *fs, u_int *c) 715 1.1 thorpej { 716 1.1 thorpej u_char buf[4]; 717 1.1 thorpej u_int x; 718 1.1 thorpej int err; 719 1.1 thorpej 720 1.1 thorpej err = ioread(fs, secbyt(fs->lsnfat) + fatoff(fs->fatsz, *c), buf, 721 1.1 thorpej fs->fatsz != 32 ? 2 : 4); 722 1.1 thorpej if (err) 723 1.1 thorpej return err; 724 1.1 thorpej x = fs->fatsz != 32 ? getushort(buf) : getulong(buf); 725 1.1 thorpej *c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x; 726 1.1 thorpej return 0; 727 1.1 thorpej } 728 1.1 thorpej 729 1.1 thorpej /* 730 1.1 thorpej * Is cluster an end-of-chain marker? 731 1.1 thorpej */ 732 1.1 thorpej static int 733 1.1 thorpej fatend(u_int sz, u_int c) 734 1.1 thorpej { 735 1.1 thorpej return c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7); 736 1.1 thorpej } 737 1.1 thorpej 738 1.1 thorpej /* 739 1.1 thorpej * Offset-based I/O primitive 740 1.1 thorpej */ 741 1.1 thorpej static int 742 1.23 martin ioread(DOS_FS *fs, BYTE_OFF_T offset, void *buf, u_int nbyte) 743 1.1 thorpej { 744 1.1 thorpej char *s; 745 1.1 thorpej u_int off, n; 746 1.1 thorpej int err; 747 1.1 thorpej 748 1.1 thorpej s = buf; 749 1.1 thorpej if ((off = offset & (SECSIZ - 1))) { 750 1.1 thorpej offset -= off; 751 1.1 thorpej if ((err = iobuf(fs, bytsec(offset)))) 752 1.1 thorpej return err; 753 1.1 thorpej offset += SECSIZ; 754 1.1 thorpej if ((n = SECSIZ - off) > nbyte) 755 1.1 thorpej n = nbyte; 756 1.1 thorpej memcpy(s, fs->buf + off, n); 757 1.1 thorpej s += n; 758 1.1 thorpej nbyte -= n; 759 1.1 thorpej } 760 1.1 thorpej n = nbyte & (SECSIZ - 1); 761 1.1 thorpej if (nbyte -= n) { 762 1.1 thorpej if ((err = ioget(fs->fd, bytsec(offset), s, bytsec(nbyte)))) 763 1.1 thorpej return err; 764 1.1 thorpej offset += nbyte; 765 1.1 thorpej s += nbyte; 766 1.1 thorpej } 767 1.1 thorpej if (n) { 768 1.1 thorpej if ((err = iobuf(fs, bytsec(offset)))) 769 1.1 thorpej return err; 770 1.1 thorpej memcpy(s, fs->buf, n); 771 1.1 thorpej } 772 1.1 thorpej return 0; 773 1.1 thorpej } 774 1.1 thorpej 775 1.1 thorpej /* 776 1.1 thorpej * Buffered sector-based I/O primitive 777 1.1 thorpej */ 778 1.1 thorpej static int 779 1.12 isaki iobuf(DOS_FS *fs, u_int lsec) 780 1.1 thorpej { 781 1.1 thorpej int err; 782 1.1 thorpej 783 1.1 thorpej if (fs->bufsec != lsec) { 784 1.1 thorpej if ((err = ioget(fs->fd, lsec, fs->buf, 1))) 785 1.1 thorpej return err; 786 1.1 thorpej fs->bufsec = lsec; 787 1.1 thorpej } 788 1.1 thorpej return 0; 789 1.1 thorpej } 790 1.1 thorpej 791 1.1 thorpej /* 792 1.1 thorpej * Sector-based I/O primitive 793 1.1 thorpej */ 794 1.1 thorpej static int 795 1.12 isaki ioget(struct open_file *fd, u_int lsec, void *buf, u_int nsec) 796 1.1 thorpej { 797 1.1 thorpej size_t rsize; 798 1.1 thorpej int err; 799 1.1 thorpej 800 1.1 thorpej #ifndef LIBSA_NO_TWIDDLE 801 1.1 thorpej twiddle(); 802 1.1 thorpej #endif 803 1.1 thorpej err = DEV_STRATEGY(fd->f_dev)(fd->f_devdata, F_READ, lsec, 804 1.1 thorpej secbyt(nsec), buf, &rsize); 805 1.12 isaki return err; 806 1.1 thorpej } 807