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