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