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