DfsFsck.java

  1. /*
  2.  * Copyright (C) 2017, Google Inc. and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

  10. package org.eclipse.jgit.internal.storage.dfs;

  11. import static java.nio.charset.StandardCharsets.UTF_8;
  12. import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
  13. import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;

  14. import java.io.FileNotFoundException;
  15. import java.io.IOException;

  16. import org.eclipse.jgit.errors.CorruptPackIndexException;
  17. import org.eclipse.jgit.errors.MissingObjectException;
  18. import org.eclipse.jgit.internal.JGitText;
  19. import org.eclipse.jgit.internal.fsck.FsckError;
  20. import org.eclipse.jgit.internal.fsck.FsckError.CorruptIndex;
  21. import org.eclipse.jgit.internal.fsck.FsckError.CorruptObject;
  22. import org.eclipse.jgit.internal.fsck.FsckPackParser;
  23. import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
  24. import org.eclipse.jgit.internal.submodule.SubmoduleValidator;
  25. import org.eclipse.jgit.internal.submodule.SubmoduleValidator.SubmoduleValidationException;
  26. import org.eclipse.jgit.lib.AnyObjectId;
  27. import org.eclipse.jgit.lib.Constants;
  28. import org.eclipse.jgit.lib.GitmoduleEntry;
  29. import org.eclipse.jgit.lib.NullProgressMonitor;
  30. import org.eclipse.jgit.lib.ObjectChecker;
  31. import org.eclipse.jgit.lib.ObjectId;
  32. import org.eclipse.jgit.lib.ObjectLoader;
  33. import org.eclipse.jgit.lib.ProgressMonitor;
  34. import org.eclipse.jgit.lib.Ref;
  35. import org.eclipse.jgit.revwalk.ObjectWalk;
  36. import org.eclipse.jgit.revwalk.RevObject;

  37. /**
  38.  * Verify the validity and connectivity of a DFS repository.
  39.  */
  40. public class DfsFsck {
  41.     private final DfsRepository repo;
  42.     private final DfsObjDatabase objdb;
  43.     private ObjectChecker objChecker = new ObjectChecker();
  44.     private boolean connectivityOnly;

  45.     /**
  46.      * Initialize DFS fsck.
  47.      *
  48.      * @param repository
  49.      *            the dfs repository to check.
  50.      */
  51.     public DfsFsck(DfsRepository repository) {
  52.         repo = repository;
  53.         objdb = repo.getObjectDatabase();
  54.     }

  55.     /**
  56.      * Verify the integrity and connectivity of all objects in the object
  57.      * database.
  58.      *
  59.      * @param pm
  60.      *            callback to provide progress feedback during the check.
  61.      * @return all errors about the repository.
  62.      * @throws java.io.IOException
  63.      *             if encounters IO errors during the process.
  64.      */
  65.     public FsckError check(ProgressMonitor pm) throws IOException {
  66.         if (pm == null) {
  67.             pm = NullProgressMonitor.INSTANCE;
  68.         }

  69.         FsckError errors = new FsckError();
  70.         if (!connectivityOnly) {
  71.             objChecker.reset();
  72.             checkPacks(pm, errors);
  73.         }
  74.         checkConnectivity(pm, errors);
  75.         return errors;
  76.     }

  77.     private void checkPacks(ProgressMonitor pm, FsckError errors)
  78.             throws IOException, FileNotFoundException {
  79.         try (DfsReader ctx = objdb.newReader()) {
  80.             for (DfsPackFile pack : objdb.getPacks()) {
  81.                 DfsPackDescription packDesc = pack.getPackDescription();
  82.                 if (packDesc.getPackSource()
  83.                         == PackSource.UNREACHABLE_GARBAGE) {
  84.                     continue;
  85.                 }
  86.                 try (ReadableChannel rc = objdb.openFile(packDesc, PACK)) {
  87.                     verifyPack(pm, errors, ctx, pack, rc);
  88.                 } catch (MissingObjectException e) {
  89.                     errors.getMissingObjects().add(e.getObjectId());
  90.                 } catch (CorruptPackIndexException e) {
  91.                     errors.getCorruptIndices().add(new CorruptIndex(
  92.                             pack.getPackDescription().getFileName(INDEX),
  93.                             e.getErrorType()));
  94.                 }
  95.             }
  96.         }

  97.         checkGitModules(pm, errors);
  98.     }

  99.     private void verifyPack(ProgressMonitor pm, FsckError errors, DfsReader ctx,
  100.             DfsPackFile pack, ReadableChannel ch)
  101.                     throws IOException, CorruptPackIndexException {
  102.         FsckPackParser fpp = new FsckPackParser(objdb, ch);
  103.         fpp.setObjectChecker(objChecker);
  104.         fpp.overwriteObjectCount(pack.getPackDescription().getObjectCount());
  105.         fpp.parse(pm);
  106.         errors.getCorruptObjects().addAll(fpp.getCorruptObjects());

  107.         fpp.verifyIndex(pack.getPackIndex(ctx));
  108.     }

  109.     private void checkGitModules(ProgressMonitor pm, FsckError errors)
  110.             throws IOException {
  111.         pm.beginTask(JGitText.get().validatingGitModules,
  112.                 objChecker.getGitsubmodules().size());
  113.         for (GitmoduleEntry entry : objChecker.getGitsubmodules()) {
  114.             AnyObjectId blobId = entry.getBlobId();
  115.             ObjectLoader blob = objdb.open(blobId, Constants.OBJ_BLOB);

  116.             try {
  117.                 SubmoduleValidator.assertValidGitModulesFile(
  118.                         new String(blob.getBytes(), UTF_8));
  119.             } catch (SubmoduleValidationException e) {
  120.                 CorruptObject co = new FsckError.CorruptObject(
  121.                         blobId.toObjectId(), Constants.OBJ_BLOB,
  122.                         e.getFsckMessageId());
  123.                 errors.getCorruptObjects().add(co);
  124.             }
  125.             pm.update(1);
  126.         }
  127.         pm.endTask();
  128.     }

  129.     private void checkConnectivity(ProgressMonitor pm, FsckError errors)
  130.             throws IOException {
  131.         pm.beginTask(JGitText.get().countingObjects, ProgressMonitor.UNKNOWN);
  132.         try (ObjectWalk ow = new ObjectWalk(repo)) {
  133.             for (Ref r : repo.getRefDatabase().getRefs()) {
  134.                 ObjectId objectId = r.getObjectId();
  135.                 if (objectId == null) {
  136.                     // skip unborn branch
  137.                     continue;
  138.                 }
  139.                 RevObject tip;
  140.                 try {
  141.                     tip = ow.parseAny(objectId);
  142.                     if (r.getLeaf().getName().startsWith(Constants.R_HEADS)
  143.                             && tip.getType() != Constants.OBJ_COMMIT) {
  144.                         // heads should only point to a commit object
  145.                         errors.getNonCommitHeads().add(r.getLeaf().getName());
  146.                     }
  147.                     ow.markStart(tip);
  148.                 } catch (MissingObjectException e) {
  149.                     errors.getMissingObjects().add(e.getObjectId());
  150.                     continue;
  151.                 }
  152.             }
  153.             try {
  154.                 ow.checkConnectivity();
  155.             } catch (MissingObjectException e) {
  156.                 errors.getMissingObjects().add(e.getObjectId());
  157.             }
  158.         }
  159.         pm.endTask();
  160.     }

  161.     /**
  162.      * Use a customized object checker instead of the default one. Caller can
  163.      * specify a skip list to ignore some errors.
  164.      *
  165.      * It will be reset at the start of each {{@link #check(ProgressMonitor)}
  166.      * call.
  167.      *
  168.      * @param objChecker
  169.      *            A customized object checker.
  170.      */
  171.     public void setObjectChecker(ObjectChecker objChecker) {
  172.         this.objChecker = objChecker;
  173.     }

  174.     /**
  175.      * Whether fsck should bypass object validity and integrity checks and only
  176.      * check connectivity.
  177.      *
  178.      * @param connectivityOnly
  179.      *            whether fsck should bypass object validity and integrity
  180.      *            checks and only check connectivity. The default is
  181.      *            {@code false}, meaning to run all checks.
  182.      */
  183.     public void setConnectivityOnly(boolean connectivityOnly) {
  184.         this.connectivityOnly = connectivityOnly;
  185.     }
  186. }