xref: /haiku/src/add-ons/kernel/file_systems/exfat/Volume.cpp (revision cbe0a0c436162d78cc3f92a305b64918c839d079)
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 
50 class LabelVisitor : public EntryVisitor {
51 public:
52 								LabelVisitor(Volume* volume);
53 			bool				VisitLabel(struct exfat_entry*);
54 private:
55 			Volume*				fVolume;
56 };
57 
58 
59 LabelVisitor::LabelVisitor(Volume* volume)
60 	:
61 	fVolume(volume)
62 {
63 }
64 
65 
66 bool
67 LabelVisitor::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 
83 bool
84 exfat_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 
103 Volume::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 
117 Volume::~Volume()
118 {
119 	TRACE("Volume destructor.\n");
120 	delete fInodesClusterTree;
121 	delete fInodesInoTree;
122 }
123 
124 
125 bool
126 Volume::IsValidSuperBlock()
127 {
128 	return fSuperBlock.IsValid();
129 }
130 
131 
132 const char*
133 Volume::Name() const
134 {
135 	return fName;
136 }
137 
138 
139 status_t
140 Volume::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 
220 status_t
221 Volume::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 
237 status_t
238 Volume::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 
253 status_t
254 Volume::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 
271 cluster_t
272 Volume::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 
286 Inode*
287 Volume::FindInode(ino_t id)
288 {
289 	return fInodesInoTree->Lookup(id);
290 }
291 
292 
293 Inode*
294 Volume::FindInode(cluster_t cluster)
295 {
296 	return fInodesClusterTree->Lookup(cluster);
297 }
298 
299 
300 ino_t
301 Volume::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 
326 struct node_key*
327 Volume::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
343 Volume::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