1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include <sys/xattr.h>
7
8#include <ctype.h>
9#include <errno.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include <algorithm>
15
16#include <fs_attr.h>
17#include <TypeConstants.h>
18
19#include <syscall_utils.h>
20
21
22static const char* const kXattrNamespace = "user.haiku.";
23static const size_t kXattrNamespaceLength = 11;
24
25
26namespace {
27
28
29struct AttributeName {
30	char	name[B_ATTR_NAME_LENGTH + 32];
31	uint32	type;
32
33	void Init(const char* haikuName, uint32 type)
34	{
35		if (type == B_XATTR_TYPE) {
36			// a simple xattr -- copy the name verbatim
37			strlcpy(name, haikuName, sizeof(name));
38			this->type = type;
39		} else {
40			// a Haiku attribute -- map the name
41
42			// create a type string -- if the four bytes of the type are
43			// printable, just use them as the type string, otherwise convert
44			// to hex
45			char typeString[9];
46			uint8 typeBytes[4] = { (uint8)((type >> 24) & 0xff),
47				(uint8)((type >> 16) & 0xff), (uint8)((type >> 8) & 0xff),
48				(uint8)(type & 0xff) };
49			if (isprint(typeBytes[0]) && isprint(typeBytes[1])
50				&& isprint(typeBytes[2]) && isprint(typeBytes[3])) {
51				typeString[0] = typeBytes[0];
52				typeString[1] = typeBytes[1];
53				typeString[2] = typeBytes[2];
54				typeString[3] = typeBytes[3];
55				typeString[4] = '\0';
56			} else
57				sprintf(typeString, "%08" B_PRIx32 , type);
58
59			snprintf(name, sizeof(name), "%s%s#%s", kXattrNamespace,
60				haikuName, typeString);
61		}
62	}
63
64	void Init(const char* xattrName)
65	{
66		if (strncmp(xattrName, kXattrNamespace, kXattrNamespaceLength) == 0) {
67			// a Haiku attribute -- extract the actual name and type
68			xattrName += kXattrNamespaceLength;
69
70			if (_DecodeNameAndType(xattrName))
71				return;
72		}
73
74		// a simple xattr
75		strlcpy(name, xattrName, sizeof(name));
76		this->type = B_XATTR_TYPE;
77	}
78
79private:
80
81	bool _DecodeNameAndType(const char* xattrName)
82	{
83		const char* typeString = strrchr(xattrName, '#');
84		if (typeString == NULL || typeString == xattrName)
85			return false;
86		typeString++;
87
88		size_t typeStringLength = strlen(typeString);
89		if (typeStringLength == 4) {
90			// the type string consists of the literal type bytes
91			type = ((uint32)typeString[0] << 24) | ((uint32)typeString[1] << 16)
92				| ((uint32)typeString[2] << 8) | (uint32)typeString[3];
93		} else if (typeStringLength == 8) {
94			// must be hex-encoded
95			char* numberEnd;
96			type = strtoul(typeString, &numberEnd, 16);
97			if (numberEnd != typeString + 8)
98				return false;
99		} else
100			return false;
101
102		strlcpy(name, xattrName,
103			std::min(sizeof(name), (size_t)(typeString - xattrName)));
104			// typeString - xattrName - 1 is the name length, but we need to
105			// specify one more for the terminating null
106		return true;
107	}
108};
109
110
111struct Node {
112	Node(const char* path, bool traverseSymlinks)
113	{
114		fFileFD = open(path, O_RDONLY | (traverseSymlinks ? 0 : O_NOTRAVERSE));
115		fOwnsFileFD = true;
116	}
117
118	Node(int fileFD)
119	{
120		fFileFD = fileFD;
121		fOwnsFileFD = false;
122
123		if (fileFD < 0)
124			errno = B_FILE_ERROR;
125	}
126
127	~Node()
128	{
129		if (fFileFD >= 0 && fOwnsFileFD)
130			close(fFileFD);
131	}
132
133	int Set(const char* attribute, int flags, const void* buffer, size_t size)
134	{
135		if (fFileFD < 0)
136			return -1;
137
138		// flags to open mode
139		int openMode = O_WRONLY | O_TRUNC;
140		if (flags == XATTR_CREATE) {
141			openMode |= O_CREAT | O_EXCL;
142		} else if (flags == XATTR_REPLACE) {
143			// pure open -- attribute must exist
144		} else
145			openMode |= O_CREAT;
146
147		AttributeName attributeName;
148		attributeName.Init(attribute);
149
150		int attributeFD = fs_fopen_attr(fFileFD, attributeName.name,
151			attributeName.type, openMode);
152		if (attributeFD < 0) {
153			// translate B_ENTRY_NOT_FOUND to ENOATTR
154			if (errno == B_ENTRY_NOT_FOUND)
155				errno = ENOATTR;
156
157			return -1;
158		}
159
160		ssize_t written = write(attributeFD, buffer, size);
161
162		fs_close_attr(attributeFD);
163
164		if (written < 0)
165			return -1;
166		if ((size_t)written != size)
167			RETURN_AND_SET_ERRNO(B_FILE_ERROR);
168
169		return 0;
170	}
171
172	ssize_t Get(const char* attribute, void* buffer, size_t size)
173	{
174		if (fFileFD < 0)
175			return -1;
176
177		AttributeName attributeName;
178		attributeName.Init(attribute);
179
180		// get the attribute size -- we read all or nothing
181		attr_info info;
182		if (fs_stat_attr(fFileFD, attributeName.name, &info) != 0) {
183			// translate B_ENTRY_NOT_FOUND to ENOATTR
184			if (errno == B_ENTRY_NOT_FOUND)
185				errno = ENOATTR;
186			return -1;
187		}
188
189		// if an empty buffer is given, return the attribute size
190		if (size == 0)
191			return info.size;
192
193		// if the buffer is too small, fail
194		if (size < info.size) {
195			errno = ERANGE;
196			return -1;
197		}
198
199		ssize_t bytesRead = fs_read_attr(fFileFD, attributeName.name,
200			info.type, 0, buffer, info.size);
201
202		// translate B_ENTRY_NOT_FOUND to ENOATTR
203		if (bytesRead < 0 && errno == B_ENTRY_NOT_FOUND)
204			errno = ENOATTR;
205
206		return bytesRead;
207	}
208
209	int Remove(const char* attribute)
210	{
211		if (fFileFD < 0)
212			return -1;
213
214		AttributeName attributeName;
215		attributeName.Init(attribute);
216
217		int result = fs_remove_attr(fFileFD, attributeName.name);
218
219		// translate B_ENTRY_NOT_FOUND to ENOATTR
220		if (result != 0 && errno == B_ENTRY_NOT_FOUND)
221			errno = ENOATTR;
222
223		return result;
224	}
225
226	ssize_t GetList(void* _buffer, size_t size)
227	{
228		char* buffer = (char*)_buffer;
229
230		if (fFileFD < 0)
231			return -1;
232
233		// open attribute directory
234		DIR* dir = fs_fopen_attr_dir(fFileFD);
235		if (dir == NULL)
236			return -1;
237
238		// read the attributes
239		size_t remainingSize = size;
240		size_t totalSize = 0;
241		while (struct dirent* entry = readdir(dir)) {
242			attr_info info;
243			if (fs_stat_attr(fFileFD, entry->d_name, &info) != 0)
244				continue;
245
246			AttributeName attributeName;
247			attributeName.Init(entry->d_name, info.type);
248
249			size_t nameLength = strlen(attributeName.name);
250			totalSize += nameLength + 1;
251
252			if (remainingSize > nameLength) {
253				strcpy((char*)buffer, attributeName.name);
254				buffer += nameLength + 1;
255				remainingSize -= nameLength + 1;
256			} else
257				remainingSize = 0;
258		}
259
260		closedir(dir);
261
262		// If the buffer was too small, fail.
263		if (size != 0 && totalSize > size) {
264			errno = ERANGE;
265			return -1;
266		}
267
268		return totalSize;
269	}
270
271private:
272	int		fFileFD;
273	bool	fOwnsFileFD;
274};
275
276
277}	// namespace
278
279
280// #pragma mark -
281
282
283ssize_t
284getxattr(const char* path, const char* attribute, void* buffer, size_t size)
285{
286	return Node(path, true).Get(attribute, buffer, size);
287}
288
289
290ssize_t
291lgetxattr(const char* path, const char* attribute, void* buffer, size_t size)
292{
293	return Node(path, false).Get(attribute, buffer, size);
294}
295
296
297ssize_t
298fgetxattr(int fd, const char* attribute, void* buffer, size_t size)
299{
300	return Node(fd).Get(attribute, buffer, size);
301}
302
303
304int
305setxattr(const char* path, const char* attribute, const void* buffer,
306	size_t size, int flags)
307{
308	return Node(path, true).Set(attribute, flags, buffer, size);
309}
310
311
312int
313lsetxattr(const char* path, const char* attribute, const void* buffer,
314	size_t size, int flags)
315{
316	return Node(path, false).Set(attribute, flags, buffer, size);
317}
318
319
320int
321fsetxattr(int fd, const char* attribute, const void* buffer, size_t size,
322	int flags)
323{
324	return Node(fd).Set(attribute, flags, buffer, size);
325}
326
327
328int
329removexattr (const char* path, const char* attribute)
330{
331	return Node(path, true).Remove(attribute);
332}
333
334
335int
336lremovexattr (const char* path, const char* attribute)
337{
338	return Node(path, false).Remove(attribute);
339}
340
341
342int
343fremovexattr (int fd, const char* attribute)
344{
345	return Node(fd).Remove(attribute);
346}
347
348
349ssize_t
350listxattr(const char* path, char* buffer, size_t size)
351{
352	return Node(path, true).GetList(buffer, size);
353}
354
355
356ssize_t
357llistxattr(const char* path, char* buffer, size_t size)
358{
359	return Node(path, false).GetList(buffer, size);
360}
361
362
363ssize_t
364flistxattr(int fd, char* buffer, size_t size)
365{
366	return Node(fd).GetList(buffer, size);
367}
368