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