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 uint32 cluster; 112 if (pos >= fSize) 113 return B_BAD_VALUE; 114 if (index >= fClusterCount) 115 return B_BAD_VALUE; 116 117 bool found = false; 118 int i; 119 for (i = 0; i < CLUSTER_MAP_CACHE_SIZE; i++) { 120 if (fClusterMapCache[i].block == index) { 121 cluster = fClusterMapCache[i].cluster; 122 found = true; 123 break; 124 } 125 } 126 if (!found) { 127 #if 1 128 uint32 count = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize(); 129 cluster = fFirstCluster; 130 if (fSize == UINT32_MAX) // it's a directory, try a large enough value 131 count = 10; 132 for (i = 0; i < index && fVolume.IsValidCluster(cluster); i++) { 133 if (fVolume.IsLastCluster(cluster)) 134 break; 135 //TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, cluster)); 136 cluster = fVolume.NextCluster(cluster); 137 //TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, cluster)); 138 } 139 #endif 140 #if 0 141 cluster = fVolume.NextCluster(cluster, index); 142 #endif 143 } 144 if (!fVolume.IsValidCluster(cluster)) 145 return ENOENT; 146 fClusterMapCache[fClusterMapCacheLast].block = index; 147 fClusterMapCache[fClusterMapCacheLast].cluster = cluster; 148 fClusterMapCacheLast++; 149 fClusterMapCacheLast %= CLUSTER_MAP_CACHE_SIZE; 150 151 //cluster = fClusters[index]; 152 // convert to position 153 offset = fVolume.ToOffset(cluster); 154 offset += (pos %= fVolume.ClusterSize()); 155 // convert to block + offset 156 block = fVolume.ToBlock(offset); 157 offset %= fVolume.BlockSize(); 158 //TRACE(("FATFS::Stream::FindBlock: %Ld:%Ld\n", block, offset)); 159 return B_OK; 160 } 161 162 163 status_t 164 Stream::ReadAt(off_t pos, uint8 *buffer, size_t *_length) 165 { 166 TRACE(("FATFS::Stream::%s(%Ld, )\n", __FUNCTION__, pos)); 167 status_t err; 168 // set/check boundaries for pos/length 169 170 if (pos < 0) 171 return B_BAD_VALUE; 172 if (pos >= fSize) { 173 *_length = 0; 174 return B_NO_ERROR; 175 } 176 177 #if 0 178 // lazily build the cluster list 179 if (!fClusters) { 180 err = BuildClusterList(); 181 if (err < B_OK) 182 return err; 183 } 184 #endif 185 186 size_t length = *_length; 187 188 if (pos + length > fSize) 189 length = fSize - pos; 190 191 off_t num; // block number 192 off_t offset; 193 if (FindBlock(pos, num, offset) < B_OK) { 194 *_length = 0; 195 return B_BAD_VALUE; 196 } 197 198 uint32 bytesRead = 0; 199 uint32 blockSize = fVolume.BlockSize(); 200 uint32 blockShift = fVolume.BlockShift(); 201 uint8 *block; 202 203 // the first block_run we read could not be aligned to the block_size boundary 204 // (read partial block at the beginning) 205 206 // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 207 if (pos % blockSize != 0) { 208 CachedBlock cached(fVolume, num); 209 if ((block = cached.Block()) == NULL) { 210 *_length = 0; 211 return B_BAD_VALUE; 212 } 213 214 bytesRead = blockSize - (pos % blockSize); 215 if (length < bytesRead) 216 bytesRead = length; 217 218 memcpy(buffer, block + (pos % blockSize), bytesRead); 219 pos += bytesRead; 220 221 length -= bytesRead; 222 if (length == 0) { 223 *_length = bytesRead; 224 return B_OK; 225 } 226 227 if (FindBlock(pos, num, offset) < B_OK) { 228 *_length = bytesRead; 229 return B_BAD_VALUE; 230 } 231 } 232 233 // the first block_run is already filled in at this point 234 // read the following complete blocks using cached_read(), 235 // the last partial block is read using the generic Cache class 236 237 bool partial = false; 238 239 while (length > 0) { 240 // offset is the offset to the current pos in the block_run 241 242 if (length < blockSize) { 243 CachedBlock cached(fVolume, num); 244 if ((block = cached.Block()) == NULL) { 245 *_length = bytesRead; 246 return B_BAD_VALUE; 247 } 248 memcpy(buffer + bytesRead, block, length); 249 bytesRead += length; 250 partial = true; 251 break; 252 } 253 254 if (read_pos(fVolume.Device(), fVolume.ToOffset(num), 255 buffer + bytesRead, fVolume.BlockSize()) < B_OK) { 256 *_length = bytesRead; 257 return B_BAD_VALUE; 258 } 259 260 int32 bytes = fVolume.BlockSize(); 261 length -= bytes; 262 bytesRead += bytes; 263 if (length == 0) 264 break; 265 266 pos += bytes; 267 268 if (FindBlock(pos, num, offset) < B_OK) { 269 *_length = bytesRead; 270 return B_BAD_VALUE; 271 } 272 } 273 274 *_length = bytesRead; 275 return B_NO_ERROR; 276 } 277 278 status_t 279 Stream::BuildClusterList() 280 { 281 #if 0 282 TRACE(("FATFS::Stream::%s()\n", __FUNCTION__)); 283 uint32 count = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize(); 284 uint32 c = fFirstCluster; 285 int i; 286 287 if (fSize == UINT32_MAX) // it's a directory, try a large enough value 288 count = 10; 289 //fClusters = (uint32 *)malloc(count * sizeof(uint32)); 290 for (i = 0; i < count && fVolume.IsValidCluster(c); i++) { 291 if (fVolume.IsLastCluster(c)) 292 break; 293 TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, c)); 294 fClusters[i] = c; 295 c = fVolume.NextCluster(c); 296 TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, c)); 297 // XXX: try to realloc() for dirs maybe ? 298 } 299 fClusterCount = i; 300 TRACE(("FATFS::Stream::%s: %d clusters in chain\n", __FUNCTION__, i)); 301 #endif 302 return B_OK; 303 } 304