xref: /haiku/src/add-ons/kernel/file_systems/xfs/NodeAttribute.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
1 /*
2  * Copyright 2022, Raghav Sharma, raghavself28@gmail.com
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "NodeAttribute.h"
8 
9 #include "VerifyHeader.h"
10 
11 
12 NodeAttribute::NodeAttribute(Inode* inode)
13 	:
14 	fInode(inode),
15 	fName(NULL),
16 	fLeafBuffer(NULL),
17 	fNodeBuffer(NULL)
18 {
19 	fLastEntryOffset	=	0;
20 	fLastNodeOffset		=	0;
21 	fNodeFlag			=	0;
22 }
23 
24 
25 NodeAttribute::~NodeAttribute()
26 {
27 	delete fMap;
28 	delete[] fLeafBuffer;
29 	delete[] fNodeBuffer;
30 }
31 
32 
33 status_t
34 NodeAttribute::Init()
35 {
36 	status_t status;
37 
38 	fMap = new(std::nothrow) ExtentMapEntry;
39 	if (fMap == NULL)
40 		return B_NO_MEMORY;
41 
42 	status = _FillMapEntry(0);
43 
44 	if (status != B_OK)
45 		return status;
46 
47     // allocate memory for both Node and Leaf Buffer
48 	int len = fInode->DirBlockSize();
49 	fLeafBuffer = new(std::nothrow) char[len];
50 	if (fLeafBuffer == NULL)
51 		return B_NO_MEMORY;
52 	fNodeBuffer = new(std::nothrow) char[len];
53 	if (fNodeBuffer == NULL)
54 		return B_NO_MEMORY;
55 
56 	// fill up Node buffer just one time
57 	status = _FillBuffer(fNodeBuffer, fMap->br_startblock);
58 	if (status != B_OK)
59 		return status;
60 
61 	NodeHeader* header = NodeHeader::Create(fInode,fNodeBuffer);
62 	if (header == NULL)
63 		return B_NO_MEMORY;
64 
65 	if (!VerifyHeader<NodeHeader>(header, fNodeBuffer, fInode, 0, fMap, ATTR_NODE)) {
66 		ERROR("Invalid data header");
67 		delete header;
68 		return B_BAD_VALUE;
69 	}
70 	delete header;
71 
72 	return B_OK;
73 }
74 
75 
76 status_t
77 NodeAttribute::_FillMapEntry(xfs_extnum_t num)
78 {
79 	void* attributeFork = DIR_AFORK_PTR(fInode->Buffer(),
80 		fInode->CoreInodeSize(), fInode->ForkOffset());
81 
82 	uint64* pointerToMap = (uint64*)(((char*)attributeFork + num * EXTENT_SIZE));
83 	uint64 firstHalf = pointerToMap[0];
84 	uint64 secondHalf = pointerToMap[1];
85 		//dividing the 128 bits into 2 parts.
86 
87 	firstHalf = B_BENDIAN_TO_HOST_INT64(firstHalf);
88 	secondHalf = B_BENDIAN_TO_HOST_INT64(secondHalf);
89 	fMap->br_state = firstHalf >> 63;
90 	fMap->br_startoff = (firstHalf & MASK(63)) >> 9;
91 	fMap->br_startblock = ((firstHalf & MASK(9)) << 43) | (secondHalf >> 21);
92 	fMap->br_blockcount = secondHalf & MASK(21);
93 
94 	return B_OK;
95 }
96 
97 
98 xfs_fsblock_t
99 NodeAttribute::_LogicalToFileSystemBlock(uint32 logicalBlock)
100 {
101 	xfs_extnum_t totalExtents = fInode->AttrExtentsCount();
102 
103 	// If logicalBlock is lesser than or equal to ExtentsCount then
104 	// simply read and return extent block count.
105 	// else read last Map and add difference of logical block offset
106 	if (logicalBlock < (uint32)totalExtents) {
107 		_FillMapEntry(logicalBlock);
108 		return fMap->br_startblock;
109 	} else {
110 		_FillMapEntry(totalExtents - 1);
111 		return fMap->br_startblock + logicalBlock - fMap->br_startoff;
112 	}
113 }
114 
115 
116 status_t
117 NodeAttribute::_FillBuffer(char* buffer, xfs_fsblock_t block)
118 {
119 	int len = fInode->DirBlockSize();
120 
121 	xfs_daddr_t readPos =
122 		fInode->FileSystemBlockToAddr(block);
123 
124 	if (read_pos(fInode->GetVolume()->Device(), readPos, buffer, len) != len) {
125 		ERROR("Extent::FillBlockBuffer(): IO Error");
126 		return B_IO_ERROR;
127 	}
128 
129 	return B_OK;
130 }
131 
132 
133 status_t
134 NodeAttribute::Open(const char* name, int openMode, attr_cookie** _cookie)
135 {
136 	TRACE("NodeAttribute::Open\n");
137 
138 	size_t length = strlen(name);
139 	status_t status = Lookup(name, &length);
140 	if (status < B_OK)
141 		return status;
142 
143 	attr_cookie* cookie = new(std::nothrow) attr_cookie;
144 	if (cookie == NULL)
145 		return B_NO_MEMORY;
146 
147 	fName = name;
148 
149 	// initialize the cookie
150 	strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH);
151 	cookie->open_mode = openMode;
152 	cookie->create = false;
153 
154 	*_cookie = cookie;
155 	return B_OK;
156 }
157 
158 
159 status_t
160 NodeAttribute::Stat(attr_cookie* cookie, struct stat& stat)
161 {
162 	TRACE("NodeAttribute::Stat\n");
163 
164 	fName = cookie->name;
165 
166 	size_t namelength = strlen(fName);
167 
168 	status_t status;
169 
170 	// check if this attribute exists
171 	status = Lookup(fName, &namelength);
172 
173 	if(status != B_OK)
174 		return status;
175 
176 	// We have valid attribute entry to stat
177 	if (fLocalEntry != NULL) {
178 		uint16 valuelen = B_BENDIAN_TO_HOST_INT16(fLocalEntry->valuelen);
179 		stat.st_type = B_XATTR_TYPE;
180 		stat.st_size = valuelen + fLocalEntry->namelen;
181 	} else {
182 		uint32 valuelen = B_BENDIAN_TO_HOST_INT32(fRemoteEntry->valuelen);
183 		stat.st_type = B_XATTR_TYPE;
184 		stat.st_size = valuelen + fRemoteEntry->namelen;
185 	}
186 
187 	return B_OK;
188 }
189 
190 
191 status_t
192 NodeAttribute::Read(attr_cookie* cookie, off_t pos, uint8* buffer, size_t* length)
193 {
194 	TRACE("NodeAttribute::Read\n");
195 
196 	if (pos < 0)
197 		return B_BAD_VALUE;
198 
199 	fName = cookie->name;
200 
201 	size_t namelength = strlen(fName);
202 
203 	status_t status;
204 
205 	status = Lookup(fName, &namelength);
206 
207 	if (status != B_OK)
208 		return status;
209 
210 	uint32 lengthToRead = 0;
211 
212 	if (fLocalEntry != NULL) {
213 		uint16 valuelen = B_BENDIAN_TO_HOST_INT16(fLocalEntry->valuelen);
214 		if (pos + *length > valuelen)
215 			lengthToRead = valuelen - pos;
216 		else
217 			lengthToRead = *length;
218 
219 		char* ptrToOffset = (char*) fLocalEntry + sizeof(uint16)
220 		+ sizeof(uint8) + fLocalEntry->namelen;
221 
222 		memcpy(buffer, ptrToOffset, lengthToRead);
223 
224 		*length = lengthToRead;
225 
226 		return B_OK;
227 	} else {
228 		uint32 valuelen = B_BENDIAN_TO_HOST_INT32(fRemoteEntry->valuelen);
229 		if (pos + *length > valuelen)
230 			lengthToRead = valuelen - pos;
231 		else
232 			lengthToRead = *length;
233 
234 		// For remote value blocks, value is stored in seperate file system block
235 		uint32 blkno = B_BENDIAN_TO_HOST_INT32(fRemoteEntry->valueblk);
236 
237 		xfs_daddr_t readPos =
238 			fInode->FileSystemBlockToAddr(blkno);
239 
240 		if (fInode->Version() == 3)
241 			pos += sizeof(AttrRemoteHeader);
242 
243 		readPos += pos;
244 
245 		// TODO : Implement remote header checks for V5 file system
246 		if (read_pos(fInode->GetVolume()->Device(), readPos, buffer, lengthToRead)
247 			!= lengthToRead) {
248 			ERROR("Extent::FillBlockBuffer(): IO Error");
249 			return B_IO_ERROR;
250 		}
251 
252 		*length = lengthToRead;
253 
254 		return B_OK;
255 	}
256 }
257 
258 
259 status_t
260 NodeAttribute::GetNext(char* name, size_t* nameLength)
261 {
262 	TRACE("NodeAttribute::GetNext\n");
263 
264 	NodeHeader* node = NodeHeader::Create(fInode, fNodeBuffer);
265 	NodeEntry* firstNodeEntry = (NodeEntry*)(fNodeBuffer + NodeHeader::Size(fInode));
266 
267 	int totalNodeEntries = node->Count();
268 
269 	delete node;
270 
271 	for (int i = fLastNodeOffset; i < totalNodeEntries; i++) {
272 
273 		NodeEntry* nodeEntry = (NodeEntry*)((char*)firstNodeEntry + i * sizeof(NodeEntry));
274 		fLastNodeOffset = i;
275 
276 		// if we are at next node entry fill up leaf buffer
277 		if (fNodeFlag == 0) {
278 			// First see the leaf block from NodeEntry and logical block offset
279 			uint32 logicalBlock = B_BENDIAN_TO_HOST_INT32(nodeEntry->before);
280 			TRACE("Logical block : %d", logicalBlock);
281 			// Now calculate File system Block of This logical block
282 			xfs_fsblock_t block = _LogicalToFileSystemBlock(logicalBlock);
283 			_FillBuffer(fLeafBuffer, block);
284 			fNodeFlag = 1;
285 			fLastEntryOffset = 0;
286 		}
287 
288 		AttrLeafHeader* header  = AttrLeafHeader::Create(fInode, fLeafBuffer);
289 		AttrLeafEntry* firstLeafEntry =
290 			(AttrLeafEntry*)(fLeafBuffer + AttrLeafHeader::Size(fInode));
291 
292 		int totalEntries = header->Count();
293 
294 		delete header;
295 
296 		for (int j = fLastEntryOffset; j < totalEntries; j++) {
297 
298 			AttrLeafEntry* entry = (AttrLeafEntry*)(
299 				(char*)firstLeafEntry + j * sizeof(AttrLeafEntry));
300 
301 			uint32 offset = B_BENDIAN_TO_HOST_INT16(entry->nameidx);
302 			TRACE("offset:(%" B_PRIu16 ")\n", offset);
303 			fLastEntryOffset = j + 1;
304 
305 			// First check if its local or remote value
306 			if (entry->flags & XFS_ATTR_LOCAL) {
307 				AttrLeafNameLocal* local  = (AttrLeafNameLocal*)(fLeafBuffer + offset);
308 				memcpy(name, local->nameval, local->namelen);
309 				name[local->namelen] = '\0';
310 				*nameLength = local->namelen + 1;
311 				TRACE("Entry found name : %s, namelength : %ld", name, *nameLength);
312 				return B_OK;
313 			} else {
314 				AttrLeafNameRemote* remote  = (AttrLeafNameRemote*)(fLeafBuffer + offset);
315 				memcpy(name, remote->name, remote->namelen);
316 				name[remote->namelen] = '\0';
317 				*nameLength = remote->namelen + 1;
318 				TRACE("Entry found name : %s, namelength : %ld", name, *nameLength);
319 				return B_OK;
320 			}
321 		}
322 
323 		// we are now at next nodeEntry initialize it as 0
324 		fNodeFlag = 0;
325 	}
326 
327 	return B_ENTRY_NOT_FOUND;
328 }
329 
330 
331 status_t
332 NodeAttribute::Lookup(const char* name, size_t* nameLength)
333 {
334 	TRACE("NodeAttribute::Lookup\n");
335 	uint32 hashValueOfRequest = hashfunction(name, *nameLength);
336 	TRACE("Hashval:(%" B_PRIu32 ")\n", hashValueOfRequest);
337 
338 	// first we need to find leaf block which might contain our entry
339 	NodeHeader* node = NodeHeader::Create(fInode, fNodeBuffer);
340 	NodeEntry* nodeEntry = (NodeEntry*)(fNodeBuffer + NodeHeader::Size(fInode));
341 
342 	int TotalNodeEntries = node->Count();
343 	int left = 0;
344 	int right = TotalNodeEntries - 1;
345 
346 	delete node;
347 
348 	hashLowerBound<NodeEntry>(nodeEntry, left, right, hashValueOfRequest);
349 
350 	// We found our potential leaf block, now read leaf buffer
351 	// First see the leaf block from NodeEntry and logical block offset
352 	uint32 logicalBlock = B_BENDIAN_TO_HOST_INT32(nodeEntry[left].before);
353 	// Now calculate File system Block of This logical block
354 	xfs_fsblock_t block = _LogicalToFileSystemBlock(logicalBlock);
355 	_FillBuffer(fLeafBuffer, block);
356 
357 	AttrLeafHeader* header  = AttrLeafHeader::Create(fInode,fLeafBuffer);
358 	AttrLeafEntry* entry = (AttrLeafEntry*)(fLeafBuffer + AttrLeafHeader::Size(fInode));
359 
360 	int numberOfLeafEntries = header->Count();
361 	left = 0;
362 	right = numberOfLeafEntries - 1;
363 
364 	delete header;
365 
366 	hashLowerBound<AttrLeafEntry>(entry, left, right, hashValueOfRequest);
367 
368 	while (B_BENDIAN_TO_HOST_INT32(entry[left].hashval)
369 			== hashValueOfRequest) {
370 
371 		uint32 offset = B_BENDIAN_TO_HOST_INT16(entry[left].nameidx);
372 		TRACE("offset:(%" B_PRIu16 ")\n", offset);
373 		int status;
374 
375 		// First check if its local or remote value
376 		if (entry[left].flags & XFS_ATTR_LOCAL) {
377 			AttrLeafNameLocal* local  = (AttrLeafNameLocal*)(fLeafBuffer + offset);
378 			char* PtrToOffset = (char*)local + sizeof(uint8) + sizeof(uint16);
379 			status = strncmp(name, PtrToOffset, *nameLength);
380 			if (status == 0) {
381 				fLocalEntry = local;
382 				fRemoteEntry = NULL;
383 				return B_OK;
384 			}
385 		} else {
386 			AttrLeafNameRemote* remote  = (AttrLeafNameRemote*)(fLeafBuffer + offset);
387 			char* PtrToOffset = (char*)remote + sizeof(uint8) + 2 * sizeof(uint32);
388 			status = strncmp(name, PtrToOffset, *nameLength);
389 			if (status == 0) {
390 				fRemoteEntry = remote;
391 				fLocalEntry = NULL;
392 				return B_OK;
393 			}
394 		}
395 		left++;
396 	}
397 
398 	return B_ENTRY_NOT_FOUND;
399 }