1 #include <dprintf.h> 2 #include <stdio.h> 3 #include <ctype.h> 4 #include <string.h> 5 #include <sys/dirent.h> 6 #include <cache.h> 7 #include <core.h> 8 #include <disk.h> 9 #include <fs.h> 10 #include <ilog2.h> 11 #include <klibc/compiler.h> 12 #include "codepage.h" 13 #include "fat_fs.h" 14 15 static struct inode * new_fat_inode(struct fs_info *fs) 16 { 17 struct inode *inode = alloc_inode(fs, 0, sizeof(struct fat_pvt_inode)); 18 if (!inode) 19 malloc_error("inode structure"); 20 21 return inode; 22 } 23 24 /* 25 * Check for a particular sector in the FAT cache 26 */ 27 static const void *get_fat_sector(struct fs_info *fs, sector_t sector) 28 { 29 return get_cache(fs->fs_dev, FAT_SB(fs)->fat + sector); 30 } 31 32 static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num) 33 { 34 uint32_t next_cluster = 0; 35 sector_t fat_sector; 36 uint32_t offset; 37 uint32_t sector_mask = SECTOR_SIZE(fs) - 1; 38 const uint8_t *data; 39 40 switch(FAT_SB(fs)->fat_type) { 41 case FAT12: 42 offset = clust_num + (clust_num >> 1); 43 fat_sector = offset >> SECTOR_SHIFT(fs); 44 offset &= sector_mask; 45 data = get_fat_sector(fs, fat_sector); 46 if (offset == sector_mask) { 47 /* 48 * we got the end of the one fat sector, 49 * but we have just one byte and we need two, 50 * so store the low part, then read the next fat 51 * sector, read the high part, then combine it. 52 */ 53 next_cluster = data[offset]; 54 data = get_fat_sector(fs, fat_sector + 1); 55 next_cluster += data[0] << 8; 56 } else { 57 next_cluster = *(const uint16_t *)(data + offset); 58 } 59 60 if (clust_num & 0x0001) 61 next_cluster >>= 4; /* cluster number is ODD */ 62 else 63 next_cluster &= 0x0fff; /* cluster number is EVEN */ 64 break; 65 66 case FAT16: 67 offset = clust_num << 1; 68 fat_sector = offset >> SECTOR_SHIFT(fs); 69 offset &= sector_mask; 70 data = get_fat_sector(fs, fat_sector); 71 next_cluster = *(const uint16_t *)(data + offset); 72 break; 73 74 case FAT32: 75 offset = clust_num << 2; 76 fat_sector = offset >> SECTOR_SHIFT(fs); 77 offset &= sector_mask; 78 data = get_fat_sector(fs, fat_sector); 79 next_cluster = *(const uint32_t *)(data + offset); 80 next_cluster &= 0x0fffffff; 81 break; 82 } 83 84 return next_cluster; 85 } 86 87 static int fat_next_extent(struct inode *inode, uint32_t lstart) 88 { 89 struct fs_info *fs = inode->fs; 90 struct fat_sb_info *sbi = FAT_SB(fs); 91 uint32_t mcluster = lstart >> sbi->clust_shift; 92 uint32_t lcluster; 93 uint32_t pcluster; 94 uint32_t tcluster; 95 uint32_t xcluster; 96 const uint32_t cluster_bytes = UINT32_C(1) << sbi->clust_byte_shift; 97 const uint32_t cluster_secs = UINT32_C(1) << sbi->clust_shift; 98 sector_t data_area = sbi->data; 99 100 tcluster = (inode->size + cluster_bytes - 1) >> sbi->clust_byte_shift; 101 if (mcluster >= tcluster) 102 goto err; /* Requested cluster beyond end of file */ 103 104 lcluster = PVT(inode)->offset >> sbi->clust_shift; 105 pcluster = ((PVT(inode)->here - data_area) >> sbi->clust_shift) + 2; 106 107 if (lcluster > mcluster || PVT(inode)->here < data_area) { 108 lcluster = 0; 109 pcluster = PVT(inode)->start_cluster; 110 } 111 112 for (;;) { 113 if (pcluster-2 >= sbi->clusters) { 114 inode->size = lcluster << sbi->clust_shift; 115 goto err; 116 } 117 118 if (lcluster >= mcluster) 119 break; 120 121 lcluster++; 122 pcluster = get_next_cluster(fs, pcluster); 123 } 124 125 inode->next_extent.pstart = 126 ((sector_t)(pcluster-2) << sbi->clust_shift) + data_area; 127 inode->next_extent.len = cluster_secs; 128 xcluster = 0; /* Nonsense */ 129 130 while (++lcluster < tcluster) { 131 xcluster = get_next_cluster(fs, pcluster); 132 if (xcluster != ++pcluster) 133 break; /* Not contiguous */ 134 inode->next_extent.len += cluster_secs; 135 } 136 137 /* Note: ->here is bogus if ->offset >= EOF, but that's okay */ 138 PVT(inode)->offset = lcluster << sbi->clust_shift; 139 PVT(inode)->here = ((xcluster-2) << sbi->clust_shift) + data_area; 140 141 return 0; 142 143 err: 144 dprintf("fat_next_extent: return error\n"); 145 return -1; 146 } 147 148 static sector_t get_next_sector(struct fs_info* fs, uint32_t sector) 149 { 150 struct fat_sb_info *sbi = FAT_SB(fs); 151 sector_t data_area = sbi->data; 152 sector_t data_sector; 153 uint32_t cluster; 154 int clust_shift = sbi->clust_shift; 155 156 if (sector < data_area) { 157 /* Root directory sector... */ 158 sector++; 159 if (sector >= data_area) 160 sector = 0; /* Ran out of root directory, return EOF */ 161 return sector; 162 } 163 164 data_sector = sector - data_area; 165 if ((data_sector + 1) & sbi->clust_mask) /* Still in the same cluster */ 166 return sector + 1; /* Next sector inside cluster */ 167 168 /* get a new cluster */ 169 cluster = data_sector >> clust_shift; 170 cluster = get_next_cluster(fs, cluster + 2) - 2; 171 172 if (cluster >= sbi->clusters) 173 return 0; 174 175 /* return the start of the new cluster */ 176 sector = (cluster << clust_shift) + data_area; 177 return sector; 178 } 179 180 /* 181 * The FAT is a single-linked list. We remember the last place we 182 * were, so for a forward seek we can move forward from there, but 183 * for a reverse seek we have to start over... 184 */ 185 static sector_t get_the_right_sector(struct file *file) 186 { 187 struct inode *inode = file->inode; 188 uint32_t sector_pos = file->offset >> SECTOR_SHIFT(file->fs); 189 uint32_t where; 190 sector_t sector; 191 192 if (sector_pos < PVT(inode)->offset) { 193 /* Reverse seek */ 194 where = 0; 195 sector = PVT(inode)->start; 196 } else { 197 where = PVT(inode)->offset; 198 sector = PVT(inode)->here; 199 } 200 201 while (where < sector_pos) { 202 sector = get_next_sector(file->fs, sector); 203 where++; 204 } 205 206 PVT(inode)->offset = sector_pos; 207 PVT(inode)->here = sector; 208 209 return sector; 210 } 211 212 /* 213 * Get the next sector in sequence 214 */ 215 static sector_t next_sector(struct file *file) 216 { 217 struct inode *inode = file->inode; 218 sector_t sector = get_next_sector(file->fs, PVT(inode)->here); 219 PVT(inode)->offset++; 220 PVT(inode)->here = sector; 221 222 return sector; 223 } 224 225 /** 226 * mangle_name: 227 * 228 * Mangle a filename pointed to by src into a buffer pointed 229 * to by dst; ends on encountering any whitespace. 230 * dst is preserved. 231 * 232 * This verifies that a filename is < FILENAME_MAX characters, 233 * doesn't contain whitespace, zero-pads the output buffer, 234 * and removes redundant slashes. 235 * 236 * Unlike the generic version, this also converts backslashes to 237 * forward slashes. 238 * 239 */ 240 static void vfat_mangle_name(char *dst, const char *src) 241 { 242 char *p = dst; 243 int i = FILENAME_MAX-1; 244 char c; 245 246 while (not_whitespace(c = *src)) { 247 if (c == '\\') 248 c = '/'; 249 250 if (c == '/') { 251 if (src[1] == '/' || src[1] == '\\') { 252 src++; 253 i--; 254 continue; 255 } 256 } 257 i--; 258 *dst++ = *src++; 259 } 260 261 while (1) { 262 if (dst == p) 263 break; 264 if (dst[-1] != '/') 265 break; 266 if ((dst[-1] == '/') && ((dst - 1) == p)) 267 break; 268 269 dst--; 270 i++; 271 } 272 273 i++; 274 for (; i > 0; i --) 275 *dst++ = '\0'; 276 } 277 278 /* 279 * Mangle a normal style string to DOS style string. 280 */ 281 static void mangle_dos_name(char *mangle_buf, const char *src) 282 { 283 int i; 284 unsigned char c; 285 286 if (src[0] == '.' && (!src[1] || (src[1] == '.' && !src[2]))) { 287 /* . and .. mangle to their respective zero-padded version */ 288 i = stpcpy(mangle_buf, src) - mangle_buf; 289 } else { 290 i = 0; 291 while (i < 11) { 292 c = *src++; 293 294 if ((c <= ' ') || (c == '/')) 295 break; 296 297 if (c == '.') { 298 while (i < 8) 299 mangle_buf[i++] = ' '; 300 i = 8; 301 continue; 302 } 303 304 c = codepage.upper[c]; 305 if (i == 0 && c == 0xe5) 306 c = 0x05; /* Special hack for the first byte only! */ 307 308 mangle_buf[i++] = c; 309 } 310 } 311 312 while (i < 11) 313 mangle_buf[i++] = ' '; 314 315 mangle_buf[i] = '\0'; 316 } 317 318 /* 319 * Match a string name against a longname. "len" is the number of 320 * codepoints in the input; including padding. 321 * 322 * Returns true on match. 323 */ 324 static bool vfat_match_longname(const char *str, const uint16_t *match, 325 int len) 326 { 327 unsigned char c = -1; /* Nonzero: we have not yet seen NUL */ 328 uint16_t cp; 329 330 dprintf("Matching: %s len %d\n", str, len); 331 332 while (len) { 333 cp = *match++; 334 len--; 335 if (!cp) 336 break; 337 c = *str++; 338 if (cp != codepage.uni[0][c] && cp != codepage.uni[1][c]) 339 return false; /* Also handles c == '\0' */ 340 } 341 342 /* This should have been the end of the matching string */ 343 if (*str) 344 return false; 345 346 /* Any padding entries must be FFFF */ 347 while (len--) 348 if (*match++ != 0xffff) 349 return false; 350 351 return true; 352 } 353 354 /* 355 * Convert an UTF-16 longname to the system codepage; return 356 * the length on success or -1 on failure. 357 */ 358 static int vfat_cvt_longname(char *entry_name, const uint16_t *long_name) 359 { 360 struct unicache { 361 uint16_t utf16; 362 uint8_t cp; 363 }; 364 static struct unicache unicache[256]; 365 struct unicache *uc; 366 uint16_t cp; 367 unsigned int c; 368 char *p = entry_name; 369 370 do { 371 cp = *long_name++; 372 uc = &unicache[cp % 256]; 373 374 if (__likely(uc->utf16 == cp)) { 375 *p++ = uc->cp; 376 } else { 377 for (c = 0; c < 512; c++) { 378 /* This is a bit hacky... */ 379 if (codepage.uni[0][c] == cp) { 380 uc->utf16 = cp; 381 *p++ = uc->cp = (uint8_t)c; 382 goto found; 383 } 384 } 385 return -1; /* Impossible character */ 386 found: 387 ; 388 } 389 } while (cp); 390 391 return (p-entry_name)-1; 392 } 393 394 static void copy_long_chunk(uint16_t *buf, const struct fat_dir_entry *de) 395 { 396 const struct fat_long_name_entry *le = 397 (const struct fat_long_name_entry *)de; 398 399 memcpy(buf, le->name1, 5 * 2); 400 memcpy(buf + 5, le->name2, 6 * 2); 401 memcpy(buf + 11, le->name3, 2 * 2); 402 } 403 404 static uint8_t get_checksum(const char *dir_name) 405 { 406 int i; 407 uint8_t sum = 0; 408 409 for (i = 11; i; i--) 410 sum = ((sum & 1) << 7) + (sum >> 1) + (uint8_t)*dir_name++; 411 return sum; 412 } 413 414 415 /* compute the first sector number of one dir where the data stores */ 416 static inline sector_t first_sector(struct fs_info *fs, 417 const struct fat_dir_entry *dir) 418 { 419 const struct fat_sb_info *sbi = FAT_SB(fs); 420 sector_t first_clust; 421 sector_t sector; 422 423 first_clust = (dir->first_cluster_high << 16) + dir->first_cluster_low; 424 if (first_clust == 0) 425 sector = sbi->root; /* first_clust == 0 means root directory */ 426 else 427 sector = ((first_clust - 2) << sbi->clust_shift) + sbi->data; 428 429 return sector; 430 } 431 432 static inline enum dirent_type get_inode_mode(uint8_t attr) 433 { 434 return (attr & FAT_ATTR_DIRECTORY) ? DT_DIR : DT_REG; 435 } 436 437 438 static struct inode *vfat_find_entry(const char *dname, struct inode *dir) 439 { 440 struct fs_info *fs = dir->fs; 441 struct inode *inode; 442 const struct fat_dir_entry *de; 443 struct fat_long_name_entry *long_de; 444 445 char mangled_name[12]; 446 uint16_t long_name[260]; /* == 20*13 */ 447 int long_len; 448 449 sector_t dir_sector = PVT(dir)->start; 450 uint8_t vfat_init, vfat_next, vfat_csum = 0; 451 uint8_t id; 452 int slots; 453 int entries; 454 int checksum; 455 int long_match = 0; 456 457 slots = (strlen(dname) + 12) / 13; 458 if (slots > 20) 459 return NULL; /* Name too long */ 460 461 slots |= 0x40; 462 vfat_init = vfat_next = slots; 463 long_len = slots*13; 464 465 /* Produce the shortname version, in case we need it. */ 466 mangle_dos_name(mangled_name, dname); 467 468 while (dir_sector) { 469 de = get_cache(fs->fs_dev, dir_sector); 470 entries = 1 << (fs->sector_shift - 5); 471 472 while (entries--) { 473 if (de->name[0] == 0) 474 return NULL; 475 476 if (de->attr == 0x0f) { 477 /* 478 * It's a long name entry. 479 */ 480 long_de = (struct fat_long_name_entry *)de; 481 id = long_de->id; 482 if (id != vfat_next) 483 goto not_match; 484 485 if (id & 0x40) { 486 /* get the initial checksum value */ 487 vfat_csum = long_de->checksum; 488 id &= 0x3f; 489 long_len = id * 13; 490 491 /* ZERO the long_name buffer */ 492 memset(long_name, 0, sizeof long_name); 493 } else { 494 if (long_de->checksum != vfat_csum) 495 goto not_match; 496 } 497 498 vfat_next = --id; 499 500 /* got the long entry name */ 501 copy_long_chunk(long_name + id*13, de); 502 503 /* 504 * If we got the last entry, check it. 505 * Or, go on with the next entry. 506 */ 507 if (id == 0) { 508 if (!vfat_match_longname(dname, long_name, long_len)) 509 goto not_match; 510 long_match = 1; 511 } 512 de++; 513 continue; /* Try the next entry */ 514 } else { 515 /* 516 * It's a short entry 517 */ 518 if (de->attr & 0x08) /* ignore volume labels */ 519 goto not_match; 520 521 if (long_match) { 522 /* 523 * We already have a VFAT long name match. However, the 524 * match is only valid if the checksum matches. 525 */ 526 checksum = get_checksum(de->name); 527 if (checksum == vfat_csum) 528 goto found; /* Got it */ 529 } else { 530 if (!memcmp(mangled_name, de->name, 11)) 531 goto found; 532 } 533 } 534 535 not_match: 536 vfat_next = vfat_init; 537 long_match = 0; 538 539 de++; 540 } 541 542 /* Try with the next sector */ 543 dir_sector = get_next_sector(fs, dir_sector); 544 } 545 return NULL; /* Nothing found... */ 546 547 found: 548 inode = new_fat_inode(fs); 549 inode->size = de->file_size; 550 PVT(inode)->start_cluster = 551 (de->first_cluster_high << 16) + de->first_cluster_low; 552 if (PVT(inode)->start_cluster == 0) { 553 /* Root directory */ 554 int root_size = FAT_SB(fs)->root_size; 555 556 PVT(inode)->start_cluster = FAT_SB(fs)->root_cluster; 557 inode->size = root_size ? root_size << fs->sector_shift : ~0; 558 PVT(inode)->start = PVT(inode)->here = FAT_SB(fs)->root; 559 } else { 560 PVT(inode)->start = PVT(inode)->here = first_sector(fs, de); 561 } 562 inode->mode = get_inode_mode(de->attr); 563 564 return inode; 565 } 566 567 static struct inode *vfat_iget_root(struct fs_info *fs) 568 { 569 struct inode *inode = new_fat_inode(fs); 570 int root_size = FAT_SB(fs)->root_size; 571 572 /* 573 * For FAT32, the only way to get the root directory size is to 574 * follow the entire FAT chain to the end... which seems pointless. 575 */ 576 PVT(inode)->start_cluster = FAT_SB(fs)->root_cluster; 577 inode->size = root_size ? root_size << fs->sector_shift : ~0; 578 PVT(inode)->start = PVT(inode)->here = FAT_SB(fs)->root; 579 inode->mode = DT_DIR; 580 581 return inode; 582 } 583 584 static struct inode *vfat_iget(const char *dname, struct inode *parent) 585 { 586 return vfat_find_entry(dname, parent); 587 } 588 589 static int vfat_readdir(struct file *file, struct dirent *dirent) 590 { 591 struct fs_info *fs = file->fs; 592 const struct fat_dir_entry *de; 593 const char *data; 594 const struct fat_long_name_entry *long_de; 595 596 sector_t sector = get_the_right_sector(file); 597 598 uint16_t long_name[261]; /* == 20*13 + 1 (to guarantee null) */ 599 char filename[261]; 600 int name_len = 0; 601 602 uint8_t vfat_next, vfat_csum; 603 uint8_t id; 604 int entries_left; 605 bool long_entry = false; 606 int sec_off = file->offset & ((1 << fs->sector_shift) - 1); 607 608 data = get_cache(fs->fs_dev, sector); 609 de = (const struct fat_dir_entry *)(data + sec_off); 610 entries_left = ((1 << fs->sector_shift) - sec_off) >> 5; 611 612 vfat_next = vfat_csum = 0xff; 613 614 while (1) { 615 while (entries_left--) { 616 if (de->name[0] == 0) 617 return -1; /* End of directory */ 618 if ((uint8_t)de->name[0] == 0xe5) 619 goto invalid; 620 621 if (de->attr == 0x0f) { 622 /* 623 * It's a long name entry. 624 */ 625 long_de = (struct fat_long_name_entry *)de; 626 id = long_de->id; 627 628 if (id & 0x40) { 629 /* init vfat_csum */ 630 vfat_csum = long_de->checksum; 631 id &= 0x3f; 632 if (id >= 20) 633 goto invalid; /* Too long! */ 634 635 /* ZERO the long_name buffer */ 636 memset(long_name, 0, sizeof long_name); 637 } else { 638 if (long_de->checksum != vfat_csum || id != vfat_next) 639 goto invalid; 640 } 641 642 vfat_next = --id; 643 644 /* got the long entry name */ 645 copy_long_chunk(long_name + id*13, de); 646 647 if (id == 0) { 648 name_len = vfat_cvt_longname(filename, long_name); 649 if (name_len > 0 && name_len < sizeof(dirent->d_name)) 650 long_entry = true; 651 } 652 653 goto next; 654 } else { 655 /* 656 * It's a short entry 657 */ 658 if (de->attr & 0x08) /* ignore volume labels */ 659 goto invalid; 660 661 if (long_entry && get_checksum(de->name) == vfat_csum) { 662 /* Got a long entry */ 663 } else { 664 /* Use the shortname */ 665 int i; 666 uint8_t c; 667 char *p = filename; 668 669 for (i = 0; i < 8; i++) { 670 c = de->name[i]; 671 if (c == ' ') 672 break; 673 if (de->lcase & LCASE_BASE) 674 c = codepage.lower[c]; 675 *p++ = c; 676 } 677 if (de->name[8] != ' ') { 678 *p++ = '.'; 679 for (i = 8; i < 11; i++) { 680 c = de->name[i]; 681 if (c == ' ') 682 break; 683 if (de->lcase & LCASE_EXT) 684 c = codepage.lower[c]; 685 *p++ = c; 686 } 687 } 688 *p = '\0'; 689 name_len = p - filename; 690 } 691 goto got; /* Got something one way or the other */ 692 } 693 694 invalid: 695 long_entry = false; 696 next: 697 de++; 698 file->offset += sizeof(struct fat_dir_entry); 699 } 700 701 /* Try with the next sector */ 702 sector = next_sector(file); 703 if (!sector) 704 return -1; 705 de = get_cache(fs->fs_dev, sector); 706 entries_left = 1 << (fs->sector_shift - 5); 707 } 708 709 got: 710 name_len++; /* Include final null */ 711 dirent->d_ino = de->first_cluster_low | (de->first_cluster_high << 16); 712 dirent->d_off = file->offset; 713 dirent->d_reclen = offsetof(struct dirent, d_name) + name_len; 714 dirent->d_type = get_inode_mode(de->attr); 715 memcpy(dirent->d_name, filename, name_len); 716 717 file->offset += sizeof(*de); /* Update for next reading */ 718 719 return 0; 720 } 721 722 /* init. the fs meta data, return the block size in bits */ 723 static int vfat_fs_init(struct fs_info *fs) 724 { 725 struct fat_bpb fat; 726 struct fat_sb_info *sbi; 727 struct disk *disk = fs->fs_dev->disk; 728 int sectors_per_fat; 729 uint32_t clusters; 730 sector_t total_sectors; 731 732 fs->sector_shift = fs->block_shift = disk->sector_shift; 733 fs->sector_size = 1 << fs->sector_shift; 734 fs->block_size = 1 << fs->block_shift; 735 736 disk->rdwr_sectors(disk, &fat, 0, 1, 0); 737 738 /* XXX: Find better sanity checks... */ 739 if (!fat.bxResSectors || !fat.bxFATs) 740 return -1; 741 sbi = malloc(sizeof(*sbi)); 742 if (!sbi) 743 malloc_error("fat_sb_info structure"); 744 fs->fs_info = sbi; 745 746 sectors_per_fat = fat.bxFATsecs ? : fat.fat32.bxFATsecs_32; 747 total_sectors = fat.bxSectors ? : fat.bsHugeSectors; 748 749 sbi->fat = fat.bxResSectors; 750 sbi->root = sbi->fat + sectors_per_fat * fat.bxFATs; 751 sbi->root_size = root_dir_size(fs, &fat); 752 sbi->data = sbi->root + sbi->root_size; 753 754 sbi->clust_shift = ilog2(fat.bxSecPerClust); 755 sbi->clust_byte_shift = sbi->clust_shift + fs->sector_shift; 756 sbi->clust_mask = fat.bxSecPerClust - 1; 757 sbi->clust_size = fat.bxSecPerClust << fs->sector_shift; 758 759 clusters = (total_sectors - sbi->data) >> sbi->clust_shift; 760 if (clusters <= 0xff4) { 761 sbi->fat_type = FAT12; 762 } else if (clusters <= 0xfff4) { 763 sbi->fat_type = FAT16; 764 } else { 765 sbi->fat_type = FAT32; 766 767 if (clusters > 0x0ffffff4) 768 clusters = 0x0ffffff4; /* Maximum possible */ 769 770 if (fat.fat32.extended_flags & 0x80) { 771 /* Non-mirrored FATs, we need to read the active one */ 772 sbi->fat += (fat.fat32.extended_flags & 0x0f) * sectors_per_fat; 773 } 774 775 /* FAT32: root directory is a cluster chain */ 776 sbi->root = sbi->data 777 + ((fat.fat32.root_cluster-2) << sbi->clust_shift); 778 } 779 sbi->clusters = clusters; 780 781 /* fs UUID - serial number */ 782 if (FAT32 == sbi->fat_type) 783 sbi->uuid = fat.fat32.num_serial; 784 else 785 sbi->uuid = fat.fat12_16.num_serial; 786 787 /* Initialize the cache */ 788 cache_init(fs->fs_dev, fs->block_shift); 789 790 return fs->block_shift; 791 } 792 793 static int vfat_copy_superblock(void *buf) 794 { 795 struct fat_bpb fat; 796 struct disk *disk; 797 size_t sb_off; 798 void *dst; 799 int sb_len; 800 801 disk = this_fs->fs_dev->disk; 802 disk->rdwr_sectors(disk, &fat, 0, 1, 0); 803 804 /* XXX: Find better sanity checks... */ 805 if (!fat.bxResSectors || !fat.bxFATs) 806 return -1; 807 808 sb_off = offsetof(struct fat_bpb, sector_size); 809 sb_len = offsetof(struct fat_bpb, fat12_16) - sb_off \ 810 + sizeof(fat.fat12_16); 811 812 /* 813 * Only copy fields of the superblock we actually care about. 814 */ 815 dst = buf + sb_off; 816 memcpy(dst, (void *)&fat + sb_off, sb_len); 817 818 return 0; 819 } 820 821 #define FAT_UUID_LEN (4 + 1 + 4 + 1) 822 static char *vfat_fs_uuid(struct fs_info *fs) 823 { 824 char *uuid = NULL; 825 char *ptr; 826 827 uuid = malloc(FAT_UUID_LEN); 828 if (!uuid) 829 return NULL; 830 831 if (snprintf(uuid, FAT_UUID_LEN, "%04x-%04x", 832 (uint16_t)(FAT_SB(fs)->uuid >> 16), 833 (uint16_t)FAT_SB(fs)->uuid) < 0) { 834 free(uuid); 835 return NULL; 836 } 837 838 for (ptr = uuid; ptr && *ptr; ptr++) 839 *ptr = toupper(*ptr); 840 841 return uuid; 842 } 843 844 const struct fs_ops vfat_fs_ops = { 845 .fs_name = "vfat", 846 .fs_flags = FS_USEMEM | FS_THISIND, 847 .fs_init = vfat_fs_init, 848 .searchdir = NULL, 849 .getfssec = generic_getfssec, 850 .close_file = generic_close_file, 851 .mangle_name = vfat_mangle_name, 852 .chdir_start = generic_chdir_start, 853 .open_config = generic_open_config, 854 .readdir = vfat_readdir, 855 .iget_root = vfat_iget_root, 856 .iget = vfat_iget, 857 .next_extent = fat_next_extent, 858 .copy_super = vfat_copy_superblock, 859 .fs_uuid = vfat_fs_uuid, 860 }; 861