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 ipc_server_default_config_type config(); 224 void *interface(); 225 }}} 226 227 The `config` operation returns a default configuration object containing 228 details pertinent to the initialisation of a server for the resource, these 229 involving aspects of incoming messages and the handler function to deal with 230 such messages. Typically, such an object, along with the handler function, 231 will be generated by a tool such as `idl`. 232 233 The `interface` operation returns a pointer to the resource instance coerced 234 to an appropriate type. Such a type will be that of the most specialised IPC 235 interface type inherited by the resource implementation, as expected by the 236 handler function for the resource. 237 238 For example, consider the following: 239 240 {{{ 241 class FilePager : public Pager, public MappedFileObject 242 }}} 243 244 Here, the `interface` operation will return a pointer coerced to 245 `MappedFileObject`, and the `config` operation will return a suitable 246 configuration object. The `idl` tool will produce an object called 247 `config_MappedFileObject` for this purpose along with a handler function 248 called `handle_MappedFileObject` available via the configuration object. 249 250 == Registries == 251 252 The basic mechanism for obtaining a resource involves a registry, as 253 illustrated by the following diagram. 254 255 ######## A graph showing the use of a registry in obtaining resources 256 257 {{{#!graphviz 258 #format svg 259 #transform notugly 260 digraph registry { 261 node [fontsize="12.0",fontname="sans-serif",shape=box]; 262 edge [fontsize="12.0",fontname="sans-serif"]; 263 rankdir=LR; 264 265 subgraph { 266 node [label="OpenerContextResource"]; 267 rank=same; 268 269 OpenerContextResource1 -> OpenerContextResource2 [dir=none,style=dotted]; 270 } 271 272 subgraph { 273 node [label="OpenerResource"]; 274 rank=same; 275 276 OpenerResource1 -> OpenerResource2 [dir=none,style=dotted]; 277 } 278 279 subgraph { 280 rank=same; 281 282 ResourceRegistry -> Resource; 283 } 284 285 subgraph { 286 rank=same; 287 288 ProviderRegistry -> Provider; 289 } 290 291 OpenerContextResource1 -> OpenerResource1 [label="open"]; 292 OpenerResource1 -> ResourceRegistry [label="get_resource"]; 293 ResourceRegistry -> ProviderRegistry [label="get/set"]; 294 295 Provider -> Resource -> OpenerResource2 -> OpenerContextResource2; 296 } 297 }}} 298 299 ######## 300 301 === Concurrency and Locking === 302 303 The `ResourceRegistry` coordinates access to filesystem resources and, through 304 synchronisation, prevents conflicting operations from occurring concurrently. 305 For example, a removal operation on a file may not be allowed to occur while 306 an opening operation is in progress. 307 308 To achieve this, a common lock is employed to serialise access, with any 309 underlying filesystem operations being initiated from the registry while the 310 lock is held. An object providing the `FileOpening` interface for a filesystem 311 provides such operations, and the following interaction pattern is thereby 312 employed: 313 314 ######## A graph showing the registry coordinating filesystem operations 315 316 {{{#!graphviz 317 #format svg 318 #transform notugly 319 digraph registry_operations { 320 node [fontsize="12.0",fontname="sans-serif",shape=box]; 321 edge [fontsize="12.0",fontname="sans-serif"]; 322 rankdir=LR; 323 324 OpenerContextResource -> OpenerResource -> ResourceRegistry -> FileOpening; 325 } 326 }}} 327 328 ######## 329 330 === Openers and Filesystem Access === 331 332 Since the `ResourceRegistry` functionality is generic, it could be specialised 333 for each filesystem or be configured with an appropriate reference to a 334 `FileOpening` object. The `OpenerResource` would then be generic, invoking the 335 registry which would, in turn, invoke the opening object. 336 337 However, the chosen approach is to permit `OpenerResource` objects to 338 implement the `FileOpening` interface for each filesystem, meaning that the 339 `ResourceRegistry` will end up being called by the opener and then invoking 340 the opener in return. This is slightly more convenient than the alternative 341 since the opener can be configured with a given user identity, and such 342 identity details will ultimately be employed when accessing the underlying 343 filesystem itself.