xref: /haiku/src/add-ons/kernel/file_systems/bfs/Volume.cpp (revision 51978af14a173e7fae0563b562be5603bc652aeb)
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 #include <fs_volume.h>
17 
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.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.ByteOrder() != SUPER_BLOCK_FS_LENDIAN
49 		|| (1UL << fSuperBlock.BlockShift()) != fSuperBlock.BlockSize()
50 		|| fSuperBlock.AllocationGroups() < 1
51 		|| fSuperBlock.AllocationGroupShift() < 1
52 		|| fSuperBlock.BlocksPerAllocationGroup() < 1
53 		|| fSuperBlock.NumBlocks() < 10
54 		|| fSuperBlock.AllocationGroups() != divide_roundup(fSuperBlock.NumBlocks(),
55 			1L << fSuperBlock.AllocationGroupShift()))
56 		return false;
57 
58 	return true;
59 }
60 
61 
62 void
63 Volume::Panic()
64 {
65 	FATAL(("we have to panic... switch to read-only mode!\n"));
66 	fFlags |= VOLUME_READ_ONLY;
67 #ifdef USER
68 	debugger("BFS panics!");
69 #elif defined(DEBUG)
70 	kernel_debugger("BFS panics!");
71 #endif
72 }
73 
74 
75 status_t
76 Volume::Mount(const char *deviceName, uint32 flags)
77 {
78 	if (flags & B_MOUNT_READ_ONLY)
79 		fFlags |= VOLUME_READ_ONLY;
80 
81 	// ToDo: validate the FS in write mode as well!
82 #if (B_HOST_IS_LENDIAN && defined(BFS_BIG_ENDIAN_ONLY)) \
83 	|| (B_HOST_IS_BENDIAN && defined(BFS_LITTLE_ENDIAN_ONLY))
84 	// in big endian mode, we only mount read-only for now
85 	flags |= B_MOUNT_READ_ONLY;
86 #endif
87 
88 	fDevice = open(deviceName, flags & B_MOUNT_READ_ONLY ? O_RDONLY : O_RDWR);
89 
90 	// if we couldn't open the device, try read-only (don't rely on a specific error code)
91 	if (fDevice < B_OK && (flags & B_MOUNT_READ_ONLY) == 0) {
92 		fDevice = open(deviceName, O_RDONLY);
93 		fFlags |= VOLUME_READ_ONLY;
94 	}
95 
96 	if (fDevice < B_OK)
97 		RETURN_ERROR(fDevice);
98 
99 	// check if it's a regular file, and if so, disable the cache for the
100 	// underlaying file system
101 	struct stat stat;
102 	if (fstat(fDevice, &stat) < 0)
103 		RETURN_ERROR(B_ERROR);
104 
105 //#ifndef USER
106 	if (stat.st_mode & S_FILE && ioctl(fDevice, IOCTL_FILE_UNCACHED_IO, NULL) < 0) {
107 		// mount read-only if the cache couldn't be disabled
108 #	ifdef DEBUG
109 		FATAL(("couldn't disable cache for image file - system may dead-lock!\n"));
110 #	else
111 		FATAL(("couldn't disable cache for image file!\n"));
112 		Panic();
113 #	endif
114 	}
115 //#endif
116 
117 	// read the super block
118 	char buffer[1024];
119 	if (read_pos(fDevice, 0, buffer, sizeof(buffer)) != sizeof(buffer))
120 		return B_IO_ERROR;
121 
122 	status_t status = B_OK;
123 
124 	// Note: that does work only for x86, for PowerPC, the super block
125 	// is located at offset 0!
126 	memcpy(&fSuperBlock, buffer + 512, sizeof(disk_super_block));
127 	if (!IsValidSuperBlock()) {
128 #ifndef BFS_LITTLE_ENDIAN_ONLY
129 		memcpy(&fSuperBlock, buffer, sizeof(disk_super_block));
130 		if (!IsValidSuperBlock()) {
131 			close(fDevice);
132 			return B_BAD_VALUE;
133 		}
134 #else
135 		close(fDevice);
136 		return B_BAD_VALUE;
137 #endif
138 	}
139 
140 	if (IsValidSuperBlock()) {
141 		// set the current log pointers, so that journaling will work correctly
142 		fLogStart = fSuperBlock.LogStart();
143 		fLogEnd = fSuperBlock.LogEnd();
144 
145 		// initialize short hands to the super block (to save byte swapping)
146 		fBlockSize = fSuperBlock.BlockSize();
147 		fBlockShift = fSuperBlock.BlockShift();
148 		fAllocationGroupShift = fSuperBlock.AllocationGroupShift();
149 
150 		if (init_cache_for_device(fDevice, NumBlocks()) == B_OK) {
151 			fJournal = new Journal(this);
152 			// replaying the log is the first thing we will do on this disk
153 			if (fJournal && fJournal->InitCheck() == B_OK
154 				&& fBlockAllocator.Initialize() == B_OK) {
155 				fRootNode = new Inode(this, ToVnode(Root()));
156 
157 				if (fRootNode && fRootNode->InitCheck() == B_OK) {
158 					if (new_vnode(fID, ToVnode(Root()), (void *)fRootNode) == B_OK) {
159 						// try to get indices root dir
160 
161 						// question: why doesn't get_vnode() work here??
162 						// answer: we have not yet backpropagated the pointer to the
163 						// volume in bfs_mount(), so bfs_read_vnode() can't get it.
164 						// But it's not needed to do that anyway.
165 
166 						fIndicesNode = new Inode(this, ToVnode(Indices()));
167 						if (fIndicesNode == NULL
168 							|| fIndicesNode->InitCheck() < B_OK
169 							|| !fIndicesNode->IsContainer()) {
170 							INFORM(("bfs: volume doesn't have indices!\n"));
171 
172 							if (fIndicesNode) {
173 								// if this is the case, the index root node is gone bad, and
174 								// BFS switch to read-only mode
175 								fFlags |= VOLUME_READ_ONLY;
176 								fIndicesNode = NULL;
177 							}
178 						}
179 
180 						// all went fine
181 						return B_OK;
182 					} else
183 						status = B_NO_MEMORY;
184 				} else
185 					status = B_BAD_VALUE;
186 
187 				FATAL(("could not create root node: new_vnode() failed!\n"));
188 			} else {
189 				// ToDo: improve error reporting for a bad journal
190 				status = B_NO_MEMORY;
191 				FATAL(("could not initialize journal/block bitmap allocator!\n"));
192 			}
193 
194 			remove_cached_device_blocks(fDevice, NO_WRITES);
195 		} else {
196 			FATAL(("could not initialize cache!\n"));
197 			status = B_IO_ERROR;
198 		}
199 		FATAL(("invalid super block!\n"));
200 	}
201 	else
202 		status = B_BAD_VALUE;
203 
204 	close(fDevice);
205 
206 	return status;
207 }
208 
209 
210 status_t
211 Volume::Unmount()
212 {
213 	// This will also flush the log & all blocks to disk
214 	delete fJournal;
215 	fJournal = NULL;
216 
217 	delete fIndicesNode;
218 
219 	remove_cached_device_blocks(fDevice, IsReadOnly() ? NO_WRITES : ALLOW_WRITES);
220 	close(fDevice);
221 
222 	return B_OK;
223 }
224 
225 
226 status_t
227 Volume::Sync()
228 {
229 	return fJournal->FlushLogAndBlocks();
230 }
231 
232 
233 status_t
234 Volume::ValidateBlockRun(block_run run)
235 {
236 	if (run.AllocationGroup() < 0 || run.AllocationGroup() > (int32)AllocationGroups()
237 		|| run.Start() > (1UL << AllocationGroupShift())
238 		|| run.length == 0
239 		|| uint32(run.Length() + run.Start()) > (1UL << AllocationGroupShift())) {
240 		Panic();
241 		FATAL(("*** invalid run(%ld,%d,%d)\n", run.AllocationGroup(), run.Start(), run.Length()));
242 		return B_BAD_DATA;
243 	}
244 	return B_OK;
245 }
246 
247 
248 block_run
249 Volume::ToBlockRun(off_t block) const
250 {
251 	block_run run;
252 	run.allocation_group = HOST_ENDIAN_TO_BFS_INT32(block >> AllocationGroupShift());
253 	run.start = HOST_ENDIAN_TO_BFS_INT16(block & ~((1LL << AllocationGroupShift()) - 1));
254 	run.length = HOST_ENDIAN_TO_BFS_INT16(1);
255 	return run;
256 }
257 
258 
259 status_t
260 Volume::CreateIndicesRoot(Transaction *transaction)
261 {
262 	off_t id;
263 	status_t status = Inode::Create(transaction, NULL, NULL,
264 		S_INDEX_DIR | S_STR_INDEX | S_DIRECTORY | 0700, 0, 0, &id, &fIndicesNode);
265 	if (status < B_OK)
266 		RETURN_ERROR(status);
267 
268 	fSuperBlock.indices = ToBlockRun(id);
269 	return WriteSuperBlock();
270 }
271 
272 
273 status_t
274 Volume::AllocateForInode(Transaction *transaction, const Inode *parent, mode_t type, block_run &run)
275 {
276 	return fBlockAllocator.AllocateForInode(transaction, &parent->BlockRun(), type, run);
277 }
278 
279 
280 status_t
281 Volume::WriteSuperBlock()
282 {
283 	if (write_pos(fDevice, 512, &fSuperBlock, sizeof(disk_super_block)) != sizeof(disk_super_block))
284 		return B_IO_ERROR;
285 
286 	return B_OK;
287 }
288 
289 
290 void
291 Volume::UpdateLiveQueries(Inode *inode, const char *attribute, int32 type, const uint8 *oldKey,
292 	size_t oldLength, const uint8 *newKey, size_t newLength)
293 {
294 	if (fQueryLock.Lock() < B_OK)
295 		return;
296 
297 	Query *query = NULL;
298 	while ((query = fQueries.Next(query)) != NULL)
299 		query->LiveUpdate(inode, attribute, type, oldKey, oldLength, newKey, newLength);
300 
301 	fQueryLock.Unlock();
302 }
303 
304 
305 /** Checks if there is a live query whose results depend on the presence
306  *	or value of the specified attribute.
307  *	Don't use it if you already have all the data together to evaluate
308  *	the queries - it wouldn't safe you anything in this case.
309  */
310 
311 bool
312 Volume::CheckForLiveQuery(const char *attribute)
313 {
314 	// ToDo: check for a live query that depends on the specified attribute
315 	return true;
316 }
317 
318 
319 void
320 Volume::AddQuery(Query *query)
321 {
322 	if (fQueryLock.Lock() < B_OK)
323 		return;
324 
325 	fQueries.Add(query);
326 
327 	fQueryLock.Unlock();
328 }
329 
330 
331 void
332 Volume::RemoveQuery(Query *query)
333 {
334 	if (fQueryLock.Lock() < B_OK)
335 		return;
336 
337 	fQueries.Remove(query);
338 
339 	fQueryLock.Unlock();
340 }
341 
342