1 /* 2 * Copyright 2008, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * François Revol <revol@free.fr> 7 */ 8 9 10 #include "Stream.h" 11 12 #include <stdint.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <unistd.h> 16 17 #include <algorithm> 18 19 #include <boot/FileMapDisk.h> 20 21 #include "CachedBlock.h" 22 #include "Directory.h" 23 #include "File.h" 24 25 26 //#define TRACE(x) dprintf x 27 #define TRACE(x) do {} while (0) 28 29 30 using namespace FATFS; 31 32 33 Stream::Stream(Volume &volume, uint32 chain, off_t size, const char *name) 34 : 35 fVolume(volume), 36 fFirstCluster(chain), 37 //fClusters(NULL), 38 fClusterMapCacheLast(0), 39 fSize(size) 40 { 41 TRACE(("FATFS::Stream::(, %d, %Ld, %s)\n", chain, size, name)); 42 fName[FATFS_NAME_LENGTH] = '\0'; 43 strlcpy(fName, name, FATFS_NAME_LENGTH+1); 44 fClusterCount = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize(); 45 if (size == UINT32_MAX) 46 fClusterCount = 10; // ? 47 for (int i = 0; i < CLUSTER_MAP_CACHE_SIZE; i++) { 48 fClusterMapCache[i].block = -1; 49 fClusterMapCache[i].cluster = fVolume.InvalidClusterID(); 50 } 51 } 52 53 54 Stream::~Stream() 55 { 56 TRACE(("FATFS::Stream::~()\n")); 57 } 58 59 60 status_t 61 Stream::InitCheck() 62 { 63 if (fSize && !fVolume.IsValidCluster(fFirstCluster)) 64 return B_BAD_VALUE; 65 return B_OK; 66 } 67 68 69 status_t 70 Stream::GetName(char *nameBuffer, size_t bufferSize) const 71 { 72 return strlcpy(nameBuffer, fName, bufferSize); 73 } 74 75 76 status_t 77 Stream::GetFileMap(struct file_map_run *runs, int32 *count) 78 { 79 int32 i; 80 uint32 cluster = fFirstCluster; 81 uint32 next = fVolume.InvalidClusterID(); 82 off_t offset = 0LL; 83 84 for (i = 0; i < *count; i++) { 85 runs[i].offset = offset; 86 runs[i].block = fVolume.ToBlock(cluster); 87 runs[i].len = fVolume.ClusterSize(); 88 do { 89 next = fVolume.NextCluster(cluster); 90 if (next != cluster + 1) 91 break; 92 runs[i].len += fVolume.ClusterSize(); 93 } while (true); 94 if (!fVolume.IsValidCluster(next)) 95 break; 96 cluster = next; 97 offset += runs[i].len; 98 } 99 100 // too big 101 if (i == *count && fVolume.IsValidCluster(next)) 102 return B_ERROR; 103 104 *count = i; 105 return B_OK; 106 } 107 108 109 status_t 110 Stream::_FindCluster(off_t pos, uint32& _cluster) 111 { 112 //TRACE(("FATFS::Stream::%s(%Ld,,)\n", __FUNCTION__, pos)); 113 uint32 index = (uint32)(pos / fVolume.ClusterSize()); 114 if (pos > fSize || index >= fClusterCount) 115 return B_BAD_VALUE; 116 117 uint32 cluster = 0; 118 bool found = false; 119 uint32 i; 120 for (i = 0; i < CLUSTER_MAP_CACHE_SIZE; i++) { 121 if (fClusterMapCache[i].block == index) { 122 cluster = fClusterMapCache[i].cluster; 123 found = true; 124 break; 125 } 126 } 127 if (!found) { 128 #if 1 129 uint32 count = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize(); 130 cluster = fFirstCluster; 131 if (fSize == UINT32_MAX) // it's a directory, try a large enough value 132 count = 10; 133 for (i = 0; i < index && fVolume.IsValidCluster(cluster); i++) { 134 if (fVolume.IsLastCluster(cluster)) 135 break; 136 //TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, cluster)); 137 cluster = fVolume.NextCluster(cluster); 138 //TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, cluster)); 139 } 140 #endif 141 #if 0 142 cluster = fVolume.NextCluster(cluster, index); 143 #endif 144 } 145 if (!fVolume.IsValidCluster(cluster)) 146 return B_ENTRY_NOT_FOUND; 147 148 fClusterMapCache[fClusterMapCacheLast].block = index; 149 fClusterMapCache[fClusterMapCacheLast].cluster = cluster; 150 fClusterMapCacheLast++; 151 fClusterMapCacheLast %= CLUSTER_MAP_CACHE_SIZE; 152 153 _cluster = cluster; 154 return B_OK; 155 } 156 157 158 status_t 159 Stream::_FindOrCreateCluster(off_t pos, uint32& _cluster, bool& _added) 160 { 161 status_t error = _FindCluster(pos, _cluster); 162 if (error == B_OK) { 163 _added = false; 164 return B_OK; 165 } 166 167 // iterate through the cluster list 168 uint32 index = (uint32)(pos / fVolume.ClusterSize()); 169 uint32 cluster = fFirstCluster; 170 uint32 clusterCount = 0; 171 if (cluster != 0) { 172 uint32 nextCluster = cluster; 173 while (clusterCount <= index) { 174 if (!fVolume.IsValidCluster(nextCluster) 175 || fVolume.IsLastCluster(nextCluster)) { 176 break; 177 } 178 179 cluster = nextCluster; 180 clusterCount++; 181 nextCluster = fVolume.NextCluster(nextCluster); 182 } 183 } 184 185 if (clusterCount > index) { 186 // the cluster existed after all 187 _cluster = cluster; 188 _added = false; 189 return B_OK; 190 } 191 192 while (clusterCount <= index) { 193 uint32 newCluster; 194 error = fVolume.AllocateCluster(cluster, newCluster); 195 if (error != B_OK) 196 return error; 197 198 if (clusterCount == 0) 199 fFirstCluster = newCluster; 200 201 // TODO: We should support to zero out the new cluster. Maybe make this 202 // and optional parameter of WriteAt(). 203 204 cluster = newCluster; 205 clusterCount++; 206 } 207 208 _cluster = cluster; 209 _added = true; 210 return B_OK; 211 } 212 213 214 status_t 215 Stream::FindBlock(off_t pos, off_t &block, off_t &offset) 216 { 217 uint32 cluster; 218 status_t error = _FindCluster(pos, cluster); 219 if (error != B_OK) 220 return error; 221 222 // convert to position 223 offset = fVolume.ClusterToOffset(cluster); 224 offset += (pos %= fVolume.ClusterSize()); 225 226 // convert to block + offset 227 block = fVolume.ToBlock(offset); 228 offset %= fVolume.BlockSize(); 229 230 return B_OK; 231 } 232 233 234 status_t 235 Stream::ReadAt(off_t pos, void *_buffer, size_t *_length, off_t *diskOffset) 236 { 237 TRACE(("FATFS::Stream::%s(%Ld, )\n", __FUNCTION__, pos)); 238 239 uint8* buffer = (uint8*)_buffer; 240 241 // set/check boundaries for pos/length 242 if (pos < 0) 243 return B_BAD_VALUE; 244 if (pos >= fSize) { 245 *_length = 0; 246 return B_OK; 247 } 248 249 #if 0 250 // lazily build the cluster list 251 if (!fClusters) { 252 status_t status = BuildClusterList(); 253 if (status != B_OK) 254 return status; 255 } 256 #endif 257 258 size_t length = *_length; 259 260 if (pos + length > fSize) 261 length = fSize - pos; 262 263 off_t num; // block number 264 off_t offset; 265 if (FindBlock(pos, num, offset) < B_OK) { 266 *_length = 0; 267 return B_BAD_VALUE; 268 } 269 270 if (diskOffset != NULL) 271 *diskOffset = fVolume.BlockToOffset(num) + offset; 272 273 uint32 bytesRead = 0; 274 uint32 blockSize = fVolume.BlockSize(); 275 uint8 *block; 276 277 // the first block_run we read could not be aligned to the block_size boundary 278 // (read partial block at the beginning) 279 280 // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 281 if (pos % blockSize != 0) { 282 CachedBlock cached(fVolume, num); 283 if ((block = cached.Block()) == NULL) { 284 *_length = 0; 285 return B_BAD_VALUE; 286 } 287 288 bytesRead = blockSize - (pos % blockSize); 289 if (length < bytesRead) 290 bytesRead = length; 291 292 memcpy(buffer, block + (pos % blockSize), bytesRead); 293 pos += bytesRead; 294 295 length -= bytesRead; 296 if (length == 0) { 297 *_length = bytesRead; 298 return B_OK; 299 } 300 301 if (FindBlock(pos, num, offset) < B_OK) { 302 *_length = bytesRead; 303 return B_BAD_VALUE; 304 } 305 } 306 307 // the first block_run is already filled in at this point 308 // read the following complete blocks using cached_read(), 309 // the last partial block is read using the generic Cache class 310 311 bool partial = false; 312 313 while (length > 0) { 314 // offset is the offset to the current pos in the block_run 315 316 if (length < blockSize) { 317 CachedBlock cached(fVolume, num); 318 if ((block = cached.Block()) == NULL) { 319 *_length = bytesRead; 320 return B_BAD_VALUE; 321 } 322 memcpy(buffer + bytesRead, block, length); 323 bytesRead += length; 324 partial = true; 325 break; 326 } 327 328 if (read_pos(fVolume.Device(), fVolume.BlockToOffset(num), 329 buffer + bytesRead, fVolume.BlockSize()) < B_OK) { 330 *_length = bytesRead; 331 return B_BAD_VALUE; 332 } 333 334 int32 bytes = fVolume.BlockSize(); 335 length -= bytes; 336 bytesRead += bytes; 337 if (length == 0) 338 break; 339 340 pos += bytes; 341 342 if (FindBlock(pos, num, offset) < B_OK) { 343 *_length = bytesRead; 344 return B_BAD_VALUE; 345 } 346 } 347 348 *_length = bytesRead; 349 return B_OK; 350 } 351 352 353 status_t 354 Stream::WriteAt(off_t pos, const void* _buffer, size_t* _length, 355 off_t* diskOffset) 356 { 357 if (pos < 0) 358 return B_BAD_VALUE; 359 360 const uint8* buffer = (const uint8*)_buffer; 361 size_t length = *_length; 362 size_t totalWritten = 0; 363 status_t error = B_OK; 364 365 CachedBlock cachedBlock(fVolume); 366 367 while (length > 0) { 368 // get the cluster 369 uint32 cluster; 370 bool added; 371 error = _FindOrCreateCluster(pos, cluster, added); 372 if (error != B_OK) 373 break; 374 375 // convert to position 376 off_t inClusterOffset = pos % fVolume.ClusterSize(); 377 off_t offset = fVolume.ClusterToOffset(cluster) + inClusterOffset; 378 379 if (diskOffset != NULL) { 380 *diskOffset = offset; 381 diskOffset = NULL; 382 } 383 384 // convert to block + offset 385 off_t block = fVolume.ToBlock(offset); 386 size_t inBlockOffset = offset % fVolume.BlockSize(); 387 388 // write 389 size_t toWrite = std::min(fVolume.BlockSize() - inBlockOffset, length); 390 if (toWrite == (size_t)fVolume.BlockSize()) { 391 // write the whole block 392 ssize_t written = write_pos(fVolume.Device(), 393 fVolume.BlockToOffset(block), buffer, fVolume.BlockSize()); 394 if (written < 0) { 395 error = written; 396 break; 397 } 398 if (written != fVolume.BlockSize()) { 399 error = B_ERROR; 400 break; 401 } 402 } else { 403 // write a partial block -- need to read it from disk first 404 error = cachedBlock.SetTo(block, CachedBlock::READ); 405 if (error != B_OK) 406 break; 407 408 memcpy(cachedBlock.Block() + inBlockOffset, buffer, toWrite); 409 410 error = cachedBlock.Flush(); 411 if (error != B_OK) 412 break; 413 } 414 415 totalWritten += toWrite; 416 pos += toWrite; 417 buffer += toWrite; 418 length -= toWrite; 419 420 if (pos > fSize) 421 fSize = pos; 422 } 423 424 *_length = totalWritten; 425 return totalWritten > 0 ? B_OK : error; 426 } 427 428 429 status_t 430 Stream::BuildClusterList() 431 { 432 #if 0 433 TRACE(("FATFS::Stream::%s()\n", __FUNCTION__)); 434 uint32 count = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize(); 435 uint32 c = fFirstCluster; 436 int i; 437 438 if (fSize == UINT32_MAX) // it's a directory, try a large enough value 439 count = 10; 440 //fClusters = (uint32 *)malloc(count * sizeof(uint32)); 441 for (i = 0; i < count && fVolume.IsValidCluster(c); i++) { 442 if (fVolume.IsLastCluster(c)) 443 break; 444 TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, c)); 445 fClusters[i] = c; 446 c = fVolume.NextCluster(c); 447 TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, c)); 448 // XXX: try to realloc() for dirs maybe ? 449 } 450 fClusterCount = i; 451 TRACE(("FATFS::Stream::%s: %d clusters in chain\n", __FUNCTION__, i)); 452 #endif 453 return B_OK; 454 } 455