2 * Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de.
3 * Copyright 2011, Jérôme Duval, korli@users.berlios.de.
4 * Copyright 2014 Haiku, Inc. All rights reserved.
6 * Distributed under the terms of the MIT License.
9 * Axel Dörfler, axeld@pinc-software.de
10 * Jérôme Duval, korli@users.berlios.de
11 * John Scipione, jscipione@gmail.com
15 //! Superblock, mounting, etc.
28 #include <fs_volume.h>
30 #include <util/AutoLock.h>
32 #include "CachedBlock.h"
39 # define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x)
41 # define TRACE(x...) ;
43 # define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x)
48 DeviceOpener(int fd
, int mode
);
49 DeviceOpener(const char* device
, int mode
);
52 int Open(const char* device
, int mode
);
53 int Open(int fd
, int mode
);
54 void* InitCache(off_t numBlocks
, uint32 blockSize
);
55 void RemoveCache(bool allowWrites
);
59 int Device() const { return fDevice
; }
60 int Mode() const { return fMode
; }
61 bool IsReadOnly() const
62 { return _IsReadOnly(fMode
); }
64 status_t
GetSize(off_t
* _size
,
65 uint32
* _blockSize
= NULL
);
68 static bool _IsReadOnly(int mode
)
69 { return (mode
& O_RWMASK
) == O_RDONLY
;}
70 static bool _IsReadWrite(int mode
)
71 { return (mode
& O_RWMASK
) == O_RDWR
;}
79 // #pragma mark - DeviceOpener
82 DeviceOpener::DeviceOpener(const char* device
, int mode
)
90 DeviceOpener::DeviceOpener(int fd
, int mode
)
98 DeviceOpener::~DeviceOpener()
108 DeviceOpener::Open(const char* device
, int mode
)
110 fDevice
= open(device
, mode
| O_NOCACHE
);
114 if (fDevice
< 0 && _IsReadWrite(mode
)) {
115 // try again to open read-only (don't rely on a specific error code)
116 return Open(device
, O_RDONLY
| O_NOCACHE
);
122 if (_IsReadWrite(mode
)) {
123 // check out if the device really allows for read/write access
124 device_geometry geometry
;
125 if (!ioctl(fDevice
, B_GET_GEOMETRY
, &geometry
)) {
126 if (geometry
.read_only
) {
127 // reopen device read-only
129 return Open(device
, O_RDONLY
| O_NOCACHE
);
140 DeviceOpener::Open(int fd
, int mode
)
153 DeviceOpener::InitCache(off_t numBlocks
, uint32 blockSize
)
155 return fBlockCache
= block_cache_create(fDevice
, numBlocks
, blockSize
,
161 DeviceOpener::RemoveCache(bool allowWrites
)
163 if (fBlockCache
== NULL
)
166 block_cache_delete(fBlockCache
, allowWrites
);
178 /*! Returns the size of the device in bytes. It uses B_GET_GEOMETRY
179 to compute the size, or fstat() if that failed.
182 DeviceOpener::GetSize(off_t
* _size
, uint32
* _blockSize
)
184 device_geometry geometry
;
185 if (ioctl(fDevice
, B_GET_GEOMETRY
, &geometry
) < 0) {
186 // maybe it's just a file
188 if (fstat(fDevice
, &stat
) < 0)
192 *_size
= stat
.st_size
;
193 if (_blockSize
) // that shouldn't cause us any problems
200 *_size
= 1ULL * geometry
.head_count
* geometry
.cylinder_count
201 * geometry
.sectors_per_track
* geometry
.bytes_per_sector
;
204 *_blockSize
= geometry
.bytes_per_sector
;
210 // #pragma mark - LabelVisitor
213 class LabelVisitor
: public EntryVisitor
{
215 LabelVisitor(Volume
* volume
);
216 bool VisitLabel(struct exfat_entry
*);
222 LabelVisitor::LabelVisitor(Volume
* volume
)
230 LabelVisitor::VisitLabel(struct exfat_entry
* entry
)
232 TRACE("LabelVisitor::VisitLabel()\n");
233 char name
[B_FILE_NAME_LENGTH
];
234 status_t result
= get_volume_name(entry
, name
, sizeof(name
));
238 fVolume
->SetName(name
);
243 // #pragma mark - exfat_super_block::IsValid()
247 exfat_super_block::IsValid()
249 // TODO: check some more values!
250 if (strncmp(filesystem
, EXFAT_SUPER_BLOCK_MAGIC
, sizeof(filesystem
)) != 0)
252 if (signature
!= 0xaa55)
254 if (jump_boot
[0] != 0xeb || jump_boot
[1] != 0x76 || jump_boot
[2] != 0x90)
256 if (version_minor
!= 0 || version_major
!= 1)
263 // #pragma mark - Volume
266 Volume::Volume(fs_volume
* volume
)
273 mutex_init(&fLock
, "exfat volume");
274 fInodesClusterTree
= new InodesClusterTree
;
275 fInodesInoTree
= new InodesInoTree
;
276 memset(fName
, 0, sizeof(fName
));
282 TRACE("Volume destructor.\n");
283 delete fInodesClusterTree
;
284 delete fInodesInoTree
;
289 Volume::IsValidSuperBlock()
291 return fSuperBlock
.IsValid();
303 Volume::Mount(const char* deviceName
, uint32 flags
)
305 flags
|= B_MOUNT_READ_ONLY
;
306 // we only support read-only for now
308 if ((flags
& B_MOUNT_READ_ONLY
) != 0) {
309 TRACE("Volume::Mount(): Read only\n");
311 TRACE("Volume::Mount(): Read write\n");
314 DeviceOpener
opener(deviceName
, (flags
& B_MOUNT_READ_ONLY
) != 0
315 ? O_RDONLY
: O_RDWR
);
316 fDevice
= opener
.Device();
317 if (fDevice
< B_OK
) {
318 ERROR("Volume::Mount(): couldn't open device\n");
322 if (opener
.IsReadOnly())
323 fFlags
|= VOLUME_READ_ONLY
;
325 // read the superblock
326 status_t status
= Identify(fDevice
, &fSuperBlock
);
327 if (status
!= B_OK
) {
328 ERROR("Volume::Mount(): Identify() failed\n");
332 fBlockSize
= 1 << fSuperBlock
.BlockShift();
333 TRACE("block size %" B_PRIu32
"\n", fBlockSize
);
334 fEntriesPerBlock
= (fBlockSize
/ sizeof(struct exfat_entry
));
336 // check that the device is large enough to hold the partition
338 status
= opener
.GetSize(&deviceSize
);
342 off_t partitionSize
= (off_t
)fSuperBlock
.NumBlocks()
343 << fSuperBlock
.BlockShift();
344 if (deviceSize
< partitionSize
)
347 fBlockCache
= opener
.InitCache(fSuperBlock
.NumBlocks(), fBlockSize
);
348 if (fBlockCache
== NULL
)
351 TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache
);
356 Inode
rootNode(this, fSuperBlock
.RootDirCluster(), 0);
357 rootIno
= rootNode
.ID();
360 status
= get_vnode(fFSVolume
, rootIno
, (void**)&fRootNode
);
361 if (status
!= B_OK
) {
362 ERROR("could not create root node: get_vnode() failed!\n");
366 TRACE("Volume::Mount(): Found root node: %" B_PRIdINO
" (%s)\n",
367 fRootNode
->ID(), strerror(fRootNode
->InitCheck()));
372 DirectoryIterator
iterator(fRootNode
);
373 LabelVisitor
visitor(this);
374 iterator
.Iterate(visitor
);
376 if (fName
[0] == '\0')
377 get_default_volume_name(partitionSize
, fName
, sizeof(fName
));
386 TRACE("Volume::Unmount()\n");
388 TRACE("Volume::Unmount(): Putting root node\n");
389 put_vnode(fFSVolume
, RootNode()->ID());
390 TRACE("Volume::Unmount(): Deleting the block cache\n");
391 block_cache_delete(fBlockCache
, !IsReadOnly());
392 TRACE("Volume::Unmount(): Closing device\n");
395 TRACE("Volume::Unmount(): Done\n");
401 Volume::LoadSuperBlock()
403 CachedBlock
cached(this);
404 const uint8
* block
= cached
.SetTo(EXFAT_SUPER_BLOCK_OFFSET
/ fBlockSize
);
409 memcpy(&fSuperBlock
, block
+ EXFAT_SUPER_BLOCK_OFFSET
% fBlockSize
,
410 sizeof(fSuperBlock
));
417 Volume::ClusterToBlock(cluster_t cluster
, fsblock_t
&block
)
421 block
= ((cluster
- 2) << SuperBlock().BlocksPerClusterShift())
422 + SuperBlock().FirstDataBlock();
423 TRACE("Volume::ClusterToBlock() cluster %" B_PRIu32
" %u %" B_PRIu32
": %"
424 B_PRIu64
", %" B_PRIu32
"\n", cluster
,
425 SuperBlock().BlocksPerClusterShift(), SuperBlock().FirstDataBlock(),
426 block
, SuperBlock().FirstFatBlock());
432 Volume::NextCluster(cluster_t _cluster
)
434 uint32 clusterPerBlock
= fBlockSize
/ sizeof(cluster_t
);
435 CachedBlock
block(this);
436 fsblock_t blockNum
= SuperBlock().FirstFatBlock()
437 + _cluster
/ clusterPerBlock
;
438 cluster_t
*cluster
= (cluster_t
*)block
.SetTo(blockNum
);
439 cluster
+= _cluster
% clusterPerBlock
;
440 TRACE("Volume::NextCluster() cluster %" B_PRIu32
" next %" B_PRIu32
"\n",
447 Volume::FindInode(ino_t id
)
449 return fInodesInoTree
->Lookup(id
);
454 Volume::FindInode(cluster_t cluster
)
456 return fInodesClusterTree
->Lookup(cluster
);
461 Volume::GetIno(cluster_t cluster
, uint32 offset
, ino_t parent
)
464 key
.cluster
= cluster
;
467 MutexLocker
locker(fLock
);
468 struct node
* node
= fNodeTree
.Lookup(key
);
470 TRACE("Volume::GetIno() cached cluster %" B_PRIu32
" offset %" B_PRIu32
471 " ino %" B_PRIdINO
"\n", cluster
, offset
, node
->ino
);
474 node
= new struct node();
476 node
->ino
= _NextID();
477 node
->parent
= parent
;
478 fNodeTree
.Insert(node
);
479 fInoTree
.Insert(node
);
480 TRACE("Volume::GetIno() new cluster %" B_PRIu32
" offset %" B_PRIu32
481 " ino %" B_PRIdINO
"\n", cluster
, offset
, node
->ino
);
487 Volume::GetNode(ino_t ino
, ino_t
&parent
)
489 MutexLocker
locker(fLock
);
490 struct node
* node
= fInoTree
.Lookup(ino
);
492 parent
= node
->parent
;
499 // #pragma mark - Disk scanning and initialization
503 Volume::Identify(int fd
, exfat_super_block
* superBlock
)
505 if (read_pos(fd
, EXFAT_SUPER_BLOCK_OFFSET
, superBlock
,
506 sizeof(exfat_super_block
)) != sizeof(exfat_super_block
))
509 if (!superBlock
->IsValid()) {
510 ERROR("invalid superblock!\n");