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