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 __u32 idx = 0; 559 __u32 startsect; 560 int ret; 561 562 if (clustnum > 0) 563 startsect = mydata->data_begin + 564 clustnum * mydata->clust_size; 565 else 566 startsect = mydata->rootdir_sect; 567 568 debug("clustnum: %d, startsect: %d\n", clustnum, startsect); 569 570 if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) { 571 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); 572 573 printf("FAT: Misaligned buffer address (%p)\n", buffer); 574 575 while (size >= mydata->sect_size) { 576 memcpy(tmpbuf, buffer, mydata->sect_size); 577 ret = disk_write(startsect++, 1, tmpbuf); 578 if (ret != 1) { 579 debug("Error writing data (got %d)\n", ret); 580 return -1; 581 } 582 583 buffer += mydata->sect_size; 584 size -= mydata->sect_size; 585 } 586 } else if (size >= mydata->sect_size) { 587 idx = size / mydata->sect_size; 588 ret = disk_write(startsect, idx, buffer); 589 if (ret != idx) { 590 debug("Error writing data (got %d)\n", ret); 591 return -1; 592 } 593 594 startsect += idx; 595 idx *= mydata->sect_size; 596 buffer += idx; 597 size -= idx; 598 } 599 600 if (size) { 601 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); 602 603 memcpy(tmpbuf, buffer, size); 604 ret = disk_write(startsect, 1, tmpbuf); 605 if (ret != 1) { 606 debug("Error writing data (got %d)\n", ret); 607 return -1; 608 } 609 } 610 611 return 0; 612 } 613 614 /* 615 * Find the first empty cluster 616 */ 617 static int find_empty_cluster(fsdata *mydata) 618 { 619 __u32 fat_val, entry = 3; 620 621 while (1) { 622 fat_val = get_fatent_value(mydata, entry); 623 if (fat_val == 0) 624 break; 625 entry++; 626 } 627 628 return entry; 629 } 630 631 /* 632 * Write directory entries in 'get_dentfromdir_block' to block device 633 */ 634 static void flush_dir_table(fsdata *mydata, dir_entry **dentptr) 635 { 636 int dir_newclust = 0; 637 638 if (set_cluster(mydata, dir_curclust, 639 get_dentfromdir_block, 640 mydata->clust_size * mydata->sect_size) != 0) { 641 printf("error: wrinting directory entry\n"); 642 return; 643 } 644 dir_newclust = find_empty_cluster(mydata); 645 set_fatent_value(mydata, dir_curclust, dir_newclust); 646 if (mydata->fatsize == 32) 647 set_fatent_value(mydata, dir_newclust, 0xffffff8); 648 else if (mydata->fatsize == 16) 649 set_fatent_value(mydata, dir_newclust, 0xfff8); 650 651 dir_curclust = dir_newclust; 652 653 if (flush_fat_buffer(mydata) < 0) 654 return; 655 656 memset(get_dentfromdir_block, 0x00, 657 mydata->clust_size * mydata->sect_size); 658 659 *dentptr = (dir_entry *) get_dentfromdir_block; 660 } 661 662 /* 663 * Set empty cluster from 'entry' to the end of a file 664 */ 665 static int clear_fatent(fsdata *mydata, __u32 entry) 666 { 667 __u32 fat_val; 668 669 while (1) { 670 fat_val = get_fatent_value(mydata, entry); 671 if (fat_val != 0) 672 set_fatent_value(mydata, entry, 0); 673 else 674 break; 675 676 if (fat_val == 0xfffffff || fat_val == 0xffff) 677 break; 678 679 entry = fat_val; 680 } 681 682 /* Flush fat buffer */ 683 if (flush_fat_buffer(mydata) < 0) 684 return -1; 685 686 return 0; 687 } 688 689 /* 690 * Write at most 'maxsize' bytes from 'buffer' into 691 * the file associated with 'dentptr' 692 * Update the number of bytes written in *gotsize and return 0 693 * or return -1 on fatal errors. 694 */ 695 static int 696 set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer, 697 loff_t maxsize, loff_t *gotsize) 698 { 699 loff_t filesize = FAT2CPU32(dentptr->size); 700 unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; 701 __u32 curclust = START(dentptr); 702 __u32 endclust = 0, newclust = 0; 703 loff_t actsize; 704 705 *gotsize = 0; 706 debug("Filesize: %llu bytes\n", filesize); 707 708 if (maxsize > 0 && filesize > maxsize) 709 filesize = maxsize; 710 711 debug("%llu bytes\n", filesize); 712 713 if (!curclust) { 714 if (filesize) { 715 debug("error: nonempty clusterless file!\n"); 716 return -1; 717 } 718 return 0; 719 } 720 721 actsize = bytesperclust; 722 endclust = curclust; 723 do { 724 /* search for consecutive clusters */ 725 while (actsize < filesize) { 726 newclust = determine_fatent(mydata, endclust); 727 728 if ((newclust - 1) != endclust) 729 goto getit; 730 731 if (CHECK_CLUST(newclust, mydata->fatsize)) { 732 debug("newclust: 0x%x\n", newclust); 733 debug("Invalid FAT entry\n"); 734 return 0; 735 } 736 endclust = newclust; 737 actsize += bytesperclust; 738 } 739 740 /* set remaining bytes */ 741 actsize = filesize; 742 if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 743 debug("error: writing cluster\n"); 744 return -1; 745 } 746 *gotsize += actsize; 747 748 /* Mark end of file in FAT */ 749 if (mydata->fatsize == 16) 750 newclust = 0xffff; 751 else if (mydata->fatsize == 32) 752 newclust = 0xfffffff; 753 set_fatent_value(mydata, endclust, newclust); 754 755 return 0; 756 getit: 757 if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 758 debug("error: writing cluster\n"); 759 return -1; 760 } 761 *gotsize += actsize; 762 filesize -= actsize; 763 buffer += actsize; 764 765 if (CHECK_CLUST(newclust, mydata->fatsize)) { 766 debug("newclust: 0x%x\n", newclust); 767 debug("Invalid FAT entry\n"); 768 return 0; 769 } 770 actsize = bytesperclust; 771 curclust = endclust = newclust; 772 } while (1); 773 } 774 775 /* 776 * Set start cluster in directory entry 777 */ 778 static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr, 779 __u32 start_cluster) 780 { 781 if (mydata->fatsize == 32) 782 dentptr->starthi = 783 cpu_to_le16((start_cluster & 0xffff0000) >> 16); 784 dentptr->start = cpu_to_le16(start_cluster & 0xffff); 785 } 786 787 /* 788 * Fill dir_entry 789 */ 790 static void fill_dentry(fsdata *mydata, dir_entry *dentptr, 791 const char *filename, __u32 start_cluster, __u32 size, __u8 attr) 792 { 793 set_start_cluster(mydata, dentptr, start_cluster); 794 dentptr->size = cpu_to_le32(size); 795 796 dentptr->attr = attr; 797 798 set_name(dentptr, filename); 799 } 800 801 /* 802 * Check whether adding a file makes the file system to 803 * exceed the size of the block device 804 * Return -1 when overflow occurs, otherwise return 0 805 */ 806 static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size) 807 { 808 __u32 startsect, sect_num, offset; 809 810 if (clustnum > 0) { 811 startsect = mydata->data_begin + 812 clustnum * mydata->clust_size; 813 } else { 814 startsect = mydata->rootdir_sect; 815 } 816 817 sect_num = div_u64_rem(size, mydata->sect_size, &offset); 818 819 if (offset != 0) 820 sect_num++; 821 822 if (startsect + sect_num > cur_part_info.start + total_sector) 823 return -1; 824 return 0; 825 } 826 827 /* 828 * Check if adding several entries exceed one cluster boundary 829 */ 830 static int is_next_clust(fsdata *mydata, dir_entry *dentptr) 831 { 832 int cur_position; 833 834 cur_position = (__u8 *)dentptr - get_dentfromdir_block; 835 836 if (cur_position >= mydata->clust_size * mydata->sect_size) 837 return 1; 838 else 839 return 0; 840 } 841 842 static dir_entry *empty_dentptr; 843 /* 844 * Find a directory entry based on filename or start cluster number 845 * If the directory entry is not found, 846 * the new position for writing a directory entry will be returned 847 */ 848 static dir_entry *find_directory_entry(fsdata *mydata, int startsect, 849 char *filename, dir_entry *retdent, __u32 start) 850 { 851 __u32 curclust = (startsect - mydata->data_begin) / mydata->clust_size; 852 853 debug("get_dentfromdir: %s\n", filename); 854 855 while (1) { 856 dir_entry *dentptr; 857 858 int i; 859 860 if (get_cluster(mydata, curclust, get_dentfromdir_block, 861 mydata->clust_size * mydata->sect_size) != 0) { 862 printf("Error: reading directory block\n"); 863 return NULL; 864 } 865 866 dentptr = (dir_entry *)get_dentfromdir_block; 867 868 dir_curclust = curclust; 869 870 for (i = 0; i < DIRENTSPERCLUST; i++) { 871 char s_name[14], l_name[VFAT_MAXLEN_BYTES]; 872 873 l_name[0] = '\0'; 874 if (dentptr->name[0] == DELETED_FLAG) { 875 dentptr++; 876 if (is_next_clust(mydata, dentptr)) 877 break; 878 continue; 879 } 880 if ((dentptr->attr & ATTR_VOLUME)) { 881 if (vfat_enabled && 882 (dentptr->attr & ATTR_VFAT) && 883 (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { 884 get_long_file_name(mydata, curclust, 885 get_dentfromdir_block, 886 &dentptr, l_name); 887 debug("vfatname: |%s|\n", l_name); 888 } else { 889 /* Volume label or VFAT entry */ 890 dentptr++; 891 if (is_next_clust(mydata, dentptr)) 892 break; 893 continue; 894 } 895 } 896 if (dentptr->name[0] == 0) { 897 debug("Dentname == NULL - %d\n", i); 898 empty_dentptr = dentptr; 899 return NULL; 900 } 901 902 get_name(dentptr, s_name); 903 904 if (strcmp(filename, s_name) 905 && strcmp(filename, l_name)) { 906 debug("Mismatch: |%s|%s|\n", 907 s_name, l_name); 908 dentptr++; 909 if (is_next_clust(mydata, dentptr)) 910 break; 911 continue; 912 } 913 914 memcpy(retdent, dentptr, sizeof(dir_entry)); 915 916 debug("DentName: %s", s_name); 917 debug(", start: 0x%x", START(dentptr)); 918 debug(", size: 0x%x %s\n", 919 FAT2CPU32(dentptr->size), 920 (dentptr->attr & ATTR_DIR) ? 921 "(DIR)" : ""); 922 923 return dentptr; 924 } 925 926 /* 927 * In FAT16/12, the root dir is locate before data area, shows 928 * in following: 929 * ------------------------------------------------------------- 930 * | Boot | FAT1 & 2 | Root dir | Data (start from cluster #2) | 931 * ------------------------------------------------------------- 932 * 933 * As a result if curclust is in Root dir, it is a negative 934 * number or 0, 1. 935 * 936 */ 937 if (mydata->fatsize != 32 && (int)curclust <= 1) { 938 /* Current clust is in root dir, set to next clust */ 939 curclust++; 940 if ((int)curclust <= 1) 941 continue; /* continue to find */ 942 943 /* Reach the end of root dir */ 944 empty_dentptr = dentptr; 945 return NULL; 946 } 947 948 curclust = get_fatent_value(mydata, dir_curclust); 949 if (IS_LAST_CLUST(curclust, mydata->fatsize)) { 950 empty_dentptr = dentptr; 951 return NULL; 952 } 953 if (CHECK_CLUST(curclust, mydata->fatsize)) { 954 debug("curclust: 0x%x\n", curclust); 955 debug("Invalid FAT entry\n"); 956 return NULL; 957 } 958 } 959 960 return NULL; 961 } 962 963 static int do_fat_write(const char *filename, void *buffer, loff_t size, 964 loff_t *actwrite) 965 { 966 dir_entry *dentptr, *retdent; 967 __u32 startsect; 968 __u32 start_cluster; 969 boot_sector bs; 970 volume_info volinfo; 971 fsdata datablock; 972 fsdata *mydata = &datablock; 973 int cursect; 974 int ret = -1, name_len; 975 char l_filename[VFAT_MAXLEN_BYTES]; 976 977 *actwrite = size; 978 dir_curclust = 0; 979 980 if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) { 981 debug("error: reading boot sector\n"); 982 return -1; 983 } 984 985 total_sector = bs.total_sect; 986 if (total_sector == 0) 987 total_sector = (int)cur_part_info.size; /* cast of lbaint_t */ 988 989 if (mydata->fatsize == 32) 990 mydata->fatlength = bs.fat32_length; 991 else 992 mydata->fatlength = bs.fat_length; 993 994 mydata->fat_sect = bs.reserved; 995 996 cursect = mydata->rootdir_sect 997 = mydata->fat_sect + mydata->fatlength * bs.fats; 998 num_of_fats = bs.fats; 999 1000 mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0]; 1001 mydata->clust_size = bs.cluster_size; 1002 1003 if (mydata->fatsize == 32) { 1004 mydata->data_begin = mydata->rootdir_sect - 1005 (mydata->clust_size * 2); 1006 } else { 1007 int rootdir_size; 1008 1009 rootdir_size = ((bs.dir_entries[1] * (int)256 + 1010 bs.dir_entries[0]) * 1011 sizeof(dir_entry)) / 1012 mydata->sect_size; 1013 mydata->data_begin = mydata->rootdir_sect + 1014 rootdir_size - 1015 (mydata->clust_size * 2); 1016 } 1017 1018 mydata->fatbufnum = -1; 1019 mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE); 1020 if (mydata->fatbuf == NULL) { 1021 debug("Error: allocating memory\n"); 1022 return -1; 1023 } 1024 1025 if (disk_read(cursect, 1026 (mydata->fatsize == 32) ? 1027 (mydata->clust_size) : 1028 PREFETCH_BLOCKS, do_fat_read_at_block) < 0) { 1029 debug("Error: reading rootdir block\n"); 1030 goto exit; 1031 } 1032 dentptr = (dir_entry *) do_fat_read_at_block; 1033 1034 name_len = strlen(filename); 1035 if (name_len >= VFAT_MAXLEN_BYTES) 1036 name_len = VFAT_MAXLEN_BYTES - 1; 1037 1038 memcpy(l_filename, filename, name_len); 1039 l_filename[name_len] = 0; /* terminate the string */ 1040 downcase(l_filename); 1041 1042 startsect = mydata->rootdir_sect; 1043 retdent = find_directory_entry(mydata, startsect, 1044 l_filename, dentptr, 0); 1045 if (retdent) { 1046 /* Update file size and start_cluster in a directory entry */ 1047 retdent->size = cpu_to_le32(size); 1048 start_cluster = START(retdent); 1049 1050 if (start_cluster) { 1051 if (size) { 1052 ret = check_overflow(mydata, start_cluster, 1053 size); 1054 if (ret) { 1055 printf("Error: %llu overflow\n", size); 1056 goto exit; 1057 } 1058 } 1059 1060 ret = clear_fatent(mydata, start_cluster); 1061 if (ret) { 1062 printf("Error: clearing FAT entries\n"); 1063 goto exit; 1064 } 1065 1066 if (!size) 1067 set_start_cluster(mydata, retdent, 0); 1068 } else if (size) { 1069 ret = start_cluster = find_empty_cluster(mydata); 1070 if (ret < 0) { 1071 printf("Error: finding empty cluster\n"); 1072 goto exit; 1073 } 1074 1075 ret = check_overflow(mydata, start_cluster, size); 1076 if (ret) { 1077 printf("Error: %llu overflow\n", size); 1078 goto exit; 1079 } 1080 1081 set_start_cluster(mydata, retdent, start_cluster); 1082 } 1083 } else { 1084 /* Set short name to set alias checksum field in dir_slot */ 1085 set_name(empty_dentptr, filename); 1086 fill_dir_slot(mydata, &empty_dentptr, filename); 1087 1088 if (size) { 1089 ret = start_cluster = find_empty_cluster(mydata); 1090 if (ret < 0) { 1091 printf("Error: finding empty cluster\n"); 1092 goto exit; 1093 } 1094 1095 ret = check_overflow(mydata, start_cluster, size); 1096 if (ret) { 1097 printf("Error: %llu overflow\n", size); 1098 goto exit; 1099 } 1100 } else { 1101 start_cluster = 0; 1102 } 1103 1104 /* Set attribute as archieve for regular file */ 1105 fill_dentry(mydata, empty_dentptr, filename, 1106 start_cluster, size, 0x20); 1107 1108 retdent = empty_dentptr; 1109 } 1110 1111 ret = set_contents(mydata, retdent, buffer, size, actwrite); 1112 if (ret < 0) { 1113 printf("Error: writing contents\n"); 1114 goto exit; 1115 } 1116 debug("attempt to write 0x%llx bytes\n", *actwrite); 1117 1118 /* Flush fat buffer */ 1119 ret = flush_fat_buffer(mydata); 1120 if (ret) { 1121 printf("Error: flush fat buffer\n"); 1122 goto exit; 1123 } 1124 1125 /* Write directory table to device */ 1126 ret = set_cluster(mydata, dir_curclust, get_dentfromdir_block, 1127 mydata->clust_size * mydata->sect_size); 1128 if (ret) 1129 printf("Error: writing directory entry\n"); 1130 1131 exit: 1132 free(mydata->fatbuf); 1133 return ret; 1134 } 1135 1136 int file_fat_write(const char *filename, void *buffer, loff_t offset, 1137 loff_t maxsize, loff_t *actwrite) 1138 { 1139 if (offset != 0) { 1140 printf("Error: non zero offset is currently not suported.\n"); 1141 return -1; 1142 } 1143 1144 printf("writing %s\n", filename); 1145 return do_fat_write(filename, buffer, maxsize, actwrite); 1146 } 1147