1
2#include <BeOSBuildCompatibility.h>
3#include <syscalls.h>
4
5#include <dirent.h>
6#include <errno.h>
7#include <fcntl.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <unistd.h>
11#include <sys/stat.h>
12
13#include <string>
14
15#include <fs_attr.h>
16
17#include "fs_impl.h"
18#include "fs_descriptors.h"
19
20
21// Include the interface to the host platform attributes support, if it shall be
22// used to tag files with unique IDs to identify their attribute directory.
23#if HAIKU_HOST_USE_XATTR_REF
24#	if defined(HAIKU_HOST_PLATFORM_LINUX)
25#		include "fs_attr_xattr.h"
26#	elif defined(HAIKU_HOST_PLATFORM_FREEBSD)
27#		include "fs_attr_extattr.h"
28#	elif defined(HAIKU_HOST_PLATFORM_DARWIN)
29#		include "fs_attr_bsdxattr.h"
30#	else
31#		error No attribute support for this host platform!
32#	endif
33#endif
34
35
36using namespace std;
37using namespace BPrivate;
38
39static const char *sAttributeDirBasePath = HAIKU_BUILD_ATTRIBUTES_DIR;
40
41#if HAIKU_HOST_USE_XATTR_REF
42static const char* const kIDAttributeName = "id";
43#endif
44
45
46// init_attribute_dir_base_dir
47static status_t
48init_attribute_dir_base_dir()
49{
50	static bool initialized = false;
51	static status_t initError;
52
53	if (initialized)
54		return initError;
55
56	// stat the dir
57	struct stat st;
58	initError = B_OK;
59	if (lstat(sAttributeDirBasePath, &st) == 0) {
60		if (!S_ISDIR(st.st_mode)) {
61			// the attribute dir base dir is no directory
62			fprintf(stderr, "init_attribute_dir_base_dir(): The Attribute "
63				"directory base directory exists, but is no directory!\n");
64			initError = B_FILE_ERROR;
65		}
66
67	} else {
68		// doesn't exist yet: create it
69		if (mkdir(sAttributeDirBasePath, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
70			initError = errno;
71	}
72
73	initialized = true;
74	return initError;
75}
76
77// escape_attr_name
78static string
79escape_attr_name(const char *name)
80{
81	string escapedName("_");
82	while (*name != '\0') {
83		// we replace '/' with "_s" and '_' with "__"
84		if (*name == '/')
85			escapedName += "_s";
86		else if (*name == '_')
87			escapedName += "__";
88		else
89			escapedName += *name;
90
91		name++;
92	}
93
94	return escapedName;
95}
96
97// deescape_attr_name
98static string
99deescape_attr_name(const char *name)
100{
101	if (name[0] != '_') {
102		debugger("deescape_attr_name(): name doesn't start with '_'!\n");
103		return "___";
104	}
105	name++;
106
107	string deescapedName;
108	while (*name != '\0') {
109		if (*name == '_') {
110			name++;
111			if (*name == 's') {
112				deescapedName += '/';
113			} else if (*name == '_') {
114				deescapedName += '_';
115			} else {
116				debugger("deescape_attr_name(): name contains invalid escaped "
117					"sequence!\n");
118				name--;
119			}
120		} else
121			deescapedName += *name;
122
123		name++;
124	}
125
126	return deescapedName;
127}
128
129
130#if HAIKU_HOST_USE_XATTR_REF
131
132
133static status_t
134make_unique_node_id(string& _id)
135{
136	// open random device
137	int fd = open("/dev/urandom", O_RDONLY);
138	if (fd < 0) {
139		fd = open("/dev/random", O_RDONLY);
140		if (fd < 0)
141			return B_NOT_SUPPORTED;
142	}
143
144	// read bytes
145	uint8 buffer[16];
146	ssize_t bytesRead = read(fd, buffer, sizeof(buffer));
147	status_t error = B_OK;
148	if (bytesRead < 0)
149		error = errno;
150	close(fd);
151
152	if (error != B_OK)
153		return error;
154
155	if (bytesRead != (ssize_t)sizeof(buffer))
156		error = B_ERROR;
157
158	// convert to hex string
159	static const char* const kHexChars = "0123456789abcdef";
160	_id.clear();
161	for (size_t i = 0; i < sizeof(buffer); i++) {
162		_id += kHexChars[buffer[i] >> 4];
163		_id += kHexChars[buffer[i] & 0xf];
164	}
165
166	return B_OK;
167}
168
169
170static status_t
171get_id_attribute(const char *path, int fd, string& _id)
172{
173	// list_attributes() and remove_attribute() are unused here -- prevent the
174	// warning
175	(void)list_attributes;
176	(void)remove_attribute;
177
178	string attributeName(kAttributeNamespace);
179	attributeName += kIDAttributeName;
180
181	char buffer[64];
182	ssize_t bytesRead = get_attribute(fd, path, attributeName.c_str(), buffer,
183		sizeof(buffer));
184	if (bytesRead < 0) {
185		// On Linux only priviledged users are allowed to set attributes on
186		// symlinks. So, if this is a symlink, we don't even try and instead
187		// construct a symlink specific node ID.
188		status_t error = errno;
189		struct stat st;
190		if (path == NULL || lstat(path, &st) < 0 || !S_ISLNK(st.st_mode))
191			return error;
192
193		char buffer[32];
194		snprintf(buffer, sizeof(buffer), "symlink-%" B_PRIdINO, st.st_ino);
195		_id = buffer;
196		return B_OK;
197	}
198
199	_id = string(buffer, bytesRead);
200	return B_OK;
201}
202
203
204static status_t
205set_id_attribute(const char *path, int fd, const char* id)
206{
207	string attributeName(kAttributeNamespace);
208	attributeName += kIDAttributeName;
209
210	if (set_attribute(fd, path, attributeName.c_str(), id, strlen(id)) < 0)
211		return errno;
212	return B_OK;
213}
214
215
216static string
217get_attribute_dir_path(NodeRef ref, const char *path, int fd)
218{
219	string id;
220	status_t error = get_id_attribute(path, fd, id);
221	if (error != B_OK)
222		id = "_no_attributes_";
223
224	string attrDirPath(sAttributeDirBasePath);
225	attrDirPath += '/';
226	attrDirPath += id;
227	return attrDirPath;
228}
229
230
231static status_t
232get_attribute_dir_path_needed(NodeRef ref, const char *path, int fd,
233	string& _attrDirPath)
234{
235	string id;
236	status_t error = get_id_attribute(path, fd, id);
237	if (error != B_OK) {
238		error = make_unique_node_id(id);
239		if (error != B_OK)
240			return error;
241
242		error = set_id_attribute(path, fd, id.c_str());
243		if (error != B_OK)
244			return error;
245	}
246
247	_attrDirPath = sAttributeDirBasePath;
248	_attrDirPath += '/';
249	_attrDirPath += id;
250	return B_OK;
251}
252
253
254#else
255
256
257static string
258get_attribute_dir_path(NodeRef ref, const char *path, int fd)
259{
260	string attrDirPath(sAttributeDirBasePath);
261	char buffer[32];
262	sprintf(buffer, "/%" B_PRIdINO, ref.node);
263	attrDirPath += buffer;
264	return attrDirPath;
265}
266
267
268static status_t
269get_attribute_dir_path_needed(NodeRef ref, const char *path, int fd,
270	string& _attrDirPath)
271{
272	_attrDirPath = get_attribute_dir_path(ref, path, fd);
273	return B_OK;
274}
275
276
277#endif
278
279
280// ensure_attribute_dir_exists
281static status_t
282ensure_attribute_dir_exists(NodeRef ref, const char *path, int fd)
283{
284	// init the base directory and get the attribute directory path
285	status_t error = init_attribute_dir_base_dir();
286	if (error != B_OK)
287		return error;
288
289	string attrDirPath;
290	error = get_attribute_dir_path_needed(ref, path, fd, attrDirPath);
291	if (error != B_OK)
292		return error;
293
294	// stat the dir
295	struct stat st;
296	if (lstat(attrDirPath.c_str(), &st) == 0) {
297		if (!S_ISDIR(st.st_mode)) {
298			// the attribute dir is no directory
299			fprintf(stderr, "ensure_attribute_dir_exists(): Attribute "
300				"directory for node %lld exists, but is no directory!\n",
301				(long long)ref.node);
302			return B_FILE_ERROR;
303		}
304
305		return B_OK;
306	}
307
308	// doesn't exist yet: create it
309	if (mkdir(attrDirPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) < 0)
310		return errno;
311
312	return B_OK;
313}
314
315// open_attr_dir
316static DIR *
317open_attr_dir(NodeRef ref, const char *path, int fd)
318{
319	// make sure the directory exists
320	status_t error = ensure_attribute_dir_exists(ref, path, fd);
321	if (error != B_OK) {
322		errno = error;
323		return NULL;
324	}
325
326	// open it
327	string dirPath(get_attribute_dir_path(ref, path, fd));
328	return opendir(dirPath.c_str());
329}
330
331// get_attribute_path
332static status_t
333get_attribute_path(NodeRef ref, const char *path, int fd,
334	const char *attribute, string &attrPath, string &typePath)
335{
336	if (!attribute || strlen(attribute) == 0)
337		return B_BAD_VALUE;
338
339	// make sure the attribute dir for the node exits
340	status_t error = ensure_attribute_dir_exists(ref, path, fd);
341	if (error != B_OK) {
342		errno = error;
343		return -1;
344	}
345
346	// construct the attribute path
347	attrPath = get_attribute_dir_path(ref, path, fd) + '/';
348	string attrName(escape_attr_name(attribute));
349	typePath = attrPath + "t" + attrName;
350	attrPath += attrName;
351
352	return B_OK;
353}
354
355
356// get_attribute_path_virtual_fd
357static status_t
358get_attribute_path_virtual_fd(int fd, const char *attribute, string &attrPath,
359	string &typePath)
360{
361	// stat the file to get a NodeRef
362	struct stat st;
363	status_t error = _kern_read_stat(fd, NULL, false, &st, sizeof(st));
364	if (error != B_OK)
365		return error;
366	NodeRef ref(st);
367
368	// Try to get a path. If we can't get a path, this is must be a "real"
369	// (i.e. system) file descriptor, which is just as well.
370	string path;
371	bool pathValid = (get_path(fd, NULL, path) == B_OK);
372
373	// get the attribute path
374	return get_attribute_path(ref, (pathValid ? path.c_str() : NULL),
375		(pathValid ? -1 : fd), attribute, attrPath, typePath);
376}
377
378
379// get_attribute_path
380static status_t
381get_attribute_path(int fd, const char *attribute, string &attrPath,
382	string &typePath)
383{
384	if (get_descriptor(fd)) {
385		// This is a virtual file descriptor -- we have a special function
386		// for handling it.
387		return  get_attribute_path_virtual_fd(fd, attribute, attrPath,
388			typePath);
389	} else {
390		// This is a real (i.e. system) file descriptor -- fstat() it and
391		// build the path.
392
393		// stat the file to get a NodeRef
394		struct stat st;
395		if (fstat(fd, &st) < 0)
396			return errno;
397		NodeRef ref(st);
398
399		return get_attribute_path(ref, NULL, fd, attribute, attrPath, typePath);
400	}
401}
402
403
404// # pragma mark - Public API
405
406
407// fs_open_attr_dir
408DIR *
409fs_open_attr_dir(const char *path)
410{
411	struct stat st;
412	if (lstat(path, &st))
413		return NULL;
414
415	return open_attr_dir(NodeRef(st), path, -1);
416}
417
418// fs_fopen_attr_dir
419DIR *
420fs_fopen_attr_dir(int fd)
421{
422	struct stat st;
423
424	status_t error = _kern_read_stat(fd, NULL, false, &st,
425		sizeof(struct stat));
426	if (error != B_OK) {
427		errno = error;
428		return NULL;
429	}
430
431	// Try to get a path. If we can't get a path, this is must be a "real"
432	// (i.e. system) file descriptor, which is just as well.
433	string path;
434	bool pathValid = (get_path(fd, NULL, path) == B_OK);
435
436	// get the attribute path
437	return open_attr_dir(NodeRef(st), (pathValid ? path.c_str() : NULL),
438		(pathValid ? -1 : fd));
439}
440
441// fs_close_attr_dir
442int
443fs_close_attr_dir(DIR *dir)
444{
445	return closedir(dir);
446}
447
448// fs_read_attr_dir
449struct dirent *
450fs_read_attr_dir(DIR *dir)
451{
452	struct dirent *entry = NULL;
453	while (true) {
454		// read the next entry
455		entry = readdir(dir);
456		if (!entry)
457			return NULL;
458
459		// ignore administrative entries; the
460		if (entry->d_name[0] == '_') {
461			string attrName = deescape_attr_name(entry->d_name);
462			strcpy(entry->d_name, attrName.c_str());
463			return entry;
464		}
465	}
466}
467
468// fs_rewind_attr_dir
469void
470fs_rewind_attr_dir(DIR *dir)
471{
472	rewinddir(dir);
473}
474
475// fs_fopen_attr
476int
477fs_fopen_attr(int fd, const char *attribute, uint32 type, int openMode)
478{
479	if (!attribute) {
480		errno = B_BAD_VALUE;
481		return -1;
482	}
483
484	// get the attribute path
485	string attrPath;
486	string typePath;
487	status_t error = get_attribute_path(fd, attribute, attrPath, typePath);
488	if (error != B_OK) {
489		errno = error;
490		return -1;
491	}
492
493	// check, if the attribute already exists
494	struct stat st;
495	bool exists = (lstat(attrPath.c_str(), &st) == 0);
496
497	// open the attribute
498	int attrFD = open(attrPath.c_str(), openMode, S_IRWXU | S_IRWXG | S_IRWXO);
499	if (attrFD < 0)
500		return -1;
501
502	// set the type, if the attribute didn't exist yet
503	if (!exists) {
504		// create a file prefixed "t"
505		int typeFD = creat(typePath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
506		if (typeFD >= 0) {
507			// write the type into the file
508			if (write(typeFD, &type, sizeof(type)) < 0)
509				error = errno;
510
511			close(typeFD);
512
513		} else
514			error = errno;
515
516		// remove type and attribute file, if something went wrong
517		if (error != B_OK) {
518			if (typeFD > 0) {
519				unlink(typePath.c_str());
520			}
521
522			close(attrFD);
523			unlink(attrPath.c_str());
524
525			errno = error;
526			return -1;
527		}
528	}
529
530	return attrFD;
531}
532
533// fs_close_attr
534int
535fs_close_attr(int fd)
536{
537	return close(fd);
538}
539
540// fs_read_attr
541ssize_t
542fs_read_attr(int fd, const char *attribute, uint32 type, off_t pos,
543	void *buffer, size_t readBytes)
544{
545	// open the attribute
546	int attrFD = fs_fopen_attr(fd, attribute, type, O_RDONLY);
547	if (attrFD < 0)
548		return attrFD;
549
550	// read
551	ssize_t bytesRead = read_pos(attrFD, pos, buffer, readBytes);
552	status_t error = errno;
553
554	// close the attribute
555	fs_close_attr(attrFD);
556
557	if (bytesRead < 0) {
558		errno = error;
559		return -1;
560	}
561
562	return bytesRead;
563}
564
565// fs_write_attr
566ssize_t
567fs_write_attr(int fd, const char *attribute, uint32 type, off_t pos,
568	const void *buffer, size_t readBytes)
569{
570	// open the attribute
571	int attrFD = fs_fopen_attr(fd, attribute, type,
572		O_WRONLY | O_CREAT | O_TRUNC);
573	if (attrFD < 0) {
574		// Setting user attributes on symlinks is not allowed (xattr). So, if
575		// this is a symlink and we're only supposed to write a "BEOS:TYPE"
576		// attribute we silently pretend to have succeeded.
577		struct stat st;
578		if (strcmp(attribute, "BEOS:TYPE") == 0 && fstat(fd, &st) == 0
579			&& S_ISLNK(st.st_mode)) {
580			return readBytes;
581		}
582		return attrFD;
583	}
584
585	// read
586	ssize_t bytesWritten = write_pos(attrFD, pos, buffer, readBytes);
587	status_t error = errno;
588
589	// close the attribute
590	fs_close_attr(attrFD);
591
592	if (bytesWritten < 0) {
593		errno = error;
594		return -1;
595	}
596
597	return bytesWritten;
598}
599
600// fs_remove_attr
601int
602fs_remove_attr(int fd, const char *attribute)
603{
604	if (!attribute) {
605		errno = B_BAD_VALUE;
606		return -1;
607	}
608
609	// get the attribute path
610	string attrPath;
611	string typePath;
612	status_t error = get_attribute_path(fd, attribute, attrPath, typePath);
613	if (error != B_OK) {
614		errno = error;
615		return -1;
616	}
617
618	// remove the attribute
619	if (unlink(attrPath.c_str()) < 0)
620		return -1;
621
622	unlink(typePath.c_str());
623
624	return B_OK;
625}
626
627// fs_stat_attr
628int
629fs_stat_attr(int fd, const char *attribute, struct attr_info *attrInfo)
630{
631	if (!attribute || !attrInfo) {
632		errno = B_BAD_VALUE;
633		return -1;
634	}
635
636	// get the attribute path
637	string attrPath;
638	string typePath;
639	status_t error = get_attribute_path(fd, attribute, attrPath, typePath);
640	if (error != B_OK) {
641		errno = error;
642		return -1;
643	}
644
645	// stat the attribute file to get the size of the attribute
646	struct stat st;
647	if (lstat(attrPath.c_str(), &st) < 0)
648		return -1;
649
650	attrInfo->size = st.st_size;
651
652	// now open the attribute type file and read the attribute's type
653	int typeFD = open(typePath.c_str(), O_RDONLY);
654	if (typeFD < 0)
655		return -1;
656
657	ssize_t bytesRead = read(typeFD, &attrInfo->type, sizeof(attrInfo->type));
658	if (bytesRead < 0)
659		error = errno;
660	else if (bytesRead < (ssize_t)sizeof(attrInfo->type))
661		error = B_FILE_ERROR;
662
663	close(typeFD);
664
665	// fail on error
666	if (error != B_OK) {
667		errno = error;
668		return -1;
669	}
670
671	return 0;
672}
673
674
675// #pragma mark - Private Syscalls
676
677
678// _kern_open_attr_dir
679int
680_kern_open_attr_dir(int fd, const char *path)
681{
682	// get node ref for the node
683	struct stat st;
684	status_t error = _kern_read_stat(fd, path, false, &st,
685		sizeof(struct stat));
686	if (error != B_OK) {
687		errno = error;
688		return -1;
689	}
690	NodeRef ref(st);
691
692	// If a path was given, get a usable path.
693	string realPath;
694	if (path) {
695		error = get_path(fd, path, realPath);
696		if (error != B_OK)
697			return error;
698	}
699
700	// open the attr dir
701	DIR *dir = open_attr_dir(ref, (path ? realPath.c_str() : NULL),
702		(path ? -1 : fd));
703	if (!dir)
704		return errno;
705
706	// create descriptor
707	AttrDirDescriptor *descriptor = new AttrDirDescriptor(dir, ref);
708	return add_descriptor(descriptor);
709}
710
711// _kern_rename_attr
712status_t
713_kern_rename_attr(int fromFile, const char *fromName, int toFile,
714	const char *toName)
715{
716	if (!fromName || !toName)
717		return B_BAD_VALUE;
718
719	// get the attribute paths
720	string fromAttrPath;
721	string fromTypePath;
722	status_t error = get_attribute_path_virtual_fd(fromFile, fromName,
723		fromAttrPath, fromTypePath);
724	if (error != B_OK)
725		return error;
726
727	string toAttrPath;
728	string toTypePath;
729	error = get_attribute_path_virtual_fd(toFile, toName, toAttrPath,
730		toTypePath);
731	if (error != B_OK)
732		return error;
733
734	// rename the attribute and type files
735	if (rename(fromAttrPath.c_str(), toAttrPath.c_str()) < 0)
736		return errno;
737
738	if (rename(fromTypePath.c_str(), toTypePath.c_str()) < 0) {
739		// renaming the type file failed: try to rename back the attribute file
740		error = errno;
741
742		rename(toAttrPath.c_str(), fromAttrPath.c_str());
743
744		return error;
745	}
746
747	return B_OK;
748}
749
750// _kern_remove_attr
751status_t
752_kern_remove_attr(int fd, const char *name)
753{
754	if (!name)
755		return B_BAD_VALUE;
756
757	// get the attribute path
758	string attrPath;
759	string typePath;
760	status_t error = get_attribute_path_virtual_fd(fd, name, attrPath,
761		typePath);
762	if (error != B_OK)
763		return error;
764
765	// remove the attribute
766	if (unlink(attrPath.c_str()) < 0)
767		return errno;
768
769	unlink(typePath.c_str());
770
771	return B_OK;
772}
773
774
775// __get_attribute_dir_path
776extern "C" bool __get_attribute_dir_path(const struct stat* st,
777	const char* path, char* buffer);
778bool
779__get_attribute_dir_path(const struct stat* st, const char* path, char* buffer)
780{
781	NodeRef ref(*st);
782	string dirPath = get_attribute_dir_path(ref, path, -1);
783	strcpy(buffer, dirPath.c_str());
784	return true;
785}
786