1 /* 2 ** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 ** Distributed under the terms of the OpenBeOS License. 4 */ 5 6 7 #include "Volume.h" 8 #include "Directory.h" 9 #include "CachedBlock.h" 10 11 #include <boot/partitions.h> 12 #include <boot/platform.h> 13 #include <util/kernel_cpp.h> 14 15 #include <string.h> 16 #include <unistd.h> 17 #include <fcntl.h> 18 #include <stdio.h> 19 #include <stdlib.h> 20 21 //#define TRACE(x) dprintf x 22 #define TRACE(x) do {} while (0) 23 24 using namespace FATFS; 25 26 27 Volume::Volume(boot::Partition *partition) 28 : 29 fRoot(NULL) 30 { 31 TRACE(("%s()\n", __FUNCTION__)); 32 if ((fDevice = open_node(partition, O_RDONLY)) < B_OK) 33 return; 34 35 fCachedBlock = new CachedBlock(*this); 36 if (fCachedBlock == NULL) 37 return; 38 39 uint8 *buf; 40 /* = (char *)malloc(4096); 41 if (buf == NULL) 42 return;*/ 43 44 fBlockSize = partition->block_size; 45 switch (fBlockSize) { 46 case 0x200: 47 fBlockShift = 9; 48 break; 49 case 0x400: 50 fBlockShift = 10; 51 break; 52 case 0x800: 53 fBlockShift = 11; 54 break; 55 default: 56 goto err1; 57 } 58 TRACE(("%s: reading bootsector\n", __FUNCTION__)); 59 // read boot sector 60 buf = fCachedBlock->SetTo(0); 61 if (buf == NULL) 62 goto err1; 63 64 TRACE(("%s: checking signature\n", __FUNCTION__)); 65 // check the signature 66 if (((buf[0x1fe] != 0x55) || (buf[0x1ff] != 0xaa)) && (buf[0x15] == 0xf8)) 67 goto err1; 68 69 if (!memcmp(buf+3, "NTFS ", 8) || !memcmp(buf+3, "HPFS ", 8)) 70 goto err1; 71 72 TRACE(("%s: signature ok\n", __FUNCTION__)); 73 fBytesPerSector = read16(buf,0xb); 74 switch (fBytesPerSector) { 75 case 0x200: 76 fSectorShift = 9; 77 break; 78 case 0x400: 79 fSectorShift = 10; 80 break; 81 case 0x800: 82 fSectorShift = 11; 83 break; 84 default: 85 goto err1; 86 } 87 TRACE(("%s: block shift %d\n", __FUNCTION__, fBlockShift)); 88 89 fSectorsPerCluster = buf[0xd]; 90 switch (fSectorsPerCluster) { 91 case 1: case 2: case 4: case 8: 92 case 0x10: case 0x20: case 0x40: case 0x80: 93 break; 94 default: 95 goto err1; 96 } 97 TRACE(("%s: sect/cluster %d\n", __FUNCTION__, fSectorsPerCluster)); 98 fClusterShift = fSectorShift; 99 for (uint32 spc = fSectorsPerCluster; !(spc & 0x01); ) { 100 spc >>= 1; 101 fClusterShift += 1; 102 } 103 TRACE(("%s: cluster shift %d\n", __FUNCTION__, fClusterShift)); 104 105 fReservedSectors = read16(buf,0xe); 106 fFatCount = buf[0x10]; 107 if ((fFatCount == 0) || (fFatCount > 8)) 108 goto err1; 109 110 fMediaDesc = buf[0x15]; 111 if ((fMediaDesc != 0xf0) && (fMediaDesc < 0xf8)) 112 goto err1; 113 114 fSectorsPerFat = read16(buf,0x16); 115 if (fSectorsPerFat == 0) { 116 // FAT32 117 fFatBits = 32; 118 fSectorsPerFat = read32(buf,0x24); 119 fTotalSectors = read32(buf,0x20); 120 bool lFatMirrored = !(buf[0x28] & 0x80); 121 fActiveFat = (lFatMirrored) ? (buf[0x28] & 0xf) : 0; 122 fDataStart = fReservedSectors + fFatCount * fSectorsPerFat; 123 fTotalClusters = (fTotalSectors - fDataStart) / fSectorsPerCluster; 124 fRootDirCluster = read32(buf,0x2c); 125 if (fRootDirCluster >= fTotalClusters) 126 goto err1; 127 128 fFSInfoSector = read16(buf, 0x30); 129 if (fFSInfoSector < 1 || fFSInfoSector > fTotalSectors) 130 goto err1; 131 } else { 132 // FAT12/16 133 // XXX:FIXME 134 fFatBits = 16; 135 goto err1; 136 } 137 TRACE(("%s: block size %d, sector size %d, sectors/cluster %d\n", __FUNCTION__, 138 fBlockSize, fBytesPerSector, fSectorsPerCluster)); 139 TRACE(("%s: block shift %d, sector shift %d, cluster shift %d\n", __FUNCTION__, 140 fBlockShift, fSectorShift, fClusterShift)); 141 TRACE(("%s: reserved %d, max root entries %d, media %02x, sectors/fat %d\n", __FUNCTION__, 142 fReservedSectors, fMaxRootEntries, fMediaDesc, fSectorsPerFat)); 143 144 145 //if (fTotalSectors > partition->sectors_per_track * partition->cylinder_count * partition->head_count) 146 if ((off_t)fTotalSectors * fBytesPerSector > partition->size) 147 goto err1; 148 149 TRACE(("%s: found fat%d filesystem, root dir at cluster %d\n", __FUNCTION__, 150 fFatBits, fRootDirCluster)); 151 152 fRoot = new Directory(*this, 0, fRootDirCluster, "/"); 153 return; 154 155 err1: 156 TRACE("fatfs: cannot mount (bad superblock ?)\n"); 157 // XXX !? this triple-faults in QEMU .. 158 //delete fCachedBlock; 159 } 160 161 162 Volume::~Volume() 163 { 164 delete fRoot; 165 delete fCachedBlock; 166 close(fDevice); 167 } 168 169 170 status_t 171 Volume::InitCheck() 172 { 173 if (fCachedBlock == NULL) 174 return B_ERROR; 175 if (fRoot == NULL) 176 return B_ERROR; 177 178 return B_OK; 179 } 180 181 182 status_t 183 Volume::GetName(char *name, size_t size) const 184 { 185 //TODO: WRITEME 186 return strlcpy(name, "UNKNOWN", size); 187 } 188 189 190 off_t 191 Volume::ClusterToOffset(uint32 cluster) const 192 { 193 return (fDataStart << SectorShift()) + ((cluster - 2) << ClusterShift()); 194 } 195 196 uint32 197 Volume::NextCluster(uint32 cluster, uint32 skip) 198 { 199 //TRACE(("%s(%d, %d)\n", __FUNCTION__, cluster, skip)); 200 // lookup the FAT for next cluster in chain 201 off_t offset; 202 uint8 *buf; 203 int32 next; 204 int fatBytes = (FatBits() + 7) / 8; 205 206 switch (fFatBits) { 207 case 32: 208 case 16: 209 break; 210 //XXX handle FAT12 211 default: 212 return InvalidClusterID(); 213 } 214 215 again: 216 offset = fBytesPerSector * fReservedSectors; 217 //offset += fActiveFat * fTotalClusters * fFatBits / 8; 218 offset += cluster * fatBytes; 219 220 if (!IsValidCluster(cluster)) 221 return InvalidClusterID(); 222 223 buf = fCachedBlock->SetTo(ToBlock(offset)); 224 if (!buf) 225 return InvalidClusterID(); 226 227 offset %= BlockSize(); 228 229 switch (fFatBits) { 230 case 32: 231 next = read32(buf, offset); 232 next &= 0x0fffffff; 233 break; 234 case 16: 235 next = read16(buf, offset); 236 break; 237 default: 238 return InvalidClusterID(); 239 } 240 if (skip--) { 241 cluster = next; 242 goto again; 243 } 244 return next; 245 } 246 247 248 bool 249 Volume::IsValidCluster(uint32 cluster) const 250 { 251 if (cluster > 1 && cluster < fTotalClusters) 252 return true; 253 return false; 254 } 255 256 257 bool 258 Volume::IsLastCluster(uint32 cluster) const 259 { 260 if (cluster >= fTotalClusters && ((cluster & 0xff8) == 0xff8)) 261 return true; 262 return false; 263 } 264 265 266 /*! Allocates a free cluster. 267 If \a previousCluster is a valid cluster idnex, its chain pointer is 268 changed to point to the newly allocated cluster. 269 */ 270 status_t 271 Volume::AllocateCluster(uint32 previousCluster, uint32& _newCluster) 272 { 273 if (fFatBits != 32) 274 return B_UNSUPPORTED; 275 // TODO: Support FAT16 and FAT12. 276 277 const int fatBytes = FatBits() / 8; 278 const uint32 blockOffsetMask = (uint32)BlockSize() - 1; 279 280 // Iterate through the FAT to find a free cluster. 281 off_t offset = fBytesPerSector * fReservedSectors; 282 offset += 2 * fatBytes; 283 for (uint32 i = 2; i < fTotalClusters; i++, offset += fatBytes) { 284 uint8* buffer = fCachedBlock->SetTo(ToBlock(offset)); 285 if (buffer == NULL) 286 return B_ERROR; 287 288 uint32 value = read32(buffer, offset & blockOffsetMask); 289 if (value == 0) { 290 // found one -- mark it used (end of file) 291 status_t error = _UpdateCluster(i, 0x0ffffff8); 292 if (error != B_OK) 293 return error; 294 295 // If a previous cluster was given, update its list link. 296 if (IsValidCluster(previousCluster)) { 297 error = _UpdateCluster(previousCluster, i); 298 if (error != B_OK) { 299 _UpdateCluster(i, 0); 300 return error; 301 } 302 } 303 304 _ClusterAllocated(i); 305 306 _newCluster = i; 307 return B_OK; 308 } 309 } 310 311 return B_DEVICE_FULL; 312 } 313 314 315 status_t 316 Volume::_UpdateCluster(uint32 cluster, uint32 value) 317 { 318 if (fFatBits != 32 && fFatBits != 16) 319 return B_UNSUPPORTED; 320 // TODO: Support FAT12. 321 322 if (!IsValidCluster(cluster)) 323 return InvalidClusterID(); 324 325 // get the buffer we need to change 326 const int fatBytes = FatBits() / 8; 327 328 for (uint8 i = 0; i < fFatCount; i++) { 329 off_t offset 330 = fBytesPerSector * (fReservedSectors + i * fSectorsPerFat); 331 offset += cluster * fatBytes; 332 333 uint8* buffer = fCachedBlock->SetTo(ToBlock(offset)); 334 if (buffer == NULL) 335 return InvalidClusterID(); 336 337 offset %= BlockSize(); 338 339 // set the value 340 switch (fFatBits) { 341 case 32: 342 *(uint32*)(buffer + offset) = B_HOST_TO_LENDIAN_INT32(value); 343 break; 344 case 16: 345 *(uint16*)(buffer + offset) = B_HOST_TO_LENDIAN_INT16(value); 346 break; 347 default: 348 return InvalidClusterID(); 349 } 350 351 // write the block back to disk 352 status_t error = fCachedBlock->Flush(); 353 if (error != B_OK) { 354 fCachedBlock->Unset(); 355 return error; 356 } 357 } 358 359 return B_OK; 360 } 361 362 363 status_t 364 Volume::_ClusterAllocated(uint32 cluster) 365 { 366 // update the FS info 367 368 // exists only for FAT32 369 if (fFatBits != 32) 370 return B_OK; 371 372 off_t offset = fBytesPerSector * fFSInfoSector; 373 374 status_t error = fCachedBlock->SetTo(offset / BlockSize(), 375 CachedBlock::READ); 376 if (error != B_OK) 377 return error; 378 379 uint8* buffer = fCachedBlock->Block() + offset % BlockSize(); 380 381 // update number of free cluster 382 int32 freeClusters = read32(buffer, 0x1e8); 383 if (freeClusters != -1) 384 write32(buffer, 0x1e8, freeClusters - 1); 385 386 // update number of most recently allocated cluster 387 write32(buffer, 0x1ec, cluster); 388 389 // write the block back 390 error = fCachedBlock->Flush(); 391 if (error != B_OK) 392 fCachedBlock->Unset(); 393 394 return error; 395 } 396 397 398 // #pragma mark - 399 400 401 float 402 dosfs_identify_file_system(boot::Partition *partition) 403 { 404 TRACE(("%s()\n", __FUNCTION__)); 405 Volume volume(partition); 406 407 return volume.InitCheck() < B_OK ? 0 : 0.8; 408 } 409 410 411 static status_t 412 dosfs_get_file_system(boot::Partition *partition, ::Directory **_root) 413 { 414 TRACE(("%s()\n", __FUNCTION__)); 415 Volume *volume = new Volume(partition); 416 if (volume == NULL) 417 return B_NO_MEMORY; 418 419 if (volume->InitCheck() < B_OK) { 420 delete volume; 421 return B_ERROR; 422 } 423 424 *_root = volume->Root(); 425 return B_OK; 426 } 427 428 429 file_system_module_info gFATFileSystemModule = { 430 "file_systems/dosfs/v1", 431 kPartitionTypeFAT32, // XXX:FIXME: FAT16 too ? 432 dosfs_identify_file_system, 433 dosfs_get_file_system 434 }; 435 436