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
318#include "LocalFD.h"
319
320}	// unnamed namspace
321
322
323// # pragma mark - Public API
324
325
326// fs_open_attr_dir
327DIR *
328fs_open_attr_dir(const char *path)
329{
330	AttributeDirectory* attrDir = new AttributeDirectory;
331
332	status_t error = attrDir->Init(path, -1);
333	if (error != B_OK) {
334		errno = error;
335		delete attrDir;
336		return NULL;
337	}
338
339	return attrDir->FakeDir();
340}
341
342// fs_fopen_attr_dir
343DIR *
344fs_fopen_attr_dir(int fd)
345{
346	AttributeDirectory* attrDir = new AttributeDirectory;
347
348	status_t error = attrDir->Init(NULL, fd);
349	if (error != B_OK) {
350		errno = error;
351		delete attrDir;
352		return NULL;
353	}
354
355	return attrDir->FakeDir();
356}
357
358// fs_close_attr_dir
359int
360fs_close_attr_dir(DIR *dir)
361{
362	AttributeDirectory* attrDir = AttributeDirectory::Get(dir);
363	if (!attrDir) {
364		errno = B_BAD_VALUE;
365		return -1;
366	}
367
368	delete attrDir;
369	return 0;
370}
371
372// fs_read_attr_dir
373struct dirent *
374fs_read_attr_dir(DIR *dir)
375{
376	// get attr dir
377	AttributeDirectory* attrDir = AttributeDirectory::Get(dir);
378	if (!attrDir) {
379		errno = B_BAD_VALUE;
380		return NULL;
381	}
382
383	// read attr dir
384	dirent* entry = NULL;
385	status_t error = attrDir->ReadDir(&entry);
386	if (error != B_OK) {
387		errno = error;
388		return NULL;
389	}
390
391	return entry;
392}
393
394// fs_rewind_attr_dir
395void
396fs_rewind_attr_dir(DIR *dir)
397{
398	// get attr dir
399	AttributeDirectory* attrDir = AttributeDirectory::Get(dir);
400	if (attrDir)
401		attrDir->RewindDir();
402}
403
404// fs_fopen_attr
405int
406fs_fopen_attr(int fd, const char *attribute, uint32 type, int openMode)
407{
408	if (fd < 0) {
409		errno = B_BAD_VALUE;
410		return -1;
411	}
412
413	AttributeDescriptor* descriptor = new(std::nothrow) AttributeDescriptor(fd,
414		attribute, type, openMode);
415	if (descriptor == NULL) {
416		errno = B_NO_MEMORY;
417		return -1;
418	}
419
420	status_t error = descriptor->Init();
421	if (error != B_OK) {
422		delete descriptor;
423		errno = error;
424		return -1;
425	}
426
427	int attributeFD = add_descriptor(descriptor);
428	if (attributeFD < 0) {
429		delete descriptor;
430		errno = B_NO_MEMORY;
431		return -1;
432	}
433
434	return attributeFD;
435}
436
437// fs_close_attr
438int
439fs_close_attr(int fd)
440{
441	status_t error = delete_descriptor(fd);
442	if (error != 0) {
443		errno = error;
444		return -1;
445	}
446
447	return 0;
448}
449
450// fs_read_attr
451ssize_t
452fs_read_attr(int fd, const char *_attribute, uint32 type, off_t pos,
453	void *buffer, size_t readBytes)
454{
455	// check params
456	if (pos < 0 || pos + readBytes > kMaxAttributeLength
457		|| !_attribute || !buffer) {
458		errno = B_BAD_VALUE;
459		return -1;
460	}
461
462	// resolve FD
463	LocalFD localFD;
464	status_t error = localFD.Init(fd);
465	if (error != B_OK) {
466		errno = error;
467		return -1;
468	}
469
470	// mangle the attribute name
471	string attribute = mangle_attribute_name(_attribute);
472
473	// read the attribute
474	char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength];
475	ssize_t bytesRead = sizeof(attributeBuffer);
476	if (localFD.Path()) {
477		bytesRead = get_attribute(-1, localFD.Path(), attribute.c_str(),
478			attributeBuffer, bytesRead);
479	} else {
480		bytesRead = get_attribute(localFD.FD(), NULL, attribute.c_str(),
481			attributeBuffer, bytesRead);
482	}
483	if (bytesRead < 0) {
484		// Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute
485		// doesn't exist.
486		if (errno == ENOATTR || errno == ENODATA)
487			errno = B_ENTRY_NOT_FOUND;
488		return -1;
489	}
490
491	// check length for sanity
492	if ((size_t)bytesRead < sizeof(AttributeHeader)) {
493		fprintf(stderr, "fs_read_attr(): attribute \"%s\" is shorter than the "
494			"AttributeHeader!\n", attribute.c_str());
495		errno = B_ERROR;
496		return -1;
497	}
498
499	// copy the result into the provided buffer
500	bytesRead -= sizeof(AttributeHeader) + pos;
501	if (bytesRead < 0) {
502		// that means pos > <attribute size>
503		errno = B_BAD_VALUE;
504		return -1;
505	}
506
507	if (bytesRead > 0) {
508		if ((size_t)bytesRead > readBytes)
509			bytesRead = readBytes;
510		memcpy(buffer, attributeBuffer + sizeof(AttributeHeader) + pos,
511			bytesRead);
512	}
513
514	return bytesRead;
515}
516
517// fs_write_attr
518ssize_t
519fs_write_attr(int fd, const char *_attribute, uint32 type, off_t pos,
520	const void *buffer, size_t writeBytes)
521{
522	// check params
523	if (pos < 0 || pos + writeBytes > kMaxAttributeLength
524		|| _attribute == NULL || (writeBytes > 0 && buffer == NULL)) {
525		errno = B_BAD_VALUE;
526		return -1;
527	}
528
529	// resolve FD
530	LocalFD localFD;
531	status_t error = localFD.Init(fd);
532	if (error != B_OK) {
533		errno = error;
534		return -1;
535	}
536
537	// mangle the attribute name
538	string attribute = mangle_attribute_name(_attribute);
539
540	// prepare an attribute buffer
541	char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength];
542	AttributeHeader* header = (AttributeHeader*)attributeBuffer;
543	header->type = type;
544	memset(attributeBuffer + sizeof(AttributeHeader), 0, pos);
545	if (writeBytes > 0) {
546		memcpy(attributeBuffer + sizeof(AttributeHeader) + pos, buffer,
547			writeBytes);
548	}
549
550	// write the attribute
551	ssize_t toWrite = sizeof(AttributeHeader) + pos + writeBytes;
552	int result;
553	if (localFD.Path()) {
554		result = set_attribute(-1, localFD.Path(), attribute.c_str(),
555			attributeBuffer, toWrite);
556	} else {
557		result = set_attribute(localFD.FD(), NULL, attribute.c_str(),
558			attributeBuffer, toWrite);
559	}
560	if (result < 0) {
561		// Setting user attributes on symlinks is not allowed (xattr). So, if
562		// this is a symlink and we're only supposed to write a "BEOS:TYPE"
563		// attribute we silently pretend to have succeeded.
564		if (localFD.IsSymlink() && strcmp(_attribute, "BEOS:TYPE") == 0)
565			return writeBytes;
566		return -1;
567	}
568
569	return writeBytes;
570}
571
572// fs_remove_attr
573int
574fs_remove_attr(int fd, const char *_attribute)
575{
576	// check params
577	if (!_attribute) {
578		errno = B_BAD_VALUE;
579		return -1;
580	}
581
582	// resolve FD
583	LocalFD localFD;
584	status_t error = localFD.Init(fd);
585	if (error != B_OK) {
586		errno = error;
587		return -1;
588	}
589
590	// mangle the attribute name
591	string attribute = mangle_attribute_name(_attribute);
592
593	// remove attribute
594	int result;
595	if (localFD.Path())
596		result = remove_attribute(-1, localFD.Path(), attribute.c_str());
597	else
598		result = remove_attribute(localFD.FD(), NULL, attribute.c_str());
599
600	if (result < 0) {
601		// Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute
602		// doesn't exist.
603		if (errno == ENOATTR || errno == ENODATA)
604			errno = B_ENTRY_NOT_FOUND;
605		return -1;
606	}
607	return 0;
608}
609
610// fs_stat_attr
611int
612fs_stat_attr(int fd, const char *_attribute, struct attr_info *attrInfo)
613{
614	if (!_attribute || !attrInfo) {
615		errno = B_BAD_VALUE;
616		return -1;
617	}
618
619	// resolve FD
620	LocalFD localFD;
621	status_t error = localFD.Init(fd);
622	if (error != B_OK) {
623		errno = error;
624		return -1;
625	}
626
627	// mangle the attribute name
628	string attribute = mangle_attribute_name(_attribute);
629
630	// read the attribute
631	char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength];
632	ssize_t bytesRead = sizeof(AttributeHeader) + kMaxAttributeLength;
633	if (localFD.Path()) {
634		bytesRead = get_attribute(-1, localFD.Path(), attribute.c_str(),
635			attributeBuffer, bytesRead);
636	} else {
637		bytesRead = get_attribute(localFD.FD(), NULL, attribute.c_str(),
638			attributeBuffer, bytesRead);
639	}
640	if (bytesRead < 0) {
641		// Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute
642		// doesn't exist.
643		if (errno == ENOATTR || errno == ENODATA)
644			errno = B_ENTRY_NOT_FOUND;
645		return -1;
646	}
647
648	// check length for sanity
649	if ((size_t)bytesRead < sizeof(AttributeHeader)) {
650		fprintf(stderr, "fs_stat_attr(): attribute \"%s\" is shorter than the "
651			"AttributeHeader!\n", attribute.c_str());
652		errno = B_ERROR;
653		return -1;
654	}
655
656	attrInfo->size = bytesRead - sizeof(AttributeHeader);
657	attrInfo->type = ((AttributeHeader*)attributeBuffer)->type;
658
659	return 0;
660}
661
662
663// #pragma mark - Private Syscalls
664
665
666#ifndef BUILDING_FS_SHELL
667
668// _kern_open_attr_dir
669int
670_kern_open_attr_dir(int fd, const char *path)
671{
672	// get node ref for the node
673	struct stat st;
674	status_t error = _kern_read_stat(fd, path, false, &st,
675		sizeof(struct stat));
676	if (error != B_OK) {
677		errno = error;
678		return -1;
679	}
680	NodeRef ref(st);
681
682	DIR* dir;
683	if (path) {
684		// If a path was given, get a usable path.
685		string realPath;
686		status_t error = get_path(fd, path, realPath);
687		if (error != B_OK)
688			return error;
689
690		dir = fs_open_attr_dir(realPath.c_str());
691	} else
692		dir = fs_fopen_attr_dir(fd);
693
694	if (!dir)
695		return errno;
696
697	// create descriptor
698	AttrDirDescriptor *descriptor = new AttrDirDescriptor(dir, ref);
699	return add_descriptor(descriptor);
700}
701
702// _kern_rename_attr
703status_t
704_kern_rename_attr(int fromFile, const char *fromName, int toFile,
705	const char *toName)
706{
707	// not supported ATM
708	return B_BAD_VALUE;
709}
710
711// _kern_remove_attr
712status_t
713_kern_remove_attr(int fd, const char *name)
714{
715	if (!name)
716		return B_BAD_VALUE;
717
718	if (fs_remove_attr(fd, name) < 0)
719		return errno;
720	return B_OK;
721}
722
723#endif	// ! BUILDING_FS_SHELL
724