示例#1
0
def parse_tree(text, strict=False):
    """Parse a tree text.

    :param text: Serialized text to parse
    :return: iterator of tuples of (name, mode, sha)
    :raise ObjectFormatException: if the object was malformed in some way
    """
    count = 0
    length = len(text)
    while count < length:
        mode_end = text.index(b" ", count)
        mode_text = text[count:mode_end]
        if strict and mode_text.startswith(b"0"):
            raise ObjectFormatException("Invalid mode '%s'" % mode_text)
        try:
            mode = int(mode_text, 8)
        except ValueError:
            raise ObjectFormatException("Invalid mode '%s'" % mode_text)
        name_end = text.index(b"\0", mode_end)
        name = text[mode_end + 1:name_end]
        count = name_end + 21
        sha = text[name_end + 1:count]
        if len(sha) != 20:
            raise ObjectFormatException("Sha has invalid length")
        hexsha = sha_to_hex(sha)
        yield (name, mode, hexsha)
示例#2
0
    def check(self):
        """Check this object for internal consistency.

        :raise ObjectFormatException: if the object is malformed in some way
        """
        super(Tag, self).check()
        self._check_has_member("_object_sha", "missing object sha")
        self._check_has_member("_object_class", "missing object type")
        self._check_has_member("_name", "missing tag name")

        if not self._name:
            raise ObjectFormatException("empty tag name")

        check_hexsha(self._object_sha, "invalid object sha")

        if getattr(self, "_tagger", None):
            check_identity(self._tagger, "invalid tagger")

        self._check_has_member("_tag_time", "missing tag time")
        check_time(self._tag_time)

        last = None
        for field, _ in _parse_message(self._chunked_text):
            if field == _OBJECT_HEADER and last is not None:
                raise ObjectFormatException("unexpected object")
            elif field == _TYPE_HEADER and last != _OBJECT_HEADER:
                raise ObjectFormatException("unexpected type")
            elif field == _TAG_HEADER and last != _TYPE_HEADER:
                raise ObjectFormatException("unexpected tag name")
            elif field == _TAGGER_HEADER and last != _TAG_HEADER:
                raise ObjectFormatException("unexpected tagger")
            last = field
示例#3
0
 def _deserialize(self, chunks):
     """Grab the metadata attached to the tag"""
     self._tagger = None
     self._tag_time = None
     self._tag_timezone = None
     self._tag_timezone_neg_utc = False
     for field, value in _parse_message(chunks):
         if field == _OBJECT_HEADER:
             self._object_sha = value
         elif field == _TYPE_HEADER:
             obj_class = object_class(value)
             if not obj_class:
                 raise ObjectFormatException("Not a known type: %s" % value)
             self._object_class = obj_class
         elif field == _TAG_HEADER:
             self._name = value
         elif field == _TAGGER_HEADER:
             (
                 self._tagger,
                 self._tag_time,
                 (self._tag_timezone, self._tag_timezone_neg_utc),
             ) = parse_time_entry(value)
         elif field is None:
             self._message = value
         else:
             raise ObjectFormatException("Unknown field %s" % field)
示例#4
0
    def check(self):
        """Check this object for internal consistency.

        :raise ObjectFormatException: if the object is malformed in some way
        """
        super(Tree, self).check()
        last = None
        allowed_modes = (
            stat.S_IFREG | 0o755,
            stat.S_IFREG | 0o644,
            stat.S_IFLNK,
            stat.S_IFDIR,
            S_IFGITLINK,
            # TODO: optionally exclude as in git fsck --strict
            stat.S_IFREG | 0o664,
        )
        for name, mode, sha in parse_tree(b"".join(self._chunked_text), True):
            check_hexsha(sha, "invalid sha %s" % sha)
            if b"/" in name or name in (b"", b".", b"..", b".git"):
                raise ObjectFormatException("invalid name %s" %
                                            name.decode("utf-8", "replace"))

            if mode not in allowed_modes:
                raise ObjectFormatException("invalid mode %06o" % mode)

            entry = (name, (mode, sha))
            if last:
                if key_entry(last) > key_entry(entry):
                    raise ObjectFormatException("entries not sorted")
                if name == last[0]:
                    raise ObjectFormatException("duplicate entry %s" % name)
            last = entry
示例#5
0
 def _parse_object_header(magic, f):
     """Parse a new style object, creating it but not reading the file."""
     num_type = (ord(magic[0:1]) >> 4) & 7
     obj_class = object_class(num_type)
     if not obj_class:
         raise ObjectFormatException("Not a known type %d" % num_type)
     return obj_class()
示例#6
0
 def _parse_legacy_object(self, map):
     """Parse a legacy object, setting the raw string."""
     text = _decompress(map)
     header_end = text.find(b"\0")
     if header_end < 0:
         raise ObjectFormatException("Invalid object header, no \\0")
     self.set_raw_string(text[header_end + 1:])
示例#7
0
 def from_file(cls, f):
     """Get the contents of a SHA file on disk."""
     try:
         obj = cls._parse_file(f)
         obj._sha = None
         return obj
     except (IndexError, ValueError):
         raise ObjectFormatException("invalid object header")
示例#8
0
def check_hexsha(hex, error_msg):
    """Check if a string is a valid hex sha string.

    :param hex: Hex string to check
    :param error_msg: Error message to use in exception
    :raise ObjectFormatException: Raised when the string is not valid
    """
    if not valid_hexsha(hex):
        raise ObjectFormatException("%s %s" % (error_msg, hex))
示例#9
0
    def _check_has_member(self, member, error_msg):
        """Check that the object has a given member variable.

        :param member: the member variable to check for
        :param error_msg: the message for an error if the member is missing
        :raise ObjectFormatException: with the given error_msg if member is
            missing or is None
        """
        if getattr(self, member, None) is None:
            raise ObjectFormatException(error_msg)
示例#10
0
 def _deserialize(self, chunks):
     """Grab the entries in the tree"""
     try:
         parsed_entries = parse_tree(b"".join(chunks))
     except ValueError as e:
         raise ObjectFormatException(e)
     # TODO: list comprehension is for efficiency in the common (small)
     # case; if memory efficiency in the large case is a concern, use a
     # genexp.
     self._entries = dict([(n, (m, s)) for n, m, s in parsed_entries])
示例#11
0
def check_time(time_seconds):
    """Check if the specified time is not prone to overflow error.

    This will raise an exception if the time is not valid.

    :param time_info: author/committer/tagger info

    """
    # Prevent overflow error
    if time_seconds > MAX_TIME:
        raise ObjectFormatException("Date field should not exceed %s" %
                                    MAX_TIME)
示例#12
0
    def check(self):
        """Check this object for internal consistency.

        :raise ObjectFormatException: if the object is malformed in some way
        """
        super(Commit, self).check()
        self._check_has_member("_tree", "missing tree")
        self._check_has_member("_author", "missing author")
        self._check_has_member("_committer", "missing committer")
        self._check_has_member("_author_time", "missing author time")
        self._check_has_member("_commit_time", "missing commit time")

        for parent in self._parents:
            check_hexsha(parent, "invalid parent sha")
        check_hexsha(self._tree, "invalid tree sha")

        check_identity(self._author, "invalid author")
        check_identity(self._committer, "invalid committer")

        check_time(self._author_time)
        check_time(self._commit_time)

        last = None
        for field, _ in _parse_message(self._chunked_text):
            if field == _TREE_HEADER and last is not None:
                raise ObjectFormatException("unexpected tree")
            elif field == _PARENT_HEADER and last not in (_PARENT_HEADER,
                                                          _TREE_HEADER):
                raise ObjectFormatException("unexpected parent")
            elif field == _AUTHOR_HEADER and last not in (_TREE_HEADER,
                                                          _PARENT_HEADER):
                raise ObjectFormatException("unexpected author")
            elif field == _COMMITTER_HEADER and last != _AUTHOR_HEADER:
                raise ObjectFormatException("unexpected committer")
            elif field == _ENCODING_HEADER and last != _COMMITTER_HEADER:
                raise ObjectFormatException("unexpected encoding")
            last = field
示例#13
0
def check_identity(identity, error_msg):
    """Check if the specified identity is valid.

    This will raise an exception if the identity is not valid.

    :param identity: Identity string
    :param error_msg: Error message to use in exception
    """
    email_start = identity.find(b"<")
    email_end = identity.find(b">")
    if (email_start < 0 or email_end < 0 or email_end <= email_start
            or identity.find(b"<", email_start + 1) >= 0
            or identity.find(b">", email_end + 1) >= 0
            or not identity.endswith(b">")):
        raise ObjectFormatException(error_msg)
示例#14
0
    def check(self):
        """Check this object for internal consistency.

        :raise ObjectFormatException: if the object is malformed in some way
        :raise ChecksumMismatch: if the object was created with a SHA that does
            not match its contents
        """
        # TODO: if we find that error-checking during object parsing is a
        # performance bottleneck, those checks should be moved to the class's
        # check() method during optimization so we can still check the object
        # when necessary.
        old_sha = self.id
        try:
            self._deserialize(self.as_raw_chunks())
            self._sha = None
            new_sha = self.id
        except Exception as e:
            raise ObjectFormatException(e)
        if old_sha != new_sha:
            raise ChecksumMismatch(new_sha, old_sha)
示例#15
0
 def _parse_legacy_object_header(magic, f):
     """Parse a legacy object, creating it but not reading the file."""
     bufsize = 1024
     decomp = zlib.decompressobj()
     header = decomp.decompress(magic)
     start = 0
     end = -1
     while end < 0:
         extra = f.read(bufsize)
         header += decomp.decompress(extra)
         magic += extra
         end = header.find(b"\0", start)
         start = len(header)
     header = header[:end]
     type_name, size = header.split(b" ", 1)
     size = int(size)  # sanity check
     obj_class = object_class(type_name)
     if not obj_class:
         raise ObjectFormatException("Not a known type: %s" % type_name)
     return obj_class()
示例#16
0
def parse_time_entry(value):
    """Parse time entry behavior

    :param value: Bytes representing a git commit/tag line
    :raise: ObjectFormatException in case of parsing error (malformed
            field date)
    :return: Tuple of (author, time, (timezone, timezone_neg_utc))
    """
    try:
        sep = value.rindex(b"> ")
    except ValueError:
        return (value, None, (None, False))
    try:
        person = value[0:sep + 1]
        rest = value[sep + 2:]
        timetext, timezonetext = rest.rsplit(b" ", 1)
        time = int(timetext)
        timezone, timezone_neg_utc = parse_timezone(timezonetext)
    except ValueError as e:
        raise ObjectFormatException(e)
    return person, time, (timezone, timezone_neg_utc)