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