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