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