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