1 = Components = 2 3 Access to files is provided by a number of programs acting as components. For 4 convenience, the component-level operations are wrapped up in a 5 [[ClientLibrary|client library]] that aims to provide simpler, more familiar 6 mechanisms for opening, reading, writing, and closing files, together with 7 various other operations. 8 9 Components are provided by functionality in [[Libraries#libfsserver| 10 `libfsserver`]] used by programs found in the `servers` directory within the 11 `departure` package. In general, each component runs in its own server thread. 12 13 <<TableOfContents(2,3)>> 14 15 Components are accessed via interfaces defined using the interface description 16 language supported by the `idl` tool provided by the idl4re distribution. 17 Interface operations in this document are described using excerpts from the 18 appropriate interface descriptions. 19 20 == Filesystems == 21 22 Filesystems implement the `Filesystem` interface which provides the 23 `open_for_user` operation: 24 25 {{{ 26 open_for_user(in user_t user, out cap opener) 27 }}} 28 29 The operation yields a file opener appropriate for the given [[Users|user]] 30 credentials. 31 32 ######## A graph showing the interactions between components 33 34 {{{#!graphviz 35 #format svg 36 #transform notugly 37 digraph open_for_user { 38 node [fontsize="12.0",fontname="sans-serif",shape=box]; 39 edge [fontsize="12.0",fontname="sans-serif"]; 40 rankdir=LR; 41 42 subgraph { 43 node [label="Client"]; 44 rank=min; 45 46 Client1; Client2; 47 } 48 49 subgraph { 50 rank=max; 51 52 Filesystem; 53 54 subgraph { 55 node [label="Opener\n(user)"]; 56 Opener; 57 } 58 } 59 60 Client1 -> Client2 [dir=none,style=dotted]; 61 62 Client1 -> Filesystem [label="open_for_user(user)"]; 63 Filesystem -> Opener; 64 Opener -> Client2; 65 66 } 67 }}} 68 69 ######## 70 71 In pseudocode, the operations as conducted by the client program are as 72 follows: 73 74 {{{ 75 opener = filesystem.open_for_user(user) 76 }}} 77 78 ((Openers)) 79 == File and Directory Openers == 80 81 Openers implement the `Opener` interface which provides the `context` 82 operation: 83 84 {{{ 85 context(out cap context) 86 }}} 87 88 Each client program, task or thread obtains its own context because it will 89 need its own dedicated channel for communication with the filesystem. 90 91 ######## A graph showing the interactions between components 92 93 {{{#!graphviz 94 #format svg 95 #transform notugly 96 digraph context { 97 node [fontsize="12.0",fontname="sans-serif",shape=box]; 98 edge [fontsize="12.0",fontname="sans-serif"]; 99 rankdir=LR; 100 101 subgraph { 102 node [label="Client"]; 103 rank=min; 104 105 Client1; Client2; 106 } 107 108 subgraph { 109 rank=max; 110 111 subgraph { 112 node [label="Opener\n(user)"]; 113 Opener; 114 } 115 116 subgraph { 117 node [label="OpenerContext"]; 118 OpenerContext; 119 } 120 } 121 122 Client1 -> Client2 [dir=none,style=dotted]; 123 124 Client1 -> Opener [label="context()"]; 125 Opener -> OpenerContext; 126 OpenerContext -> Client2; 127 } 128 }}} 129 130 ######## 131 132 In pseudocode, the operations as conducted by the client program are as 133 follows: 134 135 {{{ 136 context = opener.context() 137 }}} 138 139 == Opener Contexts == 140 141 An opener context acts as a dataspace, meaning that it can be attached to a 142 task using a region manager and provide a buffer via a region of mapped memory 143 that the task can write to. In the case of a context, the task will write a 144 filesystem path indicating the file to be opened. 145 146 Each context allows a client program to request access to individual files via 147 operations provided by the `OpenerContext` interface, of which the most 148 pertinent is the `open` operation: 149 150 {{{ 151 open(in flags_t flags, out offset_t size, out cap file, 152 out object_flags_t object_flags) 153 }}} 154 155 Using the path information written to the context's memory region, the `open` 156 operation will obtain a reference to a file-like object whose characteristics 157 are described by the accompanying `object_flags`, these helping the client to 158 distinguish between files that support arbitrary memory mapping operations and 159 pipes that mandate sequential region-by-region access. 160 161 Alongside regular files, directories may also be opened. Reading from them 162 yields a listing of directory entries. 163 164 ######## A graph showing the interactions between components 165 166 {{{#!graphviz 167 #format svg 168 #transform notugly 169 digraph open { 170 node [fontsize="12.0",fontname="sans-serif",shape=box]; 171 edge [fontsize="12.0",fontname="sans-serif"]; 172 rankdir=LR; 173 174 subgraph { 175 node [label="Client"]; 176 rank=min; 177 178 Client1; Client2; Client3; 179 } 180 181 subgraph { 182 rank=same; 183 184 Memory [label="filename",shape=note]; 185 } 186 187 subgraph { 188 rank=max; 189 190 subgraph { 191 node [label="OpenerContext"]; 192 OpenerContext1; OpenerContext2; 193 } 194 195 Object [label="MappedFile\nor\nDirectory"]; 196 } 197 198 Client1 -> Client2 -> Client3 [dir=none,style=dotted]; 199 OpenerContext1 -> OpenerContext2 [dir=none,style=dotted]; 200 201 Client1 -> Memory -> OpenerContext1; 202 203 Client2 -> OpenerContext2 [label="open(flags, ...)"]; 204 OpenerContext2 -> Object; 205 Object -> Client3; 206 } 207 }}} 208 209 ######## 210 211 In pseudocode, the operations as conducted by the client program are as 212 follows: 213 214 {{{ 215 context.write("filename") # this being a memory access operation 216 file = context.open(flags, ...) 217 }}} 218 219 === Removing === 220 221 Filesystem objects are removed by invoking the `remove` operation on an opener 222 context: 223 224 {{{ 225 remove() 226 }}} 227 228 The path information identifying the object must first be written to the 229 context's memory region. 230 231 === Renaming === 232 233 Filesystem objects are renamed by invoking the `rename` operation on an opener 234 context: 235 236 {{{ 237 rename() 238 }}} 239 240 The path information of the affected object and the destination of the rename 241 operation must first be written to the context's memory region. The 242 destination path follows immediately after the terminating byte of the 243 affected path. 244 245 === Statistics/Metadata === 246 247 Statistics or metadata for a filesystem object can be obtained by invoking the 248 `stat` operation on an opener context: 249 250 {{{ 251 stat() 252 }}} 253 254 The path information identifying the object must first be written to the 255 context's memory region. As a result of the invocation, a `stat` data 256 structure will be written to the start of the memory region. 257 258 == Files == 259 260 Files themselves act as dataspaces, meaning that they can be attached to a 261 task using a region manager and provide their content via a region of mapped 262 memory. Files implement the `MappedFile` interface. 263 264 Control over the region of the file provided via mapped memory occurs 265 using the `mmap` operation: 266 267 {{{ 268 mmap(in offset_t position, in offset_t length, 269 in offset_t start_visible, in offset_t end_visible, 270 out offset_t start_pos, out offset_t end_pos, 271 out offset_t size) 272 }}} 273 274 Files also implement the more general `File` interface that provides the 275 `resize` operation: 276 277 {{{ 278 resize(inout offset_t size) 279 }}} 280 281 This allows the portion of the memory region dedicated to the file's contents 282 to be extended. 283 284 == Directories == 285 286 Directories are obtained, like files, using the `open` operation. They 287 implement the `Directory` interface. 288 289 To read directory listings, the `opendir` operation is used to obtain a 290 directory reader: 291 292 {{{ 293 opendir(out offset_t size, out cap file, out object_flags_t object_flags) 294 }}} 295 296 Directory readers are meant to be accessed like files, meaning that it should 297 be possible to attach them to a task using a region manager and access the 298 provided content, this being a listing of directory entries, via the mapped 299 region. 300 301 However, unlike files which may support arbitrary mapping of their contents, 302 the provided content may be supplied by a pipe endpoint, thereby not 303 supporting precisely the same navigation mechanisms as those supported by 304 files. 305 306 Reading from an opened directory is achieved as shown in the following 307 diagram. 308 309 ######## A graph showing the interactions between components 310 311 {{{#!graphviz 312 #format svg 313 #transform notugly 314 digraph components { 315 node [fontsize="12.0",fontname="sans-serif",shape=box]; 316 edge [fontsize="12.0",fontname="sans-serif"]; 317 rankdir=LR; 318 319 subgraph { 320 node [label="Client"]; 321 rank=min; 322 323 Client1; Client2; Client3; Client4; 324 } 325 326 subgraph { 327 rank=same; 328 329 Memory [label="entries",shape=note]; 330 } 331 332 subgraph { 333 rank=max; 334 335 Directory; 336 337 subgraph { 338 node [label="Reader"]; 339 340 Reader1; Reader2; Reader3; 341 } 342 } 343 344 Client1 -> Client2 -> Client3 -> Client4 [dir=none,style=dotted]; 345 Reader1 -> Reader2 -> Reader3 [dir=none,style=dotted]; 346 347 Client1 -> Directory [label="opendir()"]; 348 Directory -> Reader1; 349 Reader1 -> Client2; 350 351 Client3 -> Reader2 [label="current_region()"]; 352 Reader3 -> Memory -> Client4; 353 } 354 }}} 355 356 ######## 357 358 In pseudocode, the operations as conducted by the client program are as 359 follows: 360 361 {{{ 362 reader = directory.opendir() 363 reader.current_region() 364 entries = reader.read() # this being a memory access operation 365 }}} 366 367 == Pipe Openers == 368 369 Distinct from filesystems but potentially used by them, pipe openers provide a 370 means of obtaining pipes, which are channels that support unidirectional 371 communication via shared memory. 372 373 Pipe openers implement the `PipeOpener` interface and support the following 374 operation: 375 376 {{{ 377 pipe(in offset_t size, out cap reader, out cap writer) 378 }}} 379 380 The size is indicated to request pipe regions long enough for the needs of the 381 communicating parties, with both reader and writer endpoint capabilities being 382 returned. Such capabilities may be propagated to the eventual parties, these 383 typically being separate tasks. 384 385 == Pipes == 386 387 Although not generally obtained from filesystems, pipes may be involved in 388 providing content from some filesystem objects such as directories. However, 389 they are also obtained directly from an appropriate pipe server providing pipe 390 opening facilities. 391 392 Pipes expose single regions of shared memory to their endpoints, with the 393 writing endpoint populating one region while the reading endpoint accesses the 394 other. The reading endpoint may advance to the region being written, and this 395 will free up a new region for the writer when it has filled its region. When 396 the writer itself advances, it permits the reader to consume all data in the 397 fully populated region. Naturally, the reader may not advance ahead of the 398 writer. 399 400 Pipes implement the `Pipe` interface and a number of operations to support 401 this interaction mechanism. 402 403 The details of an endpoint's current region can be queried using the following 404 operation: 405 406 {{{ 407 current_region(out offset_t populated_size, out offset_t size) 408 }}} 409 410 This provides details of the populated size (or amount of written data) in a 411 region along with the size of the region. 412 413 Navigation to the next available region of the pipe is performed using the 414 following operation: 415 416 {{{ 417 next_region(inout offset_t populated_size, out offset_t size) 418 }}} 419 420 Here, the populated size may be specified by the writer so that the reader may 421 query the current region's properties using the appropriate operation. 422 423 The status of the pipe can be queried using the `closed` operation: 424 425 {{{ 426 closed(out int closed) 427 }}} 428 429 This indicates through a boolean-equivalent parameter whether one or both 430 endpoints have been closed.