xref: /haiku/src/libs/gnu/xattr.cpp (revision 5d80f760f53ff6d38291255625da58c1a002201b)
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 
Init__anon0ee8c14d0111::AttributeName33 	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 
Init__anon0ee8c14d0111::AttributeName64 	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 
79 private:
80 
_DecodeNameAndType__anon0ee8c14d0111::AttributeName81 	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 
111 struct Node {
Node__anon0ee8c14d0111::Node112 	Node(const char* path, bool traverseSymlinks)
113 	{
114 		fFileFD = open(path, O_RDONLY | (traverseSymlinks ? 0 : O_NOTRAVERSE));
115 		fOwnsFileFD = true;
116 	}
117 
Node__anon0ee8c14d0111::Node118 	Node(int fileFD)
119 	{
120 		fFileFD = fileFD;
121 		fOwnsFileFD = false;
122 
123 		if (fileFD < 0)
124 			errno = B_FILE_ERROR;
125 	}
126 
~Node__anon0ee8c14d0111::Node127 	~Node()
128 	{
129 		if (fFileFD >= 0 && fOwnsFileFD)
130 			close(fFileFD);
131 	}
132 
Set__anon0ee8c14d0111::Node133 	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 
Get__anon0ee8c14d0111::Node172 	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 
Remove__anon0ee8c14d0111::Node209 	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 
GetList__anon0ee8c14d0111::Node226 	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 
271 private:
272 	int		fFileFD;
273 	bool	fOwnsFileFD;
274 };
275 
276 
277 }	// namespace
278 
279 
280 // #pragma mark -
281 
282 
283 ssize_t
getxattr(const char * path,const char * attribute,void * buffer,size_t size)284 getxattr(const char* path, const char* attribute, void* buffer, size_t size)
285 {
286 	return Node(path, true).Get(attribute, buffer, size);
287 }
288 
289 
290 ssize_t
lgetxattr(const char * path,const char * attribute,void * buffer,size_t size)291 lgetxattr(const char* path, const char* attribute, void* buffer, size_t size)
292 {
293 	return Node(path, false).Get(attribute, buffer, size);
294 }
295 
296 
297 ssize_t
fgetxattr(int fd,const char * attribute,void * buffer,size_t size)298 fgetxattr(int fd, const char* attribute, void* buffer, size_t size)
299 {
300 	return Node(fd).Get(attribute, buffer, size);
301 }
302 
303 
304 int
setxattr(const char * path,const char * attribute,const void * buffer,size_t size,int flags)305 setxattr(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 
312 int
lsetxattr(const char * path,const char * attribute,const void * buffer,size_t size,int flags)313 lsetxattr(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 
320 int
fsetxattr(int fd,const char * attribute,const void * buffer,size_t size,int flags)321 fsetxattr(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 
328 int
removexattr(const char * path,const char * attribute)329 removexattr (const char* path, const char* attribute)
330 {
331 	return Node(path, true).Remove(attribute);
332 }
333 
334 
335 int
lremovexattr(const char * path,const char * attribute)336 lremovexattr (const char* path, const char* attribute)
337 {
338 	return Node(path, false).Remove(attribute);
339 }
340 
341 
342 int
fremovexattr(int fd,const char * attribute)343 fremovexattr (int fd, const char* attribute)
344 {
345 	return Node(fd).Remove(attribute);
346 }
347 
348 
349 ssize_t
listxattr(const char * path,char * buffer,size_t size)350 listxattr(const char* path, char* buffer, size_t size)
351 {
352 	return Node(path, true).GetList(buffer, size);
353 }
354 
355 
356 ssize_t
llistxattr(const char * path,char * buffer,size_t size)357 llistxattr(const char* path, char* buffer, size_t size)
358 {
359 	return Node(path, false).GetList(buffer, size);
360 }
361 
362 
363 ssize_t
flistxattr(int fd,char * buffer,size_t size)364 flistxattr(int fd, char* buffer, size_t size)
365 {
366 	return Node(fd).GetList(buffer, size);
367 }
368