(*Definition genv := Genv.t Clight.fundef Ctypes.type.*)

Inductive fmode: Type := RDONLY | WRONLY | RDWR.(*Definition genv := Genv.t Clight.fundef Ctypes.type.*)

Inductive fmode: Type := RDONLY | WRONLY | RDWR.(*Definition genv := Genv.t Clight.fundef Ctypes.type.*)

Inductive fmode: Type := RDONLY | WRONLY | RDWR.(*Definition genv := Genv.t Clight.fundef Ctypes.type.*)

Inductive fmode: Type := RDONLY | WRONLY | RDWR.Definition fwritable (mode: fmode) :=
  match mode with
  | RDONLY => false
  | WRONLY => true
  | RDWR => true
  end.(* model of a simple "flat" filesystem *)

Inductive file: Type := mkfile: forall (size: nat) (contents: forall (ofs: nat), memval), file.(* model of a simple "flat" filesystem *)

Inductive file: Type := mkfile: forall (size: nat) (contents: forall (ofs: nat), memval), file.Definition fptr := nat.(*typedef struct {
  int cur_dir; // i-node of the current working directory
  int n_inodes; // allocated i-nodes
  int n_open_files;
  uint8_t map_open_files[BITMAP_SIZE(N_INODES)];
  uint8_t map_inodes[BITMAP_SIZE(N_INODES)];
  uint8_t map_blocks[BITMAP_SIZE(N_BLOCKS)];
  uint32_t fd_map[MAX_OPEN_FILES]; // maps FDs to i-nodes
  uint32_t fptr_map[MAX_OPEN_FILES]; // maps FDs to file pointers
  file_mode_t fperm_map[MAX_OPEN_FILES]; // maps FDs to file permissions
  uint8_t block_cache[CACHE_N_BLOCKS][BLOCK_SIZE]; // data block cache
  uint32_t cache_map[CACHE_N_BLOCKS]; // maps cache block # to data block #
  // maps cache block # to approximate time since last access
  uint32_t cache_last_access_map[CACHE_N_BLOCKS];
} fs_data;*)

Inductive fs_data: Type := mkfsdata: forall
  (fdtable: forall (fd: int), option (fmode*fptr)) (*None = fd unused*)
  (fcache: forall (fd: int), option file) (*models in-memory storage*)
  (fnames: forall (fd: int), option int) (*map from file descriptors to file names*),
  fs_data.(*typedef struct {
  int cur_dir; // i-node of the current working directory
  int n_inodes; // allocated i-nodes
  int n_open_files;
  uint8_t map_open_files[BITMAP_SIZE(N_INODES)];
  uint8_t map_inodes[BITMAP_SIZE(N_INODES)];
  uint8_t map_blocks[BITMAP_SIZE(N_BLOCKS)];
  uint32_t fd_map[MAX_OPEN_FILES]; // maps FDs to i-nodes
  uint32_t fptr_map[MAX_OPEN_FILES]; // maps FDs to file pointers
  file_mode_t fperm_map[MAX_OPEN_FILES]; // maps FDs to file permissions
  uint8_t block_cache[CACHE_N_BLOCKS][BLOCK_SIZE]; // data block cache
  uint32_t cache_map[CACHE_N_BLOCKS]; // maps cache block # to data block #
  // maps cache block # to approximate time since last access
  uint32_t cache_last_access_map[CACHE_N_BLOCKS];
} fs_data;*)

Inductive fs_data: Type := mkfsdata: forall
  (fdtable: forall (fd: int), option (fmode*fptr)) (*None = fd unused*)
  (fcache: forall (fd: int), option file) (*models in-memory storage*)
  (fnames: forall (fd: int), option int) (*map from file descriptors to file names*),
  fs_data.Inductive os_step: genv -> xT -> mem -> xT -> mem -> Prop :=
| os_corestep: forall ge z c fs m c' m',
  corestep csem ge c m c' m' ->
  os_step ge (mkxT z c fs) m (mkxT z c' fs) m'
| os_open: forall ge z s m c md0 fname0 md fname unused_fd fs',
  let fs := get_fs s in
  at_external csem (get_core s) = Some (SYS_OPEN, SYS_OPEN_SIG, fname0::md0::nil) ->
  alloc_fd fs = Some unused_fd ->
  val2omode md0 = Some md ->
  val2oint fname0 = Some fname ->
  ~is_open fs fname ->
  fs_open fs unused_fd fname md = Some fs' ->
  after_external csem (Some (Vint unused_fd)) (get_core s) = Some c ->
  os_step ge s m (mkxT z c fs') m
| os_read: forall ge z s m c fd0 fd buf adr nbytes0 nbytes bytes m',
  let fs := get_fs s in
  at_external csem (get_core s) = Some (SYS_READ, SYS_READ_SIG, fd0::buf::nbytes0::nil) ->
  val2oint fd0 = Some fd ->
  val2oadr buf = Some adr ->
  val2oint nbytes0 = Some nbytes ->
  fs_read fs fd (nat_of_Z (Int.unsigned nbytes)) = Some bytes ->
  Forall is_defchar bytes ->
  (Zlength bytes > 0)%Z ->
  Mem.storebytes m (fst adr) (Int.unsigned (snd adr)) bytes = Some m' ->
  after_external csem (Some (Vint (Int.repr (Zlength bytes)))) (get_core s) = Some c ->
  os_step ge s m (mkxT z c fs) m'
(*| os_write: forall ge z s m c fd0 fd adr buf nbytes0 nbytes bytes fs' nbytes_written sig,
  let fs := get_fs s in
  at_external csem (get_core s) = Some (SYS_WRITE, sig, fd0::buf::nbytes0::nil) ->
  val2oint fd0 = Some fd ->
  val2oadr buf = Some adr ->
  val2oint nbytes0 = Some nbytes ->
  Mem.loadbytes m (fst adr) (Int.unsigned (snd adr)) (Int.unsigned nbytes) = Some bytes ->
  fs_write fs fd bytes = Some (nbytes_written, fs') ->
  after_external csem (Some (Vint (Int.repr (Z_of_nat nbytes_written)))) (get_core s) = Some c ->
  os_step ge s m (mkxT z c fs') m*).Inductive fs: Type := mkfs: forall
  (MAX_FILE_DESCRIPTORS: int)
  (fsdata: fs_data)
  (fstore: forall (name: int), option file), (*models on-disk storage*)
  fs.Definition get_max_fds := match fs with mkfs maxfds _ _ => maxfds end.Definition get_fsdata := match fs with mkfs _ fsdata _ => fsdata end.Definition get_fstore := match fs with mkfs _ _ fstore => fstore end.Definition get_fdtable := match get_fsdata with mkfsdata fdtable _ _ => fdtable end.Definition get_fcache := match get_fsdata with mkfsdata _ fcache _ => fcache end.Definition get_fnames := match get_fsdata with mkfsdata _ _ fnames => fnames end.Definition get_file := get_fcache fd.Definition get_fmode :=
  match get_fdtable fd with
  | None => None
  | Some (md, cur) => Some md
  end.Definition get_fptr :=
  match get_fdtable fd with
  | None => None
  | Some (md, cur) => Some cur
  end.Definition get_size := match f with mkfile sz _ => sz end.Definition get_contents := match f with mkfile _ contents => contents end.Definition isSome {A: Type} (a: option A) :=
  match a with
  | None => false
  | Some _ => true
  end.Fixpoint get_nfiles_open_aux (n: nat) (fcache: int -> option (fmode*fptr)) :=
  match n with
  | O => O
  | S n' => if isSome (fcache (Int.repr (Z_of_nat n)))
              then S (get_nfiles_open_aux n' fcache)
              else get_nfiles_open_aux n' fcache
  end.Definition get_nfiles_open :=
  get_nfiles_open_aux (nat_of_Z (Int.unsigned get_max_fds)) get_fdtable.Fixpoint find_unused_fd (n: nat) (fdtable: int -> option (fmode*fptr)) :=
  match n with
  | O => None
  | S n' => match fdtable (Int.repr (Z_of_nat n)) with
            | None => Some (Int.repr (Z_of_nat n))
            | Some _ => find_unused_fd n' fdtable
            end
  end.Definition max_fds: nat := nat_of_Z (Int.unsigned (get_max_fds fs)).Definition alloc_fd := find_unused_fd max_fds (get_fdtable fs).

Lemma alloc_fd_success : (get_nfiles_open fs < max_fds)%nat -> exists unused_fd, alloc_fd = Some unused_fd.