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