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