1/* Inode - inode access functions 2 * 3 * Copyright 2001-2008, Axel D��rfler, axeld@pinc-software.de 4 * This file may be used under the terms of the MIT License. 5 */ 6#ifndef INODE_H 7#define INODE_H 8 9 10#include <KernelExport.h> 11#ifdef USER 12# include <stdio.h> 13#endif 14 15#include "cache.h" 16#include "fsproto.h" 17 18#include <string.h> 19#include <unistd.h> 20 21#include "Volume.h" 22#include "Journal.h" 23#include "Lock.h" 24#include "Chain.h" 25#include "Debug.h" 26 27 28class BPlusTree; 29class TreeIterator; 30class AttributeIterator; 31class InodeAllocator; 32 33 34enum inode_type { 35 S_DIRECTORY = S_IFDIR, 36 S_FILE = S_IFREG, 37 S_SYMLINK = S_IFLNK, 38 39 S_INDEX_TYPES = (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX | S_LONG_LONG_INDEX 40 | S_ULONG_LONG_INDEX | S_FLOAT_INDEX | S_DOUBLE_INDEX) 41}; 42 43 44// The CachedBlock class is completely implemented as inlines. 45// It should be used when cache single blocks to make sure they 46// will be properly released after use (and it's also very 47// convenient to use them). 48 49class CachedBlock { 50 public: 51 CachedBlock(Volume *volume); 52 CachedBlock(Volume *volume, off_t block, bool empty = false); 53 CachedBlock(Volume *volume, block_run run, bool empty = false); 54 CachedBlock(CachedBlock *cached); 55 ~CachedBlock(); 56 57 inline void Keep(); 58 inline void Unset(); 59 inline uint8 *SetTo(off_t block, bool empty = false); 60 inline uint8 *SetTo(block_run run, bool empty = false); 61 inline status_t WriteBack(Transaction *transaction); 62 63 uint8 *Block() const { return fBlock; } 64 off_t BlockNumber() const { return fBlockNumber; } 65 uint32 BlockSize() const { return fVolume->BlockSize(); } 66 uint32 BlockShift() const { return fVolume->BlockShift(); } 67 68 private: 69 CachedBlock(const CachedBlock &); 70 CachedBlock &operator=(const CachedBlock &); 71 // no implementation 72 73 protected: 74 Volume *fVolume; 75 off_t fBlockNumber; 76 uint8 *fBlock; 77}; 78 79//-------------------------------------- 80 81class Inode : public CachedBlock { 82 public: 83 Inode(Volume *volume, vnode_id id, bool empty = false, uint8 reenter = 0); 84 Inode(CachedBlock *cached); 85 ~Inode(); 86 87 bfs_inode *Node() const { return (bfs_inode *)fBlock; } 88 vnode_id ID() const { return fVolume->ToVnode(fBlockNumber); } 89 90 ReadWriteLock &Lock() { return fLock; } 91 SimpleLock &SmallDataLock() { return fSmallDataLock; } 92 93 mode_t Mode() const { return Node()->Mode(); } 94 uint32 Type() const { return Node()->Type(); } 95 int32 Flags() const { return Node()->Flags(); } 96 bool IsContainer() const { return Mode() & (S_DIRECTORY | S_INDEX_DIR | S_ATTR_DIR); } 97 // note, that this test will also be true for S_IFBLK (not that it's used in the fs :) 98 bool IsDirectory() const { return (Mode() & (S_DIRECTORY | S_INDEX_DIR | S_ATTR_DIR)) == S_DIRECTORY; } 99 bool IsIndex() const { return (Mode() & (S_INDEX_DIR | 0777)) == S_INDEX_DIR; } 100 // that's a stupid check, but AFAIK the only possible method... 101 102 bool IsDeleted() const { return (Flags() & INODE_DELETED) != 0; } 103 104 bool IsAttributeDirectory() const { return (Mode() & S_ATTR_DIR) != 0; } 105 bool IsAttribute() const { return Mode() & S_ATTR; } 106 bool IsFile() const { return (Mode() & (S_IFMT | S_ATTR)) == S_FILE; } 107 bool IsRegularNode() const { return (Mode() & (S_ATTR_DIR | S_INDEX_DIR | S_ATTR)) == 0; } 108 // a regular node in the standard namespace (i.e. not an index or attribute) 109 bool IsSymLink() const { return S_ISLNK(Mode()); } 110 bool HasUserAccessableStream() const { return IsFile(); } 111 // currently only files can be accessed with bfs_read()/bfs_write() 112 113 off_t Size() const { return Node()->data.Size(); } 114 off_t LastModified() const { return Node()->last_modified_time; } 115 116 block_run &BlockRun() const { return Node()->inode_num; } 117 block_run &Parent() const { return Node()->parent; } 118 block_run &Attributes() const { return Node()->attributes; } 119 Volume *GetVolume() const { return fVolume; } 120 121 status_t InitCheck(bool checkNode = true); 122 123 status_t CheckPermissions(int accessMode) const; 124 125 // small_data access methods 126 status_t MakeSpaceForSmallData(Transaction *transaction, const char *name, int32 length); 127 status_t RemoveSmallData(Transaction *transaction, const char *name); 128 status_t AddSmallData(Transaction *transaction, const char *name, uint32 type, 129 const uint8 *data, size_t length, bool force = false); 130 status_t GetNextSmallData(small_data **_smallData) const; 131 small_data *FindSmallData(const char *name) const; 132 const char *Name() const; 133 status_t GetName(char *buffer) const; 134 status_t SetName(Transaction *transaction, const char *name); 135 136 // high-level attribute methods 137 status_t ReadAttribute(const char *name, int32 type, off_t pos, uint8 *buffer, size_t *_length); 138 status_t WriteAttribute(Transaction *transaction, const char *name, int32 type, off_t pos, const uint8 *buffer, size_t *_length); 139 status_t RemoveAttribute(Transaction *transaction, const char *name); 140 141 // attribute methods 142 status_t GetAttribute(const char *name, Inode **attribute); 143 void ReleaseAttribute(Inode *attribute); 144 status_t CreateAttribute(Transaction *transaction, const char *name, uint32 type, Inode **attribute); 145 146 // for directories only: 147 status_t GetTree(BPlusTree **); 148 bool IsEmpty(); 149 150 // manipulating the data stream 151 status_t FindBlockRun(off_t pos, block_run &run, off_t &offset); 152 153 status_t ReadAt(off_t pos, uint8 *buffer, size_t *length); 154 status_t WriteAt(Transaction *transaction, off_t pos, const uint8 *buffer, size_t *length); 155 status_t FillGapWithZeros(off_t oldSize, off_t newSize); 156 157 status_t SetFileSize(Transaction *transaction, off_t size); 158 status_t Append(Transaction *transaction, off_t bytes); 159 status_t Trim(Transaction *transaction); 160 161 status_t Free(Transaction *transaction); 162 status_t Sync(); 163 164 // create/remove inodes 165 status_t Remove(Transaction *transaction, const char *name, off_t *_id = NULL, 166 bool isDirectory = false); 167 static status_t Create(Transaction *transaction, Inode *parent, const char *name, 168 int32 mode, int omode, uint32 type, off_t *_id = NULL, Inode **_inode = NULL); 169 170 // index maintaining helper 171 void UpdateOldSize() { fOldSize = Size(); } 172 void UpdateOldLastModified() { fOldLastModified = Node()->LastModifiedTime(); } 173 off_t OldSize() { return fOldSize; } 174 off_t OldLastModified() { return fOldLastModified; } 175 176 // file cache 177 void *FileCache() const { return fCache; } 178 void SetFileCache(void *cache) { fCache = cache; } 179 180 private: 181 Inode(const Inode &); 182 Inode &operator=(const Inode &); 183 // no implementation 184 185 friend void dump_inode(Inode &inode); 186 friend class AttributeIterator; 187 friend class InodeAllocator; 188 189 void Initialize(); 190 191 status_t RemoveSmallData(small_data *item, int32 index); 192 193 void AddIterator(AttributeIterator *iterator); 194 void RemoveIterator(AttributeIterator *iterator); 195 196 status_t FreeStaticStreamArray(Transaction *transaction, int32 level, block_run run, 197 off_t size, off_t offset, off_t &max); 198 status_t FreeStreamArray(Transaction *transaction, block_run *array, uint32 arrayLength, 199 off_t size, off_t &offset, off_t &max); 200 status_t AllocateBlockArray(Transaction *transaction, block_run &run); 201 status_t GrowStream(Transaction *transaction, off_t size); 202 status_t ShrinkStream(Transaction *transaction, off_t size); 203 204 BPlusTree *fTree; 205 Inode *fAttributes; 206 ReadWriteLock fLock; 207 off_t fOldSize; // we need those values to ensure we will remove 208 off_t fOldLastModified; // the correct keys from the indices 209 void *fCache; 210 211 mutable SimpleLock fSmallDataLock; 212 Chain<AttributeIterator> fIterators; 213}; 214 215 216// The Vnode class provides a convenience layer upon get_vnode(), so that 217// you don't have to call put_vnode() anymore, which may make code more 218// readable in some cases 219 220class Vnode { 221 public: 222 Vnode(Volume *volume, vnode_id id) 223 : 224 fVolume(volume), 225 fID(id) 226 { 227 } 228 229 Vnode(Volume *volume, block_run run) 230 : 231 fVolume(volume), 232 fID(volume->ToVnode(run)) 233 { 234 } 235 236 ~Vnode() 237 { 238 Put(); 239 } 240 241 status_t Get(Inode **inode) 242 { 243 // should we check inode against NULL here? it should not be necessary 244#ifdef UNSAFE_GET_VNODE 245 RecursiveLocker locker(fVolume->Lock()); 246#endif 247 return get_vnode(fVolume->ID(), fID, (void **)inode); 248 } 249 250 void Put() 251 { 252 if (fVolume) 253 put_vnode(fVolume->ID(), fID); 254 fVolume = NULL; 255 } 256 257 void Keep() 258 { 259 fVolume = NULL; 260 } 261 262 private: 263 Volume *fVolume; 264 vnode_id fID; 265}; 266 267 268class AttributeIterator { 269 public: 270 AttributeIterator(Inode *inode); 271 ~AttributeIterator(); 272 273 status_t Rewind(); 274 status_t GetNext(char *name, size_t *length, uint32 *type, vnode_id *id); 275 276 private: 277 int32 fCurrentSmallData; 278 Inode *fInode, *fAttributes; 279 TreeIterator *fIterator; 280 void *fBuffer; 281 282 private: 283 friend class Chain<AttributeIterator>; 284 friend class Inode; 285 286 void Update(uint16 index, int8 change); 287 AttributeIterator *fNext; 288}; 289 290 291//-------------------------------------- 292// inlines 293 294 295inline 296CachedBlock::CachedBlock(Volume *volume) 297 : 298 fVolume(volume), 299 fBlockNumber(-1), 300 fBlock(NULL) 301{ 302} 303 304 305inline 306CachedBlock::CachedBlock(Volume *volume, off_t block, bool empty) 307 : 308 fVolume(volume), 309 fBlock(NULL) 310{ 311 SetTo(block, empty); 312} 313 314 315inline 316CachedBlock::CachedBlock(Volume *volume, block_run run, bool empty) 317 : 318 fVolume(volume), 319 fBlock(NULL) 320{ 321 SetTo(volume->ToBlock(run), empty); 322} 323 324 325inline 326CachedBlock::CachedBlock(CachedBlock *cached) 327 : 328 fVolume(cached->fVolume), 329 fBlockNumber(cached->BlockNumber()), 330 fBlock(cached->fBlock) 331{ 332 cached->Keep(); 333} 334 335 336inline 337CachedBlock::~CachedBlock() 338{ 339 Unset(); 340} 341 342 343inline void 344CachedBlock::Keep() 345{ 346 fBlock = NULL; 347} 348 349 350inline void 351CachedBlock::Unset() 352{ 353 if (fBlock != NULL) 354 release_block(fVolume->Device(), fBlockNumber); 355} 356 357 358inline uint8 * 359CachedBlock::SetTo(off_t block, bool empty) 360{ 361 Unset(); 362 fBlockNumber = block; 363 return fBlock = empty ? (uint8 *)get_empty_block(fVolume->Device(), block, BlockSize()) 364 : (uint8 *)get_block(fVolume->Device(), block, BlockSize()); 365} 366 367 368inline uint8 * 369CachedBlock::SetTo(block_run run, bool empty) 370{ 371 return SetTo(fVolume->ToBlock(run), empty); 372} 373 374 375inline status_t 376CachedBlock::WriteBack(Transaction *transaction) 377{ 378 if (transaction == NULL || fBlock == NULL) 379 RETURN_ERROR(B_BAD_VALUE); 380 381 return transaction->WriteBlocks(fBlockNumber, fBlock); 382} 383 384 385/** Converts the "omode", the open flags given to bfs_open(), into 386 * access modes, e.g. since O_RDONLY requires read access to the 387 * file, it will be converted to R_OK. 388 */ 389 390inline int 391oModeToAccess(int omode) 392{ 393 omode &= O_RWMASK; 394 if (omode == O_RDONLY) 395 return R_OK; 396 else if (omode == O_WRONLY) 397 return W_OK; 398 399 return R_OK | W_OK; 400} 401 402#endif /* INODE_H */ 403