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