xref: /haiku/src/kits/storage/Entry.cpp (revision 5c6260dc232fcb2d4d5d1103c1623dba9663b753)
1 /*
2  * Copyright 2002-2009, 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 /*!
12 	\file Entry.cpp
13 	BEntry and entry_ref implementations.
14 */
15 
16 
17 #include <Entry.h>
18 
19 #include <fcntl.h>
20 #include <new>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include <compat/sys/stat.h>
27 
28 #include <Directory.h>
29 #include <Path.h>
30 #include <SymLink.h>
31 
32 #include <syscalls.h>
33 
34 #include "storage_support.h"
35 
36 
37 using namespace std;
38 
39 
40 //	#pragma mark - struct entry_ref
41 
42 
43 /*! \struct entry_ref
44 	\brief A filesystem entry represented as a name in a concrete directory.
45 
46 	entry_refs may refer to pre-existing (concrete) files, as well as
47 	non-existing (abstract) files. However, the parent directory of the file
48 	\b must exist.
49 
50 	The result of this dichotomy is a blending of the persistence gained by
51 	referring to entries with a reference to their internal filesystem node and
52 	the flexibility gained by referring to entries by name.
53 
54 	For example, if the directory in which the entry resides (or a directory
55 	further up in the hierarchy) is moved or renamed, the entry_ref will still
56 	refer to the correct file (whereas a pathname to the previous location of
57 	the file would now be invalid).
58 
59 	On the other hand, say that the entry_ref refers to a concrete file. If the
60 	file itself is renamed, the entry_ref now refers to an abstract file with
61 	the old name (the upside in this case is that abstract entries may be
62 	represented by entry_refs without preallocating an internal filesystem node
63 	for them).
64 */
65 
66 
67 /*!	Creates an unitialized entry_ref.
68 */
69 entry_ref::entry_ref()
70 	:
71 	device((dev_t)-1),
72 	directory((ino_t)-1),
73 	name(NULL)
74 {
75 }
76 
77 
78 /*! \brief Creates an entry_ref initialized to the given file name in the given
79 	directory on the given device.
80 
81 	\p name may refer to either a pre-existing file in the given directory, or
82 	a non-existent file. No explicit checking is done to verify validity of the
83 	given arguments, but later use of the entry_ref will fail if \p dev is not
84 	a valid device or \p dir is a not a directory on \p dev.
85 
86 	\param dev the device on which the entry's parent directory resides
87 	\param dir the directory in which the entry resides
88 	\param name the leaf name of the entry, which is not required to exist
89 */
90 entry_ref::entry_ref(dev_t dev, ino_t dir, const char* name)
91 	:
92 	device(dev),
93 	directory(dir),
94 	name(NULL)
95 {
96 	set_name(name);
97 }
98 
99 
100 /*! \brief Creates a copy of the given entry_ref.
101 
102 	\param ref a reference to an entry_ref to copy
103 */
104 entry_ref::entry_ref(const entry_ref& ref)
105 	:
106 	device(ref.device),
107 	directory(ref.directory),
108 	name(NULL)
109 {
110 	set_name(ref.name);
111 }
112 
113 
114 /*!	\brief Destroys the object and frees the storage allocated for the leaf
115 		name, if necessary.
116 */
117 entry_ref::~entry_ref()
118 {
119 	free(name);
120 }
121 
122 
123 /*! \brief Set the entry_ref's leaf name, freeing the storage allocated for any
124 		previous name and then making a copy of the new name.
125 
126 	\param name pointer to a null-terminated string containing the new name for
127 	the entry. May be \c NULL.
128 */
129 status_t
130 entry_ref::set_name(const char* name)
131 {
132 	free(this->name);
133 
134 	if (name == NULL) {
135 		this->name = NULL;
136 	} else {
137 		this->name = strdup(name);
138 		if (!this->name)
139 			return B_NO_MEMORY;
140 	}
141 
142 	return B_OK;
143 }
144 
145 
146 /*! \brief Compares the entry_ref with another entry_ref, returning true if
147 		they are equal.
148 	\return
149 	- \c true - The entry_refs are equal
150 	- \c false - The entry_refs are not equal
151 */
152 bool
153 entry_ref::operator==(const entry_ref& ref) const
154 {
155 	return (device == ref.device
156 		&& directory == ref.directory
157 		&& (name == ref.name
158 			|| (name != NULL && ref.name != NULL
159 				&& strcmp(name, ref.name) == 0)));
160 }
161 
162 
163 /*! \brief Compares the entry_ref with another entry_ref, returning true if
164 		they are not equal.
165 	\return
166 	- \c true - The entry_refs are not equal
167 	- \c false - The entry_refs are equal
168 */
169 bool
170 entry_ref::operator!=(const entry_ref& ref) const
171 {
172 	return !(*this == ref);
173 }
174 
175 
176 /*! \brief Makes the entry_ref a copy of the entry_ref specified by \a ref.
177 	\param ref the entry_ref to copy
178 	\return
179 	- A reference to the copy
180 */
181 entry_ref&
182 entry_ref::operator=(const entry_ref& ref)
183 {
184 	if (this == &ref)
185 		return *this;
186 
187 	device = ref.device;
188 	directory = ref.directory;
189 	set_name(ref.name);
190 	return *this;
191 }
192 
193 
194 /*!	\var dev_t entry_ref::device
195 	\brief The device id of the storage device on which the entry resides
196 
197 */
198 
199 /*!	\var ino_t entry_ref::directory
200 	\brief The inode number of the directory in which the entry resides
201 */
202 
203 /*!	\var char *entry_ref::name
204 	\brief The leaf name of the entry
205 */
206 
207 
208 //	#pragma mark - BEntry
209 
210 
211 /*!	\class BEntry
212 	\brief A location in the filesystem
213 
214 	The BEntry class defines objects that represent "locations" in the file system
215 	hierarchy.  Each location (or entry) is given as a name within a directory. For
216 	example, when you create a BEntry thus:
217 
218 	\code
219 	BEntry entry("/boot/home/fido");
220 	\endcode
221 
222 	...you're telling the BEntry object to represent the location of the file
223 	called fido within the directory \c "/boot/home".
224 
225 	\author <a href='mailto:bonefish@users.sf.net'>Ingo Weinhold</a>
226 	\author <a href='mailto:tylerdauwalder@users.sf.net'>Tyler Dauwalder</a>
227 	\author <a href='mailto:scusack@users.sf.net'>Simon Cusack</a>
228 
229 	\version 0.0.0
230 */
231 
232 
233 /*!	\brief Creates an uninitialized BEntry object.
234 	Should be followed by a	call to one of the SetTo functions, or an
235 	assignment:
236 	- SetTo(const BDirectory*, const char*, bool)
237 	- SetTo(const entry_ref*, bool)
238 	- SetTo(const char*, bool)
239 	- operator=(const BEntry&)
240 */
241 BEntry::BEntry()
242 	:
243 	fDirFd(-1),
244 	fName(NULL),
245 	fCStatus(B_NO_INIT)
246 {
247 }
248 
249 
250 /*!	\brief Creates a BEntry initialized to the given directory and path
251 		combination.
252 
253 	If traverse is true and \c dir/path refers to a symlink, the BEntry will
254 	refer to the linked file; if false,	the BEntry will refer to the symlink
255 	itself.
256 
257 	\param dir directory in which \a path resides
258 	\param path relative path reckoned off of \a dir
259 	\param traverse whether or not to traverse symlinks
260 	\see SetTo(const BDirectory*, const char *, bool)
261 */
262 BEntry::BEntry(const BDirectory* dir, const char* path, bool traverse)
263 	:
264 	fDirFd(-1),
265 	fName(NULL),
266 	fCStatus(B_NO_INIT)
267 {
268 	SetTo(dir, path, traverse);
269 }
270 
271 
272 /*!	\brief Creates a BEntry for the file referred to by the given entry_ref.
273 	If traverse is true and \a ref refers to a symlink, the BEntry
274 	will refer to the linked file; if false, the BEntry will refer
275 	to the symlink itself.
276 
277 	\param ref the entry_ref referring to the given file
278 	\param traverse whether or not symlinks are to be traversed
279 	\see SetTo(const entry_ref*, bool)
280 */
281 BEntry::BEntry(const entry_ref* ref, bool traverse)
282 	:
283 	fDirFd(-1),
284 	fName(NULL),
285 	fCStatus(B_NO_INIT)
286 {
287 	SetTo(ref, traverse);
288 }
289 
290 
291 /*!	\brief Creates a BEntry initialized to the given path.
292 
293 	If \a path is relative, it will be reckoned off the current working
294 	directory. If \a path refers to a symlink and traverse is true, the BEntry
295 	will refer to the linked file. If traverse is false, the BEntry will refer
296 	to the symlink itself.
297 
298 	\param path the file of interest
299 	\param traverse whether or not symlinks are to be traversed
300 	\see SetTo(const char*, bool)
301 */
302 BEntry::BEntry(const char* path, bool traverse)
303 	:
304 	fDirFd(-1),
305 	fName(NULL),
306 	fCStatus(B_NO_INIT)
307 {
308 	SetTo(path, traverse);
309 }
310 
311 
312 /*!	\brief Creates a copy of the given BEntry.
313 	\param entry the entry to be copied
314 	\see operator=(const BEntry&)
315 */
316 BEntry::BEntry(const BEntry& entry)
317 	:
318 	fDirFd(-1),
319 	fName(NULL),
320 	fCStatus(B_NO_INIT)
321 {
322 	*this = entry;
323 }
324 
325 
326 /*!	\brief Frees all of the BEntry's allocated resources.
327 	\see Unset()
328 */
329 BEntry::~BEntry()
330 {
331 	Unset();
332 }
333 
334 
335 /*!	\brief Returns the result of the most recent construction or SetTo() call.
336 	\return
337 		- \c B_OK Success
338 		- \c B_NO_INIT The object has been Unset() or is uninitialized
339 		- <code>some error code</code>
340 */
341 status_t
342 BEntry::InitCheck() const
343 {
344 	return fCStatus;
345 }
346 
347 
348 /*!	\brief Returns true if the Entry exists in the filesytem, false otherwise.
349 	\return
350 		- \c true - The entry exists
351 		- \c false - The entry does not exist
352 */
353 bool
354 BEntry::Exists() const
355 {
356 	// just stat the beast
357 	struct stat st;
358 	return GetStat(&st) == B_OK;
359 }
360 
361 
362 /*!	\fn status_t BEntry::GetStat(struct stat *result) const
363 	\brief Fills in a stat structure for the entry. The information is copied
364 		into the \c stat structure pointed to by \a result.
365 
366 	\b NOTE: The BStatable object does not cache the stat structure; every time
367 	you call GetStat(), fresh stat information is retrieved.
368 
369 	\param result pointer to a pre-allocated structure into which the stat
370 		information will be copied
371 	\return
372 	- \c B_OK - Success
373 	- "error code" - Failure
374 */
375 
376 
377 /*! \brief Reinitializes the BEntry to the path or directory path combination,
378 	resolving symlinks if traverse is true
379 
380 	\return
381 	- \c B_OK - Success
382 	- "error code" - Failure
383 */
384 status_t
385 BEntry::SetTo(const BDirectory* dir, const char* path, bool traverse)
386 {
387 	// check params
388 	if (!dir)
389 		return (fCStatus = B_BAD_VALUE);
390 	if (path && path[0] == '\0')	// R5 behaviour
391 		path = NULL;
392 
393 	// if path is absolute, let the path-only SetTo() do the job
394 	if (BPrivate::Storage::is_absolute_path(path))
395 		return SetTo(path, traverse);
396 
397 	Unset();
398 
399 	if (dir->InitCheck() != B_OK)
400 		fCStatus = B_BAD_VALUE;
401 
402 	// dup() the dir's FD and let set() do the rest
403 	int dirFD = _kern_dup(dir->get_fd());
404 	if (dirFD < 0)
405 		return (fCStatus = dirFD);
406 	return (fCStatus = _SetTo(dirFD, path, traverse));
407 }
408 
409 
410 /*! \brief Reinitializes the BEntry to the entry_ref, resolving symlinks if
411 		traverse is true.
412 
413 	\return
414 	- \c B_OK - Success
415 	- "error code" - Failure
416 */
417 status_t
418 BEntry::SetTo(const entry_ref* ref, bool traverse)
419 {
420 	Unset();
421 	if (ref == NULL)
422 		return (fCStatus = B_BAD_VALUE);
423 
424 	// if ref-name is absolute, let the path-only SetTo() do the job
425 	if (BPrivate::Storage::is_absolute_path(ref->name))
426 		return SetTo(ref->name, traverse);
427 
428 	// open the directory and let set() do the rest
429 	int dirFD = _kern_open_dir_entry_ref(ref->device, ref->directory, NULL);
430 	if (dirFD < 0)
431 		return (fCStatus = dirFD);
432 	return (fCStatus = _SetTo(dirFD, ref->name, traverse));
433 }
434 
435 
436 /*! \brief Reinitializes the BEntry object to the path, resolving symlinks if
437 		traverse is true.
438 
439 	\return
440 	- \c B_OK - Success
441 	- "error code" - Failure
442 */
443 status_t
444 BEntry::SetTo(const char* path, bool traverse)
445 {
446 	Unset();
447 	// check the argument
448 	if (!path)
449 		return (fCStatus = B_BAD_VALUE);
450 	return (fCStatus = _SetTo(-1, path, traverse));
451 }
452 
453 
454 /*! \brief Reinitializes the BEntry to an uninitialized BEntry object */
455 void
456 BEntry::Unset()
457 {
458 	// Close the directory
459 	if (fDirFd >= 0)
460 		_kern_close(fDirFd);
461 
462 	// Free our leaf name
463 	free(fName);
464 
465 	fDirFd = -1;
466 	fName = NULL;
467 	fCStatus = B_NO_INIT;
468 }
469 
470 
471 /*! \brief Gets an entry_ref structure for the BEntry.
472 
473 	\param ref pointer to a preallocated entry_ref into which the result is
474 		copied
475 	\return
476 	- \c B_OK - Success
477 	- "error code" - Failure
478 
479  */
480 status_t
481 BEntry::GetRef(entry_ref* ref) const
482 {
483 	if (fCStatus != B_OK)
484 		return B_NO_INIT;
485 
486 	if (ref == NULL)
487 		return B_BAD_VALUE;
488 
489 	struct stat st;
490 	status_t error = _kern_read_stat(fDirFd, NULL, false, &st,
491 		sizeof(struct stat));
492 	if (error == B_OK) {
493 		ref->device = st.st_dev;
494 		ref->directory = st.st_ino;
495 		error = ref->set_name(fName);
496 	}
497 	return error;
498 }
499 
500 
501 /*! \brief Gets the path for the BEntry.
502 
503 	\param path pointer to a pre-allocated BPath object into which the result
504 		is stored
505 	\return
506 	- \c B_OK - Success
507 	- "error code" - Failure
508 
509 */
510 status_t
511 BEntry::GetPath(BPath* path) const
512 {
513 	if (fCStatus != B_OK)
514 		return B_NO_INIT;
515 
516 	if (path == NULL)
517 		return B_BAD_VALUE;
518 
519 	return path->SetTo(this);
520 }
521 
522 
523 /*! \brief Gets the parent of the BEntry as another BEntry.
524 
525 	If the function fails, the argument is Unset(). Destructive calls to
526 	GetParent() are allowed, i.e.:
527 
528 	\code
529 	BEntry entry("/boot/home/fido");
530 	status_t err;
531 	char name[B_FILE_NAME_LENGTH];
532 
533 	// Spit out the path components backwards, one at a time.
534 	do {
535 		entry.GetName(name);
536 		printf("> %s\n", name);
537 	} while ((err=entry.GetParent(&entry)) == B_OK);
538 
539 	// Complain for reasons other than reaching the top.
540 	if (err != B_ENTRY_NOT_FOUND)
541 		printf(">> Error: %s\n", strerror(err));
542 	\endcode
543 
544 	will output:
545 
546 	\code
547 	> fido
548 	> home
549 	> boot
550 	> .
551 	\endcode
552 
553 	\param entry pointer to a pre-allocated BEntry object into which the result
554 		is stored
555 	\return
556 	- \c B_OK - Success
557 	- \c B_ENTRY_NOT_FOUND - Attempted to get the parent of the root directory
558 		\c "/"
559 	- "error code" - Failure
560 */
561 status_t BEntry::GetParent(BEntry* entry) const
562 {
563 	// check parameter and initialization
564 	if (fCStatus != B_OK)
565 		return B_NO_INIT;
566 	if (entry == NULL)
567 		return B_BAD_VALUE;
568 
569 	// check whether we are the root directory
570 	// It is sufficient to check whether our leaf name is ".".
571 	if (strcmp(fName, ".") == 0)
572 		return B_ENTRY_NOT_FOUND;
573 
574 	// open the parent directory
575 	char leafName[B_FILE_NAME_LENGTH];
576 	int parentFD = _kern_open_parent_dir(fDirFd, leafName, B_FILE_NAME_LENGTH);
577 	if (parentFD < 0)
578 		return parentFD;
579 
580 	// set close on exec flag on dir FD
581 	fcntl(parentFD, F_SETFD, FD_CLOEXEC);
582 
583 	// init the entry
584 	entry->Unset();
585 	entry->fDirFd = parentFD;
586 	entry->fCStatus = entry->_SetName(leafName);
587 	if (entry->fCStatus != B_OK)
588 		entry->Unset();
589 	return entry->fCStatus;
590 }
591 
592 
593 /*! \brief Gets the parent of the BEntry as a BDirectory.
594 
595 	If the function fails, the argument is Unset().
596 
597 	\param dir pointer to a pre-allocated BDirectory object into which the
598 		result is stored
599 	\return
600 	- \c B_OK - Success
601 	- \c B_ENTRY_NOT_FOUND - Attempted to get the parent of the root directory
602 		\c "/"
603 	- "error code" - Failure
604 */
605 status_t
606 BEntry::GetParent(BDirectory* dir) const
607 {
608 	// check initialization and parameter
609 	if (fCStatus != B_OK)
610 		return B_NO_INIT;
611 	if (dir == NULL)
612 		return B_BAD_VALUE;
613 	// check whether we are the root directory
614 	// It is sufficient to check whether our leaf name is ".".
615 	if (strcmp(fName, ".") == 0)
616 		return B_ENTRY_NOT_FOUND;
617 	// get a node ref for the directory and init it
618 	struct stat st;
619 	status_t error = _kern_read_stat(fDirFd, NULL, false, &st,
620 		sizeof(struct stat));
621 	if (error != B_OK)
622 		return error;
623 	node_ref ref;
624 	ref.device = st.st_dev;
625 	ref.node = st.st_ino;
626 	return dir->SetTo(&ref);
627 	// TODO: This can be optimized: We already have a FD for the directory,
628 	// so we could dup() it and set it on the directory. We just need a private
629 	// API for being able to do that.
630 }
631 
632 
633 /*! \brief Gets the name of the entry's leaf.
634 
635 	\c buffer must be pre-allocated and of sufficient
636 	length to hold the entire string. A length of \c B_FILE_NAME_LENGTH is
637 	recommended.
638 
639 	\param buffer pointer to a pre-allocated string into which the result is
640 		copied
641 	\return
642 	- \c B_OK - Success
643 	- "error code" - Failure
644 */
645 status_t
646 BEntry::GetName(char* buffer) const
647 {
648 	status_t result = B_ERROR;
649 
650 	if (fCStatus != B_OK) {
651 		result = B_NO_INIT;
652 	} else if (buffer == NULL) {
653 		result = B_BAD_VALUE;
654 	} else {
655 		strcpy(buffer, fName);
656 		result = B_OK;
657 	}
658 
659 	return result;
660 }
661 
662 
663 /*! \brief Renames the BEntry to path, replacing an existing entry if clobber
664 		is true.
665 
666 	NOTE: The BEntry must refer to an existing file. If it is abstract, this
667 		method will fail.
668 
669 	\param path Pointer to a string containing the new name for the entry. May
670 		be absolute or relative. If relative, the entry is renamed within its
671 		current directory.
672 	\param clobber If \c false and a file with the name given by \c path already
673 		exists, the method will fail. If \c true and such a file exists, it will
674 		be overwritten.
675 	\return
676 	- \c B_OK - Success
677 	- \c B_ENTRY_EXISTS - The new location is already taken and \c clobber was
678 		\c false
679 	- \c B_ENTRY_NOT_FOUND - Attempted to rename an abstract entry
680 	- "error code" - Failure
681 
682 */
683 status_t
684 BEntry::Rename(const char* path, bool clobber)
685 {
686 	// check parameter and initialization
687 	if (path == NULL)
688 		return B_BAD_VALUE;
689 	if (fCStatus != B_OK)
690 		return B_NO_INIT;
691 	// get an entry representing the target location
692 	BEntry target;
693 	status_t error;
694 	if (BPrivate::Storage::is_absolute_path(path)) {
695 		error = target.SetTo(path);
696 	} else {
697 		int dirFD = _kern_dup(fDirFd);
698 		if (dirFD < 0)
699 			return dirFD;
700 		// init the entry
701 		error = target.fCStatus = target._SetTo(dirFD, path, false);
702 	}
703 	if (error != B_OK)
704 		return error;
705 	return _Rename(target, clobber);
706 }
707 
708 
709 /*! \brief Moves the BEntry to directory or directory+path combination,
710 		replacing an existing entry if clobber is true.
711 
712 	NOTE: The BEntry must refer to an existing file. If it is abstract, this
713 	method will fail.
714 
715 	\param dir Pointer to a pre-allocated BDirectory into which the entry should
716 		be moved.
717 	\param path Optional new leaf name for the entry. May be a simple leaf or a
718 		relative path; either way, \c path is reckoned off of \c dir. If
719 		\c NULL, the entry retains its previous leaf name.
720 	\param clobber If \c false and an entry already exists at the specified
721 		destination, the method will fail. If \c true and such an entry exists,
722 		it will be overwritten.
723 	\return - \c B_OK - Success
724 		- \c B_ENTRY_EXISTS - The new location is already taken and \c clobber
725 			was \c false
726 		- \c B_ENTRY_NOT_FOUND - Attempted to move an abstract entry
727 		- "error code" - Failure
728 */
729 status_t
730 BEntry::MoveTo(BDirectory* dir, const char* path, bool clobber)
731 {
732 	// check parameters and initialization
733 	if (fCStatus != B_OK)
734 		return B_NO_INIT;
735 	if (dir == NULL)
736 		return B_BAD_VALUE;
737 	if (dir->InitCheck() != B_OK)
738 		return B_BAD_VALUE;
739 	// NULL path simply means move without renaming
740 	if (path == NULL)
741 		path = fName;
742 	// get an entry representing the target location
743 	BEntry target;
744 	status_t error = target.SetTo(dir, path);
745 	if (error != B_OK)
746 		return error;
747 	return _Rename(target, clobber);
748 }
749 
750 
751 /*! \brief Removes the entry from the file system.
752 
753 	NOTE: If any file descriptors are open on the file when Remove() is called,
754 	the chunk of data they refer to will continue to exist until all such file
755 	descriptors are closed. The BEntry object, however, becomes abstract and
756 	no longer refers to any actual data in the filesystem.
757 
758 	\return
759 	- B_OK - Success
760 	- "error code" - Failure
761 */
762 status_t
763 BEntry::Remove()
764 {
765 	if (fCStatus != B_OK)
766 		return B_NO_INIT;
767 
768 	if (IsDirectory())
769 		return _kern_remove_dir(fDirFd, fName);
770 
771 	return _kern_unlink(fDirFd, fName);
772 }
773 
774 
775 /*! \brief Returns true if the BEntry and \c item refer to the same entry or
776 		if they are both uninitialized.
777 
778 	\return \c true - Both BEntry objects refer to the same entry or they are
779 		both uninitialzed
780 		\c false - The BEntry objects refer to different entries
781  */
782 bool
783 BEntry::operator==(const BEntry& item) const
784 {
785 	// First check statuses
786 	if (this->InitCheck() != B_OK && item.InitCheck() != B_OK) {
787 		return true;
788 	} else if (this->InitCheck() == B_OK && item.InitCheck() == B_OK) {
789 
790 		// Directories don't compare well directly, so we'll
791 		// compare entry_refs instead
792 		entry_ref ref1, ref2;
793 		if (this->GetRef(&ref1) != B_OK)
794 			return false;
795 		if (item.GetRef(&ref2) != B_OK)
796 			return false;
797 		return (ref1 == ref2);
798 
799 	} else {
800 		return false;
801 	}
802 
803 }
804 
805 
806 /*! \brief Returns false if the BEntry and \c item refer to the same entry or
807 		if they are both uninitialized.
808 
809 	\return \c true - The BEntry objects refer to different entries
810 		\c false - Both BEntry objects refer to the same entry or they are
811 		both uninitialzed
812  */
813 bool
814 BEntry::operator!=(const BEntry& item) const
815 {
816 	return !(*this == item);
817 }
818 
819 
820 /*! \brief Reinitializes the BEntry to be a copy of the argument
821 
822 	\return
823 	- A reference to the copy
824 */
825 BEntry&
826 BEntry::operator=(const BEntry& item)
827 {
828 	if (this == &item)
829 		return *this;
830 
831 	Unset();
832 	if (item.fCStatus == B_OK) {
833 		fDirFd = _kern_dup(item.fDirFd);
834 		if (fDirFd >= 0)
835 			fCStatus = _SetName(item.fName);
836 		else
837 			fCStatus = fDirFd;
838 
839 		if (fCStatus != B_OK)
840 			Unset();
841 	}
842 
843 	return *this;
844 }
845 
846 
847 void BEntry::_PennyEntry1(){}
848 void BEntry::_PennyEntry2(){}
849 void BEntry::_PennyEntry3(){}
850 void BEntry::_PennyEntry4(){}
851 void BEntry::_PennyEntry5(){}
852 void BEntry::_PennyEntry6(){}
853 
854 
855 /*! \brief Updates the BEntry with the data from the stat structure according
856 		to the mask.
857 */
858 status_t
859 BEntry::set_stat(struct stat& st, uint32 what)
860 {
861 	if (fCStatus != B_OK)
862 		return B_FILE_ERROR;
863 
864 	return _kern_write_stat(fDirFd, fName, false, &st, sizeof(struct stat),
865 		what);
866 }
867 
868 
869 /*! Sets the Entry to point to the entry specified by the path \a path relative
870 	to the given directory. If \a traverse is \c true and the given entry is a
871 	symlink, the object is recursively set to point to the entry pointed to by
872 	the symlink.
873 
874 	If \a path is an absolute path, \a dirFD is ignored.
875 	If \a dirFD is -1, path is considered relative to the current directory
876 	(unless it is an absolute path, that is).
877 
878 	The ownership of the file descriptor \a dirFD is transferred to the
879 	function, regardless of whether it succeeds or fails. The caller must not
880 	close the FD afterwards.
881 
882 	\param dirFD File descriptor of a directory relative to which path is to
883 		   be considered. May be -1, when the current directory shall be
884 		   considered.
885 	\param path Pointer to a path relative to the given directory.
886 	\param traverse If \c true and the given entry is a symlink, the object is
887 		   recursively set to point to the entry linked to by the symlink.
888 	\return
889 	- B_OK - Success
890 	- "error code" - Failure
891 */
892 status_t
893 BEntry::_SetTo(int dirFD, const char* path, bool traverse)
894 {
895 	bool requireConcrete = false;
896 	FDCloser fdCloser(dirFD);
897 	char tmpPath[B_PATH_NAME_LENGTH];
898 	char leafName[B_FILE_NAME_LENGTH];
899 	int32 linkLimit = B_MAX_SYMLINKS;
900 	while (true) {
901 		if (!path || strcmp(path, ".") == 0) {
902 			// "."
903 			// if no dir FD is supplied, we need to open the current directory
904 			// first
905 			if (dirFD < 0) {
906 				dirFD = _kern_open_dir(-1, ".");
907 				if (dirFD < 0)
908 					return dirFD;
909 				fdCloser.SetTo(dirFD);
910 			}
911 			// get the parent directory
912 			int parentFD = _kern_open_parent_dir(dirFD, leafName,
913 				B_FILE_NAME_LENGTH);
914 			if (parentFD < 0)
915 				return parentFD;
916 			dirFD = parentFD;
917 			fdCloser.SetTo(dirFD);
918 			break;
919 		} else if (strcmp(path, "..") == 0) {
920 			// ".."
921 			// open the parent directory
922 			int parentFD = _kern_open_dir(dirFD, "..");
923 			if (parentFD < 0)
924 				return parentFD;
925 			dirFD = parentFD;
926 			fdCloser.SetTo(dirFD);
927 			// get the parent's parent directory
928 			parentFD = _kern_open_parent_dir(dirFD, leafName,
929 				B_FILE_NAME_LENGTH);
930 			if (parentFD < 0)
931 				return parentFD;
932 			dirFD = parentFD;
933 			fdCloser.SetTo(dirFD);
934 			break;
935 		} else {
936 			// an ordinary path; analyze it
937 			char dirPath[B_PATH_NAME_LENGTH];
938 			status_t error = BPrivate::Storage::parse_path(path, dirPath,
939 				leafName);
940 			if (error != B_OK)
941 				return error;
942 			// special case: root directory ("/")
943 			if (leafName[0] == '\0' && dirPath[0] == '/')
944 				strcpy(leafName, ".");
945 			if (leafName[0] == '\0') {
946 				// the supplied path is already a leaf
947 				error = BPrivate::Storage::check_entry_name(dirPath);
948 				if (error != B_OK)
949 					return error;
950 				strcpy(leafName, dirPath);
951 				// if no directory was given, we need to open the current dir
952 				// now
953 				if (dirFD < 0) {
954 					char* cwd = getcwd(tmpPath, B_PATH_NAME_LENGTH);
955 					if (!cwd)
956 						return B_ERROR;
957 					dirFD = _kern_open_dir(-1, cwd);
958 					if (dirFD < 0)
959 						return dirFD;
960 					fdCloser.SetTo(dirFD);
961 				}
962 			} else if (strcmp(leafName, ".") == 0
963 					|| strcmp(leafName, "..") == 0) {
964 				// We have to resolve this to get the entry name. Just open
965 				// the dir and let the next iteration deal with it.
966 				dirFD = _kern_open_dir(-1, path);
967 				if (dirFD < 0)
968 					return dirFD;
969 				fdCloser.SetTo(dirFD);
970 				path = NULL;
971 				continue;
972 			} else {
973 				int parentFD = _kern_open_dir(dirFD, dirPath);
974 				if (parentFD < 0)
975 					return parentFD;
976 				dirFD = parentFD;
977 				fdCloser.SetTo(dirFD);
978 			}
979 			// traverse symlinks, if desired
980 			if (!traverse)
981 				break;
982 			struct stat st;
983 			error = _kern_read_stat(dirFD, leafName, false, &st,
984 				sizeof(struct stat));
985 			if (error == B_ENTRY_NOT_FOUND && !requireConcrete) {
986 				// that's fine -- the entry is abstract and was not target of
987 				// a symlink we resolved
988 				break;
989 			}
990 			if (error != B_OK)
991 				return error;
992 			// the entry is concrete
993 			if (!S_ISLNK(st.st_mode))
994 				break;
995 			requireConcrete = true;
996 			// we need to traverse the symlink
997 			if (--linkLimit < 0)
998 				return B_LINK_LIMIT;
999 			size_t bufferSize = B_PATH_NAME_LENGTH - 1;
1000 			error = _kern_read_link(dirFD, leafName, tmpPath, &bufferSize);
1001 			if (error < 0)
1002 				return error;
1003 			tmpPath[bufferSize] = '\0';
1004 			path = tmpPath;
1005 			// next round...
1006 		}
1007 	}
1008 
1009 	// set close on exec flag on dir FD
1010 	fcntl(dirFD, F_SETFD, FD_CLOEXEC);
1011 
1012 	// set the result
1013 	status_t error = _SetName(leafName);
1014 	if (error != B_OK)
1015 		return error;
1016 	fdCloser.Detach();
1017 	fDirFd = dirFD;
1018 	return B_OK;
1019 }
1020 
1021 
1022 /*! \brief Handles string allocation, deallocation, and copying for the entry's
1023 		leaf name.
1024 
1025 	\return
1026 	- B_OK - Success
1027 	- "error code" - Failure
1028 */
1029 status_t
1030 BEntry::_SetName(const char* name)
1031 {
1032 	if (name == NULL)
1033 		return B_BAD_VALUE;
1034 
1035 	free(fName);
1036 
1037 	fName = strdup(name);
1038 	if (fName == NULL)
1039 		return B_NO_MEMORY;
1040 
1041 	return B_OK;
1042 }
1043 
1044 
1045 /*!	\brief Renames the entry referred to by this object to the location
1046 		   specified by \a target.
1047 
1048 	If an entry exists at the target location, the method fails, unless
1049 	\a clobber is \c true, in which case that entry is overwritten (doesn't
1050 	work for non-empty directories, though).
1051 
1052 	If the operation was successful, this entry is made a clone of the
1053 	supplied one and the supplied one is uninitialized.
1054 
1055 	\param target The entry specifying the target location.
1056 	\param clobber If \c true, the an entry existing at the target location
1057 		   will be overwritten.
1058 	\return \c B_OK, if everything went fine, another error code otherwise.
1059 */
1060 status_t
1061 BEntry::_Rename(BEntry& target, bool clobber)
1062 {
1063 	// check, if there's an entry in the way
1064 	if (!clobber && target.Exists())
1065 		return B_FILE_EXISTS;
1066 	// rename
1067 	status_t error = _kern_rename(fDirFd, fName, target.fDirFd, target.fName);
1068 	if (error == B_OK) {
1069 		Unset();
1070 		fCStatus = target.fCStatus;
1071 		fDirFd = target.fDirFd;
1072 		fName = target.fName;
1073 		target.fCStatus = B_NO_INIT;
1074 		target.fDirFd = -1;
1075 		target.fName = NULL;
1076 	}
1077 	return error;
1078 }
1079 
1080 
1081 /*! Debugging function, dumps the given entry to stdout.
1082 
1083 	\param name	Pointer to a string to be printed along with the dump for
1084 		identification purposes.
1085 */
1086 void
1087 BEntry::_Dump(const char* name)
1088 {
1089 	if (name != NULL) {
1090 		printf("------------------------------------------------------------\n");
1091 		printf("%s\n", name);
1092 		printf("------------------------------------------------------------\n");
1093 	}
1094 
1095 	printf("fCStatus == %ld\n", fCStatus);
1096 
1097 	struct stat st;
1098 	if (fDirFd != -1
1099 		&& _kern_read_stat(fDirFd, NULL, false, &st,
1100 				sizeof(struct stat)) == B_OK) {
1101 		printf("dir.device == %ld\n", st.st_dev);
1102 		printf("dir.inode  == %lld\n", st.st_ino);
1103 	} else {
1104 		printf("dir == NullFd\n");
1105 	}
1106 
1107 	printf("leaf == '%s'\n", fName);
1108 	printf("\n");
1109 
1110 }
1111 
1112 
1113 status_t
1114 BEntry::_GetStat(struct stat* st) const
1115 {
1116 	if (fCStatus != B_OK)
1117 		return B_NO_INIT;
1118 
1119 	return _kern_read_stat(fDirFd, fName, false, st, sizeof(struct stat));
1120 }
1121 
1122 
1123 status_t
1124 BEntry::_GetStat(struct stat_beos* st) const
1125 {
1126 	struct stat newStat;
1127 	status_t error = _GetStat(&newStat);
1128 	if (error != B_OK)
1129 		return error;
1130 
1131 	convert_to_stat_beos(&newStat, st);
1132 	return B_OK;
1133 }
1134 
1135 
1136 // #pragma mark -
1137 
1138 
1139 /*!	\brief Returns an entry_ref for a given path.
1140 	\param path The path name referring to the entry
1141 	\param ref The entry_ref structure to be filled in
1142 	\return
1143 	- \c B_OK - Everything went fine.
1144 	- \c B_BAD_VALUE - \c NULL \a path or \a ref.
1145 	- \c B_ENTRY_NOT_FOUND - A (non-leaf) path component does not exist.
1146 	- \c B_NO_MEMORY - Insufficient memory for successful completion.
1147 */
1148 status_t
1149 get_ref_for_path(const char* path, entry_ref* ref)
1150 {
1151 	status_t error = path && ref ? B_OK : B_BAD_VALUE;
1152 	if (error == B_OK) {
1153 		BEntry entry(path);
1154 		error = entry.InitCheck();
1155 		if (error == B_OK)
1156 			error = entry.GetRef(ref);
1157 	}
1158 	return error;
1159 }
1160 
1161 
1162 /*!	\brief Returns whether an entry is less than another.
1163 	The components are compared in order \c device, \c directory, \c name.
1164 	A \c NULL \c name is less than any non-null name.
1165 
1166 	\return
1167 	- true - a < b
1168 	- false - a >= b
1169 */
1170 bool
1171 operator<(const entry_ref& a, const entry_ref& b)
1172 {
1173 	return (a.device < b.device
1174 		|| (a.device == b.device
1175 			&& (a.directory < b.directory
1176 			|| (a.directory == b.directory
1177 				&& ((a.name == NULL && b.name != NULL)
1178 				|| (a.name != NULL && b.name != NULL
1179 					&& strcmp(a.name, b.name) < 0))))));
1180 }
1181 
1182 
1183 // #pragma mark - symbol versions
1184 
1185 
1186 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
1187 #	if __GNUC__ == 2	// gcc 2
1188 
1189 	B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat",
1190 		"GetStat__C6BEntryP4stat@@LIBBE_TEST");
1191 
1192 #	else	// gcc 4
1193 
1194 	// Haiku GetStat()
1195 	B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat",
1196 		"_ZNK6BEntry7GetStatEP4stat@@LIBBE_TEST");
1197 
1198 #	endif	// gcc 4
1199 #else	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
1200 #	if __GNUC__ == 2	// gcc 2
1201 
1202 	// BeOS compatible GetStat()
1203 	B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP9stat_beos",
1204 		"GetStat__C6BEntryP4stat@LIBBE_BASE");
1205 
1206 	// Haiku GetStat()
1207 	B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat",
1208 		"GetStat__C6BEntryP4stat@@LIBBE_1_ALPHA1");
1209 
1210 #	else	// gcc 4
1211 
1212 	// BeOS compatible GetStat()
1213 	B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP9stat_beos",
1214 		"_ZNK6BEntry7GetStatEP4stat@LIBBE_BASE");
1215 
1216 	// Haiku GetStat()
1217 	B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat",
1218 		"_ZNK6BEntry7GetStatEP4stat@@LIBBE_1_ALPHA1");
1219 
1220 #	endif	// gcc 4
1221 #endif	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
1222