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