xref: /haiku/src/kits/storage/Entry.cpp (revision a30a4a41f948ebb03b95dab065a27a584ac0c97a)
1 /*
2  * Copyright 2002-2012, Haiku Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Tyler Dauwalder
7  *		Ingo Weinhold, bonefish@users.sf.net
8  */
9 
10 
11 #include <Entry.h>
12 
13 #include <fcntl.h>
14 #include <new>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 
20 #include <compat/sys/stat.h>
21 
22 #include <Directory.h>
23 #include <Path.h>
24 #include <SymLink.h>
25 
26 #include <syscalls.h>
27 
28 #include "storage_support.h"
29 
30 
31 using namespace std;
32 
33 
34 //	#pragma mark - struct entry_ref
35 
36 
37 entry_ref::entry_ref()
38 	:
39 	device((dev_t)-1),
40 	directory((ino_t)-1),
41 	name(NULL)
42 {
43 }
44 
45 
46 entry_ref::entry_ref(dev_t dev, ino_t dir, const char* name)
47 	:
48 	device(dev),
49 	directory(dir),
50 	name(NULL)
51 {
52 	set_name(name);
53 }
54 
55 
56 entry_ref::entry_ref(const entry_ref& ref)
57 	:
58 	device(ref.device),
59 	directory(ref.directory),
60 	name(NULL)
61 {
62 	set_name(ref.name);
63 }
64 
65 
66 entry_ref::~entry_ref()
67 {
68 	free(name);
69 }
70 
71 
72 status_t
73 entry_ref::set_name(const char* name)
74 {
75 	free(this->name);
76 
77 	if (name == NULL) {
78 		this->name = NULL;
79 	} else {
80 		this->name = strdup(name);
81 		if (!this->name)
82 			return B_NO_MEMORY;
83 	}
84 
85 	return B_OK;
86 }
87 
88 
89 bool
90 entry_ref::operator==(const entry_ref& ref) const
91 {
92 	return (device == ref.device
93 		&& directory == ref.directory
94 		&& (name == ref.name
95 			|| (name != NULL && ref.name != NULL
96 				&& strcmp(name, ref.name) == 0)));
97 }
98 
99 
100 bool
101 entry_ref::operator!=(const entry_ref& ref) const
102 {
103 	return !(*this == ref);
104 }
105 
106 
107 entry_ref&
108 entry_ref::operator=(const entry_ref& ref)
109 {
110 	if (this == &ref)
111 		return *this;
112 
113 	device = ref.device;
114 	directory = ref.directory;
115 	set_name(ref.name);
116 	return *this;
117 }
118 
119 
120 //	#pragma mark - BEntry
121 
122 
123 BEntry::BEntry()
124 	:
125 	fDirFd(-1),
126 	fName(NULL),
127 	fCStatus(B_NO_INIT)
128 {
129 }
130 
131 
132 BEntry::BEntry(const BDirectory* dir, const char* path, bool traverse)
133 	:
134 	fDirFd(-1),
135 	fName(NULL),
136 	fCStatus(B_NO_INIT)
137 {
138 	SetTo(dir, path, traverse);
139 }
140 
141 
142 BEntry::BEntry(const entry_ref* ref, bool traverse)
143 	:
144 	fDirFd(-1),
145 	fName(NULL),
146 	fCStatus(B_NO_INIT)
147 {
148 	SetTo(ref, traverse);
149 }
150 
151 
152 BEntry::BEntry(const char* path, bool traverse)
153 	:
154 	fDirFd(-1),
155 	fName(NULL),
156 	fCStatus(B_NO_INIT)
157 {
158 	SetTo(path, traverse);
159 }
160 
161 
162 BEntry::BEntry(const BEntry& entry)
163 	:
164 	fDirFd(-1),
165 	fName(NULL),
166 	fCStatus(B_NO_INIT)
167 {
168 	*this = entry;
169 }
170 
171 
172 BEntry::~BEntry()
173 {
174 	Unset();
175 }
176 
177 
178 status_t
179 BEntry::InitCheck() const
180 {
181 	return fCStatus;
182 }
183 
184 
185 bool
186 BEntry::Exists() const
187 {
188 	// just stat the beast
189 	struct stat st;
190 	return GetStat(&st) == B_OK;
191 }
192 
193 
194 const char*
195 BEntry::Name() const
196 {
197 	if (fCStatus != B_OK)
198 		return NULL;
199 
200 	return fName;
201 }
202 
203 
204 status_t
205 BEntry::SetTo(const BDirectory* dir, const char* path, bool traverse)
206 {
207 	// check params
208 	if (!dir)
209 		return (fCStatus = B_BAD_VALUE);
210 	if (path && path[0] == '\0')	// R5 behaviour
211 		path = NULL;
212 
213 	// if path is absolute, let the path-only SetTo() do the job
214 	if (BPrivate::Storage::is_absolute_path(path))
215 		return SetTo(path, traverse);
216 
217 	Unset();
218 
219 	if (dir->InitCheck() != B_OK)
220 		fCStatus = B_BAD_VALUE;
221 
222 	// dup() the dir's FD and let set() do the rest
223 	int dirFD = _kern_dup(dir->get_fd());
224 	if (dirFD < 0)
225 		return (fCStatus = dirFD);
226 	return (fCStatus = _SetTo(dirFD, path, traverse));
227 }
228 
229 
230 status_t
231 BEntry::SetTo(const entry_ref* ref, bool traverse)
232 {
233 	Unset();
234 	if (ref == NULL)
235 		return (fCStatus = B_BAD_VALUE);
236 
237 	// if ref-name is absolute, let the path-only SetTo() do the job
238 	if (BPrivate::Storage::is_absolute_path(ref->name))
239 		return SetTo(ref->name, traverse);
240 
241 	// open the directory and let set() do the rest
242 	int dirFD = _kern_open_dir_entry_ref(ref->device, ref->directory, NULL);
243 	if (dirFD < 0)
244 		return (fCStatus = dirFD);
245 	return (fCStatus = _SetTo(dirFD, ref->name, traverse));
246 }
247 
248 
249 status_t
250 BEntry::SetTo(const char* path, bool traverse)
251 {
252 	Unset();
253 	// check the argument
254 	if (!path)
255 		return (fCStatus = B_BAD_VALUE);
256 	return (fCStatus = _SetTo(-1, path, traverse));
257 }
258 
259 
260 void
261 BEntry::Unset()
262 {
263 	// Close the directory
264 	if (fDirFd >= 0)
265 		_kern_close(fDirFd);
266 
267 	// Free our leaf name
268 	free(fName);
269 
270 	fDirFd = -1;
271 	fName = NULL;
272 	fCStatus = B_NO_INIT;
273 }
274 
275 
276 status_t
277 BEntry::GetRef(entry_ref* ref) const
278 {
279 	if (fCStatus != B_OK)
280 		return B_NO_INIT;
281 
282 	if (ref == NULL)
283 		return B_BAD_VALUE;
284 
285 	struct stat st;
286 	status_t error = _kern_read_stat(fDirFd, NULL, false, &st,
287 		sizeof(struct stat));
288 	if (error == B_OK) {
289 		ref->device = st.st_dev;
290 		ref->directory = st.st_ino;
291 		error = ref->set_name(fName);
292 	}
293 	return error;
294 }
295 
296 
297 status_t
298 BEntry::GetPath(BPath* path) const
299 {
300 	if (fCStatus != B_OK)
301 		return B_NO_INIT;
302 
303 	if (path == NULL)
304 		return B_BAD_VALUE;
305 
306 	return path->SetTo(this);
307 }
308 
309 
310 status_t BEntry::GetParent(BEntry* entry) const
311 {
312 	// check parameter and initialization
313 	if (fCStatus != B_OK)
314 		return B_NO_INIT;
315 	if (entry == NULL)
316 		return B_BAD_VALUE;
317 
318 	// check whether we are the root directory
319 	// It is sufficient to check whether our leaf name is ".".
320 	if (strcmp(fName, ".") == 0)
321 		return B_ENTRY_NOT_FOUND;
322 
323 	// open the parent directory
324 	char leafName[B_FILE_NAME_LENGTH];
325 	int parentFD = _kern_open_parent_dir(fDirFd, leafName, B_FILE_NAME_LENGTH);
326 	if (parentFD < 0)
327 		return parentFD;
328 
329 	// set close on exec flag on dir FD
330 	fcntl(parentFD, F_SETFD, FD_CLOEXEC);
331 
332 	// init the entry
333 	entry->Unset();
334 	entry->fDirFd = parentFD;
335 	entry->fCStatus = entry->_SetName(leafName);
336 	if (entry->fCStatus != B_OK)
337 		entry->Unset();
338 	return entry->fCStatus;
339 }
340 
341 
342 status_t
343 BEntry::GetParent(BDirectory* dir) const
344 {
345 	// check initialization and parameter
346 	if (fCStatus != B_OK)
347 		return B_NO_INIT;
348 	if (dir == NULL)
349 		return B_BAD_VALUE;
350 	// check whether we are the root directory
351 	// It is sufficient to check whether our leaf name is ".".
352 	if (strcmp(fName, ".") == 0)
353 		return B_ENTRY_NOT_FOUND;
354 	// get a node ref for the directory and init it
355 	struct stat st;
356 	status_t error = _kern_read_stat(fDirFd, NULL, false, &st,
357 		sizeof(struct stat));
358 	if (error != B_OK)
359 		return error;
360 	node_ref ref;
361 	ref.device = st.st_dev;
362 	ref.node = st.st_ino;
363 	return dir->SetTo(&ref);
364 	// TODO: This can be optimized: We already have a FD for the directory,
365 	// so we could dup() it and set it on the directory. We just need a private
366 	// API for being able to do that.
367 }
368 
369 
370 status_t
371 BEntry::GetName(char* buffer) const
372 {
373 	if (fCStatus != B_OK)
374 		return B_NO_INIT;
375 	if (buffer == NULL)
376 		return B_BAD_VALUE;
377 
378 	strcpy(buffer, fName);
379 	return B_OK;
380 }
381 
382 
383 status_t
384 BEntry::Rename(const char* path, bool clobber)
385 {
386 	// check parameter and initialization
387 	if (path == NULL)
388 		return B_BAD_VALUE;
389 	if (fCStatus != B_OK)
390 		return B_NO_INIT;
391 	// get an entry representing the target location
392 	BEntry target;
393 	status_t error;
394 	if (BPrivate::Storage::is_absolute_path(path)) {
395 		error = target.SetTo(path);
396 	} else {
397 		int dirFD = _kern_dup(fDirFd);
398 		if (dirFD < 0)
399 			return dirFD;
400 		// init the entry
401 		error = target.fCStatus = target._SetTo(dirFD, path, false);
402 	}
403 	if (error != B_OK)
404 		return error;
405 	return _Rename(target, clobber);
406 }
407 
408 
409 status_t
410 BEntry::MoveTo(BDirectory* dir, const char* path, bool clobber)
411 {
412 	// check parameters and initialization
413 	if (fCStatus != B_OK)
414 		return B_NO_INIT;
415 	if (dir == NULL)
416 		return B_BAD_VALUE;
417 	if (dir->InitCheck() != B_OK)
418 		return B_BAD_VALUE;
419 	// NULL path simply means move without renaming
420 	if (path == NULL)
421 		path = fName;
422 	// get an entry representing the target location
423 	BEntry target;
424 	status_t error = target.SetTo(dir, path);
425 	if (error != B_OK)
426 		return error;
427 	return _Rename(target, clobber);
428 }
429 
430 
431 status_t
432 BEntry::Remove()
433 {
434 	if (fCStatus != B_OK)
435 		return B_NO_INIT;
436 
437 	if (IsDirectory())
438 		return _kern_remove_dir(fDirFd, fName);
439 
440 	return _kern_unlink(fDirFd, fName);
441 }
442 
443 
444 bool
445 BEntry::operator==(const BEntry& item) const
446 {
447 	// First check statuses
448 	if (this->InitCheck() != B_OK && item.InitCheck() != B_OK) {
449 		return true;
450 	} else if (this->InitCheck() == B_OK && item.InitCheck() == B_OK) {
451 
452 		// Directories don't compare well directly, so we'll
453 		// compare entry_refs instead
454 		entry_ref ref1, ref2;
455 		if (this->GetRef(&ref1) != B_OK)
456 			return false;
457 		if (item.GetRef(&ref2) != B_OK)
458 			return false;
459 		return (ref1 == ref2);
460 
461 	} else {
462 		return false;
463 	}
464 
465 }
466 
467 
468 bool
469 BEntry::operator!=(const BEntry& item) const
470 {
471 	return !(*this == item);
472 }
473 
474 
475 BEntry&
476 BEntry::operator=(const BEntry& item)
477 {
478 	if (this == &item)
479 		return *this;
480 
481 	Unset();
482 	if (item.fCStatus == B_OK) {
483 		fDirFd = _kern_dup(item.fDirFd);
484 		if (fDirFd >= 0)
485 			fCStatus = _SetName(item.fName);
486 		else
487 			fCStatus = fDirFd;
488 
489 		if (fCStatus != B_OK)
490 			Unset();
491 	}
492 
493 	return *this;
494 }
495 
496 
497 void BEntry::_PennyEntry1(){}
498 void BEntry::_PennyEntry2(){}
499 void BEntry::_PennyEntry3(){}
500 void BEntry::_PennyEntry4(){}
501 void BEntry::_PennyEntry5(){}
502 void BEntry::_PennyEntry6(){}
503 
504 
505 /*!	Updates the BEntry with the data from the stat structure according
506 	to the \a what mask.
507 
508 	\param st The stat structure to set.
509 	\param what A mask
510 
511 	\returns A status code.
512 	\retval B_OK Everything went fine.
513 	\retval B_FILE_ERROR There was an error writing to the BEntry object.
514 */
515 status_t
516 BEntry::set_stat(struct stat& st, uint32 what)
517 {
518 	if (fCStatus != B_OK)
519 		return B_FILE_ERROR;
520 
521 	return _kern_write_stat(fDirFd, fName, false, &st, sizeof(struct stat),
522 		what);
523 }
524 
525 
526 /*!	Sets the entry to point to the entry specified by the path \a path
527 	relative to the given directory.
528 
529 	If \a traverse is \c true and the given entry is a symbolic link, the
530 	object is recursively set to point to the entry pointed to by the symlink.
531 
532 	If \a path is an absolute path, \a dirFD is ignored.
533 
534 	If \a dirFD is -1, \a path is considered relative to the current directory
535 	(unless it is an absolute path).
536 
537 	The ownership of the file descriptor \a dirFD is transferred to the
538 	method, regardless of whether it succeeds or fails. The caller must not
539 	close the FD afterwards.
540 
541 	\param dirFD File descriptor of a directory relative to which path is to
542 		be considered. May be -1 if the current directory shall be considered.
543 	\param path Pointer to a path relative to the given directory.
544 	\param traverse If \c true and the given entry is a symbolic link, the
545 		object is recursively set to point to the entry linked to by the
546 		symbolic link.
547 
548 	\returns \c B_OK on success, or an error code on failure.
549 */
550 status_t
551 BEntry::_SetTo(int dirFD, const char* path, bool traverse)
552 {
553 	bool requireConcrete = false;
554 	FDCloser fdCloser(dirFD);
555 	char tmpPath[B_PATH_NAME_LENGTH];
556 	char leafName[B_FILE_NAME_LENGTH];
557 	int32 linkLimit = B_MAX_SYMLINKS;
558 	while (true) {
559 		if (!path || strcmp(path, ".") == 0) {
560 			// "."
561 			// if no dir FD is supplied, we need to open the current directory
562 			// first
563 			if (dirFD < 0) {
564 				dirFD = _kern_open_dir(-1, ".");
565 				if (dirFD < 0)
566 					return dirFD;
567 				fdCloser.SetTo(dirFD);
568 			}
569 			// get the parent directory
570 			int parentFD = _kern_open_parent_dir(dirFD, leafName,
571 				B_FILE_NAME_LENGTH);
572 			if (parentFD < 0)
573 				return parentFD;
574 			dirFD = parentFD;
575 			fdCloser.SetTo(dirFD);
576 			break;
577 		} else if (strcmp(path, "..") == 0) {
578 			// ".."
579 			// open the parent directory
580 			int parentFD = _kern_open_dir(dirFD, "..");
581 			if (parentFD < 0)
582 				return parentFD;
583 			dirFD = parentFD;
584 			fdCloser.SetTo(dirFD);
585 			// get the parent's parent directory
586 			parentFD = _kern_open_parent_dir(dirFD, leafName,
587 				B_FILE_NAME_LENGTH);
588 			if (parentFD < 0)
589 				return parentFD;
590 			dirFD = parentFD;
591 			fdCloser.SetTo(dirFD);
592 			break;
593 		} else {
594 			// an ordinary path; analyze it
595 			char dirPath[B_PATH_NAME_LENGTH];
596 			status_t error = BPrivate::Storage::parse_path(path, dirPath,
597 				leafName);
598 			if (error != B_OK)
599 				return error;
600 			// special case: root directory ("/")
601 			if (leafName[0] == '\0' && dirPath[0] == '/')
602 				strcpy(leafName, ".");
603 			if (leafName[0] == '\0') {
604 				// the supplied path is already a leaf
605 				error = BPrivate::Storage::check_entry_name(dirPath);
606 				if (error != B_OK)
607 					return error;
608 				strcpy(leafName, dirPath);
609 				// if no directory was given, we need to open the current dir
610 				// now
611 				if (dirFD < 0) {
612 					char* cwd = getcwd(tmpPath, B_PATH_NAME_LENGTH);
613 					if (!cwd)
614 						return B_ERROR;
615 					dirFD = _kern_open_dir(-1, cwd);
616 					if (dirFD < 0)
617 						return dirFD;
618 					fdCloser.SetTo(dirFD);
619 				}
620 			} else if (strcmp(leafName, ".") == 0
621 					|| strcmp(leafName, "..") == 0) {
622 				// We have to resolve this to get the entry name. Just open
623 				// the dir and let the next iteration deal with it.
624 				dirFD = _kern_open_dir(-1, path);
625 				if (dirFD < 0)
626 					return dirFD;
627 				fdCloser.SetTo(dirFD);
628 				path = NULL;
629 				continue;
630 			} else {
631 				int parentFD = _kern_open_dir(dirFD, dirPath);
632 				if (parentFD < 0)
633 					return parentFD;
634 				dirFD = parentFD;
635 				fdCloser.SetTo(dirFD);
636 			}
637 			// traverse symlinks, if desired
638 			if (!traverse)
639 				break;
640 			struct stat st;
641 			error = _kern_read_stat(dirFD, leafName, false, &st,
642 				sizeof(struct stat));
643 			if (error == B_ENTRY_NOT_FOUND && !requireConcrete) {
644 				// that's fine -- the entry is abstract and was not target of
645 				// a symlink we resolved
646 				break;
647 			}
648 			if (error != B_OK)
649 				return error;
650 			// the entry is concrete
651 			if (!S_ISLNK(st.st_mode))
652 				break;
653 			requireConcrete = true;
654 			// we need to traverse the symlink
655 			if (--linkLimit < 0)
656 				return B_LINK_LIMIT;
657 			size_t bufferSize = B_PATH_NAME_LENGTH - 1;
658 			error = _kern_read_link(dirFD, leafName, tmpPath, &bufferSize);
659 			if (error < 0)
660 				return error;
661 			tmpPath[bufferSize] = '\0';
662 			path = tmpPath;
663 			// next round...
664 		}
665 	}
666 
667 	// set close on exec flag on dir FD
668 	fcntl(dirFD, F_SETFD, FD_CLOEXEC);
669 
670 	// set the result
671 	status_t error = _SetName(leafName);
672 	if (error != B_OK)
673 		return error;
674 	fdCloser.Detach();
675 	fDirFd = dirFD;
676 	return B_OK;
677 }
678 
679 
680 /*!	Handles string allocation, deallocation, and copying for the
681 	leaf name of the entry.
682 
683 	\param name The leaf \a name of the entry.
684 
685 	\returns A status code.
686 	\retval B_OK Everything went fine.
687 	\retval B_BAD_VALUE \a name is \c NULL.
688 	\retval B_NO_MEMORY Ran out of memory trying to allocate \a name.
689 */
690 status_t
691 BEntry::_SetName(const char* name)
692 {
693 	if (name == NULL)
694 		return B_BAD_VALUE;
695 
696 	free(fName);
697 
698 	fName = strdup(name);
699 	if (fName == NULL)
700 		return B_NO_MEMORY;
701 
702 	return B_OK;
703 }
704 
705 
706 /*!	Renames the entry referred to by this object to the location
707 	specified by \a target.
708 
709 	If an entry exists at the target location, the method fails, unless
710 	\a clobber is \c true, in which case that entry is overwritten (doesn't
711 	work for non-empty directories, though).
712 
713 	If the operation was successful, this entry is made a clone of the
714 	supplied one and the supplied one is uninitialized.
715 
716 	\param target The entry specifying the target location.
717 	\param clobber If \c true, the an entry existing at the target location
718 		   will be overwritten.
719 
720 	\return \c B_OK, if everything went fine, another error code otherwise.
721 */
722 status_t
723 BEntry::_Rename(BEntry& target, bool clobber)
724 {
725 	// check, if there's an entry in the way
726 	if (!clobber && target.Exists())
727 		return B_FILE_EXISTS;
728 	// rename
729 	status_t error = _kern_rename(fDirFd, fName, target.fDirFd, target.fName);
730 	if (error == B_OK) {
731 		Unset();
732 		fCStatus = target.fCStatus;
733 		fDirFd = target.fDirFd;
734 		fName = target.fName;
735 		target.fCStatus = B_NO_INIT;
736 		target.fDirFd = -1;
737 		target.fName = NULL;
738 	}
739 	return error;
740 }
741 
742 
743 /*!	Debugging function, dumps the given entry to stdout.
744 
745 	\param name A pointer to a string to be printed along with the dump for
746 		   identification purposes.
747 */
748 void
749 BEntry::_Dump(const char* name)
750 {
751 	if (name != NULL) {
752 		printf("------------------------------------------------------------\n");
753 		printf("%s\n", name);
754 		printf("------------------------------------------------------------\n");
755 	}
756 
757 	printf("fCStatus == %" B_PRId32 "\n", fCStatus);
758 
759 	struct stat st;
760 	if (fDirFd != -1
761 		&& _kern_read_stat(fDirFd, NULL, false, &st,
762 				sizeof(struct stat)) == B_OK) {
763 		printf("dir.device == %" B_PRIdDEV "\n", st.st_dev);
764 		printf("dir.inode  == %" B_PRIdINO "\n", st.st_ino);
765 	} else {
766 		printf("dir == NullFd\n");
767 	}
768 
769 	printf("leaf == '%s'\n", fName);
770 	printf("\n");
771 
772 }
773 
774 
775 status_t
776 BEntry::_GetStat(struct stat* st) const
777 {
778 	if (fCStatus != B_OK)
779 		return B_NO_INIT;
780 
781 	return _kern_read_stat(fDirFd, fName, false, st, sizeof(struct stat));
782 }
783 
784 
785 status_t
786 BEntry::_GetStat(struct stat_beos* st) const
787 {
788 	struct stat newStat;
789 	status_t error = _GetStat(&newStat);
790 	if (error != B_OK)
791 		return error;
792 
793 	convert_to_stat_beos(&newStat, st);
794 	return B_OK;
795 }
796 
797 
798 // #pragma mark -
799 
800 
801 status_t
802 get_ref_for_path(const char* path, entry_ref* ref)
803 {
804 	status_t error = path && ref ? B_OK : B_BAD_VALUE;
805 	if (error == B_OK) {
806 		BEntry entry(path);
807 		error = entry.InitCheck();
808 		if (error == B_OK)
809 			error = entry.GetRef(ref);
810 	}
811 	return error;
812 }
813 
814 
815 bool
816 operator<(const entry_ref& a, const entry_ref& b)
817 {
818 	return (a.device < b.device
819 		|| (a.device == b.device
820 			&& (a.directory < b.directory
821 			|| (a.directory == b.directory
822 				&& ((a.name == NULL && b.name != NULL)
823 				|| (a.name != NULL && b.name != NULL
824 					&& strcmp(a.name, b.name) < 0))))));
825 }
826 
827 
828 // #pragma mark - symbol versions
829 
830 
831 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
832 #	if __GNUC__ == 2	// gcc 2
833 
834 	B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat",
835 		"GetStat__C6BEntryP4stat@@LIBBE_TEST");
836 
837 #	else	// gcc 4
838 
839 	// Haiku GetStat()
840 	B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat",
841 		"_ZNK6BEntry7GetStatEP4stat@@LIBBE_TEST");
842 
843 #	endif	// gcc 4
844 #else	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
845 #	if __GNUC__ == 2	// gcc 2
846 
847 	// BeOS compatible GetStat()
848 	B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP9stat_beos",
849 		"GetStat__C6BEntryP4stat@LIBBE_BASE");
850 
851 	// Haiku GetStat()
852 	B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat",
853 		"GetStat__C6BEntryP4stat@@LIBBE_1_ALPHA1");
854 
855 #	else	// gcc 4
856 
857 	// BeOS compatible GetStat()
858 	B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP9stat_beos",
859 		"_ZNK6BEntry7GetStatEP4stat@LIBBE_BASE");
860 
861 	// Haiku GetStat()
862 	B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat",
863 		"_ZNK6BEntry7GetStatEP4stat@@LIBBE_1_ALPHA1");
864 
865 #	endif	// gcc 4
866 #endif	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
867