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