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