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/sys/err.h> 27 #include <l4/sys/types.h> 28 29 #include <stdlib.h> 30 #include <stdio.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #include <map> 35 36 #include <fsclient/fsdesc.h> 37 #include <fsclient/fs_ipc.h> 38 #include <fsclient/ops.h> 39 #include <fsserver/server.h> 40 #include "fspath.h" 41 42 43 44 /* Mountpoint-to-server mapping. */ 45 46 typedef std::map<const char *, l4_cap_idx_t> MountTable; 47 typedef std::map<const char *, l4_cap_idx_t>::iterator MountTableIterator; 48 typedef std::map<const char *, l4_cap_idx_t>::value_type MountTableEntry; 49 50 51 52 /* Virtual filesystem server. */ 53 54 class Vfs_server : public OpeningServer 55 { 56 private: 57 MountTable _servers; 58 59 public: 60 explicit Vfs_server() 61 : OpeningServer() 62 { 63 } 64 65 void dispatch(ipc_message_t *msg) 66 { 67 switch (l4_msgtag_label(msg->tag)) 68 { 69 case Fs_op_mount: 70 mount(msg); 71 break; 72 73 case Fs_op_open: 74 open(msg); 75 break; 76 77 case Fs_op_getfs: 78 getfs(msg); 79 break; 80 81 default: 82 ipc_message_send_error(msg, -L4_EBADPROTO); 83 break; 84 } 85 } 86 87 void getfs(ipc_message_t *msg) 88 { 89 _open(Fs_op_getfs, msg); 90 } 91 92 void open(ipc_message_t *msg) 93 { 94 _open(Fs_op_open, msg); 95 } 96 97 void _open(int op, ipc_message_t *msg) 98 { 99 file_descriptor_t desc; 100 int flags; 101 long err; 102 103 /* Obtain the flags. */ 104 105 flags = ipc_message_get_word(msg, 0); 106 107 /* Obtain the dataspace and IRQ object. */ 108 109 err = fsdesc_import_object(msg, 0, &desc.obj); 110 if (err) 111 { 112 ipc_message_send_error(msg, -L4_EIO); 113 return; 114 } 115 116 /* Match the path to a mountpoint. */ 117 118 MountTableIterator entry = find_mountpoint(desc.obj.buffer); 119 120 if (entry == _servers.end()) 121 { 122 ipc_message_send_error(msg, -L4_ENOENT); 123 return; 124 } 125 126 /* Rewrite the path for the selected filesystem. */ 127 128 rewrite_path(desc.obj.buffer, entry->first); 129 130 /* Find the path within the selected filesystem. */ 131 132 /* Propagate the message to the identified filesystem. 133 NOTE: The L4_MSGTAG_PROPAGATE flag is not supported by Fiasco.OC, so this 134 NOTE: object has to act as intermediary. */ 135 136 desc.server = entry->second; 137 138 err = fs_ipc_open(&desc, flags, op); 139 if (err) 140 { 141 ipc_message_send_error(msg, -L4_EIO); 142 return; 143 } 144 145 /* Free the capabilities and buffer from this task. */ 146 147 fsdesc_deallocate_object(&desc.obj); 148 149 /* Return the file size. */ 150 151 ipc_message_add_word(msg, desc.obj.size); 152 153 /* Export and eventually free the server reference from this task. */ 154 155 ipc_message_propagate_item(msg, desc.ref); 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 ipc_message_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 ipc_message_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 ipc_message_discard_dataspace(msg, 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 terminate with path separator if absent. 201 NOTE: Should also ensure zero termination. */ 202 203 _servers.insert(MountTableEntry(strdup(path), server)); 204 } 205 206 MountTableIterator find_mountpoint(const char *path) 207 { 208 /* Search only until "later" paths are encountered. Such paths will either 209 incorporate components that are themselves greater than those in the path 210 or will be longer than the path. */ 211 212 MountTableIterator it, found = _servers.end(), 213 limit = _servers.upper_bound(path); 214 size_t length, longest = 0; 215 216 /* Find the largest matching path. */ 217 218 for (it = _servers.begin(); it != limit; it++) 219 { 220 length = match_path(path, it->first); 221 222 if (length >= longest) 223 found = it; 224 } 225 226 return found; 227 } 228 229 ssize_t match_path(const char *path, const char *mountpoint) 230 { 231 size_t length = strlen(mountpoint); 232 int suffixed = (mountpoint[length - 1] == '/'); 233 234 /* The empty mountpoint always matches; 235 suffixed mountpoints may match as prefixes; 236 suffixed mountpoints may match unsuffixed paths; 237 unsuffixed mountpoints must match exactly. */ 238 239 /* NOTE: Unsuffixed mountpoints might support subpaths by insisting on 240 a path separator as the first subsequent character. */ 241 242 if (!length || 243 (suffixed && !strncmp(mountpoint, path, length)) || 244 (suffixed && (strlen(path) == length - 1) && !strncmp(mountpoint, path, length - 1)) || 245 !strcmp(mountpoint, path)) 246 return length; 247 248 return -1; 249 } 250 }; 251 252 253 254 int main(int argc, char *argv[]) 255 { 256 FILE *fp; 257 char buffer[256], *sep; 258 l4_cap_idx_t fscap; 259 260 if (argc < 2) 261 { 262 printf("Need a mount table.\n"); 263 return 1; 264 } 265 266 /* Initialise and register a new server object. */ 267 268 Vfs_server server_obj; 269 270 if (server_obj.bind("export")) 271 { 272 printf("Could not bind thread.\n"); 273 return 1; 274 } 275 276 /* Mount filesystems. */ 277 278 fp = fopen(argv[1], "r"); 279 if (fp == NULL) 280 { 281 printf("Could not open %s.\n", argv[1]); 282 return 1; 283 } 284 285 /* Obtain capabilities for mountpoints. */ 286 287 while (fgets(buffer, 256, fp) != NULL) 288 { 289 sep = strchr(buffer, (int) '\t'); 290 if (sep == NULL) 291 continue; 292 293 /* Terminate newline-suffixed strings. */ 294 295 if (sep[strlen(sep) - 1] == '\n') 296 sep[strlen(sep) - 1] = '\0'; 297 298 /* Obtain the named capability. */ 299 300 fscap = l4re_env_get_cap(sep + 1); 301 302 if (l4_is_invalid_cap(fscap)) 303 continue; 304 305 /* Terminate the path and register the mountpoint. */ 306 307 *sep = '\0'; 308 server_obj.mount_at_path(fscap, buffer); 309 } 310 311 fclose(fp); 312 313 /* Enter the IPC server loop. */ 314 315 server_obj.loop(); 316 return 0; 317 }