1 /* 2 * Copyright 2011, Jérôme Duval, korli@users.berlios.de. 3 * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. 4 * Copyright 2005-2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. 5 * This file may be used under the terms of the MIT License. 6 */ 7 8 9 #include "Inode.h" 10 11 #include <string.h> 12 #include <stdlib.h> 13 #include <zlib.h> 14 15 #include "BPlusTree.h" 16 #include "CachedBlock.h" 17 #include "Utility.h" 18 19 20 #undef ASSERT 21 //#define TRACE_BTRFS 22 #ifdef TRACE_BTRFS 23 # define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x) 24 # define ASSERT(x) { if (!(x)) kernel_debugger("btrfs: assert failed: " #x "\n"); } 25 #else 26 # define TRACE(x...) ; 27 # define ASSERT(x) ; 28 #endif 29 #define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x) 30 31 32 Inode::Inode(Volume* volume, ino_t id) 33 : 34 fVolume(volume), 35 fID(id), 36 fCache(NULL), 37 fMap(NULL) 38 { 39 rw_lock_init(&fLock, "btrfs inode"); 40 41 fInitStatus = UpdateNodeFromDisk(); 42 if (fInitStatus == B_OK) { 43 if (!IsDirectory() && !IsSymLink()) { 44 fCache = file_cache_create(fVolume->ID(), ID(), Size()); 45 fMap = file_map_create(fVolume->ID(), ID(), Size()); 46 } 47 } 48 } 49 50 51 Inode::Inode(Volume* volume) 52 : 53 fVolume(volume), 54 fID(0), 55 fCache(NULL), 56 fMap(NULL), 57 fInitStatus(B_NO_INIT) 58 { 59 rw_lock_init(&fLock, "btrfs inode"); 60 } 61 62 63 Inode::~Inode() 64 { 65 TRACE("Inode destructor\n"); 66 file_cache_delete(FileCache()); 67 file_map_delete(Map()); 68 TRACE("Inode destructor: Done\n"); 69 } 70 71 72 status_t 73 Inode::InitCheck() 74 { 75 return fInitStatus; 76 } 77 78 79 status_t 80 Inode::UpdateNodeFromDisk() 81 { 82 struct btrfs_key search_key; 83 search_key.SetType(BTRFS_KEY_TYPE_INODE_ITEM); 84 search_key.SetObjectID(fID); 85 search_key.SetOffset(0); 86 87 struct btrfs_inode *node; 88 if (fVolume->FSTree()->FindExact(search_key, (void**)&node) != B_OK) { 89 ERROR("Inode::UpdateNodeFromDisk(): Couldn't find inode %" 90 B_PRIdINO "\n", fID); 91 return B_ENTRY_NOT_FOUND; 92 } 93 94 memcpy(&fNode, node, sizeof(struct btrfs_inode)); 95 free(node); 96 return B_OK; 97 } 98 99 100 status_t 101 Inode::CheckPermissions(int accessMode) const 102 { 103 // you never have write access to a read-only volume 104 if ((accessMode & W_OK) != 0 && fVolume->IsReadOnly()) 105 return B_READ_ONLY_DEVICE; 106 107 // get node permissions 108 mode_t mode = Mode(); 109 int userPermissions = (mode & S_IRWXU) >> 6; 110 int groupPermissions = (mode & S_IRWXG) >> 3; 111 int otherPermissions = mode & S_IRWXO; 112 113 // get the node permissions for this uid/gid 114 int permissions = 0; 115 uid_t uid = geteuid(); 116 gid_t gid = getegid(); 117 118 if (uid == 0) { 119 // user is root 120 // root has always read/write permission, but at least one of the 121 // X bits must be set for execute permission 122 permissions = userPermissions | groupPermissions | otherPermissions 123 | R_OK | W_OK; 124 } else if (uid == (uid_t)fNode.UserID()) { 125 // user is node owner 126 permissions = userPermissions; 127 } else if (gid == (gid_t)fNode.GroupID()) { 128 // user is in owning group 129 permissions = groupPermissions; 130 } else { 131 // user is one of the others 132 permissions = otherPermissions; 133 } 134 135 return (accessMode & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED; 136 return B_OK; 137 } 138 139 140 status_t 141 Inode::FindBlock(off_t pos, off_t& physical, off_t *_length) 142 { 143 struct btrfs_key search_key; 144 search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA); 145 search_key.SetObjectID(fID); 146 search_key.SetOffset(pos + 1); 147 148 btrfs_extent_data *extent_data; 149 status_t status = fVolume->FSTree()->FindPrevious(search_key, 150 (void**)&extent_data); 151 if (status != B_OK) { 152 ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%lx\n", status); 153 return status; 154 } 155 156 TRACE("Inode::FindBlock(%" B_PRIdINO ") key.Offset() %lld\n", ID(), 157 search_key.Offset()); 158 159 off_t diff = pos - search_key.Offset(); 160 off_t logical = 0; 161 if (extent_data->Type() == BTRFS_EXTENT_DATA_REGULAR) 162 logical = diff + extent_data->disk_offset; 163 else 164 panic("unknown extent type; %d\n", extent_data->Type()); 165 status = fVolume->FindBlock(logical, physical); 166 if (_length != NULL) 167 *_length = extent_data->Size() - diff; 168 TRACE("Inode::FindBlock(%" B_PRIdINO ") %lld physical %lld\n", ID(), 169 pos, physical); 170 171 free(extent_data); 172 return status; 173 } 174 175 176 status_t 177 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length) 178 { 179 size_t length = *_length; 180 181 // set/check boundaries for pos/length 182 if (pos < 0) { 183 ERROR("inode %" B_PRIdINO ": ReadAt failed(pos %lld, length %lu)\n", 184 ID(), pos, length); 185 return B_BAD_VALUE; 186 } 187 188 if (pos >= Size() || length == 0) { 189 TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %lld, length %lu)\n", 190 ID(), pos, length); 191 *_length = 0; 192 return B_NO_ERROR; 193 } 194 195 // the file cache doesn't seem to like non block aligned file offset 196 // so we avoid the file cache for inline extents 197 struct btrfs_key search_key; 198 search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA); 199 search_key.SetObjectID(fID); 200 search_key.SetOffset(pos + 1); 201 202 size_t item_size; 203 btrfs_extent_data *extent_data; 204 status_t status = fVolume->FSTree()->FindPrevious(search_key, 205 (void**)&extent_data, &item_size); 206 if (status != B_OK) { 207 ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%lx\n", status); 208 return status; 209 } 210 211 uint8 compression = extent_data->Compression(); 212 if (FileCache() != NULL 213 && extent_data->Type() == BTRFS_EXTENT_DATA_REGULAR) { 214 TRACE("inode %" B_PRIdINO ": ReadAt cache (pos %lld, length %lu)\n", 215 ID(), pos, length); 216 free(extent_data); 217 if (compression == BTRFS_EXTENT_COMPRESS_NONE) 218 return file_cache_read(FileCache(), NULL, pos, buffer, _length); 219 else if (compression == BTRFS_EXTENT_COMPRESS_ZLIB) 220 panic("zlib isn't unsupported for regular extent\n"); 221 else 222 panic("unknown extent compression; %d\n", compression); 223 } 224 225 TRACE("Inode::ReadAt(%" B_PRIdINO ") key.Offset() %lld\n", ID(), 226 search_key.Offset()); 227 228 off_t diff = pos - search_key.Offset(); 229 if (extent_data->Type() != BTRFS_EXTENT_DATA_INLINE) 230 panic("unknown extent type; %d\n", extent_data->Type()); 231 232 *_length = min_c(extent_data->Size() - diff, *_length); 233 if (compression == BTRFS_EXTENT_COMPRESS_NONE) 234 memcpy(buffer, extent_data->inline_data, *_length); 235 else if (compression == BTRFS_EXTENT_COMPRESS_ZLIB) { 236 char in[2048]; 237 z_stream zStream = { 238 (Bytef*)in, // next in 239 sizeof(in), // avail in 240 0, // total in 241 NULL, // next out 242 0, // avail out 243 0, // total out 244 0, // msg 245 0, // state 246 Z_NULL, // zalloc 247 Z_NULL, // zfree 248 Z_NULL, // opaque 249 0, // data type 250 0, // adler 251 0, // reserved 252 }; 253 254 int status; 255 ssize_t offset = 0; 256 size_t inline_size = item_size - 13; 257 bool headerRead = false; 258 259 TRACE("Inode::ReadAt(%" B_PRIdINO ") diff %lld size %ld\n", ID(), 260 diff, item_size); 261 262 do { 263 ssize_t bytesRead = min_c(sizeof(in), inline_size - offset); 264 memcpy(in, extent_data->inline_data + offset, bytesRead); 265 if (bytesRead != (ssize_t)sizeof(in)) { 266 if (bytesRead <= 0) { 267 status = Z_STREAM_ERROR; 268 break; 269 } 270 } 271 272 zStream.avail_in = bytesRead; 273 zStream.next_in = (Bytef*)in; 274 275 if (!headerRead) { 276 headerRead = true; 277 278 zStream.avail_out = length; 279 zStream.next_out = (Bytef*)buffer; 280 281 status = inflateInit2(&zStream, 15); 282 if (status != Z_OK) { 283 free(extent_data); 284 return B_ERROR; 285 } 286 } 287 288 status = inflate(&zStream, Z_SYNC_FLUSH); 289 offset += bytesRead; 290 if (diff > 0) { 291 zStream.next_out -= max_c(bytesRead, diff); 292 diff -= max_c(bytesRead, diff); 293 } 294 295 if (zStream.avail_in != 0 && status != Z_STREAM_END) { 296 TRACE("Inode::ReadAt() didn't read whole block: %s\n", 297 zStream.msg); 298 } 299 } while (status == Z_OK); 300 301 inflateEnd(&zStream); 302 303 if (status != Z_STREAM_END) { 304 TRACE("Inode::ReadAt() inflating failed: %d!\n", status); 305 free(extent_data); 306 return B_BAD_DATA; 307 } 308 309 *_length = zStream.total_out; 310 311 } else 312 panic("unknown extent compression; %d\n", compression); 313 free(extent_data); 314 return B_OK; 315 316 } 317 318 319 status_t 320 Inode::FindParent(ino_t *id) 321 { 322 struct btrfs_key search_key; 323 search_key.SetType(BTRFS_KEY_TYPE_INODE_REF); 324 search_key.SetObjectID(fID); 325 search_key.SetOffset(-1); 326 327 void *node_ref; 328 if (fVolume->FSTree()->FindPrevious(search_key, &node_ref) != B_OK) { 329 ERROR("Inode::FindParent(): Couldn't find inode for %" B_PRIdINO "\n", 330 fID); 331 return B_ERROR; 332 } 333 334 free(node_ref); 335 *id = search_key.Offset(); 336 TRACE("Inode::FindParent() for %" B_PRIdINO ": %" B_PRIdINO "\n", fID, 337 *id); 338 339 return B_OK; 340 } 341 342