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