1.1 --- a/libfsserver/lib/files/ext2_file_opener.cc Sat Aug 07 22:55:39 2021 +0200
1.2 +++ b/libfsserver/lib/files/ext2_file_opener.cc Sat Aug 07 23:19:20 2021 +0200
1.3 @@ -81,7 +81,7 @@
1.4 {
1.5 /* Subscribe to space and closure notifications on the pipe. */
1.6
1.7 - long err = client_set_blocking(writer, NOTIFY_SPACE_AVAILABLE);
1.8 + long err = client_set_blocking(writer, NOTIFY_SPACE_AVAILABLE | NOTIFY_PEER_CLOSED);
1.9
1.10 if (err)
1.11 {
1.12 @@ -135,16 +135,20 @@
1.13 *size = reader->size;
1.14 *cap = reader->ref;
1.15
1.16 - /* Discard the reader structure but do not close the reader itself. */
1.17 -
1.18 - delete reader;
1.19 -
1.20 /* Spawn a independent thread for reading the directory details and writing
1.21 them to the pipe. */
1.22
1.23 std::thread(_read_directory, this, fileid, writer).detach();
1.24
1.25 - return L4_EOK;
1.26 + /* Discard the reader structure but preserve the capability. */
1.27 +
1.28 + reader->ref = L4_INVALID_CAP;
1.29 + file_close(reader);
1.30 +
1.31 + /* Return an indication that the capability will be propagated and not
1.32 + retained. This is explicitly supported by the opener context. */
1.33 +
1.34 + return IPC_MESSAGE_SENT;
1.35 }
1.36
1.37 /* Thread payload helper method. */
1.38 @@ -195,10 +199,12 @@
1.39
1.40 /* Write the structure to the pipe. */
1.41
1.42 - offset_t nwritten = 0;
1.43 + offset_t nwritten = client_write(dir->writer, (const void *) dirent, reclen);
1.44
1.45 - while (nwritten < reclen)
1.46 - nwritten += client_write(dir->writer, (const void *) (dirent + nwritten), reclen - nwritten);
1.47 + /* Stop writing if the pipe is closed. */
1.48 +
1.49 + if (nwritten < reclen)
1.50 + return DIRENT_ABORT;
1.51
1.52 return 0;
1.53 }
2.1 --- a/libfsserver/lib/files/host_file_opener.cc Sat Aug 07 22:55:39 2021 +0200
2.2 +++ b/libfsserver/lib/files/host_file_opener.cc Sat Aug 07 23:19:20 2021 +0200
2.3 @@ -40,7 +40,7 @@
2.4
2.5 /* Subscribe to space and closure notifications on the pipe. */
2.6
2.7 - long err = client_set_blocking(writer, NOTIFY_SPACE_AVAILABLE);
2.8 + long err = client_set_blocking(writer, NOTIFY_SPACE_AVAILABLE | NOTIFY_PEER_CLOSED);
2.9
2.10 if (err)
2.11 {
2.12 @@ -52,10 +52,12 @@
2.13
2.14 while ((dirent = readdir(dir)) != NULL)
2.15 {
2.16 - offset_t nwritten = 0;
2.17 + offset_t nwritten = client_write(writer, (const void *) dirent, dirent->d_reclen);
2.18
2.19 - while (nwritten < dirent->d_reclen)
2.20 - nwritten += client_write(writer, (const void *) (dirent + nwritten), dirent->d_reclen - nwritten);
2.21 + /* Stop writing if the pipe is closed. */
2.22 +
2.23 + if (nwritten < dirent->d_reclen)
2.24 + break;
2.25 }
2.26
2.27 client_close(writer);
2.28 @@ -104,9 +106,10 @@
2.29 *size = reader->size;
2.30 *cap = reader->ref;
2.31
2.32 - /* Discard the reader structure but do not close the reader itself. */
2.33 + /* Discard the reader structure but preserve the capability. */
2.34
2.35 - delete reader;
2.36 + reader->ref = L4_INVALID_CAP;
2.37 + file_close(reader);
2.38
2.39 /* Spawn a independent thread for reading the directory details and writing
2.40 them to the pipe. */
3.1 --- a/libfsserver/lib/files/opener_context_resource.cc Sat Aug 07 22:55:39 2021 +0200
3.2 +++ b/libfsserver/lib/files/opener_context_resource.cc Sat Aug 07 23:19:20 2021 +0200
3.3 @@ -19,6 +19,8 @@
3.4 * Boston, MA 02110-1301, USA
3.5 */
3.6
3.7 +#include <ipc/cap_alloc.h>
3.8 +
3.9 #include "opener_context_resource.h"
3.10 #include "opener_context_object_server.h"
3.11 #include "opener_resource.h"
3.12 @@ -72,7 +74,19 @@
3.13 if (path == NULL)
3.14 return -L4_EINVAL;
3.15
3.16 - return _opener->open(path, flags, size, file);
3.17 + long err = _opener->open(path, flags, size, file);
3.18 +
3.19 + /* Handle propagated capabilities. By indicating the special status, the
3.20 + operation is first completed and then the capability is discarded. */
3.21 +
3.22 + if (err == IPC_MESSAGE_SENT)
3.23 + {
3.24 + complete_OpenerContext_open(*size, *file);
3.25 + ipc_cap_free_um(*file);
3.26 + return IPC_MESSAGE_SENT;
3.27 + }
3.28 +
3.29 + return err;
3.30 }
3.31
3.32 // vim: tabstop=4 expandtab shiftwidth=4
4.1 --- a/tests/dstest_file_readdir.cc Sat Aug 07 22:55:39 2021 +0200
4.2 +++ b/tests/dstest_file_readdir.cc Sat Aug 07 23:19:20 2021 +0200
4.3 @@ -34,6 +34,94 @@
4.4
4.5 #define DIRENT_CORE_SIZE (sizeof(struct dirent) - sizeof(((struct dirent *) 0)->d_name))
4.6
4.7 +
4.8 +
4.9 +/* Return a directory entry. This must be freed by the caller after use. */
4.10 +
4.11 +static struct dirent *read_directory_entry(file_t *file)
4.12 +{
4.13 + char buffer[DIRENT_CORE_SIZE];
4.14 + offset_t nread = client_read(file, buffer, DIRENT_CORE_SIZE);
4.15 +
4.16 + /* Stop if no new structure can be successfully read. */
4.17 +
4.18 + if (nread != DIRENT_CORE_SIZE)
4.19 + return NULL;
4.20 +
4.21 + struct dirent *dirent = (struct dirent *) buffer;
4.22 + offset_t remaining = dirent->d_reclen - DIRENT_CORE_SIZE;
4.23 +
4.24 + /* Allocate a buffer for the complete structure. */
4.25 +
4.26 + char *entry = (char *) calloc(DIRENT_CORE_SIZE + remaining, sizeof(char));
4.27 +
4.28 + if (entry == NULL)
4.29 + return NULL;
4.30 +
4.31 + /* Copy the start of the entry into a new buffer. */
4.32 +
4.33 + memcpy(entry, buffer, DIRENT_CORE_SIZE);
4.34 +
4.35 + /* Append to the entry buffer. */
4.36 +
4.37 + char *current = entry + DIRENT_CORE_SIZE;
4.38 +
4.39 + nread = client_read(file, current, remaining);
4.40 +
4.41 + /* Stop if no complete structure can be successfully read. */
4.42 +
4.43 + if (nread != remaining)
4.44 + {
4.45 + free(entry);
4.46 + return NULL;
4.47 + }
4.48 +
4.49 + return (struct dirent *) entry;
4.50 +}
4.51 +
4.52 +
4.53 +
4.54 +static file_t *open_file(char *filename, bool have_uid, sys_uid_t uid)
4.55 +{
4.56 + /* With a user, open a user-specific file opener. */
4.57 +
4.58 + if (have_uid)
4.59 + {
4.60 + l4_cap_idx_t opener = client_open_for_user((user_t) {uid, uid, 0022});
4.61 +
4.62 + if (l4_is_invalid_cap(opener))
4.63 + {
4.64 + printf("Could not obtain opener for file.\n");
4.65 + return NULL;
4.66 + }
4.67 +
4.68 + /* Invoke the open method to receive the file reference. */
4.69 +
4.70 + return client_open_using(filename, O_DIRECTORY, opener);
4.71 + }
4.72 + else
4.73 + {
4.74 + return client_open(filename, O_DIRECTORY);
4.75 + }
4.76 +}
4.77 +
4.78 +
4.79 +
4.80 +static long open_directory(file_t *file)
4.81 +{
4.82 + // NOTE: To be replaced by a proper mechanism identifying the nature of each
4.83 + // NOTE: obtained object.
4.84 +
4.85 + file->can_mmap = 0;
4.86 + file->has_size = 0;
4.87 +
4.88 + /* Register the reader for notification. */
4.89 +
4.90 + return client_set_blocking(file, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED);
4.91 +}
4.92 +
4.93 +
4.94 +
4.95 int main(int argc, char *argv[])
4.96 {
4.97 if (argc < 2)
4.98 @@ -45,28 +133,7 @@
4.99 char *filename = argv[1];
4.100 bool have_uid = (argc > 2) && strlen(argv[2]);
4.101 sys_uid_t uid = have_uid ? atoi(argv[2]) : 0;
4.102 - file_t *file;
4.103 -
4.104 - /* With a user, open a user-specific file opener. */
4.105 -
4.106 - if (have_uid)
4.107 - {
4.108 - l4_cap_idx_t opener = client_open_for_user((user_t) {uid, uid, 0022});
4.109 -
4.110 - if (l4_is_invalid_cap(opener))
4.111 - {
4.112 - printf("Could not obtain opener for file.\n");
4.113 - return 1;
4.114 - }
4.115 -
4.116 - /* Invoke the open method to receive the file reference. */
4.117 -
4.118 - file = client_open_using(filename, O_DIRECTORY, opener);
4.119 - }
4.120 - else
4.121 - {
4.122 - file = client_open(filename, O_DIRECTORY);
4.123 - }
4.124 + file_t *file = open_file(filename, have_uid, uid);
4.125
4.126 if (file == NULL)
4.127 {
4.128 @@ -74,15 +141,7 @@
4.129 return 1;
4.130 }
4.131
4.132 - // NOTE: To be replaced by a proper mechanism identifying the nature of each
4.133 - // NOTE: obtained object.
4.134 -
4.135 - file->can_mmap = 0;
4.136 - file->has_size = 0;
4.137 -
4.138 - /* Register the reader for notification. */
4.139 -
4.140 - long err = client_set_blocking(file, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED);
4.141 + long err = open_directory(file);
4.142
4.143 if (err)
4.144 {
4.145 @@ -90,48 +149,44 @@
4.146 return 1;
4.147 }
4.148
4.149 - char buffer[DIRENT_CORE_SIZE];
4.150 - offset_t nread = client_read(file, buffer, DIRENT_CORE_SIZE);
4.151 - offset_t total = 0;
4.152 -
4.153 - while (1)
4.154 - {
4.155 - if (!nread && (file->notifications & NOTIFY_PEER_CLOSED))
4.156 - break;
4.157 -
4.158 - total += nread;
4.159 -
4.160 - if (total == DIRENT_CORE_SIZE)
4.161 - {
4.162 - struct dirent *dirent = (struct dirent *) buffer;
4.163 - int remaining = dirent->d_reclen - DIRENT_CORE_SIZE;
4.164 - char entry[DIRENT_CORE_SIZE + remaining], *current;
4.165 + struct dirent *dirent;
4.166
4.167 - memcpy(entry, buffer, DIRENT_CORE_SIZE);
4.168 - current = entry + DIRENT_CORE_SIZE;
4.169 -
4.170 - do
4.171 - {
4.172 - nread = client_read(file, current, remaining);
4.173 - remaining -= nread;
4.174 - current += nread;
4.175 - }
4.176 - while (nread && remaining);
4.177 -
4.178 - if (remaining)
4.179 - break;
4.180 -
4.181 - dirent = (struct dirent *) entry;
4.182 - printf("> %s\n", dirent->d_name);
4.183 -
4.184 - total = 0;
4.185 - }
4.186 -
4.187 - nread = client_read(file, buffer, DIRENT_CORE_SIZE);
4.188 + while ((dirent = read_directory_entry(file)) != NULL)
4.189 + {
4.190 + printf("> %s\n", dirent->d_name);
4.191 + free(dirent);
4.192 }
4.193
4.194 printf("Directory shown.\n");
4.195
4.196 + /* Open again, reading a single entry only. */
4.197 +
4.198 + file = open_file(filename, have_uid, uid);
4.199 +
4.200 + if (file == NULL)
4.201 + {
4.202 + printf("Could not obtain directory.\n");
4.203 + return 1;
4.204 + }
4.205 +
4.206 + err = open_directory(file);
4.207 +
4.208 + if (err)
4.209 + {
4.210 + printf("Could not subscribe to notifications: %s\n", l4sys_errtostr(err));
4.211 + return 1;
4.212 + }
4.213 +
4.214 + dirent = read_directory_entry(file);
4.215 +
4.216 + if (dirent != NULL)
4.217 + {
4.218 + printf("> %s\n", dirent->d_name);
4.219 + free(dirent);
4.220 + }
4.221 +
4.222 + printf("Entry shown.\n");
4.223 +
4.224 return 0;
4.225 }
4.226