patch #7448
[mldonkey.git] / src / utils / lib / unix32.ml
blob13d34b88434f067705987435d59ed5a8629fd639
2 (* Copyright 2001, 2002 b8_bavard, b8_fee_carabine, INRIA *)
3 (*
4 This file is part of mldonkey.
6 mldonkey is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 mldonkey is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with mldonkey; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 open Int64ops
23 open Printf2
25 let log_prefix = "[Ux32]"
27 let lprintf_nl fmt =
28 lprintf_nl2 log_prefix fmt
30 let chunk_min_size = ref 65000L
32 let max_buffered = ref (Int64.of_int (1024 * 1024))
34 let create_file_mode = ref 0o664
35 let create_dir_mode = ref 0o755
36 let verbose = ref false
37 let max_cache_size_default = 50
38 let max_cache_size = ref max_cache_size_default
40 let mini (x: int) (y: int) =
41 if x > y then y else x
43 let ro_flag = [Unix.O_RDONLY]
44 let rw_flag = [Unix.O_RDWR]
45 let rw_creat_flag = [Unix.O_CREAT; Unix.O_RDWR]
47 external external_start : unit -> unit = "external_start"
48 external external_exit : unit -> unit = "external_exit"
49 external uname : unit -> string = "ml_uname"
50 external os_supported : unit -> bool = "ml_os_supported"
52 (* let really_write fd s pos len =
53 try
54 Unix2.really_write fd s pos len
55 with e -> raise e *)
56 let really_write = Unix2.really_write
58 module FDCache = struct
60 type t = {
61 mutable fd : Unix.file_descr option;
62 mutable filename : string;
63 mutable writable : bool;
64 mutable destroyed : bool; (* could we get rid of this ? *)
67 let cache_size = ref 0
68 let cache = Fifo.create ()
70 let close t =
71 if not t.destroyed then
72 match t.fd with
73 | Some fd ->
74 if !verbose then lprintf_nl "Close %s" t.filename;
75 (try Unix.close fd with e ->
76 lprintf_nl "Exception in FDCache.close %s: %s"
77 t.filename
78 (Printexc2.to_string e);
79 raise e);
80 t.fd <- None;
81 decr cache_size
82 | None -> ()
84 let is_closed t =
85 if t.destroyed then
86 true
87 else match t.fd with
88 | Some fd -> false
89 | None -> true
91 let rec close_one () =
92 if not (Fifo.empty cache) then
93 let t = Fifo.take cache in
94 match t.fd with
95 | None ->
96 close_one ()
97 | Some fd ->
98 try
99 close t
100 with _ -> close_one ()
102 let check_destroyed t =
103 if t.destroyed then
104 failwith (Printf.sprintf
105 "Unix32.check_destroyed %s: Cannot use destroyed FD" t.filename)
107 let destroy t =
108 if not t.destroyed then begin
109 (try close t with _ -> ());
110 t.destroyed <- true
113 let _local_force_fd creat t =
114 check_destroyed t;
115 let fd =
116 match t.fd with
117 | Some fd -> fd
118 | None ->
119 if !cache_size >= !max_cache_size then close_one ();
120 let fd = Unix2.tryopen_umask 0 (fun _old_umask ->
121 (* disable umask restrictions temporarily;
122 rights on downloaded files should be dictated
123 by create_file_mode instead *)
125 if t.writable then
126 Unix.openfile t.filename
127 (if creat then rw_creat_flag else rw_flag)
128 !create_file_mode
129 else
130 Unix.openfile t.filename ro_flag 0o400
131 with e ->
132 if !verbose then lprintf_nl "Exception in FDCache._local_force_fd %s (%s): %s"
133 t.filename
134 (if t.writable then "rw" else "ro")
135 (Printexc2.to_string e);
136 raise e)
138 incr cache_size;
139 (* lprintf "local_force: opening %d\n" (Obj.magic fd_rw); *)
140 Fifo.put cache t;
141 t.fd <- Some fd;
144 (* lprintf "local_force_fd %d\n" (Obj.magic fd); *)
147 let create f writable =
148 if !verbose then lprintf_nl "Open %s (%s)" f (if writable then "rw" else "ro");
149 let t =
151 filename = f;
152 writable = writable;
153 fd = None;
154 destroyed = false;
158 let _fd = _local_force_fd true t in
160 with
161 Unix.Unix_error (Unix.EISDIR, _, _) -> t
162 | Unix.Unix_error (Unix.EACCES, _, _) when Autoconf.windows -> t
163 | e -> raise e
165 let local_force_fd t = _local_force_fd false t
167 let close_all () =
168 while not (Fifo.empty cache) do
169 close_one ()
170 done
172 let rename t f =
174 check_destroyed t;
175 close t;
176 Unix2.rename t.filename f;
177 destroy t
178 with e ->
179 if !verbose then lprintf_nl "Exception in FDCache.rename %s %s: %s"
180 t.filename
182 (Printexc2.to_string e);
183 raise e
185 let ftruncate64 t len sparse =
187 check_destroyed t;
188 Unix2.c_ftruncate64 (local_force_fd t) len sparse
189 with e ->
190 if !verbose then lprintf_nl "Exception in FDCache.ftruncate64 %s %Ld (%s): %s"
191 t.filename
193 (if sparse then "sparse" else "not sparse")
194 (Printexc2.to_string e);
195 raise e
197 let getsize64 t =
199 check_destroyed t;
200 Unix2.c_getfdsize64 (local_force_fd t)
201 with
202 Unix.Unix_error (Unix.EISDIR, _, _) -> 0L
203 | Unix.Unix_error (Unix.EACCES, _, _) when Autoconf.windows -> 0L
204 | e ->
205 if !verbose then lprintf_nl "Exception in FDCache.getsize64 %s: %s"
206 t.filename
207 (Printexc2.to_string e);
208 raise e
210 let owner t =
211 let module U = Unix.LargeFile in
213 check_destroyed t;
214 let s =
216 U.fstat (local_force_fd t)
217 with
218 (* Unix.fstat is not supported on MinGW, Unix.stat is supported *)
219 | Invalid_argument _ -> U.stat t.filename
221 let user = Unix.getpwuid s.U.st_uid in
222 let group = Unix.getgrgid s.U.st_gid in
223 user.Unix.pw_name, group.Unix.gr_name
224 with
225 | Not_found -> "", ""
226 | e -> if !verbose then
227 lprintf_nl "Exception in FDCache.owner %s: %s"
228 t.filename (Printexc2.to_string e);
229 raise e
231 let mtime64 t =
233 check_destroyed t;
234 let st = Unix.LargeFile.stat t.filename in
235 st.Unix.LargeFile.st_mtime
236 with e ->
237 if !verbose then lprintf_nl "Exception in FDCache.mtime64 %s: %s"
238 t.filename
239 (Printexc2.to_string e);
240 raise e
242 let exists t =
244 check_destroyed t;
245 Sys.file_exists t.filename
246 with e ->
247 if !verbose then lprintf_nl "Exception in FDCache.exists %s: %s"
248 t.filename
249 (Printexc2.to_string e);
250 raise e
252 let remove t =
254 check_destroyed t;
255 if exists t then
256 Sys.remove t.filename;
257 destroy t
258 with e ->
259 if !verbose then lprintf_nl "Exception in FDCache.remove %s: %s"
260 t.filename
261 (Printexc2.to_string e);
262 raise e
264 let multi_rename t f file =
266 check_destroyed t;
267 close t;
268 (let d = (Filename.dirname (Filename.concat f file)) in
269 Unix2.safe_mkdir d;
270 Unix2.chmod d !create_dir_mode;
271 Unix2.can_write_to_directory d);
272 (try
273 Unix2.rename t.filename (Filename.concat f file);
274 with
275 Unix.Unix_error (Unix.EACCES, _, _) when
276 Autoconf.windows && (Unix2.list_directory t.filename = [])
277 -> remove t);
278 destroy t
279 with e ->
280 lprintf_nl "Exception in FDCache.multi_rename %s %s: %s"
281 t.filename
282 (Filename.concat f file)
283 (Printexc2.to_string e);
284 raise e
286 let read file file_pos string string_pos len =
288 let fd = local_force_fd file in
289 ignore(Unix2.c_seek64 fd file_pos Unix.SEEK_SET);
290 if !verbose then
291 lprintf_nl "really_read %s %Ld %d"
292 file.filename
293 file_pos
294 len;
295 Unix2.really_read fd string string_pos len
296 with e ->
297 if !verbose then lprintf_nl "Exception in FDCache.read %s %Ld %d: %s"
298 file.filename
299 file_pos
301 (Printexc2.to_string e);
302 raise e
304 let write file file_pos string string_pos len =
306 assert (file.writable);
307 let fd = local_force_fd file in
308 ignore(Unix2.c_seek64 fd file_pos Unix.SEEK_SET);
309 if !verbose then
310 lprintf_nl "really_write %s %Ld %d"
311 file.filename
312 file_pos
313 len;
314 really_write fd string string_pos len
315 with e ->
316 if !verbose then lprintf_nl "Exception in FDCache.write file %s file_pos=%Ld len=%d string_pos=%d, string length=%d: %s"
317 file.filename
318 file_pos
320 string_pos
321 (String.length string)
322 (Printexc2.to_string e);
323 raise e
325 let copy_chunk t1 t2 pos1 pos2 len64 =
326 check_destroyed t1;
327 check_destroyed t2;
328 let buffer_len = 128 * 1024 in
329 let buffer_len64 = Int64.of_int buffer_len in
330 let buffer = String.make buffer_len '\001' in
331 let rec iter remaining pos1 pos2 =
332 let len64 = min remaining buffer_len64 in
333 let len = Int64.to_int len64 in
334 if len > 0 then begin
335 read t1 pos1 buffer 0 len;
336 write t2 pos2 buffer 0 len;
337 iter (remaining -- len64) (pos1 ++ len64) (pos2 ++ len64)
341 iter len64 pos1 pos2
342 with e ->
343 if !verbose then lprintf_nl "Exception in FDCache.copy_chunk %s %Ld to %s %Ld (%Ld): %s"
344 t1.filename
345 pos1
346 t2.filename
347 pos2
348 len64
349 (Printexc2.to_string e);
350 raise e
354 module type File = sig
355 type t
356 val create : string -> bool -> t
357 val apply_on_chunk : t -> int64 -> int64 ->
358 (Unix.file_descr -> int64 -> 'a) -> 'a
359 val close : t -> unit
360 val rename : t -> string -> unit
361 val ftruncate64 : t -> int64 -> bool -> unit
362 val getsize64 : t -> int64
363 val mtime64 : t -> float
364 val exists : t -> bool
365 val remove : t -> unit
366 val read : t -> int64 -> string -> int -> int -> unit
367 val write : t -> int64 -> string -> int -> int -> unit
368 val destroy : t -> unit
369 val is_closed : t -> bool
373 module DiskFile = struct
375 type t = FDCache.t
377 let create = FDCache.create
379 let apply_on_chunk t pos_s len_s f =
380 let fd = FDCache.local_force_fd t in
381 f fd pos_s
383 let close = FDCache.close
385 let rename = FDCache.rename
387 let ftruncate64 = FDCache.ftruncate64
389 let getsize64 = FDCache.getsize64
390 let owner = FDCache.owner
391 let mtime64 = FDCache.mtime64
392 let exists = FDCache.exists
393 let remove = FDCache.remove
394 let read = FDCache.read
395 let write = FDCache.write
396 let destroy = FDCache.destroy
397 let is_closed = FDCache.is_closed
400 let zero_chunk_len = 65536L
401 let zero_chunk_name = "zero_chunk"
402 let zero_chunk_fd_option = ref None
403 let zero_chunk_fd () =
404 match !zero_chunk_fd_option with
405 Some fd -> fd
406 | None ->
407 let fd = FDCache.create zero_chunk_name true in
408 FDCache.ftruncate64 fd zero_chunk_len false;
409 zero_chunk_fd_option := Some fd;
412 module MultiFile = struct
414 type file = {
415 mutable filename : string;
416 mutable pos : int64;
417 mutable len : int64;
418 mutable current_len : int64;
419 mutable fd : FDCache.t;
420 mutable tail : file list;
423 type t = {
424 mutable dirname : string;
425 mutable size : int64;
426 mutable files : file list;
427 mutable tree : btree;
430 and btree =
431 Node of int64 * btree * btree
432 | Leaf of file
434 let rec make_tree files =
435 match files with
436 | [||] -> failwith "make_tree: no files"
437 | [| file |] -> Leaf file
438 | _ ->
439 let len = Array.length files in
440 let middle = len / 2 in
441 let pos = files.(middle).pos in
442 Node (pos,
443 make_tree (Array.sub files 0 middle),
444 make_tree (Array.sub files middle (len - middle)))
446 let make_tree files =
447 make_tree (Array.of_list (
448 List.filter (fun file -> file.len > zero) files))
450 let rec find_file tree file_pos =
451 match tree with
452 Leaf file -> file
453 | Node (pos, tree1, tree2) ->
454 find_file
455 (if file_pos < pos then tree1 else tree2) file_pos
457 let rec subfile_tree_map indent tree f=
458 match tree with
459 | Leaf file ->
460 f file.filename file.pos file.len file.current_len
461 | Node (pos, tree1, tree2) ->
462 subfile_tree_map (indent ^ " ") tree1 f;
463 subfile_tree_map (indent ^ " ") tree2 f
465 let rec print_tree indent tree =
466 match tree with
467 Leaf file -> lprintf_nl "%s - %s (%Ld,%Ld)"
468 indent file.filename file.pos file.len
469 | Node (pos, tree1, tree2) ->
470 lprintf_nl "%scut at %Ld" indent pos;
471 print_tree (indent ^ " ") tree1;
472 print_tree (indent ^ " ") tree2
474 let create dirname writable files =
475 Unix2.safe_mkdir dirname;
476 let rec iter files pos files2 =
477 match files with
478 [] ->
479 let rec reverse_files files tail =
480 match files with
481 [] -> tail
482 | file :: others ->
483 file.tail <- tail;
484 reverse_files others (file :: tail)
486 let files = reverse_files files2 [] in
488 dirname = dirname;
489 size = pos;
490 files = List.rev files2;
491 tree = make_tree files;
493 | (filename, size) :: tail ->
494 let temp_filename = Filename.concat dirname filename in
495 Unix2.safe_mkdir (Filename.dirname temp_filename);
496 let fd = FDCache.create temp_filename writable in
497 iter tail (pos ++ size)
499 filename = filename;
500 pos = pos;
501 len = size;
502 current_len = FDCache.getsize64 fd;
503 fd = fd;
504 tail = [];
505 } :: files2)
508 iter files zero []
510 let build t = ()
512 let find_file t chunk_begin =
513 let file = find_file t.tree chunk_begin in
514 file, file.tail
516 let do_on_remaining tail arg remaining f =
518 let rec iter_files list arg remaining =
519 match list with
520 [] -> assert false
521 | file :: tail ->
523 let len = min remaining file.len in
524 let arg = f file arg len in
525 let remaining = remaining -- len in
526 if remaining > zero then
527 iter_files tail arg remaining
530 iter_files tail arg remaining
532 let rec fill_zeros file_out file_pos max_len =
533 if max_len > zero then
534 let max_possible_write = min max_len zero_chunk_len in
535 FDCache.copy_chunk (zero_chunk_fd ()) file_out
536 zero file_pos max_possible_write;
537 fill_zeros file_out (file_pos ++ max_possible_write)
538 (max_len -- max_possible_write)
541 let apply_on_chunk t chunk_begin chunk_len f =
542 let (file, tail) = find_file t chunk_begin in
543 let chunk_end = chunk_begin ++ chunk_len in
544 let file_begin = file.pos in
545 let max_current_pos = file_begin ++ file.current_len in
546 if max_current_pos >= chunk_end then
547 let fd = FDCache.local_force_fd file.fd in
548 f fd (chunk_begin -- file_begin)
549 else
550 let temp_file = Filename2.temp_file "chunk" ".tmp" in
551 let file_out = FDCache.create temp_file true in
554 (* first file *)
555 let in_pos = chunk_begin -- file_begin in
556 let in_len = max_current_pos -- chunk_begin in
557 FDCache.copy_chunk file.fd file_out
558 in_pos zero in_len;
559 let zeros =
560 min (chunk_len -- in_len) (file.len -- file.current_len) in
561 fill_zeros file_out in_len zeros;
562 let in_len = in_len ++ zeros in
564 (* other files *)
565 do_on_remaining tail in_len (chunk_len -- in_len)
566 (fun file file_pos len ->
567 let max_current_pos = min len file.current_len in
568 FDCache.copy_chunk file.fd file_out
569 zero file_pos max_current_pos;
570 let file_pos = file_pos ++ max_current_pos in
571 let zeros = len -- max_current_pos in
572 fill_zeros file_out file_pos zeros;
573 file_pos ++ zeros
575 FDCache.close file_out;
576 let fd = FDCache.local_force_fd file_out in
577 let v = f fd zero in
578 FDCache.close file_out;
579 Sys.remove temp_file;
581 with e ->
582 if !verbose then lprintf_nl "Exception in MultiFile.apply_on_chunk %s %Ld %Ld: %s"
583 t.dirname
584 chunk_begin
585 chunk_len
586 (Printexc2.to_string e);
587 (try FDCache.close file_out with _ -> ());
588 (try Sys.remove temp_file with _ -> ());
589 raise e
591 let close t =
592 List.iter (fun file -> FDCache.close file.fd) t.files
594 let destroy t =
595 List.iter (fun file -> FDCache.destroy file.fd) t.files
597 let rename t f =
598 close t;
599 List.iter (fun file -> FDCache.multi_rename file.fd f file.filename)
600 t.files
602 let ftruncate64 t size sparse =
603 t.size <- size
605 let getsize64 t = t.size
607 let mtime64 t =
609 let st = Unix.LargeFile.stat t.dirname in
610 st.Unix.LargeFile.st_mtime
611 with e ->
612 if !verbose then lprintf_nl "Exception in MultiFile.mtime64 %s: %s"
613 t.dirname
614 (Printexc2.to_string e);
615 raise e
617 let exists t =
618 Sys.file_exists t.dirname
620 let remove t =
622 close t;
623 if Sys.file_exists t.dirname then
624 Unix2.remove_all_directory t.dirname
625 with e ->
626 if !verbose then lprintf_nl "Exception in MultiFile.remove %s: %s"
627 t.dirname
628 (Printexc2.to_string e);
629 raise e
631 let file_write file in_file_pos s in_string_pos len =
632 (* prevent write to zero-byte files so BT downloads finish *)
633 if len <> 0 then
634 begin
635 let len64 = Int64.of_int len in
636 let in_file_len = in_file_pos ++ len64 in
637 FDCache.write file.fd in_file_pos s in_string_pos len;
638 file.current_len <- max file.current_len in_file_len
641 let file_read file in_file_pos s in_string_pos len =
642 let len64 = Int64.of_int len in
643 let in_file_len = in_file_pos ++ len64 in
644 if in_file_len <= file.current_len then
645 FDCache.read file.fd in_file_pos s in_string_pos len
646 else
647 let possible_len64 = max zero (file.current_len -- in_file_pos) in
648 let possible_len = Int64.to_int possible_len64 in
649 if possible_len64 > zero then
650 FDCache.read file.fd in_file_pos s in_string_pos possible_len;
651 String.fill s (in_string_pos + possible_len) (len - possible_len) '\000'
653 let io f t chunk_begin string string_pos len =
654 let (file, tail) = find_file t chunk_begin in
656 let chunk_len = Int64.of_int len in
657 let chunk_end = chunk_begin ++ chunk_len in
658 let file_begin = file.pos in
659 let file_end = file_begin ++ file.len in
660 if file_end >= chunk_end then
661 f file (chunk_begin -- file_begin)
662 string string_pos len
663 else
665 (* first file *)
666 let in_pos = chunk_begin -- file_begin in
667 let first_read = file_end -- chunk_begin in
668 let in_len = Int64.to_int first_read in
669 f file in_pos string string_pos in_len;
671 (* other files *)
672 do_on_remaining tail (string_pos + in_len)
673 (chunk_len -- first_read)
674 (fun file string_pos len64 ->
675 let len = Int64.to_int len64 in
676 f file zero string string_pos len;
677 string_pos + len
680 let read t chunk_begin string string_pos len =
681 io file_read t chunk_begin string string_pos len
683 let write t chunk_begin string string_pos len =
684 io file_write t chunk_begin string string_pos len ;
685 let len64 = Int64.of_int len in
686 t.size <- max t.size (chunk_begin ++ len64);
687 let time = Unix.time () in
688 (* this does not work on win32 because of
689 file locking i guess, therefore ignore
690 all exceptions *)
691 (try Unix.utimes t.dirname time time with _ -> ());
694 let is_closed _ = false
697 module SparseFile = struct
699 type chunk = {
700 mutable chunkname : string;
701 mutable pos : int64;
702 mutable len : int64;
703 mutable fd : FDCache.t;
706 type t = {
707 mutable filename : string;
708 mutable dirname : string;
709 mutable size : int64;
710 mutable chunks : chunk array;
711 mutable writable : bool;
714 let zero_chunk () =
716 chunkname = zero_chunk_name;
717 pos = zero;
718 len = zero_chunk_len;
719 fd = zero_chunk_fd ();
722 let is_closed _ = false
724 let create filename writable =
725 (* lprintf_nl "SparseFile.create %s" filename; *)
726 let dirname = filename ^ ".chunks" in
727 (* lprintf_nl "Creating directory %s" dirname; *)
728 Unix2.safe_mkdir dirname;
729 Unix2.can_write_to_directory dirname;
731 filename = filename;
732 dirname = dirname;
733 chunks = [||];
734 size = zero;
735 writable = writable;
738 let find_read_pos t pos =
739 let nchunks = Array.length t.chunks in
740 let rec iter t start len =
741 if len <= 0 then start else
742 let milen = len/2 in
743 let med = start + milen in
744 let chunk = t.chunks.(med) in
745 if chunk.pos <= pos && pos -- chunk.pos < chunk.len then med
746 else
747 if chunk.pos > pos then
748 if med = start then start
749 else
750 iter t start milen
751 else
752 let next = med+1 in
753 iter t next (start+len-next)
755 iter t 0 nchunks
757 let find_write_pos t pos =
758 let nchunks = Array.length t.chunks in
759 let rec iter t start len =
760 if len <= 0 then start
761 else
762 let milen = len/2 in
763 let med = start + milen in
764 let chunk = t.chunks.(med) in
765 if chunk.pos <= pos && pos -- chunk.pos <= chunk.len then med
766 else
767 if chunk.pos > pos then
768 if med = start then start
769 else
770 iter t start milen
771 else
772 let next = med+1 in
773 iter t next (start+len-next)
775 iter t 0 nchunks
777 (* (*** debugging code ***)
779 let find_read_pos2 t pos =
780 let rec iter t i len =
781 if i = len then i
782 else
783 let chunk = t.chunks.(i) in
784 if chunk.pos ++ chunk.len > pos then i
785 else
786 iter t (i+1) len
788 iter t 0 (Array.length t.chunks)
790 let find_write_pos2 t pos =
791 let rec iter t i len =
792 if i = len then i
793 else
794 let chunk = t.chunks.(i) in
795 if chunk.pos ++ chunk.len >= pos then i
796 else
797 iter t (i+1) len
799 iter t 0 (Array.length t.chunks)
801 let _ =
802 let one = 1L in
803 let two = 2L in
804 let three = 3L in
805 for i = 0 to 3 do
806 let chunks = Array.init i (fun i ->
807 let name = string_of_int i in
808 let pos = 3 * i + 1 in
809 let fd = FDCache.create name writable in
810 lprintf " %d [%d - %d]" i pos (pos+2);
812 chunkname = name;
813 pos = Int64.of_int pos;
814 len = two;
815 fd = fd;
817 ) in
818 lprintf_nl "";
819 let t = {
820 filename = "";
821 dirname = "";
822 size = zero;
823 chunks = chunks;
824 } in
825 for j = 0 to 3 * i + 3 do
826 let pos = (Int64.of_int j) in
827 let i1 = find_write_pos t pos in
828 lprintf "(%d -> %d) " j i1;
829 let i2 = find_write_pos2 t pos in
830 assert (i1 = i2)
831 done;
832 lprintf_nl "";
833 done;
834 exit 0
837 let build t = ()
839 let apply_on_chunk t chunk_begin chunk_len f =
841 let index = find_read_pos t chunk_begin in
842 let nchunks = Array.length t.chunks in
844 if index < nchunks &&
845 ( let chunk = t.chunks.(index) in
846 chunk.pos <= chunk_begin &&
847 chunk.len >= (chunk_len -- (chunk_begin -- chunk.pos))
848 ) then
850 let chunk = t.chunks.(index) in
851 let in_chunk_pos = chunk_begin -- chunk.pos in
852 let fd = FDCache.local_force_fd chunk.fd in
853 f fd in_chunk_pos
855 else
857 let temp_file = Filename2.temp_file "chunk" ".tmp" in
858 let file_out = FDCache.create temp_file t.writable in
860 let rec iter pos index chunk_begin chunk_len =
861 if chunk_len > zero then
862 let chunk =
863 if index >= nchunks then
864 let z = zero_chunk () in
865 z.pos <- chunk_begin;
867 else
868 let chunk = t.chunks.(index) in
869 let next_pos = chunk.pos in
870 if next_pos > chunk_begin then
871 let z = zero_chunk () in
872 z.pos <- chunk_begin;
873 z.len <- min zero_chunk_len (next_pos -- chunk_begin);
875 else
876 chunk
879 let in_chunk_pos = chunk_begin -- chunk.pos in
880 let max_len = min chunk_len (chunk.len -- in_chunk_pos) in
882 FDCache.copy_chunk chunk.fd file_out
883 in_chunk_pos pos max_len;
884 iter (pos ++ max_len) (index+1) (chunk_begin ++ max_len)
885 (chunk_len -- max_len)
889 iter zero (find_read_pos t chunk_begin) chunk_begin chunk_len;
891 FDCache.close file_out;
892 let fd = FDCache.local_force_fd file_out in
893 let v = f fd zero in
894 Sys.remove temp_file;
896 with e ->
897 if !verbose then lprintf_nl "Exception in SparseFile.apply_on_chunk %s %Ld %Ld: %s"
898 t.dirname
899 chunk_begin
900 chunk_len
901 (Printexc2.to_string e);
902 (try FDCache.close file_out with _ -> ());
903 (try Sys.remove temp_file with _ -> ());
904 raise e
906 let close t =
907 Array.iter (fun file -> FDCache.close file.fd) t.chunks
909 let destroy t =
910 Array.iter (fun file -> FDCache.destroy file.fd) t.chunks
912 let rename t f =
913 close t;
915 let chunk_begin = zero in
916 let chunk_len = t.size in
917 let nchunks = Array.length t.chunks in
919 let file_out = FDCache.create f true in
921 let rec iter pos index chunk_begin chunk_len =
922 if chunk_len > zero then
923 let chunk =
924 if index >= nchunks then
925 let z = zero_chunk () in
926 z.pos <- chunk_begin;
928 else
929 let chunk = t.chunks.(index) in
930 let next_pos = chunk.pos in
931 if next_pos > chunk_begin then
932 let z = zero_chunk () in
933 z.pos <- chunk_begin;
934 z.len <- min zero_chunk_len (next_pos -- chunk_begin);
936 else
937 chunk
940 let in_chunk_pos = chunk_begin -- chunk.pos in
941 let max_len = min chunk_len (chunk.len -- in_chunk_pos) in
943 FDCache.copy_chunk chunk.fd file_out
944 in_chunk_pos pos max_len;
946 if chunk.fd != zero_chunk_fd () then FDCache.remove chunk.fd;
948 iter (pos ++ max_len) (index+1) (chunk_begin ++ max_len)
949 (chunk_len -- max_len)
952 iter zero 0 chunk_begin chunk_len;
953 FDCache.close file_out;
955 (* (* why is that commented off ? Does SparseFile.rename actually work ? *)
956 Sys.rename t.dirname f;
957 List.iter (fun file ->
958 file.fd.FDCache.filename <- Filename.concat t.dirname file.filename
959 ) t.files
962 let ftruncate64 t size sparse =
963 t.size <- size
965 let getsize64 t = t.size
967 let mtime64 t =
969 let st = Unix.LargeFile.stat t.dirname in
970 st.Unix.LargeFile.st_mtime
971 with e ->
972 if !verbose then lprintf_nl "Exception in SparseFile.mtime64 %s: %s"
973 t.dirname
974 (Printexc2.to_string e);
975 raise e
977 let exists t =
978 Sys.file_exists t.dirname
980 let remove t =
982 close t;
983 (* lprintf "Removing %s\n" t.dirname; *)
984 Unix2.remove_all_directory t.dirname
985 with e ->
986 if !verbose then lprintf_nl "Exception in SparseFile.remove %s: %s"
987 t.dirname
988 (Printexc2.to_string e);
989 raise e
991 let read t chunk_begin string string_pos chunk_len =
992 let chunk_len64 = Int64.of_int chunk_len in
994 let nchunks = Array.length t.chunks in
995 let rec iter string_pos index chunk_begin chunk_len64 =
997 if chunk_len64 > zero then
998 let chunk =
999 if index >= nchunks then
1000 let z = zero_chunk () in
1001 z.pos <- chunk_begin;
1003 else
1004 let chunk = t.chunks.(index) in
1005 let next_pos = chunk.pos in
1006 if next_pos > chunk_begin then
1007 let z = zero_chunk () in
1008 z.pos <- chunk_begin;
1009 z.len <- min zero_chunk_len (next_pos -- chunk_begin);
1011 else
1012 chunk
1015 let in_chunk_pos = chunk_begin -- chunk.pos in
1016 let max_len64 = min chunk_len64 (chunk.len -- in_chunk_pos) in
1017 let max_len = Int64.to_int max_len64 in
1019 FDCache.read chunk.fd in_chunk_pos string string_pos max_len;
1020 iter (string_pos + max_len) (index+1) (chunk_begin ++ max_len64)
1021 (chunk_len64 -- max_len64)
1024 iter string_pos (find_read_pos t chunk_begin) chunk_begin chunk_len64
1026 let write t chunk_begin string string_pos len =
1027 let index = find_write_pos t chunk_begin in
1028 let len64 = Int64.of_int len in
1029 let nchunks = Array.length t.chunks in
1031 if index = Array.length t.chunks then begin
1032 (* lprintf "Adding chunk at end\n"; *)
1033 let chunk_name = Int64.to_string chunk_begin in
1034 let chunk_name = Filename.concat t.dirname chunk_name in
1035 let fd = FDCache.create chunk_name t.writable in
1036 let chunk = {
1037 chunkname = chunk_name;
1038 pos = chunk_begin;
1039 len = zero;
1040 fd = fd;
1041 } in
1042 let new_array = Array.create (nchunks+1) chunk in
1043 Array.blit t.chunks 0 new_array 0 nchunks;
1044 t.chunks <- new_array
1046 end else
1047 if t.chunks.(index).pos > chunk_begin then begin
1048 (* lprintf "Inserting chunk\n"; *)
1049 let chunk_name = Int64.to_string chunk_begin in
1050 let chunk_name = Filename.concat t.dirname chunk_name in
1051 let fd = FDCache.create chunk_name t.writable in
1052 let chunk = {
1053 chunkname = chunk_name;
1054 pos = chunk_begin;
1055 len = zero;
1056 fd = fd;
1057 } in
1058 let new_array = Array.create (nchunks+1) chunk in
1059 Array.blit t.chunks 0 new_array 0 index;
1060 Array.blit t.chunks index new_array (index+1) (nchunks-index);
1061 t.chunks <- new_array;
1062 end;
1064 let nchunks = Array.length t.chunks in
1065 let rec iter index chunk_begin string_pos len =
1066 if len > 0 then
1067 let next_index, max_len, max_len64 =
1068 if index = nchunks-1 then
1069 index, len, Int64.of_int len
1070 else
1071 let max_pos = t.chunks.(index+1).pos in
1072 let max_possible_len64 = max_pos -- chunk_begin in
1073 let len64 = Int64.of_int len in
1074 let max_len64 = min max_possible_len64 len64 in
1075 let max_len = Int64.to_int max_len64 in
1076 if max_len64 = max_possible_len64 then
1077 index+1, max_len, max_len64
1078 else
1079 index, max_len, max_len64
1082 let chunk = t.chunks.(index) in
1083 let begin_pos = chunk_begin -- chunk.pos in
1084 FDCache.write chunk.fd begin_pos string string_pos max_len;
1085 chunk.len <- chunk.len ++ max_len64;
1087 iter next_index (chunk_begin ++ max_len64)
1088 (string_pos + max_len) (len - max_len)
1090 iter index chunk_begin string_pos len;
1092 t.size <- max t.size (chunk_begin ++ len64);
1094 let time = Unix.time () in
1095 (* this does not work on win32 because of
1096 file locking i guess, therefore ignore
1097 all exceptions *)
1098 (try Unix.utimes t.dirname time time with _ -> ())
1102 type file_kind =
1103 | MultiFile of MultiFile.t
1104 | DiskFile of DiskFile.t
1105 | SparseFile of SparseFile.t
1106 | Destroyed
1108 type file = {
1109 mutable file_kind : file_kind;
1110 mutable filename : string;
1111 mutable writable : bool;
1112 mutable error : exn option;
1113 mutable buffers : (string * int * int * int64 * int64) list;
1116 module H = Weak.Make(struct
1117 type old_t = file
1118 type t = old_t
1119 let hash t = Hashtbl.hash (t.filename, t.writable)
1121 let equal x y = x.filename = y.filename && x.writable = y.writable
1122 end)
1124 let dummy = {
1125 file_kind = Destroyed;
1126 filename = "";
1127 writable = false;
1128 error = None;
1129 buffers = [];
1132 let table = H.create 100
1134 let destroyed t = t.file_kind = Destroyed
1136 let fd_exists f =
1138 let fd = H.find table { dummy with filename = f } in
1139 match fd.file_kind with
1140 | DiskFile fd -> not (DiskFile.is_closed fd)
1141 | MultiFile fd -> not (MultiFile.is_closed fd)
1142 | SparseFile fd -> not (SparseFile.is_closed fd)
1143 | Destroyed -> false
1144 with Not_found -> false
1146 let create f writable creator =
1148 let fd = H.find table { dummy with filename = f; writable = writable } in
1149 (* lprintf "%s already exists\n" f; *)
1151 with Not_found ->
1152 let t = {
1153 file_kind = creator f;
1154 filename = f;
1155 writable = writable;
1156 error = None;
1157 buffers = [];
1158 } in
1159 H.add table t;
1162 (* check if a writable descriptor on the same file exists *)
1163 let find_writable fd =
1164 if fd.writable then Some fd
1165 else
1167 Some (H.find table { fd with writable = true })
1168 with Not_found -> None
1170 let create_diskfile filename writable =
1171 create filename writable (fun f -> DiskFile (DiskFile.create f writable))
1173 let create_multifile filename writable files =
1174 create filename writable (fun f ->
1175 MultiFile (MultiFile.create f writable files))
1177 let create_sparsefile filename writable =
1178 create filename writable (fun f ->
1179 SparseFile (SparseFile.create f writable))
1181 let ftruncate64 t len sparse =
1182 match t.file_kind with
1183 | DiskFile t -> DiskFile.ftruncate64 t len sparse
1184 | MultiFile t -> MultiFile.ftruncate64 t len sparse
1185 | SparseFile t -> SparseFile.ftruncate64 t len sparse
1186 | Destroyed -> failwith "Unix32.ftruncate64 on destroyed FD"
1188 let mtime64 t =
1189 match t.file_kind with
1190 | DiskFile t -> DiskFile.mtime64 t
1191 | MultiFile t -> MultiFile.mtime64 t
1192 | SparseFile t -> SparseFile.mtime64 t
1193 | Destroyed -> failwith "Unix32.mtime64 on destroyed FD"
1195 let getsize64 t =
1196 match t.file_kind with
1197 | DiskFile t -> DiskFile.getsize64 t
1198 (* only avoid opening rw on shared files, shared files can only be DiskFile *)
1199 | MultiFile t -> MultiFile.getsize64 t
1200 | SparseFile t -> SparseFile.getsize64 t
1201 | Destroyed -> failwith "Unix32.getsize64 on destroyed FD"
1203 let fds_size = Unix2.c_getdtablesize ()
1205 let buffered_bytes = ref Int64.zero
1206 let modified_files = ref []
1208 let filename t = t.filename
1210 let write file file_pos string string_pos len =
1211 if len > 0 then
1212 match file.file_kind with
1213 | DiskFile t -> DiskFile.write t file_pos string string_pos len
1214 | MultiFile t -> MultiFile.write t file_pos string string_pos len
1215 | SparseFile t -> SparseFile.write t file_pos string string_pos len
1216 | Destroyed -> failwith "Unix32.write on destroyed FD"
1217 else
1218 lprintf_nl "Unix32.write: error, invalid argument len = 0"
1220 let buffer = Buffer.create 65000
1222 let flush_buffer t offset =
1223 if !verbose then lprintf_nl "flush_buffer";
1224 let s = Buffer.contents buffer in
1225 Buffer.reset buffer;
1226 let len = String.length s in
1228 if !verbose then lprintf_nl "seek64 %Ld" offset;
1229 if len > 0 then write t offset s 0 len;
1231 let fd, offset = fd_of_chunk t offset (Int64.of_int len) in
1232 let final_pos = Unix2.c_seek64 fd offset Unix.SEEK_SET in
1233 if verbose then lprintf_nl "really_write %d" len;
1234 Unix2.really_write fd s 0 len;
1236 buffered_bytes := !buffered_bytes -- (Int64.of_int len);
1237 if !verbose then lprintf_nl "written %d bytes (%Ld)" len !buffered_bytes;
1238 with e ->
1239 if !verbose then lprintf_nl "exception %s in flush_buffer" (Printexc2.to_string e);
1240 t.buffers <- (s, 0, len, offset, Int64.of_int len) :: t.buffers;
1241 raise e
1243 let flush_fd t =
1244 match find_writable t with
1245 | None -> ()
1246 | Some t ->
1247 if t.buffers = [] then () else
1248 let list =
1249 List.sort (fun (_, _, _, o1, l1) (_, _, _, o2, l2) ->
1250 let c = compare o1 o2 in
1251 if c = 0 then compare l2 l1 else c)
1252 t.buffers
1254 if !verbose then lprintf_nl "flush_fd";
1255 t.buffers <- list;
1256 let rec iter_out () =
1257 match t.buffers with
1258 | [] -> ()
1259 | (s, pos_s, len_s, offset, len) :: tail ->
1260 Buffer.reset buffer;
1261 Buffer.add_substring buffer s pos_s len_s;
1262 t.buffers <- tail;
1263 iter_in offset len
1265 and iter_in offset len =
1266 match t.buffers with
1267 | [] -> flush_buffer t offset
1268 | (s, pos_s, len_s, offset2, len2) :: tail ->
1269 let in_offset = offset ++ len -- offset2 in
1270 if in_offset = Int64.zero then begin
1271 Buffer.add_substring buffer s pos_s len_s;
1272 t.buffers <- tail;
1273 iter_in offset (len ++ len2);
1274 end else
1275 if in_offset < Int64.zero then begin
1276 flush_buffer t offset;
1277 iter_out ()
1278 end else
1279 let keep_len = len2 -- in_offset in
1280 if !verbose then lprintf_nl "overlap %Ld" keep_len;
1281 t.buffers <- tail;
1282 if keep_len <= 0L then begin
1283 buffered_bytes := !buffered_bytes -- len2;
1284 iter_in offset len
1285 end else begin
1286 let new_pos = len2 -- keep_len in
1287 Buffer.add_substring buffer s
1288 (pos_s + Int64.to_int new_pos) (Int64.to_int keep_len);
1289 buffered_bytes := !buffered_bytes -- new_pos;
1290 iter_in offset (len ++ keep_len)
1293 iter_out ()
1295 let read t file_pos string string_pos len =
1296 flush_fd t;
1297 match t.file_kind with
1298 | DiskFile t -> DiskFile.read t file_pos string string_pos len
1299 | MultiFile t -> MultiFile.read t file_pos string string_pos len
1300 | SparseFile t -> SparseFile.read t file_pos string string_pos len
1301 | Destroyed -> failwith
1302 (Printf.sprintf "Unix32.read on destroyed FD %s"
1303 (filename t))
1305 let flush _ =
1307 if !verbose then lprintf_nl "flush all";
1308 let rec iter list =
1309 match list with
1310 | [] -> []
1311 | t :: tail ->
1313 flush_fd t;
1314 t.error <- None;
1315 iter tail
1316 with e ->
1317 t.error <- Some e;
1318 t :: (iter tail)
1320 modified_files := iter !modified_files;
1321 if !buffered_bytes <> 0L then
1322 lprintf_nl "[ERROR] remaining bytes after flush"
1323 with e ->
1324 if !verbose then lprintf_nl "[ERROR] Exception %s in Unix32.flush"
1325 (Printexc2.to_string e)
1327 let buffered_write t offset s pos_s len_s =
1328 let len = Int64.of_int len_s in
1329 match t.error with
1330 | None ->
1331 if len > Int64.zero then begin
1332 if not (List.memq t !modified_files) then
1333 modified_files := t :: !modified_files;
1334 t.buffers <- (s, pos_s, len_s, offset, len) :: t.buffers;
1335 buffered_bytes := !buffered_bytes ++ len;
1336 if !verbose then
1337 lprintf_nl "buffering %Ld bytes (%Ld)" len !buffered_bytes;
1339 (* Don't buffer more than 1 Mo *)
1340 if !buffered_bytes > !max_buffered then flush ()
1342 | Some e ->
1343 raise e
1345 let buffered_write_copy t offset s pos_s len_s =
1346 buffered_write t offset (String.sub s pos_s len_s) 0 len_s
1348 let copy_chunk t1 t2 pos1 pos2 len =
1349 flush_fd t1;
1350 flush_fd t2;
1351 let buffer_size = 128 * 1024 in
1352 let buffer = String.make buffer_size '\001' in
1353 let rec iter remaining pos1 pos2 =
1354 let len = mini remaining buffer_size in
1355 if len > 0 then begin
1356 read t1 pos1 buffer 0 len;
1357 write t2 pos2 buffer 0 len;
1358 let len64 = Int64.of_int len in
1359 iter (remaining - len) (pos1 ++ len64) (pos2 ++ len64)
1362 iter len pos1 pos2
1364 let mega = megabytes 1
1366 let rec copy t1 t2 pos1 pos2 len64 =
1367 if len64 > mega then begin
1368 copy_chunk t1 t2 pos1 pos2 (Int64.to_int mega);
1369 copy t1 t2 (pos1 ++ mega) (pos2 ++ mega) (len64 -- mega)
1370 end else
1371 copy_chunk t1 t2 pos1 pos2 (Int64.to_int len64)
1373 let close_all = FDCache.close_all
1375 let close t =
1376 flush_fd t;
1377 match t.file_kind with
1378 | DiskFile t -> DiskFile.close t
1379 | MultiFile t -> MultiFile.close t
1380 | SparseFile t -> SparseFile.close t
1381 | Destroyed -> () (* failwith "Unix32.close on destroyed FD" *)
1383 let destroy t =
1384 if t.file_kind <> Destroyed then begin
1385 H.remove table t;
1386 (match t.file_kind with
1387 | DiskFile t -> DiskFile.destroy t
1388 | MultiFile t -> MultiFile.destroy t
1389 | SparseFile t -> SparseFile.destroy t
1390 | Destroyed -> ());
1391 t.file_kind <- Destroyed
1394 let create_rw filename = create_diskfile filename true
1395 let create_ro filename = create_diskfile filename false
1397 let apply_on_chunk t pos len f =
1398 match t.file_kind with
1399 | DiskFile t -> DiskFile.apply_on_chunk t pos len f
1400 | MultiFile tt ->
1401 flush_fd t;
1402 MultiFile.apply_on_chunk tt pos len f
1403 | SparseFile t -> SparseFile.apply_on_chunk t pos len f
1404 | Destroyed -> failwith "Unix32.apply_on_chunk on destroyed FD"
1406 let exists t =
1407 flush_fd t;
1408 match t.file_kind with
1409 | DiskFile t -> DiskFile.exists t
1410 | MultiFile t -> MultiFile.exists t
1411 | SparseFile t -> SparseFile.exists t
1412 | Destroyed -> failwith "Unix32.exists on destroyed FD"
1414 let remove t =
1415 flush_fd t;
1416 close t;
1417 match t.file_kind with
1418 | DiskFile t -> DiskFile.remove t
1419 | MultiFile t -> MultiFile.remove t
1420 | SparseFile t -> SparseFile.remove t
1421 | Destroyed -> failwith "Unix32.remove on destroyed FD"
1423 let getsize s =
1424 let old_fd_exists = fd_exists s in
1425 let fd = create_ro s in
1426 let size = getsize64 fd in
1427 if not old_fd_exists then close fd;
1428 size
1430 let mtime s =
1431 let old_fd_exists = fd_exists s in
1432 let fd = create_ro s in
1433 let time = mtime64 fd in
1434 if not old_fd_exists then close fd;
1435 time
1437 let file_exists s =
1438 let old_fd_exists = fd_exists s in
1440 let fd = create_ro s in
1441 let exists =
1443 exists fd
1444 with Unix.Unix_error (Unix.ENOENT, _, _) -> false
1446 if not old_fd_exists then close fd;
1447 exists
1448 with Unix.Unix_error (Unix.ENOENT, _, _) -> false
1450 let owner_fd t =
1451 match t.file_kind with
1452 | DiskFile t -> DiskFile.owner t
1453 | MultiFile t -> "", ""
1454 | SparseFile t -> "", ""
1455 | Destroyed -> "", ""
1457 let owner s =
1458 let old_fd_exists = fd_exists s in
1459 let fd = create_ro s in
1460 let user,pass =
1462 owner_fd fd
1463 with _ -> "", ""
1465 if not old_fd_exists then close fd;
1466 user, pass
1468 let rename t f =
1469 flush_fd t;
1470 close t;
1471 match t.file_kind with
1472 | DiskFile t -> DiskFile.rename t f
1473 | MultiFile t -> MultiFile.rename t f
1474 | SparseFile t -> SparseFile.rename t f
1475 | Destroyed -> failwith "Unix32.rename on destroyed FD"
1477 (* module MultiFile_Test = (MultiFile : File) *)
1478 module DiskFile_Test = (DiskFile : File)
1479 module SparseFile_Test = (SparseFile : File)
1481 module SharedParts = struct
1483 (*************************************************************************)
1484 (* *)
1485 (* Files sharing parts *)
1486 (* *)
1487 (*************************************************************************)
1489 (* TODO: share file parts:
1490 The following functions have to be rewritten:
1491 * rename, remove: copy the shared parts out before destroying the file.
1492 * write, buffered_write, buffered_write_copy, read: split
1493 * allocate_chunk: remove this
1494 * copy_chunk: remove this
1497 type t = {
1498 file : file;
1499 mutable file_parts : part list;
1502 and part = {
1503 mutable part_file : file;
1504 mutable part_begin : int64;
1505 mutable part_len : int64;
1506 mutable part_end : int64;
1507 mutable part_shared : t list;
1510 let copy_shared_parts_out file parts =
1511 List2.tail_map (fun part ->
1512 if part.part_file == file then
1513 match part.part_shared with
1514 | [] -> part
1515 | t :: tail ->
1516 lprintf_nl "Copy shared part to another file";
1517 copy part.part_file t.file part.part_begin part.part_begin
1518 part.part_len;
1519 lprintf_nl " Copy done.";
1520 part.part_file <- t.file;
1521 part.part_shared <- tail;
1522 { part with part_file = file; part_shared = [] }
1523 else part
1524 ) parts
1526 let copy_shared_parts_in file parts =
1527 List2.tail_map (fun part ->
1528 if part.part_file != file then begin
1529 lprintf_nl "Copy shared part to another file";
1530 copy part.part_file file part.part_begin part.part_begin
1531 part.part_len;
1532 lprintf_nl " Copy done.";
1533 part.part_shared <- List.filter (fun t -> t.file != file)
1534 part.part_shared;
1535 { part with part_file = file; part_shared = [] }
1536 end else
1537 part
1538 ) parts
1540 let remove t =
1541 t.file_parts <- copy_shared_parts_out t.file t.file_parts;
1542 remove t.file
1544 let destroy t =
1545 t.file_parts <- copy_shared_parts_out t.file t.file_parts;
1546 destroy t.file
1548 let old_close = close
1550 let close t = close t.file
1551 let getsize64 t = getsize64 t.file
1552 let filename t = filename t.file
1554 let rename t file_name =
1555 t.file_parts <- copy_shared_parts_in t.file t.file_parts;
1556 t.file_parts <- copy_shared_parts_out t.file t.file_parts;
1557 rename t.file file_name
1559 let mtime64 t = mtime64 t.file
1560 let flush_fd t = flush_fd t.file
1562 let apply_on_parts f t file_pos s pos len =
1563 List.iter (fun part ->
1564 f part.part_file file_pos s pos len) t.file_parts
1566 let buffered_write t file_pos s pos len =
1567 apply_on_parts buffered_write t file_pos s pos len
1569 let buffered_write_copy t file_pos s pos len =
1570 apply_on_parts buffered_write_copy t file_pos s pos len
1572 let write t file_pos s pos len =
1573 apply_on_parts write t file_pos s pos len
1575 let read t file_pos s pos len =
1576 apply_on_parts read t file_pos s pos len
1580 (* TODO: there is no need to create a temporary file when the wanted chunk
1581 * overlaps different parts, but these parts are on the same physical file. *)
1582 let apply_on_chunk t chunk_begin chunk_len f =
1583 let chunk_end = chunk_begin ++ chunk_len in
1584 let rec iter list =
1585 match list with
1586 | [] -> assert false
1587 | part :: tail ->
1588 if part.part_begin <= chunk_begin &&
1589 part.part_end >= chunk_end then
1590 apply_on_chunk part.part_file chunk_begin chunk_len f
1591 else
1592 if part.part_end > chunk_begin then
1593 make_temp_file list
1594 else
1595 iter tail
1597 and make_temp_file list =
1598 let temp_file = Filename2.temp_file "chunk" ".tmp" in
1599 let file_out = create_rw temp_file in
1601 let rec fill pos chunk_begin chunk_len list =
1602 if chunk_len > zero then
1603 match list with
1604 | [] -> ()
1605 | part :: tail ->
1607 let tocopy = min chunk_len (part.part_end -- chunk_begin) in
1608 copy_chunk part.part_file file_out chunk_begin pos
1609 (Int64.to_int tocopy);
1610 fill (pos ++ tocopy) (chunk_begin ++ tocopy)
1611 (chunk_len -- tocopy) tail
1613 fill zero chunk_begin chunk_len list;
1614 old_close file_out;
1616 let v = apply_on_chunk file_out zero chunk_len f in
1617 Sys.remove temp_file;
1619 with e ->
1620 (try Sys.remove temp_file with _ -> ());
1621 raise e
1624 iter t.file_parts
1626 let ftruncate64 t len sparse =
1627 ftruncate64 t.file len sparse
1629 let maxint64 = megabytes 1000000
1631 let create file =
1632 let part = {
1633 part_file = file;
1634 part_begin = zero;
1635 part_end = maxint64;
1636 part_len = maxint64;
1637 part_shared = []
1638 } in
1639 { file = file; file_parts = [part] }
1641 let create_diskfile file_name writable =
1642 create (create_diskfile file_name writable)
1644 let create_multifile file_name writable files =
1645 create (create_multifile file_name writable files)
1647 let create_sparsefile file_name writable =
1648 create (create_sparsefile file_name writable)
1650 let create_ro filename =
1651 create (create_ro filename)
1653 let create_rw filename =
1654 create (create_rw filename)
1656 let is_closed = FDCache.is_closed
1659 (* the new part (shared_begin, shared_len) is shared between t1 and t2.
1660 It will be kept inside t1, and used by t2. The problem is what happens
1661 when these two files have already been partially downloaded ? This
1662 problem has to be solved at a upper level. *)
1663 let shared_part t1 t2 shared_begin shared_len =
1664 let shared_end = shared_begin ++ shared_len in
1669 (* subfile tree map function*)
1670 let subfile_tree_map t f =
1671 match t.file_kind with
1672 | MultiFile t -> MultiFile.subfile_tree_map "" t.MultiFile.tree f; ()
1673 | _ -> ()
1676 let find_file t chunk_begin =
1677 match t.file_kind with
1678 | MultiFile t ->
1679 let (sf, tail) = (MultiFile.find_file t chunk_begin) in
1680 (sf.MultiFile.filename, sf.MultiFile.pos , sf.MultiFile.len)
1681 | _ -> ("unimplemeted" , 0L, 0L)
1683 let find_file_index t index =
1684 match t.file_kind with
1685 | MultiFile t ->
1686 let sf = List.nth t.MultiFile.files index in
1687 (sf.MultiFile.filename, sf.MultiFile.pos , sf.MultiFile.len)
1688 | _ -> ("unimplemeted" , 0L, 0L)
1690 type t = file
1693 let bad_fd =
1694 let t = create_rw "/dev/null" in
1695 t.file_kind <- Destroyed;
1699 type statfs = {
1700 f_type : int64; (* type of filesystem *)
1701 f_bsize : int64; (* optimal transfer block size *)
1702 f_blocks : int64; (* total data blocks in file system *)
1703 f_bfree : int64; (* free blocks in fs *)
1704 f_bavail : int64; (* free blocks avail to non-superuser *)
1705 f_files : int64; (* total file nodes in file system *)
1706 f_ffree : int64; (* free file nodes in fs *)
1707 f_fsid : unit; (* See note in statfs(2) *)
1708 f_fnamelen : int64; (* maximum length of filenames, maximum Sys.max_string_length *)
1709 f_basetype : string; (* type of filesystem - Solaris, (-1) on other systems, use f_type there *)
1710 f_frsize : int64; (* Fundamental file system block size, (-1) if not provided by system *)
1713 exception Not_supported
1714 exception Error
1716 let _ = Callback.register_exception "not supported" Not_supported
1717 let _ = Callback.register_exception "error" Error
1719 external statfs : string -> statfs = "statfs_statfs"
1721 let _bsize sf =
1723 if sf.f_frsize = Int64.zero || sf.f_frsize = -1L then
1724 Some sf.f_bsize
1725 else
1726 Some sf.f_frsize
1727 with e -> None
1730 let bsize dir =
1732 _bsize (statfs dir)
1733 with e -> None
1735 let blocks dir =
1737 Some (statfs dir).f_blocks
1738 with e -> None
1740 let bfree dir =
1742 Some (statfs dir).f_bfree
1743 with e -> None
1745 let bavail dir =
1747 Some (statfs dir).f_bavail
1748 with e -> None
1750 let fnamelen dir =
1752 Some (Int64.to_int (min (statfs dir).f_fnamelen (Int64.of_int Sys.max_string_length)))
1753 with e -> None
1755 let disktotal dir =
1756 (* total disk space in bytes *)
1758 let s = statfs dir in
1759 match _bsize s with
1760 | Some bsize ->
1761 Some (bsize ** s.f_blocks)
1762 | None -> None
1763 with e -> None
1765 let diskfree dir =
1766 (* free disk space in bytes *)
1768 let s = statfs dir in
1769 match _bsize s with
1770 | Some bsize ->
1771 Some (bsize ** s.f_bavail)
1772 | None -> None
1773 with e -> None
1775 let diskused dir =
1776 (* used disk space in bytes *)
1778 let s = statfs dir in
1779 match _bsize s with
1780 | Some bsize ->
1781 Some (bsize ** (s.f_blocks -- s.f_bavail))
1782 | None -> None
1783 with e -> None
1785 let percentused dir =
1786 (* percentage of used disk space *)
1787 match diskfree dir, disktotal dir with
1788 | Some dfree, Some dtotal ->
1789 begin
1791 Some (Int64.to_int (100L -- (dfree ** 100L // dtotal)))
1792 with Division_by_zero -> None
1794 | _ -> None
1796 let percentfree dir =
1797 (* percentage of free disk space *)
1798 match diskfree dir, disktotal dir with
1799 | Some dfree, Some dtotal ->
1800 begin
1802 Some (Int64.to_int (dfree ** 100L // dtotal))
1803 with Division_by_zero -> None
1805 | _ -> None
1807 let filesystem dir =
1809 let s = statfs dir in
1810 match s.f_type with
1811 (* values copied from statfs(2) manpage *)
1812 | 0xadf5L -> "ADFS_SUPER_MAGIC"
1813 | 0xADFFL -> "AFFS_SUPER_MAGIC"
1814 | 0x42465331L -> "BEFS_SUPER_MAGIC"
1815 | 0x1BADFACEL -> "BFS_MAGIC"
1816 | 0xFF534D42L -> "CIFS_MAGIC_NUMBER"
1817 | 0x73757245L -> "CODA_SUPER_MAGIC"
1818 | 0x012FF7B7L -> "COH_SUPER_MAGIC"
1819 | 0x28cd3d45L -> "CRAMFS_MAGIC"
1820 | 0x1373L -> "DEVFS_SUPER_MAGIC"
1821 | 0x00414A53L -> "EFS_SUPER_MAGIC"
1822 | 0x137DL -> "EXT_SUPER_MAGIC"
1823 | 0xEF51L -> "ext2" (* EXT2_OLD_SUPER_MAGIC *)
1824 | 0xEF53L -> "ext2/3" (* EXT2/3_SUPER_MAGIC *)
1825 | 0x4244L -> "HFS_SUPER_MAGIC"
1826 | 0xF995E849L -> "HPFS_SUPER_MAGIC"
1827 | 0x958458f6L -> "HUGETLBFS_MAGIC"
1828 | 0x9660L -> "ISOFS_SUPER_MAGIC"
1829 | 0x4000L -> "ISOFS_SUPER_MAGIC_WIN" (* from coreutils-5.2.1, stat.c *)
1830 | 0x4004L -> "ISOFS_SUPER_MAGIC_R_WIN" (* from coreutils-5.2.1, stat.c *)
1831 | 0x72b6L -> "JFFS2_SUPER_MAGIC"
1832 | 0x3153464aL -> "JFS_SUPER_MAGIC"
1833 | 0x137FL -> "MINIX_SUPER_MAGIC"
1834 | 0x138FL -> "MINIX_SUPER_MAGIC2"
1835 | 0x2468L -> "MINIX2_SUPER_MAGIC"
1836 | 0x2478L -> "MINIX2_SUPER_MAGIC2"
1837 | 0x4d44L -> "msdos" (* MSDOS_SUPER_MAGIC *)
1838 | 0x4006L -> "fat" (* from coreutils-5.2.1, stat.c *)
1839 | 0x564cL -> "NCP_SUPER_MAGIC"
1840 | 0x6969L -> "NFS_SUPER_MAGIC"
1841 | 0x5346544eL -> "ntfs" (* NTFS_SB_MAGIC *)
1842 | 0x9fa1L -> "OPENPROM_SUPER_MAGIC"
1843 | 0x9fa0L -> "PROC_SUPER_MAGIC"
1844 | 0x002fL -> "QNX4_SUPER_MAGIC"
1845 | 0x52654973L -> "reiserfs" (* REISERFS_SUPER_MAGIC *)
1846 | 0x52345362L -> "reiser4"
1847 | 0x7275L -> "ROMFS_MAGIC"
1848 | 0x517BL -> "smb" (* SMB_SUPER_MAGIC *)
1849 | 0x012FF7B6L -> "SYSV2_SUPER_MAGIC"
1850 | 0x012FF7B5L -> "SYSV4_SUPER_MAGIC"
1851 | 0x01021994L -> "tmpfs" (* TMPFS_MAGIC *)
1852 | 0x15013346L -> "UDF_SUPER_MAGIC"
1853 | 0x00011954L -> "UFS_MAGIC"
1854 | 0x9fa2L -> "USBDEVICE_SUPER_MAGIC"
1855 | 0xa501FCF5L -> "VXFS_SUPER_MAGIC"
1856 | 0x012FF7B4L -> "XENIX_SUPER_MAGIC"
1857 | 0x58465342L -> "xfs" (* XFS_SUPER_MAGIC *)
1858 | 0x012FD16DL -> "_XIAFS_SUPER_MAGIC"
1859 | 5L -> "iso9660" (* Cygwin *)
1860 | 6L -> "fat" (* Cygwin *)
1861 | 0x700FFL -> "ntfs" (* Cygwin *)
1862 | 0xC3L -> "ext2/3" (* Cygwin *)
1863 | _ -> if s.f_basetype <> "-1" then
1864 s.f_basetype
1865 else
1866 Printf.sprintf "unknown (%Ld)" s.f_type
1867 with e -> "not supported"
1869 let set_max_cache_size v =
1870 max_cache_size := v;
1871 while !FDCache.cache_size > !max_cache_size do FDCache.close_one () done
1873 let get_max_cache_size () = !max_cache_size
1875 let _ =
1876 Heap.add_memstat "Unix32" (fun level buf ->
1877 let counter = ref 0 in
1878 H.iter (fun _ -> incr counter) table;
1879 Printf.bprintf buf " table: %d\n" !counter;
1880 Printf.bprintf buf " modified_files: %d\n" (List.length !modified_files);
1881 Printf.bprintf buf " max cache_size: %d\n" !max_cache_size;
1882 Printf.bprintf buf " fd cache_size: %d\n" !FDCache.cache_size