xref: /haiku/src/add-ons/kernel/file_systems/btrfs/Inode.cpp (revision 70d5966963513ff47a456ba70b7d2eec0f99648d)
1 /*
2  * Copyright 2011, Jérôme Duval, korli@users.berlios.de.
3  * Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
4  * This file may be used under the terms of the MIT License.
5  */
6 
7 
8 #include "Inode.h"
9 
10 #include <string.h>
11 #include <stdlib.h>
12 
13 #include "BPlusTree.h"
14 #include "CachedBlock.h"
15 #include "Utility.h"
16 
17 
18 #undef ASSERT
19 //#define TRACE_BTRFS
20 #ifdef TRACE_BTRFS
21 #	define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
22 #	define ASSERT(x) { if (!(x)) kernel_debugger("btrfs: assert failed: " #x "\n"); }
23 #else
24 #	define TRACE(x...) ;
25 #	define ASSERT(x) ;
26 #endif
27 #define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)
28 
29 
30 Inode::Inode(Volume* volume, ino_t id)
31 	:
32 	fVolume(volume),
33 	fID(id),
34 	fCache(NULL),
35 	fMap(NULL)
36 {
37 	rw_lock_init(&fLock, "btrfs inode");
38 
39 	fInitStatus = UpdateNodeFromDisk();
40 	if (fInitStatus == B_OK) {
41 		if (!IsDirectory() && !IsSymLink()) {
42 			fCache = file_cache_create(fVolume->ID(), ID(), Size());
43 			fMap = file_map_create(fVolume->ID(), ID(), Size());
44 		}
45 	}
46 }
47 
48 
49 Inode::Inode(Volume* volume)
50 	:
51 	fVolume(volume),
52 	fID(0),
53 	fCache(NULL),
54 	fMap(NULL),
55 	fInitStatus(B_NO_INIT)
56 {
57 	rw_lock_init(&fLock, "btrfs inode");
58 }
59 
60 
61 Inode::~Inode()
62 {
63 	TRACE("Inode destructor\n");
64 	file_cache_delete(FileCache());
65 	file_map_delete(Map());
66 	TRACE("Inode destructor: Done\n");
67 }
68 
69 
70 status_t
71 Inode::InitCheck()
72 {
73 	return fInitStatus;
74 }
75 
76 
77 status_t
78 Inode::UpdateNodeFromDisk()
79 {
80 	struct btrfs_key search_key;
81 	search_key.SetType(BTRFS_KEY_TYPE_INODE_ITEM);
82 	search_key.SetObjectID(fID);
83 	search_key.SetOffset(0);
84 
85 	struct btrfs_inode *node;
86 	if (fVolume->FSTree()->FindExact(search_key, (void**)&node) != B_OK) {
87 		ERROR("Inode::UpdateNodeFromDisk(): Couldn't find inode %"
88 			B_PRIdINO "\n", fID);
89 		return B_ENTRY_NOT_FOUND;
90 	}
91 
92 	memcpy(&fNode, node, sizeof(struct btrfs_inode));
93 	free(node);
94 	return B_OK;
95 }
96 
97 
98 status_t
99 Inode::CheckPermissions(int accessMode) const
100 {
101 	// you never have write access to a read-only volume
102 	if ((accessMode & W_OK) != 0 && fVolume->IsReadOnly())
103 		return B_READ_ONLY_DEVICE;
104 
105 	// get node permissions
106 	mode_t mode = Mode();
107 	int userPermissions = (mode & S_IRWXU) >> 6;
108 	int groupPermissions = (mode & S_IRWXG) >> 3;
109 	int otherPermissions = mode & S_IRWXO;
110 
111 	// get the node permissions for this uid/gid
112 	int permissions = 0;
113 	uid_t uid = geteuid();
114 	gid_t gid = getegid();
115 
116 	if (uid == 0) {
117 		// user is root
118 		// root has always read/write permission, but at least one of the
119 		// X bits must be set for execute permission
120 		permissions = userPermissions | groupPermissions | otherPermissions
121 			| R_OK | W_OK;
122 	} else if (uid == (uid_t)fNode.UserID()) {
123 		// user is node owner
124 		permissions = userPermissions;
125 	} else if (gid == (gid_t)fNode.GroupID()) {
126 		// user is in owning group
127 		permissions = groupPermissions;
128 	} else {
129 		// user is one of the others
130 		permissions = otherPermissions;
131 	}
132 
133 	return (accessMode & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED;
134 	return B_OK;
135 }
136 
137 
138 status_t
139 Inode::FindBlock(off_t pos, off_t& physical, off_t *_length)
140 {
141 	struct btrfs_key search_key;
142 	search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA);
143 	search_key.SetObjectID(fID);
144 	search_key.SetOffset(pos + 1);
145 
146 	btrfs_extent_data *extent_data;
147 	status_t status = fVolume->FSTree()->FindPrevious(search_key,
148 		(void**)&extent_data);
149 	if (status != B_OK) {
150 		ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%lx\n", status);
151 		return status;
152 	}
153 
154 	TRACE("Inode::FindBlock(%" B_PRIdINO ") key.Offset() %lld\n", ID(),
155 		search_key.Offset());
156 
157 	off_t diff = pos - search_key.Offset();
158 	off_t logical = 0;
159 	if (extent_data->Type() == BTRFS_EXTENT_DATA_REGULAR)
160 		logical = diff + extent_data->disk_offset;
161 	else
162 		panic("unknown extent type; %d\n", extent_data->Type());
163 	status = fVolume->FindBlock(logical, physical);
164 	if (_length != NULL)
165 		*_length = extent_data->Size() - diff;
166 	TRACE("Inode::FindBlock(%" B_PRIdINO ") %lld physical %lld\n", ID(),
167 		pos, physical);
168 
169 	free(extent_data);
170 	return status;
171 }
172 
173 
174 status_t
175 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length)
176 {
177 	size_t length = *_length;
178 
179 	// set/check boundaries for pos/length
180 	if (pos < 0) {
181 		ERROR("inode %" B_PRIdINO ": ReadAt failed(pos %lld, length %lu)\n",
182 			ID(), pos, length);
183 		return B_BAD_VALUE;
184 	}
185 
186 	if (pos >= Size() || length == 0) {
187 		TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %lld, length %lu)\n",
188 			ID(), pos, length);
189 		*_length = 0;
190 		return B_NO_ERROR;
191 	}
192 
193 	// the file cache doesn't seem to like non block aligned file offset
194 	// so we avoid the file cache for inline extents
195 	struct btrfs_key search_key;
196 	search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA);
197 	search_key.SetObjectID(fID);
198 	search_key.SetOffset(pos + 1);
199 
200 	btrfs_extent_data *extent_data;
201 	status_t status = fVolume->FSTree()->FindPrevious(search_key,
202 		(void**)&extent_data);
203 	if (status != B_OK) {
204 		ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%lx\n", status);
205 		return status;
206 	}
207 
208 	if (FileCache() != NULL
209 		&& extent_data->Type() == BTRFS_EXTENT_DATA_REGULAR) {
210 		TRACE("inode %" B_PRIdINO ": ReadAt cache (pos %lld, length %lu)\n",
211 			ID(), pos, length);
212 		free(extent_data);
213 		return file_cache_read(FileCache(), NULL, pos, buffer, _length);
214 	}
215 
216 	TRACE("Inode::ReadAt(%" B_PRIdINO ") key.Offset() %lld\n", ID(),
217 		search_key.Offset());
218 
219 	off_t diff = pos - search_key.Offset();
220 	if (extent_data->Type() != BTRFS_EXTENT_DATA_INLINE)
221 		panic("unknown extent type; %d\n", extent_data->Type());
222 
223 	*_length = min_c(extent_data->MemoryBytes() - diff, *_length);
224 	memcpy(buffer, extent_data->inline_data, *_length);
225 	free(extent_data);
226 	return B_OK;
227 
228 }
229 
230 
231 status_t
232 Inode::FindParent(ino_t *id)
233 {
234 	struct btrfs_key search_key;
235 	search_key.SetType(BTRFS_KEY_TYPE_INODE_REF);
236 	search_key.SetObjectID(fID);
237 	search_key.SetOffset(-1);
238 
239 	void *node_ref;
240 	if (fVolume->FSTree()->FindPrevious(search_key, &node_ref) != B_OK) {
241 		ERROR("Inode::FindParent(): Couldn't find inode for %" B_PRIdINO "\n",
242 			fID);
243 		return B_ERROR;
244 	}
245 
246 	free(node_ref);
247 	*id = search_key.Offset();
248 	TRACE("Inode::FindParent() for %" B_PRIdINO ": %" B_PRIdINO "\n", fID,
249 		*id);
250 
251 	return B_OK;
252 }
253 
254