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