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