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// LongDirent
133struct LongDirent : dirent {
134	char name[B_FILE_NAME_LENGTH];
135};
136
137// AttributeHeader
138struct AttributeHeader {
139	uint32	type;
140};
141
142// AttributeDirectory
143class AttributeDirectory {
144public:
145	AttributeDirectory()
146		: fFileFD(-1),
147		  fFakeDir(NULL),
148		  fListing(NULL),
149		  fListingLength(-1),
150		  fListingIndex(0)
151	{
152	}
153
154	~AttributeDirectory()
155	{
156		if (fFileFD >= 0)
157			close(fFileFD);
158
159		if (fFakeDir) {
160			AttrDirMap::iterator it = sAttributeDirectories.find(fFakeDir);
161			if (it != sAttributeDirectories.end())
162				sAttributeDirectories.erase(it);
163
164			closedir(fFakeDir);
165		}
166
167		free(fListing);
168	}
169
170	static AttributeDirectory* Get(DIR* dir)
171	{
172		AttrDirMap::iterator it = sAttributeDirectories.find(dir);
173		if (it == sAttributeDirectories.end())
174			return NULL;
175		return it->second;
176	}
177
178	status_t Init(const char* path, int fileFD)
179	{
180		// open a fake DIR
181		if (!fFakeDir) {
182			fFakeDir = opendir(".");
183			if (!fFakeDir)
184				return B_ERROR;
185
186			sAttributeDirectories[fFakeDir] = this;
187		}
188
189		string tempPath;
190		if (!path) {
191			// We've got no path. If the file descriptor is one of our own and
192			// not a system FD, we need to get a path for it.
193			Descriptor*	descriptor = get_descriptor(fileFD);
194			if (descriptor && !descriptor->IsSystemFD()) {
195				status_t error = descriptor->GetPath(tempPath);
196				if (error != B_OK)
197					return error;
198				path = tempPath.c_str();
199				fileFD = -1;
200			}
201		}
202
203		if (path) {
204			// A path was given -- check, if it's a symlink. If so we need to
205			// keep the path, otherwise we open a FD.
206			struct stat st;
207			if (lstat(path, &st))
208				return B_ENTRY_NOT_FOUND;
209
210			if (S_ISLNK(st.st_mode)) {
211				fFileFD = -1;
212				fPath = path;
213			} else {
214				fFileFD = open(path, O_RDONLY);
215				if (fFileFD < 0)
216					return errno;
217				fPath = "";
218			}
219		} else {
220			// FD was given -- dup it.
221			fFileFD = dup(fileFD);
222			if (fFileFD < 0)
223				return errno;
224			fPath = "";
225		}
226
227		fListingLength = -1;
228		fListingIndex = 0;
229
230		return B_OK;
231	}
232
233	DIR* FakeDir() const	{ return fFakeDir; }
234
235	status_t ReadDir(struct dirent** _entry)
236	{
237		// make sure we have a current listing
238		status_t error = _CheckListing();
239		if (error != B_OK)
240			return error;
241
242		// keep going until we find an entry or hit the end of dir
243		while (fListingIndex < fListingLength) {
244			// get next entry name
245			const char* name = fListing + fListingIndex;
246			int nameLen = strlen(name);
247			fListingIndex += nameLen + 1;
248
249			// check the attribute namespace
250			string demangledName;
251			if (!demangle_attribute_name(name, demangledName))
252				continue;
253			name = demangledName.c_str();
254			nameLen = demangledName.length();
255
256			if (nameLen == 0) {
257				// Uh, weird attribute.
258				return B_ERROR;
259			}
260
261			// prepare the dirent
262			strcpy(fDirent.d_name, name);
263			fDirent.d_ino = 0;
264// TODO: We need the node ID!
265
266			*_entry = &fDirent;
267			return B_OK;
268		}
269
270		// end of dir
271		*_entry = NULL;
272		return B_OK;
273	}
274
275	void RewindDir()
276	{
277		fListingIndex = 0;
278	}
279
280private:
281	status_t _CheckListing()
282	{
283		if (fListing && fListingLength >= 0)
284			return B_OK;
285
286		char listing[kMaxAttributeListingLength];
287
288		// get the listing
289		ssize_t length = list_attributes(fFileFD, fPath.c_str(), listing,
290			kMaxAttributeListingLength);
291		if (length < 0)
292			return errno;
293
294		// clone the on-stack listing
295		char* newListing = (char*)realloc(fListing, length);
296		if (!newListing)
297			return B_NO_MEMORY;
298		memcpy(newListing, listing, length);
299
300		fListing = newListing;
301		fListingLength = length;
302		fListingIndex = 0;
303
304		return B_OK;
305	}
306
307private:
308	int			fFileFD;
309	string		fPath;
310	DIR*		fFakeDir;
311	LongDirent	fDirent;
312	char*		fListing;
313	int			fListingLength;
314	int			fListingIndex;
315};
316
317// LocalFD
318class LocalFD {
319public:
320	LocalFD()
321	{
322	}
323
324	~LocalFD()
325	{
326	}
327
328	status_t Init(int fd)
329	{
330#ifndef BUILDING_FS_SHELL
331		Descriptor* descriptor = get_descriptor(fd);
332		if (descriptor && !descriptor->IsSystemFD()) {
333			// we need to get a path
334			fFD = -1;
335			return descriptor->GetPath(fPath);
336		}
337#endif
338
339		fFD = fd;
340		fPath = "";
341		return B_OK;
342	}
343
344	int FD() const
345	{
346		return fFD;
347	}
348
349	const char* Path() const
350	{
351		return (fFD < 0 ? fPath.c_str() : NULL);
352	}
353
354	bool IsSymlink() const
355	{
356		struct stat st;
357		int result;
358		if (Path())
359			result = lstat(Path(), &st);
360		else
361			result = fstat(fFD, &st);
362
363		return (result == 0 && S_ISLNK(st.st_mode));
364	}
365
366private:
367	string	fPath;
368	int		fFD;
369};
370
371}	// unnamed namspace
372
373
374// # pragma mark - Public API
375
376
377// fs_open_attr_dir
378DIR *
379fs_open_attr_dir(const char *path)
380{
381	AttributeDirectory* attrDir = new AttributeDirectory;
382
383	status_t error = attrDir->Init(path, -1);
384	if (error != B_OK) {
385		errno = error;
386		delete attrDir;
387		return NULL;
388	}
389
390	return attrDir->FakeDir();
391}
392
393// fs_fopen_attr_dir
394DIR *
395fs_fopen_attr_dir(int fd)
396{
397	AttributeDirectory* attrDir = new AttributeDirectory;
398
399	status_t error = attrDir->Init(NULL, fd);
400	if (error != B_OK) {
401		errno = error;
402		delete attrDir;
403		return NULL;
404	}
405
406	return attrDir->FakeDir();
407}
408
409// fs_close_attr_dir
410int
411fs_close_attr_dir(DIR *dir)
412{
413	AttributeDirectory* attrDir = AttributeDirectory::Get(dir);
414	if (!attrDir) {
415		errno = B_BAD_VALUE;
416		return -1;
417	}
418
419	delete attrDir;
420	return 0;
421}
422
423// fs_read_attr_dir
424struct dirent *
425fs_read_attr_dir(DIR *dir)
426{
427	// get attr dir
428	AttributeDirectory* attrDir = AttributeDirectory::Get(dir);
429	if (!attrDir) {
430		errno = B_BAD_VALUE;
431		return NULL;
432	}
433
434	// read attr dir
435	dirent* entry = NULL;
436	status_t error = attrDir->ReadDir(&entry);
437	if (error != B_OK) {
438		errno = error;
439		return NULL;
440	}
441
442	return entry;
443}
444
445// fs_rewind_attr_dir
446void
447fs_rewind_attr_dir(DIR *dir)
448{
449	// get attr dir
450	AttributeDirectory* attrDir = AttributeDirectory::Get(dir);
451	if (attrDir)
452		attrDir->RewindDir();
453}
454
455// fs_fopen_attr
456int
457fs_fopen_attr(int fd, const char *attribute, uint32 type, int openMode)
458{
459	if (fd < 0) {
460		errno = B_BAD_VALUE;
461		return -1;
462	}
463
464	AttributeDescriptor* descriptor = new(std::nothrow) AttributeDescriptor(fd,
465		attribute, type, openMode);
466	if (descriptor == NULL) {
467		errno = B_NO_MEMORY;
468		return -1;
469	}
470
471	status_t error = descriptor->Init();
472	if (error != B_OK) {
473		delete descriptor;
474		errno = error;
475		return -1;
476	}
477
478	int attributeFD = add_descriptor(descriptor);
479	if (attributeFD < 0) {
480		delete descriptor;
481		errno = B_NO_MEMORY;
482		return -1;
483	}
484
485	return attributeFD;
486}
487
488// fs_close_attr
489int
490fs_close_attr(int fd)
491{
492	status_t error = delete_descriptor(fd);
493	if (error != 0) {
494		errno = error;
495		return -1;
496	}
497
498	return 0;
499}
500
501// fs_read_attr
502ssize_t
503fs_read_attr(int fd, const char *_attribute, uint32 type, off_t pos,
504	void *buffer, size_t readBytes)
505{
506	// check params
507	if (pos < 0 || pos + readBytes > kMaxAttributeLength
508		|| !_attribute || !buffer) {
509		errno = B_BAD_VALUE;
510		return -1;
511	}
512
513	// resolve FD
514	LocalFD localFD;
515	status_t error = localFD.Init(fd);
516	if (error != B_OK) {
517		errno = error;
518		return -1;
519	}
520
521	// mangle the attribute name
522	string attribute = mangle_attribute_name(_attribute);
523
524	// read the attribute
525	char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength];
526	ssize_t bytesRead = min_c((size_t)kMaxAttributeLength, readBytes)
527		+ sizeof(AttributeHeader);
528	if (localFD.Path()) {
529		bytesRead = get_attribute(-1, localFD.Path(), attribute.c_str(),
530			attributeBuffer, bytesRead);
531	} else {
532		bytesRead = get_attribute(localFD.FD(), NULL, attribute.c_str(),
533			attributeBuffer, bytesRead);
534	}
535	if (bytesRead < 0) {
536		// Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute
537		// doesn't exist.
538		if (errno == ENOATTR || errno == ENODATA)
539			errno = B_ENTRY_NOT_FOUND;
540		return -1;
541	}
542
543	// check length for sanity
544	if ((size_t)bytesRead < sizeof(AttributeHeader)) {
545		fprintf(stderr, "fs_read_attr(): attribute \"%s\" is shorter than the "
546			"AttributeHeader!\n", attribute.c_str());
547		errno = B_ERROR;
548		return -1;
549	}
550
551	// copy the result into the provided buffer
552	bytesRead -= sizeof(AttributeHeader);
553	if (bytesRead > pos) {
554		memcpy(buffer, attributeBuffer + sizeof(AttributeHeader) + pos,
555			bytesRead - pos);
556	} else
557		bytesRead = 0;
558
559	return bytesRead;
560}
561
562// fs_write_attr
563ssize_t
564fs_write_attr(int fd, const char *_attribute, uint32 type, off_t pos,
565	const void *buffer, size_t writeBytes)
566{
567	// check params
568	if (pos < 0 || pos + writeBytes > kMaxAttributeLength
569		|| _attribute == NULL || (writeBytes > 0 && buffer == NULL)) {
570		errno = B_BAD_VALUE;
571		return -1;
572	}
573
574	// resolve FD
575	LocalFD localFD;
576	status_t error = localFD.Init(fd);
577	if (error != B_OK) {
578		errno = error;
579		return -1;
580	}
581
582	// mangle the attribute name
583	string attribute = mangle_attribute_name(_attribute);
584
585	// prepare an attribute buffer
586	char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength];
587	AttributeHeader* header = (AttributeHeader*)attributeBuffer;
588	header->type = type;
589	memset(attributeBuffer + sizeof(AttributeHeader), 0, pos);
590	if (writeBytes > 0) {
591		memcpy(attributeBuffer + sizeof(AttributeHeader) + pos, buffer,
592			writeBytes);
593	}
594
595	// write the attribute
596	ssize_t toWrite = sizeof(AttributeHeader) + pos + writeBytes;
597	int result;
598	if (localFD.Path()) {
599		result = set_attribute(-1, localFD.Path(), attribute.c_str(),
600			attributeBuffer, toWrite);
601	} else {
602		result = set_attribute(localFD.FD(), NULL, attribute.c_str(),
603			attributeBuffer, toWrite);
604	}
605	if (result < 0) {
606		// Setting user attributes on symlinks is not allowed (xattr). So, if
607		// this is a symlink and we're only supposed to write a "BEOS:TYPE"
608		// attribute we silently pretend to have succeeded.
609		if (localFD.IsSymlink() && strcmp(_attribute, "BEOS:TYPE") == 0)
610			return writeBytes;
611		return -1;
612	}
613
614	return writeBytes;
615}
616
617// fs_remove_attr
618int
619fs_remove_attr(int fd, const char *_attribute)
620{
621	// check params
622	if (!_attribute) {
623		errno = B_BAD_VALUE;
624		return -1;
625	}
626
627	// resolve FD
628	LocalFD localFD;
629	status_t error = localFD.Init(fd);
630	if (error != B_OK) {
631		errno = error;
632		return -1;
633	}
634
635	// mangle the attribute name
636	string attribute = mangle_attribute_name(_attribute);
637
638	// remove attribute
639	int result;
640	if (localFD.Path())
641		result = remove_attribute(-1, localFD.Path(), attribute.c_str());
642	else
643		result = remove_attribute(localFD.FD(), NULL, attribute.c_str());
644
645	if (result < 0) {
646		// Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute
647		// doesn't exist.
648		if (errno == ENOATTR || errno == ENODATA)
649			errno = B_ENTRY_NOT_FOUND;
650		return -1;
651	}
652	return 0;
653}
654
655// fs_stat_attr
656int
657fs_stat_attr(int fd, const char *_attribute, struct attr_info *attrInfo)
658{
659	if (!_attribute || !attrInfo) {
660		errno = B_BAD_VALUE;
661		return -1;
662	}
663
664	// resolve FD
665	LocalFD localFD;
666	status_t error = localFD.Init(fd);
667	if (error != B_OK) {
668		errno = error;
669		return -1;
670	}
671
672	// mangle the attribute name
673	string attribute = mangle_attribute_name(_attribute);
674
675	// read the attribute
676	char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength];
677	ssize_t bytesRead = sizeof(AttributeHeader) + kMaxAttributeLength;
678	if (localFD.Path()) {
679		bytesRead = get_attribute(-1, localFD.Path(), attribute.c_str(),
680			attributeBuffer, bytesRead);
681	} else {
682		bytesRead = get_attribute(localFD.FD(), NULL, attribute.c_str(),
683			attributeBuffer, bytesRead);
684	}
685	if (bytesRead < 0) {
686		// Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute
687		// doesn't exist.
688		if (errno == ENOATTR || errno == ENODATA)
689			errno = B_ENTRY_NOT_FOUND;
690		return -1;
691	}
692
693	// check length for sanity
694	if ((size_t)bytesRead < sizeof(AttributeHeader)) {
695		fprintf(stderr, "fs_stat_attr(): attribute \"%s\" is shorter than the "
696			"AttributeHeader!\n", attribute.c_str());
697		errno = B_ERROR;
698		return -1;
699	}
700
701	attrInfo->size = bytesRead - sizeof(AttributeHeader);
702	attrInfo->type = ((AttributeHeader*)attributeBuffer)->type;
703
704	return 0;
705}
706
707
708// #pragma mark - Private Syscalls
709
710
711#ifndef BUILDING_FS_SHELL
712
713// _kern_open_attr_dir
714int
715_kern_open_attr_dir(int fd, const char *path)
716{
717	// get node ref for the node
718	struct stat st;
719	status_t error = _kern_read_stat(fd, path, false, &st,
720		sizeof(struct stat));
721	if (error != B_OK) {
722		errno = error;
723		return -1;
724	}
725	NodeRef ref(st);
726
727	DIR* dir;
728	if (path) {
729		// If a path was given, get a usable path.
730		string realPath;
731		status_t error = get_path(fd, path, realPath);
732		if (error != B_OK)
733			return error;
734
735		dir = fs_open_attr_dir(realPath.c_str());
736	} else
737		dir = fs_fopen_attr_dir(fd);
738
739	if (!dir)
740		return errno;
741
742	// create descriptor
743	AttrDirDescriptor *descriptor = new AttrDirDescriptor(dir, ref);
744	return add_descriptor(descriptor);
745}
746
747// _kern_rename_attr
748status_t
749_kern_rename_attr(int fromFile, const char *fromName, int toFile,
750	const char *toName)
751{
752	// not supported ATM
753	return B_BAD_VALUE;
754}
755
756// _kern_remove_attr
757status_t
758_kern_remove_attr(int fd, const char *name)
759{
760	if (!name)
761		return B_BAD_VALUE;
762
763	if (fs_remove_attr(fd, name) < 0)
764		return errno;
765	return B_OK;
766}
767
768#endif	// ! BUILDING_FS_SHELL
769