open Misc open Common module type CACHE = sig module Blk : sig val block_size : int (* taille des blocs *) type blk (* type opaque pour représenter les blocs en mémoire, il contient entre autres, le numéro du bloc et un tampon associé *) val bread : int -> blk (* prend en argument un numéro de bloc et retourne sa représentation mémoire. Indique au cache que la représentation mémoire est utilisée. *) val bzeros : int -> blk (* prend en argument un numéro de bloc et retourne sa représentation mémoire remplie de zéros *) val read_string : blk -> int -> string -> int -> int -> unit (* [ read_string b boff s soff len ] place dans la chaîne de caractères [s], à partir de la position [soff], [len] caractères lus, à partir de la position [boff] dans le bloc représenté en mémoire par [b] *) val write_string : string -> int -> blk -> int -> int -> unit (* [ write_string s soff b boff len ] place dans le bloc représenté par [b], à partir de la position [boff], [len] caractères lus dans la chaîne de caractères [s] à partir de la position [soff] *) val read_int : blk -> int -> int (* [ read_int b boff ] retourne l'entier dont les 4 octets de la représentation binaire ont été récupérés, à partir de la position [boff], dans le bloc représenté par [b] *) val write_int : int -> blk -> int -> unit (* [ write_int i b boff ] place les 4 octets de la représentation binaire de l'entier [i] dans le bloc représenté par [b] à partir de la position [boff] *) val brelse : blk -> unit (* Indique au cache que la représentation mémoire du bloc passé en argument n'est plus utilisée. Doit être exécuté après un bread sur le même bloc. *) val sync : unit -> unit (* copie sur le disque, tous les blocs dont la représentation en mémoire ne correspond pas à celle sur le disque *) end module Inode : sig type inode = { mutable reference_number : int; (* nombre de références actives : descripteurs ouverts *) stats : stats; (* informations de statut *) blocktbl : int array (* table des blocs *) } (* type utilisé pour la représentation des inodes en mémoire *) val blocktbl_offset : int (* offset de la table des blocs dans l'inode *) val blocktbl_size : int (* taille de la table des blocs *) val max_file_size : int (* taille maximale d'un fichier *) val iget : int -> inode (* retourne la représentation mémoire de l'inode dont le numéro est passé en argument et incrémente son compteur de références. *) val iput : inode -> unit (* décrémente le nombre de référence sur l'inode. L'inode peut être écrit sur le disque et son cache récupéré lorsqu'il n'est plus référencé. *) val sync : unit -> unit (* synchronise l'ensemble des représentations mémoire des inodes avec celles sur le disque *) val busy : unit -> int (* retourne false si tous les inodes sont libres, true sinon *) end module Super : sig val root_inode : int (* numéro du bloc contenant l'inode de la racine du système de fichier *) val inode_nb : int (* nombre total d'inodes dans le système de fichiers *) val sfstype : unit -> bool (* vérifie que le système de fichiers est au format "sfs0" *) val force_sfstype : unit -> unit (* déclare le système de fichiers au format "sfs0" *) val inode_nb_ref : int ref (* nombre d'inodes utilisés *) val last_free_inode : int ref (* le dernier inode libre trouvé *) val free_block_list : int ref (* liste des blocs libres *) val free_block_nb : int ref (* nombre de blocs libres *) val sync : unit -> unit (* synchronise le super-bloc en mémoire avec le disque *) end val block_nb : int (* nombre total de blocs (réguliers et inodes) *) val close : unit -> unit (* débranche le disque brutalement *) end module Make (D : Disk.DISK) : CACHE = struct module Blk = struct let block_size = D.block_size type blk = { bid : int; mutable sync : bool; buf : string; mutable ref : int } let block_cache = Hashtbl.create 13 let bread n = try let b = Hashtbl.find block_cache n in assert (b.ref = 0); b.ref <- b.ref + 1; b with Not_found -> let buffer = String.create block_size in D.read_block n buffer 0; let b = { bid = n; sync = true; buf = buffer; ref = 1 } in Hashtbl.add block_cache n b; b let bzeros n = try let b = Hashtbl.find block_cache n in assert (b.ref = 0); b.sync <- false; String.fill b.buf 0 block_size '\000'; b.ref <- 1; b with Not_found -> let buffer = String.make block_size '\000' in let b = { bid = n; sync = false; buf = buffer; ref = 1 } in Hashtbl.add block_cache n b; b let read_string blk blk_offset str str_offset len = String.blit blk.buf blk_offset str str_offset len let write_string str str_offset blk blk_offset len = String.blit str str_offset blk.buf blk_offset len; blk.sync <- false let read_int blk offset = read_int blk.buf offset let write_int n blk offset = write_int n blk.buf offset; blk.sync <- false let brelse blk = if not blk.sync then begin D.write_block blk.bid blk.buf 0; blk.sync <- true end; assert (blk.ref = 1); blk.ref <- blk.ref - 1 let sync() = Hashtbl.iter (fun n b -> assert (b.ref == 0 && b.sync)) block_cache end module Inode = struct type inode = { mutable reference_number : int; stats : stats; blocktbl : int array } (* Début de la table des blocs dans l'inode *) let blocktbl_offset = 12 (* Taille maximale d'un fichier *) let blocktbl_size = (D.block_size - blocktbl_offset) / 4 let max_file_size = D.block_size * blocktbl_size (* Lit un inode en mémoire à partir des données du disque *) let read_inode n = let buffer = String.create D.block_size in D.read_block n buffer 0; let kind = match (read_int buffer 0) with 0 -> S_REG | 1 -> S_DIR | _ -> system_error EIO "read_inode" "kind" in let nlink = read_int buffer 4 in let size = read_int buffer 8 in if size > max_file_size then system_error EIO "read_inode" "size"; let init_block i = (read_int buffer (i*4 + blocktbl_offset)) in let blocktbl = Array.init blocktbl_size init_block in { reference_number = 0; stats = { st_dev = 0; st_ino = n ; st_kind = kind ; st_nlink = nlink; st_size = size; }; blocktbl = blocktbl; } (* Écrit un inode en mémoire sur le disque *) let write_inode inode = if inode.stats.st_size > max_file_size then system_error EIO "write_inode" "size"; let buffer = String.create D.block_size in let kind = match inode.stats.st_kind with S_REG -> 0 | S_DIR -> 1 in write_int kind buffer 0; write_int inode.stats.st_nlink buffer 4; write_int inode.stats.st_size buffer 8; for i = 0 to blocktbl_size - 1 do write_int inode.blocktbl.(i) buffer (i*4+blocktbl_offset) done; D.write_block inode.stats.st_ino buffer 0 let inode_cache = Hashtbl.create 13 let iget n = let inode = try Hashtbl.find inode_cache n with Not_found -> let inode = read_inode n in Hashtbl.add inode_cache n inode; inode in inode.reference_number <- inode.reference_number + 1; inode let iput inode = assert (inode.reference_number > 0); inode.reference_number <- inode.reference_number - 1; write_inode inode; if inode.reference_number = 0 then Hashtbl.remove inode_cache inode.stats.st_ino let sync() = Hashtbl.iter (fun _ n -> write_inode n) inode_cache exception Found of int let busy() = try Hashtbl.iter (fun n v -> if v.reference_number <> 0 then raise (Found n)) inode_cache; 0 with Found n -> n end module Super = struct let (inode_nb_ref, last_free_inode, free_block_list, free_block_nb, fstype) = let buffer = String.create D.block_size in D.read_block 0 buffer 0; ref (read_int buffer 0), ref (read_int buffer 4), ref (read_int buffer 8), ref (read_int buffer 12), ref (String.sub buffer (D.block_size - 4) 4) let root_inode = 1 let inode_nb = !inode_nb_ref let sfs0 = "sfs0" let sfstype() = !fstype = sfs0 let force_sfstype() = fstype := sfs0 let sync() = let buffer = String.make D.block_size '\000' in write_int !inode_nb_ref buffer 0; write_int !last_free_inode buffer 4; write_int !free_block_list buffer 8; write_int !free_block_nb buffer 12; String.blit !fstype 0 buffer (D.block_size - 4) 4; D.write_block 0 buffer 0 end let block_nb = D.block_nb let close = D.close end ;;