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