1 = Filesystem Access = 2 3 Within the [[ServerLibrary|filesystem server library]], a number of different 4 abstractions and mechanisms are employed to provide access to filesystem 5 objects. An overview of these abstractions is presented below. 6 7 ######## A graph showing the relationships between filesystem access 8 ######## components 9 10 {{{#!graphviz 11 #format svg 12 #transform notugly 13 digraph components { 14 node [fontsize="12.0",fontname="sans-serif",shape=box]; 15 edge [fontsize="12.0",fontname="sans-serif"]; 16 rankdir=LR; 17 18 subgraph { 19 rank=same; 20 21 Filesystem_note [shape=note,style=filled,fillcolor=gold,label="Configures opener\ncomponents"]; 22 Filesystem; 23 24 Filesystem_note -> Filesystem [dir=none,style=dotted]; 25 26 open_for_user [fontsize="10.0",shape=ellipse]; 27 28 Opener; 29 Opener_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfile open\noperation"]; 30 31 Opener -> Opener_note [dir=none,style=dotted]; 32 } 33 34 subgraph { 35 rank=same; 36 37 ResourceRegistry_note [shape=note,style=filled,fillcolor=gold,label="Records\nopen file\nsessions"]; 38 ResourceRegistry; 39 40 ResourceRegistry_note -> ResourceRegistry [dir=none,style=dotted]; 41 } 42 43 subgraph { 44 rank=same; 45 46 Resource_note [shape=note,style=filled,fillcolor=gold,label="Provides\nfilesystem\ncontent"]; 47 Resource [label="Resource\n(Pager)"]; 48 49 Resource_note -> Resource [dir=none,style=dotted]; 50 } 51 52 subgraph { 53 rank=same; 54 55 Provider_note [shape=note,style=filled,fillcolor=gold,label="Represents\nopen file"]; 56 Provider; 57 58 Provider_note -> Provider [dir=none,style=dotted]; 59 } 60 61 subgraph { 62 rank=same; 63 64 PageMapper_note [shape=note,style=filled,fillcolor=gold,label="Provides\npopulated\nfile pages"]; 65 PageMapper; 66 67 PageMapper_note -> PageMapper [dir=none,style=dotted]; 68 } 69 70 ProviderRegistry [shape=record,label="<head> ProviderRegistry | ... | { file-n | provider-n } | ... "]; 71 72 subgraph { 73 rank=same; 74 75 FileOpening_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfilesystem\nfunctionality"]; 76 FileOpening; 77 78 FileOpening_note -> FileOpening [dir=none,style=dotted]; 79 } 80 81 subgraph { 82 rank=same; 83 84 Accessor_note [shape=note,style=filled,fillcolor=gold,label="Populates\nfile pages"]; 85 Accessor; 86 Accessor_note -> Accessor [dir=none,style=dotted]; 87 } 88 89 /* Configuring an opener. */ 90 91 Filesystem -> open_for_user [dir=none]; 92 open_for_user -> Opener; 93 94 /* Opening a file. */ 95 96 Opener -> ResourceRegistry -> Resource; 97 ResourceRegistry -> FileOpening; 98 99 Provider -> PageMapper; 100 FileOpening -> Accessor; 101 102 /* Closing a file. */ 103 104 Resource -> Provider -> ProviderRegistry:head; 105 106 /* Open file management. */ 107 108 ResourceRegistry -> ProviderRegistry:head; 109 } 110 }}} 111 112 ######## 113 114 Firstly, an `Opener` must be obtained from a `Filesystem`, this configuring 115 the `Opener` for a particular user identity. 116 117 An `Opener` requests a `Resource` from a `ResourceRegistry`, each `Resource` 118 acting as a [[ServerLibrary#Pager|`Pager`]] and providing the actual access 119 mechanism to file content for a particular program. Since many programs may 120 access the same file simultaneously, a `Provider` object represents a file 121 opened in the system, retaining a `PageMapper` that coordinates the collective 122 access to the file (see the [[Paging|paging documentation]] for more 123 information). 124 125 A `ProviderRegistry` maintains the record of opened files, yielding a new 126 `Provider` for an unopened file or an existing `Provider` for a file that is 127 already open. The `ProviderRegistry` monitors the usage of files, discarding 128 any `Provider` no longer in use. 129 130 == Opening Files == 131 132 Opening a file involves the identification of the filesystem object involved, 133 the acquisition of a `Provider` representing the file, and the creation of a 134 `Resource` to deliver file content to the requesting program. 135 136 ######## A graph showing the opening mechanism for an already-open file 137 138 {{{#!graphviz 139 #format svg 140 #transform notugly 141 digraph opening_open { 142 node [fontsize="12.0",fontname="sans-serif",shape=box]; 143 edge [fontsize="12.0",fontname="sans-serif"]; 144 rankdir=LR; 145 146 subgraph { 147 rank=same; 148 149 Opener_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfile open\noperation"]; 150 Opener; 151 152 Opener_note -> Opener [dir=none,style=dotted]; 153 } 154 155 subgraph { 156 rank=same; 157 158 ResourceRegistry_note [shape=note,style=filled,fillcolor=gold,label="Records\nopen file\nsessions"]; 159 ResourceRegistry; 160 161 Resource_returned [label="Resource\n(Pager)"]; 162 Resource_returned_note [shape=note,style=filled,fillcolor=gold,label="Provides\nfilesystem\ncontent"]; 163 164 ResourceRegistry_note -> ResourceRegistry [dir=none,style=dotted]; 165 Resource_returned -> Resource_returned_note [dir=none,style=dotted]; 166 } 167 168 subgraph { 169 rank=same; 170 171 Resource [label="Resource\n(Pager)"]; 172 173 FileOpening_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfilesystem\nfunctionality"]; 174 FileOpening; 175 176 FileOpening_note -> FileOpening [dir=none,style=dotted]; 177 } 178 179 subgraph { 180 rank=max; 181 182 ProviderRegistry_note [shape=note,style=filled,fillcolor=gold,label="Provides\nopen files"]; 183 ProviderRegistry; 184 185 Provider; 186 Provider_note [shape=note,style=filled,fillcolor=gold,label="Represents\nopen file"]; 187 188 ProviderRegistry_note -> ProviderRegistry [dir=none,style=dotted]; 189 Provider -> Provider_note [dir=none,style=dotted]; 190 } 191 192 /* Obtaining a resource from the registry. */ 193 194 Opener -> ResourceRegistry [label="get_resource"]; 195 ResourceRegistry -> Resource_returned [dir=none]; 196 Resource_returned -> Opener; 197 198 /* Obtaining a provider. */ 199 200 ResourceRegistry -> FileOpening [label="get_fileid"]; 201 ResourceRegistry -> ProviderRegistry [label="get(fileid)"]; 202 ProviderRegistry -> Provider; 203 204 /* Obtaining the resource from the provider. */ 205 206 ResourceRegistry -> Provider [label="make_resource"]; 207 Provider -> Resource [dir=none]; 208 Resource -> ResourceRegistry; 209 } 210 }}} 211 212 ######## 213 214 Where a provider does not already exist, with the file not having been opened 215 already, some extra interactions occur: 216 217 ######## A graph showing opening mechanism details for an unopened file 218 219 {{{#!graphviz 220 #format svg 221 #transform notugly 222 digraph opening_unopened { 223 node [fontsize="12.0",fontname="sans-serif",shape=box]; 224 edge [fontsize="12.0",fontname="sans-serif"]; 225 rankdir=LR; 226 227 Opener; 228 229 ResourceRegistry; 230 231 subgraph { 232 rank=same; 233 234 Provider_note [shape=note,style=filled,fillcolor=gold,label="Created\nand set in\nregistry"]; 235 Provider; 236 237 Accessor; 238 PageMapper; 239 PageMapper_note [shape=note,style=filled,fillcolor=gold,label="Created\nfor provider"]; 240 241 Provider_note -> Provider [dir=none,style=dotted]; 242 PageMapper -> PageMapper_note [dir=none,style=dotted]; 243 } 244 245 subgraph { 246 rank=max; 247 248 ProviderRegistry_note [shape=note,style=filled,fillcolor=gold,label="Records\nopen files"]; 249 ProviderRegistry; 250 FileOpening; 251 FileOpening_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfilesystem\nfunctionality"]; 252 253 ProviderRegistry_note -> ProviderRegistry [dir=none,style=dotted]; 254 FileOpening -> FileOpening_note [dir=none,style=dotted]; 255 } 256 257 Opener -> ResourceRegistry [dir=none,style=dashed]; 258 259 /* Obtaining a new provider. */ 260 261 ResourceRegistry -> FileOpening [label="make_accessor"]; 262 FileOpening -> Accessor [dir=none]; 263 Accessor -> PageMapper -> ResourceRegistry; 264 265 ResourceRegistry -> Provider [dir=none]; 266 Provider -> ProviderRegistry [label="set(fileid)"]; 267 } 268 }}} 269 270 ######## 271 272 The `Resource` obtained to deliver file content to a client will be served by 273 a distinct thread through a dedicated communications endpoint, leaving the 274 `Opener` available to satisfy other requests. This new thread will remain 275 active while a file remains open, being terminated when the file is closed and 276 the communications endpoint released by the client. 277 278 == Notifications == 279 280 Within the filesystem access architecture, users of files may act in ways that 281 may notify other users of the same file. To support such notifications, an 282 interface is provided for filesystem clients to subscribe and unsubscribe to 283 notifications. The notifications themselves occur when files are opened, 284 modified and closed. Pipes also generate notifications when a pipe reader 285 makes more space available for the writer. 286 287 ######## A graph showing subscription to notifications 288 289 {{{#!graphviz 290 #format svg 291 #transform notugly 292 digraph notification_subscriptions { 293 node [fontsize="12.0",fontname="sans-serif",shape=box]; 294 edge [fontsize="12.0",fontname="sans-serif"]; 295 rankdir=LR; 296 297 subgraph { 298 rank=same; 299 300 Client1 [label="Client\nprogram"]; 301 Client2 [label="Client\nprogram"]; 302 } 303 304 subgraph { 305 rank=same; 306 307 Notifier1_note [shape=note,style=filled,fillcolor=gold,label="Created for\nnotifications"]; 308 Notifier1 [label="Notifier"]; 309 Notifier2 [label="Notifier"]; 310 311 Notifier1_note -> Notifier1 -> Notifier2 [dir=none,style=dotted]; 312 } 313 314 subgraph { 315 rank=same; 316 317 NotifierResource1_note [shape=note,style=filled,fillcolor=gold,label="Created to\nreceive\nnotifications"]; 318 NotifierResource1 [label="NotifierResource"]; 319 NotifierResource2 [label="NotifierResource"]; 320 321 NotifierResource1_note -> NotifierResource1 -> NotifierResource2 [dir=none,style=dotted]; 322 } 323 324 subgraph { 325 rank=same; 326 327 Resource1 [label="Resource\n(Pager)"]; 328 Resource2 [label="Resource\n(Pager)"]; 329 } 330 331 subgraph { 332 rank=same; 333 334 NotifierResource1_subscribe_note [shape=note,style=filled,fillcolor=gold,label="Propagated to\nprovider"]; 335 NotifierResource1_subscribe [label="NotifierResource"]; 336 NotifierResource2_subscribe [label="NotifierResource"]; 337 338 NotifierResource1_subscribe_note -> NotifierResource1_subscribe -> NotifierResource2_subscribe [dir=none,style=dotted]; 339 } 340 341 subgraph { 342 rank=same; 343 344 Provider_note [shape=note,style=filled,fillcolor=gold,label="Manages file\nsubscriptions"]; 345 Provider [label="Provider"]; 346 347 Provider_note -> Provider [dir=none,style=dotted]; 348 } 349 350 /* Subscribing. */ 351 352 Client1 -> Notifier1 [label="subscribe"]; 353 Notifier1 -> NotifierResource1 [dir=none]; 354 NotifierResource1 -> Resource1 [label="subscribe"]; 355 356 Resource1 -> NotifierResource1_subscribe [dir=none]; 357 NotifierResource1_subscribe -> Provider [label="subscribe"]; 358 359 Client2 -> Notifier2 [label="subscribe"]; 360 Notifier2 -> NotifierResource2 [dir=none]; 361 NotifierResource2 -> Resource2 [label="subscribe"]; 362 363 Resource2 -> NotifierResource2_subscribe [dir=none]; 364 NotifierResource2_subscribe -> Provider [label="subscribe"]; 365 } 366 }}} 367 368 ######## 369 370 An example of a notification scenario is given below: 371 372 ######## A graph showing notification 373 374 {{{#!graphviz 375 #format svg 376 #transform notugly 377 digraph notifications { 378 node [fontsize="12.0",fontname="sans-serif",shape=box]; 379 edge [fontsize="12.0",fontname="sans-serif"]; 380 rankdir=LR; 381 382 subgraph { 383 rank=min; 384 385 Client2 [label="Client\nprogram"]; 386 Client1 [label="Client\nprogram"]; 387 } 388 389 subgraph { 390 rank=max; 391 392 Provider_note [shape=note,style=filled,fillcolor=gold,label="Propagates\nnotifications"]; 393 Provider [label="Provider"]; 394 395 NotifierResource1 [label="NotifierResource"]; 396 NotifierResource1_note [shape=note,style=filled,fillcolor=gold,label="Receives\nnotifications"]; 397 398 Provider_note -> Provider [dir=none,style=dotted]; 399 Provider -> NotifierResource1 [dir=none,style=invis]; 400 NotifierResource1 -> NotifierResource1_note [dir=none,style=dotted]; 401 } 402 403 subgraph { 404 rank=same; 405 406 Resource2_note [shape=note,style=filled,fillcolor=gold,label="Generates\nnotification"]; 407 Resource2 [label="Resource\n(Pager)"]; 408 409 Notifier1 [label="Notifier"]; 410 Notifier1_note [shape=note,style=filled,fillcolor=gold,label="Created for\nnotifications"]; 411 412 Resource2_note -> Resource2 [dir=none,style=dotted]; 413 Notifier1 -> Notifier1_note [dir=none,style=dotted]; 414 } 415 416 /* A notification scenario. */ 417 418 Client1 -> Notifier1 [label="wait"]; 419 420 Client2 -> Resource2 [label="resize"]; 421 Resource2 -> Provider [label="notify_others"]; 422 423 Provider -> NotifierResource1 [label="notify"]; 424 NotifierResource1 -> Notifier1 [label="notify"]; 425 } 426 }}} 427 428 ######## 429 430 Notifications provide the basis for blocking reading and writing operations, 431 with pipe endpoints depending on such blocking for efficient use of pipes. The 432 interactions involved when transferring data through pipes are depicted below. 433 434 ######## A graph showing notifications employed by pipe endpoints 435 436 {{{#!graphviz 437 #format svg 438 #transform notugly 439 digraph pipe_notifications { 440 node [fontsize="12.0",fontname="sans-serif",shape=box]; 441 edge [fontsize="12.0",fontname="sans-serif"]; 442 rankdir=LR; 443 444 subgraph { 445 rank=min; 446 447 Client1_note [shape=note,style=filled,fillcolor=gold,label="Fill pipe\nthen request\nmore space"]; 448 Client1 [label="Client\nprogram\n(writer)"]; 449 Client1_wait_note [shape=note,style=filled,fillcolor=gold,label="Waiting after\nfilling pipe"]; 450 Client1_wait [label="Client\nprogram\n(writer)"]; 451 452 Client1_note -> Client1 [dir=none,style=dotted]; 453 Client1_wait_note -> Client1_wait [dir=none,style=dotted]; 454 455 Client1 -> Client1_wait_note [dir=none,style=invis]; 456 } 457 458 subgraph { 459 rank=max; 460 461 Client2_note [shape=note,style=filled,fillcolor=gold,label="Waiting for\nmore content\nbefore reading"]; 462 Client2 [label="Client\nprogram\n(reader)"]; 463 Client2_read_note [shape=note,style=filled,fillcolor=gold,label="Empty pipe\nthen request\nmore content"]; 464 Client2_read [label="Client\nprogram\n(reader)"]; 465 466 Client2_note -> Client2 [dir=none,style=dotted]; 467 Client2_read_note -> Client2_read [dir=none,style=dotted]; 468 } 469 470 subgraph { 471 rank=same; 472 473 Notifier1 [label="Notifier"]; 474 Resource1 [label="Resource\n(PipePager)"]; 475 } 476 477 subgraph { 478 rank=same; 479 480 Notifier2 [label="Notifier"]; 481 Resource2 [label="Resource\n(PipePager)"]; 482 } 483 484 subgraph { 485 rank=same; 486 487 PipePaging [label="Provider\n(PipePaging)"]; 488 PipePaging_space [label="Provider\n(PipePaging)"]; 489 } 490 491 /* Making content available. */ 492 493 Client1 -> Resource1 [label="next_region"]; 494 Resource1 -> PipePaging [label="add_region"]; 495 PipePaging -> Notifier2 [label="notify"]; 496 Client2 -> Notifier2 [label="wait"]; 497 498 /* Making space available. */ 499 500 Client2_read -> Resource2 [label="next_region"]; 501 Resource2 -> PipePaging_space [label="next_region"]; 502 PipePaging_space -> Notifier1 [label="notify"]; 503 Client1_wait -> Notifier1 [label="wait"]; 504 } 505 }}} 506 507 ######## 508 509 == Closing Files == 510 511 Files are closed when client programs release their references to the resource 512 capabilities used to access files. The mechanism by which this is done 513 involves the registration of an IRQ object that is bound to the same thread as 514 the one used by resources to service file access requests. This IRQ object is 515 associated with the IPC gate through which such requests occur, and the IRQ 516 object is configured to deliver an interrupt message when the final reference 517 to the IPC gate has been released. 518 519 When a client program terminates, its references to capabilities should be 520 released, and this should cause a reference counter associated with the IPC 521 gate to decrement. Upon the loss of the final reference and the delivery of 522 the interrupt, the server framework will call a `close` method on the 523 `Resource` object concerned, this implementing the 524 [[ServerLibrary#Accountable|`Accountable`]] interface. 525 526 ######## A graph showing the closing mechanism 527 528 {{{#!graphviz 529 #format svg 530 #transform notugly 531 digraph closing { 532 node [fontsize="12.0",fontname="sans-serif",shape=box]; 533 edge [fontsize="12.0",fontname="sans-serif"]; 534 rankdir=LR; 535 536 Client [label="Client\nprogram"]; 537 538 subgraph { 539 rank=same; 540 541 Gate_note [shape=note,style=filled,fillcolor=gold,label="Actual\nendpoint"]; 542 Gate [label="IPC gate",shape="octagon"]; 543 IRQ [shape="triangle"]; 544 ResourceServer; 545 ResourceServer_note [shape=note,style=filled,fillcolor=gold,label="Handles\nrequests"]; 546 547 Gate_note -> Gate [dir=none,style=dotted]; 548 ResourceServer -> ResourceServer_note [dir=none,style=dotted]; 549 } 550 551 Resource [label="Resource\n(Pager)"]; 552 553 Provider; 554 555 ProviderRegistry; 556 557 Client -> Gate [style=dashed,label="(release)"]; 558 Gate -> IRQ -> ResourceServer; 559 ResourceServer -> Resource [label="close"]; 560 Resource -> Provider [label="notify_others\nunsubscribe"]; 561 Resource -> ProviderRegistry [label="detach"]; 562 ProviderRegistry -> Provider [label="detach\ndelete"]; 563 } 564 }}} 565 566 ######## 567 568 As a consequence of closing a file, an `unsubscribe` operation performed on 569 the `Provider` should cause any `Notifier` objects associated with the 570 `Resource` object to be released. Meanwhile, the `ResourceServer` will discard 571 the `Resource` object before the server thread terminates. 572 573 == Removing Files == 574 575 Unix filesystem semantics typically allow a file to be "unlinked" while still 576 being accessed by a program, with accesses still being directed to the file, 577 but with the file being removed from a directory and potentially being 578 replaced by another file. To support this, a mechanism is provided to defer 579 any removal of an open file. 580 581 ######## A graph showing the removal mechanism 582 583 {{{#!graphviz 584 #format svg 585 #transform notugly 586 digraph removing { 587 node [fontsize="12.0",fontname="sans-serif",shape=box]; 588 edge [fontsize="12.0",fontname="sans-serif"]; 589 rankdir=LR; 590 591 subgraph { 592 rank=same; 593 594 Client1_note [shape=note,style=filled,fillcolor=gold,label="Removing\nopen file"]; 595 Client1 [label="Client\nprogram"]; 596 Client2 [label="Client\nprogram"]; 597 Client2_note [shape=note,style=filled,fillcolor=gold,label="Closes\nopen file"]; 598 599 Client1_note -> Client1 [dir=none,style=dotted]; 600 Client2 -> Client2_note [dir=none,style=dotted]; 601 602 Client1 -> Client2 [dir=none,style=invis]; 603 } 604 605 subgraph { 606 rank=same; 607 608 Resource [label="Resource\n(Pager)"]; 609 Opener; 610 } 611 612 subgraph { 613 rank=same; 614 615 Provider1 [label="Provider"]; 616 Provider1_note [shape=note,style=filled,fillcolor=gold,label="Maintains\nremoval\nstate"]; 617 Provider2 [label="Provider"]; 618 619 Provider1 -> Provider1_note -> Provider2 [dir=none,style=dotted]; 620 } 621 622 ProviderRegistry1 [label="ProviderRegistry"]; 623 ProviderRegistry2 [label="ProviderRegistry"]; 624 625 subgraph { 626 rank=same; 627 628 FileOpening; 629 Accessor_note [shape=note,style=filled,fillcolor=gold,label="Performing\nfilesystem\noperations"]; 630 Accessor; 631 632 FileOpening -> Accessor_note -> Accessor [dir=none,style=dotted]; 633 } 634 635 /* Removing the file while it remains open. */ 636 637 Client1 -> Opener [label="remove\n(via context)"]; 638 Opener -> ResourceRegistry [label="remove_provider"]; 639 ResourceRegistry -> FileOpening [label="get_fileid\nunlink_object"]; 640 ResourceRegistry -> ProviderRegistry1 [label="get(fileid)"]; 641 ResourceRegistry -> Provider1 [label="remove_pending"]; 642 643 /* File closure and deferred removal. */ 644 645 Client2 -> Resource [label="close"]; 646 Resource -> Provider2 [label="notify_others\nunsubscribe"]; 647 Resource -> ProviderRegistry2 [label="detach"]; 648 ProviderRegistry2 -> Provider2 [label="remove"]; 649 Provider2 -> Accessor [label="remove"]; 650 } 651 }}} 652 653 ########