1 /* 2 * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. 3 * This file may be used under the terms of the MIT License. 4 */ 5 6 7 #include "Inode.h" 8 9 #include <fs_cache.h> 10 11 #include "CachedBlock.h" 12 13 14 //#define TRACE_EXT2 15 #ifdef TRACE_EXT2 16 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x) 17 #else 18 # define TRACE(x...) ; 19 #endif 20 21 22 Inode::Inode(Volume* volume, ino_t id) 23 : 24 fVolume(volume), 25 fID(id), 26 fCache(NULL), 27 fMap(NULL), 28 fNode(NULL) 29 { 30 rw_lock_init(&fLock, "ext2 inode"); 31 32 uint32 block; 33 if (volume->GetInodeBlock(id, block) == B_OK) { 34 TRACE("inode %Ld at block %lu\n", ID(), block); 35 uint8* inodeBlock = (uint8*)block_cache_get(volume->BlockCache(), 36 block); 37 if (inodeBlock != NULL) { 38 fNode = (ext2_inode*)(inodeBlock + volume->InodeBlockIndex(id) 39 * volume->InodeSize()); 40 } 41 } 42 43 if (fNode != NULL) { 44 // TODO: we don't need a cache for short symlinks 45 fCache = file_cache_create(fVolume->ID(), ID(), Size()); 46 fMap = file_map_create(fVolume->ID(), ID(), Size()); 47 } 48 } 49 50 51 Inode::~Inode() 52 { 53 file_cache_delete(FileCache()); 54 file_map_delete(Map()); 55 56 if (fNode != NULL) { 57 uint32 block; 58 if (fVolume->GetInodeBlock(ID(), block) == B_OK) 59 block_cache_put(fVolume->BlockCache(), block); 60 } 61 } 62 63 64 status_t 65 Inode::InitCheck() 66 { 67 return fNode != NULL ? B_OK : B_ERROR; 68 } 69 70 71 status_t 72 Inode::CheckPermissions(int accessMode) const 73 { 74 uid_t user = geteuid(); 75 gid_t group = getegid(); 76 77 // you never have write access to a read-only volume 78 if (accessMode & W_OK && fVolume->IsReadOnly()) 79 return B_READ_ONLY_DEVICE; 80 81 // root users always have full access (but they can't execute files without 82 // any execute permissions set) 83 if (user == 0) { 84 if (!((accessMode & X_OK) != 0 && (Mode() & S_IXUSR) == 0) 85 || S_ISDIR(Mode())) 86 return B_OK; 87 } 88 89 // shift mode bits, to check directly against accessMode 90 mode_t mode = Mode(); 91 if (user == (uid_t)fNode->UserID()) 92 mode >>= 6; 93 else if (group == (gid_t)fNode->GroupID()) 94 mode >>= 3; 95 96 if (accessMode & ~(mode & S_IRWXO)) 97 return B_NOT_ALLOWED; 98 99 return B_OK; 100 } 101 102 103 status_t 104 Inode::FindBlock(off_t offset, uint32& block) 105 { 106 uint32 perBlock = fVolume->BlockSize() / 4; 107 uint32 perIndirectBlock = perBlock * perBlock; 108 uint32 index = offset >> fVolume->BlockShift(); 109 110 if (offset >= Size()) 111 return B_ENTRY_NOT_FOUND; 112 113 if (index < EXT2_DIRECT_BLOCKS) { 114 // direct blocks 115 block = B_LENDIAN_TO_HOST_INT32(Node().stream.direct[index]); 116 } else if ((index -= EXT2_DIRECT_BLOCKS) < perBlock) { 117 // indirect blocks 118 CachedBlock cached(fVolume); 119 uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32( 120 Node().stream.indirect)); 121 if (indirectBlocks == NULL) 122 return B_IO_ERROR; 123 124 block = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index]); 125 } else if ((index -= perBlock) < perIndirectBlock) { 126 // double indirect blocks 127 CachedBlock cached(fVolume); 128 uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32( 129 Node().stream.double_indirect)); 130 if (indirectBlocks == NULL) 131 return B_IO_ERROR; 132 133 indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32( 134 indirectBlocks[index / perBlock])); 135 if (indirectBlocks == NULL) 136 return B_IO_ERROR; 137 138 block = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index & (perBlock - 1)]); 139 } else if ((index -= perIndirectBlock) / perBlock < perIndirectBlock) { 140 // triple indirect blocks 141 CachedBlock cached(fVolume); 142 uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32( 143 Node().stream.triple_indirect)); 144 if (indirectBlocks == NULL) 145 return B_IO_ERROR; 146 147 indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32( 148 indirectBlocks[index / perIndirectBlock])); 149 if (indirectBlocks == NULL) 150 return B_IO_ERROR; 151 152 indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32( 153 indirectBlocks[(index / perBlock) & (perBlock - 1)])); 154 if (indirectBlocks == NULL) 155 return B_IO_ERROR; 156 157 block = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index & (perBlock - 1)]); 158 } else { 159 // outside of the possible data stream 160 dprintf("ext2: block outside datastream!\n"); 161 return B_ERROR; 162 } 163 164 TRACE("inode %Ld: FindBlock(offset %Ld): %lu\n", ID(), offset, block); 165 return B_OK; 166 } 167 168 169 status_t 170 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length) 171 { 172 size_t length = *_length; 173 174 // set/check boundaries for pos/length 175 if (pos < 0) 176 return B_BAD_VALUE; 177 178 if (pos >= Size() || length == 0) { 179 *_length = 0; 180 return B_NO_ERROR; 181 } 182 183 return file_cache_read(FileCache(), NULL, pos, buffer, _length); 184 } 185 186