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
32Inode::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
51Inode::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
63Inode::~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
72status_t
73Inode::InitCheck()
74{
75	return fInitStatus;
76}
77
78
79status_t
80Inode::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
100status_t
101Inode::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
140status_t
141Inode::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
176status_t
177Inode::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
319status_t
320Inode::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