xref: /haiku/src/add-ons/kernel/file_systems/xfs/Symlink.cpp (revision c237c4ce593ee823d9867fd997e51e4c447f5623)
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 
12 Symlink::Symlink(Inode* inode)
13 	:
14 	fInode(inode),
15 	fSymlinkBuffer(NULL)
16 {
17 }
18 
19 
20 Symlink::~Symlink()
21 {
22 	delete fSymlinkBuffer;
23 }
24 
25 
26 status_t
27 Symlink::_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 
48 status_t
49 Symlink::_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 
72 status_t
73 Symlink::_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 
93 status_t
94 Symlink::_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 
130 status_t
131 Symlink::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 }