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 fs_object_t fsobj; 161 l4_cap_idx_t server; 162 long err; 163 164 /* Obtain the filesystem capability. */ 165 166 err = ipc_message_import_capability(msg, 0, &server); 167 if (err) 168 { 169 ipc_message_send_error(msg, -L4_EIO); 170 return; 171 } 172 173 /* Obtain the dataspace. */ 174 175 err = fsdesc_import_dataspace(msg, 1, &fsobj); 176 if (err) 177 { 178 ipc_message_send_error(msg, -L4_EIO); 179 return; 180 } 181 182 /* Obtain the object's path. */ 183 184 char *path = fsdesc_get_name(&fsobj); 185 186 if (path == NULL) 187 { 188 ipc_message_send_error(msg, -L4_EINVAL); 189 return; 190 } 191 192 /* Mount the supplied capability. */ 193 194 mount_at_path(server, path); 195 196 /* Free the capability and buffer from this task. */ 197 198 fsdesc_deallocate_buffer(&fsobj); 199 } 200 201 /* Non-exported methods. */ 202 203 void mount_at_path(l4_cap_idx_t server, const char *path) 204 { 205 /* NOTE: Should terminate with path separator if absent. 206 NOTE: Should also ensure zero termination. */ 207 208 _servers.insert(MountTableEntry(strdup(path), server)); 209 } 210 211 MountTableIterator find_mountpoint(const char *path) 212 { 213 /* Search only until "later" paths are encountered. Such paths will either 214 incorporate components that are themselves greater than those in the path 215 or will be longer than the path. */ 216 217 MountTableIterator it, found = _servers.end(), 218 limit = _servers.upper_bound(path); 219 size_t length, longest = 0; 220 221 /* Find the largest matching path. */ 222 223 for (it = _servers.begin(); it != limit; it++) 224 { 225 length = match_path(path, it->first); 226 227 if (length >= longest) 228 found = it; 229 } 230 231 return found; 232 } 233 234 ssize_t match_path(const char *path, const char *mountpoint) 235 { 236 size_t length = strlen(mountpoint); 237 int suffixed = (mountpoint[length - 1] == '/'); 238 239 /* The empty mountpoint always matches; 240 suffixed mountpoints may match as prefixes; 241 suffixed mountpoints may match unsuffixed paths; 242 unsuffixed mountpoints must match exactly. */ 243 244 /* NOTE: Unsuffixed mountpoints might support subpaths by insisting on 245 a path separator as the first subsequent character. */ 246 247 if (!length || 248 (suffixed && !strncmp(mountpoint, path, length)) || 249 (suffixed && (strlen(path) == length - 1) && !strncmp(mountpoint, path, length - 1)) || 250 !strcmp(mountpoint, path)) 251 return length; 252 253 return -1; 254 } 255 }; 256 257 258 259 int main(int argc, char *argv[]) 260 { 261 FILE *fp; 262 char buffer[256], *sep; 263 l4_cap_idx_t fscap; 264 265 if (argc < 2) 266 { 267 printf("Need a mount table.\n"); 268 return 1; 269 } 270 271 /* Initialise and register a new server object. */ 272 273 Vfs_server server_obj; 274 275 if (server_obj.bind("export")) 276 { 277 printf("Could not bind thread.\n"); 278 return 1; 279 } 280 281 /* Mount filesystems. */ 282 283 fp = fopen(argv[1], "r"); 284 if (fp == NULL) 285 { 286 printf("Could not open %s.\n", argv[1]); 287 return 1; 288 } 289 290 /* Obtain capabilities for mountpoints. */ 291 292 while (fgets(buffer, 256, fp) != NULL) 293 { 294 sep = strchr(buffer, (int) '\t'); 295 if (sep == NULL) 296 continue; 297 298 /* Terminate newline-suffixed strings. */ 299 300 if (sep[strlen(sep) - 1] == '\n') 301 sep[strlen(sep) - 1] = '\0'; 302 303 /* Obtain the named capability. */ 304 305 fscap = l4re_env_get_cap(sep + 1); 306 307 if (l4_is_invalid_cap(fscap)) 308 continue; 309 310 /* Terminate the path and register the mountpoint. */ 311 312 *sep = '\0'; 313 server_obj.mount_at_path(fscap, buffer); 314 } 315 316 fclose(fp); 317 318 /* Enter the IPC server loop. */ 319 320 server_obj.loop(); 321 return 0; 322 }