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
33using 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.
65entry_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*/
86entry_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*/
99entry_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*/
112entry_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*/
124status_t
125entry_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*/
148bool
149entry_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*/
163bool
164entry_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*/
175entry_ref&
176entry_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*/
237BEntry::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*/
256BEntry::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*/
275BEntry::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*/
296BEntry::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*/
310BEntry::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*/
322BEntry::~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*/
334status_t
335BEntry::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*/
346bool
347BEntry::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*/
366status_t
367BEntry::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*/
382status_t
383BEntry::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*/
415status_t
416BEntry::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*/
437status_t
438BEntry::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 */
449void
450BEntry::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 */
475status_t
476BEntry::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*/
504status_t
505BEntry::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*/
553status_t
554BEntry::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*/
596status_t
597BEntry::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*/
634status_t
635BEntry::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*/
669status_t
670BEntry::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*/
712status_t
713BEntry::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*/
745status_t
746BEntry::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 */
761bool
762BEntry::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 */
792bool
793BEntry::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*/
804BEntry&
805BEntry::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. */
827void BEntry::_PennyEntry1(){}
828void BEntry::_PennyEntry2(){}
829void BEntry::_PennyEntry3(){}
830void BEntry::_PennyEntry4(){}
831void BEntry::_PennyEntry5(){}
832void BEntry::_PennyEntry6(){}
833
834
835/*! \brief Updates the BEntry with the data from the stat structure according to the mask.
836*/
837status_t
838BEntry::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*/
871status_t
872BEntry::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*/
1006status_t
1007BEntry::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*/
1037status_t
1038BEntry::_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*/
1065void
1066BEntry::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 == %" B_PRId32 "\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", (long long)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*/
1104status_t
1105get_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*/
1126bool
1127operator<(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