let file_tree ?(follow_symlinks=false) path =
    let directory p l = return (`Node (p, l)) in
    let file p l = return (`Leaf (p, l)) in
    let rec find_aux ?name_to_report path =
      let name =
        match name_to_report with
        | Some s -> s
        | None -> Filename.basename path in
      file_info path
      >>= begin function
      | `Absent -> fail (`File_not_found path)
      | `Block_device
      | `Character_device
      | `Fifo
      | `Regular_file _
      | `Socket as k -> file name k
      | `Symlink content as k ->
        begin match follow_symlinks with
        | true ->
          let continue =
            if Filename.is_relative content
            then (Filename.concat path content)
            else content in
          find_aux ~name_to_report:(Filename.basename path) continue
        | false ->
          file name k
        end
      | `Directory ->
        let `Stream next_dir = list_directory path in
        let rec loop acc =
          next_dir ()
          >>= begin function
          | Some ".."
          | Some "." -> loop acc
          | Some name -> loop (name :: acc)
          | None -> return acc
          end
        in
        loop []
        >>= fun sub_list ->
        List.fold_left
          ~init:(return []) (List.sort ~cmp:String.compare sub_list)
          ~f:(fun prev dir ->
              prev >>= fun l ->
              find_aux (Filename.concat path dir)
              >>= fun newone ->
              return (newone :: l))
        >>| List.rev
        >>= fun sub_tree ->
        directory name sub_tree
      end
    in
    (find_aux path
     >>< function
     | `Ok o -> return o
     | `Error e ->
       begin match e with
       | `Io_exn e -> fail (`System (`File_tree path, `Exn e))
       | `File_not_found _ as e -> fail (`System (`File_tree path, e))
       | `System e -> fail (`System e)
       end)