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, %lld, %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(%lld,,)\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 cluster = fFirstCluster; 130 for (i = 0; i < index && fVolume.IsValidCluster(cluster); i++) { 131 if (fVolume.IsLastCluster(cluster)) 132 break; 133 //TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, cluster)); 134 cluster = fVolume.NextCluster(cluster); 135 //TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, cluster)); 136 } 137 #endif 138 #if 0 139 cluster = fVolume.NextCluster(cluster, index); 140 #endif 141 } 142 if (!fVolume.IsValidCluster(cluster)) 143 return B_ENTRY_NOT_FOUND; 144 145 fClusterMapCache[fClusterMapCacheLast].block = index; 146 fClusterMapCache[fClusterMapCacheLast].cluster = cluster; 147 fClusterMapCacheLast++; 148 fClusterMapCacheLast %= CLUSTER_MAP_CACHE_SIZE; 149 150 _cluster = cluster; 151 return B_OK; 152 } 153 154 155 status_t 156 Stream::_FindOrCreateCluster(off_t pos, uint32& _cluster, bool& _added) 157 { 158 status_t error = _FindCluster(pos, _cluster); 159 if (error == B_OK) { 160 _added = false; 161 return B_OK; 162 } 163 164 // iterate through the cluster list 165 uint32 index = (uint32)(pos / fVolume.ClusterSize()); 166 uint32 cluster = fFirstCluster; 167 uint32 clusterCount = 0; 168 if (cluster != 0) { 169 uint32 nextCluster = cluster; 170 while (clusterCount <= index) { 171 if (!fVolume.IsValidCluster(nextCluster) 172 || fVolume.IsLastCluster(nextCluster)) { 173 break; 174 } 175 176 cluster = nextCluster; 177 clusterCount++; 178 nextCluster = fVolume.NextCluster(nextCluster); 179 } 180 } 181 182 if (clusterCount > index) { 183 // the cluster existed after all 184 _cluster = cluster; 185 _added = false; 186 return B_OK; 187 } 188 189 while (clusterCount <= index) { 190 uint32 newCluster; 191 error = fVolume.AllocateCluster(cluster, newCluster); 192 if (error != B_OK) 193 return error; 194 195 if (clusterCount == 0) 196 fFirstCluster = newCluster; 197 198 // TODO: We should support to zero out the new cluster. Maybe make this 199 // and optional parameter of WriteAt(). 200 201 cluster = newCluster; 202 clusterCount++; 203 } 204 205 _cluster = cluster; 206 _added = true; 207 return B_OK; 208 } 209 210 211 status_t 212 Stream::FindBlock(off_t pos, off_t &block, off_t &offset) 213 { 214 uint32 cluster; 215 status_t error = _FindCluster(pos, cluster); 216 if (error != B_OK) 217 return error; 218 219 // convert to position 220 offset = fVolume.ClusterToOffset(cluster); 221 offset += (pos %= fVolume.ClusterSize()); 222 223 // convert to block + offset 224 block = fVolume.ToBlock(offset); 225 offset %= fVolume.BlockSize(); 226 227 return B_OK; 228 } 229 230 231 status_t 232 Stream::ReadAt(off_t pos, void *_buffer, size_t *_length, off_t *diskOffset) 233 { 234 TRACE(("FATFS::Stream::%s(%lld, )\n", __FUNCTION__, pos)); 235 236 uint8* buffer = (uint8*)_buffer; 237 238 // set/check boundaries for pos/length 239 if (pos < 0) 240 return B_BAD_VALUE; 241 if (pos >= fSize) { 242 *_length = 0; 243 return B_OK; 244 } 245 246 #if 0 247 // lazily build the cluster list 248 if (!fClusters) { 249 status_t status = BuildClusterList(); 250 if (status != B_OK) 251 return status; 252 } 253 #endif 254 255 size_t length = *_length; 256 257 if (pos + (off_t)length > fSize) 258 length = fSize - pos; 259 260 off_t num; // block number 261 off_t offset; 262 if (FindBlock(pos, num, offset) < B_OK) { 263 *_length = 0; 264 return B_BAD_VALUE; 265 } 266 267 if (diskOffset != NULL) 268 *diskOffset = fVolume.BlockToOffset(num) + offset; 269 270 uint32 bytesRead = 0; 271 uint32 blockSize = fVolume.BlockSize(); 272 uint8 *block; 273 274 // the first block_run we read could not be aligned to the block_size boundary 275 // (read partial block at the beginning) 276 277 // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 278 if (pos % blockSize != 0) { 279 CachedBlock cached(fVolume, num); 280 if ((block = cached.Block()) == NULL) { 281 *_length = 0; 282 return B_BAD_VALUE; 283 } 284 285 bytesRead = blockSize - (pos % blockSize); 286 if (length < bytesRead) 287 bytesRead = length; 288 289 memcpy(buffer, block + (pos % blockSize), bytesRead); 290 pos += bytesRead; 291 292 length -= bytesRead; 293 if (length == 0) { 294 *_length = bytesRead; 295 return B_OK; 296 } 297 298 if (FindBlock(pos, num, offset) < B_OK) { 299 *_length = bytesRead; 300 return B_BAD_VALUE; 301 } 302 } 303 304 // the first block_run is already filled in at this point 305 // read the following complete blocks using cached_read(), 306 // the last partial block is read using the generic Cache class 307 308 while (length > 0) { 309 // offset is the offset to the current pos in the block_run 310 311 if (length < blockSize) { 312 CachedBlock cached(fVolume, num); 313 if ((block = cached.Block()) == NULL) { 314 *_length = bytesRead; 315 return B_BAD_VALUE; 316 } 317 memcpy(buffer + bytesRead, block, length); 318 bytesRead += length; 319 break; 320 } 321 322 if (read_pos(fVolume.Device(), fVolume.BlockToOffset(num), 323 buffer + bytesRead, fVolume.BlockSize()) < B_OK) { 324 *_length = bytesRead; 325 return B_BAD_VALUE; 326 } 327 328 int32 bytes = fVolume.BlockSize(); 329 length -= bytes; 330 bytesRead += bytes; 331 if (length == 0) 332 break; 333 334 pos += bytes; 335 336 if (FindBlock(pos, num, offset) < B_OK) { 337 *_length = bytesRead; 338 return B_BAD_VALUE; 339 } 340 } 341 342 *_length = bytesRead; 343 return B_OK; 344 } 345 346 347 status_t 348 Stream::WriteAt(off_t pos, const void* _buffer, size_t* _length, 349 off_t* diskOffset) 350 { 351 if (pos < 0) 352 return B_BAD_VALUE; 353 354 const uint8* buffer = (const uint8*)_buffer; 355 size_t length = *_length; 356 size_t totalWritten = 0; 357 status_t error = B_OK; 358 359 CachedBlock cachedBlock(fVolume); 360 361 while (length > 0) { 362 // get the cluster 363 uint32 cluster; 364 bool added; 365 error = _FindOrCreateCluster(pos, cluster, added); 366 if (error != B_OK) 367 break; 368 369 // convert to position 370 off_t inClusterOffset = pos % fVolume.ClusterSize(); 371 off_t offset = fVolume.ClusterToOffset(cluster) + inClusterOffset; 372 373 if (diskOffset != NULL) { 374 *diskOffset = offset; 375 diskOffset = NULL; 376 } 377 378 // convert to block + offset 379 off_t block = fVolume.ToBlock(offset); 380 size_t inBlockOffset = offset % fVolume.BlockSize(); 381 382 // write 383 size_t toWrite = std::min((size_t)fVolume.BlockSize() - inBlockOffset, 384 length); 385 if (toWrite == (size_t)fVolume.BlockSize()) { 386 // write the whole block 387 ssize_t written = write_pos(fVolume.Device(), 388 fVolume.BlockToOffset(block), buffer, fVolume.BlockSize()); 389 if (written < 0) { 390 error = written; 391 break; 392 } 393 if (written != fVolume.BlockSize()) { 394 error = B_ERROR; 395 break; 396 } 397 } else { 398 // write a partial block -- need to read it from disk first 399 error = cachedBlock.SetTo(block, CachedBlock::READ); 400 if (error != B_OK) 401 break; 402 403 memcpy(cachedBlock.Block() + inBlockOffset, buffer, toWrite); 404 405 error = cachedBlock.Flush(); 406 if (error != B_OK) 407 break; 408 } 409 410 totalWritten += toWrite; 411 pos += toWrite; 412 buffer += toWrite; 413 length -= toWrite; 414 415 if (pos > fSize) 416 fSize = pos; 417 } 418 419 *_length = totalWritten; 420 return totalWritten > 0 ? B_OK : error; 421 } 422 423 424 status_t 425 Stream::BuildClusterList() 426 { 427 #if 0 428 TRACE(("FATFS::Stream::%s()\n", __FUNCTION__)); 429 uint32 count = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize(); 430 uint32 c = fFirstCluster; 431 int i; 432 433 if (fSize == UINT32_MAX) // it's a directory, try a large enough value 434 count = 10; 435 //fClusters = (uint32 *)malloc(count * sizeof(uint32)); 436 for (i = 0; i < count && fVolume.IsValidCluster(c); i++) { 437 if (fVolume.IsLastCluster(c)) 438 break; 439 TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, c)); 440 fClusters[i] = c; 441 c = fVolume.NextCluster(c); 442 TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, c)); 443 // XXX: try to realloc() for dirs maybe ? 444 } 445 fClusterCount = i; 446 TRACE(("FATFS::Stream::%s: %d clusters in chain\n", __FUNCTION__, i)); 447 #endif 448 return B_OK; 449 } 450