1 /* 2 * Copyright 2011, Jérôme Duval, korli@users.berlios.de. 3 * Copyright 2008-2014, 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 return check_access_permissions(accessMode, Mode(), (gid_t)fNode.GroupID(), 108 (uid_t)fNode.UserID()); 109 } 110 111 112 status_t 113 Inode::FindBlock(off_t pos, off_t& physical, off_t *_length) 114 { 115 struct btrfs_key search_key; 116 search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA); 117 search_key.SetObjectID(fID); 118 search_key.SetOffset(pos + 1); 119 120 btrfs_extent_data *extent_data; 121 status_t status = fVolume->FSTree()->FindPrevious(search_key, 122 (void**)&extent_data); 123 if (status != B_OK) { 124 ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%" B_PRIx32 125 "\n", status); 126 return status; 127 } 128 129 TRACE("Inode::FindBlock(%" B_PRIdINO ") key.Offset() %" B_PRId64 "\n", 130 ID(), search_key.Offset()); 131 132 off_t diff = pos - search_key.Offset(); 133 off_t logical = 0; 134 if (extent_data->Type() == BTRFS_EXTENT_DATA_REGULAR) 135 logical = diff + extent_data->disk_offset; 136 else 137 panic("unknown extent type; %d\n", extent_data->Type()); 138 status = fVolume->FindBlock(logical, physical); 139 if (_length != NULL) 140 *_length = extent_data->Size() - diff; 141 TRACE("Inode::FindBlock(%" B_PRIdINO ") %" B_PRIdOFF " physical %" 142 B_PRIdOFF "\n", ID(), pos, physical); 143 144 free(extent_data); 145 return status; 146 } 147 148 149 status_t 150 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length) 151 { 152 size_t length = *_length; 153 154 // set/check boundaries for pos/length 155 if (pos < 0) { 156 ERROR("inode %" B_PRIdINO ": ReadAt failed(pos %" B_PRIdOFF 157 ", length %lu)\n", ID(), pos, length); 158 return B_BAD_VALUE; 159 } 160 161 if (pos >= Size() || length == 0) { 162 TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %" B_PRIdOFF 163 ", length %lu)\n", ID(), pos, length); 164 *_length = 0; 165 return B_NO_ERROR; 166 } 167 168 // the file cache doesn't seem to like non block aligned file offset 169 // so we avoid the file cache for inline extents 170 struct btrfs_key search_key; 171 search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA); 172 search_key.SetObjectID(fID); 173 search_key.SetOffset(pos + 1); 174 175 size_t item_size; 176 btrfs_extent_data *extent_data; 177 status_t status = fVolume->FSTree()->FindPrevious(search_key, 178 (void**)&extent_data, &item_size); 179 if (status != B_OK) { 180 ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%" B_PRIx32 181 "\n", status); 182 return status; 183 } 184 185 uint8 compression = extent_data->Compression(); 186 if (FileCache() != NULL 187 && extent_data->Type() == BTRFS_EXTENT_DATA_REGULAR) { 188 TRACE("inode %" B_PRIdINO ": ReadAt cache (pos %" B_PRIdOFF ", length %lu)\n", 189 ID(), pos, length); 190 free(extent_data); 191 if (compression == BTRFS_EXTENT_COMPRESS_NONE) 192 return file_cache_read(FileCache(), NULL, pos, buffer, _length); 193 else if (compression == BTRFS_EXTENT_COMPRESS_ZLIB) 194 panic("zlib isn't unsupported for regular extent\n"); 195 else 196 panic("unknown extent compression; %d\n", compression); 197 } 198 199 TRACE("Inode::ReadAt(%" B_PRIdINO ") key.Offset() %" B_PRId64 "\n", ID(), 200 search_key.Offset()); 201 202 off_t diff = pos - search_key.Offset(); 203 if (extent_data->Type() != BTRFS_EXTENT_DATA_INLINE) 204 panic("unknown extent type; %d\n", extent_data->Type()); 205 206 *_length = min_c(extent_data->Size() - diff, *_length); 207 if (compression == BTRFS_EXTENT_COMPRESS_NONE) 208 memcpy(buffer, extent_data->inline_data, *_length); 209 else if (compression == BTRFS_EXTENT_COMPRESS_ZLIB) { 210 char in[2048]; 211 z_stream zStream = { 212 (Bytef*)in, // next in 213 sizeof(in), // avail in 214 0, // total in 215 NULL, // next out 216 0, // avail out 217 0, // total out 218 0, // msg 219 0, // state 220 Z_NULL, // zalloc 221 Z_NULL, // zfree 222 Z_NULL, // opaque 223 0, // data type 224 0, // adler 225 0, // reserved 226 }; 227 228 int status; 229 ssize_t offset = 0; 230 size_t inline_size = item_size - 13; 231 bool headerRead = false; 232 233 TRACE("Inode::ReadAt(%" B_PRIdINO ") diff %" B_PRIdOFF " size %" 234 B_PRIuSIZE "\n", ID(), diff, item_size); 235 236 do { 237 ssize_t bytesRead = min_c(sizeof(in), inline_size - offset); 238 memcpy(in, extent_data->inline_data + offset, bytesRead); 239 if (bytesRead != (ssize_t)sizeof(in)) { 240 if (bytesRead <= 0) { 241 status = Z_STREAM_ERROR; 242 break; 243 } 244 } 245 246 zStream.avail_in = bytesRead; 247 zStream.next_in = (Bytef*)in; 248 249 if (!headerRead) { 250 headerRead = true; 251 252 zStream.avail_out = length; 253 zStream.next_out = (Bytef*)buffer; 254 255 status = inflateInit2(&zStream, 15); 256 if (status != Z_OK) { 257 free(extent_data); 258 return B_ERROR; 259 } 260 } 261 262 status = inflate(&zStream, Z_SYNC_FLUSH); 263 offset += bytesRead; 264 if (diff > 0) { 265 zStream.next_out -= max_c(bytesRead, diff); 266 diff -= max_c(bytesRead, diff); 267 } 268 269 if (zStream.avail_in != 0 && status != Z_STREAM_END) { 270 TRACE("Inode::ReadAt() didn't read whole block: %s\n", 271 zStream.msg); 272 } 273 } while (status == Z_OK); 274 275 inflateEnd(&zStream); 276 277 if (status != Z_STREAM_END) { 278 TRACE("Inode::ReadAt() inflating failed: %d!\n", status); 279 free(extent_data); 280 return B_BAD_DATA; 281 } 282 283 *_length = zStream.total_out; 284 285 } else 286 panic("unknown extent compression; %d\n", compression); 287 free(extent_data); 288 return B_OK; 289 290 } 291 292 293 status_t 294 Inode::FindParent(ino_t *id) 295 { 296 struct btrfs_key search_key; 297 search_key.SetType(BTRFS_KEY_TYPE_INODE_REF); 298 search_key.SetObjectID(fID); 299 search_key.SetOffset(-1); 300 301 void *node_ref; 302 if (fVolume->FSTree()->FindPrevious(search_key, &node_ref) != B_OK) { 303 ERROR("Inode::FindParent(): Couldn't find inode for %" B_PRIdINO "\n", 304 fID); 305 return B_ERROR; 306 } 307 308 free(node_ref); 309 *id = search_key.Offset(); 310 TRACE("Inode::FindParent() for %" B_PRIdINO ": %" B_PRIdINO "\n", fID, 311 *id); 312 313 return B_OK; 314 } 315 316