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