paul@384 | 1 | = Server Library = |
paul@384 | 2 | |
paul@384 | 3 | Within the filesystem server library, a number of different abstractions and |
paul@384 | 4 | mechanisms are employed to provide access to filesystem objects. |
paul@384 | 5 | |
paul@427 | 6 | The server library is provided by `libfsserver` within the `departure` |
paul@427 | 7 | package. |
paul@427 | 8 | |
paul@384 | 9 | <<TableOfContents(2,3)>> |
paul@384 | 10 | |
paul@384 | 11 | This document uses C-style interface syntax since the components operating |
paul@384 | 12 | within the described mechanisms will themselves be described using such |
paul@384 | 13 | syntax. |
paul@384 | 14 | |
paul@384 | 15 | == Accountable == |
paul@384 | 16 | |
paul@384 | 17 | This interface provides the following operations: |
paul@384 | 18 | |
paul@384 | 19 | {{{ |
paul@390 | 20 | void attach(); |
paul@390 | 21 | unsigned int detach(); |
paul@384 | 22 | }}} |
paul@384 | 23 | |
paul@384 | 24 | Its purpose is to provide reference counting for components, with the `attach` |
paul@384 | 25 | operation incrementing the number of users of a component, and with the |
paul@384 | 26 | `detach` operation decrementing the number of users and returning the |
paul@384 | 27 | resulting number of users. When components no longer have any attached users, |
paul@384 | 28 | they may perform operations to tidy up after themselves and permit their |
paul@384 | 29 | deallocation. |
paul@384 | 30 | |
paul@384 | 31 | == Accessors == |
paul@384 | 32 | |
paul@384 | 33 | Accessors provide the means of accessing filesystem object data and metadata. |
paul@384 | 34 | Conceptually, files and directories are both exposed by accessors, although |
paul@384 | 35 | the interfaces and mechanisms may differ between these types of objects. |
paul@384 | 36 | |
paul@384 | 37 | === Directory Accessors === |
paul@384 | 38 | |
paul@384 | 39 | Currently, directory accessors provide support for traversing directory |
paul@384 | 40 | listings, obtaining the relevant filesystem metadata using the underlying |
paul@384 | 41 | filesystem access library. |
paul@384 | 42 | |
paul@384 | 43 | === File Accessors === |
paul@384 | 44 | |
paul@384 | 45 | File content is accessed through an interface with the following operations: |
paul@384 | 46 | |
paul@384 | 47 | {{{ |
paul@390 | 48 | void close(); |
paul@390 | 49 | void fill(Flexpage *flexpage); |
paul@390 | 50 | void flush(Flexpage *flexpage); |
paul@390 | 51 | offset_t get_size(); |
paul@390 | 52 | void set_size(offset_t size); |
paul@384 | 53 | }}} |
paul@384 | 54 | |
paul@384 | 55 | The operations need to be supported with actual filesystem operations. For |
paul@384 | 56 | example, ext2-based filesystems employ a specific abstraction which invokes |
paul@384 | 57 | library functions provided by the `libext2fs` package. |
paul@384 | 58 | |
paul@384 | 59 | == Providers == |
paul@384 | 60 | |
paul@384 | 61 | Providers encapsulate the essential functionality for accessing filesystem |
paul@384 | 62 | objects. Implementing the `Accountable` interface, they are shared by |
paul@384 | 63 | resources and discarded when no resources are using them. |
paul@384 | 64 | |
paul@384 | 65 | The following operations are supported by providers: |
paul@384 | 66 | |
paul@384 | 67 | {{{ |
paul@390 | 68 | ProviderRegistry *registry(); |
paul@390 | 69 | long make_resource(offset_t *size, object_flags_t *object_flags, Resource **resource); |
paul@390 | 70 | bool removal_pending(); |
paul@390 | 71 | void remove(); |
paul@390 | 72 | void remove_pending(bool remove); |
paul@384 | 73 | }}} |
paul@384 | 74 | |
paul@384 | 75 | Providers are associated with filesystem objects in a registry which can be |
paul@384 | 76 | obtained from each provider using the `registry` operation. |
paul@384 | 77 | |
paul@384 | 78 | Providers also support the creation of resources through which each user of a |
paul@384 | 79 | provider exercises its use of that provider. The `make_resource` operation |
paul@384 | 80 | performs the creation and returns size, flags and resource instance details. |
paul@384 | 81 | |
paul@384 | 82 | The removal of providers can be directed using the `remove_pending` and |
paul@384 | 83 | `remove` operations. Where `remove_pending` has been called with a true value |
paul@384 | 84 | as its parameter, the `removal_pending` operation will also return a true |
paul@384 | 85 | value, and upon the provider being discarded, the `remove` operation will be |
paul@384 | 86 | invoked. The `remove` operation performs the appropriate underlying operation |
paul@384 | 87 | to remove the object being provided. |
paul@384 | 88 | |
paul@384 | 89 | Typically, removal of providers is managed by the provider registry when |
paul@384 | 90 | resources are closed and detached from providers. |
paul@384 | 91 | |
paul@384 | 92 | ######## A graph showing the removal mechanism |
paul@384 | 93 | |
paul@384 | 94 | {{{#!graphviz |
paul@384 | 95 | #format svg |
paul@384 | 96 | #transform notugly |
paul@384 | 97 | digraph provider_removal { |
paul@384 | 98 | node [fontsize="12.0",fontname="sans-serif",shape=box]; |
paul@384 | 99 | edge [fontsize="12.0",fontname="sans-serif"]; |
paul@384 | 100 | rankdir=LR; |
paul@384 | 101 | |
paul@384 | 102 | ResourceServer -> Resource [label="close"]; |
paul@384 | 103 | Resource -> ProviderRegistry [label="detach"]; |
paul@384 | 104 | ProviderRegistry -> Provider [label="remove"]; |
paul@384 | 105 | } |
paul@384 | 106 | }}} |
paul@384 | 107 | |
paul@384 | 108 | ######## |
paul@384 | 109 | |
paul@384 | 110 | == Resources == |
paul@384 | 111 | |
paul@384 | 112 | Resources are objects accessed by clients that support a basic level of |
paul@384 | 113 | accounting and management. |
paul@384 | 114 | |
paul@384 | 115 | The base interface of a resource is as follows: |
paul@384 | 116 | |
paul@384 | 117 | {{{ |
paul@390 | 118 | void activate(); |
paul@390 | 119 | void close(); |
paul@384 | 120 | }}} |
paul@384 | 121 | |
paul@384 | 122 | Activation of a resource is an optional operation that performs any |
paul@384 | 123 | initialisation before a resource is made available to its user. |
paul@384 | 124 | |
paul@384 | 125 | In practice, other operations are required to make resources useful. |
paul@384 | 126 | |
paul@384 | 127 | In some cases, resources provide the mechanism by which each user of a |
paul@384 | 128 | filesystem object may access that object independently. They would then |
paul@384 | 129 | effectively provide a session in which accesses can occur. |
paul@384 | 130 | |
paul@384 | 131 | === Directory Resources === |
paul@384 | 132 | |
paul@384 | 133 | Directory resources primarily expose the contents of directories in the |
paul@384 | 134 | filesystem to a user. They employ directory accessors which concern themselves |
paul@384 | 135 | with the actual filesystem content. |
paul@384 | 136 | |
paul@384 | 137 | [[Components#Directories|Directory components]] are provided using directory |
paul@384 | 138 | resources. |
paul@384 | 139 | |
paul@384 | 140 | <<Anchor(Pager)>> |
paul@384 | 141 | === Pagers or File Resources === |
paul@384 | 142 | |
paul@384 | 143 | Pagers are resources that support dataspace access operations, thus allowing |
paul@384 | 144 | the resources to expose filesystem content in mapped memory regions to a |
paul@384 | 145 | particular user of the object providing the content. |
paul@384 | 146 | |
paul@384 | 147 | [[Components#Files|File components]] and [[Components#Pipes|pipe components]] |
paul@384 | 148 | are provided using pagers. |
paul@384 | 149 | |
paul@384 | 150 | === Filesystem Resources === |
paul@384 | 151 | |
paul@384 | 152 | Filesystem resources provide the entry point for access to a filesystem by |
paul@384 | 153 | other components or programs. Since filesystems often enforce identity-based |
paul@384 | 154 | access controls, a filesystem resource will typically support the |
paul@384 | 155 | `open_for_user` operation in various forms, with the result of this operation |
paul@384 | 156 | being the instantiation of an `OpenerResource` configured for the indicated |
paul@384 | 157 | user identity. |
paul@384 | 158 | |
paul@384 | 159 | == Registries == |
paul@384 | 160 | |
paul@384 | 161 | The basic mechanism for obtaining a resource involves a registry, as |
paul@384 | 162 | illustrated by the following diagram. |
paul@384 | 163 | |
paul@384 | 164 | ######## A graph showing the use of a registry in obtaining resources |
paul@384 | 165 | |
paul@384 | 166 | {{{#!graphviz |
paul@384 | 167 | #format svg |
paul@384 | 168 | #transform notugly |
paul@384 | 169 | digraph registry { |
paul@384 | 170 | node [fontsize="12.0",fontname="sans-serif",shape=box]; |
paul@384 | 171 | edge [fontsize="12.0",fontname="sans-serif"]; |
paul@384 | 172 | rankdir=LR; |
paul@384 | 173 | |
paul@384 | 174 | subgraph { |
paul@384 | 175 | node [label="OpenerContextResource"]; |
paul@384 | 176 | rank=same; |
paul@384 | 177 | |
paul@384 | 178 | OpenerContextResource1 -> OpenerContextResource2 [dir=none,style=dotted]; |
paul@384 | 179 | } |
paul@384 | 180 | |
paul@384 | 181 | subgraph { |
paul@384 | 182 | node [label="OpenerResource"]; |
paul@384 | 183 | rank=same; |
paul@384 | 184 | |
paul@384 | 185 | OpenerResource1 -> OpenerResource2 [dir=none,style=dotted]; |
paul@384 | 186 | } |
paul@384 | 187 | |
paul@384 | 188 | subgraph { |
paul@384 | 189 | rank=same; |
paul@384 | 190 | |
paul@384 | 191 | ResourceRegistry -> Resource; |
paul@384 | 192 | } |
paul@384 | 193 | |
paul@384 | 194 | subgraph { |
paul@384 | 195 | rank=same; |
paul@384 | 196 | |
paul@384 | 197 | ProviderRegistry -> Provider; |
paul@384 | 198 | } |
paul@384 | 199 | |
paul@384 | 200 | OpenerContextResource1 -> OpenerResource1 [label="open"]; |
paul@384 | 201 | OpenerResource1 -> ResourceRegistry [label="get_resource"]; |
paul@384 | 202 | ResourceRegistry -> ProviderRegistry [label="get/set"]; |
paul@384 | 203 | |
paul@384 | 204 | Provider -> Resource -> OpenerResource2 -> OpenerContextResource2; |
paul@384 | 205 | } |
paul@384 | 206 | }}} |
paul@384 | 207 | |
paul@384 | 208 | ######## |
paul@384 | 209 | |
paul@384 | 210 | The `ResourceRegistry` coordinates access to filesystem resources and, through |
paul@384 | 211 | synchronisation, prevents conflicting operations from occurring concurrently. |
paul@384 | 212 | For example, a removal operation on a file may not be allowed to occur while |
paul@384 | 213 | an opening operation is in progress. |
paul@384 | 214 | |
paul@384 | 215 | To achieve this, a common lock is employed to serialise access, with any |
paul@384 | 216 | underlying filesystem operations being initiated from the registry while the |
paul@384 | 217 | lock is held. An object providing the `FileOpening` interface for a filesystem |
paul@384 | 218 | provides such operations, and the following interaction pattern is thereby |
paul@384 | 219 | employed: |
paul@384 | 220 | |
paul@384 | 221 | ######## A graph showing the registry coordinating filesystem operations |
paul@384 | 222 | |
paul@384 | 223 | {{{#!graphviz |
paul@384 | 224 | #format svg |
paul@384 | 225 | #transform notugly |
paul@384 | 226 | digraph registry_operations { |
paul@384 | 227 | node [fontsize="12.0",fontname="sans-serif",shape=box]; |
paul@384 | 228 | edge [fontsize="12.0",fontname="sans-serif"]; |
paul@384 | 229 | rankdir=LR; |
paul@384 | 230 | |
paul@384 | 231 | OpenerContextResource -> OpenerResource -> ResourceRegistry -> FileOpening; |
paul@384 | 232 | } |
paul@384 | 233 | }}} |
paul@384 | 234 | |
paul@384 | 235 | ######## |
paul@384 | 236 | |
paul@384 | 237 | Since the `ResourceRegistry` functionality is generic, it could be specialised |
paul@384 | 238 | for each filesystem or be configured with an appropriate reference to a |
paul@384 | 239 | `FileOpening` object. The `OpenerResource` would then be generic, invoking the |
paul@384 | 240 | registry which would, in turn, invoke the opening object. |
paul@384 | 241 | |
paul@384 | 242 | However, the chosen approach is to permit `OpenerResource` objects to |
paul@384 | 243 | implement the `FileOpening` interface for each filesystem, meaning that the |
paul@384 | 244 | `ResourceRegistry` will end up being called by the opener and then invoking |
paul@384 | 245 | the opener in return. This is slightly more convenient than the alternative |
paul@384 | 246 | since the opener can be configured with a given user identity, and such |
paul@384 | 247 | identity details will ultimately be employed when accessing the underlying |
paul@384 | 248 | filesystem itself. |