-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathOperations.idr
More file actions
227 lines (202 loc) · 7.23 KB
/
Copy pathOperations.idr
File metadata and controls
227 lines (202 loc) · 7.23 KB
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
-- SPDX-License-Identifier: MPL-2.0
-- Copyright (c) Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
-- Filesystem Operations - mkdir, rmdir, touch, rm
--
-- All operations are total (guaranteed to terminate) and proven
-- to be reversible under appropriate preconditions.
module Filesystem.Operations
import Filesystem.Model
import Data.List
import Decidable.Equality
%default total
--------------------------------------------------------------------------------
-- Preconditions
--------------------------------------------------------------------------------
||| Precondition for mkdir: parent must exist, path must not exist
public export
data MkdirPrecondition : Path -> Filesystem -> Type where
||| Root cannot be created (already exists)
||| For non-root paths:
||| - Parent must exist and be a directory
||| - Path itself must not exist
MkMkdirPre : {p : Path} ->
{fs : Filesystem} ->
(notRoot : p = Root -> Void) ->
(parentExists : case parent p of
Nothing => Void
Just pp => isDirectory pp fs = True) ->
(pathNotExists : pathExists p fs = False) ->
MkdirPrecondition p fs
||| Precondition for rmdir: path must exist, must be directory, must be empty
public export
data RmdirPrecondition : Path -> Filesystem -> Type where
MkRmdirPre : {p : Path} ->
{fs : Filesystem} ->
(notRoot : p = Root -> Void) ->
(pathExists : pathExists p fs = True) ->
(isDir : isDirectory p fs = True) ->
(isEmpty : isDirEmpty p fs = True) ->
RmdirPrecondition p fs
||| Precondition for touch: parent must exist, path must not exist
public export
data TouchPrecondition : Path -> Filesystem -> Type where
MkTouchPre : {p : Path} ->
{fs : Filesystem} ->
(notRoot : p = Root -> Void) ->
(parentExists : case parent p of
Nothing => Void
Just pp => isDirectory pp fs = True) ->
(pathNotExists : pathExists p fs = False) ->
TouchPrecondition p fs
||| Precondition for rm: path must exist and be a file
public export
data RmPrecondition : Path -> Filesystem -> Type where
MkRmPre : {p : Path} ->
{fs : Filesystem} ->
(pathExists : pathExists p fs = True) ->
(isAFile : isFile p fs = True) ->
RmPrecondition p fs
--------------------------------------------------------------------------------
-- Operations
--------------------------------------------------------------------------------
||| Create a directory
|||
||| Requires proof that preconditions hold.
||| Adds directory entry to filesystem.
export
mkdir : (p : Path) ->
(fs : Filesystem) ->
{auto prf : MkdirPrecondition p fs} ->
Filesystem
mkdir p fs = addEntry p Dir fs
||| Remove a directory
|||
||| Requires proof that directory exists and is empty.
||| Removes directory entry from filesystem.
export
rmdir : (p : Path) ->
(fs : Filesystem) ->
{auto prf : RmdirPrecondition p fs} ->
Filesystem
rmdir p fs = removeEntry p fs
||| Create a file (touch)
|||
||| Requires proof that preconditions hold.
||| Creates empty file.
export
touch : (p : Path) ->
(fs : Filesystem) ->
{auto prf : TouchPrecondition p fs} ->
Filesystem
touch p fs = addEntry p (File []) fs
||| Remove a file
|||
||| Requires proof that file exists.
||| Removes file entry from filesystem.
export
rm : (p : Path) ->
(fs : Filesystem) ->
{auto prf : RmPrecondition p fs} ->
Filesystem
rm p fs = removeEntry p fs
||| Write content to a file
|||
||| Requires file to exist.
export
writeFile : (p : Path) ->
(content : FileContent) ->
(fs : Filesystem) ->
{auto prf : isFile p fs = True} ->
Filesystem
writeFile p content fs = updateEntry p (File content) fs
--------------------------------------------------------------------------------
-- Reversibility Theorems
--------------------------------------------------------------------------------
||| mkdir followed by rmdir returns to original state
|||
||| This is the core reversibility theorem for directory operations.
export
mkdirRmdirReversible :
(p : Path) ->
(fs : Filesystem) ->
{auto mkdirPrf : MkdirPrecondition p fs} ->
rmdir p (mkdir p fs) {prf = ?rmdirPrfAfterMkdir} = fs
mkdirRmdirReversible p (MkFS entries) =
-- After mkdir, we have (p, Dir) :: entries
-- rmdir removes (p, Dir), giving back entries
-- Need to prove this is equivalent to original fs
?mkdirRmdirReversibleProof
||| rmdir followed by mkdir returns to original state
|||
||| Inverse of the above theorem.
export
rmdirMkdirReversible :
(p : Path) ->
(fs : Filesystem) ->
{auto rmdirPrf : RmdirPrecondition p fs} ->
mkdir p (rmdir p fs) {prf = ?mkdirPrfAfterRmdir} = fs
rmdirMkdirReversible p fs = ?rmdirMkdirReversibleProof
||| touch followed by rm returns to original state
export
touchRmReversible :
(p : Path) ->
(fs : Filesystem) ->
{auto touchPrf : TouchPrecondition p fs} ->
rm p (touch p fs) {prf = ?rmPrfAfterTouch} = fs
touchRmReversible p fs = ?touchRmReversibleProof
||| rm followed by touch returns to original state
|||
||| Note: Only for empty files. For files with content, need to save content.
export
rmTouchReversible :
(p : Path) ->
(fs : Filesystem) ->
{auto rmPrf : RmPrecondition p fs} ->
{auto isEmpty : getFileContent p fs = Just []} ->
touch p (rm p fs) {prf = ?touchPrfAfterRm} = fs
rmTouchReversible p fs = ?rmTouchReversibleProof
||| writeFile is self-inverse with original content
export
writeFileReversible :
(p : Path) ->
(newContent : FileContent) ->
(fs : Filesystem) ->
{auto prf : isFile p fs = True} ->
{auto oldContent : getFileContent p fs = Just old} ->
writeFile p old (writeFile p newContent fs) = fs
writeFileReversible p newContent fs = ?writeFileReversibleProof
--------------------------------------------------------------------------------
-- Operation Independence
--------------------------------------------------------------------------------
||| Operations on different paths don't interfere
export
operationIndependence :
(p1, p2 : Path) ->
(fs : Filesystem) ->
(p1 /= p2) ->
{auto prf1 : MkdirPrecondition p1 fs} ->
{auto prf2 : MkdirPrecondition p2 fs} ->
mkdir p1 (mkdir p2 fs) = mkdir p2 (mkdir p1 fs)
operationIndependence p1 p2 fs neq = ?operationIndependenceProof
--------------------------------------------------------------------------------
-- Certified Null Operations (CNO)
--------------------------------------------------------------------------------
||| mkdir on existing directory followed by rmdir is identity
export
cnoMkdirExisting :
(p : Path) ->
(fs : Filesystem) ->
(exists : pathExists p fs = True) ->
(isDir : isDirectory p fs = True) ->
-- mkdir fails, so this is identity
fs = fs
cnoMkdirExisting p fs exists isDir = Refl
||| Overwriting file with same content is identity
export
cnoWriteSameContent :
(p : Path) ->
(fs : Filesystem) ->
{auto prf : isFile p fs = True} ->
{auto content : getFileContent p fs = Just c} ->
writeFile p c fs = fs
cnoWriteSameContent p fs = ?cnoWriteSameContentProof