1/* 2 Copyright 1999-2001, Be Incorporated. All Rights Reserved. 3 This file may be used under the terms of the Be Sample Code License. 4*/ 5 6 7#include "iter.h" 8 9#include <string.h> 10 11#include <fs_cache.h> 12#include <fs_info.h> 13#include <KernelExport.h> 14 15#include "dosfs.h" 16#include "fat.h" 17#include "util.h" 18 19 20#define DPRINTF(a,b) if (debug_iter > (a)) dprintf b 21 22 23static int 24_validate_cs_(nspace *vol, uint32 cluster, uint32 sector) 25{ 26 if (sector < 0) return -1; 27 28 if ((vol->fat_bits != 32) && IS_FIXED_ROOT(cluster)) { // fat12 or fat16 root 29 if (sector >= vol->root_sectors) 30 return -1; 31 return 0; 32 } 33 34 if (sector >= vol->sectors_per_cluster) return -1; 35 36 if (!IS_DATA_CLUSTER(cluster)) return -1; 37 38 return 0; 39} 40 41 42off_t 43csi_to_block(struct csi *csi) 44{ 45 // presumes the caller has already called _validate_cs_ on the argument 46 ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0); 47 if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0) 48 return EINVAL; 49 50 if (IS_FIXED_ROOT(csi->cluster)) 51 return csi->vol->root_start + csi->sector; 52 53 return csi->vol->data_start + 54 (off_t)(csi->cluster - 2)* csi->vol->sectors_per_cluster + 55 csi->sector; 56} 57 58 59int 60init_csi(nspace *vol, uint32 cluster, uint32 sector, struct csi *csi) 61{ 62 int ret; 63 if ((ret = _validate_cs_(vol,cluster,sector)) != 0) 64 return ret; 65 66 csi->vol = vol; csi->cluster = cluster; csi->sector = sector; 67 68 return 0; 69} 70 71 72int 73iter_csi(struct csi *csi, int sectors) 74{ 75 if (csi->sector == 0xffff) // check if already at end of chain 76 return -1; 77 78 if (sectors < 0) 79 return EINVAL; 80 81 if (sectors == 0) 82 return 0; 83 84 if (IS_FIXED_ROOT(csi->cluster)) { 85 csi->sector += sectors; 86 if (csi->sector < csi->vol->root_sectors) 87 return 0; 88 } else { 89 csi->sector += sectors; 90 if (csi->sector < csi->vol->sectors_per_cluster) 91 return 0; 92 csi->cluster = get_nth_fat_entry(csi->vol, csi->cluster, 93 csi->sector / csi->vol->sectors_per_cluster); 94 95 if ((int32)csi->cluster < 0) { 96 csi->sector = 0xffff; 97 return csi->cluster; 98 } 99 100 if (vIS_DATA_CLUSTER(csi->vol,csi->cluster)) { 101 csi->sector %= csi->vol->sectors_per_cluster; 102 return 0; 103 } 104 } 105 106 csi->sector = 0xffff; 107 108 return -1; 109} 110 111 112uint8 * 113csi_get_block(struct csi *csi) 114{ 115 if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0) 116 return NULL; 117 118 return (uint8 *)block_cache_get_etc(csi->vol->fBlockCache, 119 csi_to_block(csi), 1, csi->vol->bytes_per_sector); 120} 121 122 123status_t 124csi_release_block(struct csi *csi) 125{ 126 if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0) 127 return EINVAL; 128 129 block_cache_put(csi->vol->fBlockCache, csi_to_block(csi)); 130 return B_OK; 131} 132 133 134status_t 135csi_make_writable(struct csi *csi) 136{ 137 if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0) 138 return EINVAL; 139 140 return block_cache_make_writable(csi->vol->fBlockCache, csi_to_block(csi), 141 -1); 142} 143 144 145/* XXX: not the most efficient implementation, but it gets the job done */ 146status_t 147csi_read_blocks(struct csi *csi, uint8 *buffer, ssize_t len) 148{ 149 struct csi old_csi; 150 uint32 sectors; 151 off_t block; 152 status_t err; 153 char *buf = buffer; 154 int32 i; 155 156 ASSERT(len >= csi->vol->bytes_per_sector); 157 158 if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0) 159 return EINVAL; 160 161 sectors = 1; 162 block = csi_to_block(csi); 163 164 while (1) { 165 old_csi = *csi; 166 err = iter_csi(csi, 1); 167 if (len < (sectors + 1) * csi->vol->bytes_per_sector) 168 break; 169 if ((err < B_OK) || (block + sectors != csi_to_block(csi))) 170 break; 171 sectors++; 172 } 173 174 for (i = block; i < block + sectors; i++) { 175 char *blockData = (char *)block_cache_get(csi->vol->fBlockCache, i); 176 memcpy(buf, blockData, csi->vol->bytes_per_sector); 177 buf += csi->vol->bytes_per_sector; 178 block_cache_put(csi->vol->fBlockCache, i); 179 } 180 181 *csi = old_csi; 182 183 return sectors * csi->vol->bytes_per_sector; 184} 185 186 187status_t 188csi_write_blocks(struct csi *csi, uint8 *buffer, ssize_t len) 189{ 190 struct csi old_csi; 191 uint32 sectors; 192 off_t block; 193 status_t err; 194 char *buf = buffer; 195 int32 i; 196 197 ASSERT(len >= csi->vol->bytes_per_sector); 198 199 ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0); 200 if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0) 201 return EINVAL; 202 203 sectors = 1; 204 block = csi_to_block(csi); 205 206 while (1) { 207 old_csi = *csi; 208 err = iter_csi(csi, 1); 209 if (len < (sectors + 1) * csi->vol->bytes_per_sector) 210 break; 211 if ((err < B_OK) || (block + sectors != csi_to_block(csi))) 212 break; 213 sectors++; 214 } 215 216 for (i = block; i < block + sectors; i++) { 217 char *blockData = block_cache_get_writable_etc(csi->vol->fBlockCache, i, 218 0, 1, -1); 219 memcpy(blockData, buf, csi->vol->bytes_per_sector); 220 buf += csi->vol->bytes_per_sector; 221 block_cache_put(csi->vol->fBlockCache, i); 222 } 223 224 /* return the last state of the iterator because that's what dosfs_write 225 * expects. this lets it meaningfully cache the state even when it's 226 * writing to the end of the file. */ 227 *csi = old_csi; 228 229 return sectors * csi->vol->bytes_per_sector; 230} 231 232 233status_t 234csi_write_block(struct csi *csi, uint8 *buffer) 235{ 236 off_t block; 237 char *blockData; 238 239 block = csi_to_block(csi); 240 241 ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0); 242 if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0) 243 return EINVAL; 244 245 blockData = block_cache_get_writable_etc(csi->vol->fBlockCache, block, 0, 1, 246 -1); 247 memcpy(blockData, buffer, csi->vol->bytes_per_sector); 248 block_cache_put(csi->vol->fBlockCache, block); 249 250 return B_OK; 251} 252 253 254// #pragma mark - 255 256 257static void 258_diri_release_current_block_(struct diri *diri) 259{ 260 ASSERT(diri->current_block); 261 if (diri->current_block == NULL) 262 return; 263 csi_release_block(&(diri->csi)); 264 diri->current_block = NULL; 265} 266 267 268uint8 * 269diri_init(nspace *vol, uint32 cluster, uint32 index, struct diri *diri) 270{ 271 diri->current_block = NULL; 272 273 if (cluster >= vol->total_clusters + 2) 274 return NULL; 275 276 if (init_csi(vol,cluster,0,&(diri->csi)) != 0) 277 return NULL; 278 279 diri->starting_cluster = cluster; 280 diri->current_index = index; 281 if (index >= vol->bytes_per_sector / 0x20 282 && iter_csi(&(diri->csi), diri->current_index 283 / (vol->bytes_per_sector / 0x20)) != 0) 284 return NULL; 285 286 // get current sector 287 diri->current_block = csi_get_block(&diri->csi); 288 289 if (diri->current_block == NULL) 290 return NULL; 291 292 // now the diri is valid 293 return diri->current_block 294 + (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20; 295} 296 297 298int 299diri_free(struct diri *diri) 300{ 301 if (diri->current_block) 302 _diri_release_current_block_(diri); 303 304 return 0; 305} 306 307 308uint8 * 309diri_current_entry(struct diri *diri) 310{ 311 if (diri->current_block == NULL) 312 return NULL; 313 314 return diri->current_block 315 + (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20; 316} 317 318 319uint8 * 320diri_next_entry(struct diri *diri) 321{ 322 if (diri->current_block == NULL) 323 return NULL; 324 325 if ((++diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20)) == 0) { 326 _diri_release_current_block_(diri); 327 if (iter_csi(&(diri->csi), 1) != 0) 328 return NULL; 329 diri->current_block = csi_get_block(&(diri->csi)); 330 if (diri->current_block == NULL) 331 return NULL; 332 } 333 334 return diri->current_block 335 + (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20; 336} 337 338 339uint8 * 340diri_rewind(struct diri *diri) 341{ 342 if (diri->current_index > (diri->csi.vol->bytes_per_sector / 0x20 - 1)) { 343 if (diri->current_block) 344 _diri_release_current_block_(diri); 345 if (init_csi(diri->csi.vol, diri->starting_cluster, 0, &(diri->csi)) != 0) 346 return NULL; 347 diri->current_block = csi_get_block(&diri->csi); 348 } 349 diri->current_index = 0; 350 return diri->current_block; 351} 352 353 354void 355diri_make_writable(struct diri *diri) 356{ 357 csi_make_writable(&diri->csi); 358} 359