| 1 | #include <fs/fat.h> |
| 2 | #include <mem/alloc.h> |
| 3 | #include <stdbool.h> |
| 4 | #include <stdint.h> |
| 5 | #include <string.h> |
| 6 | |
| 7 | bool fat_read_file(struct fat_fs *fs, struct fat_dirent *ent, uint32_t offset, |
| 8 | uint32_t size, uint8_t *out_buf) { |
| 9 | if (!ent || !out_buf || offset >= ent->filesize) |
| 10 | return false; |
| 11 | |
| 12 | uint32_t cluster_size = fs->cluster_size; |
| 13 | uint32_t file_size = ent->filesize; |
| 14 | |
| 15 | // Do not read past end of file |
| 16 | if (offset + size > file_size) |
| 17 | size = file_size - offset; |
| 18 | |
| 19 | uint32_t cluster = fat_get_dir_cluster(d: ent); |
| 20 | if (cluster == 0) |
| 21 | return false; |
| 22 | |
| 23 | uint8_t *temp_buf = kmalloc(cluster_size); |
| 24 | uint32_t read = 0; |
| 25 | |
| 26 | // reach the starting offset |
| 27 | uint32_t skip_clusters = offset / cluster_size; |
| 28 | uint32_t skip_offset = offset % cluster_size; |
| 29 | |
| 30 | while (skip_clusters--) { |
| 31 | if (fat_is_eoc(fs, cluster)) |
| 32 | goto oops; |
| 33 | |
| 34 | cluster = fat_read_fat_entry(fs, cluster); |
| 35 | } |
| 36 | |
| 37 | while (read < size && !fat_is_eoc(fs, cluster)) { |
| 38 | if (!fat_read_cluster(fs, cluster, buffer: temp_buf)) |
| 39 | goto oops; |
| 40 | |
| 41 | uint32_t to_copy = cluster_size - skip_offset; |
| 42 | if (to_copy > (size - read)) |
| 43 | to_copy = size - read; |
| 44 | |
| 45 | memcpy(out_buf + read, temp_buf + skip_offset, to_copy); |
| 46 | read += to_copy; |
| 47 | |
| 48 | skip_offset = 0; |
| 49 | if (read < size) |
| 50 | cluster = fat_read_fat_entry(fs, cluster); |
| 51 | } |
| 52 | |
| 53 | kfree(temp_buf); |
| 54 | return true; |
| 55 | oops: |
| 56 | kfree(temp_buf); |
| 57 | return false; |
| 58 | } |
| 59 | |
| 60 | bool fat_write_file(struct fat_fs *fs, struct fat_dirent *ent, uint32_t offset, |
| 61 | const uint8_t *data, uint32_t size) { |
| 62 | if (!ent || !data) |
| 63 | return false; |
| 64 | |
| 65 | uint32_t cluster_size = fs->cluster_size; |
| 66 | uint32_t cluster = fat_get_dir_cluster(d: ent); |
| 67 | if (cluster == 0) { |
| 68 | cluster = fat_alloc_cluster(fs); |
| 69 | if (cluster == 0) |
| 70 | return false; |
| 71 | |
| 72 | ent->high_cluster = cluster >> 16; |
| 73 | ent->low_cluster = cluster & 0xFFFF; |
| 74 | |
| 75 | fat_write_fat_entry(fs, cluster, value: fat_eoc(fs)); |
| 76 | } |
| 77 | |
| 78 | uint32_t end_offset = offset + size; |
| 79 | uint32_t needed_clusters = (end_offset + cluster_size - 1) / cluster_size; |
| 80 | |
| 81 | // extend chain to needed length |
| 82 | uint32_t chain_len = 1; |
| 83 | uint32_t current = cluster; |
| 84 | while (!fat_is_eoc(fs, cluster: current)) { |
| 85 | current = fat_read_fat_entry(fs, cluster: current); |
| 86 | chain_len++; |
| 87 | } |
| 88 | |
| 89 | current = cluster; |
| 90 | if (chain_len < needed_clusters) { |
| 91 | for (uint32_t i = chain_len; i < needed_clusters; i++) { |
| 92 | uint32_t new_cluster = fat_alloc_cluster(fs); |
| 93 | if (!new_cluster) |
| 94 | return false; |
| 95 | |
| 96 | fat_write_fat_entry(fs, cluster: current, value: new_cluster); |
| 97 | current = new_cluster; |
| 98 | } |
| 99 | fat_write_fat_entry(fs, cluster: current, value: fat_eoc(fs)); |
| 100 | } |
| 101 | |
| 102 | // Reset to start and walk to offset |
| 103 | current = cluster; |
| 104 | uint32_t skip_clusters = offset / cluster_size; |
| 105 | uint32_t skip_offset = offset % cluster_size; |
| 106 | for (uint32_t i = 0; i < skip_clusters; i++) { |
| 107 | current = fat_read_fat_entry(fs, cluster: current); |
| 108 | } |
| 109 | |
| 110 | uint8_t *temp_buf = kmalloc(cluster_size); |
| 111 | uint32_t written = 0; |
| 112 | |
| 113 | while (written < size && !fat_is_eoc(fs, cluster: current)) { |
| 114 | if (skip_offset || (size - written) < cluster_size) { |
| 115 | // Partial write — read-modify-write |
| 116 | if (!fat_read_cluster(fs, cluster: current, buffer: temp_buf)) |
| 117 | goto oops; |
| 118 | } |
| 119 | |
| 120 | uint32_t to_write = cluster_size - skip_offset; |
| 121 | if (to_write > (size - written)) |
| 122 | to_write = size - written; |
| 123 | |
| 124 | memcpy(temp_buf + skip_offset, data + written, to_write); |
| 125 | |
| 126 | if (!fat_write_cluster(fs, cluster: current, buffer: temp_buf)) |
| 127 | goto oops; |
| 128 | |
| 129 | written += to_write; |
| 130 | skip_offset = 0; |
| 131 | |
| 132 | if (written < size) |
| 133 | current = fat_read_fat_entry(fs, cluster: current); |
| 134 | } |
| 135 | |
| 136 | if (end_offset > ent->filesize) |
| 137 | ent->filesize = end_offset; |
| 138 | |
| 139 | kfree(temp_buf); |
| 140 | return true; |
| 141 | oops: |
| 142 | kfree(temp_buf); |
| 143 | return false; |
| 144 | } |
| 145 | |