1.1 --- a/conf/dstest_host_readdir.cfg Wed Aug 04 17:13:47 2021 +0200
1.2 +++ b/conf/dstest_host_readdir.cfg Wed Aug 04 17:30:14 2021 +0200
1.3 @@ -4,11 +4,22 @@
1.4
1.5 local l = L4.default_loader;
1.6
1.7 -local server = l:new_channel();
1.8 +local pipe_server = l:new_channel();
1.9
1.10 l:startv({
1.11 caps = {
1.12 - server = server:svr(),
1.13 + server = pipe_server:svr(),
1.14 + },
1.15 + log = { "pipes", "r" },
1.16 + },
1.17 + "rom/dstest_pipe_server", "10");
1.18 +
1.19 +local host_server = l:new_channel();
1.20 +
1.21 +l:startv({
1.22 + caps = {
1.23 + pipes = pipe_server,
1.24 + server = host_server:svr(),
1.25 },
1.26 log = { "server", "r" },
1.27 },
1.28 @@ -16,7 +27,7 @@
1.29
1.30 l:startv({
1.31 caps = {
1.32 - server = server,
1.33 + server = host_server,
1.34 },
1.35 log = { "client", "g" },
1.36 },
2.1 --- a/conf/dstest_host_readdir.list Wed Aug 04 17:13:47 2021 +0200
2.2 +++ b/conf/dstest_host_readdir.list Wed Aug 04 17:30:14 2021 +0200
2.3 @@ -5,6 +5,7 @@
2.4 module ned
2.5 module dstest_host_readdir
2.6 module dstest_host_server
2.7 +module dstest_pipe_server
2.8 module lib4re-c.so
2.9 module lib4re-c-util.so
2.10 module lib4re.so
3.1 --- a/libfsserver/include/fsserver/host_file_opener.h Wed Aug 04 17:13:47 2021 +0200
3.2 +++ b/libfsserver/include/fsserver/host_file_opener.h Wed Aug 04 17:30:14 2021 +0200
3.3 @@ -54,6 +54,10 @@
3.4
3.5 virtual bool accessing_file(const char *path, flags_t flags, fileid_t fileid);
3.6
3.7 + /* Convenience methods obtaining different pager types. */
3.8 +
3.9 + virtual long get_directory(const char *path, flags_t flags, fileid_t fileid, offset_t *size, l4_cap_idx_t *cap);
3.10 +
3.11 public:
3.12 explicit HostFileOpener(FilePaging *paging)
3.13 : OpenerResource(paging)
4.1 --- a/libfsserver/lib/files/host_file_opener.cc Wed Aug 04 17:13:47 2021 +0200
4.2 +++ b/libfsserver/lib/files/host_file_opener.cc Wed Aug 04 17:30:14 2021 +0200
4.3 @@ -19,11 +19,50 @@
4.4 * Boston, MA 02110-1301, USA
4.5 */
4.6
4.7 +#include <thread>
4.8 +
4.9 +#include <dirent.h>
4.10 #include <sys/stat.h>
4.11
4.12 +#include <fsclient/client.h>
4.13 +
4.14 #include "host_file_accessor.h"
4.15 #include "host_file_opener.h"
4.16
4.17 +
4.18 +
4.19 +/* Thread payload. */
4.20 +
4.21 +static void read_directory(const char *path, file_t *writer)
4.22 +{
4.23 + DIR *dir = opendir(path);
4.24 + struct dirent *dirent;
4.25 +
4.26 + /* Subscribe to space and closure notifications on the pipe. */
4.27 +
4.28 + long err = client_set_blocking(writer, NOTIFY_SPACE_AVAILABLE);
4.29 +
4.30 + if (err)
4.31 + {
4.32 + client_close(writer);
4.33 + return;
4.34 + }
4.35 +
4.36 + /* Write directory entries to the pipe, closing the pipe when finished. */
4.37 +
4.38 + while ((dirent = readdir(dir)) != NULL)
4.39 + {
4.40 + offset_t nwritten = 0;
4.41 +
4.42 + while (nwritten < dirent->d_reclen)
4.43 + nwritten += client_write(writer, (const void *) (dirent + nwritten), dirent->d_reclen - nwritten);
4.44 + }
4.45 +
4.46 + client_close(writer);
4.47 +}
4.48 +
4.49 +
4.50 +
4.51 HostFileOpener::~HostFileOpener()
4.52 {
4.53 }
4.54 @@ -50,6 +89,33 @@
4.55 return (st.st_mode & S_IFREG) ? true : false;
4.56 }
4.57
4.58 +long HostFileOpener::get_directory(const char *path, flags_t flags, fileid_t fileid, offset_t *size, l4_cap_idx_t *cap)
4.59 +{
4.60 + file_t *reader, *writer;
4.61 +
4.62 + // NOTE: Might be more appropriate to use lower-level file operations to
4.63 + // NOTE: avoid unnecessary mapping of the reader's memory region.
4.64 +
4.65 + long err = client_pipe(&reader, &writer);
4.66 +
4.67 + if (err)
4.68 + return err;
4.69 +
4.70 + *size = reader->size;
4.71 + *cap = reader->ref;
4.72 +
4.73 + /* Discard the reader structure but do not close the reader itself. */
4.74 +
4.75 + delete reader;
4.76 +
4.77 + /* Spawn a independent thread for reading the directory details and writing
4.78 + them to the pipe. */
4.79 +
4.80 + std::thread(read_directory, path, writer).detach();
4.81 +
4.82 + return L4_EOK;
4.83 +}
4.84 +
4.85 /* Return a file identifier for the given 'path'. */
4.86
4.87 long HostFileOpener::get_fileid(const char *path, flags_t flags, fileid_t *fileid)
5.1 --- a/tests/dstest_host_readdir.cc Wed Aug 04 17:13:47 2021 +0200
5.2 +++ b/tests/dstest_host_readdir.cc Wed Aug 04 17:30:14 2021 +0200
5.3 @@ -1,7 +1,7 @@
5.4 /*
5.5 - * Test directory reading.
5.6 + * Test directory reading operations.
5.7 *
5.8 - * Copyright (C) 2021 Paul Boddie <paul@boddie.org.uk>
5.9 + * Copyright (C) 2020, 2021 Paul Boddie <paul@boddie.org.uk>
5.10 *
5.11 * This program is free software; you can redistribute it and/or
5.12 * modify it under the terms of the GNU General Public License as
5.13 @@ -19,34 +19,113 @@
5.14 * Boston, MA 02110-1301, USA
5.15 */
5.16
5.17 +#include <l4/re/env.h>
5.18 +#include <l4/sys/err.h>
5.19 +
5.20 #include <dirent.h>
5.21 #include <stdio.h>
5.22 +#include <string.h>
5.23 +#include <stdlib.h>
5.24 +
5.25 +#include <fsclient/client.h>
5.26 +#include <systypes/fcntl.h>
5.27
5.28
5.29
5.30 +#define DIRENT_CORE_SIZE (sizeof(struct dirent) - sizeof(((struct dirent *) 0)->d_name))
5.31 +
5.32 int main(int argc, char *argv[])
5.33 {
5.34 if (argc < 2)
5.35 {
5.36 - printf("Need directory name.\n");
5.37 + printf("Need a directory name and an optional user identifier (if used with a filesystem).\n");
5.38 + return 1;
5.39 + }
5.40 +
5.41 + char *filename = argv[1];
5.42 + bool have_uid = (argc > 2) && strlen(argv[2]);
5.43 + sys_uid_t uid = have_uid ? atoi(argv[2]) : 0;
5.44 + file_t *file;
5.45 +
5.46 + /* With a user, open a user-specific file opener. */
5.47 +
5.48 + if (have_uid)
5.49 + {
5.50 + l4_cap_idx_t opener = client_open_for_user((user_t) {uid, uid, 0022});
5.51 +
5.52 + if (l4_is_invalid_cap(opener))
5.53 + {
5.54 + printf("Could not obtain opener for file.\n");
5.55 + return 1;
5.56 + }
5.57 +
5.58 + /* Invoke the open method to receive the file reference. */
5.59 +
5.60 + file = client_open_using(filename, O_DIRECTORY, opener);
5.61 + }
5.62 + else
5.63 + {
5.64 + file = client_open(filename, O_DIRECTORY);
5.65 + }
5.66 +
5.67 + if (file == NULL)
5.68 + {
5.69 + printf("Could not obtain directory.\n");
5.70 return 1;
5.71 }
5.72
5.73 - /* Obtain directory name. */
5.74 + // NOTE: To be replaced by a proper mechanism identifying the nature of each
5.75 + // NOTE: obtained object.
5.76 +
5.77 + file->can_mmap = 0;
5.78 + file->has_size = 0;
5.79
5.80 - char *filename = argv[1];
5.81 - DIR *dir = opendir(filename);
5.82 + /* Register the reader for notification. */
5.83 +
5.84 + long err = client_set_blocking(file, NOTIFY_CONTENT_AVAILABLE);
5.85
5.86 - if (dir == NULL)
5.87 + if (err)
5.88 {
5.89 - printf("Could not obtain directory: %s\n", filename);
5.90 + printf("Could not subscribe to notifications: %s\n", l4sys_errtostr(err));
5.91 return 1;
5.92 }
5.93
5.94 - struct dirent *dirent;
5.95 + char buffer[DIRENT_CORE_SIZE];
5.96 + offset_t nread = client_read(file, buffer, DIRENT_CORE_SIZE);
5.97 + offset_t total = 0;
5.98 +
5.99 + while (nread)
5.100 + {
5.101 + total += nread;
5.102 +
5.103 + if (total == DIRENT_CORE_SIZE)
5.104 + {
5.105 + struct dirent *dirent = (struct dirent *) buffer;
5.106 + int remaining = dirent->d_reclen - DIRENT_CORE_SIZE;
5.107 + char entry[DIRENT_CORE_SIZE + remaining], *current;
5.108 +
5.109 + memcpy(entry, buffer, DIRENT_CORE_SIZE);
5.110 + current = entry + DIRENT_CORE_SIZE;
5.111
5.112 - while ((dirent = readdir(dir)) != NULL)
5.113 - printf("> %s\n", dirent->d_name);
5.114 + do
5.115 + {
5.116 + nread = client_read(file, current, remaining);
5.117 + remaining -= nread;
5.118 + current += nread;
5.119 + }
5.120 + while (nread && remaining);
5.121 +
5.122 + if (remaining)
5.123 + break;
5.124 +
5.125 + dirent = (struct dirent *) entry;
5.126 + printf("> %s\n", dirent->d_name);
5.127 +
5.128 + total = 0;
5.129 + }
5.130 +
5.131 + nread = client_read(file, buffer, DIRENT_CORE_SIZE);
5.132 + }
5.133
5.134 printf("Directory shown.\n");
5.135