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