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 #include "CachedBlock.h" 12 #include "Directory.h" 13 #include "File.h" 14 15 #include <util/kernel_cpp.h> 16 #include <boot/FileMapDisk.h> 17 18 #include <stdint.h> 19 #include <stdlib.h> 20 #include <unistd.h> 21 #include <string.h> 22 23 //#define TRACE(x) dprintf x 24 #define TRACE(x) do {} while (0) 25 26 27 using namespace FATFS; 28 29 30 Stream::Stream(Volume &volume, uint32 chain, off_t size, const char *name) 31 : 32 fVolume(volume), 33 fFirstCluster(chain), 34 //fClusters(NULL), 35 fClusterMapCacheLast(0), 36 fSize(size) 37 { 38 TRACE(("FATFS::Stream::(, %d, %Ld, %s)\n", chain, size, name)); 39 fName[FATFS_NAME_LENGTH] = '\0'; 40 strlcpy(fName, name, FATFS_NAME_LENGTH+1); 41 fClusterCount = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize(); 42 if (size == UINT32_MAX) 43 fClusterCount = 10; // ? 44 for (int i = 0; i < CLUSTER_MAP_CACHE_SIZE; i++) { 45 fClusterMapCache[i].block = -1; 46 fClusterMapCache[i].cluster = fVolume.InvalidClusterID(); 47 } 48 } 49 50 51 Stream::~Stream() 52 { 53 TRACE(("FATFS::Stream::~()\n")); 54 } 55 56 57 status_t 58 Stream::InitCheck() 59 { 60 if (fSize && !fVolume.IsValidCluster(fFirstCluster)) 61 return B_BAD_VALUE; 62 return B_OK; 63 } 64 65 66 status_t 67 Stream::GetName(char *nameBuffer, size_t bufferSize) const 68 { 69 return strlcpy(nameBuffer, fName, bufferSize); 70 } 71 72 73 status_t 74 Stream::GetFileMap(struct file_map_run *runs, int32 *count) 75 { 76 int32 i; 77 uint32 cluster = fFirstCluster; 78 uint32 next = fVolume.InvalidClusterID(); 79 off_t offset = 0LL; 80 81 for (i = 0; i < *count; i++) { 82 runs[i].offset = offset; 83 runs[i].block = fVolume.ToBlock(cluster); 84 runs[i].len = fVolume.ClusterSize(); 85 do { 86 next = fVolume.NextCluster(cluster); 87 if (next != cluster + 1) 88 break; 89 runs[i].len += fVolume.ClusterSize(); 90 } while (true); 91 if (!fVolume.IsValidCluster(next)) 92 break; 93 cluster = next; 94 offset += runs[i].len; 95 } 96 97 // too big 98 if (i == *count && fVolume.IsValidCluster(next)) 99 return B_ERROR; 100 101 *count = i; 102 return B_OK; 103 } 104 105 106 status_t 107 Stream::FindBlock(off_t pos, off_t &block, off_t &offset) 108 { 109 //TRACE(("FATFS::Stream::%s(%Ld,,)\n", __FUNCTION__, pos)); 110 uint32 index = (uint32)(pos / fVolume.ClusterSize()); 111 if (pos >= fSize || index >= fClusterCount) 112 return B_BAD_VALUE; 113 114 uint32 cluster = 0; 115 bool found = false; 116 uint32 i; 117 for (i = 0; i < CLUSTER_MAP_CACHE_SIZE; i++) { 118 if (fClusterMapCache[i].block == index) { 119 cluster = fClusterMapCache[i].cluster; 120 found = true; 121 break; 122 } 123 } 124 if (!found) { 125 #if 1 126 uint32 count = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize(); 127 cluster = fFirstCluster; 128 if (fSize == UINT32_MAX) // it's a directory, try a large enough value 129 count = 10; 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 = fClusters[index]; 151 // convert to position 152 offset = fVolume.ToOffset(cluster); 153 offset += (pos %= fVolume.ClusterSize()); 154 // convert to block + offset 155 block = fVolume.ToBlock(offset); 156 offset %= fVolume.BlockSize(); 157 //TRACE(("FATFS::Stream::FindBlock: %Ld:%Ld\n", block, offset)); 158 return B_OK; 159 } 160 161 162 status_t 163 Stream::ReadAt(off_t pos, uint8 *buffer, size_t *_length) 164 { 165 TRACE(("FATFS::Stream::%s(%Ld, )\n", __FUNCTION__, pos)); 166 167 // set/check boundaries for pos/length 168 if (pos < 0) 169 return B_BAD_VALUE; 170 if (pos >= fSize) { 171 *_length = 0; 172 return B_OK; 173 } 174 175 #if 0 176 // lazily build the cluster list 177 if (!fClusters) { 178 status_t status = BuildClusterList(); 179 if (status != B_OK) 180 return status; 181 } 182 #endif 183 184 size_t length = *_length; 185 186 if (pos + length > fSize) 187 length = fSize - pos; 188 189 off_t num; // block number 190 off_t offset; 191 if (FindBlock(pos, num, offset) < B_OK) { 192 *_length = 0; 193 return B_BAD_VALUE; 194 } 195 196 uint32 bytesRead = 0; 197 uint32 blockSize = fVolume.BlockSize(); 198 uint8 *block; 199 200 // the first block_run we read could not be aligned to the block_size boundary 201 // (read partial block at the beginning) 202 203 // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 204 if (pos % blockSize != 0) { 205 CachedBlock cached(fVolume, num); 206 if ((block = cached.Block()) == NULL) { 207 *_length = 0; 208 return B_BAD_VALUE; 209 } 210 211 bytesRead = blockSize - (pos % blockSize); 212 if (length < bytesRead) 213 bytesRead = length; 214 215 memcpy(buffer, block + (pos % blockSize), bytesRead); 216 pos += bytesRead; 217 218 length -= bytesRead; 219 if (length == 0) { 220 *_length = bytesRead; 221 return B_OK; 222 } 223 224 if (FindBlock(pos, num, offset) < B_OK) { 225 *_length = bytesRead; 226 return B_BAD_VALUE; 227 } 228 } 229 230 // the first block_run is already filled in at this point 231 // read the following complete blocks using cached_read(), 232 // the last partial block is read using the generic Cache class 233 234 bool partial = false; 235 236 while (length > 0) { 237 // offset is the offset to the current pos in the block_run 238 239 if (length < blockSize) { 240 CachedBlock cached(fVolume, num); 241 if ((block = cached.Block()) == NULL) { 242 *_length = bytesRead; 243 return B_BAD_VALUE; 244 } 245 memcpy(buffer + bytesRead, block, length); 246 bytesRead += length; 247 partial = true; 248 break; 249 } 250 251 if (read_pos(fVolume.Device(), fVolume.ToOffset(num), 252 buffer + bytesRead, fVolume.BlockSize()) < B_OK) { 253 *_length = bytesRead; 254 return B_BAD_VALUE; 255 } 256 257 int32 bytes = fVolume.BlockSize(); 258 length -= bytes; 259 bytesRead += bytes; 260 if (length == 0) 261 break; 262 263 pos += bytes; 264 265 if (FindBlock(pos, num, offset) < B_OK) { 266 *_length = bytesRead; 267 return B_BAD_VALUE; 268 } 269 } 270 271 *_length = bytesRead; 272 return B_OK; 273 } 274 275 276 status_t 277 Stream::BuildClusterList() 278 { 279 #if 0 280 TRACE(("FATFS::Stream::%s()\n", __FUNCTION__)); 281 uint32 count = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize(); 282 uint32 c = fFirstCluster; 283 int i; 284 285 if (fSize == UINT32_MAX) // it's a directory, try a large enough value 286 count = 10; 287 //fClusters = (uint32 *)malloc(count * sizeof(uint32)); 288 for (i = 0; i < count && fVolume.IsValidCluster(c); i++) { 289 if (fVolume.IsLastCluster(c)) 290 break; 291 TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, c)); 292 fClusters[i] = c; 293 c = fVolume.NextCluster(c); 294 TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, c)); 295 // XXX: try to realloc() for dirs maybe ? 296 } 297 fClusterCount = i; 298 TRACE(("FATFS::Stream::%s: %d clusters in chain\n", __FUNCTION__, i)); 299 #endif 300 return B_OK; 301 } 302