/* * This file is part of Fim - File Integrity Manager * * Copyright (C) 2017 Etienne Vrignaud * * Fim is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Fim is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Fim. If not, see <http://www.gnu.org/licenses/>. */ package org.fim.internal; import org.fim.model.Context; import org.fim.model.CorruptedStateException; import org.fim.model.FileHash; import org.fim.model.FileState; import org.fim.model.State; import org.fim.util.Logger; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import static org.fim.model.Constants.NO_HASH; public class StateManager { public static final String STATE_EXTENSION = ".json.gz"; private final Context context; public StateManager(Context context) { this.context = context; } public void createNewState(State state) throws IOException { int lastStateNumber = getLastStateNumber(); lastStateNumber++; state.saveToGZipFile(getStateFile(lastStateNumber)); saveLastStateNumber(lastStateNumber); } public State loadLastState() throws IOException { int lastStateNumber = getLastStateNumber(); if (lastStateNumber <= 0) { throw new IllegalStateException(String.format("Unable to load the last State from directory %s", context.getRepositoryStatesDir())); } return loadState(lastStateNumber); } public State loadState(int stateNumber) throws IOException { return loadState(stateNumber, true); } public State loadState(int stateNumber, boolean loadFullState) throws IOException { Path stateFile = getStateFile(stateNumber); if (!Files.exists(stateFile)) { throw new IllegalStateException(String.format("Unable to load State file %d from directory %s", stateNumber, context.getRepositoryStatesDir())); } try { State state = State.loadFromGZipFile(stateFile, loadFullState); if (loadFullState) { adjustAccordingToHashMode(state); } return state; } catch (CorruptedStateException e) { throw new IllegalStateException(String.format("The content of the State file #%d have been modified and may be corrupted", stateNumber)); } } private void adjustAccordingToHashMode(State state) { // Replace by 'no_hash' accurately to be able to compare the FileState entry // Keep the original hash before changing by 'no_hash' in order to fill correctly the previousFileState switch (context.getHashMode()) { case dontHash: for (FileState fileState : state.getFileStates()) { fileState.storeOriginalHash(); fileState.setFileHash(new FileHash(NO_HASH, NO_HASH, NO_HASH)); } break; case hashSmallBlock: for (FileState fileState : state.getFileStates()) { fileState.storeOriginalHash(); FileHash fileHash = fileState.getFileHash(); fileState.setFileHash(new FileHash(fileHash.getSmallBlockHash(), NO_HASH, NO_HASH)); } break; case hashMediumBlock: for (FileState fileState : state.getFileStates()) { fileState.storeOriginalHash(); FileHash fileHash = fileState.getFileHash(); fileState.setFileHash(new FileHash(fileHash.getSmallBlockHash(), fileHash.getMediumBlockHash(), NO_HASH)); } break; case hashAll: // Nothing to do break; } } /** * @return the State file formatted like this: <statesDir>/state_<stateNumber>.json.gz */ public Path getStateFile(int stateNumber) { return context.getRepositoryStatesDir().resolve("state_" + stateNumber + STATE_EXTENSION); } public int getLastStateNumber() { int number; boolean lastStateFileDesynchronized = false; SettingsManager settingsManager = new SettingsManager(context); if (settingsManager.isCreated()) { number = settingsManager.getLastStateNumber(); if (Files.exists(getStateFile(number)) && !Files.exists(getStateFile(number + 1))) { return number; } if (number > 0) { lastStateFileDesynchronized = true; } } for (int index = 1; ; index++) { if (!Files.exists(getStateFile(index))) { number = index - 1; if (lastStateFileDesynchronized) { Logger.error(String.format("lastStateNumber desynchronized. Resetting it to %d.", number)); saveLastStateNumber(number); } return number; } } } public void saveLastStateNumber(int lastStateNumber) { if (lastStateNumber != -1) { SettingsManager settingsManager = new SettingsManager(context); settingsManager.setLastStateNumber(lastStateNumber); settingsManager.save(); } } }