/// <summary> /// Check a canonical formatted tree for errors. /// </summary> /// <param name="raw">The raw tree data. The array is never modified.</param> /// <exception cref="CorruptObjectException">If any error was detected.</exception> public void checkTree(byte[] raw) { int sz = raw.Length; int ptr = 0; int lastNameB = 0, lastNameE = 0, lastMode = 0; while (ptr < sz) { int thisMode = 0; for (; ;) { if (ptr == sz) { throw new CorruptObjectException("truncated in mode"); } byte c = raw[ptr++]; if (' ' == c) { break; } if (c < '0' || c > '7') { throw new CorruptObjectException("invalid mode character"); } if (thisMode == 0 && c == '0') { throw new CorruptObjectException("mode starts with '0'"); } thisMode <<= 3; thisMode += (c - (byte)'0'); } if (FileMode.FromBits(thisMode).ObjectType == ObjectType.Bad) { throw new CorruptObjectException("invalid mode " + NB.DecimalToBase(thisMode, 8)); } int thisNameB = ptr; for (; ;) { if (ptr == sz) { throw new CorruptObjectException("truncated in name"); } byte c = raw[ptr++]; if (c == '\0') { break; } if (c == '/') { throw new CorruptObjectException("name contains '/'"); } } if (thisNameB + 1 == ptr) { throw new CorruptObjectException("zero length name"); } if (raw[thisNameB] == '.') { int nameLen = (ptr - 1) - thisNameB; if (nameLen == 1) { throw new CorruptObjectException("invalid name '.'"); } if (nameLen == 2 && raw[thisNameB + 1] == '.') { throw new CorruptObjectException("invalid name '..'"); } } if (duplicateName(raw, thisNameB, ptr - 1)) { throw new CorruptObjectException("duplicate entry names"); } if (lastNameB != 0) { int cmp = pathCompare(raw, lastNameB, lastNameE, lastMode, thisNameB, ptr - 1, thisMode); if (cmp > 0) { throw new CorruptObjectException("incorrectly sorted"); } } lastNameB = thisNameB; lastNameE = ptr - 1; lastMode = thisMode; ptr += Constants.OBJECT_ID_LENGTH; if (ptr > sz) { throw new CorruptObjectException("truncated in object id"); } } }