btrfs: [] on the end of a struct field is a variable length array.
[haiku.git] / src / add-ons / kernel / file_systems / exfat / Volume.cpp
blob44f6d2e189dbe839987147f4322d9680c1078388
1 /*
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.
8 * Authors:
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.
18 #include "Volume.h"
20 #include <errno.h>
21 #include <unistd.h>
22 #include <new>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
27 #include <fs_cache.h>
28 #include <fs_volume.h>
30 #include <util/AutoLock.h>
32 #include "CachedBlock.h"
33 #include "Inode.h"
34 #include "Utility.h"
37 //#define TRACE_EXFAT
38 #ifdef TRACE_EXFAT
39 # define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x)
40 #else
41 # define TRACE(x...) ;
42 #endif
43 # define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x)
46 class DeviceOpener {
47 public:
48 DeviceOpener(int fd, int mode);
49 DeviceOpener(const char* device, int mode);
50 ~DeviceOpener();
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);
57 void Keep();
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);
67 private:
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;}
73 int fDevice;
74 int fMode;
75 void* fBlockCache;
79 // #pragma mark - DeviceOpener
82 DeviceOpener::DeviceOpener(const char* device, int mode)
84 fBlockCache(NULL)
86 Open(device, mode);
90 DeviceOpener::DeviceOpener(int fd, int mode)
92 fBlockCache(NULL)
94 Open(fd, mode);
98 DeviceOpener::~DeviceOpener()
100 if (fDevice >= 0) {
101 RemoveCache(false);
102 close(fDevice);
108 DeviceOpener::Open(const char* device, int mode)
110 fDevice = open(device, mode | O_NOCACHE);
111 if (fDevice < 0)
112 fDevice = errno;
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);
119 if (fDevice >= 0) {
120 // opening succeeded
121 fMode = mode;
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
128 close(fDevice);
129 return Open(device, O_RDONLY | O_NOCACHE);
135 return fDevice;
140 DeviceOpener::Open(int fd, int mode)
142 fDevice = dup(fd);
143 if (fDevice < 0)
144 return errno;
146 fMode = mode;
148 return fDevice;
152 void*
153 DeviceOpener::InitCache(off_t numBlocks, uint32 blockSize)
155 return fBlockCache = block_cache_create(fDevice, numBlocks, blockSize,
156 IsReadOnly());
160 void
161 DeviceOpener::RemoveCache(bool allowWrites)
163 if (fBlockCache == NULL)
164 return;
166 block_cache_delete(fBlockCache, allowWrites);
167 fBlockCache = NULL;
171 void
172 DeviceOpener::Keep()
174 fDevice = -1;
178 /*! Returns the size of the device in bytes. It uses B_GET_GEOMETRY
179 to compute the size, or fstat() if that failed.
181 status_t
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
187 struct stat stat;
188 if (fstat(fDevice, &stat) < 0)
189 return B_ERROR;
191 if (_size)
192 *_size = stat.st_size;
193 if (_blockSize) // that shouldn't cause us any problems
194 *_blockSize = 512;
196 return B_OK;
199 if (_size) {
200 *_size = 1ULL * geometry.head_count * geometry.cylinder_count
201 * geometry.sectors_per_track * geometry.bytes_per_sector;
203 if (_blockSize)
204 *_blockSize = geometry.bytes_per_sector;
206 return B_OK;
210 // #pragma mark - LabelVisitor
213 class LabelVisitor : public EntryVisitor {
214 public:
215 LabelVisitor(Volume* volume);
216 bool VisitLabel(struct exfat_entry*);
217 private:
218 Volume* fVolume;
222 LabelVisitor::LabelVisitor(Volume* volume)
224 fVolume(volume)
229 bool
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));
235 if (result != B_OK)
236 return false;
238 fVolume->SetName(name);
239 return true;
243 // #pragma mark - exfat_super_block::IsValid()
246 bool
247 exfat_super_block::IsValid()
249 // TODO: check some more values!
250 if (strncmp(filesystem, EXFAT_SUPER_BLOCK_MAGIC, sizeof(filesystem)) != 0)
251 return false;
252 if (signature != 0xaa55)
253 return false;
254 if (jump_boot[0] != 0xeb || jump_boot[1] != 0x76 || jump_boot[2] != 0x90)
255 return false;
256 if (version_minor != 0 || version_major != 1)
257 return false;
259 return true;
263 // #pragma mark - Volume
266 Volume::Volume(fs_volume* volume)
268 fFSVolume(volume),
269 fFlags(0),
270 fRootNode(NULL),
271 fNextId(1)
273 mutex_init(&fLock, "exfat volume");
274 fInodesClusterTree = new InodesClusterTree;
275 fInodesInoTree = new InodesInoTree;
276 memset(fName, 0, sizeof(fName));
280 Volume::~Volume()
282 TRACE("Volume destructor.\n");
283 delete fInodesClusterTree;
284 delete fInodesInoTree;
288 bool
289 Volume::IsValidSuperBlock()
291 return fSuperBlock.IsValid();
295 const char*
296 Volume::Name() const
298 return fName;
302 status_t
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");
310 } else {
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");
319 return fDevice;
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");
329 return status;
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
337 off_t deviceSize;
338 status = opener.GetSize(&deviceSize);
339 if (status != B_OK)
340 return status;
342 off_t partitionSize = (off_t)fSuperBlock.NumBlocks()
343 << fSuperBlock.BlockShift();
344 if (deviceSize < partitionSize)
345 return B_BAD_VALUE;
347 fBlockCache = opener.InitCache(fSuperBlock.NumBlocks(), fBlockSize);
348 if (fBlockCache == NULL)
349 return B_ERROR;
351 TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache);
353 ino_t rootIno;
354 // ready
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");
363 return status;
366 TRACE("Volume::Mount(): Found root node: %" B_PRIdINO " (%s)\n",
367 fRootNode->ID(), strerror(fRootNode->InitCheck()));
369 // all went fine
370 opener.Keep();
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));
379 return B_OK;
383 status_t
384 Volume::Unmount()
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");
393 close(fDevice);
395 TRACE("Volume::Unmount(): Done\n");
396 return B_OK;
400 status_t
401 Volume::LoadSuperBlock()
403 CachedBlock cached(this);
404 const uint8* block = cached.SetTo(EXFAT_SUPER_BLOCK_OFFSET / fBlockSize);
406 if (block == NULL)
407 return B_IO_ERROR;
409 memcpy(&fSuperBlock, block + EXFAT_SUPER_BLOCK_OFFSET % fBlockSize,
410 sizeof(fSuperBlock));
412 return B_OK;
416 status_t
417 Volume::ClusterToBlock(cluster_t cluster, fsblock_t &block)
419 if (cluster < 2)
420 return B_BAD_VALUE;
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());
427 return B_OK;
431 cluster_t
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",
441 _cluster, *cluster);
442 return *cluster;
446 Inode*
447 Volume::FindInode(ino_t id)
449 return fInodesInoTree->Lookup(id);
453 Inode*
454 Volume::FindInode(cluster_t cluster)
456 return fInodesClusterTree->Lookup(cluster);
460 ino_t
461 Volume::GetIno(cluster_t cluster, uint32 offset, ino_t parent)
463 struct node_key key;
464 key.cluster = cluster;
465 key.offset = offset;
467 MutexLocker locker(fLock);
468 struct node* node = fNodeTree.Lookup(key);
469 if (node != NULL) {
470 TRACE("Volume::GetIno() cached cluster %" B_PRIu32 " offset %" B_PRIu32
471 " ino %" B_PRIdINO "\n", cluster, offset, node->ino);
472 return node->ino;
474 node = new struct node();
475 node->key = key;
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);
482 return node->ino;
486 struct node_key*
487 Volume::GetNode(ino_t ino, ino_t &parent)
489 MutexLocker locker(fLock);
490 struct node* node = fInoTree.Lookup(ino);
491 if (node != NULL) {
492 parent = node->parent;
493 return &node->key;
495 return NULL;
499 // #pragma mark - Disk scanning and initialization
502 /*static*/ status_t
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))
507 return B_IO_ERROR;
509 if (!superBlock->IsValid()) {
510 ERROR("invalid superblock!\n");
511 return B_BAD_VALUE;
514 return B_OK;