1 /* 2 * Access several filesystems. 3 * 4 * Copyright (C) 2018, 2019, 2020 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/re/env.h> 23 #include <l4/sys/err.h> 24 25 #include <stdlib.h> 26 #include <stdio.h> 27 #include <string.h> 28 #include <unistd.h> 29 30 #include <map> 31 32 #include <fsclient/fsdesc.h> 33 #include <fsclient/fsdesc_client.h> 34 #include "fspath.h" 35 36 /* Exposure of the resource. */ 37 38 #include <fsserver/file_resource_server.h> 39 40 /* Implemented interfaces. */ 41 42 #include <fsserver/user_filesystem_resource.h> 43 44 45 46 /* Mountpoint-to-server mapping. */ 47 48 typedef std::map<const char *, l4_cap_idx_t> MountTable; 49 typedef std::map<const char *, l4_cap_idx_t>::iterator MountTableIterator; 50 typedef std::map<const char *, l4_cap_idx_t>::value_type MountTableEntry; 51 52 53 54 /* Virtual filesystem resource. */ 55 56 class Vfs_resource : public UserFilesystemResource 57 { 58 private: 59 MountTable _servers; 60 61 public: 62 explicit Vfs_resource() 63 : UserFilesystemResource() 64 { 65 } 66 67 68 69 /* Obtain an object as a filesystem reference. */ 70 71 long getfs_object(fs_object_t *fsobj, l4_cap_idx_t *fs) 72 { 73 file_descriptor_t desc; 74 long err; 75 76 /* Obtain a suitable descriptor. */ 77 78 err = init_desc(&desc, fsobj); 79 80 if (err) 81 return err; 82 83 /* Return a reference to any directly-accessed object. */ 84 85 char *path = fsdesc_get_name(fsobj); 86 87 if ((path != NULL) && !strlen(path)) 88 { 89 *fs = desc.server; 90 return L4_EOK; 91 } 92 93 /* Obtain a context. */ 94 95 err = fs_context(&desc); 96 97 if (err) 98 return err; 99 100 /* Find the path within the selected filesystem. */ 101 102 strcpy(desc.cobj.buffer, fsobj->buffer); 103 104 /* Propagate the message to the identified filesystem. 105 The L4_MSGTAG_PROPAGATE flag is not supported by Fiasco.OC, so this 106 object has to act as intermediary. */ 107 108 err = fs_context_getfs(&desc); 109 110 if (err) 111 { 112 fsdesc_deallocate_context(&desc); 113 return err; 114 } 115 116 /* Free the context and its buffer from this task. */ 117 118 fsdesc_deallocate_context(&desc); 119 120 /* Export and eventually free the server reference from this task. */ 121 122 *fs = discard_cap(desc.ref); 123 124 return L4_EOK; 125 } 126 127 /* Mount a filesystem at a particular location. */ 128 129 long mount_object(fs_object_t *fsobj, l4_cap_idx_t fs) 130 { 131 /* Obtain the object's path. */ 132 133 char *path = fsdesc_get_name(fsobj); 134 135 if (path == NULL) 136 return -L4_EINVAL; 137 138 /* Mount the supplied capability. */ 139 140 mount_at_path(fs, path); 141 142 return L4_EOK; 143 } 144 145 /* Open a filesystem object. */ 146 147 long open_object(fs_object_t *fsobj, int flags, size_t *size, l4_cap_idx_t *file) 148 { 149 file_descriptor_t desc; 150 long err; 151 152 /* Obtain a suitable descriptor. */ 153 154 err = init_desc(&desc, fsobj); 155 156 if (err) 157 return err; 158 159 /* Obtain a context. */ 160 161 err = fs_context(&desc); 162 163 if (err) 164 return err; 165 166 /* Find the path within the selected filesystem. */ 167 168 strcpy(desc.cobj.buffer, fsobj->buffer); 169 170 /* Propagate the message to the identified filesystem. 171 The L4_MSGTAG_PROPAGATE flag is not supported by Fiasco.OC, so this 172 object has to act as intermediary. */ 173 174 err = fs_context_open(&desc, flags); 175 176 if (err) 177 { 178 fsdesc_deallocate_context(&desc); 179 return err; 180 } 181 182 /* Free the context and its buffer from this task. */ 183 184 fsdesc_deallocate_context(&desc); 185 186 /* Export and eventually free the server reference from this task. */ 187 188 *size = desc.obj.size; 189 *file = discard_cap(desc.ref); 190 191 return L4_EOK; 192 } 193 194 195 196 /* Non-exported methods. */ 197 198 /* Initialise a descriptor for accessing a mounted filesystem. */ 199 200 long init_desc(file_descriptor_t *desc, fs_object_t *obj) 201 { 202 /* Match the path to a mountpoint. */ 203 204 char *path = fsdesc_get_name(obj); 205 206 if (path == NULL) 207 return -L4_EINVAL; 208 209 MountTableIterator entry = find_mountpoint(path); 210 211 if (entry == _servers.end()) 212 return -L4_ENOENT; 213 214 /* Rewrite the path for the selected filesystem. */ 215 216 rewrite_path(path, entry->first); 217 218 /* Use the filesystem as the target of any query. */ 219 220 desc->server = entry->second; 221 222 return L4_EOK; 223 } 224 225 void mount_at_path(l4_cap_idx_t server, const char *path) 226 { 227 /* NOTE: Should terminate with path separator if absent. 228 NOTE: Should also ensure zero termination. */ 229 230 _servers.insert(MountTableEntry(strdup(path), server)); 231 } 232 233 MountTableIterator find_mountpoint(const char *path) 234 { 235 /* Search only until "later" paths are encountered. Such paths will either 236 incorporate components that are themselves greater than those in the path 237 or will be longer than the path. */ 238 239 MountTableIterator it, found = _servers.end(), 240 limit = _servers.upper_bound(path); 241 ssize_t length, longest = 0; 242 243 /* Find the largest matching path. */ 244 245 for (it = _servers.begin(); it != limit; it++) 246 { 247 length = match_path(path, it->first); 248 249 if (length >= longest) 250 found = it; 251 } 252 253 return found; 254 } 255 256 ssize_t match_path(const char *path, const char *mountpoint) 257 { 258 size_t length = strlen(mountpoint); 259 int suffixed = (mountpoint[length - 1] == '/'); 260 261 /* The empty mountpoint always matches; 262 suffixed mountpoints may match as prefixes; 263 suffixed mountpoints may match unsuffixed paths; 264 unsuffixed mountpoints must match exactly. */ 265 266 /* NOTE: Unsuffixed mountpoints might support subpaths by insisting on 267 a path separator as the first subsequent character. */ 268 269 if (!length || 270 (suffixed && !strncmp(mountpoint, path, length)) || 271 (suffixed && (strlen(path) == length - 1) && !strncmp(mountpoint, path, length - 1)) || 272 !strcmp(mountpoint, path)) 273 return length; 274 275 return -1; 276 } 277 }; 278 279 280 281 int main(int argc, char *argv[]) 282 { 283 FILE *fp; 284 char buffer[256], *sep; 285 l4_cap_idx_t fscap; 286 287 if (argc < 2) 288 { 289 printf("Need a mount table.\n"); 290 return 1; 291 } 292 293 /* Initialise and register a new server object. */ 294 295 Vfs_resource resource; 296 UserFilesystemServer server(&resource); 297 298 if (server.bind("export")) 299 { 300 printf("Could not bind thread.\n"); 301 return 1; 302 } 303 304 /* Mount filesystems. */ 305 306 fp = fopen(argv[1], "r"); 307 if (fp == NULL) 308 { 309 printf("Could not open %s.\n", argv[1]); 310 return 1; 311 } 312 313 /* Obtain capabilities for mountpoints. */ 314 315 while (fgets(buffer, 256, fp) != NULL) 316 { 317 sep = strchr(buffer, (int) '\t'); 318 if (sep == NULL) 319 continue; 320 321 /* Terminate newline-suffixed strings. */ 322 323 if (sep[strlen(sep) - 1] == '\n') 324 sep[strlen(sep) - 1] = '\0'; 325 326 /* Obtain the named capability. */ 327 328 fscap = l4re_env_get_cap(sep + 1); 329 330 if (l4_is_invalid_cap(fscap)) 331 continue; 332 333 /* Terminate the path and register the mountpoint. */ 334 335 *sep = '\0'; 336 resource.mount_at_path(fscap, buffer); 337 } 338 339 fclose(fp); 340 341 /* Enter the IPC server loop. */ 342 343 printf("Starting vfs resource...\n"); 344 server.start(); 345 return 0; 346 }