let fill_buffer_with_dirent buffer name n =
  let len = String.length name in
  assert (String.length buffer >= dirent_size);
  assert (len <= filename_max_size);
  String.blit name 0 buffer 0 len;
  if len < filename_max_size then
    for i = len to filename_max_size - 1 do buffer.[i] <- '\000' done;
  Misc.write_int n buffer filename_max_size

let add_dirent dir_inode name inode_nb =
  let descr = open_inode dir_inode true in
  let buffer = String.create dirent_size in
  let rec find_free_entry pos =
    if pos < descr.inode.stats.st_size then
      let _ = lseek descr pos SEEK_SET in
      let _ = read descr buffer 0 1 in
      if buffer.[0] <> '\000' then
 find_free_entry (pos + dirent_size)
      else pos
    else descr.inode.stats.st_size in
  let pos = find_free_entry (2 * dirent_sizein
  let k = lseek descr pos SEEK_SET in
  fill_buffer_with_dirent buffer name inode_nb;
  let _ = write descr buffer 0 dirent_size in
  close descr

let validate_name name =
  String.length name > 0  &&
  (let rec ok i = i < 0 || name.[i] <> '\000' && ok (i - 1) in
  ok (String.length name -1))

let mkdir name =
  let dirname = Filename.dirname name in
  let dir_inode =
    try namei dirname
    with Not_found -> system_error ENOENT "mkdir" dirname in
  try_finalize begin fun () ->
      let basename = Filename.basename name in
      if not (validate_name basenamethen
          system_error EINVAL "mkdir" basename;
      try
        let inode = find_name_in_dir_inode dir_inode basename in
        iput inode;
        system_error EEXIST "mkdir" name
      with End_of_file ->
        let new_inode = ialloc S_DIR in
        add_dirent new_inode Filename.current_dir_name new_inode.stats.st_ino;
        new_inode.stats.st_nlink <- 2; (* current_dir_name. *)
        add_dirent new_inode Filename.parent_dir_name dir_inode.stats.st_ino;
        add_dirent dir_inode basename new_inode.stats.st_ino;
        (* above sequence cannot fail *)
        iput new_inode;
        (* dir_inode has one more link: parent_dir_name in new_inode *)
        dir_inode.stats.st_nlink <- dir_inode.stats.st_nlink + 1;
    end ()
    iput dir_inode

      
let openfile name flags =
  if List.sort compare flags <> [ O_RDWRO_CREATthen
    implementation_error "Manadatory flags are [ O_RDWR; O_CREAT ]";
  let dirname = Filename.dirname name in
  let dir_inode =
    try
      namei dirname
    with Not_found ->
      system_error EACCESS "openfile" dirname in
  try_finalize begin function () ->
    if dir_inode.stats.st_kind != S_DIR then
      system_error ENOTDIR "openfile" dirname;
    let filename = Filename.basename name in
    if not (validate_name filenamethen
      system_error EINVAL "openfile" filename;
    let inode =
      try
        let inode = find_name_in_dir_inode dir_inode filename in
        let system_error x = iput inodesystem_error x in
        if inode.stats.st_kind != S_REG then
          system_error EISDIR "openfile" name;
        if inode.stats.st_size > max_file_size then
          system_error EIO "Inconsistent file system" name;
        inode
      with End_of_file ->
        let new_inode = ialloc S_REG in
        add_dirent dir_inode filename new_inode.stats.st_ino;
        new_inode in
     open_inode inode false
   end ()
     iput dir_inode
;;