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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
//! Per-operation options shared by the embedded runtime and HTTP client.
use crate::{
AccessGrants, AccessRevisionNo, ActorId, AttributeKey, AttributeRevisionNo, AttributeValue,
CommitId, CommitPrecondition, DeleteDirectoryBehavior, DestinationBehavior, InodeId,
RevisionNo,
};
use crate::{SnapshotId, Subject};
use std::collections::BTreeMap;
/// Whether a path or inode read includes the attribute projection.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum AttributeInclusion {
/// Include the inode's attribute map and revision.
Include,
/// Omit the inode's attribute map and revision.
#[default]
Omit,
}
impl std::fmt::Display for AttributeInclusion {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(match self {
Self::Include => "true",
Self::Omit => "false",
})
}
}
/// Commit settings shared by every filesystem mutation.
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CommitOptions {
/// Actor responsible for the commit, as supplied by the application.
pub actor_id: ActorId,
/// The subject whose grants authorize the commit.
#[serde(skip)]
pub subject: Option<Subject>,
/// The optional idempotency key, generated by LoonFS when absent.
pub commit_id: Option<CommitId>,
/// The optional commit message that forms part of the commit identity.
pub message: Option<String>,
/// Ordered admission conditions evaluated before any operations.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub preconditions: Vec<CommitPrecondition>,
}
impl CommitOptions {
/// Creates settings with no commit ID or message.
pub fn new(actor: ActorId) -> Self {
Self {
actor_id: actor,
subject: None,
commit_id: None,
message: None,
preconditions: Vec::new(),
}
}
}
/// Options for stating one path.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StatPathOptions {
/// Whether to include the inode's attribute map and revision, enabled by default.
pub include_attributes: AttributeInclusion,
/// Read the entry from this snapshot.
pub snapshot_id: Option<SnapshotId>,
}
impl Default for StatPathOptions {
fn default() -> Self {
Self {
include_attributes: AttributeInclusion::Include,
snapshot_id: None,
}
}
}
/// Options for listing a directory.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ListPathEntriesOptions {
/// Whether to include each entry's attribute map and revision, disabled by default.
pub include_attributes: AttributeInclusion,
/// Read the directory from this snapshot.
pub snapshot_id: Option<SnapshotId>,
}
/// Options for listing a directory's children by parent inode.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ListInodeChildrenOptions {
/// Whether to include each entry's attribute map and revision, disabled by default.
pub include_attributes: AttributeInclusion,
/// Read the directory from this snapshot.
pub snapshot_id: Option<SnapshotId>,
}
/// Options for writing and removing an inode's attributes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UpdateAttributesOptions {
/// The attributes to write, replacing values for matching keys and leaving other
/// keys unchanged.
pub set: BTreeMap<AttributeKey, AttributeValue>,
/// Keys to remove.
pub remove: Vec<AttributeKey>,
/// Actor, commit ID, and message.
pub commit: CommitOptions,
/// The inode that the path must still resolve to before the update.
pub expected_inode_id: Option<InodeId>,
/// With an inode precondition, the attribute revision that must still be current.
pub expected_attributes_revision_no: Option<AttributeRevisionNo>,
}
impl UpdateAttributesOptions {
/// Creates an empty attribute update for this actor.
pub fn new(actor: ActorId) -> Self {
Self {
set: BTreeMap::new(),
remove: Vec::new(),
commit: CommitOptions::new(actor),
expected_inode_id: None,
expected_attributes_revision_no: None,
}
}
}
/// Options for replacing an inode's access row.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UpdateAccessOptions {
/// Whether the directory stops inheritance from its ancestors.
pub boundary: bool,
/// The complete direct grants after the update.
pub grants: AccessGrants,
/// Actor, commit ID, and message.
pub commit: CommitOptions,
/// The inode that the path must still resolve to before the update.
pub expected_inode_id: Option<InodeId>,
/// With an inode precondition, the access revision that must still be current.
pub expected_access_revision_no: Option<AccessRevisionNo>,
}
impl UpdateAccessOptions {
/// An update that replaces the grants and clears the boundary.
pub fn new(actor: ActorId, grants: AccessGrants) -> Self {
Self {
boundary: false,
grants,
commit: CommitOptions::new(actor),
expected_inode_id: None,
expected_access_revision_no: None,
}
}
}
/// Options for writing a file path.
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PutFileOptions {
/// Create-only or replace-existing behavior.
pub behavior: DestinationBehavior,
/// Actor, commit ID, and message.
pub commit: CommitOptions,
/// The inode that the path must still reference when using `Replace` behavior.
pub expected_inode_id: Option<InodeId>,
/// The revision that must still be current when using `Replace` behavior with
/// `expected_inode_id`.
pub expected_revision_no: Option<RevisionNo>,
}
impl PutFileOptions {
/// Creates options that refuse to replace an existing file.
pub fn new(actor: ActorId) -> Self {
Self {
behavior: DestinationBehavior::NoReplace,
commit: CommitOptions::new(actor),
expected_inode_id: None,
expected_revision_no: None,
}
}
}
/// Options for creating a directory.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CreateDirectoryOptions {
/// Actor, commit ID, and message.
pub commit: CommitOptions,
/// Also create missing ancestor directories, like `put_file` does.
pub parents: bool,
}
impl CreateDirectoryOptions {
/// Creates options that do not create missing parent directories.
pub fn new(actor: ActorId) -> Self {
Self {
commit: CommitOptions::new(actor),
parents: false,
}
}
}
/// Options for deleting a path.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeleteOptions {
/// Directory delete behavior.
pub behavior: DeleteDirectoryBehavior,
/// Actor, commit ID, and message.
pub commit: CommitOptions,
/// The inode that the path must still resolve to before deletion.
pub expected_inode_id: Option<InodeId>,
}
impl DeleteOptions {
/// Creates options for a non-recursive delete.
pub fn new(actor: ActorId) -> Self {
Self {
behavior: DeleteDirectoryBehavior::NonRecursive,
commit: CommitOptions::new(actor),
expected_inode_id: None,
}
}
}
/// Options for moving a path.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MoveOptions {
/// Create-only or replace-existing behavior for the destination.
pub behavior: DestinationBehavior,
/// Actor, commit ID, and message.
pub commit: CommitOptions,
/// The inode that the destination must still reference when using `Replace` behavior.
pub expected_destination_inode_id: Option<InodeId>,
/// The revision that must still be current when using `Replace` behavior with
/// `expected_destination_inode_id`.
pub expected_destination_revision_no: Option<RevisionNo>,
}
impl MoveOptions {
/// Creates options that refuse to replace the destination.
pub fn new(actor: ActorId) -> Self {
Self {
behavior: DestinationBehavior::NoReplace,
commit: CommitOptions::new(actor),
expected_destination_inode_id: None,
expected_destination_revision_no: None,
}
}
}
/// Options for copying a file path.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CopyOptions {
/// Create-only or replace-existing behavior for the destination.
pub behavior: DestinationBehavior,
/// Actor, commit ID, and message.
pub commit: CommitOptions,
/// The inode that the destination must still reference when using `Replace` behavior.
pub expected_destination_inode_id: Option<InodeId>,
/// The revision that must still be current when using `Replace` behavior with
/// `expected_destination_inode_id`.
pub expected_destination_revision_no: Option<RevisionNo>,
}
impl CopyOptions {
/// Creates options that refuse to replace the destination.
pub fn new(actor: ActorId) -> Self {
Self {
behavior: DestinationBehavior::NoReplace,
commit: CommitOptions::new(actor),
expected_destination_inode_id: None,
expected_destination_revision_no: None,
}
}
}
/// Options for restoring a file revision by path.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RestoreRevisionOptions {
/// Actor, commit ID, and message.
pub commit: CommitOptions,
}
impl RestoreRevisionOptions {
/// Creates restore options for this actor.
pub fn new(actor: ActorId) -> Self {
Self {
commit: CommitOptions::new(actor),
}
}
}
/// Options for recovering a deleted file or subtree.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UndeleteOptions {
/// Actor, commit ID, and message.
pub commit: CommitOptions,
}
impl UndeleteOptions {
/// Creates undelete options for this actor.
pub fn new(actor: ActorId) -> Self {
Self {
commit: CommitOptions::new(actor),
}
}
}
/// Options for starting a direct multipart upload.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct DirectMultipartUploadOptions {
/// The byte length of every part except the last, or `None` for the server
/// default; providers allow at most 10,000 parts.
pub part_size_bytes: Option<u64>,
}
/// Selects the source state for a namespace fork.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ForkNamespaceOptions {
/// Application-supplied actor creating the namespace.
pub actor_id: ActorId,
/// Fork from this live snapshot instead of the current head.
pub snapshot_id: Option<crate::SnapshotId>,
}
impl ForkNamespaceOptions {
/// Selects the current head.
pub fn new(actor_id: ActorId) -> Self {
Self {
actor_id,
snapshot_id: None,
}
}
}