1 /* 2 * fat_write.c 3 * 4 * R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim 5 * 6 * SPDX-License-Identifier: GPL-2.0+ 7 */ 8 9 #include <common.h> 10 #include <command.h> 11 #include <config.h> 12 #include <fat.h> 13 #include <asm/byteorder.h> 14 #include <part.h> 15 #include <linux/ctype.h> 16 #include <div64.h> 17 #include <linux/math64.h> 18 #include "fat.c" 19 20 static void uppercase(char *str, int len) 21 { 22 int i; 23 24 for (i = 0; i < len; i++) { 25 *str = toupper(*str); 26 str++; 27 } 28 } 29 30 static int total_sector; 31 static int disk_write(__u32 block, __u32 nr_blocks, void *buf) 32 { 33 ulong ret; 34 35 if (!cur_dev) 36 return -1; 37 38 if (cur_part_info.start + block + nr_blocks > 39 cur_part_info.start + total_sector) { 40 printf("error: overflow occurs\n"); 41 return -1; 42 } 43 44 ret = blk_dwrite(cur_dev, cur_part_info.start + block, nr_blocks, buf); 45 if (nr_blocks && ret == 0) 46 return -1; 47 48 return ret; 49 } 50 51 /* 52 * Set short name in directory entry 53 */ 54 static void set_name(dir_entry *dirent, const char *filename) 55 { 56 char s_name[VFAT_MAXLEN_BYTES]; 57 char *period; 58 int period_location, len, i, ext_num; 59 60 if (filename == NULL) 61 return; 62 63 len = strlen(filename); 64 if (len == 0) 65 return; 66 67 strcpy(s_name, filename); 68 uppercase(s_name, len); 69 70 period = strchr(s_name, '.'); 71 if (period == NULL) { 72 period_location = len; 73 ext_num = 0; 74 } else { 75 period_location = period - s_name; 76 ext_num = len - period_location - 1; 77 } 78 79 /* Pad spaces when the length of file name is shorter than eight */ 80 if (period_location < 8) { 81 memcpy(dirent->name, s_name, period_location); 82 for (i = period_location; i < 8; i++) 83 dirent->name[i] = ' '; 84 } else if (period_location == 8) { 85 memcpy(dirent->name, s_name, period_location); 86 } else { 87 memcpy(dirent->name, s_name, 6); 88 dirent->name[6] = '~'; 89 dirent->name[7] = '1'; 90 } 91 92 if (ext_num < 3) { 93 memcpy(dirent->ext, s_name + period_location + 1, ext_num); 94 for (i = ext_num; i < 3; i++) 95 dirent->ext[i] = ' '; 96 } else 97 memcpy(dirent->ext, s_name + period_location + 1, 3); 98 99 debug("name : %s\n", dirent->name); 100 debug("ext : %s\n", dirent->ext); 101 } 102 103 static __u8 num_of_fats; 104 /* 105 * Write fat buffer into block device 106 */ 107 static int flush_dirty_fat_buffer(fsdata *mydata) 108 { 109 int getsize = FATBUFBLOCKS; 110 __u32 fatlength = mydata->fatlength; 111 __u8 *bufptr = mydata->fatbuf; 112 __u32 startblock = mydata->fatbufnum * FATBUFBLOCKS; 113 114 debug("debug: evicting %d, dirty: %d\n", mydata->fatbufnum, 115 (int)mydata->fat_dirty); 116 117 if ((!mydata->fat_dirty) || (mydata->fatbufnum == -1)) 118 return 0; 119 120 startblock += mydata->fat_sect; 121 122 if (getsize > fatlength) 123 getsize = fatlength; 124 125 /* Write FAT buf */ 126 if (disk_write(startblock, getsize, bufptr) < 0) { 127 debug("error: writing FAT blocks\n"); 128 return -1; 129 } 130 131 if (num_of_fats == 2) { 132 /* Update corresponding second FAT blocks */ 133 startblock += mydata->fatlength; 134 if (disk_write(startblock, getsize, bufptr) < 0) { 135 debug("error: writing second FAT blocks\n"); 136 return -1; 137 } 138 } 139 mydata->fat_dirty = 0; 140 141 return 0; 142 } 143 144 /* 145 * Get the entry at index 'entry' in a FAT (12/16/32) table. 146 * On failure 0x00 is returned. 147 * When bufnum is changed, write back the previous fatbuf to the disk. 148 */ 149 static __u32 get_fatent_value(fsdata *mydata, __u32 entry) 150 { 151 __u32 bufnum; 152 __u32 off16, offset; 153 __u32 ret = 0x00; 154 __u16 val1, val2; 155 156 if (CHECK_CLUST(entry, mydata->fatsize)) { 157 printf("Error: Invalid FAT entry: 0x%08x\n", entry); 158 return ret; 159 } 160 161 switch (mydata->fatsize) { 162 case 32: 163 bufnum = entry / FAT32BUFSIZE; 164 offset = entry - bufnum * FAT32BUFSIZE; 165 break; 166 case 16: 167 bufnum = entry / FAT16BUFSIZE; 168 offset = entry - bufnum * FAT16BUFSIZE; 169 break; 170 case 12: 171 bufnum = entry / FAT12BUFSIZE; 172 offset = entry - bufnum * FAT12BUFSIZE; 173 break; 174 175 default: 176 /* Unsupported FAT size */ 177 return ret; 178 } 179 180 debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n", 181 mydata->fatsize, entry, entry, offset, offset); 182 183 /* Read a new block of FAT entries into the cache. */ 184 if (bufnum != mydata->fatbufnum) { 185 int getsize = FATBUFBLOCKS; 186 __u8 *bufptr = mydata->fatbuf; 187 __u32 fatlength = mydata->fatlength; 188 __u32 startblock = bufnum * FATBUFBLOCKS; 189 190 if (getsize > fatlength) 191 getsize = fatlength; 192 193 startblock += mydata->fat_sect; /* Offset from start of disk */ 194 195 /* Write back the fatbuf to the disk */ 196 if (flush_dirty_fat_buffer(mydata) < 0) 197 return -1; 198 199 if (disk_read(startblock, getsize, bufptr) < 0) { 200 debug("Error reading FAT blocks\n"); 201 return ret; 202 } 203 mydata->fatbufnum = bufnum; 204 } 205 206 /* Get the actual entry from the table */ 207 switch (mydata->fatsize) { 208 case 32: 209 ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]); 210 break; 211 case 16: 212 ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]); 213 break; 214 case 12: 215 off16 = (offset * 3) / 4; 216 217 switch (offset & 0x3) { 218 case 0: 219 ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]); 220 ret &= 0xfff; 221 break; 222 case 1: 223 val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); 224 val1 &= 0xf000; 225 val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); 226 val2 &= 0x00ff; 227 ret = (val2 << 4) | (val1 >> 12); 228 break; 229 case 2: 230 val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); 231 val1 &= 0xff00; 232 val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); 233 val2 &= 0x000f; 234 ret = (val2 << 8) | (val1 >> 8); 235 break; 236 case 3: 237 ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); 238 ret = (ret & 0xfff0) >> 4; 239 break; 240 default: 241 break; 242 } 243 break; 244 } 245 debug("FAT%d: ret: %08x, entry: %08x, offset: %04x\n", 246 mydata->fatsize, ret, entry, offset); 247 248 return ret; 249 } 250 251 /* 252 * Set the file name information from 'name' into 'slotptr', 253 */ 254 static int str2slot(dir_slot *slotptr, const char *name, int *idx) 255 { 256 int j, end_idx = 0; 257 258 for (j = 0; j <= 8; j += 2) { 259 if (name[*idx] == 0x00) { 260 slotptr->name0_4[j] = 0; 261 slotptr->name0_4[j + 1] = 0; 262 end_idx++; 263 goto name0_4; 264 } 265 slotptr->name0_4[j] = name[*idx]; 266 (*idx)++; 267 end_idx++; 268 } 269 for (j = 0; j <= 10; j += 2) { 270 if (name[*idx] == 0x00) { 271 slotptr->name5_10[j] = 0; 272 slotptr->name5_10[j + 1] = 0; 273 end_idx++; 274 goto name5_10; 275 } 276 slotptr->name5_10[j] = name[*idx]; 277 (*idx)++; 278 end_idx++; 279 } 280 for (j = 0; j <= 2; j += 2) { 281 if (name[*idx] == 0x00) { 282 slotptr->name11_12[j] = 0; 283 slotptr->name11_12[j + 1] = 0; 284 end_idx++; 285 goto name11_12; 286 } 287 slotptr->name11_12[j] = name[*idx]; 288 (*idx)++; 289 end_idx++; 290 } 291 292 if (name[*idx] == 0x00) 293 return 1; 294 295 return 0; 296 /* Not used characters are filled with 0xff 0xff */ 297 name0_4: 298 for (; end_idx < 5; end_idx++) { 299 slotptr->name0_4[end_idx * 2] = 0xff; 300 slotptr->name0_4[end_idx * 2 + 1] = 0xff; 301 } 302 end_idx = 5; 303 name5_10: 304 end_idx -= 5; 305 for (; end_idx < 6; end_idx++) { 306 slotptr->name5_10[end_idx * 2] = 0xff; 307 slotptr->name5_10[end_idx * 2 + 1] = 0xff; 308 } 309 end_idx = 11; 310 name11_12: 311 end_idx -= 11; 312 for (; end_idx < 2; end_idx++) { 313 slotptr->name11_12[end_idx * 2] = 0xff; 314 slotptr->name11_12[end_idx * 2 + 1] = 0xff; 315 } 316 317 return 1; 318 } 319 320 static int is_next_clust(fsdata *mydata, dir_entry *dentptr); 321 static void flush_dir_table(fsdata *mydata, dir_entry **dentptr); 322 323 /* 324 * Fill dir_slot entries with appropriate name, id, and attr 325 * The real directory entry is returned by 'dentptr' 326 */ 327 static void 328 fill_dir_slot(fsdata *mydata, dir_entry **dentptr, const char *l_name) 329 { 330 dir_slot *slotptr = (dir_slot *)get_contents_vfatname_block; 331 __u8 counter = 0, checksum; 332 int idx = 0, ret; 333 334 /* Get short file name checksum value */ 335 checksum = mkcksum((*dentptr)->name, (*dentptr)->ext); 336 337 do { 338 memset(slotptr, 0x00, sizeof(dir_slot)); 339 ret = str2slot(slotptr, l_name, &idx); 340 slotptr->id = ++counter; 341 slotptr->attr = ATTR_VFAT; 342 slotptr->alias_checksum = checksum; 343 slotptr++; 344 } while (ret == 0); 345 346 slotptr--; 347 slotptr->id |= LAST_LONG_ENTRY_MASK; 348 349 while (counter >= 1) { 350 if (is_next_clust(mydata, *dentptr)) { 351 /* A new cluster is allocated for directory table */ 352 flush_dir_table(mydata, dentptr); 353 } 354 memcpy(*dentptr, slotptr, sizeof(dir_slot)); 355 (*dentptr)++; 356 slotptr--; 357 counter--; 358 } 359 360 if (is_next_clust(mydata, *dentptr)) { 361 /* A new cluster is allocated for directory table */ 362 flush_dir_table(mydata, dentptr); 363 } 364 } 365 366 static __u32 dir_curclust; 367 368 /* 369 * Extract the full long filename starting at 'retdent' (which is really 370 * a slot) into 'l_name'. If successful also copy the real directory entry 371 * into 'retdent' 372 * If additional adjacent cluster for directory entries is read into memory, 373 * then 'get_contents_vfatname_block' is copied into 'get_dentfromdir_block' and 374 * the location of the real directory entry is returned by 'retdent' 375 * Return 0 on success, -1 otherwise. 376 */ 377 static int 378 get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster, 379 dir_entry **retdent, char *l_name) 380 { 381 dir_entry *realdent; 382 dir_slot *slotptr = (dir_slot *)(*retdent); 383 dir_slot *slotptr2 = NULL; 384 __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ? 385 PREFETCH_BLOCKS : 386 mydata->clust_size); 387 __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff; 388 int idx = 0, cur_position = 0; 389 390 if (counter > VFAT_MAXSEQ) { 391 debug("Error: VFAT name is too long\n"); 392 return -1; 393 } 394 395 while ((__u8 *)slotptr < buflimit) { 396 if (counter == 0) 397 break; 398 if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter) 399 return -1; 400 slotptr++; 401 counter--; 402 } 403 404 if ((__u8 *)slotptr >= buflimit) { 405 if (curclust == 0) 406 return -1; 407 curclust = get_fatent_value(mydata, dir_curclust); 408 if (CHECK_CLUST(curclust, mydata->fatsize)) { 409 debug("curclust: 0x%x\n", curclust); 410 printf("Invalid FAT entry\n"); 411 return -1; 412 } 413 414 dir_curclust = curclust; 415 416 if (get_cluster(mydata, curclust, get_contents_vfatname_block, 417 mydata->clust_size * mydata->sect_size) != 0) { 418 debug("Error: reading directory block\n"); 419 return -1; 420 } 421 422 slotptr2 = (dir_slot *)get_contents_vfatname_block; 423 while (counter > 0) { 424 if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK) 425 & 0xff) != counter) 426 return -1; 427 slotptr2++; 428 counter--; 429 } 430 431 /* Save the real directory entry */ 432 realdent = (dir_entry *)slotptr2; 433 while ((__u8 *)slotptr2 > get_contents_vfatname_block) { 434 slotptr2--; 435 slot2str(slotptr2, l_name, &idx); 436 } 437 } else { 438 /* Save the real directory entry */ 439 realdent = (dir_entry *)slotptr; 440 } 441 442 do { 443 slotptr--; 444 if (slot2str(slotptr, l_name, &idx)) 445 break; 446 } while (!(slotptr->id & LAST_LONG_ENTRY_MASK)); 447 448 l_name[idx] = '\0'; 449 if (*l_name == DELETED_FLAG) 450 *l_name = '\0'; 451 else if (*l_name == aRING) 452 *l_name = DELETED_FLAG; 453 downcase(l_name); 454 455 /* Return the real directory entry */ 456 *retdent = realdent; 457 458 if (slotptr2) { 459 memcpy(get_dentfromdir_block, get_contents_vfatname_block, 460 mydata->clust_size * mydata->sect_size); 461 cur_position = (__u8 *)realdent - get_contents_vfatname_block; 462 *retdent = (dir_entry *) &get_dentfromdir_block[cur_position]; 463 } 464 465 return 0; 466 } 467 468 /* 469 * Set the entry at index 'entry' in a FAT (16/32) table. 470 */ 471 static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value) 472 { 473 __u32 bufnum, offset; 474 475 switch (mydata->fatsize) { 476 case 32: 477 bufnum = entry / FAT32BUFSIZE; 478 offset = entry - bufnum * FAT32BUFSIZE; 479 break; 480 case 16: 481 bufnum = entry / FAT16BUFSIZE; 482 offset = entry - bufnum * FAT16BUFSIZE; 483 break; 484 default: 485 /* Unsupported FAT size */ 486 return -1; 487 } 488 489 /* Read a new block of FAT entries into the cache. */ 490 if (bufnum != mydata->fatbufnum) { 491 int getsize = FATBUFBLOCKS; 492 __u8 *bufptr = mydata->fatbuf; 493 __u32 fatlength = mydata->fatlength; 494 __u32 startblock = bufnum * FATBUFBLOCKS; 495 496 fatlength *= mydata->sect_size; 497 startblock += mydata->fat_sect; 498 499 if (getsize > fatlength) 500 getsize = fatlength; 501 502 if (flush_dirty_fat_buffer(mydata) < 0) 503 return -1; 504 505 if (disk_read(startblock, getsize, bufptr) < 0) { 506 debug("Error reading FAT blocks\n"); 507 return -1; 508 } 509 mydata->fatbufnum = bufnum; 510 } 511 512 /* Mark as dirty */ 513 mydata->fat_dirty = 1; 514 515 /* Set the actual entry */ 516 switch (mydata->fatsize) { 517 case 32: 518 ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value); 519 break; 520 case 16: 521 ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value); 522 break; 523 default: 524 return -1; 525 } 526 527 return 0; 528 } 529 530 /* 531 * Determine the entry value at index 'entry' in a FAT (16/32) table 532 */ 533 static __u32 determine_fatent(fsdata *mydata, __u32 entry) 534 { 535 __u32 next_fat, next_entry = entry + 1; 536 537 while (1) { 538 next_fat = get_fatent_value(mydata, next_entry); 539 if (next_fat == 0) { 540 set_fatent_value(mydata, entry, next_entry); 541 break; 542 } 543 next_entry++; 544 } 545 debug("FAT%d: entry: %08x, entry_value: %04x\n", 546 mydata->fatsize, entry, next_entry); 547 548 return next_entry; 549 } 550 551 /* 552 * Write at most 'size' bytes from 'buffer' into the specified cluster. 553 * Return 0 on success, -1 otherwise. 554 */ 555 static int 556 set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, 557 unsigned long size) 558 { 559 __u32 idx = 0; 560 __u32 startsect; 561 int ret; 562 563 if (clustnum > 0) 564 startsect = mydata->data_begin + 565 clustnum * mydata->clust_size; 566 else 567 startsect = mydata->rootdir_sect; 568 569 debug("clustnum: %d, startsect: %d\n", clustnum, startsect); 570 571 if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) { 572 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); 573 574 printf("FAT: Misaligned buffer address (%p)\n", buffer); 575 576 while (size >= mydata->sect_size) { 577 memcpy(tmpbuf, buffer, mydata->sect_size); 578 ret = disk_write(startsect++, 1, tmpbuf); 579 if (ret != 1) { 580 debug("Error writing data (got %d)\n", ret); 581 return -1; 582 } 583 584 buffer += mydata->sect_size; 585 size -= mydata->sect_size; 586 } 587 } else if (size >= mydata->sect_size) { 588 idx = size / mydata->sect_size; 589 ret = disk_write(startsect, idx, buffer); 590 if (ret != idx) { 591 debug("Error writing data (got %d)\n", ret); 592 return -1; 593 } 594 595 startsect += idx; 596 idx *= mydata->sect_size; 597 buffer += idx; 598 size -= idx; 599 } 600 601 if (size) { 602 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); 603 604 memcpy(tmpbuf, buffer, size); 605 ret = disk_write(startsect, 1, tmpbuf); 606 if (ret != 1) { 607 debug("Error writing data (got %d)\n", ret); 608 return -1; 609 } 610 } 611 612 return 0; 613 } 614 615 /* 616 * Find the first empty cluster 617 */ 618 static int find_empty_cluster(fsdata *mydata) 619 { 620 __u32 fat_val, entry = 3; 621 622 while (1) { 623 fat_val = get_fatent_value(mydata, entry); 624 if (fat_val == 0) 625 break; 626 entry++; 627 } 628 629 return entry; 630 } 631 632 /* 633 * Write directory entries in 'get_dentfromdir_block' to block device 634 */ 635 static void flush_dir_table(fsdata *mydata, dir_entry **dentptr) 636 { 637 int dir_newclust = 0; 638 639 if (set_cluster(mydata, dir_curclust, 640 get_dentfromdir_block, 641 mydata->clust_size * mydata->sect_size) != 0) { 642 printf("error: wrinting directory entry\n"); 643 return; 644 } 645 dir_newclust = find_empty_cluster(mydata); 646 set_fatent_value(mydata, dir_curclust, dir_newclust); 647 if (mydata->fatsize == 32) 648 set_fatent_value(mydata, dir_newclust, 0xffffff8); 649 else if (mydata->fatsize == 16) 650 set_fatent_value(mydata, dir_newclust, 0xfff8); 651 652 dir_curclust = dir_newclust; 653 654 if (flush_dirty_fat_buffer(mydata) < 0) 655 return; 656 657 memset(get_dentfromdir_block, 0x00, 658 mydata->clust_size * mydata->sect_size); 659 660 *dentptr = (dir_entry *) get_dentfromdir_block; 661 } 662 663 /* 664 * Set empty cluster from 'entry' to the end of a file 665 */ 666 static int clear_fatent(fsdata *mydata, __u32 entry) 667 { 668 __u32 fat_val; 669 670 while (1) { 671 fat_val = get_fatent_value(mydata, entry); 672 if (fat_val != 0) 673 set_fatent_value(mydata, entry, 0); 674 else 675 break; 676 677 if (fat_val == 0xfffffff || fat_val == 0xffff) 678 break; 679 680 entry = fat_val; 681 } 682 683 /* Flush fat buffer */ 684 if (flush_dirty_fat_buffer(mydata) < 0) 685 return -1; 686 687 return 0; 688 } 689 690 /* 691 * Write at most 'maxsize' bytes from 'buffer' into 692 * the file associated with 'dentptr' 693 * Update the number of bytes written in *gotsize and return 0 694 * or return -1 on fatal errors. 695 */ 696 static int 697 set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer, 698 loff_t maxsize, loff_t *gotsize) 699 { 700 loff_t filesize = FAT2CPU32(dentptr->size); 701 unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; 702 __u32 curclust = START(dentptr); 703 __u32 endclust = 0, newclust = 0; 704 loff_t actsize; 705 706 *gotsize = 0; 707 debug("Filesize: %llu bytes\n", filesize); 708 709 if (maxsize > 0 && filesize > maxsize) 710 filesize = maxsize; 711 712 debug("%llu bytes\n", filesize); 713 714 if (!curclust) { 715 if (filesize) { 716 debug("error: nonempty clusterless file!\n"); 717 return -1; 718 } 719 return 0; 720 } 721 722 actsize = bytesperclust; 723 endclust = curclust; 724 do { 725 /* search for consecutive clusters */ 726 while (actsize < filesize) { 727 newclust = determine_fatent(mydata, endclust); 728 729 if ((newclust - 1) != endclust) 730 goto getit; 731 732 if (CHECK_CLUST(newclust, mydata->fatsize)) { 733 debug("newclust: 0x%x\n", newclust); 734 debug("Invalid FAT entry\n"); 735 return 0; 736 } 737 endclust = newclust; 738 actsize += bytesperclust; 739 } 740 741 /* set remaining bytes */ 742 actsize = filesize; 743 if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 744 debug("error: writing cluster\n"); 745 return -1; 746 } 747 *gotsize += actsize; 748 749 /* Mark end of file in FAT */ 750 if (mydata->fatsize == 16) 751 newclust = 0xffff; 752 else if (mydata->fatsize == 32) 753 newclust = 0xfffffff; 754 set_fatent_value(mydata, endclust, newclust); 755 756 return 0; 757 getit: 758 if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 759 debug("error: writing cluster\n"); 760 return -1; 761 } 762 *gotsize += actsize; 763 filesize -= actsize; 764 buffer += actsize; 765 766 if (CHECK_CLUST(newclust, mydata->fatsize)) { 767 debug("newclust: 0x%x\n", newclust); 768 debug("Invalid FAT entry\n"); 769 return 0; 770 } 771 actsize = bytesperclust; 772 curclust = endclust = newclust; 773 } while (1); 774 } 775 776 /* 777 * Set start cluster in directory entry 778 */ 779 static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr, 780 __u32 start_cluster) 781 { 782 if (mydata->fatsize == 32) 783 dentptr->starthi = 784 cpu_to_le16((start_cluster & 0xffff0000) >> 16); 785 dentptr->start = cpu_to_le16(start_cluster & 0xffff); 786 } 787 788 /* 789 * Fill dir_entry 790 */ 791 static void fill_dentry(fsdata *mydata, dir_entry *dentptr, 792 const char *filename, __u32 start_cluster, __u32 size, __u8 attr) 793 { 794 set_start_cluster(mydata, dentptr, start_cluster); 795 dentptr->size = cpu_to_le32(size); 796 797 dentptr->attr = attr; 798 799 set_name(dentptr, filename); 800 } 801 802 /* 803 * Check whether adding a file makes the file system to 804 * exceed the size of the block device 805 * Return -1 when overflow occurs, otherwise return 0 806 */ 807 static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size) 808 { 809 __u32 startsect, sect_num, offset; 810 811 if (clustnum > 0) { 812 startsect = mydata->data_begin + 813 clustnum * mydata->clust_size; 814 } else { 815 startsect = mydata->rootdir_sect; 816 } 817 818 sect_num = div_u64_rem(size, mydata->sect_size, &offset); 819 820 if (offset != 0) 821 sect_num++; 822 823 if (startsect + sect_num > cur_part_info.start + total_sector) 824 return -1; 825 return 0; 826 } 827 828 /* 829 * Check if adding several entries exceed one cluster boundary 830 */ 831 static int is_next_clust(fsdata *mydata, dir_entry *dentptr) 832 { 833 int cur_position; 834 835 cur_position = (__u8 *)dentptr - get_dentfromdir_block; 836 837 if (cur_position >= mydata->clust_size * mydata->sect_size) 838 return 1; 839 else 840 return 0; 841 } 842 843 static dir_entry *empty_dentptr; 844 /* 845 * Find a directory entry based on filename or start cluster number 846 * If the directory entry is not found, 847 * the new position for writing a directory entry will be returned 848 */ 849 static dir_entry *find_directory_entry(fsdata *mydata, int startsect, 850 char *filename, dir_entry *retdent, __u32 start) 851 { 852 __u32 curclust = (startsect - mydata->data_begin) / mydata->clust_size; 853 854 debug("get_dentfromdir: %s\n", filename); 855 856 while (1) { 857 dir_entry *dentptr; 858 859 int i; 860 861 if (get_cluster(mydata, curclust, get_dentfromdir_block, 862 mydata->clust_size * mydata->sect_size) != 0) { 863 printf("Error: reading directory block\n"); 864 return NULL; 865 } 866 867 dentptr = (dir_entry *)get_dentfromdir_block; 868 869 dir_curclust = curclust; 870 871 for (i = 0; i < DIRENTSPERCLUST; i++) { 872 char s_name[14], l_name[VFAT_MAXLEN_BYTES]; 873 874 l_name[0] = '\0'; 875 if (dentptr->name[0] == DELETED_FLAG) { 876 dentptr++; 877 if (is_next_clust(mydata, dentptr)) 878 break; 879 continue; 880 } 881 if ((dentptr->attr & ATTR_VOLUME)) { 882 if (vfat_enabled && 883 (dentptr->attr & ATTR_VFAT) && 884 (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { 885 get_long_file_name(mydata, curclust, 886 get_dentfromdir_block, 887 &dentptr, l_name); 888 debug("vfatname: |%s|\n", l_name); 889 } else { 890 /* Volume label or VFAT entry */ 891 dentptr++; 892 if (is_next_clust(mydata, dentptr)) 893 break; 894 continue; 895 } 896 } 897 if (dentptr->name[0] == 0) { 898 debug("Dentname == NULL - %d\n", i); 899 empty_dentptr = dentptr; 900 return NULL; 901 } 902 903 get_name(dentptr, s_name); 904 905 if (strcmp(filename, s_name) 906 && strcmp(filename, l_name)) { 907 debug("Mismatch: |%s|%s|\n", 908 s_name, l_name); 909 dentptr++; 910 if (is_next_clust(mydata, dentptr)) 911 break; 912 continue; 913 } 914 915 memcpy(retdent, dentptr, sizeof(dir_entry)); 916 917 debug("DentName: %s", s_name); 918 debug(", start: 0x%x", START(dentptr)); 919 debug(", size: 0x%x %s\n", 920 FAT2CPU32(dentptr->size), 921 (dentptr->attr & ATTR_DIR) ? 922 "(DIR)" : ""); 923 924 return dentptr; 925 } 926 927 /* 928 * In FAT16/12, the root dir is locate before data area, shows 929 * in following: 930 * ------------------------------------------------------------- 931 * | Boot | FAT1 & 2 | Root dir | Data (start from cluster #2) | 932 * ------------------------------------------------------------- 933 * 934 * As a result if curclust is in Root dir, it is a negative 935 * number or 0, 1. 936 * 937 */ 938 if (mydata->fatsize != 32 && (int)curclust <= 1) { 939 /* Current clust is in root dir, set to next clust */ 940 curclust++; 941 if ((int)curclust <= 1) 942 continue; /* continue to find */ 943 944 /* Reach the end of root dir */ 945 empty_dentptr = dentptr; 946 return NULL; 947 } 948 949 curclust = get_fatent_value(mydata, dir_curclust); 950 if (IS_LAST_CLUST(curclust, mydata->fatsize)) { 951 empty_dentptr = dentptr; 952 return NULL; 953 } 954 if (CHECK_CLUST(curclust, mydata->fatsize)) { 955 debug("curclust: 0x%x\n", curclust); 956 debug("Invalid FAT entry\n"); 957 return NULL; 958 } 959 } 960 961 return NULL; 962 } 963 964 static int do_fat_write(const char *filename, void *buffer, loff_t size, 965 loff_t *actwrite) 966 { 967 dir_entry *dentptr, *retdent; 968 __u32 startsect; 969 __u32 start_cluster; 970 boot_sector bs; 971 volume_info volinfo; 972 fsdata datablock; 973 fsdata *mydata = &datablock; 974 int cursect; 975 int ret = -1, name_len; 976 char l_filename[VFAT_MAXLEN_BYTES]; 977 978 *actwrite = size; 979 dir_curclust = 0; 980 981 if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) { 982 debug("error: reading boot sector\n"); 983 return -1; 984 } 985 986 total_sector = bs.total_sect; 987 if (total_sector == 0) 988 total_sector = (int)cur_part_info.size; /* cast of lbaint_t */ 989 990 if (mydata->fatsize == 32) 991 mydata->fatlength = bs.fat32_length; 992 else 993 mydata->fatlength = bs.fat_length; 994 995 mydata->fat_sect = bs.reserved; 996 997 cursect = mydata->rootdir_sect 998 = mydata->fat_sect + mydata->fatlength * bs.fats; 999 num_of_fats = bs.fats; 1000 1001 mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0]; 1002 mydata->clust_size = bs.cluster_size; 1003 1004 if (mydata->fatsize == 32) { 1005 mydata->data_begin = mydata->rootdir_sect - 1006 (mydata->clust_size * 2); 1007 } else { 1008 int rootdir_size; 1009 1010 rootdir_size = ((bs.dir_entries[1] * (int)256 + 1011 bs.dir_entries[0]) * 1012 sizeof(dir_entry)) / 1013 mydata->sect_size; 1014 mydata->data_begin = mydata->rootdir_sect + 1015 rootdir_size - 1016 (mydata->clust_size * 2); 1017 } 1018 1019 mydata->fatbufnum = -1; 1020 mydata->fat_dirty = 0; 1021 mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE); 1022 if (mydata->fatbuf == NULL) { 1023 debug("Error: allocating memory\n"); 1024 return -1; 1025 } 1026 1027 if (disk_read(cursect, 1028 (mydata->fatsize == 32) ? 1029 (mydata->clust_size) : 1030 PREFETCH_BLOCKS, do_fat_read_at_block) < 0) { 1031 debug("Error: reading rootdir block\n"); 1032 goto exit; 1033 } 1034 dentptr = (dir_entry *) do_fat_read_at_block; 1035 1036 name_len = strlen(filename); 1037 if (name_len >= VFAT_MAXLEN_BYTES) 1038 name_len = VFAT_MAXLEN_BYTES - 1; 1039 1040 memcpy(l_filename, filename, name_len); 1041 l_filename[name_len] = 0; /* terminate the string */ 1042 downcase(l_filename); 1043 1044 startsect = mydata->rootdir_sect; 1045 retdent = find_directory_entry(mydata, startsect, 1046 l_filename, dentptr, 0); 1047 if (retdent) { 1048 /* Update file size and start_cluster in a directory entry */ 1049 retdent->size = cpu_to_le32(size); 1050 start_cluster = START(retdent); 1051 1052 if (start_cluster) { 1053 if (size) { 1054 ret = check_overflow(mydata, start_cluster, 1055 size); 1056 if (ret) { 1057 printf("Error: %llu overflow\n", size); 1058 goto exit; 1059 } 1060 } 1061 1062 ret = clear_fatent(mydata, start_cluster); 1063 if (ret) { 1064 printf("Error: clearing FAT entries\n"); 1065 goto exit; 1066 } 1067 1068 if (!size) 1069 set_start_cluster(mydata, retdent, 0); 1070 } else if (size) { 1071 ret = start_cluster = find_empty_cluster(mydata); 1072 if (ret < 0) { 1073 printf("Error: finding empty cluster\n"); 1074 goto exit; 1075 } 1076 1077 ret = check_overflow(mydata, start_cluster, size); 1078 if (ret) { 1079 printf("Error: %llu overflow\n", size); 1080 goto exit; 1081 } 1082 1083 set_start_cluster(mydata, retdent, start_cluster); 1084 } 1085 } else { 1086 /* Set short name to set alias checksum field in dir_slot */ 1087 set_name(empty_dentptr, filename); 1088 fill_dir_slot(mydata, &empty_dentptr, filename); 1089 1090 if (size) { 1091 ret = start_cluster = find_empty_cluster(mydata); 1092 if (ret < 0) { 1093 printf("Error: finding empty cluster\n"); 1094 goto exit; 1095 } 1096 1097 ret = check_overflow(mydata, start_cluster, size); 1098 if (ret) { 1099 printf("Error: %llu overflow\n", size); 1100 goto exit; 1101 } 1102 } else { 1103 start_cluster = 0; 1104 } 1105 1106 /* Set attribute as archieve for regular file */ 1107 fill_dentry(mydata, empty_dentptr, filename, 1108 start_cluster, size, 0x20); 1109 1110 retdent = empty_dentptr; 1111 } 1112 1113 ret = set_contents(mydata, retdent, buffer, size, actwrite); 1114 if (ret < 0) { 1115 printf("Error: writing contents\n"); 1116 goto exit; 1117 } 1118 debug("attempt to write 0x%llx bytes\n", *actwrite); 1119 1120 /* Flush fat buffer */ 1121 ret = flush_dirty_fat_buffer(mydata); 1122 if (ret) { 1123 printf("Error: flush fat buffer\n"); 1124 goto exit; 1125 } 1126 1127 /* Write directory table to device */ 1128 ret = set_cluster(mydata, dir_curclust, get_dentfromdir_block, 1129 mydata->clust_size * mydata->sect_size); 1130 if (ret) 1131 printf("Error: writing directory entry\n"); 1132 1133 exit: 1134 free(mydata->fatbuf); 1135 return ret; 1136 } 1137 1138 int file_fat_write(const char *filename, void *buffer, loff_t offset, 1139 loff_t maxsize, loff_t *actwrite) 1140 { 1141 if (offset != 0) { 1142 printf("Error: non zero offset is currently not supported.\n"); 1143 return -1; 1144 } 1145 1146 printf("writing %s\n", filename); 1147 return do_fat_write(filename, buffer, maxsize, actwrite); 1148 } 1149