xref: /haiku/src/add-ons/kernel/file_systems/bfs/Volume.cpp (revision 2807c36668a1730dd59bc39de65e0b8f88cd5d0d)
1 /* Volume - BFS super block, mounting, etc.
2 **
3 ** Initial version by Axel Dörfler, axeld@pinc-software.de
4 ** This file may be used under the terms of the OpenBeOS License.
5 */
6 
7 
8 #include "Debug.h"
9 #include "cpp.h"
10 #include "Volume.h"
11 #include "Journal.h"
12 #include "Inode.h"
13 #include "Query.h"
14 
15 #include <KernelExport.h>
16 
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <ctype.h>
22 
23 
24 Volume::Volume(nspace_id id)
25 	:
26 	fID(id),
27 	fBlockAllocator(this),
28 	fLock("bfs volume"),
29 	fDirtyCachedBlocks(0),
30 	fUniqueID(0),
31 	fFlags(0)
32 {
33 }
34 
35 
36 Volume::~Volume()
37 {
38 }
39 
40 
41 bool
42 Volume::IsValidSuperBlock()
43 {
44 	if (fSuperBlock.magic1 != (int32)SUPER_BLOCK_MAGIC1
45 		|| fSuperBlock.magic2 != (int32)SUPER_BLOCK_MAGIC2
46 		|| fSuperBlock.magic3 != (int32)SUPER_BLOCK_MAGIC3
47 		|| (int32)fSuperBlock.block_size != fSuperBlock.inode_size
48 		|| fSuperBlock.fs_byte_order != SUPER_BLOCK_FS_LENDIAN
49 		|| (1UL << fSuperBlock.block_shift) != fSuperBlock.block_size
50 		|| fSuperBlock.num_ags < 1
51 		|| fSuperBlock.ag_shift < 1
52 		|| fSuperBlock.blocks_per_ag < 1
53 		|| fSuperBlock.num_blocks < 10
54 		|| fSuperBlock.num_ags != divide_roundup(fSuperBlock.num_blocks,1L << fSuperBlock.ag_shift))
55 		return false;
56 
57 	return true;
58 }
59 
60 
61 void
62 Volume::Panic()
63 {
64 	FATAL(("we have to panic... switch to read-only mode!\n"));
65 	fFlags |= VOLUME_READ_ONLY;
66 #ifdef USER
67 	debugger("BFS panics!");
68 #elif defined(DEBUG)
69 	kernel_debugger("BFS panics!");
70 #endif
71 }
72 
73 
74 status_t
75 Volume::Mount(const char *deviceName, uint32 flags)
76 {
77 	if (flags & B_MOUNT_READ_ONLY)
78 		fFlags |= VOLUME_READ_ONLY;
79 
80 	fDevice = open(deviceName,flags & B_MOUNT_READ_ONLY ? O_RDONLY : O_RDWR);
81 
82 	// if we couldn't open the device, try read-only (don't rely on a specific error code)
83 	if (fDevice < B_OK && (flags & B_MOUNT_READ_ONLY) == 0) {
84 		fDevice = open(deviceName, O_RDONLY);
85 		fFlags |= VOLUME_READ_ONLY;
86 	}
87 
88 	if (fDevice < B_OK)
89 		RETURN_ERROR(fDevice);
90 
91 	// check if it's a regular file, and if so, disable the cache for the
92 	// underlaying file system
93 	struct stat stat;
94 	if (fstat(fDevice, &stat) < 0)
95 		RETURN_ERROR(B_ERROR);
96 
97 //#ifndef USER
98 	if (stat.st_mode & S_FILE && ioctl(fDevice, IOCTL_FILE_UNCACHED_IO, NULL) < 0) {
99 		// mount read-only if the cache couldn't be disabled
100 #	ifdef DEBUG
101 		FATAL(("couldn't disable cache for image file - system may dead-lock!\n"));
102 #	else
103 		FATAL(("couldn't disable cache for image file!\n"));
104 		Panic();
105 #	endif
106 	}
107 //#endif
108 
109 	// read the super block
110 	char buffer[1024];
111 	if (read_pos(fDevice, 0, buffer, sizeof(buffer)) != sizeof(buffer))
112 		return B_IO_ERROR;
113 
114 	status_t status = B_OK;
115 
116 	// Note: that does work only for x86, for PowerPC, the super block
117 	// is located at offset 0!
118 	memcpy(&fSuperBlock, buffer + 512, sizeof(disk_super_block));
119 
120 	if (IsValidSuperBlock()) {
121 		// set the current log pointers, so that journaling will work correctly
122 		fLogStart = fSuperBlock.log_start;
123 		fLogEnd = fSuperBlock.log_end;
124 
125 		if (init_cache_for_device(fDevice, NumBlocks()) == B_OK) {
126 			fJournal = new Journal(this);
127 			// replaying the log is the first thing we will do on this disk
128 			if (fJournal && fJournal->InitCheck() == B_OK
129 				&& fBlockAllocator.Initialize() == B_OK) {
130 				fRootNode = new Inode(this, ToVnode(Root()));
131 
132 				if (fRootNode && fRootNode->InitCheck() == B_OK) {
133 					if (new_vnode(fID, ToVnode(Root()),(void *)fRootNode) == B_OK) {
134 						// try to get indices root dir
135 
136 						// question: why doesn't get_vnode() work here??
137 						// answer: we have not yet backpropagated the pointer to the
138 						// volume in bfs_mount(), so bfs_read_vnode() can't get it.
139 						// But it's not needed to do that anyway.
140 
141 						fIndicesNode = new Inode(this, ToVnode(Indices()));
142 						if (fIndicesNode == NULL
143 							|| fIndicesNode->InitCheck() < B_OK
144 							|| !fIndicesNode->IsContainer()) {
145 							INFORM(("bfs: volume doesn't have indices!\n"));
146 
147 							if (fIndicesNode) {
148 								// if this is the case, the index root node is gone bad, and
149 								// BFS switch to read-only mode
150 								fFlags |= VOLUME_READ_ONLY;
151 								fIndicesNode = NULL;
152 							}
153 						}
154 
155 						// all went fine
156 						return B_OK;
157 					} else
158 						status = B_NO_MEMORY;
159 				} else
160 					status = B_BAD_VALUE;
161 
162 				FATAL(("could not create root node: new_vnode() failed!\n"));
163 			} else {
164 				// ToDo: improve error reporting for a bad journal
165 				status = B_NO_MEMORY;
166 				FATAL(("could not initialize journal/block bitmap allocator!\n"));
167 			}
168 
169 			remove_cached_device_blocks(fDevice, NO_WRITES);
170 		} else {
171 			FATAL(("could not initialize cache!\n"));
172 			status = B_IO_ERROR;
173 		}
174 		FATAL(("invalid super block!\n"));
175 	}
176 	else
177 		status = B_BAD_VALUE;
178 
179 	close(fDevice);
180 
181 	return status;
182 }
183 
184 
185 status_t
186 Volume::Unmount()
187 {
188 	// This will also flush the log & all blocks to disk
189 	delete fJournal;
190 	fJournal = NULL;
191 
192 	delete fIndicesNode;
193 
194 	remove_cached_device_blocks(fDevice, IsReadOnly() ? NO_WRITES : ALLOW_WRITES);
195 	close(fDevice);
196 
197 	return B_OK;
198 }
199 
200 
201 status_t
202 Volume::Sync()
203 {
204 	return fJournal->FlushLogAndBlocks();
205 }
206 
207 
208 status_t
209 Volume::ValidateBlockRun(block_run run)
210 {
211 	if (run.allocation_group < 0 || run.allocation_group > AllocationGroups()
212 		|| run.start > (1LL << AllocationGroupShift())
213 		|| run.length == 0
214 		|| (uint32)run.length + run.start > (1LL << AllocationGroupShift())) {
215 		Panic();
216 		FATAL(("*** invalid run(%ld,%d,%d)\n", run.allocation_group, run.start, run.length));
217 		return B_BAD_DATA;
218 	}
219 	return B_OK;
220 }
221 
222 
223 block_run
224 Volume::ToBlockRun(off_t block) const
225 {
226 	block_run run;
227 	run.allocation_group = block >> fSuperBlock.ag_shift;
228 	run.start = block & ~((1LL << fSuperBlock.ag_shift) - 1);
229 	run.length = 1;
230 	return run;
231 }
232 
233 
234 status_t
235 Volume::CreateIndicesRoot(Transaction *transaction)
236 {
237 	off_t id;
238 	status_t status = Inode::Create(transaction, NULL, NULL,
239 		S_INDEX_DIR | S_STR_INDEX | S_DIRECTORY | 0700, 0, 0, &id, &fIndicesNode);
240 	if (status < B_OK)
241 		RETURN_ERROR(status);
242 
243 	fSuperBlock.indices = ToBlockRun(id);
244 	return WriteSuperBlock();
245 }
246 
247 
248 status_t
249 Volume::AllocateForInode(Transaction *transaction, const Inode *parent, mode_t type, block_run &run)
250 {
251 	return fBlockAllocator.AllocateForInode(transaction, &parent->BlockRun(), type, run);
252 }
253 
254 
255 status_t
256 Volume::WriteSuperBlock()
257 {
258 	if (write_pos(fDevice, 512, &fSuperBlock, sizeof(disk_super_block)) != sizeof(disk_super_block))
259 		return B_IO_ERROR;
260 
261 	return B_OK;
262 }
263 
264 
265 void
266 Volume::UpdateLiveQueries(Inode *inode, const char *attribute, int32 type, const uint8 *oldKey,
267 	size_t oldLength, const uint8 *newKey, size_t newLength)
268 {
269 	if (fQueryLock.Lock() < B_OK)
270 		return;
271 
272 	Query *query = NULL;
273 	while ((query = fQueries.Next(query)) != NULL)
274 		query->LiveUpdate(inode, attribute, type, oldKey, oldLength, newKey, newLength);
275 
276 	fQueryLock.Unlock();
277 }
278 
279 
280 void
281 Volume::AddQuery(Query *query)
282 {
283 	if (fQueryLock.Lock() < B_OK)
284 		return;
285 
286 	fQueries.Add(query);
287 
288 	fQueryLock.Unlock();
289 }
290 
291 
292 void
293 Volume::RemoveQuery(Query *query)
294 {
295 	if (fQueryLock.Lock() < B_OK)
296 		return;
297 
298 	fQueries.Remove(query);
299 
300 	fQueryLock.Unlock();
301 }
302 
303