1//----------------------------------------------------------------------
2//  This software is part of the OpenBeOS distribution and is covered
3//  by the MIT License.
4//---------------------------------------------------------------------
5/*!
6	\file Directory.cpp
7	BDirectory implementation.
8*/
9
10#include <fcntl.h>
11#include <string.h>
12
13#include <Directory.h>
14#include <Entry.h>
15#include <File.h>
16#include <fs_info.h>
17#include <Path.h>
18#include <SymLink.h>
19
20#include <syscalls.h>
21
22#include "storage_support.h"
23
24#ifdef USE_OPENBEOS_NAMESPACE
25namespace OpenBeOS {
26#endif
27
28// constructor
29//! Creates an uninitialized BDirectory object.
30BDirectory::BDirectory()
31		  : BNode(),
32			BEntryList(),
33			fDirFd(-1)
34{
35}
36
37// copy constructor
38//! Creates a copy of the supplied BDirectory.
39/*!	\param dir the BDirectory object to be copied
40*/
41BDirectory::BDirectory(const BDirectory &dir)
42		  : BNode(),
43			BEntryList(),
44			fDirFd(-1)
45{
46	*this = dir;
47}
48
49// constructor
50/*! \brief Creates a BDirectory and initializes it to the directory referred
51	to by the supplied entry_ref.
52	\param ref the entry_ref referring to the directory
53*/
54BDirectory::BDirectory(const entry_ref *ref)
55		  : BNode(),
56			BEntryList(),
57			fDirFd(-1)
58{
59	SetTo(ref);
60}
61
62// constructor
63/*! \brief Creates a BDirectory and initializes it to the directory referred
64	to by the supplied node_ref.
65	\param nref the node_ref referring to the directory
66*/
67BDirectory::BDirectory(const node_ref *nref)
68		  : BNode(),
69			BEntryList(),
70			fDirFd(-1)
71{
72	SetTo(nref);
73}
74
75// constructor
76/*! \brief Creates a BDirectory and initializes it to the directory referred
77	to by the supplied BEntry.
78	\param entry the BEntry referring to the directory
79*/
80BDirectory::BDirectory(const BEntry *entry)
81		  : BNode(),
82			BEntryList(),
83			fDirFd(-1)
84{
85	SetTo(entry);
86}
87
88// constructor
89/*! \brief Creates a BDirectory and initializes it to the directory referred
90	to by the supplied path name.
91	\param path the directory's path name
92*/
93BDirectory::BDirectory(const char *path)
94		  : BNode(),
95			BEntryList(),
96			fDirFd(-1)
97{
98	SetTo(path);
99}
100
101// constructor
102/*! \brief Creates a BDirectory and initializes it to the directory referred
103	to by the supplied path name relative to the specified BDirectory.
104	\param dir the BDirectory, relative to which the directory's path name is
105		   given
106	\param path the directory's path name relative to \a dir
107*/
108BDirectory::BDirectory(const BDirectory *dir, const char *path)
109		  : BNode(),
110			BEntryList(),
111			fDirFd(-1)
112{
113	SetTo(dir, path);
114}
115
116// destructor
117//! Frees all allocated resources.
118/*! If the BDirectory is properly initialized, the directory's file descriptor
119	is closed.
120*/
121BDirectory::~BDirectory()
122{
123	// Also called by the BNode destructor, but we rather try to avoid
124	// problems with calling virtual functions in the base class destructor.
125	// Depending on the compiler implementation an object may be degraded to
126	// an object of the base class after the destructor of the derived class
127	// has been executed.
128	close_fd();
129}
130
131// SetTo
132/*! \brief Re-initializes the BDirectory to the directory referred to by the
133	supplied entry_ref.
134	\param ref the entry_ref referring to the directory
135	\return
136	- \c B_OK: Everything went fine.
137	- \c B_BAD_VALUE: \c NULL \a ref.
138	- \c B_ENTRY_NOT_FOUND: Directory not found.
139	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
140	- \c B_NO_MEMORY: Insufficient memory for operation.
141	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
142	- \c B_BUSY: A node was busy.
143	- \c B_FILE_ERROR: A general file error.
144	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
145*/
146status_t
147BDirectory::SetTo(const entry_ref *ref)
148{
149	// open node
150	status_t error = _SetTo(ref, true);
151	if (error != B_OK)
152		return error;
153
154	// open dir
155	error = set_dir_fd(_kern_open_dir_entry_ref(ref->device, ref->directory, ref->name));
156	if (error < 0) {
157		Unset();
158		return (fCStatus = error);
159	}
160
161	// set close on exec flag on dir FD
162	fcntl(fDirFd, F_SETFD, FD_CLOEXEC);
163
164	return B_OK;
165}
166
167// SetTo
168/*! \brief Re-initializes the BDirectory to the directory referred to by the
169	supplied node_ref.
170	\param nref the node_ref referring to the directory
171	\return
172	- \c B_OK: Everything went fine.
173	- \c B_BAD_VALUE: \c NULL \a nref.
174	- \c B_ENTRY_NOT_FOUND: Directory not found.
175	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
176	- \c B_NO_MEMORY: Insufficient memory for operation.
177	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
178	- \c B_BUSY: A node was busy.
179	- \c B_FILE_ERROR: A general file error.
180	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
181*/
182status_t
183BDirectory::SetTo(const node_ref *nref)
184{
185	Unset();
186	status_t error = (nref ? B_OK : B_BAD_VALUE);
187	if (error == B_OK) {
188		entry_ref ref(nref->device, nref->node, ".");
189		error = SetTo(&ref);
190	}
191	set_status(error);
192	return error;
193}
194
195// SetTo
196/*! \brief Re-initializes the BDirectory to the directory referred to by the
197	supplied BEntry.
198	\param entry the BEntry referring to the directory
199	\return
200	- \c B_OK: Everything went fine.
201	- \c B_BAD_VALUE: \c NULL \a entry.
202	- \c B_ENTRY_NOT_FOUND: Directory not found.
203	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
204	- \c B_NO_MEMORY: Insufficient memory for operation.
205	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
206	- \c B_BUSY: A node was busy.
207	- \c B_FILE_ERROR: A general file error.
208	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
209*/
210status_t
211BDirectory::SetTo(const BEntry *entry)
212{
213	if (!entry) {
214		Unset();
215		return (fCStatus = B_BAD_VALUE);
216	}
217
218	// open node
219	status_t error = _SetTo(entry->fDirFd, entry->fName, true);
220	if (error != B_OK)
221		return error;
222
223	// open dir
224	error = set_dir_fd(_kern_open_dir(entry->fDirFd, entry->fName));
225	if (error < 0) {
226		Unset();
227		return (fCStatus = error);
228	}
229
230	// set close on exec flag on dir FD
231	fcntl(fDirFd, F_SETFD, FD_CLOEXEC);
232
233	return B_OK;
234}
235
236// SetTo
237/*! \brief Re-initializes the BDirectory to the directory referred to by the
238	supplied path name.
239	\param path the directory's path name
240	\return
241	- \c B_OK: Everything went fine.
242	- \c B_BAD_VALUE: \c NULL \a path.
243	- \c B_ENTRY_NOT_FOUND: Directory not found.
244	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
245	- \c B_NO_MEMORY: Insufficient memory for operation.
246	- \c B_NAME_TOO_LONG: The supplied path name (\a path) is too long.
247	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
248	- \c B_BUSY: A node was busy.
249	- \c B_FILE_ERROR: A general file error.
250	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
251	- \c B_NOT_A_DIRECTORY: \a path includes a non-directory.
252*/
253status_t
254BDirectory::SetTo(const char *path)
255{
256	// open node
257	status_t error = _SetTo(-1, path, true);
258	if (error != B_OK)
259		return error;
260
261	// open dir
262	error = set_dir_fd(_kern_open_dir(-1, path));
263	if (error < 0) {
264		Unset();
265		return (fCStatus = error);
266	}
267
268	// set close on exec flag on dir FD
269	fcntl(fDirFd, F_SETFD, FD_CLOEXEC);
270
271	return B_OK;
272}
273
274// SetTo
275/*! \brief Re-initializes the BDirectory to the directory referred to by the
276	supplied path name relative to the specified BDirectory.
277	\param dir the BDirectory, relative to which the directory's path name is
278		   given
279	\param path the directory's path name relative to \a dir
280	\return
281	- \c B_OK: Everything went fine.
282	- \c B_BAD_VALUE: \c NULL \a dir or \a path, or \a path is absolute.
283	- \c B_ENTRY_NOT_FOUND: Directory not found.
284	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
285	- \c B_NO_MEMORY: Insufficient memory for operation.
286	- \c B_NAME_TOO_LONG: The supplied path name (\a path) is too long.
287	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
288	- \c B_BUSY: A node was busy.
289	- \c B_FILE_ERROR: A general file error.
290	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
291	- \c B_NOT_A_DIRECTORY: \a path includes a non-directory.
292*/
293status_t
294BDirectory::SetTo(const BDirectory *dir, const char *path)
295{
296	if (!dir || !path || BPrivate::Storage::is_absolute_path(path)) {
297		Unset();
298		return (fCStatus = B_BAD_VALUE);
299	}
300
301	// open node
302	status_t error = _SetTo(dir->fDirFd, path, true);
303	if (error != B_OK)
304		return error;
305
306	// open dir
307	error = set_dir_fd(_kern_open_dir(dir->fDirFd, path));
308	if (error < 0) {
309		Unset();
310		return (fCStatus = error);
311	}
312
313	// set close on exec flag on dir FD
314	fcntl(fDirFd, F_SETFD, FD_CLOEXEC);
315
316	return B_OK;
317}
318
319// GetEntry
320//! Returns a BEntry referring to the directory represented by this object.
321/*!	If the initialization of \a entry fails, it is Unset().
322	\param entry a pointer to the entry that shall be set to refer to the
323		   directory
324	\return
325	- \c B_OK: Everything went fine.
326	- \c B_BAD_VALUE: \c NULL \a entry.
327	- \c B_ENTRY_NOT_FOUND: Directory not found.
328	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
329	- \c B_NO_MEMORY: Insufficient memory for operation.
330	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
331	- \c B_BUSY: A node was busy.
332	- \c B_FILE_ERROR: A general file error.
333	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
334*/
335status_t
336BDirectory::GetEntry(BEntry *entry) const
337{
338	if (!entry)
339		return B_BAD_VALUE;
340	if (InitCheck() != B_OK)
341		return B_NO_INIT;
342	return entry->SetTo(this, ".", false);
343}
344
345// FindEntry
346/*! \brief Finds an entry referred to by a path relative to the directory
347	represented by this BDirectory.
348	\a path may be absolute. If the BDirectory is not properly initialized,
349	the entry is search relative to the current directory.
350	If the entry couldn't be found, \a entry is Unset().
351	\param path the entry's path name. May be relative to this directory or
352		   absolute.
353	\param entry a pointer to a BEntry to be initialized with the found entry
354	\param traverse specifies whether to follow it, if the found entry
355		   is a symbolic link.
356	\return
357	- \c B_OK: Everything went fine.
358	- \c B_BAD_VALUE: \c NULL \a path or \a entry.
359	- \c B_ENTRY_NOT_FOUND: Entry not found.
360	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
361	- \c B_NO_MEMORY: Insufficient memory for operation.
362	- \c B_NAME_TOO_LONG: The supplied path name (\a path) is too long.
363	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
364	- \c B_BUSY: A node was busy.
365	- \c B_FILE_ERROR: A general file error.
366	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
367	- \c B_NOT_A_DIRECTORY: \a path includes a non-directory.
368	\note The functionality of this method differs from the one of
369		  BEntry::SetTo(BDirectory *, const char *, bool) in that the
370		  latter doesn't require the entry to be existent, whereas this
371		  function does.
372*/
373status_t
374BDirectory::FindEntry(const char *path, BEntry *entry, bool traverse) const
375{
376	status_t error = (path && entry ? B_OK : B_BAD_VALUE);
377	if (entry)
378		entry->Unset();
379	if (error == B_OK) {
380		// init a potentially abstract entry
381		if (InitCheck() == B_OK)
382			error = entry->SetTo(this, path, traverse);
383		else
384			error = entry->SetTo(path, traverse);
385		// fail, if entry is abstract
386		if (error == B_OK && !entry->Exists()) {
387			error = B_ENTRY_NOT_FOUND;
388			entry->Unset();
389		}
390	}
391	return error;
392}
393
394// Contains
395/*!	\brief Returns whether this directory or any of its subdirectories
396	at any level contain the entry referred to by the supplied path name.
397	Only entries that match the node flavor specified by \a nodeFlags are
398	considered.
399	If the BDirectory is not properly initialized, the method returns \c false.
400	A non-absolute path is considered relative to the current directory.
401
402	\note R5's implementation always returns \c true given an absolute path or
403	an unitialized directory. This implementation is not compatible with that
404	behavior. Instead it converts the path into a BEntry and passes it to the
405	other version of Contains().
406
407	\param path the entry's path name. May be relative to this directory or
408		   absolute.
409	\param nodeFlags Any of the following:
410		   - \c B_FILE_NODE: The entry must be a file.
411		   - \c B_DIRECTORY_NODE: The entry must be a directory.
412		   - \c B_SYMLINK_NODE: The entry must be a symbolic link.
413		   - \c B_ANY_NODE: The entry may be of any kind.
414	\return
415	- \c true, if the entry exists, its kind does match \nodeFlags and the
416	  BDirectory is properly initialized and does contain the entry at any
417	  level,
418	- \c false, otherwise
419*/
420bool
421BDirectory::Contains(const char *path, int32 nodeFlags) const
422{
423	// check initialization and parameters
424	if (InitCheck() != B_OK)
425		return false;
426	if (!path)
427		return true;	// mimic R5 behavior
428	// turn the path into a BEntry and let the other version do the work
429	BEntry entry;
430	if (BPrivate::Storage::is_absolute_path(path))
431		entry.SetTo(path);
432	else
433		entry.SetTo(this, path);
434	return Contains(&entry, nodeFlags);
435}
436
437// Contains
438/*!	\brief Returns whether this directory or any of its subdirectories
439	at any level contain the entry referred to by the supplied BEntry.
440	Only entries that match the node flavor specified by \a nodeFlags are
441	considered.
442	\param entry a BEntry referring to the entry
443	\param nodeFlags Any of the following:
444		   - \c B_FILE_NODE: The entry must be a file.
445		   - \c B_DIRECTORY_NODE: The entry must be a directory.
446		   - \c B_SYMLINK_NODE: The entry must be a symbolic link.
447		   - \c B_ANY_NODE: The entry may be of any kind.
448	\return
449	- \c true, if the BDirectory is properly initialized and the entry of the
450	  matching kind could be found,
451	- \c false, otherwise
452*/
453bool
454BDirectory::Contains(const BEntry *entry, int32 nodeFlags) const
455{
456	bool result = (entry);
457	// check, if the entry exists at all
458	if (result)
459		result = entry->Exists();
460	// test the node kind
461	if (result) {
462		switch (nodeFlags) {
463			case B_FILE_NODE:
464				result = entry->IsFile();
465				break;
466			case B_DIRECTORY_NODE:
467				result = entry->IsDirectory();
468				break;
469			case B_SYMLINK_NODE:
470				result = entry->IsSymLink();
471				break;
472			case B_ANY_NODE:
473				break;
474			default:
475				result = false;
476				break;
477		}
478	}
479	// If the directory is initialized, get the canonical paths of the dir and
480	// the entry and check, if the latter is a prefix of the first one.
481	if (result && InitCheck() == B_OK) {
482		BPath dirPath(this, ".", true);
483		BPath entryPath(entry);
484		if (dirPath.InitCheck() == B_OK && entryPath.InitCheck() == B_OK) {
485			result = !strncmp(dirPath.Path(), entryPath.Path(),
486				strlen(dirPath.Path()));
487		} else
488			result = false;
489	}
490	return result;
491}
492
493// GetStatFor
494/*!	\brief Returns the stat structure of the entry referred to by the supplied
495	path name.
496	\param path the entry's path name. May be relative to this directory or
497		   absolute, or \c NULL to get the directories stat info.
498	\param st a pointer to the stat structure to be filled in by this function
499	\return
500	- \c B_OK: Everything went fine.
501	- \c B_BAD_VALUE: \c NULL \a st.
502	- \c B_ENTRY_NOT_FOUND: Entry not found.
503	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
504	- \c B_NO_MEMORY: Insufficient memory for operation.
505	- \c B_NAME_TOO_LONG: The supplied path name (\a path) is too long.
506	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
507	- \c B_BUSY: A node was busy.
508	- \c B_FILE_ERROR: A general file error.
509	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
510	- \c B_NOT_A_DIRECTORY: \a path includes a non-directory.
511*/
512status_t
513BDirectory::GetStatFor(const char *path, struct stat *st) const
514{
515	if (!st)
516		return B_BAD_VALUE;
517	if (InitCheck() != B_OK)
518		return B_NO_INIT;
519	status_t error = B_OK;
520	if (path) {
521		if (strlen(path) == 0)
522			return B_ENTRY_NOT_FOUND;
523		error = _kern_read_stat(fDirFd, path, false, st, sizeof(struct stat));
524	} else
525		error = GetStat(st);
526	return error;
527}
528
529// GetNextEntry
530//! Returns the BDirectory's next entry as a BEntry.
531/*!	Unlike GetNextDirents() this method ignores the entries "." and "..".
532	\param entry a pointer to a BEntry to be initialized to the found entry
533	\param traverse specifies whether to follow it, if the found entry
534		   is a symbolic link.
535	\note The iterator used by this method is the same one used by
536		  GetNextRef(), GetNextDirents(), Rewind() and CountEntries().
537	\return
538	- \c B_OK: Everything went fine.
539	- \c B_BAD_VALUE: \c NULL \a entry.
540	- \c B_ENTRY_NOT_FOUND: No more entries found.
541	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
542	- \c B_NO_MEMORY: Insufficient memory for operation.
543	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
544	- \c B_BUSY: A node was busy.
545	- \c B_FILE_ERROR: A general file error.
546	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
547*/
548status_t
549BDirectory::GetNextEntry(BEntry *entry, bool traverse)
550{
551	status_t error = (entry ? B_OK : B_BAD_VALUE);
552	if (error == B_OK) {
553		entry_ref ref;
554		error = GetNextRef(&ref);
555		if (error == B_OK)
556			error = entry->SetTo(&ref, traverse);
557	}
558	return error;
559}
560
561// GetNextRef
562//! Returns the BDirectory's next entry as an entry_ref.
563/*!	Unlike GetNextDirents() this method ignores the entries "." and "..".
564	\param ref a pointer to an entry_ref to be filled in with the data of the
565		   found entry
566	\param traverse specifies whether to follow it, if the found entry
567		   is a symbolic link.
568	\note The iterator used be this method is the same one used by
569		  GetNextEntry(), GetNextDirents(), Rewind() and CountEntries().
570	\return
571	- \c B_OK: Everything went fine.
572	- \c B_BAD_VALUE: \c NULL \a ref.
573	- \c B_ENTRY_NOT_FOUND: No more entries found.
574	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
575	- \c B_NO_MEMORY: Insufficient memory for operation.
576	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
577	- \c B_BUSY: A node was busy.
578	- \c B_FILE_ERROR: A general file error.
579	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
580*/
581status_t
582BDirectory::GetNextRef(entry_ref *ref)
583{
584	status_t error = (ref ? B_OK : B_BAD_VALUE);
585	if (error == B_OK && InitCheck() != B_OK)
586		error = B_FILE_ERROR;
587	if (error == B_OK) {
588		BPrivate::Storage::LongDirEntry entry;
589		bool next = true;
590		while (error == B_OK && next) {
591			if (GetNextDirents(&entry, sizeof(entry), 1) != 1) {
592				error = B_ENTRY_NOT_FOUND;
593			} else {
594				next = (!strcmp(entry.d_name, ".")
595						|| !strcmp(entry.d_name, ".."));
596			}
597		}
598		if (error == B_OK) {
599			ref->device = fDirNodeRef.device;
600			ref->directory = fDirNodeRef.node;
601			error = ref->set_name(entry.d_name);
602		}
603	}
604	return error;
605}
606
607// GetNextDirents
608//! Returns the BDirectory's next entries as dirent structures.
609/*!	Unlike GetNextEntry() and GetNextRef(), this method returns also
610	the entries "." and "..".
611	\param buf a pointer to a buffer to be filled with dirent structures of
612		   the found entries
613	\param count the maximal number of entries to be returned.
614	\note The iterator used by this method is the same one used by
615		  GetNextEntry(), GetNextRef(), Rewind() and CountEntries().
616	\return
617	- The number of dirent structures stored in the buffer, 0 when there are
618	  no more entries to be returned.
619	- \c B_BAD_VALUE: \c NULL \a buf.
620	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
621	- \c B_NO_MEMORY: Insufficient memory for operation.
622	- \c B_NAME_TOO_LONG: The entry's name is too long for the buffer.
623	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
624	- \c B_BUSY: A node was busy.
625	- \c B_FILE_ERROR: A general file error.
626	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
627*/
628int32
629BDirectory::GetNextDirents(dirent *buf, size_t bufSize, int32 count)
630{
631	if (!buf)
632		return B_BAD_VALUE;
633	if (InitCheck() != B_OK)
634		return B_FILE_ERROR;
635	return _kern_read_dir(fDirFd, buf, bufSize, count);
636}
637
638// Rewind
639//!	Rewinds the directory iterator.
640/*!	\return
641	- \c B_OK: Everything went fine.
642	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
643	- \c B_NO_MEMORY: Insufficient memory for operation.
644	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
645	- \c B_BUSY: A node was busy.
646	- \c B_FILE_ERROR: A general file error.
647	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
648	\see GetNextEntry(), GetNextRef(), GetNextDirents(), CountEntries()
649*/
650status_t
651BDirectory::Rewind()
652{
653	if (InitCheck() != B_OK)
654		return B_FILE_ERROR;
655	return _kern_rewind_dir(fDirFd);
656}
657
658// CountEntries
659//!	Returns the number of entries in this directory.
660/*!	CountEntries() uses the directory iterator also used by GetNextEntry(),
661	GetNextRef() and GetNextDirents(). It does a Rewind(), iterates through
662	the entries and Rewind()s again. The entries "." and ".." are not counted.
663	\return
664	- the number of entries in the directory (not counting "." and "..").
665	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
666	- \c B_NO_MEMORY: Insufficient memory for operation.
667	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
668	- \c B_BUSY: A node was busy.
669	- \c B_FILE_ERROR: A general file error.
670	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
671	\see GetNextEntry(), GetNextRef(), GetNextDirents(), Rewind()
672*/
673int32
674BDirectory::CountEntries()
675{
676	status_t error = Rewind();
677	if (error != B_OK)
678		return error;
679	int32 count = 0;
680	BPrivate::Storage::LongDirEntry entry;
681	while (error == B_OK) {
682		if (GetNextDirents(&entry, sizeof(entry), 1) != 1)
683			break;
684		if (strcmp(entry.d_name, ".") != 0 && strcmp(entry.d_name, "..") != 0)
685			count++;
686	}
687	Rewind();
688	return (error == B_OK ? count : error);
689}
690
691// CreateDirectory
692//! Creates a new directory.
693/*! If an entry with the supplied name does already exist, the method fails.
694	\param path the new directory's path name. May be relative to this
695		   directory or absolute.
696	\param dir a pointer to a BDirectory to be initialized to the newly
697		   created directory. May be \c NULL.
698	\return
699	- \c B_OK: Everything went fine.
700	- \c B_BAD_VALUE: \c NULL \a path.
701	- \c B_ENTRY_NOT_FOUND: \a path does not refer to a possible entry.
702	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
703	- \c B_NO_MEMORY: Insufficient memory for operation.
704	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
705	- \c B_BUSY: A node was busy.
706	- \c B_FILE_ERROR: A general file error.
707	- \c B_FILE_EXISTS: An entry with that name does already exist.
708	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
709*/
710status_t
711BDirectory::CreateDirectory(const char *path, BDirectory *dir)
712{
713	if (!path)
714		return B_BAD_VALUE;
715	// create the dir
716	status_t error = _kern_create_dir(fDirFd, path,
717		S_IRWXU | S_IRWXG | S_IRWXU);
718	if (error != B_OK)
719		return error;
720	if (!dir)
721		return B_OK;
722	// init the supplied BDirectory
723	if (InitCheck() != B_OK || BPrivate::Storage::is_absolute_path(path))
724		return dir->SetTo(path);
725	else
726		return dir->SetTo(this, path);
727}
728
729// CreateFile
730//! Creates a new file.
731/*!	If a file with the supplied name does already exist, the method fails,
732	unless it is passed \c false to \a failIfExists -- in that case the file
733	is truncated to zero size. The new BFile will operate in \c B_READ_WRITE
734	mode.
735	\param path the new file's path name. May be relative to this
736		   directory or absolute.
737	\param file a pointer to a BFile to be initialized to the newly
738		   created file. May be \c NULL.
739	\param failIfExists \c true, if the method should fail when the file
740		   already exists, \c false otherwise
741	\return
742	- \c B_OK: Everything went fine.
743	- \c B_BAD_VALUE: \c NULL \a path.
744	- \c B_ENTRY_NOT_FOUND: \a path does not refer to a possible entry.
745	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
746	- \c B_NO_MEMORY: Insufficient memory for operation.
747	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
748	- \c B_BUSY: A node was busy.
749	- \c B_FILE_ERROR: A general file error.
750	- \c B_FILE_EXISTS: A file with that name does already exist and
751	  \c true has been passed for \a failIfExists.
752	- \c B_IS_A_DIRECTORY: A directory with the supplied name does already
753	  exist.
754	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
755*/
756status_t
757BDirectory::CreateFile(const char *path, BFile *file, bool failIfExists)
758{
759	if (!path)
760		return B_BAD_VALUE;
761	// Let BFile do the dirty job.
762	uint32 openMode = B_READ_WRITE | B_CREATE_FILE
763					  | (failIfExists ? B_FAIL_IF_EXISTS : 0);
764	BFile tmpFile;
765	BFile *realFile = (file ? file : &tmpFile);
766	status_t error = B_OK;
767	if (InitCheck() == B_OK && !BPrivate::Storage::is_absolute_path(path))
768		error = realFile->SetTo(this, path, openMode);
769	else
770		error = realFile->SetTo(path, openMode);
771	if (error != B_OK && file) // mimic R5 behavior
772		file->Unset();
773	return error;
774}
775
776// CreateSymLink
777//! Creates a new symbolic link.
778/*! If an entry with the supplied name does already exist, the method fails.
779	\param path the new symbolic link's path name. May be relative to this
780		   directory or absolute.
781	\param linkToPath the path the symbolic link shall point to.
782	\param dir a pointer to a BSymLink to be initialized to the newly
783		   created symbolic link. May be \c NULL.
784	\return
785	- \c B_OK: Everything went fine.
786	- \c B_BAD_VALUE: \c NULL \a path or \a linkToPath.
787	- \c B_ENTRY_NOT_FOUND: \a path does not refer to a possible entry.
788	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
789	- \c B_NO_MEMORY: Insufficient memory for operation.
790	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
791	- \c B_BUSY: A node was busy.
792	- \c B_FILE_ERROR: A general file error.
793	- \c B_FILE_EXISTS: An entry with that name does already exist.
794	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
795*/
796status_t
797BDirectory::CreateSymLink(const char *path, const char *linkToPath,
798						  BSymLink *link)
799{
800	if (!path || !linkToPath)
801		return B_BAD_VALUE;
802	// create the symlink
803	status_t error = _kern_create_symlink(fDirFd, path, linkToPath,
804		S_IRWXU | S_IRWXG | S_IRWXU);
805	if (error != B_OK)
806		return error;
807	if (!link)
808		return B_OK;
809	// init the supplied BSymLink
810	if (InitCheck() != B_OK || BPrivate::Storage::is_absolute_path(path))
811		return link->SetTo(path);
812	else
813		return link->SetTo(this, path);
814}
815
816// =
817//! Assigns another BDirectory to this BDirectory.
818/*!	If the other BDirectory is uninitialized, this one wi'll be too. Otherwise
819	it will refer to the same directory, unless an error occurs.
820	\param dir the original BDirectory
821	\return a reference to this BDirectory
822*/
823BDirectory &
824BDirectory::operator=(const BDirectory &dir)
825{
826	if (&dir != this) {	// no need to assign us to ourselves
827		Unset();
828		if (dir.InitCheck() == B_OK)
829			SetTo(&dir, ".");
830	}
831	return *this;
832}
833
834
835// FBC
836void BDirectory::_ErectorDirectory1() {}
837void BDirectory::_ErectorDirectory2() {}
838void BDirectory::_ErectorDirectory3() {}
839void BDirectory::_ErectorDirectory4() {}
840void BDirectory::_ErectorDirectory5() {}
841void BDirectory::_ErectorDirectory6() {}
842
843// close_fd
844//! Closes the BDirectory's file descriptor.
845void
846BDirectory::close_fd()
847{
848	if (fDirFd >= 0) {
849		_kern_close(fDirFd);
850		fDirFd = -1;
851	}
852	BNode::close_fd();
853}
854
855//! Returns the BDirectory's file descriptor.
856/*!	To be used instead of accessing the BDirectory's private \c fDirFd member
857	directly.
858	\return the file descriptor, or -1, if not properly initialized.
859*/
860int
861BDirectory::get_fd() const
862{
863	return fDirFd;
864}
865
866status_t
867BDirectory::set_dir_fd(int fd)
868{
869	if (fd < 0)
870		return fd;
871
872	fDirFd = fd;
873
874	status_t error = GetNodeRef(&fDirNodeRef);
875	if (error != B_OK)
876		close_fd();
877
878	return error;
879}
880
881
882
883// C functions
884
885// create_directory
886//! Creates all missing directories along a given path.
887/*!	\param path the directory path name.
888	\param mode a permission specification, which shall be used for the
889		   newly created directories.
890	\return
891	- \c B_OK: Everything went fine.
892	- \c B_BAD_VALUE: \c NULL \a path.
893	- \c B_ENTRY_NOT_FOUND: \a path does not refer to a possible entry.
894	- \c B_PERMISSION_DENIED: Directory permissions didn't allow operation.
895	- \c B_NO_MEMORY: Insufficient memory for operation.
896	- \c B_LINK_LIMIT: Indicates a cyclic loop within the file system.
897	- \c B_BUSY: A node was busy.
898	- \c B_FILE_ERROR: A general file error.
899	- \c B_NOT_A_DIRECTORY: An entry other than a directory with that name does
900	  already exist.
901	- \c B_NO_MORE_FDS: The application has run out of file descriptors.
902	\todo Check for efficency.
903*/
904status_t
905create_directory(const char *path, mode_t mode)
906{
907	if (!path)
908		return B_BAD_VALUE;
909	// That's the strategy: We start with the first component of the supplied
910	// path, create a BPath object from it and successively add the following
911	// components. Each time we get a new path, we check, if the entry it
912	// refers to exists and is a directory. If it doesn't exist, we try
913	// to create it. This goes on, until we're done with the input path or
914	// an error occurs.
915	BPath dirPath;
916	char *component;
917	int32 nextComponent;
918	do {
919		// get the next path component
920		status_t error = BPrivate::Storage::parse_first_path_component(path,
921			component, nextComponent);
922		if (error != B_OK)
923			return error;
924		// append it to the BPath
925		if (dirPath.InitCheck() == B_NO_INIT)	// first component
926			error = dirPath.SetTo(component);
927		else
928			error = dirPath.Append(component);
929		delete[] component;
930		if (error != B_OK)
931			return error;
932		path += nextComponent;
933		// create a BEntry from the BPath
934		BEntry entry;
935		error = entry.SetTo(dirPath.Path(), true);
936		if (error != B_OK)
937			return error;
938		// check, if it exists
939		if (entry.Exists()) {
940			// yep, it exists
941			if (!entry.IsDirectory())	// but is no directory
942				return B_NOT_A_DIRECTORY;
943		} else {
944			// it doesn't exist -- create it
945			error = _kern_create_dir(-1, dirPath.Path(), mode);
946			if (error != B_OK)
947				return error;
948		}
949	} while (nextComponent != 0);
950	return B_OK;
951}
952
953
954#ifdef USE_OPENBEOS_NAMESPACE
955};		// namespace OpenBeOS
956#endif
957
958