1 /* 2 * Copyright 2011, Jérôme Duval, korli@users.berlios.de. 3 * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. 4 * This file may be used under the terms of the MIT License. 5 */ 6 7 8 #include "Inode.h" 9 10 #include <string.h> 11 #include <stdlib.h> 12 13 #include "BPlusTree.h" 14 #include "CachedBlock.h" 15 #include "Utility.h" 16 17 18 #undef ASSERT 19 //#define TRACE_BTRFS 20 #ifdef TRACE_BTRFS 21 # define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x) 22 # define ASSERT(x) { if (!(x)) kernel_debugger("btrfs: assert failed: " #x "\n"); } 23 #else 24 # define TRACE(x...) ; 25 # define ASSERT(x) ; 26 #endif 27 #define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x) 28 29 30 Inode::Inode(Volume* volume, ino_t id) 31 : 32 fVolume(volume), 33 fID(id), 34 fCache(NULL), 35 fMap(NULL) 36 { 37 rw_lock_init(&fLock, "btrfs inode"); 38 39 fInitStatus = UpdateNodeFromDisk(); 40 if (fInitStatus == B_OK) { 41 if (!IsDirectory() && !IsSymLink()) { 42 fCache = file_cache_create(fVolume->ID(), ID(), Size()); 43 fMap = file_map_create(fVolume->ID(), ID(), Size()); 44 } 45 } 46 } 47 48 49 Inode::Inode(Volume* volume) 50 : 51 fVolume(volume), 52 fID(0), 53 fCache(NULL), 54 fMap(NULL), 55 fInitStatus(B_NO_INIT) 56 { 57 rw_lock_init(&fLock, "btrfs inode"); 58 } 59 60 61 Inode::~Inode() 62 { 63 TRACE("Inode destructor\n"); 64 file_cache_delete(FileCache()); 65 file_map_delete(Map()); 66 TRACE("Inode destructor: Done\n"); 67 } 68 69 70 status_t 71 Inode::InitCheck() 72 { 73 return fInitStatus; 74 } 75 76 77 status_t 78 Inode::UpdateNodeFromDisk() 79 { 80 struct btrfs_key search_key; 81 search_key.SetType(BTRFS_KEY_TYPE_INODE_ITEM); 82 search_key.SetObjectID(fID); 83 search_key.SetOffset(0); 84 85 struct btrfs_inode *node; 86 if (fVolume->FSTree()->FindExact(search_key, (void**)&node) != B_OK) { 87 ERROR("Inode::UpdateNodeFromDisk(): Couldn't find inode %" 88 B_PRIdINO "\n", fID); 89 return B_ENTRY_NOT_FOUND; 90 } 91 92 memcpy(&fNode, node, sizeof(struct btrfs_inode)); 93 free(node); 94 return B_OK; 95 } 96 97 98 status_t 99 Inode::CheckPermissions(int accessMode) const 100 { 101 // you never have write access to a read-only volume 102 if ((accessMode & W_OK) != 0 && fVolume->IsReadOnly()) 103 return B_READ_ONLY_DEVICE; 104 105 // get node permissions 106 mode_t mode = Mode(); 107 int userPermissions = (mode & S_IRWXU) >> 6; 108 int groupPermissions = (mode & S_IRWXG) >> 3; 109 int otherPermissions = mode & S_IRWXO; 110 111 // get the node permissions for this uid/gid 112 int permissions = 0; 113 uid_t uid = geteuid(); 114 gid_t gid = getegid(); 115 116 if (uid == 0) { 117 // user is root 118 // root has always read/write permission, but at least one of the 119 // X bits must be set for execute permission 120 permissions = userPermissions | groupPermissions | otherPermissions 121 | R_OK | W_OK; 122 } else if (uid == (uid_t)fNode.UserID()) { 123 // user is node owner 124 permissions = userPermissions; 125 } else if (gid == (gid_t)fNode.GroupID()) { 126 // user is in owning group 127 permissions = groupPermissions; 128 } else { 129 // user is one of the others 130 permissions = otherPermissions; 131 } 132 133 return (accessMode & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED; 134 return B_OK; 135 } 136 137 138 status_t 139 Inode::FindBlock(off_t pos, off_t& physical, off_t *_length) 140 { 141 struct btrfs_key search_key; 142 search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA); 143 search_key.SetObjectID(fID); 144 search_key.SetOffset(pos + 1); 145 146 btrfs_extent_data *extent_data; 147 status_t status = fVolume->FSTree()->FindPrevious(search_key, 148 (void**)&extent_data); 149 if (status != B_OK) { 150 ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%lx\n", status); 151 return status; 152 } 153 154 TRACE("Inode::FindBlock(%" B_PRIdINO ") key.Offset() %lld\n", ID(), 155 search_key.Offset()); 156 157 off_t diff = pos - search_key.Offset(); 158 off_t logical = 0; 159 if (extent_data->Type() == BTRFS_EXTENT_DATA_REGULAR) 160 logical = diff + extent_data->disk_offset; 161 else 162 panic("unknown extent type; %d\n", extent_data->Type()); 163 status = fVolume->FindBlock(logical, physical); 164 if (_length != NULL) 165 *_length = extent_data->Size() - diff; 166 TRACE("Inode::FindBlock(%" B_PRIdINO ") %lld physical %lld\n", ID(), 167 pos, physical); 168 169 free(extent_data); 170 return status; 171 } 172 173 174 status_t 175 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length) 176 { 177 size_t length = *_length; 178 179 // set/check boundaries for pos/length 180 if (pos < 0) { 181 ERROR("inode %" B_PRIdINO ": ReadAt failed(pos %lld, length %lu)\n", 182 ID(), pos, length); 183 return B_BAD_VALUE; 184 } 185 186 if (pos >= Size() || length == 0) { 187 TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %lld, length %lu)\n", 188 ID(), pos, length); 189 *_length = 0; 190 return B_NO_ERROR; 191 } 192 193 // the file cache doesn't seem to like non block aligned file offset 194 // so we avoid the file cache for inline extents 195 struct btrfs_key search_key; 196 search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA); 197 search_key.SetObjectID(fID); 198 search_key.SetOffset(pos + 1); 199 200 btrfs_extent_data *extent_data; 201 status_t status = fVolume->FSTree()->FindPrevious(search_key, 202 (void**)&extent_data); 203 if (status != B_OK) { 204 ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%lx\n", status); 205 return status; 206 } 207 208 if (FileCache() != NULL 209 && extent_data->Type() == BTRFS_EXTENT_DATA_REGULAR) { 210 TRACE("inode %" B_PRIdINO ": ReadAt cache (pos %lld, length %lu)\n", 211 ID(), pos, length); 212 free(extent_data); 213 return file_cache_read(FileCache(), NULL, pos, buffer, _length); 214 } 215 216 TRACE("Inode::ReadAt(%" B_PRIdINO ") key.Offset() %lld\n", ID(), 217 search_key.Offset()); 218 219 off_t diff = pos - search_key.Offset(); 220 if (extent_data->Type() != BTRFS_EXTENT_DATA_INLINE) 221 panic("unknown extent type; %d\n", extent_data->Type()); 222 223 *_length = min_c(extent_data->MemoryBytes() - diff, *_length); 224 memcpy(buffer, extent_data->inline_data, *_length); 225 free(extent_data); 226 return B_OK; 227 228 } 229 230 231 status_t 232 Inode::FindParent(ino_t *id) 233 { 234 struct btrfs_key search_key; 235 search_key.SetType(BTRFS_KEY_TYPE_INODE_REF); 236 search_key.SetObjectID(fID); 237 search_key.SetOffset(-1); 238 239 void *node_ref; 240 if (fVolume->FSTree()->FindPrevious(search_key, &node_ref) != B_OK) { 241 ERROR("Inode::FindParent(): Couldn't find inode for %" B_PRIdINO "\n", 242 fID); 243 return B_ERROR; 244 } 245 246 free(node_ref); 247 *id = search_key.Offset(); 248 TRACE("Inode::FindParent() for %" B_PRIdINO ": %" B_PRIdINO "\n", fID, 249 *id); 250 251 return B_OK; 252 } 253 254