1/*
2 * Copyright 2008-2010, Axel D��rfler, axeld@pinc-software.de.
3 * Copyright 2011-2019, J��r��me Duval, jerome.duval@gmail.com.
4 * Copyright 2014 Haiku, Inc. All rights reserved.
5 *
6 * Distributed under the terms of the MIT License.
7 *
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
12 */
13
14
15//! Superblock, mounting, etc.
16
17
18#include "Volume.h"
19
20#include <errno.h>
21#include <unistd.h>
22#include <new>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include <fs_cache.h>
28#include <fs_volume.h>
29
30#include <util/AutoLock.h>
31
32#include "CachedBlock.h"
33#include "DeviceOpener.h"
34#include "Inode.h"
35#include "Utility.h"
36
37
38//#define TRACE_EXFAT
39#ifdef TRACE_EXFAT
40#	define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x)
41#else
42#	define TRACE(x...) ;
43#endif
44#	define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x)
45
46
47//	#pragma mark - LabelVisitor
48
49
50class LabelVisitor : public EntryVisitor {
51public:
52								LabelVisitor(Volume* volume);
53			bool				VisitLabel(struct exfat_entry*);
54private:
55			Volume*				fVolume;
56};
57
58
59LabelVisitor::LabelVisitor(Volume* volume)
60	:
61	fVolume(volume)
62{
63}
64
65
66bool
67LabelVisitor::VisitLabel(struct exfat_entry* entry)
68{
69	TRACE("LabelVisitor::VisitLabel()\n");
70	char name[B_FILE_NAME_LENGTH];
71	status_t result = get_volume_name(entry, name, sizeof(name));
72	if (result != B_OK)
73		return false;
74
75	fVolume->SetName(name);
76	return true;
77}
78
79
80//	#pragma mark - exfat_super_block::IsValid()
81
82
83bool
84exfat_super_block::IsValid()
85{
86	// TODO: check some more values!
87	if (strncmp(filesystem, EXFAT_SUPER_BLOCK_MAGIC, sizeof(filesystem)) != 0)
88		return false;
89	if (signature != 0xaa55)
90		return false;
91	if (jump_boot[0] != 0xeb || jump_boot[1] != 0x76 || jump_boot[2] != 0x90)
92		return false;
93	if (version_minor != 0 || version_major != 1)
94		return false;
95
96	return true;
97}
98
99
100//	#pragma mark - Volume
101
102
103Volume::Volume(fs_volume* volume)
104	:
105	fFSVolume(volume),
106	fFlags(0),
107	fRootNode(NULL),
108	fNextId(1)
109{
110	mutex_init(&fLock, "exfat volume");
111	fInodesClusterTree = new InodesClusterTree;
112	fInodesInoTree = new InodesInoTree;
113	memset(fName, 0, sizeof(fName));
114}
115
116
117Volume::~Volume()
118{
119	TRACE("Volume destructor.\n");
120	delete fInodesClusterTree;
121	delete fInodesInoTree;
122}
123
124
125bool
126Volume::IsValidSuperBlock()
127{
128	return fSuperBlock.IsValid();
129}
130
131
132const char*
133Volume::Name() const
134{
135	return fName;
136}
137
138
139status_t
140Volume::Mount(const char* deviceName, uint32 flags)
141{
142	flags |= B_MOUNT_READ_ONLY;
143		// we only support read-only for now
144
145	if ((flags & B_MOUNT_READ_ONLY) != 0) {
146		TRACE("Volume::Mount(): Read only\n");
147	} else {
148		TRACE("Volume::Mount(): Read write\n");
149	}
150
151	DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0
152		? O_RDONLY : O_RDWR);
153	fDevice = opener.Device();
154	if (fDevice < B_OK) {
155		ERROR("Volume::Mount(): couldn't open device\n");
156		return fDevice;
157	}
158
159	if (opener.IsReadOnly())
160		fFlags |= VOLUME_READ_ONLY;
161
162	// read the superblock
163	status_t status = Identify(fDevice, &fSuperBlock);
164	if (status != B_OK) {
165		ERROR("Volume::Mount(): Identify() failed\n");
166		return status;
167	}
168
169	fBlockSize = 1 << fSuperBlock.BlockShift();
170	TRACE("block size %" B_PRIu32 "\n", fBlockSize);
171	fEntriesPerBlock = (fBlockSize / sizeof(struct exfat_entry));
172
173	// check that the device is large enough to hold the partition
174	off_t deviceSize;
175	status = opener.GetSize(&deviceSize);
176	if (status != B_OK)
177		return status;
178
179	off_t partitionSize = (off_t)fSuperBlock.NumBlocks()
180		<< fSuperBlock.BlockShift();
181	if (deviceSize < partitionSize)
182		return B_BAD_VALUE;
183
184	fBlockCache = opener.InitCache(fSuperBlock.NumBlocks(), fBlockSize);
185	if (fBlockCache == NULL)
186		return B_ERROR;
187
188	TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache);
189
190	ino_t rootIno;
191	// ready
192	{
193		Inode rootNode(this, fSuperBlock.RootDirCluster(), 0);
194		rootIno = rootNode.ID();
195	}
196
197	status = get_vnode(fFSVolume, rootIno, (void**)&fRootNode);
198	if (status != B_OK) {
199		ERROR("could not create root node: get_vnode() failed!\n");
200		return status;
201	}
202
203	TRACE("Volume::Mount(): Found root node: %" B_PRIdINO " (%s)\n",
204		fRootNode->ID(), strerror(fRootNode->InitCheck()));
205
206	// all went fine
207	opener.Keep();
208
209	DirectoryIterator iterator(fRootNode);
210	LabelVisitor visitor(this);
211	iterator.Iterate(visitor);
212
213	if (fName[0] == '\0')
214		get_default_volume_name(partitionSize, fName, sizeof(fName));
215
216	return B_OK;
217}
218
219
220status_t
221Volume::Unmount()
222{
223	TRACE("Volume::Unmount()\n");
224
225	TRACE("Volume::Unmount(): Putting root node\n");
226	put_vnode(fFSVolume, RootNode()->ID());
227	TRACE("Volume::Unmount(): Deleting the block cache\n");
228	block_cache_delete(fBlockCache, !IsReadOnly());
229	TRACE("Volume::Unmount(): Closing device\n");
230	close(fDevice);
231
232	TRACE("Volume::Unmount(): Done\n");
233	return B_OK;
234}
235
236
237status_t
238Volume::LoadSuperBlock()
239{
240	CachedBlock cached(this);
241	const uint8* block = cached.SetTo(EXFAT_SUPER_BLOCK_OFFSET / fBlockSize);
242
243	if (block == NULL)
244		return B_IO_ERROR;
245
246	memcpy(&fSuperBlock, block + EXFAT_SUPER_BLOCK_OFFSET % fBlockSize,
247		sizeof(fSuperBlock));
248
249	return B_OK;
250}
251
252
253status_t
254Volume::ClusterToBlock(cluster_t cluster, fsblock_t &block)
255{
256	if ((cluster - EXFAT_FIRST_DATA_CLUSTER) >= SuperBlock().ClusterCount()
257		|| cluster < EXFAT_FIRST_DATA_CLUSTER) {
258		return B_BAD_VALUE;
259	}
260	block = ((fsblock_t)(cluster - EXFAT_FIRST_DATA_CLUSTER)
261		<< SuperBlock().BlocksPerClusterShift())
262		+ SuperBlock().FirstDataBlock();
263	TRACE("Volume::ClusterToBlock() cluster %" B_PRIu32 " %u %" B_PRIu32 ": %"
264		B_PRIu64 ", %" B_PRIu32 "\n", cluster,
265		SuperBlock().BlocksPerClusterShift(), SuperBlock().FirstDataBlock(),
266		block, SuperBlock().FirstFatBlock());
267	return B_OK;
268}
269
270
271cluster_t
272Volume::NextCluster(cluster_t _cluster)
273{
274	uint32 clusterPerBlock = fBlockSize / sizeof(cluster_t);
275	CachedBlock block(this);
276	fsblock_t blockNum = SuperBlock().FirstFatBlock()
277		+ _cluster / clusterPerBlock;
278	cluster_t *cluster = (cluster_t *)block.SetTo(blockNum);
279	cluster += _cluster % clusterPerBlock;
280	TRACE("Volume::NextCluster() cluster %" B_PRIu32 " next %" B_PRIu32 "\n",
281		_cluster, *cluster);
282	return *cluster;
283}
284
285
286Inode*
287Volume::FindInode(ino_t id)
288{
289	return fInodesInoTree->Lookup(id);
290}
291
292
293Inode*
294Volume::FindInode(cluster_t cluster)
295{
296	return fInodesClusterTree->Lookup(cluster);
297}
298
299
300ino_t
301Volume::GetIno(cluster_t cluster, uint32 offset, ino_t parent)
302{
303	struct node_key key;
304	key.cluster = cluster;
305	key.offset = offset;
306
307	MutexLocker locker(fLock);
308	struct node* node = fNodeTree.Lookup(key);
309	if (node != NULL) {
310		TRACE("Volume::GetIno() cached cluster %" B_PRIu32 " offset %" B_PRIu32
311			" ino %" B_PRIdINO "\n", cluster, offset, node->ino);
312		return node->ino;
313	}
314	node = new struct node();
315	node->key = key;
316	node->ino = _NextID();
317	node->parent = parent;
318	fNodeTree.Insert(node);
319	fInoTree.Insert(node);
320	TRACE("Volume::GetIno() new cluster %" B_PRIu32 " offset %" B_PRIu32
321		" ino %" B_PRIdINO "\n", cluster, offset, node->ino);
322	return node->ino;
323}
324
325
326struct node_key*
327Volume::GetNode(ino_t ino, ino_t &parent)
328{
329	MutexLocker locker(fLock);
330	struct node* node = fInoTree.Lookup(ino);
331	if (node != NULL) {
332		parent = node->parent;
333		return &node->key;
334	}
335	return NULL;
336}
337
338
339//	#pragma mark - Disk scanning and initialization
340
341
342/*static*/ status_t
343Volume::Identify(int fd, exfat_super_block* superBlock)
344{
345	if (read_pos(fd, EXFAT_SUPER_BLOCK_OFFSET, superBlock,
346			sizeof(exfat_super_block)) != sizeof(exfat_super_block))
347		return B_IO_ERROR;
348
349	if (!superBlock->IsValid()) {
350		ERROR("invalid superblock!\n");
351		return B_BAD_VALUE;
352	}
353
354	return B_OK;
355}
356