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