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
12NodeAttribute::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
25NodeAttribute::~NodeAttribute()
26{
27	delete fMap;
28	delete[] fLeafBuffer;
29	delete[] fNodeBuffer;
30}
31
32
33status_t
34NodeAttribute::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
76status_t
77NodeAttribute::_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
98xfs_fsblock_t
99NodeAttribute::_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
116status_t
117NodeAttribute::_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
133status_t
134NodeAttribute::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
159status_t
160NodeAttribute::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
191status_t
192NodeAttribute::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
259status_t
260NodeAttribute::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
331status_t
332NodeAttribute::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}