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 out offset_t start_pos, out offset_t end_pos, 255 out offset_t size) 256 }}} 257 258 Files also implement the more general `File` interface that provides the 259 `resize` operation: 260 261 {{{ 262 resize(inout offset_t size) 263 }}} 264 265 This allows the portion of the memory region dedicated to the file's contents 266 to be extended. 267 268 == Directories == 269 270 Directories are obtained, like files, using the `open` operation. They 271 implement the `Directory` interface. 272 273 To read directory listings, the `opendir` operation is used to obtain a 274 directory reader: 275 276 {{{ 277 opendir(out offset_t size, out cap file, out object_flags_t object_flags) 278 }}} 279 280 Directory readers are meant to be accessed like files, meaning that it should 281 be possible to attach them to a task using a region manager and access the 282 provided content, this being a listing of directory entries, via the mapped 283 region. 284 285 However, unlike files which may support arbitrary mapping of their contents, 286 the provided content may be supplied by a pipe endpoint, thereby not 287 supporting precisely the same navigation mechanisms as those supported by 288 files. 289 290 == Pipe Openers == 291 292 Distinct from filesystems but potentially used by them, pipe openers provide a 293 means of obtaining pipes, which are channels that support unidirectional 294 communication via shared memory. 295 296 Pipe openers implement the `PipeOpener` interface and support the following 297 operation: 298 299 {{{ 300 pipe(in offset_t size, out cap reader, out cap writer) 301 }}} 302 303 The size is indicated to request pipe regions long enough for the needs of the 304 communicating parties, with both reader and writer endpoint capabilities being 305 returned. Such capabilities may be propagated to the eventual parties, these 306 typically being separate tasks. 307 308 == Pipes == 309 310 Although not generally obtained from filesystems, pipes may be involved in 311 providing content from some filesystem objects such as directories. However, 312 they are also obtained directly from an appropriate pipe server providing pipe 313 opening facilities. 314 315 Pipes expose single regions of shared memory to their endpoints, with the 316 writing endpoint populating one region while the reading endpoint accesses the 317 other. The reading endpoint may advance to the region being written, and this 318 will free up a new region for the writer when it has filled its region. When 319 the writer itself advances, it permits the reader to consume all data in the 320 fully populated region. Naturally, the reader may not advance ahead of the 321 writer. 322 323 Pipes implement the `Pipe` interface and a number of operations to support 324 this interaction mechanism. 325 326 The details of an endpoint's current region can be queried using the following 327 operation: 328 329 {{{ 330 current_region(out offset_t populated_size, out offset_t size) 331 }}} 332 333 This provides details of the populated size (or amount of written data) in a 334 region along with the size of the region. 335 336 Navigation to the next available region of the pipe is performed using the 337 following operation: 338 339 {{{ 340 next_region(inout offset_t populated_size, out offset_t size) 341 }}} 342 343 Here, the populated size may be specified by the writer so that the reader may 344 query the current region's properties using the appropriate operation. 345 346 The status of the pipe can be queried using the `closed` operation: 347 348 {{{ 349 closed(out int closed) 350 }}} 351 352 This indicates through a boolean-equivalent parameter whether one or both 353 endpoints have been closed.