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 * set_cluster() - write data to cluster 392 * 393 * Write 'size' bytes from 'buffer' into the specified cluster. 394 * 395 * @mydata: data to be written 396 * @clustnum: cluster to be written to 397 * @buffer: data to be written 398 * @size: bytes to be written (but not more than the size of a cluster) 399 * Return: 0 on success, -1 otherwise 400 */ 401 static int 402 set_cluster(fsdata *mydata, u32 clustnum, u8 *buffer, u32 size) 403 { 404 u32 idx = 0; 405 u32 startsect; 406 int ret; 407 408 if (clustnum > 0) 409 startsect = clust_to_sect(mydata, clustnum); 410 else 411 startsect = mydata->rootdir_sect; 412 413 debug("clustnum: %d, startsect: %d\n", clustnum, startsect); 414 415 if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) { 416 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); 417 418 debug("FAT: Misaligned buffer address (%p)\n", buffer); 419 420 while (size >= mydata->sect_size) { 421 memcpy(tmpbuf, buffer, mydata->sect_size); 422 ret = disk_write(startsect++, 1, tmpbuf); 423 if (ret != 1) { 424 debug("Error writing data (got %d)\n", ret); 425 return -1; 426 } 427 428 buffer += mydata->sect_size; 429 size -= mydata->sect_size; 430 } 431 } else if (size >= mydata->sect_size) { 432 idx = size / mydata->sect_size; 433 ret = disk_write(startsect, idx, buffer); 434 if (ret != idx) { 435 debug("Error writing data (got %d)\n", ret); 436 return -1; 437 } 438 439 startsect += idx; 440 idx *= mydata->sect_size; 441 buffer += idx; 442 size -= idx; 443 } 444 445 if (size) { 446 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); 447 /* Do not leak content of stack */ 448 memset(tmpbuf, 0, mydata->sect_size); 449 memcpy(tmpbuf, buffer, size); 450 ret = disk_write(startsect, 1, tmpbuf); 451 if (ret != 1) { 452 debug("Error writing data (got %d)\n", ret); 453 return -1; 454 } 455 } 456 457 return 0; 458 } 459 460 static __u8 tmpbuf_cluster[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN); 461 462 /* 463 * Read and modify data on existing and consecutive cluster blocks 464 */ 465 static int 466 get_set_cluster(fsdata *mydata, __u32 clustnum, loff_t pos, __u8 *buffer, 467 loff_t size, loff_t *gotsize) 468 { 469 unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; 470 __u32 startsect; 471 loff_t wsize; 472 int clustcount, i, ret; 473 474 *gotsize = 0; 475 if (!size) 476 return 0; 477 478 assert(pos < bytesperclust); 479 startsect = clust_to_sect(mydata, clustnum); 480 481 debug("clustnum: %d, startsect: %d, pos: %lld\n", 482 clustnum, startsect, pos); 483 484 /* partial write at beginning */ 485 if (pos) { 486 wsize = min(bytesperclust - pos, size); 487 ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster); 488 if (ret != mydata->clust_size) { 489 debug("Error reading data (got %d)\n", ret); 490 return -1; 491 } 492 493 memcpy(tmpbuf_cluster + pos, buffer, wsize); 494 ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster); 495 if (ret != mydata->clust_size) { 496 debug("Error writing data (got %d)\n", ret); 497 return -1; 498 } 499 500 size -= wsize; 501 buffer += wsize; 502 *gotsize += wsize; 503 504 startsect += mydata->clust_size; 505 506 if (!size) 507 return 0; 508 } 509 510 /* full-cluster write */ 511 if (size >= bytesperclust) { 512 clustcount = lldiv(size, bytesperclust); 513 514 if (!((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1))) { 515 wsize = clustcount * bytesperclust; 516 ret = disk_write(startsect, 517 clustcount * mydata->clust_size, 518 buffer); 519 if (ret != clustcount * mydata->clust_size) { 520 debug("Error writing data (got %d)\n", ret); 521 return -1; 522 } 523 524 size -= wsize; 525 buffer += wsize; 526 *gotsize += wsize; 527 528 startsect += clustcount * mydata->clust_size; 529 } else { 530 for (i = 0; i < clustcount; i++) { 531 memcpy(tmpbuf_cluster, buffer, bytesperclust); 532 ret = disk_write(startsect, 533 mydata->clust_size, 534 tmpbuf_cluster); 535 if (ret != mydata->clust_size) { 536 debug("Error writing data (got %d)\n", 537 ret); 538 return -1; 539 } 540 541 size -= bytesperclust; 542 buffer += bytesperclust; 543 *gotsize += bytesperclust; 544 545 startsect += mydata->clust_size; 546 } 547 } 548 } 549 550 /* partial write at end */ 551 if (size) { 552 wsize = size; 553 ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster); 554 if (ret != mydata->clust_size) { 555 debug("Error reading data (got %d)\n", ret); 556 return -1; 557 } 558 memcpy(tmpbuf_cluster, buffer, wsize); 559 ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster); 560 if (ret != mydata->clust_size) { 561 debug("Error writing data (got %d)\n", ret); 562 return -1; 563 } 564 565 size -= wsize; 566 buffer += wsize; 567 *gotsize += wsize; 568 } 569 570 assert(!size); 571 572 return 0; 573 } 574 575 /* 576 * Find the first empty cluster 577 */ 578 static int find_empty_cluster(fsdata *mydata) 579 { 580 __u32 fat_val, entry = 3; 581 582 while (1) { 583 fat_val = get_fatent(mydata, entry); 584 if (fat_val == 0) 585 break; 586 entry++; 587 } 588 589 return entry; 590 } 591 592 /* 593 * Write directory entries in itr's buffer to block device 594 */ 595 static int flush_dir_table(fat_itr *itr) 596 { 597 fsdata *mydata = itr->fsdata; 598 int dir_newclust = 0; 599 unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; 600 601 if (set_cluster(mydata, itr->clust, itr->block, bytesperclust) != 0) { 602 printf("error: writing directory entry\n"); 603 return -1; 604 } 605 dir_newclust = find_empty_cluster(mydata); 606 set_fatent_value(mydata, itr->clust, dir_newclust); 607 if (mydata->fatsize == 32) 608 set_fatent_value(mydata, dir_newclust, 0xffffff8); 609 else if (mydata->fatsize == 16) 610 set_fatent_value(mydata, dir_newclust, 0xfff8); 611 else if (mydata->fatsize == 12) 612 set_fatent_value(mydata, dir_newclust, 0xff8); 613 614 itr->clust = dir_newclust; 615 itr->next_clust = dir_newclust; 616 617 if (flush_dirty_fat_buffer(mydata) < 0) 618 return -1; 619 620 memset(itr->block, 0x00, bytesperclust); 621 622 itr->dent = (dir_entry *)itr->block; 623 itr->last_cluster = 1; 624 itr->remaining = bytesperclust / sizeof(dir_entry) - 1; 625 626 return 0; 627 } 628 629 /* 630 * Set empty cluster from 'entry' to the end of a file 631 */ 632 static int clear_fatent(fsdata *mydata, __u32 entry) 633 { 634 __u32 fat_val; 635 636 while (!CHECK_CLUST(entry, mydata->fatsize)) { 637 fat_val = get_fatent(mydata, entry); 638 if (fat_val != 0) 639 set_fatent_value(mydata, entry, 0); 640 else 641 break; 642 643 entry = fat_val; 644 } 645 646 /* Flush fat buffer */ 647 if (flush_dirty_fat_buffer(mydata) < 0) 648 return -1; 649 650 return 0; 651 } 652 653 /* 654 * Set start cluster in directory entry 655 */ 656 static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr, 657 __u32 start_cluster) 658 { 659 if (mydata->fatsize == 32) 660 dentptr->starthi = 661 cpu_to_le16((start_cluster & 0xffff0000) >> 16); 662 dentptr->start = cpu_to_le16(start_cluster & 0xffff); 663 } 664 665 /* 666 * Check whether adding a file makes the file system to 667 * exceed the size of the block device 668 * Return -1 when overflow occurs, otherwise return 0 669 */ 670 static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size) 671 { 672 __u32 startsect, sect_num, offset; 673 674 if (clustnum > 0) 675 startsect = clust_to_sect(mydata, clustnum); 676 else 677 startsect = mydata->rootdir_sect; 678 679 sect_num = div_u64_rem(size, mydata->sect_size, &offset); 680 681 if (offset != 0) 682 sect_num++; 683 684 if (startsect + sect_num > total_sector) 685 return -1; 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, loff_t pos, __u8 *buffer, 697 loff_t maxsize, loff_t *gotsize) 698 { 699 loff_t filesize; 700 unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; 701 __u32 curclust = START(dentptr); 702 __u32 endclust = 0, newclust = 0; 703 loff_t cur_pos, offset, actsize, wsize; 704 705 *gotsize = 0; 706 filesize = pos + maxsize; 707 708 debug("%llu bytes\n", filesize); 709 710 if (!filesize) { 711 if (!curclust) 712 return 0; 713 if (!CHECK_CLUST(curclust, mydata->fatsize) || 714 IS_LAST_CLUST(curclust, mydata->fatsize)) { 715 clear_fatent(mydata, curclust); 716 set_start_cluster(mydata, dentptr, 0); 717 return 0; 718 } 719 debug("curclust: 0x%x\n", curclust); 720 debug("Invalid FAT entry\n"); 721 return -1; 722 } 723 724 if (!curclust) { 725 assert(pos == 0); 726 goto set_clusters; 727 } 728 729 /* go to cluster at pos */ 730 cur_pos = bytesperclust; 731 while (1) { 732 if (pos <= cur_pos) 733 break; 734 if (IS_LAST_CLUST(curclust, mydata->fatsize)) 735 break; 736 737 newclust = get_fatent(mydata, curclust); 738 if (!IS_LAST_CLUST(newclust, mydata->fatsize) && 739 CHECK_CLUST(newclust, mydata->fatsize)) { 740 debug("curclust: 0x%x\n", curclust); 741 debug("Invalid FAT entry\n"); 742 return -1; 743 } 744 745 cur_pos += bytesperclust; 746 curclust = newclust; 747 } 748 if (IS_LAST_CLUST(curclust, mydata->fatsize)) { 749 assert(pos == cur_pos); 750 goto set_clusters; 751 } 752 753 assert(pos < cur_pos); 754 cur_pos -= bytesperclust; 755 756 /* overwrite */ 757 assert(IS_LAST_CLUST(curclust, mydata->fatsize) || 758 !CHECK_CLUST(curclust, mydata->fatsize)); 759 760 while (1) { 761 /* search for allocated consecutive clusters */ 762 actsize = bytesperclust; 763 endclust = curclust; 764 while (1) { 765 if (filesize <= (cur_pos + actsize)) 766 break; 767 768 newclust = get_fatent(mydata, endclust); 769 770 if (IS_LAST_CLUST(newclust, mydata->fatsize)) 771 break; 772 if (CHECK_CLUST(newclust, mydata->fatsize)) { 773 debug("curclust: 0x%x\n", curclust); 774 debug("Invalid FAT entry\n"); 775 return -1; 776 } 777 778 actsize += bytesperclust; 779 endclust = newclust; 780 } 781 782 /* overwrite to <curclust..endclust> */ 783 if (pos < cur_pos) 784 offset = 0; 785 else 786 offset = pos - cur_pos; 787 wsize = min(cur_pos + actsize, filesize) - pos; 788 if (get_set_cluster(mydata, curclust, offset, 789 buffer, wsize, &actsize)) { 790 printf("Error get-and-setting cluster\n"); 791 return -1; 792 } 793 buffer += wsize; 794 *gotsize += wsize; 795 cur_pos += offset + wsize; 796 797 if (filesize <= cur_pos) 798 break; 799 800 /* CHECK: newclust = get_fatent(mydata, endclust); */ 801 802 if (IS_LAST_CLUST(newclust, mydata->fatsize)) 803 /* no more clusters */ 804 break; 805 806 curclust = newclust; 807 } 808 809 if (filesize <= cur_pos) { 810 /* no more write */ 811 newclust = get_fatent(mydata, endclust); 812 if (!IS_LAST_CLUST(newclust, mydata->fatsize)) { 813 /* truncate the rest */ 814 clear_fatent(mydata, newclust); 815 816 /* Mark end of file in FAT */ 817 if (mydata->fatsize == 12) 818 newclust = 0xfff; 819 else if (mydata->fatsize == 16) 820 newclust = 0xffff; 821 else if (mydata->fatsize == 32) 822 newclust = 0xfffffff; 823 set_fatent_value(mydata, endclust, newclust); 824 } 825 826 return 0; 827 } 828 829 curclust = endclust; 830 filesize -= cur_pos; 831 assert(!(cur_pos % bytesperclust)); 832 833 set_clusters: 834 /* allocate and write */ 835 assert(!pos); 836 837 /* Assure that curclust is valid */ 838 if (!curclust) { 839 curclust = find_empty_cluster(mydata); 840 set_start_cluster(mydata, dentptr, curclust); 841 } else { 842 newclust = get_fatent(mydata, curclust); 843 844 if (IS_LAST_CLUST(newclust, mydata->fatsize)) { 845 newclust = determine_fatent(mydata, curclust); 846 set_fatent_value(mydata, curclust, newclust); 847 curclust = newclust; 848 } else { 849 debug("error: something wrong\n"); 850 return -1; 851 } 852 } 853 854 /* TODO: already partially written */ 855 if (check_overflow(mydata, curclust, filesize)) { 856 printf("Error: no space left: %llu\n", filesize); 857 return -1; 858 } 859 860 actsize = bytesperclust; 861 endclust = curclust; 862 do { 863 /* search for consecutive clusters */ 864 while (actsize < filesize) { 865 newclust = determine_fatent(mydata, endclust); 866 867 if ((newclust - 1) != endclust) 868 /* write to <curclust..endclust> */ 869 goto getit; 870 871 if (CHECK_CLUST(newclust, mydata->fatsize)) { 872 debug("newclust: 0x%x\n", newclust); 873 debug("Invalid FAT entry\n"); 874 return 0; 875 } 876 endclust = newclust; 877 actsize += bytesperclust; 878 } 879 880 /* set remaining bytes */ 881 actsize = filesize; 882 if (set_cluster(mydata, curclust, buffer, (u32)actsize) != 0) { 883 debug("error: writing cluster\n"); 884 return -1; 885 } 886 *gotsize += actsize; 887 888 /* Mark end of file in FAT */ 889 if (mydata->fatsize == 12) 890 newclust = 0xfff; 891 else if (mydata->fatsize == 16) 892 newclust = 0xffff; 893 else if (mydata->fatsize == 32) 894 newclust = 0xfffffff; 895 set_fatent_value(mydata, endclust, newclust); 896 897 return 0; 898 getit: 899 if (set_cluster(mydata, curclust, buffer, (u32)actsize) != 0) { 900 debug("error: writing cluster\n"); 901 return -1; 902 } 903 *gotsize += actsize; 904 filesize -= actsize; 905 buffer += actsize; 906 907 if (CHECK_CLUST(newclust, mydata->fatsize)) { 908 debug("newclust: 0x%x\n", newclust); 909 debug("Invalid FAT entry\n"); 910 return 0; 911 } 912 actsize = bytesperclust; 913 curclust = endclust = newclust; 914 } while (1); 915 916 return 0; 917 } 918 919 /* 920 * Fill dir_entry 921 */ 922 static void fill_dentry(fsdata *mydata, dir_entry *dentptr, 923 const char *filename, __u32 start_cluster, __u32 size, __u8 attr) 924 { 925 set_start_cluster(mydata, dentptr, start_cluster); 926 dentptr->size = cpu_to_le32(size); 927 928 dentptr->attr = attr; 929 930 set_name(dentptr, filename); 931 } 932 933 /* 934 * Find a directory entry based on filename or start cluster number 935 * If the directory entry is not found, 936 * the new position for writing a directory entry will be returned 937 */ 938 static dir_entry *find_directory_entry(fat_itr *itr, char *filename) 939 { 940 int match = 0; 941 942 while (fat_itr_next(itr)) { 943 /* check both long and short name: */ 944 if (!strcasecmp(filename, itr->name)) 945 match = 1; 946 else if (itr->name != itr->s_name && 947 !strcasecmp(filename, itr->s_name)) 948 match = 1; 949 950 if (!match) 951 continue; 952 953 if (itr->dent->name[0] == '\0') 954 return NULL; 955 else 956 return itr->dent; 957 } 958 959 if (!itr->dent && !itr->is_root && flush_dir_table(itr)) 960 /* indicate that allocating dent failed */ 961 itr->dent = NULL; 962 963 return NULL; 964 } 965 966 static int split_filename(char *filename, char **dirname, char **basename) 967 { 968 char *p, *last_slash, *last_slash_cont; 969 970 again: 971 p = filename; 972 last_slash = NULL; 973 last_slash_cont = NULL; 974 while (*p) { 975 if (ISDIRDELIM(*p)) { 976 last_slash = p; 977 last_slash_cont = p; 978 /* continuous slashes */ 979 while (ISDIRDELIM(*p)) 980 last_slash_cont = p++; 981 if (!*p) 982 break; 983 } 984 p++; 985 } 986 987 if (last_slash) { 988 if (last_slash_cont == (filename + strlen(filename) - 1)) { 989 /* remove trailing slashes */ 990 *last_slash = '\0'; 991 goto again; 992 } 993 994 if (last_slash == filename) { 995 /* avoid ""(null) directory */ 996 *dirname = "/"; 997 } else { 998 *last_slash = '\0'; 999 *dirname = filename; 1000 } 1001 1002 *last_slash_cont = '\0'; 1003 *basename = last_slash_cont + 1; 1004 } else { 1005 *dirname = "/"; /* root by default */ 1006 *basename = filename; 1007 } 1008 1009 return 0; 1010 } 1011 1012 static int normalize_longname(char *l_filename, const char *filename) 1013 { 1014 const char *p, legal[] = "!#$%&\'()-.@^`_{}~"; 1015 char c; 1016 int name_len; 1017 1018 /* Check that the filename is valid */ 1019 for (p = filename; p < filename + strlen(filename); p++) { 1020 c = *p; 1021 1022 if (('0' <= c) && (c <= '9')) 1023 continue; 1024 if (('A' <= c) && (c <= 'Z')) 1025 continue; 1026 if (('a' <= c) && (c <= 'z')) 1027 continue; 1028 if (strchr(legal, c)) 1029 continue; 1030 /* extended code */ 1031 if ((0x80 <= c) && (c <= 0xff)) 1032 continue; 1033 1034 return -1; 1035 } 1036 1037 /* Normalize it */ 1038 name_len = strlen(filename); 1039 if (name_len >= VFAT_MAXLEN_BYTES) 1040 /* should return an error? */ 1041 name_len = VFAT_MAXLEN_BYTES - 1; 1042 1043 memcpy(l_filename, filename, name_len); 1044 l_filename[name_len] = 0; /* terminate the string */ 1045 downcase(l_filename, INT_MAX); 1046 1047 return 0; 1048 } 1049 1050 int file_fat_write_at(const char *filename, loff_t pos, void *buffer, 1051 loff_t size, loff_t *actwrite) 1052 { 1053 dir_entry *retdent; 1054 fsdata datablock = { .fatbuf = NULL, }; 1055 fsdata *mydata = &datablock; 1056 fat_itr *itr = NULL; 1057 int ret = -1; 1058 char *filename_copy, *parent, *basename; 1059 char l_filename[VFAT_MAXLEN_BYTES]; 1060 1061 debug("writing %s\n", filename); 1062 1063 filename_copy = strdup(filename); 1064 if (!filename_copy) 1065 return -ENOMEM; 1066 1067 split_filename(filename_copy, &parent, &basename); 1068 if (!strlen(basename)) { 1069 ret = -EINVAL; 1070 goto exit; 1071 } 1072 1073 filename = basename; 1074 if (normalize_longname(l_filename, filename)) { 1075 printf("FAT: illegal filename (%s)\n", filename); 1076 ret = -EINVAL; 1077 goto exit; 1078 } 1079 1080 itr = malloc_cache_aligned(sizeof(fat_itr)); 1081 if (!itr) { 1082 ret = -ENOMEM; 1083 goto exit; 1084 } 1085 1086 ret = fat_itr_root(itr, &datablock); 1087 if (ret) 1088 goto exit; 1089 1090 total_sector = datablock.total_sect; 1091 1092 ret = fat_itr_resolve(itr, parent, TYPE_DIR); 1093 if (ret) { 1094 printf("%s: doesn't exist (%d)\n", parent, ret); 1095 goto exit; 1096 } 1097 1098 retdent = find_directory_entry(itr, l_filename); 1099 1100 if (retdent) { 1101 if (fat_itr_isdir(itr)) { 1102 ret = -EISDIR; 1103 goto exit; 1104 } 1105 1106 /* A file exists */ 1107 if (pos == -1) 1108 /* Append to the end */ 1109 pos = FAT2CPU32(retdent->size); 1110 if (pos > retdent->size) { 1111 /* No hole allowed */ 1112 ret = -EINVAL; 1113 goto exit; 1114 } 1115 1116 /* Update file size in a directory entry */ 1117 retdent->size = cpu_to_le32(pos + size); 1118 } else { 1119 /* Create a new file */ 1120 1121 if (itr->is_root) { 1122 /* root dir cannot have "." or ".." */ 1123 if (!strcmp(l_filename, ".") || 1124 !strcmp(l_filename, "..")) { 1125 ret = -EINVAL; 1126 goto exit; 1127 } 1128 } 1129 1130 if (!itr->dent) { 1131 printf("Error: allocating new dir entry\n"); 1132 ret = -EIO; 1133 goto exit; 1134 } 1135 1136 if (pos) { 1137 /* No hole allowed */ 1138 ret = -EINVAL; 1139 goto exit; 1140 } 1141 1142 memset(itr->dent, 0, sizeof(*itr->dent)); 1143 1144 /* Set short name to set alias checksum field in dir_slot */ 1145 set_name(itr->dent, filename); 1146 if (fill_dir_slot(itr, filename)) { 1147 ret = -EIO; 1148 goto exit; 1149 } 1150 1151 /* Set attribute as archive for regular file */ 1152 fill_dentry(itr->fsdata, itr->dent, filename, 0, size, 0x20); 1153 1154 retdent = itr->dent; 1155 } 1156 1157 ret = set_contents(mydata, retdent, pos, buffer, size, actwrite); 1158 if (ret < 0) { 1159 printf("Error: writing contents\n"); 1160 ret = -EIO; 1161 goto exit; 1162 } 1163 debug("attempt to write 0x%llx bytes\n", *actwrite); 1164 1165 /* Flush fat buffer */ 1166 ret = flush_dirty_fat_buffer(mydata); 1167 if (ret) { 1168 printf("Error: flush fat buffer\n"); 1169 ret = -EIO; 1170 goto exit; 1171 } 1172 1173 /* Write directory table to device */ 1174 ret = set_cluster(mydata, itr->clust, itr->block, 1175 mydata->clust_size * mydata->sect_size); 1176 if (ret) { 1177 printf("Error: writing directory entry\n"); 1178 ret = -EIO; 1179 } 1180 1181 exit: 1182 free(filename_copy); 1183 free(mydata->fatbuf); 1184 free(itr); 1185 return ret; 1186 } 1187 1188 int file_fat_write(const char *filename, void *buffer, loff_t offset, 1189 loff_t maxsize, loff_t *actwrite) 1190 { 1191 return file_fat_write_at(filename, offset, buffer, maxsize, actwrite); 1192 } 1193 1194 static int fat_dir_entries(fat_itr *itr) 1195 { 1196 fat_itr *dirs; 1197 fsdata fsdata = { .fatbuf = NULL, }, *mydata = &fsdata; 1198 /* for FATBUFSIZE */ 1199 int count; 1200 1201 dirs = malloc_cache_aligned(sizeof(fat_itr)); 1202 if (!dirs) { 1203 debug("Error: allocating memory\n"); 1204 count = -ENOMEM; 1205 goto exit; 1206 } 1207 1208 /* duplicate fsdata */ 1209 fat_itr_child(dirs, itr); 1210 fsdata = *dirs->fsdata; 1211 1212 /* allocate local fat buffer */ 1213 fsdata.fatbuf = malloc_cache_aligned(FATBUFSIZE); 1214 if (!fsdata.fatbuf) { 1215 debug("Error: allocating memory\n"); 1216 count = -ENOMEM; 1217 goto exit; 1218 } 1219 fsdata.fatbufnum = -1; 1220 dirs->fsdata = &fsdata; 1221 1222 for (count = 0; fat_itr_next(dirs); count++) 1223 ; 1224 1225 exit: 1226 free(fsdata.fatbuf); 1227 free(dirs); 1228 return count; 1229 } 1230 1231 static int delete_dentry(fat_itr *itr) 1232 { 1233 fsdata *mydata = itr->fsdata; 1234 dir_entry *dentptr = itr->dent; 1235 1236 /* free cluster blocks */ 1237 clear_fatent(mydata, START(dentptr)); 1238 if (flush_dirty_fat_buffer(mydata) < 0) { 1239 printf("Error: flush fat buffer\n"); 1240 return -EIO; 1241 } 1242 1243 /* 1244 * update a directory entry 1245 * TODO: 1246 * - long file name support 1247 * - find and mark the "new" first invalid entry as name[0]=0x00 1248 */ 1249 memset(dentptr, 0, sizeof(*dentptr)); 1250 dentptr->name[0] = 0xe5; 1251 1252 if (set_cluster(mydata, itr->clust, itr->block, 1253 mydata->clust_size * mydata->sect_size) != 0) { 1254 printf("error: writing directory entry\n"); 1255 return -EIO; 1256 } 1257 1258 return 0; 1259 } 1260 1261 int fat_unlink(const char *filename) 1262 { 1263 fsdata fsdata = { .fatbuf = NULL, }; 1264 fat_itr *itr = NULL; 1265 int n_entries, ret; 1266 char *filename_copy, *dirname, *basename; 1267 1268 filename_copy = strdup(filename); 1269 if (!filename_copy) { 1270 printf("Error: allocating memory\n"); 1271 ret = -ENOMEM; 1272 goto exit; 1273 } 1274 split_filename(filename_copy, &dirname, &basename); 1275 1276 if (!strcmp(dirname, "/") && !strcmp(basename, "")) { 1277 printf("Error: cannot remove root\n"); 1278 ret = -EINVAL; 1279 goto exit; 1280 } 1281 1282 itr = malloc_cache_aligned(sizeof(fat_itr)); 1283 if (!itr) { 1284 printf("Error: allocating memory\n"); 1285 ret = -ENOMEM; 1286 goto exit; 1287 } 1288 1289 ret = fat_itr_root(itr, &fsdata); 1290 if (ret) 1291 goto exit; 1292 1293 total_sector = fsdata.total_sect; 1294 1295 ret = fat_itr_resolve(itr, dirname, TYPE_DIR); 1296 if (ret) { 1297 printf("%s: doesn't exist (%d)\n", dirname, ret); 1298 ret = -ENOENT; 1299 goto exit; 1300 } 1301 1302 if (!find_directory_entry(itr, basename)) { 1303 printf("%s: doesn't exist\n", basename); 1304 ret = -ENOENT; 1305 goto exit; 1306 } 1307 1308 if (fat_itr_isdir(itr)) { 1309 n_entries = fat_dir_entries(itr); 1310 if (n_entries < 0) { 1311 ret = n_entries; 1312 goto exit; 1313 } 1314 if (n_entries > 2) { 1315 printf("Error: directory is not empty: %d\n", 1316 n_entries); 1317 ret = -EINVAL; 1318 goto exit; 1319 } 1320 } 1321 1322 ret = delete_dentry(itr); 1323 1324 exit: 1325 free(fsdata.fatbuf); 1326 free(itr); 1327 free(filename_copy); 1328 1329 return ret; 1330 } 1331 1332 int fat_mkdir(const char *new_dirname) 1333 { 1334 dir_entry *retdent; 1335 fsdata datablock = { .fatbuf = NULL, }; 1336 fsdata *mydata = &datablock; 1337 fat_itr *itr = NULL; 1338 char *dirname_copy, *parent, *dirname; 1339 char l_dirname[VFAT_MAXLEN_BYTES]; 1340 int ret = -1; 1341 loff_t actwrite; 1342 unsigned int bytesperclust; 1343 dir_entry *dotdent = NULL; 1344 1345 dirname_copy = strdup(new_dirname); 1346 if (!dirname_copy) 1347 goto exit; 1348 1349 split_filename(dirname_copy, &parent, &dirname); 1350 if (!strlen(dirname)) { 1351 ret = -EINVAL; 1352 goto exit; 1353 } 1354 1355 if (normalize_longname(l_dirname, dirname)) { 1356 printf("FAT: illegal filename (%s)\n", dirname); 1357 ret = -EINVAL; 1358 goto exit; 1359 } 1360 1361 itr = malloc_cache_aligned(sizeof(fat_itr)); 1362 if (!itr) { 1363 ret = -ENOMEM; 1364 goto exit; 1365 } 1366 1367 ret = fat_itr_root(itr, &datablock); 1368 if (ret) 1369 goto exit; 1370 1371 total_sector = datablock.total_sect; 1372 1373 ret = fat_itr_resolve(itr, parent, TYPE_DIR); 1374 if (ret) { 1375 printf("%s: doesn't exist (%d)\n", parent, ret); 1376 goto exit; 1377 } 1378 1379 retdent = find_directory_entry(itr, l_dirname); 1380 1381 if (retdent) { 1382 printf("%s: already exists\n", l_dirname); 1383 ret = -EEXIST; 1384 goto exit; 1385 } else { 1386 if (itr->is_root) { 1387 /* root dir cannot have "." or ".." */ 1388 if (!strcmp(l_dirname, ".") || 1389 !strcmp(l_dirname, "..")) { 1390 ret = -EINVAL; 1391 goto exit; 1392 } 1393 } 1394 1395 if (!itr->dent) { 1396 printf("Error: allocating new dir entry\n"); 1397 ret = -EIO; 1398 goto exit; 1399 } 1400 1401 memset(itr->dent, 0, sizeof(*itr->dent)); 1402 1403 /* Set short name to set alias checksum field in dir_slot */ 1404 set_name(itr->dent, dirname); 1405 fill_dir_slot(itr, dirname); 1406 1407 /* Set attribute as archive for regular file */ 1408 fill_dentry(itr->fsdata, itr->dent, dirname, 0, 0, 1409 ATTR_DIR | ATTR_ARCH); 1410 1411 retdent = itr->dent; 1412 } 1413 1414 /* Default entries */ 1415 bytesperclust = mydata->clust_size * mydata->sect_size; 1416 dotdent = malloc_cache_aligned(bytesperclust); 1417 if (!dotdent) { 1418 ret = -ENOMEM; 1419 goto exit; 1420 } 1421 memset(dotdent, 0, bytesperclust); 1422 1423 memcpy(dotdent[0].name, ". ", 8); 1424 memcpy(dotdent[0].ext, " ", 3); 1425 dotdent[0].attr = ATTR_DIR | ATTR_ARCH; 1426 1427 memcpy(dotdent[1].name, ".. ", 8); 1428 memcpy(dotdent[1].ext, " ", 3); 1429 dotdent[1].attr = ATTR_DIR | ATTR_ARCH; 1430 set_start_cluster(mydata, &dotdent[1], itr->start_clust); 1431 1432 ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent, 1433 bytesperclust, &actwrite); 1434 if (ret < 0) { 1435 printf("Error: writing contents\n"); 1436 goto exit; 1437 } 1438 /* Write twice for "." */ 1439 set_start_cluster(mydata, &dotdent[0], START(retdent)); 1440 ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent, 1441 bytesperclust, &actwrite); 1442 if (ret < 0) { 1443 printf("Error: writing contents\n"); 1444 goto exit; 1445 } 1446 1447 /* Flush fat buffer */ 1448 ret = flush_dirty_fat_buffer(mydata); 1449 if (ret) { 1450 printf("Error: flush fat buffer\n"); 1451 goto exit; 1452 } 1453 1454 /* Write directory table to device */ 1455 ret = set_cluster(mydata, itr->clust, itr->block, 1456 mydata->clust_size * mydata->sect_size); 1457 if (ret) 1458 printf("Error: writing directory entry\n"); 1459 1460 exit: 1461 free(dirname_copy); 1462 free(mydata->fatbuf); 1463 free(itr); 1464 free(dotdent); 1465 return ret; 1466 } 1467