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