1 /*
2 * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include "Volume.h"
8
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/stat.h>
15
16 #include <new>
17
18 #include <fs_cache.h>
19
20 #include <AutoDeleter.h>
21 #include <util/AutoLock.h>
22
23 #include "Block.h"
24 #include "BlockAllocator.h"
25 #include "checksumfs.h"
26 #include "checksumfs_private.h"
27 #include "DebugSupport.h"
28 #include "Directory.h"
29 #include "File.h"
30 #include "SuperBlock.h"
31 #include "SymLink.h"
32 #include "Transaction.h"
33
34
Volume(uint32 flags)35 Volume::Volume(uint32 flags)
36 :
37 fFSVolume(NULL),
38 fFD(-1),
39 fFlags(B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME
40 | ((flags & B_FS_IS_READONLY) != 0 ? B_FS_IS_READONLY : 0)),
41 fBlockCache(NULL),
42 fTotalBlocks(0),
43 fName(NULL),
44 fBlockAllocator(NULL),
45 fRootDirectory(NULL)
46 {
47 mutex_init(&fLock, "checksumfs volume");
48 mutex_init(&fTransactionLock, "checksumfs transaction");
49 }
50
51
~Volume()52 Volume::~Volume()
53 {
54 delete fRootDirectory;
55 delete fBlockAllocator;
56
57 if (fBlockCache != NULL)
58 block_cache_delete(fBlockCache, false);
59
60 if (fFD >= 0)
61 close(fFD);
62
63 free(fName);
64
65 mutex_destroy(&fTransactionLock);
66 mutex_destroy(&fLock);
67 }
68
69
70 status_t
Init(const char * device)71 Volume::Init(const char* device)
72 {
73 // open the device
74 if (!IsReadOnly()) {
75 fFD = open(device, O_RDWR);
76
77 // If opening read-write fails, we retry read-only.
78 if (fFD < 0)
79 fFlags |= B_FS_IS_READONLY;
80 }
81
82 if (IsReadOnly())
83 fFD = open(device, O_RDONLY);
84
85 if (fFD < 0)
86 return errno;
87
88 // get the size
89 struct stat st;
90 if (fstat(fFD, &st) < 0)
91 return errno;
92
93 off_t size;
94 switch (st.st_mode & S_IFMT) {
95 case S_IFREG:
96 size = st.st_size;
97 break;
98 case S_IFCHR:
99 case S_IFBLK:
100 {
101 device_geometry geometry;
102 if (ioctl(fFD, B_GET_GEOMETRY, &geometry, sizeof(geometry)) < 0)
103 return errno;
104
105 size = (off_t)geometry.bytes_per_sector * geometry.sectors_per_track
106 * geometry.cylinder_count * geometry.head_count;
107 break;
108 }
109 default:
110 return B_BAD_VALUE;
111 }
112
113 return _Init(size / B_PAGE_SIZE);
114 }
115
116
117 status_t
Init(int fd,uint64 totalBlocks)118 Volume::Init(int fd, uint64 totalBlocks)
119 {
120 fFD = dup(fd);
121 if (fFD < 0)
122 RETURN_ERROR(errno);
123
124 return _Init(totalBlocks);
125 }
126
127
128 status_t
Mount(fs_volume * fsVolume)129 Volume::Mount(fs_volume* fsVolume)
130 {
131 fFSVolume = fsVolume;
132
133 // load the superblock
134 Block block;
135 if (!block.GetReadable(this, kCheckSumFSSuperBlockOffset / B_PAGE_SIZE))
136 RETURN_ERROR(B_ERROR);
137
138 SuperBlock* superBlock = (SuperBlock*)block.Data();
139 if (!superBlock->Check(fTotalBlocks))
140 RETURN_ERROR(B_BAD_DATA);
141
142 // copy the volume name
143 fName = strdup(superBlock->Name());
144 if (fName == NULL)
145 RETURN_ERROR(B_NO_MEMORY);
146
147 // init the block allocator
148 status_t error = fBlockAllocator->Init(superBlock->BlockBitmap(),
149 superBlock->FreeBlocks());
150 if (error != B_OK)
151 RETURN_ERROR(error);
152
153 // load the root directory
154 Node* rootNode;
155 error = ReadNode(superBlock->RootDirectory(), rootNode);
156 if (error != B_OK)
157 RETURN_ERROR(error);
158
159 fRootDirectory = dynamic_cast<Directory*>(rootNode);
160 if (fRootDirectory == NULL) {
161 delete rootNode;
162 RETURN_ERROR(B_BAD_DATA);
163 }
164
165 error = PublishNode(fRootDirectory, 0);
166 if (error != B_OK) {
167 delete fRootDirectory;
168 fRootDirectory = NULL;
169 return error;
170 }
171
172 return B_OK;
173 }
174
175
176 void
Unmount()177 Volume::Unmount()
178 {
179 status_t error = block_cache_sync(fBlockCache);
180 if (error != B_OK) {
181 dprintf("checksumfs: Error: Failed to sync block cache when "
182 "unmounting!\n");
183 }
184 }
185
186
187 status_t
Initialize(const char * name)188 Volume::Initialize(const char* name)
189 {
190 fName = strdup(name);
191 if (fName == NULL)
192 RETURN_ERROR(B_NO_MEMORY);
193
194 Transaction transaction(this);
195 status_t error = transaction.Start();
196 if (error != B_OK)
197 RETURN_ERROR(error);
198
199 error = fBlockAllocator->Initialize(transaction);
200 if (error != B_OK)
201 RETURN_ERROR(error);
202
203 // create the root directory
204 error = CreateDirectory(S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH,
205 transaction, fRootDirectory);
206 if (error != B_OK)
207 RETURN_ERROR(error);
208
209 transaction.KeepNode(fRootDirectory);
210 fRootDirectory->SetHardLinks(1);
211
212 // write the superblock
213 Block block;
214 if (!block.GetZero(this, kCheckSumFSSuperBlockOffset / B_PAGE_SIZE,
215 transaction)) {
216 RETURN_ERROR(B_ERROR);
217 }
218
219 SuperBlock* superBlock = (SuperBlock*)block.Data();
220 superBlock->Initialize(this);
221
222 block.Put();
223
224 // commit the transaction and flush the block cache
225 error = transaction.Commit();
226 if (error != B_OK)
227 RETURN_ERROR(error);
228
229 return block_cache_sync(fBlockCache);
230 }
231
232
233 void
GetInfo(fs_info & info)234 Volume::GetInfo(fs_info& info)
235 {
236 MutexLocker locker(fLock);
237
238 info.flags = fFlags;
239 info.block_size = B_PAGE_SIZE;
240 info.io_size = B_PAGE_SIZE * 16; // random value
241 info.total_blocks = fTotalBlocks;
242 info.free_blocks = fBlockAllocator->FreeBlocks();
243 info.total_nodes = 1; // phew, who cares?
244 info.free_nodes = info.free_blocks;
245 strlcpy(info.volume_name, fName, sizeof(info.volume_name));
246 }
247
248
249 status_t
NewNode(Node * node)250 Volume::NewNode(Node* node)
251 {
252 return new_vnode(fFSVolume, node->BlockIndex(), node, &gCheckSumFSVnodeOps);
253 }
254
255
256 status_t
PublishNode(Node * node,uint32 flags)257 Volume::PublishNode(Node* node, uint32 flags)
258 {
259 return publish_vnode(fFSVolume, node->BlockIndex(), node,
260 &gCheckSumFSVnodeOps, node->Mode(), flags);
261 }
262
263
264 status_t
GetNode(uint64 blockIndex,Node * & _node)265 Volume::GetNode(uint64 blockIndex, Node*& _node)
266 {
267 return get_vnode(fFSVolume, blockIndex, (void**)&_node);
268 }
269
270
271 status_t
PutNode(Node * node)272 Volume::PutNode(Node* node)
273 {
274 return put_vnode(fFSVolume, node->BlockIndex());
275 }
276
277
278 status_t
RemoveNode(Node * node)279 Volume::RemoveNode(Node* node)
280 {
281 return remove_vnode(fFSVolume, node->BlockIndex());
282 }
283
284
285 status_t
UnremoveNode(Node * node)286 Volume::UnremoveNode(Node* node)
287 {
288 return unremove_vnode(fFSVolume, node->BlockIndex());
289 }
290
291
292 status_t
ReadNode(uint64 blockIndex,Node * & _node)293 Volume::ReadNode(uint64 blockIndex, Node*& _node)
294 {
295 if (blockIndex == 0 || blockIndex >= fTotalBlocks)
296 return B_BAD_VALUE;
297
298 // load the node's block
299 Block block;
300 if (!block.GetReadable(this, blockIndex))
301 return B_ERROR;
302
303 checksumfs_node* nodeData = (checksumfs_node*)block.Data();
304
305 // create the Node object
306 Node* node;
307 switch (nodeData->mode & S_IFMT) {
308 // TODO: Don't directly access mode!
309 case S_IFDIR:
310 node = new(std::nothrow) Directory(this, blockIndex, *nodeData);
311 break;
312 case S_IFREG:
313 node = new(std::nothrow) File(this, blockIndex, *nodeData);
314 break;
315 case S_IFLNK:
316 node = new(std::nothrow) SymLink(this, blockIndex, *nodeData);
317 break;
318 default:
319 node = new(std::nothrow) Node(this, blockIndex, *nodeData);
320 break;
321 }
322
323 if (node == NULL)
324 return B_NO_MEMORY;
325
326 // TODO: Sanity check the node!
327
328 _node = node;
329 return B_OK;
330 }
331
332
333 status_t
CreateDirectory(mode_t mode,Transaction & transaction,Directory * & _directory)334 Volume::CreateDirectory(mode_t mode, Transaction& transaction,
335 Directory*& _directory)
336 {
337 Directory* directory = new(std::nothrow) Directory(this,
338 (mode & S_IUMSK) | S_IFDIR);
339
340 status_t error = _CreateNode(directory, transaction);
341 if (error != B_OK)
342 return error;
343
344 _directory = directory;
345 return B_OK;
346 }
347
348
349 status_t
CreateFile(mode_t mode,Transaction & transaction,File * & _file)350 Volume::CreateFile(mode_t mode, Transaction& transaction, File*& _file)
351 {
352 File* file = new(std::nothrow) File(this, (mode & S_IUMSK) | S_IFREG);
353
354 status_t error = _CreateNode(file, transaction);
355 if (error != B_OK)
356 return error;
357
358 _file = file;
359 return B_OK;
360 }
361
362
363 status_t
CreateSymLink(mode_t mode,Transaction & transaction,SymLink * & _symLink)364 Volume::CreateSymLink(mode_t mode, Transaction& transaction, SymLink*& _symLink)
365 {
366 SymLink* symLink = new(std::nothrow) SymLink(this,
367 (mode & S_IUMSK) | S_IFLNK);
368
369 status_t error = _CreateNode(symLink, transaction);
370 if (error != B_OK)
371 return error;
372
373 _symLink = symLink;
374 return B_OK;
375 }
376
377
378 status_t
DeleteNode(Node * node)379 Volume::DeleteNode(Node* node)
380 {
381 // let the node delete data associated with it
382 node->DeletingNode();
383
384 uint64 blockIndex = node->BlockIndex();
385
386 // delete the node itself
387 Transaction transaction(this);
388 status_t error = transaction.Start();
389 if (error == B_OK) {
390 error = fBlockAllocator->Free(node->BlockIndex(), 1, transaction);
391 if (error == B_OK) {
392 error = transaction.Commit();
393 if (error != B_OK) {
394 ERROR("Failed to commit transaction for deleting node at %"
395 B_PRIu64 "\n", blockIndex);
396 }
397 } else {
398 ERROR("Failed to free block for node at %" B_PRIu64 "\n",
399 blockIndex);
400 }
401 } else {
402 ERROR("Failed to start transaction for deleting node at %" B_PRIu64
403 "\n", blockIndex);
404 }
405
406 transaction.Abort();
407
408 delete node;
409
410 return error;
411 }
412
413
414 status_t
SetName(const char * name)415 Volume::SetName(const char* name)
416 {
417 if (name == NULL || strlen(name) > kCheckSumFSNameLength)
418 return B_BAD_VALUE;
419
420 // clone the name
421 char* newName = strdup(name);
422 if (newName == NULL)
423 return B_NO_MEMORY;
424 MemoryDeleter newNameDeleter(newName);
425
426 // start a transaction
427 Transaction transaction(this);
428 status_t error = transaction.Start();
429 if (error != B_OK)
430 return error;
431
432 // we lock the volume now, to keep the locking order (transaction -> volume)
433 MutexLocker locker(fLock);
434
435 // update the superblock
436 Block block;
437 if (!block.GetWritable(this, kCheckSumFSSuperBlockOffset / B_PAGE_SIZE,
438 transaction)) {
439 return B_ERROR;
440 }
441
442 SuperBlock* superBlock = (SuperBlock*)block.Data();
443 superBlock->SetName(newName);
444
445 block.Put();
446
447 // commit the transaction
448 error = transaction.Commit();
449 if (error != B_OK)
450 return error;
451
452 // Everything went fine. We can replace the name. Since we still have the
453 // volume lock, there's no race condition.
454 free(fName);
455 fName = (char*)newNameDeleter.Detach();
456
457 return B_OK;
458 }
459
460
461 status_t
_Init(uint64 totalBlocks)462 Volume::_Init(uint64 totalBlocks)
463 {
464 fTotalBlocks = totalBlocks;
465 if (fTotalBlocks * B_PAGE_SIZE < kCheckSumFSMinSize)
466 RETURN_ERROR(B_BAD_VALUE);
467
468 // create a block cache
469 fBlockCache = block_cache_create(fFD, fTotalBlocks, B_PAGE_SIZE,
470 IsReadOnly());
471 if (fBlockCache == NULL)
472 RETURN_ERROR(B_NO_MEMORY);
473
474 // create the block allocator
475 fBlockAllocator = new(std::nothrow) BlockAllocator(this);
476 if (fBlockAllocator == NULL)
477 RETURN_ERROR(B_NO_MEMORY);
478
479 return B_OK;
480 }
481
482
483 status_t
_CreateNode(Node * node,Transaction & transaction)484 Volume::_CreateNode(Node* node, Transaction& transaction)
485 {
486 if (node == NULL)
487 return B_NO_MEMORY;
488
489 ObjectDeleter<Node> nodeDeleter(node);
490
491 // allocate a free block
492 AllocatedBlock allocatedBlock(fBlockAllocator, transaction);
493 status_t error = allocatedBlock.Allocate();
494 if (error != B_OK)
495 return error;
496
497 // clear the block
498 {
499 Block block;
500 if (!block.GetZero(this, allocatedBlock.Index(), transaction))
501 return B_ERROR;
502 }
503
504 node->SetBlockIndex(allocatedBlock.Index());
505
506 // attach the node to the transaction
507 error = transaction.AddNode(node, TRANSACTION_DELETE_NODE);
508 if (error != B_OK)
509 return error;
510
511 allocatedBlock.Detach();
512 nodeDeleter.Detach();
513
514 return B_OK;
515 }
516