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 == Files == 205 206 Files themselves act as dataspaces, meaning that they can be attached to a 207 task using a region manager and provide their content via a region of mapped 208 memory. Files implement the `MappedFile` interface. 209 210 Control over the region of the file provided via mapped memory occurs 211 using the `mmap` operation: 212 213 {{{ 214 mmap(in offset_t position, in offset_t length, 215 out offset_t start_pos, out offset_t end_pos, 216 out offset_t size) 217 }}} 218 219 Files also implement the more general `File` interface that provides the 220 `resize` operation: 221 222 {{{ 223 resize(inout offset_t size) 224 }}} 225 226 This allows the portion of the memory region dedicated to the file's contents 227 to be extended. 228 229 == Directories == 230 231 Directories are obtained, like files, using the `open` operation. They 232 implement the `Directory` interface. 233 234 To read directory listings, the `opendir` operation is used to obtain a 235 directory reader: 236 237 {{{ 238 opendir(out offset_t size, out cap file, out object_flags_t object_flags) 239 }}} 240 241 Directory readers are meant to be accessed like files, meaning that it should 242 be possible to attach them to a task using a region manager and access the 243 provided content, this being a listing of directory entries, via the mapped 244 region. 245 246 However, unlike files which may support arbitrary mapping of their contents, 247 the provided content may be supplied by a pipe endpoint, thereby not 248 supporting precisely the same navigation mechanisms as those supported by 249 files. 250 251 == Pipe Openers == 252 253 Distinct from filesystems but potentially used by them, pipe openers provide a 254 means of obtaining pipes, which are channels that support unidirectional 255 communication via shared memory. 256 257 Pipe openers implement the `PipeOpener` interface and support the following 258 operation: 259 260 {{{ 261 pipe(in offset_t size, out cap reader, out cap writer) 262 }}} 263 264 The size is indicated to request pipe regions long enough for the needs of the 265 communicating parties, with both reader and writer endpoint capabilities being 266 returned. Such capabilities may be propagated to the eventual parties, these 267 typically being separate tasks. 268 269 == Pipes == 270 271 Although not generally obtained from filesystems, pipes may be involved in 272 providing content from some filesystem objects such as directories. However, 273 they are also obtained directly from an appropriate pipe server providing pipe 274 opening facilities. 275 276 Pipes expose single regions of shared memory to their endpoints, with the 277 writing endpoint populating one region while the reading endpoint accesses the 278 other. The reading endpoint may advance to the region being written, and this 279 will free up a new region for the writer when it has filled its region. When 280 the writer itself advances, it permits the reader to consume all data in the 281 fully populated region. Naturally, the reader may not advance ahead of the 282 writer. 283 284 Pipes implement the `Pipe` interface and a number of operations to support 285 this interaction mechanism. 286 287 The details of an endpoint's current region can be queried using the following 288 operation: 289 290 {{{ 291 current_region(out offset_t populated_size, out offset_t size) 292 }}} 293 294 This provides details of the populated size (or amount of written data) in a 295 region along with the size of the region. 296 297 Navigation to the next available region of the pipe is performed using the 298 following operation: 299 300 {{{ 301 next_region(inout offset_t populated_size, out offset_t size) 302 }}} 303 304 Here, the populated size may be specified by the writer so that the reader may 305 query the current region's properties using the appropriate operation. 306 307 The status of the pipe can be queried using the `closed` operation: 308 309 {{{ 310 closed(out int closed) 311 }}} 312 313 This indicates through a boolean-equivalent parameter whether one or both 314 endpoints have been closed.