| 245 | | sector = f->first_FAT_sector; |
| 246 | | if (f->maximum_cluster_number < RESERVED_CLUSTER_12_BIT) { |
| 247 | | unsigned byte_index = cluster + (cluster>>1); |
| 248 | | uint8_t *p; |
| 249 | | read_buffer(f, sector + byte_index/SECTOR_SIZE); |
| 250 | | p = f->buffer.buf + byte_index%SECTOR_SIZE; |
| 251 | | *p = (cluster&1) ? (*p & 0x0F) | (x<<4) : x; |
| 252 | | f->buffer_status = DIRTY; |
| 253 | | read_buffer(f, sector + (byte_index+1)/SECTOR_SIZE); |
| 254 | | p = f->buffer.buf + (byte_index+1)%SECTOR_SIZE; |
| 255 | | *p = (cluster&1) ? x>>4 : (*p&0xF0) | (x>>8); |
| 256 | | } else { |
| 257 | | read_buffer(f, sector + cluster/(SECTOR_SIZE/2)); |
| 258 | | f->buffer.fat[cluster%(SECTOR_SIZE/2)] = x; |
| 259 | | } |
| 260 | | f->buffer_status = DIRTY; |
| | 251 | sector = f->first_FAT_sector; |
| | 252 | if (f->maximum_cluster_number < RESERVED_CLUSTER_12_BIT) { |
| | 253 | unsigned byte_index = cluster + (cluster >> 1); |
| | 254 | uint8_t *p; |
| | 255 | read_buffer (f, sector + byte_index / SECTOR_SIZE); |
| | 256 | p = f->buffer.buf + byte_index % SECTOR_SIZE; |
| | 257 | *p = (cluster & 1) ? (*p & 0x0F) | (x << 4) : x; |
| | 258 | f->buffer_status = DIRTY; |
| | 259 | read_buffer (f, sector + (byte_index + 1) / SECTOR_SIZE); |
| | 260 | p = f->buffer.buf + (byte_index + 1) % SECTOR_SIZE; |
| | 261 | *p = (cluster & 1) ? x >> 4 : (*p & 0xF0) | (x >> 8); |
| | 262 | } |
| | 263 | else { |
| | 264 | read_buffer (f, sector + cluster / (SECTOR_SIZE / 2)); |
| | 265 | f->buffer.fat[cluster % (SECTOR_SIZE / 2)] = x; |
| | 266 | } |
| | 267 | f->buffer_status = DIRTY; |
| 269 | | static void check_file_character(struct rdcf *f, unsigned c) |
| 270 | | { |
| 271 | | static uint8_t table[32] = { |
| 272 | | 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0xDC, 0x00, 0xFC, |
| 273 | | 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x90, |
| 274 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
| 275 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF |
| 276 | | }; |
| 277 | | if (table[c>>3] & 1<<(c&7)) error_exit(f, ~EINVAL); |
| | 276 | static void check_file_character (struct rdcf *f, unsigned c) |
| | 277 | { |
| | 278 | static uint8_t table[32] = { |
| | 279 | 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0xDC, 0x00, 0xFC, |
| | 280 | 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x90, |
| | 281 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
| | 282 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF |
| | 283 | }; |
| | 284 | if (table[c >> 3] & 1 << (c & 7)) |
| | 285 | error_exit (f, ~EINVAL); |
| 354 | | static void update_directory_entry(struct rdcf *f, int delete_entry) |
| 355 | | { |
| 356 | | struct directory *d = find_directory(f); |
| 357 | | if (f->file.attribute & RDCF_VOLUME || f->file.spec[0] == '.') |
| 358 | | memcpy(d->name_extension, f->file.spec, NAME_SIZE+EXTENSION_SIZE); |
| 359 | | else |
| 360 | | spec_to_name_extension(f, d->name_extension, f->file.spec); |
| 361 | | if (delete_entry) d->name_extension[0] = DELETED_FILE; |
| 362 | | d->attribute = f->file.attribute; |
| 363 | | d->date = (f->file.date_and_time.year-1980)<<9 | |
| 364 | | f->file.date_and_time.month<<5 | f->file.date_and_time.day; |
| 365 | | d->time = f->file.date_and_time.hour<<11 | |
| 366 | | f->file.date_and_time.minute<<5 | f->file.date_and_time.second>>1; |
| 367 | | d->first_cluster = f->file.first_cluster; |
| 368 | | d->size = f->file.size; |
| 369 | | memset(d->reserved, 0, sizeof(d->reserved)); |
| | 370 | static void update_directory_entry (struct rdcf *f, int delete_entry) |
| | 371 | { |
| | 372 | struct directory *d = find_directory (f); |
| | 373 | if (f->file.attribute & RDCF_VOLUME || f->file.spec[0] == '.') |
| | 374 | memcpy (d->name_extension, f->file.spec, NAME_SIZE + EXTENSION_SIZE); |
| | 375 | else |
| | 376 | spec_to_name_extension (f, d->name_extension, f->file.spec); |
| | 377 | if (delete_entry) |
| | 378 | d->name_extension[0] = DELETED_FILE; |
| | 379 | d->attribute = f->file.attribute; |
| | 380 | d->date = (f->file.date_and_time.year - 1980) << 9 | |
| | 381 | f->file.date_and_time.month << 5 | f->file.date_and_time.day; |
| | 382 | d->time = f->file.date_and_time.hour << 11 | |
| | 383 | f->file.date_and_time.minute << 5 | f->file.date_and_time.second >> 1; |
| | 384 | d->first_cluster = f->file.first_cluster; |
| | 385 | d->size = f->file.size; |
| | 386 | memset (d->reserved, 0, sizeof (d->reserved)); |
| 437 | | static int find_file_in_directory_or_find_volume(struct rdcf *f, |
| 438 | | const uint8_t *name_extension) |
| 439 | | { |
| 440 | | unsigned empty_cluster = 2; |
| 441 | | unsigned empty_index = NO_DIRECTORY_INDEX; |
| 442 | | unsigned number_of_directory_entries = f->directory_cluster == 0 ? |
| 443 | | (f->first_data_sector - f->first_directory_sector)*ENTRIES_PER_SECTOR : |
| 444 | | ENTRIES_PER_SECTOR*f->sectors_per_cluster; |
| 445 | | f->directory_first_cluster = f->directory_cluster; |
| 446 | | while (1) { |
| 447 | | for (f->directory_index = 0; |
| 448 | | f->directory_index < number_of_directory_entries; f->directory_index++) { |
| 449 | | struct directory *d = find_directory(f); |
| 450 | | if ((d->name_extension[0] == DELETED_FILE || |
| 451 | | d->name_extension[0] == END_DIRECTORY) && |
| 452 | | empty_index == NO_DIRECTORY_INDEX) { |
| 453 | | empty_cluster = f->directory_cluster; |
| 454 | | empty_index = f->directory_index; |
| 455 | | } |
| 456 | | if (d->name_extension[0] == END_DIRECTORY) break; |
| 457 | | if ((name_extension == NULL && |
| 458 | | (d->name_extension[0] != DELETED_FILE && d->attribute & RDCF_VOLUME)) || |
| 459 | | (name_extension != NULL && |
| 460 | | (memcmp(d->name_extension, name_extension, NAME_SIZE+EXTENSION_SIZE) |
| 461 | | == 0 && (d->attribute&RDCF_VOLUME) == 0))) { |
| 462 | | read_directory_entry(f); |
| 463 | | return 1; |
| 464 | | } |
| 465 | | } |
| 466 | | if (f->directory_index < number_of_directory_entries || |
| 467 | | f->directory_cluster == 0) { |
| 468 | | break; |
| 469 | | } |
| 470 | | { |
| 471 | | unsigned x = FAT_entry(f, f->directory_cluster); |
| 472 | | if (x >= f->last_cluster_mark) break; |
| 473 | | f->directory_cluster = x; |
| 474 | | } |
| 475 | | } |
| 476 | | f->directory_index = empty_index; |
| 477 | | if (f->directory_index != NO_DIRECTORY_INDEX) |
| 478 | | f->directory_cluster = empty_cluster; |
| 479 | | if (name_extension != NULL) |
| 480 | | name_extension_to_spec(f->file.spec, name_extension); |
| 481 | | return 0; |
| | 455 | static int find_file_in_directory_or_find_volume (struct rdcf *f, |
| | 456 | const uint8_t * |
| | 457 | name_extension) |
| | 458 | { |
| | 459 | unsigned empty_cluster = 2; |
| | 460 | unsigned empty_index = NO_DIRECTORY_INDEX; |
| | 461 | unsigned number_of_directory_entries = f->directory_cluster == 0 ? |
| | 462 | (f->first_data_sector - f->first_directory_sector) * ENTRIES_PER_SECTOR : |
| | 463 | ENTRIES_PER_SECTOR * f->sectors_per_cluster; |
| | 464 | f->directory_first_cluster = f->directory_cluster; |
| | 465 | while (1) { |
| | 466 | for (f->directory_index = 0; |
| | 467 | f->directory_index < number_of_directory_entries; |
| | 468 | f->directory_index++) { |
| | 469 | struct directory *d = find_directory (f); |
| | 470 | if ((d->name_extension[0] == DELETED_FILE || |
| | 471 | d->name_extension[0] == END_DIRECTORY) && |
| | 472 | empty_index == NO_DIRECTORY_INDEX) { |
| | 473 | empty_cluster = f->directory_cluster; |
| | 474 | empty_index = f->directory_index; |
| | 475 | } |
| | 476 | if (d->name_extension[0] == END_DIRECTORY) |
| | 477 | break; |
| | 478 | if ((name_extension == NULL && |
| | 479 | (d->name_extension[0] != DELETED_FILE |
| | 480 | && d->attribute & RDCF_VOLUME)) || (name_extension != NULL |
| | 481 | && |
| | 482 | (memcmp |
| | 483 | (d->name_extension, |
| | 484 | name_extension, |
| | 485 | NAME_SIZE + EXTENSION_SIZE) |
| | 486 | == 0 |
| | 487 | && (d-> |
| | 488 | attribute & RDCF_VOLUME) |
| | 489 | == 0))) { |
| | 490 | read_directory_entry (f); |
| | 491 | return 1; |
| | 492 | } |
| | 493 | } |
| | 494 | if (f->directory_index < number_of_directory_entries || |
| | 495 | f->directory_cluster == 0) { |
| | 496 | break; |
| | 497 | } |
| | 498 | { |
| | 499 | unsigned x = FAT_entry (f, f->directory_cluster); |
| | 500 | if (x >= f->last_cluster_mark) |
| | 501 | break; |
| | 502 | f->directory_cluster = x; |
| | 503 | } |
| | 504 | } |
| | 505 | f->directory_index = empty_index; |
| | 506 | if (f->directory_index != NO_DIRECTORY_INDEX) |
| | 507 | f->directory_cluster = empty_cluster; |
| | 508 | if (name_extension != NULL) |
| | 509 | name_extension_to_spec (f->file.spec, name_extension); |
| | 510 | return 0; |
| 496 | | static int find_file(struct rdcf *f, const char *spec) |
| 497 | | { |
| 498 | | /* start with root directory */ |
| 499 | | f->directory_cluster = 0; |
| 500 | | while (1) { |
| 501 | | int found; |
| 502 | | uint8_t name_extension[NAME_SIZE+EXTENSION_SIZE]; |
| 503 | | /* scan name and extension */ |
| 504 | | spec = spec_to_name_extension(f, name_extension, spec); |
| 505 | | /* look it up in directory */ |
| 506 | | found = find_file_in_directory_or_find_volume(f, name_extension); |
| 507 | | /* if this is the end of the file specification, return */ |
| 508 | | if (*spec == 0) return found; |
| 509 | | /* otherwise, the name and extension were a subdirectory in the path */ |
| 510 | | if (!found || (f->file.attribute&RDCF_DIRECTORY) == 0) |
| 511 | | error_exit(f, ~EISDIR); |
| 512 | | f->directory_cluster = f->file.first_cluster; |
| 513 | | /* skip over the \ after the subdirectory */ |
| 514 | | spec++; |
| 515 | | } |
| | 525 | static int find_file (struct rdcf *f, const char *spec) |
| | 526 | { |
| | 527 | /* start with root directory */ |
| | 528 | f->directory_cluster = 0; |
| | 529 | while (1) { |
| | 530 | int found; |
| | 531 | uint8_t name_extension[NAME_SIZE + EXTENSION_SIZE]; |
| | 532 | /* scan name and extension */ |
| | 533 | spec = spec_to_name_extension (f, name_extension, spec); |
| | 534 | /* look it up in directory */ |
| | 535 | found = find_file_in_directory_or_find_volume (f, name_extension); |
| | 536 | /* if this is the end of the file specification, return */ |
| | 537 | if (*spec == 0) |
| | 538 | return found; |
| | 539 | /* otherwise, the name and extension were a subdirectory in the path */ |
| | 540 | if (!found || (f->file.attribute & RDCF_DIRECTORY) == 0) |
| | 541 | error_exit (f, ~EISDIR); |
| | 542 | f->directory_cluster = f->file.first_cluster; |
| | 543 | /* skip over the \ after the subdirectory */ |
| | 544 | spec++; |
| | 545 | } |
| 660 | | int rdcf_close(struct rdcf *f) |
| 661 | | { |
| 662 | | if ((f->result=setjmp(f->error)) != 0) return f->result; |
| 663 | | if (f->mode & WRITTEN) { |
| 664 | | f->buffer_status = EMPTY; |
| 665 | | f->file.attribute |= RDCF_ARCHIVE; |
| 666 | | rdcf_get_date_and_time(&f->file.date_and_time); |
| 667 | | // do not allow empty files. |
| 668 | | update_directory_entry(f, (f->file.size) ? 0 : 1); |
| 669 | | flush_buffer(f); |
| 670 | | } |
| 671 | | return 0; |
| 672 | | } |
| 673 | | |
| 674 | | int rdcf_delete(struct rdcf *f, const char *spec) |
| 675 | | { |
| 676 | | if ((f->result=setjmp(f->error)) != 0) return f->result; |
| 677 | | spec = initialize_fcb(f, spec); |
| 678 | | if (*spec == 0) /* delete volume label */ { |
| 679 | | if (!find_file_in_directory_or_find_volume(f, NULL)) |
| 680 | | error_exit(f, ~ENOENT); |
| 681 | | } else if (!find_file(f, spec)) |
| 682 | | error_exit(f, ~ENOENT); |
| 683 | | /* check to see that a directory is empty before deleting it */ |
| 684 | | else if (f->file.attribute & RDCF_DIRECTORY) { |
| 685 | | unsigned cluster = f->file.first_cluster; |
| 686 | | while (cluster != EMPTY_CLUSTER && cluster < f->last_cluster_mark) { |
| 687 | | unsigned sector = first_sector_in_cluster(f, cluster); |
| 688 | | unsigned sector_count = f->sectors_per_cluster; |
| 689 | | unsigned next_cluster = FAT_entry(f, cluster); |
| 690 | | do { |
| 691 | | unsigned entry_count = ENTRIES_PER_SECTOR; |
| 692 | | uint8_t *p = f->buffer.buf; |
| 693 | | read_buffer(f, sector); |
| 694 | | do { |
| 695 | | unsigned c = *p; |
| 696 | | if (c == END_DIRECTORY) break; |
| 697 | | if (c != DELETED_FILE && c != '.') error_exit(f, ~EACCES); |
| 698 | | p += sizeof(struct directory); |
| 699 | | } while (--entry_count != 0); |
| 700 | | if (entry_count != 0) break; |
| 701 | | } while (--sector_count != 0); |
| 702 | | cluster = next_cluster; |
| 703 | | } |
| 704 | | } else check_write_access(f); |
| 705 | | release_FAT_entries(f); |
| 706 | | update_directory_entry(f, 1); |
| 707 | | flush_buffer(f); |
| 708 | | return 0; |
| 709 | | } |
| 710 | | |
| 711 | | int rdcf_rename(struct rdcf *f, const char *old_spec, const char *new_spec) |
| 712 | | { |
| 713 | | uint8_t name_extension[NAME_SIZE+EXTENSION_SIZE]; |
| 714 | | if ((f->result=setjmp(f->error)) != 0) return f->result; |
| 715 | | old_spec = initialize_fcb(f, old_spec); |
| 716 | | if (!find_file(f, old_spec)) error_exit(f, ~ENOENT); |
| 717 | | if (f->file.attribute & (RDCF_HIDDEN+RDCF_SYSTEM)) |
| 718 | | error_exit(f, ~EACCES); |
| 719 | | spec_to_name_extension(f, name_extension, new_spec); |
| 720 | | f->directory_cluster = f->directory_first_cluster; |
| 721 | | if (find_file_in_directory_or_find_volume(f, name_extension)) |
| 722 | | error_exit(f, ~ENOENT); |
| 723 | | find_file(f, old_spec); |
| 724 | | name_extension_to_spec(f->file.spec, name_extension); |
| 725 | | update_directory_entry(f, 0); |
| 726 | | flush_buffer(f); |
| 727 | | return 0; |
| 728 | | } |
| 729 | | |
| 730 | | int rdcf_open(struct rdcf *f, const char *spec, unsigned mode) |
| 731 | | { |
| 732 | | int found; |
| 733 | | if ((f->result=setjmp(f->error)) != 0) return f->result; |
| 734 | | // make sure it is not open on another stream. |
| 735 | | f->mode = 0; |
| 736 | | found = find_file(f, initialize_fcb(f, spec)); |
| 737 | | if (found && f->file.attribute&RDCF_DIRECTORY) |
| 738 | | error_exit(f, ~EISDIR); |
| 739 | | // see how to open the file. |
| 740 | | if (mode & _FTRUNC && found) { |
| 741 | | // simply empty the file of clusters. |
| 742 | | if (found) { |
| 743 | | check_write_access(f); |
| 744 | | release_FAT_entries(f); |
| 745 | | flush_buffer(f); |
| 746 | | } else lengthen_directory_if_necessary(f); |
| 747 | | f->file.first_cluster = EMPTY_CLUSTER; |
| 748 | | f->file.size = 0L; |
| 749 | | } |
| 750 | | // create new dir entry if mode is not purely read: "r" |
| 751 | | if (!found && (mode & 0x3)) { |
| 752 | | f->file.attribute = RDCF_ARCHIVE; |
| 753 | | rdcf_get_date_and_time(&f->file.date_and_time); |
| 754 | | f->mode |= WRITTEN; |
| 755 | | f->file.first_cluster = EMPTY_CLUSTER; |
| 756 | | f->file.size = 0L; |
| 757 | | update_directory_entry(f, 0); |
| 758 | | flush_buffer(f); |
| 759 | | } |
| 760 | | if ((mode & 0x3) == 0) { |
| 761 | | // open for reading. |
| 762 | | f->mode |= RDCF_READ; |
| 763 | | if (!found) error_exit(f, ~ENOENT); |
| 764 | | } else { |
| 765 | | // open for writing or |
| 766 | | f->mode |= RDCF_WRITE; |
| 767 | | // open for read write. |
| 768 | | if ((mode & 0x3) == 2) f->mode |= RDCF_READ; |
| 769 | | check_write_access(f); |
| 770 | | } |
| 771 | | f->last_cluster = EMPTY_CLUSTER; |
| 772 | | f->position = 0; |
| 773 | | f->cluster = f->file.first_cluster; |
| 774 | | if (mode & _FAPPEND) { |
| 775 | | // seek to end of file. |
| 776 | | if ((found = rdcf_seek(f, f->file.size)) != 0) return found; |
| 777 | | } |
| 778 | | return 0; |
| | 699 | int rdcf_close (struct rdcf *f) |
| | 700 | { |
| | 701 | if ((f->result = setjmp (f->error)) != 0) |
| | 702 | return f->result; |
| | 703 | if (f->mode & WRITTEN) { |
| | 704 | f->buffer_status = EMPTY; |
| | 705 | f->file.attribute |= RDCF_ARCHIVE; |
| | 706 | rdcf_get_date_and_time (&f->file.date_and_time); |
| | 707 | // do not allow empty files. |
| | 708 | update_directory_entry (f, (f->file.size) ? 0 : 1); |
| | 709 | flush_buffer (f); |
| | 710 | } |
| | 711 | return 0; |
| | 712 | } |
| | 713 | |
| | 714 | int rdcf_delete (struct rdcf *f, const char *spec) |
| | 715 | { |
| | 716 | if ((f->result = setjmp (f->error)) != 0) |
| | 717 | return f->result; |
| | 718 | spec = initialize_fcb (f, spec); |
| | 719 | if (*spec == 0) { /* delete volume label */ |
| | 720 | if (!find_file_in_directory_or_find_volume (f, NULL)) |
| | 721 | error_exit (f, ~ENOENT); |
| | 722 | } |
| | 723 | else if (!find_file (f, spec)) |
| | 724 | error_exit (f, ~ENOENT); |
| | 725 | /* check to see that a directory is empty before deleting it */ |
| | 726 | else if (f->file.attribute & RDCF_DIRECTORY) { |
| | 727 | unsigned cluster = f->file.first_cluster; |
| | 728 | while (cluster != EMPTY_CLUSTER && cluster < f->last_cluster_mark) { |
| | 729 | unsigned sector = first_sector_in_cluster (f, cluster); |
| | 730 | unsigned sector_count = f->sectors_per_cluster; |
| | 731 | unsigned next_cluster = FAT_entry (f, cluster); |
| | 732 | do { |
| | 733 | unsigned entry_count = ENTRIES_PER_SECTOR; |
| | 734 | uint8_t *p = f->buffer.buf; |
| | 735 | read_buffer (f, sector); |
| | 736 | do { |
| | 737 | unsigned c = *p; |
| | 738 | if (c == END_DIRECTORY) |
| | 739 | break; |
| | 740 | if (c != DELETED_FILE && c != '.') |
| | 741 | error_exit (f, ~EACCES); |
| | 742 | p += sizeof (struct directory); |
| | 743 | } while (--entry_count != 0); |
| | 744 | if (entry_count != 0) |
| | 745 | break; |
| | 746 | } while (--sector_count != 0); |
| | 747 | cluster = next_cluster; |
| | 748 | } |
| | 749 | } |
| | 750 | else |
| | 751 | check_write_access (f); |
| | 752 | release_FAT_entries (f); |
| | 753 | update_directory_entry (f, 1); |
| | 754 | flush_buffer (f); |
| | 755 | return 0; |
| | 756 | } |
| | 757 | |
| | 758 | int rdcf_rename (struct rdcf *f, const char *old_spec, const char *new_spec) |
| | 759 | { |
| | 760 | uint8_t name_extension[NAME_SIZE + EXTENSION_SIZE]; |
| | 761 | if ((f->result = setjmp (f->error)) != 0) |
| | 762 | return f->result; |
| | 763 | old_spec = initialize_fcb (f, old_spec); |
| | 764 | if (!find_file (f, old_spec)) |
| | 765 | error_exit (f, ~ENOENT); |
| | 766 | if (f->file.attribute & (RDCF_HIDDEN + RDCF_SYSTEM)) |
| | 767 | error_exit (f, ~EACCES); |
| | 768 | spec_to_name_extension (f, name_extension, new_spec); |
| | 769 | f->directory_cluster = f->directory_first_cluster; |
| | 770 | if (find_file_in_directory_or_find_volume (f, name_extension)) |
| | 771 | error_exit (f, ~ENOENT); |
| | 772 | find_file (f, old_spec); |
| | 773 | name_extension_to_spec (f->file.spec, name_extension); |
| | 774 | update_directory_entry (f, 0); |
| | 775 | flush_buffer (f); |
| | 776 | return 0; |
| | 777 | } |
| | 778 | |
| | 779 | int rdcf_open (struct rdcf *f, const char *spec, unsigned mode) |
| | 780 | { |
| | 781 | int found; |
| | 782 | if ((f->result = setjmp (f->error)) != 0) |
| | 783 | return f->result; |
| | 784 | // make sure it is not open on another stream. |
| | 785 | f->mode = 0; |
| | 786 | found = find_file (f, initialize_fcb (f, spec)); |
| | 787 | if (found && f->file.attribute & RDCF_DIRECTORY) |
| | 788 | error_exit (f, ~EISDIR); |
| | 789 | // see how to open the file. |
| | 790 | if (mode & _FTRUNC && found) { |
| | 791 | // simply empty the file of clusters. |
| | 792 | if (found) { |
| | 793 | check_write_access (f); |
| | 794 | release_FAT_entries (f); |
| | 795 | flush_buffer (f); |
| | 796 | } |
| | 797 | else |
| | 798 | lengthen_directory_if_necessary (f); |
| | 799 | f->file.first_cluster = EMPTY_CLUSTER; |
| | 800 | f->file.size = 0L; |
| | 801 | } |
| | 802 | // create new dir entry if mode is not purely read: "r" |
| | 803 | if (!found && (mode & 0x3)) { |
| | 804 | f->file.attribute = RDCF_ARCHIVE; |
| | 805 | rdcf_get_date_and_time (&f->file.date_and_time); |
| | 806 | f->mode |= WRITTEN; |
| | 807 | f->file.first_cluster = EMPTY_CLUSTER; |
| | 808 | f->file.size = 0L; |
| | 809 | update_directory_entry (f, 0); |
| | 810 | flush_buffer (f); |
| | 811 | } |
| | 812 | if ((mode & 0x3) == 0) { |
| | 813 | // open for reading. |
| | 814 | f->mode |= RDCF_READ; |
| | 815 | if (!found) |
| | 816 | error_exit (f, ~ENOENT); |
| | 817 | } |
| | 818 | else { |
| | 819 | // open for writing or |
| | 820 | f->mode |= RDCF_WRITE; |
| | 821 | // open for read write. |
| | 822 | if ((mode & 0x3) == 2) |
| | 823 | f->mode |= RDCF_READ; |
| | 824 | check_write_access (f); |
| | 825 | } |
| | 826 | f->last_cluster = EMPTY_CLUSTER; |
| | 827 | f->position = 0; |
| | 828 | f->cluster = f->file.first_cluster; |
| | 829 | if (mode & _FAPPEND) { |
| | 830 | // seek to end of file. |
| | 831 | if ((found = rdcf_seek (f, f->file.size)) != 0) |
| | 832 | return found; |
| | 833 | } |
| | 834 | return 0; |
| 788 | | int rdcf_read(struct rdcf *f, void *buf, int count) |
| 789 | | { |
| 790 | | uint32_t size = f->file.size; |
| 791 | | unsigned unread_bytes = count; |
| 792 | | uint32_t position = f->position; |
| 793 | | char *buffer = buf; |
| 794 | | if ((f->result=setjmp(f->error)) != 0) return f->result; |
| 795 | | if ((f->mode & RDCF_READ) == 0) error_exit(f, ~EBADFD); |
| 796 | | f->buffer_status = EMPTY; |
| 797 | | while (unread_bytes>0) { |
| 798 | | unsigned n = unread_bytes; |
| 799 | | unsigned rest_of_sector = SECTOR_SIZE - position%SECTOR_SIZE; |
| 800 | | unsigned sector; |
| 801 | | if (size<position+n) n = size-position; |
| 802 | | if (n==0) break; |
| 803 | | sector = first_sector_in_cluster(f, f->cluster) + |
| 804 | | (position/SECTOR_SIZE)%f->sectors_per_cluster; |
| 805 | | if (n>rest_of_sector) n = rest_of_sector; |
| 806 | | if (position%SECTOR_SIZE==0 && n==SECTOR_SIZE) |
| 807 | | read_sector(f, sector, buffer); |
| 808 | | else /* read a partial sector */ { |
| 809 | | read_buffer(f, sector); |
| 810 | | memcpy(buffer, &f->buffer.buf[position%SECTOR_SIZE], n); |
| 811 | | } |
| 812 | | buffer += n; |
| 813 | | position += n; |
| 814 | | unread_bytes -= n; |
| 815 | | if (position%(f->sectors_per_cluster*SECTOR_SIZE)==0 && position<size) { |
| 816 | | unsigned next_cluster = FAT_entry(f, f->cluster); |
| 817 | | if (next_cluster >= f->last_cluster_mark) f->last_cluster = f->cluster; |
| 818 | | f->cluster = next_cluster; |
| 819 | | } |
| 820 | | } |
| 821 | | f->position = position; |
| 822 | | return f->result = count - unread_bytes; |
| 823 | | } |
| 824 | | |
| 825 | | |
| 826 | | int rdcf_seek(struct rdcf *f, uint32_t offset) |
| 827 | | { |
| 828 | | unsigned i, cluster; |
| 829 | | if ((f->result=setjmp(f->error)) != 0) return f->result; |
| 830 | | if (offset>f->file.size) error_exit(f, ~ESPIPE); |
| 831 | | f->buffer_status = EMPTY; |
| 832 | | cluster = f->file.first_cluster; |
| 833 | | for (i=offset/(f->sectors_per_cluster*SECTOR_SIZE); i>0; i--) { |
| 834 | | unsigned new_cluster = FAT_entry(f, cluster); |
| 835 | | if (new_cluster >= f->last_cluster_mark) f->last_cluster = cluster; |
| 836 | | cluster = new_cluster; |
| 837 | | } |
| 838 | | f->cluster = cluster; |
| 839 | | f->position = offset; |
| 840 | | return 0; |
| 841 | | } |
| 842 | | |
| 843 | | static int real_rdcf_write(struct rdcf *f, const uint8_t *buf, int count) |
| 844 | | { |
| 845 | | uint32_t size = f->file.size; |
| 846 | | uint32_t position = f->position; |
| 847 | | unsigned unwritten_bytes = count; |
| 848 | | const char *buffer = buf; |
| 849 | | if ((f->result=setjmp(f->error)) != 0) return f->result; |
| 850 | | f->buffer_status = EMPTY; |
| 851 | | if ((f->mode & RDCF_WRITE) == 0) error_exit(f, ~EBADFD); |
| 852 | | while (unwritten_bytes>0) { |
| 853 | | unsigned sector; |
| 854 | | unsigned n = unwritten_bytes; |
| 855 | | unsigned rest_of_sector = SECTOR_SIZE - position%SECTOR_SIZE; |
| 856 | | if (n>rest_of_sector) n = rest_of_sector; |
| 857 | | if (f->cluster == EMPTY_CLUSTER || f->cluster >= f->last_cluster_mark) { |
| 858 | | unsigned new_cluster = |
| 859 | | add_new_cluster(f, f->last_cluster, f->first_possibly_empty_cluster); |
| 860 | | if (new_cluster == EMPTY_CLUSTER) break; |
| 861 | | f->first_possibly_empty_cluster = new_cluster+1; |
| 862 | | f->cluster = f->last_cluster = new_cluster; |
| 863 | | if (f->file.first_cluster==EMPTY_CLUSTER) |
| 864 | | f->file.first_cluster = new_cluster; |
| 865 | | } |
| 866 | | sector = first_sector_in_cluster(f, f->cluster) + |
| 867 | | (position/SECTOR_SIZE)%f->sectors_per_cluster; |
| 868 | | if (position%SECTOR_SIZE==0 && |
| 869 | | (n==SECTOR_SIZE || position+n>=size)) { |
| 870 | | write_sector(f, sector, buffer); |
| 871 | | } else /* write a partial sector */ { |
| 872 | | read_buffer(f, sector); |
| 873 | | memcpy(&f->buffer.buf[position%SECTOR_SIZE], buffer, n); |
| 874 | | f->buffer_status = DIRTY; |
| 875 | | } |
| 876 | | buffer += n; |
| 877 | | position += n; |
| 878 | | unwritten_bytes -= n; |
| 879 | | if (position>size) size = position; |
| 880 | | if (position%(f->sectors_per_cluster*SECTOR_SIZE)==0) { |
| 881 | | unsigned next_cluster = FAT_entry(f, f->cluster); |
| 882 | | if (next_cluster >= f->last_cluster_mark) |
| 883 | | f->last_cluster = f->cluster; |
| 884 | | f->cluster = next_cluster; |
| 885 | | } |
| 886 | | } |
| 887 | | flush_buffer(f); |
| 888 | | f->position = position; |
| 889 | | f->file.size = size; |
| 890 | | f->mode |= WRITTEN; |
| | 844 | int rdcf_read (struct rdcf *f, void *buf, int count) |
| | 845 | { |
| | 846 | uint32_t size = f->file.size; |
| | 847 | unsigned unread_bytes = count; |
| | 848 | uint32_t position = f->position; |
| | 849 | char *buffer = buf; |
| | 850 | if ((f->result = setjmp (f->error)) != 0) |
| | 851 | return f->result; |
| | 852 | if ((f->mode & RDCF_READ) == 0) |
| | 853 | error_exit (f, ~EBADFD); |
| | 854 | f->buffer_status = EMPTY; |
| | 855 | while (unread_bytes > 0) { |
| | 856 | unsigned n = unread_bytes; |
| | 857 | unsigned rest_of_sector = SECTOR_SIZE - position % SECTOR_SIZE; |
| | 858 | unsigned sector; |
| | 859 | if (size < position + n) |
| | 860 | n = size - position; |
| | 861 | if (n == 0) |
| | 862 | break; |
| | 863 | sector = first_sector_in_cluster (f, f->cluster) + |
| | 864 | (position / SECTOR_SIZE) % f->sectors_per_cluster; |
| | 865 | if (n > rest_of_sector) |
| | 866 | n = rest_of_sector; |
| | 867 | if (position % SECTOR_SIZE == 0 && n == SECTOR_SIZE) |
| | 868 | read_sector (f, sector, buffer); |
| | 869 | else { /* read a partial sector */ |
| | 870 | |
| | 871 | read_buffer (f, sector); |
| | 872 | memcpy (buffer, &f->buffer.buf[position % SECTOR_SIZE], n); |
| | 873 | } |
| | 874 | buffer += n; |
| | 875 | position += n; |
| | 876 | unread_bytes -= n; |
| | 877 | if (position % (f->sectors_per_cluster * SECTOR_SIZE) == 0 |
| | 878 | && position < size) { |
| | 879 | unsigned next_cluster = FAT_entry (f, f->cluster); |
| | 880 | if (next_cluster >= f->last_cluster_mark) |
| | 881 | f->last_cluster = f->cluster; |
| | 882 | f->cluster = next_cluster; |
| | 883 | } |
| | 884 | } |
| | 885 | f->position = position; |
| | 886 | return f->result = count - unread_bytes; |
| | 887 | } |
| | 888 | |
| | 889 | |
| | 890 | int rdcf_seek (struct rdcf *f, uint32_t offset) |
| | 891 | { |
| | 892 | unsigned i, cluster; |
| | 893 | if ((f->result = setjmp (f->error)) != 0) |
| | 894 | return f->result; |
| | 895 | if (offset > f->file.size) |
| | 896 | error_exit (f, ~ESPIPE); |
| | 897 | f->buffer_status = EMPTY; |
| | 898 | cluster = f->file.first_cluster; |
| | 899 | for (i = offset / (f->sectors_per_cluster * SECTOR_SIZE); i > 0; i--) { |
| | 900 | unsigned new_cluster = FAT_entry (f, cluster); |
| | 901 | if (new_cluster >= f->last_cluster_mark) |
| | 902 | f->last_cluster = cluster; |
| | 903 | cluster = new_cluster; |
| | 904 | } |
| | 905 | f->cluster = cluster; |
| | 906 | f->position = offset; |
| | 907 | return 0; |
| | 908 | } |
| | 909 | |
| | 910 | static int real_rdcf_write (struct rdcf *f, const uint8_t * buf, int count) |
| | 911 | { |
| | 912 | uint32_t size = f->file.size; |
| | 913 | uint32_t position = f->position; |
| | 914 | unsigned unwritten_bytes = count; |
| | 915 | const char *buffer = buf; |
| | 916 | if ((f->result = setjmp (f->error)) != 0) |
| | 917 | return f->result; |
| | 918 | f->buffer_status = EMPTY; |
| | 919 | if ((f->mode & RDCF_WRITE) == 0) |
| | 920 | error_exit (f, ~EBADFD); |
| | 921 | while (unwritten_bytes > 0) { |
| | 922 | unsigned sector; |
| | 923 | unsigned n = unwritten_bytes; |
| | 924 | unsigned rest_of_sector = SECTOR_SIZE - position % SECTOR_SIZE; |
| | 925 | if (n > rest_of_sector) |
| | 926 | n = rest_of_sector; |
| | 927 | if (f->cluster == EMPTY_CLUSTER || f->cluster >= f->last_cluster_mark) { |
| | 928 | unsigned new_cluster = |
| | 929 | add_new_cluster (f, f->last_cluster, f->first_possibly_empty_cluster); |
| | 930 | if (new_cluster == EMPTY_CLUSTER) |
| | 931 | break; |
| | 932 | f->first_possibly_empty_cluster = new_cluster + 1; |
| | 933 | f->cluster = f->last_cluster = new_cluster; |
| | 934 | if (f->file.first_cluster == EMPTY_CLUSTER) |
| | 935 | f->file.first_cluster = new_cluster; |
| | 936 | } |
| | 937 | sector = first_sector_in_cluster (f, f->cluster) + |
| | 938 | (position / SECTOR_SIZE) % f->sectors_per_cluster; |
| | 939 | if (position % SECTOR_SIZE == 0 && |
| | 940 | (n == SECTOR_SIZE || position + n >= size)) { |
| | 941 | write_sector (f, sector, buffer); |
| | 942 | } |
| | 943 | else { /* write a partial sector */ |
| | 944 | |
| | 945 | read_buffer (f, sector); |
| | 946 | memcpy (&f->buffer.buf[position % SECTOR_SIZE], buffer, n); |
| | 947 | f->buffer_status = DIRTY; |
| | 948 | } |
| | 949 | buffer += n; |
| | 950 | position += n; |
| | 951 | unwritten_bytes -= n; |
| | 952 | if (position > size) |
| | 953 | size = position; |
| | 954 | if (position % (f->sectors_per_cluster * SECTOR_SIZE) == 0) { |
| | 955 | unsigned next_cluster = FAT_entry (f, f->cluster); |
| | 956 | if (next_cluster >= f->last_cluster_mark) |
| | 957 | f->last_cluster = f->cluster; |
| | 958 | f->cluster = next_cluster; |
| | 959 | } |
| | 960 | } |
| | 961 | flush_buffer (f); |
| | 962 | f->position = position; |
| | 963 | f->file.size = size; |
| | 964 | f->mode |= WRITTEN; |
| 923 | | int rdcf_attribute(struct rdcf *f, const char *spec, unsigned attribute) |
| 924 | | { |
| 925 | | if ((f->result=setjmp(f->error)) != 0) return f->result; |
| 926 | | if (!find_file(f, initialize_fcb(f, spec)) || |
| 927 | | f->file.attribute & RDCF_DIRECTORY) { |
| 928 | | error_exit(f, ~ENOENT); |
| 929 | | } |
| 930 | | f->file.attribute = (f->file.attribute & ~CHANGEABLE_ATTRIBUTES) | |
| 931 | | (attribute & CHANGEABLE_ATTRIBUTES); |
| 932 | | update_directory_entry(f, 0); |
| 933 | | flush_buffer(f); |
| 934 | | return 0; |
| 935 | | } |
| 936 | | |
| 937 | | int rdcf_date_and_time(struct rdcf *f, const char *spec, |
| 938 | | struct rdcf_date_and_time *p) |
| 939 | | { |
| 940 | | if ((f->result=setjmp(f->error)) != 0) return f->result; |
| 941 | | spec = initialize_fcb(f, spec); |
| 942 | | if (*spec == 0) { |
| 943 | | f->directory_cluster = 0; |
| 944 | | if (!find_file_in_directory_or_find_volume(f, NULL)) |
| 945 | | error_exit(f, ~ENOENT); |
| 946 | | } else { |
| 947 | | if (!find_file(f, spec)) error_exit(f, ~ENOENT); |
| 948 | | } |
| 949 | | f->file.date_and_time = *p; |
| 950 | | if ((f->file.attribute & (RDCF_DIRECTORY+RDCF_VOLUME)) == 0) { |
| 951 | | check_write_access(f); |
| 952 | | f->file.attribute |= RDCF_ARCHIVE; |
| 953 | | } |
| 954 | | update_directory_entry(f, 0); |
| 955 | | if (f->file.attribute & RDCF_DIRECTORY) update_dot_and_dot_dot(f); |
| 956 | | flush_buffer(f); |
| 957 | | return 0; |
| 958 | | } |
| 959 | | |
| 960 | | int rdcf_directory(struct rdcf *f, const char *spec) |
| 961 | | { |
| 962 | | /* uint8_t name_extension[NAME_SIZE+EXTENSION_SIZE]; ??? */ |
| 963 | | if ((f->result=setjmp(f->error)) != 0) return f->result; |
| 964 | | if (find_file(f, initialize_fcb(f, spec))) error_exit(f, ~EISDIR); |
| 965 | | /* spec_to_name_extension(f, name_extension, f->file.spec); ??? */ |
| 966 | | /* name_extension_to_spec(f->file.spec, name_extension); ??? */ |
| 967 | | /* determine whether there is enough free space for directory */ |
| 968 | | { |
| 969 | | unsigned cluster = 2; |
| 970 | | unsigned required_clusters = |
| 971 | | f->directory_index == NO_DIRECTORY_INDEX ? 2 : 1; |
| 972 | | for (cluster = 2; required_clusters != 0; cluster++) { |
| 973 | | if (cluster > f->maximum_cluster_number) error_exit(f, ~ENOSPC); |
| 974 | | if (FAT_entry(f, cluster)==EMPTY_CLUSTER) required_clusters--; |
| 975 | | } |
| 976 | | } |
| 977 | | lengthen_directory_if_necessary(f); |
| 978 | | f->file.attribute = RDCF_DIRECTORY; |
| 979 | | f->file.first_cluster = add_new_cluster(f, EMPTY_CLUSTER, 2); |
| 980 | | clear_cluster(f, f->file.first_cluster); |
| 981 | | f->file.size = 0L; |
| 982 | | rdcf_get_date_and_time(&f->file.date_and_time); |
| 983 | | update_directory_entry(f, 0); |
| 984 | | update_dot_and_dot_dot(f); |
| 985 | | flush_buffer(f); |
| 986 | | return 0; |
| 987 | | } |
| 988 | | |
| 989 | | long int rdcf_free_space(struct rdcf *f |
| | 998 | int rdcf_attribute (struct rdcf *f, const char *spec, unsigned attribute) |
| | 999 | { |
| | 1000 | if ((f->result = setjmp (f->error)) != 0) |
| | 1001 | return f->result; |
| | 1002 | if (!find_file (f, initialize_fcb (f, spec)) || |
| | 1003 | f->file.attribute & RDCF_DIRECTORY) { |
| | 1004 | error_exit (f, ~ENOENT); |
| | 1005 | } |
| | 1006 | f->file.attribute = (f->file.attribute & ~CHANGEABLE_ATTRIBUTES) | |
| | 1007 | (attribute & CHANGEABLE_ATTRIBUTES); |
| | 1008 | update_directory_entry (f, 0); |
| | 1009 | flush_buffer (f); |
| | 1010 | return 0; |
| | 1011 | } |
| | 1012 | |
| | 1013 | int rdcf_date_and_time (struct rdcf *f, const char *spec, |
| | 1014 | struct rdcf_date_and_time *p) |
| | 1015 | { |
| | 1016 | if ((f->result = setjmp (f->error)) != 0) |
| | 1017 | return f->result; |
| | 1018 | spec = initialize_fcb (f, spec); |
| | 1019 | if (*spec == 0) { |
| | 1020 | f->directory_cluster = 0; |
| | 1021 | if (!find_file_in_directory_or_find_volume (f, NULL)) |
| | 1022 | error_exit (f, ~ENOENT); |
| | 1023 | } |
| | 1024 | else { |
| | 1025 | if (!find_file (f, spec)) |
| | 1026 | error_exit (f, ~ENOENT); |
| | 1027 | } |
| | 1028 | f->file.date_and_time = *p; |
| | 1029 | if ((f->file.attribute & (RDCF_DIRECTORY + RDCF_VOLUME)) == 0) { |
| | 1030 | check_write_access (f); |
| | 1031 | f->file.attribute |= RDCF_ARCHIVE; |
| | 1032 | } |
| | 1033 | update_directory_entry (f, 0); |
| | 1034 | if (f->file.attribute & RDCF_DIRECTORY) |
| | 1035 | update_dot_and_dot_dot (f); |
| | 1036 | flush_buffer (f); |
| | 1037 | return 0; |
| | 1038 | } |
| | 1039 | |
| | 1040 | int rdcf_directory (struct rdcf *f, const char *spec) |
| | 1041 | { |
| | 1042 | /* uint8_t name_extension[NAME_SIZE+EXTENSION_SIZE]; ??? */ |
| | 1043 | if ((f->result = setjmp (f->error)) != 0) |
| | 1044 | return f->result; |
| | 1045 | if (find_file (f, initialize_fcb (f, spec))) |
| | 1046 | error_exit (f, ~EISDIR); |
| | 1047 | /* spec_to_name_extension(f, name_extension, f->file.spec); ??? */ |
| | 1048 | /* name_extension_to_spec(f->file.spec, name_extension); ??? */ |
| | 1049 | /* determine whether there is enough free space for directory */ |
| | 1050 | { |
| | 1051 | unsigned cluster = 2; |
| | 1052 | unsigned required_clusters = |
| | 1053 | f->directory_index == NO_DIRECTORY_INDEX ? 2 : 1; |
| | 1054 | for (cluster = 2; required_clusters != 0; cluster++) { |
| | 1055 | if (cluster > f->maximum_cluster_number) |
| | 1056 | error_exit (f, ~ENOSPC); |
| | 1057 | if (FAT_entry (f, cluster) == EMPTY_CLUSTER) |
| | 1058 | required_clusters--; |
| | 1059 | } |
| | 1060 | } |
| | 1061 | lengthen_directory_if_necessary (f); |
| | 1062 | f->file.attribute = RDCF_DIRECTORY; |
| | 1063 | f->file.first_cluster = add_new_cluster (f, EMPTY_CLUSTER, 2); |
| | 1064 | clear_cluster (f, f->file.first_cluster); |
| | 1065 | f->file.size = 0L; |
| | 1066 | rdcf_get_date_and_time (&f->file.date_and_time); |
| | 1067 | update_directory_entry (f, 0); |
| | 1068 | update_dot_and_dot_dot (f); |
| | 1069 | flush_buffer (f); |
| | 1070 | return 0; |
| | 1071 | } |
| | 1072 | |
| | 1073 | long int rdcf_free_space (struct rdcf *f |
| 1003 | | for (cluster = 2; cluster <= f->maximum_cluster_number; cluster++) { |
| 1004 | | if (FAT_entry(f, cluster) == EMPTY_CLUSTER) number_of_empty_clusters++; |
| 1005 | | } |
| 1006 | | f->file.size = (uint32_t) number_of_empty_clusters * |
| 1007 | | (f->sectors_per_cluster * SECTOR_SIZE); |
| 1008 | | return (long)(f->file.size); |
| 1009 | | } |
| 1010 | | |
| 1011 | | int rdcf_get_file_information(struct rdcf *f, const char *spec, unsigned idx) |
| 1012 | | { |
| 1013 | | if ((f->result=setjmp(f->error)) != 0) return f->result; |
| 1014 | | find_entry(f, spec, idx); |
| 1015 | | read_directory_entry(f); |
| 1016 | | return f->result = f->file.spec[0] == DELETED_FILE ? ENOENT : |
| 1017 | | f->file.spec[0] == 0 ? ENOSPC : 0; |
| 1018 | | } |
| 1019 | | |
| 1020 | | int rdcf_next_file_information(struct rdcf *f) |
| 1021 | | { |
| 1022 | | if ((f->result=setjmp(f->error)) != 0) return f->result; |
| 1023 | | f->directory_index++; |
| 1024 | | if (f->directory_cluster == 0) { |
| 1025 | | if (f->directory_index >= |
| 1026 | | (f->first_data_sector - f->first_directory_sector)*ENTRIES_PER_SECTOR) { |
| 1027 | | error_exit(f, ~ENOSPC); |
| 1028 | | } |
| 1029 | | } else { |
| 1030 | | if (f->directory_index >= ENTRIES_PER_SECTOR*f->sectors_per_cluster) { |
| 1031 | | f->directory_cluster = FAT_entry(f, f->directory_cluster); |
| 1032 | | if (f->directory_cluster >= f->last_cluster_mark) |
| 1033 | | error_exit(f, ~ENOSPC); |
| 1034 | | f->directory_index = 0; |
| 1035 | | } |
| 1036 | | } |
| 1037 | | read_directory_entry(f); |
| 1038 | | return f->result = f->file.spec[0] == DELETED_FILE ? ENOENT : |
| 1039 | | f->file.spec[0] == 0 ? ENOSPC : 0; |
| | 1089 | for (cluster = 2; cluster <= f->maximum_cluster_number; cluster++) { |
| | 1090 | if (FAT_entry (f, cluster) == EMPTY_CLUSTER) |
| | 1091 | number_of_empty_clusters++; |
| | 1092 | } |
| | 1093 | f->file.size = (uint32_t) number_of_empty_clusters * |
| | 1094 | (f->sectors_per_cluster * SECTOR_SIZE); |
| | 1095 | return (long) (f->file.size); |
| | 1096 | } |
| | 1097 | |
| | 1098 | int rdcf_get_file_information (struct rdcf *f, const char *spec, unsigned idx) |
| | 1099 | { |
| | 1100 | if ((f->result = setjmp (f->error)) != 0) |
| | 1101 | return f->result; |
| | 1102 | find_entry (f, spec, idx); |
| | 1103 | read_directory_entry (f); |
| | 1104 | return f->result = f->file.spec[0] == DELETED_FILE ? ENOENT : |
| | 1105 | f->file.spec[0] == 0 ? ENOSPC : 0; |
| | 1106 | } |
| | 1107 | |
| | 1108 | int rdcf_next_file_information (struct rdcf *f) |
| | 1109 | { |
| | 1110 | if ((f->result = setjmp (f->error)) != 0) |
| | 1111 | return f->result; |
| | 1112 | f->directory_index++; |
| | 1113 | if (f->directory_cluster == 0) { |
| | 1114 | if (f->directory_index >= |
| | 1115 | (f->first_data_sector - |
| | 1116 | f->first_directory_sector) * ENTRIES_PER_SECTOR) { |
| | 1117 | error_exit (f, ~ENOSPC); |
| | 1118 | } |
| | 1119 | } |
| | 1120 | else { |
| | 1121 | if (f->directory_index >= ENTRIES_PER_SECTOR * f->sectors_per_cluster) { |
| | 1122 | f->directory_cluster = FAT_entry (f, f->directory_cluster); |
| | 1123 | if (f->directory_cluster >= f->last_cluster_mark) |
| | 1124 | error_exit (f, ~ENOSPC); |
| | 1125 | f->directory_index = 0; |
| | 1126 | } |
| | 1127 | } |
| | 1128 | read_directory_entry (f); |
| | 1129 | return f->result = f->file.spec[0] == DELETED_FILE ? ENOENT : |
| | 1130 | f->file.spec[0] == 0 ? ENOSPC : 0; |