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
LabelVisitor(Volume * volume)59 LabelVisitor::LabelVisitor(Volume* volume)
60 :
61 fVolume(volume)
62 {
63 }
64
65
66 bool
VisitLabel(struct exfat_entry * entry)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
IsValid()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
Volume(fs_volume * volume)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
~Volume()117 Volume::~Volume()
118 {
119 TRACE("Volume destructor.\n");
120 delete fInodesClusterTree;
121 delete fInodesInoTree;
122 }
123
124
125 bool
IsValidSuperBlock()126 Volume::IsValidSuperBlock()
127 {
128 return fSuperBlock.IsValid();
129 }
130
131
132 const char*
Name() const133 Volume::Name() const
134 {
135 return fName;
136 }
137
138
139 status_t
Mount(const char * deviceName,uint32 flags)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
Unmount()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
LoadSuperBlock()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
ClusterToBlock(cluster_t cluster,fsblock_t & block)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
NextCluster(cluster_t _cluster)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*
FindInode(ino_t id)287 Volume::FindInode(ino_t id)
288 {
289 return fInodesInoTree->Lookup(id);
290 }
291
292
293 Inode*
FindInode(cluster_t cluster)294 Volume::FindInode(cluster_t cluster)
295 {
296 return fInodesClusterTree->Lookup(cluster);
297 }
298
299
300 ino_t
GetIno(cluster_t cluster,uint32 offset,ino_t parent)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*
GetNode(ino_t ino,ino_t & parent)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
Identify(int fd,exfat_super_block * superBlock)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