1/*
2 * Copyright 2022, Raghav Sharma, raghavself28@gmail.com
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "Symlink.h"
8
9#include "VerifyHeader.h"
10
11
12Symlink::Symlink(Inode* inode)
13	:
14	fInode(inode),
15	fSymlinkBuffer(NULL)
16{
17}
18
19
20Symlink::~Symlink()
21{
22	delete fSymlinkBuffer;
23}
24
25
26status_t
27Symlink::_FillMapEntry()
28{
29	void* pointerToMap = DIR_DFORK_PTR(fInode->Buffer(), fInode->CoreInodeSize());
30
31	uint64 firstHalf = *((uint64*)pointerToMap);
32	uint64 secondHalf = *((uint64*)pointerToMap + 1);
33		//dividing the 128 bits into 2 parts.
34	firstHalf = B_BENDIAN_TO_HOST_INT64(firstHalf);
35	secondHalf = B_BENDIAN_TO_HOST_INT64(secondHalf);
36	fMap.br_state = firstHalf >> 63;
37	fMap.br_startoff = (firstHalf & MASK(63)) >> 9;
38	fMap.br_startblock = ((firstHalf & MASK(9)) << 43) | (secondHalf >> 21);
39	fMap.br_blockcount = secondHalf & MASK(21);
40	TRACE("Extent::Init: startoff:(%" B_PRIu64 "), startblock:(%" B_PRIu64 "),"
41		"blockcount:(%" B_PRIu64 "),state:(%" B_PRIu8 ")\n", fMap.br_startoff, fMap.br_startblock,
42		fMap.br_blockcount, fMap.br_state);
43
44	return B_OK;
45}
46
47
48status_t
49Symlink::_FillBuffer()
50{
51	if (fMap.br_state != 0)
52		return B_BAD_VALUE;
53
54	int len = fInode->DirBlockSize();
55	fSymlinkBuffer = new(std::nothrow) char[len];
56	if (fSymlinkBuffer == NULL)
57		return B_NO_MEMORY;
58
59	xfs_daddr_t readPos =
60		fInode->FileSystemBlockToAddr(fMap.br_startblock);
61
62	if (read_pos(fInode->GetVolume()->Device(), readPos, fSymlinkBuffer, len)
63		!= len) {
64		ERROR("Extent::FillBlockBuffer(): IO Error");
65		return B_IO_ERROR;
66	}
67
68	return B_OK;
69}
70
71
72status_t
73Symlink::_ReadLocalLink(off_t pos, char* buffer, size_t* _length)
74{
75	// All symlinks contents are inside inode itself
76
77	size_t lengthToRead = fInode->Size();
78
79	if (*_length < lengthToRead)
80		lengthToRead = *_length;
81
82	char* offset = (char*)(DIR_DFORK_PTR(fInode->Buffer(), fInode->CoreInodeSize()));
83
84	memcpy(buffer, offset, lengthToRead);
85
86	*_length = lengthToRead;
87
88	return B_OK;
89
90}
91
92
93status_t
94Symlink::_ReadExtentLink(off_t pos, char* buffer, size_t* _length)
95{
96	status_t status;
97	// First fill up extent, then Symlink block buffer
98	status = _FillMapEntry();
99	if (status != B_OK)
100		return status;
101
102	status = _FillBuffer();
103	if (status != B_OK)
104		return status;
105
106	uint32 offset = 0;
107	// If it is Version 5 xfs then we have Symlink header
108	if (fInode->Version() == 3) {
109		SymlinkHeader* header = (SymlinkHeader*)fSymlinkBuffer;
110		if (!VerifyHeader<SymlinkHeader>(header, fSymlinkBuffer, fInode, 0, &fMap, 0)) {
111			ERROR("Invalid data header");
112			return B_BAD_VALUE;
113		}
114		offset += sizeof(SymlinkHeader);
115	}
116
117	size_t lengthToRead = fInode->Size();
118
119	if (*_length < lengthToRead)
120		lengthToRead = *_length;
121
122	memcpy(buffer, fSymlinkBuffer + offset, lengthToRead);
123
124	*_length = lengthToRead;
125
126	return B_OK;
127}
128
129
130status_t
131Symlink::ReadLink(off_t pos, char* buffer, size_t* _length)
132{
133	switch (fInode->Format()) {
134		case XFS_DINODE_FMT_LOCAL:
135			return _ReadLocalLink(pos, buffer, _length);
136		case XFS_DINODE_FMT_EXTENTS:
137			return _ReadExtentLink(pos, buffer, _length);
138		default:
139			return B_BAD_VALUE;
140	}
141}