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