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