1 /* 2 * Access several filesystems. 3 * 4 * Copyright (C) 2018, 2019 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/util/util.h> 23 24 #include <l4/re/c/dataspace.h> 25 #include <l4/re/env.h> 26 #include <l4/re/c/util/cap_alloc.h> 27 #include <l4/sys/err.h> 28 #include <l4/sys/kernel_object.h> 29 #include <l4/sys/types.h> 30 31 #include <stdlib.h> 32 #include <stdio.h> 33 #include <string.h> 34 #include <unistd.h> 35 36 #include <fsclient/fs_client.h> 37 #include <fsclient/fsdesc.h> 38 #include <fsclient/fs_ipc.h> 39 #include <fsserver/server.h> 40 #include <ipc/util_ipc.h> 41 #include <ipc/server.h> 42 #include "fspath.h" 43 44 45 46 /* Virtual filesystem server. */ 47 48 class Vfs_server : public OpeningServer 49 { 50 private: 51 /* NOTE: Improvised structure! */ 52 53 l4_cap_idx_t _servers[10]; 54 const char *_mountpoints[10]; 55 int _mounted = 0; 56 57 public: 58 explicit Vfs_server() 59 : OpeningServer() 60 { 61 } 62 63 void dispatch(ipc_message_t *msg) 64 { 65 switch (l4_msgtag_label(msg->tag)) 66 { 67 case Fs_op_mount: 68 mount(msg); 69 break; 70 71 case Fs_op_open: 72 open(msg); 73 break; 74 75 case Fs_op_getfs: 76 getfs(msg); 77 break; 78 79 default: 80 send_error(msg, -L4_EBADPROTO); 81 break; 82 } 83 } 84 85 void getfs(ipc_message_t *msg) 86 { 87 _open(Fs_op_getfs, msg); 88 } 89 90 void open(ipc_message_t *msg) 91 { 92 _open(Fs_op_open, msg); 93 } 94 95 void _open(int op, ipc_message_t *msg) 96 { 97 int flags; 98 l4_cap_idx_t server, irq; 99 l4re_ds_t ds; 100 size_t size; 101 char *buffer; 102 long err; 103 104 /* Obtain the flags. */ 105 106 flags = ipc_message_get_word(msg, 0); 107 108 /* Obtain the dataspace and IRQ object. */ 109 110 err = ipc_message_import_dataspace(msg, 0, &ds, (l4_addr_t *) &buffer) || 111 ipc_message_import_capability(msg, 1, &irq); 112 if (err) 113 { 114 send_error(msg, -L4_EIO); 115 return; 116 } 117 118 /* Match the path to a mountpoint. */ 119 120 int selected = find_mountpoint(buffer); 121 122 if (selected < 0) 123 { 124 send_error(msg, -L4_ENOENT); 125 return; 126 } 127 128 /* Rewrite the path for the selected filesystem. */ 129 130 rewrite_path(buffer, _mountpoints[selected]); 131 132 /* Find the path within the selected filesystem. */ 133 134 /* Propagate the message to the identified filesystem. 135 NOTE: The L4_MSGTAG_PROPAGATE flag is not supported by Fiasco.OC, so this 136 NOTE: object has to act as intermediary. */ 137 138 err = fs_ipc_open(flags, _servers[selected], op, ds, irq, &server, &size); 139 if (err) 140 { 141 send_error(msg, -L4_EIO); 142 return; 143 } 144 145 /* Free the capability and buffer from this task. */ 146 147 discard_dataspace(ds, (l4_addr_t) buffer); 148 149 /* Return the file size. */ 150 151 ipc_message_add_word(msg, size); 152 153 /* Export and eventually free the server reference from this task. */ 154 155 ipc_message_propagate_item(msg, server); 156 } 157 158 void mount(ipc_message_t *msg) 159 { 160 l4_cap_idx_t server; 161 l4re_ds_t ds; 162 char *buffer; 163 long err; 164 165 /* Obtain the filesystem capability. */ 166 167 err = ipc_message_import_capability(msg, 0, &server); 168 if (err) 169 { 170 send_error(msg, -L4_EIO); 171 return; 172 } 173 174 /* Obtain the dataspace. */ 175 176 err = ipc_message_import_dataspace(msg, 1, &ds, (l4_addr_t *) &buffer); 177 if (err) 178 { 179 send_error(msg, -L4_EIO); 180 return; 181 } 182 183 /* Obtain the object's path. */ 184 185 char *path = buffer; 186 187 /* Mount the supplied capability. */ 188 189 mount_at_path(server, path); 190 191 /* Free the capability and buffer from this task. */ 192 193 discard_dataspace(ds, (l4_addr_t) buffer); 194 } 195 196 /* Non-exported methods. */ 197 198 void mount_at_path(l4_cap_idx_t server, const char *path) 199 { 200 /* NOTE: Should return error, terminate with path separator if absent. */ 201 202 if (_mounted >= 10) 203 return; 204 205 _servers[_mounted] = server; 206 _mountpoints[_mounted] = strdup(path); 207 208 _mounted++; 209 } 210 211 int find_mountpoint(const char *path) 212 { 213 int i, matching; 214 size_t length, longest; 215 216 if (!_mounted) 217 return -1; 218 219 matching = -1; 220 longest = 0; 221 222 /* Find the largest matching path. */ 223 224 for (i = 0; i < _mounted; i++) 225 { 226 length = strlen(_mountpoints[i]); 227 228 /* The empty mountpoint always matches; 229 suffixed mountpoints may match as prefixes; 230 suffixed mountpoints may match unsuffixed paths; 231 unsuffixed mountpoints must match exactly. */ 232 233 /* NOTE: Unsuffixed mountpoints might support subpaths by insisting on 234 a path separator as the first subsequent character. */ 235 236 if (!length || 237 ((_mountpoints[i][length - 1] == '/') && !strncmp(_mountpoints[i], path, length)) || 238 ((_mountpoints[i][length - 1] == '/') && (strlen(path) == length - 1) && !strncmp(_mountpoints[i], path, length - 1)) || 239 !strcmp(_mountpoints[i], path)) 240 { 241 if (length >= longest) 242 matching = i; 243 } 244 } 245 246 return matching; 247 } 248 }; 249 250 251 252 int main(int argc, char *argv[]) 253 { 254 FILE *fp; 255 char buffer[256], *sep; 256 l4_cap_idx_t fscap, server; 257 258 if (argc < 2) 259 { 260 printf("Need a mount table.\n"); 261 return 1; 262 } 263 264 /* Initialise and register a new server object. */ 265 266 Vfs_server server_obj; 267 268 if (ipc_server_bind("export", l4_umword_t(&server_obj), &server)) 269 { 270 printf("Could not bind thread.\n"); 271 return 1; 272 } 273 274 /* Mount filesystems. */ 275 276 fp = fopen(argv[1], "r"); 277 if (fp == NULL) 278 { 279 printf("Could not open %s.\n", argv[1]); 280 return 1; 281 } 282 283 /* Obtain capabilities for mountpoints. */ 284 285 while (fgets(buffer, 256, fp) != NULL) 286 { 287 sep = strchr(buffer, (int) '\t'); 288 if (sep == NULL) 289 continue; 290 291 /* Terminate newline-suffixed strings. */ 292 293 if (sep[strlen(sep) - 1] == '\n') 294 sep[strlen(sep) - 1] = '\0'; 295 296 /* Obtain the named capability. */ 297 298 fscap = l4re_env_get_cap(sep + 1); 299 300 if (l4_is_invalid_cap(fscap)) 301 continue; 302 303 /* Terminate the path and register the mountpoint. */ 304 305 *sep = '\0'; 306 server_obj.mount_at_path(fscap, buffer); 307 } 308 309 fclose(fp); 310 311 /* Enter the IPC server loop. */ 312 313 server_obj.loop(); 314 return 0; 315 }