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