boot loader: Implement subset of <dirent.h> API

* Add opendir(), closedir(), readdir(), rewinddir().
* Add open_directory(), similar to opendir(), but basing the path off a
  specified directory.
This commit is contained in:
Ingo Weinhold 2014-04-01 08:31:35 +02:00
parent 09c07e4c5c
commit 2f019bd6ca
2 changed files with 136 additions and 0 deletions

View File

@ -6,6 +6,8 @@
#define KERNEL_BOOT_VFS_H
#include <dirent.h>
#include <SupportDefs.h>
#include <util/DoublyLinkedList.h>
@ -146,6 +148,7 @@ extern status_t mount_file_systems(stage2_args *args);
extern int open_node(Node *node, int mode);
extern int open_from(Directory *directory, const char *path, int mode,
mode_t permissions = 0);
extern DIR* open_directory(Directory* baseDirectory, const char* path);
extern Node *get_node_from(int fd);

View File

@ -1,5 +1,6 @@
/*
* Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
@ -36,6 +37,14 @@ using namespace boot;
#endif
struct __DIR {
Directory* directory;
void* cookie;
dirent entry;
char nameBuffer[B_FILE_NAME_LENGTH - 1];
};
class Descriptor {
public:
Descriptor(Node *node, void *cookie);
@ -734,6 +743,19 @@ get_node_for_path(Directory *directory, char *path, Node **_node)
}
/*! Version of get_node_for_path() not modifying \a path.
*/
static status_t
get_node_for_path(Directory* directory, const char* path, Node** _node)
{
char* mutablePath = strdup(path);
if (mutablePath == NULL)
return B_NO_MEMORY;
MemoryDeleter mutablePathDeleter(mutablePath);
return get_node_for_path(directory, mutablePath, _node);
}
// #pragma mark -
@ -1009,3 +1031,114 @@ fstat(int fd, struct stat *stat)
RETURN_AND_SET_ERRNO(descriptor->Stat(*stat));
}
DIR*
open_directory(Directory* baseDirectory, const char* path)
{
DIR* dir = new(std::nothrow) DIR;
if (dir == NULL) {
errno = B_NO_MEMORY;
return NULL;
}
ObjectDeleter<DIR> dirDeleter(dir);
Node* node;
status_t error = get_node_for_path(baseDirectory, path, &node);
if (error != B_OK) {
errno = error;
return NULL;
}
MethodDeleter<Node, status_t> nodeReleaser(node, &Node::Release);
if (!S_ISDIR(node->Type())) {
errno = error;
return NULL;
}
dir->directory = static_cast<Directory*>(node);
error = dir->directory->Open(&dir->cookie, O_RDONLY);
if (error != B_OK) {
errno = error;
return NULL;
}
nodeReleaser.Detach();
return dirDeleter.Detach();
}
DIR*
opendir(const char* dirName)
{
return open_directory(gRoot, dirName);
}
int
closedir(DIR* dir)
{
if (dir != NULL) {
dir->directory->Close(dir->cookie);
dir->directory->Release();
delete dir;
}
return 0;
}
struct dirent*
readdir(DIR* dir)
{
if (dir == NULL) {
errno = B_BAD_VALUE;
return NULL;
}
for (;;) {
status_t error = dir->directory->GetNextEntry(dir->cookie,
dir->entry.d_name, B_FILE_NAME_LENGTH);
if (error != B_OK) {
errno = error;
return NULL;
}
dir->entry.d_pdev = 0;
// not supported
dir->entry.d_pino = dir->directory->Inode();
dir->entry.d_dev = dir->entry.d_pdev;
// not supported
if (strcmp(dir->entry.d_name, ".") == 0
|| strcmp(dir->entry.d_name, "..") == 0) {
// Note: That's obviously not correct for "..", but we can't
// retrieve that information.
dir->entry.d_ino = dir->entry.d_pino;
} else {
Node* node = dir->directory->Lookup(dir->entry.d_name, false);
if (node == NULL)
continue;
dir->entry.d_ino = node->Inode();
node->Release();
}
return &dir->entry;
}
}
void
rewinddir(DIR* dir)
{
if (dir == NULL) {
errno = B_BAD_VALUE;
return;
}
status_t error = dir->directory->Rewind(dir->cookie);
if (error != B_OK)
errno = error;
}