1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * fat_write.c 4 * 5 * R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim 6 */ 7 8 #include <common.h> 9 #include <command.h> 10 #include <config.h> 11 #include <fat.h> 12 #include <asm/byteorder.h> 13 #include <part.h> 14 #include <linux/ctype.h> 15 #include <div64.h> 16 #include <linux/math64.h> 17 #include "fat.c" 18 19 static void uppercase(char *str, int len) 20 { 21 int i; 22 23 for (i = 0; i < len; i++) { 24 *str = toupper(*str); 25 str++; 26 } 27 } 28 29 static int total_sector; 30 static int disk_write(__u32 block, __u32 nr_blocks, void *buf) 31 { 32 ulong ret; 33 34 if (!cur_dev) 35 return -1; 36 37 if (cur_part_info.start + block + nr_blocks > 38 cur_part_info.start + total_sector) { 39 printf("error: overflow occurs\n"); 40 return -1; 41 } 42 43 ret = blk_dwrite(cur_dev, cur_part_info.start + block, nr_blocks, buf); 44 if (nr_blocks && ret == 0) 45 return -1; 46 47 return ret; 48 } 49 50 /* 51 * Set short name in directory entry 52 */ 53 static void set_name(dir_entry *dirent, const char *filename) 54 { 55 char s_name[VFAT_MAXLEN_BYTES]; 56 char *period; 57 int period_location, len, i, ext_num; 58 59 if (filename == NULL) 60 return; 61 62 len = strlen(filename); 63 if (len == 0) 64 return; 65 66 strcpy(s_name, filename); 67 uppercase(s_name, len); 68 69 period = strchr(s_name, '.'); 70 if (period == NULL) { 71 period_location = len; 72 ext_num = 0; 73 } else { 74 period_location = period - s_name; 75 ext_num = len - period_location - 1; 76 } 77 78 /* Pad spaces when the length of file name is shorter than eight */ 79 if (period_location < 8) { 80 memcpy(dirent->name, s_name, period_location); 81 for (i = period_location; i < 8; i++) 82 dirent->name[i] = ' '; 83 } else if (period_location == 8) { 84 memcpy(dirent->name, s_name, period_location); 85 } else { 86 memcpy(dirent->name, s_name, 6); 87 dirent->name[6] = '~'; 88 dirent->name[7] = '1'; 89 } 90 91 if (ext_num < 3) { 92 memcpy(dirent->ext, s_name + period_location + 1, ext_num); 93 for (i = ext_num; i < 3; i++) 94 dirent->ext[i] = ' '; 95 } else 96 memcpy(dirent->ext, s_name + period_location + 1, 3); 97 98 debug("name : %s\n", dirent->name); 99 debug("ext : %s\n", dirent->ext); 100 } 101 102 /* 103 * Write fat buffer into block device 104 */ 105 static int flush_dirty_fat_buffer(fsdata *mydata) 106 { 107 int getsize = FATBUFBLOCKS; 108 __u32 fatlength = mydata->fatlength; 109 __u8 *bufptr = mydata->fatbuf; 110 __u32 startblock = mydata->fatbufnum * FATBUFBLOCKS; 111 112 debug("debug: evicting %d, dirty: %d\n", mydata->fatbufnum, 113 (int)mydata->fat_dirty); 114 115 if ((!mydata->fat_dirty) || (mydata->fatbufnum == -1)) 116 return 0; 117 118 /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */ 119 if (startblock + getsize > fatlength) 120 getsize = fatlength - startblock; 121 122 startblock += mydata->fat_sect; 123 124 /* Write FAT buf */ 125 if (disk_write(startblock, getsize, bufptr) < 0) { 126 debug("error: writing FAT blocks\n"); 127 return -1; 128 } 129 130 if (mydata->fats == 2) { 131 /* Update corresponding second FAT blocks */ 132 startblock += mydata->fatlength; 133 if (disk_write(startblock, getsize, bufptr) < 0) { 134 debug("error: writing second FAT blocks\n"); 135 return -1; 136 } 137 } 138 mydata->fat_dirty = 0; 139 140 return 0; 141 } 142 143 /* 144 * Set the file name information from 'name' into 'slotptr', 145 */ 146 static int str2slot(dir_slot *slotptr, const char *name, int *idx) 147 { 148 int j, end_idx = 0; 149 150 for (j = 0; j <= 8; j += 2) { 151 if (name[*idx] == 0x00) { 152 slotptr->name0_4[j] = 0; 153 slotptr->name0_4[j + 1] = 0; 154 end_idx++; 155 goto name0_4; 156 } 157 slotptr->name0_4[j] = name[*idx]; 158 (*idx)++; 159 end_idx++; 160 } 161 for (j = 0; j <= 10; j += 2) { 162 if (name[*idx] == 0x00) { 163 slotptr->name5_10[j] = 0; 164 slotptr->name5_10[j + 1] = 0; 165 end_idx++; 166 goto name5_10; 167 } 168 slotptr->name5_10[j] = name[*idx]; 169 (*idx)++; 170 end_idx++; 171 } 172 for (j = 0; j <= 2; j += 2) { 173 if (name[*idx] == 0x00) { 174 slotptr->name11_12[j] = 0; 175 slotptr->name11_12[j + 1] = 0; 176 end_idx++; 177 goto name11_12; 178 } 179 slotptr->name11_12[j] = name[*idx]; 180 (*idx)++; 181 end_idx++; 182 } 183 184 if (name[*idx] == 0x00) 185 return 1; 186 187 return 0; 188 /* Not used characters are filled with 0xff 0xff */ 189 name0_4: 190 for (; end_idx < 5; end_idx++) { 191 slotptr->name0_4[end_idx * 2] = 0xff; 192 slotptr->name0_4[end_idx * 2 + 1] = 0xff; 193 } 194 end_idx = 5; 195 name5_10: 196 end_idx -= 5; 197 for (; end_idx < 6; end_idx++) { 198 slotptr->name5_10[end_idx * 2] = 0xff; 199 slotptr->name5_10[end_idx * 2 + 1] = 0xff; 200 } 201 end_idx = 11; 202 name11_12: 203 end_idx -= 11; 204 for (; end_idx < 2; end_idx++) { 205 slotptr->name11_12[end_idx * 2] = 0xff; 206 slotptr->name11_12[end_idx * 2 + 1] = 0xff; 207 } 208 209 return 1; 210 } 211 212 static int flush_dir_table(fat_itr *itr); 213 214 /* 215 * Fill dir_slot entries with appropriate name, id, and attr 216 * 'itr' will point to a next entry 217 */ 218 static int 219 fill_dir_slot(fat_itr *itr, const char *l_name) 220 { 221 __u8 temp_dir_slot_buffer[MAX_LFN_SLOT * sizeof(dir_slot)]; 222 dir_slot *slotptr = (dir_slot *)temp_dir_slot_buffer; 223 __u8 counter = 0, checksum; 224 int idx = 0, ret; 225 226 /* Get short file name checksum value */ 227 checksum = mkcksum(itr->dent->name, itr->dent->ext); 228 229 do { 230 memset(slotptr, 0x00, sizeof(dir_slot)); 231 ret = str2slot(slotptr, l_name, &idx); 232 slotptr->id = ++counter; 233 slotptr->attr = ATTR_VFAT; 234 slotptr->alias_checksum = checksum; 235 slotptr++; 236 } while (ret == 0); 237 238 slotptr--; 239 slotptr->id |= LAST_LONG_ENTRY_MASK; 240 241 while (counter >= 1) { 242 memcpy(itr->dent, slotptr, sizeof(dir_slot)); 243 slotptr--; 244 counter--; 245 if (!fat_itr_next(itr)) 246 if (!itr->dent && !itr->is_root && flush_dir_table(itr)) 247 return -1; 248 } 249 250 if (!itr->dent && !itr->is_root) 251 /* 252 * don't care return value here because we have already 253 * finished completing an entry with name, only ending up 254 * no more entry left 255 */ 256 flush_dir_table(itr); 257 258 return 0; 259 } 260 261 /* 262 * Set the entry at index 'entry' in a FAT (12/16/32) table. 263 */ 264 static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value) 265 { 266 __u32 bufnum, offset, off16; 267 __u16 val1, val2; 268 269 switch (mydata->fatsize) { 270 case 32: 271 bufnum = entry / FAT32BUFSIZE; 272 offset = entry - bufnum * FAT32BUFSIZE; 273 break; 274 case 16: 275 bufnum = entry / FAT16BUFSIZE; 276 offset = entry - bufnum * FAT16BUFSIZE; 277 break; 278 case 12: 279 bufnum = entry / FAT12BUFSIZE; 280 offset = entry - bufnum * FAT12BUFSIZE; 281 break; 282 default: 283 /* Unsupported FAT size */ 284 return -1; 285 } 286 287 /* Read a new block of FAT entries into the cache. */ 288 if (bufnum != mydata->fatbufnum) { 289 int getsize = FATBUFBLOCKS; 290 __u8 *bufptr = mydata->fatbuf; 291 __u32 fatlength = mydata->fatlength; 292 __u32 startblock = bufnum * FATBUFBLOCKS; 293 294 /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */ 295 if (startblock + getsize > fatlength) 296 getsize = fatlength - startblock; 297 298 if (flush_dirty_fat_buffer(mydata) < 0) 299 return -1; 300 301 startblock += mydata->fat_sect; 302 303 if (disk_read(startblock, getsize, bufptr) < 0) { 304 debug("Error reading FAT blocks\n"); 305 return -1; 306 } 307 mydata->fatbufnum = bufnum; 308 } 309 310 /* Mark as dirty */ 311 mydata->fat_dirty = 1; 312 313 /* Set the actual entry */ 314 switch (mydata->fatsize) { 315 case 32: 316 ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value); 317 break; 318 case 16: 319 ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value); 320 break; 321 case 12: 322 off16 = (offset * 3) / 4; 323 324 switch (offset & 0x3) { 325 case 0: 326 val1 = cpu_to_le16(entry_value) & 0xfff; 327 ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff; 328 ((__u16 *)mydata->fatbuf)[off16] |= val1; 329 break; 330 case 1: 331 val1 = cpu_to_le16(entry_value) & 0xf; 332 val2 = (cpu_to_le16(entry_value) >> 4) & 0xff; 333 334 ((__u16 *)mydata->fatbuf)[off16] &= ~0xf000; 335 ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 12); 336 337 ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xff; 338 ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2; 339 break; 340 case 2: 341 val1 = cpu_to_le16(entry_value) & 0xff; 342 val2 = (cpu_to_le16(entry_value) >> 8) & 0xf; 343 344 ((__u16 *)mydata->fatbuf)[off16] &= ~0xff00; 345 ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 8); 346 347 ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xf; 348 ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2; 349 break; 350 case 3: 351 val1 = cpu_to_le16(entry_value) & 0xfff; 352 ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff0; 353 ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 4); 354 break; 355 default: 356 break; 357 } 358 359 break; 360 default: 361 return -1; 362 } 363 364 return 0; 365 } 366 367 /* 368 * Determine the next free cluster after 'entry' in a FAT (12/16/32) table 369 * and link it to 'entry'. EOC marker is not set on returned entry. 370 */ 371 static __u32 determine_fatent(fsdata *mydata, __u32 entry) 372 { 373 __u32 next_fat, next_entry = entry + 1; 374 375 while (1) { 376 next_fat = get_fatent(mydata, next_entry); 377 if (next_fat == 0) { 378 /* found free entry, link to entry */ 379 set_fatent_value(mydata, entry, next_entry); 380 break; 381 } 382 next_entry++; 383 } 384 debug("FAT%d: entry: %08x, entry_value: %04x\n", 385 mydata->fatsize, entry, next_entry); 386 387 return next_entry; 388 } 389 390 /* 391 * Write at most 'size' bytes from 'buffer' into the specified cluster. 392 * Return 0 on success, -1 otherwise. 393 */ 394 static int 395 set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, 396 unsigned long size) 397 { 398 __u32 idx = 0; 399 __u32 startsect; 400 int ret; 401 402 if (clustnum > 0) 403 startsect = clust_to_sect(mydata, clustnum); 404 else 405 startsect = mydata->rootdir_sect; 406 407 debug("clustnum: %d, startsect: %d\n", clustnum, startsect); 408 409 if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) { 410 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); 411 412 debug("FAT: Misaligned buffer address (%p)\n", buffer); 413 414 while (size >= mydata->sect_size) { 415 memcpy(tmpbuf, buffer, mydata->sect_size); 416 ret = disk_write(startsect++, 1, tmpbuf); 417 if (ret != 1) { 418 debug("Error writing data (got %d)\n", ret); 419 return -1; 420 } 421 422 buffer += mydata->sect_size; 423 size -= mydata->sect_size; 424 } 425 } else if (size >= mydata->sect_size) { 426 idx = size / mydata->sect_size; 427 ret = disk_write(startsect, idx, buffer); 428 if (ret != idx) { 429 debug("Error writing data (got %d)\n", ret); 430 return -1; 431 } 432 433 startsect += idx; 434 idx *= mydata->sect_size; 435 buffer += idx; 436 size -= idx; 437 } 438 439 if (size) { 440 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); 441 442 memcpy(tmpbuf, buffer, size); 443 ret = disk_write(startsect, 1, tmpbuf); 444 if (ret != 1) { 445 debug("Error writing data (got %d)\n", ret); 446 return -1; 447 } 448 } 449 450 return 0; 451 } 452 453 static __u8 tmpbuf_cluster[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN); 454 455 /* 456 * Read and modify data on existing and consecutive cluster blocks 457 */ 458 static int 459 get_set_cluster(fsdata *mydata, __u32 clustnum, loff_t pos, __u8 *buffer, 460 loff_t size, loff_t *gotsize) 461 { 462 unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; 463 __u32 startsect; 464 loff_t wsize; 465 int clustcount, i, ret; 466 467 *gotsize = 0; 468 if (!size) 469 return 0; 470 471 assert(pos < bytesperclust); 472 startsect = clust_to_sect(mydata, clustnum); 473 474 debug("clustnum: %d, startsect: %d, pos: %lld\n", 475 clustnum, startsect, pos); 476 477 /* partial write at beginning */ 478 if (pos) { 479 wsize = min(bytesperclust - pos, size); 480 ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster); 481 if (ret != mydata->clust_size) { 482 debug("Error reading data (got %d)\n", ret); 483 return -1; 484 } 485 486 memcpy(tmpbuf_cluster + pos, buffer, wsize); 487 ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster); 488 if (ret != mydata->clust_size) { 489 debug("Error writing data (got %d)\n", ret); 490 return -1; 491 } 492 493 size -= wsize; 494 buffer += wsize; 495 *gotsize += wsize; 496 497 startsect += mydata->clust_size; 498 499 if (!size) 500 return 0; 501 } 502 503 /* full-cluster write */ 504 if (size >= bytesperclust) { 505 clustcount = lldiv(size, bytesperclust); 506 507 if (!((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1))) { 508 wsize = clustcount * bytesperclust; 509 ret = disk_write(startsect, 510 clustcount * mydata->clust_size, 511 buffer); 512 if (ret != clustcount * mydata->clust_size) { 513 debug("Error writing data (got %d)\n", ret); 514 return -1; 515 } 516 517 size -= wsize; 518 buffer += wsize; 519 *gotsize += wsize; 520 521 startsect += clustcount * mydata->clust_size; 522 } else { 523 for (i = 0; i < clustcount; i++) { 524 memcpy(tmpbuf_cluster, buffer, bytesperclust); 525 ret = disk_write(startsect, 526 mydata->clust_size, 527 tmpbuf_cluster); 528 if (ret != mydata->clust_size) { 529 debug("Error writing data (got %d)\n", 530 ret); 531 return -1; 532 } 533 534 size -= bytesperclust; 535 buffer += bytesperclust; 536 *gotsize += bytesperclust; 537 538 startsect += mydata->clust_size; 539 } 540 } 541 } 542 543 /* partial write at end */ 544 if (size) { 545 wsize = size; 546 ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster); 547 if (ret != mydata->clust_size) { 548 debug("Error reading data (got %d)\n", ret); 549 return -1; 550 } 551 memcpy(tmpbuf_cluster, buffer, wsize); 552 ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster); 553 if (ret != mydata->clust_size) { 554 debug("Error writing data (got %d)\n", ret); 555 return -1; 556 } 557 558 size -= wsize; 559 buffer += wsize; 560 *gotsize += wsize; 561 } 562 563 assert(!size); 564 565 return 0; 566 } 567 568 /* 569 * Find the first empty cluster 570 */ 571 static int find_empty_cluster(fsdata *mydata) 572 { 573 __u32 fat_val, entry = 3; 574 575 while (1) { 576 fat_val = get_fatent(mydata, entry); 577 if (fat_val == 0) 578 break; 579 entry++; 580 } 581 582 return entry; 583 } 584 585 /* 586 * Write directory entries in itr's buffer to block device 587 */ 588 static int flush_dir_table(fat_itr *itr) 589 { 590 fsdata *mydata = itr->fsdata; 591 int dir_newclust = 0; 592 unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; 593 594 if (set_cluster(mydata, itr->clust, itr->block, bytesperclust) != 0) { 595 printf("error: writing directory entry\n"); 596 return -1; 597 } 598 dir_newclust = find_empty_cluster(mydata); 599 set_fatent_value(mydata, itr->clust, dir_newclust); 600 if (mydata->fatsize == 32) 601 set_fatent_value(mydata, dir_newclust, 0xffffff8); 602 else if (mydata->fatsize == 16) 603 set_fatent_value(mydata, dir_newclust, 0xfff8); 604 else if (mydata->fatsize == 12) 605 set_fatent_value(mydata, dir_newclust, 0xff8); 606 607 itr->clust = dir_newclust; 608 itr->next_clust = dir_newclust; 609 610 if (flush_dirty_fat_buffer(mydata) < 0) 611 return -1; 612 613 memset(itr->block, 0x00, bytesperclust); 614 615 itr->dent = (dir_entry *)itr->block; 616 itr->last_cluster = 1; 617 itr->remaining = bytesperclust / sizeof(dir_entry) - 1; 618 619 return 0; 620 } 621 622 /* 623 * Set empty cluster from 'entry' to the end of a file 624 */ 625 static int clear_fatent(fsdata *mydata, __u32 entry) 626 { 627 __u32 fat_val; 628 629 while (!CHECK_CLUST(entry, mydata->fatsize)) { 630 fat_val = get_fatent(mydata, entry); 631 if (fat_val != 0) 632 set_fatent_value(mydata, entry, 0); 633 else 634 break; 635 636 entry = fat_val; 637 } 638 639 /* Flush fat buffer */ 640 if (flush_dirty_fat_buffer(mydata) < 0) 641 return -1; 642 643 return 0; 644 } 645 646 /* 647 * Set start cluster in directory entry 648 */ 649 static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr, 650 __u32 start_cluster) 651 { 652 if (mydata->fatsize == 32) 653 dentptr->starthi = 654 cpu_to_le16((start_cluster & 0xffff0000) >> 16); 655 dentptr->start = cpu_to_le16(start_cluster & 0xffff); 656 } 657 658 /* 659 * Check whether adding a file makes the file system to 660 * exceed the size of the block device 661 * Return -1 when overflow occurs, otherwise return 0 662 */ 663 static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size) 664 { 665 __u32 startsect, sect_num, offset; 666 667 if (clustnum > 0) 668 startsect = clust_to_sect(mydata, clustnum); 669 else 670 startsect = mydata->rootdir_sect; 671 672 sect_num = div_u64_rem(size, mydata->sect_size, &offset); 673 674 if (offset != 0) 675 sect_num++; 676 677 if (startsect + sect_num > total_sector) 678 return -1; 679 return 0; 680 } 681 682 /* 683 * Write at most 'maxsize' bytes from 'buffer' into 684 * the file associated with 'dentptr' 685 * Update the number of bytes written in *gotsize and return 0 686 * or return -1 on fatal errors. 687 */ 688 static int 689 set_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, __u8 *buffer, 690 loff_t maxsize, loff_t *gotsize) 691 { 692 loff_t filesize; 693 unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; 694 __u32 curclust = START(dentptr); 695 __u32 endclust = 0, newclust = 0; 696 loff_t cur_pos, offset, actsize, wsize; 697 698 *gotsize = 0; 699 filesize = pos + maxsize; 700 701 debug("%llu bytes\n", filesize); 702 703 if (!filesize) { 704 if (!curclust) 705 return 0; 706 if (!CHECK_CLUST(curclust, mydata->fatsize) || 707 IS_LAST_CLUST(curclust, mydata->fatsize)) { 708 clear_fatent(mydata, curclust); 709 set_start_cluster(mydata, dentptr, 0); 710 return 0; 711 } 712 debug("curclust: 0x%x\n", curclust); 713 debug("Invalid FAT entry\n"); 714 return -1; 715 } 716 717 if (!curclust) { 718 assert(pos == 0); 719 goto set_clusters; 720 } 721 722 /* go to cluster at pos */ 723 cur_pos = bytesperclust; 724 while (1) { 725 if (pos <= cur_pos) 726 break; 727 if (IS_LAST_CLUST(curclust, mydata->fatsize)) 728 break; 729 730 newclust = get_fatent(mydata, curclust); 731 if (!IS_LAST_CLUST(newclust, mydata->fatsize) && 732 CHECK_CLUST(newclust, mydata->fatsize)) { 733 debug("curclust: 0x%x\n", curclust); 734 debug("Invalid FAT entry\n"); 735 return -1; 736 } 737 738 cur_pos += bytesperclust; 739 curclust = newclust; 740 } 741 if (IS_LAST_CLUST(curclust, mydata->fatsize)) { 742 assert(pos == cur_pos); 743 goto set_clusters; 744 } 745 746 assert(pos < cur_pos); 747 cur_pos -= bytesperclust; 748 749 /* overwrite */ 750 assert(IS_LAST_CLUST(curclust, mydata->fatsize) || 751 !CHECK_CLUST(curclust, mydata->fatsize)); 752 753 while (1) { 754 /* search for allocated consecutive clusters */ 755 actsize = bytesperclust; 756 endclust = curclust; 757 while (1) { 758 if (filesize <= (cur_pos + actsize)) 759 break; 760 761 newclust = get_fatent(mydata, endclust); 762 763 if (IS_LAST_CLUST(newclust, mydata->fatsize)) 764 break; 765 if (CHECK_CLUST(newclust, mydata->fatsize)) { 766 debug("curclust: 0x%x\n", curclust); 767 debug("Invalid FAT entry\n"); 768 return -1; 769 } 770 771 actsize += bytesperclust; 772 endclust = newclust; 773 } 774 775 /* overwrite to <curclust..endclust> */ 776 if (pos < cur_pos) 777 offset = 0; 778 else 779 offset = pos - cur_pos; 780 wsize = min(cur_pos + actsize, filesize) - pos; 781 if (get_set_cluster(mydata, curclust, offset, 782 buffer, wsize, &actsize)) { 783 printf("Error get-and-setting cluster\n"); 784 return -1; 785 } 786 buffer += wsize; 787 *gotsize += wsize; 788 cur_pos += offset + wsize; 789 790 if (filesize <= cur_pos) 791 break; 792 793 /* CHECK: newclust = get_fatent(mydata, endclust); */ 794 795 if (IS_LAST_CLUST(newclust, mydata->fatsize)) 796 /* no more clusters */ 797 break; 798 799 curclust = newclust; 800 } 801 802 if (filesize <= cur_pos) { 803 /* no more write */ 804 newclust = get_fatent(mydata, endclust); 805 if (!IS_LAST_CLUST(newclust, mydata->fatsize)) { 806 /* truncate the rest */ 807 clear_fatent(mydata, newclust); 808 809 /* Mark end of file in FAT */ 810 if (mydata->fatsize == 12) 811 newclust = 0xfff; 812 else if (mydata->fatsize == 16) 813 newclust = 0xffff; 814 else if (mydata->fatsize == 32) 815 newclust = 0xfffffff; 816 set_fatent_value(mydata, endclust, newclust); 817 } 818 819 return 0; 820 } 821 822 curclust = endclust; 823 filesize -= cur_pos; 824 assert(!(cur_pos % bytesperclust)); 825 826 set_clusters: 827 /* allocate and write */ 828 assert(!pos); 829 830 /* Assure that curclust is valid */ 831 if (!curclust) { 832 curclust = find_empty_cluster(mydata); 833 set_start_cluster(mydata, dentptr, curclust); 834 } else { 835 newclust = get_fatent(mydata, curclust); 836 837 if (IS_LAST_CLUST(newclust, mydata->fatsize)) { 838 newclust = determine_fatent(mydata, curclust); 839 set_fatent_value(mydata, curclust, newclust); 840 curclust = newclust; 841 } else { 842 debug("error: something wrong\n"); 843 return -1; 844 } 845 } 846 847 /* TODO: already partially written */ 848 if (check_overflow(mydata, curclust, filesize)) { 849 printf("Error: no space left: %llu\n", filesize); 850 return -1; 851 } 852 853 actsize = bytesperclust; 854 endclust = curclust; 855 do { 856 /* search for consecutive clusters */ 857 while (actsize < filesize) { 858 newclust = determine_fatent(mydata, endclust); 859 860 if ((newclust - 1) != endclust) 861 /* write to <curclust..endclust> */ 862 goto getit; 863 864 if (CHECK_CLUST(newclust, mydata->fatsize)) { 865 debug("newclust: 0x%x\n", newclust); 866 debug("Invalid FAT entry\n"); 867 return 0; 868 } 869 endclust = newclust; 870 actsize += bytesperclust; 871 } 872 873 /* set remaining bytes */ 874 actsize = filesize; 875 if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 876 debug("error: writing cluster\n"); 877 return -1; 878 } 879 *gotsize += actsize; 880 881 /* Mark end of file in FAT */ 882 if (mydata->fatsize == 12) 883 newclust = 0xfff; 884 else if (mydata->fatsize == 16) 885 newclust = 0xffff; 886 else if (mydata->fatsize == 32) 887 newclust = 0xfffffff; 888 set_fatent_value(mydata, endclust, newclust); 889 890 return 0; 891 getit: 892 if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 893 debug("error: writing cluster\n"); 894 return -1; 895 } 896 *gotsize += actsize; 897 filesize -= actsize; 898 buffer += actsize; 899 900 if (CHECK_CLUST(newclust, mydata->fatsize)) { 901 debug("newclust: 0x%x\n", newclust); 902 debug("Invalid FAT entry\n"); 903 return 0; 904 } 905 actsize = bytesperclust; 906 curclust = endclust = newclust; 907 } while (1); 908 909 return 0; 910 } 911 912 /* 913 * Fill dir_entry 914 */ 915 static void fill_dentry(fsdata *mydata, dir_entry *dentptr, 916 const char *filename, __u32 start_cluster, __u32 size, __u8 attr) 917 { 918 set_start_cluster(mydata, dentptr, start_cluster); 919 dentptr->size = cpu_to_le32(size); 920 921 dentptr->attr = attr; 922 923 set_name(dentptr, filename); 924 } 925 926 /* 927 * Find a directory entry based on filename or start cluster number 928 * If the directory entry is not found, 929 * the new position for writing a directory entry will be returned 930 */ 931 static dir_entry *find_directory_entry(fat_itr *itr, char *filename) 932 { 933 int match = 0; 934 935 while (fat_itr_next(itr)) { 936 /* check both long and short name: */ 937 if (!strcasecmp(filename, itr->name)) 938 match = 1; 939 else if (itr->name != itr->s_name && 940 !strcasecmp(filename, itr->s_name)) 941 match = 1; 942 943 if (!match) 944 continue; 945 946 if (itr->dent->name[0] == '\0') 947 return NULL; 948 else 949 return itr->dent; 950 } 951 952 if (!itr->dent && !itr->is_root && flush_dir_table(itr)) 953 /* indicate that allocating dent failed */ 954 itr->dent = NULL; 955 956 return NULL; 957 } 958 959 static int split_filename(char *filename, char **dirname, char **basename) 960 { 961 char *p, *last_slash, *last_slash_cont; 962 963 again: 964 p = filename; 965 last_slash = NULL; 966 last_slash_cont = NULL; 967 while (*p) { 968 if (ISDIRDELIM(*p)) { 969 last_slash = p; 970 last_slash_cont = p; 971 /* continuous slashes */ 972 while (ISDIRDELIM(*p)) 973 last_slash_cont = p++; 974 if (!*p) 975 break; 976 } 977 p++; 978 } 979 980 if (last_slash) { 981 if (last_slash_cont == (filename + strlen(filename) - 1)) { 982 /* remove trailing slashes */ 983 *last_slash = '\0'; 984 goto again; 985 } 986 987 if (last_slash == filename) { 988 /* avoid ""(null) directory */ 989 *dirname = "/"; 990 } else { 991 *last_slash = '\0'; 992 *dirname = filename; 993 } 994 995 *last_slash_cont = '\0'; 996 *basename = last_slash_cont + 1; 997 } else { 998 *dirname = "/"; /* root by default */ 999 *basename = filename; 1000 } 1001 1002 return 0; 1003 } 1004 1005 static int normalize_longname(char *l_filename, const char *filename) 1006 { 1007 const char *p, legal[] = "!#$%&\'()-.@^`_{}~"; 1008 char c; 1009 int name_len; 1010 1011 /* Check that the filename is valid */ 1012 for (p = filename; p < filename + strlen(filename); p++) { 1013 c = *p; 1014 1015 if (('0' <= c) && (c <= '9')) 1016 continue; 1017 if (('A' <= c) && (c <= 'Z')) 1018 continue; 1019 if (('a' <= c) && (c <= 'z')) 1020 continue; 1021 if (strchr(legal, c)) 1022 continue; 1023 /* extended code */ 1024 if ((0x80 <= c) && (c <= 0xff)) 1025 continue; 1026 1027 return -1; 1028 } 1029 1030 /* Normalize it */ 1031 name_len = strlen(filename); 1032 if (name_len >= VFAT_MAXLEN_BYTES) 1033 /* should return an error? */ 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, INT_MAX); 1039 1040 return 0; 1041 } 1042 1043 int file_fat_write_at(const char *filename, loff_t pos, void *buffer, 1044 loff_t size, loff_t *actwrite) 1045 { 1046 dir_entry *retdent; 1047 fsdata datablock = { .fatbuf = NULL, }; 1048 fsdata *mydata = &datablock; 1049 fat_itr *itr = NULL; 1050 int ret = -1; 1051 char *filename_copy, *parent, *basename; 1052 char l_filename[VFAT_MAXLEN_BYTES]; 1053 1054 debug("writing %s\n", filename); 1055 1056 filename_copy = strdup(filename); 1057 if (!filename_copy) 1058 return -ENOMEM; 1059 1060 split_filename(filename_copy, &parent, &basename); 1061 if (!strlen(basename)) { 1062 ret = -EINVAL; 1063 goto exit; 1064 } 1065 1066 filename = basename; 1067 if (normalize_longname(l_filename, filename)) { 1068 printf("FAT: illegal filename (%s)\n", filename); 1069 ret = -EINVAL; 1070 goto exit; 1071 } 1072 1073 itr = malloc_cache_aligned(sizeof(fat_itr)); 1074 if (!itr) { 1075 ret = -ENOMEM; 1076 goto exit; 1077 } 1078 1079 ret = fat_itr_root(itr, &datablock); 1080 if (ret) 1081 goto exit; 1082 1083 total_sector = datablock.total_sect; 1084 1085 ret = fat_itr_resolve(itr, parent, TYPE_DIR); 1086 if (ret) { 1087 printf("%s: doesn't exist (%d)\n", parent, ret); 1088 goto exit; 1089 } 1090 1091 retdent = find_directory_entry(itr, l_filename); 1092 1093 if (retdent) { 1094 if (fat_itr_isdir(itr)) { 1095 ret = -EISDIR; 1096 goto exit; 1097 } 1098 1099 /* A file exists */ 1100 if (pos == -1) 1101 /* Append to the end */ 1102 pos = FAT2CPU32(retdent->size); 1103 if (pos > retdent->size) { 1104 /* No hole allowed */ 1105 ret = -EINVAL; 1106 goto exit; 1107 } 1108 1109 /* Update file size in a directory entry */ 1110 retdent->size = cpu_to_le32(pos + size); 1111 } else { 1112 /* Create a new file */ 1113 1114 if (itr->is_root) { 1115 /* root dir cannot have "." or ".." */ 1116 if (!strcmp(l_filename, ".") || 1117 !strcmp(l_filename, "..")) { 1118 ret = -EINVAL; 1119 goto exit; 1120 } 1121 } 1122 1123 if (!itr->dent) { 1124 printf("Error: allocating new dir entry\n"); 1125 ret = -EIO; 1126 goto exit; 1127 } 1128 1129 if (pos) { 1130 /* No hole allowed */ 1131 ret = -EINVAL; 1132 goto exit; 1133 } 1134 1135 memset(itr->dent, 0, sizeof(*itr->dent)); 1136 1137 /* Set short name to set alias checksum field in dir_slot */ 1138 set_name(itr->dent, filename); 1139 if (fill_dir_slot(itr, filename)) { 1140 ret = -EIO; 1141 goto exit; 1142 } 1143 1144 /* Set attribute as archive for regular file */ 1145 fill_dentry(itr->fsdata, itr->dent, filename, 0, size, 0x20); 1146 1147 retdent = itr->dent; 1148 } 1149 1150 ret = set_contents(mydata, retdent, pos, buffer, size, actwrite); 1151 if (ret < 0) { 1152 printf("Error: writing contents\n"); 1153 ret = -EIO; 1154 goto exit; 1155 } 1156 debug("attempt to write 0x%llx bytes\n", *actwrite); 1157 1158 /* Flush fat buffer */ 1159 ret = flush_dirty_fat_buffer(mydata); 1160 if (ret) { 1161 printf("Error: flush fat buffer\n"); 1162 ret = -EIO; 1163 goto exit; 1164 } 1165 1166 /* Write directory table to device */ 1167 ret = set_cluster(mydata, itr->clust, itr->block, 1168 mydata->clust_size * mydata->sect_size); 1169 if (ret) { 1170 printf("Error: writing directory entry\n"); 1171 ret = -EIO; 1172 } 1173 1174 exit: 1175 free(filename_copy); 1176 free(mydata->fatbuf); 1177 free(itr); 1178 return ret; 1179 } 1180 1181 int file_fat_write(const char *filename, void *buffer, loff_t offset, 1182 loff_t maxsize, loff_t *actwrite) 1183 { 1184 return file_fat_write_at(filename, offset, buffer, maxsize, actwrite); 1185 } 1186 1187 static int fat_dir_entries(fat_itr *itr) 1188 { 1189 fat_itr *dirs; 1190 fsdata fsdata = { .fatbuf = NULL, }, *mydata = &fsdata; 1191 /* for FATBUFSIZE */ 1192 int count; 1193 1194 dirs = malloc_cache_aligned(sizeof(fat_itr)); 1195 if (!dirs) { 1196 debug("Error: allocating memory\n"); 1197 count = -ENOMEM; 1198 goto exit; 1199 } 1200 1201 /* duplicate fsdata */ 1202 fat_itr_child(dirs, itr); 1203 fsdata = *dirs->fsdata; 1204 1205 /* allocate local fat buffer */ 1206 fsdata.fatbuf = malloc_cache_aligned(FATBUFSIZE); 1207 if (!fsdata.fatbuf) { 1208 debug("Error: allocating memory\n"); 1209 count = -ENOMEM; 1210 goto exit; 1211 } 1212 fsdata.fatbufnum = -1; 1213 dirs->fsdata = &fsdata; 1214 1215 for (count = 0; fat_itr_next(dirs); count++) 1216 ; 1217 1218 exit: 1219 free(fsdata.fatbuf); 1220 free(dirs); 1221 return count; 1222 } 1223 1224 static int delete_dentry(fat_itr *itr) 1225 { 1226 fsdata *mydata = itr->fsdata; 1227 dir_entry *dentptr = itr->dent; 1228 1229 /* free cluster blocks */ 1230 clear_fatent(mydata, START(dentptr)); 1231 if (flush_dirty_fat_buffer(mydata) < 0) { 1232 printf("Error: flush fat buffer\n"); 1233 return -EIO; 1234 } 1235 1236 /* 1237 * update a directory entry 1238 * TODO: 1239 * - long file name support 1240 * - find and mark the "new" first invalid entry as name[0]=0x00 1241 */ 1242 memset(dentptr, 0, sizeof(*dentptr)); 1243 dentptr->name[0] = 0xe5; 1244 1245 if (set_cluster(mydata, itr->clust, itr->block, 1246 mydata->clust_size * mydata->sect_size) != 0) { 1247 printf("error: writing directory entry\n"); 1248 return -EIO; 1249 } 1250 1251 return 0; 1252 } 1253 1254 int fat_unlink(const char *filename) 1255 { 1256 fsdata fsdata = { .fatbuf = NULL, }; 1257 fat_itr *itr = NULL; 1258 int n_entries, ret; 1259 char *filename_copy, *dirname, *basename; 1260 1261 filename_copy = strdup(filename); 1262 split_filename(filename_copy, &dirname, &basename); 1263 1264 if (!strcmp(dirname, "/") && !strcmp(basename, "")) { 1265 printf("Error: cannot remove root\n"); 1266 ret = -EINVAL; 1267 goto exit; 1268 } 1269 1270 itr = malloc_cache_aligned(sizeof(fat_itr)); 1271 if (!itr) { 1272 printf("Error: allocating memory\n"); 1273 return -ENOMEM; 1274 } 1275 1276 ret = fat_itr_root(itr, &fsdata); 1277 if (ret) 1278 goto exit; 1279 1280 total_sector = fsdata.total_sect; 1281 1282 ret = fat_itr_resolve(itr, dirname, TYPE_DIR); 1283 if (ret) { 1284 printf("%s: doesn't exist (%d)\n", dirname, ret); 1285 ret = -ENOENT; 1286 goto exit; 1287 } 1288 1289 if (!find_directory_entry(itr, basename)) { 1290 printf("%s: doesn't exist\n", basename); 1291 ret = -ENOENT; 1292 goto exit; 1293 } 1294 1295 if (fat_itr_isdir(itr)) { 1296 n_entries = fat_dir_entries(itr); 1297 if (n_entries < 0) { 1298 ret = n_entries; 1299 goto exit; 1300 } 1301 if (n_entries > 2) { 1302 printf("Error: directory is not empty: %d\n", 1303 n_entries); 1304 ret = -EINVAL; 1305 goto exit; 1306 } 1307 } 1308 1309 ret = delete_dentry(itr); 1310 1311 exit: 1312 free(fsdata.fatbuf); 1313 free(itr); 1314 free(filename_copy); 1315 1316 return ret; 1317 } 1318 1319 int fat_mkdir(const char *new_dirname) 1320 { 1321 dir_entry *retdent; 1322 fsdata datablock = { .fatbuf = NULL, }; 1323 fsdata *mydata = &datablock; 1324 fat_itr *itr = NULL; 1325 char *dirname_copy, *parent, *dirname; 1326 char l_dirname[VFAT_MAXLEN_BYTES]; 1327 int ret = -1; 1328 loff_t actwrite; 1329 unsigned int bytesperclust; 1330 dir_entry *dotdent = NULL; 1331 1332 dirname_copy = strdup(new_dirname); 1333 if (!dirname_copy) 1334 goto exit; 1335 1336 split_filename(dirname_copy, &parent, &dirname); 1337 if (!strlen(dirname)) { 1338 ret = -EINVAL; 1339 goto exit; 1340 } 1341 1342 if (normalize_longname(l_dirname, dirname)) { 1343 printf("FAT: illegal filename (%s)\n", dirname); 1344 ret = -EINVAL; 1345 goto exit; 1346 } 1347 1348 itr = malloc_cache_aligned(sizeof(fat_itr)); 1349 if (!itr) { 1350 ret = -ENOMEM; 1351 goto exit; 1352 } 1353 1354 ret = fat_itr_root(itr, &datablock); 1355 if (ret) 1356 goto exit; 1357 1358 total_sector = datablock.total_sect; 1359 1360 ret = fat_itr_resolve(itr, parent, TYPE_DIR); 1361 if (ret) { 1362 printf("%s: doesn't exist (%d)\n", parent, ret); 1363 goto exit; 1364 } 1365 1366 retdent = find_directory_entry(itr, l_dirname); 1367 1368 if (retdent) { 1369 printf("%s: already exists\n", l_dirname); 1370 ret = -EEXIST; 1371 goto exit; 1372 } else { 1373 if (itr->is_root) { 1374 /* root dir cannot have "." or ".." */ 1375 if (!strcmp(l_dirname, ".") || 1376 !strcmp(l_dirname, "..")) { 1377 ret = -EINVAL; 1378 goto exit; 1379 } 1380 } 1381 1382 if (!itr->dent) { 1383 printf("Error: allocating new dir entry\n"); 1384 ret = -EIO; 1385 goto exit; 1386 } 1387 1388 memset(itr->dent, 0, sizeof(*itr->dent)); 1389 1390 /* Set short name to set alias checksum field in dir_slot */ 1391 set_name(itr->dent, dirname); 1392 fill_dir_slot(itr, dirname); 1393 1394 /* Set attribute as archive for regular file */ 1395 fill_dentry(itr->fsdata, itr->dent, dirname, 0, 0, 1396 ATTR_DIR | ATTR_ARCH); 1397 1398 retdent = itr->dent; 1399 } 1400 1401 /* Default entries */ 1402 bytesperclust = mydata->clust_size * mydata->sect_size; 1403 dotdent = malloc_cache_aligned(bytesperclust); 1404 if (!dotdent) { 1405 ret = -ENOMEM; 1406 goto exit; 1407 } 1408 memset(dotdent, 0, bytesperclust); 1409 1410 memcpy(dotdent[0].name, ". ", 8); 1411 memcpy(dotdent[0].ext, " ", 3); 1412 dotdent[0].attr = ATTR_DIR | ATTR_ARCH; 1413 1414 memcpy(dotdent[1].name, ".. ", 8); 1415 memcpy(dotdent[1].ext, " ", 3); 1416 dotdent[1].attr = ATTR_DIR | ATTR_ARCH; 1417 set_start_cluster(mydata, &dotdent[1], itr->start_clust); 1418 1419 ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent, 1420 bytesperclust, &actwrite); 1421 if (ret < 0) { 1422 printf("Error: writing contents\n"); 1423 goto exit; 1424 } 1425 /* Write twice for "." */ 1426 set_start_cluster(mydata, &dotdent[0], START(retdent)); 1427 ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent, 1428 bytesperclust, &actwrite); 1429 if (ret < 0) { 1430 printf("Error: writing contents\n"); 1431 goto exit; 1432 } 1433 1434 /* Flush fat buffer */ 1435 ret = flush_dirty_fat_buffer(mydata); 1436 if (ret) { 1437 printf("Error: flush fat buffer\n"); 1438 goto exit; 1439 } 1440 1441 /* Write directory table to device */ 1442 ret = set_cluster(mydata, itr->clust, itr->block, 1443 mydata->clust_size * mydata->sect_size); 1444 if (ret) 1445 printf("Error: writing directory entry\n"); 1446 1447 exit: 1448 free(dirname_copy); 1449 free(mydata->fatbuf); 1450 free(itr); 1451 free(dotdent); 1452 return ret; 1453 } 1454