xref: /haiku/src/tests/system/kernel/file_corruption/fs/Volume.cpp (revision 25a7b01d15612846f332751841da3579db313082)
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