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