xref: /haiku/src/kits/storage/Entry.cpp (revision d3d8b26997fac34a84981e6d2b649521de2cc45a)
1 //----------------------------------------------------------------------
2 //  This software is part of the Haiku distribution and is covered
3 //  by the MIT license.
4 //---------------------------------------------------------------------
5 /*!
6 	\file Entry.cpp
7 	BEntry and entry_ref implementations.
8 */
9 
10 #include <fcntl.h>
11 #include <new>
12 #include <stdio.h>
13 #include <string.h>
14 #include <unistd.h>
15 
16 #include <Directory.h>
17 #include <Entry.h>
18 #include <Path.h>
19 #include <SymLink.h>
20 #include "storage_support.h"
21 
22 #include <syscalls.h>
23 
24 #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 
527 	// check whether we are the root directory
528 	// It is sufficient to check whether our leaf name is ".".
529 	if (strcmp(fName, ".") == 0)
530 		return B_ENTRY_NOT_FOUND;
531 
532 	// open the parent directory
533 	char leafName[B_FILE_NAME_LENGTH];
534 	int parentFD = _kern_open_parent_dir(fDirFd, leafName, B_FILE_NAME_LENGTH);
535 	if (parentFD < 0)
536 		return parentFD;
537 
538 	// set close on exec flag on dir FD
539 	fcntl(parentFD, F_SETFD, FD_CLOEXEC);
540 
541 	// init the entry
542 	entry->Unset();
543 	entry->fDirFd = parentFD;
544 	entry->fCStatus = entry->set_name(leafName);
545 	if (entry->fCStatus != B_OK)
546 		entry->Unset();
547 	return entry->fCStatus;
548 }
549 
550 /*! \brief Gets the parent of the BEntry as a BDirectory.
551 
552 	If the function fails, the argument is Unset().
553 
554 	\param dir pointer to a pre-allocated BDirectory object into which the result is stored
555 	\return
556 	- \c B_OK - Success
557 	- \c B_ENTRY_NOT_FOUND - Attempted to get the parent of the root directory \c "/"
558 	- "error code" - Failure
559 */
560 status_t
561 BEntry::GetParent(BDirectory *dir) const
562 {
563 	// check initialization and parameter
564 	if (fCStatus != B_OK)
565 		return B_NO_INIT;
566 	if (dir == NULL)
567 		return B_BAD_VALUE;
568 	// check whether we are the root directory
569 	// It is sufficient to check whether our leaf name is ".".
570 	if (strcmp(fName, ".") == 0)
571 		return B_ENTRY_NOT_FOUND;
572 	// get a node ref for the directory and init it
573 	struct stat st;
574 	status_t error = _kern_read_stat(fDirFd, NULL, false, &st,
575 		sizeof(struct stat));
576 	if (error != B_OK)
577 		return error;
578 	node_ref ref;
579 	ref.device = st.st_dev;
580 	ref.node = st.st_ino;
581 	return dir->SetTo(&ref);
582 	// TODO: This can be optimized: We already have a FD for the directory,
583 	// so we could dup() it and set it on the directory. We just need a private
584 	// API for being able to do that.
585 }
586 
587 /*! \brief Gets the name of the entry's leaf.
588 
589 	\c buffer must be pre-allocated and of sufficient
590 	length to hold the entire string. A length of \c B_FILE_NAME_LENGTH is recommended.
591 
592 	\param buffer pointer to a pre-allocated string into which the result is copied
593 	\return
594 	- \c B_OK - Success
595 	- "error code" - Failure
596 */
597 status_t
598 BEntry::GetName(char *buffer) const
599 {
600 	status_t result = B_ERROR;
601 
602 	if (fCStatus != B_OK) {
603 		result = B_NO_INIT;
604 	} else if (buffer == NULL) {
605 		result = B_BAD_VALUE;
606 	} else {
607 		strcpy(buffer, fName);
608 		result = B_OK;
609 	}
610 
611 	return result;
612 }
613 
614 /*! \brief Renames the BEntry to path, replacing an existing entry if clobber is true.
615 
616 	NOTE: The BEntry must refer to an existing file. If it is abstract, this method will fail.
617 
618 	\param path Pointer to a string containing the new name for the entry.  May
619 	            be absolute or relative. If relative, the entry is renamed within its
620 	            current directory.
621 	\param clobber If \c false and a file with the name given by \c path already exists,
622 	               the method will fail. If \c true and such a file exists, it will
623 	               be overwritten.
624 	\return
625 	- \c B_OK - Success
626 	- \c B_ENTRY_EXISTS - The new location is already taken and \c clobber was \c false
627 	- \c B_ENTRY_NOT_FOUND - Attempted to rename an abstract entry
628 	- "error code" - Failure
629 
630 */
631 status_t
632 BEntry::Rename(const char *path, bool clobber)
633 {
634 	// check parameter and initialization
635 	if (path == NULL)
636 		return B_BAD_VALUE;
637 	if (fCStatus != B_OK)
638 		return B_NO_INIT;
639 	// get an entry representing the target location
640 	BEntry target;
641 	status_t error;
642 	if (BPrivate::Storage::is_absolute_path(path)) {
643 		error = target.SetTo(path);
644 	} else {
645 		int dirFD = _kern_dup(fDirFd);
646 		if (dirFD < 0)
647 			return dirFD;
648 		// init the entry
649 		error = target.fCStatus = target.set(dirFD, path, false);
650 	}
651 	if (error != B_OK)
652 		return error;
653 	return _Rename(target, clobber);
654 }
655 
656 /*! \brief Moves the BEntry to directory or directory+path combination, replacing an existing entry if clobber is true.
657 
658 	NOTE: The BEntry must refer to an existing file. If it is abstract, this method will fail.
659 
660 	\param dir Pointer to a pre-allocated BDirectory into which the entry should be moved.
661 	\param path Optional new leaf name for the entry. May be a simple leaf or a relative path;
662 	            either way, \c path is reckoned off of \c dir. If \c NULL, the entry retains
663 	            its previous leaf name.
664 	\param clobber If \c false and an entry already exists at the specified destination,
665 	               the method will fail. If \c true and such an entry exists, it will
666 	               be overwritten.
667 	\return
668 	- \c B_OK - Success
669 	- \c B_ENTRY_EXISTS - The new location is already taken and \c clobber was \c false
670 	- \c B_ENTRY_NOT_FOUND - Attempted to move an abstract entry
671 	- "error code" - Failure
672 */
673 status_t
674 BEntry::MoveTo(BDirectory *dir, const char *path, bool clobber)
675 {
676 	// check parameters and initialization
677 	if (fCStatus != B_OK)
678 		return B_NO_INIT;
679 	if (dir == NULL)
680 		return B_BAD_VALUE;
681 	if (dir->InitCheck() != B_OK)
682 		return B_BAD_VALUE;
683 	// NULL path simply means move without renaming
684 	if (path == NULL)
685 		path = fName;
686 	// get an entry representing the target location
687 	BEntry target;
688 	status_t error = target.SetTo(dir, path);
689 	if (error != B_OK)
690 		return error;
691 	return _Rename(target, clobber);
692 }
693 
694 /*! \brief Removes the entry from the file system.
695 
696 	NOTE: If any file descriptors are open on the file when Remove() is called,
697 	the chunk of data they refer to will continue to exist until all such file
698 	descriptors are closed. The BEntry object, however, becomes abstract and
699 	no longer refers to any actual data in the filesystem.
700 
701 	\return
702 	- B_OK - Success
703 	- "error code" - Failure
704 */
705 status_t
706 BEntry::Remove()
707 {
708 	if (fCStatus != B_OK)
709 		return B_NO_INIT;
710 
711 	if (IsDirectory())
712 		return _kern_remove_dir(fDirFd, fName);
713 
714 	return _kern_unlink(fDirFd, fName);
715 }
716 
717 
718 /*! \brief	Returns true if the BEntry and \c item refer to the same entry or
719 			if they are both uninitialized.
720 
721 	\return
722 	- true - Both BEntry objects refer to the same entry or they are both uninitialzed
723 	- false - The BEntry objects refer to different entries
724  */
725 bool
726 BEntry::operator==(const BEntry &item) const
727 {
728 	// First check statuses
729 	if (this->InitCheck() != B_OK && item.InitCheck() != B_OK) {
730 		return true;
731 	} else if (this->InitCheck() == B_OK && item.InitCheck() == B_OK) {
732 
733 		// Directories don't compare well directly, so we'll
734 		// compare entry_refs instead
735 		entry_ref ref1, ref2;
736 		if (this->GetRef(&ref1) != B_OK)
737 			return false;
738 		if (item.GetRef(&ref2) != B_OK)
739 			return false;
740 		return (ref1 == ref2);
741 
742 	} else {
743 		return false;
744 	}
745 
746 }
747 
748 /*! \brief	Returns false if the BEntry and \c item refer to the same entry or
749 			if they are both uninitialized.
750 
751 	\return
752 	- true - The BEntry objects refer to different entries
753 	- false - Both BEntry objects refer to the same entry or they are both uninitialzed
754  */
755 bool
756 BEntry::operator!=(const BEntry &item) const
757 {
758 	return !(*this == item);
759 }
760 
761 /*! \brief Reinitializes the BEntry to be a copy of the argument
762 
763 	\return
764 	- A reference to the copy
765 */
766 BEntry&
767 BEntry::operator=(const BEntry &item)
768 {
769 	if (this == &item)
770 		return *this;
771 
772 	Unset();
773 	if (item.fCStatus == B_OK) {
774 		fDirFd = _kern_dup(item.fDirFd);
775 		if (fDirFd >= 0)
776 			fCStatus = set_name(item.fName);
777 		else
778 			fCStatus = fDirFd;
779 
780 		if (fCStatus != B_OK)
781 			Unset();
782 	}
783 
784 	return *this;
785 }
786 
787 /*! Reserved for future use. */
788 void BEntry::_PennyEntry1(){}
789 /*! Reserved for future use. */
790 void BEntry::_PennyEntry2(){}
791 /*! Reserved for future use. */
792 void BEntry::_PennyEntry3(){}
793 /*! Reserved for future use. */
794 void BEntry::_PennyEntry4(){}
795 /*! Reserved for future use. */
796 void BEntry::_PennyEntry5(){}
797 /*! Reserved for future use. */
798 void BEntry::_PennyEntry6(){}
799 
800 /*! \brief Updates the BEntry with the data from the stat structure according to the mask.
801 */
802 status_t
803 BEntry::set_stat(struct stat &st, uint32 what)
804 {
805 	if (fCStatus != B_OK)
806 		return B_FILE_ERROR;
807 
808 	return _kern_write_stat(fDirFd, fName, false, &st, sizeof(struct stat),
809 		what);
810 }
811 
812 /*! Sets the Entry to point to the entry specified by the path \a path relative
813 	to the given directory. If \a traverse is \c true and the given entry is a
814 	symlink, the object is recursively set to point to the entry pointed to by
815 	the symlink.
816 
817 	If \a path is an absolute path, \a dirFD is ignored.
818 	If \a dirFD is -1, path is considered relative to the current directory
819 	(unless it is an absolute path, that is).
820 
821 	The ownership of the file descriptor \a dirFD is transferred to the
822 	function, regardless of whether it succeeds or fails. The caller must not
823 	close the FD afterwards.
824 
825 	\param dirFD File descriptor of a directory relative to which path is to
826 		   be considered. May be -1, when the current directory shall be
827 		   considered.
828 	\param path Pointer to a path relative to the given directory.
829 	\param traverse If \c true and the given entry is a symlink, the object is
830 		   recursively set to point to the entry linked to by the symlink.
831 	\return
832 	- B_OK - Success
833 	- "error code" - Failure
834 */
835 status_t
836 BEntry::set(int dirFD, const char *path, bool traverse)
837 {
838 	bool requireConcrete = false;
839 	FDCloser fdCloser(dirFD);
840 	char tmpPath[B_PATH_NAME_LENGTH];
841 	char leafName[B_FILE_NAME_LENGTH];
842 	int32 linkLimit = B_MAX_SYMLINKS;
843 	while (true) {
844 		if (!path || strcmp(path, ".") == 0) {
845 			// "."
846 			// if no dir FD is supplied, we need to open the current directory
847 			// first
848 			if (dirFD < 0) {
849 				dirFD = _kern_open_dir(-1, ".");
850 				if (dirFD < 0)
851 					return dirFD;
852 				fdCloser.SetTo(dirFD);
853 			}
854 			// get the parent directory
855 			int parentFD = _kern_open_parent_dir(dirFD, leafName,
856 				B_FILE_NAME_LENGTH);
857 			if (parentFD < 0)
858 				return parentFD;
859 			dirFD = parentFD;
860 			fdCloser.SetTo(dirFD);
861 			break;
862 		} else if (strcmp(path, "..") == 0) {
863 			// ".."
864 			// open the parent directory
865 			int parentFD = _kern_open_dir(dirFD, "..");
866 			if (parentFD < 0)
867 				return parentFD;
868 			dirFD = parentFD;
869 			fdCloser.SetTo(dirFD);
870 			// get the parent's parent directory
871 			parentFD = _kern_open_parent_dir(dirFD, leafName,
872 				B_FILE_NAME_LENGTH);
873 			if (parentFD < 0)
874 				return parentFD;
875 			dirFD = parentFD;
876 			fdCloser.SetTo(dirFD);
877 			break;
878 		} else {
879 			// an ordinary path; analyze it
880 			char dirPath[B_PATH_NAME_LENGTH];
881 			status_t error = BPrivate::Storage::parse_path(path, dirPath,
882 				leafName);
883 			if (error != B_OK)
884 				return error;
885 			// special case: root directory ("/")
886 			if (leafName[0] == '\0' && dirPath[0] == '/')
887 				strcpy(leafName, ".");
888 			if (leafName[0] == '\0') {
889 				// the supplied path is already a leaf
890 				error = BPrivate::Storage::check_entry_name(dirPath);
891 				if (error != B_OK)
892 					return error;
893 				strcpy(leafName, dirPath);
894 				// if no directory was given, we need to open the current dir
895 				// now
896 				if (dirFD < 0) {
897 					char *cwd = getcwd(tmpPath, B_PATH_NAME_LENGTH);
898 					if (!cwd)
899 						return B_ERROR;
900 					dirFD = _kern_open_dir(-1, cwd);
901 					if (dirFD < 0)
902 						return dirFD;
903 					fdCloser.SetTo(dirFD);
904 				}
905 			} else {
906 				int parentFD = _kern_open_dir(dirFD, dirPath);
907 				if (parentFD < 0)
908 					return parentFD;
909 				dirFD = parentFD;
910 				fdCloser.SetTo(dirFD);
911 			}
912 			// traverse symlinks, if desired
913 			if (!traverse)
914 				break;
915 			struct stat st;
916 			error = _kern_read_stat(dirFD, leafName, false, &st,
917 				sizeof(struct stat));
918 			if (error == B_ENTRY_NOT_FOUND && !requireConcrete) {
919 				// that's fine -- the entry is abstract and was not target of
920 				// a symlink we resolved
921 				break;
922 			}
923 			if (error != B_OK)
924 				return error;
925 			// the entry is concrete
926 			if (!S_ISLNK(st.st_mode))
927 				break;
928 			requireConcrete = true;
929 			// we need to traverse the symlink
930 			if (--linkLimit < 0)
931 				return B_LINK_LIMIT;
932 			size_t bufferSize = B_PATH_NAME_LENGTH;
933 			error = _kern_read_link(dirFD, leafName, tmpPath, &bufferSize);
934 			if (error < 0)
935 				return error;
936 			path = tmpPath;
937 			// next round...
938 		}
939 	}
940 
941 	// set close on exec flag on dir FD
942 	fcntl(dirFD, F_SETFD, FD_CLOEXEC);
943 
944 	// set the result
945 	status_t error = set_name(leafName);
946 	if (error != B_OK)
947 		return error;
948 	fdCloser.Detach();
949 	fDirFd = dirFD;
950 	return B_OK;
951 }
952 
953 /*! \brief Handles string allocation, deallocation, and copying for the entry's leaf name.
954 
955 	\return
956 	- B_OK - Success
957 	- "error code" - Failure
958 */
959 status_t
960 BEntry::set_name(const char *name)
961 {
962 	if (name == NULL)
963 		return B_BAD_VALUE;
964 
965 	free(fName);
966 
967 	fName = strdup(name);
968 	if (!fName)
969 		return B_NO_MEMORY;
970 
971 	return B_OK;
972 }
973 
974 // _Rename
975 /*!	\brief Renames the entry referred to by this object to the location
976 		   specified by \a target.
977 
978 	If an entry exists at the target location, the method fails, unless
979 	\a clobber is \c true, in which case that entry is overwritten (doesn't
980 	work for non-empty directories, though).
981 
982 	If the operation was successful, this entry is made a clone of the
983 	supplied one and the supplied one is uninitialized.
984 
985 	\param target The entry specifying the target location.
986 	\param clobber If \c true, the an entry existing at the target location
987 		   will be overwritten.
988 	\return \c B_OK, if everything went fine, another error code otherwise.
989 */
990 status_t
991 BEntry::_Rename(BEntry& target, bool clobber)
992 {
993 	// check, if there's an entry in the way
994 	if (!clobber && target.Exists())
995 		return B_FILE_EXISTS;
996 	// rename
997 	status_t error = _kern_rename(fDirFd, fName, target.fDirFd, target.fName);
998 	if (error == B_OK) {
999 		Unset();
1000 		fCStatus = target.fCStatus;
1001 		fDirFd = target.fDirFd;
1002 		fName = target.fName;
1003 		target.fCStatus = B_NO_INIT;
1004 		target.fDirFd = -1;
1005 		target.fName = NULL;
1006 	}
1007 	return error;
1008 }
1009 
1010 
1011 /*! Debugging function, dumps the given entry to stdout. This function is not part of
1012 	the R5 implementation, and thus calls to it will mean you can't link with the
1013 	R5 Storage Kit.
1014 
1015 	\param name	Pointer to a string to be printed along with the dump for identification
1016 				purposes.
1017 
1018 	*/
1019 void
1020 BEntry::Dump(const char *name)
1021 {
1022 	if (name != NULL) {
1023 		printf("------------------------------------------------------------\n");
1024 		printf("%s\n", name);
1025 		printf("------------------------------------------------------------\n");
1026 	}
1027 
1028 	printf("fCStatus == %ld\n", fCStatus);
1029 
1030 	struct stat st;
1031 	if (fDirFd != -1
1032 		&& _kern_read_stat(fDirFd, NULL, false, &st,
1033 				sizeof(struct stat)) == B_OK) {
1034 		printf("dir.device == %ld\n", st.st_dev);
1035 		printf("dir.inode  == %lld\n", st.st_ino);
1036 	} else {
1037 		printf("dir == NullFd\n");
1038 	}
1039 
1040 	printf("leaf == '%s'\n", fName);
1041 	printf("\n");
1042 
1043 }
1044 
1045 // get_ref_for_path
1046 /*!	\brief Returns an entry_ref for a given path.
1047 	\param path The path name referring to the entry
1048 	\param ref The entry_ref structure to be filled in
1049 	\return
1050 	- \c B_OK - Everything went fine.
1051 	- \c B_BAD_VALUE - \c NULL \a path or \a ref.
1052 	- \c B_ENTRY_NOT_FOUND - A (non-leaf) path component does not exist.
1053 	- \c B_NO_MEMORY - Insufficient memory for successful completion.
1054 */
1055 status_t
1056 get_ref_for_path(const char *path, entry_ref *ref)
1057 {
1058 	status_t error = (path && ref ? B_OK : B_BAD_VALUE);
1059 	if (error == B_OK) {
1060 		BEntry entry(path);
1061 		error = entry.InitCheck();
1062 		if (error == B_OK)
1063 			error = entry.GetRef(ref);
1064 	}
1065 	return error;
1066 }
1067 
1068 // <
1069 /*!	\brief Returns whether an entry is less than another.
1070 	The components are compared in order \c device, \c directory, \c name.
1071 	A \c NULL \c name is less than any non-null name.
1072 
1073 	\return
1074 	- true - a < b
1075 	- false - a >= b
1076 */
1077 bool
1078 operator<(const entry_ref & a, const entry_ref & b)
1079 {
1080 	return (a.device < b.device
1081 		|| (a.device == b.device
1082 			&& (a.directory < b.directory
1083 			|| (a.directory == b.directory
1084 				&& (a.name == NULL && b.name != NULL
1085 				|| (a.name != NULL && b.name != NULL
1086 					&& strcmp(a.name, b.name) < 0))))));
1087 }
1088 
1089 
1090 
1091 
1092