1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
//
// Wildland Project
//
// Copyright © 2022 Golem Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 3 as published by
// the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

pub mod error;
pub mod events;
pub mod fs_stat;
pub mod node_stat;
pub mod stream;
pub mod wl_permissions;

use std::fmt::Display;
use std::sync::{Arc, Mutex};

pub use error::DfsFrontendError;
pub use events::EventReceiver;
pub use fs_stat::FsStat;
pub use node_stat::NodeStat;
pub use stream::*;
pub use wl_permissions::WlPermissions;

use super::unix_timestamp::UnixTimestamp;
use crate::Storage;

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct DirEntry {
    /// file or dir name
    pub item_name: String,

    pub stat: NodeStat,
}

impl DirEntry {
    pub fn item_name(&self) -> String {
        self.item_name.clone()
    }

    pub fn stat(&self) -> NodeStat {
        self.stat.clone()
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SpaceUsage {
    used_space: u64,
    total_space: u64,
}

impl SpaceUsage {
    pub fn new(used_space: u64, total_space: u64) -> Self {
        Self {
            used_space,
            total_space,
        }
    }

    pub fn get_used_space(&self) -> u64 {
        self.used_space
    }

    pub fn get_total_space(&self) -> u64 {
        self.total_space
    }
}

impl Display for SpaceUsage {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}/{} bytes",
            self.get_used_space(),
            self.get_total_space()
        )
    }
}

/// Interface that DFS exposes towards filesystem-like frontend providers
///
/// DFS methods may return error that are not related with particular operation but rather
/// with wildland system in general. Those errors could be:
///
/// - `PathResolutionError` - happens when path resolving failed, e.g. due to the catlib error.
/// - `StorageNotResponsive` - happens when none of storages that operation involves returns an answer.
/// - `Generic` - unanticipated errors.
#[mockall::automock]
pub trait DfsFrontend: Send + Sync {
    /// Returns vector of entries found under the provided path.
    /// It may merge entries from multiple containers.
    ///
    /// # Errors:
    /// - `NotADirectory` - for paths that don't represent directories
    /// - `NoSuchPath` - requested path does not exist
    ///
    fn read_dir(&self, path: String) -> Result<Vec<DirEntry>, DfsFrontendError>;

    /// Returns metadata of a node.
    ///
    /// # Errors:
    ///
    /// - `NoSuchPath` - requested path does not exist
    fn metadata(&self, path: String) -> Result<NodeStat, DfsFrontendError>;

    /// Removes a file
    ///
    /// # Errors:
    /// - `NoSuchPath` - requested path does not exist
    /// - `NotAFile` - provided path represents a node that is not a file
    fn remove_file(&self, path: String) -> Result<(), DfsFrontendError>;

    /// Rename a file or directory to a new path, if new path does not exist yet.
    /// In contrast to POSIX-like rename operation, it returns error in case of new path existence
    /// in all cases, so it is up to a caller whether to remove a node under new path or not.
    ///
    /// # Errors:
    /// `NoSuchPath` - source not found
    /// `SourceIsParentOfTarget` - new directory would be a subdirectory of itself
    /// `MoveBetweenContainers` - `new_path` is in different Container than `old_path`
    /// `PathAlreadyExists` - `new_path` already exists
    fn rename(&self, old_path: String, new_path: String) -> Result<(), DfsFrontendError>;

    /// Changes the permissions of the underlying file.
    fn set_permissions(
        &self,
        path: String,
        permissions: WlPermissions,
    ) -> Result<(), DfsFrontendError>;

    /// Not supported yet - it always returns `DfsFrontendError::Generic(_)`
    fn set_owner(&self, path: String) -> Result<(), DfsFrontendError>;

    /// Creates a new, empty directory at the provided path
    ///
    /// # Errors:
    /// `InvalidParent` - a parent of the given path doesn’t exist.
    /// `PathAlreadyExists` - path already exists.
    fn create_dir(&self, path: String) -> Result<(), DfsFrontendError>;

    /// Removes a directory
    /// If `is_recursive` is set to true, it will remove all the children of the directory
    ///
    /// # Errors:
    /// `NotADirectory` - path does not represent a directory
    /// `NoSuchPath` - no such path exists
    /// `DirNotEmpty` - directory is not empty
    fn remove_dir(&self, path: String, is_recursive: bool) -> Result<(), DfsFrontendError>;

    /// Returns information about a mounted filesystem. Path is the pathname of any file within the
    /// mounted filesystem.
    ///
    /// # Errors:
    /// `NoSuchPath` - no such path exists
    fn stat_fs(&self, path: String) -> Result<FsStat, DfsFrontendError>;

    // Methods that are not part of the Posix-like FS API

    /// Returns receiver that can listen to DFS events.
    /// Events may be split between different `EventReceiver`.
    fn get_receiver(&self) -> Arc<Mutex<dyn EventReceiver>>;

    /// Attempts to mount given Storage. This method's state
    /// should not be considered emphemeral.
    ///
    /// This functionality may be used during container mount to ensure
    /// storage being healthy, before any FS operations are performed.
    fn mount(&self, storage: &Storage) -> Result<(), DfsFrontendError>;

    /// Returns (used, total) space in bytes
    ///
    fn get_space_usage(&self, storage: &Storage) -> Result<SpaceUsage, DfsFrontendError>;

    /// Checks whether the Storage is accessible
    ///
    fn is_accessible(&self, storage: &Storage) -> Result<bool, DfsFrontendError>;

    //
    // Non-POSIX-like access to files
    //

    /// Downloads a file and writes it to the provided output stream
    ///
    fn download(
        &self,
        path: String,
        output: Box<dyn OStream>,
        progress_reporter: Box<dyn ProgressReporter>,
        abort_flag: &AbortFlag,
    ) -> Result<(), DfsFrontendError>;

    /// Reads a file from the provided input stream and uploads it to the backend
    ///
    fn upload(
        &self,
        path: String,
        input: Box<dyn IStream>,
        progress_reporter: Box<dyn ProgressReporter>,
        abort_flag: &AbortFlag,
        creation_time: Option<UnixTimestamp>,
    ) -> Result<(), DfsFrontendError>;

    /// Finds path of object which matches the uuid
    ///
    fn get_path(&self, identifier: String) -> Result<String, DfsFrontendError>;
}