9
9
| File_exists of path
10
10
| Is_directory of path
11
11
| Remove of path * string
12
+ | Rename of path * path
12
13
| Mkdir of path * string
13
14
| Rmdir of path * string
14
15
| Readdir of path
76
77
map (fun path -> File_exists path) (path_gen s);
77
78
map (fun path -> Is_directory path) (path_gen s);
78
79
map (fun (path ,new_dir_name ) -> Remove (path, new_dir_name)) (pair_gen s);
80
+ map2 (fun old_path new_path -> Rename (old_path, new_path)) (path_gen s) (path_gen s);
79
81
map (fun (path ,new_dir_name ) -> Mkdir (path, new_dir_name)) (pair_gen s);
80
82
map (fun (path ,delete_dir_name ) -> Rmdir (path, delete_dir_name)) (pair_gen s);
81
83
map (fun path -> Readdir path) (path_gen s);
@@ -102,6 +104,35 @@ struct
102
104
103
105
let mem_model fs path = find_opt_model fs path <> None
104
106
107
+ let path_is_a_dir fs path =
108
+ match find_opt_model fs path with
109
+ | None
110
+ | Some File -> false
111
+ | Some (Directory _ ) -> true
112
+
113
+ let path_is_a_file fs path =
114
+ match find_opt_model fs path with
115
+ | None
116
+ | Some (Directory _ ) -> false
117
+ | Some File -> true
118
+
119
+ let rec path_prefixes path = match path with
120
+ | [] -> []
121
+ | [_] -> []
122
+ | n ::ns -> [n]::(List. map (fun p -> n::p) (path_prefixes ns))
123
+
124
+ let separate_path path =
125
+ match List. rev path with
126
+ | [] -> None
127
+ | name ::rev_path -> Some (List. rev rev_path, name)
128
+
129
+ let rec is_true_prefix path1 path2 = match path1, path2 with
130
+ | [] , [] -> false
131
+ | [] , _ ::_ -> true
132
+ | _ ::_ , [] -> false
133
+ | n1 ::p1 , n2 ::p2 -> n1= n2 && is_true_prefix p1 p2
134
+
135
+ (* generic removal function *)
105
136
let rec remove_model fs path file_name =
106
137
match fs with
107
138
| File -> fs
@@ -110,46 +141,14 @@ struct
110
141
| [] ->
111
142
(match Map_names. find_opt file_name d.fs_map with
112
143
| None
113
- | Some (Directory _ ) -> fs
114
- | Some File -> Directory { fs_map = Map_names. remove file_name d.fs_map }
115
- )
144
+ | Some _ -> Directory { fs_map = Map_names. remove file_name d.fs_map })
116
145
| dir ::dirs ->
117
146
Directory
118
147
{ fs_map = Map_names. update dir (function
119
148
| None -> None
120
149
| Some File -> Some File
121
150
| Some (Directory _ as d' ) -> Some (remove_model d' dirs file_name)) d.fs_map
122
- }
123
- (*
124
- (match Map_names.find_opt dir d.fs_map with
125
- | None
126
- | Some File -> fs
127
- | Some (Directory _ as d') ->
128
- let fs' = remove_model d' dirs file_name in
129
- Directory { fs_map = Map_names.update dir d.fs_map }
130
- )
131
- *)
132
- )
133
-
134
- let rec mkdir_model fs path new_dir_name =
135
- match fs with
136
- | File -> fs
137
- | Directory d ->
138
- (match path with
139
- | [] ->
140
- let new_dir = Directory {fs_map = Map_names. empty} in
141
- Directory {fs_map = Map_names. add new_dir_name new_dir d.fs_map}
142
- | next_in_path :: tl_path ->
143
- (match Map_names. find_opt next_in_path d.fs_map with
144
- | None -> fs
145
- | Some sub_fs ->
146
- let nfs = mkdir_model sub_fs tl_path new_dir_name in
147
- if nfs = sub_fs
148
- then fs
149
- else
150
- let new_map = Map_names. remove next_in_path d.fs_map in
151
- let new_map = Map_names. add next_in_path nfs new_map in
152
- Directory {fs_map = new_map}))
151
+ })
153
152
154
153
let readdir_model fs path =
155
154
match find_opt_model fs path with
@@ -159,60 +158,70 @@ struct
159
158
| File -> None
160
159
| Directory d -> Some (Map_names. fold (fun k _ l -> k::l) d.fs_map [] ))
161
160
162
- let rec rmdir_model fs path delete_dir_name =
161
+ (* generic insertion function *)
162
+ let rec insert_model fs path new_file_name sub_tree =
163
163
match fs with
164
164
| File -> fs
165
165
| Directory d ->
166
166
(match path with
167
167
| [] ->
168
- (match Map_names. find_opt delete_dir_name d.fs_map with
169
- | Some (Directory target ) when Map_names. is_empty target.fs_map ->
170
- Directory {fs_map = Map_names. remove delete_dir_name d.fs_map}
171
- | None | Some File | Some (Directory _ ) -> fs)
168
+ Directory {fs_map = Map_names. add new_file_name sub_tree d.fs_map}
172
169
| next_in_path :: tl_path ->
173
170
(match Map_names. find_opt next_in_path d.fs_map with
174
171
| None -> fs
175
172
| Some sub_fs ->
176
- let nfs = rmdir_model sub_fs tl_path delete_dir_name in
177
- if nfs = sub_fs
178
- then fs
179
- else Directory {fs_map = (update_map_name d.fs_map next_in_path nfs)}))
180
-
181
- let rec mkfile_model fs path new_file_name =
182
- match fs with
183
- | File -> fs
184
- | Directory d ->
185
- (match path with
186
- | [] ->
187
- let new_file = File in
188
- Directory {fs_map = Map_names. add new_file_name new_file d.fs_map}
189
- | next_in_path :: tl_path ->
190
- (match Map_names. find_opt next_in_path d.fs_map with
191
- | None -> fs
192
- | Some sub_fs ->
193
- let nfs = mkfile_model sub_fs tl_path new_file_name in
173
+ let nfs = insert_model sub_fs tl_path new_file_name sub_tree in
194
174
if nfs = sub_fs
195
175
then fs
196
176
else Directory {fs_map = update_map_name d.fs_map next_in_path nfs}))
197
177
178
+ let rename_model fs old_path new_path =
179
+ match separate_path old_path, separate_path new_path with
180
+ | None , _
181
+ | _ , None -> fs
182
+ | Some (old_path_pref , old_name ), Some (new_path_pref , new_name ) ->
183
+ (match find_opt_model fs new_path_pref with
184
+ | None
185
+ | Some File -> fs
186
+ | Some (Directory _ ) ->
187
+ (match find_opt_model fs old_path with
188
+ | None -> fs
189
+ | Some sub_fs ->
190
+ let fs' = remove_model fs old_path_pref old_name in
191
+ insert_model fs' new_path_pref new_name sub_fs))
192
+
198
193
let next_state c fs =
199
194
match c with
200
195
| File_exists _path -> fs
201
196
| Mkdir (path , new_dir_name ) ->
202
197
if mem_model fs (path @ [new_dir_name])
203
198
then fs
204
- else mkdir_model fs path new_dir_name
205
- | Remove (path , file_name ) -> remove_model fs path file_name
199
+ else insert_model fs path new_dir_name (Directory {fs_map = Map_names. empty})
200
+ | Remove (path , file_name ) ->
201
+ if find_opt_model fs (path @ [file_name]) = Some File
202
+ then remove_model fs path file_name
203
+ else fs
204
+ | Rename (old_path , new_path ) ->
205
+ if is_true_prefix old_path new_path
206
+ then fs
207
+ else
208
+ (match find_opt_model fs old_path with
209
+ | None -> fs
210
+ | Some File ->
211
+ if (not (mem_model fs new_path) || path_is_a_file fs new_path) then rename_model fs old_path new_path else fs
212
+ | Some (Directory _ ) ->
213
+ if (not (mem_model fs new_path) || readdir_model fs new_path = Some [] ) then rename_model fs old_path new_path else fs)
206
214
| Is_directory _path -> fs
207
215
| Rmdir (path ,delete_dir_name ) ->
208
- if mem_model fs (path @ [delete_dir_name])
209
- then rmdir_model fs path delete_dir_name
216
+ let complete_path = path @ [delete_dir_name] in
217
+ if mem_model fs complete_path && readdir_model fs complete_path = Some []
218
+ then remove_model fs path delete_dir_name
210
219
else fs
211
220
| Readdir _path -> fs
212
221
| Mkfile (path , new_file_name ) ->
213
222
if mem_model fs (path @ [new_file_name])
214
223
then fs
215
- else mkfile_model fs path new_file_name
224
+ else insert_model fs path new_file_name File
216
225
217
226
let init_sut () =
218
227
try Sys. mkdir sandbox_root 0o700
@@ -237,6 +246,7 @@ struct
237
246
| File_exists path -> Res (bool , Sys. file_exists (p path))
238
247
| Is_directory path -> Res (result bool exn , protect Sys. is_directory (p path))
239
248
| Remove (path , file_name ) -> Res (result unit exn , protect Sys. remove ((p path) / file_name))
249
+ | Rename (old_path , new_path ) -> Res (result unit exn , protect (Sys. rename (p old_path)) (p new_path))
240
250
| Mkdir (path , new_dir_name ) ->
241
251
Res (result unit exn , protect (Sys. mkdir ((p path) / new_dir_name)) 0o755 )
242
252
| Rmdir (path , delete_dir_name ) ->
@@ -246,18 +256,6 @@ struct
246
256
| Mkfile (path , new_file_name ) ->
247
257
Res (result unit exn , protect mkfile (p path / new_file_name))
248
258
249
- let fs_is_a_dir fs = match fs with | Directory _ -> true | File -> false
250
-
251
- let path_is_a_dir fs path =
252
- match find_opt_model fs path with
253
- | None -> false
254
- | Some target_fs -> fs_is_a_dir target_fs
255
-
256
- let rec path_prefixes path = match path with
257
- | [] -> []
258
- | [_] -> []
259
- | n ::ns -> [n]::(List. map (fun p -> n::p) (path_prefixes ns))
260
-
261
259
let postcond c (fs : filesys ) res =
262
260
match c, res with
263
261
| File_exists path , Res ((Bool,_ ),b ) -> b = mem_model fs path
@@ -269,19 +267,34 @@ struct
269
267
| Some File -> b = false
270
268
| None -> false )
271
269
| Error (Sys_error s ) ->
272
- (s = (p path) ^ " : No such file or directory" && find_opt_model fs path = None ) ||
273
- (s = p path ^ " : Not a directory" && List. exists (fun pref -> Some File = find_opt_model fs pref) (path_prefixes path))
270
+ (s = (p path) ^ " : No such file or directory" && not (mem_model fs path) ) ||
271
+ (s = p path ^ " : Not a directory" && List. exists (fun pref -> not (path_is_a_dir fs pref) ) (path_prefixes path))
274
272
| _ -> false )
275
273
| Remove (path , file_name ), Res ((Result (Unit,Exn),_ ), res ) ->
276
274
let complete_path = (path @ [file_name]) in
277
275
(match res with
278
276
| Ok () -> mem_model fs complete_path && path_is_a_dir fs path && not (path_is_a_dir fs complete_path)
279
277
| Error (Sys_error s ) ->
280
- (s = (p complete_path) ^ " : No such file or directory" && find_opt_model fs complete_path = None ) ||
278
+ (s = (p complete_path) ^ " : No such file or directory" && not (mem_model fs complete_path) ) ||
281
279
(s = (p complete_path) ^ " : Is a directory" && path_is_a_dir fs complete_path) ||
282
280
(s = (p complete_path) ^ " : Not a directory" && not (path_is_a_dir fs path))
283
281
| Error _ -> false
284
282
)
283
+ | Rename (old_path , new_path ), Res ((Result (Unit,Exn),_ ), res ) ->
284
+ (match res with
285
+ | Ok () -> mem_model fs old_path
286
+ | Error (Sys_error s ) ->
287
+ (s = " No such file or directory" &&
288
+ not (mem_model fs old_path) || List. exists (fun pref -> not (path_is_a_dir fs pref)) (path_prefixes new_path)) ||
289
+ (s = " Invalid argument" && is_true_prefix old_path new_path) ||
290
+ (s = " Not a directory" &&
291
+ List. exists (path_is_a_file fs) (path_prefixes old_path) ||
292
+ List. exists (path_is_a_file fs) (path_prefixes new_path) ||
293
+ path_is_a_dir fs old_path && mem_model fs new_path && not (path_is_a_dir fs new_path)) ||
294
+ (s = " Is a directory" && path_is_a_dir fs new_path) ||
295
+ (s = " Directory not empty" &&
296
+ is_true_prefix new_path old_path || (path_is_a_dir fs new_path && not (readdir_model fs new_path = Some [] )))
297
+ | Error _ -> false )
285
298
| Mkdir (path , new_dir_name ), Res ((Result (Unit,Exn),_ ), res ) ->
286
299
let complete_path = (path @ [new_dir_name]) in
287
300
(match res with
0 commit comments