xref: /haiku/src/build/libroot/fs_attr_generic.cpp (revision 579f1dbca962a2a03df54f69fdc6e9423f91f20e)
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 
36 using namespace std;
37 using namespace BPrivate;
38 
39 static const char *sAttributeDirBasePath = HAIKU_BUILD_ATTRIBUTES_DIR;
40 
41 #if HAIKU_HOST_USE_XATTR_REF
42 static const char* const kIDAttributeName = "id";
43 #endif
44 
45 
46 // init_attribute_dir_base_dir
47 static status_t
48 init_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
78 static string
79 escape_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
98 static string
99 deescape_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 
133 static status_t
134 make_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 
170 static status_t
171 get_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 
204 static status_t
205 set_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 
216 static string
217 get_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 
231 static status_t
232 get_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 
257 static string
258 get_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 
268 static status_t
269 get_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
281 static status_t
282 ensure_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
316 static DIR *
317 open_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
332 static status_t
333 get_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
357 static status_t
358 get_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
380 static status_t
381 get_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
408 DIR *
409 fs_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
419 DIR *
420 fs_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
442 int
443 fs_close_attr_dir(DIR *dir)
444 {
445 	return closedir(dir);
446 }
447 
448 // fs_read_attr_dir
449 struct dirent *
450 fs_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
469 void
470 fs_rewind_attr_dir(DIR *dir)
471 {
472 	rewinddir(dir);
473 }
474 
475 // fs_fopen_attr
476 int
477 fs_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
534 int
535 fs_close_attr(int fd)
536 {
537 	return close(fd);
538 }
539 
540 // fs_read_attr
541 ssize_t
542 fs_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
566 ssize_t
567 fs_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
601 int
602 fs_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
628 int
629 fs_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
679 int
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
712 status_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
751 status_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
776 extern "C" bool __get_attribute_dir_path(const struct stat* st,
777 	const char* path, char* buffer);
778 bool
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