Full Code of DocJade/fluster_rs for AI

master 36150f2f440a cached
133 files
678.4 KB
162.7k tokens
593 symbols
1 requests
Download .txt
Showing preview only (722K chars total). Download the full file or copy to clipboard to get everything.
Repository: DocJade/fluster_rs
Branch: master
Commit: 36150f2f440a
Files: 133
Total size: 678.4 KB

Directory structure:
gitextract_wp7ph85n/

├── .gitignore
├── .vscode/
│   └── settings.json
├── Cargo.toml
├── LICENSE.txt
├── build.rs
├── readme.md
├── src/
│   ├── error_types/
│   │   ├── block.rs
│   │   ├── conversions.rs
│   │   ├── critical.rs
│   │   ├── drive.rs
│   │   ├── filesystem.rs
│   │   ├── header.rs
│   │   └── mod.rs
│   ├── filesystem/
│   │   ├── disk_backup/
│   │   │   ├── mod.rs
│   │   │   ├── restore.rs
│   │   │   └── update.rs
│   │   ├── file_attributes/
│   │   │   ├── conversion.rs
│   │   │   └── mod.rs
│   │   ├── file_handle/
│   │   │   ├── file_handle_methods.rs
│   │   │   ├── file_handle_struct.rs
│   │   │   └── mod.rs
│   │   ├── filesystem_struct.rs
│   │   ├── fuse_filesystem_methods.rs
│   │   ├── internal_filesystem_methods.rs
│   │   ├── item_flag/
│   │   │   ├── flag_struct.rs
│   │   │   └── mod.rs
│   │   └── mod.rs
│   ├── filesystem_design/
│   │   ├── allocation_spec.md
│   │   ├── dense_disk.md
│   │   ├── design_choices.md
│   │   ├── disk_header.md
│   │   ├── disk_layout.md
│   │   ├── inode_format.md
│   │   ├── pool_header.md
│   │   ├── pool_layout.md
│   │   └── possible_speed_improvments.md
│   ├── helpers/
│   │   ├── hex_view.rs
│   │   └── mod.rs
│   ├── lib.rs
│   ├── main.rs
│   ├── pool/
│   │   ├── disk/
│   │   │   ├── blank_disk/
│   │   │   │   ├── blank_disk_methods.rs
│   │   │   │   ├── blank_disk_struct.rs
│   │   │   │   └── mod.rs
│   │   │   ├── drive_methods.rs
│   │   │   ├── drive_struct.rs
│   │   │   ├── generic/
│   │   │   │   ├── block/
│   │   │   │   │   ├── allocate/
│   │   │   │   │   │   ├── block_allocation.rs
│   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   └── tests.rs
│   │   │   │   │   ├── block_structs.rs
│   │   │   │   │   ├── crc.rs
│   │   │   │   │   └── mod.rs
│   │   │   │   ├── disk_trait.rs
│   │   │   │   ├── generic_structs/
│   │   │   │   │   ├── find_space.rs
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   └── pointer_struct.rs
│   │   │   │   ├── io/
│   │   │   │   │   ├── cache/
│   │   │   │   │   │   ├── cache_implementation.rs
│   │   │   │   │   │   ├── cache_io.rs
│   │   │   │   │   │   ├── cached_allocation.rs
│   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   └── statistics.rs
│   │   │   │   │   ├── checked_io.rs
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   ├── read.rs
│   │   │   │   │   ├── wipe.rs
│   │   │   │   │   └── write.rs
│   │   │   │   └── mod.rs
│   │   │   ├── mod.rs
│   │   │   ├── pool_disk/
│   │   │   │   ├── block/
│   │   │   │   │   ├── header/
│   │   │   │   │   │   ├── header_methods.rs
│   │   │   │   │   │   ├── header_struct.rs
│   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   └── tests.rs
│   │   │   │   │   └── mod.rs
│   │   │   │   ├── mod.rs
│   │   │   │   ├── pool_disk_methods.rs
│   │   │   │   └── pool_disk_struct.rs
│   │   │   ├── standard_disk/
│   │   │   │   ├── block/
│   │   │   │   │   ├── directory/
│   │   │   │   │   │   ├── directory_methods.rs
│   │   │   │   │   │   ├── directory_struct.rs
│   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   └── tests.rs
│   │   │   │   │   ├── file_extents/
│   │   │   │   │   │   ├── file_extents_methods.rs
│   │   │   │   │   │   ├── file_extents_struct.rs
│   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   └── tests.rs
│   │   │   │   │   ├── header/
│   │   │   │   │   │   ├── header_methods.rs
│   │   │   │   │   │   ├── header_struct.rs
│   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   └── tests.rs
│   │   │   │   │   ├── inode/
│   │   │   │   │   │   ├── inode_methods.rs
│   │   │   │   │   │   ├── inode_struct.rs
│   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   └── tests.rs
│   │   │   │   │   ├── io/
│   │   │   │   │   │   ├── directory/
│   │   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   │   ├── movement.rs
│   │   │   │   │   │   │   ├── read.rs
│   │   │   │   │   │   │   ├── tests.rs
│   │   │   │   │   │   │   ├── types.rs
│   │   │   │   │   │   │   └── write.rs
│   │   │   │   │   │   ├── file/
│   │   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   │   ├── movement.rs
│   │   │   │   │   │   │   ├── read.rs
│   │   │   │   │   │   │   ├── tests.rs
│   │   │   │   │   │   │   └── write.rs
│   │   │   │   │   │   ├── inode/
│   │   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   │   ├── read.rs
│   │   │   │   │   │   │   ├── tests.rs
│   │   │   │   │   │   │   └── write.rs
│   │   │   │   │   │   └── mod.rs
│   │   │   │   │   └── mod.rs
│   │   │   │   ├── mod.rs
│   │   │   │   ├── standard_disk_methods.rs
│   │   │   │   └── standard_disk_struct.rs
│   │   │   └── unknown_disk/
│   │   │       ├── mod.rs
│   │   │       ├── unknown_disk_methods.rs
│   │   │       └── unknown_disk_struct.rs
│   │   ├── io/
│   │   │   ├── allocate.rs
│   │   │   └── mod.rs
│   │   ├── mod.rs
│   │   └── pool_actions/
│   │       ├── mod.rs
│   │       ├── pool_methods.rs
│   │       └── pool_struct.rs
│   └── tui/
│       ├── layout.rs
│       ├── mod.rs
│       ├── notify.rs
│       ├── prompts.rs
│       ├── state.rs
│       ├── tasks.rs
│       └── tui_struct.rs
├── tests/
│   ├── directory.rs
│   ├── file.rs
│   ├── mount_filesystem.rs
│   ├── start_filesystem.rs
│   └── test_common.rs
└── windows.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
/target
/temp_disks
.vscode/launch.json


================================================
FILE: .vscode/settings.json
================================================
{
    "rust-analyzer.runnables.extraEnv": {
        "RUST_LOG": "info,fluster_fs=debug" // debug level logs in tests
    },
    "rust-analyzer.runnables.cargo.args": [
        "--no-fail-fast",
        "--",
        "--nocapture"
    ],
    "cSpell.words": [
        "uncatagorized"
    ]
}

================================================
FILE: Cargo.toml
================================================
[package]
name = "fluster_fs"
version = "0.1.0"
edition = "2024"

[dependencies]
bitflags = "2.9.1"
clap = { version = "4.5.41", features = ["derive"] }
crc32c = "0.6.8"
ctrlc = "3.4.7"
enum_dispatch = "0.3.13"
env_logger = "0.11.8"
fuse_mt = "0.6.1"
lazy_static = "1.5.0"
libc = "0.2.174"
log = "0.4.27"
log-panics = { version = "2.1.0", features = ["with-backtrace"] }
once_cell = "1.21.3"
oneshot = "0.1.11"
rand = "0.9.1"
ratatui = "0.29.0"
rprompt = "2.2.0"
tempfile = "3.20.0"
test-log = "0.2.18"
thiserror = "2.0.12"
tui-logger = "0.17.3"
tui-textarea = "0.7.0"

# Floppy builds should be small enough to fit on a single floppy.
# Because thats funny.
[profile.floppy]
inherits = "release"
strip = true  # Automatically strip symbols from the binary.
opt-level = "z"  # Optimize for size.
lto = true
codegen-units = 1
# panic = "abort" # We will NOT use abort. Abort is gross.

================================================
FILE: LICENSE.txt
================================================
Attribution 4.0 International

=======================================================================

Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.

Using Creative Commons Public Licenses

Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.

     Considerations for licensors: Our public licenses are
     intended for use by those authorized to give the public
     permission to use material in ways otherwise restricted by
     copyright and certain other rights. Our licenses are
     irrevocable. Licensors should read and understand the terms
     and conditions of the license they choose before applying it.
     Licensors should also secure all rights necessary before
     applying our licenses so that the public can reuse the
     material as expected. Licensors should clearly mark any
     material not subject to the license. This includes other CC-
     licensed material, or material used under an exception or
     limitation to copyright. More considerations for licensors:
    wiki.creativecommons.org/Considerations_for_licensors

     Considerations for the public: By using one of our public
     licenses, a licensor grants the public permission to use the
     licensed material under specified terms and conditions. If
     the licensor's permission is not necessary for any reason--for
     example, because of any applicable exception or limitation to
     copyright--then that use is not regulated by the license. Our
     licenses grant only permissions under copyright and certain
     other rights that a licensor has authority to grant. Use of
     the licensed material may still be restricted for other
     reasons, including because others have copyright or other
     rights in the material. A licensor may make special requests,
     such as asking that all changes be marked or described.
     Although not required by our licenses, you are encouraged to
     respect those requests where reasonable. More considerations
     for the public:
    wiki.creativecommons.org/Considerations_for_licensees

=======================================================================

Creative Commons Attribution 4.0 International Public License

By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution 4.0 International Public License ("Public License"). To the
extent this Public License may be interpreted as a contract, You are
granted the Licensed Rights in consideration of Your acceptance of
these terms and conditions, and the Licensor grants You such rights in
consideration of benefits the Licensor receives from making the
Licensed Material available under these terms and conditions.


Section 1 -- Definitions.

  a. Adapted Material means material subject to Copyright and Similar
     Rights that is derived from or based upon the Licensed Material
     and in which the Licensed Material is translated, altered,
     arranged, transformed, or otherwise modified in a manner requiring
     permission under the Copyright and Similar Rights held by the
     Licensor. For purposes of this Public License, where the Licensed
     Material is a musical work, performance, or sound recording,
     Adapted Material is always produced where the Licensed Material is
     synched in timed relation with a moving image.

  b. Adapter's License means the license You apply to Your Copyright
     and Similar Rights in Your contributions to Adapted Material in
     accordance with the terms and conditions of this Public License.

  c. Copyright and Similar Rights means copyright and/or similar rights
     closely related to copyright including, without limitation,
     performance, broadcast, sound recording, and Sui Generis Database
     Rights, without regard to how the rights are labeled or
     categorized. For purposes of this Public License, the rights
     specified in Section 2(b)(1)-(2) are not Copyright and Similar
     Rights.

  d. Effective Technological Measures means those measures that, in the
     absence of proper authority, may not be circumvented under laws
     fulfilling obligations under Article 11 of the WIPO Copyright
     Treaty adopted on December 20, 1996, and/or similar international
     agreements.

  e. Exceptions and Limitations means fair use, fair dealing, and/or
     any other exception or limitation to Copyright and Similar Rights
     that applies to Your use of the Licensed Material.

  f. Licensed Material means the artistic or literary work, database,
     or other material to which the Licensor applied this Public
     License.

  g. Licensed Rights means the rights granted to You subject to the
     terms and conditions of this Public License, which are limited to
     all Copyright and Similar Rights that apply to Your use of the
     Licensed Material and that the Licensor has authority to license.

  h. Licensor means the individual(s) or entity(ies) granting rights
     under this Public License.

  i. Share means to provide material to the public by any means or
     process that requires permission under the Licensed Rights, such
     as reproduction, public display, public performance, distribution,
     dissemination, communication, or importation, and to make material
     available to the public including in ways that members of the
     public may access the material from a place and at a time
     individually chosen by them.

  j. Sui Generis Database Rights means rights other than copyright
     resulting from Directive 96/9/EC of the European Parliament and of
     the Council of 11 March 1996 on the legal protection of databases,
     as amended and/or succeeded, as well as other essentially
     equivalent rights anywhere in the world.

  k. You means the individual or entity exercising the Licensed Rights
     under this Public License. Your has a corresponding meaning.


Section 2 -- Scope.

  a. License grant.

       1. Subject to the terms and conditions of this Public License,
          the Licensor hereby grants You a worldwide, royalty-free,
          non-sublicensable, non-exclusive, irrevocable license to
          exercise the Licensed Rights in the Licensed Material to:

            a. reproduce and Share the Licensed Material, in whole or
               in part; and

            b. produce, reproduce, and Share Adapted Material.

       2. Exceptions and Limitations. For the avoidance of doubt, where
          Exceptions and Limitations apply to Your use, this Public
          License does not apply, and You do not need to comply with
          its terms and conditions.

       3. Term. The term of this Public License is specified in Section
          6(a).

       4. Media and formats; technical modifications allowed. The
          Licensor authorizes You to exercise the Licensed Rights in
          all media and formats whether now known or hereafter created,
          and to make technical modifications necessary to do so. The
          Licensor waives and/or agrees not to assert any right or
          authority to forbid You from making technical modifications
          necessary to exercise the Licensed Rights, including
          technical modifications necessary to circumvent Effective
          Technological Measures. For purposes of this Public License,
          simply making modifications authorized by this Section 2(a)
          (4) never produces Adapted Material.

       5. Downstream recipients.

            a. Offer from the Licensor -- Licensed Material. Every
               recipient of the Licensed Material automatically
               receives an offer from the Licensor to exercise the
               Licensed Rights under the terms and conditions of this
               Public License.

            b. No downstream restrictions. You may not offer or impose
               any additional or different terms or conditions on, or
               apply any Effective Technological Measures to, the
               Licensed Material if doing so restricts exercise of the
               Licensed Rights by any recipient of the Licensed
               Material.

       6. No endorsement. Nothing in this Public License constitutes or
          may be construed as permission to assert or imply that You
          are, or that Your use of the Licensed Material is, connected
          with, or sponsored, endorsed, or granted official status by,
          the Licensor or others designated to receive attribution as
          provided in Section 3(a)(1)(A)(i).

  b. Other rights.

       1. Moral rights, such as the right of integrity, are not
          licensed under this Public License, nor are publicity,
          privacy, and/or other similar personality rights; however, to
          the extent possible, the Licensor waives and/or agrees not to
          assert any such rights held by the Licensor to the limited
          extent necessary to allow You to exercise the Licensed
          Rights, but not otherwise.

       2. Patent and trademark rights are not licensed under this
          Public License.

       3. To the extent possible, the Licensor waives any right to
          collect royalties from You for the exercise of the Licensed
          Rights, whether directly or through a collecting society
          under any voluntary or waivable statutory or compulsory
          licensing scheme. In all other cases the Licensor expressly
          reserves any right to collect such royalties.


Section 3 -- License Conditions.

Your exercise of the Licensed Rights is expressly made subject to the
following conditions.

  a. Attribution.

       1. If You Share the Licensed Material (including in modified
          form), You must:

            a. retain the following if it is supplied by the Licensor
               with the Licensed Material:

                 i. identification of the creator(s) of the Licensed
                    Material and any others designated to receive
                    attribution, in any reasonable manner requested by
                    the Licensor (including by pseudonym if
                    designated);

                ii. a copyright notice;

               iii. a notice that refers to this Public License;

                iv. a notice that refers to the disclaimer of
                    warranties;

                 v. a URI or hyperlink to the Licensed Material to the
                    extent reasonably practicable;

            b. indicate if You modified the Licensed Material and
               retain an indication of any previous modifications; and

            c. indicate the Licensed Material is licensed under this
               Public License, and include the text of, or the URI or
               hyperlink to, this Public License.

       2. You may satisfy the conditions in Section 3(a)(1) in any
          reasonable manner based on the medium, means, and context in
          which You Share the Licensed Material. For example, it may be
          reasonable to satisfy the conditions by providing a URI or
          hyperlink to a resource that includes the required
          information.

       3. If requested by the Licensor, You must remove any of the
          information required by Section 3(a)(1)(A) to the extent
          reasonably practicable.

       4. If You Share Adapted Material You produce, the Adapter's
          License You apply must not prevent recipients of the Adapted
          Material from complying with this Public License.


Section 4 -- Sui Generis Database Rights.

Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:

  a. for the avoidance of doubt, Section 2(a)(1) grants You the right
     to extract, reuse, reproduce, and Share all or a substantial
     portion of the contents of the database;

  b. if You include all or a substantial portion of the database
     contents in a database in which You have Sui Generis Database
     Rights, then the database in which You have Sui Generis Database
     Rights (but not its individual contents) is Adapted Material; and

  c. You must comply with the conditions in Section 3(a) if You Share
     all or a substantial portion of the contents of the database.

For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.


Section 5 -- Disclaimer of Warranties and Limitation of Liability.

  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.

  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.

  c. The disclaimer of warranties and limitation of liability provided
     above shall be interpreted in a manner that, to the extent
     possible, most closely approximates an absolute disclaimer and
     waiver of all liability.


Section 6 -- Term and Termination.

  a. This Public License applies for the term of the Copyright and
     Similar Rights licensed here. However, if You fail to comply with
     this Public License, then Your rights under this Public License
     terminate automatically.

  b. Where Your right to use the Licensed Material has terminated under
     Section 6(a), it reinstates:

       1. automatically as of the date the violation is cured, provided
          it is cured within 30 days of Your discovery of the
          violation; or

       2. upon express reinstatement by the Licensor.

     For the avoidance of doubt, this Section 6(b) does not affect any
     right the Licensor may have to seek remedies for Your violations
     of this Public License.

  c. For the avoidance of doubt, the Licensor may also offer the
     Licensed Material under separate terms or conditions or stop
     distributing the Licensed Material at any time; however, doing so
     will not terminate this Public License.

  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
     License.


Section 7 -- Other Terms and Conditions.

  a. The Licensor shall not be bound by any additional or different
     terms or conditions communicated by You unless expressly agreed.

  b. Any arrangements, understandings, or agreements regarding the
     Licensed Material not stated herein are separate from and
     independent of the terms and conditions of this Public License.


Section 8 -- Interpretation.

  a. For the avoidance of doubt, this Public License does not, and
     shall not be interpreted to, reduce, limit, restrict, or impose
     conditions on any use of the Licensed Material that could lawfully
     be made without permission under this Public License.

  b. To the extent possible, if any provision of this Public License is
     deemed unenforceable, it shall be automatically reformed to the
     minimum extent necessary to make it enforceable. If the provision
     cannot be reformed, it shall be severed from this Public License
     without affecting the enforceability of the remaining terms and
     conditions.

  c. No term or condition of this Public License will be waived and no
     failure to comply consented to unless expressly agreed to by the
     Licensor.

  d. Nothing in this Public License constitutes or may be interpreted
     as a limitation upon, or waiver of, any privileges and immunities
     that apply to the Licensor or You, including from the legal
     processes of any jurisdiction or authority.


=======================================================================

Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.

Creative Commons may be contacted at creativecommons.org.


================================================
FILE: build.rs
================================================
fn main() {
    #[cfg(target_os = "windows")]
    panic!(
        "The `fuser` crate cannot be built on windows. You must build and use fluster_fs through WSL."
    );
}


================================================
FILE: readme.md
================================================
  
<h1 align="center">
  <br>
  <img src="https://github.com/DocJade/fluster_rs/blob/master/img/flustered.png?raw=true" alt="Fluster" width="256">
  <br>
  Fluster
  <br>
</h1>

<h3 align="center">A futuristic filesystem that's stuck in the past.</h4>

<p align="center">
  <img alt="Blazingly fast!" src="https://img.shields.io/badge/Blazingly_fast!-000000?logo=rust&logoColor=white">
  <a href="https://kofi.docjade.com/">
    <img alt="Support me on Ko-fi!" src="https://img.shields.io/badge/Support%20me%20on%20Ko--fi!-FF5E5B?logo=ko-fi&logoColor=white">
  </a>
  <a href="https://en.wikipedia.org/wiki/Gluten">
	<img alt="Gluten free!" src="https://img.shields.io/badge/Gluten_free!-blue">
</a>
</p>

<p align="center">
  <a href="#features">Features</a> •
  <a href="#how-to-use">How To Use</a> •
  <a href="#credits">Credits</a> •
  <a href="#license">License</a>
</p>


<p align="center">
	<img src="https://github.com/DocJade/fluster_rs/blob/master/img/WindowsTerminal_7J9iESbHvl.png?raw=true"alt="Fluster playing \"Bad Apple\"" width="720">
</p>

## Features

*  Multi-disk
	* Spans a single filesystem across as many floppy disks as are required to store the data, treating them as a single pool.
* Disk failure detection
	* Automatically detects and troubleshoot drive and disk issues.
* Automatic backups
	* Floppy disks are unreliable, so every block operation is backed up to `/var/fluster` in case disk recovery is required.
* Tiered caching
	* Triple tiered, in-memory cache to minimize disk swapping, while only using 2 floppy disks worth of memory.
* Error checking
	* Every 512 byte block has a 4 byte CRC to detect corruption or bad reads, and disk operations will automatically retry if the CRC fails.
* FUSE based
	* Built on [FUSE](https://github.com/libfuse/libfuse), which makes Fluster! mountable on any UNIX or UNIX-like system that supports FUSE.

 
## How To Use

To clone and run Fluster!, you'll need [Rust](www.rust-lang.org), a FUSE implementation, a floppy drive, and at least two floppy disks.

### Prerequisites
#### For Linux & macOS

- **Install:**
	- **Rust:** Follow the official installation [guide](https://www.rust-lang.org/).
	- **FUSE:** On most Linux distributions, libfuse is available through your package manager (e.g., sudo apt-get install libfuse-dev).
		- On macOS, you may need [macFUSE](https://macfuse.github.io/), although I have not tested Fluster! on MacOS at all, since you should use a real operating system.

#### For Windows Users
- If you're running Fluster! on Windows, please [read this guide](https://github.com/DocJade/fluster_rs/blob/master/windows.md).

### Building and running

#### Build Fluster!:
```bash
# Clone the repository
git clone https://github.com/DocJade/fluster_rs

# Go into the repository
cd fluster_fs

# Build with the recommended 'floppy' profile
cargo build --profile floppy
```

#### Run Fluster!:
```bash
# Example usage:
# Create a directory to mount the filesystem
mkdir ~/fluster_mount_point
# Run the app (requires root privileges for mounting)
sudo ./target/floppy/fluster_fs --block-device-path "/dev/sdX" --mount-point "~/fluster_mount_point"
```
- Replace /dev/sdX with the actual path to your floppy drive.

#### Unmounting Fluster!:
```bash
fusermount -u ~/fluster_mount_point
```
- Do note that unmounting Fluster! does not immediately shut down fluster, you will still need to swap disks to flush the cache to disk.
## Credits

- [DocJade](https://docjade.com/) (That's me!)
- [Rust](https://www.rust-lang.org/) ([Not the bad Rust](https://rust.facepunch.com/))
- [The Rippingtons](https://www.rippingtons.com/), who kept me from going insane while writing this.
- [Femtanyl](https://femtanyl.bandcamp.com/), who helped me go insane while writing this.

## Notes:
Originally I planned to keep an up-to-date implementation spec of Fluster! in `./filesystem_design`, but this slowly became more and more out of date, as is now very un-representative of the final product. Some information in there such as Inode blocks and how they are constructed should be mostly up to date. Dense disks aren't real, and they cannot hurt you.

If you're wondering "Where is the EXE so I can just run it myself!" Please understand that there isn't one, and for good reason. If you don't know how to build and run this project yourself, chances are you would use it improperly, and possibly wipe your C: drive.

## See Fluster! in action

[YouTube link](https://www.youtube.com/watch?v=cTPBGZcTRqo)

## You may also like...

- Pornography, search "boobs" on google for more info.

## License

<a href="https://github.com/DocJade/fluster_rs">Fluster!</a> © 2025 by <a href="https://docjade.com/">DocJade</a> is licensed under <a href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International</a>

---

> [I should just come up with a cool name and the rest will like as itself.](https://www.youtube.com/watch?v=dmzk5y_mrlg)


================================================
FILE: src/error_types/block.rs
================================================
// Blocks usually return similar types of errors.
use thiserror::Error;

#[derive(Debug, Error, PartialEq)]
/// Errors related to block manipulation. Not disk level modification, but our custom block types.
pub enum BlockManipulationError {
    #[error("Adding content to this block failed, due to the block not having enough capacity for the new content.")]
    OutOfRoom,
    #[error("This method can only be called on the final block in a chain of this type of block.")]
    NotFinalBlockInChain,
    #[error("The arguments given for this operation are out of bounds, or otherwise not supported.")]
    Impossible,
    #[error("The data that was attempted to be retrieved from this block did not exist.")]
    NotPresent
}

================================================
FILE: src/error_types/conversions.rs
================================================
// Conversions between all of the lower types.

//
// Imports
//

use std::io::ErrorKind;
use std::time::Duration;
use log::debug;
use log::error;

use log::warn;
use thiserror::Error;
use crate::error_types::critical::CriticalError;
use crate::error_types::drive::DriveError;
use crate::error_types::drive::DriveIOError;
use crate::error_types::drive::InvalidDriveReason;
use crate::error_types::drive::WrappedIOError;
use crate::pool::disk::generic::generic_structs::pointer_struct::DiskPointer;
use crate::tui::notify::NotifyTui;
use crate::tui::tasks::TaskType;



// Not error type can just be converted upwards willy-nilly, that led to the old
// and horrible FloppyDiskError type which everything ended up returning. Not good.

// We do not allow string errors. This is RUST damn it, not python!

// We also have a custom conversion error type, so lower level callers can get more info
// about what they need to do to be able to perform the cast to a higher error type.

#[derive(Debug, Clone, Copy, Error, PartialEq)]
/// Errors related to IO on the inserted floppy disk.
pub enum CannotConvertError {
    #[error("You must retry this operation. If retrying repeatedly fails, throw a Critical error.")]
    MustRetry,
}

//
// Drive errors
//


impl TryFrom<DriveIOError> for DriveError {
    type Error = CannotConvertError;

    fn try_from(value: DriveIOError) -> Result<Self, Self::Error> {
        match value {
            DriveIOError::Retry => {
                // Operation must be retried, cant cast that upwards.
                Err(CannotConvertError::MustRetry)
            },
        }
    }
}

//
// std::io:Error wrapping
//

impl WrappedIOError {
    pub(crate) fn wrap(io_error: std::io::Error, error_origin: DiskPointer) -> Self {
        WrappedIOError {
            io_error,
            error_origin,
        }
    }
}

//
// WrappedIOError to DriveIOError
//

impl TryFrom<WrappedIOError> for DriveIOError {
    type Error = CannotConvertError;

    fn try_from(value: WrappedIOError) -> Result<Self, Self::Error> {

        // Sleep for a tad just in case we're doing a retry
        std::thread::sleep(Duration::from_secs(1));

        // Log where we were trying to do IO at when the error occurred.
        debug!("IO error occured while trying to access disk {} block {}", value.error_origin.disk, value.error_origin.block);
        debug!("Error type: {:#?}", value.io_error);

        match value.io_error.kind() {
            ErrorKind::NotFound => {
                // The floppy drive path is not there.
                CriticalError::DriveInaccessible(InvalidDriveReason::NotFound).handle();
                // If handling worked, can retry.
                Err(CannotConvertError::MustRetry)
            },
            ErrorKind::PermissionDenied => {
                // Dont have permission to perform IO on the drive.
                // Nothing we can do.
                CriticalError::DriveInaccessible(InvalidDriveReason::PermissionDenied).handle();
                // If handling worked, can retry.
                Err(CannotConvertError::MustRetry)
            },
            ErrorKind::ConnectionRefused |
            ErrorKind::ConnectionReset |
            ErrorKind::HostUnreachable |
            ErrorKind::NetworkUnreachable |
            ErrorKind::ConnectionAborted |
            ErrorKind::NotConnected |
            ErrorKind::AddrInUse |
            ErrorKind::AddrNotAvailable  |
            ErrorKind::NetworkDown |
            ErrorKind::StaleNetworkFileHandle => {
                // Okay you should not be using fluster over the network dawg.
                // 100% your fault
                CriticalError::DriveInaccessible(InvalidDriveReason::Networking).handle();
                // We cant recover from that
                unreachable!("Networked floppy drive??? Really??? gtfo");
            },
            ErrorKind::BrokenPipe => {
                // What
                // I doubt you could even make fluster start with pipes.
                unreachable!("Broken pipe with fluster, why are you using pipes in the first place???");
            },
            ErrorKind::AlreadyExists => {
                // Fluster does not create files during IO operations, only in backups.
                // Therefore this should not happen.
                // Especially since we always open the backups if they already exist.
                unreachable!("Fluster tried to create a file that already existed somehow. This should be impossible.");
            },
            ErrorKind::WouldBlock => {
                // Fluster does not ask for blocking IO.
                // In theory this can just be retried.
                Err(CannotConvertError::MustRetry)
            },
            ErrorKind::NotADirectory => {
                // This should never happen, since we always try to write to a file, not a directory.
                unreachable!("Fluster does not open directories, this is impossible.");
            },
            ErrorKind::IsADirectory => {
                // User has passed in a directory for the floppy disk drive instead of a file for it.
                CriticalError::DriveInaccessible(InvalidDriveReason::NotAFile).handle();
                // We cant recover from that, but pretend we can
                Err(CannotConvertError::MustRetry)
            },
            ErrorKind::DirectoryNotEmpty => {
                // Fluster does not try to delete directories.
                unreachable!("Fluster does not delete directories, this should be impossible.");
            },
            ErrorKind::ReadOnlyFilesystem => {
                // Cant use fluster on read-only floppy for obvious reasons.
                CriticalError::DriveInaccessible(InvalidDriveReason::ReadOnly).handle();
                // If it was just the write-protect notch, we can recover.
                Err(CannotConvertError::MustRetry)
            },
            ErrorKind::InvalidInput => {
                // The paramaters given for the IO action were bad, chances are, retrying this wont
                // do anything. We're cooked.
                // But hopefully this shouldn't happen because I'm epic sauce :D
                unreachable!("Invalid input parameters into IO action.")
            },
            ErrorKind::InvalidData => {
                // See above, blah blah blah epic sauce
                unreachable!("Data not valid for the operation showed up in IO action.")
            },
            ErrorKind::TimedOut => {
                // The IO took too long, we should be able to try again.
                Err(CannotConvertError::MustRetry)
            },
            ErrorKind::WriteZero => {
                // Writing a complete bytestream failed.
                // Maybe the operation was canceled and needs to be retried?
                // Not sure if the floppy drive requires minimum write sizes, but 512 aught to be enough.

                // We dont cast this up, we make the caller retry the write.
                Err(CannotConvertError::MustRetry)
            },
            ErrorKind::StorageFull => {
                // Fluster does not use a filesystem when doing writes to the disk.
                // Maybe this could happen when attempting to write past the end of the disk?
                // But we have bounds checking for that.
                warn!("Floppy drive claims to be full, we dont care.");
                Err(CannotConvertError::MustRetry)
            },
            ErrorKind::NotSeekable => {
                // We must be able to seek files to read and write from them, this is a
                // configuration issue.
                CriticalError::DriveInaccessible(InvalidDriveReason::NotSeekable).handle();
                Err(CannotConvertError::MustRetry)
            },
            ErrorKind::QuotaExceeded => {
                // Not sure what other quotas other than size are possible, the man page
                // quota(1) doesn't specify any other quota types.
                // Plus, this shouldn't happen for raw IO, right?
                unreachable!("Floppy drives shouldn't have a quota.");
            },
            ErrorKind::FileTooLarge => {
                // Fluster does not use an underlying filesystem.
                // Very funny since the biggest files we deal with are in the low MBs
                unreachable!("Somehow a write was too large, even though we dont use a filesystem directly.");
            },
            ErrorKind::ResourceBusy => {
                // Disk is busy, we can retry though.
                // Force caller to retry.
                Err(CannotConvertError::MustRetry)
            },
            ErrorKind::ExecutableFileBusy => {
                // If you're somehow running the floppy drive as an executable,
                // you have bigger issues.
                unreachable!("How are you running the floppy drive as an executable?");
            },
            ErrorKind::Deadlock => {
                // File locking deadlock, not much we can do here except try again.
                // Force caller to retry
                Err(CannotConvertError::MustRetry)
            },
            ErrorKind::CrossesDevices => {
                // Fluster does not do renames on the floppy disk path.
                unreachable!("Fluster does not support rename the file paths, this should never happen.");
            },
            ErrorKind::TooManyLinks => {
                // We do not create links.
                unreachable!("Fluster does not support links, no idea how we got here.");
            },
            ErrorKind::InvalidFilename => {
                // The path to the disk is invalid somehow.
                CriticalError::DriveInaccessible(InvalidDriveReason::InvalidPath).handle();
                // We cant recover from that, but in case we can, just try again.
                Err(CannotConvertError::MustRetry)
            },
            ErrorKind::ArgumentListTooLong => {
                // Fluster does not call programs
                unreachable!("Fluster wasn't able to call an external program. Wait, we don't do that? Huh?");
            },
            ErrorKind::Interrupted => {
                // "Interrupted operations can typically be retried."
                // Force caller to retry
                Err(CannotConvertError::MustRetry)
            },
            ErrorKind::Unsupported => {
                // Whatever operation we're trying to do, its not possible.
                // Not really much we can do here either.
                CriticalError::DriveInaccessible(InvalidDriveReason::UnsupportedOS).handle();
                // We cant recover from that, so this will never be returned.
                Err(CannotConvertError::MustRetry)
            },
            ErrorKind::UnexpectedEof => {
                // This would happen if we read past the end of the floppy disk,
                // which should be protected by guard conditions.
                // Maybe someone's trying to run fluster with 8" disks?
                // We'll just retry the operation, since this should be guarded anyways.
                // Force caller to retry
                Err(CannotConvertError::MustRetry)
            },
            ErrorKind::OutOfMemory => {
                // Bro what
                // Nothing we can really do.
                panic!("Please visit https://downloadmoreram.com/ then re-run Fluster.");
            },
            ErrorKind::Other => {
                // "This ErrorKind is not used by the standard library."
                // This is impossible to reach.
                unreachable!("Somehow got an `other` error kind, this is impossible as far as i can tell.");
            },
            _ => {
                // This error is newer than the rust version fluster was originally written for.
                // GLHF!
                
                // Is the floppy drive empty?
                // code: 123,
                // message: "No medium found",
                if let Some(raw) = value.io_error.raw_os_error() && raw == 123_i32 {
                    // No disk is in the drive.
                    // This can happen even if there is a disk in the drive, so we keep
                    // trying.
                    debug!("Is no disk inserted?");
                    // Just keep retrying, if there is an issue with the floppy drive, we need to
                    // eventually end up in the panic handler.

                    // Show user that we're waiting for the drive to spin up
                    // We wait 5 seconds. That's usually fast enough.
                    let handle = NotifyTui::start_task(TaskType::WaitingForDriveSpinUp, 5*5);
                    for _ in 0..5*5 {
                        NotifyTui::complete_task_step(&handle);
                        std::thread::sleep(Duration::from_millis(100));
                    }
                    NotifyTui::finish_task(handle);
                    return Err(CannotConvertError::MustRetry)
                }

                // Well, we'll just pretend we can retry any unknown error...
                warn!("UNKNOWN ERROR KIND:");
                warn!("{value:#?}");
                warn!("Ignoring, pretending we can retry...");
                Ok(DriveIOError::Retry)
            },
        }
    }
}

================================================
FILE: src/error_types/critical.rs
================================================
// Critical errors are errors that we cannot recover from without some sort of higher intervention.
// Returning this error type means you've done all you possibly can, and need saving at a higher level, or
// we are in a unrecoverable state.

use std::{
    fs::OpenOptions,
    os::unix::fs::FileExt,
    path::PathBuf,
    process::exit
};

use thiserror::Error;
use log::error;

use crate::{
    error_types::drive::InvalidDriveReason,
    filesystem::{
        disk_backup::restore::restore_disk,
        filesystem_struct::FLOPPY_PATH
    },
    tui::prompts::TuiPrompt
};

#[derive(Debug, Clone, Copy, Error, PartialEq)]
/// Use this error type if an error happens that you are unable to
/// recover from without intervention.
/// 
/// Creating critical errors is a last resort. Whatever error that was causing
/// your failure must be passed in.
pub enum CriticalError {
    #[error("The floppy drive is inaccessible for some reason.")]
    DriveInaccessible(InvalidDriveReason),
    #[error("We've retried an operation too many times. Something must be wrong.")]
    OutOfRetries(RetryCapError) // Keep track of where we ran out of retries.
}

#[derive(Debug, Clone, Copy, PartialEq)]
/// When you run out of retries on an operation, its useful to know what kind of issue was occurring.
pub enum RetryCapError {
    /// Opening the disk is repeatedly failing
    OpenDisk,
    /// Attempting to write a block is repeatedly failing.
    WriteBlock,
    /// Attempting to read a block is repeatedly failing.
    ReadBlock,
}

//
// =========
// Attempt to recover
// =========
//

impl CriticalError {
    /// Try to recover from a critical error.
    /// 
    /// Returns nothing, since if recovery fails, fluster has shut down.
    /// If this function completes successfully, you can re-attempt the operation that resulted in the critical error.
    /// This should only be called once per operation, if you are consistently calling attempt_recovery, there is a deeper
    /// issue that you must address.
    pub(crate) fn handle(self) {
        go_handle_critical(self)
    }
}


fn go_handle_critical(error: CriticalError) {

    // Critical recovery is not allowed in tests.
    if cfg!(test) {
        panic!("Tried to recover from a critical error! {error:#?}");
    }

    let mitgated = match error {
        CriticalError::DriveInaccessible(invalid_drive_reason) => handle_drive_inaccessible(invalid_drive_reason),
        CriticalError::OutOfRetries(reason) => handle_out_of_retries(reason),
    };


    // If that worked, the caller that caused this critical to be thrown should be able to
    // complete whatever operation they need.
    if mitgated {
        return
    }

    // None of that worked. We must give up.
    // .o7
    println!("Critical error recovery has failed.");
    println!("{error:#?}");
    // Disk/drive are in unknown state, but by god we still must try to flush via panicking.
    panic!("Fluster! has encountered an unrecoverable error, and must shut down.\nGoodbye.");
}



//
// Sub-type handlers
//


// Returns true if mitigation succeeded.
fn handle_drive_inaccessible(reason: InvalidDriveReason) -> bool {
    match reason {
        InvalidDriveReason::NotAFile => {
            // A non-file cannot be used as a floppy disk
            inform_improper_floppy_drive()
        },
        InvalidDriveReason::PermissionDenied => {
            // Need to be able to do IO obviously.
            inform_improper_floppy_drive()
        },
        InvalidDriveReason::Networking => {
            // Cant use network drives
            inform_improper_floppy_drive()
        },
        InvalidDriveReason::ReadOnly => {
            // Did they leave the write protect notch on?
            inform_improper_floppy_drive()
        },
        InvalidDriveReason::NotSeekable => {
            // Floppy drives must be seekable.
            inform_improper_floppy_drive()
        },
        InvalidDriveReason::InvalidPath => {
            // Need to be able to get to the drive
            inform_improper_floppy_drive()
        },
        InvalidDriveReason::UnsupportedOS => {
            // Homebrew OS maybe? We don't use that many
            // file operations, certainly not many unusual ones, thus
            // this shouldn't happen on normal platforms.
            error!("Simple file-based IO is marked as unsupported by your operating system.");
            error!("I'm assuming you're using a non-standard Rust build target / OS destination.");
            error!("Obviously I cannot support that. If you really want to use Fluster (why?), you'll have to");
            error!("update Fluster to make it compatible with your system/setup. Good luck!");
            // We shouldn't've even finished a single write yet, there shouldn't be anything to flush.
            exit(-1); 
        },
        InvalidDriveReason::NotFound => {
            // Maybe the drive is tweaking?
            // Ask the user if they wanna do troubleshooting.
            loop {
                let response = TuiPrompt::prompt_input(
                    "Floppy drive error.".to_string(),
                    "The floppy drive was not found, would you like to retry, or start troubleshooting?\n
                    (R)etry / (T)roubleshoot".to_string(),
                    true
                );
                if response.starts_with('r') {
                    // User just wants to the retry.
                    // retrun true, since we've "done all we can"
                    return true;
                } else if response.starts_with('t') {
                    // Since the drive is not found, we will first
                    return troubleshooter();
                }
            }
            
        },
    }
}

/// Returns true if mitigation succeeded.
/// 
/// yes this is the same as the other handler, but whatever
fn handle_out_of_retries(reason:RetryCapError) -> bool {
    match reason {
        RetryCapError::OpenDisk => {
            // Run the troubleshooter
            troubleshooter()
        },
        RetryCapError::WriteBlock => troubleshooter(),
        RetryCapError::ReadBlock => troubleshooter(),
    }
}


//
// User guided troubleshooting
//

/// Returns true if we were able to pinpoint the issue and resolve it.
fn troubleshooter() -> bool {
    // Inform the user that the troubleshooter is running.
    println!("Fluster is troubleshooting, please wait...");

    // Do the easiest things first, preferably ones that do not involve interaction.

    // Run the disk checker.

    // If that passes, we now know:
    // - Every block on the disk is readable
    // - Every block on the disk is writable.
    // - The drive is connected properly and is working.
    
    // If all of that is working, troubleshooting is done, since we did not find any issues.
    // But this is suspicious. Why did the troubleshooter get called when everything is working?
    if check_disk() {
        TuiPrompt::prompt_enter(
            "Strange...".to_string(),
            "Troubleshooter unexpectedly found nothing wrong.\n
            Suggestion: You should cancel all file operations and unmount Fluster to flush everything
            to disk, just in case.\n
            If you are already in the process of unmounting, good luck!".to_string(),
            true
        );
        return true;
    }
    
    // Something is wrong with the disk or the drive. We will now walk through the
    // fastest and easiest options first.
    
    // Ask the user to re-seat the disk.
    TuiPrompt::prompt_enter(
        "Troubleshooting: Re-seat floppy.".to_string(),
        "Please eject the floppy disk, then re-insert it.\n
        If the disk is currently spinning, please wait a moment to see if it will stop spinning before
        performing the ejection. If the disk continues to spin regardless, proceed with ejection.".to_string(),
        true
    );
    
    // Maybe re-seating was all we needed?
    if check_disk() {
        // Neat.
        troubleshooter_finished();
        return true
    }

    // Now we know for sure that either the disk is dead, or the drive is not working.

    // Let's try another disk, that'll let us narrow it down if the disk was bad.
    TuiPrompt::prompt_enter(
        "Troubleshooting: Different disk.".to_string(),
        "Please swap disks to any known good disk. Remember which disk was removed.".to_string(),
        true
    );

    // Run the check then have the user put the possibly bad disk back in for continuity.
    let disk_bad = check_disk();

    TuiPrompt::prompt_enter(
        "Troubleshooting: Return disk.".to_string(),
        "Please swap back to the disk you previously removed.".to_string(),
        true
    );

    // Now, if the known good disk passed the disk check, we know that it's the drive that is having issues.
    // Otherwise, the disk is bad.

    if disk_bad {
        // Bummer, we need to replace this disk.
        do_disk_restore();

        // Disk has been restored.
        return true;
    }

    // Disk wasn't bad, so the drive must be the issue.

    // Try un-plugging and plugging it back in lmao.

    loop {
        do_remount();

        if check_disk() {
            // Remounting fixed it.
            troubleshooter_finished();
            return true;
        };

        // That failed. Retry?
        let prompted = TuiPrompt::prompt_input(
            "Troubleshooting: Try again?".to_string(),
            "Check disk is still failing.\n
            This is our last troubleshooting step before completely giving up.\n
            You can also forcibly restore a disk if needed.\n
            Would you like to try re-mounting again, or throw in the towel?\n
            (Y)es/(D)isk restore/(G)ive up".to_string(),
            true
        );

        if prompted.to_ascii_lowercase().contains('g') {
            // user gives up.
            break
        } else if prompted.to_ascii_lowercase().contains('d') {
            do_disk_restore();
        }
    }

    // Remounting did not work, and the user has given up.
    TuiPrompt::prompt_enter(
        "Troubleshooting failed. :(".to_string(),
        "Troubleshooting has failed. No fix that was attempted worked.\n
        All of the disks are backed up to the backup directory. No data should be lost, although there might be partially written data.\n
        Worst comes to worst, you can re-image all of your disks from backups.\n
        Before restoring those disks though, make sure to back-up the backups, since they might be slightly corrupt.".to_string(),
        false
    );
    false
}


//
// Troubleshooting actions
//

/// Read every block on the disk to determine if the disk is bad.
/// 
/// This may take a while.
/// 
/// Returns true if every block was read and written correctly.
fn check_disk() -> bool {
    println!("Checking if the disk and drive are working...");
    // Just loop over all of the blocks and try reading them.
    // We need to do it manually ourselves since we dont want
    // to throw another critical error while handling another one.

    // Open the disk currently in the drive.
    // If we cannot lock, we obviously cant use the drive. Thus returns false.
    let disk_path = if let Ok(guard) = FLOPPY_PATH.try_lock() {
        guard.clone()
    } else {
        // The lock is poisoned, which means we died somewhere else.
        // So since we're already in the troubleshooter, we'll just clear the lock :clueless: then
        // return false.
        FLOPPY_PATH.clear_poison();
        return false
    };
    
    // Read the entire thing in one go
    println!("Open floppy drive...");
    let disk_file = match OpenOptions::new().read(true).write(true).open(&disk_path) {
        Ok(ok) => ok,
        Err(error) => {
            // There is something wrong with reading in the drive, which would imply that
            // the drive is inaccessible or something. We cannot resolve here.
            println!("Failed to open drive.");
            println!("{error:#?}");
            return false;
        },
    };
    println!("Ok.");
    
    // Now read in the entire disk.
    println!("Reading entire disk...");
    let mut whole_disk: Vec<u8> = vec![0; 512*2880];
    let _ = disk_file.sync_all();
    let read_result = disk_file.read_exact_at(&mut whole_disk, 0);
    let _ = disk_file.sync_all();
    
    // If that failed at all, checking the disk is bad either due to the drive, or the disk.
    if let Err(error) =  read_result {
        // Read failed. Something is up.
        println!("Fail.");
        println!("{error:#?}");
        return false;
    };
    println!("Ok.");
    
    // Now we write the entire disk back again to see if every block accepts writes.
    println!("Writing entire disk...");
    let _ = disk_file.sync_all();
    let write_result = disk_file.write_all_at(&whole_disk, 0);
    let _ = disk_file.sync_all();
    
    // Did the write work?
    if let Err(error) = write_result {
        // nope
        println!("Fail.");
        println!("{error:#?}");
        return false;
    };
    println!("Ok.");
    println!("Disk and drive appear to be working correctly.");
    true
}


/// Some actions might change the path to the floppy disk drive, we need to let the user update that
/// if they need.
fn update_drive_path() {
    // what's the new path
    let new_path: std::path::PathBuf;
    loop {
        let possible = TuiPrompt::prompt_input(
            "Troubleshooting: Path change.".to_string(),
            "If the path to the floppy drive has changed due to the re-mount, please
            enter the new path. Otherwise hit enter.".to_string(),
            false
        );
        // If nothing was entered, the path has not changed
        if possible.is_empty() {
            // no change.
            return
        }
        let could_be = PathBuf::from(possible);
        let maybe = match could_be.canonicalize() {
            Ok(ok) => ok,
            Err(err) => {
                // what
                TuiPrompt::prompt_enter(
                    "Invalid path.".to_string(),
                    format!("Unable to canonicalize path. Please provide a valid path.\n\n{err:#?}"),
                    false
                );
                continue;
            },
        };
        if std::fs::exists(&maybe).unwrap_or(false) {
            // Good.
            new_path = maybe;
            break
        } else {
            TuiPrompt::prompt_enter(
                "Invalid path.".to_string(),
                "Unable to either open path, or confirm it exists. Please provide a valid path.".to_string(),
                false
            );
            continue;
        }
    }

    // Set that new path
    if let Ok(mut something) = FLOPPY_PATH.try_lock() {
        *something = new_path;
    } else {
        // The lock is poisoned, we'll just uhhh... ignore that.
        // The woes of a non-transactional filesystem.
        // TODO: kill PastJade for not thinking that far ahead.
        FLOPPY_PATH.clear_poison();
        if let Ok(mut something_the_sequel) = FLOPPY_PATH.try_lock() {
            *something_the_sequel = new_path;
        } else {
            // ????
            // Already VERY cooked, might as well panic for fun!
            panic!("Tried to clear poison on the floppy path but that didn't work! Giving up.");
        }
    }
}




//
// User actions
//



/// Ask the user to remount the floppy drive.
fn do_remount() {
    TuiPrompt::prompt_enter(
        "Troubleshooting: Remount drive.".to_string(),
        "Please re-mount the floppy drive.\n
        You can find more information about remounting in the README.\n
        Press enter after you have finished re-mounting the drive.".to_string(),
        false
    );
    // This might have changed the path to the floppy drive.
    update_drive_path();
}

/// Inform the user that the disk needs to be re-created.
/// 
/// Make sure to put the bad disk back in the drive beforehand so the user
/// knows what disk to discard.
fn do_disk_restore() {
    TuiPrompt::prompt_enter(
        "Troubleshooting: Bad disk.".to_string(),
        "The troubleshooter has determined that the disk currently within the drive is bad.\n
        This disk will need to be re-created.".to_string(),
        false
    );

    // Now start the restore.
    let mut failure = false;
    loop {
        if failure {
            // We tried restoring the disk, but the restore failed.
            // Tell the user and ask if they want to attempt to restore to
            // the same disk again, or have them put in a new disk.
            TuiPrompt::prompt_enter(
                "Restoration: Failure.".to_string(),
                "Restoring disk has failed. Restoring can be retried though.\n
                If you would like to attempt restoring to the same disk that you inserted previously,
                leave it in the drive, and ignore the message about swapping disks.\n
                You can re-try as many times as you would like, but if the new disk continues to fail, you
                should try using another disk to restore onto.\n
                If you just cannot seem to restore to a new disk, idk man you're cooked lmao good luck bozo.".to_string(),
                false
            );
        }
        
        // Pull out the bad one, disk restore needs an empty drive.
        // We need to know what disk it was.
        let disk_number: u16;
        loop {
            let to_convert = TuiPrompt::prompt_input(
                "Restoration: New disk.".to_string(),
                "Please remove the bad disk currently inserted in the drive, then
                enter it's disk number.".to_string(),
                false
            );
            if let Ok(number) = to_convert.parse::<u16>() {
                disk_number = number;
                break;
            }
            TuiPrompt::prompt_enter(
                "Restoration: Bad number.".to_string(),
                "Parsing error, please try again. Only enter the number of the disk.".to_string(),
                false
            );
        }

        // Now restore that disk.
        if restore_disk(disk_number) {
            // restore worked!
            return
        }
        // Restoration failed, it can be retried.
        failure = true;
    }
}

//
// User information
//

// fn inform_improper_mount_point() -> ! {
//     TuiPrompt::prompt_enter(
//         "Bad mount point.".to_string(),
//         "The point where you have tried to mount fluster is invalid for some reason.\n
//         Please re-confirm that the mount point is valid, then re-run fluster. Good luck!".to_string(),
//         true
//     );
//     exit(-1) // Exiting, no floppy drive to flush with.
// }

fn inform_improper_floppy_drive() -> bool {
    // We cannot use this floppy drive.

    // First check if the user has inserted a write-protected disk
    loop {
        let prompted: String = TuiPrompt::prompt_input(
            "Troubleshooting: Write protected.".to_string(),
            "Please remove the floppy disk from the drive, and confirm that it is not set to read-only.\n\n
            Was the disk set to read-only? \"yes\"/\"no\"".to_string(),
            false
        );
        if prompted.contains('y') {
            // Whoops!
            TuiPrompt::prompt_enter(
                "Troubleshooting finished.".to_string(),
                "Cool! That means we do not need to shut down.
                Please set the disk to read/write, and insert it back into the drive.\n
                Please make sure you do not insert write protected disks in the future, or the troubleshooter will start again.".to_string(),
                false
            );
            return true;
        } else if prompted.contains('n') {
            // Well crap.
            break
        };
    }

    // Disk was not write protected. Drive is bad.
    TuiPrompt::prompt_enter(
        "Troubleshooting failed.".to_string(),
        "Fluster is unable to access the floppy disk from your floppy drive.\n
        Please make sure that the path you provided for the drive is:\n
        - Valid\n
        - A file, and not a directory\n
        - Accessible by your current user\n
        - Is not over the network\n
        - Is not mounted as read-only\n\n
        Fluster will now exit, since operating without a drive is not possible.".to_string(),
        true
    );
    exit(-1); // Exiting, since we cant even flush the cache due to no floppy drive.
}

// Helper just do dedupe
fn troubleshooter_finished() {
    TuiPrompt::prompt_enter(
        "Troubleshooting succeeded!".to_string(),
        "Troubleshooting finished successfully.".to_string(),
        false
    );
}

================================================
FILE: src/error_types/drive.rs
================================================
// Error types pertaining to the floppy drive itself.
// We do not allow string errors. This is RUST damn it, not python!
use thiserror::Error;

use crate::pool::disk::generic::generic_structs::pointer_struct::DiskPointer;

#[derive(Debug, Error, PartialEq)]
/// Super-error about the floppy drive itself.
/// 
/// We are unable to handle read errors at this level. All IO related errors
/// are within the DriveIOError type.
pub enum DriveError {
    #[error("No disk is currently inserted.")]
    DriveEmpty,
    #[error("The operation failed for non-critical reasons, but no corruption occurred, and the operation can be retried with the same arguments.")]
    Retry,
    #[error("An operation on this disk is taking too long..")]
    TakingTooLong
}

#[derive(Debug, Clone, Copy, Error, PartialEq)]
/// Errors related to IO on the inserted floppy disk.
/// 
/// This is only used at the lowest of levels on actual IO operations.
pub enum DriveIOError {
    #[error("The operation failed for non-critical reasons, but no corruption occurred, and the operation can be retried with the same arguments.")]
    Retry,
}

#[derive(Debug)]
pub struct WrappedIOError {
    /// The io error that you are trying to handle
    pub(super) io_error: std::io::Error,
    /// The DiskPointer to where this issue occurred.
    pub(super) error_origin: DiskPointer
}



#[derive(Debug, PartialEq, Clone, Copy)]
/// Reasons why we cannot use the provided floppy disk path
pub enum InvalidDriveReason {
    /// Pointed at a folder instead of a file.
    NotAFile,
    /// We dont have permission to access the path provided
    PermissionDenied,
    /// We do not support using fluster over the network.
    Networking,
    /// Disk must be read and write.
    ReadOnly,
    /// File that refers to the floppy drive is not seekable.
    NotSeekable,
    /// The path is invalid in some way.
    InvalidPath,
    /// The filesystem (or operating system) that you're running fluster on
    /// does not support basic disk IO.
    UnsupportedOS,
    /// Generic "not found"
    NotFound
}

================================================
FILE: src/error_types/filesystem.rs
================================================
use libc::c_int;
use log::error;

use crate::error_types::drive::DriveError;

//
//
// ======
// C Error values
// ======
//
//

// Errors gleamed from
// https://man7.org/linux/man-pages/man3/errno.3.html
// https://man7.org/linux/man-pages/man2/openat.2.html

/// Bro thinks he's Shakespeare.
pub(in super::super) const FILE_NAME_TOO_LONG: c_int = libc::ENAMETOOLONG;
/// Tried to modify a non-empty directory in a way that required it to be empty.
pub(in super::super) const DIRECTORY_NOT_EMPTY: c_int = libc::ENOTEMPTY;
/// This seat's taken.
pub(in super::super) const ITEM_ALREADY_EXISTS: c_int = libc::EEXIST;
/// Tried to do directory stuff to a file.
pub(in super::super) const NOT_A_DIRECTORY: c_int = libc::ENOTDIR;
/// Ad hominem
pub(in super::super) const INVALID_ARGUMENT: c_int = libc::EINVAL;
/// Tried to do things to a directory that it does not support.
pub(in super::super) const IS_A_DIRECTORY: c_int = libc::EISDIR;
// /// Function not implemented.
// pub(in super::super) const UNIMPLEMENTED: c_int = libc::ENOSYS;
/// This operation is not supported in this filesystem.
// pub(in super::super) const UNSUPPORTED: c_int = libc::ENOTSUP; 
/// Access denied / files does not exist.
pub(in super::super) const NO_SUCH_ITEM: c_int = libc::ENOENT;
/// Tried to seek to an invalid file position.
// pub(in super::super) const INVALID_SEEK: c_int = libc::ESPIPE;
/// Tried to use a filehandle that is stale. New one is required.
pub(in super::super) const STALE_HANDLE: c_int = libc::ESTALE;
// Generic IO error. The dreaded OS(5) Input/Output error.
pub(in super::super) const GENERIC_FAILURE: c_int = libc::EIO;
/// You are insane.
// pub(in super::super) const FILE_TOO_BIG: c_int = libc::EFBIG;
/// Operation was interrupted for some reason, but can be retried.
pub(in super::super) const TRY_AGAIN: c_int = libc::ERESTART;
/// Device / filesystem is busy, try again later.
/// 
/// Should never happen in fluster due to being single threaded.
pub(in super::super) const BUSY: c_int = libc::EBUSY;

impl From<DriveError> for c_int {
    fn from(value: DriveError) -> Self {
        match value {
            DriveError::DriveEmpty => {
                // The drive empty error should never get this high
                error!("Drive empty error should never make it to the filesystem level!");
                error!("Telling file system that we are busy...");
                BUSY
            },
            DriveError::Retry => TRY_AGAIN,
            DriveError::TakingTooLong => BUSY,
        }
    }
}

================================================
FILE: src/error_types/header.rs
================================================
// Errors for header conversions.
use thiserror::Error;

#[derive(Debug, Error, PartialEq)]
/// Errors related to block manipulation. Not disk level modification, but our custom block types.
pub enum HeaderError {
    #[error("This is not a header of the requested type.")]
    Invalid,
    #[error("The block that was requested to turn into a header is completely blank.")]
    Blank,
}

================================================
FILE: src/error_types/mod.rs
================================================
pub(crate) mod drive;
pub(crate) mod conversions;
pub(crate) mod block;
pub(crate) mod filesystem;
pub(crate) mod critical;
pub(crate) mod header;

================================================
FILE: src/filesystem/disk_backup/mod.rs
================================================
pub mod restore;
pub mod update;

================================================
FILE: src/filesystem/disk_backup/restore.rs
================================================
// Restore a disk from a backup.

use std::{fs::File, io::{
    Read,
    Seek
}, os::unix::fs::FileExt};

use log::{debug, error, warn};

use crate::{filesystem::filesystem_struct::FLOPPY_PATH, tui::{notify::NotifyTui, prompts::TuiPrompt, tasks::TaskType}};

/// Returns true if the entire disk was re-created successfully.
/// 
/// Assumes the drive is empty when called.
pub fn restore_disk(number: u16) -> bool {
    debug!("Beginning restore of disk `{number}`.");
    
    // Get a new blank disk.
    TuiPrompt::prompt_enter(
        "Insert blank disk.".to_string(),
        format!("Please insert a brand new, blank disk that will become the new disk {number}, then press enter.\n
        WARNING: Disk will NOT be checked for blankness, this WILL destroy data if a non-blank disk is inserted!"),
        false
    );
    
    let handle = NotifyTui::start_task(TaskType::RestoreDisk, 6);
    // Find the disk in the backup folder
    // If it't not in there, you're cooked.
    // Try opening the backup file at most 5 times.

    let mut tries = 1_u8;
    let mut backed_up: std::fs::File;
    debug!("Opening backup file...");
    loop {
        match std::fs::OpenOptions::new()
        .read(true)
        .open(format!("/var/fluster/disk_{number}.fluster_backup")) {
            Ok(ok) => {
                backed_up = ok;
                break;
            },
            Err(_) => {
                if tries == 5 {
                    // ruh roh
                    warn!("Fail. Out of retries.");
                    return false;
                } else {
                    warn!("Fail, trying again...");
                    tries += 1;
                    continue;
                }
            },
        };
    };

    NotifyTui::complete_task_step(&handle);
    
    // Now read in the entire floppy backup.
    // Again, at most 5 tries.
    
    let mut bytes: Vec<u8> = Vec::with_capacity(2880*512);
    let mut tries = 1_u8;
    debug!("Reading backup data...");
    loop {
        if backed_up.rewind().is_ok() && backed_up.read_to_end(&mut bytes).is_ok() {
            // All good.
            break
        }
        if tries == 5 {
            // cooked.
            error!("Fail. Out of retries.");
            return false;
        }
        debug!("Fail, trying again...");
        bytes.clear();
        tries += 1;
        continue;
    };

    NotifyTui::complete_task_step(&handle);

    // Copy the entire contents of that backup to the new disk.

    // We'll just dump the entire file to the block device without using our floppy handler,
    // since we cant really trust my logic hehe

    // Get the path to the floppy drive block device.
    // We'll pre-clear poison, just in case.
    FLOPPY_PATH.clear_poison();
    let block_path = if let Ok(path) = FLOPPY_PATH.lock()  {
        path.clone()
    } else {
        // well... cooked
        error!("Floppy path is poisoned!");
        return false
    };

    NotifyTui::complete_task_step(&handle);

    // Open the block device as a file
    let block_file: File = if let Ok(opened) = File::options().read(true).write(true).open(block_path) {
        opened
    } else {
        // Well, we couldn't open the floppy path. Return false.
        return false;
    };

    NotifyTui::complete_task_step(&handle);
    
    // Now write all that in
    let mut write_worked = false;
    for _ in 0..5 {
        let result = block_file.write_all_at(&bytes, 0);
        match result {
            Ok(_) => {
                // write finished!
                write_worked = true;
                break
            },
            Err(err) => {
                // That didn't work!
                error!("Writing to the drive failed!");
                error!("{err:#?}");
                continue;
            },
        }
    }

    NotifyTui::complete_task_step(&handle);

    // Now sync the disk to pause until the data all actually hits the disk.
    // Not a big issue if this doesnt work, we're still gonna wait for the disk to spin down
    // before swapping disks.
    let _ = block_file.sync_all();
    NotifyTui::complete_task_step(&handle);

    // Did that work?
    if write_worked {
        // Disk written!
        NotifyTui::finish_task(handle);
        debug!("Disk restored!");
        true
    } else {
        // Well shoot.
        NotifyTui::cancel_task(handle);
        error!("Disk restore failed!");
        false
    }
}

================================================
FILE: src/filesystem/disk_backup/update.rs
================================================
// Update the backup disk with new contents.

// The path that the disk backups go in /var/fluster

// Porting fluster? Then you've gotta update this for sure.

use std::os::unix::fs::FileExt;

use log::error;

use crate::{filesystem::filesystem_struct::WRITE_BACKUPS, pool::disk::generic::{block::block_structs::RawBlock, generic_structs::pointer_struct::DiskPointer}};

pub(crate) fn update_backup(block: &RawBlock) {
    // Ignore backups if needed.
    if let Some(ian_the_bool) = WRITE_BACKUPS.get() {
        if !ian_the_bool {
            // Skip, backups are disabled.
            return
        }
    } else {
        // The backups flag hasn't been set up, which should be impossible, but we'll just return
        return
    }

    // Make the backup folder if it does not exist yet
    if std::fs::create_dir_all("/var/fluster").is_err() {
        // Unable to create the folders and such.
        error!("Fluster needs to be able to create/use /var/fluster for disk backups.");
        error!("We cannot continue without backups. Shutting down. If you are unable to use");
        error!("backups, set the flag.");
        panic!("Unable to update backups!"); // we panic here, since we still want to flush the disks.
    }

    // Open or create the backup file for the disk
    let disk_path: String = format!("/var/fluster/disk_{}.fluster_backup", block.block_origin.disk);
    let backup_file = if let Ok(file) = std::fs::OpenOptions::new().create(true).truncate(false).write(true).open(disk_path) {
        file
    } else {
        // Cannot open the backup file, we're cooked.
        error!("Fluster was unable to create or open one of its backup files, if you see this spamming your logs, its probably chronic.");
        error!("Fix your permissions, or backups wont work!");
        // pretend we did the backup, crashing is worse.
        return
    };

    // Write in that block. We will try twice at maximum.
    for _ in 0..2 {
        if backup_file.write_all_at(&block.data, block.block_origin.block as u64 * 512).is_err() {
            // That did not work. Crap.
            // We'll try again.
        } else {
            // That worked!
            return
        }
    }

    // We couldn't update the file.
    error!("Fluster failed to write to a backup for one of the disks, if you see this spamming your logs, its probably chronic.");
    error!("You should investigate!");

}

pub(crate) fn large_update_backup(start: DiskPointer, data: &[u8]) {
    // Ignore backups if needed.
    if let Some(ian_the_bool) = WRITE_BACKUPS.get() {
        if !ian_the_bool {
            // Skip, backups are disabled.
            return
        }
    } else {
        // The backups flag hasn't been set up, which should be impossible, but we'll just return
        return
    }

    // Open or create the backup file for the disk
    let disk_path: String = format!("/var/fluster/disk_{}.fluster_backup", start.disk);
    let backup_file = if let Ok(file) = std::fs::OpenOptions::new().create(true).truncate(false).write(true).open(disk_path) {
        file
    } else {
        // Cannot open the backup file, we're cooked.
        error!("Fluster was unable to create or open one of its backup files, if you see this spamming your logs, its probably chronic.");
        error!("Fix your permissions, or backups wont work!");
        // pretend we did the backup, crashing is worse.
        return
    };

    // Write in that block. We will try twice at maximum.
    for _ in 0..2 {
        if backup_file.write_all_at(data, start.block as u64 * 512).is_err() {
            // That did not work. Crap.
            // We'll try again.
        } else {
            // That worked!
            return
        }
    }

    // We couldn't update the file.
    error!("Fluster failed to write to a backup for one of the disks, if you see this spamming your logs, its probably chronic.");
    error!("You should investigate!");
}

================================================
FILE: src/filesystem/file_attributes/conversion.rs
================================================
use fuse_mt::{FileAttr, FileType};
use libc::c_int;
use log::debug;
use std::time::SystemTime;

use crate::{
    error_types::drive::DriveError, filesystem::file_handle::file_handle_struct::FileHandle, pool::disk::standard_disk::block::directory::directory_struct::{
                DirectoryItem, DirectoryItemFlags
            }, tui::{notify::NotifyTui, tasks::TaskType}
};



// Take in a file handle and spit out its attributes.
impl TryFrom<FileHandle> for FileAttr {
    type Error = c_int;
    
    fn try_from(value: FileHandle) -> Result<Self, Self::Error> {
        debug!("Retrieving file metadata from handle...");
        // Get the directory item
        let item: DirectoryItem = value.get_directory_item()?;
        Ok(go_get_metadata(item)?)
    }
}

// You can also call this on DirectoryItem
impl TryFrom<DirectoryItem> for FileAttr {
    type Error = DriveError;

    fn try_from(value: DirectoryItem) -> Result<Self, Self::Error> {
        go_get_metadata(value)
    }
}

fn go_get_metadata(item: DirectoryItem) -> Result<FileAttr, DriveError> {
    debug!("Extracting metadata from item `{}`...", item.name);
    let handle = NotifyTui::start_task(TaskType::GetMetadata(item.name.clone()), 3);
    // Now for ease of implementation, we (very stupidly) ignore all file access permissions,
    // owner information, and group owner information.
    
    // Root owns all files (user id 0)
    // Owner is in the superuser group (group id 0)
    // All permission bits are set (very scary!) go execute a jpeg, i dont even care anymore.
    
    // Due to this, we also do not check any permissions on reads or writes! :D
    
    
    
    // How big is it
    debug!("Getting size...");
    let size: u64 = item.get_size()?;
    NotifyTui::complete_task_step(&handle);
    
    // extract the times
    debug!("Created at...");
    let creation_time: SystemTime = item.get_created_time()?.into();
    debug!("Modified at...");
    let modified_time: SystemTime = item.get_modified_time()?.into();
    NotifyTui::complete_task_step(&handle);
    
    // "What kind of item is this?"
    // https://www.tiktok.com/@ki2myyysc6/video/7524954406438161694
    let file_kind: FileType = if item.flags.contains(DirectoryItemFlags::IsDirectory) {
        // "This is a directory, used for holding items in a filesystem, such as files or other directories."
        debug!("Is a directory...");
        FileType::Directory
    } else {
        // "This is a file, used to store arbitrary data, it is very useful!"
        debug!("Is a file...");
        FileType::RegularFile
    };
    NotifyTui::complete_task_step(&handle);
    
    debug!("Metadata done.");
    NotifyTui::finish_task(handle);
    
    // Put it all together
    Ok(FileAttr {
        // Size of item in bytes.
        size,
        // Bytes div_ceil 512
        blocks: size.div_ceil(512),
        // We dont support access times.
        atime: SystemTime::UNIX_EPOCH,
        // modification time
        mtime: modified_time,
        // metadata change, not supported
        ctime: SystemTime::UNIX_EPOCH,
        // creation time
        crtime: creation_time,
        // file type
        kind: file_kind,
        // File permissions, not supported
        perm: 0o777, // All permission bits
        // links not supported
        nlink: 2, // This has to be set to 2 or things get angry, idk.
        // owner id, always root
        uid: 0,
        // owner group, always root
        gid: 0,
        // special id, not supported
        rdev: 0,
        // macos flags, who gaf? not me. use a real operating system /bait
        flags: 0,
    })
}

================================================
FILE: src/filesystem/file_attributes/mod.rs
================================================
pub mod conversion;

================================================
FILE: src/filesystem/file_handle/file_handle_methods.rs
================================================
// Make the handle do things.

use std::{collections::HashMap, sync::{Arc, Mutex}};

use lazy_static::lazy_static;
use libc::c_int;
use log::{debug, error};

//
// Global info about open files
//

struct LoveHandles {
    /// Hashmap of the currently allocated handles
    allocated: HashMap<u64, FileHandle>,
    /// Highest allocated number (is kept up to date internally)
    highest: u64,
    /// Recently freed handles (ie open space in the hashmap)
    free: Vec<u64>
}

impl LoveHandles {
    /// Make a new one, should only be called once.
    fn new() -> Self {
        // Empty
        LoveHandles {
            allocated: HashMap::new(),
            highest: 0,
            free: Vec::new(),
        }
    }

    /// Make a new handle
    fn make_handle(&mut self, item: FileHandle) -> u64 {
        // Get a number
        let num = self.next_free();

        // Put it in the hashmap.
        // We also assert that we have not already used this number.
        assert!(self.allocated.insert(num, item).is_none(), "We already used this handle number, even though we thought it was free!");

        // All done.
        num
    }

    /// Get the handle back
    fn read_handle(&self, number: u64) -> FileHandle {
        // Handles are not read after freeing, doing so is undefined behavior.
        if let Some(handle) = self.allocated.get(&number) {
            // Cool, it's there.
            handle.clone()
        } else {
            // We are cooked.
            error!("Tried to read a handle that was not allocated!");
            panic!("Use after free on handle.");
        }
    }

    /// Get the next free handle (internal abstraction)
    fn next_free(&mut self) -> u64 {
        // Prefer vec items
        if self.free.is_empty() {
            // Time for a new number then.
            let give = self.highest;
            self.highest += 1;
            return give;
        }

        // There is a vec item.
        self.free.pop().expect("Guarded.")
    }

    /// You need to let go...
    fn release_handle(&mut self, number: u64) {
        // Handles are only ever freed once. Freeing an empty handle is undefined behavior, thus we
        // cant do anything but give up.
        if self.allocated.remove(&number).is_none() {
            // Bad!
            error!("Tried to free a handle that was not allocated!");
            panic!("Double free on handle.");
        };

        // Is this number right below the current highest?
        if number == self.highest - 1 {
            // Yep! Reduce highest.
            self.highest -= 1;
        }
    }
}



lazy_static! {
    static ref LOANED_HANDLES: Arc<Mutex<LoveHandles>> = Arc::new(Mutex::new(LoveHandles::new()));
}





//
// The actual handles
//

use crate::{
    error_types::filesystem::*,
    filesystem::file_handle::file_handle_struct::FileHandle,
    pool::disk::{
        standard_disk::block::{
            directory::directory_struct::{
                DirectoryBlock,
                DirectoryItem
            },
            io::directory::types::NamedItem
        }
    }
};

impl FileHandle {
    /// The name of the file/folder, if it exists.
    /// This will return None on the root.
    pub fn name(&self) -> &str {
        // Get the name, if it exists.
        if let Some(name) = self.path.file_name() {
            name.to_str().expect("Should be valid UTF8")
        } else {
            // No name, this must be the root.
            ""
        }
    }

    /// Allocate the file handle for tracking.
    /// 
    /// Will block.
    /// 
    /// Does not create a new ItemHandle, only stores it.
    pub fn allocate(self) -> u64 {
        // This is blocking.
        let read_handles = &mut LOANED_HANDLES.lock().expect("Other mutex holders should not panic.");
        // Add it
        read_handles.make_handle(self)
    }

    /// Get contents of handle.
    /// 
    /// Will block.
    pub fn read(handle: u64) -> Self {
        // This is blocking
        let read_handles = LOANED_HANDLES.lock().expect("Other mutex holders should not panic.");
        read_handles.read_handle(handle)
    }

    /// Release a handle.
    /// 
    /// Will block.
    pub fn drop_handle(handle: u64) {
        // This is blocking
        let read_handles = &mut LOANED_HANDLES.lock().expect("Other mutex holders should not panic.");
        read_handles.release_handle(handle);
    }

    /// Check if this handle is a file or a directory by attempting to read it from disk, otherwise
    /// deducing the type from it's path string.
    pub fn is_file(&self) -> Result<Option<bool>, c_int> {
        // Annoyingly, rust's PathBuf type doesn't have a way to test if itself is a directory
        // without reading from disk, which makes it completely useless for deducing if the passed argument
        // is a file or folder. Very very annoying.
        //
        // You can't just check for file extensions, since files do not _need_ an extension...
        //
        // The approach i'll take is to see if the path ends with a delimiter. good luck lmao

        // But before we fallback to the path based deduction, we can attempt to load the file from disk if it exists.
        // If it does, we have our sure answer about what type it is, otherwise we will use the crappy string logic.

        // I don't particularly enjoy needing to access the disk here, but chances are if you're trying to find what type
        // something is, you'll be modifying it soon anyways.
        
        let name: String = self.name().to_string();
        debug!("Attempting to deduce if `{}` is a file or directory...", self.path.display());
        
        // Does the parent exist?
        debug!("Checking if it already exists...");
        if let Some(parent) = DirectoryBlock::try_find_directory(self.path.parent())? {
            // Parent does exist, is this item there in either form?
            let file: NamedItem = NamedItem::File(name.clone());
            let directory: NamedItem = NamedItem::Directory(name.clone());
            let maybe_file = parent.find_item(&file)?;
            if maybe_file.is_some() {
                // It was a file
                debug!("Yes, and it's a file.");
                return Ok(Some(true));
            }
            let maybe_directory = parent.find_item(&directory)?;
            if maybe_directory.is_some() {
                // It was a directory.
                debug!("Yes, and it's a directory.");
                return Ok(Some(false));
            }
        }
        debug!("Item did not exist!");

        // Rather than guess, we'll just return that the file did not exist, which should not the be the case for
        // file handles, but maybe the caller just had a stale handle, or is spoofing a new file?
        Ok(None)
    }

    
    /// Loads in and returns the directory item if it exists.
    pub fn get_directory_item(&self) -> Result<DirectoryItem, c_int> {
        // Open the containing folder
        let block = match DirectoryBlock::try_find_directory(self.path.parent())? {
            Some(ok) => ok,
            None => {
                // Containing block did not exist.
                return Err(NO_SUCH_ITEM);
            },
        };

        let named_item = if let Some(bool) = self.get_named_item()? {
            // There was an item with this name.
            bool
        } else {
            // We are trying to deduce the name of an item that does not exist.
            return Err(NO_SUCH_ITEM);
        };

        // Find the item
        if let Some(exists) = block.find_item(&named_item)? {
            // File existed.
            Ok(exists)
        } else {
            // No such item.
            Err(NO_SUCH_ITEM)
        }
    }

    /// Get a named item from this handle.
    pub(crate) fn get_named_item(&self) -> Result<Option<NamedItem>, c_int> {
        // Get a name
        let name: String = self.name().to_string();
        let file_check = self.is_file()?;
        
        // If this is none, the caller is trying to extract a named item from
        // an invalid handle or a spoofed file. We return None, since the item did not
        // exist in either form.

        if let Some(is_file) = file_check {
            // An item was there.
            // Deduce the type
            if is_file {
                // yeah its a file
                return Ok(Some(NamedItem::File(name)));
            } else {
                // dir
                return Ok(Some(NamedItem::Directory(name)));
            }
        }

        // There was no item.
        Ok(None)

        
    }
}

================================================
FILE: src/filesystem/file_handle/file_handle_struct.rs
================================================
//
//
// ======
// Handle type
// ======
//
//


// We are in charge of our own file handle management. Fun! (lie)
// So we need a way to hand out and retrieve them.



/// Handle for any type of item (file or directory).
#[derive(Debug, Clone)]
pub(crate) struct FileHandle {
    /// The path of this file/folder.
    pub path: Box<std::path::Path>, // Non-static size, thus boxed.
}

================================================
FILE: src/filesystem/file_handle/mod.rs
================================================
pub(super) mod file_handle_methods;
pub(super) mod file_handle_struct;

================================================
FILE: src/filesystem/filesystem_struct.rs
================================================
// This is where the fun begins

// Imports

use crate::pool::pool_actions::pool_struct::Pool;
use std::{
    path::PathBuf,
    sync::{Arc, Mutex, OnceLock},
};
// Structs, Enums, Flags

pub struct FlusterFS {
    #[allow(dead_code)] // it's lying.
    pub(crate) pool: Arc<Mutex<Pool>>,
}

use lazy_static::lazy_static;

// Global varibles
// We need to access the path quite deep down into the disk functions, passing it all the way down there would be silly.
// Same with the virtual disk flag.
lazy_static! {
    /// Use virtual disks instead of actually mounting the provided floppy drive path.
    pub(crate) static ref USE_VIRTUAL_DISKS: Mutex<Option<PathBuf>> = Mutex::new(None);
    /// The full path to the floppy drive.
    pub(crate) static ref FLOPPY_PATH: Mutex<PathBuf> = Mutex::new(PathBuf::new());
}

// Backups cannot be disabled at runtime, so we use a once lock for them
/// Enable and disable backing up disks to /var/fluster
pub(crate) static WRITE_BACKUPS: OnceLock<bool> = OnceLock::new();
// TUI cannot be disabled mid run.
pub(crate) static USE_TUI: OnceLock<bool> = OnceLock::new();

/// Options availble at time of pool creation / filesystem load
pub struct FilesystemOptions {
    /// Use virtual disks in a temp folder instead of accessing the floppy drive.
    /// This option is used for testing.
    #[allow(dead_code)] // it's lying.
    pub(super) use_virtual_disks: Option<PathBuf>,
    /// The location of the floppy drive block device
    #[allow(dead_code)] // it's lying.
    pub(super) floppy_drive: PathBuf,
    /// Enable backing up disks to /var/fluster
    #[allow(dead_code)] // it's lying.
    pub(super) enable_backup: bool,
    /// Enable the TUI
    #[allow(dead_code)] // it's lying.
    pub(super) enable_tui: bool
}


================================================
FILE: src/filesystem/fuse_filesystem_methods.rs
================================================
// The actual FUSE filesystem layer.

//
//
// ======
// Imports
// ======
//
//

use std::{ffi::OsStr, path::Path, time::Duration};

use fuse_mt::{DirectoryEntry, FileAttr, FileType, FilesystemMT, Statfs};
use log::{debug, error, info, warn};
use rand::Rng;

use crate::{
    filesystem::{
        filesystem_struct::FlusterFS,
        item_flag::flag_struct::ItemFlag
    },
    pool::{disk::{
        generic::io::cache::cache_io::CachedBlockIO,
        standard_disk::block::{
            directory::directory_struct::{
                DirectoryBlock, DirectoryItem, DirectoryItemFlags
            },
            io::directory::types::NamedItem
        }
    }, pool_actions::pool_struct::{Pool, GLOBAL_POOL}}, tui::{notify::NotifyTui, prompts::TuiPrompt, tasks::TaskType}
};

use super::file_handle::file_handle_struct::FileHandle;
use crate::error_types::filesystem::*;

use fuse_mt::CreatedEntry;




//
//
// ======
// Constants
// ======
//
//

// You should probably be able to set your own custom TTL on mount, but
// guess what? You should also probably be able to chown files. but that ain't happening either.
// Hard coded to one year, see issue #51
const HANDLE_TIME_TO_LIVE: Duration = Duration::from_secs(365*24*60*60);



//
//
// ======
// The actual fuse layer
// ======
//
// There's a lot of stuff in here we technically dont need. And I'm going to assume the information on this page is correct
// https://www.cs.hmc.edu/~geoff/classes/hmc.cs135.201001/homework/fuse/fuse_doc.html
// I have archived this page on internet archive.
// Thanks Geoff! I hope your life is going well, 16 years later.
//
// In theory, some calls like truncate() should be handled by the os before other operations here. We will still check for those flags, just in case.
//
// Also, in theory again, we should NEVER modify the flags before returning them. Linux VFS depends on this (in theory)

impl FilesystemMT for FlusterFS {
    // The most British function in Fluster
    fn init(&self, _req: fuse_mt::RequestInfo) -> fuse_mt::ResultEmpty {
        // To speed up reads / reduce swaps, we will read the entire directory tree structure into memory
        // on startup.

        // So we will list the root directory.
        // None means we get the root
        let root = DirectoryBlock::try_find_directory(None).expect("There should be a root directory before init").expect("ditto");

        // Keep listing each subdirectory until we're done, and if its not a directory, get the size of the file to
        // load in its info as well.

        let mut all_items: Vec<DirectoryItem> = root.list().expect("should be there");

        while let Some(item) = all_items.pop() {
            if item.flags.contains(DirectoryItemFlags::IsDirectory) {
                let block = item.get_directory_block().expect("If this doesn't work we're cooked anyways");
                all_items.extend(block.list().expect("Ditto").into_iter());
            } else {
                // This is a file, just get the size
                // We do this twice to make sure it get promoted.
                let _ = item.get_size().expect("cooked");
                let _ = item.get_size().expect("cooked");
            }
        }

        Ok(())
    }

    // Called when filesystem is unmounted. Should flush all data to disk.
    fn destroy(&self) {
        // Inform user the filesystem is shutting down
        TuiPrompt::prompt_enter("Fluster! is shutting down.".to_string(),
            "Cache will now be flushed to disk".to_string(),
            true
        );
        info!("Shutting down filesystem...");
        // Flush all of the tiers of cache.
        info!("Flushing cache...");
        // We dont retry flushing the cache, since if the flushing fails, that means it went all the way through
        // the troubleshooter and everything. If we retry here, chances are the cache has already dropped some blocks,
        // so we wouldn't even be able to properly finish it at this point.
        CachedBlockIO::flush().expect("I sure hope cache flushing works!");
        // Now flush pool information
        info!("Flushing pool info...");
        // Same story here.
        Pool::flush().expect("I sure hope pool flushing works!");
        info!("Goodbye! .o/");
    }

    // Get file attributes of an item.
    fn getattr(
        &self,
        _req: fuse_mt::RequestInfo,
        path: &std::path::Path,
        fh: Option<u64>,
    ) -> fuse_mt::ResultEntry {
        debug!("Getting attributes of `{}`...", path.display());

        // I already wrote a method for this yay
        // but that assumes we have a handle.
        if let Some(handle) = fh {
            debug!("Handle was provided, getting and returning attributes.");
            // Handle exists, easy path.
            return Ok(
                (
                    HANDLE_TIME_TO_LIVE,
                    FileHandle::read(handle).try_into()?
                )
            )
        }

        // No handle, dang.
        
        // Go find that sucker

        // Making a temporary handle (doesn't need to be allocated) lets us call some easier methods.
        // We cant just use the TryInto FileAttr since we dont know for sure if the item exists yet.
        let temp_handle: FileHandle = FileHandle {
            path: path.into(),
        };

        // Go get the item.
        // This will automatically throw the correct error if the file/folder did not exist.
        let found_item = temp_handle.get_directory_item()?;

        // Get the attributes
        debug!("Getting attributes of item...");
        let found_attributes: FileAttr = found_item.try_into()?;
        debug!("Done! Returning.");

        Ok(
            (
                HANDLE_TIME_TO_LIVE,
                found_attributes
            )
        )
    }

    // We dont support file permissions.
    
    // fn chmod(
    //     &self,
    //     _req: fuse_mt::RequestInfo,
    //     _path: &std::path::Path,
    //     _fh: Option<u64>,
    //     _mode: u32,
    // ) -> fuse_mt::ResultEmpty {
    //     Err(libc::ENOSYS)
    // }

    // We dont support file permissions.
    // fn chown(
    //     &self,
    //     _req: fuse_mt::RequestInfo,
    //     _path: &std::path::Path,
    //     _fh: Option<u64>,
    //     _uid: Option<u32>,
    //     _gid: Option<u32>,
    // ) -> fuse_mt::ResultEmpty {
    //     Err(libc::ENOSYS)
    // }

    // File truncation is supported.
    // Does not always truncate file to 0 bytes long.
    fn truncate(
        &self,
        _req: fuse_mt::RequestInfo,
        path: &std::path::Path,
        fh: Option<u64>,
        size: u64,
    ) -> fuse_mt::ResultEmpty {
        debug!("Truncating `{}` to be `{}` bytes long...", path.display(), size);
        let task_handle = NotifyTui::start_task(
            TaskType::FilesystemTruncateFile(
                path.file_name().unwrap_or(OsStr::new("?")).display().to_string()
            ),
            2
        );
        // Get a file handle
        let handle: FileHandle = if let Some(exists) = fh {
            debug!("File handle was passed in, using that...");
            // Got a handle from the call, no fancy work.
            // Read it in
            FileHandle::read(exists)
        } else {
            debug!("No handle provided, spoofing...");
            // Temp handle that we will not allocate.
            FileHandle {
                path: path.into(),
            }
        };

        debug!("Handle obtained.");

        // Go load the file to truncate.
        // Will return properly if item does not exist.
        let found_item = handle.get_directory_item()?;
        NotifyTui::complete_task_step(&task_handle);
        
        // Now with the directory item, we can run the truncation.
        debug!("Starting truncation...");
        found_item.truncate(size)?;
        NotifyTui::complete_task_step(&task_handle);
        debug!("Truncation finished.");
        NotifyTui::finish_task(task_handle);
        // All done.
        Ok(())
    }

    // We do not support manually updating timestamps.
    // fn utimens(
    //     &self,
    //     _req: fuse_mt::RequestInfo,
    //     _path: &std::path::Path,
    //     _fh: Option<u64>,
    //     _atime: Option<std::time::SystemTime>,
    //     _mtime: Option<std::time::SystemTime>,
    // ) -> fuse_mt::ResultEmpty {
    //     Err(libc::ENOSYS)
    // }

    // We do not support manually updating timestamps.
    // fn utimens_macos(
    //     &self,
    //     _req: fuse_mt::RequestInfo,
    //     _path: &std::path::Path,
    //     _fh: Option<u64>,
    //     _crtime: Option<std::time::SystemTime>,
    //     _chgtime: Option<std::time::SystemTime>,
    //     _bkuptime: Option<std::time::SystemTime>,
    //     _flags: Option<u32>,
    // ) -> fuse_mt::ResultEmpty {
    //     Err(libc::ENOSYS)
    // }

    // We do not support symbolic links.
    // fn readlink(&self, _req: fuse_mt::RequestInfo, _path: &std::path::Path) -> fuse_mt::ResultData {
    //     Err(libc::ENOSYS)
    // }

    // "This function is rarely needed, since it's uncommon to make these objects inside special-purpose filesystems."
    // This is for fancy things like block objects i believe, we do not support this.
    // fn mknod(
    //     &self,
    //     _req: fuse_mt::RequestInfo,
    //     _parent: &std::path::Path,
    //     _name: &std::ffi::OsStr,
    //     _mode: u32,
    //     _rdev: u32,
    // ) -> fuse_mt::ResultEntry {
    //     Err(libc::ENOSYS)
    // }

    // Create a new directory if it does not already exist.
    // Returns file attributes about the new directory
    fn mkdir(
        &self,
        _req: fuse_mt::RequestInfo,
        parent: &std::path::Path,
        name: &std::ffi::OsStr,
        _mode: u32, // Permission bit related. Do not need.
    ) -> fuse_mt::ResultEntry {
        debug!("Creating new directory in `{}` named `{}`.", parent.display(), name.display());
        let handle = NotifyTui::start_task(TaskType::FilesystemMakeDirectory(name.display().to_string()), 3);
        // Make sure the name isn't too long
        if name.len() > 255 {
            debug!("Name is too long.");
            return Err(FILE_NAME_TOO_LONG);
        }

        // the new directory
        let new_dir: DirectoryItem;
        let the_name: String = name.to_str().expect("Should be valid utf8").to_string();

        // Open parent
        if let Some(mut parent) = DirectoryBlock::try_find_directory(Some(parent))? {
            NotifyTui::complete_task_step(&handle);
            debug!("Checking if directory exists...");
            if parent.find_item(&NamedItem::Directory(the_name.clone()))?.is_some() {
                // Directory already exists.
                debug!("Directory already exists.");
                NotifyTui::cancel_task(handle);
                return Err(ITEM_ALREADY_EXISTS)
            }
            
            // Make the directory
            debug!("It did not, creating directory...");
            new_dir = parent.make_directory(the_name)?;
            NotifyTui::complete_task_step(&handle);
            debug!("Directory created.");
        } else {
            // No such parent
            debug!("Parent did not exist.");
            NotifyTui::cancel_task(handle);
            return Err(NO_SUCH_ITEM);
        }
        
        // Now we need attribute information about it.
        debug!("Getting attribute info...");
        let attributes: FileAttr = new_dir.try_into()?;
        NotifyTui::complete_task_step(&handle);
        debug!("Done.");
        
        // All done!
        debug!("Directory created successfully.");
        NotifyTui::finish_task(handle);
        Ok(
            (
                HANDLE_TIME_TO_LIVE,
                attributes
            )
        )
    }

    // Deletes a file.
    fn unlink(
        &self,
        _req: fuse_mt::RequestInfo,
        parent: &std::path::Path,
        name: &std::ffi::OsStr,
    ) -> fuse_mt::ResultEmpty {
        debug!("Deleting file `{}` from directory `{}`...", name.display(), parent.display());

        let handle = NotifyTui::start_task(TaskType::FilesystemDeleteFile(name.display().to_string()), 3);

        // Make a fake handle to lookup the file we are looking for
        let temp_handle: FileHandle = FileHandle {
            path: parent.join(name).into(),
        };

        // This will return properly if the item did not exist.
        let file = temp_handle.get_directory_item()?;
        NotifyTui::complete_task_step(&handle);
        
        // Make sure it's a file
        if file.flags.contains(DirectoryItemFlags::IsDirectory) {
            // Cannot unlink directories.
            debug!("A directory was provided, not a file.");
            NotifyTui::cancel_task(handle);
            return Err(NOT_A_DIRECTORY);
        };
        
        // Now we need the parent directory block to perform the removal
        debug!("Looking for file...");
        if let Some(mut parent_dir) = DirectoryBlock::try_find_directory(Some(parent))? {
            NotifyTui::complete_task_step(&handle);
            // Delete the file
            if parent_dir.delete_file(file.into())?.is_some() {
                NotifyTui::complete_task_step(&handle);
                // All done.
                debug!("File deleted.");
                NotifyTui::finish_task(handle);
                Ok(())
            } else {
                // Weird, we checked that the directory was there, but when we went to delete it, it wasnt???
                warn!("We found the directory to delete, but when we tried to delete it, it was missing.");
                // this should not happen lmao, but whatever.
                NotifyTui::cancel_task(handle);
                Err(NO_SUCH_ITEM)
            }
        } else {
            // Should be impossible to get here, since we were already able to get the item from the parent
            // a few lines ago.
            // But we'll still gracefully handle it just in case.
            debug!("Parent folder does not exist.");
            NotifyTui::cancel_task(handle);
            Err(NO_SUCH_ITEM)
        }
    }

    // Deletes a directory.
    // Should fail if the directory is not empty.
    fn rmdir(
        &self,
        _req: fuse_mt::RequestInfo,
        parent: &std::path::Path,
        name: &std::ffi::OsStr,
    ) -> fuse_mt::ResultEmpty {
        debug!("Attempting to remove directory `{}` from `{}`...", name.display(), parent.display());

        let handle = NotifyTui::start_task(TaskType::FilesystemRemoveDirectory(name.display().to_string()), 4);

        let string_name: String = name.to_str().expect("Should be valid utf8").to_string();

        // Open the parent directory
        if let Some(parent_dir) = DirectoryBlock::try_find_directory(Some(parent))? {
            NotifyTui::complete_task_step(&handle);
            // Parent exists, get the child
            if let Some(child_dir) = parent_dir.find_item(&NamedItem::Directory(string_name))? {
                NotifyTui::complete_task_step(&handle);
                // Directory exists.
                
                // Make sure this is actually a directory
                if !child_dir.flags.contains(DirectoryItemFlags::IsDirectory) {
                    // Not a dir
                    debug!("Provided item is not a directory.");
                    NotifyTui::cancel_task(handle);
                    return Err(NOT_A_DIRECTORY);
                }
                
                // Get the block
                let block_to_delete = child_dir.get_directory_block()?;
                NotifyTui::complete_task_step(&handle);
                
                // Make sure it's empty
                if !block_to_delete.is_empty()? {
                    // Nope.
                    debug!("Directory is not empty, cannot delete.");
                    NotifyTui::cancel_task(handle);
                    return Err(DIRECTORY_NOT_EMPTY);
                }
                
                // Run the deletion.
                debug!("Deleting directory...");
                block_to_delete.delete_self(child_dir)?;
                NotifyTui::complete_task_step(&handle);
                NotifyTui::finish_task(handle);
                debug!("Done.");
                Ok(())
                
            } else {
                // child directory did not exist.
                debug!("The directory we wanted to delete does not exist.");
                NotifyTui::cancel_task(handle);
                Err(NO_SUCH_ITEM)
            }
        } else {
            // parent dir went to get milk
            debug!("Parent directory does not exist.");
            NotifyTui::cancel_task(handle);
            Err(NO_SUCH_ITEM)
        }
    }

    // We do not support symlinks.
    // fn symlink(
    //     &self,
    //     _req: fuse_mt::RequestInfo,
    //     _parent: &std::path::Path,
    //     _name: &std::ffi::OsStr,
    //     _target: &std::path::Path,
    // ) -> fuse_mt::ResultEntry {
    //     Err(libc::ENOSYS)
    // }

    // Renames / moves item.
    // Complicated error logic due to https://man7.org/linux/man-pages/man2/rename.2.html
    fn rename(
        &self,
        _req: fuse_mt::RequestInfo,
        parent: &std::path::Path,
        name: &std::ffi::OsStr,
        newparent: &std::path::Path,
        newname: &std::ffi::OsStr,
    ) -> fuse_mt::ResultEmpty {
        debug!("Renaming a item from `{}` to `{}`,", name.display(), newname.display());
        debug!("and moving from `{}` to `{}`.", parent.display(), newparent.display());

        // According to the man pages, we should get some flags here. but we dont.
        // I assume things like RENAME_NOREPLACE are being handled for us then.

        // Any case:
        // EISDIR newpath is an existing directory, but oldpath is not a directory.
        // EINVAL The new pathname contained a path prefix of the old, or, more generally, an attempt was made to make a directory a
        //  subdirectory of itself. (recursion moment)
        // ENOENT The link named by oldpath does not exist; or, a directory component in newpath does not exist; or, oldpath or newpath
        //  is an empty string. (TLDR this is a catch all)

        // If we are moving a directory:
        // The new path must not exist, or be empty.
        // ENOTEMPTY or EEXIST newpath is a nonempty directory
        // ENOTDIR A component used as a directory in oldpath or newpath is not, in fact, a directory.  Or, oldpath is a directory, and
        //  newpath exists but is not a directory. (Cant move non directories into directories, cant move directory into non directory.)

        // "If newpath exists but the operation fails for some reason,
        //  rename() guarantees to leave an instance of newpath in place."
        // why word it like this lmao
        // if the destination already exists, but the move fails, keep what was already at the destination.

        // Also in theory, we should be checking if anyone is reading this item and if they are, return busy.
        // but there isnt any infra for that yet, and with the one year timeouts, you would have to wait a while.
        // fun. we will ignore it until something explodes.



        // This is gonna be REALLY complicated. lol.
        
        

        // Make sure the new name isn't too long
        if newname.len() > 255 {
            // too long
            warn!("New item name was too long");
            return Err(FILE_NAME_TOO_LONG);
        }


        // Now, the "easy" cases.
        // Where we're coming from (including name of file/folder)
        let source_full_temp_handle: FileHandle = FileHandle {
            path: parent.join(name).into(),
        };

        // Where we want to go
        // Where we're coming from (including name of file/folder)
        let destination_full_temp_handle: FileHandle = FileHandle {
            path: newparent.join(newname).into(),
        };

        // If they are the same, we dont need to do anything at all.
        if source_full_temp_handle.path == destination_full_temp_handle.path {
            // why...
            debug!("Source and destination are the same, skipping.");
            return Ok(());
        }

        // Make sure the two are the same underlying type
        let source_is_file = match source_full_temp_handle.is_file()? {
            Some(bool) => bool,
            None => {
                // The source item does not exist.
                return Err(NO_SUCH_ITEM);
            },
        };

        let destination_is_file = match destination_full_temp_handle.is_file()? {
            Some(bool) => bool,
            None => {
                // The destination item does not exist, which means we're not going to replace a pre-existing item, thus we
                // can just copy what the other file type is, since we will be creating this later.
                source_is_file
            },
        };


        debug!("Making sure the two are of the same type...");
        if source_is_file == destination_is_file {
            debug!(
                "Types are the same, both are {}.",
                if source_is_file {
                    "files"
                } else {
                    "directories"
                }
            )
            // They are both the same.
        } else {
            // Types are different, we cannot do that
            warn!("Types are different, we cannot perform this rename/move.");
            return Err(NOT_A_DIRECTORY);
        }

        // Now that we know the two types are the same,
        // Grab the parents and the item we are attempting to move depending on type.

        // For both types of rename/move operations, we must have:
        // - The parent folder of the source, and destination
        // - The item we are renaming
        // But we do not need the destination item to exist.
        // Any logic around that is handled differently depending on if this was a file or not.

        // I wrote directory handling first, then came back for file movement. Standing on the shoulders of myself, i know that
        // directories always return a Err(NO_SUCH_ITEM) if either of the parents do not exist. This is also true of files, so we
        // can perform that check out here.
        
        debug!("Trying to obtain the parents of the source and destination, and the directory item for the item we are trying to move.");

        let source_item_name: String = name.to_str().expect("Should be valid utf8").to_string();
        let destination_item_name: String = newname.to_str().expect("Should be valid utf8").to_string();

        debug!("Checking if parents existed...");
        // Try to get the source parent directory, then try to get the item refering to the dir we are moving.
        let mut source_parent_dir: DirectoryBlock = if let Some(exist) = DirectoryBlock::try_find_directory(Some(parent))? {
            // Good.
            debug!("Source parent exists.");
            exist
        } else {
            // missing
            warn!("Source parent did not exist. Cannot continue.");
            return Err(NO_SUCH_ITEM);
        };

        let mut destination_parent_dir: DirectoryBlock = if let Some(exist) = DirectoryBlock::try_find_directory(Some(newparent))? {
            // Good.
            debug!("Destination parent exists.");
            exist
        } else {
            // missing
            warn!("Destination parent did not exist. Cannot continue.");
            return Err(NO_SUCH_ITEM);
        };

        // Item logic must be handled lower down, but we can at least abstract the calls out at this point to work with options later.

        // We know the kind here so we can abstract this away as well.
        let maybe_source_directory_item: Option<DirectoryItem>;
        let maybe_destination_directory_item: Option<DirectoryItem>;
        if source_is_file {
            // both files.
            maybe_source_directory_item = source_parent_dir.find_item(&NamedItem::File(source_item_name.clone()))?;
            maybe_destination_directory_item = destination_parent_dir.find_item(&NamedItem::File(destination_item_name.clone()))?;
        } else {
            // both directories.
            maybe_source_directory_item = source_parent_dir.find_item(&NamedItem::Directory(source_item_name.clone()))?;
            maybe_destination_directory_item = destination_parent_dir.find_item(&NamedItem::Directory(destination_item_name.clone()))?;
        };

        // The following complicated move logic requires that the two parent directories be different. If the source and 
        // destination directories are the same, we can just rename the inode, skipping all of the fancier operations.

        if parent == newparent {
            // Sweet!
            // We dont even need the destination info
            drop(maybe_destination_directory_item);
            drop(destination_full_temp_handle);
            drop(destination_parent_dir);

            // Source must exist.
            let source = match maybe_source_directory_item {
                Some(ok) => ok,
                None => {
                    // Can't rename nothing.
                    return Err(NO_SUCH_ITEM);
                },
            };

            // Type does not matter, we can just update the name in the directory, since inodes do not hold that info.
            // Explicitly check the error, silently returning when this fails is bad.
            let rename_result = match source_parent_dir.try_rename_item(&source.into(), destination_item_name) {
                Ok(ok) => ok,
                Err(err) => {
                    // Renaming failed for a lower level issue!
                    warn!("Item rename failed! Why?");
                    warn!("`{err:#?}`");
                    // Bail out
                    return Err(err.into());
                },
            };


            if rename_result {
                // rename worked.
                debug!("Item renamed successfully.");
                return Ok(())
            } else {
                // Somehow the item we wanted to rename disapeared, we'll return a generic error.
                // Since panicing at this high of a level would be stupid. Even though this branch is
                // unlikely.
                warn!("Item to rename disappeared!");
                return Err(GENERIC_FAILURE);
            }
        }

        // This rename moves the item between directories.


        // we branch depending on if it was a file or directory, handling is slightly different
        if source_is_file {
            //
            // File movement.
            //
            debug!("Starting file movement...");
            // If the new item exists, it will be replaced. (see rename(2) manpage)

            // The source item must exist.
            debug!("Checking that source item exists...");
            let source_item = if let Some(existed) = maybe_source_directory_item {
                // good
                debug!("Yes it does.");
                existed
            } else {
                // cant move nothing.
                warn!("Source item did not exist. Cannot perform rename/move.");
                return Err(NO_SUCH_ITEM);
            };

            debug!("Checking if destination file already existed...");
            // Check if the destination file already exists, that will change our behavior on failure.
            if maybe_destination_directory_item.is_some() {
                // Destination item exists, we will be overwriting this, but we will hold onto it just in case.
                // In theory we should try to put this back if the rename fails.

                // Since the item exists already, we will extract it. To delete the old file we need to have the file in the block. Which
                // complicates things a bit. So we pull it out, and when we are ready to delete it, we rename it, put it back in, and delete it.
                let extracted_old: DirectoryItem = match destination_parent_dir.find_and_extract_item(&NamedItem::File(destination_item_name.clone())) {
                    Ok(ok) => match ok {
                        Some(ok) => ok,
                        None => {
                            // We tried to extract it, but it was no longer there?
                            // No data has been modified (by us here at least), entire operation can be retried.
                            warn!("Destination item was no longer there when extraction was attempted. Non-fatal, but weird.");
                            return Err(NO_SUCH_ITEM)
                        },
                    },
                    Err(error) => {
                        // The extraction failed at _some_ point, unknown where. Chances are the destination item is now gone. But we have failed.
                        warn!("Extracting the old item failed for a low level reason, we have to bail.");
                        return Err(error.into())
                    },
                };

                // Now we have that copy for later, we can stick a copy of the source item into the destination
                debug!("Inserting copy of the source file into the destination directory...");
                // We must give it the new name first:
                let mut renamed_source_item = source_item;
                renamed_source_item.name = destination_item_name.clone();
                renamed_source_item.name_length = destination_item_name.len() as u8; // Already checked name length.
                match destination_parent_dir.add_item(&renamed_source_item) {
                    Ok(_) => (),
                    Err(error) => {
                        // Failed to add item to destination
                        warn!("Failed to put copy into the destination for low level reason.");
                        // Try to put back the file that was here before.
                        warn!("Attempting to restore previous item if possible...");
                        // Chances are if the previous one failed, this will to, but whatever.
                        if destination_parent_dir.add_item(&extracted_old).is_ok() {
                            warn!("Previous file restored.");
                        } else {
                            // oof
                            warn!("Failed to restore previous file. File has been permanently lost.");
                        }
                        // As good as it'll get. Return the error that caused us to fail.
                        return Err(error.into())
                    },
                }
                debug!("Done.");
                debug!("Removing old item from origin directory...");

                // Now go to the parent of the source and tell em to slime tf outta the old item

                match source_parent_dir.find_and_extract_item(&NamedItem::File(source_item_name.clone())) {
                    Ok(ok) => {
                        match ok {
                            Some(_found) => {
                                // Removal worked
                                debug!("Source item removed.")
                            },
                            None => {
                                // Item we tried to remove was not there.
                                // Weird, but if we got this far, it must have at least made it into the destination.
                                warn!("The source item we tried to remove was not there. Which is fine, since that was the goal anyways.");
                            },
                        }
                    },
                    Err(err) => {
                        // We have now failed to remove the previous item, but the new one is in place. This is good enough.
                        // If the item is still in that other block, it is now a duplicate reference of the underlying file.
                        // Not great... but not much I can do here. No transactions means no safe actions!
                        warn!("Failed to remove previous item, it may still be there. Good enough.");
                        // The move has now technically finished, even though we have errored at this point, so guess what?
                        debug!("Removal failed due to: {err:#?}");
                        return Ok(());
                    },
                };

                // Now that the file is no longer where it used to live, we can delete the item that used to live in the destination that
                // we overwrote.

                // If this fails, the item is no longer in the file, but will still occupy blocks on disk, which isn't great, but at least
                // you cant get to them.

                // Rename the item so we dont collide with the newly moved in file
                let mut renamed: DirectoryItem = extracted_old;
                renamed.name.push_str(".fluster_old");
                // Is this too long now?
                if renamed.name.len() > 255 {
                    // Shoot, go with a random name instead.
                    let mut random: rand::prelude::ThreadRng = rand::rng();
                    let random_name: u128 = random.random();
                    let mut random_name_string = random_name.to_string();
                    random_name_string.truncate(128);
                    renamed.name = random_name_string;
                    renamed.name_length = renamed.name.len() as u8; // will fit
                }

                // Hold onto the new name for later
                let deletion_name: String = renamed.name.clone();

                // Put the renamed item in there.
                debug!("Re-inserting old file with a new name to delete it...");
                match destination_parent_dir.add_item(&renamed) {
                    Ok(_) => {
                        // Adding worked.
                        debug!("Inserted.")
                    },
                    Err(err) => {
                        // Damn. We will just leak the blocks this took up.
                        warn!("Insertion failed.");
                        warn!("Just to keep going, we will leak the blocks that the old file references.");
                        warn!("Rename \"finished\".");
                        debug!("Failed due to: {err:#?}");
                        // Rename still worked overall.
                        return Ok(());
                    },
                }
                // Now kill it
                debug!("Deleting the old file...");
                match destination_parent_dir.delete_file(NamedItem::File(deletion_name)) {
                    Ok(ok) => match ok {
                        Some(_) => {
                            // File was deleted.
                            debug!("Old file deleted.")
                        },
                        None => {
                            // The file did not exist?!?
                            warn!("Somehow, we added the file, and when we went to delete it, it no longer existed.");
                            warn!("It probably leaked, but there is nothing we can do.");
                            // Good enough!
                            warn!("Good enough. Rename finished.");
                            return Ok(());
                        },
                    },
                    Err(err) => {
                        // Yet another leak scenario.
                        warn!("Deletion failed.");
                        warn!("Just to keep going, we will leak the blocks that the old file references.");
                        warn!("Rename \"finished\".");
                        debug!("Failed due to: {err:#?}");
                        // Rename still worked overall.
                        return Ok(());
                    },
                }

                // File has been deleted. Cleanup is now finished.
                // Done moving file, which replaced a pre-existing file.
            } else {
                // We are not trying to overwrite a pre-existing file, this makes our lives easier.
                debug!("Destination item does not exist yet. We will create it.");

                // We must give it the new name first:
                let mut renamed_source_item = source_item;
                renamed_source_item.name = destination_item_name.clone();
                renamed_source_item.name_length = destination_item_name.len() as u8; // Already checked name length.

                // Put the file into the destination
                debug!("Adding source file to destination directory...");
                match destination_parent_dir.add_item(&renamed_source_item) {
                    Ok(_) => {
                        // That worked
                    },
                    Err(err) => {
                        // Failed to add to destination
                        // Drive level issue.
                        warn!("Failed at a level lower than us. Unknown state.");
                        debug!("Failed due to: {err:#?}");
                        return Err(err.into())
                    },
                }
                debug!("File added.");

                // Now we need to remove the old item.
                match source_parent_dir.delete_file(NamedItem::File(source_item_name)) {
                    Ok(ok) => {
                        // if ok is none, the item disappeared, which should not happen.
                        // Yes its weird that the item dissapeared, but its better to let the caller
                        // re-list the directory and try the rename again if needed. Otherwise we would
                        // just crash, which is, sub-par.

                        if ok.is_none() {
                            warn!("The item we were renaming disappeared!");
                            return Err(GENERIC_FAILURE)
                        }
                    },
                    Err(err) => {
                        // The file made it to the destination, but removing the original failed.
                        // The old item may still be there, or it leaked blocks due to failed cleanup.
                        warn!("Failed to delete source item, it may still be there.");
                        warn!("Blocks were probably leaked.");
                        warn!("Non-critical deletion failure: {err:#?}")
                        // Good enough.
                    },
                }
            }
            // All done.
            debug!("File moved successfully.");
            Ok(())
        } else {
            //
            // Directory movement.
            //
            debug!("Starting directory movement...");
            
            // Make sure we aren't trying to make a self referential folder
            debug!("Checking for recursion...");
            if destination_full_temp_handle.path.starts_with(&source_full_temp_handle.path) {
                // Destination contains the source, therefore this is self referential.
                warn!("Cannot move directory inside of itself.");
                return Err(INVALID_ARGUMENT);
            }
            debug!("No recursion.");

            
            // To fulfill the requirement of the destination directory being empty, we will check if the directory exists in the
            // parent. If it does, make sure its empty, if it is, then we will update the DirectoryItem to point at the block for
            // the start of the DirectoryBlocks of the source directory.
            // If the directory does not exist, we will create it and still do the same pointer swap.
            // if the directory is not empty, we have to cancel the move.

            // Do both directories exist?
            debug!("Checking if the parents contain directory item we want to move...");
            // source
            let source_directory_item: DirectoryItem = if let Some(item) = maybe_source_directory_item {
                // Source exists
                item
            } else {
                // Item was not there. Cant copy nothing.
                debug!("Source directory missing, cannot rename/move folder.");
                return Err(NO_SUCH_ITEM);
            };
            debug!("Source good.");

            // Check that the destination folder exists and is empty.
            if let Some(item) = maybe_destination_directory_item {
                // Destination has to be empty
                debug!("Destination already existed, making sure its empty...");
                if item.clone().get_directory_block()?.is_empty()? {
                    // All good, we will delete the directory since we are going to replace it.
                    debug!("It's empty, it will be deleted soon");
                } else {
                    // Directory was not empty, we cannot continue
                    warn!("Destination directory was not empty, cannot rename/move folder.");
                    return Err(DIRECTORY_NOT_EMPTY);
                }
            } else {
                // no destination, we will make it
                debug!("Destination did not exist, creating...");
                // Annoying clone.
                let _ = destination_parent_dir.make_directory(destination_item_name.clone())?;
                // yes we are creating it to just remove it again, but we are supposed to (in theory if i remember correctly idk its 1am) leave
                // a folder at the destination even if the move fails.
            };
            debug!("Destination good.");


            // Now for the fun part, we can extract the DirectoryItem from the first directory, and swap it into
            // the second one, thus the new folder points at the contents without moving the underlying files in the folder.

            debug!("Swapping DirectoryItems...");

            // Remove the destination folder
            // "Inshallah he will be grounded into a fine dust"

            // We have to tread lightly at this point. If the swap fails, we would lose data.
            // 🤓 erm actually the data would still be there, just not referenced- SHUT UP

            // Extract, and delete. Extraction cleans up anything this used to point to for us.
            debug!("Extracting destination...");
            let _extracted_dest = match destination_parent_dir.find_and_extract_item(&NamedItem::Directory(destination_item_name.clone())) {
                Ok(ok) => {
                    // Directory had to've been there, right?
                    if let Some(worked) = ok {
                        // Found and extracted the item, we will hold onto it incase we need to recreate it.
                        worked
                    } else {
                        // What
                        warn!("Tried to delete the destination directory to prepare for swap, but it was no longer there!");
                        // This should be impossible.
                        // But just in case, er, say the operation failed.
                        return Err(GENERIC_FAILURE);
                    }
                    // We will hold onto it just in case, even though it's empty.
                },
                Err(err) => {

                    // This should fail tests.
                    if cfg!(test) {
                        panic!("{err:#?}");
                    }

                    // Drive level issue.
                    warn!("Failed at a level lower than us. Unknown state.");
                    return Err(err.into())
                },
            };
            debug!("Destination extracted");

            // Now get a COPY of the source, we wont remove the source until we know for sure we have properly moved the folder.
            // Wait, we already have it, in `source_directory_item`, duh
            
            // Attempt to add the directory to the new parent
            debug!("Inserting copy of source directory...");

            // Make sure we rename dat mf fr
            let mut renamed_source_dir_item = source_directory_item;
            renamed_source_dir_item.name = destination_item_name.clone();
            renamed_source_dir_item.name_length = destination_item_name.len() as u8; // Length checked above

            match destination_parent_dir.add_item(&renamed_source_dir_item) {
                Ok(_) => {
                    // that worked
                    // No need to do anything
                },
                Err(err) => {
                    // Drive level issue.
                    warn!("Failed at a level lower than us. Unknown state.");
                    // Attempt to uphold POSIX standard (like hell the rest of fluster is compliant) by
                    // at least attempting to put the original directory back again.
                    // We dont actually need to though, since it hasn't been extracted yet.

                    // This should fail tests.
                    if cfg!(test) {
                        panic!("{err:#?}");
                    }

                    return Err(err.into())
                },
            }
            debug!("Insertion succeeded.");
            
            // Now that the data has been safely pointed at from the new location, we will remove the old reference to it.
            debug!("Removing old source...");
            let ashes = match source_parent_dir.find_and_extract_item(&NamedItem::Directory(source_item_name.clone())) {
                Ok(ash) => ash,
                Err(err) => {
                    // Drive level issue.
                    warn!("Failed at a level lower than us. Unknown state.");
                    // So now we have properly moved the source into the destination, but the source might still be there.
                    // This wouldn't be too bad, if not for the fact that now both DirectoryBlocks contain the same DirectoryItem which
                    // points to the same directory. Thus they have become hard-linked. Not great.
                    // Good luck lmao.
                    warn!("There isn't really anything we can do at this point, a hard link has been created due to this.");
                    warn!("We consider this good enough. Done moving directory.");
                    // shoulda thought ahead and made fluster more transactional, oh well.
                    // We have to ignore the error, but might as well print it.
                    debug!("{err:#?}");

                    // This should fail tests.
                    if cfg!(test) {
                        panic!("{err:#?}");
                    }

                    return Ok(());
                },
            };


            if let Some(_to_ashes) = ashes {
                // It was there, and removed.
                // We're all done.
                debug!("Done.")
            } else {
                // ????? DIRECTORY IS NOT THERE (GONE) (STOLEM)
                warn!("Somehow, we copied the source directory across correctly, but now the source is missing so we cant remove it.");
                // I mean like, this still worked, no?
                // goals:
                // put source in destination: check
                // source is no longer there: check
                // sooooooo
                // GOOD ENOUGH!
                warn!("...but that's close enough.");
            };

            // All done!
            debug!("Rename finished, directories renamed/moved.");
            Ok(())
        }
        // this comment is unreachable, all cases are covered.
    }

    // We do not support hard links.
    // fn link(
    //     &self,
    //     _req: fuse_mt::RequestInfo,
    //     _path: &std::path::Path,
    //     _newparent: &std::path::Path,
    //     _newname: &std::ffi::OsStr,
    // ) -> fuse_mt::ResultEntry {
    //     Err(libc::ENOSYS)
    // }

    // Open a file and get a handle that will be used to access it.
    // Does not create files.
    fn open(
        &self,
        _req: fuse_mt::RequestInfo,
        path: &std::path::Path,
        flags: u32,
    ) -> fuse_mt::ResultOpen {
        debug!("Opening item at path `{}`...", path.display());
        let task_handle = NotifyTui::start_task(TaskType::FilesystemOpenFile(
            path.file_name().unwrap_or(OsStr::new("?")).display().to_string()
        ), 4);
        // Deduce the open permissions.
        debug!("Deducing flags...");
        let converted_flag: ItemFlag = ItemFlag::deduce_flag(flags)?;
        debug!("Ok.");

        // We require at least one of the read/write flags.
        // ...or, more correctly: we would require them if we used them.
        // We dont. Everything is read/write.

        // We ignore any flags that are not valid for this method, such as
        // truncation or creation flags.

        // open() always returns a brand new file handle, regardless if that file was
        // already open somewhere else.
        let mut handle: FileHandle = FileHandle {
            path: path.into(),
        };

        // We do not allocate the file handle until we are sure we will use it.

        // Make sure the name of the file is not too long.
        if handle.name().len() > 255 {
            warn!("File name is too long.");
            // File name was too long.
            NotifyTui::cancel_task(task_handle);
            return Err(FILE_NAME_TOO_LONG)
        }
        
        // If this is the dot directory, we need to go up a level to read ourselves.
        if handle.name() == "." {
            // Go up a path.
            // If this returns none, all is well
            handle.path = handle.path.parent().unwrap_or(Path::new("")).into();
        }
        
        // Load in info about where the file should be.
        // This will bail if a low level floppy issue happens.
        debug!("Attempting to load in the parent directory...");
        let containing_dir_block: DirectoryBlock = match DirectoryBlock::try_find_directory(handle.path.parent())? {
            Some(ok) => ok,
            None => {
                // Cannot load files from directories that do not exist.
                warn!("Directory that the item was supposed to be contained within does not exist.");
                NotifyTui::cancel_task(task_handle);
                return Err(NO_SUCH_ITEM)
            },
        };
        debug!("Directory loaded.");
        NotifyTui::complete_task_step(&task_handle);
        
        // At this point. We need to know if we are looking for a directory or a file.
        debug!("Deducing request item type...");
        let extracted_name = handle.name();
        
        let item_to_find: NamedItem = match handle.get_named_item()? {
            Some(item) => item,
            None => {
                // No such item exists.
                NotifyTui::cancel_task(task_handle);
                return Err(NO_SUCH_ITEM);
            },
        };
        
        if item_to_find.is_file() {
            debug!("Looking for a file...");
        } else {
            debug!("Looking for a directory...");
        }
        debug!("Named `{extracted_name}`.");
        
        // Hold onto the item until we need it
        let found_item: DirectoryItem;
        
        // Now load in the directory item.
        debug!("Attempting to find the item...");
        if let Some(exists) = containing_dir_block.find_item(&item_to_find)? {
            debug!("Item exists.");
            found_item = exists;
        } else {
            // No item
            debug!("Item does not exist.");
            NotifyTui::cancel_task(task_handle);
            return Err(NO_SUCH_ITEM);
        }
        NotifyTui::complete_task_step(&task_handle);
        
        // We have now loaded in the directory item, or bailed out if needed.
        
        // Assert that this is a directory if required.
        // In theory we could check this earlier, but it's good to ensure that the underlying
        // item agrees.
        if converted_flag.contains(ItemFlag::ASSERT_DIRECTORY) {
            debug!("Caller wants to ensure they are opening a directory.");
            if !found_item.flags.contains(DirectoryItemFlags::IsDirectory) {
                debug!("This is not a directory.");
                NotifyTui::cancel_task(task_handle);
                return Err(NOT_A_DIRECTORY)
            }
            debug!("This is a directory.");
        }
        
        // We are done creating/loading the file, its time to get a handle.
        debug!("Getting a handle on things...");
        let new_handle: u64 = handle.allocate();

        NotifyTui::complete_task_step(&task_handle);
        
        // Done!
        debug!("Opening finished.");
        Ok((new_handle, converted_flag.into()))
    }

    // Read file data from a file handle.
    // "Note that it is not an error for this call to request to read past the end of the file,
    //   and you should only return data up to the end of the file
    //   (i.e. the number of bytes returned will be fewer than requested; possibly even zero).
    //   Do not extend the file in this case."
    //
    // Uses callbacks, wacky, not sure how that works.
    fn read(
        &self,
        _req: fuse_mt::RequestInfo,
        path: &std::path::Path,
        fh: u64,
        offset: u64,
        size: u32,
        callback: impl FnOnce(fuse_mt::ResultSlice<'_>) -> fuse_mt::CallbackResult,
    ) -> fuse_mt::CallbackResult {
        debug!("Reading `{}` bytes from file `{}`", size, path.display());

        let task_handle = NotifyTui::start_task(
            TaskType::FilesystemReadFile(
                path.file_name()
                .unwrap_or(OsStr::new("?")).display().to_string()
            ),
            3
        );

        // Open the file handle
        let got_handle = FileHandle::read(fh);

        // Still not sure if we need to check this, but whatever.
        if got_handle.path != path.into() {
            // They aren't the same? not sure what to do with that
            error!("readdir() tried to read a path, but provided a handle to a different path.");
            error!("fh: `{}` | path: `{}`", got_handle.path.display(), path.display());
            error!("Not sure what to do here, giving up.");
            NotifyTui::cancel_task(task_handle);
            return callback(Err(GENERIC_FAILURE));
        }
        
        // Try finding the directory item
        let file = match got_handle.get_directory_item() {
            Ok(ok) => ok,
            Err(err) => {
                // Getting the item failed, maybe it wasn't there.
                NotifyTui::cancel_task(task_handle);
                return callback(Err(err))
            },
        };
        NotifyTui::complete_task_step(&task_handle);
        
        // Make sure that it's a file.
        if file.flags.contains(DirectoryItemFlags::IsDirectory) {
            // Can't read a directory!
            warn!("Tried to read a directory as a file. Ignoring...");
            return callback(Err(IS_A_DIRECTORY));
        }
        
        // Found a file!
        // We need to bound our read by the size of the file, since the read() filesystem call can
        // try to read past the end.
        let file_size = match file.get_size() {
            Ok(ok) => ok,
            Err(error) => {
                // Lower level error
                NotifyTui::cancel_task(task_handle);
                warn!("Failed to get size of file! Giving up...");
                return callback(Err(error.into()))
            },
        };

        NotifyTui::complete_task_step(&task_handle);
        
        // Subtract the offset to idk man why am i explaining this im sure you understand.
        // Reads are limited to 4GB long, which should be way above our max read size anyways.

        // If a read bigger than that comes in, we'll ignore it.
        let checking_size = std::cmp::min(size as u64, file_size - offset);
        let bounded_read_length = if checking_size > u32::MAX.into() {
            // Cant do that.

            // Also if you're here, that means the file you're trying to read is actually >4GB.
            // You are insane.

            warn!("Tried to read more than 4GB at once!");
            NotifyTui::cancel_task(task_handle);
            return callback(Err(INVALID_ARGUMENT));
        } else {
            // Size checked, this cast is safe.
            checking_size as u32
        };

        if bounded_read_length != size {
            // size did change.
            debug!("Read was too large, truncated to `{bounded_read_length}` bytes.");
        }

        // Do the read.
        // This vec might be HUGE, this is why we need to limit the read size on the filesystem.
        debug!("Starting read...");
        let read_buffer: Vec<u8> = match file.read_file(offset, bounded_read_length) {
            Ok(ok) => ok,
            Err(error) => {
                // Lower level error
                warn!("Failed while reading the file! Giving up...");
                NotifyTui::cancel_task(task_handle);
                return callback(Err(error.into()))
            },
        };
        NotifyTui::complete_task_step(&task_handle);
        debug!("Read finished.");
        NotifyTui::finish_task(task_handle);

        // All done!
        callback(Ok(&read_buffer))
    }

    // Write data to a file using a file handle.
    fn write(
        &self,
        _req: fuse_mt::RequestInfo,
        path: &std::path::Path,
        fh: u64,
        offset: u64,
        data: Vec<u8>,
        _flags: u32, // hehe
    ) -> fuse_mt::ResultWrite {
        debug!("Writing `{}` bytes to file `{}`...", data.len(), path.display());
        let task_handle = NotifyTui::start_task(
            TaskType::FilesystemWriteFile(
                path.file_name()
                .unwrap_or(OsStr::new("?")).display().to_string()
            ),
            2
        );

        // Open the file handle
        let got_handle = FileHandle::read(fh);

        // Still not sure if we need to check this, but whatever.
        if got_handle.path != path.into() {
            // They aren't the same? not sure what to do with that
            error!("readdir() tried to read a path, but provided a handle to a different path.");
            error!("fh: `{}` | path: `{}`", got_handle.path.display(), path.display());
            error!("Not sure what to do here, giving up.");
            NotifyTui::cancel_task(task_handle);
            return Err(GENERIC_FAILURE);
        }

        // Try finding the directory item
        let file = got_handle.get_directory_item()?;
        NotifyTui::complete_task_step(&task_handle);
        
        // man page:
        // If count is zero and fd refers to a regular file, then write() may
        // return a failure status if one of the errors below is detected.
        // If no errors are detected, or error detection is not performed, 0
        // is returned without causing any other effect.  If count is zero
        // and fd refers to a file other than a regular file, the results are
        // not specified.
        //
        // So if we want to write zero bytes, do nothing
        if data.is_empty() {
            // uh ok then
            debug!("Caller wanted to write 0 bytes. Skipping write.");
            NotifyTui::cancel_task(task_handle);
            return Ok(0);
        }




        // Now write to the file!
        debug!("Starting write...");
        let bytes_written = file.write_file(&data, offset)?;
        NotifyTui::complete_task_step(&task_handle);
        debug!("Write completed.");

        // According to spec, we just tell the caller how many bytes we wrote.
        // If we didn't write everything, its on them to keep going.
        // https://man7.org/linux/man-pages/man2/write.2.html

        // Return the number of bytes written.
        NotifyTui::finish_task(task_handle);
        Ok(bytes_written)
    }

    // Flushing does not do anything, since we manually handle our caching.
    fn flush(
        &self,
        _req: fuse_mt::RequestInfo,
        _path: &std::path::Path,
        _fh: u64,
        _lock_owner: u64,
    ) -> fuse_mt::ResultEmpty {

        // We dont want the OS to be able to flush the cache to disk, this could happen randomly for no reason.
        // We are responsible for tracking how stale the cache is.

        // The only time we will flush the cache from this level is on shutdown.
        
        Ok(())
    }

    // Releasing a file handle.
    fn release(
        &self,
        _req: fuse_mt::RequestInfo,
        _path: &std::path::Path,
        fh: u64,
        _flags: u32,
        _lock_owner: u64,
        _flush: bool,
    ) -> fuse_mt::ResultEmpty {
        FileHandle::drop_handle(fh);
        Ok(())
    }

    // See flush()
    fn fsync(
        &self,
        _req: fuse_mt::RequestInfo,
        _path: &std::path::Path,
        _fh: u64,
        _datasync: bool,
    ) -> fuse_mt::ResultEmpty {
        Ok(())
    }

    // Open a directory and get a handle to it
    fn opendir(
        &self,
        req: fuse_mt::RequestInfo,
        path: &std::path::Path,
        flags: u32,
    ) -> fuse_mt::ResultOpen {

        // This just gets pushed over to open(), since
        // we already handle directories over there.
        //
        // Should we handle files and directories both in open? maybe not.
        self.open(req, path, flags)
    }

    // List the contents of a directory.
    // "Return one or more directory entries (struct dirent) to the caller."
    // "This is one of the most complex FUSE functions." Oof.
    // "The readdir function is somewhat like read, in that it starts at a
    //  given offset and returns results in a caller-supplied buffer."
    // "However, the offset not a byte offset" What the hell
    // "...and the results are a series of struct dirents rather than being uninterpreted bytes" those are just words Geoffery
    //
    // Luckily we are working at a level way above that!
    fn readdir(
        &self,
        _req: fuse_mt::RequestInfo,
        path: &std::path::Path,
        fh: u64,
    ) -> fuse_mt::ResultReaddir {
        debug!("Getting contents of directory `{}`...", path.display());

        let task_handle = NotifyTui::start_task(TaskType::FilesystemReadDirectory(
            path.file_name().unwrap_or(OsStr::new("?")).display().to_string()
            ), 
            4
        );

        // Make sure the file handle and the incoming path are the same. I assume they should be, but
        // cant hurt to check.
        let got_handle = FileHandle::read(fh);
        
        if got_handle.path != path.into() {
            // They aren't the same? not sure what to do with that
            error!("readdir() tried to read a path, but provided a handle to a different path.");
            error!("fh: `{}` | path: `{}`", got_handle.path.display(), path.display());
            error!("Not sure what to do here, giving up.");
            NotifyTui::cancel_task(task_handle);
            return Err(GENERIC_FAILURE);
        }
        
        // Since we have a handle, getting the directory is easy.
        debug!("Getting the directory item from handle...");
        let dir_item: DirectoryItem = if let Ok(exists) = got_handle.get_directory_item() {
            // good
            exists
        } else {
            // Tried to read in a directory item that did not exist, yet we have a handle to it?
            // Guess the handle must be stale?
            
            // Yes, get_directory_item() returns its own error, but we should get rid of the invalid handle.

            warn!("Tried to read in a directory item from a handle, but the item was not there. Returning stale.");
            NotifyTui::cancel_task(task_handle);
            return Err(STALE_HANDLE)
        };

        NotifyTui::complete_task_step(&task_handle);
        
        // Double check that this is a file.
        if !dir_item.flags.contains(DirectoryItemFlags::IsDirectory) {
            // No.
            warn!("Tried to call readdir on a file!");
            NotifyTui::cancel_task(task_handle);
            return Err(NOT_A_DIRECTORY);
        }
        
        debug!("Getting directory block...");
        let dir_block = dir_item.get_directory_block()?;
        NotifyTui::complete_task_step(&task_handle);
        
        // List the files off
        debug!("Listing items...");
        let items = dir_block.list()?;
        NotifyTui::complete_task_step(&task_handle);
        
        // Now pull out the names and types
        let mut listed_items: Vec<DirectoryEntry> = items.iter().map(|item| {
            let kind = if item.flags.contains(DirectoryItemFlags::IsDirectory) {
                FileType::Directory
            } else {
                FileType::RegularFile
            };
            
            DirectoryEntry {
                name: item.name.clone().into(),
                kind,
            }
        }).collect();
        NotifyTui::complete_task_step(&task_handle);
        
        // Now add the unix `.` item.
        listed_items.push(
            DirectoryEntry {
                name: std::ffi::OsStr::new(".").into(),
                kind: FileType::Directory,
            }
        );

        NotifyTui::finish_task(task_handle);
        
        // All done!
        debug!("Done. Directory contained `{}` items.", listed_items.len());
        Ok(listed_items)
    }

    // See release()
    fn releasedir(
        &self,
        _req: fuse_mt::RequestInfo,
        _path: &std::path::Path,
        fh: u64,
        _flags: u32,
    ) -> fuse_mt::ResultEmpty {
        FileHandle::drop_handle(fh);
        Ok(())
    }

    // See flush()
    fn fsyncdir(
        &self,
        _req: fuse_mt::RequestInfo,
        _path: &std::path::Path,
        _fh: u64,
        _datasync: bool,
    ) -> fuse_mt::ResultEmpty {
        Ok(())
    }

    // Get file system statistics.
    // Seemingly contains information about the file system, like optimal block size and max file name length
    fn statfs(&self, _req: fuse_mt::RequestInfo, _path: &std::path::Path) -> fuse_mt::ResultStatfs {
        // This does not appear to be required, but we can implement it anyways.

        // Get the pool header, we need it for disk counts and such.
        // If this doesnt work, just tell the caller to try again later.

        let pool = if let Ok(pool_inner) = GLOBAL_POOL.get().expect("Global pool should be created at this stage!").try_lock() {
            pool_inner.header
        } else {
            // Lock failed.
            debug!("Tried to get stats about the pool, but locking the pool failed. Skipping...");
            return Err(TRY_AGAIN)
        };

        // Number of blocks on the device is just the highest disk*2880
        let blocks: u64 = pool.highest_known_disk as u64 * 2880;

        // We know how many blocks are free.
        // Not sure how Linux reacts to disks that grow on the fly, but
        // it seems like a likely enough use-case that this should be fine...
        let bfree: u64 = pool.pool_standard_blocks_free.into();

        // The number of available blocks is the same as the number of free blocks.
        let bavail = bfree;

        // Knowing how many files exist is a process that would take a VERY long time to figure out
        // typically. This seems like a bad idea to actually look up on the fly.
        // Just say there's a few.
        let files: u64 = 985;

        // We don't have a limit to file inodes, so we always have a lot free.
        let ffree: u64 = 12345;

        // Blocks are 512 bytes, technically, but innards of those blocks are limited...
        // Not that huge of a deal for transfers to be aligned, so we'll just say 512 still.
        let bsize: u32 = 512;

        // Max filename length is 255 characters.
        let namelen: u32 = 255;

        // Fragments are the min size that can be allocated to a file, which in our case is roughly a block.
        let frsize: u32 = 512;

        let stat: Statfs = Statfs {
            blocks,
            bfree,
            bavail,
            files,
            ffree,
            bsize,
            namelen,
            frsize,
        };

        Ok(stat)
    }

    // Extended attributes are not supported.
    // fn setxattr(
    //     &self,
    //     _req: fuse_mt::RequestInfo,
    //     _path: &std::path::Path,
    //     _name: &std::ffi::OsStr,
    //     _value: &[u8],
    //     _flags: u32,
    //     _position: u32,
    // ) -> fuse_mt::ResultEmpty {
    //     Err(libc::ENOSYS)
    // }

    // Extended attributes are not supported.
    // fn getxattr(
    //     &self,
    //     _req: fuse_mt::RequestInfo,
    //     _path: &std::path::Path,
    //     _name: &std::ffi::OsStr,
    //     _size: u32,
    // ) -> fuse_mt::ResultXattr {
    //     Err(libc::ENOSYS)
    // }

    // Extended attributes are not supported.
    // fn listxattr(
    //     &self,
    //     _req: fuse_mt::RequestInfo,
    //     _path: &std::path::Path,
    //     _size: u32,
    // ) -> fuse_mt::ResultXattr {
    //     Err(libc::ENOSYS)
    // }

    // Extended attributes are not supported.
    // fn removexattr(
    //     &self,
    //     _req: fuse_mt::RequestInfo,
    //     _path: &std::path::Path,
    //     _name: &std::ffi::OsStr,
    // ) -> fuse_mt::ResultEmpty {
    //     Err(libc::ENOSYS)
    // }

    // "This call is not required but is highly recommended." Okay then we wont do it muhahaha
    // fn access(
    //     &self,
    //     _req: fuse_mt::RequestInfo,
    //     _path: &std::path::Path,
    //     _mask: u32,
    // ) -> fuse_mt::ResultEmpty {
    //     Err(libc::ENOSYS)
    // }

    // Creates and opens a new file, returns a file handle.
    fn create(
        &self,
        req: fuse_mt::RequestInfo,
        parent: &std::path::Path,
        name: &std::ffi::OsStr,
        _mode: u32,
        flags: u32,
    ) -> fuse_mt::ResultCreate {
        debug!("Creating new file named `{}` in `{}`...", name.display(), parent.display());

        let task_handle = NotifyTui::start_task(TaskType::FilesystemCreateFile(name.display().to_string()), 5);

        // Extract the flags
        // Will bail if needed.
        let deduced_flags: ItemFlag = ItemFlag::deduce_flag(flags)?;

        // Is the name too long?
        if name.len() > 255 {
            debug!("File name is too long. Bailing.");
            NotifyTui::cancel_task(task_handle);
            return Err(FILE_NAME_TOO_LONG)
        }
        
        // Try and load in the parent directory
        // This will bail if a low level floppy issue happens.
        debug!("Attempting to load in the parent directory...");
        let mut containing_dir_block: DirectoryBlock = match DirectoryBlock::try_find_directory(Some(parent))? {
            Some(ok) => ok,
            None => {
                // Nope, no parent.
                warn!("Cannot create files in directories that do not exist.");
                NotifyTui::cancel_task(task_handle);
                return Err(NO_SUCH_ITEM)
            },
        };
        debug!("Directory loaded.");
        NotifyTui::complete_task_step(&task_handle);
        
        // Make sure the file does not already exist.
        debug!("Checking if file already exists...");
        let converted_name: String = name.to_str().expect("Should be valid UTF8.").to_string();
        // Will bail if needed.
        if let Some(exists) = containing_dir_block.find_item(&NamedItem::File(converted_name.clone()))? {
            NotifyTui::complete_task_step(&task_handle);
            debug!("File already exists.");
            // But do we care?
            if deduced_flags.contains(ItemFlag::CREATE_EXCLUSIVE) {
                // Yes we do, this is a failure.
                debug!("Caller wanted to create this file, not open it. Bailing.");
                NotifyTui::cancel_task(task_handle);
                return Err(ITEM_ALREADY_EXISTS)
            }
            
            // Since the file already exists we can skip the creation process.
            // just load it in as usual.
            
            // Full item path
            let constructed_path: &Path = &parent.join(name);
            
            // Dont care about the returned flags, they wont change anyways.
            let (file_handle, _): (u64, u32) = self.open(req, constructed_path, flags)?;
            NotifyTui::complete_task_step(&task_handle);
            
            // Get the innards of the handle
            let handle_inner: FileHandle = FileHandle::read(file_handle);
            NotifyTui::complete_task_step(&task_handle);

            // Truncate if needed (open(2) syscall)
            // Must be a file
            if deduced_flags.contains(ItemFlag::TRUNCATE) && !exists.flags.contains(DirectoryItemFlags::IsDirectory) {
                self.truncate(req, constructed_path, Some(file_handle), 0)?; // Truncate to 0
            }
            
            // Get the metadata from that
            debug!("Getting file attributes...");
            let facebook_data: FileAttr = handle_inner.try_into()?;
            NotifyTui::complete_task_step(&task_handle);
            
            // Put it all together
            // No idea what the TTL should be set to. I'm assuming that's how long the handles last?
            // I will never drop handles on my side, the OS has to drop em.
            debug!("Done reading in file, returning.");
            NotifyTui::finish_task(task_handle);
            return Ok(CreatedEntry {
                ttl: HANDLE_TIME_TO_LIVE,
                attr: facebook_data,
                fh: file_handle,
                flags,  // We use the same flags we came in with. Not the one from the loaded file.
                        // Is that a bad idea? No idea. Seems to work just fine.
            })
        }

        NotifyTui::complete_task_step(&task_handle);
        
        // File did not exist, actually creating it...
        debug!("Creating file...");
        let resulting_item: DirectoryItem = containing_dir_block.new_file(converted_name)?;
        NotifyTui::complete_task_step(&task_handle);
        debug!("Created file.");

        // Full item path
        let constructed_path: &Path = &parent.join(name);

        // Construct and return the handle to the new file
        let new_handle: FileHandle = FileHandle {
            path: constructed_path.into(),
        };

        // We can get attributes directly from the directory item we just made
        let attributes: FileAttr = resulting_item.try_into()?;
        NotifyTui::complete_task_step(&task_handle);

        // Allocate the handle for it
        let handle_num: u64 = new_handle.allocate();
        NotifyTui::complete_task_step(&task_handle);

        // Assemble it, and we're done!
        NotifyTui::finish_task(task_handle);
        debug!("Done creating file.");
        Ok(CreatedEntry {
            ttl: HANDLE_TIME_TO_LIVE,
            attr: attributes,
            fh: handle_num,
            flags,
        })
    }
}


================================================
FILE: src/filesystem/internal_filesystem_methods.rs
================================================
// For stuff like initialization and options.

//
//
// ======
// Imports
// ======
//
//

use std::path::PathBuf;

use log::debug;

use crate::filesystem::filesystem_struct::USE_TUI;
use crate::filesystem::filesystem_struct::WRITE_BACKUPS;
use crate::pool::pool_actions::pool_struct::Pool;
use crate::filesystem::filesystem_struct::FilesystemOptions;
use crate::filesystem::filesystem_struct::FlusterFS;
use crate::filesystem::filesystem_struct::FLOPPY_PATH;
use crate::filesystem::filesystem_struct::USE_VIRTUAL_DISKS;


//
//
// ======
// Implementations
// ======
//
//


// Filesystem option setup. Does not start filesystem.
impl FilesystemOptions {
    /// Initializes options for the filesystem, also configures the virtual disks if needed.
    pub fn new(use_virtual_disks: Option<PathBuf>, floppy_drive: PathBuf, backup: Option<bool>, enable_tui: bool) -> Self {
        debug!("Configuring file system options...");
        // Set the globals
        // set the floppy disk path
        debug!("Setting the floppy path...");
        debug!("Locking FLOPPY_PATH...");
        // There's no way anyone else has a lock on this or its poisoned at this point.
        *FLOPPY_PATH
            .try_lock()
            .expect("Fluster! Is single threaded.") = floppy_drive.clone();
        debug!("Done.");

        // Set the virtual disk flag if needed
        if let Some(path) = use_virtual_disks.clone() {
            debug!("Setting up virtual disks...");
            // Sanity checks
            // Make sure this is a directory, and that the directory already exists
            if !path.is_dir() || !path.exists() {
                // Why must you do this
                panic!("Virtual disk argument must be a valid path to a pre-existing directory.");
            }

            debug!("Locking USE_VIRTUAL_DISKS...");
            *USE_VIRTUAL_DISKS
                .try_lock()
                .expect("Fluster! Is single threaded.") = Some(path.to_path_buf());
            debug!("Done.");
        };

        // Disable backups if needed.
        // Backups default to being enabled.
        let enable_backup = backup.unwrap_or(true);
        debug!("Setting WRITE_BACKUPS...");
        WRITE_BACKUPS.set(enable_backup).expect("This should only ever be called once.");
        debug!("Done.");

        // Disable tui
        // TUI is enabled by default
        debug!("Setting USE_TUI...");
        USE_TUI.set(enable_tui).expect("This should only ever be called once.");
        debug!("Done.");


        debug!("Done configuring.");
        Self {
            use_virtual_disks,
            floppy_drive,
            enable_backup,
            enable_tui,
        }
    }
}

// Starting the filesystem.
impl FlusterFS {
    /// Create new filesystem handle, this will kick off the whole process of loading in information about the pool.
    /// Takes in options to configure the new pool.
    pub fn start(options: &FilesystemOptions) -> Self {
        debug!("Starting file system...");
        // Right now we dont use the options for anything, but they do initialize the globals we need, so we still need to pass it in.
        #[allow(dead_code)]
        #[allow(unused_variables)]
        let unused = options;
        let fs = FlusterFS { pool: Pool::load() };
        debug!("Done starting filesystem.");
        fs
    }
}

================================================
FILE: src/filesystem/item_flag/flag_struct.rs
================================================
use bitflags::bitflags;
use libc::c_int;
use log::warn;

//
//
// ======
// Flag type
// ======
//
//

// Flags are handled with bare u32 integers,
// hence we have a bitflag type to make dealing with them easier.

// Open documentation:
// https://man7.org/linux/man-pages/man2/openat.2.html
// The flags are in libc::

// When it says "Has no effect", I mean on the fluster side. Fluster just does not care
// about this flag being set or unset.

// I'm pretty sure that the read/write flags do not overlap. If they do I will split this into multiple types.

// All of the c flags are i32 for reasons unknown to me, so we have to cast all of them lol
// Not sure why the fuse_mt crate uses u32...

bitflags! {
    /// Flags that items have.
    #[derive(Debug, PartialEq, Eq, Clone, Copy)]
    pub(crate) struct ItemFlag: u32 {
        /// The file is opened in append mode.
        /// Before each write, the file offset is positioned at the end of the file.
        /// The modification of the file offset and write is done as one atomic step.
        const APPEND = libc::O_APPEND as u32;
        
        /// Async, fluster does not support this. Thus we will not
        /// add this bit to the flags.
        /// 
        /// Has no effect.
        const O_ASYNC = libc::O_ASYNC as u32;

        /// Has to do with closing when executing, ignoring, good luck.
        /// 
        /// Has no effect
        const O_CLOEXEC = libc::O_CLOEXEC as u32;

        /// If the path does not exist, create it as a regular file.
        const CREATE = libc::O_CREAT as u32;

        /// Has to do with direct IO. We don't really care, since we have no special
        /// handling for this kinda thing.
        /// 
        /// Has no effect.
        const O_DIRECT = libc::O_DIRECT as u32;

        /// Fail the open if the path is not a directory.
        const ASSERT_DIRECTORY = libc::O_DIRECTORY as u32;

        /// Has to do with data syncing. We do not care.
        /// 
        /// Has no effect
        const O_DSYNC = libc::O_DSYNC as u32;

        /// Ensure that call creates the file. if this is set and O_CREAT is also set, we're
        /// supposed to turn a EEXIST on open if path already exists.
        /// 
        /// O_EXCL is undefined if used without O_CREAT (unless pointing at block devices which fluster is not.)
        const CREATE_EXCLUSIVE = libc::O_EXCL as u32;
        
        /// Deals with filesizes with offsets that can be greated than off_t (I think that's 32 bit)
        /// 
        /// If you need files that big, fluster is not the tool for you.
        /// Has no effect.
        const O_LARGEFILE = libc::O_LARGEFILE as u32;
        
        /// Do not update file access time.
        /// 
        /// Cool, we don't support that anyways.
        /// 
        /// Has no effect.
        const O_NOATIME = libc::O_NOATIME as u32;

        /// If path is a terminal device, do not control it or whatever.
        /// 
        /// Fluster! does not have terminal devices.
        /// Has no effect.
        const O_NOCTTY = libc::O_NOCTTY as u32;

        /// Symbolic link related. We do not support links.
        // const O_NOFOLLOW = libc::O_NOFOLLOW;
        
        /// Open in non-blocking mode.
        /// Fluster is single threaded. EVERYTHING blocks dawg.
        /// 
        /// Has no effect.
        const O_NONBLOCK = libc::O_NONBLOCK as u32;
        const O_NDELAY = libc::O_NDELAY as u32; // Alternate name for same flag

        /// Dont follow symlinks.
        /// 
        /// Fluster does not have symlinks.
        /// Has no effect.
        const O_NOFOLLOW = libc::O_NOFOLLOW as u32; // Alternate name for same flag
        
        /// Gets file descriptor for this path but not the actual file.
        /// 
        /// Guess what buddy? you'll just get the whole file regardless.
        /// 
        /// Has no effect.
        const O_PATH = libc::O_PATH as u32;
        
        /// Do synchronized file I/O.
        /// 
        /// This is supposed to force sync to disk, but we are silly and don't care :)
        /// 
        /// Has no effect.
        const O_SYNC = libc::O_SYNC as u32;
        
        /// Creates unnamed tempfiles.
        /// 
        /// We do not support this.
        /// Has no effect.
        const O_TMPFILE  = libc::O_TMPFILE as u32;
        
        /// If the file already exists, truncate it.
        /// 
        /// There is already a truncate method on the filesystem, but this may get called elsewhere
        /// so we still need to care elsewhere.
        const TRUNCATE = libc::O_TRUNC as u32;


    }
}

/// Convert a flag to a u32 for use in returning.
impl From<ItemFlag> for u32 {
    fn from(value: ItemFlag) -> Self {
        value.bits()
    }
}

/// Tried to convert a u32 into a valid flag, returns an `Unsupported` error if a non-existent flag is set.
impl ItemFlag {
    pub fn deduce_flag(value: u32) -> Result<Self, c_int> {
        // All bits must be used. We need to know what they all are.
        if let Some(valid) = ItemFlag::from_bits(value) {
            // All good.
            Ok(valid)
        } else {
            // Has invalid bits set.
            // We will print some information to deduce the unused bits.
            warn!("Incoming flag bits had unused bits set. This operation is unimplemented.");
            warn!("Listing known and unknown flags:");

            warn!("Known:");
            let known = ItemFlag::from_bits_truncate(value);
            for name in known.iter_names() {
                warn!("`{}` with value `{}` (binary: `{:0>32b}`)", name.0, name.1.bits(), name.1.bits())
            }

            warn!("Unknown:");
            let unknown_bits = value & !ItemFlag::all().bits();
            warn!("{unknown_bits:0>32b}");
            // now print out the values of those bits
            warn!("Values for those bits:");
            for i in 0..32 {
                // shift over to mask out the bit
                let mask: u32 = 1 << i as u32;
                // if the bit is set, print the value
                if mask & unknown_bits != 0 {
                    warn!("{mask} (hex {mask:X})")
                }
            }

            // I've spent several hours trying to track down what 0x8000 is supposed to be a flag for, no luck.
            // I'm just going to assume its some internal flag at this point. We will ignore all flag bits that we dont know.

            warn!("Continuing anyways, but ignoring those bits may have side effects.");
            Ok(known)
        }
    }
}

================================================
FILE: src/filesystem/item_flag/mod.rs
================================================
pub(super) mod flag_struct;

================================================
FILE: src/filesystem/mod.rs
================================================
mod fuse_filesystem_methods;
mod internal_filesystem_methods;
pub mod filesystem_struct;
mod file_handle;
mod item_flag;
mod file_attributes;
pub mod disk_backup;

================================================
FILE: src/filesystem_design/allocation_spec.md
================================================
# Find and allocate blocks

We take in a number (u16) of blocks that the caller wishes to reserve.
We return a `Result<Vec<DiskPointer>, AllocationError>`

`AllocationError` contains types for block situations such as `NotEnoughSpace`, which requires the caller to add more disks to the pool.

### Note:
This operation does not flag blocks as used, this section is read only.
The caller is responsible for updating the allocation tables in the headers of disks they write to.

Finding the next block follows these steps:

## Scanning:
Before we can write any data, we need to ensure we have all the room for it.
This is the discovery phase, no data is written.

Terminology:
- `Start Disk`
- - The disk where the allocation of blocks begins. (Allocations of blocks always go upwards away from the pool disk).
- `Allocation length`
- - The number of blocks we wish to allocate.
- `Note`
- - Copy this value into memory off of the disk for later use.

### Process:
- Insert the pool disk, `Note`: `highest_known_disk`, `pool_blocks_free`, and `disk_with_next_free_block`.
- - If `disk_with_next_free_block` == u16::MAX:
- - - There are no more free blocks. We need another disk. Return `NotEnoughSpace`
- - If `pool_blocks_free` < `Allocation length`:
- - - There aren't enough free blocks. We need another disk. Return `NotEnoughSpace`
- Insert `pool_blocks_free`, hereafter referred to as the `Start disk`
- Goto `Find Blocks`
- If not enough space is found:
- - There aren't enough free blocks in the entire pool.
- - This should have been caught by `pool_blocks_free`.
- - An assertion will go here. We should never hit this branch.
- Update `pool_blocks_free` with how many blocks were allocated
- Update `disk_with_next_free_block`:
- - `Note`: Disk and Block numbers of the final allocated block
- - If the block number is the final block on the disk:
- - - Set `disk_with_next_free_block` to u16::MAX.
- - - Otherwise, set `disk_with_next_free_block` to the Disk of the final allocated block.



## Find Blocks
Incoming arguments:
- `Start Disk`
- - Disk number to start our search from.
- `Allocation length`
- - The number of blocks we wish to allocate.
- `End Disk`
- - The disk number of the final disk in the pool.

Returns:
- `Vec<DiskPointer>`

Terminology:
- Variable `Index`
- - The current disk we are examining. (Lies within range `Start Disk`..=`End Disk`)
- - Starts at `Start Disk`
- Variable `Free blocks seen`
- - Keeps track of how many free blocks we have seen across all disks up to this point.
- Variable `Block pointers`
- - A Vec of `<DiskPointer>`s to each free block we are considering.
- Variable `Blocks remaining`
- - A count of how many more blocks we need to allocate

### Note:
This section does not flag blocks as used, this section is read only.

### Process:
- Insert disk `Index`
- Count number of blocks free in allocation table.
- If the number of blocks free is >= `Blocks remaining`
- - Copy as many disk pointers into `Block pointers` as there are `Blocks remaining`, and return the pointers.
- Copy pointers to all of the free blocks into `Block pointers`, decrement `Blocks remaining` accordingly.
- If `Index` >= `End Disk`
- - There were not enough free blocks.
- - This should not be possible. Caller must guarantee that there is enough free space.
- - Assertion goes here.
- Increment `Index`
- Loop

================================================
FILE: src/filesystem_design/dense_disk.md
================================================
# Dense disk

Sometimes, you've got a really big file. And I mean REALLY big.

If a file is over the size of a full floppy, find out how many full floppies it can span and reserve those
the rest of the file will go in data blocks as usual

# Disk header format

The disk header lives on block 0 of every disk.

Header format:

| offset | length | Field                                                 |
| ------ | ------ | ----------------------------------------------------- |
| 0      | 8      | Magic number for identifying a fluster drive.Fluster! |
| 8      | 1      | Bitflags                                              |
| 9      | 2      | Disk number                                           |
| -      | -      | Reserved                                              |
| 148    | 360    | Block usage bitplane                                  |
| 509    | 4      | CRC                                                   |

Bitflags:

| bit | flag                                           |
| --- | ---------------------------------------------- |
| 0   | Reserved                                       |
| 1   | Reserved                                       |
| 2   | Reserved                                       |
| 3   | Reserved                                       |
| 4   | Reserved                                       |
| 5   | Reserved                                       |
| 7   | Reserved for Standard disks. Must never be set.|
| 6   | Marker bit, Must always be set.                |
| 8   | Reserved for Pool headers. Must never be set.  |

================================================
FILE: src/filesystem_design/design_choices.md
================================================
# Reasons for certain things

# Why 4 byte CRCs?
After 20MB of read-write with random head seeking, I only got 1 failed byte.
A 4 byte crc on our 512 byte block gives us a hamming distance of 6, which is probably even overkill unless
the floppy drive is actively being shaken by a pit bull who mistook it for a toddler.

# Why little endian?
Stack exchange said it was cool.

# What order are the bitflags in the documentation?
flag 0 is the least significant bit

# Why are some reads bigger than they need to be?
I was having an issue reading just 8 bytes into a buffer.
Turns out Windows wont let you read directly from a floppy disk into a buffer smaller than 512 bytes.
This took an annoyingly long time to figure out.

# Why do file extent blocks have a `bytes_free` field even though they arent dynamically allocated?
Ease of use.

# A lot of stuff seems wasteful cpu wise...
Think of it this way, 99% of the time we will be waiting for data from disk, so it evens out!

# Why is an entire disk dedicated to information about the pool?

Chances are, if you are using this filesystem, you are storing many files across many floppies.

Finding a file is a slow and tedious process. We have to start from the first disk and search, possibly swapping between many disks before finding the file we are seeking. Most of this overhead comes from looking up the location of the file inode, not loading the file itself.

Dedicating an entire disk to pool information lets us keep a cache of file locations, skipping the entire search process.
This will result in fewer disk swaps, and a massive speedup in search time.

# Why is the project laid out like that?
Originally, I didn't want to accidentally give access to private functions used for subsystems, but I ended up repeatedly dividing everything up until I was left with Pool::Disk::(Some disk type) then each disk implements its own innards, or uses generic functions from Pool::Disk.

Organizationally, I feel like it makes sense, but the amount of nesting is pretty wild.
This is my first time trying to keep a project organized in a sensible way, so... lol.

# Why are there so many comments?
I've had too many hobby projects in the pass where I've thought to myself, "The code is self documenting". Sure, that might be the case, but the amount of mental effort it takes to understand what's going on 3 days after I wrote something is a LOT higher than if I just left some comments.

I'd prefer too many comments over a headache trying to reverse-engineer what I was thinking previously.

Also it lowers the bar of entry for the casual viewer :D

# Why do some of the higher level abstractions use so many generic traits?
While writing this project, I learned more and more about traits, so I started using them because they're cool!

================================================
FILE: src/filesystem_design/disk_header.md
================================================
# Block layout

512 bytes in size
A floppy disk can hold 2880 blocks of this size.

# Header update situations:

New disk is created:

- Highest known disk has to be updated on the root disk (disk 0)

# Disk header format

The disk header lives on block 0 of every disk.

Header format:

| offset | length | Field                                                 |
| ------ | ------ | ----------------------------------------------------- |
| 0      | 8      | Magic number for identifying a fluster drive.Fluster! |
| 8      | 1      | Bitflags                                              |
| 9      | 2      | Disk number (u16)                                     |
| -      | -      | Reserved                                              |
| 148    | 360    | Block usage bitplane                                  |
| 509    | 4      | CRC                                                   |

Bitflags:

| bit | flag                                          |
| --- | --------------------------------------------- |
| 0   | Reserved                                      |
| 1   | Reserved                                      |
| 2   | Reserved                                      |
| 3   | Reserved                                      |
| 4   | Reserved                                      |
| 5   | Reserved                                      |
| 6   | Marker bit, Must always be set.               |
| 7   | Reserved for Dense disks.  Must never be set. |
| 8   | Reserved for Pool headers. Must never be set. |

8 bytes:
1 byte: bitflags
2 bytes: Disk number
138 bytes: Reserved
360 bytes: Block usage bitplane

Final 4 byte: crc


================================================
FILE: src/filesystem_design/disk_layout.md
================================================
# Disk Layout
Block 0: Disk header
// Only required on the origin disk
Block 1: Inode block
Block 2: Directory block

Unless its a dense disk,
dense disks only have the header.

Remaining blocks: any inode, directory, or data.


# Block types
Header (See `disk_header`)
Inode
Directory Data
File Extents
Data

# Data block
1 byte: bitflags
    0: Reserved for future use
    1: Reserved for future use
    2: Reserved for future use
    3: Reserved for future use
    4: Reserved for future use
    5: Reserved for future use
    6: Reserved for future use
    7: Reserved for future use

remaining bytes: raw data

final 4 bytes: CRC

# Directory block

Items on the directory block don't need to be in any
specific order, we do not index directly into these
blocks.

1 byte: bitflags
    0: This is the last directory block on the disk.
    1: Reserved for future use
    2: Reserved for future use
    3: Reserved for future use
    4: Reserved for future use
    5: Reserved for future use
    6: Reserved for future use
    7: Reserved for future use
2 bytes: number of free bytes
4 bytes: next directory block (disk pointer, we have no idea where the next directory could be.)
    - If u16:MAX then this is the end of the directory chain

remaining bytes: directory data

final 4 bytes: CRC

# File Extents block
1 byte: bitflags
    0: Reserved for future use
    1: Reserved for future use
    2: Reserved for future use
    3: Reserved for future use
    4: Reserved for future use
    5: Reserved for future use
    6: Reserved for future use
    7: Reserved for future use
2 bytes: number of free bytes
4 bytes: Next block
    - 2 Bytes: Disk number
    - 2 Bytes: Block on disk
    - if all 4 bytes are full 1's, this is the final block

remaining bytes: extent data

final 4 bytes: CRC

# Inode block
1 byte: bitflags
    0: This is the last inode block on the disk.
    1: Reserved for future use
    2: Reserved for future use
    3: Reserved for future use
    4: Reserved for future use
    5: Reserved for future use
    6: Reserved for future use
    7: Reserved for future use
2 bytes: number of free bytes
4 bytes: next directory block (disk pointer, we have no idea where the next directory could be.)
    - If u16:MAX then this is the end of the directory chain

remaining bytes: inode data

final 4 bytes: CRC

If you are on the final inode disk and realize you need to make another inode block, you update the
bitflag and reserve another block.
If you are out of blocks on that disk, go to the next disk if bit 1 is set.
If bit 1 is not set, then you can simply go to the next disk indicated. Otherwise you must find a _NEW_ disk
to put the next inode block on and update flags accordingly. (New disk inodes must be in the default position)

On disk 0, the first inode in the block MUST be a directory referencing `/` aka the root.

================================================
FILE: src/filesystem_design/inode_format.md
================================================
# Example Traversal

Lets find `/foo/bar.txt`

### Start at Root Inode
The location of the root inode is fixed (Disk 0, Block 1, Slot 0).
Read the Inode at this address. It's a Directory type.

### Find Root's Directory Data
From the root Inode, get the pointer to its Directory block.

### Scan Root's Directory Data for `foo`
Read that DirectoryDataBlock.
Search its children map for the key `foo`.
If found, you get the InodeAddress for `foo`.
If not found and there's a next_block pointer, follow the chain and repeat the search.

### Find `foo`'s Directory Data
Read the Inode at `foo`'s address. It's also a Directory type.
From this Inode, get the pointer to its Directory block.

### Scan `foo`'s Directory Data for `bar.txt`
Read the DirectoryDataBlock for foo.
Search its children map for the key `bar.txt`.
If found, you get the InodeAddress for `bar.txt`.

### Find `bar.txt`'s Extents
Read the Inode at `bar.txt`'s address. It's a File type.
From this Inode, get the pointer to its first_extent_block.

### Read File Data
Read that FileExtentBlock to get the list of FileExtents,
which finally tell you which blocks on which disks hold the actual file data.
Extents in this file are in order.


# Inode format
1 byte: bitflags
    - 0: File type (0 directory, 1 file)
    - 1: Reserved for future use
    - 2: Reserved for future use
    - 3: Reserved for future use
    - 4: Reserved for future use
    - 5: Reserved for future use
    - 6: Reserved for future use
    - 7: Marker bit (Always set)
4-12 bytes: Inode data
    * File:
        - 8 bytes for size
        - 4 bytes for pointer to the File Extents block
            - 2 Bytes: Disk number
            - 2 Bytes: Block on disk
    * Directory:
        - 4 bytes for pointer to Directory Data block
            - 2 Bytes: Disk number
            - 2 Bytes: Block on disk
12 bytes: Created timestamp
    - 8 bytes: Seconds since epoch
    - 4 bytes: nanosecond offset
12 bytes: Modified timestamp
    - 8 bytes: Seconds since epoch
    - 4 bytes: nanosecond offset


# Inode block
see `disk_layout`

# Directory block
see `disk_layout`


# Directory item format
1 byte: bitflags
    0: Inode is on this disk
    1: Reserved for future use
    2: Reserved for future use
    3: Reserved for future use
    4: Reserved for future use
    5: Reserved for future use
    6: Reserved for future use
    7: Marker bit (Always set)
1 byte: length of item name
? bytes: item name
3-5 bytes: inode location
    - 2 Bytes: Disk number (Not included if flag set)
    - 2 Bytes: Block on disk
    - 1 Byte: Index into inode block



# File Extents block
see `disk_layout`

# Extent format
bitflag considerations:
- You cannot have a local dense-disk.

1 byte: bitflags
    0: This extent is a dense-disk
    1: The block is on this disk
    2: Reserved for future use
    3: Reserved for future use
    4: Reserved for future use
    5: Reserved for future use
    6: Reserved for future use
    7: Marker bit (Always set)
2-5 Bytes: extent information
    - 2 Bytes: Disk number (Not included if block is local)
    - 2 Bytes: Start block (Not included if dense)
    - 1 Byte: Length (Not included if dense)


================================================
FILE: src/filesystem_design/pool_header.md
================================================
# Pool header

The root disk only holds information about the pool. Blocks cannot be stored to this disk.

| Offset | Length | Field                                                                                          |
| ------ | ------ | ---------------------------------------------------------------------------------------------- |
| 0      | 8      | Magic number for idenifying a fluster drive `Fluster!`                                         |
| 8      | 1      | Bitflags                                                                                       |
| 9      | 2      | Highest known disk number.                                                                     |
| 11     | 2      | Disk with the next free block in the pool.<br />Set to u16::MAX if the final disk has no room. |
| 13     | 4      | Number of blocks free across all disks in the pool.                                            |
| -      | -      | Reserved                                                                                       |
| 148    | 360    | Block usage bitplane                                                                           |
| 509    | 4      | Block CRC                                                                                      |

Bitflags:

| bit | flag                                      |
| --- | ----------------------------------------- |
| 0   | Reserved                                  |
| 1   | Reserved                                  |
| 2   | Reserved                                  |
| 3   | Reserved                                  |
| 4   | Reserved                                  |
| 5   | Reserved                                  |
| 6   | Reserved                                  |
| 7   | Reserved                                  |
| 8   | Marks this as a pool header. Must be set. |


================================================
FILE: src/filesystem_design/pool_layout.md
================================================
# The pool

The pool will be our highest level of abstraction on top of the disks, every action against the underlying disks should be done through the pool.

================================================
FILE: src/filesystem_design/possible_speed_improvments.md
================================================
# Speed improvement ideas

# Disk pre-seek
If we know we are about to change disks, is it possible to pre-align the head of the drive
to the next block we will read on the next disk while the user swaps?

# In-memory inode cache
This might be practically mandatory to get any usability out of the file system unless
we want to be swapping tons of disks for every read operation.

You should be able to enable or disable this on the fly if you want.

================================================
FILE: src/helpers/hex_view.rs
================================================
// Take in a vec of bytes and return a hex view of it

pub fn hex_view(bytes: Vec<u8>) -> String {
    let mut offset = 0;
    let bytes_length = bytes.len();

    let mut screen_string = String::new();

    // push the header
    screen_string.push_str(" Offset(h)  00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n");

    while offset < bytes_length {
        // make the line
        let mut line = String::new();
        // first goes the offset, padded so its 10 characters long
        line.push_str(&format!("{offset:0>10X}  "));
        // now for all the numbers
        for i in 0..16 {
            // skip if we are outside of range
            if offset + i >= bytes_length {
                line.push_str("  ");
            } else {
                let byte = bytes[offset + i];
                let byte_component = format!("{byte:02X} ");
                line.push_str(&byte_component);
            }
        }

        // now for the text version
        line.push(' ');
        for i in 0..16 {
            let mut character: char;
            if offset + i >= bytes_length {
                character = ' ';
            } else {
                // convert
                let byte = bytes[offset + i];
                character = char::from_u32(byte as u32).unwrap_or('?');
                // unless:
                if !character.is_ascii() || character.is_ascii_control() {
                    character = '.';
                }
            }

            line.push(character);
        }

        // line is done. Add it to the screen
        screen_string.push_str(&line);
        screen_string.push('\n');

        // Now increment the offset
        offset += 16;
    }

    // done!
    screen_string
}


================================================
FILE: src/helpers/mod.rs
================================================
pub mod hex_view;


================================================
FILE: src/lib.rs
================================================
// The library/filesystem cannot use unwraps.
#![deny(clippy::unwrap_used)]

// Asserts need to have a reason.
#![deny(clippy::missing_assert_message)]

// Gotta use all the results.
#![deny(unused_results)]
// I need to force some methods to only be used in special places.
// Doing the publicity for it would be a pain, so we just piggyback on
// depreciated
#![deny(deprecated)]

// Only use the filesystem in main.rs
// We only support 64 bit systems. since we expect usize to be that size.
#[cfg(target_pointer_width = "64")]
pub mod filesystem;

// Within the crate, we can use:
mod helpers;
mod error_types;
mod pool;
pub mod tui;

================================================
FILE: src/main.rs
================================================
use std::{
    ffi::OsStr,
    path::PathBuf,
    sync::{
        atomic::{
            AtomicBool,
            Ordering
        },
        Arc
    },
    thread,
    time::Duration
};

use clap::Parser;
use fluster_fs::{
    filesystem::filesystem_struct::{
        FilesystemOptions,
        FlusterFS
    },
    tui::notify::TUI_MANAGER
};

// Logging
use env_logger::Env;
use ratatui::{
    crossterm::{
        execute,
        terminal::{
            disable_raw_mode,
            enable_raw_mode,
            EnterAlternateScreen,
            LeaveAlternateScreen
        }
    },
    prelude::CrosstermBackend,
    Terminal
};

use tui_logger::TuiLoggerFile;

#[derive(Parser)]
struct Cli {
    /// Path to the floppy bloc
Download .txt
gitextract_wp7ph85n/

├── .gitignore
├── .vscode/
│   └── settings.json
├── Cargo.toml
├── LICENSE.txt
├── build.rs
├── readme.md
├── src/
│   ├── error_types/
│   │   ├── block.rs
│   │   ├── conversions.rs
│   │   ├── critical.rs
│   │   ├── drive.rs
│   │   ├── filesystem.rs
│   │   ├── header.rs
│   │   └── mod.rs
│   ├── filesystem/
│   │   ├── disk_backup/
│   │   │   ├── mod.rs
│   │   │   ├── restore.rs
│   │   │   └── update.rs
│   │   ├── file_attributes/
│   │   │   ├── conversion.rs
│   │   │   └── mod.rs
│   │   ├── file_handle/
│   │   │   ├── file_handle_methods.rs
│   │   │   ├── file_handle_struct.rs
│   │   │   └── mod.rs
│   │   ├── filesystem_struct.rs
│   │   ├── fuse_filesystem_methods.rs
│   │   ├── internal_filesystem_methods.rs
│   │   ├── item_flag/
│   │   │   ├── flag_struct.rs
│   │   │   └── mod.rs
│   │   └── mod.rs
│   ├── filesystem_design/
│   │   ├── allocation_spec.md
│   │   ├── dense_disk.md
│   │   ├── design_choices.md
│   │   ├── disk_header.md
│   │   ├── disk_layout.md
│   │   ├── inode_format.md
│   │   ├── pool_header.md
│   │   ├── pool_layout.md
│   │   └── possible_speed_improvments.md
│   ├── helpers/
│   │   ├── hex_view.rs
│   │   └── mod.rs
│   ├── lib.rs
│   ├── main.rs
│   ├── pool/
│   │   ├── disk/
│   │   │   ├── blank_disk/
│   │   │   │   ├── blank_disk_methods.rs
│   │   │   │   ├── blank_disk_struct.rs
│   │   │   │   └── mod.rs
│   │   │   ├── drive_methods.rs
│   │   │   ├── drive_struct.rs
│   │   │   ├── generic/
│   │   │   │   ├── block/
│   │   │   │   │   ├── allocate/
│   │   │   │   │   │   ├── block_allocation.rs
│   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   └── tests.rs
│   │   │   │   │   ├── block_structs.rs
│   │   │   │   │   ├── crc.rs
│   │   │   │   │   └── mod.rs
│   │   │   │   ├── disk_trait.rs
│   │   │   │   ├── generic_structs/
│   │   │   │   │   ├── find_space.rs
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   └── pointer_struct.rs
│   │   │   │   ├── io/
│   │   │   │   │   ├── cache/
│   │   │   │   │   │   ├── cache_implementation.rs
│   │   │   │   │   │   ├── cache_io.rs
│   │   │   │   │   │   ├── cached_allocation.rs
│   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   └── statistics.rs
│   │   │   │   │   ├── checked_io.rs
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   ├── read.rs
│   │   │   │   │   ├── wipe.rs
│   │   │   │   │   └── write.rs
│   │   │   │   └── mod.rs
│   │   │   ├── mod.rs
│   │   │   ├── pool_disk/
│   │   │   │   ├── block/
│   │   │   │   │   ├── header/
│   │   │   │   │   │   ├── header_methods.rs
│   │   │   │   │   │   ├── header_struct.rs
│   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   └── tests.rs
│   │   │   │   │   └── mod.rs
│   │   │   │   ├── mod.rs
│   │   │   │   ├── pool_disk_methods.rs
│   │   │   │   └── pool_disk_struct.rs
│   │   │   ├── standard_disk/
│   │   │   │   ├── block/
│   │   │   │   │   ├── directory/
│   │   │   │   │   │   ├── directory_methods.rs
│   │   │   │   │   │   ├── directory_struct.rs
│   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   └── tests.rs
│   │   │   │   │   ├── file_extents/
│   │   │   │   │   │   ├── file_extents_methods.rs
│   │   │   │   │   │   ├── file_extents_struct.rs
│   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   └── tests.rs
│   │   │   │   │   ├── header/
│   │   │   │   │   │   ├── header_methods.rs
│   │   │   │   │   │   ├── header_struct.rs
│   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   └── tests.rs
│   │   │   │   │   ├── inode/
│   │   │   │   │   │   ├── inode_methods.rs
│   │   │   │   │   │   ├── inode_struct.rs
│   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   └── tests.rs
│   │   │   │   │   ├── io/
│   │   │   │   │   │   ├── directory/
│   │   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   │   ├── movement.rs
│   │   │   │   │   │   │   ├── read.rs
│   │   │   │   │   │   │   ├── tests.rs
│   │   │   │   │   │   │   ├── types.rs
│   │   │   │   │   │   │   └── write.rs
│   │   │   │   │   │   ├── file/
│   │   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   │   ├── movement.rs
│   │   │   │   │   │   │   ├── read.rs
│   │   │   │   │   │   │   ├── tests.rs
│   │   │   │   │   │   │   └── write.rs
│   │   │   │   │   │   ├── inode/
│   │   │   │   │   │   │   ├── mod.rs
│   │   │   │   │   │   │   ├── read.rs
│   │   │   │   │   │   │   ├── tests.rs
│   │   │   │   │   │   │   └── write.rs
│   │   │   │   │   │   └── mod.rs
│   │   │   │   │   └── mod.rs
│   │   │   │   ├── mod.rs
│   │   │   │   ├── standard_disk_methods.rs
│   │   │   │   └── standard_disk_struct.rs
│   │   │   └── unknown_disk/
│   │   │       ├── mod.rs
│   │   │       ├── unknown_disk_methods.rs
│   │   │       └── unknown_disk_struct.rs
│   │   ├── io/
│   │   │   ├── allocate.rs
│   │   │   └── mod.rs
│   │   ├── mod.rs
│   │   └── pool_actions/
│   │       ├── mod.rs
│   │       ├── pool_methods.rs
│   │       └── pool_struct.rs
│   └── tui/
│       ├── layout.rs
│       ├── mod.rs
│       ├── notify.rs
│       ├── prompts.rs
│       ├── state.rs
│       ├── tasks.rs
│       └── tui_struct.rs
├── tests/
│   ├── directory.rs
│   ├── file.rs
│   ├── mount_filesystem.rs
│   ├── start_filesystem.rs
│   └── test_common.rs
└── windows.md
Download .txt
SYMBOL INDEX (593 symbols across 82 files)

FILE: build.rs
  function main (line 1) | fn main() {

FILE: src/error_types/block.rs
  type BlockManipulationError (line 6) | pub enum BlockManipulationError {

FILE: src/error_types/conversions.rs
  type CannotConvertError (line 35) | pub enum CannotConvertError {
  type Error (line 46) | type Error = CannotConvertError;
  method try_from (line 48) | fn try_from(value: DriveIOError) -> Result<Self, Self::Error> {
  method wrap (line 63) | pub(crate) fn wrap(io_error: std::io::Error, error_origin: DiskPointer) ...
  type Error (line 76) | type Error = CannotConvertError;
  method try_from (line 78) | fn try_from(value: WrappedIOError) -> Result<Self, Self::Error> {

FILE: src/error_types/critical.rs
  type CriticalError (line 30) | pub enum CriticalError {
    method handle (line 61) | pub(crate) fn handle(self) {
  type RetryCapError (line 39) | pub enum RetryCapError {
  function go_handle_critical (line 67) | fn go_handle_critical(error: CriticalError) {
  function handle_drive_inaccessible (line 102) | fn handle_drive_inaccessible(reason: InvalidDriveReason) -> bool {
  function handle_out_of_retries (line 166) | fn handle_out_of_retries(reason:RetryCapError) -> bool {
  function troubleshooter (line 183) | fn troubleshooter() -> bool {
  function check_disk (line 312) | fn check_disk() -> bool {
  function update_drive_path (line 381) | fn update_drive_path() {
  function do_remount (line 451) | fn do_remount() {
  function do_disk_restore (line 467) | fn do_disk_restore() {
  function inform_improper_floppy_drive (line 539) | fn inform_improper_floppy_drive() -> bool {
  function troubleshooter_finished (line 583) | fn troubleshooter_finished() {

FILE: src/error_types/drive.rs
  type DriveError (line 12) | pub enum DriveError {
  type DriveIOError (line 25) | pub enum DriveIOError {
  type WrappedIOError (line 31) | pub struct WrappedIOError {
  type InvalidDriveReason (line 42) | pub enum InvalidDriveReason {

FILE: src/error_types/filesystem.rs
  constant FILE_NAME_TOO_LONG (line 19) | pub(in super::super) const FILE_NAME_TOO_LONG: c_int = libc::ENAMETOOLONG;
  constant DIRECTORY_NOT_EMPTY (line 21) | pub(in super::super) const DIRECTORY_NOT_EMPTY: c_int = libc::ENOTEMPTY;
  constant ITEM_ALREADY_EXISTS (line 23) | pub(in super::super) const ITEM_ALREADY_EXISTS: c_int = libc::EEXIST;
  constant NOT_A_DIRECTORY (line 25) | pub(in super::super) const NOT_A_DIRECTORY: c_int = libc::ENOTDIR;
  constant INVALID_ARGUMENT (line 27) | pub(in super::super) const INVALID_ARGUMENT: c_int = libc::EINVAL;
  constant IS_A_DIRECTORY (line 29) | pub(in super::super) const IS_A_DIRECTORY: c_int = libc::EISDIR;
  constant NO_SUCH_ITEM (line 35) | pub(in super::super) const NO_SUCH_ITEM: c_int = libc::ENOENT;
  constant STALE_HANDLE (line 39) | pub(in super::super) const STALE_HANDLE: c_int = libc::ESTALE;
  constant GENERIC_FAILURE (line 41) | pub(in super::super) const GENERIC_FAILURE: c_int = libc::EIO;
  constant TRY_AGAIN (line 45) | pub(in super::super) const TRY_AGAIN: c_int = libc::ERESTART;
  constant BUSY (line 49) | pub(in super::super) const BUSY: c_int = libc::EBUSY;
  method from (line 52) | fn from(value: DriveError) -> Self {

FILE: src/error_types/header.rs
  type HeaderError (line 6) | pub enum HeaderError {

FILE: src/filesystem/disk_backup/restore.rs
  function restore_disk (line 15) | pub fn restore_disk(number: u16) -> bool {

FILE: src/filesystem/disk_backup/update.rs
  function update_backup (line 13) | pub(crate) fn update_backup(block: &RawBlock) {
  function large_update_backup (line 63) | pub(crate) fn large_update_backup(start: DiskPointer, data: &[u8]) {

FILE: src/filesystem/file_attributes/conversion.rs
  type Error (line 16) | type Error = c_int;
  method try_from (line 18) | fn try_from(value: FileHandle) -> Result<Self, Self::Error> {
  type Error (line 28) | type Error = DriveError;
  method try_from (line 30) | fn try_from(value: DirectoryItem) -> Result<Self, Self::Error> {
  function go_get_metadata (line 35) | fn go_get_metadata(item: DirectoryItem) -> Result<FileAttr, DriveError> {

FILE: src/filesystem/file_handle/file_handle_methods.rs
  type LoveHandles (line 13) | struct LoveHandles {
    method new (line 24) | fn new() -> Self {
    method make_handle (line 34) | fn make_handle(&mut self, item: FileHandle) -> u64 {
    method read_handle (line 47) | fn read_handle(&self, number: u64) -> FileHandle {
    method next_free (line 60) | fn next_free(&mut self) -> u64 {
    method release_handle (line 74) | fn release_handle(&mut self, number: u64) {
  method name (line 122) | pub fn name(&self) -> &str {
  method allocate (line 137) | pub fn allocate(self) -> u64 {
  method read (line 147) | pub fn read(handle: u64) -> Self {
  method drop_handle (line 156) | pub fn drop_handle(handle: u64) {
  method is_file (line 164) | pub fn is_file(&self) -> Result<Option<bool>, c_int> {
  method get_directory_item (line 210) | pub fn get_directory_item(&self) -> Result<DirectoryItem, c_int> {
  method get_named_item (line 239) | pub(crate) fn get_named_item(&self) -> Result<Option<NamedItem>, c_int> {

FILE: src/filesystem/file_handle/file_handle_struct.rs
  type FileHandle (line 17) | pub(crate) struct FileHandle {

FILE: src/filesystem/filesystem_struct.rs
  type FlusterFS (line 12) | pub struct FlusterFS {
  type FilesystemOptions (line 36) | pub struct FilesystemOptions {

FILE: src/filesystem/fuse_filesystem_methods.rs
  constant HANDLE_TIME_TO_LIVE (line 52) | const HANDLE_TIME_TO_LIVE: Duration = Duration::from_secs(365*24*60*60);
  method init (line 73) | fn init(&self, _req: fuse_mt::RequestInfo) -> fuse_mt::ResultEmpty {
  method destroy (line 102) | fn destroy(&self) {
  method getattr (line 123) | fn getattr(
  method truncate (line 197) | fn truncate(
  method mkdir (line 288) | fn mkdir(
  method unlink (line 348) | fn unlink(
  method rmdir (line 405) | fn rmdir(
  method rename (line 480) | fn rename(
  method open (line 1105) | fn open(
  method read (line 1234) | fn read(
  method write (line 1344) | fn write(
  method flush (line 1414) | fn flush(
  method release (line 1431) | fn release(
  method fsync (line 1445) | fn fsync(
  method opendir (line 1456) | fn opendir(
  method readdir (line 1479) | fn readdir(
  method releasedir (line 1572) | fn releasedir(
  method fsyncdir (line 1584) | fn fsyncdir(
  method statfs (line 1596) | fn statfs(&self, _req: fuse_mt::RequestInfo, _path: &std::path::Path) ->...
  method create (line 1708) | fn create(

FILE: src/filesystem/internal_filesystem_methods.rs
  method new (line 36) | pub fn new(use_virtual_disks: Option<PathBuf>, floppy_drive: PathBuf, ba...
  method start (line 93) | pub fn start(options: &FilesystemOptions) -> Self {

FILE: src/filesystem/item_flag/flag_struct.rs
  function from (line 138) | fn from(value: ItemFlag) -> Self {
  method deduce_flag (line 145) | pub fn deduce_flag(value: u32) -> Result<Self, c_int> {

FILE: src/helpers/hex_view.rs
  function hex_view (line 3) | pub fn hex_view(bytes: Vec<u8>) -> String {

FILE: src/main.rs
  type Cli (line 43) | struct Cli {
  function main (line 62) | fn main() {

FILE: src/pool/disk/blank_disk/blank_disk_methods.rs
  method unchecked_read_block (line 26) | fn unchecked_read_block(&self, _block_number: u16) -> Result<RawBlock, D...
  method unchecked_write_block (line 32) | fn unchecked_write_block(&mut self, block: &RawBlock) -> Result<(), Driv...
  method disk_file (line 38) | fn disk_file(self) -> File {
  method get_disk_number (line 43) | fn get_disk_number(&self) -> u16 {
  method set_disk_number (line 52) | fn set_disk_number(&mut self, _disk_number: u16) -> () {
  method disk_file_mut (line 59) | fn disk_file_mut(&mut self) -> &mut File {
  method flush (line 64) | fn flush(&mut self) -> Result<(), DriveError> {
  method unchecked_write_large (line 71) | fn unchecked_write_large(&mut self, data:Vec<u8>, start_block:DiskPointe...
  method unchecked_read_multiple_blocks (line 77) | fn unchecked_read_multiple_blocks(&self, _block_number: u16, _num_block_...
  method new (line 84) | pub fn new(file: File) -> Self {
  method get_allocation_table (line 91) | fn get_allocation_table(&self) ->  &[u8] {
  method set_allocation_table (line 96) | fn set_allocation_table(&mut self, _new_table: &[u8]) -> Result<(), Driv...

FILE: src/pool/disk/blank_disk/blank_disk_struct.rs
  type BlankDisk (line 4) | pub struct BlankDisk {

FILE: src/pool/disk/drive_methods.rs
  method open_direct (line 60) | pub fn open_direct(disk_number: u16) -> Result<DiskType, DriveError> {
  method open (line 67) | pub fn open(disk_number: u16) -> Result<DiskType, DriveError> {
  method get_blank_disk (line 72) | pub fn get_blank_disk(disk_number: u16) -> Result<BlankDisk, DriveError> {
  method currently_inserted_disk_number (line 77) | pub fn currently_inserted_disk_number() -> u16 {
  function open_and_deduce_disk (line 84) | fn open_and_deduce_disk(disk_number: u16, new_disk: bool) -> Result<Disk...
  function get_floppy_drive_file (line 148) | fn get_floppy_drive_file(disk_number: u16, new_disk: bool) -> Result<Fil...
  function check_for_magic (line 303) | pub fn check_for_magic(block_bytes: &[u8]) -> bool {
  function prompt_for_disk (line 312) | fn prompt_for_disk(disk_number: u16) -> Result<DiskType, DriveError> {
  function prompt_for_blank_disk (line 395) | fn prompt_for_blank_disk(disk_number: u16) -> Result<BlankDisk, DriveErr...
  function display_info_and_ask_wipe (line 451) | pub fn display_info_and_ask_wipe(disk: &mut DiskType) -> Result<(), Driv...

FILE: src/pool/disk/drive_struct.rs
  type FloppyDrive (line 23) | pub struct FloppyDrive {
  type DiskType (line 31) | pub enum DiskType {
  type DiskBootstrap (line 62) | pub trait DiskBootstrap {
    method bootstrap (line 66) | fn bootstrap(file: File, disk_number: u16) -> Result<Self, DriveError>
    method from_header (line 70) | fn from_header(block: RawBlock, file: File) -> Self;

FILE: src/pool/disk/generic/block/allocate/block_allocation.rs
  type BlockAllocation (line 27) | pub trait BlockAllocation {
    method get_allocation_table (line 29) | fn get_allocation_table(&self) -> &[u8];
    method set_allocation_table (line 32) | fn set_allocation_table(&mut self, new_table: &[u8]) -> Result<(), Dri...
    method find_free_blocks (line 36) | fn find_free_blocks(&self, blocks: u16) -> Result<Vec<u16>, u16> {
    method allocate_blocks (line 42) | fn allocate_blocks(&mut self, blocks: &Vec<u16>) -> Result<u16, DriveE...
    method free_blocks (line 48) | fn free_blocks(&mut self, blocks: &Vec<u16>) -> Result<u16, DriveError> {
    method is_block_allocated (line 53) | fn is_block_allocated(&self, block_number: u16) -> bool {
  function go_find_free_blocks (line 58) | fn go_find_free_blocks<T: BlockAllocation + ?Sized>(
  function go_allocate_or_free_blocks (line 95) | fn go_allocate_or_free_blocks<T: BlockAllocation + ?Sized>(
  function go_check_block_allocated (line 154) | fn go_check_block_allocated<T: BlockAllocation + ?Sized>(caller: &T, blo...

FILE: src/pool/disk/generic/block/allocate/tests.rs
  function allocate_and_free_one_block (line 17) | fn allocate_and_free_one_block() {
  function oversized_allocation (line 42) | fn oversized_allocation() {
  function saturate_table (line 52) | fn saturate_table() {
  function marking (line 94) | fn marking() {
  type TestTable (line 111) | struct TestTable {
    method new (line 116) | fn new() -> Self {
  method get_allocation_table (line 124) | fn get_allocation_table(&self) -> &[u8] {
  method set_allocation_table (line 128) | fn set_allocation_table(&mut self, new_table: &[u8]) -> Result<(), Drive...

FILE: src/pool/disk/generic/block/block_structs.rs
  type RawBlock (line 12) | pub struct RawBlock {

FILE: src/pool/disk/generic/block/crc.rs
  function check_crc (line 5) | pub fn check_crc(block: [u8; 512]) -> bool {
  function compute_crc (line 12) | pub fn compute_crc(bytes: &[u8]) -> [u8; 4] {
  function add_crc_to_block (line 18) | pub(crate) fn add_crc_to_block(block: &mut [u8; 512]) {

FILE: src/pool/disk/generic/disk_trait.rs
  type GenericDiskMethods (line 21) | pub trait GenericDiskMethods {
    method unchecked_read_block (line 24) | fn unchecked_read_block(&self, block_number: u16) -> Result<RawBlock, ...
    method unchecked_read_multiple_blocks (line 28) | fn unchecked_read_multiple_blocks(&self, block_number: u16, num_block_...
    method unchecked_write_block (line 31) | fn unchecked_write_block(&mut self, block: &RawBlock) -> Result<(), Dr...
    method unchecked_write_large (line 34) | fn unchecked_write_large(&mut self, data: Vec<u8>, start_block: DiskPo...
    method disk_file (line 37) | fn disk_file(self) -> File;
    method disk_file_mut (line 40) | fn disk_file_mut(&mut self) -> &mut File;
    method get_disk_number (line 43) | fn get_disk_number(&self) -> u16;
    method set_disk_number (line 46) | fn set_disk_number(&mut self, disk_number: u16);
    method flush (line 50) | fn flush(&mut self) -> Result<(), DriveError>;

FILE: src/pool/disk/generic/generic_structs/find_space.rs
  type BytePingPong (line 4) | pub trait BytePingPong {
    method to_bytes (line 6) | fn to_bytes(&self) -> Vec<u8>;
    method from_bytes (line 9) | fn from_bytes(bytes: &[u8]) -> Self;
  function find_free_space (line 16) | pub fn find_free_space<T: BytePingPong>(data: &[u8], requested_space: us...

FILE: src/pool/disk/generic/generic_structs/pointer_struct.rs
  type DiskPointer (line 4) | pub(crate) struct DiskPointer {
    method to_bytes (line 10) | pub(crate) fn to_bytes(self) -> [u8; 4] {
    method from_bytes (line 16) | pub(crate) fn from_bytes(bytes: [u8; 4]) -> Self {
    method get_random (line 24) | pub(crate) fn get_random() -> Self {
    method new_final_pointer (line 33) | pub(crate) fn new_final_pointer() -> Self {
    method no_destination (line 40) | pub(crate) fn no_destination(&self) -> bool {

FILE: src/pool/disk/generic/io/cache/cache_implementation.rs
  constant CACHE_SIZE (line 74) | const CACHE_SIZE: usize = 2880 * 2;
  constant CACHE_SIZE (line 76) | const CACHE_SIZE: usize = 2880 * 16;
  type BlockCache (line 92) | pub(super) struct BlockCache {
    method new (line 140) | fn new() -> Self {
    method try_find (line 159) | pub(super) fn try_find(pointer: DiskPointer) -> Option<CachedBlock> {
    method try_find_silent (line 164) | pub(super) fn try_find_silent(pointer: DiskPointer) -> Option<CachedBl...
    method add_or_update_item (line 173) | pub(super) fn add_or_update_item(item: CachedBlock) -> Result<(), Driv...
    method get_hit_rate (line 178) | pub(super) fn get_hit_rate() -> f64 {
    method get_pressure (line 185) | pub(super) fn get_pressure() -> f64 {
    method promote_item (line 190) | fn promote_item(&mut self, item: CachedBlock) {
    method remove_item (line 199) | pub(super) fn remove_item(pointer: &DiskPointer) {
    method flush (line 216) | pub(super) fn flush(tier_number: usize) -> Result<(), DriveError> {
    method cleanup_tier (line 228) | pub(super) fn cleanup_tier(tier_number: usize) -> Option<u64> {
    method flush_a_disk (line 241) | pub(super) fn flush_a_disk(disk_number: u16) -> Result<u64, DriveError> {
    method most_common_disk (line 256) | pub(super) fn most_common_disk() -> (u16, u16) {
    method get_tier_space (line 263) | pub(super) fn get_tier_space(tier_number: usize) -> usize {
  type TieredCache (line 107) | struct TieredCache {
    method new (line 271) | fn new(size: usize) -> Self {
    method find_item (line 282) | fn find_item(&self, pointer: &DiskPointer) -> Option<usize> {
    method get_item (line 292) | fn get_item(&mut self, index: usize, silent: bool) -> Option<&CachedBl...
    method extract_item (line 298) | fn extract_item(&mut self, index: usize) -> Option<CachedBlock> {
    method add_item (line 304) | fn add_item(&mut self, item: CachedBlock) {
    method update_item (line 312) | fn update_item(&mut self, index: usize, new_item: CachedBlock) {
    method get_best (line 318) | fn get_best(&mut self) -> Option<CachedBlock> {
    method get_worst (line 324) | fn get_worst(&mut self) -> Option<CachedBlock> {
    method is_full (line 328) | fn is_full(&self) -> bool {
  type CachedBlock (line 119) | pub(super) struct CachedBlock {
    method into_raw (line 336) | pub(super) fn into_raw(self) -> RawBlock {
    method from_raw (line 345) | pub(super) fn from_raw(block: &RawBlock, requires_flush: bool) -> Self {
  function go_try_find_cache (line 360) | fn go_try_find_cache(pointer: DiskPointer, silent: bool) -> Option<Cache...
  function go_promote_item_cache (line 411) | fn go_promote_item_cache(cache: &mut BlockCache, t0_item: CachedBlock) {
  function go_add_or_update_item_cache (line 452) | fn go_add_or_update_item_cache(block: CachedBlock) -> Result<(), DriveEr...
  function go_remove_item_cache (line 538) | fn go_remove_item_cache(pointer: &DiskPointer) {
  function go_make_new_tier (line 573) | fn go_make_new_tier(size: usize) -> TieredCache {
  function go_find_tier_item (line 586) | fn go_find_tier_item(tier: &TieredCache, pointer: &DiskPointer) -> Optio...
  function go_get_tier_item (line 599) | fn go_get_tier_item(tier: &mut TieredCache, index: usize, silent: bool) ...
  function go_extract_tier_item (line 620) | fn go_extract_tier_item(tier: &mut TieredCache, index: usize) -> Option<...
  function go_add_tier_item (line 630) | fn go_add_tier_item(tier: &mut TieredCache, item: CachedBlock) {
  function go_update_tier_item (line 644) | fn go_update_tier_item(tier: &mut TieredCache, index: usize, new_item: C...
  function go_get_tier_best (line 663) | fn go_get_tier_best(tier: &mut TieredCache) -> Option<CachedBlock> {
  function go_get_tier_worst (line 674) | fn go_get_tier_worst(tier: &mut TieredCache) -> Option<CachedBlock> {
  function go_flush_tier (line 685) | fn go_flush_tier(tier_number: usize) -> Result<(), DriveError> {
  function go_cleanup_tier (line 853) | fn go_cleanup_tier(tier_number: usize) -> Option<u64> {
  function go_flush_disk_from_cache (line 921) | fn go_flush_disk_from_cache(disk_number: u16) -> Result<u64, DriveError> {
  function go_check_tier_full (line 1026) | fn go_check_tier_full(tier: &TieredCache) -> bool {
  function go_find_most_common_disk (line 1030) | fn go_find_most_common_disk() -> (u16, u16) {
  function go_get_cache_pressure (line 1055) | fn go_get_cache_pressure() -> f64 {
  function go_get_tier_free_space (line 1061) | fn go_get_tier_free_space(tier_number: usize) -> usize {
  function disk_load_header_invalidation (line 1078) | pub(in super::super::cache) fn disk_load_header_invalidation(disk_number...

FILE: src/pool/disk/generic/io/cache/cache_io.rs
  type CachedBlockIO (line 33) | pub struct CachedBlockIO {
    method status_of_cached_block (line 77) | pub fn status_of_cached_block(block_origin: DiskPointer) -> Option<boo...
    method read_block (line 90) | pub fn read_block(block_origin: DiskPointer) -> Result<RawBlock, Drive...
    method update_block (line 108) | pub fn update_block(raw_block: &RawBlock) -> Result<(), DriveError> {
    method remove_block (line 115) | pub fn remove_block(block_origin: &DiskPointer) {
    method flush (line 120) | pub fn flush() -> Result<(), DriveError> {
  function go_read_cached_block (line 138) | fn go_read_cached_block(block_location: DiskPointer) -> Result<RawBlock,...
  function go_update_cached_block (line 236) | fn go_update_cached_block(raw_block: &RawBlock) -> Result<(), DriveError> {

FILE: src/pool/disk/generic/io/cache/cached_allocation.rs
  type CachedAllocationDisk (line 21) | pub(crate) struct CachedAllocationDisk {
    method open (line 37) | pub(crate) fn open(disk_number: u16) -> Result<Self, DriveError> {
  method get_allocation_table (line 60) | fn get_allocation_table(&self) -> &[u8] {
  method set_allocation_table (line 65) | fn set_allocation_table(&mut self,new_table: &[u8]) -> Result<(), DriveE...
  method drop (line 79) | fn drop(&mut self) {

FILE: src/pool/disk/generic/io/cache/statistics.rs
  constant HIT_MEMORY (line 8) | const HIT_MEMORY: usize = 10_000;
  type BlockCacheStatistics (line 21) | pub(super) struct BlockCacheStatistics {
    method new (line 37) | fn new() -> Self {
    method get_hit_rate (line 43) | pub(super) fn get_hit_rate() -> f64 {
    method record_hit (line 56) | pub(super) fn record_hit() {
    method record_miss (line 70) | pub(super) fn record_miss() {

FILE: src/pool/disk/generic/io/checked_io.rs
  type CheckedIO (line 24) | pub(super) trait CheckedIO: BlockAllocation + GenericDiskMethods {
    method checked_read (line 30) | fn checked_read(&self, block_number: u16) -> Result<RawBlock, DriveErr...
    method checked_write (line 45) | fn checked_write(&mut self, block: &RawBlock) -> Result<(), DriveError> {
    method checked_update (line 74) | fn checked_update(&mut self, block: &RawBlock) -> Result<(), DriveErro...
    method checked_large_update (line 89) | fn checked_large_update(&mut self, data: Vec<u8>, start_block: DiskPoi...

FILE: src/pool/disk/generic/io/read.rs
  function read_block_direct (line 50) | pub(crate) fn read_block_direct(
  function read_multiple_blocks_direct (line 176) | pub(crate) fn read_multiple_blocks_direct(

FILE: src/pool/disk/generic/io/wipe.rs
  function destroy_disk (line 10) | pub(crate) fn destroy_disk(disk: &mut File) -> Result<(), DriveError> {

FILE: src/pool/disk/generic/io/write.rs
  function write_block_direct (line 34) | pub(crate) fn write_block_direct(disk_file: &File, block: &RawBlock, has...
  function write_large_direct (line 143) | pub(crate) fn write_large_direct(disk_file: &File, data: &[u8], start_bl...
  function large_write_fallback (line 239) | fn large_write_fallback(disk_file: &File, data: &[u8], start_block: Disk...

FILE: src/pool/disk/pool_disk/block/header/header_methods.rs
  method read (line 33) | pub fn read() -> Result<Self, DriveError> {
  method write (line 37) | pub fn write(&self) -> Result<(), DriveError> {
  method to_block (line 41) | pub fn to_block(self) -> RawBlock {
  method from_block (line 45) | pub fn from_block(block: &RawBlock) -> Result<PoolDiskHeader, HeaderErro...
  function read_pool_header_from_disk (line 52) | fn read_pool_header_from_disk() -> Result<PoolDiskHeader, DriveError> {
  function prompt_for_new_pool (line 125) | fn prompt_for_new_pool(disk: BlankDisk) -> Result<(), DriveError> {
  function pool_header_from_raw_block (line 158) | fn pool_header_from_raw_block(block: &RawBlock) -> Result<PoolDiskHeader...
  function pool_header_to_raw_block (line 229) | fn pool_header_to_raw_block(header: PoolDiskHeader) -> RawBlock {
  function create_new_pool_disk (line 287) | fn create_new_pool_disk(mut disk: BlankDisk) -> Result<(), DriveError> {
  function new_pool_header (line 309) | fn new_pool_header() -> PoolDiskHeader {
  function write_pool_header_to_disk (line 345) | fn write_pool_header_to_disk(header: &PoolDiskHeader) -> Result<(), Driv...
  function disk_wiper_mode (line 368) | fn disk_wiper_mode() -> ! {
  function disk_restore_mode (line 432) | fn disk_restore_mode() -> ! {

FILE: src/pool/disk/pool_disk/block/header/header_struct.rs
  type PoolDiskHeader (line 12) | pub struct PoolDiskHeader {

FILE: src/pool/disk/pool_disk/block/header/tests.rs
  function block_ping_pong (line 19) | fn block_ping_pong() {
  method random (line 33) | fn random() -> Self {
  method random (line 53) | fn random() -> Self {
  function random_allocations (line 59) | fn random_allocations() -> [u8; 360] {

FILE: src/pool/disk/pool_disk/pool_disk_methods.rs
  method bootstrap (line 34) | fn bootstrap(_file: File, _disk_number: u16) -> Result<Self, DriveError> {
  method from_header (line 41) | fn from_header(block: RawBlock, file: File) -> Self {
  method get_allocation_table (line 64) | fn get_allocation_table(&self) -> &[u8] {
  method set_allocation_table (line 68) | fn set_allocation_table(&mut self, new_table: &[u8]) -> Result<(), Drive...
  method unchecked_read_block (line 80) | fn unchecked_read_block(&self, block_number: u16) -> Result<RawBlock, Dr...
  method unchecked_write_block (line 86) | fn unchecked_write_block(&mut self, block: &RawBlock) -> Result<(), Driv...
  method disk_file (line 92) | fn disk_file(self) -> File {
  method get_disk_number (line 97) | fn get_disk_number(&self) -> u16 {
  method set_disk_number (line 102) | fn set_disk_number(&mut self, disk_number: u16) {
  method disk_file_mut (line 107) | fn disk_file_mut(&mut self) -> &mut File {
  method flush (line 112) | fn flush(&mut self) -> Result<(), DriveError> {
  method unchecked_write_large (line 119) | fn unchecked_write_large(&mut self, _data:Vec<u8>, _start_block: DiskPoi...
  method unchecked_read_multiple_blocks (line 127) | fn unchecked_read_multiple_blocks(&self, _block_number: u16, _num_block_...

FILE: src/pool/disk/pool_disk/pool_disk_struct.rs
  type PoolDisk (line 9) | pub struct PoolDisk {

FILE: src/pool/disk/standard_disk/block/directory/directory_methods.rs
  method from (line 33) | fn from(block: RawBlock) -> Self {
  method to_block (line 42) | pub fn to_block(&self) -> RawBlock {
  method from_block (line 45) | pub fn from_block(block: &RawBlock) -> Self {
  method try_add_item (line 52) | pub fn try_add_item(&mut self, item: &DirectoryItem) -> Result<(), Block...
  method try_remove_item (line 60) | pub(in super::super) fn try_remove_item(
  method new (line 74) | pub fn new(origin: DiskPointer) -> Self {
  method get_items (line 81) | pub fn get_items(&self) -> Vec<DirectoryItem> {
  method is_empty (line 86) | pub fn is_empty(&self) -> Result<bool, DriveError> {
  function directory_block_try_remove_item (line 93) | fn directory_block_try_remove_item(
  function directory_block_try_add_item (line 117) | fn directory_block_try_add_item(
  function new_directory_block (line 145) | fn new_directory_block(origin: DiskPointer) -> DirectoryBlock {
  function directory_block_to_bytes (line 177) | fn directory_block_to_bytes(block: &DirectoryBlock) -> RawBlock {
  function directory_block_from_bytes (line 213) | fn directory_block_from_bytes(block: &RawBlock) -> DirectoryBlock {
  method item_bytes_from_vec (line 242) | fn item_bytes_from_vec(&self, destination_disk: u16) -> [u8; 501] {
  method item_vec_from_bytes (line 258) | fn item_vec_from_bytes(bytes: &[u8], origin_disk: u16) -> Vec<DirectoryI...
  method to_bytes (line 295) | pub(super) fn to_bytes(&self, destination_disk: u16) -> Vec<u8> {
  method from_bytes (line 314) | pub(super) fn from_bytes(bytes: &[u8], origin_disk: u16) -> (u8, Self) {
  method get_size (line 348) | pub(crate) fn get_size(&self) -> Result<u64, DriveError> {
  method get_created_time (line 387) | pub(crate) fn get_created_time(&self) -> Result<InodeTimestamp, DriveErr...
  method get_modified_time (line 394) | pub(crate) fn get_modified_time(&self) -> Result<InodeTimestamp, DriveEr...
  method get_directory_block (line 409) | pub(crate) fn get_directory_block(&self) -> Result<DirectoryBlock, Drive...

FILE: src/pool/disk/standard_disk/block/directory/directory_struct.rs
  type DirectoryItem (line 15) | pub struct DirectoryItem {
  type DirectoryBlock (line 25) | pub(crate) struct DirectoryBlock {

FILE: src/pool/disk/standard_disk/block/directory/tests.rs
  function blank_directory_block_serialization (line 20) | fn blank_directory_block_serialization() {
  function directory_item_serialization (line 33) | fn directory_item_serialization() {
  function add_and_remove_to_directory_block (line 76) | fn add_and_remove_to_directory_block() {
  function adding_and_removing_updates_size (line 96) | fn adding_and_removing_updates_size() {
  method new (line 126) | fn new() -> Self {
  method get_random (line 134) | fn get_random() -> Self {
  function get_random_name (line 150) | fn get_random_name() -> String {

FILE: src/pool/disk/standard_disk/block/file_extents/file_extents_methods.rs
  constant DATA_BLOCK_OVERHEAD (line 5) | pub(crate) const DATA_BLOCK_OVERHEAD: u64 = 5;
  method from (line 30) | fn from(value: RawBlock) -> FileExtentBlock {
  method extents_to_bytes (line 37) | pub(super) fn extents_to_bytes(&self, destination_disk_number: u16) -> V...
  method from_block (line 41) | pub(crate) fn from_block(block: &RawBlock) -> Self {
  method to_block (line 49) | pub(crate) fn to_block(&self) -> RawBlock {
  method add_extent (line 58) | pub(crate) fn add_extent(&mut self, extent: FileExtent) -> Result<(), Bl...
  method new (line 69) | pub(crate) fn new(block_origin: DiskPointer) -> Self {
  method get_extents (line 85) | pub(crate) fn get_extents(&self) -> Vec<FileExtent> {
  method force_replace_all_extents (line 123) | pub(in super::super::super::block) fn force_replace_all_extents(&mut sel...
  function extent_block_add_extent (line 166) | fn extent_block_add_extent(
  function from_bytes (line 205) | fn from_bytes(block: &RawBlock) -> FileExtentBlock {
  function to_block (line 232) | fn to_block(extent_block: &FileExtentBlock) -> RawBlock {
  function extents_to_bytes (line 272) | fn extents_to_bytes(extents: &[FileExtent], destination_disk_number: u16...
  function bytes_to_extents (line 287) | fn bytes_to_extents(bytes: &[u8], origin_disk_number: u16) -> Vec<FileEx...
  method to_bytes (line 324) | pub(super) fn to_bytes(mut self, destination_disk_number: u16) -> Vec<u8> {
  method from_bytes (line 365) | pub(super) fn from_bytes(bytes: &[u8], origin_disk_number: u16) -> (u8, ...
  method get_pointers (line 415) | pub(crate) fn get_pointers(&self) -> Vec<DiskPointer> {
  method new (line 428) | pub(crate) fn new(start_block: DiskPointer, length: u8) -> Self {
  method default (line 440) | pub fn default() -> Self {

FILE: src/pool/disk/standard_disk/block/file_extents/file_extents_struct.rs
  type FileExtent (line 12) | pub struct FileExtent {
  type FileExtentBlock (line 37) | pub struct FileExtentBlock {

FILE: src/pool/disk/standard_disk/block/file_extents/tests.rs
  function random_extents_serialization (line 19) | fn random_extents_serialization() {
  function empty_extent_block_serialization (line 35) | fn empty_extent_block_serialization() {
  function full_extent_block (line 47) | fn full_extent_block() {
  function random_block_serialization (line 74) | fn random_block_serialization() {
  method get_random (line 92) | fn get_random(block_origin: DiskPointer) -> Self {
  method random (line 116) | fn random() -> Self {
  method new (line 134) | fn new() -> Self {

FILE: src/pool/disk/standard_disk/block/header/header_methods.rs
  method from_block (line 11) | pub fn from_block(raw_block: &RawBlock) -> StandardDiskHeader {
  method to_block (line 14) | pub fn to_block(&self) -> RawBlock {
  method from (line 21) | fn from(value: RawBlock) -> Self {
  function extract_header (line 29) | fn extract_header(raw_block: &RawBlock) -> StandardDiskHeader {
  function to_disk_block (line 52) | fn to_disk_block(header: &StandardDiskHeader) -> RawBlock {

FILE: src/pool/disk/standard_disk/block/header/header_struct.rs
  type StandardDiskHeader (line 8) | pub struct StandardDiskHeader {

FILE: src/pool/disk/standard_disk/block/inode/inode_methods.rs
  method from (line 32) | fn from(value: RawBlock) -> Self {
  method to_bytes (line 39) | fn to_bytes(&self) -> Vec<u8> {
  method from_bytes (line 43) | fn from_bytes(bytes: &[u8]) -> Self {
  method to_block (line 49) | pub fn to_block(&self) -> RawBlock {
  method from_block (line 52) | pub fn from_block(block: &RawBlock) -> Self {
  method new (line 60) | pub fn new(block_origin: DiskPointer) -> Self {
  method try_add_inode (line 69) | pub fn try_add_inode(&mut self, inode: Inode) -> Result<u16, BlockManipu...
  method try_remove_inode (line 79) | pub fn try_remove_inode(&mut self, inode_offset: u16) -> Result<(), Bloc...
  method try_read_inode (line 85) | pub fn try_read_inode(&self, inode_offset: u16) -> Result<Inode, BlockMa...
  method new_destination (line 93) | pub fn new_destination(&mut self, pointer: DiskPointer) {
  method update_inode (line 109) | pub fn update_inode(&mut self, inode_offset: u16, updated_inode: Inode,)...
  function inode_block_try_read_inode (line 137) | fn inode_block_try_read_inode(block: &InodeBlock, offset: u16) -> Result...
  function inode_block_try_remove_inode (line 150) | fn inode_block_try_remove_inode(
  function inode_block_try_add_inode (line 203) | fn inode_block_try_add_inode(
  function new_inode_block (line 241) | fn new_inode_block(block_origin: DiskPointer) -> InodeBlock {
  function from_raw_block (line 269) | fn from_raw_block(block: &RawBlock) -> InodeBlock {
  function to_raw_bytes (line 296) | fn to_raw_bytes(block: &InodeBlock) -> RawBlock {
  method as_bytes (line 336) | pub(super) fn as_bytes(&self) -> Vec<u8> {
  method from_bytes (line 367) | pub(super) fn from_bytes(bytes: &[u8]) -> Self {
  method as_bytes (line 428) | fn as_bytes(&self) -> [u8; 12] {
  method from_bytes (line 434) | fn from_bytes(bytes: [u8; 12]) -> Self {
  method as_bytes (line 443) | fn as_bytes(&self) -> [u8; 4] {
  method from_bytes (line 446) | fn from_bytes(bytes: [u8; 4]) -> Self {
  method from_disk_pointer (line 452) | pub fn from_disk_pointer(pointer: DiskPointer) -> Self {
  method to_bytes (line 458) | pub(super) fn to_bytes(self) -> [u8; 12] {
  method from_bytes (line 464) | pub(super) fn from_bytes(bytes: [u8; 12]) -> Self {
  method now (line 471) | pub fn now() -> Self {
  method as_bytes (line 492) | pub fn as_bytes(&self, destination_disk: u16) -> Vec<u8> {
  method from_bytes (line 533) | pub fn from_bytes(bytes: &[u8], origin_disk: u16) -> (u8, Self) {
  method new (line 582) | pub(crate) fn new(pointer: DiskPointer, offset: u16) -> Self {
  method next_block (line 595) | pub fn next_block(&self) -> Option<DiskPointer> {
  method new (line 610) | pub fn new(disk_pointer: DiskPointer) -> Self {
  method get_size (line 617) | pub fn get_size(&self) -> u64 {
  method set_size (line 623) | pub fn set_size(&mut self, size: u64) {
  method extract_file (line 631) | pub fn extract_file(&self) -> Option<InodeFile> {
  method extract_directory (line 634) | pub fn extract_directory(&self) -> Option<InodeDirectory> {
  method from (line 649) | fn from(value: InodeTimestamp) -> Self {
  method extract (line 667) | pub(super) fn extract(&self) -> (PackedInodeLocationFlags, u16) {
  method new (line 687) | fn new(flags: PackedInodeLocationFlags, offset: u16) -> Self {
  method from_u16 (line 698) | pub(super) fn from_u16(incoming: u16) -> Self {

FILE: src/pool/disk/standard_disk/block/inode/inode_struct.rs
  type Inode (line 12) | pub(crate) struct Inode {
  type InodeFile (line 21) | pub struct InodeFile {
  type InodeDirectory (line 29) | pub(crate) struct InodeDirectory {
  type InodeTimestamp (line 36) | pub struct InodeTimestamp {
  type InodeLocation (line 43) | pub struct InodeLocation {
  type InodeOffsetPacking (line 64) | pub(super) struct InodeOffsetPacking {
  type InodeBlock (line 88) | pub struct InodeBlock {

FILE: src/pool/disk/standard_disk/block/inode/tests.rs
  function blank_inode_block_serialization (line 22) | fn blank_inode_block_serialization() {
  function fill_inode_block (line 35) | fn fill_inode_block() {
  function filled_inode_block_serialization (line 65) | fn filled_inode_block_serialization() {
  function add_and_read_inode (line 90) | fn add_and_read_inode() {
  function inode_location_consistency (line 106) | fn inode_location_consistency() {
  function inode_correct_sizes (line 118) | fn inode_correct_sizes() {
  function inode_consistent_serialization (line 133) | fn inode_consistent_serialization() {
  function timestamp_consistent_serialization (line 153) | fn timestamp_consistent_serialization() {
  method get_random (line 176) | pub(crate) fn get_random() -> Self {
  method get_random (line 205) | pub(crate) fn get_random() -> Self {
  method get_random (line 216) | pub(crate) fn get_random() -> Self {
  method get_random (line 228) | pub(crate) fn get_random() -> Self {
  method get_random (line 245) | pub(crate) fn get_random() -> Self {

FILE: src/pool/disk/standard_disk/block/io/directory/movement.rs
  method change_directory (line 41) | pub fn change_directory(
  method try_find_directory (line 110) | pub(crate) fn try_find_directory(maybe_path: Option<&Path>) -> Result<Op...

FILE: src/pool/disk/standard_disk/block/io/directory/read.rs
  method find_item (line 29) | pub fn find_item(
  method list (line 77) | pub fn list(&self) -> Result<Vec<DirectoryItem>, DriveError> {
  method get_size (line 86) | pub fn get_size(&self) -> Result<u64, DriveError> {
  method is_root (line 115) | fn is_root(&self) -> bool {
  method find_and_extract_item (line 137) | pub(crate) fn find_and_extract_item(&mut self, item_to_find: &NamedItem)...
  method block_extract_item (line 246) | fn block_extract_item(&mut self, item_to_find: &NamedItem) -> Result<Opt...
  method try_rename_item (line 281) | pub(crate) fn try_rename_item(&mut self, to_rename: &NamedItem, new_name...
  function go_list_directory (line 338) | fn go_list_directory(
  function get_blocks (line 376) | fn get_blocks(start_block_location: DiskPointer) -> Result<Vec<Directory...

FILE: src/pool/disk/standard_disk/block/io/directory/tests.rs
  function add_directory (line 24) | fn add_directory() {
  function creating_only_makes_one_directory (line 35) | fn creating_only_makes_one_directory() {
  function add_and_delete_directory (line 52) | fn add_and_delete_directory() {
  function deletion_shrinks (line 71) | fn deletion_shrinks() {
  function rename_items (line 95) | fn rename_items() {
  function add_directory_and_list (line 139) | fn add_directory_and_list() {
  function nested_directory_hell (line 157) | fn nested_directory_hell() {
  function directories_switch_disks (line 212) | fn directories_switch_disks() -> Result<(), ()> {
  function get_filesystem (line 237) | pub fn get_filesystem() -> FlusterFS {
  function get_new_temp_dir (line 246) | pub fn get_new_temp_dir() -> TempDir {

FILE: src/pool/disk/standard_disk/block/io/directory/types.rs
  type NamedItem (line 9) | pub(crate) enum NamedItem {
    method debug_strings (line 16) | pub fn debug_strings(&self) -> (&'static str, &String) {
    method find_in (line 24) | pub fn find_in(&self, to_search: &[DirectoryItem]) -> Option<Directory...
    method is_file (line 35) | pub fn is_file(&self) -> bool {
    method from (line 46) | fn from(value: DirectoryItem) -> Self {

FILE: src/pool/disk/standard_disk/block/io/directory/write.rs
  method add_item (line 36) | pub fn add_item(
  method make_directory (line 51) | pub fn make_directory(
  method delete_self (line 71) | pub fn delete_self(self, self_item: DirectoryItem) -> Result<(), DriveEr...
  function go_make_directory (line 115) | fn go_make_directory(
  function go_make_new_directory_block (line 188) | fn go_make_new_directory_block() -> Result<DiskPointer, DriveError> {
  function go_add_item (line 203) | fn go_add_item(
  function go_find_next_or_extend_block (line 265) | fn go_find_next_or_extend_block(

FILE: src/pool/disk/standard_disk/block/io/file/movement.rs
  method byte_finder (line 26) | pub(super) fn byte_finder(byte_offset: u64) -> (usize, u16) {
  method get_inode (line 47) | pub(crate) fn get_inode(&self) -> Result<Inode, DriveError> {

FILE: src/pool/disk/standard_disk/block/io/file/read.rs
  method as_pointers (line 37) | pub(super) fn as_pointers(&self) -> Result<Vec<DiskPointer>, DriveError> {
  method as_extents (line 41) | pub(super) fn as_extents(&self) -> Result<Vec<FileExtent>, DriveError> {
  method get_root_block (line 46) | fn get_root_block(&self) -> Result<FileExtentBlock, DriveError> {
  method read (line 50) | fn read(&self, seek_point: u64, size: u32) -> Result<Vec<u8>, DriveError> {
  method read_file (line 68) | pub fn read_file(&self, seek_point: u64, size: u32) -> Result<Vec<u8>, D...
  function go_to_pointers (line 100) | fn go_to_pointers(location: &InodeFile) -> Result<Vec<DiskPointer>, Driv...
  function go_to_extents (line 124) | fn go_to_extents(
  function go_get_root_block (line 163) | fn go_get_root_block(file: &InodeFile) -> Result<FileExtentBlock, DriveE...
  function go_read_file (line 173) | fn go_read_file(file: &InodeFile, seek_point: u64, size: u32) -> Result<...
  function read_bytes_from_block (line 241) | fn read_bytes_from_block(buffer: &mut [u8], buffer_offset: usize, block:...

FILE: src/pool/disk/standard_disk/block/io/file/tests.rs
  function create_blank (line 11) | fn create_blank() {
  function write_small_file (line 24) | fn write_small_file() {
  function write_big_file (line 49) | fn write_big_file() {
  function make_lots_of_files (line 77) | fn make_lots_of_files() {
  function make_lots_of_filled_files (line 91) | fn make_lots_of_filled_files() {
  function write_and_read_small (line 118) | fn write_and_read_small() {
  function write_and_read_large (line 156) | fn write_and_read_large() {
  function read_and_write_random_files (line 198) | fn read_and_write_random_files() {
  function check_byte_vec_equality (line 247) | fn check_byte_vec_equality(a: &[u8], b: &[u8]) {

FILE: src/pool/disk/standard_disk/block/io/file/write.rs
  method write (line 61) | fn write(&mut self, bytes: &[u8], seek_point: u64) -> Result<u32, DriveE...
  method new_file (line 76) | pub fn new_file(&mut self, name: String) -> Result<DirectoryItem, DriveE...
  method delete_file (line 88) | pub fn delete_file(&mut self, file: NamedItem) -> Result<Option<()>, Dri...
  method write_file (line 131) | pub fn write_file(&self, bytes: &[u8], seek_point: u64) -> Result<u32, D...
  method truncate (line 196) | pub fn truncate(&self, new_size: u64) -> Result<(), DriveError> {
  function go_write (line 201) | fn go_write(inode_file: &mut InodeFile, bytes: &[u8], seek_point: u64) -...
  function update_block (line 301) | fn update_block(block: DiskPointer, bytes: &[u8], offset: u16) -> Result...
  function expand_file (line 362) | fn expand_file(inode_file: InodeFile, blocks: u16) -> Result<Vec<DiskPoi...
  function expand_extent_block (line 385) | fn expand_extent_block(block: &mut FileExtentBlock) -> Result<(), DriveE...
  function expanding_add_extents (line 409) | fn expanding_add_extents(file: InodeFile, extents: &[FileExtent]) -> Res...
  function pointers_into_extents (line 481) | fn pointers_into_extents(pointers: &[DiskPointer]) -> Vec<FileExtent> {
  function go_make_new_file (line 531) | fn go_make_new_file(directory_block: &mut DirectoryBlock, name: String) ...
  function truncate_or_delete_file (line 604) | fn truncate_or_delete_file(item: &DirectoryItem, delete: bool, new_size:...
  function truncate_cleanup (line 1094) | fn truncate_cleanup(pre_collected: Vec<DiskPointer>, next_extent_block: ...
  function flush_to_disk (line 1183) | fn flush_to_disk(block: &FileExtentBlock) -> Result<(), DriveError> {

FILE: src/pool/disk/standard_disk/block/io/inode/tests.rs
  function add_inode (line 24) | fn add_inode() {
  function add_many_inode (line 32) | fn add_many_inode() {
  function get_filesystem (line 40) | fn get_filesystem() -> FlusterFS {
  function get_new_temp_dir (line 52) | fn get_new_temp_dir() -> TempDir {

FILE: src/pool/disk/standard_disk/block/io/inode/write.rs
  method fast_add_inode (line 65) | pub fn fast_add_inode(inode: Inode) -> Result<InodeLocation, DriveError> {
  method add_inode (line 98) | pub fn add_inode(inode: Inode) -> Result<InodeLocation, DriveError> {
  function go_add_inode (line 122) | fn go_add_inode(inode: Inode, start_block: InodeBlock) -> Result<InodeLo...
  function get_next_block (line 187) | fn get_next_block(current_block: InodeBlock) -> Result<DiskPointer, Driv...
  function make_new_inode_block (line 212) | fn make_new_inode_block() -> Result<DiskPointer, DriveError> {

FILE: src/pool/disk/standard_disk/standard_disk_methods.rs
  method bootstrap (line 64) | fn bootstrap(file: File, disk_number: u16) -> Result<StandardDisk, Drive...
  method from_header (line 171) | fn from_header(block: RawBlock, file: File) -> Self {
  method get_allocation_table (line 188) | fn get_allocation_table(&self) -> &[u8] {
  method set_allocation_table (line 192) | fn set_allocation_table(&mut self, new_table: &[u8]) -> Result<(), Drive...
  method spoof (line 209) | fn spoof() -> Self {
  function create (line 228) | fn create(file: File, disk_number: u16) -> Result<StandardDisk, DriveErr...
  function initialize_numbered (line 257) | fn initialize_numbered(disk: &mut StandardDisk, disk_number: u16) -> Res...
  method unchecked_read_block (line 302) | fn unchecked_read_block(&self, block_number: u16) -> Result<RawBlock, Dr...
  method unchecked_write_block (line 308) | fn unchecked_write_block(&mut self, block: &RawBlock) -> Result<(), Driv...
  method unchecked_write_large (line 314) | fn unchecked_write_large(&mut self, data:Vec<u8>, start_block:DiskPointe...
  method disk_file (line 319) | fn disk_file(self) -> File {
  method get_disk_number (line 324) | fn get_disk_number(&self) -> u16 {
  method set_disk_number (line 329) | fn set_disk_number(&mut self, disk_number: u16) {
  method disk_file_mut (line 334) | fn disk_file_mut(&mut self) -> &mut File {
  method flush (line 340) | fn flush(&mut self) -> Result<(), DriveError> {
  method unchecked_read_multiple_blocks (line 347) | fn unchecked_read_multiple_blocks(&self, block_number: u16, num_block_to...

FILE: src/pool/disk/standard_disk/standard_disk_struct.rs
  type StandardDisk (line 9) | pub struct StandardDisk {

FILE: src/pool/disk/unknown_disk/unknown_disk_methods.rs
  method unchecked_read_block (line 22) | fn unchecked_read_block(&self, _block_number: u16) -> Result<RawBlock, D...
  method unchecked_write_block (line 28) | fn unchecked_write_block(&mut self, block: &RawBlock) -> Result<(), Driv...
  method disk_file (line 34) | fn disk_file(self) -> File {
  method get_disk_number (line 39) | fn get_disk_number(&self) -> u16 {
  method set_disk_number (line 45) | fn set_disk_number(&mut self, _disk_number: u16) {
  method disk_file_mut (line 52) | fn disk_file_mut(&mut self) -> &mut File {
  method flush (line 57) | fn flush(&mut self) -> Result<(), DriveError> {
  method unchecked_write_large (line 64) | fn unchecked_write_large(&mut self, _data: Vec<u8>, _start_block: DiskPo...
  method unchecked_read_multiple_blocks (line 70) | fn unchecked_read_multiple_blocks(&self, _block_number: u16, _num_block_...
  method new (line 76) | pub fn new(file: File) -> Self {
  method get_allocation_table (line 83) | fn get_allocation_table(&self) ->  &[u8] {
  method set_allocation_table (line 89) | fn set_allocation_table(&mut self, _new_table: &[u8]) -> Result<(), Driv...

FILE: src/pool/disk/unknown_disk/unknown_disk_struct.rs
  type UnknownDisk (line 5) | pub struct UnknownDisk {

FILE: src/pool/io/allocate.rs
  method find_and_allocate_pool_blocks (line 75) | pub fn find_and_allocate_pool_blocks(blocks: u16, add_crc: bool) -> Resu...
  method free_pool_block_from_disk (line 91) | pub fn free_pool_block_from_disk(blocks: &[DiskPointer]) -> Result<u16, ...
  function go_find_free_pool_blocks (line 96) | fn go_find_free_pool_blocks(blocks: u16, add_crc: bool) -> Result<Vec<Di...
  function block_indexes_to_pointers (line 239) | fn block_indexes_to_pointers(blocks: &Vec<u16>, disk: u16) -> Vec<DiskPo...
  function write_empty_crc (line 252) | fn write_empty_crc(blocks: &[u16], disk: u16) -> Result<(), DriveError> {
  function go_deallocate_pool_block (line 281) | fn go_deallocate_pool_block(blocks: &[DiskPointer]) -> Result<u16, Drive...

FILE: src/pool/pool_actions/pool_methods.rs
  method flush (line 32) | pub fn flush() -> Result<(), DriveError> {
  method load (line 37) | pub fn load() -> Arc<Mutex<Pool>> {
  method new_disk (line 42) | pub fn new_disk<T: DiskBootstrap>() -> Result<T, DriveError> {
  method initalize (line 46) | fn initalize() -> Result<(), DriveError> {
  method get_root_directory (line 52) | pub fn get_root_directory() -> Result<DirectoryBlock, DriveError> {
  method get_root_directory_item (line 56) | pub fn get_root_directory_item() -> DirectoryItem {
  function flush_pool (line 62) | pub(super) fn flush_pool() -> Result<(), DriveError> {
  function load (line 88) | pub(super) fn load() -> Arc<Mutex<Pool>> {
  function initalize_pool (line 176) | fn initalize_pool() -> Result<(), DriveError> {
  function add_disk (line 195) | fn add_disk<T: DiskBootstrap>() -> Result<T, DriveError> {
  function pool_get_root_directory (line 260) | fn pool_get_root_directory() -> Result<DirectoryBlock, DriveError> {
  function pool_get_root_inode_location (line 278) | fn pool_get_root_inode_location() -> InodeLocation {
  function pool_get_root_directory_item (line 287) | fn pool_get_root_directory_item() -> DirectoryItem {

FILE: src/pool/pool_actions/pool_struct.rs
  type Pool (line 24) | pub struct Pool {

FILE: src/tui/layout.rs
  type FlusterTUI (line 52) | pub struct FlusterTUI<'a> {
  function draw (line 69) | pub fn draw(&mut self, frame: &mut Frame) {
  function pop_up_handler (line 314) | fn pop_up_handler(frame: &mut Frame, incoming_pop_up: &mut Option<TuiPro...

FILE: src/tui/notify.rs
  type NotifyTui (line 57) | pub(crate) struct NotifyTui {
    method disk_swapped (line 67) | pub(crate) fn disk_swapped(new_disk: u16) {
    method block_read (line 75) | pub(crate) fn block_read(number: u16) {
    method block_written (line 82) | pub(crate) fn block_written(amount: u16) {
    method swap_saved (line 93) | pub(crate) fn swap_saved() {
    method cache_flushed (line 100) | pub(crate) fn cache_flushed() {
    method read_cached (line 107) | pub(crate) fn read_cached() {
    method write_cached (line 114) | pub(crate) fn write_cached() {
    method set_cache_hit_rate (line 121) | pub(crate) fn set_cache_hit_rate(rate: f64) {
    method set_cache_pressure (line 128) | pub(crate) fn set_cache_pressure(pressure: f64) {
    method start_task (line 142) | pub(crate) fn start_task(task_type: TaskType, steps: u64) -> TaskHandle {
    method complete_task_step (line 190) | pub(crate) fn complete_task_step(_handle: &TaskHandle) {
    method complete_multiple_task_steps (line 206) | pub(crate) fn complete_multiple_task_steps(_handle: &TaskHandle, steps...
    method add_steps_to_task (line 222) | pub(crate) fn add_steps_to_task(_handle: &TaskHandle, steps: u64) {
    method finish_task (line 238) | pub(crate) fn finish_task(mut handle: TaskHandle) {
    method cancel_task (line 260) | pub(crate) fn cancel_task(mut handle: TaskHandle) {
    method force_cancel_task (line 281) | pub(super) fn force_cancel_task() {

FILE: src/tui/prompts.rs
  type TuiPrompt (line 18) | pub(crate) struct TuiPrompt<'a> {
  function prompt_enter (line 51) | pub(crate) fn prompt_enter(title: String, content: String, flash: bool) {
  function prompt_input (line 94) | pub(crate) fn prompt_input(title: String, content: String, flash: bool) ...
  function prompt_wait_for_disk_swap (line 144) | pub(crate) fn prompt_wait_for_disk_swap(title: String, content: String, ...
  function get_block_device_size (line 292) | fn get_block_device_size(path: &Path) -> Result<u64, DriveError> {
  function disabled_prompt_enter (line 350) | fn disabled_prompt_enter(prompt: TuiPrompt) {
  function disabled_prompt_input (line 355) | fn disabled_prompt_input(_prompt: TuiPrompt) -> String {

FILE: src/tui/state.rs
  type FlusterTUIState (line 8) | pub(super) struct FlusterTUIState {
    method new (line 55) | pub(super) fn new() -> Self {

FILE: src/tui/tasks.rs
  type ProgressableTask (line 21) | pub(crate) struct ProgressableTask {
    method new (line 241) | pub(super) fn new(task_type: TaskType, steps: u64) -> ProgressableTask {
    method add_work (line 251) | pub(super) fn add_work(&mut self, steps_to_add: u64) {
    method finish_steps (line 263) | pub(super) fn finish_steps(&mut self, steps: u64) {
    method add_sub_task (line 277) | pub(super) fn add_sub_task(&mut self, new_sub_task: ProgressableTask) {
    method finish_task (line 296) | pub(super) fn finish_task(mut self) -> Option<ProgressableTask> {
    method cancel_task (line 317) | pub(super) fn cancel_task(mut self) -> Option<ProgressableTask> {
    method get_tasks_info (line 331) | pub(super) fn get_tasks_info(&self) -> Vec<TaskInfo> {
  type TaskInfo (line 31) | pub(super) struct TaskInfo {
    method new (line 137) | fn new(task_type: TaskType, steps: u64) -> TaskInfo {
    method name (line 147) | pub(super) fn name(&self) -> String {
    method progress (line 210) | pub(super) fn progress(&self) -> f64 {
    method time_passed (line 215) | pub(super) fn time_passed(&self) -> String {
    method time_remaining (line 223) | pub(super) fn time_remaining(&self) -> String {
  type TaskType (line 44) | pub(crate) enum TaskType {
  type TaskHandle (line 96) | pub(crate) struct TaskHandle {
    method new (line 125) | pub(super) fn new() -> Self {
  method drop (line 111) | fn drop(&mut self) {

FILE: src/tui/tui_struct.rs
  function new (line 12) | pub(super) fn new() -> Self {

FILE: tests/directory.rs
  function create_directory (line 25) | fn create_directory() {
  function enter_and_list_directory (line 115) | fn enter_and_list_directory() {
  function check_for_dot (line 166) | fn check_for_dot() {
  function move_empty_directory (line 219) | fn move_empty_directory() {
  function directory_creation_and_removal (line 261) | fn directory_creation_and_removal() {
  function rename_lots_of_items (line 300) | fn rename_lots_of_items() {
  function rename_burn_in (line 459) | fn rename_burn_in() {

FILE: tests/file.rs
  function make_file_small (line 12) | fn make_file_small() {
  function make_file_large (line 53) | fn make_file_large() {
  function make_and_read_file_small (line 97) | fn make_and_read_file_small() {
  function make_and_read_file_large (line 151) | fn make_and_read_file_large() {
  function move_file (line 208) | fn move_file() {
  function delete_file (line 253) | fn delete_file() {
  function update_file_small (line 297) | fn update_file_small() {
  function update_file_large (line 384) | fn update_file_large() {

FILE: tests/mount_filesystem.rs
  function mount_filesystem (line 9) | fn mount_filesystem() {

FILE: tests/start_filesystem.rs
  function filesystem_starts (line 6) | fn filesystem_starts() {

FILE: tests/test_common.rs
  function get_new_temp_dir (line 13) | pub fn get_new_temp_dir() -> TempDir {
  function get_actually_temp_dir (line 25) | pub fn get_actually_temp_dir() -> TempDir {
  function start_filesystem (line 31) | pub fn start_filesystem() -> FuseMT<FlusterFS> {
  function unmount (line 43) | pub fn unmount(mount_point: PathBuf) {
  function test_mount_options (line 52) | pub fn test_mount_options() -> Vec<&'static OsStr> {
Condensed preview — 133 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (725K chars).
[
  {
    "path": ".gitignore",
    "chars": 40,
    "preview": "/target\n/temp_disks\n.vscode/launch.json\n"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 290,
    "preview": "{\n    \"rust-analyzer.runnables.extraEnv\": {\n        \"RUST_LOG\": \"info,fluster_fs=debug\" // debug level logs in tests\n   "
  },
  {
    "path": "Cargo.toml",
    "chars": 883,
    "preview": "[package]\nname = \"fluster_fs\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n[dependencies]\nbitflags = \"2.9.1\"\nclap = { version = \""
  },
  {
    "path": "LICENSE.txt",
    "chars": 18652,
    "preview": "Attribution 4.0 International\n\n=======================================================================\n\nCreative Commons"
  },
  {
    "path": "build.rs",
    "chars": 170,
    "preview": "fn main() {\n    #[cfg(target_os = \"windows\")]\n    panic!(\n        \"The `fuser` crate cannot be built on windows. You mus"
  },
  {
    "path": "readme.md",
    "chars": 4945,
    "preview": "  \n<h1 align=\"center\">\n  <br>\n  <img src=\"https://github.com/DocJade/fluster_rs/blob/master/img/flustered.png?raw=true\" "
  },
  {
    "path": "src/error_types/block.rs",
    "chars": 725,
    "preview": "// Blocks usually return similar types of errors.\nuse thiserror::Error;\n\n#[derive(Debug, Error, PartialEq)]\n/// Errors r"
  },
  {
    "path": "src/error_types/conversions.rs",
    "chars": 13389,
    "preview": "// Conversions between all of the lower types.\n\n//\n// Imports\n//\n\nuse std::io::ErrorKind;\nuse std::time::Duration;\nuse l"
  },
  {
    "path": "src/error_types/critical.rs",
    "chars": 20818,
    "preview": "// Critical errors are errors that we cannot recover from without some sort of higher intervention.\n// Returning this er"
  },
  {
    "path": "src/error_types/drive.rs",
    "chars": 2070,
    "preview": "// Error types pertaining to the floppy drive itself.\n// We do not allow string errors. This is RUST damn it, not python"
  },
  {
    "path": "src/error_types/filesystem.rs",
    "chars": 2526,
    "preview": "use libc::c_int;\nuse log::error;\n\nuse crate::error_types::drive::DriveError;\n\n//\n//\n// ======\n// C Error values\n// ====="
  },
  {
    "path": "src/error_types/header.rs",
    "chars": 387,
    "preview": "// Errors for header conversions.\nuse thiserror::Error;\n\n#[derive(Debug, Error, PartialEq)]\n/// Errors related to block "
  },
  {
    "path": "src/error_types/mod.rs",
    "chars": 146,
    "preview": "pub(crate) mod drive;\npub(crate) mod conversions;\npub(crate) mod block;\npub(crate) mod filesystem;\npub(crate) mod critic"
  },
  {
    "path": "src/filesystem/disk_backup/mod.rs",
    "chars": 32,
    "preview": "pub mod restore;\npub mod update;"
  },
  {
    "path": "src/filesystem/disk_backup/restore.rs",
    "chars": 4444,
    "preview": "// Restore a disk from a backup.\n\nuse std::{fs::File, io::{\n    Read,\n    Seek\n}, os::unix::fs::FileExt};\n\nuse log::{deb"
  },
  {
    "path": "src/filesystem/disk_backup/update.rs",
    "chars": 3952,
    "preview": "// Update the backup disk with new contents.\n\n// The path that the disk backups go in /var/fluster\n\n// Porting fluster? "
  },
  {
    "path": "src/filesystem/file_attributes/conversion.rs",
    "chars": 3640,
    "preview": "use fuse_mt::{FileAttr, FileType};\nuse libc::c_int;\nuse log::debug;\nuse std::time::SystemTime;\n\nuse crate::{\n    error_t"
  },
  {
    "path": "src/filesystem/file_attributes/mod.rs",
    "chars": 19,
    "preview": "pub mod conversion;"
  },
  {
    "path": "src/filesystem/file_handle/file_handle_methods.rs",
    "chars": 8626,
    "preview": "// Make the handle do things.\n\nuse std::{collections::HashMap, sync::{Arc, Mutex}};\n\nuse lazy_static::lazy_static;\nuse l"
  },
  {
    "path": "src/filesystem/file_handle/file_handle_struct.rs",
    "chars": 384,
    "preview": "//\n//\n// ======\n// Handle type\n// ======\n//\n//\n\n\n// We are in charge of our own file handle management. Fun! (lie)\n// So"
  },
  {
    "path": "src/filesystem/file_handle/mod.rs",
    "chars": 70,
    "preview": "pub(super) mod file_handle_methods;\npub(super) mod file_handle_struct;"
  },
  {
    "path": "src/filesystem/filesystem_struct.rs",
    "chars": 1770,
    "preview": "// This is where the fun begins\n\n// Imports\n\nuse crate::pool::pool_actions::pool_struct::Pool;\nuse std::{\n    path::Path"
  },
  {
    "path": "src/filesystem/fuse_filesystem_methods.rs",
    "chars": 75472,
    "preview": "// The actual FUSE filesystem layer.\n\n//\n//\n// ======\n// Imports\n// ======\n//\n//\n\nuse std::{ffi::OsStr, path::Path, time"
  },
  {
    "path": "src/filesystem/internal_filesystem_methods.rs",
    "chars": 3355,
    "preview": "// For stuff like initialization and options.\n\n//\n//\n// ======\n// Imports\n// ======\n//\n//\n\nuse std::path::PathBuf;\n\nuse "
  },
  {
    "path": "src/filesystem/item_flag/flag_struct.rs",
    "chars": 6579,
    "preview": "use bitflags::bitflags;\nuse libc::c_int;\nuse log::warn;\n\n//\n//\n// ======\n// Flag type\n// ======\n//\n//\n\n// Flags are hand"
  },
  {
    "path": "src/filesystem/item_flag/mod.rs",
    "chars": 27,
    "preview": "pub(super) mod flag_struct;"
  },
  {
    "path": "src/filesystem/mod.rs",
    "chars": 162,
    "preview": "mod fuse_filesystem_methods;\nmod internal_filesystem_methods;\npub mod filesystem_struct;\nmod file_handle;\nmod item_flag;"
  },
  {
    "path": "src/filesystem_design/allocation_spec.md",
    "chars": 3350,
    "preview": "# Find and allocate blocks\n\nWe take in a number (u16) of blocks that the caller wishes to reserve.\nWe return a `Result<V"
  },
  {
    "path": "src/filesystem_design/dense_disk.md",
    "chars": 1573,
    "preview": "# Dense disk\n\nSometimes, you've got a really big file. And I mean REALLY big.\n\nIf a file is over the size of a full flop"
  },
  {
    "path": "src/filesystem_design/design_choices.md",
    "chars": 2792,
    "preview": "# Reasons for certain things\n\n# Why 4 byte CRCs?\nAfter 20MB of read-write with random head seeking, I only got 1 failed "
  },
  {
    "path": "src/filesystem_design/disk_header.md",
    "chars": 1643,
    "preview": "# Block layout\n\n512 bytes in size\nA floppy disk can hold 2880 blocks of this size.\n\n# Header update situations:\n\nNew dis"
  },
  {
    "path": "src/filesystem_design/disk_layout.md",
    "chars": 2856,
    "preview": "# Disk Layout\nBlock 0: Disk header\n// Only required on the origin disk\nBlock 1: Inode block\nBlock 2: Directory block\n\nUn"
  },
  {
    "path": "src/filesystem_design/inode_format.md",
    "chars": 3170,
    "preview": "# Example Traversal\n\nLets find `/foo/bar.txt`\n\n### Start at Root Inode\nThe location of the root inode is fixed (Disk 0, "
  },
  {
    "path": "src/filesystem_design/pool_header.md",
    "chars": 1861,
    "preview": "# Pool header\n\nThe root disk only holds information about the pool. Blocks cannot be stored to this disk.\n\n| Offset | Le"
  },
  {
    "path": "src/filesystem_design/pool_layout.md",
    "chars": 157,
    "preview": "# The pool\n\nThe pool will be our highest level of abstraction on top of the disks, every action against the underlying d"
  },
  {
    "path": "src/filesystem_design/possible_speed_improvments.md",
    "chars": 448,
    "preview": "# Speed improvement ideas\n\n# Disk pre-seek\nIf we know we are about to change disks, is it possible to pre-align the head"
  },
  {
    "path": "src/helpers/hex_view.rs",
    "chars": 1727,
    "preview": "// Take in a vec of bytes and return a hex view of it\n\npub fn hex_view(bytes: Vec<u8>) -> String {\n    let mut offset = "
  },
  {
    "path": "src/helpers/mod.rs",
    "chars": 18,
    "preview": "pub mod hex_view;\n"
  },
  {
    "path": "src/lib.rs",
    "chars": 637,
    "preview": "// The library/filesystem cannot use unwraps.\n#![deny(clippy::unwrap_used)]\n\n// Asserts need to have a reason.\n#![deny(c"
  },
  {
    "path": "src/main.rs",
    "chars": 7014,
    "preview": "use std::{\n    ffi::OsStr,\n    path::PathBuf,\n    sync::{\n        atomic::{\n            AtomicBool,\n            Ordering"
  },
  {
    "path": "src/pool/disk/blank_disk/blank_disk_methods.rs",
    "chars": 3680,
    "preview": "// Yep.\n\nuse std::fs::File;\n\nuse log::error;\n\nuse crate::{\n    error_types::drive::DriveError,\n        pool::disk::{\n   "
  },
  {
    "path": "src/pool/disk/blank_disk/blank_disk_struct.rs",
    "chars": 161,
    "preview": "// Need for type constraints\n\n#[derive(Debug)]\npub struct BlankDisk {\n    /// Every disk type needs a file!\n    pub(in s"
  },
  {
    "path": "src/pool/disk/blank_disk/mod.rs",
    "chars": 55,
    "preview": "pub mod blank_disk_methods;\npub mod blank_disk_struct;\n"
  },
  {
    "path": "src/pool/disk/drive_methods.rs",
    "chars": 18176,
    "preview": "// Methods that are generic across all types of disk.\n\n// Using the floppy drive interface should work like this:\n// Req"
  },
  {
    "path": "src/pool/disk/drive_struct.rs",
    "chars": 2252,
    "preview": "// I think I slipped a disk.\n\n// Imports\n\nuse crate::{error_types::drive::DriveError, pool::disk::{\n    blank_disk::blan"
  },
  {
    "path": "src/pool/disk/generic/block/allocate/block_allocation.rs",
    "chars": 6303,
    "preview": "// Find, reserve, or even free blocks!\n\n// We do not allow these operations to be misused, if invalid state is provided,"
  },
  {
    "path": "src/pool/disk/generic/block/allocate/mod.rs",
    "chars": 50,
    "preview": "pub mod block_allocation;\n#[cfg(test)]\nmod tests;\n"
  },
  {
    "path": "src/pool/disk/generic/block/allocate/tests.rs",
    "chars": 4404,
    "preview": "// I allocate development time to testing.\n// Unwrapping is okay here, since we want unexpected outcomes to fail tests.\n"
  },
  {
    "path": "src/pool/disk/generic/block/block_structs.rs",
    "chars": 492,
    "preview": "// Structs that can be deduced from a block\n\n// Imports\nuse crate::pool::disk::generic::generic_structs::pointer_struct:"
  },
  {
    "path": "src/pool/disk/generic/block/crc.rs",
    "chars": 885,
    "preview": "// CRC check\n\n// Check whether the CRC matches the block or not\n// returns true if crc matches the block correctly.\npub "
  },
  {
    "path": "src/pool/disk/generic/block/mod.rs",
    "chars": 53,
    "preview": "pub mod allocate;\npub mod block_structs;\npub mod crc;"
  },
  {
    "path": "src/pool/disk/generic/disk_trait.rs",
    "chars": 1603,
    "preview": "// All types of disk MUST implement this.\n// Enforced by traits.\n\nuse std::fs::File;\n\nuse enum_dispatch::enum_dispatch;\n"
  },
  {
    "path": "src/pool/disk/generic/generic_structs/find_space.rs",
    "chars": 3367,
    "preview": "// A cool function that finds free space in a slice of bytes\n\n// Trait constraint that all input types must meet\npub tra"
  },
  {
    "path": "src/pool/disk/generic/generic_structs/mod.rs",
    "chars": 44,
    "preview": "pub mod find_space;\npub mod pointer_struct;\n"
  },
  {
    "path": "src/pool/disk/generic/generic_structs/pointer_struct.rs",
    "chars": 1290,
    "preview": "/// Points to a specific block on a disk\n\n#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]\npub(crate) struct DiskPoint"
  },
  {
    "path": "src/pool/disk/generic/io/cache/cache_implementation.rs",
    "chars": 42331,
    "preview": "// Non-public cache construction\n\n// Some details about the cache:\n// The lowest tier, 0, is completely emptied when it'"
  },
  {
    "path": "src/pool/disk/generic/io/cache/cache_io.rs",
    "chars": 10153,
    "preview": "// External interaction with the block cache\n\nuse crate::{\n    error_types::drive::DriveError,\n    pool::disk::{\n       "
  },
  {
    "path": "src/pool/disk/generic/io/cache/cached_allocation.rs",
    "chars": 4189,
    "preview": "// Sidestep the disk if possible when marking a block as allocated.\n\nuse log::error;\n\nuse crate::{\n    error_types::driv"
  },
  {
    "path": "src/pool/disk/generic/io/cache/mod.rs",
    "chars": 100,
    "preview": "mod cache_implementation;\npub(crate) mod cache_io;\nmod statistics;\npub(crate) mod cached_allocation;"
  },
  {
    "path": "src/pool/disk/generic/io/cache/statistics.rs",
    "chars": 2326,
    "preview": "// Statistics about the cache\n\nuse std::{collections::VecDeque, sync::Mutex};\n\nuse lazy_static::lazy_static;\n\n// Holds t"
  },
  {
    "path": "src/pool/disk/generic/io/checked_io.rs",
    "chars": 4648,
    "preview": "// IO operations that ensure allocations are properly set.\n// We panic in here if we try to read/write in an invalid way"
  },
  {
    "path": "src/pool/disk/generic/io/mod.rs",
    "chars": 80,
    "preview": "// pub mod checked_io;\npub mod read;\npub mod wipe;\npub mod write;\npub mod cache;"
  },
  {
    "path": "src/pool/disk/generic/io/read.rs",
    "chars": 10610,
    "preview": "// Reading!\n\n// Safety\n#![deny(clippy::unwrap_used)]\n#![deny(clippy::expect_used)]\n\n// Imports\n\nuse log::{\n    error,\n  "
  },
  {
    "path": "src/pool/disk/generic/io/wipe.rs",
    "chars": 1631,
    "preview": "// Squeaky clean!\n\nuse std::{fs::File, time::Duration};\n\nuse log::debug;\n\nuse crate::{error_types::drive::DriveError, po"
  },
  {
    "path": "src/pool/disk/generic/io/write.rs",
    "chars": 10960,
    "preview": "// Writing!\n\n// Safety\n#![deny(clippy::unwrap_used)]\n#![deny(clippy::expect_used)]\n\n// Imports\n\nuse log::{\n    trace,\n  "
  },
  {
    "path": "src/pool/disk/generic/mod.rs",
    "chars": 71,
    "preview": "pub mod block;\npub mod disk_trait;\npub mod generic_structs;\npub mod io;"
  },
  {
    "path": "src/pool/disk/mod.rs",
    "chars": 142,
    "preview": "pub mod blank_disk;\nmod drive_methods;\npub mod drive_struct;\npub mod generic;\npub mod pool_disk;\npub mod standard_disk;\n"
  },
  {
    "path": "src/pool/disk/pool_disk/block/header/header_methods.rs",
    "chars": 16350,
    "preview": "// So no head?\n\n// Imports\n\nuse std::process::exit;\n\nuse log::debug;\nuse log::warn;\n\nuse crate::error_types::drive::Driv"
  },
  {
    "path": "src/pool/disk/pool_disk/block/header/header_struct.rs",
    "chars": 1042,
    "preview": "// Header for the pool disk\n\n// Imports\nuse bitflags::bitflags;\n\nuse crate::pool::disk::generic::generic_structs::pointe"
  },
  {
    "path": "src/pool/disk/pool_disk/block/header/mod.rs",
    "chars": 67,
    "preview": "mod header_methods;\npub mod header_struct;\n#[cfg(test)]\nmod tests;\n"
  },
  {
    "path": "src/pool/disk/pool_disk/block/header/tests.rs",
    "chars": 1998,
    "preview": "// Head in the pool? Preposterous!\n// Unwrapping is okay here, since we want unexpected outcomes to fail tests.\n#![allow"
  },
  {
    "path": "src/pool/disk/pool_disk/block/mod.rs",
    "chars": 16,
    "preview": "pub mod header;\n"
  },
  {
    "path": "src/pool/disk/pool_disk/mod.rs",
    "chars": 68,
    "preview": "pub mod block;\npub mod pool_disk_methods;\npub mod pool_disk_struct;\n"
  },
  {
    "path": "src/pool/disk/pool_disk/pool_disk_methods.rs",
    "chars": 4508,
    "preview": "// Pool disk\n\n//Imports\n\nuse std::fs::File;\n\nuse log::error;\n\nuse crate::{\n    error_types::drive::DriveError,\n    pool:"
  },
  {
    "path": "src/pool/disk/pool_disk/pool_disk_struct.rs",
    "chars": 344,
    "preview": "// poooool\n\n// Imports\n\nuse crate::pool::disk::pool_disk::block::header::header_struct::PoolDiskHeader;\n\n// Structs, Enu"
  },
  {
    "path": "src/pool/disk/standard_disk/block/directory/directory_methods.rs",
    "chars": 13529,
    "preview": "// Directory? Is that come kind of surgery?\n\n// imports\n\n// Implementations\n\nuse log::debug;\n\nuse crate::{error_types::{"
  },
  {
    "path": "src/pool/disk/standard_disk/block/directory/directory_struct.rs",
    "chars": 1574,
    "preview": "// Directory struct!\n\n// Imports\n\nuse bitflags::bitflags;\n\nuse crate::pool::disk::{\n    generic::generic_structs::pointe"
  },
  {
    "path": "src/pool/disk/standard_disk/block/directory/mod.rs",
    "chars": 80,
    "preview": "mod directory_methods;\npub(crate) mod directory_struct;\n#[cfg(test)]\nmod tests;\n"
  },
  {
    "path": "src/pool/disk/standard_disk/block/directory/tests.rs",
    "chars": 5459,
    "preview": "// Directory tests\n// Unwrapping is okay here, since we want unexpected outcomes to fail tests.\n#![allow(clippy::unwrap_"
  },
  {
    "path": "src/pool/disk/standard_disk/block/file_extents/file_extents_methods.rs",
    "chars": 14947,
    "preview": "// Method acting, for extents.\n\n// Consts\n// This may change if I decide to get rid of the flags on data blocks, so here"
  },
  {
    "path": "src/pool/disk/standard_disk/block/file_extents/file_extents_struct.rs",
    "chars": 1583,
    "preview": "// File extents\n\n// Imports\n\nuse bitflags::bitflags;\n\nuse crate::pool::disk::generic::generic_structs::pointer_struct::D"
  },
  {
    "path": "src/pool/disk/standard_disk/block/file_extents/mod.rs",
    "chars": 83,
    "preview": "pub mod file_extents_methods;\npub mod file_extents_struct;\n#[cfg(test)]\nmod tests;\n"
  },
  {
    "path": "src/pool/disk/standard_disk/block/file_extents/tests.rs",
    "chars": 4320,
    "preview": "// Tests are cool.\n\n// Imports\n\nuse crate::error_types::block::BlockManipulationError;\nuse crate::pool::disk::generic::g"
  },
  {
    "path": "src/pool/disk/standard_disk/block/header/header_methods.rs",
    "chars": 2471,
    "preview": "// Imports\n\nuse crate::pool::disk::{\n    generic::{block::{block_structs::RawBlock, crc::add_crc_to_block}, generic_stru"
  },
  {
    "path": "src/pool/disk/standard_disk/block/header/header_struct.rs",
    "chars": 573,
    "preview": "// Imports\nuse bitflags::bitflags;\n\n// Structs, Enums, Flags\n\n/// The header of a disk\n#[derive(Debug, PartialEq, Eq, Cl"
  },
  {
    "path": "src/pool/disk/standard_disk/block/header/mod.rs",
    "chars": 67,
    "preview": "mod header_methods;\npub mod header_struct;\n#[cfg(test)]\nmod tests;\n"
  },
  {
    "path": "src/pool/disk/standard_disk/block/header/tests.rs",
    "chars": 56,
    "preview": "// You need to test head? You can try on me, I guess...\n"
  },
  {
    "path": "src/pool/disk/standard_disk/block/inode/inode_methods.rs",
    "chars": 22783,
    "preview": "// Inode a block, then he moved away.\n\n// Imports\n\n// Implementations\n\nuse std::time::Duration;\nuse std::time::SystemTim"
  },
  {
    "path": "src/pool/disk/standard_disk/block/inode/inode_struct.rs",
    "chars": 3351,
    "preview": "// Inode layout\n\n// Imports\n\nuse bitflags::bitflags;\n\nuse crate::pool::disk::generic::generic_structs::pointer_struct::D"
  },
  {
    "path": "src/pool/disk/standard_disk/block/inode/mod.rs",
    "chars": 65,
    "preview": "mod inode_methods;\npub mod inode_struct;\n#[cfg(test)]\nmod tests;\n"
  },
  {
    "path": "src/pool/disk/standard_disk/block/inode/tests.rs",
    "chars": 7773,
    "preview": "// inode the tests.\n// Unwrapping is okay here, since we want unexpected outcomes to fail tests.\n#![allow(clippy::unwrap"
  },
  {
    "path": "src/pool/disk/standard_disk/block/io/directory/mod.rs",
    "chars": 97,
    "preview": "pub mod movement;\npub mod read;\n#[cfg(test)]\npub mod tests;\npub(crate) mod types;\npub mod write;\n"
  },
  {
    "path": "src/pool/disk/standard_disk/block/io/directory/movement.rs",
    "chars": 5817,
    "preview": "// Helpers to move between directories\n\nuse std::path::{\n    Component,\n    Path\n};\n\nuse log::debug;\n\nuse crate::{error_"
  },
  {
    "path": "src/pool/disk/standard_disk/block/io/directory/read.rs",
    "chars": 16298,
    "preview": "// Higher level abstractions for reading directories.\n\nuse log::{debug, error, warn};\n\nuse crate::{error_types::drive::D"
  },
  {
    "path": "src/pool/disk/standard_disk/block/io/directory/tests.rs",
    "chars": 8694,
    "preview": "// Files, direct to thee.\n// Unwrapping is okay here, since we want unexpected outcomes to fail tests.\n#![allow(clippy::"
  },
  {
    "path": "src/pool/disk/standard_disk/block/io/directory/types.rs",
    "chars": 1925,
    "preview": "// Helper types.\n\nuse crate::pool::disk::standard_disk::block::directory::directory_struct::{\n    DirectoryItemFlags, Di"
  },
  {
    "path": "src/pool/disk/standard_disk/block/io/directory/write.rs",
    "chars": 10801,
    "preview": "// Write a new directory into a directory block\n\nuse log::{debug, error};\n\nuse crate::{error_types::drive::DriveError, p"
  },
  {
    "path": "src/pool/disk/standard_disk/block/io/file/mod.rs",
    "chars": 70,
    "preview": "pub mod write;\npub mod read;\npub mod movement;\n#[cfg(test)]\nmod tests;"
  },
  {
    "path": "src/pool/disk/standard_disk/block/io/file/movement.rs",
    "chars": 2306,
    "preview": "// We need to go to seek points and such.\n\nuse log::debug;\n\nuse crate::{error_types::drive::DriveError, pool::disk::{\n  "
  },
  {
    "path": "src/pool/disk/standard_disk/block/io/file/read.rs",
    "chars": 9844,
    "preview": "// Reading a block is way easier than writing it.\n// Must use cached IO, does not touch disk directly.\n\n\nuse log::{\n    "
  },
  {
    "path": "src/pool/disk/standard_disk/block/io/file/tests.rs",
    "chars": 9050,
    "preview": "// Files, direct to thee.\n// Unwrapping is okay here, since we want unexpected outcomes to fail tests.\n#![allow(clippy::"
  },
  {
    "path": "src/pool/disk/standard_disk/block/io/file/write.rs",
    "chars": 49568,
    "preview": "// Writing files.\n\n// We will take in InodeFile(s) instead of Extent related types, since we need info about how big fil"
  },
  {
    "path": "src/pool/disk/standard_disk/block/io/inode/mod.rs",
    "chars": 53,
    "preview": "pub mod read;\n#[cfg(test)]\nmod tests;\npub mod write;\n"
  },
  {
    "path": "src/pool/disk/standard_disk/block/io/inode/read.rs",
    "chars": 268,
    "preview": "// Functions from other files are used for reading inodes at this level.\n// If you need to touch an inode, you're probab"
  },
  {
    "path": "src/pool/disk/standard_disk/block/io/inode/tests.rs",
    "chars": 1718,
    "preview": "// Inode tests.\n// Unwrapping is okay here, since we want unexpected outcomes to fail tests.\n#![allow(clippy::unwrap_use"
  },
  {
    "path": "src/pool/disk/standard_disk/block/io/inode/write.rs",
    "chars": 8124,
    "preview": "// Inode this was going somewhere...\n\n// Imports\n\n// Implementations\n\n// Functions\n\nuse log::trace;\n\nuse crate::{\n    er"
  },
  {
    "path": "src/pool/disk/standard_disk/block/io/mod.rs",
    "chars": 47,
    "preview": "pub(crate) mod directory;\nmod file;\nmod inode;\n"
  },
  {
    "path": "src/pool/disk/standard_disk/block/mod.rs",
    "chars": 80,
    "preview": "pub mod directory;\nmod file_extents;\npub mod header;\npub mod inode;\npub mod io;\n"
  },
  {
    "path": "src/pool/disk/standard_disk/mod.rs",
    "chars": 72,
    "preview": "pub mod block;\nmod standard_disk_methods;\npub mod standard_disk_struct;\n"
  },
  {
    "path": "src/pool/disk/standard_disk/standard_disk_methods.rs",
    "chars": 12616,
    "preview": "// Imports\nuse std::fs::File;\n\nuse log::debug;\n\nuse crate::{\n    error_types::drive::DriveError,\n    pool::{\n        dis"
  },
  {
    "path": "src/pool/disk/standard_disk/standard_disk_struct.rs",
    "chars": 384,
    "preview": "// Information about a standard disk\n\n// Imports\n\nuse super::block::header::header_struct::StandardDiskHeader;\n\n// Struc"
  },
  {
    "path": "src/pool/disk/unknown_disk/mod.rs",
    "chars": 59,
    "preview": "pub mod unknown_disk_methods;\npub mod unknown_disk_struct;\n"
  },
  {
    "path": "src/pool/disk/unknown_disk/unknown_disk_methods.rs",
    "chars": 3092,
    "preview": "use std::fs::File;\n\nuse crate::{\n    error_types::drive::DriveError,\n    pool::disk::{\n        generic::{\n            bl"
  },
  {
    "path": "src/pool/disk/unknown_disk/unknown_disk_struct.rs",
    "chars": 253,
    "preview": "// Sometimes we know nothing about a disk, but we still need a type for it\n// so we can satisfy type constraints on Disk"
  },
  {
    "path": "src/pool/io/allocate.rs",
    "chars": 13770,
    "preview": "// Pool level block allocations\n\n\nuse log::{debug, error};\n\nuse crate::{\n    error_types::drive::DriveError, pool::{\n   "
  },
  {
    "path": "src/pool/io/mod.rs",
    "chars": 18,
    "preview": "pub mod allocate;\n"
  },
  {
    "path": "src/pool/mod.rs",
    "chars": 55,
    "preview": "pub(crate) mod disk;\npub mod io;\npub mod pool_actions;\n"
  },
  {
    "path": "src/pool/pool_actions/mod.rs",
    "chars": 43,
    "preview": "pub mod pool_methods;\npub mod pool_struct;\n"
  },
  {
    "path": "src/pool/pool_actions/pool_methods.rs",
    "chars": 10264,
    "preview": "// Interacting with the pool\n\n// Imports\n\nuse super::pool_struct::GLOBAL_POOL;\nuse super::pool_struct::Pool;\nuse crate::"
  },
  {
    "path": "src/pool/pool_actions/pool_struct.rs",
    "chars": 813,
    "preview": "// Did you know, if lightning struct a pool, everyone dies?\n// Imports\n\nuse crate::pool::disk::pool_disk::block::header:"
  },
  {
    "path": "src/tui/layout.rs",
    "chars": 15864,
    "preview": "// how da tui looks.\n\nuse std::{\n    time::{\n        Duration,\n        Instant\n    }\n};\n\nuse ratatui::{\n    crossterm,\n "
  },
  {
    "path": "src/tui/mod.rs",
    "chars": 133,
    "preview": "pub(crate) mod layout;\npub(crate) mod state;\npub(crate) mod tui_struct;\npub mod notify;\npub(crate) mod tasks;\npub(crate)"
  },
  {
    "path": "src/tui/notify.rs",
    "chars": 10111,
    "preview": "// Notify the TUI about changes in Fluster!\n// This is the only place we lock the TUI state.\n\nuse std::sync::Mutex;\n\nuse"
  },
  {
    "path": "src/tui/prompts.rs",
    "chars": 13829,
    "preview": "// Gotta talk to people sometimes.\n\nuse std::path::Path;\nuse std::path::PathBuf;\nuse std::time::Instant;\n\nuse log::debug"
  },
  {
    "path": "src/tui/state.rs",
    "chars": 2385,
    "preview": "// Stuff for tracking the state of the TUI and Fluster.\n// Need to be careful in here not to lock at the same time\n// as"
  },
  {
    "path": "src/tui/tasks.rs",
    "chars": 12836,
    "preview": "// Keeping track of what we're working on\n\nuse std::time::Instant;\n\nuse crate::tui::notify::NotifyTui;\n\n\n/// Progress of"
  },
  {
    "path": "src/tui/tui_struct.rs",
    "chars": 450,
    "preview": "// The struct that holds everything needed to render the tui\n\nuse std::time::Instant;\n\nuse crate::tui::{\n    layout::Flu"
  },
  {
    "path": "tests/directory.rs",
    "chars": 15829,
    "preview": "use std::{\n    error::Error,\n    ffi::OsStr,\n    thread,\n    time::Duration\n};\n\nuse log::{\n    error,\n    info\n};\nuse ra"
  },
  {
    "path": "tests/file.rs",
    "chars": 14796,
    "preview": "use std::{thread, time::Duration};\n\nuse rand::{rng, rngs::ThreadRng, Rng, RngCore};\n// We want to see logs while testing"
  },
  {
    "path": "tests/mount_filesystem.rs",
    "chars": 1134,
    "preview": "use std::{thread, time::Duration};\n\nuse test_log::test; // We want to see logs while testing.\nuse crate::test_common::te"
  },
  {
    "path": "tests/start_filesystem.rs",
    "chars": 186,
    "preview": "use test_log::test; // We want to see logs while testing.\npub mod test_common;\n\n#[test]\n// Try starting up the filesyste"
  },
  {
    "path": "tests/test_common.rs",
    "chars": 2357,
    "preview": "use std::{ffi::OsStr, path::PathBuf};\n\nuse fluster_fs::filesystem::filesystem_struct::{FilesystemOptions, FlusterFS};\nus"
  },
  {
    "path": "windows.md",
    "chars": 4596,
    "preview": "This is my punishment for not using Linux in the year of the Linux desktop smh.\nSpecial thanks to [Chris Harringon](http"
  }
]

About this extraction

This page contains the full source code of the DocJade/fluster_rs GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 133 files (678.4 KB), approximately 162.7k tokens, and a symbol index with 593 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!