2 (* Copyright 2001, 2002 b8_bavard, b8_fee_carabine, INRIA *)
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
25 let log_prefix = "[Ux32]"
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 =
54 Unix2.really_write fd s pos len
56 let really_write = Unix2.really_write
58 module FDCache
= struct
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
()
71 if not t
.destroyed
then
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"
78 (Printexc2.to_string e
);
91 let rec close_one () =
92 if not
(Fifo.empty
cache) then
93 let t = Fifo.take
cache in
100 with _
-> close_one ()
102 let check_destroyed t =
104 failwith
(Printf.sprintf
105 "Unix32.check_destroyed %s: Cannot use destroyed FD" t.filename
)
108 if not
t.destroyed
then begin
109 (try close t with _
-> ());
113 let _local_force_fd creat
t =
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 *)
126 Unix.openfile
t.filename
127 (if creat
then rw_creat_flag else rw_flag)
130 Unix.openfile
t.filename
ro_flag 0o400
132 if !verbose then lprintf_nl "Exception in FDCache._local_force_fd %s (%s): %s"
134 (if t.writable
then "rw" else "ro")
135 (Printexc2.to_string e
);
139 (* lprintf "local_force: opening %d\n" (Obj.magic fd_rw); *)
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");
158 let _fd = _local_force_fd true t in
161 Unix.Unix_error
(Unix.EISDIR
, _
, _
) -> t
162 | Unix.Unix_error
(Unix.EACCES
, _
, _
) when Autoconf.windows
-> t
165 let local_force_fd t = _local_force_fd false t
168 while not
(Fifo.empty
cache) do
176 Unix2.rename t.filename f
;
179 if !verbose then lprintf_nl "Exception in FDCache.rename %s %s: %s"
182 (Printexc2.to_string e
);
185 let ftruncate64 t len sparse
=
188 Unix2.c_ftruncate64
(local_force_fd t) len sparse
190 if !verbose then lprintf_nl "Exception in FDCache.ftruncate64 %s %Ld (%s): %s"
193 (if sparse
then "sparse" else "not sparse")
194 (Printexc2.to_string e
);
200 Unix2.c_getfdsize64
(local_force_fd t)
202 Unix.Unix_error
(Unix.EISDIR
, _
, _
) -> 0L
203 | Unix.Unix_error
(Unix.EACCES
, _
, _
) when Autoconf.windows
-> 0L
205 if !verbose then lprintf_nl "Exception in FDCache.getsize64 %s: %s"
207 (Printexc2.to_string e
);
211 let module U
= Unix.LargeFile
in
216 U.fstat
(local_force_fd t)
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
225 | Not_found
-> "", ""
226 | e
-> if !verbose then
227 lprintf_nl "Exception in FDCache.owner %s: %s"
228 t.filename
(Printexc2.to_string e
);
234 let st = Unix.LargeFile.stat
t.filename
in
235 st.Unix.LargeFile.st_mtime
237 if !verbose then lprintf_nl "Exception in FDCache.mtime64 %s: %s"
239 (Printexc2.to_string e
);
245 Sys.file_exists
t.filename
247 if !verbose then lprintf_nl "Exception in FDCache.exists %s: %s"
249 (Printexc2.to_string e
);
256 Sys.remove t.filename
;
259 if !verbose then lprintf_nl "Exception in FDCache.remove %s: %s"
261 (Printexc2.to_string e
);
264 let multi_rename t f file
=
268 (let d = (Filename.dirname
(Filename.concat f file
)) in
270 Unix2.chmod
d !create_dir_mode;
271 Unix2.can_write_to_directory
d);
273 Unix2.rename t.filename
(Filename.concat f file
);
275 Unix.Unix_error
(Unix.EACCES
, _
, _
) when
276 Autoconf.windows
&& (Unix2.list_directory
t.filename
= [])
280 lprintf_nl "Exception in FDCache.multi_rename %s %s: %s"
282 (Filename.concat f file
)
283 (Printexc2.to_string 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
);
291 lprintf_nl "really_read %s %Ld %d"
295 Unix2.really_read
fd string string_pos len
297 if !verbose then lprintf_nl "Exception in FDCache.read %s %Ld %d: %s"
301 (Printexc2.to_string 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
);
310 lprintf_nl "really_write %s %Ld %d"
314 really_write fd string string_pos len
316 if !verbose then lprintf_nl "Exception in FDCache.write file %s file_pos=%Ld len=%d string_pos=%d, string length=%d: %s"
321 (String.length
string)
322 (Printexc2.to_string e
);
325 let copy_chunk t1 t2 pos1 pos2 len64
=
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)
343 if !verbose then lprintf_nl "Exception in FDCache.copy_chunk %s %Ld to %s %Ld (%Ld): %s"
349 (Printexc2.to_string e
);
354 module type File
= sig
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
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
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
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
415 mutable filename
: string;
418 mutable current_len
: int64
;
419 mutable fd : FDCache.t;
420 mutable tail
: file list
;
424 mutable dirname
: string;
425 mutable size
: int64
;
426 mutable files
: file list
;
427 mutable tree
: btree
;
431 Node
of int64
* btree
* btree
434 let rec make_tree files
=
436 | [||] -> failwith
"make_tree: no files"
437 | [| file
|] -> Leaf file
439 let len = Array.length files
in
440 let middle = len / 2 in
441 let pos = files
.(middle).pos in
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
=
453 | Node
(pos, tree1
, tree2
) ->
455 (if file_pos
< pos then tree1
else tree2
) file_pos
457 let rec subfile_tree_map indent tree f
=
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
=
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
=
479 let rec reverse_files files tail
=
484 reverse_files others
(file
:: tail
)
486 let files = reverse_files files2
[] in
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
)
502 current_len
= FDCache.getsize64 fd;
512 let find_file t chunk_begin
=
513 let file = find_file t.tree chunk_begin
in
516 let do_on_remaining tail arg remaining f
=
518 let rec iter_files list arg remaining
=
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)
550 let temp_file = Filename2.temp_file "chunk" ".tmp" in
551 let file_out = FDCache.create temp_file true in
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
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
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;
575 FDCache.close file_out;
576 let fd = FDCache.local_force_fd file_out in
578 FDCache.close file_out;
579 Sys.remove temp_file;
582 if !verbose then lprintf_nl "Exception in MultiFile.apply_on_chunk %s %Ld %Ld: %s"
586 (Printexc2.to_string e
);
587 (try FDCache.close file_out with _
-> ());
588 (try Sys.remove temp_file with _
-> ());
592 List.iter (fun file -> FDCache.close file.fd) t.files
595 List.iter (fun file -> FDCache.destroy file.fd) t.files
599 List.iter (fun file -> FDCache.multi_rename file.fd f
file.filename
)
602 let ftruncate64 t size sparse
=
605 let getsize64 t = t.size
609 let st = Unix.LargeFile.stat
t.dirname
in
610 st.Unix.LargeFile.st_mtime
612 if !verbose then lprintf_nl "Exception in MultiFile.mtime64 %s: %s"
614 (Printexc2.to_string e
);
618 Sys.file_exists
t.dirname
623 if Sys.file_exists
t.dirname
then
624 Unix2.remove_all_directory
t.dirname
626 if !verbose then lprintf_nl "Exception in MultiFile.remove %s: %s"
628 (Printexc2.to_string 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 *)
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
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
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;
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;
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
691 (try Unix.utimes
t.dirname
time time with _
-> ());
694 let is_closed _
= false
697 module SparseFile
= struct
700 mutable chunkname
: string;
703 mutable fd : FDCache.t;
707 mutable filename
: string;
708 mutable dirname
: string;
709 mutable size
: int64
;
710 mutable chunks
: chunk array
;
711 mutable writable
: bool;
716 chunkname
= zero_chunk_name;
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;
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
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
747 if chunk.pos > pos then
748 if med = start
then start
753 iter t next (start
+len-next)
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
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
767 if chunk.pos > pos then
768 if med = start
then start
773 iter t next (start
+len-next)
777 (* (*** debugging code ***)
779 let find_read_pos2 t pos =
780 let rec iter t i
len =
783 let chunk = t.chunks
.(i
) in
784 if chunk.pos ++ chunk.len > pos then i
788 iter t 0 (Array.length
t.chunks
)
790 let find_write_pos2 t pos =
791 let rec iter t i
len =
794 let chunk = t.chunks
.(i
) in
795 if chunk.pos ++ chunk.len >= pos then i
799 iter t 0 (Array.length
t.chunks
)
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);
813 pos = Int64.of_int
pos;
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
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))
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
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
863 if index >= nchunks then
864 let z = zero_chunk () in
865 z.pos <- chunk_begin
;
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
);
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
894 Sys.remove temp_file;
897 if !verbose then lprintf_nl "Exception in SparseFile.apply_on_chunk %s %Ld %Ld: %s"
901 (Printexc2.to_string e
);
902 (try FDCache.close file_out with _ -> ());
903 (try Sys.remove temp_file with _ -> ());
907 Array.iter (fun file -> FDCache.close file.fd) t.chunks
910 Array.iter (fun file -> FDCache.destroy file.fd) t.chunks
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
924 if index >= nchunks then
925 let z = zero_chunk () in
926 z.pos <- chunk_begin;
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);
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
962 let ftruncate64 t size sparse
=
965 let getsize64 t = t.size
969 let st = Unix.LargeFile.stat
t.dirname in
970 st.Unix.LargeFile.st_mtime
972 if !verbose then lprintf_nl "Exception in SparseFile.mtime64 %s: %s"
974 (Printexc2.to_string e
);
978 Sys.file_exists
t.dirname
983 (* lprintf "Removing %s\n" t.dirname; *)
984 Unix2.remove_all_directory
t.dirname
986 if !verbose then lprintf_nl "Exception in SparseFile.remove %s: %s"
988 (Printexc2.to_string 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
999 if index >= nchunks then
1000 let z = zero_chunk () in
1001 z.pos <- chunk_begin;
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);
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
1037 chunkname
= chunk_name;
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
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
1053 chunkname
= chunk_name;
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;
1064 let nchunks = Array.length
t.chunks in
1065 let rec iter index chunk_begin string_pos
len =
1067 let next_index, max_len, max_len64 =
1068 if index = nchunks-1 then
1069 index, len, Int64.of_int
len
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
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
1098 (try Unix.utimes
t.dirname time time with _ -> ())
1103 | MultiFile
of MultiFile.t
1104 | DiskFile
of DiskFile.t
1105 | SparseFile
of SparseFile.t
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
1119 let hash t = Hashtbl.hash (t.filename
, t.writable
)
1121 let equal x y
= x
.filename
= y
.filename
&& x
.writable
= y
.writable
1125 file_kind
= Destroyed
;
1132 let table = H.create 100
1134 let destroyed t = t.file_kind
= Destroyed
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; *)
1153 file_kind
= creator f
;
1155 writable
= writable
;
1162 (* check if a writable descriptor on the same file exists *)
1163 let find_writable fd =
1164 if fd.writable
then Some
fd
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"
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"
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 =
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"
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;
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
;
1244 match find_writable t with
1247 if t.buffers
= [] then () else
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)
1254 if !verbose then lprintf_nl "flush_fd";
1256 let rec iter_out () =
1257 match t.buffers
with
1259 | (s, pos_s
, len_s
, offset
, len) :: tail
->
1260 Buffer.reset
buffer;
1261 Buffer.add_substring
buffer s pos_s len_s
;
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
;
1273 iter_in offset
(len ++ len2
);
1275 if in_offset < Int64.zero
then begin
1276 flush_buffer t offset
;
1279 let keep_len = len2
-- in_offset in
1280 if !verbose then lprintf_nl "overlap %Ld" keep_len;
1282 if keep_len <= 0L then begin
1283 buffered_bytes := !buffered_bytes -- len2
;
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)
1295 let read t file_pos string string_pos
len =
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"
1307 if !verbose then lprintf_nl "flush all";
1320 modified_files := iter !modified_files;
1321 if !buffered_bytes <> 0L then
1322 lprintf_nl "[ERROR] remaining bytes after flush"
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
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;
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 ()
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 =
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)
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)
1371 copy_chunk t1 t2 pos1 pos2
(Int64.to_int
len64)
1373 let close_all = FDCache.close_all
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" *)
1384 if t.file_kind
<> Destroyed
then begin
1386 (match t.file_kind
with
1387 | DiskFile
t -> DiskFile.destroy t
1388 | MultiFile
t -> MultiFile.destroy t
1389 | SparseFile
t -> SparseFile.destroy t
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
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"
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"
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"
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;
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;
1438 let old_fd_exists = fd_exists s in
1440 let fd = create_ro s in
1444 with Unix.Unix_error
(Unix.ENOENT
, _, _) -> false
1446 if not
old_fd_exists then close fd;
1448 with Unix.Unix_error
(Unix.ENOENT
, _, _) -> false
1451 match t.file_kind
with
1452 | DiskFile
t -> DiskFile.owner t
1453 | MultiFile
t -> "", ""
1454 | SparseFile
t -> "", ""
1455 | Destroyed
-> "", ""
1458 let old_fd_exists = fd_exists s in
1459 let fd = create_ro s in
1465 if not
old_fd_exists then close fd;
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 (*************************************************************************)
1485 (* Files sharing parts *)
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
1499 mutable file_parts
: part
list;
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
1516 lprintf_nl "Copy shared part to another file";
1517 copy part
.part_file
t.file part
.part_begin part
.part_begin
1519 lprintf_nl " Copy done.";
1520 part
.part_file
<- t.file;
1521 part
.part_shared
<- tail
;
1522 { part
with part_file
= file; part_shared
= [] }
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
1532 lprintf_nl " Copy done.";
1533 part
.part_shared
<- List.filter
(fun t -> t.file != file)
1535 { part
with part_file
= file; part_shared
= [] }
1541 t.file_parts
<- copy_shared_parts_out t.file t.file_parts
;
1545 t.file_parts
<- copy_shared_parts_out t.file t.file_parts
;
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
1586 | [] -> assert false
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
1592 if part
.part_end
> chunk_begin then
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
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;
1616 let v = apply_on_chunk file_out zero
chunk_len f
in
1617 Sys.remove temp_file;
1620 (try Sys.remove temp_file with _ -> ());
1626 let ftruncate64 t len sparse
=
1627 ftruncate64 t.file len sparse
1629 let maxint64 = megabytes
1000000
1635 part_end
= maxint64;
1636 part_len
= maxint64;
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
; ()
1676 let find_file t chunk_begin =
1677 match t.file_kind
with
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
1686 let sf = List.nth
t.MultiFile.files index in
1687 (sf.MultiFile.filename, sf.MultiFile.pos , sf.MultiFile.len)
1688 | _ -> ("unimplemeted" , 0L, 0L)
1694 let t = create_rw "/dev/null" in
1695 t.file_kind <- Destroyed;
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
1716 let _ = Callback.register_exception
"not supported" Not_supported
1717 let _ = Callback.register_exception
"error" Error
1719 external statfs
: string -> statfs
= "statfs_statfs"
1723 if sf.f_frsize
= Int64.zero
|| sf.f_frsize
= -1L then
1737 Some
(statfs dir
).f_blocks
1742 Some
(statfs dir
).f_bfree
1747 Some
(statfs dir
).f_bavail
1752 Some
(Int64.to_int
(min
(statfs dir
).f_fnamelen
(Int64.of_int
Sys.max_string_length
)))
1756 (* total disk space in bytes *)
1758 let s = statfs dir
in
1761 Some
(bsize ** s.f_blocks
)
1766 (* free disk space in bytes *)
1768 let s = statfs dir
in
1771 Some
(bsize ** s.f_bavail
)
1776 (* used disk space in bytes *)
1778 let s = statfs dir
in
1781 Some
(bsize ** (s.f_blocks
-- s.f_bavail
))
1785 let percentused dir
=
1786 (* percentage of used disk space *)
1787 match diskfree dir
, disktotal dir
with
1788 | Some dfree
, Some dtotal
->
1791 Some
(Int64.to_int
(100L -- (dfree
** 100L // dtotal
)))
1792 with Division_by_zero
-> None
1796 let percentfree dir
=
1797 (* percentage of free disk space *)
1798 match diskfree dir
, disktotal dir
with
1799 | Some dfree
, Some dtotal
->
1802 Some
(Int64.to_int
(dfree
** 100L // dtotal
))
1803 with Division_by_zero
-> None
1807 let filesystem dir
=
1809 let s = statfs dir
in
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
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
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