1 = Server Library = 2 3 Within the filesystem server library, a number of different abstractions and 4 mechanisms are employed to provide access to filesystem objects. 5 6 The server library is provided by [[Libraries#libfsserver|`libfsserver`]] 7 within the `departure` package. 8 9 <<TableOfContents(2,3)>> 10 11 This document uses C-style interface syntax since the components operating 12 within the described mechanisms will themselves be described using such 13 syntax. 14 15 ((Accountable)) 16 == Accountables == 17 18 The `Accountable` interface provides the following operations: 19 20 {{{ 21 void attach(); 22 unsigned int detach(); 23 }}} 24 25 Its purpose is to provide reference counting for components, with the `attach` 26 operation incrementing the number of users of a component, and with the 27 `detach` operation decrementing the number of users and returning the 28 resulting number of users. When components no longer have any attached users, 29 they may perform operations to tidy up after themselves and permit their 30 deallocation. 31 32 The [[#Provider|`Provider`]] abstraction employs this interface to record its 33 usage by multiple resources. 34 35 == Accessors == 36 37 Accessors provide the means of accessing filesystem object data and metadata. 38 Conceptually, files and directories are both exposed by accessors, although 39 the interfaces and mechanisms may differ between these types of objects. 40 41 === Directory Accessors === 42 43 Currently, directory accessors provide support for traversing directory 44 listings, obtaining the relevant filesystem metadata using the underlying 45 filesystem access library. 46 47 The `DirectoryAccessor` interface provides the following operation: 48 49 {{{ 50 void read_directory(file_t *writer); 51 }}} 52 53 The `read_directory` operation is presented with a writer object into which 54 directory listing data will be written. In the case of the ext2 directory 55 accessor, the writer is presented to a directory iterator which traverses the 56 directory data structure, invoking a callback sending directory entries via 57 the writer to the client. 58 59 === File Accessors === 60 61 File content is accessed through the `Accessor` interface with the following 62 operations: 63 64 {{{ 65 void close(); 66 void fill(Flexpage *flexpage); 67 void flush(Flexpage *flexpage); 68 offset_t get_size(); 69 void set_size(offset_t size); 70 }}} 71 72 The operations need to be supported with actual filesystem operations. For 73 example, ext2-based filesystems employ a specific abstraction which invokes 74 library functions provided by [[Libraries#libext2fs|`libext2fs`]]. 75 76 ((Provider)) 77 == Providers == 78 79 Providers encapsulate the essential functionality for accessing filesystem 80 objects. Implementing the `Accountable` interface, they are shared by 81 resources and discarded when no resources are using them. 82 83 The following operations are supported by providers as defined by the 84 `Provider` interface: 85 86 {{{ 87 ProviderRegistry *registry(); 88 long make_resource(offset_t *size, object_flags_t *object_flags, Resource **resource); 89 bool removal_pending(); 90 void remove_pending(bool remove); 91 }}} 92 93 === Origin and Ownership === 94 95 A provider is created to represent a filesystem object when an attempt is made 96 to open that object. It is then recorded in a provider registry so that 97 subsequent attempts to open the object yield a common provider instance. The 98 provider registry having ownership of each provider can be obtained using its 99 `registry` operation. 100 101 See the [[Filesystem Access#Opening Files|file opening mechanism]] for details 102 of the creation and registration of providers. 103 104 === Resource Creation === 105 106 Providers also support the creation of resources through which each user of a 107 provider exercises its use of that provider. The `make_resource` operation 108 performs the creation and returns size, flags and resource instance details. 109 110 See the [[Filesystem Access#Opening Files|file opening mechanism]] for details 111 of resource creation. 112 113 === Deallocation === 114 115 Deallocation of providers is managed by the provider registry when resources 116 are closed and detached from providers. Since the registry effectively has 117 ownership of the provider, having registered it, the registry must therefore 118 be involved in its deallocation, deregistering it first. 119 120 ######## A graph showing the deallocation mechanism 121 122 {{{#!graphviz 123 #format svg 124 #transform notugly 125 digraph provider_removal { 126 node [fontsize="12.0",fontname="sans-serif",shape=box]; 127 edge [fontsize="12.0",fontname="sans-serif"]; 128 rankdir=LR; 129 130 ResourceServer -> Resource [label="close"]; 131 Resource -> ProviderRegistry [label="detach"]; 132 ProviderRegistry -> Provider [label="detach\ndelete"]; 133 } 134 }}} 135 136 ######## 137 138 === Filesystem Object Removal === 139 140 The removal of filesystem objects that are being represented by providers can 141 be directed using the `remove_pending` operation. Where `remove_pending` has 142 been called with a true value as its parameter, the `removal_pending` 143 operation will also return a true value, and upon the provider being 144 discarded, a removal operation will be invoked on the underlying object being 145 provided. 146 147 See the [[Filesystem Access#Removing Files|file removal mechanism]] for more 148 details of the invocations involved. 149 150 ((Resource)) 151 == Resources == 152 153 Resources are objects accessed by clients that support a basic level of 154 accounting and management. They act as servers and receive messages via the 155 interprocess communication (IPC) mechanism. 156 157 The `Resource` abstraction is intended to work with lower-level mechanisms 158 provided by [[Libraries#libipc|`libipc`]] involving data structures and 159 functions that are usable in the C programming language. This abstraction 160 integrates the fundamental `libipc` support with C++. 161 162 The generic operations of the `Resource` interface are as follows: 163 164 {{{ 165 void activate(); 166 void close(); 167 }}} 168 169 The activation of a resource, supported by `activate`, is an optional 170 operation that performs any initialisation before a resource is made available 171 as a server. 172 173 The `close` operation is invoked when resources are to be discarded. 174 175 See the [[Filesystem Access#Closing Files|file closing mechanism]] for the 176 context in which the `close` operation is invoked. 177 178 In practice, other operations are required to make resources useful. Such 179 other operations are provided by classes inheriting from `Resource` and thus 180 specialising it. Such classes also inherit from IPC interface types so as to 181 be able to support the invocation of operations exposed by such interfaces. 182 183 In some cases, resources provide the mechanism by which each user of a 184 filesystem object may access that object independently. They would then 185 effectively provide a session in which accesses can occur. 186 187 === Directory Resources === 188 189 Directory resources primarily expose the contents of directories in the 190 filesystem to a user. They employ directory accessors which concern themselves 191 with the actual filesystem content. 192 193 [[Components#Directories|Directory components]] are provided using directory 194 resources. 195 196 <<Anchor(Pager)>> 197 === Pagers or File Resources === 198 199 Pagers are resources that support dataspace access operations, thus allowing 200 the resources to expose filesystem content in mapped memory regions to a 201 particular user of the object providing the content. 202 203 [[Components#Files|File components]] and [[Components#Pipes|pipe components]] 204 are provided using pagers. 205 206 === Filesystem Resources === 207 208 Filesystem resources provide the entry point for access to a filesystem by 209 other components or programs. Since filesystems often enforce identity-based 210 access controls, a filesystem resource will typically support the 211 `open_for_user` operation in various forms, with the result of this operation 212 being the instantiation of an `OpenerResource` configured for the indicated 213 user identity. 214 215 === Server Framework Integration === 216 217 Resources must also support specific operations for integration with the 218 lower-level IPC handling implemented in `libipc`. The following operations 219 will be invoked by the server framework in `libfsserver` to configure a server 220 controlled by `libipc`: 221 222 {{{ 223 int expected_items(); 224 ipc_server_handler_type handler(); 225 void *interface(); 226 }}} 227 228 The `expected_items` operation returns the number of message items expected 229 from clients invoking server operations exposed via IPC. Typically, the 230 returned value will be calculated for a given server interface by a tool such 231 as `idl`, provided by the idl4re distribution. 232 233 The `handler` operation returns a reference to a handler function able to 234 interpret the operation codes in incoming messages and to dispatch to the 235 appropriate operations provided by the resource. Typically, such a function 236 will be generated by a tool such as `idl`. 237 238 The `interface` operation returns a pointer to the resource instance, coerced 239 to an appropriate type for the handler function. Such a type will be that of 240 the most specialised IPC interface type inherited by the resource 241 implementation. The function returned by the `handler` operation will expect a 242 pointer to an instance of this appropriate type. 243 244 For example, consider the following: 245 246 {{{ 247 class FilePager : public Pager, public MappedFileObject 248 }}} 249 250 Here, the `interface` operation will return a pointer coerced to 251 `MappedFileObject`, and the `handler` operation will return a reference to a 252 function expecting to be able to interpret this pointer as referring to 253 precisely that type. 254 255 == Registries == 256 257 The basic mechanism for obtaining a resource involves a registry, as 258 illustrated by the following diagram. 259 260 ######## A graph showing the use of a registry in obtaining resources 261 262 {{{#!graphviz 263 #format svg 264 #transform notugly 265 digraph registry { 266 node [fontsize="12.0",fontname="sans-serif",shape=box]; 267 edge [fontsize="12.0",fontname="sans-serif"]; 268 rankdir=LR; 269 270 subgraph { 271 node [label="OpenerContextResource"]; 272 rank=same; 273 274 OpenerContextResource1 -> OpenerContextResource2 [dir=none,style=dotted]; 275 } 276 277 subgraph { 278 node [label="OpenerResource"]; 279 rank=same; 280 281 OpenerResource1 -> OpenerResource2 [dir=none,style=dotted]; 282 } 283 284 subgraph { 285 rank=same; 286 287 ResourceRegistry -> Resource; 288 } 289 290 subgraph { 291 rank=same; 292 293 ProviderRegistry -> Provider; 294 } 295 296 OpenerContextResource1 -> OpenerResource1 [label="open"]; 297 OpenerResource1 -> ResourceRegistry [label="get_resource"]; 298 ResourceRegistry -> ProviderRegistry [label="get/set"]; 299 300 Provider -> Resource -> OpenerResource2 -> OpenerContextResource2; 301 } 302 }}} 303 304 ######## 305 306 The `ResourceRegistry` coordinates access to filesystem resources and, through 307 synchronisation, prevents conflicting operations from occurring concurrently. 308 For example, a removal operation on a file may not be allowed to occur while 309 an opening operation is in progress. 310 311 To achieve this, a common lock is employed to serialise access, with any 312 underlying filesystem operations being initiated from the registry while the 313 lock is held. An object providing the `FileOpening` interface for a filesystem 314 provides such operations, and the following interaction pattern is thereby 315 employed: 316 317 ######## A graph showing the registry coordinating filesystem operations 318 319 {{{#!graphviz 320 #format svg 321 #transform notugly 322 digraph registry_operations { 323 node [fontsize="12.0",fontname="sans-serif",shape=box]; 324 edge [fontsize="12.0",fontname="sans-serif"]; 325 rankdir=LR; 326 327 OpenerContextResource -> OpenerResource -> ResourceRegistry -> FileOpening; 328 } 329 }}} 330 331 ######## 332 333 Since the `ResourceRegistry` functionality is generic, it could be specialised 334 for each filesystem or be configured with an appropriate reference to a 335 `FileOpening` object. The `OpenerResource` would then be generic, invoking the 336 registry which would, in turn, invoke the opening object. 337 338 However, the chosen approach is to permit `OpenerResource` objects to 339 implement the `FileOpening` interface for each filesystem, meaning that the 340 `ResourceRegistry` will end up being called by the opener and then invoking 341 the opener in return. This is slightly more convenient than the alternative 342 since the opener can be configured with a given user identity, and such 343 identity details will ultimately be employed when accessing the underlying 344 filesystem itself.