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 // TODO: we could return the size of the sparse range, as this might be more 114 // than just a block 115 116 if (index < EXT2_DIRECT_BLOCKS) { 117 // direct blocks 118 block = B_LENDIAN_TO_HOST_INT32(Node().stream.direct[index]); 119 } else if ((index -= EXT2_DIRECT_BLOCKS) < perBlock) { 120 // indirect blocks 121 CachedBlock cached(fVolume); 122 uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32( 123 Node().stream.indirect)); 124 if (indirectBlocks == NULL) 125 return B_IO_ERROR; 126 127 block = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index]); 128 } else if ((index -= perBlock) < perIndirectBlock) { 129 // double indirect blocks 130 CachedBlock cached(fVolume); 131 uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32( 132 Node().stream.double_indirect)); 133 if (indirectBlocks == NULL) 134 return B_IO_ERROR; 135 136 uint32 indirectIndex 137 = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index / perBlock]); 138 if (indirectIndex == 0) { 139 // a sparse indirect block 140 block = 0; 141 } else { 142 indirectBlocks = (uint32*)cached.SetTo(indirectIndex); 143 if (indirectBlocks == NULL) 144 return B_IO_ERROR; 145 146 block = B_LENDIAN_TO_HOST_INT32( 147 indirectBlocks[index & (perBlock - 1)]); 148 } 149 } else if ((index -= perIndirectBlock) / perBlock < perIndirectBlock) { 150 // triple indirect blocks 151 CachedBlock cached(fVolume); 152 uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32( 153 Node().stream.triple_indirect)); 154 if (indirectBlocks == NULL) 155 return B_IO_ERROR; 156 157 uint32 indirectIndex 158 = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index / perIndirectBlock]); 159 if (indirectIndex == 0) { 160 // a sparse indirect block 161 block = 0; 162 } else { 163 indirectBlocks = (uint32*)cached.SetTo(indirectIndex); 164 if (indirectBlocks == NULL) 165 return B_IO_ERROR; 166 167 indirectIndex = B_LENDIAN_TO_HOST_INT32( 168 indirectBlocks[(index / perBlock) & (perBlock - 1)]); 169 if (indirectIndex == 0) { 170 // a sparse indirect block 171 block = 0; 172 } else { 173 indirectBlocks = (uint32*)cached.SetTo(indirectIndex); 174 if (indirectBlocks == NULL) 175 return B_IO_ERROR; 176 177 block = B_LENDIAN_TO_HOST_INT32( 178 indirectBlocks[index & (perBlock - 1)]); 179 } 180 } 181 } else { 182 // outside of the possible data stream 183 dprintf("ext2: block outside datastream!\n"); 184 return B_ERROR; 185 } 186 187 TRACE("inode %Ld: FindBlock(offset %Ld): %lu\n", ID(), offset, block); 188 return B_OK; 189 } 190 191 192 status_t 193 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length) 194 { 195 size_t length = *_length; 196 197 // set/check boundaries for pos/length 198 if (pos < 0) 199 return B_BAD_VALUE; 200 201 if (pos >= Size() || length == 0) { 202 *_length = 0; 203 return B_NO_ERROR; 204 } 205 206 return file_cache_read(FileCache(), NULL, pos, buffer, _length); 207 } 208 209