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