xref: /haiku/src/add-ons/kernel/file_systems/btrfs/Inode.cpp (revision 894526b51a3d931c423878fc0eb8da610fa1fb2a)
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