xref: /haiku/src/libs/gnu/xattr.cpp (revision 9760dcae2038d47442f4658c2575844c6cf92c40)
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 
22 static const char* const kXattrNamespace = "user.haiku.";
23 static const size_t kXattrNamespaceLength = 11;
24 
25 
26 namespace {
27 
28 
29 struct 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 
77 private:
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 
109 struct 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 
269 private:
270 	int		fFileFD;
271 	bool	fOwnsFileFD;
272 };
273 
274 
275 }	// namespace
276 
277 
278 // #pragma mark -
279 
280 
281 ssize_t
282 getxattr(const char* path, const char* attribute, void* buffer, size_t size)
283 {
284 	return Node(path, true).Get(attribute, buffer, size);
285 }
286 
287 
288 ssize_t
289 lgetxattr(const char* path, const char* attribute, void* buffer, size_t size)
290 {
291 	return Node(path, false).Get(attribute, buffer, size);
292 }
293 
294 
295 ssize_t
296 fgetxattr(int fd, const char* attribute, void* buffer, size_t size)
297 {
298 	return Node(fd).Get(attribute, buffer, size);
299 }
300 
301 
302 int
303 setxattr(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 
310 int
311 lsetxattr(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 
318 int
319 fsetxattr(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 
326 int
327 removexattr (const char* path, const char* attribute)
328 {
329 	return Node(path, true).Remove(attribute);
330 }
331 
332 
333 int
334 lremovexattr (const char* path, const char* attribute)
335 {
336 	return Node(path, false).Remove(attribute);
337 }
338 
339 
340 int
341 fremovexattr (int fd, const char* attribute)
342 {
343 	return Node(fd).Remove(attribute);
344 }
345 
346 
347 ssize_t
348 listxattr(const char* path, char* buffer, size_t size)
349 {
350 	return Node(path, true).GetList(buffer, size);
351 }
352 
353 
354 ssize_t
355 llistxattr(const char* path, char* buffer, size_t size)
356 {
357 	return Node(path, false).GetList(buffer, size);
358 }
359 
360 
361 ssize_t
362 flistxattr(int fd, char* buffer, size_t size)
363 {
364 	return Node(fd).GetList(buffer, size);
365 }
366