1/*
2 * Copyright 2005-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6/*!	Emulation BeOS-style attributes by mapping them to untyped attributes of
7	the host platform (xattr on Linux, extattr on FreeBSD).
8*/
9
10
11#ifdef BUILDING_FS_SHELL
12#	include "compat.h"
13#	define B_OK					0
14#	define B_BAD_VALUE			EINVAL
15#	define B_FILE_ERROR			EBADF
16#	define B_ERROR				EINVAL
17#	define B_ENTRY_NOT_FOUND	ENOENT
18#	define B_NO_MEMORY			ENOMEM
19#else
20#	include <BeOSBuildCompatibility.h>
21#	include <syscalls.h>
22
23#	include "fs_impl.h"
24#	include "fs_descriptors.h"
25#endif
26
27#include <dirent.h>
28#include <errno.h>
29#include <fcntl.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <unistd.h>
33#include <sys/stat.h>
34
35#include <map>
36#include <string>
37
38#include <fs_attr.h>
39
40
41// Include the interface to the host platform attributes support.
42#if defined(HAIKU_HOST_PLATFORM_LINUX)
43#	include "fs_attr_xattr.h"
44#elif defined(HAIKU_HOST_PLATFORM_FREEBSD)
45#	include "fs_attr_extattr.h"
46#elif defined(HAIKU_HOST_PLATFORM_DARWIN)
47#	include "fs_attr_bsdxattr.h"
48#else
49#	error No attribute support for this host platform!
50#endif
51
52
53namespace BPrivate {}
54using namespace BPrivate;
55using std::map;
56using std::string;
57
58// the maximum length of an attribute listing we support
59static const size_t kMaxAttributeListingLength = 10240;
60
61// the maximum attribute length we support
62static const size_t kMaxAttributeLength = 10240 * 4;
63
64
65// mangle_attribute_name
66static string
67mangle_attribute_name(const char* name)
68{
69	// prepend our xattr namespace and translate:
70	// '/' -> "%\"
71	// '%' -> "%%"
72
73	string mangledName = kAttributeNamespace;
74	for (int i = 0; name[i] != '\0'; i++) {
75		char c = name[i];
76		switch (c) {
77			case '/':
78				mangledName += "%\\";
79				break;
80			case '%':
81				mangledName += "%%";
82				break;
83			default:
84				mangledName += c;
85				break;
86		}
87	}
88	return mangledName;
89}
90
91
92// demangle_attribute_name
93static bool
94demangle_attribute_name(const char* name, string& demangledName)
95{
96	// chop of our xattr namespace and translate:
97	// "%\" -> '/'
98	// "%%" -> '%'
99
100	if (strncmp(name, kAttributeNamespace, kAttributeNamespaceLen) != 0)
101		return false;
102
103	name += kAttributeNamespaceLen;
104
105	demangledName = "";
106
107	for (int i = 0; name[i] != '\0'; i++) {
108		char c = name[i];
109		if (c == '%') {
110			c = name[++i];
111			if (c == '%')
112				demangledName += c;
113			else if (c == '\\')
114				demangledName += '/';
115			else
116				return false;
117		} else
118			demangledName += c;
119	}
120
121	return true;
122}
123
124
125namespace {
126
127class AttributeDirectory;
128
129typedef map<DIR*, AttributeDirectory*> AttrDirMap;
130static AttrDirMap sAttributeDirectories;
131
132// LongDirEntry
133struct LongDirEntry {
134	char _[sizeof(struct dirent) + B_FILE_NAME_LENGTH + 1];
135	struct dirent* dirent() { return (struct dirent*)_; }
136};
137
138// AttributeHeader
139struct AttributeHeader {
140	uint32	type;
141};
142
143// AttributeDirectory
144class AttributeDirectory {
145public:
146	AttributeDirectory()
147		: fFileFD(-1),
148		  fFakeDir(NULL),
149		  fListing(NULL),
150		  fListingLength(-1),
151		  fListingIndex(0)
152	{
153	}
154
155	~AttributeDirectory()
156	{
157		if (fFileFD >= 0)
158			close(fFileFD);
159
160		if (fFakeDir) {
161			AttrDirMap::iterator it = sAttributeDirectories.find(fFakeDir);
162			if (it != sAttributeDirectories.end())
163				sAttributeDirectories.erase(it);
164
165			closedir(fFakeDir);
166		}
167
168		free(fListing);
169	}
170
171	static AttributeDirectory* Get(DIR* dir)
172	{
173		AttrDirMap::iterator it = sAttributeDirectories.find(dir);
174		if (it == sAttributeDirectories.end())
175			return NULL;
176		return it->second;
177	}
178
179	status_t Init(const char* path, int fileFD)
180	{
181		// open a fake DIR
182		if (!fFakeDir) {
183			fFakeDir = opendir(".");
184			if (!fFakeDir)
185				return B_ERROR;
186
187			sAttributeDirectories[fFakeDir] = this;
188		}
189
190		string tempPath;
191		if (!path) {
192			// We've got no path. If the file descriptor is one of our own and
193			// not a system FD, we need to get a path for it.
194			Descriptor*	descriptor = get_descriptor(fileFD);
195			if (descriptor && !descriptor->IsSystemFD()) {
196				status_t error = descriptor->GetPath(tempPath);
197				if (error != B_OK)
198					return error;
199				path = tempPath.c_str();
200				fileFD = -1;
201			}
202		}
203
204		if (path) {
205			// A path was given -- check, if it's a symlink. If so we need to
206			// keep the path, otherwise we open a FD.
207			struct stat st;
208			if (lstat(path, &st))
209				return B_ENTRY_NOT_FOUND;
210
211			if (S_ISLNK(st.st_mode)) {
212				fFileFD = -1;
213				fPath = path;
214			} else {
215				fFileFD = open(path, O_RDONLY);
216				if (fFileFD < 0)
217					return errno;
218				fPath = "";
219			}
220		} else {
221			// FD was given -- dup it.
222			fFileFD = dup(fileFD);
223			if (fFileFD < 0)
224				return errno;
225			fPath = "";
226		}
227
228		fListingLength = -1;
229		fListingIndex = 0;
230
231		return B_OK;
232	}
233
234	DIR* FakeDir() const	{ return fFakeDir; }
235
236	status_t ReadDir(struct dirent** _entry)
237	{
238		// make sure we have a current listing
239		status_t error = _CheckListing();
240		if (error != B_OK)
241			return error;
242
243		// keep going until we find an entry or hit the end of dir
244		while (fListingIndex < fListingLength) {
245			// get next entry name
246			const char* name = fListing + fListingIndex;
247			int nameLen = strlen(name);
248			fListingIndex += nameLen + 1;
249
250			// check the attribute namespace
251			string demangledName;
252			if (!demangle_attribute_name(name, demangledName))
253				continue;
254			name = demangledName.c_str();
255			nameLen = demangledName.length();
256
257			if (nameLen == 0) {
258				// Uh, weird attribute.
259				return B_ERROR;
260			}
261
262			// prepare the dirent
263			strcpy(fDirent.dirent()->d_name, name);
264			fDirent.dirent()->d_ino = 0;
265// TODO: We need the node ID!
266
267			*_entry = fDirent.dirent();
268			return B_OK;
269		}
270
271		// end of dir
272		*_entry = NULL;
273		return B_OK;
274	}
275
276	void RewindDir()
277	{
278		fListingIndex = 0;
279	}
280
281private:
282	status_t _CheckListing()
283	{
284		if (fListing && fListingLength >= 0)
285			return B_OK;
286
287		char listing[kMaxAttributeListingLength];
288
289		// get the listing
290		ssize_t length = list_attributes(fFileFD, fPath.c_str(), listing,
291			kMaxAttributeListingLength);
292		if (length < 0)
293			return errno;
294
295		// clone the on-stack listing
296		char* newListing = (char*)realloc(fListing, length);
297		if (!newListing)
298			return B_NO_MEMORY;
299		memcpy(newListing, listing, length);
300
301		fListing = newListing;
302		fListingLength = length;
303		fListingIndex = 0;
304
305		return B_OK;
306	}
307
308private:
309	int			fFileFD;
310	string		fPath;
311	DIR*		fFakeDir;
312	LongDirEntry fDirent;
313	char*		fListing;
314	int			fListingLength;
315	int			fListingIndex;
316};
317
318// LocalFD
319#include "LocalFD.h"
320
321}	// unnamed namspace
322
323
324// # pragma mark - Public API
325
326
327// fs_open_attr_dir
328DIR *
329fs_open_attr_dir(const char *path)
330{
331	AttributeDirectory* attrDir = new AttributeDirectory;
332
333	status_t error = attrDir->Init(path, -1);
334	if (error != B_OK) {
335		errno = error;
336		delete attrDir;
337		return NULL;
338	}
339
340	return attrDir->FakeDir();
341}
342
343// fs_fopen_attr_dir
344DIR *
345fs_fopen_attr_dir(int fd)
346{
347	AttributeDirectory* attrDir = new AttributeDirectory;
348
349	status_t error = attrDir->Init(NULL, fd);
350	if (error != B_OK) {
351		errno = error;
352		delete attrDir;
353		return NULL;
354	}
355
356	return attrDir->FakeDir();
357}
358
359// fs_close_attr_dir
360int
361fs_close_attr_dir(DIR *dir)
362{
363	AttributeDirectory* attrDir = AttributeDirectory::Get(dir);
364	if (!attrDir) {
365		errno = B_BAD_VALUE;
366		return -1;
367	}
368
369	delete attrDir;
370	return 0;
371}
372
373// fs_read_attr_dir
374struct dirent *
375fs_read_attr_dir(DIR *dir)
376{
377	// get attr dir
378	AttributeDirectory* attrDir = AttributeDirectory::Get(dir);
379	if (!attrDir) {
380		errno = B_BAD_VALUE;
381		return NULL;
382	}
383
384	// read attr dir
385	dirent* entry = NULL;
386	status_t error = attrDir->ReadDir(&entry);
387	if (error != B_OK) {
388		errno = error;
389		return NULL;
390	}
391
392	return entry;
393}
394
395// fs_rewind_attr_dir
396void
397fs_rewind_attr_dir(DIR *dir)
398{
399	// get attr dir
400	AttributeDirectory* attrDir = AttributeDirectory::Get(dir);
401	if (attrDir)
402		attrDir->RewindDir();
403}
404
405// fs_fopen_attr
406int
407fs_fopen_attr(int fd, const char *attribute, uint32 type, int openMode)
408{
409	if (fd < 0) {
410		errno = B_BAD_VALUE;
411		return -1;
412	}
413
414	AttributeDescriptor* descriptor = new(std::nothrow) AttributeDescriptor(fd,
415		attribute, type, openMode);
416	if (descriptor == NULL) {
417		errno = B_NO_MEMORY;
418		return -1;
419	}
420
421	status_t error = descriptor->Init();
422	if (error != B_OK) {
423		delete descriptor;
424		errno = error;
425		return -1;
426	}
427
428	int attributeFD = add_descriptor(descriptor);
429	if (attributeFD < 0) {
430		delete descriptor;
431		errno = B_NO_MEMORY;
432		return -1;
433	}
434
435	return attributeFD;
436}
437
438// fs_close_attr
439int
440fs_close_attr(int fd)
441{
442	status_t error = delete_descriptor(fd);
443	if (error != 0) {
444		errno = error;
445		return -1;
446	}
447
448	return 0;
449}
450
451// fs_read_attr
452ssize_t
453fs_read_attr(int fd, const char *_attribute, uint32 type, off_t pos,
454	void *buffer, size_t readBytes)
455{
456	// check params
457	if (pos < 0 || pos + readBytes > kMaxAttributeLength
458		|| !_attribute || !buffer) {
459		errno = B_BAD_VALUE;
460		return -1;
461	}
462
463	// resolve FD
464	LocalFD localFD;
465	status_t error = localFD.Init(fd);
466	if (error != B_OK) {
467		errno = error;
468		return -1;
469	}
470
471	// mangle the attribute name
472	string attribute = mangle_attribute_name(_attribute);
473
474	// read the attribute
475	char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength];
476	ssize_t bytesRead = sizeof(attributeBuffer);
477	if (localFD.Path()) {
478		bytesRead = get_attribute(-1, localFD.Path(), attribute.c_str(),
479			attributeBuffer, bytesRead);
480	} else {
481		bytesRead = get_attribute(localFD.FD(), NULL, attribute.c_str(),
482			attributeBuffer, bytesRead);
483	}
484	if (bytesRead < 0) {
485		// Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute
486		// doesn't exist.
487		if (errno == ENOATTR || errno == ENODATA)
488			errno = B_ENTRY_NOT_FOUND;
489		return -1;
490	}
491
492	// check length for sanity
493	if ((size_t)bytesRead < sizeof(AttributeHeader)) {
494		fprintf(stderr, "fs_read_attr(): attribute \"%s\" is shorter than the "
495			"AttributeHeader!\n", attribute.c_str());
496		errno = B_ERROR;
497		return -1;
498	}
499
500	// copy the result into the provided buffer
501	bytesRead -= sizeof(AttributeHeader) + pos;
502	if (bytesRead < 0) {
503		// that means pos > <attribute size>
504		errno = B_BAD_VALUE;
505		return -1;
506	}
507
508	if (bytesRead > 0) {
509		if ((size_t)bytesRead > readBytes)
510			bytesRead = readBytes;
511		memcpy(buffer, attributeBuffer + sizeof(AttributeHeader) + pos,
512			bytesRead);
513	}
514
515	return bytesRead;
516}
517
518// fs_write_attr
519ssize_t
520fs_write_attr(int fd, const char *_attribute, uint32 type, off_t pos,
521	const void *buffer, size_t writeBytes)
522{
523	// check params
524	if (pos < 0 || pos + writeBytes > kMaxAttributeLength
525		|| _attribute == NULL || (writeBytes > 0 && buffer == NULL)) {
526		errno = B_BAD_VALUE;
527		return -1;
528	}
529
530	// resolve FD
531	LocalFD localFD;
532	status_t error = localFD.Init(fd);
533	if (error != B_OK) {
534		errno = error;
535		return -1;
536	}
537
538	// mangle the attribute name
539	string attribute = mangle_attribute_name(_attribute);
540
541	// prepare an attribute buffer
542	char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength];
543	AttributeHeader* header = (AttributeHeader*)attributeBuffer;
544	header->type = type;
545	memset(attributeBuffer + sizeof(AttributeHeader), 0, pos);
546	if (writeBytes > 0) {
547		memcpy(attributeBuffer + sizeof(AttributeHeader) + pos, buffer,
548			writeBytes);
549	}
550
551	// write the attribute
552	ssize_t toWrite = sizeof(AttributeHeader) + pos + writeBytes;
553	int result;
554	if (localFD.Path()) {
555		result = set_attribute(-1, localFD.Path(), attribute.c_str(),
556			attributeBuffer, toWrite);
557	} else {
558		result = set_attribute(localFD.FD(), NULL, attribute.c_str(),
559			attributeBuffer, toWrite);
560	}
561	if (result < 0) {
562		// Setting user attributes on symlinks is not allowed (xattr). So, if
563		// this is a symlink and we're only supposed to write a "BEOS:TYPE"
564		// attribute we silently pretend to have succeeded.
565		if (localFD.IsSymlink() && strcmp(_attribute, "BEOS:TYPE") == 0)
566			return writeBytes;
567		return -1;
568	}
569
570	return writeBytes;
571}
572
573// fs_remove_attr
574int
575fs_remove_attr(int fd, const char *_attribute)
576{
577	// check params
578	if (!_attribute) {
579		errno = B_BAD_VALUE;
580		return -1;
581	}
582
583	// resolve FD
584	LocalFD localFD;
585	status_t error = localFD.Init(fd);
586	if (error != B_OK) {
587		errno = error;
588		return -1;
589	}
590
591	// mangle the attribute name
592	string attribute = mangle_attribute_name(_attribute);
593
594	// remove attribute
595	int result;
596	if (localFD.Path())
597		result = remove_attribute(-1, localFD.Path(), attribute.c_str());
598	else
599		result = remove_attribute(localFD.FD(), NULL, attribute.c_str());
600
601	if (result < 0) {
602		// Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute
603		// doesn't exist.
604		if (errno == ENOATTR || errno == ENODATA)
605			errno = B_ENTRY_NOT_FOUND;
606		return -1;
607	}
608	return 0;
609}
610
611// fs_stat_attr
612int
613fs_stat_attr(int fd, const char *_attribute, struct attr_info *attrInfo)
614{
615	if (!_attribute || !attrInfo) {
616		errno = B_BAD_VALUE;
617		return -1;
618	}
619
620	// resolve FD
621	LocalFD localFD;
622	status_t error = localFD.Init(fd);
623	if (error != B_OK) {
624		errno = error;
625		return -1;
626	}
627
628	// mangle the attribute name
629	string attribute = mangle_attribute_name(_attribute);
630
631	// read the attribute
632	char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength];
633	ssize_t bytesRead = sizeof(AttributeHeader) + kMaxAttributeLength;
634	if (localFD.Path()) {
635		bytesRead = get_attribute(-1, localFD.Path(), attribute.c_str(),
636			attributeBuffer, bytesRead);
637	} else {
638		bytesRead = get_attribute(localFD.FD(), NULL, attribute.c_str(),
639			attributeBuffer, bytesRead);
640	}
641	if (bytesRead < 0) {
642		// Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute
643		// doesn't exist.
644		if (errno == ENOATTR || errno == ENODATA)
645			errno = B_ENTRY_NOT_FOUND;
646		return -1;
647	}
648
649	// check length for sanity
650	if ((size_t)bytesRead < sizeof(AttributeHeader)) {
651		fprintf(stderr, "fs_stat_attr(): attribute \"%s\" is shorter than the "
652			"AttributeHeader!\n", attribute.c_str());
653		errno = B_ERROR;
654		return -1;
655	}
656
657	attrInfo->size = bytesRead - sizeof(AttributeHeader);
658	attrInfo->type = ((AttributeHeader*)attributeBuffer)->type;
659
660	return 0;
661}
662
663
664// #pragma mark - Private Syscalls
665
666
667#ifndef BUILDING_FS_SHELL
668
669// _kern_open_attr_dir
670int
671_kern_open_attr_dir(int fd, const char *path)
672{
673	// get node ref for the node
674	struct stat st;
675	status_t error = _kern_read_stat(fd, path, false, &st,
676		sizeof(struct stat));
677	if (error != B_OK) {
678		errno = error;
679		return -1;
680	}
681	NodeRef ref(st);
682
683	DIR* dir;
684	if (path) {
685		// If a path was given, get a usable path.
686		string realPath;
687		status_t error = get_path(fd, path, realPath);
688		if (error != B_OK)
689			return error;
690
691		dir = fs_open_attr_dir(realPath.c_str());
692	} else
693		dir = fs_fopen_attr_dir(fd);
694
695	if (!dir)
696		return errno;
697
698	// create descriptor
699	AttrDirDescriptor *descriptor = new AttrDirDescriptor(dir, ref);
700	return add_descriptor(descriptor);
701}
702
703// _kern_rename_attr
704status_t
705_kern_rename_attr(int fromFile, const char *fromName, int toFile,
706	const char *toName)
707{
708	// not supported ATM
709	return B_BAD_VALUE;
710}
711
712// _kern_remove_attr
713status_t
714_kern_remove_attr(int fd, const char *name)
715{
716	if (!name)
717		return B_BAD_VALUE;
718
719	if (fs_remove_attr(fd, name) < 0)
720		return errno;
721	return B_OK;
722}
723
724#endif	// ! BUILDING_FS_SHELL
725