L4Re/departure

Annotated docs/wiki/Components

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