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