mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 16:57:49 +03:00
0b93429dc1
Summary: D21316793 is blocked from landing because there are a few targets in eden that are running pyre via buck targets integration. We can't do custom version overrides for projects that are using a mix of local configurations and buck integration, because buck doesn't provide an interface for setting the equivalent pyre version override. We're moving away from buck targets integration for pyre across the board, and I've run a codemod over the project to clean up all of the buck typing integration (including some residual mypy) as well as updated type ignores / fixmes accordingly. Let me know if you have any concerns; upon skimming it looks like most changes are either converting `type: ignore`s into fixmes, or removing `type: ignores`. Reviewed By: dkgi Differential Revision: D21343093 fbshipit-source-id: 5ee1436377eb526c0a679fb821c42e07cbca52a5
181 lines
6.5 KiB
Python
181 lines
6.5 KiB
Python
#!/usr/bin/env python3
|
|
# Copyright (c) Facebook, Inc. and its affiliates.
|
|
#
|
|
# This software may be used and distributed according to the terms of the
|
|
# GNU General Public License version 2.
|
|
|
|
import argparse
|
|
import binascii
|
|
import os
|
|
import shutil
|
|
import stat
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import BinaryIO, Optional
|
|
|
|
from facebook.eden.overlay.ttypes import OverlayDir
|
|
|
|
from . import cmd_util, debug as debug_mod, overlay as overlay_mod, subcmd as subcmd_mod
|
|
|
|
|
|
cmd = debug_mod.debug_cmd
|
|
|
|
|
|
@cmd("overlay", "Show data about the overlay")
|
|
# pyre-fixme[13]: Attribute `args` is never initialized.
|
|
# pyre-fixme[13]: Attribute `overlay` is never initialized.
|
|
class OverlayCmd(subcmd_mod.Subcmd):
|
|
args: argparse.Namespace
|
|
overlay: overlay_mod.Overlay
|
|
|
|
def setup_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
parser.add_argument(
|
|
"-n",
|
|
"--number",
|
|
type=int,
|
|
help="Display information for the specified inode number.",
|
|
)
|
|
parser.add_argument(
|
|
"-d", "--depth", type=int, default=0, help="Recurse to the specified depth."
|
|
)
|
|
parser.add_argument(
|
|
"-r",
|
|
"--recurse",
|
|
action="store_const",
|
|
const=-1,
|
|
dest="depth",
|
|
default=0,
|
|
help="Recursively print child entries.",
|
|
)
|
|
parser.add_argument(
|
|
"-O",
|
|
"--overlay",
|
|
help="Explicitly specify the path to the overlay directory.",
|
|
)
|
|
parser.add_argument(
|
|
"-x",
|
|
"--extract-to",
|
|
dest="output_path",
|
|
help="Copy the specified inode data to the destination path.",
|
|
)
|
|
parser.add_argument("path", nargs="?", help="The path to the eden mount point.")
|
|
|
|
def run(self, args: argparse.Namespace) -> int:
|
|
self.args = args
|
|
if args.overlay is not None:
|
|
if args.path:
|
|
rel_path = Path(args.path)
|
|
else:
|
|
rel_path = Path()
|
|
overlay_dir = Path(args.overlay)
|
|
else:
|
|
path = args.path or os.getcwd()
|
|
_instance, checkout, rel_path = cmd_util.require_checkout(args, path)
|
|
overlay_dir = checkout.state_dir.joinpath("local")
|
|
|
|
self.overlay = overlay_mod.Overlay(str(overlay_dir))
|
|
|
|
if args.number is not None:
|
|
self._process_root(args.number, Path())
|
|
elif rel_path != Path():
|
|
inode_number = self.overlay.lookup_path(rel_path)
|
|
if inode_number is None:
|
|
print(f"{rel_path} is not materialized", file=sys.stderr)
|
|
return 1
|
|
self._process_root(inode_number, rel_path)
|
|
else:
|
|
self._process_root(1, Path())
|
|
|
|
return 0
|
|
|
|
def _process_root(self, inode_number: int, initial_path: Path) -> None:
|
|
output_path: Optional[Path] = None
|
|
if self.args.output_path is not None:
|
|
output_path = Path(self.args.output_path)
|
|
|
|
with self.overlay.open_overlay_file(inode_number) as f:
|
|
header = self.overlay.read_header(f)
|
|
if header.type == overlay_mod.OverlayHeader.TYPE_DIR:
|
|
if output_path:
|
|
self.overlay.extract_dir(inode_number, output_path)
|
|
print(f"Extracted materialized directory contents to {output_path}")
|
|
else:
|
|
self._process_overlay(inode_number, initial_path)
|
|
elif header.type == overlay_mod.OverlayHeader.TYPE_FILE:
|
|
if output_path:
|
|
self.overlay.extract_file(
|
|
inode_number, output_path, stat.S_IFREG | 0o644
|
|
)
|
|
print(f"Extracted inode {inode_number} to {output_path}")
|
|
else:
|
|
self._print_file(f)
|
|
else:
|
|
raise Exception(
|
|
f"found invalid file type information in overlay "
|
|
f"file header: {type!r}"
|
|
)
|
|
|
|
def _print_file(self, f: BinaryIO) -> None:
|
|
# pyre-fixme[6]: Expected `_Reader[Variable[typing.AnyStr <: [str, bytes]]]`
|
|
# for 1st param but got `BinaryIO`.
|
|
shutil.copyfileobj(f, sys.stdout.buffer)
|
|
|
|
def _process_overlay(self, inode_number: int, path: Path, level: int = 0) -> None:
|
|
data = self.overlay.read_dir_inode(inode_number)
|
|
self._print_overlay_tree(inode_number, path, data)
|
|
|
|
# If self.args.depth is negative, recurse forever.
|
|
# Stop if self.args.depth is non-negative, and level reaches the maximum
|
|
# requested recursion depth.
|
|
if self.args.depth >= 0 and level >= self.args.depth:
|
|
return
|
|
|
|
entries = {} if data.entries is None else data.entries
|
|
for name, entry in entries.items():
|
|
if entry.hash or entry.inodeNumber is None or entry.inodeNumber == 0:
|
|
# This entry is not materialized
|
|
continue
|
|
if entry.mode is None or stat.S_IFMT(entry.mode) != stat.S_IFDIR:
|
|
# Only display data for directories
|
|
continue
|
|
print()
|
|
entry_path = path.joinpath(name)
|
|
self._process_overlay(entry.inodeNumber, entry_path, level + 1)
|
|
|
|
def _print_overlay_tree(
|
|
self, inode_number: int, path: Path, tree_data: OverlayDir
|
|
) -> None:
|
|
def hex(binhash: Optional[bytes]) -> str:
|
|
if binhash is None:
|
|
return "None"
|
|
else:
|
|
return binascii.hexlify(binhash).decode("utf-8")
|
|
|
|
print("Inode {}: {}".format(inode_number, path))
|
|
if not tree_data.entries:
|
|
return
|
|
name_width = max(len(name) for name in tree_data.entries)
|
|
for name, entry in tree_data.entries.items():
|
|
assert entry.mode is not None
|
|
perms = entry.mode & 0o7777
|
|
file_type = stat.S_IFMT(entry.mode)
|
|
if file_type == stat.S_IFREG:
|
|
file_type_flag = "f"
|
|
elif file_type == stat.S_IFDIR:
|
|
file_type_flag = "d"
|
|
elif file_type == stat.S_IFLNK:
|
|
file_type_flag = "l"
|
|
else:
|
|
file_type_flag = "?"
|
|
|
|
print(
|
|
" {:{name_width}s} : {:12d} {} {:04o} {}".format(
|
|
name,
|
|
entry.inodeNumber,
|
|
file_type_flag,
|
|
perms,
|
|
hex(entry.hash),
|
|
name_width=name_width,
|
|
)
|
|
)
|