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