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