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 } 137 138 139 status_t 140 Inode::FindBlock(off_t pos, off_t& physical, off_t *_length) 141 { 142 struct btrfs_key search_key; 143 search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA); 144 search_key.SetObjectID(fID); 145 search_key.SetOffset(pos + 1); 146 147 btrfs_extent_data *extent_data; 148 status_t status = fVolume->FSTree()->FindPrevious(search_key, 149 (void**)&extent_data); 150 if (status != B_OK) { 151 ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%" B_PRIx32 152 "\n", status); 153 return status; 154 } 155 156 TRACE("Inode::FindBlock(%" B_PRIdINO ") key.Offset() %" B_PRId64 "\n", 157 ID(), 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 ") %" B_PRIdOFF " physical %" 169 B_PRIdOFF "\n", ID(), 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 %" B_PRIdOFF 184 ", length %lu)\n", ID(), pos, length); 185 return B_BAD_VALUE; 186 } 187 188 if (pos >= Size() || length == 0) { 189 TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %" B_PRIdOFF 190 ", length %lu)\n", 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%" B_PRIx32 208 "\n", status); 209 return status; 210 } 211 212 uint8 compression = extent_data->Compression(); 213 if (FileCache() != NULL 214 && extent_data->Type() == BTRFS_EXTENT_DATA_REGULAR) { 215 TRACE("inode %" B_PRIdINO ": ReadAt cache (pos %" B_PRIdOFF ", length %lu)\n", 216 ID(), pos, length); 217 free(extent_data); 218 if (compression == BTRFS_EXTENT_COMPRESS_NONE) 219 return file_cache_read(FileCache(), NULL, pos, buffer, _length); 220 else if (compression == BTRFS_EXTENT_COMPRESS_ZLIB) 221 panic("zlib isn't unsupported for regular extent\n"); 222 else 223 panic("unknown extent compression; %d\n", compression); 224 } 225 226 TRACE("Inode::ReadAt(%" B_PRIdINO ") key.Offset() %" B_PRId64 "\n", ID(), 227 search_key.Offset()); 228 229 off_t diff = pos - search_key.Offset(); 230 if (extent_data->Type() != BTRFS_EXTENT_DATA_INLINE) 231 panic("unknown extent type; %d\n", extent_data->Type()); 232 233 *_length = min_c(extent_data->Size() - diff, *_length); 234 if (compression == BTRFS_EXTENT_COMPRESS_NONE) 235 memcpy(buffer, extent_data->inline_data, *_length); 236 else if (compression == BTRFS_EXTENT_COMPRESS_ZLIB) { 237 char in[2048]; 238 z_stream zStream = { 239 (Bytef*)in, // next in 240 sizeof(in), // avail in 241 0, // total in 242 NULL, // next out 243 0, // avail out 244 0, // total out 245 0, // msg 246 0, // state 247 Z_NULL, // zalloc 248 Z_NULL, // zfree 249 Z_NULL, // opaque 250 0, // data type 251 0, // adler 252 0, // reserved 253 }; 254 255 int status; 256 ssize_t offset = 0; 257 size_t inline_size = item_size - 13; 258 bool headerRead = false; 259 260 TRACE("Inode::ReadAt(%" B_PRIdINO ") diff %" B_PRIdOFF " size %" 261 B_PRIuSIZE "\n", ID(), diff, item_size); 262 263 do { 264 ssize_t bytesRead = min_c(sizeof(in), inline_size - offset); 265 memcpy(in, extent_data->inline_data + offset, bytesRead); 266 if (bytesRead != (ssize_t)sizeof(in)) { 267 if (bytesRead <= 0) { 268 status = Z_STREAM_ERROR; 269 break; 270 } 271 } 272 273 zStream.avail_in = bytesRead; 274 zStream.next_in = (Bytef*)in; 275 276 if (!headerRead) { 277 headerRead = true; 278 279 zStream.avail_out = length; 280 zStream.next_out = (Bytef*)buffer; 281 282 status = inflateInit2(&zStream, 15); 283 if (status != Z_OK) { 284 free(extent_data); 285 return B_ERROR; 286 } 287 } 288 289 status = inflate(&zStream, Z_SYNC_FLUSH); 290 offset += bytesRead; 291 if (diff > 0) { 292 zStream.next_out -= max_c(bytesRead, diff); 293 diff -= max_c(bytesRead, diff); 294 } 295 296 if (zStream.avail_in != 0 && status != Z_STREAM_END) { 297 TRACE("Inode::ReadAt() didn't read whole block: %s\n", 298 zStream.msg); 299 } 300 } while (status == Z_OK); 301 302 inflateEnd(&zStream); 303 304 if (status != Z_STREAM_END) { 305 TRACE("Inode::ReadAt() inflating failed: %d!\n", status); 306 free(extent_data); 307 return B_BAD_DATA; 308 } 309 310 *_length = zStream.total_out; 311 312 } else 313 panic("unknown extent compression; %d\n", compression); 314 free(extent_data); 315 return B_OK; 316 317 } 318 319 320 status_t 321 Inode::FindParent(ino_t *id) 322 { 323 struct btrfs_key search_key; 324 search_key.SetType(BTRFS_KEY_TYPE_INODE_REF); 325 search_key.SetObjectID(fID); 326 search_key.SetOffset(-1); 327 328 void *node_ref; 329 if (fVolume->FSTree()->FindPrevious(search_key, &node_ref) != B_OK) { 330 ERROR("Inode::FindParent(): Couldn't find inode for %" B_PRIdINO "\n", 331 fID); 332 return B_ERROR; 333 } 334 335 free(node_ref); 336 *id = search_key.Offset(); 337 TRACE("Inode::FindParent() for %" B_PRIdINO ": %" B_PRIdINO "\n", fID, 338 *id); 339 340 return B_OK; 341 } 342 343